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());
}
}
}