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