Skip to content

Technical Overview

Clock Implementations

PhoenixClock provides two main types of clock backends:

  • Mock Backend: Implemented by the PClockMock class. This backend simulates clock behavior by reading time values from a mock file. The mock file can be generated by recording real clock values or created manually, making it ideal for testing and reproducibility.
  • Real Backend: Implemented by classes such as PClockNs. This backend interacts with the actual system or hardware clock to provide real time measurements. You can also implement your own backend for specific clock types, such as ClockMicrosecond.

Developing Your Own Backend

To create a custom clock backend, you can use the architecture of PClockNs as a template. Your backend should implement at least the following methods:

  • now(): Returns the current time value according to your backend's logic.
  • sleep(EllapsedTime sleepTime): Pauses execution for the specified duration.

This ensures compatibility with the generic clock manager and allows seamless integration with the rest of the PhoenixClock system.


Double Backend Design

PhoenixClock is built around the concept of a double backend design. This architecture allows you to switch easily between real and mock clock backends, providing flexibility for both production and testing environments. A generic clock manager has to instantiate the two backends, the real one and the mock one and then, switch between them using the available modes.

///@brief Generic clock which can use several backends (real clock backend and a mock of clock)
template<typename _TBackend, typename _TMockBackend>
class PGenericClock

You can, for example, use the PClockMock backend provided by PhoenixClock for the mock backend and the PCLockNs as the real backend. If so, you can instatiate your clock manager as the following :

///Definition of a clock using ClockNs and ClockMock as backends
typedef PGenericClock<PClockNs, PClockMock> ClockNanoSecond;

How It Works

  • The generic clock manager (PGenericClock) holds both a real backend and a mock backend.
  • Depending on the selected mode, it delegates time queries and sleep operations to either the real or mock backend.
  • Modes include:
  • NO_MOCK: Only the real backend is used for all operations.
  • MOCK: Only the mock backend is used, simulating time operations via file I/O.
  • MOCK_RECORD: The real backend is used for time measurement, but all time values are also recorded using the mock backend for later replay or analysis.

Switching between different modes

    typedef PGenericClock<PClockNs, PClockMock> ClockNanoSecond;// Definition of the test clock for more precise time
    phoenix_createClockMock("", 3lu);                           // Fill a mock fil with 3 values for testing (0, 1, 2)
    ClockNanosecond clock;                                      // Create the clock
    clock.setMockPrefix("");                                    // Setting the mock prefix
    clock.setMode(PClockMode::NO_MOCK);                         // Switching to NO_MOCK mode to call real clock
    data_stream_assert(clock.now() != 0l);                      // Check that the real clock return the real time (should be different from 0)
    clock.setMode(PClockMode::MOCK);                            // Switching to MOCK mode to evaluate that the mock file contains the proper values
    data_stream_assert(clock.now() == 0l);                      // First value should be 0
    data_stream_assert(clock.now() == 1l);                      // Second value should be 1
    data_stream_assert(clock.now() == 2l);                      // Third value should be 2
    data_stream_assert(clock.now() == 0l);                      // 4th value should be 0. Once all mock values have been played, the sequence loops back at beginning.
    clock.sleep(1000l);                                         // Sleep for 1 microsecond in MOCK mode should not do anything
    clock.setMode(PClockMode::MOCK_RECORD);                     // Switching to MOCK_RECORD mode to revord a real clock and the replay it
    clock.sleep(1000l);                                         // Sleep for 1 microsecond in MOCK_RECORD mode should  sleep but not registrer it in the mock file
    time_t nowValue = clock.now();                              // Register current time
    data_stream_assert(nowValue != 0l);                         // Check that it is different from 0
    clock.setMode(PClockMode::MOCK);                            // Switch to MOCK mode to replay the clock
    clock.now();                                                // Skip second value (first one has already loop)
    clock.now();                                                // Skip third value
    data_stream_assert(clock.now() == nowValue);                // Check that the mock has a correct value comparing to the real clock recorded

Benefits

  • Testing: Run unit tests or integration tests without needing real hardware clocks.
  • Reproducibility: Replay recorded time sequences for debugging or validation.
  • Extensibility: Add new clock types or backends easily by following the established convention.
  • Consistent API: All backends expose the same interface, so switching is seamless.

Summary

By following this double backend design, PhoenixClock enables robust, flexible, and maintainable time management for both real-world and simulated scenarios. This is especially useful for scientific, embedded, or time-sensitive applications where reproducibility and testing are critical.