-
Notifications
You must be signed in to change notification settings - Fork 50
Better parsing and merging of GSA messages #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c9671d6
1f0a39f
80118a0
d99759a
3d6fe7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,11 @@ use nom::{ | |
| Err, IResult, InputLength, Parser, | ||
| }; | ||
|
|
||
| use crate::{parse::NmeaSentence, sentences::utils::number, Error, SentenceType}; | ||
| use crate::{ | ||
| parse::NmeaSentence, | ||
| sentences::{utils::number, GnssType}, | ||
| Error, SentenceType, | ||
| }; | ||
|
|
||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] | ||
|
|
@@ -48,6 +52,7 @@ pub struct GsaData { | |
| pub pdop: Option<f32>, | ||
| pub hdop: Option<f32>, | ||
| pub vdop: Option<f32>, | ||
| pub gnss_type: GnssType, | ||
| } | ||
|
|
||
| /// This function is take from `nom`, see `nom::multi::many0` | ||
|
|
@@ -85,7 +90,35 @@ fn gsa_prn_fields_parse(i: &str) -> IResult<&str, Vec<Option<u32>, 18>> { | |
| many0(terminated(opt(number::<u32>), char(',')))(i) | ||
| } | ||
|
|
||
| type GsaTail = (Vec<Option<u32>, 18>, Option<f32>, Option<f32>, Option<f32>); | ||
| type GsaTail = ( | ||
| Vec<Option<u32>, 18>, | ||
| Option<f32>, | ||
| Option<f32>, | ||
| Option<f32>, | ||
| Option<GnssType>, | ||
| ); | ||
|
|
||
| fn do_parse_gsa_tail_with_gnss_type(i: &str) -> IResult<&str, GsaTail> { | ||
| let (i, prns) = gsa_prn_fields_parse(i)?; | ||
| let (i, pdop) = float(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, hdop) = float(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, vdop) = float(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, gnss_type) = number::<u8>(i)?; | ||
|
|
||
| let gnss_type = match gnss_type { | ||
| 1 => Some(GnssType::Gps), | ||
| 2 => Some(GnssType::Glonass), | ||
| 3 => Some(GnssType::Galileo), | ||
| 4 => Some(GnssType::Beidou), | ||
| 5 => Some(GnssType::Qzss), | ||
| 6 => Some(GnssType::NavIC), | ||
| _ => None, | ||
| }; | ||
| Ok((i, (prns, Some(pdop), Some(hdop), Some(vdop), gnss_type))) | ||
| } | ||
|
|
||
| fn do_parse_gsa_tail(i: &str) -> IResult<&str, GsaTail> { | ||
| let (i, prns) = gsa_prn_fields_parse(i)?; | ||
|
|
@@ -94,7 +127,7 @@ fn do_parse_gsa_tail(i: &str) -> IResult<&str, GsaTail> { | |
| let (i, hdop) = float(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, vdop) = float(i)?; | ||
| Ok((i, (prns, Some(pdop), Some(hdop), Some(vdop)))) | ||
| Ok((i, (prns, Some(pdop), Some(hdop), Some(vdop), None))) | ||
| } | ||
|
|
||
| fn is_comma(x: char) -> bool { | ||
|
|
@@ -103,17 +136,33 @@ fn is_comma(x: char) -> bool { | |
|
|
||
| fn do_parse_empty_gsa_tail(i: &str) -> IResult<&str, GsaTail> { | ||
| value( | ||
| (Vec::new(), None, None, None), | ||
| (Vec::new(), None, None, None, None), | ||
| all_consuming(take_while1(is_comma)), | ||
| )(i) | ||
| } | ||
|
|
||
| fn do_parse_gsa(i: &str) -> IResult<&str, GsaData> { | ||
| fn do_parse_gsa<'a>(talker_id: &str, i: &'a str) -> IResult<&'a str, GsaData> { | ||
| let (i, mode1) = one_of("MA")(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, mode2) = one_of("123")(i)?; | ||
| let (i, _) = char(',')(i)?; | ||
| let (i, mut tail) = alt((do_parse_empty_gsa_tail, do_parse_gsa_tail))(i)?; | ||
| let (i, mut tail) = alt(( | ||
| do_parse_empty_gsa_tail, | ||
| do_parse_gsa_tail_with_gnss_type, | ||
| do_parse_gsa_tail, | ||
| ))(i)?; | ||
|
|
||
| let gnss_type = match talker_id { | ||
| "GA" => GnssType::Galileo, | ||
| "GP" => GnssType::Gps, | ||
| "GL" => GnssType::Glonass, | ||
| "BD" | "GB" => GnssType::Beidou, | ||
| "GI" => GnssType::NavIC, | ||
| "GQ" | "PQ" | "QZ" => GnssType::Qzss, | ||
| "GN" => tail.4.unwrap_or(GnssType::Gps), | ||
| _ => tail.4.unwrap_or(GnssType::Gps), | ||
|
Comment on lines
+155
to
+163
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not sure how it's handled by receivers in previous versions and multi-constelation view. GLONASS satellite numbers come in two flavors. If a sentence has a GL talker ID, expect the skyviews to be GLONASS-only and in the range 1-32; you must add 64 to get a globally-unique NMEA ID. If the sentence has a GN talker ID, the device emits a multi-constellation skyview with GLONASS IDs already in the 65-96 range.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the trouble here, according to gpsd, is that before NMEA 4.11 there was no standard way of knowing which satellite id corresponded to which system. So the ids used in GSA messages with talker GN is product dependent, and thus we have no way of distinguishing between satellites from different systems. NMEA 4.11 added the system id(last field of GSA messages) which is optional. If the system id is present all the satellites in a GSA message have the system assigned ids. gpsd does this whole thing to decide which number each satellite has: https://gitlab.com/gpsd/gpsd/-/blob/master/drivers/driver_nmea0183.c#L549 Its been some time since I made this pull request and my memory is no longer clear about exactly what is going on.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand the issue correctly, I'd suggest to use a |
||
| }; | ||
|
|
||
| Ok(( | ||
| i, | ||
| GsaData { | ||
|
|
@@ -141,6 +190,7 @@ fn do_parse_gsa(i: &str) -> IResult<&str, GsaData> { | |
| pdop: tail.1, | ||
| hdop: tail.2, | ||
| vdop: tail.3, | ||
| gnss_type, | ||
| }, | ||
| )) | ||
| } | ||
|
|
@@ -199,7 +249,7 @@ pub fn parse_gsa(sentence: NmeaSentence) -> Result<GsaData, Error> { | |
| found: sentence.message_id, | ||
| }) | ||
| } else { | ||
| Ok(do_parse_gsa(sentence.data)?.1) | ||
| Ok(do_parse_gsa(sentence.talker_id, sentence.data)?.1) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -232,6 +282,22 @@ mod tests { | |
| pdop: Some(3.6), | ||
| hdop: Some(2.1), | ||
| vdop: Some(2.2), | ||
| gnss_type: GnssType::Gps | ||
| }, | ||
| gsa | ||
| ); | ||
| let s = | ||
| parse_nmea_sentence("$GNGSA,A,3,23,02,27,10,08,,,,,,,,3.45,1.87,2.89,1*01").unwrap(); | ||
| let gsa = parse_gsa(s).unwrap(); | ||
| assert_eq!( | ||
| GsaData { | ||
| mode1: GsaMode1::Automatic, | ||
| mode2: GsaMode2::Fix3D, | ||
| fix_sats_prn: Vec::from_slice(&[23, 2, 27, 10, 8]).unwrap(), | ||
| pdop: Some(3.45), | ||
| hdop: Some(1.87), | ||
| vdop: Some(2.89), | ||
| gnss_type: GnssType::Gps | ||
| }, | ||
| gsa | ||
| ); | ||
|
|
@@ -241,6 +307,8 @@ mod tests { | |
| "$BDGSA,A,3,214,,,,,,,,,,,,1.8,1.1,1.4*18", | ||
| "$GNGSA,A,3,31,26,21,,,,,,,,,,3.77,2.55,2.77*1A", | ||
| "$GNGSA,A,3,75,86,87,,,,,,,,,,3.77,2.55,2.77*1C", | ||
| "$GNGSA,A,3,23,02,27,10,08,,,,,,,,3.45,1.87,2.89,1*01", | ||
| "$GNGSA,A,3,,,,,,,,,,,,,3.45,1.87,2.89,4*0B", | ||
| "$GPGSA,A,1,,,,*32", | ||
| ]; | ||
| for line in &gsa_examples { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain the changes in Gsv? I don't see the use of floating numbers in the string, could you verify that a float can be returned and include a test for it?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we are seeing floats(or at least fixedpoint numbers with 2 decimals), and we had parse failures due to this. Note that it does not change the type of the I can get our GNSS to produce those messages for testing.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, please! Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly this is the System ID:
https://gpsd.gitlab.io/gpsd/NMEA.html#_gsa_gps_dop_and_active_satellites
At least from this documentation I don't see 5 and 6 but if your receiver uses these numbers I'm ok with including them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gpsd is describing system id 5 and 6 here: https://gpsd.gitlab.io/gpsd/NMEA.html#_nmea_4_11_system_id_and_signal_id
Our receiver does not, afaik, produce these numbers