From 0f94e544a0c3cb8859d7b19d5ae8b3c5c120788c Mon Sep 17 00:00:00 2001 From: "meh." Date: Sun, 1 Feb 2026 11:45:08 +0100 Subject: [PATCH 1/4] Add some useful derives --- src/coords.rs | 14 +++++++------- src/datetime.rs | 7 ++++--- src/gga.rs | 4 ++-- src/gll.rs | 2 +- src/gsa.rs | 4 ++-- src/gsv.rs | 2 +- src/lib.rs | 12 ++++++++---- src/modes.rs | 4 ++-- src/mtk.rs | 6 +++--- src/rmc.rs | 2 +- src/satellite.rs | 2 +- src/vtg.rs | 2 +- src/zda.rs | 2 +- 13 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/coords.rs b/src/coords.rs index 9dd9dda..0f36a7d 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -2,7 +2,7 @@ use core::convert::TryFrom; /// Earth hemisphere -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Hemisphere { /// North North, @@ -15,7 +15,7 @@ pub enum Hemisphere { } /// Latitude as reported by receiver. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Latitude { /// Degrees pub degrees: u8, @@ -121,7 +121,7 @@ impl Latitude { } /// Longitude as reported by receiver. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Longitude { /// Degrees pub degrees: u8, @@ -227,7 +227,7 @@ impl Longitude { } /// Altitude reported by receiver typically in GGA sentence. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Altitude { /// Altitude in meters over ground. pub meters: f32, @@ -248,7 +248,7 @@ impl Altitude { } /// Speed reported by receiver typically in RMC and VTG sentences. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Speed { knots: f32, } @@ -305,7 +305,7 @@ impl Speed { } /// The course over ground. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Course { /// Course in degrees from North rotated clockwise. pub degrees: f32, @@ -331,7 +331,7 @@ impl Course { } /// The course over ground calculated from True course and magnetic variation. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct MagneticCourse { /// Course in degrees from Magnetic North Pole rotated clockwise. degrees: f32, diff --git a/src/datetime.rs b/src/datetime.rs index fd1afd0..d94d0be 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,6 +1,6 @@ //! NMEA date and time structures. /// NMEA date -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Date { /// NMEA day pub day: u8, @@ -47,7 +47,7 @@ impl Date { } /// NMEA time in UTC -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct Time { /// Hours as reported by receiver pub hours: u8, @@ -100,7 +100,8 @@ impl Time { } /// NMEA date and time in UTC -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DateTime { /// NMEA date pub date: Date, diff --git a/src/gga.rs b/src/gga.rs index 9e63c98..4139362 100644 --- a/src/gga.rs +++ b/src/gga.rs @@ -5,7 +5,7 @@ use crate::Source; use core::time::Duration; /// Geographic coordinates including altitude, GPS solution quality, DGPS usage information. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct GGA { /// Navigational system. pub source: Source, @@ -78,7 +78,7 @@ impl GGA { } /// Quality of GPS solution -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum GPSQuality { /// No solution NoFix, diff --git a/src/gll.rs b/src/gll.rs index fd8bfe7..b0259f9 100644 --- a/src/gll.rs +++ b/src/gll.rs @@ -4,7 +4,7 @@ use crate::modes::{Mode, Status}; use crate::Source; /// Geographic latitude ang longitude sentence with time of fix and receiver state. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct GLL { /// Navigational system. pub source: Source, diff --git a/src/gsa.rs b/src/gsa.rs index 2b4ed4a..5a26a9e 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -4,7 +4,7 @@ use crate::Source; const MAX_PRNS_PER_MESSAGE: usize = 12; /// GPS DOP and active satellites -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct GSA { /// Navigational system. pub source: Source, @@ -65,7 +65,7 @@ impl GSA { } /// Receiver mode of positioning. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum FixType { /// No valid position is available. NoFix, diff --git a/src/gsv.rs b/src/gsv.rs index 5466a5f..2e4fe20 100644 --- a/src/gsv.rs +++ b/src/gsv.rs @@ -3,7 +3,7 @@ use crate::satellite::Satellite; use crate::Source; const MAX_SATELLITES_PER_MESSAGE: usize = 4; /// Satellites in views including the number of SVs in view, the PRN numbers, elevations, azimuths, and SNR values. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct GSV { /// Navigational system. pub source: Source, diff --git a/src/lib.rs b/src/lib.rs index 130a079..9d5e95d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,7 +115,7 @@ pub use vtg::VTG; pub use zda::ZDA; /// Source of NMEA sentence like GPS, GLONASS or other. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Source { /// USA Global Positioning System GPS = 0b1, @@ -133,6 +133,7 @@ pub enum Source { } /// Mask for Source filter in Parser. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct SourceMask { mask: u32, } @@ -187,7 +188,7 @@ impl TryFrom<&str> for Source { } /// Various kinds of NMEA sentence like RMC, VTG or other. Used for filter by sentence type in Parser. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum Sentence { /// Recommended minimum sentence. RMC = 0b1, @@ -229,6 +230,7 @@ impl TryFrom<&str> for Sentence { } /// Mask for Sentence filter in Parser. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] pub struct SentenceMask { mask: u32, } @@ -268,7 +270,7 @@ impl BitOr for SentenceMask { /// The NMEA sentence parsing result. /// Sentences with many null fields or sentences without valid data is also parsed and returned as None. /// None ParseResult may be interpreted as working receiver but without valid data. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum ParseResult { /// The Recommended Minimum Sentence for any GNSS. Typically most used. RMC(Option), @@ -299,6 +301,7 @@ pub const MAX_SENTENCE_LENGTH: usize = 120usize; /// Parses NMEA sentences and stores intermediate parsing state. /// Parser is tolerant for errors so you should not reinitialize it after errors. +#[derive(Debug, Copy, Clone)] pub struct Parser { buffer: [u8; MAX_SENTENCE_LENGTH], buflen: usize, @@ -309,7 +312,7 @@ pub struct Parser { sentence_mask: SentenceMask, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] enum ParserState { WaitStart, ReadUntilChkSum, @@ -319,6 +322,7 @@ enum ParserState { WaitLF, } +#[derive(Debug)] struct ParserIterator<'a> { parser: &'a mut Parser, input: Iter<'a, u8>, diff --git a/src/modes.rs b/src/modes.rs index ad19bf1..ff8bd47 100644 --- a/src/modes.rs +++ b/src/modes.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub(crate) enum Status { Valid, NotValid, @@ -15,7 +15,7 @@ impl Status { } /// Receiver mode of operation. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Mode { /// Autonomous mode without any external correction. Autonomous, diff --git a/src/mtk.rs b/src/mtk.rs index acb41a5..2529aa4 100644 --- a/src/mtk.rs +++ b/src/mtk.rs @@ -2,7 +2,7 @@ use crate::Source; use core::convert::TryFrom; /// MTK NMEA packet type -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MTKPacketType { /// Related to Spoofing and Jamming detection SPF, @@ -20,7 +20,7 @@ impl TryFrom<&str> for MTKPacketType { } /// Related to Spoofing and Jamming detection . -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct PMTKSPF { /// Navigational system. pub source: Source, @@ -46,7 +46,7 @@ impl PMTKSPF { } /// Status of gps Jamming -#[derive(Debug, PartialEq, Clone,Copy)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum JammingStatus { /// No Jamming Healthy, diff --git a/src/rmc.rs b/src/rmc.rs index 760ed47..2901ee8 100644 --- a/src/rmc.rs +++ b/src/rmc.rs @@ -4,7 +4,7 @@ use crate::modes::{Mode, Status}; use crate::Source; /// Recommended Minimum Sentence for any GNSS source. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct RMC { /// Navigational system. pub source: Source, diff --git a/src/satellite.rs b/src/satellite.rs index a13e490..70d83fa 100644 --- a/src/satellite.rs +++ b/src/satellite.rs @@ -3,7 +3,7 @@ use crate::common; ///Information about satellite in view. -#[derive(Debug, PartialEq, Clone, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] pub struct Satellite { /// Satellite pseudo-random noise number. pub prn: u16, diff --git a/src/vtg.rs b/src/vtg.rs index 2ee09ef..23e08ea 100644 --- a/src/vtg.rs +++ b/src/vtg.rs @@ -3,7 +3,7 @@ use crate::modes::Mode; use crate::Source; /// The actual course and speed relative to the ground. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct VTG { /// Navigational system. pub source: Source, diff --git a/src/zda.rs b/src/zda.rs index bac0ea0..f4ae100 100644 --- a/src/zda.rs +++ b/src/zda.rs @@ -2,7 +2,7 @@ use crate::datetime::{Date, DateTime, Time}; use crate::{common, Source}; /// Geographic latitude ang longitude sentence with time of fix and receiver state. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct ZDA { /// Navigational system. pub source: Source, From e81c560bfb53d0dd6e9b703dfcb556eff237e668 Mon Sep 17 00:00:00 2001 From: "meh." Date: Sun, 1 Feb 2026 11:45:22 +0100 Subject: [PATCH 2/4] Add optional defmt derives --- Cargo.toml | 2 ++ src/coords.rs | 7 +++++++ src/datetime.rs | 2 ++ src/gga.rs | 2 ++ src/gll.rs | 1 + src/gsa.rs | 2 ++ src/gsv.rs | 1 + src/lib.rs | 8 ++++++++ src/modes.rs | 2 ++ src/mtk.rs | 3 +++ src/rmc.rs | 1 + src/satellite.rs | 1 + src/vtg.rs | 1 + src/zda.rs | 1 + 14 files changed, 34 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b1e174d..c49f456 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,13 @@ categories = ["embedded", "no-std", "parser-implementations"] readme = "README.md" [dependencies] +defmt = { version = "1", optional = true } [features] default = ["strict"] mtk = [] strict = [] +defmt = ["dep:defmt"] [badges] codecov = { repository = "nsforth/nmea0183" } diff --git a/src/coords.rs b/src/coords.rs index 0f36a7d..016962d 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -3,6 +3,7 @@ use core::convert::TryFrom; /// Earth hemisphere #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Hemisphere { /// North North, @@ -16,6 +17,7 @@ pub enum Hemisphere { /// Latitude as reported by receiver. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Latitude { /// Degrees pub degrees: u8, @@ -122,6 +124,7 @@ impl Latitude { /// Longitude as reported by receiver. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Longitude { /// Degrees pub degrees: u8, @@ -228,6 +231,7 @@ impl Longitude { /// Altitude reported by receiver typically in GGA sentence. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Altitude { /// Altitude in meters over ground. pub meters: f32, @@ -249,6 +253,7 @@ impl Altitude { /// Speed reported by receiver typically in RMC and VTG sentences. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Speed { knots: f32, } @@ -306,6 +311,7 @@ impl Speed { /// The course over ground. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Course { /// Course in degrees from North rotated clockwise. pub degrees: f32, @@ -332,6 +338,7 @@ impl Course { /// The course over ground calculated from True course and magnetic variation. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct MagneticCourse { /// Course in degrees from Magnetic North Pole rotated clockwise. degrees: f32, diff --git a/src/datetime.rs b/src/datetime.rs index d94d0be..eacc7f2 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,6 +1,7 @@ //! NMEA date and time structures. /// NMEA date #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Date { /// NMEA day pub day: u8, @@ -48,6 +49,7 @@ impl Date { /// NMEA time in UTC #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Time { /// Hours as reported by receiver pub hours: u8, diff --git a/src/gga.rs b/src/gga.rs index 4139362..f304312 100644 --- a/src/gga.rs +++ b/src/gga.rs @@ -6,6 +6,7 @@ use core::time::Duration; /// Geographic coordinates including altitude, GPS solution quality, DGPS usage information. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GGA { /// Navigational system. pub source: Source, @@ -79,6 +80,7 @@ impl GGA { /// Quality of GPS solution #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum GPSQuality { /// No solution NoFix, diff --git a/src/gll.rs b/src/gll.rs index b0259f9..baf13ae 100644 --- a/src/gll.rs +++ b/src/gll.rs @@ -5,6 +5,7 @@ use crate::Source; /// Geographic latitude ang longitude sentence with time of fix and receiver state. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GLL { /// Navigational system. pub source: Source, diff --git a/src/gsa.rs b/src/gsa.rs index 5a26a9e..05fae99 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -5,6 +5,7 @@ const MAX_PRNS_PER_MESSAGE: usize = 12; /// GPS DOP and active satellites #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GSA { /// Navigational system. pub source: Source, @@ -66,6 +67,7 @@ impl GSA { /// Receiver mode of positioning. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FixType { /// No valid position is available. NoFix, diff --git a/src/gsv.rs b/src/gsv.rs index 2e4fe20..c1f8c5d 100644 --- a/src/gsv.rs +++ b/src/gsv.rs @@ -4,6 +4,7 @@ use crate::Source; const MAX_SATELLITES_PER_MESSAGE: usize = 4; /// Satellites in views including the number of SVs in view, the PRN numbers, elevations, azimuths, and SNR values. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GSV { /// Navigational system. pub source: Source, diff --git a/src/lib.rs b/src/lib.rs index 9d5e95d..7d53400 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,6 +116,7 @@ pub use zda::ZDA; /// Source of NMEA sentence like GPS, GLONASS or other. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Source { /// USA Global Positioning System GPS = 0b1, @@ -134,6 +135,7 @@ pub enum Source { /// Mask for Source filter in Parser. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SourceMask { mask: u32, } @@ -189,6 +191,7 @@ impl TryFrom<&str> for Source { /// Various kinds of NMEA sentence like RMC, VTG or other. Used for filter by sentence type in Parser. #[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Sentence { /// Recommended minimum sentence. RMC = 0b1, @@ -231,6 +234,7 @@ impl TryFrom<&str> for Sentence { /// Mask for Sentence filter in Parser. #[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SentenceMask { mask: u32, } @@ -271,6 +275,7 @@ impl BitOr for SentenceMask { /// Sentences with many null fields or sentences without valid data is also parsed and returned as None. /// None ParseResult may be interpreted as working receiver but without valid data. #[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParseResult { /// The Recommended Minimum Sentence for any GNSS. Typically most used. RMC(Option), @@ -302,6 +307,7 @@ pub const MAX_SENTENCE_LENGTH: usize = 120usize; /// Parses NMEA sentences and stores intermediate parsing state. /// Parser is tolerant for errors so you should not reinitialize it after errors. #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Parser { buffer: [u8; MAX_SENTENCE_LENGTH], buflen: usize, @@ -313,6 +319,7 @@ pub struct Parser { } #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum ParserState { WaitStart, ReadUntilChkSum, @@ -323,6 +330,7 @@ enum ParserState { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct ParserIterator<'a> { parser: &'a mut Parser, input: Iter<'a, u8>, diff --git a/src/modes.rs b/src/modes.rs index ff8bd47..d7f4532 100644 --- a/src/modes.rs +++ b/src/modes.rs @@ -1,4 +1,5 @@ #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum Status { Valid, NotValid, @@ -16,6 +17,7 @@ impl Status { /// Receiver mode of operation. #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Mode { /// Autonomous mode without any external correction. Autonomous, diff --git a/src/mtk.rs b/src/mtk.rs index 2529aa4..179ef2a 100644 --- a/src/mtk.rs +++ b/src/mtk.rs @@ -3,6 +3,7 @@ use core::convert::TryFrom; /// MTK NMEA packet type #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MTKPacketType { /// Related to Spoofing and Jamming detection SPF, @@ -21,6 +22,7 @@ impl TryFrom<&str> for MTKPacketType { /// Related to Spoofing and Jamming detection . #[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PMTKSPF { /// Navigational system. pub source: Source, @@ -47,6 +49,7 @@ impl PMTKSPF { /// Status of gps Jamming #[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum JammingStatus { /// No Jamming Healthy, diff --git a/src/rmc.rs b/src/rmc.rs index 2901ee8..f141752 100644 --- a/src/rmc.rs +++ b/src/rmc.rs @@ -5,6 +5,7 @@ use crate::Source; /// Recommended Minimum Sentence for any GNSS source. #[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RMC { /// Navigational system. pub source: Source, diff --git a/src/satellite.rs b/src/satellite.rs index 70d83fa..40ddfc0 100644 --- a/src/satellite.rs +++ b/src/satellite.rs @@ -4,6 +4,7 @@ use crate::common; ///Information about satellite in view. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Satellite { /// Satellite pseudo-random noise number. pub prn: u16, diff --git a/src/vtg.rs b/src/vtg.rs index 23e08ea..7675963 100644 --- a/src/vtg.rs +++ b/src/vtg.rs @@ -4,6 +4,7 @@ use crate::Source; /// The actual course and speed relative to the ground. #[derive(Debug, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct VTG { /// Navigational system. pub source: Source, diff --git a/src/zda.rs b/src/zda.rs index f4ae100..514b825 100644 --- a/src/zda.rs +++ b/src/zda.rs @@ -3,6 +3,7 @@ use crate::{common, Source}; /// Geographic latitude ang longitude sentence with time of fix and receiver state. #[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ZDA { /// Navigational system. pub source: Source, From 45fec5f8bec37b8c2a3cce29db60653ab424ade5 Mon Sep 17 00:00:00 2001 From: "meh." Date: Sun, 1 Feb 2026 11:46:48 +0100 Subject: [PATCH 3/4] Make MagneticCourse field public --- src/coords.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coords.rs b/src/coords.rs index 016962d..b6adb29 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -341,7 +341,7 @@ impl Course { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct MagneticCourse { /// Course in degrees from Magnetic North Pole rotated clockwise. - degrees: f32, + pub degrees: f32, } impl From for MagneticCourse { From d6a08ffc51bf1258614fe9c6c20bee4bd30f7e78 Mon Sep 17 00:00:00 2001 From: "meh." Date: Mon, 2 Feb 2026 19:23:49 +0100 Subject: [PATCH 4/4] Add missing TXT sentence and missing QZSS source --- Cargo.toml | 2 ++ src/lib.rs | 33 +++++++++++++++++++++--- src/txt.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 src/txt.rs diff --git a/Cargo.toml b/Cargo.toml index c49f456..2f3e670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,12 @@ readme = "README.md" [dependencies] defmt = { version = "1", optional = true } +heapless = { version = "0.9", optional = true } [features] default = ["strict"] mtk = [] +txt = ["dep:heapless"] strict = [] defmt = ["dep:defmt"] diff --git a/src/lib.rs b/src/lib.rs index 7d53400..d87b332 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,8 @@ pub(crate) mod mtk; pub(crate) mod rmc; pub(crate) mod vtg; pub(crate) mod zda; +#[cfg(feature = "txt")] +pub(crate) mod txt; pub use gga::GPSQuality; pub use gga::GGA; @@ -113,6 +115,8 @@ pub use mtk::PMTKSPF; pub use rmc::RMC; pub use vtg::VTG; pub use zda::ZDA; +#[cfg(feature = "txt")] +pub use txt::{TXT, MessageType}; /// Source of NMEA sentence like GPS, GLONASS or other. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -128,9 +132,11 @@ pub enum Source { Beidou = 0b1000, /// Global Navigation Sattelite System. Some combination of other systems. Depends on receiver model, receiver settings, etc.. GNSS = 0b10000, + /// Quasi-Zenith Satellite System + QZSS = 0b100000, #[cfg(feature = "mtk")] /// MediaTek NMEA packet protocol - MTK = 0b100000, + MTK = 0b1000000, } /// Mask for Source filter in Parser. @@ -182,9 +188,14 @@ impl TryFrom<&str> for Source { "GA" => Ok(Source::Gallileo), "BD" => Ok(Source::Beidou), "GN" => Ok(Source::GNSS), + "GQ" => Ok(Source::QZSS), #[cfg(feature = "mtk")] "PM" => Ok(Source::MTK), - _ => Err("Source is not supported!"), + source => { + #[cfg(feature = "defmt")] + defmt::error!("Source is not supported: {}", source); + Err("Source is not supported!") + } } } } @@ -210,6 +221,9 @@ pub enum Sentence { GSA = 0b1000000, /// Current Date and Time. ZDA = 0b10000000, + #[cfg(feature = "txt")] + /// Text message from receiver. + TXT = 0b100000000, } impl TryFrom<&str> for Sentence { @@ -226,8 +240,14 @@ impl TryFrom<&str> for Sentence { "PMTK" => Ok(Sentence::PMTK), "GSA" => Ok(Sentence::GSA), "ZDA" => Ok(Sentence::ZDA), + #[cfg(feature = "txt")] + "TXT" => Ok(Sentence::TXT), - _ => Err("Unsupported sentence type."), + sentence => { + #[cfg(feature = "defmt")] + defmt::error!("Unsupported NMEA sentence type: {}", sentence); + Err("Unsupported sentence type.") + } } } } @@ -274,7 +294,7 @@ impl BitOr for SentenceMask { /// The NMEA sentence parsing result. /// Sentences with many null fields or sentences without valid data is also parsed and returned as None. /// None ParseResult may be interpreted as working receiver but without valid data. -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParseResult { /// The Recommended Minimum Sentence for any GNSS. Typically most used. @@ -294,6 +314,9 @@ pub enum ParseResult { GSA(Option), /// Current Date and Time. ZDA(Option), + #[cfg(feature = "txt")] + /// Text message from receiver. + TXT(Option), } #[cfg(feature = "strict")] @@ -484,6 +507,8 @@ impl Parser { Sentence::GSV => Ok(Some(ParseResult::GSV(GSV::parse(source, &mut iter)?))), Sentence::GSA => Ok(Some(ParseResult::GSA(GSA::parse(source, &mut iter)?))), Sentence::ZDA => Ok(Some(ParseResult::ZDA(ZDA::parse(source, &mut iter)?))), + #[cfg(feature = "txt")] + Sentence::TXT => Ok(Some(ParseResult::TXT(TXT::parse(source, &mut iter)?))), #[cfg(feature = "mtk")] Sentence::PMTK => { diff --git a/src/txt.rs b/src/txt.rs new file mode 100644 index 0000000..7b974df --- /dev/null +++ b/src/txt.rs @@ -0,0 +1,75 @@ +use heapless::String; + +use crate::{common, Source}; + +/// Geographic latitude ang longitude sentence with time of fix and receiver state. +#[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct TXT { + /// Navigational system. + pub source: Source, + /// Total number of sentences + pub sentences: u8, + /// Sentence number + pub number: u8, + /// Message type + pub type_: MessageType, + /// The message content + pub text: String<69>, +} + +impl TXT { + pub(crate) fn parse<'a>( + source: Source, + fields: &mut core::str::Split<'a, char>, + ) -> Result, &'static str> { + let mut fields = fields.peekable(); + let sentences = common::parse_u8(fields.next())?; + let number = common::parse_u8(fields.next())?; + let type_ = MessageType::parse(fields.next())?; + let mut text = String::<69>::new(); + + while let Some(part) = fields.next() { + text.push_str(part).map_err(|_| "exceeded string size")?; + + if fields.peek().is_some() { + text.push_str(",").map_err(|_| "exceeded string size")?; + } + } + + if let (Some(sentences), Some(number), Some(type_)) = (sentences, number, type_) { + Ok(Some(TXT { source, sentences, number, type_, text })) + } + else { + Ok(None) + } + } +} + +/// The type of message +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MessageType { + /// An error message + Error, + /// A warning message + Warning, + /// An informational message or notice + Notice, + /// An application specific message + User, +} + +impl MessageType { + pub(crate) fn parse(input: Option<&str>) -> Result, &'static str> { + match input { + Some("00") => Ok(Some(MessageType::Error)), + Some("01") => Ok(Some(MessageType::Warning)), + Some("02") => Ok(Some(MessageType::Notice)), + Some("07") => Ok(Some(MessageType::User)), + Some("") => Ok(None), + None => Ok(None), + _ => Err("Unsupported message kind!"), + } + } +}