Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ radar = ["TTM"]
## Water
water = ["DBK", "DBS", "DPT", "MTW", "VHW"]
## Vendor-specific messages
vendor-specific = ["RMZ"]
vendor-specific = ["RMZ", "RMCE", "GGAE"]
## Other
other = ["HDT", "MDA", "MWV", "TXT", "ZDA"]

Expand Down Expand Up @@ -180,6 +180,14 @@ MWV = []
## (feature: `GNSS`)
RMC = []

## RMCE - Enhanced Recommended Minimum Navigation Information (u-blox vendor specific)
## (feature: `vendor-specific`)
RMCE = []

## GGAE - Enhanced Global Positioning System Fix Data (u-blox vendor specific)
## (feature: `vendor-specific`)
GGAE = []

## PGRMZ - Garmin Altitude (Vendor specific)
## (feature: `vendor-specific`)
RMZ = []
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Supported sentences (alphabetically ordered):
- `DPT` - Depth of Water (feature: `water`)
- `GBS` - GPS Satellite Fault Detection (feature: `GNSS`)
- `GGA` - * Global Positioning System Fix Data (feature: `GNSS`)
- `GGAE` - Enhanced Global Positioning System Fix Data (feature: `vendor-specific`)
- `GLL` - * Geographic Position - Latitude/Longitude (feature: `GNSS`)
- `GNS` - * Fix data (feature: `GNSS`)
- `GSA` - * GPS DOP and active satellites (feature: `GNSS`)
Expand All @@ -32,6 +33,7 @@ Supported sentences (alphabetically ordered):
- `MTW` - Mean Temperature of Water (feature: `water`)
- `MWV` - Wind Speed and Angle (feature: `other`)
- `RMC` - * Recommended Minimum Navigation Information (feature: `GNSS`)
- `RMCE` - Enhanced Recommended Minimum Navigation Information (feature: `vendor-specific`)
- `RMZ` - PGRMZ - Garmin Altitude (feature: `vendor-specific`)
- `TTM` - Tracked target message (feature: `radar`)
- `TXT` - * Text message (feature: `other`)
Expand Down
29 changes: 29 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ fn parse_checksum(i: &str) -> IResult<&str, u8> {
}

