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