Add PPS API refclock driver

This commit is contained in:
Miroslav Lichvar 2009-10-28 12:23:00 +01:00
parent 352f03d487
commit b4069a4c3b
8 changed files with 328 additions and 4 deletions

View file

@ -41,7 +41,8 @@ OBJS = util.o sched.o regress.o local.o \
logging.o conf.o cmdmon.o md5.o keys.o \ logging.o conf.o cmdmon.o md5.o keys.o \
nameserv.o acquire.o manual.o addrfilt.o \ nameserv.o acquire.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \ cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_shm.o refclock_sock.o broadcast.o refclock.o refclock_shm.o refclock_sock.o \
refclock_pps.o
EXTRA_OBJS=@EXTRA_OBJECTS@ EXTRA_OBJS=@EXTRA_OBJECTS@

7
conf.c
View file

@ -429,7 +429,7 @@ parse_peer(const char *line)
static void static void
parse_refclock(const char *line) parse_refclock(const char *line)
{ {
int i, n, poll, dpoll, filter_length; int i, n, poll, dpoll, filter_length, pps_rate;
unsigned long ref_id; unsigned long ref_id;
double offset, delay; double offset, delay;
const char *tmp; const char *tmp;
@ -443,6 +443,7 @@ parse_refclock(const char *line)
poll = 4; poll = 4;
dpoll = 0; dpoll = 0;
filter_length = 15; filter_length = 15;
pps_rate = 0;
offset = 0.0; offset = 0.0;
delay = 1e-9; delay = 1e-9;
ref_id = 0; ref_id = 0;
@ -486,6 +487,9 @@ parse_refclock(const char *line)
if (sscanf(line, "%d%n", &filter_length, &n) != 1) { if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break; break;
} }
} else if (!strncasecmp(cmd, "rate", 4)) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
break;
} else if (!strncasecmp(cmd, "offset", 6)) { } else if (!strncasecmp(cmd, "offset", 6)) {
if (sscanf(line, "%lf%n", &offset, &n) != 1) if (sscanf(line, "%lf%n", &offset, &n) != 1)
break; break;
@ -504,6 +508,7 @@ parse_refclock(const char *line)
refclock_sources[i].driver_poll = dpoll; refclock_sources[i].driver_poll = dpoll;
refclock_sources[i].poll = poll; refclock_sources[i].poll = poll;
refclock_sources[i].filter_length = filter_length; refclock_sources[i].filter_length = filter_length;
refclock_sources[i].pps_rate = pps_rate;
refclock_sources[i].offset = offset; refclock_sources[i].offset = offset;
refclock_sources[i].delay = delay; refclock_sources[i].delay = delay;
refclock_sources[i].ref_id = ref_id; refclock_sources[i].ref_id = ref_id;

32
configure vendored
View file

@ -132,6 +132,30 @@ EOF
echo $result echo $result
} }
#}}} #}}}
#{{{ test_for_ppsapi
test_for_ppsapi () {
cat >docheck.c <<EOF;
#include <timepps.h>
int main(int argc, char **argv) {
pps_handle_t h;
pps_info_t i;
struct timespec ts;
return time_pps_fetch(&h, PPS_TSFMT_TSPEC, &i, &ts);
}
EOF
${MYCC} ${MYCFLAGS} -c -o docheck.o docheck.c >/dev/null 2>&1
if [ $? -eq 0 ]
then
result=0
else
result=1
fi
rm -f docheck.c docheck.o
echo $result
}
#}}}
#{{{ usage #{{{ usage
usage () { usage () {
cat <<EOF; cat <<EOF;
@ -378,6 +402,14 @@ else
printf "No\n" printf "No\n"
fi fi
printf "Checking for PPS API : "
if [ `test_for_ppsapi` -eq 0 ]; then
printf "Yes\n"
SYSDEFS="${SYSDEFS} -DHAVE_PPSAPI"
else
printf "No\n"
fi
if [ "x${MYCC}" = "xgcc" ]; then if [ "x${MYCC}" = "xgcc" ]; then
CCWARNFLAGS="-Wmissing-prototypes -Wall" CCWARNFLAGS="-Wmissing-prototypes -Wall"
else else

View file

@ -26,6 +26,7 @@
*/ */
#include "refclock.h" #include "refclock.h"
#include "reference.h"
#include "conf.h" #include "conf.h"
#include "local.h" #include "local.h"
#include "memory.h" #include "memory.h"
@ -37,6 +38,7 @@
/* list of refclock drivers */ /* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver; extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver; extern RefclockDriver RCL_SOCK_driver;
extern RefclockDriver RCL_PPS_driver;
struct FilterSample { struct FilterSample {
double offset; double offset;
@ -59,6 +61,7 @@ struct RCL_Instance_Record {
int poll; int poll;
int missed_samples; int missed_samples;
int leap_status; int leap_status;
int pps_rate;
struct MedianFilter filter; struct MedianFilter filter;
unsigned long ref_id; unsigned long ref_id;
double offset; double offset;
@ -72,6 +75,7 @@ struct RCL_Instance_Record {
static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES]; static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES];
static int n_sources = 0; static int n_sources = 0;
static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg); static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double afreq, static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double afreq,
double doffset, int is_step_change, void *anything); double doffset, int is_step_change, void *anything);
@ -111,6 +115,8 @@ RCL_Finalise(void)
int int
RCL_AddRefclock(RefclockParameters *params) RCL_AddRefclock(RefclockParameters *params)
{ {
int pps_source = 0;
RCL_Instance inst = &refclocks[n_sources]; RCL_Instance inst = &refclocks[n_sources];
if (n_sources == MAX_RCL_SOURCES) if (n_sources == MAX_RCL_SOURCES)
@ -120,11 +126,19 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_SHM_driver; inst->driver = &RCL_SHM_driver;
} else if (strncmp(params->driver_name, "SOCK", 4) == 0) { } else if (strncmp(params->driver_name, "SOCK", 4) == 0) {
inst->driver = &RCL_SOCK_driver; inst->driver = &RCL_SOCK_driver;
} else if (strncmp(params->driver_name, "PPS", 4) == 0) {
inst->driver = &RCL_PPS_driver;
pps_source = 1;
} else { } else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name); LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
return 0; return 0;
} }
if (!inst->driver->init && !inst->driver->poll) {
LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name);
return 0;
}
inst->data = NULL; inst->data = NULL;
inst->driver_parameter = params->driver_parameter; inst->driver_parameter = params->driver_parameter;
inst->driver_poll = params->driver_poll; inst->driver_poll = params->driver_poll;
@ -132,11 +146,19 @@ RCL_AddRefclock(RefclockParameters *params)
inst->missed_samples = 0; inst->missed_samples = 0;
inst->driver_polled = 0; inst->driver_polled = 0;
inst->leap_status = 0; inst->leap_status = 0;
inst->pps_rate = params->pps_rate;
inst->offset = params->offset; inst->offset = params->offset;
inst->delay = params->delay; inst->delay = params->delay;
inst->timeout_id = -1; inst->timeout_id = -1;
inst->source = NULL; inst->source = NULL;
if (pps_source) {
if (inst->pps_rate < 1)
inst->pps_rate = 1;
} else {
inst->pps_rate = 0;
}
if (inst->driver_poll > inst->poll) if (inst->driver_poll > inst->poll)
inst->driver_poll = inst->poll; inst->driver_poll = inst->poll;
@ -239,6 +261,85 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
return 1; return 1;
} }
int
RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
{
double correction, offset;
struct timeval cooked_time;
int rate;
struct timeval ref_time;
int is_synchronised, stratum;
double root_delay, root_dispersion, distance;
NTP_Leap leap;
unsigned long ref_id;
correction = LCL_GetOffsetCorrection(pulse_time);
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
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
return 0;
}
offset = -second - correction + instance->offset;
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
offset -= (long)(offset * rate) / (double)rate;
if (offset < -0.5 / rate)
offset += 1.0 / rate;
else if (offset >= 0.5 / rate)
offset -= 1.0 / rate;
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f",
second, offset + instance->offset);
#endif
filter_add_sample(&instance->filter, &cooked_time, offset);
instance->leap_status = LEAP_Normal;
return 1;
}
static int
pps_stratum(RCL_Instance instance, struct timeval *tv)
{
struct timeval ref_time;
int is_synchronised, stratum, i;
double root_delay, root_dispersion;
NTP_Leap leap;
unsigned long ref_id;
REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if local stratum is active
or this is the current source */
if (ref_id == instance->ref_id || REF_IsLocalActive())
return stratum - 1;
/* Or the current source is another PPS refclock */
for (i = 0; i < n_sources; i++) {
if (refclocks[i].ref_id == ref_id && refclocks[i].pps_rate)
return stratum - 1;
}
return 0;
}
static void static void
poll_timeout(void *arg) poll_timeout(void *arg)
{ {
@ -258,7 +359,7 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) { if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion; double offset, dispersion;
struct timeval sample_time; struct timeval sample_time;
int sample_ok; int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion); sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
filter_reset(&inst->filter); filter_reset(&inst->filter);
@ -269,9 +370,16 @@ poll_timeout(void *arg)
LOG(LOGS_INFO, LOGF_Refclock, "refclock filtered sample: offset=%.9f dispersion=%.9f [%s]", LOG(LOGS_INFO, LOGF_Refclock, "refclock filtered sample: offset=%.9f dispersion=%.9f [%s]",
offset, dispersion, UTI_TimevalToString(&sample_time)); offset, dispersion, UTI_TimevalToString(&sample_time));
#endif #endif
if (inst->pps_rate)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
stratum = 0;
SRC_SetReachable(inst->source); SRC_SetReachable(inst->source);
SRC_AccumulateSample(inst->source, &sample_time, offset, SRC_AccumulateSample(inst->source, &sample_time, offset,
inst->delay, dispersion, inst->delay, dispersion, 0, inst->leap_status); inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
inst->missed_samples = 0; inst->missed_samples = 0;
} else { } else {
inst->missed_samples++; inst->missed_samples++;

View file

@ -37,6 +37,7 @@ typedef struct {
int driver_poll; int driver_poll;
int poll; int poll;
int filter_length; int filter_length;
int pps_rate;
unsigned long ref_id; unsigned long ref_id;
double offset; double offset;
double delay; double delay;
@ -62,5 +63,6 @@ extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance); extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance); extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, NTP_Leap leap_status); extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, NTP_Leap leap_status);
extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second);
#endif #endif

