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

Add NTP64::as_secs_f64() #13

Merged
merged 3 commits into from
Sep 15, 2023
Merged
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
55 changes: 51 additions & 4 deletions src/ntp64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,21 @@ const NANO_PER_SEC: u64 = 1_000_000_000;
/// A NTP 64-bits format as specified in
/// [RFC-5909](https://tools.ietf.org/html/rfc5905#section-6)
///
/// The first 32-bits part is the number of second since the EPOCH of the physical clock,
/// and the second 32-bits part is the fraction of second.
/// In case it's part of a [`crate::Timestamp`] generated by an [`crate::HLC`] the last few bits are replaced
/// by the HLC logical counter. The size of this counter currently hard-coded in [`crate::CSIZE`].
/// ```text
/// 0 1 2 3
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | Seconds |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// | Fraction |
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/// ```
///
/// The 1st 32-bits part is the number of second since the EPOCH of the physical clock,
/// and the 2nd 32-bits part is the fraction of second.
/// In case it's part of a [`crate::Timestamp`] generated by an [`crate::HLC`] the last few bits
/// of the Fraction part are replaced by the HLC logical counter.
/// The size of this counter currently hard-coded as [`crate::CSIZE`].
///
/// Note that this timestamp in actually similar to a [`std::time::Duration`], as it doesn't
/// define an EPOCH. Only the [`NTP64::to_system_time()`] and [`std::fmt::Display::fmt()`] operations assume that
Expand All @@ -53,6 +64,19 @@ impl NTP64 {
self.0
}

/// Returns this NTP64 as a f64 in seconds.
///
/// The decimal part of the f64 is the result of a division of NTP64's Fraction part by 2^32.
/// As a consequence, the even if resulting f64 seems to have a precision lower than 1 nanosecond,
/// that's not the case. It still has the precision of the time source
/// (3.5 nanosecond if generated by [`crate::HLC`]).
#[inline]
pub fn as_secs_f64(&self) -> f64 {
let secs: f64 = self.as_secs() as f64;
let subsec: f64 = ((self.0 & FRAC_MASK) as f64) / FRAC_PER_SEC as f64;
secs + subsec
}

/// Returns the 32-bits seconds part.
#[inline]
pub fn as_secs(&self) -> u32 {
Expand Down Expand Up @@ -232,3 +256,26 @@ impl FromStr for NTP64 {
pub struct ParseNTP64Error {
pub cause: String,
}

mod tests {

#[test]
fn as_secs_f64() {
use crate::*;

let epoch = NTP64::default();
assert_eq!(epoch.as_secs_f64(), 0f64);

let epoch_plus_1 = NTP64(1);
assert!(epoch_plus_1 > epoch);
assert!(epoch_plus_1.as_secs_f64() > epoch.as_secs_f64());

// test that Timestamp precision is less than announced (3.5ns) in README.md
let epoch_plus_counter_max = NTP64(CMASK);
println!(
"Time precision = {} ns",
epoch_plus_counter_max.as_secs_f64() * (ntp64::NANO_PER_SEC as f64)
);
assert!(epoch_plus_counter_max.as_secs_f64() < 0.0000000035f64);
}
}