blackbox_log/frame/
gps_home.rs

1use alloc::borrow::ToOwned as _;
2use alloc::vec::Vec;
3
4use tracing::instrument;
5
6use super::{read_field_values, DataFrameKind, DataFrameProperty};
7use crate::headers::{ParseError, ParseResult};
8use crate::parser::{Encoding, InternalResult};
9use crate::predictor::{Predictor, PredictorContext};
10use crate::{Headers, Reader};
11
12#[derive(Debug, Clone)]
13pub(crate) struct GpsHomeFrame(pub(crate) GpsPosition);
14
15#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
16pub(crate) struct GpsPosition {
17    pub(crate) latitude: i32,
18    pub(crate) longitude: i32,
19}
20
21#[derive(Debug, Clone)]
22pub(crate) struct GpsHomeFrameDef<'data>([GpsHomeFieldDef<'data>; 2], Vec<Encoding>);
23
24impl<'data> GpsHomeFrameDef<'data> {
25    pub(crate) fn builder() -> GpsHomeFrameDefBuilder<'data> {
26        GpsHomeFrameDefBuilder::default()
27    }
28
29    pub(crate) fn validate(
30        &self,
31        check_predictor: impl Fn(DataFrameKind, &'data str, Predictor, usize) -> ParseResult<()>,
32        _check_unit: impl Fn(DataFrameKind, &'data str, super::Unit) -> ParseResult<()>,
33    ) -> ParseResult<()> {
34        for (
35            i,
36            GpsHomeFieldDef {
37                name, predictor, ..
38            },
39        ) in self.0.iter().enumerate()
40        {
41            check_predictor(DataFrameKind::GpsHome, name, *predictor, i)?;
42        }
43
44        Ok(())
45    }
46
47    #[instrument(level = "trace", name = "GpsHomeFrameDef::parse", skip_all)]
48    pub(crate) fn parse(
49        &self,
50        data: &mut Reader,
51        headers: &Headers,
52    ) -> InternalResult<GpsHomeFrame> {
53        let raw = read_field_values(data, &self.0, |f| f.encoding)?;
54        let _ = read_field_values(data, &self.1, |&f| f)?;
55
56        let ctx = PredictorContext::new(headers);
57        let values = raw
58            .iter()
59            .zip(self.0.iter())
60            .map(|(&raw_value, field)| {
61                let value = field.predictor.apply(raw_value, true, None, &ctx);
62
63                tracing::trace!(
64                    field = field.name,
65                    encoding = ?field.encoding,
66                    predictor = ?field.predictor,
67                    raw = raw_value,
68                    value,
69                );
70
71                value.cast_signed()
72            })
73            .collect::<Vec<_>>();
74
75        // `values` can only have two elements thanks to zipping with `self.0`
76        let [latitude, longitude, ..] = values[..] else {
77            unreachable!()
78        };
79
80        Ok(GpsHomeFrame(GpsPosition {
81            latitude,
82            longitude,
83        }))
84    }
85}
86
87#[allow(dead_code)]
88#[derive(Debug, Clone)]
89pub(crate) struct GpsHomeFieldDef<'data> {
90    pub(crate) name: &'data str,
91    pub(crate) predictor: Predictor,
92    pub(crate) encoding: Encoding,
93}
94
95#[derive(Debug, Default)]
96pub(crate) struct GpsHomeFrameDefBuilder<'data> {
97    names: Option<&'data str>,
98    predictors: Option<&'data str>,
99    encodings: Option<&'data str>,
100    signs: Option<&'data str>,
101}
102
103impl<'data> GpsHomeFrameDefBuilder<'data> {
104    pub(crate) fn update(&mut self, property: DataFrameProperty, value: &'data str) {
105        let value = Some(value);
106
107        match property {
108            DataFrameProperty::Name => self.names = value,
109            DataFrameProperty::Predictor => self.predictors = value,
110            DataFrameProperty::Encoding => self.encodings = value,
111            DataFrameProperty::Signed => self.signs = value,
112        }
113    }
114
115    pub(crate) fn parse(self) -> ParseResult<Option<GpsHomeFrameDef<'data>>> {
116        let kind = DataFrameKind::Gps;
117
118        if self.names.is_none()
119            && self.predictors.is_none()
120            && self.encodings.is_none()
121            && self.signs.is_none()
122        {
123            return Ok(None);
124        }
125
126        let mut names = super::parse_names(kind, self.names)?;
127        let mut predictors = super::parse_predictors(kind, self.predictors)?;
128        let mut encodings = super::parse_encodings(kind, self.encodings)?;
129        let mut signs = super::parse_signs(kind, self.signs)?;
130
131        let mut fields =
132            (names.by_ref().zip(signs.by_ref())).zip(predictors.by_ref().zip(encodings.by_ref()));
133
134        let latitude =
135            if let Some(((name @ "GPS_home[0]", true), (predictor, encoding))) = fields.next() {
136                GpsHomeFieldDef {
137                    name,
138                    predictor: predictor?,
139                    encoding: encoding?,
140                }
141            } else {
142                tracing::error!("missing GPS_home[0] field definition");
143                return Err(ParseError::MissingField {
144                    frame: DataFrameKind::GpsHome,
145                    field: "GPS_home[0]".to_owned(),
146                });
147            };
148
149        let longitude =
150            if let Some(((name @ "GPS_home[1]", true), (predictor, encoding))) = fields.next() {
151                GpsHomeFieldDef {
152                    name,
153                    predictor: predictor?,
154                    encoding: encoding?,
155                }
156            } else {
157                tracing::error!("missing GPS_home[1] field definition");
158                return Err(ParseError::MissingField {
159                    frame: DataFrameKind::GpsHome,
160                    field: "GPS_home[1]".to_owned(),
161                });
162            };
163
164        let rest = fields
165            .map(|(_, (_, encoding))| encoding)
166            .collect::<Result<Vec<_>, _>>()?;
167
168        if !rest.is_empty() {
169            tracing::warn!(
170                "expected only GPS_home[0] & GPS_home[1] fields in gps home frames, found {} more",
171                rest.len()
172            );
173        }
174
175        if names.next().is_some()
176            || predictors.next().is_some()
177            || encodings.next().is_some()
178            || signs.next().is_some()
179        {
180            tracing::error!("not all GPS home definition headers are of equal length");
181            return Err(ParseError::MalformedFrameDef(DataFrameKind::GpsHome));
182        }
183
184        Ok(Some(GpsHomeFrameDef([latitude, longitude], rest)))
185    }
186}