24 using slow_clock = C1;
25 using fast_clock = C2;
27 struct time_point_pair {
28 typename slow_clock::time_point slow;
29 typename fast_clock::time_point fast;
32 time_point_pair first_pair;
33 time_point_pair prev_pair;
34 time_point_pair last_pair;
35 int calibration_nr = 0;
37 static constexpr int gainShift = 60;
38 static constexpr double gainMultiplier =
static_cast<double>(1ULL << gainShift);
45 typename slow_clock::duration leapsecond_offset = 0ns;
64 typename slow_clock::time_point convert(
typename fast_clock::time_point fast_time)
const noexcept {
65 return convert(gain.
load(std::memory_order_relaxed), bias.
load(std::memory_order_relaxed), fast_time);
68 typename slow_clock::duration convert(
typename fast_clock::duration fast_duration)
const noexcept {
69 return convert(gain.
load(std::memory_order_relaxed), fast_duration);
75 void calibrate_tick() noexcept {
78 if (calibration_nr > 2) {
79 backoff = (calibration_nr - 2) * 10s;
86 if (last_pair.slow + backoff < slow_clock::now()) {
92 static time_point_pair makeCalibrationPoint() noexcept {
95 ttlet f1 = fast_clock::now();
96 ttlet s1 = slow_clock::now();
97 ttlet f2 = fast_clock::now();
98 ttlet s2 = slow_clock::now();
99 ttlet f3 = fast_clock::now();
101 if ((f2 - f1) < (f3 - f2)) {
108 void addCalibrationPoint() noexcept {
109 auto tp = makeCalibrationPoint();
110 if (calibration_nr++ == 0) {
113 prev_pair = last_pair;
117 int64_t getGain() noexcept {
119 ttlet diff_slow =
static_cast<double>((last_pair.slow - first_pair.slow).
count());
120 ttlet diff_fast =
static_cast<double>((last_pair.fast - first_pair.fast).
count());
122 if (calibration_nr < 2 || diff_fast == 0.0) {
123 return static_cast<int64_t
>(gainMultiplier);
125 auto new_gain = diff_slow / diff_fast;
126 return static_cast<int64_t
>(
std::round(new_gain * gainMultiplier));
130 typename slow_clock::duration getBias(int64_t new_gain)
noexcept {
132 auto tmp =
static_cast<ubig128
>(last_pair.fast.time_since_epoch() / 1ns);
138 tmp += (1LL << (gainShift - 1));
143 ttlet now_fast_after_gain =
typename slow_clock::duration(
static_cast<typename slow_clock::rep
>(tmp));
145 return (last_pair.slow.time_since_epoch() + leapsecond_offset) - now_fast_after_gain;
148 typename slow_clock::duration getLeapAdjustment(int64_t new_gain,
typename slow_clock::duration new_bias)
151 ttlet prev_fast_as_slow = convert(last_pair.fast);
152 ttlet next_fast_as_slow = convert(new_gain, new_bias, last_pair.fast);
153 ttlet diff_fast_as_slow = prev_fast_as_slow - next_fast_as_slow;
156 (diff_fast_as_slow >= 999ms && diff_fast_as_slow <= 1001ms) ?
158 (diff_fast_as_slow >= -1001ms && diff_fast_as_slow <= -999ms) ?
166 double getDrift() noexcept {
168 ttlet fast_to_slow_offset = convert(last_pair.fast) - last_pair.slow;
169 ttlet fast_to_slow_offset_ns =
static_cast<double>(fast_to_slow_offset / 1ns);
171 ttlet duration_since_calibration = last_pair.slow - prev_pair.slow;
172 ttlet duration_since_calibration_ns =
static_cast<double>(duration_since_calibration / 1ns);
173 return fast_to_slow_offset_ns / duration_since_calibration_ns;
176 void calibrate() noexcept {
177 addCalibrationPoint();
179 ttlet drift = getDrift();
181 ttlet do_gain_calibration = calibration_nr <= 5;
183 ttlet new_gain = do_gain_calibration ? getGain() : gain.load(
std::memory_order_relaxed);
184 ttlet new_bias = getBias(new_gain);
185 ttlet leap_adjustment = getLeapAdjustment(new_gain, new_bias);
188 if (leap_adjustment != 0ns) {
189 tt_log_info(
"Clock '{}' detected leap-second {} s", name, leap_adjustment / 1s);
192 if (do_gain_calibration) {
193 tt_log_info(
"Clock '{}' calibration {}: drift={:+} ns/s gain={:+.15} ns/tick",
194 name, calibration_nr, drift * 1000000000.0, new_gain / gainMultiplier
197 tt_log_info(
"Clock '{}' calibration {}: drift={:+} ns/s",
198 name, calibration_nr, drift * 1000000000.0
202 if (do_gain_calibration) {
203 gain.
store(new_gain, std::memory_order_relaxed);
205 bias.
store(new_bias + leap_adjustment, std::memory_order_relaxed);
206 leapsecond_offset += leap_adjustment;
209 typename slow_clock::duration convert(int64_t new_gain,
typename fast_clock::duration fast_duration)
const noexcept {
210 ttlet _new_gain =
static_cast<uint64_t
>(new_gain);
211 ttlet _fast_duration =
static_cast<uint64_t
>(fast_duration.count());
213 ttlet [lo, hi] = wide_multiply(_new_gain, _fast_duration);
215 static_assert(gainShift < 64);
216 ttlet slow_duration = (lo >> gainShift) | (hi << (64 - gainShift));
218 return typename slow_clock::duration(
static_cast<typename slow_clock::rep
>(slow_duration));
221 typename slow_clock::time_point convert(int64_t new_gain,
typename slow_clock::duration new_bias,
typename fast_clock::time_point fast_time)
const noexcept {
222 ttlet slow_period = convert(new_gain, fast_time.time_since_epoch());
223 return typename slow_clock::time_point(slow_period) + new_bias;