gnss_rs/
sbas.rs

1//! Geostationary augmentation systems
2#[cfg(feature = "sbas")]
3use crate::prelude::Constellation;
4
5//#[cfg(feature = "serde")]
6//use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "sbas")]
9use geo::{point, Contains, LineString};
10#[cfg(feature = "sbas")]
11use std::iter::FromIterator;
12#[cfg(feature = "sbas")]
13use std::str::FromStr;
14#[cfg(feature = "sbas")]
15use wkt::{Geometry, Wkt, WktFloat};
16
17#[cfg(feature = "sbas")]
18fn wkt_line_string_to_geo<T>(line_string: &wkt::types::LineString<T>) -> LineString<T>
19where
20    T: WktFloat + Default + FromStr,
21{
22    LineString::from_iter(line_string.0.iter().map(|coord| (coord.x, coord.y)))
23}
24
25#[cfg(feature = "sbas")]
26fn line_string<T>(name: &str) -> LineString<T>
27where
28    T: WktFloat + Default + FromStr,
29{
30    let mut res = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
31    res.push("data");
32    res.push(name);
33    let content = std::fs::read_to_string(res).unwrap();
34    let wkt = Wkt::from_str(&content).unwrap();
35    match wkt.item {
36        Geometry::LineString(line) => wkt_line_string_to_geo(&line),
37        _ => unreachable!(),
38    }
39}
40
41#[cfg(feature = "sbas")]
42fn load_database() -> Vec<(Constellation, geo::Polygon)> {
43    let mut db: Vec<(Constellation, geo::Polygon)> = Vec::new();
44    let db_path = env!("CARGO_MANIFEST_DIR").to_owned() + "/data/";
45    let db_path = std::path::PathBuf::from(db_path);
46    for entry in std::fs::read_dir(db_path).unwrap() {
47        let entry = entry.unwrap();
48        let path = entry.path();
49        let fullpath = &path.to_str().unwrap();
50        let extension = path.extension().unwrap().to_str().unwrap();
51        let name = path.file_stem().unwrap().to_str().unwrap();
52        if extension.eq("wkt") {
53            let poly = geo::Polygon::<f64>::new(
54                line_string(fullpath), // exterior boundaries
55                vec![],
56            ); // dont care about interior
57            if let Ok(sbas) = Constellation::from_str(&name.to_uppercase()) {
58                db.push((sbas, poly))
59            }
60        }
61    }
62    db
63}
64
65#[cfg(feature = "sbas")]
66#[cfg_attr(docsrs, doc(cfg(feature = "sbas")))]
67/// Select an augmentation system conveniently, based on given location
68/// in decimal degrees
69/// ```
70/// extern crate gnss_rs as gnss;
71/// use gnss::prelude::*;
72/// use gnss::sbas_selection;
73///
74/// let paris = (48.808378, 2.382682); // lat, lon [ddeg]
75/// let sbas = sbas_selection(paris.0, paris.1);
76/// assert_eq!(sbas, Some(Constellation::EGNOS));
77///
78/// let antartica = (-77.490631,  91.435181); // lat, lon [ddeg]
79/// let sbas = sbas_selection(antartica.0, antartica.1);
80/// assert_eq!(sbas.is_none(), true);
81///```
82pub fn sbas_selection(lat: f64, lon: f64) -> Option<Constellation> {
83    let db = load_database();
84    let point: geo::Point<f64> = point!(x: lon, y: lat,);
85    for (sbas, area) in db {
86        if area.contains(&point) {
87            return Some(sbas.clone());
88        }
89    }
90    None
91}
92
93#[cfg(feature = "sbas")]
94#[cfg(test)]
95mod test {
96    use super::*;
97    #[test]
98    #[cfg(feature = "sbas")]
99    fn sbas_helper() {
100        // PARIS --> EGNOS
101        let sbas = sbas_selection(48.808378, 2.382682);
102        assert_eq!(sbas.is_some(), true);
103        assert_eq!(sbas.unwrap(), Constellation::EGNOS);
104
105        // ANTARICA --> NONE
106        let sbas = sbas_selection(-77.490631, 91.435181);
107        assert_eq!(sbas.is_none(), true);
108
109        // LOS ANGELES --> WAAS
110        let sbas = sbas_selection(33.981431, -118.193601);
111        assert_eq!(sbas.is_some(), true);
112        assert_eq!(sbas.unwrap(), Constellation::WAAS);
113
114        // ARGENTINA --> NONE
115        let sbas = sbas_selection(-23.216639, -63.170983);
116        assert_eq!(sbas.is_none(), true);
117
118        // NIGER --> ASBAS
119        let sbas = sbas_selection(10.714217, 17.087263);
120        assert_eq!(sbas.is_some(), true);
121        assert_eq!(sbas.unwrap(), Constellation::ASBAS);
122
123        // South AFRICA --> None
124        let sbas = sbas_selection(-32.473320, 21.112770);
125        assert_eq!(sbas.is_none(), true);
126
127        // India --> GAGAN
128        let sbas = sbas_selection(19.314290, 76.798953);
129        assert_eq!(sbas.is_some(), true);
130        assert_eq!(sbas.unwrap(), Constellation::GAGAN);
131
132        // South Indian Ocean --> None
133        let sbas = sbas_selection(-29.349172, 72.773447);
134        assert_eq!(sbas.is_none(), true);
135
136        // Australia --> SPAN
137        let sbas = sbas_selection(-27.579847, 131.334992);
138        assert_eq!(sbas.is_some(), true);
139        assert_eq!(sbas.unwrap(), Constellation::SPAN);
140        // NZ --> SPAN
141        let sbas = sbas_selection(-45.113525, 169.864842);
142        assert_eq!(sbas.is_some(), true);
143        assert_eq!(sbas.unwrap(), Constellation::SPAN);
144
145        // Central China: BDSBAS
146        let sbas = sbas_selection(34.462967, 98.172480);
147        assert_eq!(sbas, Some(Constellation::BDSBAS));
148
149        // South Korea: KASS
150        let sbas = sbas_selection(37.067846, 128.34);
151        assert_eq!(sbas, Some(Constellation::KASS));
152
153        // Japan: MSAS
154        let sbas = sbas_selection(36.081095, 138.274859);
155        assert_eq!(sbas, Some(Constellation::MSAS));
156
157        // Russia: SDCM
158        let sbas = sbas_selection(60.004390, 89.090326);
159        assert_eq!(sbas, Some(Constellation::SDCM));
160    }
161}