blackbox_log/frame/
mod.rs

1//! Types for blackbox log data frames.
2
3#[macro_use]
4mod trace_field;
5
6pub(crate) mod gps;
7pub(crate) mod gps_home;
8pub(crate) mod main;
9pub(crate) mod slow;
10
11use alloc::borrow::ToOwned as _;
12use alloc::format;
13use alloc::vec::Vec;
14use core::fmt;
15use core::iter::{FusedIterator, Peekable};
16use core::marker::PhantomData;
17
18pub use self::gps::{GpsFrame, GpsFrameDef, GpsUnit, GpsValue};
19pub(crate) use self::gps_home::{GpsHomeFrame, GpsPosition};
20pub use self::main::{MainFrame, MainFrameDef, MainUnit, MainValue};
21pub use self::slow::{SlowFrame, SlowFrameDef, SlowUnit, SlowValue};
22use crate::filter::AppliedFilter;
23use crate::headers::{ParseError, ParseResult};
24use crate::parser::{Encoding, InternalResult};
25use crate::predictor::{Predictor, PredictorContext};
26use crate::units::prelude::*;
27use crate::{units, Reader};
28
29mod seal {
30    pub trait Sealed {}
31}
32
33/// A parsed data frame definition.
34///
35/// **Note:** All methods exclude any required metadata fields. See each frame's
36/// definition struct documentation for a list.
37pub trait FrameDef<'data>: seal::Sealed {
38    type Unit: Into<Unit>;
39
40    /// Returns the number of fields in the frame.
41    fn len(&self) -> usize;
42
43    /// Returns `true` if the frame is empty, or none of its fields satisfy
44    /// the configured filter.
45    fn is_empty(&self) -> bool {
46        self.len() == 0
47    }
48
49    /// Returns a field definition by its index.
50    fn get<'def>(&'def self, index: usize) -> Option<FieldDef<'data, Self::Unit>>
51    where
52        'data: 'def;
53
54    /// Iterates over all field definitions in order.
55    fn iter<'def>(&'def self) -> FieldDefIter<'data, 'def, Self>
56    where
57        Self: Sized,
58    {
59        FieldDefIter {
60            frame: self,
61            next: 0,
62            _data: &PhantomData,
63        }
64    }
65}
66
67/// Metadata describing one field.
68#[derive(Debug, Clone, PartialEq, Hash)]
69pub struct FieldDef<'data, U> {
70    pub name: &'data str,
71    pub unit: U,
72    pub signed: bool,
73}
74
75#[derive(Debug)]
76pub struct FieldDefIter<'data, 'def, F> {
77    frame: &'def F,
78    next: usize,
79    _data: &'data PhantomData<()>,
80}
81
82impl<'data, F: FrameDef<'data>> Iterator for FieldDefIter<'data, '_, F> {
83    type Item = FieldDef<'data, F::Unit>;
84
85    fn next(&mut self) -> Option<Self::Item> {
86        let value = self.frame.get(self.next)?;
87        self.next += 1;
88        Some(value)
89    }
90}
91
92impl<'data, F: FrameDef<'data>> FusedIterator for FieldDefIter<'data, '_, F> {}
93
94/// A parsed data frame.
95///
96/// **Note:** All methods exclude any required metadata fields. Those can be
97/// accessed by the inherent methods on each frame struct.
98pub trait Frame: seal::Sealed {
99    type Value: Into<Value>;
100
101    /// Returns the number of fields in the frame.
102    fn len(&self) -> usize;
103
104    /// Returns `true` if the frame is empty, or none of its fields satisfy
105    /// the configured filter.
106    fn is_empty(&self) -> bool {
107        self.len() == 0
108    }
109
110    /// Returns the raw bits of the parsed value of a field by its index.
111    ///
112    /// This ignores the signedness of the field. That can be retrieved from the
113    /// field definition returned by [`FrameDef::get`].
114    ///
115    /// **Note:** Unlike the `--raw` flag for `blackbox_decode`, this does apply
116    /// predictors. This method only skips converting the value into its proper
117    /// units.
118    fn get_raw(&self, index: usize) -> Option<u32>;
119
120    // Iterates over all raw field values in order. See [`Frame::get_raw`].
121    fn iter_raw(&self) -> RawFieldIter<'_, Self>
122    where
123        Self: Sized,
124    {
125        RawFieldIter {
126            frame: self,
127            next: 0,
128        }
129    }
130
131    /// Gets the value of a field by its index.
132    fn get(&self, index: usize) -> Option<Self::Value>;
133
134    /// Iterates over all field values in order.
135    fn iter(&self) -> FieldIter<'_, Self>
136    where
137        Self: Sized,
138    {
139        FieldIter {
140            frame: self,
141            next: 0,
142        }
143    }
144}
145
146/// An iterator over the raw values of the fields of a parsed frame. See
147/// [`Frame::iter_raw`].
148#[derive(Debug)]
149pub struct RawFieldIter<'f, F> {
150    frame: &'f F,
151    next: usize,
152}
153
154impl<F: Frame> Iterator for RawFieldIter<'_, F> {
155    type Item = u32;
156
157    fn next(&mut self) -> Option<Self::Item> {
158        let value = self.frame.get_raw(self.next)?;
159        self.next += 1;
160        Some(value)
161    }
162}
163
164impl<F: Frame> FusedIterator for RawFieldIter<'_, F> {}
165
166/// An iterator over the values of the fields of a parsed frame. See
167/// [`Frame::iter`].
168#[derive(Debug)]
169pub struct FieldIter<'f, F> {
170    frame: &'f F,
171    next: usize,
172}
173
174impl<F: Frame> Iterator for FieldIter<'_, F> {
175    type Item = F::Value;
176
177    fn next(&mut self) -> Option<Self::Item> {
178        let value = self.frame.get(self.next)?;
179        self.next += 1;
180        Some(value)
181    }
182}
183
184impl<F: Frame> FusedIterator for FieldIter<'_, F> {}
185
186/// A wrapper around a frame definition that applies any filter configured in
187/// the [`DataParser`][crate::DataParser].
188#[derive(Debug)]
189pub struct FilteredFrameDef<'a, F> {
190    def: &'a F,
191    filter: &'a AppliedFilter,
192}
193
194impl<'a, F> FilteredFrameDef<'a, F> {
195    pub(super) fn new(def: &'a F, filter: &'a AppliedFilter) -> Self {
196        Self { def, filter }
197    }
198}
199
200impl<F: seal::Sealed> seal::Sealed for FilteredFrameDef<'_, F> {}
201
202impl<'data, F: FrameDef<'data>> FrameDef<'data> for FilteredFrameDef<'_, F> {
203    type Unit = F::Unit;
204
205    #[inline]
206    fn len(&self) -> usize {
207        self.filter.len()
208    }
209
210    fn get<'def>(&'def self, index: usize) -> Option<FieldDef<'data, Self::Unit>>
211    where
212        'data: 'def,
213    {
214        let index = self.filter.get(index)?;
215        self.def.get(index)
216    }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220#[repr(u8)]
221pub enum FrameKind {
222    Event,
223    Data(DataFrameKind),
224}
225
226impl FrameKind {
227    pub(crate) const fn from_byte(byte: u8) -> Option<Self> {
228        match byte {
229            b'E' => Some(Self::Event),
230            _ => {
231                if let Some(kind) = DataFrameKind::from_byte(byte) {
232                    Some(Self::Data(kind))
233                } else {
234                    None
235                }
236            }
237        }
238    }
239}
240
241impl From<FrameKind> for char {
242    fn from(kind: FrameKind) -> Self {
243        match kind {
244            FrameKind::Event => 'E',
245            FrameKind::Data(kind) => kind.into(),
246        }
247    }
248}
249
250impl From<FrameKind> for u8 {
251    fn from(kind: FrameKind) -> Self {
252        match kind {
253            FrameKind::Event => b'E',
254            FrameKind::Data(kind) => kind.into(),
255        }
256    }
257}
258
259impl fmt::Display for FrameKind {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        match self {
262            Self::Event => f.write_str("event"),
263            Self::Data(kind) => kind.fmt(f),
264        }
265    }
266}
267
268byte_enum! {
269    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
270    #[cfg_attr(feature = "_serde", derive(serde::Serialize))]
271    #[repr(u8)]
272    pub enum DataFrameKind {
273        Intra = b'I',
274        Inter = b'P',
275        Gps = b'G',
276        GpsHome = b'H',
277        Slow = b'S',
278    }
279}
280
281impl DataFrameKind {
282    pub(crate) fn from_letter(s: &str) -> Option<Self> {
283        match s {
284            "G" => Some(Self::Gps),
285            "H" => Some(Self::GpsHome),
286            "I" => Some(Self::Intra),
287            "P" => Some(Self::Inter),
288            "S" => Some(Self::Slow),
289            _ => None,
290        }
291    }
292}
293
294impl From<DataFrameKind> for char {
295    fn from(kind: DataFrameKind) -> Self {
296        match kind {
297            DataFrameKind::Gps => 'G',
298            DataFrameKind::GpsHome => 'H',
299            DataFrameKind::Intra => 'I',
300            DataFrameKind::Inter => 'P',
301            DataFrameKind::Slow => 'S',
302        }
303    }
304}
305
306impl fmt::Display for DataFrameKind {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        let kind = match self {
309            Self::Intra => "intra",
310            Self::Inter => "inter",
311            Self::Gps => "GPS",
312            Self::GpsHome => "GPS home",
313            Self::Slow => "slow",
314        };
315
316        f.write_str(kind)
317    }
318}
319
320trait FieldDefDetails<'data> {
321    fn name(&self) -> &'data str;
322    fn predictor(&self) -> Predictor;
323    fn encoding(&self) -> Encoding;
324    fn signed(&self) -> bool;
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
328#[cfg_attr(feature = "_serde", derive(serde::Serialize))]
329pub enum Unit {
330    Amperage,
331    Voltage,
332    Acceleration,
333    Rotation,
334    FlightMode,
335    State,
336    FailsafePhase,
337    GpsCoordinate,
338    Altitude,
339    Velocity,
340    GpsHeading,
341    Boolean,
342    Unitless,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq)]
346pub enum Value {
347    Amperage(ElectricCurrent),
348    Voltage(ElectricPotential),
349    Acceleration(Acceleration),
350    Rotation(AngularVelocity),
351    FlightMode(units::FlightModeSet),
352    State(units::StateSet),
353    FailsafePhase(units::FailsafePhase),
354    Boolean(bool),
355    GpsCoordinate(f64),
356    Altitude(Length),
357    Velocity(Velocity),
358    GpsHeading(f64),
359    Unsigned(u32),
360    Signed(i32),
361}
362
363pub(crate) fn is_frame_def_header(header: &str) -> bool {
364    parse_frame_def_header(header).is_some()
365}
366
367pub(crate) fn parse_frame_def_header(header: &str) -> Option<(DataFrameKind, DataFrameProperty)> {
368    let header = header.strip_prefix("Field ")?;
369    let (kind, property) = header.split_once(' ')?;
370
371    Some((
372        DataFrameKind::from_letter(kind)?,
373        DataFrameProperty::from_name(property)?,
374    ))
375}
376
377// TODO: width?
378#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub(crate) enum DataFrameProperty {
380    Name,
381    Predictor,
382    Encoding,
383    Signed,
384}
385
386impl DataFrameProperty {
387    pub(crate) fn from_name(s: &str) -> Option<Self> {
388        match s {
389            "name" => Some(Self::Name),
390            "predictor" => Some(Self::Predictor),
391            "encoding" => Some(Self::Encoding),
392            "signed" => Some(Self::Signed),
393            _ => None,
394        }
395    }
396}
397
398fn missing_header_error(kind: DataFrameKind, property: &'static str) -> ParseError {
399    tracing::error!("missing header `Field {} {property}`", char::from(kind));
400    ParseError::MissingHeader
401}
402
403fn parse_names(
404    kind: DataFrameKind,
405    names: Option<&str>,
406) -> ParseResult<impl Iterator<Item = &'_ str>> {
407    let names = names.ok_or_else(|| missing_header_error(kind, "name"))?;
408    Ok(names.split(','))
409}
410
411fn parse_enum_list<'a, T>(
412    kind: DataFrameKind,
413    property: &'static str,
414    s: Option<&'a str>,
415    parse: impl Fn(&str) -> Option<T> + 'a,
416) -> ParseResult<impl Iterator<Item = ParseResult<T>> + 'a> {
417    let s = s.ok_or_else(|| missing_header_error(kind, property))?;
418    Ok(s.split(',').map(move |s| {
419        parse(s).ok_or_else(|| ParseError::InvalidHeader {
420            header: format!("Field {} {property}", char::from(kind)),
421            value: s.to_owned(),
422        })
423    }))
424}
425
426#[inline]
427fn parse_predictors(
428    kind: DataFrameKind,
429    predictors: Option<&'_ str>,
430) -> ParseResult<impl Iterator<Item = ParseResult<Predictor>> + '_> {
431    parse_enum_list(kind, "predictor", predictors, Predictor::from_num_str)
432}
433
434#[inline]
435fn parse_encodings(
436    kind: DataFrameKind,
437    encodings: Option<&'_ str>,
438) -> ParseResult<impl Iterator<Item = ParseResult<Encoding>> + '_> {
439    parse_enum_list(kind, "encoding", encodings, Encoding::from_num_str)
440}
441
442fn parse_signs(
443    kind: DataFrameKind,
444    names: Option<&str>,
445) -> ParseResult<impl Iterator<Item = bool> + '_> {
446    let names = names.ok_or_else(|| missing_header_error(kind, "signed"))?;
447    Ok(names.split(',').map(|s| s.trim() != "0"))
448}
449
450fn count_fields_with_same_encoding(
451    fields: &mut Peekable<impl Iterator<Item = Encoding>>,
452    max: usize,
453    encoding: Encoding,
454) -> usize {
455    let mut extra = 0;
456    while extra < max && fields.next_if_eq(&encoding).is_some() {
457        extra += 1;
458    }
459    extra
460}
461
462fn read_field_values<T>(
463    data: &mut Reader,
464    fields: &[T],
465    get_encoding: impl Fn(&T) -> Encoding,
466) -> InternalResult<Vec<u32>> {
467    let mut encodings = fields.iter().map(get_encoding).peekable();
468    let mut values = Vec::with_capacity(encodings.len());
469
470    while let Some(encoding) = encodings.next() {
471        let extra = encoding.max_chunk_size() - 1;
472        let extra = count_fields_with_same_encoding(&mut encodings, extra, encoding);
473
474        encoding.decode_into(data, extra, &mut values)?;
475    }
476
477    debug_assert_eq!(values.len(), fields.len());
478
479    Ok(values)
480}
481
482fn parse_impl<'data, F: FieldDefDetails<'data>>(
483    mut ctx: PredictorContext<'_, 'data>,
484    raw: &[u32],
485    fields: impl IntoIterator<Item = F>,
486    update_ctx: impl Fn(&mut PredictorContext<'_, 'data>, usize),
487) -> Vec<u32> {
488    let mut values = Vec::with_capacity(raw.len());
489
490    for (i, field) in fields.into_iter().enumerate() {
491        let encoding = field.encoding();
492        let predictor = field.predictor();
493
494        let raw = raw[i];
495        let signed = encoding.is_signed();
496
497        update_ctx(&mut ctx, i);
498
499        trace_field!(pre, field = field, enc = encoding, raw = raw);
500
501        let value = predictor.apply(raw, signed, Some(&values), &ctx);
502        values.push(value);
503
504        trace_field!(
505            post,
506            field = field,
507            pred = predictor,
508            final = value
509        );
510    }
511
512    values
513}