]> git.ipfire.org Git - thirdparty/nqptp.git/commitdiff
Reinstate sample lookback when a clock has just become a bus master. Check previous...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Thu, 17 Jun 2021 17:58:07 +0000 (18:58 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Thu, 17 Jun 2021 17:58:07 +0000 (18:58 +0100)
nqptp-clock-sources.c
nqptp-clock-sources.h
nqptp-message-handlers.c
nqptp.c
nqptp.h

index d2a168f307122c8d807ed26347c7d1b7f6b6428c..6a936d97f4aa5ab9a86272152bec8e73e57e80fe 100644 (file)
@@ -19,8 +19,8 @@
 
 #include "nqptp-clock-sources.h"
 #include "debug.h"
-#include "general-utilities.h"
 #include "nqptp-ptp-definitions.h"
+#include "general-utilities.h"
 #include <arpa/inet.h>
 #include <ifaddrs.h>
 #include <string.h>
@@ -69,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 {
@@ -164,8 +165,7 @@ void update_master() {
 
   int best_so_far = -1;
   int timing_peer_count = 0;
-  uint32_t acceptance_mask =
-      (1 << clock_is_qualified) | (1 << clock_is_a_timing_peer) | (1 << clock_is_valid);
+  uint32_t acceptance_mask = (1 << clock_is_qualified) | (1 << clock_is_a_timing_peer) | (1 << clock_is_valid);
   for (i = 0; i < MAX_CLOCKS; i++) {
     if ((clocks_private[i].flags & acceptance_mask) == acceptance_mask) {
       // found a possible clock candidate
@@ -204,18 +204,102 @@ 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);
-    if (old_master != best_so_far) {
-      // if it's a new master, drop all previous estimates and limits
-      clocks_private[best_so_far].previous_offset_time = 0; // resync
+    } 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 plus one sample interval in the future.
+
+      // This sample 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 + 1150000000;
+
+      // 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(3,"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(3,"new best offset");
+                best_offset_so_far = possible_offset;
+                changes_made++;
+              }
+            } else {
+              debug(3,"sample too far into the future");
+            }
+          } else {
+            debug(3,"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(2,"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(2,"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].mastership_start_time);
+      // clang-format on
+
+      clocks_private[best_so_far].previous_offset_time = 0;  // resync
     }
   }
 
index 3c69892024c6218d8468b977b985e1177677538c..b27e6e4519b2a8533dfb388597d4fd1c3fb45dd2 100644 (file)
@@ -32,6 +32,12 @@ typedef enum {
   clock_is_master
 } clock_flags;
 
+// 8 samples per seconds
+#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)
@@ -42,6 +48,7 @@ typedef struct {
   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
@@ -51,6 +58,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 0056d140c903a4989f310e82a2147ec106444861..3c76f7c63788b72eae77cfe734b8533b5040c7a7 100644 (file)
@@ -219,19 +219,35 @@ 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); // 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]);
-  uint32_t nanoseconds = nctohl(&msg->follow_up.preciseOriginTimestamp[6]);
-  uint64_t preciseOriginTimestamp = seconds_hi;
-  preciseOriginTimestamp = preciseOriginTimestamp << 32;
-  preciseOriginTimestamp = preciseOriginTimestamp + seconds_low;
-  preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
-  preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
-
-  //  if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
+    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]);
+    uint32_t nanoseconds = nctohl(&msg->follow_up.preciseOriginTimestamp[6]);
+    uint64_t preciseOriginTimestamp = seconds_hi;
+    preciseOriginTimestamp = preciseOriginTimestamp << 32;
+    preciseOriginTimestamp = preciseOriginTimestamp + seconds_low;
+    preciseOriginTimestamp = preciseOriginTimestamp * 1000000000L;
+    preciseOriginTimestamp = preciseOriginTimestamp + nanoseconds;
+
+    // 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);
@@ -292,7 +308,7 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
 
     uint32_t old_flags = clock_private_info->flags;
 
-    // if ((clock_private_info->flags & (1 << clock_is_valid)) == 0) {
+    //if ((clock_private_info->flags & (1 << clock_is_valid)) == 0) {
     //  debug(1, "clock %" PRIx64 " is now valid at: %s", packet_clock_id, clock_private_info->ip);
     //}
 
@@ -303,11 +319,9 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
 
     if (old_flags != clock_private_info->flags) {
       update_master();
-    }
-
-    if ((clock_private_info->flags & (1 << clock_is_master)) != 0) {
+    } 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 5974dcb6b7972249504de55cc4ec478203447c2c..5dc66471b7c5d3a44bfdba2364b46226b4800d7f 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 = local_time;
+    shared_memory->master_clock_start_time = mastership_start_time;
   }
   if (ip != NULL)
     strncpy((char *)&shared_memory->master_clock_ip, ip,
@@ -141,12 +141,12 @@ int main(int argc, char **argv) {
 #ifdef CONFIG_USE_GIT_VERSION_STRING
         if (git_version_string[0] != '\0')
           fprintf(stdout, "Version: %s. Shared Memory Interface Version: %u.\n", git_version_string,
-                  NQPTP_SHM_STRUCTURES_VERSION);
+                NQPTP_SHM_STRUCTURES_VERSION);
         else
 #endif
 
-          fprintf(stdout, "Version: %s. Shared Memory Interface Version: %u.\n", VERSION,
-                  NQPTP_SHM_STRUCTURES_VERSION);
+        fprintf(stdout, "Version: %s. Shared Memory Interface Version: %u.\n", VERSION,
+                NQPTP_SHM_STRUCTURES_VERSION);
         exit(EXIT_SUCCESS);
       } else if (strcmp(argv[i] + 1, "vvv") == 0) {
         debug_level = 3;
diff --git a/nqptp.h b/nqptp.h
index f9288bab3d772a4034e42da41619f62555409cc6..8d7d18a9dc7535095b75028b2ecd26db090f7039 100644 (file)
--- a/nqptp.h
+++ b/nqptp.h
@@ -32,6 +32,7 @@ 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
+