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 #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 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 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 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 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 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 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 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 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 struct timeutil_sync_state { 143 /** Pointer to reference and local rate information. */ 144 const struct timeutil_sync_config *cfg; 145 146 /** The base instant in both time scales. */ 147 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 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 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 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 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 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 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 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 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_ */ 313