Skip to main content

sbp/
sbp_iter_ext.rs

1//! Extra adaptors for iterators of [Sbp] messages.
2
3use crate::{DeserializeError, Sbp};
4
5/// An [Iterator] blanket implementation that provides extra adaptors for iterators of [Sbp] messages.
6pub trait SbpIterExt: Iterator {
7    /// Lift an `Iterator<Item = Result<Sbp>>` into an `Iterator<Item = Sbp>` by ignoring all the errors.
8    /// The iterator will terminate if an io error is encountered.
9    fn ignore_errors<'a>(self) -> HandleErrorsIter<'a, Self, DeserializeError>
10    where
11        Self: Iterator<Item = Result<Sbp, DeserializeError>> + Sized,
12    {
13        HandleErrorsIter::new(self, |e| match e {
14            DeserializeError::IoError(_) => ControlFlow::Break,
15            _ => ControlFlow::Continue,
16        })
17    }
18
19    /// Lift an `Iterator<Item = Result<Sbp>>` into an `Iterator<Item = Sbp>` by logging all the errors.
20    /// The iterator will terminate if an io error is encountered.
21    fn log_errors<'a>(self, level: log::Level) -> HandleErrorsIter<'a, Self, DeserializeError>
22    where
23        Self: Iterator<Item = Result<Sbp, DeserializeError>> + Sized,
24    {
25        HandleErrorsIter::new(self, move |e| {
26            log::log!(level, "{e}");
27            match e {
28                DeserializeError::IoError(_) => ControlFlow::Break,
29                _ => ControlFlow::Continue,
30            }
31        })
32    }
33
34    /// Lift an `Iterator<Item = Result<Sbp>>` into an `Iterator<Item = Sbp>` with a custom error handler.
35    /// You can use (ControlFlow)[self::ControlFlow] to determine if the iterator should continue or break on error.
36    fn handle_errors<'a, E, F>(self, on_err: F) -> HandleErrorsIter<'a, Self, E>
37    where
38        Self: Iterator<Item = Result<Sbp, E>> + Sized,
39        F: FnMut(&E) -> ControlFlow + 'a,
40    {
41        HandleErrorsIter::new(self, on_err)
42    }
43
44    /// Return an iterable that also includes [GpsTime]s. This method calls [SbpMessage::gps_time] on each message.
45    /// If the message has a complete GpsTime it is returned. If the message only has a TOW, this itertor will use the
46    /// last week number it has seen, or return `None` if it hasn't seen any.
47    #[cfg(feature = "swiftnav")]
48    fn with_rover_time<T>(self) -> swiftnav_impl::RoverTimeIter<Self>
49    where
50        T: swiftnav_impl::HasTime,
51        Self: Iterator<Item = T> + Sized,
52    {
53        swiftnav_impl::RoverTimeIter::new(self)
54    }
55}
56
57impl<I> SbpIterExt for I where I: Iterator + Sized {}
58
59// A less general https://doc.rust-lang.org/std/ops/enum.ControlFlow.html
60// could be replaced by that once it's stable
61/// Used to tell [HandleErrorsIter] whether it should exit early or go on as usual.
62pub enum ControlFlow {
63    Continue,
64    Break,
65}
66
67/// See [SbpIterExt::handle_errors] for more information.
68pub struct HandleErrorsIter<'a, I, E>
69where
70    I: Iterator,
71{
72    messages: I,
73    on_err: Box<dyn FnMut(&E) -> ControlFlow + 'a>,
74    err: Result<(), E>,
75}
76
77impl<'a, I, E> HandleErrorsIter<'a, I, E>
78where
79    I: Iterator<Item = Result<Sbp, E>>,
80{
81    fn new<F>(messages: I, on_err: F) -> HandleErrorsIter<'a, I, E>
82    where
83        F: FnMut(&E) -> ControlFlow + 'a,
84    {
85        Self {
86            messages,
87            on_err: Box::new(on_err),
88            err: Ok(()),
89        }
90    }
91
92    pub fn take_err(&mut self) -> Result<(), E> {
93        std::mem::replace(&mut self.err, Ok(()))
94    }
95}
96
97impl<I, E> Iterator for HandleErrorsIter<'_, I, E>
98where
99    I: Iterator<Item = Result<Sbp, E>>,
100{
101    type Item = Sbp;
102
103    fn next(&mut self) -> Option<Self::Item> {
104        match self.messages.next()? {
105            Ok(msg) => Some(msg),
106            Err(e) => {
107                let flow = (self.on_err)(&e);
108                self.err = Err(e);
109                match flow {
110                    ControlFlow::Continue => self.next(),
111                    ControlFlow::Break => None,
112                }
113            }
114        }
115    }
116}
117
118#[cfg(feature = "swiftnav")]
119mod swiftnav_impl {
120    use swiftnav::time::GpsTime;
121
122    use crate::{
123        Frame, Sbp,
124        messages::SbpMessage,
125        time::{GpsTimeError, MessageTime, RoverTime},
126    };
127
128    /// See [SbpIterExt::with_rover_time] for more information.
129    pub struct RoverTimeIter<I: Iterator> {
130        messages: I,
131        clock: Option<GpsTime>,
132    }
133
134    impl<I> RoverTimeIter<I>
135    where
136        I: Iterator,
137    {
138        pub fn new(messages: I) -> RoverTimeIter<I> {
139            Self {
140                messages,
141                clock: None,
142            }
143        }
144
145        fn update(&mut self, time: MessageTime) -> Option<GpsTime> {
146            match time {
147                MessageTime::Rover(time) => match time {
148                    RoverTime::GpsTime(time) => {
149                        self.clock = Some(time);
150                        Some(time)
151                    }
152                    RoverTime::Tow(tow) => {
153                        if let Some(clock) = self.clock {
154                            // tow came from a `GpsTime` so it must be valid
155                            let time = GpsTime::new(clock.wn(), tow).unwrap();
156                            self.clock = Some(time);
157                            Some(time)
158                        } else {
159                            // no previous time to use wn from
160                            None
161                        }
162                    }
163                },
164                _ => None,
165            }
166        }
167    }
168
169    impl<I> Iterator for RoverTimeIter<I>
170    where
171        I: Iterator,
172        I::Item: HasTime,
173    {
174        type Item = (I::Item, Option<Result<GpsTime, GpsTimeError>>);
175
176        fn next(&mut self) -> Option<Self::Item> {
177            let msg = self.messages.next()?;
178            match msg.time() {
179                Some(Ok(time)) => match self.update(time) {
180                    Some(gps_time) => Some((msg, Some(Ok(gps_time)))),
181                    None => Some((msg, None)),
182                },
183                Some(Err(e)) => Some((msg, Some(Err(e)))),
184                None => Some((msg, None)),
185            }
186        }
187    }
188
189    pub trait HasTime {
190        fn time(&self) -> Option<Result<MessageTime, GpsTimeError>>;
191    }
192
193    impl HasTime for Sbp {
194        fn time(&self) -> Option<Result<MessageTime, GpsTimeError>> {
195            self.gps_time()
196        }
197    }
198
199    impl<E> HasTime for Result<Sbp, E> {
200        fn time(&self) -> Option<Result<MessageTime, GpsTimeError>> {
201            match self {
202                Ok(m) => m.gps_time(),
203                Err(_) => None,
204            }
205        }
206    }
207
208    impl HasTime for Frame {
209        fn time(&self) -> Option<Result<MessageTime, GpsTimeError>> {
210            self.to_sbp().time()
211        }
212    }
213
214    impl<E> HasTime for Result<Frame, E> {
215        fn time(&self) -> Option<Result<MessageTime, GpsTimeError>> {
216            match self {
217                Ok(m) => m.time(),
218                Err(_) => None,
219            }
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use std::io::Cursor;
227
228    use crate::iter_messages;
229
230    use super::*;
231
232    #[cfg(feature = "swiftnav")]
233    #[test]
234    fn test_rover_time_wn() {
235        #[rustfmt::skip]
236        let data = Cursor::new(vec![
237            // MsgGPSTimeDepA TOW: 2567800 WN: 1787
238            85, 0, 1, 246, 215, 11, 251, 6, 120, 46, 39, 0, 0, 0, 0, 0, 0, 133, 36,
239            // MsgHeartbeat
240            85, 255, 255, 246, 215, 4, 0, 50, 0, 0, 249, 216,
241            // MsgPosECEFDepA TOW: 2567900
242            85, 0, 2, 246, 215, 32, 220, 46, 39, 0, 216, 41, 227, 254, 33, 154, 68, 193, 9, 151,
243            154, 124, 231, 95, 80, 193, 1, 183, 214, 139, 255, 105, 77, 65, 0, 0, 9, 0, 7, 98,
244            // MsgGPSTimeDepA TOW: 2568000 WN: 1787
245            85, 0, 1, 246, 215, 11, 251, 6, 64, 47, 39, 0, 0, 0, 0, 0, 0, 171, 190,
246        ]);
247
248        let mut with_time = iter_messages(data).ignore_errors().with_rover_time();
249
250        let gps_time = with_time.next().and_then(|(_, t)| t).unwrap().unwrap();
251        assert_eq!(gps_time.wn(), 1787);
252        assert!((gps_time.tow() - 2567.8).abs() < f64::EPSILON);
253
254        assert!(with_time.next().and_then(|(_, t)| t).is_none());
255
256        let gps_time = with_time.next().and_then(|(_, t)| t).unwrap().unwrap();
257        assert_eq!(gps_time.wn(), 1787);
258        assert!((gps_time.tow() - 2567.9).abs() < f64::EPSILON);
259
260        let gps_time = with_time.next().and_then(|(_, t)| t).unwrap().unwrap();
261        assert_eq!(gps_time.wn(), 1787);
262        assert!((gps_time.tow() - 2568.).abs() < f64::EPSILON);
263    }
264
265    #[cfg(feature = "swiftnav")]
266    #[test]
267    fn test_rover_time_result() {
268        let data = Cursor::new(vec![
269            // MsgGPSTimeDepA TOW: 2567800 WN: 1787
270            85, 0, 1, 246, 215, 11, 251, 6, 120, 46, 39, 0, 0, 0, 0, 0, 0, 133, 36,
271        ]);
272
273        let mut with_time = iter_messages(data).with_rover_time();
274
275        let msg = with_time.next().unwrap();
276        assert!(msg.0.is_ok());
277
278        let gps_time = msg.1.unwrap().unwrap();
279        assert_eq!(gps_time.wn(), 1787);
280        assert!((gps_time.tow() - 2567.8).abs() < f64::EPSILON);
281    }
282
283    #[test]
284    fn test_handle_errors() {
285        #[rustfmt::skip]
286        let data = Cursor::new(vec![
287            85, 0, 1, 246, 215, 11, 251, 6, 120, 46, 39, 0, 0, 0, 0, 0, 0, 133, 36,
288            85, 0, 1, 246, 215, 11, 251, 6, 220, 46, 39, 0, 0, 0, 0, 0, 0, 36, 159, // crc error
289            85, 0, 1, 246, 215, 11, 251, 6, 220, 46, 39, 0, 0, 0, 0, 0, 0, 36, 160,
290        ]);
291
292        let mut errors: usize = 0;
293        let messages = iter_messages(data.clone())
294            .handle_errors(|_| {
295                errors += 1;
296                ControlFlow::Continue
297            })
298            .count();
299        assert_eq!(messages, 2);
300        assert_eq!(errors, 1);
301
302        let messages = iter_messages(data).ignore_errors().count();
303        assert_eq!(messages, 2);
304    }
305
306    #[test]
307    fn test_stop_on_errors() {
308        #[rustfmt::skip]
309        let data = Cursor::new(vec![
310            85, 0, 1, 246, 215, 11, 251, 6, 120, 46, 39, 0, 0, 0, 0, 0, 0, 133, 36,
311            85, 0, 1, 246, 215, 11, 251, 6, 120, 46, 39, 0, 0, 0, 0, 0, 0, 133, 36,
312            85, 0, 1, 246, 215, 11, 251, 6, 220, 46, 39, 0, 0, 0, 0, 0, 0, 36, 159, // wrong
313            85, 0, 1, 246, 215, 11, 251, 6, 220, 46, 39, 0, 0, 0, 0, 0, 0, 36, 160,
314        ]);
315
316        let mut err_count: usize = 0;
317        let mut msg_count: usize = 0;
318
319        let mut messages = iter_messages(data).handle_errors(|_| {
320            err_count += 1;
321            ControlFlow::Break
322        });
323
324        for _ in &mut messages {
325            msg_count += 1;
326        }
327        assert!(matches!(
328            messages.take_err(),
329            Err(DeserializeError::CrcError { .. })
330        ));
331        assert_eq!(msg_count, 2);
332
333        for _ in &mut messages {
334            msg_count += 1;
335        }
336        assert!(messages.take_err().is_ok());
337        assert_eq!(msg_count, 3);
338
339        assert_eq!(messages.count(), 0);
340        assert_eq!(err_count, 1);
341    }
342}