11
11
use crate :: {
12
12
c_bindings,
13
13
coords:: { AzimuthElevation , ECEF } ,
14
- signal:: { Code , Constellation , GnssSignal } ,
14
+ signal:: { Code , Constellation , GnssSignal , InvalidGnssSignal } ,
15
15
time:: GpsTime ,
16
16
} ;
17
- use std:: fmt:: { Display , Formatter } ;
17
+ use std:: error:: Error ;
18
+ use std:: fmt;
18
19
19
20
/// Number of bytes in the Galileo INAV message
20
21
// TODO(jbangelo) bindgen doesn't catch this variable on linux for some reason
21
22
pub const GAL_INAV_CONTENT_BYTE : usize = ( 128 + 8 - 1 ) / 8 ;
22
23
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 {
40
27
Null ,
41
28
Invalid ,
42
29
WnEqualsZero ,
@@ -45,22 +32,55 @@ pub enum Status {
45
32
TooOld ,
46
33
InvalidSid ,
47
34
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 ) ,
48
49
Valid ,
49
50
}
50
51
51
52
impl Status {
52
53
fn from_ephemeris_status_t ( value : c_bindings:: ephemeris_status_t ) -> Status {
53
54
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 ) ,
60
69
c_bindings:: ephemeris_status_t_EPH_VALID => Status :: Valid ,
61
70
_ => panic ! ( "Invalid ephemeris_status_t value: {}" , value) ,
62
71
}
63
72
}
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
+ }
64
84
}
65
85
66
86
/// Orbital terms of an ephemeris
@@ -265,7 +285,10 @@ impl Ephemeris {
265
285
}
266
286
267
287
/// 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
+
269
292
let mut sat = SatelliteState {
270
293
pos : ECEF :: default ( ) ,
271
294
vel : ECEF :: default ( ) ,
@@ -288,16 +311,20 @@ impl Ephemeris {
288
311
)
289
312
} ;
290
313
291
- if result == 0 {
292
- Ok ( sat)
293
- } else {
294
- Err ( InvalidEphemeris { } )
295
- }
314
+ assert_eq ! ( result, 0 ) ;
315
+ Ok ( sat)
296
316
}
297
317
298
318
/// Calculate the azimuth and elevation of a satellite from a reference
299
319
/// 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
+
301
328
let mut sat = AzimuthElevation :: default ( ) ;
302
329
303
330
let result = unsafe {
@@ -312,16 +339,21 @@ impl Ephemeris {
312
339
)
313
340
} ;
314
341
315
- if result == 0 {
316
- Ok ( sat)
317
- } else {
318
- Err ( InvalidEphemeris { } )
319
- }
342
+ assert_eq ! ( result, 0 ) ;
343
+ Ok ( sat)
320
344
}
321
345
322
346
/// Calculate the Doppler shift of a satellite as observed at a reference
323
347
/// 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
+
325
357
let mut doppler = 0.0 ;
326
358
327
359
let result = unsafe {
@@ -335,15 +367,12 @@ impl Ephemeris {
335
367
)
336
368
} ;
337
369
338
- if result == 0 {
339
- Ok ( doppler)
340
- } else {
341
- Err ( InvalidEphemeris { } )
342
- }
370
+ assert_eq ! ( result, 0 ) ;
371
+ Ok ( doppler)
343
372
}
344
373
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 )
347
376
}
348
377
349
378
/// Gets the status of an ephemeris - is the ephemeris invalid, unhealthy,
@@ -352,6 +381,12 @@ impl Ephemeris {
352
381
Status :: from_ephemeris_status_t ( unsafe { c_bindings:: get_ephemeris_status_t ( & self . 0 ) } )
353
382
}
354
383
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
+
355
390
/// Is this ephemeris usable?
356
391
pub fn is_valid_at_time ( & self , t : GpsTime ) -> bool {
357
392
let result = unsafe { c_bindings:: ephemeris_valid ( & self . 0 , t. c_ptr ( ) ) } ;
@@ -407,13 +442,13 @@ mod tests {
407
442
#[ test]
408
443
fn bds_decode ( ) {
409
444
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
417
452
EphemerisTerms :: new_kepler (
418
453
Constellation :: Bds ,
419
454
[ -2.99999997e-10 , -2.99999997e-10 ] , // tgd
@@ -456,7 +491,7 @@ mod tests {
456
491
] ,
457
492
] ;
458
493
459
- let sid = GnssSignal :: new ( 25 , Code :: Bds2B1 ) ;
494
+ let sid = GnssSignal :: new ( 25 , Code :: Bds2B1 ) . unwrap ( ) ;
460
495
461
496
let decoded_eph = Ephemeris :: decode_bds ( & words, sid) ;
462
497
@@ -468,13 +503,13 @@ mod tests {
468
503
use super :: GAL_INAV_CONTENT_BYTE ;
469
504
470
505
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
478
513
EphemerisTerms :: new_kepler (
479
514
Constellation :: Gal ,
480
515
[ -5.5879354476928711e-09 , -6.5192580223083496e-09 ] , // tgd
0 commit comments