use crate::message::ParsedSentence;
use crate::types::{MessageType, TalkerId};
#[derive(Debug, Clone)]
pub struct VtgData {
pub talker_id: TalkerId,
pub track_true: Option<f32>,
pub track_true_indicator: Option<char>,
pub track_magnetic: Option<f32>,
pub track_magnetic_indicator: Option<char>,
pub speed_knots: Option<f32>,
pub speed_knots_indicator: Option<char>,
pub speed_kph: Option<f32>,
pub speed_kph_indicator: Option<char>,
}
impl ParsedSentence {
pub fn as_vtg(&self) -> Option<VtgData> {
if self.message_type != MessageType::VTG {
return None;
}
Some(VtgData {
talker_id: self.talker_id,
track_true: self.parse_field(1),
track_true_indicator: self.parse_field_char(2),
track_magnetic: self.parse_field(3),
track_magnetic_indicator: self.parse_field_char(4),
speed_knots: self.parse_field(5),
speed_knots_indicator: self.parse_field_char(6),
speed_kph: self.parse_field(7),
speed_kph_indicator: self.parse_field_char(8),
})
}
}
#[cfg(test)]
mod tests {
use crate::NmeaParser;
#[test]
fn test_vtg_complete_message() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.track_true, Some(54.7));
assert_eq!(vtg_data.track_true_indicator, Some('T'));
assert_eq!(vtg_data.track_magnetic, Some(34.4));
assert_eq!(vtg_data.track_magnetic_indicator, Some('M'));
assert_eq!(vtg_data.speed_knots, Some(5.5));
assert_eq!(vtg_data.speed_knots_indicator, Some('N'));
assert_eq!(vtg_data.speed_kph, Some(10.2));
assert_eq!(vtg_data.speed_kph_indicator, Some('K'));
}
#[test]
fn test_vtg_with_empty_fields() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,,T,,M,,N,,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.track_true, None);
assert_eq!(vtg_data.track_magnetic, None);
assert_eq!(vtg_data.speed_knots, None);
assert_eq!(vtg_data.speed_kph, None);
}
#[test]
fn test_vtg_zero_speed() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,0.0,T,0.0,M,0.0,N,0.0,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.speed_knots, Some(0.0));
assert_eq!(vtg_data.speed_kph, Some(0.0));
}
#[test]
fn test_vtg_high_speed() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,270.5,T,250.3,M,125.8,N,233.0,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert!((vtg_data.speed_knots.unwrap() - 125.8).abs() < 0.1);
assert!((vtg_data.speed_kph.unwrap() - 233.0).abs() < 0.1);
}
#[test]
fn test_vtg_only_true_track() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,054.7,T,,M,,N,,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.track_true, Some(54.7));
assert_eq!(vtg_data.track_magnetic, None);
}
#[test]
fn test_vtg_only_knots() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,,T,,M,5.5,N,,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.speed_knots, Some(5.5));
assert_eq!(vtg_data.speed_kph, None);
}
#[test]
fn test_vtg_only_kph() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,,T,,M,,N,10.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.speed_knots, None);
assert_eq!(vtg_data.speed_kph, Some(10.2));
}
#[test]
fn test_vtg_track_angle_ranges() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,0.0,T,0.0,M,5.5,N,10.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg().unwrap();
assert_eq!(vtg.track_true, Some(0.0));
let sentence = b"$GPVTG,359.9,T,359.9,M,5.5,N,10.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg().unwrap();
assert!((vtg.track_true.unwrap() - 359.9).abs() < 0.1);
}
#[test]
fn test_vtg_speed_conversion_accuracy() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
let knots = vtg_data.speed_knots.unwrap();
let kph = vtg_data.speed_kph.unwrap();
let expected_kph = knots * 1.852;
assert!((kph - expected_kph).abs() < 0.2);
}
#[test]
fn test_vtg_numeric_precision() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert!((vtg_data.track_true.unwrap() - 54.7).abs() < 0.1);
assert!((vtg_data.track_magnetic.unwrap() - 34.4).abs() < 0.1);
assert!((vtg_data.speed_knots.unwrap() - 5.5).abs() < 0.1);
assert!((vtg_data.speed_kph.unwrap() - 10.2).abs() < 0.1);
}
#[test]
fn test_vtg_different_talker_id() {
let parser = NmeaParser::new();
let sentence = b"$GNVTG,054.7,T,034.4,M,005.5,N,010.2,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.talker_id, crate::types::TalkerId::GN);
}
#[test]
fn test_vtg_mixed_constellation_data() {
let parser = NmeaParser::new();
let gp_sentence = b"$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48\r\n";
let gp_result = parser.parse_sentence_complete(gp_sentence);
assert!(gp_result.is_some());
let gp_msg = gp_result.unwrap();
let gp_vtg = gp_msg.as_vtg().unwrap();
assert_eq!(gp_vtg.talker_id, crate::types::TalkerId::GP);
assert_eq!(gp_vtg.track_true, Some(54.7));
let gl_sentence = b"$GLVTG,154.7,T,134.4,M,015.5,N,028.7,K*48\r\n";
let gl_result = parser.parse_sentence_complete(gl_sentence);
assert!(gl_result.is_some());
let gl_msg = gl_result.unwrap();
let gl_vtg = gl_msg.as_vtg().unwrap();
assert_eq!(gl_vtg.talker_id, crate::types::TalkerId::GL);
assert_eq!(gl_vtg.track_true, Some(154.7));
}
#[test]
fn test_vtg_easterly_heading() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,090.0,T,085.0,M,10.0,N,18.5,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.track_true, Some(90.0));
}
#[test]
fn test_vtg_westerly_heading() {
let parser = NmeaParser::new();
let sentence = b"$GPVTG,270.0,T,265.0,M,10.0,N,18.5,K*48\r\n";
let result = parser.parse_sentence_complete(sentence);
assert!(result.is_some());
let msg = result.unwrap();
let vtg = msg.as_vtg();
assert!(vtg.is_some());
let vtg_data = vtg.unwrap();
assert_eq!(vtg_data.track_true, Some(270.0));
}
}