Use Cases#
This section describes behavioral patterns for each clock type through sequence diagrams and focused code snippets. These diagrams show how the clock APIs work internally during typical operations.
For complete, runnable code examples, see Examples User Manual.
HighResSteadyTime#
HT1 — Time Polling#
Measure a short code-path latency or compute a tight deadline where call overhead matters.
HighResSteadyClock avoids a kernel call on QNX by reading the hardware cycle counter
directly — the same Now() snapshot pattern used for all clock domains, with no status
check required.
#include "score/time/high_res_steady_time/src/high_res_steady_clock.h"
#include <chrono>
void MyValidator::CheckDeadline()
{
auto hirs = score::time::HighResSteadyClock::GetInstance();
const auto deadline = hirs.Now().TimePoint() + std::chrono::seconds{3};
// ... do work ...
if (hirs.Now().TimePoint() > deadline) {
HandleDeadlineExceeded();
}
}
The full working implementation of this pattern is in
examples/time/high_res_steady_time/src/high_res_steady_time_handler.h and
examples/time/high_res_steady_time/src/main.cpp.
SteadyClock#
ST1 — Time Polling#
Measure elapsed time between two points, or derive a deadline, using a clock that is guaranteed never to go backward regardless of external time adjustments.
#include "score/time/steady_time/src/steady_clock.h"
void MyComponent::MeasureElapsed()
{
auto clock = score::time::SteadyClock::GetInstance();
const auto start = clock.Now().TimePoint();
// ... do work ...
const auto elapsed = clock.Now().TimePoint() - start;
}
The full working implementation of this pattern is in
examples/time/steady_time/src/steady_time_handler.h and
examples/time/steady_time/src/main.cpp.
SystemClock#
SC1 — Time Polling#
Record a wall-clock timestamp for logging or audit trails where the absolute calendar
time matters. Do not use SystemClock for elapsed time or timeouts — the timepoint
may jump.
#include "score/time/system_time/src/system_clock.h"
#include <chrono>
void MyLogger::LogEvent()
{
auto clock = score::time::SystemClock::GetInstance();
const auto wall_time = clock.Now().TimePoint();
const auto t = std::chrono::system_clock::to_time_t(wall_time);
LOG_INFO("Event at: {}", std::ctime(&t));
}
The full working implementation of this pattern is in
examples/time/system_time/src/system_time_handler.h and
examples/time/system_time/src/main.cpp.
VehicleTime#
VT1 — Time Polling with Status Check#
Obtain a snapshot and inspect the synchronization quality before using the time
value. Now() returns a single immutable ClockSnapshot — the timepoint and its
VehicleTimeStatus are always fetched together, with no separate status call needed.
#include "score/time/vehicle_time/src/vehicle_clock.h"
void MyComponent::CheckTime()
{
auto clock = score::time::VehicleClock::GetInstance();
auto snapshot = clock.Now();
if (snapshot.Status().IsReliable()) {
auto tp = snapshot.TimePoint();
// use tp ...
} else if (snapshot.Status().IsFlagActive(
score::time::VehicleTime::StatusFlag::kTimeOut)) {
HandleTimeout();
}
}
Note
Init() must be called once during application startup before Now() is expected
to return synchronized data (see VT2). Without it, Now() returns a snapshot with
no flags set (IsConsistent() returns false).
Status flags:
Flag |
Meaning |
|---|---|
|
Synchronized at least once to the PTP Grand Master |
|
No sync message received within the configured time window |
|
A large forward adjustment was applied |
|
A large backward adjustment was applied |
VehicleTimeStatus::IsReliable() returns true only when kSynchronized is set
and none of {kTimeOut, kTimeLeapFuture, kTimeLeapPast} is set.
VehicleTimeStatus::HasBeenSynchronized() returns true whenever kSynchronized
has been set at least once during this lifecycle, regardless of current fault flags.
VehicleTimeStatus::IsConsistent() checks that the flag combination is internally
valid (at least one flag set, and not both leap flags simultaneously).
These three methods belong to VehicleTimeStatus and encode VehicleTime-domain
semantics. ClockStatus<FlagEnumT> itself exposes only generic bit-manipulation
(IsFlagActive, IsAnyOfFlagsActive, AddFlag) and the domain-specific
PrintTo() specialization.
The full working implementation of this pattern is in
examples/time/vehicle_time/src/vehicle_time_handler.h (GetCurrentTime()) and
examples/time/vehicle_time/src/main.cpp.
VT2 — Initialization and Readiness Check#
VehicleTime requires an explicit Init() call to open the IPC channel to the
time daemon before any time data becomes available. Until Init() returns true,
Now() returns a snapshot with no flags set (IsConsistent() returns false) and
IsAvailable() returns false.
After a successful Init(), IsAvailable() returns true immediately. The
non-blocking IsAvailable() probe and the blocking WaitUntilAvailable() are useful
when Init() is retried on a background thread.
Simple startup (same thread):
#include "score/time/vehicle_time/src/vehicle_clock.h"
bool MyService::Startup()
{
auto clock = score::time::VehicleClock::GetInstance();
if (!clock.Init()) {
LOG_ERROR("VehicleTime: failed to open IPC channel");
return false;
}
auto snapshot = clock.Now();
// ...
return true;
}
Blocking wait when Init is retried from a background thread:
#include "score/time/vehicle_time/src/vehicle_clock.h"
#include <score/stop_token.hpp>
#include <chrono>
void MyService::WaitForClock(const score::cpp::stop_token& stop)
{
auto clock = score::time::VehicleClock::GetInstance();
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds{30};
if (!clock.WaitUntilAvailable(stop, deadline)) {
LOG_ERROR("VehicleTime did not become available within 30 s");
return;
}
auto snapshot = clock.Now();
// ...
}
Note
Init(), IsAvailable(), and WaitUntilAvailable() are only available on
clock domains that require explicit initialisation (currently VehicleTime).
Calling them on HighResSteadyTime, SteadyClock, or SystemClock is a compile
error — those clocks are always ready.
The full working implementation of this pattern is in
examples/time/vehicle_time/src/vehicle_time_handler.h (Init()) and
examples/time/vehicle_time/src/main.cpp.
VT3 — Async PTP Protocol Data Subscription#
VehicleTime exposes two PTP protocol data callbacks, intended primarily for
diagnostics and PTP data sanity checks:
TimeSlaveSyncData<VehicleTime>— fired on each PTP Sync/Follow_Up message pair; carries the offset, rate correction, and raw timestamps computed by the TimeSlave.PDelayMeasurementData<VehicleTime>— fired when a peer-delay measurement cycle completes; carries the measured peer delay and associated timestamps.
Warning
Both PTP data callbacks (TimeSlaveSyncData and PDelayMeasurementData) are
not yet delivered. Calling Subscribe<...>() compiles and runs without error,
but the registered callbacks will never be invoked. Delivery will be wired from a
dedicated background thread in a future change.
#include "score/time/vehicle_time/src/vehicle_clock.h"
#include "score/time/ptp/src/time_slave_sync_data.h"
#include "score/time/ptp/src/pdelay_measurement_data.h"
void MyDiagHandler::RegisterCallbacks()
{
auto clock = score::time::VehicleClock::GetInstance();
clock.Subscribe<score::time::TimeSlaveSyncData<score::time::VehicleTime>>(
[this](const auto& data) { OnTimeSyncData(data); });
clock.Subscribe<score::time::PDelayMeasurementData<score::time::VehicleTime>>(
[this](const auto& data) { OnPDelayData(data); });
}
void MyDiagHandler::Shutdown()
{
auto clock = score::time::VehicleClock::GetInstance();
clock.Unsubscribe<score::time::TimeSlaveSyncData<score::time::VehicleTime>>();
clock.Unsubscribe<score::time::PDelayMeasurementData<score::time::VehicleTime>>();
}
Warning
Callbacks are invoked on the backend thread — the callback implementation must be thread-safe.
VT4 — Synchronization Status Subscription#
Subscribe to VehicleTimeStatus changes to react when the clock synchronization state
changes — for example, when the timebase becomes synchronized and is ready to use, when a
timeout occurs, or when a large time leap is applied. This is the primary mechanism for
application components to know that VehicleTime is reliable and may be safely read.
Unlike the PTP protocol data callbacks in VT3, VehicleTimeStatus carries no protocol
internals. It delivers the same status value already available via Now().Status(),
but pushed proactively on every change rather than polled per call.
The callback fires unconditionally on the first PTP status update received after registration, and subsequently only when the flag set changes. Rate deviation is excluded from the comparison.
Warning
The VehicleTimeStatus callback is not yet delivered. Calling
Subscribe<VehicleTimeStatus>() compiles and runs without error, but the registered
callback will never be invoked. Delivery will be wired from a dedicated background
thread in a future change.
#include "score/time/vehicle_time/src/vehicle_clock.h"
void MyService::WatchClockReadiness()
{
auto clock = score::time::VehicleClock::GetInstance();
clock.Subscribe<score::time::VehicleTimeStatus>(
[this](const score::time::VehicleTimeStatus& status) {
if (status.IsReliable()) {
OnClockReady();
} else if (status.HasBeenSynchronized()) {
OnClockDegraded();
} else {
OnClockUnavailable();
}
});
}
void MyService::Shutdown()
{
auto clock = score::time::VehicleClock::GetInstance();
clock.Unsubscribe<score::time::VehicleTimeStatus>();
}
Warning
Callbacks are invoked on the backend thread — the callback implementation must be thread-safe.
The full working implementation of this pattern is in
examples/time/vehicle_time/src/vehicle_time_handler.h
(RegisterStatusCallback() / UnregisterStatusCallback()).
VT5 — Status Flag Inspection#
When mapping VehicleTime status to diagnostic outputs such as DTC bitmasks, use
IsFlagActive(flag) with the VehicleTime::StatusFlag enum to access individual
bits. For the higher-level reliability predicates (IsReliable(),
HasBeenSynchronized()), see the status flag table and method descriptions in VT1.
#include "score/time/vehicle_time/src/vehicle_clock.h"
#include <map>
using SvtFlag = score::time::VehicleTime::StatusFlag;
static const std::map<SvtFlag, uint8_t> kDiagBitMap = {
{SvtFlag::kSynchronized, 0x01U},
{SvtFlag::kTimeOut, 0x02U},
{SvtFlag::kTimeLeapFuture, 0x04U},
{SvtFlag::kTimeLeapPast, 0x08U},
{SvtFlag::kUnknown, 0x80U},
};
uint8_t BuildDiagByte(const score::time::VehicleTimeStatus& status)
{
uint8_t result{0U};
for (const auto& entry : kDiagBitMap) {
if (status.IsFlagActive(entry.first)) {
result |= entry.second;
}
}
return result;
}