pub struct FieldReader<'a> {
fields: &'a [&'a str],
idx: usize,
}
impl<'a> FieldReader<'a> {
pub fn new(fields: &'a [&'a str]) -> Self {
Self { fields, idx: 0 }
}
pub fn f32(&mut self) -> Option<f32> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
f.parse::<f32>().ok()
}
});
self.idx += 1;
val
}
pub fn f64(&mut self) -> Option<f64> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
f.parse::<f64>().ok()
}
});
self.idx += 1;
val
}
pub fn u8(&mut self) -> Option<u8> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
f.parse::<u8>().ok()
}
});
self.idx += 1;
val
}
pub fn u32(&mut self) -> Option<u32> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
f.parse::<u32>().ok()
}
});
self.idx += 1;
val
}
pub fn i8(&mut self) -> Option<i8> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
f.parse::<i8>().ok()
}
});
self.idx += 1;
val
}
pub fn char(&mut self) -> Option<char> {
let val = self
.fields
.get(self.idx)
.and_then(|f| f.chars().next().filter(|_| !f.is_empty()));
self.idx += 1;
val
}
pub fn string(&mut self) -> Option<String> {
let val = self.fields.get(self.idx).and_then(|f| {
if f.is_empty() {
None
} else {
Some((*f).to_string())
}
});
self.idx += 1;
val
}
pub fn skip(&mut self) {
self.idx += 1;
}
}
pub struct FieldWriter {
fields: Vec<String>,
}
impl FieldWriter {
pub fn new() -> Self {
Self { fields: Vec::new() }
}
pub fn f32(&mut self, value: Option<f32>) {
self.fields.push(match value {
Some(v) => format!("{v}"),
None => String::new(),
});
}
pub fn f64(&mut self, value: Option<f64>) {
self.fields.push(match value {
Some(v) => format!("{v}"),
None => String::new(),
});
}
pub fn u8(&mut self, value: Option<u8>) {
self.fields.push(match value {
Some(v) => v.to_string(),
None => String::new(),
});
}
pub fn i8(&mut self, value: Option<i8>) {
self.fields.push(match value {
Some(v) => v.to_string(),
None => String::new(),
});
}
pub fn u32(&mut self, value: Option<u32>) {
self.fields.push(match value {
Some(v) => v.to_string(),
None => String::new(),
});
}
pub fn char(&mut self, value: Option<char>) {
self.fields.push(match value {
Some(c) => c.to_string(),
None => String::new(),
});
}
pub fn fixed(&mut self, c: char) {
self.fields.push(c.to_string());
}
pub fn string(&mut self, value: Option<&str>) {
self.fields.push(value.unwrap_or("").to_string());
}
pub fn finish(self) -> Vec<String> {
self.fields
}
}
impl Default for FieldWriter {
fn default() -> Self {
Self::new()
}
}
pub trait NmeaEncodable {
const SENTENCE_TYPE: &str;
fn encode(&self) -> Vec<String>;
fn to_sentence(&self, talker: &str) -> String {
let fields = self.encode();
let field_refs: Vec<&str> = fields.iter().map(|s| s.as_str()).collect();
crate::encode_frame('$', talker, Self::SENTENCE_TYPE, &field_refs)
}
}
pub fn ddmm_to_decimal(ddmm: f64) -> f64 {
let degrees = (ddmm / 100.0).floor();
let minutes = ddmm - degrees * 100.0;
degrees + minutes / 60.0
}
pub fn decimal_to_ddmm(decimal: f64) -> f64 {
let degrees = decimal.floor();
let minutes = (decimal - degrees) * 60.0;
degrees * 100.0 + minutes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reader_char() {
let fields = &["T", "", "AB"];
let mut r = FieldReader::new(fields);
assert_eq!(r.char(), Some('T'));
assert_eq!(r.char(), None);
assert_eq!(r.char(), Some('A')); }
#[test]
fn reader_f32() {
let fields = &["270.0", "", "abc"];
let mut r = FieldReader::new(fields);
assert_eq!(r.f32(), Some(270.0));
assert_eq!(r.f32(), None);
assert_eq!(r.f32(), None); }
#[test]
fn reader_past_end() {
let fields: &[&str] = &[];
let mut r = FieldReader::new(fields);
assert_eq!(r.f32(), None);
assert_eq!(r.char(), None);
}
#[test]
fn reader_skip() {
let fields = &["10.0", "T", "20.0"];
let mut r = FieldReader::new(fields);
assert_eq!(r.f32(), Some(10.0));
r.skip();
assert_eq!(r.f32(), Some(20.0));
}
#[test]
fn reader_string() {
let fields = &["DEST", ""];
let mut r = FieldReader::new(fields);
assert_eq!(r.string(), Some("DEST".to_string()));
assert_eq!(r.string(), None);
}
#[test]
fn writer_roundtrip() {
let mut w = FieldWriter::new();
w.f32(Some(270.0));
w.fixed('T');
w.f32(None);
w.fixed('M');
let fields = w.finish();
assert_eq!(fields, vec!["270", "T", "", "M"]);
}
#[test]
fn ddmm_to_decimal_lat() {
let result = ddmm_to_decimal(4807.038);
assert!((result - 48.1173).abs() < 0.0001);
}
#[test]
fn ddmm_to_decimal_lon() {
let result = ddmm_to_decimal(1131.0);
assert!((result - 11.5167).abs() < 0.0001);
}
#[test]
fn decimal_to_ddmm_lat() {
let result = decimal_to_ddmm(48.1173);
assert!((result - 4807.038).abs() < 0.001);
}
#[test]
fn decimal_to_ddmm_roundtrip() {
let original = 5132.5200_f64;
let roundtrip = decimal_to_ddmm(ddmm_to_decimal(original));
assert!((roundtrip - original).abs() < 0.0001);
}
}