hwclock: don't drop valid samples in HCL_ProcessReadings()

Modify the HCL_ProcessReadings() function to try to always provide
a valid sample. Instead of dropping a sample outside of the expected
delay, provide its assumed quality level as a small integer (relative to
already accumulated samples), and let the caller decide what quality is
acceptable.
This commit is contained in:
Miroslav Lichvar
2025-08-11 16:00:38 +02:00
parent f78e4681ef
commit 5535384878
5 changed files with 38 additions and 20 deletions

View File

@@ -163,7 +163,8 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
int int
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3], HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err) struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality)
{ {
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err; double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq; double delay_sum, hw_sum, local_sum, local_prec, freq;
@@ -228,11 +229,13 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts); UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts); UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
*err = MAX(delay_sum / combined / 2.0, clock->precision); *err = MAX(delay_sum / combined / 2.0, clock->precision);
*quality = 2;
return 1; return 1;
} }
/* Accept the reading with minimum delay if its interval does not contain /* Indicate acceptable quality of the reading with minimum delay if its
the current offset predicted from previous samples */ interval does not contain the current offset predicted from previous
samples, or a new sample is needed to get the tracking working */
*hw_ts = tss[min_reading][1]; *hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts); UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
@@ -242,11 +245,14 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
LCL_CookTime(local_ts, &ts1, NULL); LCL_CookTime(local_ts, &ts1, NULL);
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) || if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) { ((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err); *quality = 1;
return 1; } else {
*quality = 0;
} }
return 0; DEBUG_LOG("Min-delay reading err=%e prerr=%e ql=%d", *err, pred_err, *quality);
return 1;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -38,10 +38,14 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */ /* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now); extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and /* Process new readings of the HW clock in the form of (sys, hw, sys) triplets
produce a sample which can be accumulated */ and produce a sample which can be accumulated by HCL_AccumulateSample().
Indicate the quality of the sample relative to already processed samples as
a value of 0, 1, or 2, where a sample of quality 0 should normally be
dropped. */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3], extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err); struct timespec *hw_ts, struct timespec *local_ts, double *err,
int *quality);
/* Accumulate a new sample */ /* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts, extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,

View File

@@ -519,7 +519,7 @@ poll_phc(struct Interface *iface, struct timespec *now)
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts; struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
struct timespec phc_readings[PHC_READINGS][3]; struct timespec phc_readings[PHC_READINGS][3];
double phc_err, local_err, interval; double phc_err, local_err, interval;
int n_readings; int n_readings, quality;
if (!HCL_NeedsNewSample(iface->clock, now)) if (!HCL_NeedsNewSample(iface->clock, now))
return; return;
@@ -543,7 +543,8 @@ poll_phc(struct Interface *iface, struct timespec *now)
return; return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings, if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err)) &sample_phc_ts, &sample_sys_ts, &phc_err, &quality) ||
quality <= 0)
return; return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err); LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);

View File

@@ -198,7 +198,7 @@ static int phc_poll(RCL_Instance instance)
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3]; struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc; struct phc_instance *phc;
double phc_err, local_err; double phc_err, local_err;
int n_readings; int n_readings, quality;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
@@ -210,15 +210,20 @@ static int phc_poll(RCL_Instance instance)
if (!phc->extpps) if (!phc->extpps)
RCL_UpdateReachability(instance); RCL_UpdateReachability(instance);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err)) if (!HCL_ProcessReadings(phc->clock, n_readings, readings,
&phc_ts, &sys_ts, &phc_err, &quality))
return 0; return 0;
LCL_CookTime(&sys_ts, &local_ts, &local_err); LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err); if (quality > 0)
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (phc->extpps) if (phc->extpps)
return 0; return 0;
if (quality <= 0)
return 0;
DEBUG_LOG("PHC offset: %+.9f err: %.9f", DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err); UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);

View File

@@ -32,9 +32,9 @@ test_unit(void)
{ {
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts; struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
struct timespec readings[MAX_READINGS][3]; struct timespec readings[MAX_READINGS][3];
int i, j, k, l, new_sample, n_readings, count, quality;
HCL_Instance clock; HCL_Instance clock;
double freq, jitter, interval, dj, err, sum; double freq, jitter, interval, dj, err, sum;
int i, j, k, l, new_sample, n_readings, count;
LCL_Initialise(); LCL_Initialise();
TST_RegisterDummyDrivers(); TST_RegisterDummyDrivers();
@@ -84,11 +84,13 @@ test_unit(void)
UTI_ZeroTimespec(&hw_ts); UTI_ZeroTimespec(&hw_ts);
UTI_ZeroTimespec(&local_ts); UTI_ZeroTimespec(&local_ts);
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err)) { new_sample = 0;
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter); if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err, &quality)) {
new_sample = 1; TEST_CHECK(quality >= 0 && quality <= 2);
} else { if (quality > 0) {
new_sample = 0; HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
new_sample = 1;
}
} }
} }