From da2c68fe5f56f973506de832d48656d778abaa0d Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sat, 20 May 2023 22:03:02 +0200 Subject: [PATCH 1/5] support qzss time scale Signed-off-by: Guillaume W. Bres --- src/asn1der.rs | 6 ++++-- src/epoch.rs | 20 ++++++++++++++------ src/timescale.rs | 19 +++++++++++++++---- tests/epoch.rs | 1 + tests/timescale.rs | 5 +++++ 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/asn1der.rs b/src/asn1der.rs index 99282935..e873ef3e 100644 --- a/src/asn1der.rs +++ b/src/asn1der.rs @@ -62,7 +62,8 @@ impl<'a> Decode<'a> for Epoch { TimeScale::ET => Self::from_et_duration(duration), TimeScale::TDB => Self::from_tdb_duration(duration), TimeScale::UTC => Self::from_utc_duration(duration), - TimeScale::GPST => Self::from_gpst_duration(duration), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => Self::from_gpst_duration(duration), TimeScale::GST => Self::from_gst_duration(duration), TimeScale::BDT => Self::from_bdt_duration(duration), }) @@ -107,9 +108,10 @@ fn test_encdec() { TimeScale::TT => epoch.to_tt_duration(), TimeScale::TDB => epoch.to_tdb_duration(), TimeScale::UTC => epoch.to_utc_duration(), - TimeScale::GPST => epoch.to_gpst_duration(), TimeScale::GST => epoch.to_gst_duration(), TimeScale::BDT => epoch.to_bdt_duration(), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => epoch.to_gpst_duration(), }; let e_dur = epoch.to_duration(); diff --git a/src/epoch.rs b/src/epoch.rs index 0aeb8836..d18c5453 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -272,7 +272,8 @@ impl Epoch { TimeScale::ET => Self::from_et_duration(new_duration), TimeScale::TDB => Self::from_tdb_duration(new_duration), TimeScale::UTC => Self::from_utc_duration(new_duration), - TimeScale::GPST => Self::from_gpst_duration(new_duration), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => Self::from_gpst_duration(new_duration), TimeScale::GST => Self::from_gst_duration(new_duration), TimeScale::BDT => Self::from_bdt_duration(new_duration), } @@ -728,7 +729,8 @@ impl Epoch { TimeScale::ET => Self::from_et_duration(duration_wrt_1900 - J2000_TO_J1900_DURATION), TimeScale::TDB => Self::from_tdb_duration(duration_wrt_1900 - J2000_TO_J1900_DURATION), TimeScale::UTC => Self::from_utc_duration(duration_wrt_1900), - TimeScale::GPST => { + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => { Self::from_gpst_duration(duration_wrt_1900 - GPST_REF_EPOCH.to_tai_duration()) } TimeScale::GST => { @@ -1712,9 +1714,10 @@ impl Epoch { TimeScale::ET => self.to_et_duration(), TimeScale::TDB => self.to_tdb_duration(), TimeScale::UTC => self.to_utc_duration(), - TimeScale::GPST => self.to_gpst_duration(), TimeScale::BDT => self.to_bdt_duration(), TimeScale::GST => self.to_gst_duration(), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => self.to_gpst_duration(), } } @@ -1747,7 +1750,10 @@ impl Epoch { TimeScale::TT => self.to_tt_duration(), TimeScale::TDB => self.to_tdb_duration_since_j1900(), TimeScale::UTC => self.to_utc_duration(), - TimeScale::GPST => self.to_gpst_duration() + GPST_REF_EPOCH.to_tai_duration(), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => { + self.to_gpst_duration() + GPST_REF_EPOCH.to_tai_duration() + } TimeScale::GST => self.to_gst_duration() + GST_REF_EPOCH.to_tai_duration(), TimeScale::BDT => self.to_bdt_duration() + BDT_REF_EPOCH.to_tai_duration(), } @@ -1762,7 +1768,8 @@ impl Epoch { TimeScale::ET => Self::from_et_duration(new_duration), TimeScale::TDB => Self::from_tdb_duration(new_duration), TimeScale::UTC => Self::from_utc_duration(new_duration), - TimeScale::GPST => Self::from_gpst_duration(new_duration), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => Self::from_gpst_duration(new_duration), TimeScale::GST => Self::from_gst_duration(new_duration), TimeScale::BDT => Self::from_bdt_duration(new_duration), } @@ -2737,7 +2744,8 @@ impl Epoch { TimeScale::ET => self.to_et_duration_since_j1900(), TimeScale::TDB => self.to_tdb_duration_since_j1900(), TimeScale::UTC => self.to_utc_duration(), - TimeScale::GPST => self.to_utc_duration(), + // GPST and QZSST share the same properties + TimeScale::GPST | TimeScale::QZSST => self.to_utc_duration(), TimeScale::GST => self.to_utc_duration(), TimeScale::BDT => self.to_utc_duration(), }); diff --git a/src/timescale.rs b/src/timescale.rs index 4a14a846..a5e1a383 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -78,12 +78,15 @@ pub enum TimeScale { TDB, /// Universal Coordinated Time UTC, - /// GPST Time also applies to QZSS, IRNSS and GAL constellations + /// GPS Time scale GPST, /// Galileo Time scale GST, /// BeiDou Time scale BDT, + /// QZSS Time scale has the same properties as GPST, + /// but with dedicated clocks + QZSST, } #[cfg(kani)] @@ -91,7 +94,6 @@ impl Arbitrary for TimeScale { #[inline(always)] fn any() -> Self { let ts_u8: u8 = kani::any(); - Self::from(ts_u8) } } @@ -111,6 +113,7 @@ impl Default for TimeScale { impl TimeScale { pub(crate) const fn formatted_len(&self) -> usize { match &self { + Self::QZSST => 5, Self::GPST => 4, Self::TAI | Self::TDB | Self::UTC | Self::GST | Self::BDT => 3, Self::ET | Self::TT => 2, @@ -119,7 +122,7 @@ impl TimeScale { /// Returns true if Self is based off a GNSS constellation pub const fn is_gnss(&self) -> bool { - matches!(self, Self::GPST | Self::GST | Self::BDT) + matches!(self, Self::GPST | Self::GST | Self::BDT | Self::QZSST) } /// Returns Reference Epoch (t(0)) for given timescale @@ -132,6 +135,8 @@ impl TimeScale { Self::TDB => J2000_REF_EPOCH_TDB, // Explicit on purpose in case more time scales end up being supported. Self::TT | Self::TAI | Self::UTC => J1900_REF_EPOCH, + // QZSS time shares the same starting point as GPST + Self::QZSST => GPST_REF_EPOCH, } } } @@ -148,6 +153,7 @@ impl fmt::Display for TimeScale { Self::GPST => write!(f, "GPST"), Self::GST => write!(f, "GST"), Self::BDT => write!(f, "BDT"), + Self::QZSST => write!(f, "QZSST"), } } } @@ -160,6 +166,7 @@ impl fmt::LowerHex for TimeScale { Self::GPST => write!(f, "GPS"), Self::GST => write!(f, "GAL"), Self::BDT => write!(f, "BDS"), + Self::QZSST => write!(f, "QZSS"), _ => write!(f, "{self}"), } } @@ -186,6 +193,7 @@ impl From for u8 { TimeScale::GPST => 5, TimeScale::GST => 6, TimeScale::BDT => 7, + TimeScale::QZSST => 8, } } } @@ -202,6 +210,7 @@ impl From for TimeScale { 5 => Self::GPST, 6 => Self::GST, 7 => Self::BDT, + 8 => Self::QZSST, _ => Self::TAI, } } @@ -228,6 +237,8 @@ impl FromStr for TimeScale { Ok(Self::GST) } else if val == "BDT" || val == "BDS" { Ok(Self::BDT) + } else if val == "QZSST" || val == "QZSS" { + Ok(Self::QZSST) } else { Err(Errors::ParseError(ParsingErrors::TimeSystem)) } @@ -250,7 +261,7 @@ fn test_ts() { let ts = TimeScale::from(ts_u8); let ts_u8_back: u8 = ts.into(); // If the u8 is greater than 5, it isn't valid and necessarily encoded as TAI. - if ts_u8 < 8 { + if ts_u8 < 9 { assert_eq!(ts_u8_back, ts_u8, "got {ts_u8_back} want {ts_u8}"); } else { assert_eq!(ts, TimeScale::TAI); diff --git a/tests/epoch.rs b/tests/epoch.rs index 62850a9d..f55db0be 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -955,6 +955,7 @@ fn test_format() { TimeScale::GPST => format!("{epoch:x}").replace("TAI", "GPST"), TimeScale::GST => format!("{epoch:x}").replace("TAI", "GST"), TimeScale::BDT => format!("{epoch:x}").replace("TAI", "BDT"), + TimeScale::QZSST => format!("{epoch:x}").replace("TAI", "QZSST"), } ); diff --git a/tests/timescale.rs b/tests/timescale.rs index 19c91fdd..2e0a8851 100644 --- a/tests/timescale.rs +++ b/tests/timescale.rs @@ -12,6 +12,7 @@ fn test_from_str() { ("GPST", TimeScale::GPST), ("GST", TimeScale::GST), ("BDT", TimeScale::BDT), + ("QZSST", TimeScale::QZSST), ]; for value in values { let (descriptor, expected) = value; @@ -26,6 +27,7 @@ fn test_from_str() { TimeScale::GPST => "GPS", TimeScale::GST => "GAL", TimeScale::BDT => "BDS", + TimeScale::QZSST => "QZSS", _ => descriptor, // untouched }; assert_eq!(format!("{:x}", ts), expected); @@ -41,6 +43,7 @@ fn test_from_rinex_format() { assert_eq!(TimeScale::from_str("GPS"), Ok(TimeScale::GPST)); assert_eq!(TimeScale::from_str("GAL"), Ok(TimeScale::GST)); assert_eq!(TimeScale::from_str("BDS"), Ok(TimeScale::BDT)); + assert_eq!(TimeScale::from_str("QZSS"), Ok(TimeScale::QZSST)); // Check error assert_eq!( TimeScale::from_str("FAK"), @@ -58,6 +61,8 @@ fn test_is_gnss() { assert!(!ts.is_gnss()); let ts = TimeScale::TAI; assert!(!ts.is_gnss()); + let ts = TimeScale::QZSST; + assert!(ts.is_gnss()); } #[test] From 349ef965063767dddd38bf61164d122474c3e92d Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sat, 20 May 2023 22:06:50 +0200 Subject: [PATCH 2/5] declare timescale enum as non_exhaustive Signed-off-by: Guillaume W. Bres --- src/timescale.rs | 1 + tests/epoch.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/timescale.rs b/src/timescale.rs index a5e1a383..4f0c4032 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -64,6 +64,7 @@ pub const UNIX_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { }); /// Enum of the different time systems available +#[non_exhaustive] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "python", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/tests/epoch.rs b/tests/epoch.rs index f55db0be..4becd71c 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -956,6 +956,7 @@ fn test_format() { TimeScale::GST => format!("{epoch:x}").replace("TAI", "GST"), TimeScale::BDT => format!("{epoch:x}").replace("TAI", "BDT"), TimeScale::QZSST => format!("{epoch:x}").replace("TAI", "QZSST"), + _ => format!("{epoch:x}").replace("TAI", "GNSS"), // non exhaustive GNSS time scales } ); From baf26ed95176d30d8f216afddb3ddc7426d87ad4 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sat, 20 May 2023 22:16:36 +0200 Subject: [PATCH 3/5] qzsst: documentation Signed-off-by: Guillaume W. Bres --- src/timescale.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timescale.rs b/src/timescale.rs index 4f0c4032..5da72cd1 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -200,7 +200,7 @@ impl From for u8 { } /// Allows conversion of a u8 into a TimeSystem. -/// Mapping: 1: TT; 2: ET; 3: TDB; 4: UTC; 5: GPST; 6: GST; 7: BDT; anything else: TAI +/// Mapping: 1: TT; 2: ET; 3: TDB; 4: UTC; 5: GPST; 6: GST; 7: BDT; 8: QZSST; anything else: TAI impl From for TimeScale { fn from(val: u8) -> Self { match val { From 26ba849ea7602949afbc5dfba942fb913a7c02a0 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sat, 20 May 2023 22:18:01 +0200 Subject: [PATCH 4/5] qzsst: documentation Signed-off-by: Guillaume W. Bres --- src/timescale.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timescale.rs b/src/timescale.rs index 5da72cd1..22ab3a82 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -182,7 +182,7 @@ impl TimeScale { } /// Allows conversion of a TimeSystem into a u8 -/// Mapping: TAI: 0; TT: 1; ET: 2; TDB: 3; UTC: 4; GPST: 5; GST: 6; BDT: 7; +/// Mapping: TAI: 0; TT: 1; ET: 2; TDB: 3; UTC: 4; GPST: 5; GST: 6; BDT: 7; QZSST: 8; impl From for u8 { fn from(ts: TimeScale) -> Self { match ts { From 4d0f6f05f0f64f98269cfa4651a4a21519201ff9 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 21 May 2023 11:33:27 +0200 Subject: [PATCH 5/5] tests: improve qzss coverage Signed-off-by: Guillaume W. Bres --- src/asn1der.rs | 12 ++--- src/epoch.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++----- tests/epoch.rs | 56 +++++++++++++++++++++-- 3 files changed, 167 insertions(+), 21 deletions(-) diff --git a/src/asn1der.rs b/src/asn1der.rs index e873ef3e..0e3e7731 100644 --- a/src/asn1der.rs +++ b/src/asn1der.rs @@ -62,8 +62,8 @@ impl<'a> Decode<'a> for Epoch { TimeScale::ET => Self::from_et_duration(duration), TimeScale::TDB => Self::from_tdb_duration(duration), TimeScale::UTC => Self::from_utc_duration(duration), - // GPST and QZSST share the same properties - TimeScale::GPST | TimeScale::QZSST => Self::from_gpst_duration(duration), + TimeScale::GPST => Self::from_gpst_duration(duration), + TimeScale::QZSST => Self::from_qzsst_duration(duration), TimeScale::GST => Self::from_gst_duration(duration), TimeScale::BDT => Self::from_bdt_duration(duration), }) @@ -93,7 +93,7 @@ impl<'a> Decode<'a> for Unit { // Testing the encoding and decoding of an Epoch inherently also tests the encoding and decoding of a Duration #[test] fn test_encdec() { - for ts_u8 in 0..=7 { + for ts_u8 in 0..=8 { let ts: TimeScale = ts_u8.into(); let epoch = if ts == TimeScale::UTC { @@ -108,10 +108,10 @@ fn test_encdec() { TimeScale::TT => epoch.to_tt_duration(), TimeScale::TDB => epoch.to_tdb_duration(), TimeScale::UTC => epoch.to_utc_duration(), + TimeScale::GPST => epoch.to_gpst_duration(), TimeScale::GST => epoch.to_gst_duration(), TimeScale::BDT => epoch.to_bdt_duration(), - // GPST and QZSST share the same properties - TimeScale::GPST | TimeScale::QZSST => epoch.to_gpst_duration(), + TimeScale::QZSST => epoch.to_qzsst_duration(), }; let e_dur = epoch.to_duration(); @@ -132,7 +132,7 @@ fn test_encdec() { // Check that the time scale used is preserved assert_eq!( encdec_epoch.time_scale, ts, - "Decoded time system incorrect {ts:?}" + "Decoded time system incorrect {ts:?}", ); } diff --git a/src/epoch.rs b/src/epoch.rs index d18c5453..20aa3fee 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -272,8 +272,8 @@ impl Epoch { TimeScale::ET => Self::from_et_duration(new_duration), TimeScale::TDB => Self::from_tdb_duration(new_duration), TimeScale::UTC => Self::from_utc_duration(new_duration), - // GPST and QZSST share the same properties - TimeScale::GPST | TimeScale::QZSST => Self::from_gpst_duration(new_duration), + TimeScale::GPST => Self::from_gpst_duration(new_duration), + TimeScale::QZSST => Self::from_qzsst_duration(new_duration), TimeScale::GST => Self::from_gst_duration(new_duration), TimeScale::BDT => Self::from_bdt_duration(new_duration), } @@ -347,6 +347,15 @@ impl Epoch { me } + #[must_use] + /// Initialize an Epoch from the provided duration since 1980 January 6 at midnight + pub fn from_qzsst_duration(duration: Duration) -> Self { + // QZSST and GPST share the same reference epoch + let mut me = Self::from_tai_duration(GPST_REF_EPOCH.to_tai_duration() + duration); + me.time_scale = TimeScale::QZSST; + me + } + #[must_use] /// Initialize an Epoch from the provided duration since August 21st 1999 midnight pub fn from_gst_duration(duration: Duration) -> Self { @@ -391,6 +400,10 @@ impl Epoch { Self::from_mjd_in_time_scale(days, TimeScale::GPST) } #[must_use] + pub fn from_mjd_qzsst(days: f64) -> Self { + Self::from_mjd_in_time_scale(days, TimeScale::QZSST) + } + #[must_use] pub fn from_mjd_gst(days: f64) -> Self { Self::from_mjd_in_time_scale(days, TimeScale::GST) } @@ -427,6 +440,10 @@ impl Epoch { Self::from_jde_in_time_scale(days, TimeScale::GPST) } #[must_use] + pub fn from_jde_qzsst(days: f64) -> Self { + Self::from_jde_in_time_scale(days, TimeScale::QZSST) + } + #[must_use] pub fn from_jde_gst(days: f64) -> Self { Self::from_jde_in_time_scale(days, TimeScale::GST) } @@ -573,6 +590,34 @@ impl Epoch { ) } + #[must_use] + /// Initialize an Epoch from the number of seconds since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + pub fn from_qzsst_seconds(seconds: f64) -> Self { + Self::from_duration(Duration::from_f64(seconds, Unit::Second), TimeScale::QZSST) + } + + #[must_use] + /// Initialize an Epoch from the number of days since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + pub fn from_qzsst_days(days: f64) -> Self { + Self::from_duration(Duration::from_f64(days, Unit::Day), TimeScale::QZSST) + } + + #[must_use] + /// Initialize an Epoch from the number of nanoseconds since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + /// This may be useful for time keeping devices that use QZSS as a time source. + pub fn from_qzsst_nanoseconds(nanoseconds: u64) -> Self { + Self::from_duration( + Duration { + centuries: 0, + nanoseconds, + }, + TimeScale::QZSST, + ) + } + #[must_use] /// Initialize an Epoch from the number of seconds since the GST Time Epoch, /// starting August 21st 1999 midnight (UTC) @@ -729,10 +774,13 @@ impl Epoch { TimeScale::ET => Self::from_et_duration(duration_wrt_1900 - J2000_TO_J1900_DURATION), TimeScale::TDB => Self::from_tdb_duration(duration_wrt_1900 - J2000_TO_J1900_DURATION), TimeScale::UTC => Self::from_utc_duration(duration_wrt_1900), - // GPST and QZSST share the same properties - TimeScale::GPST | TimeScale::QZSST => { + TimeScale::GPST => { Self::from_gpst_duration(duration_wrt_1900 - GPST_REF_EPOCH.to_tai_duration()) } + // QZSS and GPST share the same reference epoch + TimeScale::QZSST => { + Self::from_qzsst_duration(duration_wrt_1900 - GPST_REF_EPOCH.to_tai_duration()) + } TimeScale::GST => { Self::from_gst_duration(duration_wrt_1900 - GST_REF_EPOCH.to_tai_duration()) } @@ -1426,6 +1474,31 @@ impl Epoch { Self::from_gpst_nanoseconds(nanoseconds) } + #[cfg(feature = "python")] + #[classmethod] + /// Initialize an Epoch from the number of seconds since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + fn init_from_qzsst_seconds(_cls: &PyType, seconds: f64) -> Self { + Self::from_qzsst_seconds(seconds) + } + + #[cfg(feature = "python")] + #[classmethod] + /// Initialize an Epoch from the number of days since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + fn init_from_qzsst_days(_cls: &PyType, days: f64) -> Self { + Self::from_qzsst_days(days) + } + + #[cfg(feature = "python")] + #[classmethod] + /// Initialize an Epoch from the number of nanoseconds since the QZSS Time Epoch, + /// defined as UTC midnight of January 5th to 6th 1980 (cf. ). + /// This may be useful for time keeping devices that use QZSS as a time source. + fn init_from_qzsst_nanoseconds(_cls: &PyType, nanoseconds: u64) -> Self { + Self::from_qzsst_nanoseconds(nanoseconds) + } + #[cfg(feature = "python")] #[classmethod] /// Initialize an Epoch from the number of seconds since the Galileo Time Epoch, @@ -1987,6 +2060,31 @@ impl Epoch { self.to_gpst_duration().to_unit(Unit::Day) } + #[must_use] + /// Returns seconds past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. ). + pub fn to_qzsst_seconds(&self) -> f64 { + self.to_qzsst_duration().to_seconds() + } + + #[must_use] + /// Returns `Duration` past QZSS time Epoch. + pub fn to_qzsst_duration(&self) -> Duration { + // GPST and QZSST share the same reference epoch + self.duration_since_j1900_tai - GPST_REF_EPOCH.to_tai_duration() + } + + /// Returns nanoseconds past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. ). + /// NOTE: This function will return an error if the centuries past QZSST time are not zero. + pub fn to_qzsst_nanoseconds(&self) -> Result { + self.to_nanoseconds_in_time_scale(TimeScale::QZSST) + } + + #[must_use] + /// Returns days past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. ). + pub fn to_qzsst_days(&self) -> f64 { + self.to_gpst_duration().to_unit(Unit::Day) + } + #[must_use] /// Returns seconds past GST (Galileo) Time Epoch pub fn to_gst_seconds(&self) -> f64 { @@ -1999,6 +2097,13 @@ impl Epoch { self.duration_since_j1900_tai - GST_REF_EPOCH.to_tai_duration() } + /// Returns nanoseconds past GST (Galileo) Time Epoch, starting on August 21st 1999 Midnight UT + /// (cf. ). + /// NOTE: This function will return an error if the centuries past GST time are not zero. + pub fn to_gst_nanoseconds(&self) -> Result { + self.to_nanoseconds_in_time_scale(TimeScale::GST) + } + #[must_use] /// Returns days past GST (Galileo) Time Epoch, /// starting on August 21st 1999 Midnight UT @@ -2007,13 +2112,6 @@ impl Epoch { self.to_gst_duration().to_unit(Unit::Day) } - /// Returns nanoseconds past GST (Galileo) Time Epoch, starting on August 21st 1999 Midnight UT - /// (cf. ). - /// NOTE: This function will return an error if the centuries past GST time are not zero. - pub fn to_gst_nanoseconds(&self) -> Result { - self.to_nanoseconds_in_time_scale(TimeScale::GST) - } - #[must_use] /// Returns seconds past BDT (BeiDou) Time Epoch pub fn to_bdt_seconds(&self) -> f64 { diff --git a/tests/epoch.rs b/tests/epoch.rs index 4becd71c..b6ce55ae 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -335,12 +335,31 @@ fn gpst() { let ref_gps = Epoch::from_gregorian_utc_at_midnight(1980, 01, 06); // Test 1sec into GPS timescale - let gnss = Epoch::from_gpst_seconds(1.0); - assert_eq!(gnss, ref_gps + 1.0 * Unit::Second); + let gps_1sec = Epoch::from_gpst_seconds(1.0); + assert_eq!(gps_1sec, ref_gps + 1.0 * Unit::Second); + + // 1sec into QZSS time scale returns the same date + let qzss_1sec = Epoch::from_qzsst_seconds(1.0); + assert_eq!(gps_1sec.to_utc_duration(), qzss_1sec.to_utc_duration()); + + // GPS and QZSS share the same properties at all times + assert_eq!(gps_1sec.to_gpst_seconds(), qzss_1sec.to_qzsst_seconds()); + assert_eq!( + gps_1sec.to_gpst_nanoseconds(), + qzss_1sec.to_qzsst_nanoseconds() + ); // Test 1+1/2 day into GPS timescale - let gnss = Epoch::from_gpst_days(1.5); - assert_eq!(gnss, ref_gps + 1.5 * Unit::Day); + let gps = Epoch::from_gpst_days(1.5); + assert_eq!(gps, ref_gps + 1.5 * Unit::Day); + + // 1sec into QZSS time scale returns the same date + let qzss = Epoch::from_qzsst_days(1.5); + assert_eq!(gps.to_utc_duration(), qzss.to_utc_duration()); + + // GPS and QZSS share the same properties at all times + assert_eq!(gps.to_gpst_seconds(), qzss.to_qzsst_seconds()); + assert_eq!(gps.to_gpst_nanoseconds(), qzss.to_qzsst_nanoseconds()); let now = Epoch::from_gregorian_tai_hms(2019, 8, 24, 3, 49, 9); assert_eq!( @@ -390,6 +409,28 @@ fn gpst() { let epoch = Epoch::from_gregorian_utc_at_midnight(1980, 1, 1); assert!((epoch.to_gpst_seconds() + 5.0 * SECONDS_PER_DAY).abs() < EPSILON); assert!((epoch.to_gpst_days() + 5.0).abs() < EPSILON); + + // test other GPS / QZSS equalities + assert_eq!( + Epoch::from_mjd_gpst(0.77).to_utc_duration(), + Epoch::from_mjd_qzsst(0.77).to_utc_duration() + ); + assert_eq!( + Epoch::from_jde_gpst(1.23).to_utc_duration(), + Epoch::from_jde_qzsst(1.23).to_utc_duration() + ); + assert_eq!( + Epoch::from_qzsst_seconds(1024.768).to_utc_duration(), + Epoch::from_gpst_seconds(1024.768).to_utc_duration() + ); + assert_eq!( + Epoch::from_qzsst_days(987.654).to_utc_duration(), + Epoch::from_gpst_days(987.654).to_utc_duration() + ); + assert_eq!( + Epoch::from_qzsst_nanoseconds(543210987).to_utc_duration(), + Epoch::from_gpst_nanoseconds(543210987).to_utc_duration() + ); } #[test] @@ -1551,6 +1592,13 @@ fn test_time_of_week() { epoch_utc ); + // GPST and QZSST share the same properties at all times + let epoch_qzsst = epoch.in_time_scale(TimeScale::QZSST); + assert_eq!(epoch.to_gregorian_utc(), epoch_qzsst.to_gregorian_utc()); + + let gps_qzss_offset = TimeScale::GPST.ref_epoch() - TimeScale::QZSST.ref_epoch(); + assert_eq!(gps_qzss_offset.total_nanoseconds(), 0); // no offset + // 06/01/1980 01:00:00 = 1H into GPST <=> (0, 3_618_000_000_000) let epoch = Epoch::from_time_of_week(0, 3_618_000_000_000, TimeScale::GPST); assert_eq!(epoch.to_gregorian_utc(), (1980, 01, 06, 01, 00, 0 + 18, 00));