Add lock option for PPS refclocks

This commit is contained in:
Miroslav Lichvar 2010-01-27 19:14:38 +01:00
parent dbb550e6db
commit e261278a5c
4 changed files with 86 additions and 19 deletions

View file

@ -2289,6 +2289,12 @@ PPS signal frequency (in Hz). This option only controls how the
received pulses are aligned. To actually receive more than one
pulse per second, a negative @code{dpoll} has to be specified (-3 for
5Hz signal). The default is 1.
@item lock
This option can be used to lock a PPS refclock to another refclock
whose reference id is specified by this option. In this mode received
pulses are aligned directly to unfiltered samples from the refclock.
By default, pulses are aligned to local clock, but only when it is
well synchronised.
@item offset
This option can be used to compensate a constant error. The specified
offset (in seconds) is applied to all samples produced by the

8
conf.c
View file

@ -436,7 +436,7 @@ static void
parse_refclock(const char *line)
{
int i, n, poll, dpoll, filter_length, pps_rate;
unsigned long ref_id;
unsigned long ref_id, lock_ref_id;
double offset, delay;
const char *tmp;
char name[5], cmd[10 + 1], *param;
@ -453,6 +453,7 @@ parse_refclock(const char *line)
offset = 0.0;
delay = 1e-9;
ref_id = 0;
lock_ref_id = 0;
if (sscanf(line, "%4s%n", name, &n) != 1) {
LOG(LOGS_WARN, LOGF_Configure, "Could not read refclock driver name at line %d", line_number);
@ -481,6 +482,10 @@ parse_refclock(const char *line)
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strncasecmp(cmd, "lock", 4)) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
lock_ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strncasecmp(cmd, "poll", 4)) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@ -518,6 +523,7 @@ parse_refclock(const char *line)
refclock_sources[i].offset = offset;
refclock_sources[i].delay = delay;
refclock_sources[i].ref_id = ref_id;
refclock_sources[i].lock_ref_id = lock_ref_id;
n_refclock_sources++;
}

View file

@ -50,6 +50,7 @@ struct MedianFilter {
int length;
int index;
int used;
int last;
struct FilterSample *samples;
};
@ -65,6 +66,7 @@ struct RCL_Instance_Record {
int pps_rate;
struct MedianFilter filter;
unsigned long ref_id;
unsigned long lock_ref;
double offset;
double delay;
SCH_TimeoutID timeout_id;
@ -92,6 +94,7 @@ static void filter_init(struct MedianFilter *filter, int length);
static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset);
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset);
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
@ -172,6 +175,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_polled = 0;
inst->leap_status = 0;
inst->pps_rate = params->pps_rate;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
inst->timeout_id = -1;
@ -216,13 +220,21 @@ RCL_AddRefclock(RefclockParameters *params)
void
RCL_StartRefclocks(void)
{
int i;
int i, j;
for (i = 0; i < n_sources; i++) {
RCL_Instance inst = &refclocks[i];
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, NULL);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) {
/* Replace lock refid with index to refclocks */
for (j = 0; j < n_sources && refclocks[j].ref_id != inst->lock_ref; j++)
;
inst->lock_ref = (j < n_sources) ? j : -1;
} else
inst->lock_ref = -1;
}
if (n_sources > 0)
@ -313,22 +325,6 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
rate = instance->pps_rate;
assert(rate > 0);
/* Ignore the pulse if we are not well synchronized */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion;
if (!is_synchronised || distance >= 0.5 / rate) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse dropped second=%.9f sync=%d dist=%.9f",
second, is_synchronised, distance);
#endif
/* Drop also all stored samples */
filter_reset(&instance->filter);
return 0;
}
offset = -second - correction + instance->offset;
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
@ -338,9 +334,54 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
else if (offset >= 0.5 / rate)
offset -= 1.0 / rate;
if (instance->lock_ref != -1) {
struct timeval ref_sample_time;
double sample_diff, ref_offset, shift;
if (!filter_get_last_sample(&refclocks[instance->lock_ref].filter,
&ref_sample_time, &ref_offset))
return 0;
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= 2.0 / rate)
return 0;
/* Align the offset to the reference sample */
if ((ref_offset - offset) >= 0.0)
shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
offset += shift;
if (fabs(ref_offset - offset) >= 0.2 / rate)
return 0;
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
#endif
} else {
/* Ignore the pulse if we are not well synchronized */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion;
if (!is_synchronised || distance >= 0.5 / rate) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse dropped second=%.9f sync=%d dist=%.9f",
second, is_synchronised, distance);
#endif
/* Drop also all stored samples */
filter_reset(&instance->filter);
return 0;
}
}
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f",
second, offset + instance->offset);
second, offset);
#endif
filter_add_sample(&instance->filter, &cooked_time, offset);
@ -511,6 +552,7 @@ filter_init(struct MedianFilter *filter, int length)
filter->length = length;
filter->index = -1;
filter->used = 0;
filter->last = -1;
filter->samples = MallocArray(struct FilterSample, filter->length);
}
@ -532,6 +574,7 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
{
filter->index++;
filter->index %= filter->length;
filter->last = filter->index;
if (filter->used < filter->length)
filter->used++;
@ -539,6 +582,17 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
filter->samples[filter->index].offset = offset;
}
static int
filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset)
{
if (filter->last < 0)
return 0;
*sample_time = filter->samples[filter->last].sample_time;
*offset = filter->samples[filter->last].offset;
return 1;
}
static int
sample_compare(const void *a, const void *b)
{

View file

@ -39,6 +39,7 @@ typedef struct {
int filter_length;
int pps_rate;
unsigned long ref_id;
unsigned long lock_ref_id;
double offset;
double delay;
} RefclockParameters;