use crate::message::ParsedSentence;
use crate::types::{MessageType, TalkerId};
#[derive(Debug, Clone)]
pub struct RmcData {
pub talker_id: TalkerId,
time_data: [u8; 16],
time_len: u8,
pub status: char,
pub latitude: f64,
pub lat_direction: char,
pub longitude: f64,
pub lon_direction: char,
pub speed_knots: f32,
pub track_angle: f32,
date_data: [u8; 8],
date_len: u8,
pub magnetic_variation: Option<f32>,
pub mag_var_direction: Option<char>,
}
impl RmcData {
pub fn time(&self) -> &str {
core::str::from_utf8(&self.time_data[..self.time_len as usize]).unwrap_or("")
}
pub fn date(&self) -> &str {
core::str::from_utf8(&self.date_data[..self.date_len as usize]).unwrap_or("")
}
}
impl ParsedSentence {
pub fn as_rmc(&self) -> Option<RmcData> {
if self.message_type != MessageType::RMC {
return None;
}
let time_str = self.get_field_str(1)?;
let status = self.parse_field_char(2)?;
let latitude: f64 = self.parse_field(3)?;
let lat_direction = self.parse_field_char(4)?;
let longitude: f64 = self.parse_field(5)?;
let lon_direction = self.parse_field_char(6)?;
let speed_knots: f32 = self.parse_field(7)?;
let track_angle: f32 = self.parse_field(8)?;
let date_str = self.get_field_str(9)?;
let mut time_data = [0u8; 16];
let time_bytes = time_str.as_bytes();
let time_len = time_bytes.len().min(16) as u8;
time_data[..time_len as usize].copy_from_slice(&time_bytes[..time_len as usize]);
let mut date_data = [0u8; 8];
let date_bytes = date_str.as_bytes();
let date_len = date_bytes.len().min(8) as u8;
date_data[..date_len as usize].copy_from_slice(&date_bytes[..date_len as usize]);
Some(RmcData {
talker_id: self.talker_id,
time_data,
time_len,
status,
latitude,
lat_direction,
longitude,
lon_direction,
speed_knots,
track_angle,
date_data,
date_len,
magnetic_variation: self.parse_field(10),
mag_var_direction: self.parse_field_char(11),
})
}
}
#[cfg(test)]
mod tests {
use crate::NmeaParser;
#[test]
fn test_rmc_complete_message() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert_eq!(rmc_data.time(), "123519");
assert_eq!(rmc_data.status, 'A');
assert_eq!(rmc_data.latitude, 4807.038);
assert_eq!(rmc_data.lat_direction, 'N');
assert_eq!(rmc_data.longitude, 1131.000);
assert_eq!(rmc_data.lon_direction, 'E');
assert_eq!(rmc_data.speed_knots, 22.4);
assert_eq!(rmc_data.track_angle, 84.4);
assert_eq!(rmc_data.date(), "230394");
assert_eq!(rmc_data.magnetic_variation, Some(3.1));
assert_eq!(rmc_data.mag_var_direction, Some('W'));
}
#[test]
fn test_rmc_void_status() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,V,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert_eq!(rmc_data.status, 'V');
}
#[test]
fn test_rmc_without_magnetic_variation() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,,*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert_eq!(rmc_data.magnetic_variation, None);
assert_eq!(rmc_data.mag_var_direction, None);
}
#[test]
fn test_rmc_missing_time() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_none());
}
#[test]
fn test_rmc_missing_status() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_none());
}
#[test]
fn test_rmc_missing_date() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_none());
}
#[test]
fn test_rmc_missing_speed() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_none());
}
#[test]
fn test_rmc_missing_track_angle() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_none());
}
#[test]
fn test_rmc_zero_speed() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,0.0,0.0,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert_eq!(rmc_data.speed_knots, 0.0);
assert_eq!(rmc_data.track_angle, 0.0);
}
#[test]
fn test_rmc_numeric_precision() {
let parser = NmeaParser::new();
let sentence = b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert!((rmc_data.latitude - 4807.038).abs() < 0.001);
assert!((rmc_data.longitude - 1131.000).abs() < 0.001);
assert!((rmc_data.speed_knots - 22.4).abs() < 0.1);
assert!((rmc_data.track_angle - 84.4).abs() < 0.1);
}
#[test]
fn test_rmc_different_talker_id() {
let parser = NmeaParser::new();
let sentence = b"$GNRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let rmc = msg.as_rmc();
assert!(rmc.is_some());
let rmc_data = rmc.unwrap();
assert_eq!(rmc_data.talker_id, crate::types::TalkerId::GN);
}
#[test]
fn test_rmc_multiple_constellations() {
let parser = NmeaParser::new();
let gp_sentence =
b"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let gp_result = parser.parse_sentence_complete(gp_sentence);
assert!(gp_result.is_some());
let gp_msg = gp_result.unwrap();
let gp_rmc = gp_msg.as_rmc().unwrap();
assert_eq!(gp_rmc.talker_id, crate::types::TalkerId::GP);
let gl_sentence =
b"$GLRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let gl_result = parser.parse_sentence_complete(gl_sentence);
assert!(gl_result.is_some());
let gl_msg = gl_result.unwrap();
let gl_rmc = gl_msg.as_rmc().unwrap();
assert_eq!(gl_rmc.talker_id, crate::types::TalkerId::GL);
let ga_sentence =
b"$GARMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n";
let ga_result = parser.parse_sentence_complete(ga_sentence);
assert!(ga_result.is_some());
let ga_msg = ga_result.unwrap();
let ga_rmc = ga_msg.as_rmc().unwrap();
assert_eq!(ga_rmc.talker_id, crate::types::TalkerId::GA);
}
}