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