blackbox_log/frame/gps/
def.rs

1use alloc::borrow::ToOwned as _;
2use alloc::vec::Vec;
3
4use tracing::instrument;
5
6use super::{GpsUnit, RawGpsFrame};
7use crate::frame::{
8    self, DataFrameKind, DataFrameProperty, FieldDef, FieldDefDetails, FrameDef, GpsHomeFrame,
9};
10use crate::headers::{ParseError, ParseResult};
11use crate::parser::{decode, Encoding, InternalResult};
12use crate::predictor::{Predictor, PredictorContext};
13use crate::utils::to_base_field;
14use crate::{Headers, Reader, Unit};
15
16/// The parsed frame definition for GPS frames.
17#[derive(Debug, Clone)]
18pub struct GpsFrameDef<'data> {
19    pub(super) fields: Vec<GpsFieldDef<'data>>,
20}
21
22impl frame::seal::Sealed for GpsFrameDef<'_> {}
23
24impl<'data> FrameDef<'data> for GpsFrameDef<'data> {
25    type Unit = GpsUnit;
26
27    #[inline]
28    fn len(&self) -> usize {
29        self.fields.len()
30    }
31
32    fn get<'a>(&'a self, index: usize) -> Option<FieldDef<'data, Self::Unit>>
33    where
34        'data: 'a,
35    {
36        self.fields.get(index).map(
37            |&GpsFieldDef {
38                 name, unit, signed, ..
39             }| FieldDef { name, unit, signed },
40        )
41    }
42}
43
44impl<'data> GpsFrameDef<'data> {
45    pub(crate) fn builder() -> GpsFrameDefBuilder<'data> {
46        GpsFrameDefBuilder::default()
47    }
48
49    pub(crate) fn validate(
50        &self,
51        check_predictor: impl Fn(DataFrameKind, &'data str, Predictor, usize) -> ParseResult<()>,
52        check_unit: impl Fn(DataFrameKind, &'data str, Unit) -> ParseResult<()>,
53    ) -> ParseResult<()> {
54        for (
55            i,
56            GpsFieldDef {
57                name,
58                predictor,
59                unit,
60                ..
61            },
62        ) in self.fields.iter().enumerate()
63        {
64            check_predictor(DataFrameKind::Gps, name, *predictor, i)?;
65            check_unit(DataFrameKind::Gps, name, Unit::from(*unit))?;
66        }
67
68        Ok(())
69    }
70
71    #[instrument(level = "trace", name = "GpsFrameDef::parse", skip_all)]
72    pub(crate) fn parse(
73        &self,
74        data: &mut Reader,
75        headers: &Headers,
76        last_main_time: Option<u64>,
77        last_home: Option<&GpsHomeFrame>,
78    ) -> InternalResult<RawGpsFrame> {
79        let time = {
80            let time = last_main_time.unwrap_or(0);
81            let offset = decode::variable(data)?.into();
82
83            let time = time.saturating_add(offset);
84            tracing::trace!(time, offset);
85            time
86        };
87
88        let raw = frame::read_field_values(data, &self.fields, |f| f.encoding)?;
89
90        let ctx = PredictorContext::with_home(headers, last_home.map(|home| home.0));
91        let mut values = Vec::with_capacity(raw.len());
92
93        for (i, field) in self.fields.iter().enumerate() {
94            let raw = raw[i];
95            let signed = field.encoding.is_signed();
96
97            trace_field!(pre, field = field, enc = field.encoding, raw = raw);
98
99            let value = field.predictor.apply(raw, signed, None, &ctx);
100
101            trace_field!(
102                post,
103                field = field,
104                pred = field.predictor,
105                final = value
106            );
107
108            values.push(value);
109        }
110
111        Ok(RawGpsFrame { time, values })
112    }
113}
114
115#[allow(dead_code)]
116#[derive(Debug, Clone)]
117pub(crate) struct GpsFieldDef<'data> {
118    pub(crate) name: &'data str,
119    pub(crate) predictor: Predictor,
120    pub(crate) encoding: Encoding,
121    pub(crate) unit: GpsUnit,
122    pub(crate) signed: bool,
123}
124
125impl<'data> FieldDefDetails<'data> for &GpsFieldDef<'data> {
126    fn name(&self) -> &'data str {
127        self.name
128    }
129
130    fn predictor(&self) -> Predictor {
131        self.predictor
132    }
133
134    fn encoding(&self) -> Encoding {
135        self.encoding
136    }
137
138    fn signed(&self) -> bool {
139        self.signed
140    }
141}
142
143#[derive(Debug, Default)]
144pub(crate) struct GpsFrameDefBuilder<'data> {
145    names: Option<&'data str>,
146    predictors: Option<&'data str>,
147    encodings: Option<&'data str>,
148    signs: Option<&'data str>,
149}
150
151impl<'data> GpsFrameDefBuilder<'data> {
152    pub(crate) fn update(&mut self, property: DataFrameProperty, value: &'data str) {
153        let value = Some(value);
154
155        match property {
156            DataFrameProperty::Name => self.names = value,
157            DataFrameProperty::Predictor => self.predictors = value,
158            DataFrameProperty::Encoding => self.encodings = value,
159            DataFrameProperty::Signed => self.signs = value,
160        }
161    }
162
163    pub(crate) fn parse(self) -> ParseResult<Option<GpsFrameDef<'data>>> {
164        let kind = DataFrameKind::Gps;
165
166        if self.names.is_none()
167            && self.predictors.is_none()
168            && self.encodings.is_none()
169            && self.signs.is_none()
170        {
171            return Ok(None);
172        }
173
174        let mut names = frame::parse_names(kind, self.names)?;
175        let mut predictors = frame::parse_predictors(kind, self.predictors)?;
176        let mut encodings = frame::parse_encodings(kind, self.encodings)?;
177        let mut signs = frame::parse_signs(kind, self.signs)?;
178
179        let mut fields = (names.by_ref().zip(signs.by_ref()))
180            .zip(predictors.by_ref().zip(encodings.by_ref()))
181            .map(|((name, signed), (predictor, encoding))| {
182                Ok(GpsFieldDef {
183                    name,
184                    predictor: predictor?,
185                    encoding: encoding?,
186                    unit: unit_from_name(name),
187                    signed,
188                })
189            });
190
191        if !matches!(
192            fields.next().transpose()?,
193            Some(GpsFieldDef {
194                name: "time",
195                predictor: Predictor::LastMainFrameTime,
196                encoding: Encoding::Variable,
197                ..
198            })
199        ) {
200            return Err(ParseError::MissingField {
201                frame: DataFrameKind::Gps,
202                field: "time".to_owned(),
203            });
204        }
205
206        let mut fields = fields.collect::<Result<Vec<_>, _>>()?;
207        for (i, j) in (1..fields.len()).map(|i| (i - 1, i)) {
208            if fields[i].predictor == Predictor::HomeLat
209                && fields[j].predictor == Predictor::HomeLat
210            {
211                fields[j].predictor = Predictor::HomeLon;
212            }
213        }
214
215        if names.next().is_some()
216            || predictors.next().is_some()
217            || encodings.next().is_some()
218            || signs.next().is_some()
219        {
220            tracing::error!("not all gps definition headers are of equal length");
221            return Err(ParseError::MalformedFrameDef(DataFrameKind::Gps));
222        }
223
224        Ok(Some(GpsFrameDef { fields }))
225    }
226}
227
228fn unit_from_name(name: &str) -> GpsUnit {
229    match to_base_field(name) {
230        "GPS_coord" => GpsUnit::Coordinate,
231        "GPS_altitude" => GpsUnit::Altitude,
232        "GPS_speed" => GpsUnit::Velocity,
233        "GPS_ground_course" => GpsUnit::Heading,
234        _ => GpsUnit::Unitless,
235    }
236}