LCOV - code coverage report
Current view: top level - utilities/src - TimestampEstimatorTimeSync.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 73.5 % 68 50
Test Date: 2025-12-21 13:07:08 Functions: 80.0 % 10 8

            Line data    Source code
       1              : /**
       2              :  * @file TimestampEstimatorTimeSync.cpp
       3              :  *
       4              :  * This is part of the DUNE DAQ Software Suite, copyright 2020.
       5              :  * Licensing/copyright details are in the COPYING file that you should have
       6              :  * received with this code.
       7              :  */
       8              : 
       9              : #include "utilities/TimestampEstimatorTimeSync.hpp"
      10              : #include "utilities/Issues.hpp"
      11              : 
      12              : #include "logging/Logging.hpp"
      13              : 
      14              : #include <memory>
      15              : #include <unistd.h>
      16              : 
      17              : #define TRACE_NAME "TimestampEstimatorTimeSync" // NOLINT
      18              : 
      19              : namespace dunedaq::utilities {
      20              : 
      21            2 : TimestampEstimatorTimeSync::TimestampEstimatorTimeSync(uint32_t run_number,         // NOLINT(build/unsigned)
      22            2 :                                                        uint64_t clock_frequency_hz) // NOLINT(build/unsigned)
      23            2 :   : TimestampEstimatorTimeSync(clock_frequency_hz)
      24              : {
      25            2 :   m_run_number = run_number;
      26            2 : }
      27              : 
      28            2 : TimestampEstimatorTimeSync::TimestampEstimatorTimeSync(uint64_t clock_frequency_hz) // NOLINT(build/unsigned)
      29            2 :   : m_current_timestamp_estimate(TimeSyncPoint{ s_invalid_ts, std::chrono::time_point<std::chrono::steady_clock>() })
      30            2 :   , m_clock_frequency_hz(clock_frequency_hz)
      31            2 :   , m_most_recent_daq_time(s_invalid_ts)
      32            2 :   , m_most_recent_system_time(0)
      33            2 :   , m_run_number(0)
      34            2 :   , m_received_timesync_count(0)
      35              : {
      36            2 : }
      37              : 
      38            2 : TimestampEstimatorTimeSync::~TimestampEstimatorTimeSync() {}
      39              : 
      40              : /**
      41              :  * @brief Returns the current timestamp estimate or a special value if no valid timestamp is available
      42              :  * @return the current estimated timestamp (in units of DUNE Timing System ticks) or
      43              :  *         s_invalid_ts if no valid timestamp is currently available
      44              :  */
      45              : uint64_t // NOLINT(build/unsigned)
      46          199 : TimestampEstimatorTimeSync::get_timestamp_estimate() const
      47              : {
      48          199 :   using namespace std::chrono;
      49              : 
      50          199 :   TimeSyncPoint estimate = m_current_timestamp_estimate.load();
      51              :   // 27-May-2025, KAB: added check if a valid timestamp is available and, if not, return early
      52              :   // with the special value that indicates that none is available.
      53          199 :   if (estimate.daq_time == s_invalid_ts) {
      54              :     return estimate.daq_time;
      55              :   }
      56              : 
      57          113 :   auto delta_time_us = duration_cast<microseconds>(steady_clock::now() - estimate.system_time).count();
      58              : 
      59          113 :   const uint64_t new_timestamp = estimate.daq_time + delta_time_us * m_clock_frequency_hz / 1000000; // NOLINT
      60              : 
      61          113 :   return new_timestamp;
      62              : }
      63              : 
      64              : std::chrono::microseconds
      65            0 : TimestampEstimatorTimeSync::get_wait_estimate(uint64_t ts) const // NOLINT(build/unsigned)
      66              : {
      67            0 :   auto now = get_timestamp_estimate();
      68            0 :   if (now > ts)
      69            0 :     return std::chrono::microseconds(0);
      70            0 :   auto diff = ts - now;
      71            0 :   return std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(
      72            0 :     static_cast<double>(diff) * 1000000. / static_cast<double>(m_clock_frequency_hz)));
      73              : }
      74              : 
      75              : void
      76            2 : TimestampEstimatorTimeSync::add_timestamp_datapoint(uint64_t daq_time, uint64_t system_time) // NOLINT(build/unsigned)
      77              : {
      78            2 :   using namespace std::chrono;
      79              : 
      80            2 :   std::scoped_lock<std::mutex> lk(m_datapoint_mutex);
      81              : 
      82              :   // First, update the latest timestamp
      83            2 :   TimeSyncPoint estimate = m_current_timestamp_estimate.load();
      84            2 :   auto diff = static_cast<int64_t>(estimate.daq_time - daq_time);
      85            2 :   TLOG_DEBUG(TLVL_TIME_SYNC_PROPERTIES) << "Got a TimeSync timestamp = " << daq_time
      86            0 :                                         << ", system time = " << system_time << " when current timestamp estimate was "
      87            2 :                                         << estimate.daq_time << ". diff=" << diff;
      88              : 
      89            2 :   if (m_most_recent_daq_time == s_invalid_ts || daq_time > m_most_recent_daq_time) {
      90            2 :     m_most_recent_daq_time = daq_time;
      91            2 :     m_most_recent_system_time = system_time;
      92              :   }
      93              : 
      94            2 :   if (m_most_recent_daq_time != s_invalid_ts) {
      95              :     // Update the current timestamp estimate, based on the most recently-read TimeSync
      96            2 :     using namespace std::chrono;
      97              : 
      98            2 :     auto time_now =
      99            2 :       static_cast<uint64_t>(duration_cast<microseconds>(system_clock::now().time_since_epoch()).count()); // NOLINT
     100            2 :     auto steady_time_now = steady_clock::now();
     101              : 
     102              :     // (PAR 2021-07-22) We only want to _increase_ our timestamp
     103              :     // estimate, not _decrease_ it, so we only attempt the update if
     104              :     // our system time is later than the latest time sync's system
     105              :     // time. We can get TimeSync messages from the "future" if
     106              :     // they're coming from another host whose clock is not exactly
     107              :     // synchronized with ours: that's fine, but if the discrepancy
     108              :     // is large, then badness could happen, so emit a warning
     109              : 
     110            2 :     if (time_now < m_most_recent_system_time - 10000) {
     111            0 :       ers::warning(EarlyTimeSync(ERS_HERE, m_most_recent_system_time - time_now));
     112              :     }
     113              : 
     114            2 :     if (time_now > m_most_recent_system_time) {
     115              : 
     116            2 :       auto delta_time = time_now - m_most_recent_system_time;
     117            2 :       TLOG_DEBUG(TLVL_TIME_SYNC_PROPERTIES)
     118            2 :         << "Time diff between current system and latest TimeSync system time [us]: " << delta_time;
     119              : 
     120              :       // Warn user if current system time is more than 1s ahead of latest TimeSync system time. This could be a sign of
     121              :       // an issue, e.g. machine times out of sync
     122            2 :       if (delta_time > 1'000'000)
     123            1 :         ers::warning(LateTimeSync(ERS_HERE, delta_time));
     124              : 
     125            2 :       const uint64_t new_timestamp = m_most_recent_daq_time + delta_time * m_clock_frequency_hz / 1000000; // NOLINT
     126              : 
     127              :       // Don't ever decrease the timestamp; just wait until enough
     128              :       // time passes that we want to increase it
     129            2 :       if (estimate.daq_time == s_invalid_ts || new_timestamp >= estimate.daq_time) {
     130            2 :         TLOG_DEBUG(TLVL_TIME_SYNC_NEW_ESTIMATE)
     131            0 :           << "Storing new timestamp estimate of " << new_timestamp << " ticks (..." << std::fixed
     132            0 :           << std::setprecision(8)
     133            0 :           << (static_cast<double>(new_timestamp % (m_clock_frequency_hz * 1000)) /
     134            0 :               static_cast<double>(m_clock_frequency_hz))
     135            0 :           << " sec), mrt.daq_time is " << m_most_recent_daq_time << " ticks (..."
     136            0 :           << (static_cast<double>(m_most_recent_daq_time % (m_clock_frequency_hz * 1000)) /
     137            0 :               static_cast<double>(m_clock_frequency_hz))
     138            2 :           << " sec), delta_time is " << delta_time << " usec, clock_freq is " << m_clock_frequency_hz << " Hz";
     139            2 :         m_current_timestamp_estimate.store(TimeSyncPoint{ new_timestamp, steady_time_now });
     140            2 :       } else {
     141            0 :         TLOG_DEBUG(TLVL_TIME_SYNC_NOTES) << "Not updating timestamp estimate backwards from "
     142            0 :                                          << m_current_timestamp_estimate.load().daq_time << " to " << new_timestamp;
     143              :       }
     144              :     }
     145              :   }
     146            2 : }
     147              : 
     148              : } // namespace dunedaq::utilities
        

Generated by: LCOV version 2.0-1