use crate::sentences::utils::parse_number_in_range;
use nom::{
character::{
complete::{char, hex_digit0},
streaming::hex_digit1,
},
combinator::{map_res, opt},
IResult,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{Error, NmeaSentence, SentenceType};
use super::utils::number;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AlmData {
pub total_number_of_messages: Option<u16>,
pub sentence_number: Option<u16>,
pub satellite_prn_number: Option<u8>,
pub gps_week_number: Option<u16>,
pub sv_health: Option<u8>,
pub eccentricity: Option<u16>,
pub almanac_reference_time: Option<u8>,
pub inclination_angle: Option<u16>,
pub rate_of_right_ascension: Option<u16>,
pub root_of_semi_major_axis: Option<u32>,
pub argument_of_perigee: Option<u32>,
pub longitude_of_ascension_node: Option<u32>,
pub mean_anomaly: Option<u32>,
pub f0_clock_parameter: Option<u16>,
pub f1_clock_parameter: Option<u16>,
}
impl AlmData {
pub fn get_10bit_gps_week_number(&self) -> Option<u16> {
self.gps_week_number.map(|n| n % 1024)
}
}
pub fn parse_alm(sentence: NmeaSentence) -> Result<AlmData, Error> {
if sentence.message_id != SentenceType::ALM {
Err(Error::WrongSentenceHeader {
expected: SentenceType::ALM,
found: sentence.message_id,
})
} else {
Ok(do_parse_alm(sentence.data)?.1)
}
}
fn do_parse_alm(i: &str) -> IResult<&str, AlmData> {
let (i, total_number_of_messages) = opt(number)(i)?;
let (i, _) = char(',')(i)?;
let (i, sentence_number) = opt(number)(i)?;
let (i, _) = char(',')(i)?;
let (i, satellite_prn_number) = opt(|i| parse_number_in_range::<u8>(i, 1, 32))(i)?;
let (i, _) = char(',')(i)?;
let (i, gps_week_number) = opt(|i| parse_number_in_range::<u16>(i, 0, 8191))(i)?;
let (i, _) = char(',')(i)?;
let (i, sv_health) = opt(map_res(hex_digit1, |s| u8::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, eccentricity) = opt(map_res(hex_digit1, |s| u16::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, almanac_reference_time) = opt(map_res(hex_digit1, |s| u8::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, inclination_angle) = opt(map_res(hex_digit1, |s| u16::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, rate_of_right_ascension) = opt(map_res(hex_digit1, |s| u16::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, root_of_semi_major_axis) = opt(map_res(hex_digit1, |s| u32::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, argument_of_perigee) = opt(map_res(hex_digit1, |s| u32::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, longitude_of_ascension_node) =
opt(map_res(hex_digit1, |s| u32::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, mean_anomaly) = opt(map_res(hex_digit1, |s| u32::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, f0_clock_parameter) = opt(map_res(hex_digit0, |s| u16::from_str_radix(s, 16)))(i)?;
let (i, _) = char(',')(i)?;
let (i, f1_clock_parameter) = opt(map_res(hex_digit0, |s| u16::from_str_radix(s, 16)))(i)?;
Ok((
i,
AlmData {
total_number_of_messages,
sentence_number,
satellite_prn_number,
gps_week_number,
sv_health,
eccentricity,
almanac_reference_time,
inclination_angle,
rate_of_right_ascension,
root_of_semi_major_axis,
argument_of_perigee,
longitude_of_ascension_node,
mean_anomaly,
f0_clock_parameter,
f1_clock_parameter,
},
))
}
#[cfg(test)]
mod tests {
use crate::{parse_nmea_sentence, sentences::parse_alm};
#[test]
fn test() {
let total_number_of_messages = 31;
let sentence_number = 1;
let satellite_prn_number = 2;
let gps_week_number = 1617;
let sv_health = 0x00;
let eccentricity = 0x50f6;
let almanac_reference_time = 0x0f;
let inclination_angle = 0xfd98;
let rate_of_right_ascension = 0xfd39;
let root_of_semi_major_axis = 0xa10cf3;
let argument_of_perigee = 0x81389b;
let longitude_of_ascension_node = 0x423632;
let mean_anomaly = 0xbd913c;
let f0_clock_parameter = 0x148;
let f1_clock_parameter = 0x001;
let sentence_string =
"$GPALM,31,1,02,1617,00,50F6,0F,FD98,FD39,A10CF3,81389B,423632,BD913C,148,001*3C";
let sentence = parse_nmea_sentence(sentence_string).unwrap();
assert_eq!(sentence.checksum, sentence.calc_checksum());
assert_eq!(sentence.checksum, 0x3C);
let data = parse_alm(sentence).unwrap();
assert_eq!(
total_number_of_messages,
data.total_number_of_messages.unwrap()
);
assert_eq!(sentence_number, data.sentence_number.unwrap());
assert_eq!(satellite_prn_number, data.satellite_prn_number.unwrap());
assert_eq!(gps_week_number, data.gps_week_number.unwrap());
assert_eq!(sv_health, data.sv_health.unwrap());
assert_eq!(eccentricity, data.eccentricity.unwrap());
assert_eq!(almanac_reference_time, data.almanac_reference_time.unwrap());
assert_eq!(inclination_angle, data.inclination_angle.unwrap());
assert_eq!(
rate_of_right_ascension,
data.rate_of_right_ascension.unwrap()
);
assert_eq!(
root_of_semi_major_axis,
data.root_of_semi_major_axis.unwrap()
);
assert_eq!(argument_of_perigee, data.argument_of_perigee.unwrap());
assert_eq!(
longitude_of_ascension_node,
data.longitude_of_ascension_node.unwrap()
);
assert_eq!(mean_anomaly, data.mean_anomaly.unwrap());
assert_eq!(f0_clock_parameter, data.f0_clock_parameter.unwrap());
assert_eq!(f1_clock_parameter, data.f1_clock_parameter.unwrap());
}
}