| /* |
| * 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_ */ |