]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add PPS API refclock driver
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 28 Oct 2009 11:23:00 +0000 (12:23 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Wed, 28 Oct 2009 11:40:39 +0000 (12:40 +0100)
Makefile.in
conf.c
configure
refclock.c
refclock.h
refclock_pps.c [new file with mode: 0644]
reference.c
reference.h

index 308ff2592d7fdedf44f2cb9944749e3b0541668c..57ba2e4b6eb9450346129f7ae088dd8c19d96738 100644 (file)
@@ -41,7 +41,8 @@ OBJS = util.o sched.o regress.o local.o \
        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@
 
diff --git a/conf.c b/conf.c
index 050bef8fd64b06ccccd34bbd601f1780ac17e780..73e589b2cf421c37e0f58f870d3d9ef3fcc33c3b 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -429,7 +429,7 @@ parse_peer(const char *line)
 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;
@@ -443,6 +443,7 @@ parse_refclock(const char *line)
   poll = 4;
   dpoll = 0;
   filter_length = 15;
+  pps_rate = 0;
   offset = 0.0;
   delay = 1e-9;
   ref_id = 0;
@@ -486,6 +487,9 @@ parse_refclock(const char *line)
       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;
@@ -504,6 +508,7 @@ parse_refclock(const char *line)
   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;
index d86efa1c1ffc4df161904129a6a9c4a6f4a867e3..d9f9f8a807cd1cb147c898e1a6b6b114869862a4 100755 (executable)
--- a/configure
+++ b/configure
@@ -132,6 +132,30 @@ EOF
   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;
@@ -378,6 +402,14 @@ else
   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
index f5bb81c2ed9a938d991c6f45879a87e643469230..e0b7e1b5ba2dcb742cef6cfb1d7265741ede3cbb 100644 (file)
@@ -26,6 +26,7 @@
   */
 
 #include "refclock.h"
+#include "reference.h"
 #include "conf.h"
 #include "local.h"
 #include "memory.h"
@@ -37,6 +38,7 @@
 /* list of refclock drivers */
 extern RefclockDriver RCL_SHM_driver;
 extern RefclockDriver RCL_SOCK_driver;
+extern RefclockDriver RCL_PPS_driver;
 
 struct FilterSample {
   double offset;
@@ -59,6 +61,7 @@ struct RCL_Instance_Record {
   int poll;
   int missed_samples;
   int leap_status;
+  int pps_rate;
   struct MedianFilter filter;
   unsigned long ref_id;
   double offset;
@@ -72,6 +75,7 @@ struct RCL_Instance_Record {
 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);
@@ -111,6 +115,8 @@ RCL_Finalise(void)
 int
 RCL_AddRefclock(RefclockParameters *params)
 {
+  int pps_source = 0;
+
   RCL_Instance inst = &refclocks[n_sources];
 
   if (n_sources == MAX_RCL_SOURCES)
@@ -120,11 +126,19 @@ RCL_AddRefclock(RefclockParameters *params)
     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;
@@ -132,11 +146,19 @@ RCL_AddRefclock(RefclockParameters *params)
   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;
 
@@ -239,6 +261,85 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
   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)
 {
@@ -258,7 +359,7 @@ 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);
@@ -269,9 +370,16 @@ poll_timeout(void *arg)
       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++;
index d409886383f41520dcf372696e84450375533290..1990095bc9ab2a23c3e44648ee731493a7ef727b 100644 (file)
@@ -37,6 +37,7 @@ typedef struct {
   int driver_poll;
   int poll;
   int filter_length;
+  int pps_rate;
   unsigned long ref_id;
   double offset;
   double delay;
@@ -62,5 +63,6 @@ extern void RCL_SetDriverData(RCL_Instance instance, void *data);
 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
diff --git a/refclock_pps.c b/refclock_pps.c
new file mode 100644 (file)
index 0000000..d701b18
--- /dev/null
@@ -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
index 25e9cf643bb541cc9292d974bbc6645c8931fdcd..e5c434b1922aa9de9b8b77980a2e092d39c1280d 100644 (file)
@@ -695,6 +695,14 @@ REF_DisableLocal(void)
 
 /* ================================================== */
 
+int
+REF_IsLocalActive(void)
+{
+  return !are_we_synchronised && enable_local_stratum;
+}
+
+/* ================================================== */
+
 void
 REF_GetTrackingReport(RPT_TrackingReport *rep)
 {
index cffadb97401950ec998c0a63ca20765f0cb75d93..b4458eb222b952cbb10f137fe678c6674e4e388c 100644 (file)
@@ -140,6 +140,7 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
 
 extern void REF_EnableLocal(int stratum);
 extern void REF_DisableLocal(void);
+extern int REF_IsLocalActive(void);
 
 extern void REF_GetTrackingReport(RPT_TrackingReport *rep);