pub(crate) struct StreamClock {Show 14 fields
pub(crate) sample_interval_ns: u64,
pub(crate) next_first_sample_unix_ns: u64,
pub(crate) sequence: u64,
pub(crate) last_batch_samples: usize,
pub(crate) run_started: bool,
pub(crate) window_start_unix_ns: u128,
pub(crate) window_min_error_ns: Option<i128>,
pub(crate) previous_window_error_ns: Option<i128>,
pub(crate) step_windows: u32,
pub(crate) previous_raw_offset_ns: Option<i128>,
pub(crate) slewed_since_log_ns: i128,
pub(crate) anchor_checked: bool,
pub(crate) recent_offsets_ns: VecDeque<i128>,
pub(crate) windows_since_log: u32,
}Expand description
Tracks timestamp continuity for successive LabJack stream batches.
The LabJack stream API returns value batches without per-sample timestamps, so the streamer derives the first timestamp and sequence number for each published batch from the actual stream rate.
The LabJack crystal and the system clock disagree by a few ppm, which added up to about 4-5 s over a 26-day run on MU1. To stop that accumulating, the clock compares each batch’s last-sample time with the system clock and keeps the smallest difference seen in each 60 s window.
That minimum is not a clean drift measurement on a busy host. On MU1, with the camera software loading the CPU, read latency wandered by tens to hundreds of milliseconds from one minute to the next. An earlier version jumped the timeline by the full window minimum and so chased that noise, stepping the timestamps by up to 430 ms every minute. The clock therefore never jumps for ordinary offsets. It slews:
- The offset used for slewing is the smallest window minimum over the last 15 windows, because on a busy host a whole minute can pass without a fast read.
- An offset of at least 5 ms that points the same way in two consecutive
windows moves the timeline by at most 1 ms (less at high sample rates, see
StreamClock::max_slew_ns). Genuine drift is steady and gets followed; latency noise changes sign or size and mostly does not. - Only an offset of 2 s or more that persists for three windows, which is what a system clock step looks like (for example chronyd correcting a badly set clock), moves the timeline in one jump. That jump is logged.
The first batch of a run is anchored to the system clock at the moment it arrives, so whatever delay that one read had is built into the timeline. At the end of the first window the clock therefore corrects the anchor once, in full, by the smallest offset seen in that window, and logs it. After that only the rules above apply.
Slewing is summarized in the log about once an hour.
A new clock is created for every sampling run, so sequence numbers restart at 0 and the first batch of each run is anchored to the system clock again.
Fields§
§sample_interval_ns: u64Time between consecutive scans, in nanoseconds, from the actual scan rate.
next_first_sample_unix_ns: u64Expected Unix timestamp (ns) of the first sample of the next batch.
sequence: u64Sequence number that the next batch will get.
last_batch_samples: usizeSamples per channel in the most recent batch. Recorded but not read.
run_started: boolWhether the first batch has been timestamped.
window_start_unix_ns: u128System time (Unix ns) at which the current drift window started.
window_min_error_ns: Option<i128>Smallest receive-time minus last-sample-time seen in the current window, in
nanoseconds. None until the window has a measurement.
previous_window_error_ns: Option<i128>Window minimum of the previous window, in nanoseconds.
step_windows: u32Consecutive windows whose offset was at least CLOCK_STEP_THRESHOLD_NS
in the same direction.
previous_raw_offset_ns: Option<i128>This window’s own minimum from the previous window, for step detection.
slewed_since_log_ns: i128Total slew applied since the last summary log line, in nanoseconds.
anchor_checked: boolWhether the first window has ended and the initial anchor was checked.
recent_offsets_ns: VecDeque<i128>Window minima of the most recent windows, oldest first, at most
CLOCK_OFFSET_HISTORY_WINDOWS.
windows_since_log: u32Windows completed since the last summary log line.
Implementations§
Source§impl StreamClock
impl StreamClock
Sourcepub(crate) fn new(sample_interval_ns: u64) -> Self
pub(crate) fn new(sample_interval_ns: u64) -> Self
Creates a stream clock that has not yet seen a batch.
§Arguments
sample_interval_ns- Time between scans in nanoseconds, fromderive_sample_interval_ns.
Sourcepub(crate) fn max_slew_ns(&self) -> i128
pub(crate) fn max_slew_ns(&self) -> i128
Returns the largest slew allowed per window, in nanoseconds.
The smaller of CLOCK_MAX_SLEW_NS and half the sample interval, so that
even a backward slew leaves consecutive timestamps strictly increasing.
Sourcepub(crate) fn next_batch(
&mut self,
batch_samples: usize,
) -> Result<(u64, u64), LJMError>
pub(crate) fn next_batch( &mut self, batch_samples: usize, ) -> Result<(u64, u64), LJMError>
Returns the first sample timestamp and sequence for the next batch.
Reads the system clock and calls Self::next_batch_at. The first batch is
anchored so that its last sample falls at the current wall-clock time. Each
later batch starts where the previous one ended, with the drift handling
described in the type docs.
§Arguments
batch_samples- Samples per channel in the batch (scans in the batch).
§Returns
The Unix timestamp of the batch’s first sample in nanoseconds, and the batch sequence number.
§Errors
Returns an error if batch_samples is zero, if the system clock is before
the Unix epoch, or if a timestamp overflows u64.
Sourcepub(crate) fn next_batch_at(
&mut self,
batch_samples: usize,
now_ns: u64,
) -> Result<(u64, u64), LJMError>
pub(crate) fn next_batch_at( &mut self, batch_samples: usize, now_ns: u64, ) -> Result<(u64, u64), LJMError>
Self::next_batch with an explicit receive time, for testing.
For every batch after the first, it measures the receive time minus the
expected time of the batch’s last sample and keeps the window minimum. When
the window has lasted CLOCK_CHECK_WINDOW_NS, decide_adjustment
chooses whether to slew or step, the adjustment is added to this batch’s
first-sample time, and later batches continue from there. The window then
restarts at now_ns.
§Arguments
batch_samples- Samples per channel in the batch.now_ns- Receive time of the batch, Unix nanoseconds.
§Returns
The Unix timestamp of the batch’s first sample in nanoseconds, and the batch sequence number.
§Errors
Returns an error if batch_samples is zero or if the initial, corrected, or
next timestamp does not fit in u64.
Trait Implementations§
Source§impl Clone for StreamClock
impl Clone for StreamClock
Source§fn clone(&self) -> StreamClock
fn clone(&self) -> StreamClock
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more