From af6ae9186b8d2aad14c7d4bd974ee1676b9af422 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 22 Feb 2022 11:00:27 +0100 Subject: [PATCH] reference: allow clock adjustments without updating reference Add support for accumulating frequency and time offset without changing the reference parameters and calling the local parameter change handlers. This will allow an unsynchronized source to operate below other sources in order to stabilize the clock. --- local.c | 18 ++++++++++++++ local.h | 5 ++++ reference.c | 67 +++++++++++++++++++++++++++++++++++++++-------------- reference.h | 4 ++++ 4 files changed, 77 insertions(+), 17 deletions(-) diff --git a/local.c b/local.c index 8dbee18..80fb6ba 100644 --- a/local.c +++ b/local.c @@ -628,6 +628,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) /* ================================================== */ +int +LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate) +{ + ChangeListEntry *first_handler; + int r; + + first_handler = change_list.next; + change_list.next = &change_list; + + r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate); + + change_list.next = first_handler; + + return r; +} + +/* ================================================== */ + void lcl_InvokeDispersionNotifyHandlers(double dispersion) { diff --git a/local.h b/local.h index 63d80e9..a23d275 100644 --- a/local.h +++ b/local.h @@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap); a slew, in one easy step */ extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); +/* Same as the routine above, except it does not call the registered + parameter change handlers */ +extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, + double corr_rate); + /* Routine to read the system precision as a log to base 2 value. */ extern int LCL_GetSysPrecisionAsLog(void); diff --git a/reference.c b/reference.c index 9032323..e6b95e9 100644 --- a/reference.c +++ b/reference.c @@ -150,6 +150,9 @@ static SCH_TimeoutID fb_drift_timeout_id; static double last_ref_update; static double last_ref_update_interval; +static double last_ref_adjustment; +static int ref_adjustments; + /* ================================================== */ static NTP_Leap get_tz_leap(time_t when, int *tai_offset); @@ -286,6 +289,8 @@ REF_Initialise(void) UTI_ZeroTimespec(&our_ref_time); last_ref_update = 0.0; last_ref_update_interval = 0.0; + last_ref_adjustment = 0.0; + ref_adjustments = 0; LCL_AddParameterChangeHandler(handle_slew, NULL); @@ -960,6 +965,27 @@ fuzz_ref_time(struct timespec *ts) /* ================================================== */ +static double +get_correction_rate(double offset_sd, double update_interval) +{ + /* We want to correct the offset quickly, but we also want to keep the + frequency error caused by the correction itself low. + + Define correction rate as the area of the region bounded by the graph of + offset corrected in time. Set the rate so that the time needed to correct + an offset equal to the current sourcestats stddev will be equal to the + update interval multiplied by the correction time ratio (assuming linear + adjustment). The offset and the time needed to make the correction are + inversely proportional. + + This is only a suggestion and it's up to the system driver how the + adjustment will be executed. */ + + return correction_time_ratio * 0.5 * offset_sd * update_interval; +} + +/* ================================================== */ + void REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time, @@ -969,7 +995,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, { double uncorrected_offset, accumulate_offset, step_offset; double residual_frequency, local_abs_frequency; - double elapsed, mono_now, update_interval, correction_rate, orig_root_distance; + double elapsed, mono_now, update_interval, orig_root_distance; struct timespec now, raw_now; int manual; @@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, last_ref_update_interval = update_interval; last_offset = offset; - /* We want to correct the offset quickly, but we also want to keep the - frequency error caused by the correction itself low. - - Define correction rate as the area of the region bounded by the graph of - offset corrected in time. Set the rate so that the time needed to correct - an offset equal to the current sourcestats stddev will be equal to the - update interval multiplied by the correction time ratio (assuming linear - adjustment). The offset and the time needed to make the correction are - inversely proportional. - - This is only a suggestion and it's up to the system driver how the - adjustment will be executed. */ - - correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval; - /* Check if the clock should be stepped */ if (is_step_limit_reached(offset, uncorrected_offset)) { /* Cancel the uncorrected offset and correct the total offset by step */ @@ -1050,7 +1061,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, } /* Adjust the clock */ - LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate); + LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, + get_correction_rate(offset_sd, update_interval)); maybe_log_offset(offset, raw_now.tv_sec); @@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, avg2_moving = 1; avg2_offset = SQUARE(offset); } + + ref_adjustments = 0; +} + +/* ================================================== */ + +int +REF_AdjustReference(double offset, double frequency) +{ + double adj_corr_rate, ref_corr_rate, mono_now; + + mono_now = SCH_GetLastEventMonoTime(); + ref_adjustments++; + + adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment); + ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) / + ref_adjustments; + last_ref_adjustment = mono_now; + + return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset, + MAX(adj_corr_rate, ref_corr_rate)); } /* ================================================== */ diff --git a/reference.h b/reference.h index 09400d4..73454d4 100644 --- a/reference.h +++ b/reference.h @@ -162,6 +162,10 @@ extern void REF_SetManualReference extern void REF_SetUnsynchronised(void); +/* Make a small correction of the clock without updating the reference + parameters and calling the clock change handlers */ +extern int REF_AdjustReference(double offset, double frequency); + /* Announce a leap second before the full reference update */ extern void REF_UpdateLeapStatus(NTP_Leap leap);