gpsd_json/protocol/v3/
types.rs

1//! Common data types used in GPSD protocol v3 messages
2//!
3//! This module contains the shared data structures used across different
4//! GPSD message types. These include enumerations for fix modes, status codes,
5//! and complex types for representing GPS data.
6//!
7//! Most types correspond directly to structures defined in the GPSD C implementation.
8
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use serde_repr::Deserialize_repr;
12use serde_with::skip_serializing_none;
13
14/// GPS fix mode indicating the quality/dimension of the position fix
15///
16/// Reference: [gps_fix_t.mode](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L181)
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr)]
18#[repr(i32)]
19pub enum FixMode {
20    /// No GPS data has been seen yet
21    NotSeen = 0,
22    /// GPS is online but no fix acquired
23    NoFix = 1,
24    /// 2D fix (latitude/longitude only, no altitude)
25    Fix2D = 2,
26    /// 3D fix (full position including altitude)
27    Fix3D = 3,
28}
29
30/// GPS fix status indicating the positioning method and augmentation used
31///
32/// Reference: [gps_fix_t.status](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L192)
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr)]
34#[repr(i32)]
35pub enum FixStatus {
36    /// Unknown or no status information
37    Unknown = 0,
38    /// Standard GPS fix
39    Gps = 1,
40    /// Differential GPS (enhanced accuracy)
41    DGps = 2,
42    /// Real-Time Kinematic with fixed integers (centimeter accuracy)
43    RTKFixed = 3,
44    /// Real-Time Kinematic with float solution (decimeter accuracy)
45    RTKFloat = 4,
46    /// Dead reckoning (position estimated from sensors)
47    DR = 5,
48    /// GNSS combined with dead reckoning
49    GnssDR = 6,
50    /// Time-only fix (surveyed position, no navigation)
51    Time = 7,
52    /// Simulated/test data
53    Simulated = 8,
54    /// Precise Positioning Service
55    PpsFix = 9,
56}
57
58/// GPS antenna status
59///
60/// Indicates the electrical status of the GPS antenna connection.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr)]
62#[repr(i32)]
63pub enum AntennaStatus {
64    /// Status unknown or not reported
65    Unknown = 0,
66    /// Antenna connected and functioning normally
67    Ok = 1,
68    /// Open circuit detected (antenna disconnected)
69    Open = 2,
70    /// Short circuit detected in antenna connection
71    Short = 3,
72}
73
74/// Satellite signal quality indicator
75///
76/// Indicates the tracking status and signal quality for a satellite.
77///
78/// Reference: [satellite.qualityInd](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L2411)
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80pub enum SatQuality {
81    /// Invalid or no data
82    Invalid,
83    /// No signal detected from satellite
84    NoSignal,
85    /// Searching for satellite signal
86    Searching,
87    /// Signal acquired but not yet usable
88    Acquired,
89    /// Signal acquired but unusable (e.g., too weak)
90    Unusable,
91    /// Code lock achieved (coarse position)
92    CodeLocked,
93    /// Code and carrier lock (precise position)
94    CodeCarrierLocked,
95}
96
97impl<'de> Deserialize<'de> for SatQuality {
98    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99    where
100        D: serde::Deserializer<'de>,
101    {
102        let v = i8::deserialize(deserializer)?;
103        match v {
104            -1 => Ok(SatQuality::Invalid),
105            0 => Ok(SatQuality::NoSignal),
106            1 => Ok(SatQuality::Searching),
107            2 => Ok(SatQuality::Acquired),
108            3 => Ok(SatQuality::Unusable),
109            4 => Ok(SatQuality::CodeLocked),
110            5..=7 => Ok(SatQuality::CodeCarrierLocked),
111            _ => Err(serde::de::Error::custom(format!(
112                "invalid Satellite QualityInd value: {v}"
113            ))),
114        }
115    }
116}
117
118/// Global Navigation Satellite System identifier
119///
120/// Identifies which satellite constellation a satellite belongs to.
121///
122/// Reference: [satellite.gnssid](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L2449)
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr)]
124#[repr(u8)]
125pub enum GnssId {
126    /// GPS (USA)
127    Gps = 0,
128    /// Satellite-Based Augmentation System
129    Sbas = 1,
130    /// Galileo (European Union)
131    Gal = 2,
132    /// BeiDou (China)
133    Bd = 3,
134    /// IMES (Indoor Messaging System, Japan)
135    Imes = 4,
136    /// QZSS (Quasi-Zenith Satellite System, Japan)
137    Qzss = 5,
138    /// GLONASS (Russia)
139    Glo = 6,
140    /// IRNSS/NavIC (India)
141    Irnss = 7,
142}
143
144/// Satellite health status
145///
146/// Indicates whether a satellite's signals are reliable for navigation.
147///
148/// Reference: [satellite.health](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L2504)
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr)]
150#[repr(u8)]
151pub enum SatHealth {
152    /// Health status unknown
153    Unknown = 0,
154    /// Satellite is healthy and usable
155    Ok = 1,
156    /// Satellite is unhealthy, do not use
157    Bad = 2,
158}
159
160bitflags::bitflags! {
161    /// Device property flags
162    ///
163    /// Indicates what types of data have been seen from a GPS device.
164    /// These flags are set when GPSD detects specific data types.
165    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
166    pub struct PropertyFlags: u32 {
167        /// GPS data has been seen on this device
168        const SEEN_GPS = 0x01;
169        /// RTCM2 data has been seen on this device
170        const SEEN_RTCM2 = 0x02;
171        /// RTCM3 data has been seen on this device
172        const SEEN_RTCM3 = 0x04;
173        /// AIS data has been seen on this device
174        const SEEN_AIS = 0x08;
175    }
176}
177
178impl Serialize for PropertyFlags {
179    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
180    where
181        S: serde::Serializer,
182    {
183        serializer.serialize_u32(self.bits())
184    }
185}
186
187impl<'de> Deserialize<'de> for PropertyFlags {
188    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
189    where
190        D: serde::Deserializer<'de>,
191    {
192        let bits = u32::deserialize(deserializer)?;
193        Ok(PropertyFlags::from_bits_truncate(bits))
194    }
195}
196
197/// Serial port parity configuration
198///
199/// Defines the parity bit setting for serial communication with GPS devices.
200/// Used when configuring serial port parameters for GPS receivers.
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
202pub enum Parity {
203    /// No parity bit
204    No,
205    /// Odd parity
206    Odd,
207    /// Even parity
208    Even,
209}
210
211impl Serialize for Parity {
212    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
213    where
214        S: serde::Serializer,
215    {
216        let s = match self {
217            Parity::No => "N",
218            Parity::Odd => "O",
219            Parity::Even => "E",
220        };
221        serializer.serialize_str(s)
222    }
223}
224
225impl<'de> Deserialize<'de> for Parity {
226    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227    where
228        D: serde::Deserializer<'de>,
229    {
230        let v = String::deserialize(deserializer)?;
231        match v.as_str() {
232            "N" => Ok(Parity::No),
233            "O" => Ok(Parity::Odd),
234            "E" => Ok(Parity::Even),
235            _ => Err(serde::de::Error::custom(format!(
236                "invalid Parity value: {v}",
237            ))),
238        }
239    }
240}
241
242/// Status code for various sensor readings
243///
244/// Represents the status or alarm level for sensor readings,
245/// particularly used for magnetometer and other environmental sensors.
246#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247pub enum StatusCode {
248    /// magnetometer calibration alarm
249    Calibration,
250    /// low alarm
251    Low,
252    /// low warning
253    LowWarning,
254    /// normal
255    Normal,
256    /// high warning
257    HighWarning,
258    /// high alarm
259    High,
260    /// magnetometer voltage level alarm
261    VoltageLevel,
262}
263
264impl<'de> Deserialize<'de> for StatusCode {
265    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
266    where
267        D: serde::Deserializer<'de>,
268    {
269        let v = String::deserialize(deserializer)?;
270        match v.as_str() {
271            "C" => Ok(StatusCode::Calibration),
272            "L" => Ok(StatusCode::Low),
273            "M" => Ok(StatusCode::LowWarning),
274            "N" => Ok(StatusCode::Normal),
275            "O" => Ok(StatusCode::HighWarning),
276            "P" => Ok(StatusCode::High),
277            "V" => Ok(StatusCode::VoltageLevel),
278            _ => Err(serde::de::Error::custom(format!(
279                "invalid StatusCode value: {v}",
280            ))),
281        }
282    }
283}
284
285/// Earth-Centered, Earth-Fixed (ECEF) coordinates
286///
287/// Represents position and velocity in the ECEF coordinate system,
288/// where the origin is at Earth's center of mass.
289///
290/// Reference: [gps_fix_t.ecef](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L245)
291#[derive(Debug, Clone, PartialEq, Deserialize)]
292pub struct Ecef {
293    /// X coordinate in meters
294    #[serde(rename = "ecefx")]
295    pub x: Option<f64>,
296    /// Y coordinate in meters
297    #[serde(rename = "ecefy")]
298    pub y: Option<f64>,
299    /// Z coordinate in meters
300    #[serde(rename = "ecefz")]
301    pub z: Option<f64>,
302    /// Position accuracy in meters
303    #[serde(rename = "ecefpAcc")]
304    pub p_acc: Option<f64>,
305    /// X velocity in meters/second
306    #[serde(rename = "ecefvx")]
307    pub vx: Option<f64>,
308    /// Y velocity in meters/second
309    #[serde(rename = "ecefvy")]
310    pub vy: Option<f64>,
311    /// Z velocity in meters/second
312    #[serde(rename = "ecefvz")]
313    pub vz: Option<f64>,
314    /// Velocity accuracy in meters/second
315    #[serde(rename = "ecefvAcc")]
316    pub v_acc: Option<f64>,
317}
318
319/// North-East-Down (NED) coordinate system data
320///
321/// Represents position and velocity in the local tangent plane coordinate system.
322/// NED is a local coordinate system with origin at the receiver position.
323///
324/// Reference: [gps_fix_t.ned](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L252)
325#[derive(Debug, Clone, PartialEq, Deserialize)]
326pub struct Ned {
327    /// Relative position North in meters
328    #[serde(rename = "relN")]
329    pub rel_pos_n: Option<f64>,
330    /// Relative position East in meters
331    #[serde(rename = "relE")]
332    pub rel_pos_e: Option<f64>,
333    /// Relative position Down in meters
334    #[serde(rename = "relD")]
335    pub rel_pos_d: Option<f64>,
336    /// Relative horizontal position in meters
337    #[serde(rename = "relH")]
338    pub rel_pos_h: Option<f64>,
339    /// Relative position length in meters
340    #[serde(rename = "relL")]
341    pub rel_pos_l: Option<f64>,
342    /// Velocity North in meters/second
343    #[serde(rename = "velN")]
344    pub vel_n: Option<f64>,
345    /// Velocity East in meters/second
346    #[serde(rename = "velE")]
347    pub vel_e: Option<f64>,
348    /// Velocity Down in meters/second
349    #[serde(rename = "velD")]
350    pub vel_d: Option<f64>,
351}
352
353/// Dilution of Precision (DOP) values
354///
355/// DOP values indicate the quality of satellite geometry.
356/// Lower values indicate better precision.
357///
358/// Reference: [dop_t](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L2557)
359#[derive(Debug, Clone, PartialEq, Deserialize)]
360pub struct Dop {
361    /// Longitude dilution of precision
362    #[serde(rename = "xdop")]
363    pub x: Option<f64>,
364    /// Latitude dilution of precision
365    #[serde(rename = "ydop")]
366    pub y: Option<f64>,
367    /// Position (3D) dilution of precision
368    #[serde(rename = "pdop")]
369    pub p: Option<f64>,
370    /// Horizontal dilution of precision
371    #[serde(rename = "hdop")]
372    pub h: Option<f64>,
373    /// Vertical dilution of precision
374    #[serde(rename = "vdop")]
375    pub v: Option<f64>,
376    /// Time dilution of precision
377    #[serde(rename = "tdop")]
378    pub t: Option<f64>,
379    /// Geometric dilution of precision
380    #[serde(rename = "gdop")]
381    pub g: Option<f64>,
382}
383
384/// RTK baseline information
385///
386/// Contains data about the RTK base station and baseline vector.
387/// Used for high-precision positioning with RTK corrections.
388///
389/// Reference: [baseline_t](https://gitlab.com/gpsd/gpsd/-/blob/release-3.25/include/gps.h?ref_type=tags#L164)
390#[derive(Debug, Clone, PartialEq, Deserialize)]
391pub struct Baseline {
392    /// RTK solution status
393    #[serde(rename = "baseS")]
394    pub status: Option<FixStatus>,
395    /// Baseline East component in meters
396    #[serde(rename = "baseE")]
397    pub east: Option<f64>,
398    /// Baseline North component in meters
399    #[serde(rename = "baseN")]
400    pub north: Option<f64>,
401    /// Baseline Up component in meters
402    #[serde(rename = "baseU")]
403    pub up: Option<f64>,
404    /// Baseline length in meters
405    #[serde(rename = "baseL")]
406    pub length: Option<f64>,
407    /// Baseline course in degrees
408    #[serde(rename = "baseC")]
409    pub course: Option<f64>,
410    /// DGPS solution quality ratio
411    #[serde(rename = "dgpsRatio")]
412    pub ratio: Option<f64>,
413}
414
415/// Raw satellite measurement data
416///
417/// Contains raw observables from GPS receivers including
418/// pseudoranges, carrier phases, and signal quality metrics.
419///
420/// Reference: [json_attrs_meas](https://gitlab.com/gpsd/gpsd/-/blob/master/libgps/libgps_json.c#L226)
421#[derive(Debug, Clone, PartialEq, Deserialize)]
422pub struct Measurement {
423    /// GNSS system identifier
424    pub gnssid: Option<GnssId>,
425    /// Satellite ID within the GNSS
426    pub svid: Option<u8>,
427    /// Signal ID
428    pub sigid: Option<u8>,
429    /// Noise-to-signal ratio
430    pub nsr: Option<u8>,
431    /// Frequency channel ID (for GLONASS)
432    pub freqid: Option<u8>,
433    /// Observation code
434    pub obs: Option<String>,
435    /// Loss of Lock Indicator
436    pub lli: Option<u8>,
437    /// Carrier lock time in milliseconds
438    pub locktime: Option<u32>,
439    /// Carrier phase measurement in cycles
440    pub carrierphase: Option<f64>,
441    /// Pseudorange measurement in meters
442    pub pseudorange: Option<f64>,
443    /// Doppler frequency in Hz
444    pub doppler: Option<f64>,
445    /// Carrier-to-noise density ratio (dB-Hz)
446    pub c2c: Option<f64>,
447    /// L2C signal strength (dB-Hz)
448    pub l2c: Option<f64>,
449}
450
451/// Information about a single satellite
452///
453/// Contains tracking status, signal strength, and position data
454/// for an individual satellite.
455///
456/// Reference: [json_attrs_satellites](https://gitlab.com/gpsd/gpsd/-/blob/master/libgps/libgps_json.c?ref_type=heads#L295)
457#[derive(Debug, Clone, PartialEq, Deserialize)]
458pub struct Satellite {
459    /// Pseudo-Random Noise code (satellite identifier)
460    #[serde(rename = "PRN")]
461    pub prn: i16,
462    /// Azimuth angle in degrees (0-360)
463    #[serde(rename = "az")]
464    pub azimuth: Option<f64>,
465    /// Elevation angle in degrees (0-90)
466    #[serde(rename = "el")]
467    pub elevation: Option<f64>,
468    /// Frequency ID (for GLONASS)
469    pub freqid: Option<i8>,
470    /// GNSS system identifier
471    pub gnssid: Option<GnssId>,
472    /// Satellite health status
473    pub health: Option<SatHealth>,
474    /// Pseudorange in meters
475    pub pr: Option<f64>,
476    /// Pseudorange rate in meters/second
477    #[serde(rename = "prRate")]
478    pub pr_rate: Option<f64>,
479    /// Pseudorange residual in meters
480    #[serde(rename = "prRes")]
481    pub pr_res: Option<f64>,
482    /// Signal strength in dB-Hz
483    pub ss: Option<f64>,
484    /// Signal ID
485    pub sigid: Option<u8>,
486    /// Space vehicle ID
487    pub svid: Option<u8>,
488    /// Whether satellite is used in navigation solution
489    pub used: bool,
490    // Quality indicator (commented out in original)
491    // #[serde(rename = "qual")]
492    // pub quality: Option<SatQuality>,
493}
494
495/// GPS device configuration and status
496///
497/// Represents a GPS receiver device connected to GPSD,
498/// including its configuration parameters and current status.
499///
500/// Reference: [json_device_read](https://gitlab.com/gpsd/gpsd/-/blob/master/libgps/shared_json.c#L28)
501#[skip_serializing_none]
502#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
503pub struct Device {
504    /// Device path (e.g., "/dev/ttyUSB0")
505    pub path: Option<String>,
506    /// Timestamp when device was activated
507    pub activated: Option<DateTime<Utc>>,
508    /// Device capability flags
509    pub flags: Option<PropertyFlags>,
510    /// Driver name
511    pub driver: Option<String>,
512    /// Hex string for vendor/product or subtype
513    pub hexdata: Option<String>,
514    /// Serial number
515    pub sernum: Option<String>,
516    /// Device subtype
517    pub subtype: Option<String>,
518    /// Secondary device subtype
519    pub subtype1: Option<String>,
520    /// Native mode (0=native, 1=binary)
521    pub native: Option<i32>,
522    /// Serial port speed in bits per second
523    pub bps: Option<i32>,
524    /// Serial port parity
525    pub parity: Option<Parity>,
526    /// Number of stop bits
527    pub stopbits: Option<u32>,
528    /// Device cycle time in seconds
529    pub cycle: Option<f64>,
530    /// Minimum cycle time in seconds
531    pub mincycle: Option<f64>,
532}
533
534/// Watch mode configuration
535///
536/// Controls what data GPSD streams to the client and in what format.
537/// Used to enable/disable data streaming and configure output options.
538///
539/// Reference: [json_watch_read](https://gitlab.com/gpsd/gpsd/-/blob/master/libgps/shared_json.c#L95)
540#[skip_serializing_none]
541#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
542pub struct Watch {
543    /// Specific device to watch (or all if None)
544    pub device: Option<String>,
545    /// Enable/disable streaming
546    pub enable: Option<bool>,
547    /// Enable JSON output
548    pub json: Option<bool>,
549    /// Enable NMEA output
550    pub nmea: Option<bool>,
551    /// Enable PPS timing output
552    pub pps: Option<bool>,
553    /// Raw mode (0=off, 1=hex, 2=binary)
554    pub raw: Option<i32>,
555    /// Enable scaled output
556    pub scaled: Option<bool>,
557    /// Split AIS type 24 messages
558    pub split24: Option<bool>,
559    /// Enable timing information
560    pub timing: Option<bool>,
561    /// Remote server URL
562    pub remote: Option<String>,
563}
564
565impl Default for Watch {
566    fn default() -> Self {
567        Watch {
568            device: None,
569            enable: Some(false),
570            json: Some(false),
571            nmea: Some(false),
572            pps: Some(false),
573            raw: Some(0),
574            scaled: Some(false),
575            split24: Some(false),
576            timing: Some(false),
577            remote: None,
578        }
579    }
580}
581
582#[cfg(test)]
583mod tests {
584    use super::*;
585
586    #[test]
587    fn test_proto_v3_types_flags() {
588        let flags = PropertyFlags::SEEN_GPS | PropertyFlags::SEEN_AIS;
589        let serialized = serde_json::to_string(&flags).unwrap();
590        assert_eq!(serialized, "9");
591
592        let deserialized: PropertyFlags = serde_json::from_str(&serialized).unwrap();
593        assert_eq!(deserialized, flags);
594    }
595}