logging.o conf.o cmdmon.o md5.o keys.o \
nameserv.o acquire.o manual.o addrfilt.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@
static void
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;
double offset, delay;
const char *tmp;
poll = 4;
dpoll = 0;
filter_length = 15;
+ pps_rate = 0;
offset = 0.0;
delay = 1e-9;
ref_id = 0;
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break;
}
+ } else if (!strncasecmp(cmd, "rate", 4)) {
+ if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
+ break;
} else if (!strncasecmp(cmd, "offset", 6)) {
if (sscanf(line, "%lf%n", &offset, &n) != 1)
break;
refclock_sources[i].driver_poll = dpoll;
refclock_sources[i].poll = poll;
refclock_sources[i].filter_length = filter_length;
+ refclock_sources[i].pps_rate = pps_rate;
refclock_sources[i].offset = offset;
refclock_sources[i].delay = delay;
refclock_sources[i].ref_id = ref_id;
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 () {
cat <<EOF;
printf "No\n"
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
CCWARNFLAGS="-Wmissing-prototypes -Wall"
else
*/
#include "refclock.h"
+#include "reference.h"
#include "conf.h"
#include "local.h"
#include "memory.h"
/* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver;
+extern RefclockDriver RCL_PPS_driver;
struct FilterSample {
double offset;
int poll;
int missed_samples;
int leap_status;
+ int pps_rate;
struct MedianFilter filter;
unsigned long ref_id;
double offset;
static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES];
static int n_sources = 0;
+static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, double afreq,
double doffset, int is_step_change, void *anything);
int
RCL_AddRefclock(RefclockParameters *params)
{
+ int pps_source = 0;
+
RCL_Instance inst = &refclocks[n_sources];
if (n_sources == MAX_RCL_SOURCES)
inst->driver = &RCL_SHM_driver;
} else if (strncmp(params->driver_name, "SOCK", 4) == 0) {
inst->driver = &RCL_SOCK_driver;
+ } else if (strncmp(params->driver_name, "PPS", 4) == 0) {
+ inst->driver = &RCL_PPS_driver;
+ pps_source = 1;
} else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
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->driver_parameter = params->driver_parameter;
inst->driver_poll = params->driver_poll;
inst->missed_samples = 0;
inst->driver_polled = 0;
inst->leap_status = 0;
+ inst->pps_rate = params->pps_rate;
inst->offset = params->offset;
inst->delay = params->delay;
inst->timeout_id = -1;
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)
inst->driver_poll = inst->poll;
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
poll_timeout(void *arg)
{
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion;
struct timeval sample_time;
- int sample_ok;
+ int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
filter_reset(&inst->filter);
LOG(LOGS_INFO, LOGF_Refclock, "refclock filtered sample: offset=%.9f dispersion=%.9f [%s]",
offset, dispersion, UTI_TimevalToString(&sample_time));
#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_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;
} else {
inst->missed_samples++;
int driver_poll;
int poll;
int filter_length;
+ int pps_rate;
unsigned long ref_id;
double offset;
double delay;
extern void *RCL_GetDriverData(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_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second);
#endif
--- /dev/null
+/*
+ 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, ¶ms) < 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, ¶ms) < 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
/* ================================================== */
+int
+REF_IsLocalActive(void)
+{
+ return !are_we_synchronised && enable_local_stratum;
+}
+
+/* ================================================== */
+
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
extern void REF_EnableLocal(int stratum);
extern void REF_DisableLocal(void);
+extern int REF_IsLocalActive(void);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);