use super::*;
#[derive(Clone, Debug, PartialEq)]
pub struct GnsData {
pub source: NavigationSystem,
pub timestamp: Option<DateTime<Utc>>,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub gps_mode: GnsModeIndicator,
pub glonass_mode: GnsModeIndicator,
pub other_modes: Vec<GnsModeIndicator>,
pub satellite_count: Option<u8>,
pub hdop: Option<f64>,
pub altitude: Option<f64>,
pub geoid_separation: Option<f64>,
pub age_of_dgps: Option<f64>,
pub ref_station_id: Option<u16>,
}
impl LatLon for GnsData {
fn latitude(&self) -> Option<f64> {
self.latitude
}
fn longitude(&self) -> Option<f64> {
self.longitude
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum GnsModeIndicator {
Invalid,
Autonomous,
Differential,
Precise,
RealTimeKinematic,
RealTimeKinematicFloat,
DeadReckoning,
ManualInputMode,
SimulationMode,
}
impl GnsModeIndicator {
pub fn new(a: char) -> GnsModeIndicator {
match a {
'N' => GnsModeIndicator::Invalid,
'A' => GnsModeIndicator::Autonomous,
'D' => GnsModeIndicator::Differential,
'P' => GnsModeIndicator::Precise,
'R' => GnsModeIndicator::RealTimeKinematic,
'F' => GnsModeIndicator::RealTimeKinematicFloat,
'E' => GnsModeIndicator::DeadReckoning,
'M' => GnsModeIndicator::ManualInputMode,
'S' => GnsModeIndicator::SimulationMode,
_ => GnsModeIndicator::Invalid,
}
}
}
impl core::fmt::Display for GnsModeIndicator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
GnsModeIndicator::Invalid => write!(f, "invalid"),
GnsModeIndicator::Autonomous => write!(f, "autonomous fix"),
GnsModeIndicator::Differential => write!(f, "differential fix"),
GnsModeIndicator::Precise => write!(f, "precise fix"),
GnsModeIndicator::RealTimeKinematic => write!(f, "Real-Time Kinematic"),
GnsModeIndicator::RealTimeKinematicFloat => {
write!(f, "Real-Time Kinematic (floating point)")
}
GnsModeIndicator::DeadReckoning => write!(f, "dead reckoning"),
GnsModeIndicator::ManualInputMode => write!(f, "manual input mode"),
GnsModeIndicator::SimulationMode => write!(f, "simulation mode"),
}
}
}
pub(crate) fn handle(
sentence: &str,
nav_system: NavigationSystem,
) -> Result<ParsedMessage, ParseError> {
let now: DateTime<Utc> = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).single().unwrap();
let split: Vec<&str> = sentence.split(',').collect();
let modes: Vec<char> = split.get(6).unwrap_or(&"").chars().collect();
Ok(ParsedMessage::Gns(GnsData {
source: nav_system,
timestamp: parse_hhmmss(split.get(1).unwrap_or(&""), now).ok(),
latitude: parse_latitude_ddmm_mmm(
split.get(2).unwrap_or(&""),
split.get(3).unwrap_or(&""),
)?,
longitude: parse_longitude_dddmm_mmm(
split.get(4).unwrap_or(&""),
split.get(5).unwrap_or(&""),
)?,
gps_mode: GnsModeIndicator::new(*modes.first().unwrap_or(&' ')),
glonass_mode: GnsModeIndicator::new(*modes.get(1).unwrap_or(&' ')),
other_modes: modes
.into_iter()
.skip(2)
.map(GnsModeIndicator::new)
.collect(),
satellite_count: pick_number_field(&split, 7)?,
hdop: pick_number_field(&split, 8)?,
altitude: pick_number_field(&split, 9)?,
geoid_separation: pick_number_field(&split, 10)?,
age_of_dgps: pick_number_field(&split, 11)?,
ref_station_id: pick_number_field(&split, 12)?,
}))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_cpgns() {
let mut p = NmeaParser::new();
match p.parse_sentence(
"$GNGNS,090310.00,4806.891632,N,01134.134167,E,AAN,10,1.0,532.4,47.0,,,V*68",
) {
Ok(ps) => {
match ps {
ParsedMessage::Gns(gns) => {
assert_eq!(gns.timestamp, {
Utc.with_ymd_and_hms(2000, 01, 01, 09, 03, 10).single()
});
assert::close(gns.latitude.unwrap_or(0.0), 48.114, 0.001);
assert::close(gns.longitude.unwrap_or(0.0), 11.569, 0.001);
assert_eq!(gns.gps_mode, GnsModeIndicator::Autonomous);
assert_eq!(gns.glonass_mode, GnsModeIndicator::Autonomous);
assert_eq!(gns.other_modes[0], GnsModeIndicator::Invalid);
assert_eq!(gns.satellite_count.unwrap_or(0), 10);
assert::close(gns.hdop.unwrap_or(0.0), 0.9, 0.1);
assert::close(gns.altitude.unwrap_or(0.0), 532.4, 0.1);
assert::close(gns.geoid_separation.unwrap_or(0.0), 47.0, 0.1);
assert_eq!(gns.age_of_dgps, None);
assert_eq!(gns.ref_station_id, None);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
let mut p = NmeaParser::new();
match p.parse_sentence("$GPGNS,123519,,,,,,,,,,,,,*40") {
Ok(ps) => {
match ps {
ParsedMessage::Gns(gns) => {
assert_eq!(gns.timestamp, {
Utc.with_ymd_and_hms(2000, 1, 1, 12, 35, 19).single()
});
assert_eq!(gns.latitude, None);
assert_eq!(gns.longitude, None);
assert_eq!(gns.gps_mode, GnsModeIndicator::Invalid);
assert_eq!(gns.glonass_mode, GnsModeIndicator::Invalid);
assert!(gns.other_modes.is_empty());
assert_eq!(gns.satellite_count, None);
assert_eq!(gns.hdop, None);
assert_eq!(gns.altitude, None);
assert_eq!(gns.geoid_separation, None);
assert_eq!(gns.age_of_dgps, None);
assert_eq!(gns.ref_station_id, None);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
}
}