@@ -53,6 +53,236 @@ int64_t timeutil_timegm64(const struct tm *tm);
5353 */
5454time_t timeutil_timegm (const struct tm * tm );
5555
56+ /**
57+ * @brief Immutable state for synchronizing two clocks.
58+ *
59+ * Values required to convert durations between two time scales.
60+ *
61+ * @note The accuracy of the translation and calculated skew between sources
62+ * depends on the resolution of these frequencies. A reference frequency with
63+ * microsecond or nanosecond resolution would produce the most accurate
64+ * tracking when the local reference is the Zephyr tick counter. A reference
65+ * source like an RTC chip with 1 Hz resolution requires a much larger
66+ * interval between sampled instants to detect relative clock drift.
67+ */
68+ struct timeutil_sync_config {
69+ /** The nominal instance counter rate in Hz.
70+ *
71+ * This value is assumed to be precise, but may drift depending on
72+ * the reference clock source.
73+ *
74+ * The value must be positive.
75+ */
76+ uint32_t ref_Hz ;
77+
78+ /** The nominal local counter rate in Hz.
79+ *
80+ * This value is assumed to be inaccurate but reasonably stable. For
81+ * a local clock driven by a crystal oscillator an error of 25 ppm is
82+ * common; for an RC oscillator larger errors should be expected. The
83+ * timeutil_sync infrastructure can calculate the skew between the
84+ * local and reference clocks and apply it when converting between
85+ * time scales.
86+ *
87+ * The value must be positive.
88+ */
89+ uint32_t local_Hz ;
90+ };
91+
92+ /**
93+ * @brief Representation of an instant in two time scales.
94+ *
95+ * Capturing the same instant in two time scales provides a
96+ * registration point that can be used to convert between those time
97+ * scales.
98+ */
99+ struct timeutil_sync_instant {
100+ /** An instant in the reference time scale.
101+ *
102+ * This must never be zero in an initialized timeutil_sync_instant
103+ * object.
104+ */
105+ uint64_t ref ;
106+
107+ /** The corresponding instance in the local time scale.
108+ *
109+ * This may be zero in a valid timeutil_sync_instant object.
110+ */
111+ uint64_t local ;
112+ };
113+
114+ /**
115+ * @brief State required to convert instants between time scales.
116+ *
117+ * This state in conjunction with functions that manipulate it capture
118+ * the offset information necessary to convert between two timescales
119+ * along with information that corrects for skew due to inaccuracies
120+ * in clock rates.
121+ *
122+ * State objects should be zero-initialized before use.
123+ */
124+ struct timeutil_sync_state {
125+ /** Pointer to reference and local rate information. */
126+ const struct timeutil_sync_config * cfg ;
127+
128+ /** The base instant in both time scales. */
129+ struct timeutil_sync_instant base ;
130+
131+ /** The most recent instant in both time scales.
132+ *
133+ * This is captured here to provide data for skew calculation.
134+ */
135+ struct timeutil_sync_instant latest ;
136+
137+ /** The scale factor used to correct for clock skew.
138+ *
139+ * The nominal rate for the local counter is assumed to be
140+ * inaccurate but stable, i.e. it will generally be some
141+ * parts-per-million faster or slower than specified.
142+ *
143+ * A duration in observed local clock ticks must be multiplied by
144+ * this value to produce a duration in ticks of a clock operating at
145+ * the nominal local rate.
146+ *
147+ * A zero value indicates that the skew has not been initialized.
148+ * If the value is zero when #base is initialized the skew will be
149+ * set to 1. Otherwise the skew is assigned through
150+ * timeutil_sync_state_set_skew().
151+ */
152+ float skew ;
153+ };
154+
155+ /**
156+ * @brief Record a new instant in the time synchronization state.
157+ *
158+ * Note that this updates only the latest persisted instant. The skew
159+ * is not adjusted automatically.
160+ *
161+ * @param tsp pointer to a timeutil_sync_state object.
162+ *
163+ * @param inst the new instant to be recorded. This becomes the base
164+ * instant if there is no base instant, otherwise the value must be
165+ * strictly after the base instant in both the reference and local
166+ * time scales.
167+ *
168+ * @retval 0 if installation succeeded in providing a new base
169+ * @retval 1 if installation provided a new latest instant
170+ * @retval -EINVAL if the new instant is not compatible with the base instant
171+ */
172+ int timeutil_sync_state_update (struct timeutil_sync_state * tsp ,
173+ const struct timeutil_sync_instant * inst );
174+
175+ /**
176+ * @brief Update the state with a new skew and possibly base value.
177+ *
178+ * Set the skew from a value retrieved from persistent storage, or
179+ * calculated based on recent skew estimations including from
180+ * timeutil_sync_estimate_skew().
181+ *
182+ * Optionally update the base timestamp. If the base is replaced the
183+ * latest instant will be cleared until timeutil_sync_state_update() is
184+ * invoked.
185+ *
186+ * @param tsp pointer to a time synchronization state.
187+ *
188+ * @param skew the skew to be used. The value must be positive and
189+ * shouldn't be too far away from 1.
190+ *
191+ * @param base optional new base to be set. If provided this becomes
192+ * the base timestamp that will be used along with skew to convert
193+ * between reference and local timescale instants. Setting the base
194+ * clears the captured latest value.
195+ *
196+ * @return 0 if skew was updated
197+ * @return -EINVAL if skew was not valid
198+ */
199+ int timeutil_sync_state_set_skew (struct timeutil_sync_state * tsp , float skew ,
200+ const struct timeutil_sync_instant * base );
201+
202+ /**
203+ * @brief Estimate the skew based on current state.
204+ *
205+ * Using the base and latest syncpoints from the state determine the
206+ * skew of the local clock relative to the reference clock. See
207+ * timeutil_sync_state::skew.
208+ *
209+ * @param tsp pointer to a time synchronization state. The base and latest
210+ * syncpoints must be present and the latest syncpoint must be after
211+ * the base point in the local time scale.
212+ *
213+ * @return the estimated skew, or zero if skew could not be estimated.
214+ */
215+ float timeutil_sync_estimate_skew (const struct timeutil_sync_state * tsp );
216+
217+ /**
218+ * @brief Interpolate a reference timescale instant from a local
219+ * instant.
220+ *
221+ * @param tsp pointer to a time synchronization state. This must have a base
222+ * and a skew installed.
223+ *
224+ * @param local an instant measured in the local timescale. This may
225+ * be before or after the base instant.
226+ *
227+ * @param refp where the corresponding instant in the reference
228+ * timescale should be stored. A negative interpolated reference time
229+ * produces an error. If interpolation fails the referenced object is
230+ * not modified.
231+ *
232+ * @retval 0 if interpolated using a skew of 1
233+ * @retval 1 if interpolated using a skew not equal to 1
234+ * @retval -EINVAL
235+ * * the times synchronization state is not adequately initialized
236+ * * @p refp is null
237+ * @retval -ERANGE the interpolated reference time would be negative
238+ */
239+ int timeutil_sync_ref_from_local (const struct timeutil_sync_state * tsp ,
240+ uint64_t local , uint64_t * refp );
241+
242+ /**
243+ * @brief Interpolate a local timescale instant from a reference
244+ * instant.
245+ *
246+ * @param tsp pointer to a time synchronization state. This must have a base
247+ * and a skew installed.
248+ *
249+ * @param ref an instant measured in the reference timescale. This
250+ * may be before or after the base instant.
251+ *
252+ * @param localp where the corresponding instant in the local
253+ * timescale should be stored. An interpolated value before local
254+ * time 0 is provided without error. If interpolation fails the
255+ * referenced object is not modified.
256+ *
257+ * @retval 0 if successful with a skew of 1
258+ * @retval 1 if successful with a skew not equal to 1
259+ * @retval -EINVAL
260+ * * the time synchronization state is not adequately initialized
261+ * * @p refp is null
262+ */
263+ int timeutil_sync_local_from_ref (const struct timeutil_sync_state * tsp ,
264+ uint64_t ref , int64_t * localp );
265+
266+ /**
267+ * @brief Convert from a skew to an error in parts-per-billion.
268+ *
269+ * A skew of 1.0 has zero error. A skew less than 1 has a positive
270+ * error (clock is faster than it should be). A skew greater than one
271+ * has a negative error (clock is slower than it should be).
272+ *
273+ * Note that due to the limited precision of @c float compared with @c
274+ * double the smallest error that can be represented is about 120 ppb.
275+ * A "precise" time source may have error on the order of 2000 ppb.
276+ *
277+ * A skew greater than 3.14748 may underflow the 32-bit
278+ * representation; this represents a clock running at less than 1/3
279+ * its nominal rate.
280+ *
281+ * @return skew error represented as parts-per-billion, or INT32_MIN
282+ * if the skew cannot be represented in the return type.
283+ */
284+ int32_t timeutil_sync_skew_to_ppb (float skew );
285+
56286#ifdef __cplusplus
57287}
58288#endif
0 commit comments