Skip to main content

veloxity_core/sensors/
ingestion.rs

1use crate::{
2    math::FlightFloat,
3    packets::*,
4    params::Params,
5    sensors::processors::{
6        BaroProcessor, BatteryProcessor, CalibrationFlags, ImuProcessor, MagProcessor,
7        PassthroughAttitudeProcessor, PassthroughGNSSProcessor, PassthroughRangeProcessor,
8        PassthroughRcProcessor, PitotProcessor, SensorPacketProcessor,
9    },
10    sensors::{ProcessedSensors, SensorBus},
11};
12
13pub struct SensorProcessorSet<
14    R: FlightFloat,
15    ImuProc = ImuProcessor<R>,
16    MagProc = MagProcessor,
17    BaroProc = BaroProcessor,
18    PitotProc = PitotProcessor,
19    RangeProc = PassthroughRangeProcessor,
20    GnssProc = PassthroughGNSSProcessor,
21    BatteryProc = BatteryProcessor,
22    RcProc = PassthroughRcProcessor,
23    AttitudeProc = PassthroughAttitudeProcessor,
24> {
25    _real: core::marker::PhantomData<R>,
26    pub imu: ImuProc,
27    pub mag: MagProc,
28    pub baro: BaroProc,
29    pub pitot: PitotProc,
30    pub range: RangeProc,
31    pub gnss: GnssProc,
32    pub battery: BatteryProc,
33    pub rc: RcProc,
34    pub attitude: AttitudeProc,
35}
36
37impl<
38    R,
39    ImuProc,
40    MagProc,
41    BaroProc,
42    PitotProc,
43    RangeProc,
44    GnssProc,
45    BatteryProc,
46    RcProc,
47    AttitudeProc,
48> Default
49    for SensorProcessorSet<
50        R,
51        ImuProc,
52        MagProc,
53        BaroProc,
54        PitotProc,
55        RangeProc,
56        GnssProc,
57        BatteryProc,
58        RcProc,
59        AttitudeProc,
60    >
61where
62    R: FlightFloat,
63    ImuProc: Default,
64    MagProc: Default,
65    BaroProc: Default,
66    PitotProc: Default,
67    RangeProc: Default,
68    GnssProc: Default,
69    BatteryProc: Default,
70    RcProc: Default,
71    AttitudeProc: Default,
72{
73    fn default() -> Self {
74        Self {
75            _real: core::marker::PhantomData,
76            imu: ImuProc::default(),
77            mag: MagProc::default(),
78            baro: BaroProc::default(),
79            pitot: PitotProc::default(),
80            range: RangeProc::default(),
81            gnss: GnssProc::default(),
82            battery: BatteryProc::default(),
83            rc: RcProc::default(),
84            attitude: AttitudeProc::default(),
85        }
86    }
87}
88
89pub trait SensorProcessorAccess<R: FlightFloat> {
90    type Imu: SensorPacketProcessor<ImuPacket<R>>;
91    type Mag: SensorPacketProcessor<MagPacket>;
92    type Baro: SensorPacketProcessor<BaroPacket>;
93    type Pitot: SensorPacketProcessor<PitotPacket>;
94    type Range: SensorPacketProcessor<RangePacket>;
95    type Gnss: SensorPacketProcessor<GNSSPacket>;
96    type Battery: SensorPacketProcessor<BatteryPacket>;
97    type Rc: SensorPacketProcessor<RcPacket>;
98    type Attitude: SensorPacketProcessor<AttitudePacket>;
99
100    fn imu(&mut self) -> &mut Self::Imu;
101    fn mag(&mut self) -> &mut Self::Mag;
102    fn baro(&mut self) -> &mut Self::Baro;
103    fn pitot(&mut self) -> &mut Self::Pitot;
104    fn range(&mut self) -> &mut Self::Range;
105    fn gnss(&mut self) -> &mut Self::Gnss;
106    fn battery(&mut self) -> &mut Self::Battery;
107    fn rc(&mut self) -> &mut Self::Rc;
108    fn attitude(&mut self) -> &mut Self::Attitude;
109}
110
111impl<
112    R,
113    ImuProc,
114    MagProc,
115    BaroProc,
116    PitotProc,
117    RangeProc,
118    GnssProc,
119    BatteryProc,
120    RcProc,
121    AttitudeProc,
122> SensorProcessorAccess<R>
123    for SensorProcessorSet<
124        R,
125        ImuProc,
126        MagProc,
127        BaroProc,
128        PitotProc,
129        RangeProc,
130        GnssProc,
131        BatteryProc,
132        RcProc,
133        AttitudeProc,
134    >
135where
136    R: FlightFloat,
137    ImuProc: SensorPacketProcessor<ImuPacket<R>>,
138    MagProc: SensorPacketProcessor<MagPacket>,
139    BaroProc: SensorPacketProcessor<BaroPacket>,
140    PitotProc: SensorPacketProcessor<PitotPacket>,
141    RangeProc: SensorPacketProcessor<RangePacket>,
142    GnssProc: SensorPacketProcessor<GNSSPacket>,
143    BatteryProc: SensorPacketProcessor<BatteryPacket>,
144    RcProc: SensorPacketProcessor<RcPacket>,
145    AttitudeProc: SensorPacketProcessor<AttitudePacket>,
146{
147    type Imu = ImuProc;
148    type Mag = MagProc;
149    type Baro = BaroProc;
150    type Pitot = PitotProc;
151    type Range = RangeProc;
152    type Gnss = GnssProc;
153    type Battery = BatteryProc;
154    type Rc = RcProc;
155    type Attitude = AttitudeProc;
156
157    fn imu(&mut self) -> &mut Self::Imu {
158        &mut self.imu
159    }
160
161    fn mag(&mut self) -> &mut Self::Mag {
162        &mut self.mag
163    }
164
165    fn baro(&mut self) -> &mut Self::Baro {
166        &mut self.baro
167    }
168
169    fn pitot(&mut self) -> &mut Self::Pitot {
170        &mut self.pitot
171    }
172
173    fn range(&mut self) -> &mut Self::Range {
174        &mut self.range
175    }
176
177    fn gnss(&mut self) -> &mut Self::Gnss {
178        &mut self.gnss
179    }
180
181    fn battery(&mut self) -> &mut Self::Battery {
182        &mut self.battery
183    }
184
185    fn rc(&mut self) -> &mut Self::Rc {
186        &mut self.rc
187    }
188
189    fn attitude(&mut self) -> &mut Self::Attitude {
190        &mut self.attitude
191    }
192}
193
194pub struct SensorIngestionCtx<'a, R: FlightFloat, Processors = SensorProcessorSet<R>> {
195    pub raw: &'a mut SensorBus<R>,
196    pub processed: &'a mut ProcessedSensors<R>,
197    pub processors: &'a mut Processors,
198    pub flags: &'a mut CalibrationFlags,
199    pub params: &'a mut Params,
200    pub now_ms: u32,
201}
202
203pub fn process_sensor_bus<R, Processors>(ctx: SensorIngestionCtx<'_, R, Processors>)
204where
205    R: FlightFloat,
206    Processors: SensorProcessorAccess<R>,
207{
208    let SensorIngestionCtx {
209        raw,
210        processed,
211        processors,
212        flags,
213        params,
214        now_ms,
215    } = ctx;
216
217    if raw.imu.is_some() {
218        processed.imu = processors.imu().process(&mut raw.imu, flags, params);
219    } else {
220        processed.imu = None;
221    }
222    if raw.mag.is_some() {
223        processed.mag = processors.mag().process(&mut raw.mag, flags, params);
224    } else {
225        processed.mag = None;
226    }
227    if raw.baro.is_some() {
228        processed.baro = processors
229            .baro()
230            .process_at(&mut raw.baro, flags, params, now_ms);
231    } else {
232        processed.baro = None;
233    }
234    if raw.pitot.is_some() {
235        processed.pitot = processors
236            .pitot()
237            .process_at(&mut raw.pitot, flags, params, now_ms);
238    } else {
239        processed.pitot = None;
240    }
241    if raw.range.is_some() {
242        processed.range = processors.range().process(&mut raw.range, flags, params);
243    } else {
244        processed.range = None;
245    }
246    if raw.gnss.is_some() {
247        processed.gnss = processors.gnss().process(&mut raw.gnss, flags, params);
248    } else {
249        processed.gnss = None;
250    }
251    if raw.battery.is_some() {
252        processed.battery = processors
253            .battery()
254            .process(&mut raw.battery, flags, params);
255    }
256    if raw.rc.is_some() {
257        processed.rc = processors.rc().process(&mut raw.rc, flags, params);
258    } else {
259        processed.rc = None;
260    }
261    if raw.attitude.is_some() {
262        processed.attitude = processors
263            .attitude()
264            .process(&mut raw.attitude, flags, params);
265    } else {
266        processed.attitude = None;
267    }
268}
269
270pub fn process_imu_sensor<R, Processors>(ctx: SensorIngestionCtx<'_, R, Processors>)
271where
272    R: FlightFloat,
273    Processors: SensorProcessorAccess<R>,
274{
275    let SensorIngestionCtx {
276        raw,
277        processed,
278        processors,
279        flags,
280        params,
281        now_ms: _,
282    } = ctx;
283
284    if raw.imu.is_some() {
285        processed.imu = processors.imu().process(&mut raw.imu, flags, params);
286    } else {
287        processed.imu = None;
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use super::*;
294    use crate::{
295        packets::RosflightPacketHeader,
296        sensors::processors::{
297            PassthroughBaroProcessor, PassthroughBatteryProcessor, PassthroughImuProcessor,
298            PassthroughMagProcessor, PassthroughPitotProcessor,
299        },
300    };
301
302    #[test]
303    fn process_sensor_bus_moves_raw_packets_into_named_processed_fields() {
304        let mut raw = SensorBus::<f64>::default();
305        let mut processed = ProcessedSensors::<f64>::default();
306        let mut processors = SensorProcessorSet::<
307            f64,
308            PassthroughImuProcessor,
309            PassthroughMagProcessor,
310            PassthroughBaroProcessor,
311            PassthroughPitotProcessor,
312            PassthroughRangeProcessor,
313            PassthroughGNSSProcessor,
314            PassthroughBatteryProcessor,
315            PassthroughRcProcessor,
316            PassthroughAttitudeProcessor,
317        >::default();
318        let mut flags = CalibrationFlags::empty();
319        let mut params = Params::new();
320
321        raw.rc = Some(Ok(RcPacket {
322            header: RosflightPacketHeader {
323                timestamp: 123,
324                status: 0,
325            },
326            n_chan: 1,
327            chan: [0.0; RC_PACKET_CHANNELS],
328            lol: false,
329        }));
330
331        process_sensor_bus(SensorIngestionCtx {
332            raw: &mut raw,
333            processed: &mut processed,
334            processors: &mut processors,
335            flags: &mut flags,
336            params: &mut params,
337            now_ms: 0,
338        });
339
340        assert!(raw.rc.is_none());
341        assert_eq!(processed.rc.unwrap().header.timestamp, 123);
342    }
343
344    #[test]
345    fn process_sensor_bus_clears_stale_one_shot_packets_when_no_raw_packet_arrives() {
346        let mut raw = SensorBus::<f64>::default();
347        let mut processed = ProcessedSensors::<f64> {
348            imu: Some(ImuPacket {
349                header: RosflightPacketHeader {
350                    timestamp: 100,
351                    status: 0,
352                },
353                ..Default::default()
354            }),
355            rc: Some(RcPacket {
356                header: RosflightPacketHeader {
357                    timestamp: 100,
358                    status: 0,
359                },
360                n_chan: 1,
361                chan: [0.0; RC_PACKET_CHANNELS],
362                lol: false,
363            }),
364            ..Default::default()
365        };
366        let mut processors = SensorProcessorSet::<
367            f64,
368            PassthroughImuProcessor,
369            PassthroughMagProcessor,
370            PassthroughBaroProcessor,
371            PassthroughPitotProcessor,
372            PassthroughRangeProcessor,
373            PassthroughGNSSProcessor,
374            PassthroughBatteryProcessor,
375            PassthroughRcProcessor,
376            PassthroughAttitudeProcessor,
377        >::default();
378        let mut flags = CalibrationFlags::empty();
379        let mut params = Params::new();
380
381        process_sensor_bus(SensorIngestionCtx {
382            raw: &mut raw,
383            processed: &mut processed,
384            processors: &mut processors,
385            flags: &mut flags,
386            params: &mut params,
387            now_ms: 0,
388        });
389
390        assert!(processed.imu.is_none());
391        assert!(processed.rc.is_none());
392    }
393
394    #[test]
395    fn process_imu_sensor_leaves_service_sensor_state_intact() {
396        let mut raw = SensorBus::<f64>::default();
397        let mut processed = ProcessedSensors::<f64> {
398            rc: Some(RcPacket {
399                header: RosflightPacketHeader {
400                    timestamp: 100,
401                    status: 0,
402                },
403                n_chan: 1,
404                chan: [0.0; RC_PACKET_CHANNELS],
405                lol: false,
406            }),
407            ..Default::default()
408        };
409        let mut processors = SensorProcessorSet::<
410            f64,
411            PassthroughImuProcessor,
412            PassthroughMagProcessor,
413            PassthroughBaroProcessor,
414            PassthroughPitotProcessor,
415            PassthroughRangeProcessor,
416            PassthroughGNSSProcessor,
417            PassthroughBatteryProcessor,
418            PassthroughRcProcessor,
419            PassthroughAttitudeProcessor,
420        >::default();
421        let mut flags = CalibrationFlags::empty();
422        let mut params = Params::new();
423
424        raw.imu = Some(Ok(ImuPacket {
425            header: RosflightPacketHeader {
426                timestamp: 200,
427                status: 0,
428            },
429            ..Default::default()
430        }));
431
432        process_imu_sensor(SensorIngestionCtx {
433            raw: &mut raw,
434            processed: &mut processed,
435            processors: &mut processors,
436            flags: &mut flags,
437            params: &mut params,
438            now_ms: 0,
439        });
440
441        assert!(raw.imu.is_none());
442        assert_eq!(processed.imu.unwrap().header.timestamp, 200);
443        assert_eq!(processed.rc.unwrap().header.timestamp, 100);
444    }
445}