Add lock option for PPS refclocks
This commit is contained in:
parent
dbb550e6db
commit
e261278a5c
4 changed files with 86 additions and 19 deletions
|
@ -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
8
conf.c
|
@ -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++;
|
||||
}
|
||||
|
|
90
refclock.c
90
refclock.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue