Skip to main content

cu_gnss_payloads/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5use alloc::string::String;
6use alloc::vec::Vec;
7use bincode::{Decode, Encode};
8use cu29::prelude::*;
9use cu29::units::si::angle::degree;
10use cu29::units::si::f32::{Angle as Angle32, Length, Ratio, Time, Velocity};
11use cu29::units::si::f64::Angle as Angle64;
12use cu29::units::si::length::meter;
13use cu29::units::si::ratio::ratio;
14use cu29::units::si::time::second;
15use cu29::units::si::velocity::meter_per_second;
16use serde::{Deserialize, Serialize};
17
18#[derive(
19    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
20)]
21pub enum GnssFixType {
22    #[default]
23    NoFix,
24    DeadReckoningOnly,
25    Fix2D,
26    Fix3D,
27    GnssDeadReckoningCombined,
28    TimeOnly,
29    Reserved(u8),
30}
31
32impl From<u8> for GnssFixType {
33    fn from(value: u8) -> Self {
34        match value {
35            0 => Self::NoFix,
36            1 => Self::DeadReckoningOnly,
37            2 => Self::Fix2D,
38            3 => Self::Fix3D,
39            4 => Self::GnssDeadReckoningCombined,
40            5 => Self::TimeOnly,
41            other => Self::Reserved(other),
42        }
43    }
44}
45
46#[derive(
47    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
48)]
49pub enum GnssInfoSeverity {
50    Debug,
51    #[default]
52    Notice,
53    Warning,
54    Error,
55    Test,
56}
57
58#[derive(
59    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
60)]
61pub enum GnssAckKind {
62    #[default]
63    Ack,
64    Nak,
65}
66
67#[derive(
68    Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect,
69)]
70pub struct GnssEpochTime {
71    pub itow_ms: u32,
72    pub year: u16,
73    pub month: u8,
74    pub day: u8,
75    pub hour: u8,
76    pub minute: u8,
77    pub second: u8,
78    pub valid_date: bool,
79    pub valid_time: bool,
80    pub fully_resolved: bool,
81    pub valid_magnetic_declination: bool,
82}
83
84/// Precision notes for GNSS payloads.
85///
86/// | Field(s) | UBX/source resolution | `f32` precision at relevant scale | `f64` precision | Recommendation |
87/// | --- | --- | --- | --- | --- |
88/// | `latitude`, `longitude` | `1e-7 deg` | at `32 deg`: `3.81e-6 deg` (`0.42 m` lat, `0.36 m` lon); at `180 deg`: `1.53e-5 deg` (`1.70 m` lat) | `~1e-14 deg` | `f64` |
89/// | `height_*` | `1 mm` | at `1000 m`: `6.1e-5 m`; at `10000 m`: `9.8e-4 m` | `~1e-13..1e-12 m` | `f32` |
90/// | `velocity_*`, `ground_speed` | `1 mm/s` | at `100 m/s`: `7.6e-6 m/s`; at `1000 m/s`: `6.1e-5 m/s` | `~1e-14..1e-13 m/s` | `f32` |
91/// | `heading_*` | `1e-5 deg` | at `360 deg`: `3.05e-5 deg` | `~5.7e-14 deg` | `f32` |
92/// | `magnetic_declination` | `1e-2 deg` | at `20 deg`: `1.91e-6 deg` | `~3.6e-15 deg` | `f32` |
93/// | `accuracy.*` | `1 mm`, `1 mm/s`, `1e-5 deg`, `1 ns`, `0.01` | already below or close to source quantization | much smaller than source quantization | `f32` |
94/// | satellite/signal angles and residuals | `1 deg`, `0.1 m` | already below source quantization | much smaller than source quantization | `f32` |
95///
96/// The only fields that materially benefit from `f64` in practice are the
97/// absolute geodetic coordinates, because path planning and local projection
98/// often subtract nearby fixes and `f32` loses sub-meter detail there.
99#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
100pub struct GnssFixSolution {
101    pub fix_type: GnssFixType,
102    pub gnss_fix_ok: bool,
103    pub differential_solution: bool,
104    pub carrier_solution: u8,
105    pub invalid_llh: bool,
106    pub num_satellites_used: u8,
107    pub longitude: Angle64,
108    pub latitude: Angle64,
109    pub height_ellipsoid: Length,
110    pub height_msl: Length,
111    pub velocity_north: Velocity,
112    pub velocity_east: Velocity,
113    pub velocity_down: Velocity,
114    pub ground_speed: Velocity,
115    pub heading_motion: Angle32,
116    pub heading_vehicle: Angle32,
117    pub magnetic_declination: Angle32,
118    pub psm_state: u8,
119    pub correction_age_bucket: u8,
120}
121
122impl Default for GnssFixSolution {
123    fn default() -> Self {
124        Self {
125            fix_type: GnssFixType::NoFix,
126            gnss_fix_ok: false,
127            differential_solution: false,
128            carrier_solution: 0,
129            invalid_llh: true,
130            num_satellites_used: 0,
131            longitude: Angle64::new::<degree>(0.0),
132            latitude: Angle64::new::<degree>(0.0),
133            height_ellipsoid: Length::new::<meter>(0.0),
134            height_msl: Length::new::<meter>(0.0),
135            velocity_north: Velocity::new::<meter_per_second>(0.0),
136            velocity_east: Velocity::new::<meter_per_second>(0.0),
137            velocity_down: Velocity::new::<meter_per_second>(0.0),
138            ground_speed: Velocity::new::<meter_per_second>(0.0),
139            heading_motion: Angle32::new::<degree>(0.0),
140            heading_vehicle: Angle32::new::<degree>(0.0),
141            magnetic_declination: Angle32::new::<degree>(0.0),
142            psm_state: 0,
143            correction_age_bucket: 0,
144        }
145    }
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
149pub struct GnssAccuracy {
150    pub horizontal: Length,
151    pub vertical: Length,
152    pub speed: Velocity,
153    pub heading: Angle32,
154    pub time: Time,
155    pub position_dop: Ratio,
156}
157
158impl Default for GnssAccuracy {
159    fn default() -> Self {
160        Self {
161            horizontal: Length::new::<meter>(0.0),
162            vertical: Length::new::<meter>(0.0),
163            speed: Velocity::new::<meter_per_second>(0.0),
164            heading: Angle32::new::<degree>(0.0),
165            time: Time::new::<second>(0.0),
166            position_dop: Ratio::new::<ratio>(0.0),
167        }
168    }
169}
170
171#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
172pub struct GnssNavEpoch {
173    pub time: GnssEpochTime,
174    pub fix: GnssFixSolution,
175    pub accuracy: GnssAccuracy,
176}
177
178#[derive(
179    Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect,
180)]
181pub struct GnssSatelliteInfo {
182    pub gnss_id: u8,
183    pub sv_id: u8,
184    pub cno_dbhz: u8,
185    pub elevation: Angle32,
186    pub azimuth: Angle32,
187    pub pseudorange_residual: Length,
188    pub quality_ind: u8,
189    pub used_for_navigation: bool,
190    pub health: u8,
191    pub differential_correction_available: bool,
192    pub pseudorange_smoothed: bool,
193    pub orbit_source: u8,
194    pub sbas_corr_used: bool,
195    pub rtcm_corr_used: bool,
196    pub slas_corr_used: bool,
197    pub spartn_corr_used: bool,
198}
199
200#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
201pub struct GnssSatelliteState {
202    pub itow_ms: u32,
203    pub satellites: Vec<GnssSatelliteInfo>,
204}
205
206#[derive(
207    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
208)]
209pub struct GnssSatsInView {
210    pub itow_ms: u32,
211    pub count: u16,
212}
213
214#[derive(
215    Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect,
216)]
217pub struct GnssSignalInfo {
218    pub gnss_id: u8,
219    pub sv_id: u8,
220    pub signal_id: u8,
221    pub frequency_id: u8,
222    pub pseudorange_residual: Length,
223    pub cno_dbhz: u8,
224    pub quality_ind: u8,
225    pub correction_source: u8,
226    pub iono_model: u8,
227    pub health: u8,
228    pub pseudorange_smoothed: bool,
229    pub pseudorange_used: bool,
230    pub carrier_used: bool,
231    pub doppler_used: bool,
232    pub pseudorange_correction_used: bool,
233    pub carrier_correction_used: bool,
234    pub doppler_correction_used: bool,
235}
236
237#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
238pub struct GnssSignalState {
239    pub itow_ms: u32,
240    pub signals: Vec<GnssSignalInfo>,
241}
242
243#[derive(
244    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
245)]
246pub struct GnssRfBlockStatus {
247    pub block_id: u8,
248    pub jamming_state: u8,
249    pub antenna_status: u8,
250    pub antenna_power: u8,
251    pub post_status: u32,
252    pub noise_per_ms: u16,
253    pub agc_count: u16,
254    pub cw_jam_indicator: u8,
255    pub i_imbalance: i8,
256    pub i_magnitude: u8,
257    pub q_imbalance: i8,
258    pub q_magnitude: u8,
259}
260
261#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
262pub struct GnssRfStatus {
263    pub blocks: Vec<GnssRfBlockStatus>,
264}
265
266#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
267pub struct GnssInfoText {
268    pub severity: GnssInfoSeverity,
269    pub text: String,
270}
271
272#[derive(
273    Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Reflect,
274)]
275pub struct GnssCommandAck {
276    pub kind: GnssAckKind,
277    pub class_id: u8,
278    pub msg_id: u8,
279}
280
281#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
282pub struct GnssRawUbxFrame {
283    pub class_id: u8,
284    pub msg_id: u8,
285    pub payload: Vec<u8>,
286}
287
288#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, Encode, Decode, Reflect)]
289pub enum GnssEvent {
290    #[default]
291    None,
292    NavEpoch(GnssNavEpoch),
293    SatelliteState(GnssSatelliteState),
294    SignalState(GnssSignalState),
295    RfStatus(GnssRfStatus),
296    InfoText(GnssInfoText),
297    CommandAck(GnssCommandAck),
298    RawUbx(GnssRawUbxFrame),
299}