Skip to content

Commit a97521d

Browse files
authored
Improve Frequency and Observable support (#331)
Signed-off-by: Guillaume W. Bres <[email protected]>
1 parent 92f52af commit a97521d

File tree

6 files changed

+204
-27
lines changed

6 files changed

+204
-27
lines changed

src/carrier.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -168,32 +168,61 @@ impl std::str::FromStr for Carrier {
168168
}
169169

170170
impl Carrier {
171-
/// Returns frequency associated to this channel in MHz
171+
/// Returns [Carrier] frequency in Hz with 1kHz accuracy.
172172
pub fn frequency(&self) -> f64 {
173173
self.frequency_mega_hz() * 1.0E6
174174
}
175175

176+
/// Returns [Carrier] frequency in MHz with 1kHz accuracy.
176177
pub fn frequency_mega_hz(&self) -> f64 {
177178
match self {
178-
Self::L1 | Self::E1 | Self::B1c | Self::B1a => 1575.42_f64,
179-
Self::L2 => 1227.60_f64,
180-
Self::L5 | Self::E5a | Self::B2a => 1176.45_f64,
181-
Self::L6 => 1176.45_f64,
182-
Self::E6 => 1278.75_f64,
179+
Self::L1 | Self::E1 | Self::B1c | Self::B1a => 1575.420_f64,
180+
Self::L2 => 1227.600_f64,
181+
Self::L5 | Self::E5a | Self::B2a => 1176.450_f64,
182+
Self::L6 => 1176.450_f64,
183+
Self::E6 => 1278.750_f64,
183184
Self::E5b | Self::B2 | Self::B2b => 1207.140_f64,
184185
Self::E5a5b | Self::B2a2b => 1191.795_f64,
185186
Self::B1 => 1561.098_f64,
186-
Self::B3 | Self::B3a => 1268.52_f64,
187+
Self::B3 | Self::B3a => 1268.520_f64,
187188
Self::S => 2492.028_f64,
188189
Self::G1a => 1600.995_f64,
189-
Self::G1(None) => 1602.0_f64,
190-
Self::G1(Some(c)) => 1602.0_f64 + (*c as f64 * 9.0 / 16.0),
191-
Self::G2a => 1248.06_f64,
192-
Self::G2(None) => 1246.06_f64,
193-
Self::G2(Some(c)) => 1246.06_f64 + (*c as f64 * 7.0 / 16.0),
190+
Self::G1(None) => 1602.000_f64,
191+
Self::G1(Some(c)) => 1602.000_f64 + (*c as f64 * 9.0 / 16.0),
192+
Self::G2a => 1248.060_f64,
193+
Self::G2(None) => 1246.060_f64,
194+
Self::G2(Some(c)) => 1246.060_f64 + (*c as f64 * 7.0 / 16.0),
194195
Self::G3 => 1202.025_f64,
195-
Self::S1 => 2036.25,
196-
Self::U2 => 401.25,
196+
Self::S1 => 2036.250,
197+
Self::U2 => 401.250,
198+
}
199+
}
200+
201+
/// Builds [Constellation] [Carrier] frequency from value expressed in MHz.
202+
/// This requires a 1kHz accuracy over provided value.
203+
pub fn from_frequency_mega_hz(freq_mhz: f64) -> Result<Self, Error> {
204+
if freq_mhz.is_sign_negative() {
205+
return Err(Error::InvalidFrequency);
206+
}
207+
208+
let freq_khz = (freq_mhz * 1.0E3) as u32;
209+
210+
match freq_khz {
211+
1575_420 => Ok(Self::L1),
212+
1227_600 => Ok(Self::L2),
213+
1176_450 => Ok(Self::L5),
214+
1207_140 => Ok(Self::E5b),
215+
1191_795 => Ok(Self::E5a5b),
216+
1278_750 => Ok(Self::E6),
217+
1561_098 => Ok(Self::B1),
218+
1268_520 => Ok(Self::B3),
219+
2492_028 => Ok(Self::S),
220+
1602_000 => Ok(Self::G1(None)),
221+
1600_995 => Ok(Self::G1a),
222+
1246_000 => Ok(Self::G2(None)),
223+
1248_060 => Ok(Self::G2a),
224+
1202_025 => Ok(Self::G3),
225+
_ => Err(Error::UnknownFrequency),
197226
}
198227
}
199228

src/error.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ pub enum FormattingError {
188188
/// General error (processing, analysis..)
189189
#[derive(Debug)]
190190
pub enum Error {
191+
/// Invalid frequency
192+
InvalidFrequency,
193+
/// Unknown frequency
194+
UnknownFrequency,
191195
/// Non supported GPS [Observable]
192196
UnknownGPSObservable,
193197
/// Non supported Galileo [Observable]
@@ -204,4 +208,20 @@ pub enum Error {
204208
UnknownSBASObservable,
205209
/// Non supported DORIS [Observable]
206210
UnknownDORISObservable,
211+
/// Unknown GPS Frequency
212+
UnknownGPSFrequency,
213+
/// Unknown Galileo Frequency
214+
UnknownGalileoFrequency,
215+
/// Unknown QZSS Frequency
216+
UnknownQzssFrequency,
217+
/// Unknown Glonass Frequency
218+
UnknownGlonassFrequency,
219+
/// Unknown BDS Frequency
220+
UnknownBDSFrequency,
221+
/// Unknown IRNSS Frequency
222+
UnknownIRNSSFrequency,
223+
/// Unknown SBAS Frequency
224+
UnknownSBASFrequency,
225+
/// Unknown DORIS Frequency
226+
UnknownDORISFrequency,
207227
}

src/observable.rs

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,145 @@ impl Observable {
118118
}
119119
}
120120

121-
/// Tries to reconstruct a [Carrier] signal from this [Observable].
122-
/// This will work if our database knows this [Self::code].
123-
pub fn carrier(&self, c: Constellation) -> Result<Carrier, Error> {
121+
/// Tries to convert into [Carrier] frequency.
122+
pub fn to_carrier(&self, c: Constellation) -> Result<Carrier, Error> {
124123
Carrier::from_observable(c, self)
125124
}
126125

126+
/// Tries to create a Pseudo Range [Observable] from
127+
/// provided signal frequency in MHz and provided [Constellation].
128+
/// This requires a 1kHz accuracy on given frequency.
129+
pub fn from_pseudo_range_frequency_mega_hz(
130+
constellation: Constellation,
131+
freq_mhz: f64,
132+
) -> Result<Self, Error> {
133+
let carrier = Carrier::from_frequency_mega_hz(freq_mhz)?;
134+
135+
match constellation {
136+
Constellation::GPS => match carrier {
137+
Carrier::L1 => return Ok(Self::PseudoRange("C1C".to_string())),
138+
Carrier::L2 => return Ok(Self::PseudoRange("C2C".to_string())),
139+
Carrier::L5 => return Ok(Self::PseudoRange("C5X".to_string())),
140+
_ => return Err(Error::UnknownGPSFrequency),
141+
},
142+
Constellation::Galileo => match carrier {
143+
Carrier::L1 => return Ok(Self::PseudoRange("C1C".to_string())),
144+
Carrier::L5 => return Ok(Self::PseudoRange("C5X".to_string())),
145+
Carrier::E6 => return Ok(Self::PseudoRange("C6X".to_string())),
146+
Carrier::E5b => return Ok(Self::PseudoRange("C7X".to_string())),
147+
Carrier::E5a5b => return Ok(Self::PseudoRange("C8X".to_string())),
148+
_ => return Err(Error::UnknownGalileoFrequency),
149+
},
150+
Constellation::QZSS => match carrier {
151+
Carrier::L1 => return Ok(Self::PseudoRange("C1C".to_string())),
152+
Carrier::L2 => return Ok(Self::PseudoRange("C2X".to_string())),
153+
Carrier::L5 => return Ok(Self::PseudoRange("C5X".to_string())),
154+
Carrier::E6 => return Ok(Self::PseudoRange("C6X".to_string())),
155+
_ => return Err(Error::UnknownQzssFrequency),
156+
},
157+
Constellation::BeiDou => match carrier {
158+
Carrier::B1 => return Ok(Self::PseudoRange("C2X".to_string())),
159+
Carrier::L1 => return Ok(Self::PseudoRange("C1X".to_string())),
160+
Carrier::L5 => return Ok(Self::PseudoRange("C5X".to_string())),
161+
Carrier::E5b => return Ok(Self::PseudoRange("C7X".to_string())),
162+
Carrier::E5a5b => return Ok(Self::PseudoRange("C8X".to_string())),
163+
Carrier::B3 => return Ok(Self::PseudoRange("C6X".to_string())),
164+
_ => return Err(Error::UnknownBDSFrequency),
165+
},
166+
Constellation::Glonass => match carrier {
167+
Carrier::G1(_) => return Ok(Self::PseudoRange("C1C".to_string())),
168+
Carrier::G2(_) => return Ok(Self::PseudoRange("C2X".to_string())),
169+
Carrier::G3 => return Ok(Self::PseudoRange("C3X".to_string())),
170+
Carrier::G1a => return Ok(Self::PseudoRange("C4X".to_string())),
171+
Carrier::G2a => return Ok(Self::PseudoRange("C6X".to_string())),
172+
_ => return Err(Error::UnknownGlonassFrequency),
173+
},
174+
Constellation::IRNSS => match carrier {
175+
Carrier::L5 => return Ok(Self::PseudoRange("C5X".to_string())),
176+
Carrier::S => return Ok(Self::PseudoRange("C9X".to_string())),
177+
_ => return Err(Error::UnknownIRNSSFrequency),
178+
},
179+
_ => {},
180+
}
181+
182+
if constellation.is_sbas() {
183+
match carrier {
184+
Carrier::L1 => Ok(Self::PseudoRange("C1C".to_string())),
185+
Carrier::L5 => Ok(Self::PseudoRange("C5X".to_string())),
186+
_ => Err(Error::UnknownSBASFrequency),
187+
}
188+
} else {
189+
Err(Error::UnknownFrequency)
190+
}
191+
}
192+
193+
/// Tries to create a Phase Range [Observable] from
194+
/// provided signal frequency in MHz and provided [Constellation].
195+
/// This requires a 1kHz accuracy on given frequency.
196+
pub fn from_phase_range_frequency_mega_hz(
197+
constellation: Constellation,
198+
freq_mhz: f64,
199+
) -> Result<Self, Error> {
200+
let carrier = Carrier::from_frequency_mega_hz(freq_mhz)?;
201+
202+
match constellation {
203+
Constellation::GPS => match carrier {
204+
Carrier::L1 => return Ok(Self::PhaseRange("L1C".to_string())),
205+
Carrier::L2 => return Ok(Self::PhaseRange("L2C".to_string())),
206+
Carrier::L5 => return Ok(Self::PhaseRange("L5X".to_string())),
207+
_ => return Err(Error::UnknownGPSFrequency),
208+
},
209+
Constellation::Galileo => match carrier {
210+
Carrier::L1 => return Ok(Self::PhaseRange("L1C".to_string())),
211+
Carrier::L5 => return Ok(Self::PhaseRange("L5X".to_string())),
212+
Carrier::E6 => return Ok(Self::PhaseRange("L6X".to_string())),
213+
Carrier::E5b => return Ok(Self::PhaseRange("L7X".to_string())),
214+
Carrier::E5a5b => return Ok(Self::PhaseRange("L8X".to_string())),
215+
_ => return Err(Error::UnknownGalileoFrequency),
216+
},
217+
Constellation::QZSS => match carrier {
218+
Carrier::L1 => return Ok(Self::PhaseRange("L1C".to_string())),
219+
Carrier::L2 => return Ok(Self::PhaseRange("L2X".to_string())),
220+
Carrier::L5 => return Ok(Self::PhaseRange("L5X".to_string())),
221+
Carrier::E6 => return Ok(Self::PhaseRange("L6X".to_string())),
222+
_ => return Err(Error::UnknownQzssFrequency),
223+
},
224+
Constellation::BeiDou => match carrier {
225+
Carrier::B1 => return Ok(Self::PhaseRange("L2X".to_string())),
226+
Carrier::L1 => return Ok(Self::PhaseRange("L1X".to_string())),
227+
Carrier::L5 => return Ok(Self::PhaseRange("L5X".to_string())),
228+
Carrier::E5b => return Ok(Self::PhaseRange("L7X".to_string())),
229+
Carrier::E5a5b => return Ok(Self::PhaseRange("L8X".to_string())),
230+
Carrier::B3 => return Ok(Self::PhaseRange("L6X".to_string())),
231+
_ => return Err(Error::UnknownBDSFrequency),
232+
},
233+
Constellation::Glonass => match carrier {
234+
Carrier::G1(_) => return Ok(Self::PhaseRange("L1C".to_string())),
235+
Carrier::G2(_) => return Ok(Self::PhaseRange("L2X".to_string())),
236+
Carrier::G3 => return Ok(Self::PhaseRange("L3X".to_string())),
237+
Carrier::G1a => return Ok(Self::PhaseRange("L4X".to_string())),
238+
Carrier::G2a => return Ok(Self::PhaseRange("L6X".to_string())),
239+
_ => return Err(Error::UnknownGlonassFrequency),
240+
},
241+
Constellation::IRNSS => match carrier {
242+
Carrier::L5 => return Ok(Self::PhaseRange("L5X".to_string())),
243+
Carrier::S => return Ok(Self::PhaseRange("L9X".to_string())),
244+
_ => return Err(Error::UnknownIRNSSFrequency),
245+
},
246+
_ => {},
247+
}
248+
249+
if constellation.is_sbas() {
250+
match carrier {
251+
Carrier::L1 => Ok(Self::PhaseRange("L1C".to_string())),
252+
Carrier::L5 => Ok(Self::PhaseRange("L5X".to_string())),
253+
_ => Err(Error::UnknownSBASFrequency),
254+
}
255+
} else {
256+
Err(Error::UnknownFrequency)
257+
}
258+
}
259+
127260
/// Returns the code length (repetition period), expressed in seconds,
128261
/// of self: a valid Pseudo Range observable. This is not intended to be used
129262
/// on phase observables, although they are also determined from PRN codes.
@@ -313,7 +446,7 @@ impl Observable {
313446
/// used in signal combinations
314447
pub(crate) fn is_l1_pivot(&self, constellation: Constellation) -> bool {
315448
if self.is_phase_range_observable() || self.is_pseudo_range_observable() {
316-
if let Ok(carrier) = self.carrier(constellation) {
449+
if let Ok(carrier) = self.to_carrier(constellation) {
317450
matches!(
318451
carrier,
319452
Carrier::L1

src/observation/parsing.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,6 @@ fn parse_signals_v2(
384384
Ok(unsigned) => {
385385
lli = LliFlags::from_bits(unsigned);
386386
},
387-
#[cfg(feature = "log")]
388-
Err(e) => {
389-
//error!("parse_sig(v2) - lli: {}", e);
390-
},
391-
#[cfg(not(feature = "log"))]
392387
Err(_) => {},
393388
}
394389
}

src/observation/rinex/feature.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ impl Rinex {
312312

313313
let is_l1_pivot = v.observable.is_l1_pivot(v.sv.constellation);
314314

315-
let carrier = v.observable.carrier(v.sv.constellation);
315+
let carrier = v.observable.to_carrier(v.sv.constellation);
316316
if carrier.is_err() {
317317
continue;
318318
}
@@ -437,7 +437,7 @@ impl Rinex {
437437
for (k, v) in self.signal_observations_iter() {
438438
let is_ph = v.observable.is_phase_range_observable();
439439
let is_pr = v.observable.is_pseudo_range_observable();
440-
let carrier = v.observable.carrier(v.sv.constellation);
440+
let carrier = v.observable.to_carrier(v.sv.constellation);
441441

442442
if !is_ph && !is_pr || carrier.is_err() {
443443
continue;

src/observation/signal.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ impl SignalObservation {
8686

8787
let both_phase = phase_1 && phase_2;
8888

89-
let carrier_1 = self.observable.carrier(self.sv.constellation);
90-
let carrier_2 = rhs.observable.carrier(self.sv.constellation);
89+
let carrier_1 = self.observable.to_carrier(self.sv.constellation);
90+
let carrier_2 = rhs.observable.to_carrier(self.sv.constellation);
9191

9292
let both_ok = carrier_1.is_ok() && carrier_2.is_ok();
9393

0 commit comments

Comments
 (0)