]> git.ipfire.org Git - thirdparty/nqptp.git/commitdiff
Add a lookback to see if the master's first offset can be improved with prior estimat...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 5 Jun 2021 16:53:52 +0000 (17:53 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 5 Jun 2021 16:53:52 +0000 (17:53 +0100)
nqptp-clock-sources.c
nqptp-clock-sources.h
nqptp-message-handlers.c
nqptp.c
nqptp.h

index fbe55732b2c32247e10ec7c4fab53d60f0afed0a..8d22645cb86837d13def35978b487f65c3d5d54b 100644 (file)
@@ -20,6 +20,7 @@
 #include "nqptp-clock-sources.h"
 #include "debug.h"
 #include "nqptp-ptp-definitions.h"
+#include "general-utilities.h"
 #include <arpa/inet.h>
 #include <ifaddrs.h>
 #include <string.h>
@@ -68,6 +69,7 @@ int create_clock_source_record(char *sender_string,
     memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data));
     strncpy((char *)&clocks_private_info[i].ip, sender_string,
             FIELD_SIZEOF(clock_source_private_data, ip) - 1);
+    clocks_private_info[i].vacant_samples = MAX_TIMING_SAMPLES;
     clocks_private_info[i].in_use = 1;
     debug(2, "create record for ip: %s.", &clocks_private_info[i].ip);
   } else {
@@ -202,22 +204,106 @@ void update_master() {
     if (old_master != -1) {
       // but there was a master clock, so remove it
       debug(1, "shm interface -- remove master clock designation");
-      update_master_clock_info(0, NULL, 0, 0);
+      update_master_clock_info(0, NULL, 0, 0, 0);
     }
     if (timing_peer_count == 0)
       debug(2, "No timing peer list found");
     else
       debug(1, "No master clock not found!");
-  } else {
-    // we found a master clock
-    clocks_private[best_so_far].flags |= (1 << clock_is_master);
-    // master_clock_index = best_so_far;
-    if (old_master != best_so_far) {
-      clocks_private[best_so_far].previous_offset_time = 0; // resync when you become master
+    } else {
+      // we found a master clock
+      clocks_private[best_so_far].flags |= (1 << clock_is_master);
+
+      if (old_master != best_so_far) {
+
+/*
+      // clang-format off
+      // now we use the last few samples to calculate the best offset for the
+      // new master clock.
+
+      // the time of the oldest sample we use will become the time of the start of the
+      // mastership
+
+      // we will accept samples that would make the local-to-clock offset greatest,
+      // provided they are not too old and that they don't push the current clock time
+      // more than, say, 1000 ms into the future.
+
+      // this is the only time estimate we have when the clock is definitely a master
+      // so we use it to eliminate any previous time estimates, made when the clock wasn't designated
+      // a master, that would put it more than, say, a second further into the future
+
+      // allow the samples to give a valid master clock time up to this much later than the single definitive sample we have
+
+      uint64_t oldest_acceptable_master_clock_time = clocks_private[best_so_far].source_time + 1000000000;
+
+      // we will try to improve on this single definitive local_to_source_time_offset we have
+      int changes_made = 0;
+
+      uint64_t best_offset_so_far = clocks_private[best_so_far].local_to_source_time_offset;
+      uint64_t age_of_oldest_legitimate_sample = clocks_private[best_so_far].local_time;
+
+      int number_of_samples = MAX_TIMING_SAMPLES - clocks_private[best_so_far].vacant_samples;
+      int samples_checked = 0;
+//      if (0) {
+      if (number_of_samples > 0) {
+        debug(1,"Number of samples: %d.", number_of_samples);
+        uint64_t time_now = get_time_now();
+        uint64_t oldest_acceptable_time = time_now - 10000000000; // only go back this far (ns)
+        int i;
+        for (i = 0; i < number_of_samples; i++) {
+          int64_t age_relative_to_oldest_acceptable_time = clocks_private[best_so_far].samples[i].local_time - oldest_acceptable_time;
+          if (age_relative_to_oldest_acceptable_time > 0) {
+             if (clocks_private[best_so_far].samples[i].local_time < age_of_oldest_legitimate_sample) {
+              age_of_oldest_legitimate_sample = clocks_private[best_so_far].samples[i].local_time;
+            }
+            uint64_t possible_offset = clocks_private[best_so_far].samples[i].clock_time - clocks_private[best_so_far].samples[i].local_time;
+            uint64_t possible_master_clock_time = clocks_private[best_so_far].local_time + possible_offset;
+            int64_t age_relative_to_oldest_acceptable_master_clock_time = possible_master_clock_time - oldest_acceptable_master_clock_time;
+            if (age_relative_to_oldest_acceptable_master_clock_time <= 0) {
+              samples_checked++;
+              // so, the sample was not obtained too far in the past
+              // and it would not push the estimated master clock_time too far into the future
+              // so, if it is greater than the best_offset_so_far, then make it the new one
+              if (possible_offset > best_offset_so_far) {
+                debug(1,"new best offset");
+                best_offset_so_far = possible_offset;
+                changes_made++;
+              }
+            } else {
+              debug(1,"sample too far into the future");
+            }
+          } else {
+            debug(1,"sample too old");
+          }
+        }
+
+      }
+
+//      if (changes_made == 0) {
+//         clocks_private[best_so_far].previous_offset_time = 0;  // if you have no previous samples (how?), then resync
+//         clocks_private[best_so_far].mastership_start_time = clocks_private[best_so_far].local_time;
+//      } else {
+        clocks_private[best_so_far].mastership_start_time = age_of_oldest_legitimate_sample;
+        int64_t offset_difference = best_offset_so_far - clocks_private[best_so_far].local_to_source_time_offset;
+
+        debug(1,"Lookback difference: %f ms with %d samples checked of %d samples total.", 0.000001 * offset_difference, samples_checked, number_of_samples);
+        clocks_private[best_so_far].local_to_source_time_offset = best_offset_so_far;
+
+//        clocks_private[best_so_far].previous_offset_time = clocks_private[best_so_far].local_time;
+//        clocks_private[best_so_far].previous_offset = clocks_private[best_so_far].local_to_source_time_offset;
+//      }
+
+
+      debug(1,"Master sampling started %f ms before becoming master.", 0.000001 * (clocks_private[best_so_far].local_time - age_of_oldest_legitimate_sample));
       update_master_clock_info(clocks_private[best_so_far].clock_id,
                                (const char *)&clocks_private[best_so_far].ip,
                                clocks_private[best_so_far].local_time,
-                               clocks_private[best_so_far].local_to_source_time_offset);
+                               clocks_private[best_so_far].local_to_source_time_offset,
+                               clocks_private[best_so_far].mastership_start_time);
+      // clang-format on
+*/
+
+      clocks_private[best_so_far].previous_offset_time = 0;  // resync
     }
   }
 
index 61c5b099becba8bba2792f113bc0dbc0c65d7d23..4820a627d6a3fbe6eb84110c32e7ff95f4db2004 100644 (file)
@@ -32,16 +32,23 @@ typedef enum {
   clock_is_master
 } clock_flags;
 
+#define MAX_TIMING_SAMPLES 47
+ typedef struct {
+   uint64_t local_time, clock_time;
+ } timing_samples;
+
 // information about each clock source
 typedef struct {
   char ip[64]; // 64 is nicely aligned and bigger than INET6_ADDRSTRLEN (46)
   uint64_t clock_id;
   uint64_t local_time; // the local time when the offset was calculated
-  uint64_t origin_time;
+  uint64_t source_time;
   uint64_t local_to_source_time_offset; // add this to the local time to get source time
   uint32_t flags;
   uint16_t in_use;
   uint64_t previous_offset, previous_offset_time, last_sync_time;
+  uint64_t mastership_start_time; // set to the time of the first sample used as master
+
   // for garbage collection
   uint64_t time_of_last_use; // will be taken out of use if not used for a while and not in the
                              // timing peer group
@@ -50,6 +57,12 @@ typedef struct {
   uint64_t announce_times[4]; // we'll check qualification and currency using these
   int is_one_of_ours;         // true if it is one of our own clocks
 
+  timing_samples samples[MAX_TIMING_SAMPLES];
+  int vacant_samples; // the number of elements in the timing_samples array that are not yet used
+  int next_sample_goes_here; // point to where in the timing samples array the next entries should
+                             // go
+
+
   // these are for finding the best clock to use
   // See Figure 27 and 27 pp 89 -- 90 for the Data set comparison algorithm
 
index 7417839a9dedc0c1bb15c3b40ed913194b85acae..3c76f7c63788b72eae77cfe734b8533b5040c7a7 100644 (file)
@@ -218,18 +218,9 @@ void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clo
 
 void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
                       clock_source_private_data *clock_private_info, uint64_t reception_time) {
-  clock_private_info->flags |= (1 << clock_is_valid);
-  if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
-    debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id,
-          &clock_private_info->ip);
-    struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
 
-/*
-    uint64_t packet_clock_id = nctohl(&msg->header.clockIdentity[0]);
-    uint64_t packet_clock_id_low = nctohl(&msg->header.clockIdentity[4]);
-    packet_clock_id = packet_clock_id << 32;
-    packet_clock_id = packet_clock_id + packet_clock_id_low;
-*/
+    clock_private_info->flags |= (1 << clock_is_valid); // valid because it has at least one follow_up
+    struct ptp_follow_up_message *msg = (struct ptp_follow_up_message *)buf;
 
     uint16_t seconds_hi = nctohs(&msg->follow_up.preciseOriginTimestamp[0]);
     uint32_t seconds_low = nctohl(&msg->follow_up.preciseOriginTimestamp[2]);
@@ -240,8 +231,26 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
     preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
     preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
 
-    // preciseOriginTimestamp is called "t1" in the IEEE spec.
-    // we are using the reception time here as t2, which is a hack
+    // update our sample information
+
+    clock_private_info->samples[clock_private_info->next_sample_goes_here].local_time =
+        reception_time;
+    clock_private_info->samples[clock_private_info->next_sample_goes_here]
+        .clock_time = preciseOriginTimestamp;
+
+    if (clock_private_info->vacant_samples > 0)
+      clock_private_info->vacant_samples--;
+
+    clock_private_info->next_sample_goes_here++;
+    // if we have need to wrap.
+    if (clock_private_info->next_sample_goes_here == MAX_TIMING_SAMPLES)
+      clock_private_info->next_sample_goes_here = 0;
+
+
+//  if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
+  if (1) {
+    debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id,
+          &clock_private_info->ip);
 
     // check to see the difference between the previous preciseOriginTimestamp
 
@@ -285,7 +294,7 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
                                // 125 ms is 100 ppm.
           offset = clock_private_info->previous_offset + jitter;
         }
-      } else {
+      } else if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
         warn("Lost sync with clock %" PRIx64 " at %s. Resynchronising.",
              clock_private_info->clock_id, clock_private_info->ip);
         // leave the offset as it was coming in and take it as a sync time
@@ -305,14 +314,14 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
 
     // clock_private_info->clock_id = packet_clock_id;
     clock_private_info->local_time = reception_time;
-    clock_private_info->origin_time = preciseOriginTimestamp;
+    clock_private_info->source_time = preciseOriginTimestamp;
     clock_private_info->local_to_source_time_offset = offset;
 
     if (old_flags != clock_private_info->flags) {
       update_master();
     } else if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
       update_master_clock_info(clock_private_info->clock_id, (const char *)&clock_private_info->ip,
-                               reception_time, offset);
+                               reception_time, offset, clock_private_info->mastership_start_time);
       debug(3, "clock: %" PRIx64 ", time: %" PRIu64 ", offset: %" PRId64 ", jitter: %+f ms.",
             clock_private_info->clock_id, reception_time, offset, 0.000001 * jitter);
     }
