Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 1 | .. _timeutil_api: |
| 2 | |
| 3 | Time Utilities |
| 4 | ############## |
| 5 | |
| 6 | Overview |
| 7 | ******** |
| 8 | |
| 9 | :ref:`kernel_timing_uptime` in Zephyr is based on the a tick counter. With |
Gerard Marull-Paretas | 260decc | 2022-02-07 17:27:43 +0100 | [diff] [blame] | 10 | the default :kconfig:option:`CONFIG_TICKLESS_KERNEL` this counter advances at a |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 11 | nominally constant rate from zero at the instant the system started. The POSIX |
| 12 | equivalent to this counter is something like ``CLOCK_MONOTONIC`` or, in Linux, |
| 13 | ``CLOCK_MONOTONIC_RAW``. :c:func:`k_uptime_get()` provides a millisecond |
| 14 | representation of this time. |
| 15 | |
| 16 | Applications often need to correlate the Zephyr internal time with external |
| 17 | time scales used in daily life, such as local time or Coordinated Universal |
| 18 | Time. These systems interpret time in different ways and may have |
| 19 | discontinuities due to `leap seconds <https://what-if.xkcd.com/26/>`__ and |
| 20 | local time offsets like daylight saving time. |
| 21 | |
| 22 | Because of these discontinuities, as well as significant inaccuracies in the |
| 23 | clocks underlying the cycle counter, the offset between time estimated from |
| 24 | the Zephyr clock and the actual time in a "real" civil time scale is not |
| 25 | constant and can vary widely over the runtime of a Zephyr application. |
| 26 | |
| 27 | The time utilities API supports: |
| 28 | |
| 29 | * :ref:`converting between time representations <timeutil_repr>` |
| 30 | * :ref:`synchronizing and aligning time scales <timeutil_sync>` |
| 31 | |
| 32 | For terminology and concepts that support these functions see |
| 33 | :ref:`timeutil_concepts`. |
| 34 | |
| 35 | Time Utility APIs |
| 36 | ***************** |
| 37 | |
| 38 | .. _timeutil_repr: |
| 39 | |
| 40 | Representation Transformation |
| 41 | ============================= |
| 42 | |
| 43 | Time scale instants can be represented in multiple ways including: |
| 44 | |
| 45 | * Seconds since an epoch. POSIX representations of time in this form include |
| 46 | ``time_t`` and ``struct timespec``, which are generally interpreted as a |
| 47 | representation of `"UNIX Time" |
| 48 | <https://tools.ietf.org/html/rfc8536#section-2>`__. |
| 49 | |
| 50 | * Calendar time as a year, month, day, hour, minutes, and seconds relative to |
| 51 | an epoch. POSIX representations of time in this form include ``struct tm``. |
| 52 | |
| 53 | Keep in mind that these are simply time representations that must be |
| 54 | interpreted relative to a time scale which may be local time, UTC, or some |
| 55 | other continuous or discontinuous scale. |
| 56 | |
| 57 | Some necessary transformations are available in standard C library |
| 58 | routines. For example, ``time_t`` measuring seconds since the POSIX EPOCH is |
| 59 | converted to ``struct tm`` representing calendar time with `gmtime() |
| 60 | <https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime.html>`__. |
| 61 | Sub-second timestamps like ``struct timespec`` can also use this to produce |
| 62 | the calendar time representation and deal with sub-second offsets separately. |
| 63 | |
| 64 | The inverse transformation is not standardized: APIs like ``mktime()`` expect |
| 65 | information about time zones. Zephyr provides this transformation with |
| 66 | :c:func:`timeutil_timegm` and :c:func:`timeutil_timegm64`. |
| 67 | |
| 68 | .. doxygengroup:: timeutil_repr_apis |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 69 | |
| 70 | .. _timeutil_sync: |
| 71 | |
| 72 | Time Scale Synchronization |
| 73 | ========================== |
| 74 | |
| 75 | There are several factors that affect synchronizing time scales: |
| 76 | |
| 77 | * The rate of discrete instant representation change. For example Zephyr |
| 78 | uptime is tracked in ticks which advance at events that nominally occur at |
Gerard Marull-Paretas | 260decc | 2022-02-07 17:27:43 +0100 | [diff] [blame] | 79 | :kconfig:option:`CONFIG_SYS_CLOCK_TICKS_PER_SEC` Hertz, while an external time |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 80 | source may provide data in whole or fractional seconds (e.g. microseconds). |
| 81 | * The absolute offset required to align the two scales at a single instant. |
| 82 | * The relative error between observable instants in each scale, required to |
| 83 | align multiple instants consistently. For example a reference clock that's |
| 84 | conditioned by a 1-pulse-per-second GPS signal will be much more accurate |
| 85 | than a Zephyr system clock driven by a RC oscillator with a +/- 250 ppm |
| 86 | error. |
| 87 | |
| 88 | Synchronization or alignment between time scales is done with a multi-step |
| 89 | process: |
| 90 | |
| 91 | * An instant in a time scale is represented by an (unsigned) 64-bit integer, |
| 92 | assumed to advance at a fixed nominal rate. |
| 93 | * :c:struct:`timeutil_sync_config` records the nominal rates of a reference |
| 94 | time scale/source (e.g. TAI) and a local time source |
| 95 | (e.g. :c:func:`k_uptime_ticks`). |
| 96 | * :c:struct:`timeutil_sync_instant` records the representation of a single |
| 97 | instant in both the reference and local time scales. |
| 98 | * :c:struct:`timeutil_sync_state` provides storage for an initial instant, a |
| 99 | recently received second observation, and a skew that can adjust for |
| 100 | relative errors in the actual rate of each time scale. |
| 101 | * :c:func:`timeutil_sync_ref_from_local()` and |
| 102 | :c:func:`timeutil_sync_local_from_ref()` convert instants in one time scale |
| 103 | to another taking into account skew that can be estimated from the two |
| 104 | instances stored in the state structure by |
| 105 | :c:func:`timeutil_sync_estimate_skew`. |
| 106 | |
| 107 | .. doxygengroup:: timeutil_sync_apis |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 108 | |
| 109 | .. _timeutil_concepts: |
| 110 | |
| 111 | Concepts Underlying Time Support in Zephyr |
| 112 | ****************************************** |
| 113 | |
| 114 | Terms from `ISO/TC 154/WG 5 N0038 |
| 115 | <https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf>`__ |
| 116 | (ISO/WD 8601-1) and elsewhere: |
| 117 | |
| 118 | * A *time axis* is a representation of time as an ordered sequence of |
| 119 | instants. |
| 120 | * A *time scale* is a way of representing an instant relative to an origin |
| 121 | that serves as the epoch. |
| 122 | * A time scale is *monotonic* (increasing) if the representation of successive |
| 123 | time instants never decreases in value. |
| 124 | * A time scale is *continuous* if the representation has no abrupt changes in |
| 125 | value, e.g. jumping forward or back when going between successive instants. |
| 126 | * `Civil time <https://en.wikipedia.org/wiki/Civil_time>`__ generally refers |
| 127 | to time scales that legally defined by civil authorities, like local |
| 128 | governments, often to align local midnight to solar time. |
| 129 | |
| 130 | Relevant Time Scales |
| 131 | ==================== |
| 132 | |
| 133 | `International Atomic Time |
| 134 | <https://en.wikipedia.org/wiki/International_Atomic_Time>`__ (TAI) is a time |
Nazar Kazakov | f483b1b | 2022-03-16 21:07:43 +0000 | [diff] [blame] | 135 | scale based on averaging clocks that count in SI seconds. TAI is a monotonic |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 136 | and continuous time scale. |
| 137 | |
| 138 | `Universal Time <https://en.wikipedia.org/wiki/Universal_Time>`__ (UT) is a |
| 139 | time scale based on Earth’s rotation. UT is a discontinuous time scale as it |
| 140 | requires occasional adjustments (`leap seconds |
| 141 | <https://en.wikipedia.org/wiki/Leap_second>`__) to maintain alignment to |
| 142 | changes in Earth’s rotation. Thus the difference between TAI and UT varies |
| 143 | over time. There are several variants of UT, with `UTC |
| 144 | <https://en.wikipedia.org/wiki/Coordinated_Universal_Time>`__ being the most |
| 145 | common. |
| 146 | |
| 147 | UT times are independent of location. UT is the basis for Standard Time |
| 148 | (or "local time") which is the time at a particular location. Standard |
| 149 | time has a fixed offset from UT at any given instant, primarily |
| 150 | influenced by longitude, but the offset may be adjusted ("daylight |
| 151 | saving time") to align standard time to the local solar time. In a sense |
| 152 | local time is "more discontinuous" than UT. |
| 153 | |
| 154 | `POSIX Time <https://tools.ietf.org/html/rfc8536#section-2>`__ is a time scale |
| 155 | that counts seconds since the "POSIX epoch" at 1970-01-01T00:00:00Z (i.e. the |
| 156 | start of 1970 UTC). `UNIX Time |
| 157 | <https://tools.ietf.org/html/rfc8536#section-2>`__ is an extension of POSIX |
| 158 | time using negative values to represent times before the POSIX epoch. Both of |
| 159 | these scales assume that every day has exactly 86400 seconds. In normal use |
| 160 | instants in these scales correspond to times in the UTC scale, so they inherit |
| 161 | the discontinuity. |
| 162 | |
| 163 | The continuous analogue is `UNIX Leap Time |
| 164 | <https://tools.ietf.org/html/rfc8536#section-2>`__ which is UNIX time plus all |
| 165 | leap-second corrections added after the POSIX epoch (when TAI-UTC was 8 s). |
| 166 | |
| 167 | Example of Time Scale Differences |
| 168 | --------------------------------- |
| 169 | |
| 170 | A positive leap second was introduced at the end of 2016, increasing the |
| 171 | difference between TAI and UTC from 36 seconds to 37 seconds. There was |
| 172 | no leap second introduced at the end of 1999, when the difference |
| 173 | between TAI and UTC was only 32 seconds. The following table shows |
| 174 | relevant civil and epoch times in several scales: |
| 175 | |
| 176 | ==================== ========== =================== ======= ============== |
| 177 | UTC Date UNIX time TAI Date TAI-UTC UNIX Leap Time |
| 178 | ==================== ========== =================== ======= ============== |
| 179 | 1970-01-01T00:00:00Z 0 1970-01-01T00:00:08 +8 0 |
| 180 | 1999-12-31T23:59:28Z 946684768 2000-01-01T00:00:00 +32 946684792 |
| 181 | 1999-12-31T23:59:59Z 946684799 2000-01-01T00:00:31 +32 946684823 |
| 182 | 2000-01-01T00:00:00Z 946684800 2000-01-01T00:00:32 +32 946684824 |
| 183 | 2016-12-31T23:59:59Z 1483228799 2017-01-01T00:00:35 +36 1483228827 |
| 184 | 2016-12-31T23:59:60Z undefined 2017-01-01T00:00:36 +36 1483228828 |
| 185 | 2017-01-01T00:00:00Z 1483228800 2017-01-01T00:00:37 +37 1483228829 |
| 186 | ==================== ========== =================== ======= ============== |
| 187 | |
| 188 | Functional Requirements |
| 189 | ----------------------- |
| 190 | |
| 191 | The Zephyr tick counter has no concept of leap seconds or standard time |
| 192 | offsets and is a continuous time scale. However it can be relatively |
| 193 | inaccurate, with drifts as much as three minutes per hour (assuming an RC |
| 194 | timer with 5% tolerance). |
| 195 | |
| 196 | There are two stages required to support conversion between Zephyr time and |
| 197 | common human time scales: |
| 198 | |
| 199 | * Translation between the continuous but inaccurate Zephyr time scale and an |
| 200 | accurate external stable time scale; |
| 201 | * Translation between the stable time scale and the (possibly discontinuous) |
| 202 | civil time scale. |
| 203 | |
| 204 | The API around :c:func:`timeutil_sync_state_update()` supports the first step |
| 205 | of converting between continuous time scales. |
| 206 | |
| 207 | The second step requires external information including schedules of leap |
| 208 | seconds and local time offset changes. This may be best provided by an |
| 209 | external library, and is not currently part of the time utility APIs. |
| 210 | |
| 211 | Selecting an External Source and Time Scale |
| 212 | ------------------------------------------- |
| 213 | |
| 214 | If an application requires civil time accuracy within several seconds then UTC |
| 215 | could be used as the stable time source. However, if the external source |
| 216 | adjusts to a leap second there will be a discontinuity: the elapsed time |
| 217 | between two observations taken at 1 Hz is not equal to the numeric difference |
| 218 | between their timestamps. |
| 219 | |
| 220 | For precise activities a continuous scale that is independent of local and |
| 221 | solar adjustments simplifies things considerably. Suitable continuous scales |
| 222 | include: |
| 223 | |
| 224 | - GPS time: epoch of 1980-01-06T00:00:00Z, continuous following TAI with an |
| 225 | offset of TAI-GPS=19 s. |
Mia Koen | 0bcad09 | 2023-11-29 13:33:33 +0100 | [diff] [blame] | 226 | - Bluetooth Mesh time: epoch of 2000-01-01T00:00:00Z, continuous following TAI |
Peter Bigot | 7b3dc48 | 2020-10-07 11:59:48 -0500 | [diff] [blame] | 227 | with an offset of -32. |
| 228 | - UNIX Leap Time: epoch of 1970-01-01T00:00:00Z, continuous following TAI with |
| 229 | an offset of -8. |
| 230 | |
| 231 | Because C and Zephyr library functions support conversion between integral and |
| 232 | calendar time representations using the UNIX epoch, UNIX Leap Time is an ideal |
| 233 | choice for the external time scale. |
| 234 | |
| 235 | The mechanism used to populate synchronization points is not relevant: it may |
| 236 | involve reading from a local high-precision RTC peripheral, exchanging packets |
| 237 | over a network using a protocol like NTP or PTP, or processing NMEA messages |
| 238 | received a GPS with or without a 1pps signal. |