1 /*
2  * Copyright (c) 2019 Peter Bigot Consulting, LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Utilities supporting operation on time data structures.
10  *
11  * POSIX defines gmtime() to convert from time_t to struct tm, but all
12  * inverse transformations are non-standard or require access to time
13  * zone information.  timeutil_timegm() implements the functionality
14  * of the GNU extension timegm() function, but changes the error value
15  * as @c EOVERFLOW is not a standard C error identifier.
16  *
17  * timeutil_timegm64() is provided to support full precision
18  * conversion on platforms where @c time_t is limited to 32 bits.
19  */
20 
21 #ifndef ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
22 #define ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
23 
24 #include <time.h>
25 
26 #include <zephyr/types.h>
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 /**
33  * @defgroup timeutil_apis Time Utility APIs
34  * @defgroup timeutil_repr_apis Time Representation APIs
35  * @ingroup timeutil_apis
36  * @{
37  */
38 
39 /**
40  * @brief Convert broken-down time to a POSIX epoch offset in seconds.
41  *
42  * @param tm pointer to broken down time.
43  *
44  * @return the corresponding time in the POSIX epoch time scale.
45  *
46  * @see http://man7.org/linux/man-pages/man3/timegm.3.html
47  */
48 int64_t timeutil_timegm64(const struct tm *tm);
49 
50 /**
51  * @brief Convert broken-down time to a POSIX epoch offset in seconds.
52  *
53  * @param tm pointer to broken down time.
54  *
55  * @return the corresponding time in the POSIX epoch time scale.  If
56  * the time cannot be represented then @c (time_t)-1 is returned and
57  * @c errno is set to @c ERANGE`.
58  *
59  * @see http://man7.org/linux/man-pages/man3/timegm.3.html
60  */
61 time_t timeutil_timegm(const struct tm *tm);
62 
63 /**
64  * @}
65  * @defgroup timeutil_sync_apis Time Synchronization APIs
66  * @ingroup timeutil_apis
67  * @{
68  */
69 
70 /**
71  * @brief Immutable state for synchronizing two clocks.
72  *
73  * Values required to convert durations between two time scales.
74  *
75  * @note The accuracy of the translation and calculated skew between sources
76  * depends on the resolution of these frequencies.  A reference frequency with
77  * microsecond or nanosecond resolution would produce the most accurate
78  * tracking when the local reference is the Zephyr tick counter.  A reference
79  * source like an RTC chip with 1 Hz resolution requires a much larger
80  * interval between sampled instants to detect relative clock drift.
81  */
82 struct timeutil_sync_config {
83 	/** The nominal instance counter rate in Hz.
84 	 *
85 	 * This value is assumed to be precise, but may drift depending on
86 	 * the reference clock source.
87 	 *
88 	 * The value must be positive.
89 	 */
90 	uint32_t ref_Hz;
91 
92 	/** The nominal local counter rate in Hz.
93 	 *
94 	 * This value is assumed to be inaccurate but reasonably stable.  For
95 	 * a local clock driven by a crystal oscillator an error of 25 ppm is
96 	 * common; for an RC oscillator larger errors should be expected.  The
97 	 * timeutil_sync infrastructure can calculate the skew between the
98 	 * local and reference clocks and apply it when converting between
99 	 * time scales.
100 	 *
101 	 * The value must be positive.
102 	 */
103 	uint32_t local_Hz;
104 };
105 
106 /**
107  * @brief Representation of an instant in two time scales.
108  *
109  * Capturing the same instant in two time scales provides a
110  * registration point that can be used to convert between those time
111  * scales.
112  */
113 struct timeutil_sync_instant {
114 	/** An instant in the reference time scale.
115 	 *
116 	 * This must never be zero in an initialized timeutil_sync_instant
117 	 * object.
118 	 */
119 	uint64_t ref;
120 
121 	/** The corresponding instance in the local time scale.
122 	 *
123 	 * This may be zero in a valid timeutil_sync_instant object.
124 	 */
125 	uint64_t local;
126 };
127 
128 /**
129  * @brief State required to convert instants between time scales.
130  *
131  * This state in conjunction with functions that manipulate it capture
132  * the offset information necessary to convert between two timescales
133  * along with information that corrects for skew due to inaccuracies
134  * in clock rates.
135  *
136  * State objects should be zero-initialized before use.
137  */
138 struct timeutil_sync_state {
139 	/** Pointer to reference and local rate information. */
140 	const struct timeutil_sync_config *cfg;
141 
142 	/** The base instant in both time scales. */
143 	struct timeutil_sync_instant base;
144 
145 	/** The most recent instant in both time scales.
146 	 *
147 	 * This is captured here to provide data for skew calculation.
148 	 */
149 	struct timeutil_sync_instant latest;
150 
151 	/** The scale factor used to correct for clock skew.
152 	 *
153 	 * The nominal rate for the local counter is assumed to be
154 	 * inaccurate but stable, i.e. it will generally be some
155 	 * parts-per-million faster or slower than specified.
156 	 *
157 	 * A duration in observed local clock ticks must be multiplied by
158 	 * this value to produce a duration in ticks of a clock operating at
159 	 * the nominal local rate.
160 	 *
161 	 * A zero value indicates that the skew has not been initialized.
162 	 * If the value is zero when #base is initialized the skew will be
163 	 * set to 1.  Otherwise the skew is assigned through
164 	 * timeutil_sync_state_set_skew().
165 	 */
166 	float skew;
167 };
168 
169 /**
170  * @brief Record a new instant in the time synchronization state.
171  *
172  * Note that this updates only the latest persisted instant.  The skew
173  * is not adjusted automatically.
174  *
175  * @param tsp pointer to a timeutil_sync_state object.
176  *
177  * @param inst the new instant to be recorded.  This becomes the base
178  * instant if there is no base instant, otherwise the value must be
179  * strictly after the base instant in both the reference and local
180  * time scales.
181  *
182  * @retval 0 if installation succeeded in providing a new base
183  * @retval 1 if installation provided a new latest instant
184  * @retval -EINVAL if the new instant is not compatible with the base instant
185  */
186 int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
187 			       const struct timeutil_sync_instant *inst);
188 
189 /**
190  * @brief Update the state with a new skew and possibly base value.
191  *
192  * Set the skew from a value retrieved from persistent storage, or
193  * calculated based on recent skew estimations including from
194  * timeutil_sync_estimate_skew().
195  *
196  * Optionally update the base timestamp.  If the base is replaced the
197  * latest instant will be cleared until timeutil_sync_state_update() is
198  * invoked.
199  *
200  * @param tsp pointer to a time synchronization state.
201  *
202  * @param skew the skew to be used.  The value must be positive and
203  * shouldn't be too far away from 1.
204  *
205  * @param base optional new base to be set.  If provided this becomes
206  * the base timestamp that will be used along with skew to convert
207  * between reference and local timescale instants.  Setting the base
208  * clears the captured latest value.
209  *
210  * @return 0 if skew was updated
211  * @return -EINVAL if skew was not valid
212  */
213 int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
214 				 const struct timeutil_sync_instant *base);
215 
216 /**
217  * @brief Estimate the skew based on current state.
218  *
219  * Using the base and latest syncpoints from the state determine the
220  * skew of the local clock relative to the reference clock.  See
221  * timeutil_sync_state::skew.
222  *
223  * @param tsp pointer to a time synchronization state.  The base and latest
224  * syncpoints must be present and the latest syncpoint must be after
225  * the base point in the local time scale.
226  *
227  * @return the estimated skew, or zero if skew could not be estimated.
228  */
229 float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp);
230 
231 /**
232  * @brief Interpolate a reference timescale instant from a local
233  * instant.
234  *
235  * @param tsp pointer to a time synchronization state.  This must have a base
236  * and a skew installed.
237  *
238  * @param local an instant measured in the local timescale.  This may
239  * be before or after the base instant.
240  *
241  * @param refp where the corresponding instant in the reference
242  * timescale should be stored.  A negative interpolated reference time
243  * produces an error.  If interpolation fails the referenced object is
244  * not modified.
245  *
246  * @retval 0 if interpolated using a skew of 1
247  * @retval 1 if interpolated using a skew not equal to 1
248  * @retval -EINVAL
249  *   * the times synchronization state is not adequately initialized
250  *   * @p refp is null
251  * @retval -ERANGE the interpolated reference time would be negative
252  */
253 int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
254 				 uint64_t local, uint64_t *refp);
255 
256 /**
257  * @brief Interpolate a local timescale instant from a reference
258  * instant.
259  *
260  * @param tsp pointer to a time synchronization state.  This must have a base
261  * and a skew installed.
262  *
263  * @param ref an instant measured in the reference timescale.  This
264  * may be before or after the base instant.
265  *
266  * @param localp where the corresponding instant in the local
267  * timescale should be stored.  An interpolated value before local
268  * time 0 is provided without error.  If interpolation fails the
269  * referenced object is not modified.
270  *
271  * @retval 0 if successful with a skew of 1
272  * @retval 1 if successful with a skew not equal to 1
273  * @retval -EINVAL
274  *   * the time synchronization state is not adequately initialized
275  *   * @p refp is null
276  */
277 int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
278 				 uint64_t ref, int64_t *localp);
279 
280 /**
281  * @brief Convert from a skew to an error in parts-per-billion.
282  *
283  * A skew of 1.0 has zero error.  A skew less than 1 has a positive
284  * error (clock is faster than it should be).  A skew greater than one
285  * has a negative error (clock is slower than it should be).
286  *
287  * Note that due to the limited precision of @c float compared with @c
288  * double the smallest error that can be represented is about 120 ppb.
289  * A "precise" time source may have error on the order of 2000 ppb.
290  *
291  * A skew greater than 3.14748 may underflow the 32-bit
292  * representation; this represents a clock running at less than 1/3
293  * its nominal rate.
294  *
295  * @return skew error represented as parts-per-billion, or INT32_MIN
296  * if the skew cannot be represented in the return type.
297  */
298 int32_t timeutil_sync_skew_to_ppb(float skew);
299 
300 #ifdef __cplusplus
301 }
302 #endif
303 
304 /**
305  * @}
306  */
307 
308 #endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */
309