23 using slow_clock = C1;
24 using fast_clock = C2;
26 struct time_point_pair {
27 typename slow_clock::time_point slow;
28 typename fast_clock::time_point fast;
31 time_point_pair first_pair;
32 time_point_pair prev_pair;
33 time_point_pair last_pair;
34 int calibration_nr = 0;
36 static constexpr int gainShift = 60;
37 static constexpr double gainMultiplier =
static_cast<double>(1ULL << gainShift);
44 typename slow_clock::duration leapsecond_offset = 0ns;
63 typename slow_clock::time_point convert(
typename fast_clock::time_point fast_time)
const noexcept {
64 return convert(gain.
load(std::memory_order_relaxed), bias.
load(std::memory_order_relaxed), fast_time);
67 typename slow_clock::duration convert(
typename fast_clock::duration fast_duration)
const noexcept {
68 return convert(gain.
load(std::memory_order_relaxed), fast_duration);
74 void calibrate_tick() noexcept {
77 if (calibration_nr > 2) {
78 backoff = (calibration_nr - 2) * 10s;
85 if (last_pair.slow + backoff < slow_clock::now()) {
91 static time_point_pair makeCalibrationPoint() noexcept {
94 ttlet f1 = fast_clock::now();
95 ttlet s1 = slow_clock::now();
96 ttlet f2 = fast_clock::now();
97 ttlet s2 = slow_clock::now();
98 ttlet f3 = fast_clock::now();
100 if ((f2 - f1) < (f3 - f2)) {
107 void addCalibrationPoint() noexcept {
108 auto tp = makeCalibrationPoint();
109 if (calibration_nr++ == 0) {
112 prev_pair = last_pair;
116 int64_t getGain() noexcept {
118 ttlet diff_slow =
static_cast<double>((last_pair.slow - first_pair.slow).
count());
119 ttlet diff_fast =
static_cast<double>((last_pair.fast - first_pair.fast).
count());
121 if (calibration_nr < 2 || diff_fast == 0.0) {
122 return static_cast<int64_t
>(gainMultiplier);
124 auto new_gain = diff_slow / diff_fast;
125 return static_cast<int64_t
>(
std::round(new_gain * gainMultiplier));
129 typename slow_clock::duration getBias(int64_t new_gain)
noexcept {
131 auto tmp =
static_cast<ubig128
>(last_pair.fast.time_since_epoch() / 1ns);
137 tmp += (1LL << (gainShift - 1));
142 ttlet now_fast_after_gain =
typename slow_clock::duration(
static_cast<typename slow_clock::rep
>(tmp));
144 return (last_pair.slow.time_since_epoch() + leapsecond_offset) - now_fast_after_gain;
147 typename slow_clock::duration getLeapAdjustment(int64_t new_gain,
typename slow_clock::duration new_bias)
150 ttlet prev_fast_as_slow = convert(last_pair.fast);
151 ttlet next_fast_as_slow = convert(new_gain, new_bias, last_pair.fast);
152 ttlet diff_fast_as_slow = prev_fast_as_slow - next_fast_as_slow;
155 (diff_fast_as_slow >= 999ms && diff_fast_as_slow <= 1001ms) ?
157 (diff_fast_as_slow >= -1001ms && diff_fast_as_slow <= -999ms) ?
165 double getDrift() noexcept {
167 ttlet fast_to_slow_offset = convert(last_pair.fast) - last_pair.slow;
168 ttlet fast_to_slow_offset_ns =
static_cast<double>(fast_to_slow_offset / 1ns);
170 ttlet duration_since_calibration = last_pair.slow - prev_pair.slow;
171 ttlet duration_since_calibration_ns =
static_cast<double>(duration_since_calibration / 1ns);
172 return fast_to_slow_offset_ns / duration_since_calibration_ns;
175 void calibrate() noexcept {
176 addCalibrationPoint();
178 ttlet drift = getDrift();
180 ttlet do_gain_calibration = calibration_nr <= 5;
182 ttlet new_gain = do_gain_calibration ? getGain() : gain.load(
std::memory_order_relaxed);
183 ttlet new_bias = getBias(new_gain);
184 ttlet leap_adjustment = getLeapAdjustment(new_gain, new_bias);
187 if (leap_adjustment != 0ns) {
188 LOG_INFO(
"Clock '{}' detected leap-second {} s", name, leap_adjustment / 1s);
191 if (do_gain_calibration) {
192 LOG_INFO(
"Clock '{}' calibration {}: drift={:+} ns/s gain={:+.15} ns/tick",
193 name, calibration_nr, drift * 1000000000.0, new_gain / gainMultiplier
196 LOG_INFO(
"Clock '{}' calibration {}: drift={:+} ns/s",
197 name, calibration_nr, drift * 1000000000.0
201 if (do_gain_calibration) {
202 gain.
store(new_gain, std::memory_order_relaxed);
204 bias.
store(new_bias + leap_adjustment, std::memory_order_relaxed);
205 leapsecond_offset += leap_adjustment;
208 typename slow_clock::duration convert(int64_t new_gain,
typename fast_clock::duration fast_duration)
const noexcept {
209 ttlet _new_gain =
static_cast<uint64_t
>(new_gain);
210 ttlet _fast_duration =
static_cast<uint64_t
>(fast_duration.count());
212 ttlet [lo, hi] = wide_multiply(_new_gain, _fast_duration);
214 static_assert(gainShift < 64);
215 ttlet slow_duration = (lo >> gainShift) | (hi << (64 - gainShift));
217 return typename slow_clock::duration(
static_cast<typename slow_clock::rep
>(slow_duration));
220 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 {
221 ttlet slow_period = convert(new_gain, fast_time.time_since_epoch());
222 return typename slow_clock::time_point(slow_period) + new_bias;