1use crate::message::ParsedSentence;
46use crate::types::{MessageType, TalkerId};
47
48#[derive(Debug, Clone)]
50pub struct RmcData {
51 pub talker_id: TalkerId,
52 time_data: [u8; 16],
53 time_len: u8,
54 pub status: char,
55 pub latitude: f64,
56 pub lat_direction: char,
57 pub longitude: f64,
58 pub lon_direction: char,
59 pub speed_knots: f32,
60 pub track_angle: f32,
61 date_data: [u8; 8],
62 date_len: u8,
63 pub magnetic_variation: Option<f32>,
64 pub mag_var_direction: Option<char>,
65}
66
67impl RmcData {
68 pub fn time(&self) -> &str {
70 core::str::from_utf8(&self.time_data[..self.time_len as usize]).unwrap_or("")
71 }
72
73 pub fn date(&self) -> &str {
75 core::str::from_utf8(&self.date_data[..self.date_len as usize]).unwrap_or("")
76 }
77}
78
79impl ParsedSentence {
80 pub fn as_rmc(&self) -> Option<RmcData> {
126 if self.message_type != MessageType::RMC {
127 return None;
128 }
129
130 let time_str = self.get_field_str(1)?;
132 let status = self.parse_field_char(2)?;
133 let latitude: f64 = self.parse_field(3)?;
134 let lat_direction = self.parse_field_char(4)?;
135 let longitude: f64 = self.parse_field(5)?;
136 let lon_direction = self.parse_field_char(6)?;
137 let speed_knots: f32 = self.parse_field(7)?;
138 let track_angle: f32 = self.parse_field(8)?;
139 let date_str = self.get_field_str(9)?;
140
141 let mut time_data = [0u8; 16];
143 let time_bytes = time_str.as_bytes();
144 let time_len = time_bytes.len().min(16) as u8;
145 time_data[..time_len as usize].copy_from_slice(&time_bytes[..time_len as usize]);
146
147 let mut date_data = [0u8; 8];
149 let date_bytes = date_str.as_bytes();
150 let date_len = date_bytes.len().min(8) as u8;
151 date_data[..date_len as usize].copy_from_slice(&date_bytes[..date_len as usize]);
152
153 Some(RmcData {
154 talker_id: self.talker_id,
155 time_data,
156 time_len,
157 status,
158 latitude,
159 lat_direction,
160 longitude,
161 lon_direction,
162 speed_knots,
163 track_angle,
164 date_data,
165 date_len,
166 magnetic_variation: self.parse_field(10),
167 mag_var_direction: self.parse_field_char(11),
168 })
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use crate::NmeaParser;
175
176 #[test]
177 fn test_rmc_complete_message() {
178 let parser = NmeaParser::new();
179 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
180
181 let result = parser.parse_sentence_complete(sentence);
182
183 assert!(result.is_some());
184 let msg = result.unwrap();
185 let rmc = msg.as_rmc();
186 assert!(rmc.is_some());
187
188 let rmc_data = rmc.unwrap();
189 assert_eq!(rmc_data.time(), "123519");
190 assert_eq!(rmc_data.status, 'A');
191 assert_eq!(rmc_data.latitude, 4807.038);
192 assert_eq!(rmc_data.lat_direction, 'N');
193 assert_eq!(rmc_data.longitude, 1131.000);
194 assert_eq!(rmc_data.lon_direction, 'E');
195 assert_eq!(rmc_data.speed_knots, 22.4);
196 assert_eq!(rmc_data.track_angle, 84.4);
197 assert_eq!(rmc_data.date(), "230394");
198 assert_eq!(rmc_data.magnetic_variation, Some(3.1));
199 assert_eq!(rmc_data.mag_var_direction, Some('W'));
200 }
201
202 #[test]
203 fn test_rmc_void_status() {
204 let parser = NmeaParser::new();
205 let sentence = b"$GPRMC,123519,V,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
206
207 let result = parser.parse_sentence_complete(sentence);
208
209 assert!(result.is_some());
210 let msg = result.unwrap();
211 let rmc = msg.as_rmc();
212 assert!(rmc.is_some());
213
214 let rmc_data = rmc.unwrap();
215 assert_eq!(rmc_data.status, 'V');
216 }
217
218 #[test]
219 fn test_rmc_without_magnetic_variation() {
220 let parser = NmeaParser::new();
221 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,,*6A\r\n";
222
223 let result = parser.parse_sentence_complete(sentence);
224
225 assert!(result.is_some());
226 let msg = result.unwrap();
227 let rmc = msg.as_rmc();
228 assert!(rmc.is_some());
229
230 let rmc_data = rmc.unwrap();
231 assert_eq!(rmc_data.magnetic_variation, None);
232 assert_eq!(rmc_data.mag_var_direction, None);
233 }
234
235 #[test]
236 fn test_rmc_missing_time() {
237 let parser = NmeaParser::new();
238 let sentence = b"$GPRMC,,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
239
240 let result = parser.parse_sentence_complete(sentence);
241
242 assert!(result.is_none());
244 }
245
246 #[test]
247 fn test_rmc_missing_status() {
248 let parser = NmeaParser::new();
249 let sentence = b"$GPRMC,123519,,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
250
251 let result = parser.parse_sentence_complete(sentence);
252
253 assert!(result.is_none());
255 }
256
257 #[test]
258 fn test_rmc_missing_date() {
259 let parser = NmeaParser::new();
260 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,,003.1,W*6A\r\n";
261
262 let result = parser.parse_sentence_complete(sentence);
263
264 assert!(result.is_none());
266 }
267
268 #[test]
269 fn test_rmc_missing_speed() {
270 let parser = NmeaParser::new();
271 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,,084.4,230394,003.1,W*6A\r\n";
272
273 let result = parser.parse_sentence_complete(sentence);
274
275 assert!(result.is_none());
277 }
278
279 #[test]
280 fn test_rmc_missing_track_angle() {
281 let parser = NmeaParser::new();
282 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,,230394,003.1,W*6A\r\n";
283
284 let result = parser.parse_sentence_complete(sentence);
285
286 assert!(result.is_none());
288 }
289
290 #[test]
291 fn test_rmc_zero_speed() {
292 let parser = NmeaParser::new();
293 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,0.0,0.0,230394,003.1,W*6A\r\n";
294
295 let result = parser.parse_sentence_complete(sentence);
296
297 assert!(result.is_some());
298 let msg = result.unwrap();
299 let rmc = msg.as_rmc();
300 assert!(rmc.is_some());
301
302 let rmc_data = rmc.unwrap();
303 assert_eq!(rmc_data.speed_knots, 0.0);
304 assert_eq!(rmc_data.track_angle, 0.0);
305 }
306
307 #[test]
308 fn test_rmc_numeric_precision() {
309 let parser = NmeaParser::new();
310 let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
311
312 let result = parser.parse_sentence_complete(sentence);
313
314 assert!(result.is_some());
315 let msg = result.unwrap();
316 let rmc = msg.as_rmc();
317 assert!(rmc.is_some());
318
319 let rmc_data = rmc.unwrap();
320 assert!((rmc_data.latitude - 4807.038).abs() < 0.001);
321 assert!((rmc_data.longitude - 1131.000).abs() < 0.001);
322 assert!((rmc_data.speed_knots - 22.4).abs() < 0.1);
323 assert!((rmc_data.track_angle - 84.4).abs() < 0.1);
324 }
325
326 #[test]
327 fn test_rmc_different_talker_id() {
328 let parser = NmeaParser::new();
329 let sentence = b"$GNRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
331
332 let result = parser.parse_sentence_complete(sentence);
333
334 assert!(result.is_some());
335 let msg = result.unwrap();
336 let rmc = msg.as_rmc();
337 assert!(rmc.is_some());
338
339 let rmc_data = rmc.unwrap();
340 assert_eq!(rmc_data.talker_id, crate::types::TalkerId::GN);
341 }
342
343 #[test]
344 fn test_rmc_multiple_constellations() {
345 let parser = NmeaParser::new();
346
347 let gp_sentence =
349 b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
350 let gp_result = parser.parse_sentence_complete(gp_sentence);
351 assert!(gp_result.is_some());
352 let gp_msg = gp_result.unwrap();
353 let gp_rmc = gp_msg.as_rmc().unwrap();
354 assert_eq!(gp_rmc.talker_id, crate::types::TalkerId::GP);
355
356 let gl_sentence =
358 b"$GLRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
359 let gl_result = parser.parse_sentence_complete(gl_sentence);
360 assert!(gl_result.is_some());
361 let gl_msg = gl_result.unwrap();
362 let gl_rmc = gl_msg.as_rmc().unwrap();
363 assert_eq!(gl_rmc.talker_id, crate::types::TalkerId::GL);
364
365 let ga_sentence =
367 b"$GARMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
368 let ga_result = parser.parse_sentence_complete(ga_sentence);
369 assert!(ga_result.is_some());
370 let ga_msg = ga_result.unwrap();
371 let ga_rmc = ga_msg.as_rmc().unwrap();
372 assert_eq!(ga_rmc.talker_id, crate::types::TalkerId::GA);
373 }
374}