]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add lock option for PPS refclocks
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 27 Jan 2010 18:14:38 +0000 (19:14 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 28 Jan 2010 09:10:13 +0000 (10:10 +0100)
chrony.texi
conf.c
refclock.c
refclock.h

index b867d21865bb7c1feb42544922ff1712b5d0cf79..db48bc39f4306c124a977290141d4a7c78db85ee 100644 (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
diff --git a/conf.c b/conf.c
index 8cbc8b13def5ae2915685c1d29d64f62e5dc55d8..88a65255be132a0f505f0a464bf3d3fc3a9ccc9e 100644 (file)
--- a/conf.c
+++ b/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++;
 }
index 32d3d3206aa193c6a6e727adf149368295823ed6..0a9473a93e1bc8a693279d5e21701de9b1e9e1fc 100644 (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)
 {
index 459cb8669c1f0db8696196a9e2bd84c63848d544..f6fb6aa892ce72afe531ec17b8d19b6a5b63def6 100644 (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;