diff --git a/nqptp.c b/nqptp.c
index a2e0960c0263f87b36460d48133713674afb3adb..f390c12a9e2d8b10d631b746110083b06428a199 100644 (file)
--- a/nqptp.c
+++ b/nqptp.c
@@ -80,7 +80,7 @@ struct shm_structure *shared_memory = NULL; // this is where public clock info i
 int epoll_fd;
 
 void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time,
-                              uint64_t local_to_master_offset) {
+                              uint64_t local_to_master_offset, uint64_t mastership_start_time) {
   if (shared_memory->master_clock_id != master_clock_id)
     debug(1, "Master clock is: %" PRIx64 ".", master_clock_id);
   int rc = pthread_mutex_lock(&shared_memory->shm_mutex);
@@ -88,7 +88,7 @@ void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t
     warn("Can't acquire mutex to update master clock!");
   if (shared_memory->master_clock_id != master_clock_id) {
     shared_memory->master_clock_id = master_clock_id;
-    shared_memory->master_clock_start_time = get_time_now();
+    shared_memory->master_clock_start_time = local_time;
   }
   if (ip != NULL)
     strncpy((char *)&shared_memory->master_clock_ip, ip,
diff --git a/nqptp.h b/nqptp.h
index c1da9ebff630a3949aa223623ccc5a5228c0243b..714f99508c3217cfc928b69c0ed8a5b291b2c481 100644 (file)
--- a/nqptp.h
+++ b/nqptp.h
@@ -32,6 +32,6 @@ extern int master_clock_index;
 extern struct shm_structure *shared_memory;
 
 void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time,
-                              uint64_t local_to_master_offset);
+                              uint64_t local_to_master_offset, uint64_t mastership_start_time);
 
 #endif
\ No newline at end of file