1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Galmon integration.
//!
//! This module contains a reader for the
//! [Galmon transport protocol](https://github.com/berthubert/galmon#internals).
//! The reader can be used to obtain INAV frames and OSNMA data from the
//! [Galmon](https://github.com/berthubert/galmon) tools, such as `ubxtool`.

pub mod navmon {
    //! Galmon `navmon` protocol buffers definition.
    //!
    //! This module contains a Rust version of the protocol buffers definition
    //! [`navmon.proto`](https://github.com/berthubert/galmon/blob/master/navmon.proto).
    //! The [prost](https://crates.io/crates/prost) crate is used to generate the
    //! code in this module.
    #![allow(missing_docs)]
    #![allow(clippy::derive_partial_eq_without_eq)] // this should be fixed in prost
    include!(concat!(env!("OUT_DIR"), "/navmon_protobuf.rs"));
}

pub mod transport {
    //! Galmon transport protocol.
    use super::navmon::NavMonMessage;
    use bytes::BytesMut;
    use prost::Message;
    use std::io::{ErrorKind, Read, Write};

    /// Reader for the Galmon transport protocol.
    ///
    /// This wraps around a [`Read`] `R` and can be used to read navmon packets
    /// from `R`.
    #[derive(Debug, Clone)]
    pub struct ReadTransport<R> {
        read: R,
        buffer: BytesMut,
    }

    impl<R: Read> ReadTransport<R> {
        /// Constructs a new reader using a [`Read`] `read`.
        pub fn new(read: R) -> ReadTransport<R> {
            let default_cap = 2048;
            let mut buffer = BytesMut::with_capacity(default_cap);
            buffer.resize(default_cap, 0);
            ReadTransport { read, buffer }
        }

        /// Tries to read a navmon packet.
        ///
        /// If the read is successful, a navmon packet is returned. If EOF is reached
        /// after a packet, `None` is returned. For any other kinds of errors, an `Err`
        /// is returned.
        pub fn read_packet(&mut self) -> std::io::Result<Option<NavMonMessage>> {
            // Read 4-byte magic value and 2-byte frame length
            if let Err(e) = self.read.read_exact(&mut self.buffer[..6]) {
                match e.kind() {
                    ErrorKind::UnexpectedEof => return Ok(None),
                    _ => {
                        log::error!("could not read packet header: {}", e);
                        return Err(e);
                    }
                }
            }
            if &self.buffer[..4] != b"bert" {
                let err = "incorrect galmon magic value";
                log::error!("{}", err);
                return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, err));
            }
            let size = usize::from(u16::from_be_bytes(self.buffer[4..6].try_into().unwrap()));
            if size > self.buffer.len() {
                log::debug!("resize buffer to {}", size);
                self.buffer.resize(size, 0);
            }
            // Read protobuf frame
            if let Err(e) = self.read.read_exact(&mut self.buffer[..size]) {
                log::error!("could not read protobuf frame: {}", e);
                return Err(e);
            }
            let frame = match NavMonMessage::decode(&self.buffer[..size]) {
                Ok(f) => {
                    log::trace!("decoded protobuf frame: {:?}", f);
                    f
                }
                Err(e) => {
                    log::error!("could not decode protobuf frame: {}", e);
                    return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e));
                }
            };
            Ok(Some(frame))
        }
    }

    /// Writer for the Galmon transport protocol.
    ///
    /// This wraps around a [`Write`] `W` and can be used to write navmon packets
    /// to `W`.
    #[derive(Debug, Clone)]
    pub struct WriteTransport<W> {
        write: W,
        buffer: BytesMut,
    }

    impl<W: Write> WriteTransport<W> {
        /// Constructs a new writer using a [`Write`] `write`.
        pub fn new(write: W) -> WriteTransport<W> {
            let default_cap = 2048;
            let mut buffer = BytesMut::with_capacity(default_cap);
            buffer.reserve(default_cap);
            WriteTransport { write, buffer }
        }

        /// Tries to write a navmon packet.
        ///
        /// If the write is successful, the number of bytes writte is returned.
        pub fn write_packet(&mut self, packet: &NavMonMessage) -> std::io::Result<usize> {
            let size = packet.encoded_len();
            // Header is 6 bytes
            let total_size = size + 6;
            let cap = self.buffer.capacity();
            if total_size > cap {
                log::debug!("resize buffer to {}", total_size);
                self.buffer.reserve(total_size - cap);
            }
            self.buffer.clear();
            self.buffer.extend_from_slice(b"bert");
            let size_u16 = u16::try_from(size).unwrap();
            self.buffer.extend_from_slice(&size_u16.to_be_bytes());
            match packet.encode(&mut self.buffer) {
                Ok(()) => log::trace!("encoded protobuf frame: {:?}", packet),
                Err(e) => {
                    log::error!("could not encoded protobuf frame: {}", e);
                    return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e));
                }
            };
            match self.write.write_all(&self.buffer) {
                Ok(()) => Ok(self.buffer.len()),
                Err(e) => {
                    log::error!("could not write: {}", e);
                    Err(e)
                }
            }
        }
    }

    #[cfg(test)]
    mod test {
        use super::*;
        mod data;

        #[test]
        fn read_packets() {
            let packets = &data::GALMON_PACKETS[..];
            let mut transport = ReadTransport::new(packets);
            // There should be 17 packets in the test data
            for _ in 0..17 {
                transport.read_packet().unwrap();
            }
        }

        #[test]
        fn bad_magic() {
            let packets = &data::GALMON_PACKETS[2..];
            let mut transport = ReadTransport::new(packets);
            assert!(transport.read_packet().is_err());
        }

        #[test]
        fn short_packet() {
            let packets = &data::GALMON_PACKETS[..10];
            let mut transport = ReadTransport::new(packets);
            assert!(transport.read_packet().is_err());
        }

        #[test]
        fn read_packets_write_packets() {
            let buffer = Vec::new();
            let packets = &data::GALMON_PACKETS[..];
            let mut read = ReadTransport::new(packets);
            let mut write = WriteTransport::new(buffer);
            let mut total_size = 0;
            // There should be 17 packets in the test data
            for _ in 0..17 {
                let packet = read.read_packet().unwrap().unwrap();
                total_size += write.write_packet(&packet).unwrap();
            }
            assert_eq!(&write.write, packets);
            assert_eq!(total_size, packets.len());
        }
    }
}