/* The minimum allowed skew */
#define MIN_SKEW 1.0e-12
+/* The asymmetry of network jitter when all jitter is in one direction */
+#define MAX_ASYMMETRY 0.5
+
+/* The minimum estimated asymmetry that can activate the offset correction */
+#define MIN_ASYMMETRY 0.45
+
+/* The minimum number of consecutive asymmetries with the same sign needed
+ to activate the offset correction */
+#define MIN_ASYMMETRY_RUN 10
+
+/* The maximum value of the counter */
+#define MAX_ASYMMETRY_RUN 1000
+
/* ================================================== */
static LOG_FileID logfileid;
/* Number of runs of the same sign amongst the residuals */
int nruns;
+ /* Number of consecutive estimated asymmetries with the same sign.
+ The sign of the number encodes the sign of the asymmetry. */
+ int asymmetry_run;
+
+ /* This is the latest estimated asymmetry of network jitter */
+ double asymmetry;
+
/* This value contains the estimated frequency. This is the number
of seconds that the local clock gains relative to the reference
source per unit local time. (Positive => local clock fast,
inst->offset_time.tv_usec = 0;
inst->variance = 16.0;
inst->nruns = 0;
+ inst->asymmetry_run = 0;
+ inst->asymmetry = 0.0;
}
/* ================================================== */
}
}
+/* ================================================== */
+/* This function estimates asymmetry of network jitter on the path to the
+ source as a slope of offset against network delay in multiple linear
+ regression. If the asymmetry is significant and its sign doesn't change
+ frequently, the measured offsets (which are used later to estimate the
+ offset and frequency of the clock) are corrected to correspond to the
+ minimum network delay. This can significantly improve the accuracy and
+ stability of the estimated offset and frequency. */
+
+static void
+correct_asymmetry(SST_Stats inst, double *times_back, double *offsets)
+{
+ double asymmetry, delays[MAX_SAMPLES * REGRESS_RUNS_RATIO];
+ int i, n;
+
+ /* Don't try to estimate the asymmetry with reference clocks */
+ if (!inst->ip_addr)
+ return;
+
+ n = inst->runs_samples + inst->n_samples;
+
+ for (i = 0; i < n; i++)
+ delays[i] = inst->peer_delays[get_runsbuf_index(inst, i - inst->runs_samples)] -
+ inst->peer_delays[inst->min_delay_sample];
+
+ /* Reset the counter when the regression fails or the sign changes */
+ if (!RGR_MultipleRegress(times_back, delays, offsets, n, &asymmetry) ||
+ asymmetry * inst->asymmetry_run < 0.0) {
+ inst->asymmetry_run = 0;
+ inst->asymmetry = 0.0;
+ return;
+ }
+
+ asymmetry = CLAMP(-MAX_ASYMMETRY, asymmetry, MAX_ASYMMETRY);
+
+ if (asymmetry <= -MIN_ASYMMETRY && inst->asymmetry_run > -MAX_ASYMMETRY_RUN)
+ inst->asymmetry_run--;
+ else if (asymmetry >= MIN_ASYMMETRY && inst->asymmetry_run < MAX_ASYMMETRY_RUN)
+ inst->asymmetry_run++;
+
+ if (abs(inst->asymmetry_run) < MIN_ASYMMETRY_RUN)
+ return;
+
+ /* Correct the offsets */
+ for (i = 0; i < n; i++)
+ offsets[i] -= asymmetry * delays[i];
+
+ inst->asymmetry = asymmetry;
+}
+
/* ================================================== */
/* This defines the assumed ratio between the standard deviation of
}
}
+ correct_asymmetry(inst, times_back, offsets);
+
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,
inst->n_samples, inst->runs_samples,
stress = fabs(old_freq - inst->estimated_frequency) / old_skew;
- DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d",
+ DEBUG_LOG(LOGF_SourceStats, "off=%e freq=%e skew=%e n=%d bs=%d runs=%d asym=%f arun=%d",
inst->estimated_offset, inst->estimated_frequency, inst->skew,
- inst->n_samples, best_start, inst->nruns);
+ inst->n_samples, best_start, inst->nruns,
+ inst->asymmetry, inst->asymmetry_run);
if (logfileid != -1) {
LOG_FileWrite(logfileid, "%s %-15s %10.3e %10.3e %10.3e %10.3e %10.3e %7.1e %3d %3d %3d",