Introduce offset correction rate

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 (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.
This commit is contained in:
Miroslav Lichvar 2011-09-05 15:45:32 +02:00
parent 79e5f2be13
commit c7d0232bb1
13 changed files with 40 additions and 20 deletions

View file

@ -708,7 +708,7 @@ process_measurements(void)
LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (slew)",
fabs(estimated_offset),
(estimated_offset >= 0) ? "fast" : "slow");
LCL_AccumulateOffset(estimated_offset);
LCL_AccumulateOffset(estimated_offset, 0.0);
}
} else {

View file

@ -1354,7 +1354,7 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
usec = (long)(ntohl(rx_message->data.doffset.usec));
doffset = (double) sec + 1.0e-6 * (double) usec;
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
LCL_AccumulateOffset(doffset);
LCL_AccumulateOffset(doffset, 0.0);
tx_message->status = htons(STT_SUCCESS);
}

10
local.c
View file

@ -443,7 +443,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
/* ================================================== */
void
LCL_AccumulateOffset(double offset)
LCL_AccumulateOffset(double offset, double corr_rate)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
@ -454,7 +454,7 @@ LCL_AccumulateOffset(double offset)
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
(*drv_accrue_offset)(offset);
(*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
@ -505,7 +505,7 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
/* ================================================== */
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
@ -532,7 +532,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset)
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 + old_freq_ppm);
(*drv_accrue_offset)(doffset);
(*drv_accrue_offset)(doffset, corr_rate);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
@ -598,7 +598,7 @@ LCL_MakeStep(double threshold)
return 0;
/* Cancel remaining slew and make the step */
LCL_AccumulateOffset(correction);
LCL_AccumulateOffset(correction, 0.0);
LCL_ApplyStepOffset(-correction);
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.3f seconds", correction);

View file

@ -138,9 +138,10 @@ extern void LCL_AccumulateDeltaFrequency(double dfreq);
/* Routine to apply an offset (in seconds) to the local clock. The
argument should be positive to move the clock backwards (i.e. the
local clock is currently fast of true time), or negative to move it
forwards (i.e. it is currently slow of true time). */
forwards (i.e. it is currently slow of true time). Provided is also
a suggested correction rate (correction time * offset). */
extern void LCL_AccumulateOffset(double offset);
extern void LCL_AccumulateOffset(double offset, double corr_rate);
/* Routine to apply an immediate offset by doing a sudden step if
possible. (Intended for use after an initial estimate of offset has
@ -158,7 +159,7 @@ extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cook
/* Perform the combination of modifying the frequency and applying
a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset);
extern void LCL_AccumulateFrequencyAndOffset(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);

View file

@ -41,8 +41,9 @@ typedef double (*lcl_ReadFrequencyDriver)(void);
typedef double (*lcl_SetFrequencyDriver)(double freq_ppm);
/* System driver to accrue an offset. A positive argument means slew
the clock forwards. */
typedef void (*lcl_AccrueOffsetDriver)(double offset);
the clock forwards. The suggested correction rate of time to correct the
offset is given in 'corr_rate'. */
typedef void (*lcl_AccrueOffsetDriver)(double offset, double corr_rate);
/* System driver to apply a step offset. A positive argument means step
the clock forwards. */

View file

@ -541,6 +541,7 @@ REF_SetReference(int stratum,
IPAddr *ref_ip,
struct timeval *ref_time,
double offset,
double offset_sd,
double frequency,
double skew,
double root_delay,
@ -556,6 +557,7 @@ REF_SetReference(int stratum,
double abs_freq_ppm;
double update_interval;
double elapsed;
double correction_rate;
struct timeval now;
assert(initialised);
@ -611,6 +613,20 @@ REF_SetReference(int stratum,
}
last_ref_update = now;
/* 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 (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 = 0.5 * offset_sd * update_interval;
/* Eliminate updates that are based on totally unreliable frequency
information */
@ -645,7 +661,7 @@ REF_SetReference(int stratum,
our_residual_freq = new_freq - our_frequency;
maybe_log_offset(our_offset);
LCL_AccumulateFrequencyAndOffset(our_frequency, our_offset);
LCL_AccumulateFrequencyAndOffset(our_frequency, our_offset, correction_rate);
} else {
@ -653,7 +669,7 @@ REF_SetReference(int stratum,
LOG(LOGS_INFO, LOGF_Reference, "Skew %f too large to track, offset=%f", skew, our_offset);
#endif
maybe_log_offset(our_offset);
LCL_AccumulateOffset(our_offset);
LCL_AccumulateOffset(our_offset, correction_rate);
our_residual_freq = frequency;
}
@ -714,7 +730,7 @@ REF_SetManualReference
our_residual_freq = 0.0;
maybe_log_offset(offset);
LCL_AccumulateFrequencyAndOffset(frequency, offset);
LCL_AccumulateFrequencyAndOffset(frequency, offset, 0.0);
maybe_make_step();
abs_freq_ppm = LCL_ReadAbsoluteFrequency();

View file

@ -109,6 +109,7 @@ extern void REF_SetReference
IPAddr *ref_ip,
struct timeval *ref_time,
double offset,
double offset_sd,
double frequency,
double skew,
double root_delay,

View file

@ -664,7 +664,7 @@ handle_initial_trim(void)
sys_error_now = rtc_error_now - coef_seconds_fast;
LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now);
LCL_AccumulateOffset(sys_error_now);
LCL_AccumulateOffset(sys_error_now, 0.0);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time");
}

View file

@ -857,6 +857,7 @@ SRC_SelectSource(uint32_t match_refid)
sources[selected_source_index]->ip_addr,
&ref_time,
src_offset,
src_offset_sd,
src_frequency,
src_skew,
src_root_delay,

View file

@ -592,7 +592,7 @@ abort_slew(void)
time) */
static void
accrue_offset(double offset)
accrue_offset(double offset, double corr_rate)
{
/* Add the new offset to the register */
offset_register += offset;

View file

@ -199,7 +199,7 @@ stop_adjust(void)
slew backwards */
static void
accrue_offset(double offset)
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;

View file

@ -212,7 +212,7 @@ stop_adjust(void)
slew backwards */
static void
accrue_offset(double offset)
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;

View file

@ -216,7 +216,7 @@ stop_adjust(void)
slew backwards */
static void
accrue_offset(double offset)
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;