From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 17 Jun 2021 17:58:07 +0000 (+0100) Subject: Reinstate sample lookback when a clock has just become a bus master. Check previous... X-Git-Tag: 1.2~121 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=83ff626d3fed911ee723443f973419e5032df4a9;p=thirdparty%2Fnqptp.git Reinstate sample lookback when a clock has just become a bus master. Check previous samples to possibly get a better fix on the clock offset. --- diff --git a/nqptp-clock-sources.c b/nqptp-clock-sources.c index d2a168f..6a936d9 100644 --- a/nqptp-clock-sources.c +++ b/nqptp-clock-sources.c @@ -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 #include #include @@ -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 } } diff --git a/nqptp-clock-sources.h b/nqptp-clock-sources.h index 3c69892..b27e6e4 100644 --- a/nqptp-clock-sources.h +++ b/nqptp-clock-sources.h @@ -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 diff --git a/nqptp-message-handlers.c b/nqptp-message-handlers.c index 0056d14..3c76f7c 100644 --- a/nqptp-message-handlers.c +++ b/nqptp-message-handlers.c @@ -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 5974dcb..5dc6647 100644 --- 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 f9288ba..8d7d18a 100644 --- 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 +