Skip to content

Commit

Permalink
timeofweek: introduce time of week construction and conversion
Browse files Browse the repository at this point in the history
* (wk, tow) is usually how an epoch is expressed
by a GNSS receiver, usually in a GNSS timescale.
Wk is a rolling counter, and tow is the amount of seconds since Sunday midnight
of that week.

`tow` is usually expressed in [s] or [ms] by receivers,
we use [ns] here, to maintain crate consistency and never be limited.

Signed-off-by: Guillaume W. Bres <guillaume.bressaix@gmail.com>
  • Loading branch information
gwbres committed Dec 2, 2022
1 parent 2e3b8ad commit 0e79e1a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
39 changes: 38 additions & 1 deletion src/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions src/timescale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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. <https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29>.
pub const GPST_REF_EPOCH: Epoch = Epoch::from_tai_duration(Duration {
centuries: 0,
Expand Down
30 changes: 30 additions & 0 deletions tests/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

0 comments on commit 0e79e1a

Please sign in to comment.