Skip to content

Commit

Permalink
Merge pull request #86 from taavit/bww-message
Browse files Browse the repository at this point in the history
Added parsing BWW messages
  • Loading branch information
elpiel authored Apr 27, 2023
2 parents a4a2d27 + 75c5c1d commit d8f668b
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ NMEA Standard Sentences
- ALM
- BOD
- BWC
- BWW
- DBK
- GBS
- GGA *
Expand Down
13 changes: 13 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ pub enum Error<'a> {
ParsingError(nom::Err<nom::error::Error<&'a str>>),
/// 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.
Expand Down Expand Up @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub enum ParseResult {
ALM(AlmData),
BOD(BodData),
BWC(BwcData),
BWW(BwwData),
DBK(DbkData),
GBS(GbsData),
GGA(GgaData),
Expand Down Expand Up @@ -129,6 +130,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,
Expand Down Expand Up @@ -183,6 +185,7 @@ pub fn parse_str(sentence_input: &str) -> Result<ParseResult, Error> {
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),
Expand Down
1 change: 1 addition & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ impl<'a> Nmea {
return Ok(FixType::Invalid);
}
ParseResult::BWC(_)
| ParseResult::BWW(_)
| ParseResult::BOD(_)
| ParseResult::DBK(_)
| ParseResult::GBS(_)
Expand Down
181 changes: 181 additions & 0 deletions src/sentences/bww.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
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.
/// <https://gpsd.gitlab.io/gpsd/NMEA.html#_bww_bearing_waypoint_to_waypoint>
///
/// ```text
/// 1 2 3 4 5 6 7
/// | | | | | | |
/// $--BWW,x.x,T,x.x,M,c--c,c--c*hh<CR><LF>
/// ```
/// 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<f32>,
pub magnetic_bearing: Option<f32>,
pub to_waypoint_id: Option<ArrayString<MAX_LEN>>,
pub from_waypoint_id: Option<ArrayString<MAX_LEN>>,
}

fn do_parse_bww(i: &str) -> Result<BwwData, Error> {
// 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::ParameterLength {
max_length: MAX_LEN,
parameter_length: 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::ParameterLength {
max_length: MAX_LEN,
parameter_length: from_waypoint_id.len(),
})?,
)
} else {
None
};

Ok(BwwData {
true_bearing,
magnetic_bearing,
to_waypoint_id,
from_waypoint_id,
})
}

/// # Parse BWW message
///
/// See: <https://gpsd.gitlab.io/gpsd/NMEA.html#_bww_bearing_waypoint_to_waypoint>
pub fn parse_bww(sentence: NmeaSentence) -> Result<BwwData, Error> {
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
);
}

#[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
}
);
}
}
2 changes: 2 additions & 0 deletions src/sentences/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod aam;
mod alm;
mod bod;
mod bwc;
mod bww;
mod dbk;
mod gbs;
mod gga;
Expand Down Expand Up @@ -32,6 +33,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,
Expand Down
2 changes: 2 additions & 0 deletions tests/all_supported_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d8f668b

Please sign in to comment.