From 04d4a11b28eeeefcbc0b9ca557a491041971c5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Kr=C3=B3lak?= Date: Sat, 8 Apr 2023 20:58:07 +0200 Subject: [PATCH 1/3] Added parsing BWW messages --- README.md | 1 + src/parse.rs | 3 + src/parser.rs | 1 + src/sentences/bww.rs | 138 ++++++++++++++++++++++++++++++++ src/sentences/mod.rs | 2 + tests/all_supported_messages.rs | 2 + 6 files changed, 147 insertions(+) create mode 100644 src/sentences/bww.rs diff --git a/README.md b/README.md index 2995083..2697882 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ NMEA Standard Sentences - ALM - BOD - BWC +- BWW - DBK - GBS - GGA * diff --git a/src/parse.rs b/src/parse.rs index 1d0b734..794f24b 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -101,6 +101,7 @@ pub enum ParseResult { ALM(AlmData), BOD(BodData), BWC(BwcData), + BWW(BwwData), DBK(DbkData), GBS(GbsData), GGA(GgaData), @@ -128,6 +129,7 @@ impl From<&ParseResult> for SentenceType { ParseResult::ALM(_) => SentenceType::ALM, ParseResult::BOD(_) => SentenceType::BOD, ParseResult::BWC(_) => SentenceType::BWC, + ParseResult::BWW(_) => SentenceType::BWW, ParseResult::DBK(_) => SentenceType::DBK, ParseResult::GBS(_) => SentenceType::GBS, ParseResult::GGA(_) => SentenceType::GGA, @@ -181,6 +183,7 @@ pub fn parse_str(sentence_input: &str) -> Result { SentenceType::ALM => parse_alm(nmea_sentence).map(ParseResult::ALM), SentenceType::BOD => parse_bod(nmea_sentence).map(ParseResult::BOD), SentenceType::BWC => parse_bwc(nmea_sentence).map(ParseResult::BWC), + SentenceType::BWW => parse_bww(nmea_sentence).map(ParseResult::BWW), SentenceType::GBS => parse_gbs(nmea_sentence).map(ParseResult::GBS), SentenceType::GGA => parse_gga(nmea_sentence).map(ParseResult::GGA), SentenceType::GSV => parse_gsv(nmea_sentence).map(ParseResult::GSV), diff --git a/src/parser.rs b/src/parser.rs index dbeaf4f..b6a7367 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -353,6 +353,7 @@ impl<'a> Nmea { return Ok(FixType::Invalid); } ParseResult::BWC(_) + | ParseResult::BWW(_) | ParseResult::BOD(_) | ParseResult::DBK(_) | ParseResult::GBS(_) diff --git a/src/sentences/bww.rs b/src/sentences/bww.rs new file mode 100644 index 0000000..648dddc --- /dev/null +++ b/src/sentences/bww.rs @@ -0,0 +1,138 @@ +use arrayvec::ArrayString; +use nom::{ + bytes::complete::is_not, character::complete::char, combinator::opt, number::complete::float, +}; + +use crate::{parse::NmeaSentence, Error, SentenceType}; + +const MAX_LEN: usize = 64; + +/// BWW - Bearing - Waypoint to Waypoint +/// +/// Bearing calculated at the FROM waypoint. +/// +/// +/// ```text +/// 1 2 3 4 5 6 7 +/// | | | | | | | +/// $--BWW,x.x,T,x.x,M,c--c,c--c*hh +/// ``` +/// Field Number: +/// 1. Bearing, degrees True +/// 2. T = True +/// 3. Bearing Degrees, Magnetic +/// 4. M = Magnetic +/// 5. TO Waypoint ID +/// 6. FROM Waypoint ID +/// 7. Checksum + +#[derive(Debug, PartialEq)] +pub struct BwwData { + pub true_bearing: Option, + pub magnetic_bearing: Option, + pub to_waypoint_id: Option>, + pub from_waypoint_id: Option>, +} + +fn do_parse_bww(i: &str) -> Result { + // 1. Bearing, degrees True + let (i, true_bearing) = opt(float)(i)?; + let (i, _) = char(',')(i)?; + // 2. T = True + let (i, _) = opt(char('T'))(i)?; + let (i, _) = char(',')(i)?; + + // 3. Bearing, degrees Magnetic + let (i, magnetic_bearing) = opt(float)(i)?; + let (i, _) = char(',')(i)?; + // 4. M = Magnetic + let (i, _) = opt(char('M'))(i)?; + let (i, _) = char(',')(i)?; + + // 5. TO Waypoint ID + let (i, to_waypoint_id) = opt(is_not(","))(i)?; + + let to_waypoint_id = if let Some(to_waypoint_id) = to_waypoint_id { + Some( + ArrayString::from(to_waypoint_id) + .map_err(|_e| Error::SentenceLength(to_waypoint_id.len()))?, + ) + } else { + None + }; + + // 6. FROM Waypoint ID + let (i, _) = char(',')(i)?; + let (_i, from_waypoint_id) = opt(is_not(",*"))(i)?; + + let from_waypoint_id = if let Some(from_waypoint_id) = from_waypoint_id { + Some( + ArrayString::from(from_waypoint_id) + .map_err(|_e| Error::SentenceLength(from_waypoint_id.len()))?, + ) + } else { + None + }; + + Ok(BwwData { + true_bearing, + magnetic_bearing, + to_waypoint_id, + from_waypoint_id, + }) +} + +/// # Parse BWW message +/// +/// See: +pub fn parse_bww(sentence: NmeaSentence) -> Result { + if sentence.message_id != SentenceType::BWW { + Err(Error::WrongSentenceHeader { + expected: SentenceType::BWW, + found: sentence.message_id, + }) + } else { + Ok(do_parse_bww(sentence.data)?) + } +} + +#[cfg(test)] +mod tests { + use approx::assert_relative_eq; + + use super::*; + use crate::parse::parse_nmea_sentence; + + #[test] + fn test_parse_bww_full() { + let sentence = parse_nmea_sentence("$GPBWW,213.8,T,218.0,M,TOWPT,FROMWPT*42").unwrap(); + assert_eq!(sentence.checksum, sentence.calc_checksum()); + assert_eq!(sentence.checksum, 0x42); + + let data = parse_bww(sentence).unwrap(); + + assert_relative_eq!(data.true_bearing.unwrap(), 213.8); + assert_relative_eq!(data.magnetic_bearing.unwrap(), 218.0); + assert_eq!(&data.to_waypoint_id.unwrap(), "TOWPT"); + assert_eq!(&data.from_waypoint_id.unwrap(), "FROMWPT"); + } + + #[test] + fn test_parse_bww_with_optional_fields() { + let sentence = parse_nmea_sentence("$GPBWW,,T,,M,,*4C").unwrap(); + assert_eq!(sentence.checksum, sentence.calc_checksum()); + assert_eq!(sentence.checksum, 0x4C); + + let data = parse_bww(sentence).unwrap(); + + assert_eq!( + BwwData { + true_bearing: None, + magnetic_bearing: None, + to_waypoint_id: None, + from_waypoint_id: None, + }, + data + ); + } +} diff --git a/src/sentences/mod.rs b/src/sentences/mod.rs index 2c3e113..d6e43d6 100644 --- a/src/sentences/mod.rs +++ b/src/sentences/mod.rs @@ -4,6 +4,7 @@ mod aam; mod alm; mod bod; mod bwc; +mod bww; mod dbk; mod gbs; mod gga; @@ -31,6 +32,7 @@ pub use { alm::{parse_alm, AlmData}, bod::{parse_bod, BodData}, bwc::{parse_bwc, BwcData}, + bww::{parse_bww, BwwData}, dbk::{parse_dbk, DbkData}, faa_mode::{FaaMode, FaaModes}, fix_type::FixType, diff --git a/tests/all_supported_messages.rs b/tests/all_supported_messages.rs index bab0924..07fd58e 100644 --- a/tests/all_supported_messages.rs +++ b/tests/all_supported_messages.rs @@ -11,6 +11,8 @@ fn test_all_supported_messages() { (SentenceType::ALM, "$GPALM,1,1,15,1159,00,441D,4E,16BE,FD5E,A10C9F,4A2DA4,686E81,58CBE1,0A4,001*77"), // BWC (SentenceType::BWC, "$GPBWC,220516,5130.02,N,00046.34,W,213.8,T,218.0,M,0004.6,N,EGLM*21"), + // BWW + (SentenceType::BWW, "$GPBWW,213.8,T,218.0,M,TOWPT,FROMWPT*42"), // GGA (SentenceType::GGA, "$GPGGA,133605.0,5521.75946,N,03731.93769,E,0,00,,,M,,M,,*4F"), // GLL From 80db962f098655c5c0cb857134ad72de44c8bb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Kr=C3=B3lak?= Date: Sun, 23 Apr 2023 13:43:42 +0200 Subject: [PATCH 2/3] Added parameter error when one of strings exceed string reserved space. --- src/error.rs | 13 +++++++++++++ src/sentences/bww.rs | 12 ++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index a86cecb..ba87e4b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,6 +22,11 @@ pub enum Error<'a> { ParsingError(nom::Err>), /// The sentence was too long to be parsed, our current limit is `SENTENCE_MAX_LEN` characters. SentenceLength(usize), + /// Parameter was too long to fit into fixed ArrayString. + ParameterLength { + max_length: usize, + parameter_length: usize, + }, /// The sentence is recognized but it is not supported by the crate. Unsupported(SentenceType), /// The sentence type is unknown for this crate. @@ -69,6 +74,14 @@ impl<'a> fmt::Display for Error<'a> { "The sentence was too long to be parsed, current limit is {} characters", size ), + Error::ParameterLength { + max_length, + parameter_length: _, + } => write!( + f, + "Parameter was too long to fit into string, max length is {}", + max_length + ), Error::Unsupported(sentence) => { write!(f, "Unsupported NMEA sentence '{}'", sentence) } diff --git a/src/sentences/bww.rs b/src/sentences/bww.rs index 648dddc..f3686a8 100644 --- a/src/sentences/bww.rs +++ b/src/sentences/bww.rs @@ -54,8 +54,10 @@ fn do_parse_bww(i: &str) -> Result { let to_waypoint_id = if let Some(to_waypoint_id) = to_waypoint_id { Some( - ArrayString::from(to_waypoint_id) - .map_err(|_e| Error::SentenceLength(to_waypoint_id.len()))?, + ArrayString::from(to_waypoint_id).map_err(|_e| Error::ParameterLength { + max_length: MAX_LEN, + parameter_length: to_waypoint_id.len(), + })?, ) } else { None @@ -67,8 +69,10 @@ fn do_parse_bww(i: &str) -> Result { let from_waypoint_id = if let Some(from_waypoint_id) = from_waypoint_id { Some( - ArrayString::from(from_waypoint_id) - .map_err(|_e| Error::SentenceLength(from_waypoint_id.len()))?, + ArrayString::from(from_waypoint_id).map_err(|_e| Error::ParameterLength { + max_length: MAX_LEN, + parameter_length: from_waypoint_id.len(), + })?, ) } else { None From 75c5c1dc310f4b92aa7e55c5af24c07ebc54bfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Kr=C3=B3lak?= Date: Sun, 23 Apr 2023 22:06:56 +0200 Subject: [PATCH 3/3] Increased test coverage --- src/sentences/bww.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sentences/bww.rs b/src/sentences/bww.rs index f3686a8..bc6c817 100644 --- a/src/sentences/bww.rs +++ b/src/sentences/bww.rs @@ -139,4 +139,43 @@ mod tests { data ); } + + #[test] + fn test_parse_bww_with_wrong_sentence() { + let sentence = parse_nmea_sentence("$GPAAM,,T,,M,,*4C").unwrap(); + + assert_eq!( + parse_bww(sentence).unwrap_err(), + Error::WrongSentenceHeader { + expected: SentenceType::BWW, + found: SentenceType::AAM, + } + ); + } + + #[test] + fn test_parse_bww_with_too_long_to_waypoint_parameter() { + let sentence = parse_nmea_sentence("$GPBWW,,T,,M,ABCDEFGHIJKLMNOPQRSTUWXYZABCDEFGHIJKLMNOPQRSTUWXYZABCDEFGHIJKLMNOPQRSTUWXYZ,*4C").unwrap(); + + assert_eq!( + parse_bww(sentence).unwrap_err(), + Error::ParameterLength { + max_length: 64, + parameter_length: 75 + } + ); + } + + #[test] + fn test_parse_bww_with_too_long_from_waypoint_parameter() { + let sentence = parse_nmea_sentence("$GPBWW,,T,,M,,ABCDEFGHIJKLMNOPQRSTUWXYZABCDEFGHIJKLMNOPQRSTUWXYZABCDEFGHIJKLMNOPQRSTUWXYZ*4C").unwrap(); + + assert_eq!( + parse_bww(sentence).unwrap_err(), + Error::ParameterLength { + max_length: 64, + parameter_length: 75 + } + ); + } }