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
|