use super::*;
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct GgaData {
pub source: NavigationSystem,
#[serde(with = "json_date_time_utc")]
pub timestamp: Option<DateTime<Utc>>,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub quality: GgaQualityIndicator,
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 GgaData {
fn latitude(&self) -> Option<f64> {
self.latitude
}
fn longitude(&self) -> Option<f64> {
self.longitude
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub enum GgaQualityIndicator {
Invalid, GpsFix, DGpsFix, PpsFix, RealTimeKinematic, RealTimeKinematicFloat, DeadReckoning, ManualInputMode, SimulationMode, }
impl GgaQualityIndicator {
pub fn new(a: u8) -> GgaQualityIndicator {
match a {
0 => GgaQualityIndicator::Invalid,
1 => GgaQualityIndicator::GpsFix,
2 => GgaQualityIndicator::DGpsFix,
3 => GgaQualityIndicator::PpsFix,
4 => GgaQualityIndicator::RealTimeKinematic,
5 => GgaQualityIndicator::RealTimeKinematicFloat,
6 => GgaQualityIndicator::DeadReckoning,
7 => GgaQualityIndicator::ManualInputMode,
8 => GgaQualityIndicator::SimulationMode,
_ => GgaQualityIndicator::Invalid,
}
}
}
impl core::fmt::Display for GgaQualityIndicator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
GgaQualityIndicator::Invalid => write!(f, "invalid"),
GgaQualityIndicator::GpsFix => write!(f, "GPS fix"),
GgaQualityIndicator::DGpsFix => write!(f, "DGPS fix"),
GgaQualityIndicator::PpsFix => write!(f, "PPS fix"),
GgaQualityIndicator::RealTimeKinematic => write!(f, "Real-Time Kinematic"),
GgaQualityIndicator::RealTimeKinematicFloat => {
write!(f, "Real-Time Kinematic (floating point)")
}
GgaQualityIndicator::DeadReckoning => write!(f, "dead reckoning"),
GgaQualityIndicator::ManualInputMode => write!(f, "manual input mode"),
GgaQualityIndicator::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();
Ok(ParsedMessage::Gga(GgaData {
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(&""),
)?,
quality: GgaQualityIndicator::new(pick_number_field(&split, 6)?.unwrap_or(0)),
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, 11)?,
age_of_dgps: pick_number_field(&split, 13)?,
ref_station_id: pick_number_field(&split, 14)?,
}))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_cpgga() {
let mut p = NmeaParser::new();
match p.parse_sentence("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47")
{
Ok(ps) => {
match ps {
ParsedMessage::Gga(gga) => {
assert_eq!(gga.timestamp, {
Utc.with_ymd_and_hms(2000, 01, 01, 12, 35, 19).single()
});
assert::close(gga.latitude.unwrap_or(0.0), 48.117, 0.001);
assert::close(gga.longitude.unwrap_or(0.0), 11.517, 0.001);
assert_eq!(gga.quality, GgaQualityIndicator::GpsFix);
assert_eq!(gga.satellite_count.unwrap_or(0), 8);
assert::close(gga.hdop.unwrap_or(0.0), 0.9, 0.1);
assert::close(gga.altitude.unwrap_or(0.0), 545.4, 0.1);
assert::close(gga.geoid_separation.unwrap_or(0.0), 46.9, 0.1);
assert_eq!(gga.age_of_dgps, None);
assert_eq!(gga.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("$GPGGA,123519,4807.0,S,01131.0,W,1,08,0.9,545.4,M,46.9,M,,") {
Ok(ps) => {
match ps {
ParsedMessage::Gga(gga) => {
assert_eq!(
(gga.latitude.unwrap_or(0.0) * 1000.0).round() as i32,
-48117
);
assert_eq!(
(gga.longitude.unwrap_or(0.0) * 1000.0).round() as i32,
-11517
);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
let mut p = NmeaParser::new();
match p.parse_sentence("$GPGGA,123519,,,,,,,,,,,,,*5B") {
Ok(ps) => {
match ps {
ParsedMessage::Gga(gga) => {
assert_eq!(gga.timestamp, {
Utc.with_ymd_and_hms(2000, 01, 01, 12, 35, 19).single()
});
assert_eq!(gga.latitude, None);
assert_eq!(gga.longitude, None);
assert_eq!(gga.quality, GgaQualityIndicator::Invalid);
assert_eq!(gga.satellite_count, None);
assert_eq!(gga.hdop, None);
assert_eq!(gga.altitude, None);
assert_eq!(gga.geoid_separation, None);
assert_eq!(gga.age_of_dgps, None);
assert_eq!(gga.ref_station_id, None);
}
ParsedMessage::Incomplete => {
assert!(false);
}
_ => {
assert!(false);
}
}
}
Err(e) => {
assert_eq!(e.to_string(), "OK");
}
}
}
}