Monado OpenXR Runtime
Loading...
Searching...
No Matches
m_clock_tracking.h
Go to the documentation of this file.
1// Copyright 2022, Collabora, Ltd.
2// Copyright 2024, Jan Schmidt
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Helpers to estimate offsets between clocks
7 * @author Mateo de Mayo <mateo.demayo@collabora.com>
8 * @ingroup aux_math
9 */
10
11#pragma once
12
13#include "util/u_time.h"
14
15
16#ifdef __cplusplus
17extern "C" {
18#endif
19
20/*!
21 * Storage for a 32-bit unsigned -> 64-bit signed clock extension.
22 */
24{
25 //! The last 32-bit unsigned value.
26 uint32_t last;
27 //! The current 64-bit signed clock value.
29};
30
31/*!
32 * Extends a 32-bit unsigned clock into a 64 bit signed clock. Tolerant to slight backwards jitters.
33 *
34 * @param extender The storage for this extender.
35 * @param update The new clock sample to update with.
36 * @param ns_per_unit The amount of nanoseconds per unit of clock.
37 * @return The clock delta, in nanoseconds.
38 */
39static inline time_duration_ns
40m_clock_bit_extender_update(struct m_clock_bit_extender *extender, uint32_t update, int64_t ns_per_unit)
41{
42 time_duration_ns delta = (int32_t)(update - extender->last) * ns_per_unit;
43 extender->clock += delta;
44 extender->last = update;
45 return delta;
46}
47
48/*!
49 * Extends a 32-bit unsigned clock sample onto the extender's 64-bit clock, without updating the extender.
50 *
51 * Useful for occasional samples of the same clock from another source, which should not move the extender.
52 *
53 * @param extender The storage for this extender.
54 * @param sample The clock sample to extend.
55 * @param ns_per_unit The amount of nanoseconds per unit of clock.
56 * @return The extended clock value, in nanoseconds.
57 */
58static inline timepoint_ns
59m_clock_bit_extender_peek(const struct m_clock_bit_extender *extender, uint32_t sample, int64_t ns_per_unit)
60{
61 return extender->clock + (int32_t)(sample - extender->last) * ns_per_unit;
62}
63
64/*!
65 * Helper to estimate the offset between two clocks using exponential smoothing.
66 *
67 * Given a sample from two timestamp domains A and B that should have been
68 * sampled as close as possible, together with an estimate of the offset between
69 * A clock and B clock (or zero), it applies a smoothing average on the
70 * estimated offset and returns @p a in B clock.
71 *
72 * This estimator can be used when clock observations are arriving with a low
73 * delay and small jitter, or when accuracy is less important (on the order of
74 * the jitter that is present). It is very computationally cheap.
75 *
76 * @param freq About how many times per second this function is called. Helps setting a good decay value.
77 * @param a Timestamp in clock A of the event
78 * @param b Timestamp in clock B of the event
79 * @param[in,out] inout_a2b Pointer to the current offset estimate from A to B, or 0 if unknown.
80 * Value pointed-to will be updated.
81 * @return timepoint_ns @p a in B clock
82 */
83static inline timepoint_ns
85{
86 // This formulation of exponential filtering uses a fixed-precision integer for the
87 // alpha value and operates on the delta between the old and new a2b to avoid
88 // precision / overflow problems.
89
90 // Totally arbitrary way of computing alpha, if you have a better one, replace it
91 const time_duration_ns alpha = 1000 * (1.0 - 12.5 / freq); // Weight to put on accumulated a2b
92 time_duration_ns old_a2b = *inout_a2b;
93 time_duration_ns got_a2b = b - a;
94 time_duration_ns new_a2b;
95 if (old_a2b == 0) { // a2b has not been set yet
96 new_a2b = got_a2b;
97 } else {
98 new_a2b = ((old_a2b - got_a2b) * alpha) / 1000 + got_a2b;
99 }
100 *inout_a2b = new_a2b;
101 return a + new_a2b;
102}
103
104/*!
105 * Helper to estimate the offset between two clocks using a windowed
106 * minimum-skew estimation plus exponential smoothing. The algorithm
107 * tracks the smallest offset within the window, on the theory that
108 * minima represent samples with the lowest transmission delay and jitter.
109 *
110 * More computationally intensive than the simple m_clock_offset_a2b estimator,
111 * but can estimate a clock with accuracy in the microsecond range
112 * even in the presence of 10s of milliseconds of jitter.
113 *
114 * Based on the approach in Dominique Fober, Yann Orlarey, Stéphane Letz.
115 * Real Time Clock Skew Estimation over Network Delays. [Technical Report] GRAME. 2005.
116 * https://hal.science/hal-02158803/document
117 */
119
120/*!
121 * Allocate a struct m_clock_windowed_skew_tracker with a
122 * window of @param window_samples samples.
123 */
125m_clock_windowed_skew_tracker_alloc(const size_t window_samples);
126void
127m_clock_windowed_skew_tracker_reset(struct m_clock_windowed_skew_tracker *t);
128void
129m_clock_windowed_skew_tracker_destroy(struct m_clock_windowed_skew_tracker *t);
130
131void
132m_clock_windowed_skew_tracker_push(struct m_clock_windowed_skew_tracker *t,
133 const timepoint_ns local_ts,
134 const timepoint_ns remote_ts);
135
136bool
137m_clock_windowed_skew_tracker_to_local(struct m_clock_windowed_skew_tracker *t,
138 const timepoint_ns remote_ts,
139 timepoint_ns *local_ts);
140bool
141m_clock_windowed_skew_tracker_to_remote(struct m_clock_windowed_skew_tracker *t,
142 const timepoint_ns local_ts,
143 timepoint_ns *remote_ts);
144bool
145m_clock_windowed_skew_tracker_get_skew(struct m_clock_windowed_skew_tracker *t, time_duration_ns *skew_ns);
146
147#ifdef __cplusplus
148}
149#endif
int64_t timepoint_ns
Integer timestamp type.
Definition u_time.h:77
int64_t time_duration_ns
Integer duration type in nanoseconds.
Definition u_time.h:88
static timepoint_ns m_clock_offset_a2b(float freq, timepoint_ns a, timepoint_ns b, time_duration_ns *inout_a2b)
Helper to estimate the offset between two clocks using exponential smoothing.
Definition m_clock_tracking.h:84
static timepoint_ns m_clock_bit_extender_peek(const struct m_clock_bit_extender *extender, uint32_t sample, int64_t ns_per_unit)
Extends a 32-bit unsigned clock sample onto the extender's 64-bit clock, without updating the extende...
Definition m_clock_tracking.h:59
struct m_clock_windowed_skew_tracker * m_clock_windowed_skew_tracker_alloc(const size_t window_samples)
Allocate a struct m_clock_windowed_skew_tracker with a window of.
Definition m_clock_tracking.c:70
static time_duration_ns m_clock_bit_extender_update(struct m_clock_bit_extender *extender, uint32_t update, int64_t ns_per_unit)
Extends a 32-bit unsigned clock into a 64 bit signed clock.
Definition m_clock_tracking.h:40
Storage for a 32-bit unsigned -> 64-bit signed clock extension.
Definition m_clock_tracking.h:24
uint32_t last
The last 32-bit unsigned value.
Definition m_clock_tracking.h:26
timepoint_ns clock
The current 64-bit signed clock value.
Definition m_clock_tracking.h:28
Definition m_clock_tracking.c:40
Time-keeping: a clock that is steady, convertible to system time, and ideally high-resolution.