use nom::{
character::complete::{char, one_of},
combinator::opt,
number::complete::float,
sequence::preceded,
IResult,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{parse::NmeaSentence, Error, SentenceType};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[derive(Debug, PartialEq)]
pub struct MwvData {
pub wind_direction: Option<f32>,
pub reference: Option<MwvReference>,
pub wind_speed: Option<f32>,
pub wind_speed_units: Option<MwvWindSpeedUnits>,
pub data_valid: bool,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MwvReference {
Relative,
Theoretical,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MwvWindSpeedUnits {
KilometersPerHour,
MetersPerSecond,
Knots,
MilesPerHour,
}
pub fn parse_mwv(sentence: NmeaSentence) -> Result<MwvData, Error> {
if sentence.message_id != SentenceType::MWV {
Err(Error::WrongSentenceHeader {
expected: SentenceType::MWV,
found: sentence.message_id,
})
} else {
Ok(do_parse_mwv(sentence.data)?.1)
}
}
fn do_parse_mwv(i: &str) -> IResult<&str, MwvData> {
let (i, direction) = opt(float)(i)?;
let (i, reference_type) = opt(preceded(char(','), one_of("RT")))(i)?;
let reference_type = reference_type.map(|ch| match ch {
'R' => MwvReference::Relative,
'T' => MwvReference::Theoretical,
_ => unreachable!(),
});
let (i, _) = char(',')(i)?;
let (i, speed) = opt(float)(i)?;
let (i, wind_speed_type) = opt(preceded(char(','), one_of("KMNS")))(i)?;
let wind_speed_type = wind_speed_type.map(|ch| match ch {
'K' => MwvWindSpeedUnits::KilometersPerHour,
'M' => MwvWindSpeedUnits::MetersPerSecond,
'N' => MwvWindSpeedUnits::Knots,
'S' => MwvWindSpeedUnits::MilesPerHour,
_ => unreachable!(),
});
let (i, is_data_valid) = preceded(char(','), one_of("AV"))(i)?;
let is_data_valid = match is_data_valid {
'A' => true,
'V' => false,
_ => unreachable!(),
};
Ok((
i,
MwvData {
wind_direction: direction,
reference: reference_type,
wind_speed: speed,
wind_speed_units: wind_speed_type,
data_valid: is_data_valid,
},
))
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
use crate::parse::parse_nmea_sentence;
#[test]
fn test_parse_mwv() {
let s = parse_nmea_sentence("$WIMWV,041.1,R,01.0,N,A*16").unwrap();
assert_eq!(s.checksum, s.calc_checksum());
assert_eq!(s.checksum, 0x16);
let wimwv_data = parse_mwv(s).unwrap();
assert_relative_eq!(41.1, wimwv_data.wind_direction.unwrap());
assert_eq!(MwvReference::Relative, wimwv_data.reference.unwrap());
assert_relative_eq!(1.0, wimwv_data.wind_speed.unwrap());
assert_eq!(
MwvWindSpeedUnits::Knots,
wimwv_data.wind_speed_units.unwrap()
);
assert!(wimwv_data.data_valid);
}
}