parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
- int max_lock_age, pps_forced, stratum, tai;
+ int local, max_lock_age, pps_forced, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
poll = 4;
dpoll = 0;
filter_length = 64;
+ local = 0;
pps_forced = 0;
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break;
}
+ } else if (!strcasecmp(cmd, "local")) {
+ n = 0;
+ local = 1;
} else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
break;
refclock->driver_poll = dpoll;
refclock->poll = poll;
refclock->filter_length = filter_length;
+ refclock->local = local;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
<<leapsectz,*leapsectz*>> directive must be used with this option and the
database must be kept up to date in order for this correction to work as
expected. This option does not make sense with PPS refclocks.
+*local*:::
+This option specifies that the reference clock is an unsynchronised clock which
+is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
+it should be used as a local standard to stabilise the system clock. The
+refclock will bypass the source selection. There should be at most one refclock
+specified with this option and it should have the shortest polling interval
+among all configured sources.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
int driver_polled;
int poll;
int leap_status;
+ int local;
int pps_forced;
int pps_rate;
int pps_active;
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
+ inst->local = params->local;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
}
+ if (inst->local) {
+ inst->pps_forced = 1;
+ inst->lock_ref = inst->ref_id;
+ inst->leap_status = LEAP_Unsynchronised;
+ }
+
if (inst->driver->poll) {
int max_samples;
lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
- DEBUG_LOG("refclock pulse ignored no ref sample");
- return 0;
+ if (instance->local) {
+ /* Make the first sample in order to lock to itself */
+ ref_sample.time = *cooked_time;
+ ref_sample.offset = offset;
+ ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
+ ref_sample.root_delay = ref_sample.root_dispersion = 0;
+ } else {
+ DEBUG_LOG("refclock pulse ignored no ref sample");
+ return 0;
+ }
}
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
return 0;
}
+static void
+get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
+{
+ double offset_sd, freq_sd, skew, root_delay, root_disp;
+ SST_Stats stats = SRC_GetSourcestats(inst->source);
+
+ if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
+ UTI_ZeroTimespec(ref);
+ return;
+ }
+
+ SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
+ &skew, &root_delay, &root_disp);
+}
+
+static void
+follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
+ double prev_offset)
+{
+ SST_Stats stats = SRC_GetSourcestats(inst->source);
+ double freq, dfreq, offset, doffset, elapsed;
+ struct timespec now, ref_time;
+
+ get_local_stats(inst, &ref_time, &freq, &offset);
+
+ if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
+ return;
+
+ dfreq = (freq - prev_freq) / (1.0 - prev_freq);
+ elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
+ doffset = offset - elapsed * prev_freq - prev_offset;
+
+ if (!REF_AdjustReference(doffset, dfreq))
+ return;
+
+ LCL_ReadCookedTime(&now, NULL);
+ SST_SlewSamples(stats, &now, dfreq, doffset);
+ SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
+}
+
static void
poll_timeout(void *arg)
{
inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
+ double local_freq, local_offset;
+ struct timespec local_ref_time;
+
/* Handle special case when PPS is used with the local reference */
if (inst->pps_active && inst->lock_ref == -1)
stratum = pps_stratum(inst, &sample.time);
else
stratum = inst->stratum;
+ if (inst->local) {
+ get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
+ inst->leap_status = LEAP_Unsynchronised;
+ }
+
SRC_UpdateReachability(inst->source, 1);
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
+ if (inst->local)
+ follow_local(inst, &local_ref_time, local_freq, local_offset);
+
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);