gnss_rtk/cfg/
mod.rs

1use thiserror::Error;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6mod method;
7mod modeling;
8mod solver;
9
10pub use crate::{
11    carrier::Signal,
12    cfg::solver::SolverOpts,
13    cfg::{method::Method, modeling::Modeling},
14    prelude::TimeScale,
15};
16
17/// Configuration Error
18#[derive(Debug, Error)]
19pub enum Error {
20    #[error("invalid troposphere model")]
21    InvalidTroposphereModel,
22
23    #[error("invalid user profile")]
24    InvalidUserProfile,
25
26    #[error("invalid clock profile")]
27    InvalidClockProfile,
28}
29
30const fn default_timescale() -> TimeScale {
31    TimeScale::GPST
32}
33
34const fn max_tropo_bias() -> f64 {
35    30.0
36}
37
38const fn max_iono_bias() -> f64 {
39    10.0
40}
41
42const fn min_sv_elev() -> Option<f64> {
43    Some(12.5)
44}
45
46const fn default_code_smoothing() -> usize {
47    0
48}
49
50const fn default_eclipse_rate_percent() -> f64 {
51    10.0
52}
53
54#[derive(Default, Debug, Clone, PartialEq)]
55#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56/// System Internal Delay as defined by BIPM in
57/// "GPS Receivers Accurate Time Comparison" : the (frequency dependent)
58/// time delay introduced by the combination of:
59///  + the RF cable (up to several nanoseconds)
60///  + the distance between the antenna baseline and its APC:
61///    a couple picoseconds, and is frequency dependent
62///  + the GNSS receiver inner delay (hardware and frequency dependent)
63pub struct InternalDelay {
64    /// Delay [s]
65    pub delay: f64,
66
67    /// Carrier frequency [Hz]
68    pub frequency: f64,
69}
70
71#[derive(Debug, Clone)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73pub struct Config {
74    /// Time scale in which we express the PVT solutions,
75    /// [TimeScale::GPST] is the default value.
76    #[cfg_attr(feature = "serde", serde(default = "default_timescale"))]
77    pub timescale: TimeScale,
78
79    /// Navigation [Method] (technique) to be used.
80    #[cfg_attr(feature = "serde", serde(default))]
81    pub method: Method,
82
83    /// Select a prefered signal.
84    /// When defined, this signal will strictly be used in the navigation process.
85    /// When undefined, the algorithm will prefer the best SNR available, and the
86    /// signal frequency being used might change.
87    /// When [Method] is [Method::SPP] this should be a single frequency.
88    /// When [Method] is not [Method::SPP] and [Modeling] enables Ionospheric
89    /// bias compensation,
90    #[cfg_attr(feature = "serde", serde(default))]
91    pub prefered_signal: Option<Signal>,
92
93    /// Fixed altitude: reduces the need of 4 to 3 SV to obtain 3D solutions.
94    #[cfg_attr(feature = "serde", serde(default))]
95    pub fixed_altitude: Option<f64>,
96
97    /// Pseudo Range code smoothing (window length).
98    /// Use phase observatoins to smooth and reduce error in the pseudo range code.
99    /// This has no effect if phase observations are missing.
100    /// Set to 0 to disable this feature completely.
101    /// When parametrizing, think in terms of window duration versus Ionospheric activity.
102    #[cfg_attr(feature = "serde", serde(default = "default_code_smoothing"))]
103    pub code_smoothing: usize,
104
105    /// Internal delays to compensate for (total summation, in [s]).
106    /// Compensation is only effective if [Modeling.cable_delay]
107    /// is also turned on.
108    #[cfg_attr(feature = "serde", serde(default))]
109    pub int_delay: Vec<InternalDelay>,
110
111    /// Antenna Reference Point (ARP) expressed as ENU offset [m]
112    #[cfg_attr(feature = "serde", serde(default))]
113    pub arp_enu: Option<(f64, f64, f64)>,
114
115    /// Solver customization
116    #[cfg_attr(feature = "serde", serde(default))]
117    pub solver: SolverOpts,
118
119    /// Time Reference Delay. According to BIPM ""GPS Receivers Accurate Time Comparison""
120    /// this is the time delay between the receiver external reference clock
121    /// and the internal sampling clock. This is typically needed in
122    /// precision timing applications.
123    #[cfg_attr(feature = "serde", serde(default))]
124    pub externalref_delay_s: Option<f64>,
125
126    /// Maximal Earth / Sun occultation tolerated for each satellite.
127    /// For example, 20.0% means that we require satellites to be 80% illmuinated.
128    /// 10.0% is our default value.
129    #[cfg_attr(
130        feature = "serde",
131        serde(alias = "max_eclipse_rate", default = "default_eclipse_rate_percent")
132    )]
133    pub max_eclipse_rate_percent: f64,
134
135    /// Minimal SV elevation angle for an SV to contribute to the solution.
136    /// Use this as a simple quality criteria.
137    #[cfg_attr(feature = "serde", serde(default = "min_sv_elev"))]
138    pub min_sv_elev: Option<f64>,
139
140    /// Minimal SV Azimuth angle for an SV to contribute to the solution.
141    /// SV below that angle will not be considered.
142    /// Use this is in special navigation scenarios.
143    #[cfg_attr(feature = "serde", serde(default))]
144    pub min_sv_azim: Option<f64>,
145
146    /// Maximal SV Azimuth angle for an SV to contribute to the solution.
147    /// SV below that angle will not be considered.
148    /// Use this is in special navigation scenarios.
149    #[cfg_attr(feature = "serde", serde(default))]
150    pub max_sv_azim: Option<f64>,
151
152    /// Minimal SNR for an SV to contribute to the solution.
153    #[cfg_attr(feature = "serde", serde(default))]
154    pub min_snr: Option<f64>,
155
156    /// Maximal tropo bias that we tolerate (in [m]).
157    /// Has no effect if modeling.tropo_delay is disabled.
158    #[cfg_attr(feature = "serde", serde(default = "max_tropo_bias"))]
159    pub max_tropo_bias: f64,
160
161    /// Maximal iono bias that we tolerate (in [m]).
162    /// Has no effect if modeling.iono_delay is disabled.
163    #[cfg_attr(feature = "serde", serde(default = "max_iono_bias"))]
164    pub max_iono_bias: f64,
165
166    /// Atmospherical and Physical [Modeling] used to improve the accuracy of solution.
167    #[cfg_attr(feature = "serde", serde(default))]
168    pub modeling: Modeling,
169}
170
171impl Default for Config {
172    fn default() -> Self {
173        Self {
174            timescale: default_timescale(),
175            method: Method::default(),
176            solver: SolverOpts::default(),
177            int_delay: Default::default(),
178            modeling: Modeling::default(),
179            fixed_altitude: None,
180            prefered_signal: None,
181            arp_enu: None,
182            min_snr: None, // TODO
183            min_sv_azim: None,
184            max_sv_azim: None,
185            externalref_delay_s: None,
186            min_sv_elev: min_sv_elev(),
187            max_iono_bias: max_iono_bias(),
188            max_tropo_bias: max_tropo_bias(),
189            code_smoothing: default_code_smoothing(),
190            max_eclipse_rate_percent: default_eclipse_rate_percent(),
191        }
192    }
193}
194
195impl Config {
196    /// Returns new [Config] with desired navigation [Method]
197    pub fn with_navigation_method(&self, method: Method) -> Self {
198        let mut s = self.clone();
199        s.method = method;
200        s
201    }
202
203    /// Copies and returns [Config] with desired [Modeling], that you can
204    /// tune to improve the accuracy of your solution, or for learning purposes.
205    pub fn with_modeling(&self, modeling: Modeling) -> Self {
206        let mut s = self.clone();
207        s.modeling = modeling;
208        s
209    }
210}
211
212#[cfg(test)]
213#[cfg(feature = "serde")]
214mod test {
215    use super::*;
216    use std::io::Write;
217
218    #[test]
219    fn generate_default_preset() {
220        let cfg = Config::default();
221        let string = serde_json::to_string_pretty(&cfg).unwrap();
222        let mut fd = std::fs::File::create("default.json").unwrap();
223        write!(fd, "{}", string).unwrap();
224    }
225}