Vehicle Time Example#
Overview#
The vehicle_time example demonstrates how to use the SCORE library’s VehicleClock
in combination with HighResSteadyClock. This example shows how to work with
PTP-synchronized vehicle time alongside local monotonic time, which is essential
for automotive applications requiring distributed time synchronization.
What it does#
This example creates a VehicleTimeHandler wrapper class that:
Provides access to both SCORE
VehicleClockandHighResSteadyClockReturns combined time reports with status information
Demonstrates initialization patterns for vehicle time backends
Shows how to monitor time synchronization quality
Can be unit tested with independent clock mocks
The main program:
Initializes the vehicle time backend
Runs a loop reading both time sources simultaneously
Displays time values, reliability, and synchronization status
Handles SIGINT/SIGTERM for clean shutdown
Building and Running#
To build and run the example:
# Build the example
bazel build //examples/time/vehicle_time
# Run the example
bazel run //examples/time/vehicle_time
# Or run the built binary directly
./bazel-bin/examples/time/vehicle_time/src/vehicle_time
Note: The vehicle time backend requires proper initialization. The example will exit with error code 1 if initialization fails (e.g., no PTP service available).
Output Format#
The program outputs lines in this format:
VehicleTime + HighResSteadyTime printer started. Press Ctrl+C to stop.
[0] vehicle=1720184400.123456789 s hirs=12345.234567890 s is_reliable=yes is_consistent=yes rate_deviation=1.23e-09
[1] vehicle=1720184401.234567890 s hirs=12346.345678901 s is_reliable=yes is_consistent=yes rate_deviation=1.24e-09
...
Shutdown requested. Exiting.
Where:
- vehicle= shows the PTP-synchronized time in seconds.nanoseconds
- hirs= shows the local high-resolution steady time
- is_reliable= indicates if the vehicle time is synchronized and fault-free
- is_consistent= indicates if status flags are internally consistent
- rate_deviation= shows local clock deviation relative to PTP Grand Master
Code Structure#
VehicleTimeHandler Class#
Located in examples/time/vehicle_time/src/vehicle_time_handler.h:
class VehicleTimeHandler {
public:
bool Init() noexcept;
TimeReport GetCurrentTime() const noexcept;
void RegisterStatusCallback(VehicleTime::StatusChangedCallback callback) noexcept;
};
struct TimeReport {
std::int64_t vehicle_time_ns{0}; // PTP-synchronized time
std::int64_t high_res_steady_time_ns{0}; // Local monotonic time
bool is_reliable{false}; // Time sync quality
bool is_consistent{false}; // Status flag consistency
double rate_deviation{0.0}; // Clock drift rate
};
Key features: - Dual time sources: Both vehicle and local time in single call - Status monitoring: Reliability and consistency flags - Rate tracking: Clock deviation measurement - Callback support: Status change notifications (future feature)
Main Program#
Located in examples/time/vehicle_time/src/main.cpp:
Key features: - Initialization error handling with early exit - Combined time display showing both sources - Status information formatting for monitoring - Same signal handling pattern as other examples
Testing#
Run the unit tests:
bazel test //examples/time/vehicle_time/src:vehicle_time_handler_test
The test shows how to mock both time sources independently:
auto vehicle_mock = std::make_shared<score::time::VehicleClockBackendMock>();
auto hirs_mock = std::make_shared<score::time::HighResSteadyClockBackendMock>();
score::time::test_utils::ScopedClockOverride<score::time::VehicleTime> vg{vehicle_mock};
score::time::test_utils::ScopedClockOverride<score::time::HighResSteadyTime> hg{hirs_mock};
EXPECT_CALL(*vehicle_mock, Init()).WillOnce(Return(true));
EXPECT_CALL(*vehicle_mock, Now()).WillOnce(Return(...));
EXPECT_CALL(*hirs_mock, Now()).WillOnce(Return(...));
Bazel Build Setup#
The vehicle_time example has more complex dependencies due to dual time sources and initialization.
Target Structure#
From examples/time/vehicle_time/src/BUILD:
cc_library(
name = "time_handler",
hdrs = ["vehicle_time_handler.h"],
deps = [
"//score/time/vehicle_time:interface",
"//score/time/high_res_steady_time:interface",
],
)
cc_binary(
name = "vehicle_time",
srcs = ["main.cpp"],
deps = [
":time_handler",
"//score/time/vehicle_time", # VehicleTime production backend
"//score/time/high_res_steady_time", # HIRS production backend
"@score_baselibs//score/mw/log:console_only_backend",
],
)
cc_test(
name = "vehicle_time_handler_test",
srcs = ["vehicle_time_handler_test.cpp"],
tags = ["exclusive", "unit"], # Required for ScopedClockOverride
deps = [
":time_handler",
"//score/time/vehicle_time:vehicle_time_mock",
"//score/time/high_res_steady_time:high_res_steady_time_mock",
"@googletest//:gtest_main",
],
)
Dual Clock Dependencies#
The handler depends on two clock interfaces:
//score/time/vehicle_time:interface- VehicleTime tag and status types//score/time/high_res_steady_time:interface- HighResSteadyTime tag
The binary links both production backends, while tests link both mocks.
Key Targets#
Target |
Purpose |
|---|---|
|
VehicleTime types, status flags, callback signatures |
|
Production backend with TimeDaemon IPC |
|
Mock for Init/Now/Subscribe testing |
|
HighResSteadyTime tag |
|
Production HIRS clock backend |
|
Mock for HIRS in tests |
Testing with Dual Mocks#
The test demonstrates independent mock control:
auto vehicle_mock = std::make_shared<VehicleClockBackendMock>();
auto hirs_mock = std::make_shared<HighResSteadyClockBackendMock>();
ScopedClockOverride<VehicleTime> vg{vehicle_mock};
ScopedClockOverride<HighResSteadyTime> hg{hirs_mock};
EXPECT_CALL(*vehicle_mock, Init()).WillOnce(Return(true));
EXPECT_CALL(*vehicle_mock, Now()).WillOnce(Return(vehicle_snapshot));
EXPECT_CALL(*hirs_mock, Now()).WillOnce(Return(hirs_snapshot));
Each clock can be mocked separately with different return values and expectations.
Adapting for Your Application#
When building components that use VehicleTime:
Header-only dependencies use
:interface:cc_library( name = "my_sync_component", hdrs = ["my_sync_component.h"], deps = [ "//score/time/vehicle_time:interface", "//score/time/high_res_steady_time:interface", ], )
Binaries link production backends:
cc_binary( name = "my_app", deps = [ ":my_sync_component", "//score/time/vehicle_time", "//score/time/high_res_steady_time", ], )
Tests link mocks and require exclusive tag:
cc_test( name = "my_sync_component_test", tags = ["exclusive", "unit"], deps = [ ":my_sync_component", "//score/time/vehicle_time:vehicle_time_mock", "//score/time/high_res_steady_time:high_res_steady_time_mock", "@googletest//:gtest_main", ], )
The layered dependency structure keeps compile times minimal while enabling comprehensive testing with independent clock control.