irox_carto/
gps.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5//!
6//! GPS Status Types, Satellite Signal, Fix Type, Dilution of Precision, etc
7
8extern crate alloc;
9use alloc::string::ToString;
10use core::fmt::{Display, Formatter};
11
12use irox_tools::format;
13use irox_tools::options::MaybeFrom;
14use irox_units::units::compass::Azimuth;
15
16use crate::coordinate::Elevation;
17
18#[derive(Copy, Clone, Debug, PartialEq)]
19pub struct SatelliteSignal {
20    pub prn: u8,
21    pub azimuth: Azimuth,
22    pub elevation: Elevation,
23    pub snr: u8,
24}
25
26impl Display for SatelliteSignal {
27    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
28        f.write_fmt(format_args!(
29            "PRN: {} Az: {} El: {}, SNR: {}",
30            self.prn, self.azimuth, self.elevation, self.snr
31        ))
32    }
33}
34
35/// GPS Fix Type
36#[derive(Copy, Clone, Debug, Default, PartialEq)]
37pub enum GPSFixType {
38    #[default]
39    Unknown = 0,
40    NoFix = 1,
41    TwoDim = 2,
42    ThreeDim = 3,
43}
44impl From<i32> for GPSFixType {
45    fn from(value: i32) -> Self {
46        match value {
47            1 => GPSFixType::NoFix,
48            2 => GPSFixType::TwoDim,
49            3 => GPSFixType::ThreeDim,
50            _ => GPSFixType::Unknown,
51        }
52    }
53}
54impl From<Option<&str>> for GPSFixType {
55    fn from(value: Option<&str>) -> Self {
56        if let Some(value) = value {
57            if let Ok(value) = value.parse::<i32>() {
58                return value.into();
59            }
60        }
61        GPSFixType::Unknown
62    }
63}
64
65#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Default)]
66pub struct DilutionOfPrecision(pub f64);
67impl From<f64> for DilutionOfPrecision {
68    fn from(value: f64) -> Self {
69        DilutionOfPrecision(value)
70    }
71}
72impl From<DilutionOfPrecision> for f64 {
73    fn from(value: DilutionOfPrecision) -> Self {
74        value.0
75    }
76}
77impl MaybeFrom<Option<f64>> for DilutionOfPrecision {
78    fn maybe_from(value: Option<f64>) -> Option<Self> {
79        Some(value?.into())
80    }
81}
82impl Display for DilutionOfPrecision {
83    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
84        f.write_fmt(format_args!("{}", self.0))
85    }
86}
87
88#[derive(Debug, Copy, Clone, PartialEq, Default)]
89pub struct DOPs {
90    pub geometric: Option<DilutionOfPrecision>,
91    pub horizontal: Option<DilutionOfPrecision>,
92    pub position: Option<DilutionOfPrecision>,
93    pub time: Option<DilutionOfPrecision>,
94    pub vertical: Option<DilutionOfPrecision>,
95}
96
97impl DOPs {
98    #[must_use]
99    pub fn new() -> DOPs {
100        Default::default()
101    }
102}
103
104impl Display for DOPs {
105    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
106        let print = |x: Option<DilutionOfPrecision>| match x {
107            Some(x) => format!("{:0.3}", x.0),
108            None => "?".to_string(),
109        };
110        write!(
111            f,
112            "hdop: {} vdop: {} pdop: {} gdop: {} tdop: {}",
113            print(self.horizontal),
114            print(self.vertical),
115            print(self.position),
116            print(self.geometric),
117            print(self.time)
118        )
119    }
120}
121
122#[cfg(all(target_os = "windows", feature = "windows"))]
123pub mod windows {
124    use windows::Devices::Geolocation::Geocoordinate;
125    use windows::Foundation::IReference;
126
127    use crate::gps::{DOPs, DilutionOfPrecision};
128
129    impl DOPs {
130        pub fn maybe_from(coord: &Geocoordinate) -> Option<DOPs> {
131            let Ok(sats) = coord.SatelliteData() else {
132                return None;
133            };
134
135            let get_dop = |v: IReference<f64>| -> Option<DilutionOfPrecision> {
136                v.GetDouble().ok().map(DilutionOfPrecision)
137            };
138            let geometric = sats.GeometricDilutionOfPrecision().ok().and_then(get_dop);
139            let horizontal = sats.HorizontalDilutionOfPrecision().ok().and_then(get_dop);
140            let position = sats.PositionDilutionOfPrecision().ok().and_then(get_dop);
141            let time = sats.TimeDilutionOfPrecision().ok().and_then(get_dop);
142            let vertical = sats.VerticalDilutionOfPrecision().ok().and_then(get_dop);
143
144            Some(DOPs {
145                geometric,
146                horizontal,
147                position,
148                time,
149                vertical,
150            })
151        }
152    }
153}