From 7b3dc4856af6a4a37f91227e26ed6f80774c4fa4 Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Wed, 7 Oct 2020 11:59:48 -0500 Subject: [PATCH] doc: add a section for timeutil APIs Describe the role of these APIs, key concepts that they depend on, and expose the low-level API. Signed-off-by: Peter Bigot --- doc/reference/index.rst | 1 + doc/reference/kernel/timing/clocks.rst | 2 + doc/reference/misc/timeutil.rst | 240 +++++++++++++++++++++++++ include/sys/timeutil.h | 18 ++ 4 files changed, 261 insertions(+) create mode 100644 doc/reference/misc/timeutil.rst diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 29bb60fc973..bd8bf98b5f8 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -29,6 +29,7 @@ API Reference resource_management/index.rst shell/index.rst storage/index.rst + misc/timeutil.rst usb/index.rst usermode/index.rst util/index.rst diff --git a/doc/reference/kernel/timing/clocks.rst b/doc/reference/kernel/timing/clocks.rst index f5ec87938fb..8a28d02f4d0 100644 --- a/doc/reference/kernel/timing/clocks.rst +++ b/doc/reference/kernel/timing/clocks.rst @@ -66,6 +66,8 @@ multiples of each other and where the output fits within a single word, these conversions expand to a 2-4 operation sequence, requiring full precision only where actually required and requested. +.. _kernel_timing_uptime: + Uptime ====== diff --git a/doc/reference/misc/timeutil.rst b/doc/reference/misc/timeutil.rst new file mode 100644 index 00000000000..4ed3c7178b9 --- /dev/null +++ b/doc/reference/misc/timeutil.rst @@ -0,0 +1,240 @@ +.. _timeutil_api: + +Time Utilities +############## + +Overview +******** + +:ref:`kernel_timing_uptime` in Zephyr is based on the a tick counter. With +the default :option:`CONFIG_TICKLESS_KERNEL` this counter advances at a +nominally constant rate from zero at the instant the system started. The POSIX +equivalent to this counter is something like ``CLOCK_MONOTONIC`` or, in Linux, +``CLOCK_MONOTONIC_RAW``. :c:func:`k_uptime_get()` provides a millisecond +representation of this time. + +Applications often need to correlate the Zephyr internal time with external +time scales used in daily life, such as local time or Coordinated Universal +Time. These systems interpret time in different ways and may have +discontinuities due to `leap seconds `__ and +local time offsets like daylight saving time. + +Because of these discontinuities, as well as significant inaccuracies in the +clocks underlying the cycle counter, the offset between time estimated from +the Zephyr clock and the actual time in a "real" civil time scale is not +constant and can vary widely over the runtime of a Zephyr application. + +The time utilities API supports: + +* :ref:`converting between time representations ` +* :ref:`synchronizing and aligning time scales ` + +For terminology and concepts that support these functions see +:ref:`timeutil_concepts`. + +Time Utility APIs +***************** + +.. _timeutil_repr: + +Representation Transformation +============================= + +Time scale instants can be represented in multiple ways including: + +* Seconds since an epoch. POSIX representations of time in this form include + ``time_t`` and ``struct timespec``, which are generally interpreted as a + representation of `"UNIX Time" + `__. + +* Calendar time as a year, month, day, hour, minutes, and seconds relative to + an epoch. POSIX representations of time in this form include ``struct tm``. + +Keep in mind that these are simply time representations that must be +interpreted relative to a time scale which may be local time, UTC, or some +other continuous or discontinuous scale. + +Some necessary transformations are available in standard C library +routines. For example, ``time_t`` measuring seconds since the POSIX EPOCH is +converted to ``struct tm`` representing calendar time with `gmtime() +`__. +Sub-second timestamps like ``struct timespec`` can also use this to produce +the calendar time representation and deal with sub-second offsets separately. + +The inverse transformation is not standardized: APIs like ``mktime()`` expect +information about time zones. Zephyr provides this transformation with +:c:func:`timeutil_timegm` and :c:func:`timeutil_timegm64`. + +.. doxygengroup:: timeutil_repr_apis + :project: Zephyr + +.. _timeutil_sync: + +Time Scale Synchronization +========================== + +There are several factors that affect synchronizing time scales: + +* The rate of discrete instant representation change. For example Zephyr + uptime is tracked in ticks which advance at events that nominally occur at + :option:`CONFIG_SYS_CLOCK_TICKS_PER_SEC` Hertz, while an external time + source may provide data in whole or fractional seconds (e.g. microseconds). +* The absolute offset required to align the two scales at a single instant. +* The relative error between observable instants in each scale, required to + align multiple instants consistently. For example a reference clock that's + conditioned by a 1-pulse-per-second GPS signal will be much more accurate + than a Zephyr system clock driven by a RC oscillator with a +/- 250 ppm + error. + +Synchronization or alignment between time scales is done with a multi-step +process: + +* An instant in a time scale is represented by an (unsigned) 64-bit integer, + assumed to advance at a fixed nominal rate. +* :c:struct:`timeutil_sync_config` records the nominal rates of a reference + time scale/source (e.g. TAI) and a local time source + (e.g. :c:func:`k_uptime_ticks`). +* :c:struct:`timeutil_sync_instant` records the representation of a single + instant in both the reference and local time scales. +* :c:struct:`timeutil_sync_state` provides storage for an initial instant, a + recently received second observation, and a skew that can adjust for + relative errors in the actual rate of each time scale. +* :c:func:`timeutil_sync_ref_from_local()` and + :c:func:`timeutil_sync_local_from_ref()` convert instants in one time scale + to another taking into account skew that can be estimated from the two + instances stored in the state structure by + :c:func:`timeutil_sync_estimate_skew`. + +.. doxygengroup:: timeutil_sync_apis + :project: Zephyr + +.. _timeutil_concepts: + +Concepts Underlying Time Support in Zephyr +****************************************** + +Terms from `ISO/TC 154/WG 5 N0038 +`__ +(ISO/WD 8601-1) and elsewhere: + +* A *time axis* is a representation of time as an ordered sequence of + instants. +* A *time scale* is a way of representing an instant relative to an origin + that serves as the epoch. +* A time scale is *monotonic* (increasing) if the representation of successive + time instants never decreases in value. +* A time scale is *continuous* if the representation has no abrupt changes in + value, e.g. jumping forward or back when going between successive instants. +* `Civil time `__ generally refers + to time scales that legally defined by civil authorities, like local + governments, often to align local midnight to solar time. + +Relevant Time Scales +==================== + +`International Atomic Time +`__ (TAI) is a time +scale based on averaging clocks that count in SI seconds. TAI is a montonic +and continuous time scale. + +`Universal Time `__ (UT) is a +time scale based on Earth’s rotation. UT is a discontinuous time scale as it +requires occasional adjustments (`leap seconds +`__) to maintain alignment to +changes in Earth’s rotation. Thus the difference between TAI and UT varies +over time. There are several variants of UT, with `UTC +`__ being the most +common. + +UT times are independent of location. UT is the basis for Standard Time +(or "local time") which is the time at a particular location. Standard +time has a fixed offset from UT at any given instant, primarily +influenced by longitude, but the offset may be adjusted ("daylight +saving time") to align standard time to the local solar time. In a sense +local time is "more discontinuous" than UT. + +`POSIX Time `__ is a time scale +that counts seconds since the "POSIX epoch" at 1970-01-01T00:00:00Z (i.e. the +start of 1970 UTC). `UNIX Time +`__ is an extension of POSIX +time using negative values to represent times before the POSIX epoch. Both of +these scales assume that every day has exactly 86400 seconds. In normal use +instants in these scales correspond to times in the UTC scale, so they inherit +the discontinuity. + +The continuous analogue is `UNIX Leap Time +`__ which is UNIX time plus all +leap-second corrections added after the POSIX epoch (when TAI-UTC was 8 s). + +Example of Time Scale Differences +--------------------------------- + +A positive leap second was introduced at the end of 2016, increasing the +difference between TAI and UTC from 36 seconds to 37 seconds. There was +no leap second introduced at the end of 1999, when the difference +between TAI and UTC was only 32 seconds. The following table shows +relevant civil and epoch times in several scales: + +==================== ========== =================== ======= ============== +UTC Date UNIX time TAI Date TAI-UTC UNIX Leap Time +==================== ========== =================== ======= ============== +1970-01-01T00:00:00Z 0 1970-01-01T00:00:08 +8 0 +1999-12-31T23:59:28Z 946684768 2000-01-01T00:00:00 +32 946684792 +1999-12-31T23:59:59Z 946684799 2000-01-01T00:00:31 +32 946684823 +2000-01-01T00:00:00Z 946684800 2000-01-01T00:00:32 +32 946684824 +2016-12-31T23:59:59Z 1483228799 2017-01-01T00:00:35 +36 1483228827 +2016-12-31T23:59:60Z undefined 2017-01-01T00:00:36 +36 1483228828 +2017-01-01T00:00:00Z 1483228800 2017-01-01T00:00:37 +37 1483228829 +==================== ========== =================== ======= ============== + +Functional Requirements +----------------------- + +The Zephyr tick counter has no concept of leap seconds or standard time +offsets and is a continuous time scale. However it can be relatively +inaccurate, with drifts as much as three minutes per hour (assuming an RC +timer with 5% tolerance). + +There are two stages required to support conversion between Zephyr time and +common human time scales: + +* Translation between the continuous but inaccurate Zephyr time scale and an + accurate external stable time scale; +* Translation between the stable time scale and the (possibly discontinuous) + civil time scale. + +The API around :c:func:`timeutil_sync_state_update()` supports the first step +of converting between continuous time scales. + +The second step requires external information including schedules of leap +seconds and local time offset changes. This may be best provided by an +external library, and is not currently part of the time utility APIs. + +Selecting an External Source and Time Scale +------------------------------------------- + +If an application requires civil time accuracy within several seconds then UTC +could be used as the stable time source. However, if the external source +adjusts to a leap second there will be a discontinuity: the elapsed time +between two observations taken at 1 Hz is not equal to the numeric difference +between their timestamps. + +For precise activities a continuous scale that is independent of local and +solar adjustments simplifies things considerably. Suitable continuous scales +include: + +- GPS time: epoch of 1980-01-06T00:00:00Z, continuous following TAI with an + offset of TAI-GPS=19 s. +- Bluetooth mesh time: epoch of 2000-01-01T00:00:00Z, continuous following TAI + with an offset of -32. +- UNIX Leap Time: epoch of 1970-01-01T00:00:00Z, continuous following TAI with + an offset of -8. + +Because C and Zephyr library functions support conversion between integral and +calendar time representations using the UNIX epoch, UNIX Leap Time is an ideal +choice for the external time scale. + +The mechanism used to populate synchronization points is not relevant: it may +involve reading from a local high-precision RTC peripheral, exchanging packets +over a network using a protocol like NTP or PTP, or processing NMEA messages +received a GPS with or without a 1pps signal. diff --git a/include/sys/timeutil.h b/include/sys/timeutil.h index 26826067cda..f6e1f96c86b 100644 --- a/include/sys/timeutil.h +++ b/include/sys/timeutil.h @@ -29,6 +29,13 @@ 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. * @@ -53,6 +60,13 @@ int64_t timeutil_timegm64(const struct tm *tm); */ 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. * @@ -287,4 +301,8 @@ int32_t timeutil_sync_skew_to_ppb(float skew); } #endif +/** + * @} + */ + #endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */