1111use 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
2122pub 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
5152impl 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
0 commit comments