Skip to content

Commit c5c2425

Browse files
authored
Add proper errors (#37)
1 parent d5a024b commit c5c2425

File tree

6 files changed

+554
-240
lines changed

6 files changed

+554
-240
lines changed

build.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ fn main() {
6666
.whitelist_function("is_gal")
6767
.whitelist_function("is_qzss")
6868
.whitelist_function("sid_to_constellation")
69+
.whitelist_function("sid_valid")
6970
.whitelist_function("code_to_constellation")
7071
.whitelist_function("constellation_to_sat_count")
7172
.whitelist_function("constellation_to_string")
@@ -75,6 +76,18 @@ fn main() {
7576
.whitelist_function("sid_to_carr_freq")
7677
.whitelist_function("code_string_to_enum")
7778
.whitelist_function("code_to_string")
79+
.whitelist_var("NUM_SATS_GPS")
80+
.whitelist_var("NUM_SATS_SBAS")
81+
.whitelist_var("NUM_SATS_GLO")
82+
.whitelist_var("NUM_SATS_BDS")
83+
.whitelist_var("NUM_SATS_GAL")
84+
.whitelist_var("NUM_SATS_QZS")
85+
.whitelist_var("GPS_FIRST_PRN")
86+
.whitelist_var("SBAS_FIRST_PRN")
87+
.whitelist_var("GLO_FIRST_PRN")
88+
.whitelist_var("BDS_FIRST_PRN")
89+
.whitelist_var("GAL_FIRST_PRN")
90+
.whitelist_var("QZS_FIRST_PRN")
7891
.whitelist_function("llhrad2deg")
7992
.whitelist_function("llhdeg2rad")
8093
.whitelist_function("wgsllh2ecef")
@@ -89,6 +102,7 @@ fn main() {
89102
.whitelist_function("calc_sat_az_el")
90103
.whitelist_function("calc_sat_doppler")
91104
.whitelist_function("get_ephemeris_status_t")
105+
.whitelist_function("ephemeris_valid_detailed")
92106
.whitelist_function("ephemeris_valid")
93107
.whitelist_function("ephemeris_equal")
94108
.whitelist_function("ephemeris_healthy")

src/ephemeris.rs

Lines changed: 95 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,19 @@
1111
use crate::{
1212
c_bindings,
1313
coords::{AzimuthElevation, ECEF},
14-
signal::{Code, Constellation, GnssSignal},
14+
signal::{Code, Constellation, GnssSignal, InvalidGnssSignal},
1515
time::GpsTime,
1616
};
17-
use std::fmt::{Display, Formatter};
17+
use std::error::Error;
18+
use std::fmt;
1819

1920
/// Number of bytes in the Galileo INAV message
2021
// TODO(jbangelo) bindgen doesn't catch this variable on linux for some reason
2122
pub const GAL_INAV_CONTENT_BYTE: usize = (128 + 8 - 1) / 8;
2223

23-
/// An error indicating that the ephemeris is invalid
24-
#[derive(Copy, Clone, Debug)]
25-
pub struct InvalidEphemeris {}
26-
27-
impl Display for InvalidEphemeris {
28-
fn fmt(&self, f: &mut Formatter) -> std::result::Result<(), std::fmt::Error> {
29-
write!(f, "Invalid Ephemeris")
30-
}
31-
}
32-
33-
impl std::error::Error for InvalidEphemeris {}
34-
35-
type Result<T> = std::result::Result<T, InvalidEphemeris>;
36-
37-
/// Various statuses that an ephemeris can be in
38-
#[derive(Copy, Clone, Debug)]
39-
pub enum Status {
24+
/// Different ways an ephemeris can be invalid
25+
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
26+
pub enum InvalidEphemeris {
4027
Null,
4128
Invalid,
4229
WnEqualsZero,
@@ -45,22 +32,55 @@ pub enum Status {
4532
TooOld,
4633
InvalidSid,
4734
InvalidIod,
35+
}
36+
37+
impl fmt::Display for InvalidEphemeris {
38+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
write!(f, "Invalid ephemeris ({:?})", self)
40+
}
41+
}
42+
43+
impl Error for InvalidEphemeris {}
44+
45+
/// Various statuses that an ephemeris can be in
46+
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
47+
pub enum Status {
48+
Invalid(InvalidEphemeris),
4849
Valid,
4950
}
5051

5152
impl Status {
5253
fn from_ephemeris_status_t(value: c_bindings::ephemeris_status_t) -> Status {
5354
match value {
54-
c_bindings::ephemeris_status_t_EPH_NULL => Status::Null,
55-
c_bindings::ephemeris_status_t_EPH_INVALID => Status::Invalid,
56-
c_bindings::ephemeris_status_t_EPH_WN_EQ_0 => Status::WnEqualsZero,
57-
c_bindings::ephemeris_status_t_EPH_FIT_INTERVAL_EQ_0 => Status::FitIntervalEqualsZero,
58-
c_bindings::ephemeris_status_t_EPH_UNHEALTHY => Status::Unhealthy,
59-
c_bindings::ephemeris_status_t_EPH_TOO_OLD => Status::TooOld,
55+
c_bindings::ephemeris_status_t_EPH_NULL => Status::Invalid(InvalidEphemeris::Null),
56+
c_bindings::ephemeris_status_t_EPH_INVALID => {
57+
Status::Invalid(InvalidEphemeris::Invalid)
58+
}
59+
c_bindings::ephemeris_status_t_EPH_WN_EQ_0 => {
60+
Status::Invalid(InvalidEphemeris::WnEqualsZero)
61+
}
62+
c_bindings::ephemeris_status_t_EPH_FIT_INTERVAL_EQ_0 => {
63+
Status::Invalid(InvalidEphemeris::FitIntervalEqualsZero)
64+
}
65+
c_bindings::ephemeris_status_t_EPH_UNHEALTHY => {
66+
Status::Invalid(InvalidEphemeris::Unhealthy)
67+
}
68+
c_bindings::ephemeris_status_t_EPH_TOO_OLD => Status::Invalid(InvalidEphemeris::TooOld),
6069
c_bindings::ephemeris_status_t_EPH_VALID => Status::Valid,
6170
_ => panic!("Invalid ephemeris_status_t value: {}", value),
6271
}
6372
}
73+
74+
/// Converts a `Status` into a Result.
75+
///
76+
/// A valid status is represented by the empty `Ok` variant and an invalid status
77+
/// is represented by the `Err` variant.
78+
pub fn to_result(self) -> Result<(), InvalidEphemeris> {
79+
match self {
80+
Status::Valid => Ok(()),
81+
Status::Invalid(invalid_status) => Err(invalid_status),
82+
}
83+
}
6484
}
6585

6686
/// Orbital terms of an ephemeris
@@ -265,7 +285,10 @@ impl Ephemeris {
265285
}
266286

267287
/// Calculate satellite position, velocity and clock offset from ephemeris.
268-
pub fn calc_satellite_state(&self, t: GpsTime) -> Result<SatelliteState> {
288+
pub fn calc_satellite_state(&self, t: GpsTime) -> Result<SatelliteState, InvalidEphemeris> {
289+
// First make sure the ephemeris is valid at `t`, and bail early if it isn't
290+
self.get_detailed_status(t).to_result()?;
291+
269292
let mut sat = SatelliteState {
270293
pos: ECEF::default(),
271294
vel: ECEF::default(),
@@ -288,16 +311,20 @@ impl Ephemeris {
288311
)
289312
};
290313

291-
if result == 0 {
292-
Ok(sat)
293-
} else {
294-
Err(InvalidEphemeris {})
295-
}
314+
assert_eq!(result, 0);
315+
Ok(sat)
296316
}
297317

298318
/// Calculate the azimuth and elevation of a satellite from a reference
299319
/// position given the satellite ephemeris.
300-
pub fn calc_satellite_az_el(&self, t: &GpsTime, pos: &ECEF) -> Result<AzimuthElevation> {
320+
pub fn calc_satellite_az_el(
321+
&self,
322+
t: GpsTime,
323+
pos: ECEF,
324+
) -> Result<AzimuthElevation, InvalidEphemeris> {
325+
// First make sure the ephemeris is valid at `t`, and bail early if it isn't
326+
self.get_detailed_status(t).to_result()?;
327+
301328
let mut sat = AzimuthElevation::default();
302329

303330
let result = unsafe {
@@ -312,16 +339,21 @@ impl Ephemeris {
312339
)
313340
};
314341

315-
if result == 0 {
316-
Ok(sat)
317-
} else {
318-
Err(InvalidEphemeris {})
319-
}
342+
assert_eq!(result, 0);
343+
Ok(sat)
320344
}
321345

322346
/// Calculate the Doppler shift of a satellite as observed at a reference
323347
/// position given the satellite ephemeris.
324-
pub fn calc_satellite_doppler(&self, t: &GpsTime, pos: &ECEF, vel: &ECEF) -> Result<f64> {
348+
pub fn calc_satellite_doppler(
349+
&self,
350+
t: GpsTime,
351+
pos: ECEF,
352+
vel: ECEF,
353+
) -> Result<f64, InvalidEphemeris> {
354+
// First make sure the ephemeris is valid at `t`, and bail early if it isn't
355+
self.get_detailed_status(t).to_result()?;
356+
325357
let mut doppler = 0.0;
326358

327359
let result = unsafe {
@@ -335,15 +367,12 @@ impl Ephemeris {
335367
)
336368
};
337369

338-
if result == 0 {
339-
Ok(doppler)
340-
} else {
341-
Err(InvalidEphemeris {})
342-
}
370+
assert_eq!(result, 0);
371+
Ok(doppler)
343372
}
344373

345-
pub fn get_sid(&self) -> GnssSignal {
346-
GnssSignal::from_gnss_signal_t(self.0.sid).unwrap()
374+
pub fn get_sid(&self) -> std::result::Result<GnssSignal, InvalidGnssSignal> {
375+
GnssSignal::from_gnss_signal_t(self.0.sid)
347376
}
348377

349378
/// Gets the status of an ephemeris - is the ephemeris invalid, unhealthy,
@@ -352,6 +381,12 @@ impl Ephemeris {
352381
Status::from_ephemeris_status_t(unsafe { c_bindings::get_ephemeris_status_t(&self.0) })
353382
}
354383

384+
pub fn get_detailed_status(&self, t: GpsTime) -> Status {
385+
Status::from_ephemeris_status_t(unsafe {
386+
c_bindings::ephemeris_valid_detailed(&self.0, t.c_ptr())
387+
})
388+
}
389+
355390
/// Is this ephemeris usable?
356391
pub fn is_valid_at_time(&self, t: GpsTime) -> bool {
357392
let result = unsafe { c_bindings::ephemeris_valid(&self.0, t.c_ptr()) };
@@ -407,13 +442,13 @@ mod tests {
407442
#[test]
408443
fn bds_decode() {
409444
let expected_ephemeris = Ephemeris::new(
410-
GnssSignal::new(25, Code::Bds2B1), // sid
411-
GpsTime::new_unchecked(2091, 460800.0), // toe
412-
2.0, //ura
413-
0, // fit_interval
414-
0, // valid
415-
0, // health_bits
416-
0, // source
445+
GnssSignal::new(25, Code::Bds2B1).unwrap(), // sid
446+
GpsTime::new_unchecked(2091, 460800.0), // toe
447+
2.0, //ura
448+
0, // fit_interval
449+
0, // valid
450+
0, // health_bits
451+
0, // source
417452
EphemerisTerms::new_kepler(
418453
Constellation::Bds,
419454
[-2.99999997e-10, -2.99999997e-10], // tgd
@@ -456,7 +491,7 @@ mod tests {
456491
],
457492
];
458493

459-
let sid = GnssSignal::new(25, Code::Bds2B1);
494+
let sid = GnssSignal::new(25, Code::Bds2B1).unwrap();
460495

461496
let decoded_eph = Ephemeris::decode_bds(&words, sid);
462497

@@ -468,13 +503,13 @@ mod tests {
468503
use super::GAL_INAV_CONTENT_BYTE;
469504

470505
let expected_ephemeris = Ephemeris::new(
471-
GnssSignal::new(8, Code::GalE1b), // sid
472-
GpsTime::new_unchecked(2090, 135000.), // toe
473-
3.120000, // ura
474-
14400, // fit_interval
475-
1, // valid
476-
0, // health_bits
477-
0, // source
506+
GnssSignal::new(8, Code::GalE1b).unwrap(), // sid
507+
GpsTime::new_unchecked(2090, 135000.), // toe
508+
3.120000, // ura
509+
14400, // fit_interval
510+
1, // valid
511+
0, // health_bits
512+
0, // source
478513
EphemerisTerms::new_kepler(
479514
Constellation::Gal,
480515
[-5.5879354476928711e-09, -6.5192580223083496e-09], // tgd

src/ionosphere.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,25 @@
1010
1111
use crate::{c_bindings, time::GpsTime};
1212
use crate::{coords::ECEF, navmeas::NavigationMeasurement};
13+
use std::error::Error;
14+
use std::fmt::{Display, Formatter};
1315

1416
/// Represents an ionosphere model
1517
#[derive(Debug, Clone, PartialOrd, PartialEq)]
1618
pub struct Ionosphere(c_bindings::ionosphere_t);
1719

20+
/// An error indicating that the iono model failed to be decoded
21+
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
22+
pub struct IonoDecodeFailure;
23+
24+
impl Display for IonoDecodeFailure {
25+
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
26+
write!(f, "Error decoding iono model")
27+
}
28+
}
29+
30+
impl Error for IonoDecodeFailure {}
31+
1832
impl Ionosphere {
1933
fn as_ptr(&self) -> *const c_bindings::ionosphere_t {
2034
&self.0
@@ -54,7 +68,7 @@ impl Ionosphere {
5468
/// --------
5569
/// References:
5670
/// * IS-GPS-200H, Section 20.3.3.5.1.7
57-
pub fn decode_parameters(words: &[u32; 8]) -> Option<Ionosphere> {
71+
pub fn decode_parameters(words: &[u32; 8]) -> Result<Ionosphere, IonoDecodeFailure> {
5872
let mut iono = Ionosphere(c_bindings::ionosphere_t {
5973
toa: GpsTime::unknown(),
6074
a0: 0.0,
@@ -70,9 +84,9 @@ impl Ionosphere {
7084
let success = unsafe { c_bindings::decode_iono_parameters(words, &mut iono.0) };
7185

7286
if success {
73-
Some(iono)
87+
Ok(iono)
7488
} else {
75-
None
89+
Err(IonoDecodeFailure)
7690
}
7791
}
7892

0 commit comments

Comments
 (0)