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}
201
202pub fn process_sensor_bus<R, Processors>(ctx: SensorIngestionCtx<'_, R, Processors>)
203where
204    R: FlightFloat,
205    Processors: SensorProcessorAccess<R>,
206{
207    let SensorIngestionCtx {
208        raw,
209        processed,
210        processors,
211        flags,
212        params,
213    } = ctx;
214
215    if raw.imu.is_some() {
216        processed.imu = processors.imu().process(&mut raw.imu, flags, params);
217    } else {
218        processed.imu = None;
219    }
220    if raw.mag.is_some() {
221        processed.mag = processors.mag().process(&mut raw.mag, flags, params);
222    } else {
223        processed.mag = None;
224    }
225    if raw.baro.is_some() {
226        processed.baro = processors.baro().process(&mut raw.baro, flags, params);
227    } else {
228        processed.baro = None;
229    }
230    if raw.pitot.is_some() {
231        processed.pitot = processors.pitot().process(&mut raw.pitot, flags, params);
232    } else {
233        processed.pitot = None;
234    }
235    if raw.range.is_some() {
236        processed.range = processors.range().process(&mut raw.range, flags, params);
237    } else {
238        processed.range = None;
239    }
240    if raw.gnss.is_some() {
241        processed.gnss = processors.gnss().process(&mut raw.gnss, flags, params);
242    } else {
243        processed.gnss = None;
244    }
245    if raw.battery.is_some() {
246        processed.battery = processors
247            .battery()
248            .process(&mut raw.battery, flags, params);
249    }
250    if raw.rc.is_some() {
251        processed.rc = processors.rc().process(&mut raw.rc, flags, params);
252    } else {
253        processed.rc = None;
254    }
255    if raw.attitude.is_some() {
256        processed.attitude = processors
257            .attitude()
258            .process(&mut raw.attitude, flags, params);
259    } else {
260        processed.attitude = None;
261    }
262}
263
264pub fn process_imu_sensor<R, Processors>(ctx: SensorIngestionCtx<'_, R, Processors>)
265where
266    R: FlightFloat,
267    Processors: SensorProcessorAccess<R>,
268{
269    let SensorIngestionCtx {
270        raw,
271        processed,
272        processors,
273        flags,
274        params,
275    } = ctx;
276
277    if raw.imu.is_some() {
278        processed.imu = processors.imu().process(&mut raw.imu, flags, params);
279    } else {
280        processed.imu = None;
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287    use crate::{
288        packets::RosflightPacketHeader,
289        sensors::processors::{
290            PassthroughBaroProcessor, PassthroughBatteryProcessor, PassthroughImuProcessor,
291            PassthroughMagProcessor, PassthroughPitotProcessor,
292        },
293    };
294
295    #[test]
296    fn process_sensor_bus_moves_raw_packets_into_named_processed_fields() {
297        let mut raw = SensorBus::<f64>::default();
298        let mut processed = ProcessedSensors::<f64>::default();
299        let mut processors = SensorProcessorSet::<
300            f64,
301            PassthroughImuProcessor,
302            PassthroughMagProcessor,
303            PassthroughBaroProcessor,
304            PassthroughPitotProcessor,
305            PassthroughRangeProcessor,
306            PassthroughGNSSProcessor,
307            PassthroughBatteryProcessor,
308            PassthroughRcProcessor,
309            PassthroughAttitudeProcessor,
310        >::default();
311        let mut flags = CalibrationFlags::empty();
312        let mut params = Params::new();
313
314        raw.rc = Some(Ok(RcPacket {
315            header: RosflightPacketHeader {
316                timestamp: 123,
317                status: 0,
318            },
319            n_chan: 1,
320            chan: [0.0; RC_PACKET_CHANNELS],
321            lol: false,
322        }));
323
324        process_sensor_bus(SensorIngestionCtx {
325            raw: &mut raw,
326            processed: &mut processed,
327            processors: &mut processors,
328            flags: &mut flags,
329            params: &mut params,
330        });
331
332        assert!(raw.rc.is_none());
333        assert_eq!(processed.rc.unwrap().header.timestamp, 123);
334    }
335
336    #[test]
337    fn process_sensor_bus_clears_stale_one_shot_packets_when_no_raw_packet_arrives() {
338        let mut raw = SensorBus::<f64>::default();
339        let mut processed = ProcessedSensors::<f64> {
340            imu: Some(ImuPacket {
341                header: RosflightPacketHeader {
342                    timestamp: 100,
343                    status: 0,
344                },
345                ..Default::default()
346            }),
347            rc: Some(RcPacket {
348                header: RosflightPacketHeader {
349                    timestamp: 100,
350                    status: 0,
351                },
352                n_chan: 1,
353                chan: [0.0; RC_PACKET_CHANNELS],
354                lol: false,
355            }),
356            ..Default::default()
357        };
358        let mut processors = SensorProcessorSet::<
359            f64,
360            PassthroughImuProcessor,
361            PassthroughMagProcessor,
362            PassthroughBaroProcessor,
363            PassthroughPitotProcessor,
364            PassthroughRangeProcessor,
365            PassthroughGNSSProcessor,
366            PassthroughBatteryProcessor,
367            PassthroughRcProcessor,
368            PassthroughAttitudeProcessor,
369        >::default();
370        let mut flags = CalibrationFlags::empty();
371        let mut params = Params::new();
372
373        process_sensor_bus(SensorIngestionCtx {
374            raw: &mut raw,
375            processed: &mut processed,
376            processors: &mut processors,
377            flags: &mut flags,
378            params: &mut params,
379        });
380
381        assert!(processed.imu.is_none());
382        assert!(processed.rc.is_none());
383    }
384
385    #[test]
386    fn process_imu_sensor_leaves_service_sensor_state_intact() {
387        let mut raw = SensorBus::<f64>::default();
388        let mut processed = ProcessedSensors::<f64> {
389            rc: Some(RcPacket {
390                header: RosflightPacketHeader {
391                    timestamp: 100,
392                    status: 0,
393                },
394                n_chan: 1,
395                chan: [0.0; RC_PACKET_CHANNELS],
396                lol: false,
397            }),
398            ..Default::default()
399        };
400        let mut processors = SensorProcessorSet::<
401            f64,
402            PassthroughImuProcessor,
403            PassthroughMagProcessor,
404            PassthroughBaroProcessor,
405            PassthroughPitotProcessor,
406            PassthroughRangeProcessor,
407            PassthroughGNSSProcessor,
408            PassthroughBatteryProcessor,
409            PassthroughRcProcessor,
410            PassthroughAttitudeProcessor,
411        >::default();
412        let mut flags = CalibrationFlags::empty();
413        let mut params = Params::new();
414
415        raw.imu = Some(Ok(ImuPacket {
416            header: RosflightPacketHeader {
417                timestamp: 200,
418                status: 0,
419            },
420            ..Default::default()
421        }));
422
423        process_imu_sensor(SensorIngestionCtx {
424            raw: &mut raw,
425            processed: &mut processed,
426            processors: &mut processors,
427            flags: &mut flags,
428            params: &mut params,
429        });
430
431        assert!(raw.imu.is_none());
432        assert_eq!(processed.imu.unwrap().header.timestamp, 200);
433        assert_eq!(processed.rc.unwrap().header.timestamp, 100);
434    }
435}