rinex/navigation/rinex/feature.rs
1use crate::{
2 navigation::{BdModel, Ephemeris, IonosphereModel, KbModel, NavKey, NgModel},
3 prelude::{
4 nav::{Almanac, AzElRange, Orbit},
5 Epoch, Rinex, SV,
6 },
7};
8
9impl Rinex {
10 /// [SV] orbital state vector determination attempt, that only applies
11 /// to Navigation [Rinex].
12 /// ## Inputs
13 /// - sv: desired [SV]
14 /// - t: desired [Epoch] to express the [Orbit]al state
15 /// ## Returns
16 /// - orbital state: expressed as ECEF [Orbit]
17 pub fn sv_orbit(&self, sv: SV, t: Epoch) -> Option<Orbit> {
18 let (_, _, eph) = self.nav_ephemeris_selection(sv, t)?;
19 eph.kepler2position(sv, t)
20 }
21
22 /// [SV] (azimuth, elevation, slant range) triplet determination,
23 /// that only applies to Navigation [Rinex].
24 /// ## Inputs
25 /// - sv: target [SV]
26 /// - t: target [Epoch]
27 /// - rx_orbit: RX position expressed as an [Orbit]
28 /// - almanac: [Almanac] context
29 /// ## Returns
30 /// - [AzElRange] on calculations success
31 pub fn nav_azimuth_elevation_range(
32 &self,
33 sv: SV,
34 t: Epoch,
35 rx_orbit: Orbit,
36 almanac: &Almanac,
37 ) -> Option<AzElRange> {
38 let sv_orbit = self.sv_orbit(sv, t)?;
39 let azelrange = almanac
40 .azimuth_elevation_range_sez(sv_orbit, rx_orbit, None, None)
41 .ok()?;
42 Some(azelrange)
43 }
44
45 /// Ephemeris selection, that only applies to Navigation [Rinex].
46 /// ## Inputs
47 /// - sv: desired [SV]
48 /// - epoch: desired [Epoch]
49 /// ## Returns
50 /// - (toc, toe, [Ephemeris]) triplet if an [Ephemeris] message
51 /// was decoded in the correct time frame.
52 /// Note that `ToE` does not exist for GEO/SBAS [SV], so `ToC` is simply
53 /// copied in this case, to maintain the API.
54 pub fn nav_ephemeris_selection(&self, sv: SV, t: Epoch) -> Option<(Epoch, Epoch, &Ephemeris)> {
55 if sv.constellation.is_sbas() {
56 self.nav_ephemeris_frames_iter()
57 .filter_map(|(k, eph)| {
58 if k.sv == sv {
59 Some((k.epoch, k.epoch, eph))
60 } else {
61 None
62 }
63 })
64 .min_by_key(|(toc, _, _)| t - *toc)
65 } else {
66 self.nav_ephemeris_frames_iter()
67 .filter_map(|(k, eph)| {
68 if k.sv == sv {
69 if eph.is_valid(sv, t) {
70 if let Some(toe) = eph.toe(k.sv) {
71 Some((k.epoch, toe, eph))
72 } else {
73 None
74 }
75 } else {
76 None
77 }
78 } else {
79 None
80 }
81 })
82 .min_by_key(|(_, toe, _)| (t - *toe).abs())
83 }
84 }
85
86 /// Klobuchar [KbModel] Ionosphere model [Iterator].
87 /// RINEX V4 is the true application of this, as it provides
88 /// regular model updates (reflecting radio message stream).
89 /// Klobuchar Ionosphere models exist in RINEX2 and this
90 /// method applies similarly.
91 pub fn nav_klobuchar_models_iter(&self) -> Box<dyn Iterator<Item = (&NavKey, &KbModel)> + '_> {
92 Box::new(
93 self.nav_ionosphere_models_iter()
94 .filter_map(|(k, v)| match v {
95 IonosphereModel::Klobuchar(model) => Some((k, model)),
96 _ => None,
97 }),
98 )
99 }
100
101 /// BDGIM [BdModel] Ionosphere model [Iterator].
102 /// Refer to [Self::nav_klobuchar_models_iter] for similar examples.
103 pub fn nav_bdgim_models_iter(&self) -> Box<dyn Iterator<Item = (&NavKey, &BdModel)> + '_> {
104 Box::new(
105 self.nav_ionosphere_models_iter()
106 .filter_map(|(k, v)| match v {
107 IonosphereModel::Bdgim(model) => Some((k, model)),
108 _ => None,
109 }),
110 )
111 }
112
113 /// Nequick-G [NgModel] Ionosphere model [Iterator].
114 /// Refer to [Self::nav_klobuchar_models_iter] for similar examples.
115 pub fn nav_nequickg_models_iter(&self) -> Box<dyn Iterator<Item = (&NavKey, &NgModel)> + '_> {
116 Box::new(
117 self.nav_ionosphere_models_iter()
118 .filter_map(|(k, v)| match v {
119 IonosphereModel::NequickG(model) => Some((k, model)),
120 _ => None,
121 }),
122 )
123 }
124}