diff --git a/src/epoch.rs b/src/epoch.rs index 79cc6816..7f4a09ab 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, UNIX_REF_EPOCH, + SECONDS_PER_DAY, NANOSECONDS_PER_MILLISECOND, NANOSECONDS_PER_SECOND_U32, UNIX_REF_EPOCH, }; use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use core::fmt; @@ -2225,6 +2225,43 @@ impl Epoch { me.time_scale = new_time_scale; me } + + /// Builds an Epoch from given `wk` week counter into the desired Time scale. + /// `ns` is the amount of nanoseconds into that week, starting on Sunday midnight of that week. + #[cfg(feature = "python")] + #[staticmethod] + pub fn from_timeofweek(wk: u32, ns: u64, ts: TimeScale) -> Self { + let week = Duration::from_seconds(wk as f64 * SECONDS_PER_DAY * 7.0); + Self::from_duration(week + (ns as f64) * Unit::Nanosecond, ts) + } + + /// Converts to "time of week" (`wk, `tow`), + /// which is usually how GNSS receivers describe a timestamp. + /// `wk` is a rolling week conter into that time scale, + /// `tow` is the number of seconds since closest Sunday midnight into that week. + pub fn to_timeofweek(&self) -> (u32, u64) { + // fractionnal days in this time scale + let days = match self.time_scale { + TimeScale::GPST => self.to_gpst_days(), + TimeScale::GST => self.to_gst_days(), + TimeScale::BDT => self.to_bdt_days(), + TimeScale::TT => self.to_tt_days(), + TimeScale::TAI => self.to_tai_days(), + TimeScale::UTC => self.to_utc_days(), + TimeScale::TDB => self.to_tdb_seconds() / SECONDS_PER_DAY, + TimeScale::ET => self.to_et_seconds() / SECONDS_PER_DAY, + }; + let wk = (days / 7.0) as u32; + let residuals = (Duration::from_f64(days + 7.0, Unit::Day) - Duration::from_f64(days, Unit::Day)) + .truncated_nanoseconds(); + (wk, residuals as u64)// express residuals in ns + } + + /// Returns "weekday" in TAI timescale, starting 0: Monday, ranging 6: Sunday + pub fn weekday_tai(&self) -> u8 { + // we're helped here, because J1900 was a monday :) + (self.to_tai_days() as u64).rem_euclid(7) as u8 + } // Python helpers diff --git a/src/timescale.rs b/src/timescale.rs index 55839987..2d83f395 100644 --- a/src/timescale.rs +++ b/src/timescale.rs @@ -19,6 +19,15 @@ use core::str::FromStr; use crate::{Duration, Epoch, Errors, ParsingErrors, SECONDS_PER_DAY}; +/* +/// TAI reference Monday: first Monday encountered in the TAI timescale, +/// from our reference point (or Epoch), which is defined as J1900 in TAI. +/// This is used as a reference point for weekday determination +pub const TAI_REF_MONDAY: Epoch = Epoch { + centuries: 0, // funny enough, Day 1 in 1900 tai was a monday + nanoseconds: 0, +}; */ + /// GPS reference epoch is UTC midnight between 05 January and 06 January 1980; cf. . pub const GPST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration { centuries: 0, diff --git a/tests/epoch.rs b/tests/epoch.rs index 1a7d51de..2bebd2d6 100644 --- a/tests/epoch.rs +++ b/tests/epoch.rs @@ -1337,3 +1337,33 @@ fn test_minmax() { assert_eq!(e1, e1.max(e0)); assert_eq!(e1, e0.max(e1)); } + +#[test] +fn test_weekday() { + // J1900 was a monday + let j1900 = Epoch::from_gregorian_tai_at_midnight(1900, 01, 01); + assert_eq!(j1900.weekday_tai(), 0); + // 1 nanosec into TAI: still a monday + let j1900_fractionnal = Epoch::from_gregorian_tai(1900, 01, 01, 0, 0, 0, 1); + assert_eq!(j1900.weekday_tai(), 0); + // some portion of that day: still a mon day + let j1900_fractionnal = Epoch::from_gregorian_tai(1900, 01, 01, 10, 00, 00, 123); + assert_eq!(j1900.weekday_tai(), 0); + // Day +1: tuesday + let j1901 = j1900 + Duration::from_days(1.0); + assert_eq!(j1901.weekday_tai(), 1); + // 1 ns into tuesday, still a tuesday + let j1901 = j1901 + Duration::from_nanoseconds(1.0); + assert_eq!(j1901.weekday_tai(), 1); +} + +#[test] +fn test_gnss_timeofweek() { + // GPST + // https://www.labsat.co.uk/index.php/en/gps-time-calculator + // 01/12/2022 00:00:00 => (2238, 345_618_000_000_000) + let epoch = Epoch::from_timeofweek(2238, 345_618_000_000_000, TimeScale::GPST); + assert_eq!(epoch.to_gregorian_utc(), (2022, 12, 01, 00, 00, 00, 00)); + // test reserve op + assert_eq!(epoch.to_timeofweek(), (2238, 345_618_000_000_000)); +}