167
refclock_pps.c Normal file
View file

@ -0,0 +1,167 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
**********************************************************************
=======================================================================
PPSAPI refclock driver.
*/
#include "refclock.h"
#if HAVE_PPSAPI
#include <timepps.h>
#include "logging.h"
#include "memory.h"
#include "util.h"
struct pps_instance {
pps_handle_t handle;
unsigned long last_seq;
int edge_clear;
};
static int pps_initialise(RCL_Instance instance) {
pps_handle_t handle;
pps_params_t params;
struct pps_instance *pps;
int fd, edge_clear, mode;
char *path, *s;
path = RCL_GetDriverParameter(instance);
edge_clear = 0;
if ((s = strrchr(path, ':')) != NULL) {
*s = '\0';
edge_clear = atoi(s + 1);
}
fd = open(path, O_RDWR);
if (fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
return 0;
}
if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path);
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path);
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path);
return 0;
}
if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR;
} else {
if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR;
params.mode &= ~PPS_CAPTUREASSERT;
}
if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path);
return 0;
}
pps = MallocNew(struct pps_instance);
pps->handle = handle;
pps->last_seq = 0;
pps->edge_clear = edge_clear;
RCL_SetDriverData(instance, pps);
return 1;
}
static void pps_finalise(RCL_Instance instance)
{
struct pps_instance *pps;
pps = (struct pps_instance *)RCL_GetDriverData(instance);
time_pps_destroy(pps->handle);
Free(pps);
}
static int pps_poll(RCL_Instance instance)
{
struct pps_instance *pps;
struct timespec ts;
struct timeval tv;
pps_info_t pps_info;
unsigned long seq;
pps = (struct pps_instance *)RCL_GetDriverData(instance);
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "time_pps_fetch error");
#endif
return 0;
}
if (!pps->edge_clear) {
seq = pps_info.assert_sequence;
ts = pps_info.assert_timestamp;
} else {
seq = pps_info.clear_sequence;
ts = pps_info.clear_timestamp;
}
if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
return 0;
}
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9);
}
RefclockDriver RCL_PPS_driver = {
pps_initialise,
pps_finalise,
pps_poll
};
#else
RefclockDriver RCL_PPS_driver = { NULL, NULL, NULL };
#endif

View file

@ -695,6 +695,14 @@ REF_DisableLocal(void)
/* ================================================== */ /* ================================================== */
int
REF_IsLocalActive(void)
{
return !are_we_synchronised && enable_local_stratum;
}
/* ================================================== */
void void
REF_GetTrackingReport(RPT_TrackingReport *rep) REF_GetTrackingReport(RPT_TrackingReport *rep)
{ {

View file

@ -140,6 +140,7 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
extern void REF_EnableLocal(int stratum); extern void REF_EnableLocal(int stratum);
extern void REF_DisableLocal(void); extern void REF_DisableLocal(void);
extern int REF_IsLocalActive(void);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep); extern void REF_GetTrackingReport(RPT_TrackingReport *rep);