fn parse_sentence_type(i: &str) -> IResult<&str, SentenceType> {
// Try 4-character sentence types first (vendor-specific extensions like RMCE, GGAE)
if i.len() >= 4 {
if let Ok(sentence_type) = SentenceType::try_from(&i[..4]) {
return Ok((&i[4..], sentence_type));
}
}
// Fall back to standard 3-character sentence types
map_res(take(3usize), |sentence_type: &str| {
SentenceType::try_from(sentence_type).map_err(|_| "Unknown sentence type")
})
Expand Down Expand Up @@ -128,6 +135,8 @@ pub enum ParseResult {
MTW(MtwData),
MWV(MwvData),
RMC(RmcData),
RMCE(RmcData),
GGAE(GgaData),
TTM(TtmData),
TXT(TxtData),
VHW(VhwData),
Expand Down Expand Up @@ -164,6 +173,8 @@ impl From<&ParseResult> for SentenceType {
ParseResult::MTW(_) => SentenceType::MTW,
ParseResult::MWV(_) => SentenceType::MWV,
ParseResult::RMC(_) => SentenceType::RMC,
ParseResult::RMCE(_) => SentenceType::RMCE,
ParseResult::GGAE(_) => SentenceType::GGAE,
ParseResult::TTM(_) => SentenceType::TTM,
ParseResult::TXT(_) => SentenceType::TXT,
ParseResult::VHW(_) => SentenceType::VHW,
Expand Down Expand Up @@ -380,6 +391,24 @@ pub fn parse_str(sentence_input: &str) -> Result<ParseResult, Error<'_>> {
}
}
}
SentenceType::RMCE => {
cfg_if! {
if #[cfg(feature = "RMCE")] {
parse_rmce(nmea_sentence).map(ParseResult::RMCE)
} else {
return Err(Error::DisabledSentence);
}
}
}
SentenceType::GGAE => {
cfg_if! {
if #[cfg(feature = "GGAE")] {
parse_ggae(nmea_sentence).map(ParseResult::GGAE)
} else {
return Err(Error::DisabledSentence);
}
}
}
SentenceType::RMZ => {
cfg_if! {
if #[cfg(feature = "RMZ")] {
Expand Down
45 changes: 45 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ impl<'a> Nmea {
self.merge_gga_data(gga);
Ok(SentenceType::GGA)
}
ParseResult::GGAE(gga) => {
self.merge_gga_data(gga);
Ok(SentenceType::GGAE)
}
ParseResult::GSV(gsv) => {
self.merge_gsv_data(gsv)?;
Ok(SentenceType::GSV)
Expand All @@ -262,6 +266,10 @@ impl<'a> Nmea {
self.merge_rmc_data(rmc);
Ok(SentenceType::RMC)
}
ParseResult::RMCE(rmc) => {
self.merge_rmc_data(rmc);
Ok(SentenceType::RMCE)
}
ParseResult::GNS(gns) => {
self.merge_gns_data(gns);
Ok(SentenceType::GNS)
Expand Down Expand Up @@ -331,6 +339,17 @@ impl<'a> Nmea {
self.merge_rmc_data(rmc_data);
self.sentences_for_this_time.insert(SentenceType::RMC);
}
ParseResult::RMCE(rmc_data) => {
if rmc_data.status_of_fix == RmcStatusOfFix::Invalid {
self.clear_position_info();
return Ok(FixType::Invalid);
}
if !self.update_fix_time(rmc_data.fix_time) {
return Ok(FixType::Invalid);
}
self.merge_rmc_data(rmc_data);
self.sentences_for_this_time.insert(SentenceType::RMCE);
}
ParseResult::GNS(gns_data) => {
let fix_type: FixType = gns_data.faa_modes.into();
if !fix_type.is_valid() {
Expand All @@ -357,6 +376,20 @@ impl<'a> Nmea {
self.merge_gga_data(gga_data);
self.sentences_for_this_time.insert(SentenceType::GGA);
}
ParseResult::GGAE(gga_data) => {
match gga_data.fix_type {
Some(FixType::Invalid) | None => {
self.clear_position_info();
return Ok(FixType::Invalid);
}
_ => { /*nothing*/ }
}
if !self.update_fix_time(gga_data.fix_time) {
return Ok(FixType::Invalid);
}
self.merge_gga_data(gga_data);
self.sentences_for_this_time.insert(SentenceType::GGAE);
}
ParseResult::GLL(gll_data) => {
if !self.update_fix_time(gll_data.fix_time) {
return Ok(FixType::Invalid);
Expand Down Expand Up @@ -895,6 +928,12 @@ define_sentence_type_enum! {
///
/// Type: `GPS`
GGA,
/// GGAE - Enhanced Global Positioning System Fix Data
///
/// u-blox proprietary extension with higher precision coordinates
///
/// Type: `Vendor extensions`
GGAE,
/// GLC - Geographic Position, Loran-C
///
/// <https://gpsd.gitlab.io/gpsd/NMEA.html#_glc_geographic_position_loran_c>
Expand Down Expand Up @@ -1065,6 +1104,12 @@ define_sentence_type_enum! {
///
/// Type: `Navigation`
RMC,
/// RMCE - Enhanced Recommended Minimum Navigation Information
///
/// u-blox proprietary extension with higher precision coordinates
///
/// Type: `Vendor extensions`
RMCE,
/// PGRMZ - Garmin Altitude
///
/// <https://gpsd.gitlab.io/gpsd/NMEA.html#_pgrmz_garmin_altitude>
Expand Down
4 changes: 2 additions & 2 deletions src/sentences.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use {
faa_mode::{FaaMode, FaaModes},
fix_type::FixType,
gbs::{GbsData, parse_gbs},
gga::{GgaData, parse_gga},
gga::{GgaData, parse_gga, parse_ggae},
gll::{GllData, parse_gll},
gns::{GnsData, parse_gns},
gnss_type::GnssType,
Expand All @@ -61,7 +61,7 @@ pub use {
mda::{MdaData, parse_mda},
mtw::{MtwData, parse_mtw},
mwv::{MwvData, parse_mwv},
rmc::{RmcData, parse_rmc},
rmc::{RmcData, parse_rmc, parse_rmce},
rmz::{PgrmzData, parse_pgrmz},
ttm::{
TtmAngle, TtmData, TtmDistanceUnit, TtmReference, TtmStatus, TtmTypeOfAcquisition,
Expand Down
33 changes: 33 additions & 0 deletions src/sentences/gga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ pub fn parse_gga(sentence: NmeaSentence<'_>) -> Result<GgaData, Error<'_>> {
}
}

/// Parse GGAE message (Enhanced GGA with higher precision)
///
/// Enhanced version of GGA with higher precision coordinates.
/// Uses the same parsing logic as standard GGA.
pub fn parse_ggae(sentence: NmeaSentence<'_>) -> Result<GgaData, Error<'_>> {
if sentence.message_id != SentenceType::GGAE {
Err(Error::WrongSentenceHeader {
expected: SentenceType::GGAE,
found: sentence.message_id,
})
} else {
Ok(do_parse_gga(sentence.data)?.1)
}
}

#[cfg(not(feature = "std"))]
#[cfg(feature = "serde")]
mod serde_naive_time {
Expand Down Expand Up @@ -328,4 +343,22 @@ mod tests {

assert_eq!(data.fix_time, gga.fix_time);
}

#[test]
fn test_parse_ggae_enhanced() {
// Checksum calculated: GPGGAE,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,, = 0x33
let ggae = "$GPGGAE,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*33";
let s = parse_nmea_sentence(ggae).unwrap();
assert_eq!(s.checksum, s.calc_checksum());
let data = parse_ggae(s).unwrap();
assert_eq!(
data.fix_time,
Some(NaiveTime::from_hms_milli_opt(9, 27, 50, 0).expect("invalid time"))
);
assert_eq!(data.fix_type.unwrap(), FixType::Gps);
assert_relative_eq!(data.latitude.unwrap(), 53. + 21.6802 / 60.);
assert_relative_eq!(data.longitude.unwrap(), -(6. + 30.3372 / 60.));
assert_eq!(data.fix_satellites.unwrap(), 8);
assert_relative_eq!(data.hdop.unwrap(), 1.03);
}
}
35 changes: 35 additions & 0 deletions src/sentences/rmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ pub fn parse_rmc(sentence: NmeaSentence<'_>) -> Result<RmcData, Error<'_>> {
}
}

/// Parse RMCE message (Enhanced RMC with higher precision)
///
/// Enhanced version of RMC with higher precision coordinates.
/// Uses the same parsing logic as standard RMC.
pub fn parse_rmce(sentence: NmeaSentence<'_>) -> Result<RmcData, Error<'_>> {
if sentence.message_id != SentenceType::RMCE {
Err(Error::WrongSentenceHeader {
expected: SentenceType::RMCE,
found: sentence.message_id,
})
} else {
Ok(do_parse_rmc(sentence.data)?.1)
}
}

#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
Expand Down Expand Up @@ -387,4 +402,24 @@ mod tests {
assert_eq!(faa_mode, Some(FaaMode::Manual));
assert_eq!(nav_status, Some(RmcNavigationStatus::Estimated));
}

#[test]
fn parse_rmce_enhanced() {
// Checksum calculated: GPRMCE,225446.33,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E,A = 0x6E
let rmce = "$GPRMCE,225446.33,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E,A*6E";
let s = parse_nmea_sentence(rmce).unwrap();
assert_eq!(s.checksum, s.calc_checksum());
let rmc_data = parse_rmce(s).unwrap();
assert_eq!(
rmc_data.fix_time,
Some(NaiveTime::from_hms_milli_opt(22, 54, 46, 330).expect("invalid time"))
);
assert_eq!(
rmc_data.fix_date,
Some(NaiveDate::from_ymd_opt(1994, 11, 19).expect("invalid time"))
);
assert_relative_eq!(rmc_data.lat.unwrap(), 49.0 + 16.45 / 60.);
assert_relative_eq!(rmc_data.lon.unwrap(), -(123.0 + 11.12 / 60.));
assert_eq!(rmc_data.faa_mode, Some(FaaMode::Autonomous));
}
}