1use super::*;
17
18#[derive(Clone, Debug, PartialEq, Serialize)]
20pub struct RmcData {
21 pub source: NavigationSystem,
23
24 #[serde(with = "json_date_time_utc")]
26 pub timestamp: Option<DateTime<Utc>>,
27
28 pub status_active: Option<bool>,
30
31 pub latitude: Option<f64>,
33
34 pub longitude: Option<f64>,
36
37 pub sog_knots: Option<f64>,
39
40 pub bearing: Option<f64>,
42
43 pub variation: Option<f64>,
45}
46
47impl LatLon for RmcData {
48 fn latitude(&self) -> Option<f64> {
49 self.latitude
50 }
51
52 fn longitude(&self) -> Option<f64> {
53 self.longitude
54 }
55}
56
57pub(crate) fn handle(
61 sentence: &str,
62 nav_system: NavigationSystem,
63) -> Result<ParsedMessage, ParseError> {
64 let split: Vec<&str> = sentence.split(',').collect();
65
66 Ok(ParsedMessage::Rmc(RmcData {
67 source: nav_system,
68 timestamp: parse_yymmdd_hhmmss(split.get(9).unwrap_or(&""), split.get(1).unwrap_or(&""))
69 .ok(),
70 status_active: {
71 let s = split.get(2).unwrap_or(&"");
72 match *s {
73 "A" => Some(true),
74 "V" => Some(false),
75 "" => None,
76 _ => {
77 return Err(format!("Invalid RMC navigation receiver status: {}", s).into());
78 }
79 }
80 },
81 latitude: parse_latitude_ddmm_mmm(
82 split.get(3).unwrap_or(&""),
83 split.get(4).unwrap_or(&""),
84 )?,
85 longitude: parse_longitude_dddmm_mmm(
86 split.get(5).unwrap_or(&""),
87 split.get(6).unwrap_or(&""),
88 )?,
89 sog_knots: pick_number_field(&split, 7)?,
90 bearing: pick_number_field(&split, 8)?,
91 variation: {
92 if let Some(val) = pick_number_field::<f64>(&split, 10)? {
93 let side = split.get(11).unwrap_or(&"");
94 match *side {
95 "E" => Some(val),
96 "W" => Some(-val),
97 _ => {
98 return Err(format!("Invalid RMC variation side: {}", side).into());
99 }
100 }
101 } else {
102 None
103 }
104 },
105 }))
106}
107
108#[cfg(test)]
111mod test {
112 use super::*;
113
114 #[test]
115 fn test_parse_cprmc() {
116 let mut p = NmeaParser::new();
118 match p.parse_sentence("$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191120,020.3,E*67")
119 {
120 Ok(ps) => {
121 match ps {
122 ParsedMessage::Rmc(rmc) => {
124 assert_eq!(rmc.status_active, Some(true));
125 assert_eq!(rmc.timestamp, {
126 Utc.with_ymd_and_hms(2020, 11, 19, 22, 54, 46).single()
127 });
128 assert_eq!(rmc.sog_knots.unwrap(), 0.5);
129 assert::close(rmc.bearing.unwrap_or(0.0), 54.7, 0.1);
130 assert_eq!(rmc.variation.unwrap(), 20.3);
131 }
132 ParsedMessage::Incomplete => {
133 assert!(false);
134 }
135 _ => {
136 assert!(false);
137 }
138 }
139 }
140 Err(e) => {
141 assert_eq!(e.to_string(), "OK");
142 }
143 }
144
145 let mut p = NmeaParser::new();
147 match p.parse_sentence("$GPRMC,225446,A,,,,,,,070809,,*23") {
148 Ok(ps) => {
149 match ps {
150 ParsedMessage::Rmc(rmc) => {
152 assert_eq!(rmc.status_active, Some(true));
153 assert_eq!(rmc.timestamp, {
154 Utc.with_ymd_and_hms(2009, 8, 7, 22, 54, 46).single()
155 });
156 assert_eq!(rmc.sog_knots, None);
157 assert_eq!(rmc.bearing, None);
158 assert_eq!(rmc.variation, None);
159 }
160 ParsedMessage::Incomplete => {
161 assert!(false);
162 }
163 _ => {
164 assert!(false);
165 }
166 }
167 }
168 Err(e) => {
169 assert_eq!(e.to_string(), "OK");
170 }
171 }
172 }
173}