From 704fbff07ba9b3a369b2b77a4c47a942e585c223 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Mon, 5 Dec 2022 23:30:46 -0700 Subject: [PATCH] Hot fix for #187 Signed-off-by: Christopher Rabotin --- src/epoch.rs | 58 ++++++++++++++++++++++++------------------------ src/lib.rs | 18 +++++++++++++++ src/timescale.rs | 11 +++++++-- src/weekday.rs | 2 ++ tests/epoch.rs | 7 ++++-- 5 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/epoch.rs b/src/epoch.rs index 7309b99..f09b2ee 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -13,7 +13,7 @@ use crate::parser::Token; use crate::{ Errors, TimeScale, BDT_REF_EPOCH, DAYS_PER_YEAR_NLD, ET_EPOCH_S, GPST_REF_EPOCH, GST_REF_EPOCH, J1900_OFFSET, J2000_TO_J1900_DURATION, MJD_OFFSET, NANOSECONDS_PER_MICROSECOND, - NANOSECONDS_PER_MILLISECOND, NANOSECONDS_PER_SECOND_U32, SECONDS_PER_DAY, UNIX_REF_EPOCH, + NANOSECONDS_PER_MILLISECOND, NANOSECONDS_PER_SECOND_U32, UNIX_REF_EPOCH, }; use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use core::fmt; @@ -1191,15 +1191,20 @@ impl Epoch { /// Builds an Epoch from given `week`: elapsed weeks counter into the desired Time scale, and "ns" amount of nanoseconds since closest Sunday Midnight. /// For example, this is how GPS vehicles describe a GPST epoch. #[must_use] - pub fn from_time_of_week(week: u32, ns: u64, ts: TimeScale) -> Self { - let week = Duration::from_seconds(week as f64 * SECONDS_PER_DAY * Weekday::DAYS_PER_WEEK); - Self::from_duration(week + (ns as f64) * Unit::Nanosecond, ts) + pub fn from_time_of_week(week: u32, nanoseconds: u64, ts: TimeScale) -> Self { + let duration = i64::from(week) * Weekday::DAYS_PER_WEEK_I64 * Unit::Day + + Duration::from_parts(0, nanoseconds); + let gh_187 = match ts { + TimeScale::UTC | TimeScale::TT | TimeScale::TAI => 1.0 * Unit::Day, + _ => Duration::ZERO, + }; + Self::from_duration(duration - gh_187, ts) } #[must_use] /// Builds an UTC Epoch from given `week`: elapsed weeks counter and "ns" amount of nanoseconds since closest Sunday Midnight. - pub fn from_time_of_week_utc(week: u32, ns: u64) -> Self { - Self::from_time_of_week(week, ns, TimeScale::UTC) + pub fn from_time_of_week_utc(week: u32, nanoseconds: u64) -> Self { + Self::from_time_of_week(week, nanoseconds, TimeScale::UTC) } } @@ -2173,6 +2178,7 @@ impl Epoch { Self::compute_gregorian(self.to_tai_duration()) } + #[must_use] /// Floors this epoch to the closest provided duration /// /// # Example @@ -2195,6 +2201,7 @@ impl Epoch { Self::from_duration(self.to_duration().floor(duration), self.time_scale) } + #[must_use] /// Ceils this epoch to the closest provided duration in the TAI time system /// /// # Example @@ -2218,6 +2225,7 @@ impl Epoch { Self::from_duration(self.to_duration().ceil(duration), self.time_scale) } + #[must_use] /// Rounds this epoch to the closest provided duration in TAI /// /// # Example @@ -2234,6 +2242,7 @@ impl Epoch { Self::from_duration(self.to_duration().round(duration), self.time_scale) } + #[must_use] /// Copies this epoch and sets it to the new time scale provided. pub fn in_time_scale(&self, new_time_scale: TimeScale) -> Self { let mut me = *self; @@ -2241,14 +2250,16 @@ impl Epoch { me } + #[must_use] /// Converts this epoch into the time of week, represented as a rolling week counter into that time scale and the number of nanoseconds since closest Sunday midnight into that week. /// This is usually how GNSS receivers describe a timestamp. pub fn to_time_of_week(&self) -> (u32, u64) { // wk: rolling week counter into timescale // fractional days in this time scale - let days = self.to_duration().to_unit(Unit::Day); - let wk = (days / Weekday::DAYS_PER_WEEK).floor() as u32; - // tow: number of nanoseconds between self and previous sunday midnight / start of week + let wk = div_euclid_f64( + self.to_duration().to_unit(Unit::Day), + Weekday::DAYS_PER_WEEK, + ); let mut start_of_week = self.previous_weekday_at_midnight(Weekday::Sunday); let ref_epoch = self.time_scale.ref_epoch(); // restrict start of week/sunday to the start of the time scale @@ -2256,32 +2267,21 @@ impl Epoch { start_of_week = ref_epoch; } let dw = *self - start_of_week; // difference in weekdays [0..6] - (wk, dw.nanoseconds) + (wk as u32, dw.nanoseconds) } + #[must_use] /// Returns the weekday in provided time scale **ASSUMING** that the reference epoch of that time scale is a Monday. /// You _probably_ do not want to use this. You probably either want `weekday()` or `weekday_utc()`. /// Several time scales do _not_ have a reference day that's on a Monday, e.g. BDT. pub fn weekday_in_time_scale(&self, time_scale: TimeScale) -> Weekday { - #[cfg(feature = "std")] - { - (self - .to_duration_in_time_scale(time_scale) - .to_unit(Unit::Day) - .rem_euclid(Weekday::DAYS_PER_WEEK) - .floor() as u8) - .into() - } - #[cfg(not(feature = "std"))] - { - // The rem_euclid call of num_traits requires a pointer not a value. - (self - .to_duration_in_time_scale(time_scale) - .to_unit(Unit::Day) - .rem_euclid(&Weekday::DAYS_PER_WEEK) - .floor() as u8) - .into() - } + (rem_euclid_f64( + self.to_duration_in_time_scale(time_scale) + .to_unit(Unit::Day), + Weekday::DAYS_PER_WEEK, + ) + .floor() as u8) + .into() } /// Returns weekday (uses the TAI representation for this calculation). diff --git a/src/lib.rs b/src/lib.rs index f9ce401..1bce8a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,6 +60,24 @@ pub const J2000_TO_J1900_DURATION: Duration = Duration { nanoseconds: 3_155_716_800_000_000_000, }; +/// The Ephemeris Time reference epoch J2000. +pub const J2000_REF_EPOCH_ET: Epoch = Epoch { + duration_since_j1900_tai: Duration { + centuries: 0, + nanoseconds: 3_155_716_767_816_072_748, + }, + time_scale: TimeScale::ET, +}; + +/// The Dynamic Barycentric Time reference epoch J2000. +pub const J2000_REF_EPOCH_TDB: Epoch = Epoch { + duration_since_j1900_tai: Duration { + centuries: 0, + nanoseconds: 3_155_716_767_816_072_704, + }, + time_scale: TimeScale::ET, +}; + mod parser; mod epoch; diff --git a/src/timescale.rs b/src/timescale.rs index d9a0cb3..b4673ec 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -17,7 +17,10 @@ use serde_derive::{Deserialize, Serialize}; use core::fmt; use core::str::FromStr; -use crate::{Duration, Epoch, Errors, ParsingErrors, J2000_TO_J1900_DURATION, SECONDS_PER_DAY}; +use crate::{ + Duration, Epoch, Errors, ParsingErrors, J2000_REF_EPOCH_ET, J2000_REF_EPOCH_TDB, + J2000_TO_J1900_DURATION, SECONDS_PER_DAY, +}; /// The J1900 reference epoch (1900-01-01 at noon) TAI. pub const J1900_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration::ZERO); @@ -105,13 +108,17 @@ impl TimeScale { pub const fn is_gnss(&self) -> bool { matches!(self, Self::GPST | Self::GST | Self::BDT) } + /// Returns Reference Epoch (t(0)) for given timescale pub const fn ref_epoch(&self) -> Epoch { match self { Self::GPST => GPST_REF_EPOCH, Self::GST => GST_REF_EPOCH, Self::BDT => BDT_REF_EPOCH, - _ => J1900_REF_EPOCH, + Self::ET => J2000_REF_EPOCH_ET, + 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, } } } diff --git a/src/weekday.rs b/src/weekday.rs index 0f3a160..9c1f7ae 100644 --- a/src/weekday.rs +++ b/src/weekday.rs @@ -44,6 +44,8 @@ impl Weekday { const MAX: u8 = 7; /// Trivial, but avoid magic numbers. pub(crate) const DAYS_PER_WEEK: f64 = 7.0; + /// Trivial, but avoid magic numbers. + pub(crate) const DAYS_PER_WEEK_I64: i64 = 7; } impl From for Weekday { diff --git a/tests/epoch.rs b/tests/epoch.rs index 3a6ad9d..d5b6a3c 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -1473,9 +1473,12 @@ fn test_time_of_week() { assert_eq!(epoch.to_gregorian_utc(), (2022, 12, 01, 00, 00, 00, 00)); assert_eq!(epoch.to_time_of_week(), (2238, 345_618_000_000_000)); - let epoch_utc = epoch.in_time_scale(TimeScale::UTC); + let epoch_utc = epoch.in_time_scale(TimeScale::TT); let (utc_wk, utc_tow) = epoch_utc.to_time_of_week(); - assert_eq!(Epoch::from_time_of_week_utc(utc_wk, utc_tow), epoch_utc); + assert_eq!( + Epoch::from_time_of_week(utc_wk, utc_tow, TimeScale::TT), + epoch_utc + ); // 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);