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