Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support qzss time scale #228

Merged
merged 5 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/asn1der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ impl<'a> Decode<'a> for Epoch {
TimeScale::TDB => Self::from_tdb_duration(duration),
TimeScale::UTC => Self::from_utc_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),
})
Expand Down Expand Up @@ -92,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 {
Expand All @@ -110,6 +111,7 @@ fn test_encdec() {
TimeScale::GPST => epoch.to_gpst_duration(),
TimeScale::GST => epoch.to_gst_duration(),
TimeScale::BDT => epoch.to_bdt_duration(),
TimeScale::QZSST => epoch.to_qzsst_duration(),
};

let e_dur = epoch.to_duration();
Expand All @@ -130,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:?}",
);
}

Expand Down
128 changes: 117 additions & 11 deletions src/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ impl Epoch {
TimeScale::TDB => Self::from_tdb_duration(new_duration),
TimeScale::UTC => Self::from_utc_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),
}
Expand Down Expand Up @@ -346,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 {
Expand Down Expand Up @@ -390,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)
}
Expand Down Expand Up @@ -426,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)
}
Expand Down Expand Up @@ -572,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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
/// 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)
Expand Down Expand Up @@ -731,6 +777,10 @@ impl Epoch {
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())
}
Expand Down Expand Up @@ -1424,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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
/// 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,
Expand Down Expand Up @@ -1712,9 +1787,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(),
}
}

Expand Down Expand Up @@ -1747,7 +1823,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(),
}
Expand All @@ -1762,7 +1841,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),
}
Expand Down Expand Up @@ -1980,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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
/// NOTE: This function will return an error if the centuries past QZSST time are not zero.
pub fn to_qzsst_nanoseconds(&self) -> Result<u64, Errors> {
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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>).
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 {
Expand All @@ -1992,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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>).
/// NOTE: This function will return an error if the centuries past GST time are not zero.
pub fn to_gst_nanoseconds(&self) -> Result<u64, Errors> {
self.to_nanoseconds_in_time_scale(TimeScale::GST)
}

#[must_use]
/// Returns days past GST (Galileo) Time Epoch,
/// starting on August 21st 1999 Midnight UT
Expand All @@ -2000,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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS>).
/// NOTE: This function will return an error if the centuries past GST time are not zero.
pub fn to_gst_nanoseconds(&self) -> Result<u64, Errors> {
self.to_nanoseconds_in_time_scale(TimeScale::GST)
}

#[must_use]
/// Returns seconds past BDT (BeiDou) Time Epoch
pub fn to_bdt_seconds(&self) -> f64 {
Expand Down Expand Up @@ -2737,7 +2842,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(),
});
Expand Down
24 changes: 18 additions & 6 deletions src/timescale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub const UNIX_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
});

/// Enum of the different time systems available
#[non_exhaustive]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand All @@ -78,20 +79,22 @@ 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)]
impl Arbitrary for TimeScale {
#[inline(always)]
fn any() -> Self {
let ts_u8: u8 = kani::any();

Self::from(ts_u8)
}
}
Expand All @@ -111,6 +114,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,
Expand All @@ -119,7 +123,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
Expand All @@ -132,6 +136,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,
}
}
}
Expand All @@ -148,6 +154,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"),
}
}
}
Expand All @@ -160,6 +167,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}"),
}
}
Expand All @@ -174,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<TimeScale> for u8 {
fn from(ts: TimeScale) -> Self {
match ts {
Expand All @@ -186,12 +194,13 @@ impl From<TimeScale> for u8 {
TimeScale::GPST => 5,
TimeScale::GST => 6,
TimeScale::BDT => 7,
TimeScale::QZSST => 8,
}
}
}

/// 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<u8> for TimeScale {
fn from(val: u8) -> Self {
match val {
Expand All @@ -202,6 +211,7 @@ impl From<u8> for TimeScale {
5 => Self::GPST,
6 => Self::GST,
7 => Self::BDT,
8 => Self::QZSST,
_ => Self::TAI,
}
}
Expand All @@ -228,6 +238,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))
}
Expand All @@ -250,7 +262,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);
Expand Down
Loading