Skip to content

Commit 091c25b

Browse files
authored
Add representations of GAL/BDS/GLO times and conversions between each (#71)
1 parent 6c9a055 commit 091c25b

File tree

2 files changed

+294
-4
lines changed

2 files changed

+294
-4
lines changed

build.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ fn main() {
6161
.blocklist_type("u16")
6262
.blocklist_type("u32")
6363
.allowlist_type("gps_time_t")
64+
.allowlist_type("glo_time_t")
6465
.allowlist_type("utc_params_t")
6566
.allowlist_type("utc_tm")
6667
.allowlist_function("gpsdifftime")
@@ -76,11 +77,18 @@ fn main() {
7677
.allowlist_function("is_leap_second_event")
7778
.allowlist_function("round_to_epoch")
7879
.allowlist_function("floor_to_epoch")
80+
.allowlist_function("glo2gps")
81+
.allowlist_function("gps2glo")
7982
.allowlist_var("FLOAT_EQUALITY_EPS")
8083
.allowlist_var("MINUTE_SECS")
8184
.allowlist_var("HOUR_SECS")
8285
.allowlist_var("DAY_SECS")
8386
.allowlist_var("WEEK_SECS")
87+
.allowlist_var("BDS_WEEK_TO_GPS_WEEK")
88+
.allowlist_var("BDS_SECOND_TO_GPS_SECOND")
89+
.allowlist_var("GAL_WEEK_TO_GPS_WEEK")
90+
.allowlist_var("GLO_EPOCH_WN")
91+
.allowlist_var("GLO_EPOCH_TOW")
8492
.allowlist_type("constellation_t")
8593
.allowlist_type("code_t")
8694
.allowlist_type("gnss_signal_t")

src/time.rs

Lines changed: 286 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ pub const WEEK: Duration = Duration::from_secs(c_bindings::WEEK_SECS as u64);
2222
#[derive(Copy, Clone)]
2323
pub struct GpsTime(c_bindings::gps_time_t);
2424

25+
/// GPS timestamp of the start of Galileo time
26+
pub const GAL_TIME_START: GpsTime =
27+
GpsTime::new_unchecked(c_bindings::GAL_WEEK_TO_GPS_WEEK as i16, 0.0);
28+
/// GPS timestamp of the start of Beidou time
29+
pub const BDS_TIME_START: GpsTime = GpsTime::new_unchecked(
30+
c_bindings::BDS_WEEK_TO_GPS_WEEK as i16,
31+
c_bindings::BDS_SECOND_TO_GPS_SECOND as f64,
32+
);
33+
/// GPS timestamp of the start of Glonass time
34+
pub const GLO_TIME_START: GpsTime =
35+
GpsTime::new_unchecked(c_bindings::GLO_EPOCH_WN as i16, c_bindings::GLO_EPOCH_TOW);
36+
2537
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
2638
pub enum InvalidGpsTime {
2739
InvalidWN(i16),
@@ -58,7 +70,7 @@ impl GpsTime {
5870

5971
/// Makes a new GPS time object without checking the validity of the given
6072
/// values.
61-
pub(crate) fn new_unchecked(wn: i16, tow: f64) -> GpsTime {
73+
pub(crate) const fn new_unchecked(wn: i16, tow: f64) -> GpsTime {
6274
GpsTime(c_bindings::gps_time_t { wn, tow })
6375
}
6476

@@ -121,7 +133,7 @@ impl GpsTime {
121133
/// seconds.
122134
///
123135
/// Note: The hard coded list of leap seconds will get out of date, it is
124-
/// preferable to use `GpsTime::to_utc()` with the newest set of UTC parameters
136+
/// preferable to use [`GpsTime::to_utc()`] with the newest set of UTC parameters
125137
pub fn to_utc_hardcoded(self) -> UtcTime {
126138
let mut utc = UtcTime::default();
127139
unsafe {
@@ -139,7 +151,7 @@ impl GpsTime {
139151
/// list of leap seconds
140152
///
141153
/// Note: The hard coded list of leap seconds will get out of date, it is
142-
/// preferable to use `GpsTime::utc_offset_hardcoded()` with the newest set
154+
/// preferable to use [`GpsTime::utc_offset_hardcoded()`] with the newest set
143155
/// of UTC parameters
144156
pub fn utc_offset_hardcoded(&self) -> f64 {
145157
unsafe { c_bindings::get_gps_utc_offset(self.c_ptr(), std::ptr::null()) }
@@ -154,7 +166,7 @@ impl GpsTime {
154166
/// hardcoded list of leap seconds
155167
///
156168
/// Note: The hard coded list of leap seconds will get out of date, it is
157-
/// preferable to use `GpsTime::is_leap_second_event_hardcoded()` with the newest
169+
/// preferable to use [`GpsTime::is_leap_second_event_hardcoded()`] with the newest
158170
/// set of UTC parameters
159171
pub fn is_leap_second_event_hardcoded(&self) -> bool {
160172
unsafe { c_bindings::is_leap_second_event(self.c_ptr(), std::ptr::null()) }
@@ -169,6 +181,65 @@ impl GpsTime {
169181
pub fn floor_to_epoch(&self, soln_freq: f64) -> GpsTime {
170182
GpsTime(unsafe { c_bindings::floor_to_epoch(self.c_ptr(), soln_freq) })
171183
}
184+
185+
/// Converts the GPS time into Galileo time
186+
///
187+
/// # Panics
188+
/// This function will panic if the GPS time is before the start of Galileo
189+
/// time, i.e. [`GAL_TIME_START`]
190+
pub fn to_gal(self) -> GalTime {
191+
assert!(self.is_valid());
192+
assert!(self >= GAL_TIME_START);
193+
GalTime {
194+
wn: self.wn() - c_bindings::GAL_WEEK_TO_GPS_WEEK as i16,
195+
tow: self.tow(),
196+
}
197+
}
198+
199+
/// Converts the GPS time into Beidou time
200+
///
201+
/// # Panics
202+
/// This function will panic if the GPS time is before the start of Beidou
203+
/// time, i.e. [`BDS_TIME_START`]
204+
pub fn to_bds(self) -> BdsTime {
205+
assert!(self.is_valid());
206+
assert!(self >= BDS_TIME_START);
207+
let bds = GpsTime::new_unchecked(
208+
self.wn() - c_bindings::BDS_WEEK_TO_GPS_WEEK as i16,
209+
self.tow(),
210+
);
211+
let bds = bds - Duration::from_secs(c_bindings::BDS_SECOND_TO_GPS_SECOND as u64);
212+
BdsTime {
213+
wn: bds.wn(),
214+
tow: bds.tow(),
215+
}
216+
}
217+
218+
/// Converts a GPS time into a Glonass time
219+
///
220+
/// # Panics
221+
/// This function will panic if the GPS time is before the start of Glonass
222+
/// time, i.e. [`GLO_TIME_START`]
223+
pub fn to_glo(self, utc_params: &UtcParams) -> GloTime {
224+
assert!(self.is_valid());
225+
assert!(self >= GLO_TIME_START);
226+
GloTime(unsafe { c_bindings::gps2glo(self.c_ptr(), utc_params.c_ptr()) })
227+
}
228+
229+
/// Converts a GPS time into a Glonass time using the hardcoded list of leap
230+
/// seconds.
231+
///
232+
/// Note: The hard coded list of leap seconds will get out of date, it is
233+
/// preferable to use [`GpsTime::to_glo()`] with the newest set of UTC parameters
234+
///
235+
/// # Panics
236+
/// This function will panic if the GPS time is before the start of Glonass
237+
/// time, i.e. [`GLO_TIME_START`]
238+
pub fn to_glo_hardcoded(self) -> GloTime {
239+
assert!(self.is_valid());
240+
assert!(self >= GLO_TIME_START);
241+
GloTime(unsafe { c_bindings::gps2glo(self.c_ptr(), std::ptr::null()) })
242+
}
172243
}
173244

174245
impl fmt::Debug for GpsTime {
@@ -245,6 +316,176 @@ impl SubAssign<Duration> for GpsTime {
245316
}
246317
}
247318

319+
impl From<GalTime> for GpsTime {
320+
fn from(gal: GalTime) -> Self {
321+
gal.to_gps()
322+
}
323+
}
324+
325+
impl From<BdsTime> for GpsTime {
326+
fn from(bds: BdsTime) -> Self {
327+
bds.to_gps()
328+
}
329+
}
330+
331+
/// Representation of Galileo Time
332+
#[derive(Debug, Copy, Clone)]
333+
pub struct GalTime {
334+
wn: i16,
335+
tow: f64,
336+
}
337+
338+
impl GalTime {
339+
pub fn new(wn: i16, tow: f64) -> Result<GalTime, InvalidGpsTime> {
340+
if wn < 0 {
341+
Err(InvalidGpsTime::InvalidWN(wn))
342+
} else if !tow.is_finite() || tow < 0. || tow >= WEEK.as_secs_f64() {
343+
Err(InvalidGpsTime::InvalidTOW(tow))
344+
} else {
345+
Ok(GalTime { wn, tow })
346+
}
347+
}
348+
349+
pub fn wn(&self) -> i16 {
350+
self.wn
351+
}
352+
353+
pub fn tow(&self) -> f64 {
354+
self.tow
355+
}
356+
357+
pub fn to_gps(self) -> GpsTime {
358+
GpsTime::new_unchecked(self.wn + c_bindings::GAL_WEEK_TO_GPS_WEEK as i16, self.tow)
359+
}
360+
361+
pub fn to_bds(self) -> BdsTime {
362+
self.to_gps().to_bds()
363+
}
364+
}
365+
366+
impl From<GpsTime> for GalTime {
367+
fn from(gps: GpsTime) -> Self {
368+
gps.to_gal()
369+
}
370+
}
371+
372+
impl From<BdsTime> for GalTime {
373+
fn from(bds: BdsTime) -> Self {
374+
bds.to_gal()
375+
}
376+
}
377+
378+
/// Representation of Beidou Time
379+
#[derive(Debug, Copy, Clone)]
380+
pub struct BdsTime {
381+
wn: i16,
382+
tow: f64,
383+
}
384+
385+
impl BdsTime {
386+
pub fn new(wn: i16, tow: f64) -> Result<BdsTime, InvalidGpsTime> {
387+
if wn < 0 {
388+
Err(InvalidGpsTime::InvalidWN(wn))
389+
} else if !tow.is_finite() || tow < 0. || tow >= WEEK.as_secs_f64() {
390+
Err(InvalidGpsTime::InvalidTOW(tow))
391+
} else {
392+
Ok(BdsTime { wn, tow })
393+
}
394+
}
395+
396+
pub fn wn(&self) -> i16 {
397+
self.wn
398+
}
399+
400+
pub fn tow(&self) -> f64 {
401+
self.tow
402+
}
403+
404+
pub fn to_gps(self) -> GpsTime {
405+
let gps = GpsTime::new_unchecked(
406+
self.wn() + c_bindings::BDS_WEEK_TO_GPS_WEEK as i16,
407+
self.tow(),
408+
);
409+
gps + Duration::from_secs(c_bindings::BDS_SECOND_TO_GPS_SECOND as u64)
410+
}
411+
412+
pub fn to_gal(self) -> GalTime {
413+
self.to_gps().to_gal()
414+
}
415+
}
416+
417+
impl From<GpsTime> for BdsTime {
418+
fn from(gps: GpsTime) -> Self {
419+
gps.to_bds()
420+
}
421+
}
422+
423+
impl From<GalTime> for BdsTime {
424+
fn from(gal: GalTime) -> Self {
425+
gal.to_bds()
426+
}
427+
}
428+
429+
/// Representation of Glonass Time
430+
#[derive(Copy, Clone)]
431+
pub struct GloTime(c_bindings::glo_time_t);
432+
433+
impl GloTime {
434+
pub(crate) fn c_ptr(&self) -> *const c_bindings::glo_time_t {
435+
&self.0
436+
}
437+
438+
/// Creates a new GloTime
439+
/// nt - Day number within the four-year interval [1-1461].
440+
/// Comes from the field NT in the GLO string 4.
441+
///
442+
/// n4 - Four-year interval number starting from 1996 [1- ].
443+
/// Comes from the field N4 in the GLO string 5.
444+
///
445+
/// h/m/s come either from the field tb in the GLO string 2
446+
/// or the field tk in the GLO string 1
447+
/// h - Hours [0-24]
448+
/// m - Minutes [0-59]
449+
/// s - Seconds [0-60]
450+
pub fn new(nt: u16, n4: u8, h: u8, m: u8, s: f64) -> GloTime {
451+
GloTime(c_bindings::glo_time_t { nt, n4, h, m, s })
452+
}
453+
454+
pub fn nt(&self) -> u16 {
455+
self.0.nt
456+
}
457+
458+
pub fn n4(&self) -> u8 {
459+
self.0.n4
460+
}
461+
462+
pub fn h(&self) -> u8 {
463+
self.0.h
464+
}
465+
466+
pub fn m(&self) -> u8 {
467+
self.0.m
468+
}
469+
470+
pub fn s(&self) -> f64 {
471+
self.0.s
472+
}
473+
474+
/// Converts a Glonass time into a GPS time
475+
pub fn to_gps(self, utc_params: &UtcParams) -> GpsTime {
476+
GpsTime(unsafe { c_bindings::glo2gps(self.c_ptr(), utc_params.c_ptr()) })
477+
}
478+
479+
/// Converts a Glonass time into a GPS time using the hardcoded list of leap
480+
/// seconds.
481+
///
482+
/// Note: The hard coded list of leap seconds will get out of date, it is
483+
/// preferable to use [`GloTime::to_gps()`] with the newest set of UTC parameters
484+
pub fn to_gps_hardcoded(self) -> GpsTime {
485+
GpsTime(unsafe { c_bindings::glo2gps(self.c_ptr(), std::ptr::null()) })
486+
}
487+
}
488+
248489
/// Structure containing GPS UTC correction parameters
249490
#[derive(Clone)]
250491
pub struct UtcParams(c_bindings::utc_params_t);
@@ -1261,4 +1502,45 @@ mod tests {
12611502
assert_eq!(converted.minute(), swift_date.minute() as u32);
12621503
assert_eq!(converted.second(), swift_date.seconds() as u32);
12631504
}
1505+
1506+
#[test]
1507+
fn gps_to_gal() {
1508+
let gal = GAL_TIME_START.to_gal();
1509+
assert_eq!(gal.wn(), 0);
1510+
assert!(gal.tow().abs() < 1e-9);
1511+
let gps = gal.to_gps();
1512+
assert_eq!(gps.wn(), c_bindings::GAL_WEEK_TO_GPS_WEEK as i16);
1513+
assert!(gps.tow().abs() < 1e-9);
1514+
1515+
assert!(GalTime::new(-1, 0.0).is_err());
1516+
assert!(GalTime::new(0, -1.0).is_err());
1517+
assert!(GalTime::new(0, c_bindings::WEEK_SECS as f64 + 1.0).is_err());
1518+
}
1519+
1520+
#[test]
1521+
fn gps_to_bds() {
1522+
let bds = BDS_TIME_START.to_bds();
1523+
assert_eq!(bds.wn(), 0);
1524+
assert!(bds.tow().abs() < 1e-9);
1525+
let gps = bds.to_gps();
1526+
assert_eq!(gps.wn(), c_bindings::BDS_WEEK_TO_GPS_WEEK as i16);
1527+
assert!((gps.tow() - c_bindings::BDS_SECOND_TO_GPS_SECOND as f64).abs() < 1e-9);
1528+
1529+
assert!(BdsTime::new(-1, 0.0).is_err());
1530+
assert!(BdsTime::new(0, -1.0).is_err());
1531+
assert!(BdsTime::new(0, c_bindings::WEEK_SECS as f64 + 1.0).is_err());
1532+
}
1533+
1534+
#[test]
1535+
fn gps_to_glo() {
1536+
let glo = GLO_TIME_START.to_glo_hardcoded();
1537+
assert_eq!(glo.nt(), 1);
1538+
assert_eq!(glo.n4(), 1);
1539+
assert_eq!(glo.h(), 0);
1540+
assert_eq!(glo.m(), 0);
1541+
assert!(glo.s().abs() < 1e-9);
1542+
let gps = glo.to_gps_hardcoded();
1543+
assert_eq!(gps.wn(), c_bindings::GLO_EPOCH_WN as i16);
1544+
assert!((gps.tow() - c_bindings::GLO_EPOCH_TOW as f64).abs() < 1e-9);
1545+
}
12641546
}

0 commit comments

Comments
 (0)