use crate::nmea::field::{FieldReader, FieldWriter, NmeaEncodable};
#[derive(Debug, Clone, PartialEq)]
pub struct Gsa {
pub mode: Option<char>,
pub fix_type: Option<u8>,
pub prns: [Option<u8>; 12],
pub pdop: Option<f32>,
pub hdop: Option<f32>,
pub vdop: Option<f32>,
}
impl Gsa {
pub fn parse(fields: &[&str]) -> Option<Self> {
let mut r = FieldReader::new(fields);
let mode = r.char();
let fix_type = r.u8();
let prns = [
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
r.u8(),
];
let pdop = r.f32();
let hdop = r.f32();
let vdop = r.f32();
Some(Self {
mode,
fix_type,
prns,
pdop,
hdop,
vdop,
})
}
}
impl NmeaEncodable for Gsa {
const SENTENCE_TYPE: &str = "GSA";
fn encode(&self) -> Vec<String> {
let mut w = FieldWriter::new();
w.char(self.mode);
w.u8(self.fix_type);
for prn in &self.prns {
w.u8(*prn);
}
w.f32(self.pdop);
w.f32(self.hdop);
w.f32(self.vdop);
w.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse_frame;
#[test]
fn gsa_empty() {
let f = Gsa {
mode: None,
fix_type: None,
prns: [None; 12],
pdop: None,
hdop: None,
vdop: None,
}
.to_sentence("GP");
let frame = parse_frame(f.trim()).expect("valid");
let g = Gsa::parse(&frame.fields).expect("parse");
assert!(g.mode.is_none());
assert!(g.fix_type.is_none());
assert!(g.prns.iter().all(|p| p.is_none()));
assert!(g.pdop.is_none());
}
#[test]
fn gsa_encode_roundtrip() {
let original = Gsa {
mode: Some('A'),
fix_type: Some(3),
prns: [
Some(4),
Some(5),
None,
Some(9),
Some(12),
None,
None,
Some(24),
None,
None,
None,
None,
],
pdop: Some(2.5),
hdop: Some(1.3),
vdop: Some(2.1),
};
let sentence = original.to_sentence("GP");
let frame = parse_frame(sentence.trim()).expect("re-parse");
let parsed = Gsa::parse(&frame.fields).expect("re-parse GSA");
assert_eq!(original, parsed);
}
#[test]
fn gsa_signalk() {
let frame = parse_frame("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39")
.expect("valid signalk GSA frame");
let gsa = Gsa::parse(&frame.fields).expect("parse GSA");
assert_eq!(gsa.mode, Some('A'));
assert_eq!(gsa.fix_type, Some(3));
assert_eq!(gsa.prns[0], Some(4));
assert_eq!(gsa.prns[1], Some(5));
assert!(gsa.prns[2].is_none());
assert_eq!(gsa.prns[3], Some(9));
assert_eq!(gsa.prns[4], Some(12));
assert_eq!(gsa.prns[7], Some(24));
assert!((gsa.pdop.expect("pdop") - 2.5).abs() < 0.01);
assert!((gsa.hdop.expect("hdop") - 1.3).abs() < 0.01);
assert!((gsa.vdop.expect("vdop") - 2.1).abs() < 0.01);
}
#[test]
fn gsa_system_id_gonmea() {
let frame = parse_frame("$GNGSA,A,3,13,12,22,19,08,21,,,,,,,1.05,0.64,0.83,4*0B")
.expect("valid go-nmea GSA frame");
let gsa = Gsa::parse(&frame.fields).expect("parse GSA");
assert_eq!(gsa.mode, Some('A'));
assert_eq!(gsa.fix_type, Some(3));
assert_eq!(gsa.prns[0], Some(13));
assert_eq!(gsa.prns[1], Some(12));
assert_eq!(gsa.prns[2], Some(22));
assert_eq!(gsa.prns[3], Some(19));
assert_eq!(gsa.prns[4], Some(8));
assert_eq!(gsa.prns[5], Some(21));
assert!(gsa.prns[6].is_none());
assert!((gsa.pdop.expect("pdop") - 1.05).abs() < 0.01);
assert!((gsa.hdop.expect("hdop") - 0.64).abs() < 0.01);
assert!((gsa.vdop.expect("vdop") - 0.83).abs() < 0.01);
}
}