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}