#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>
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 {
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
}
}
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
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
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]);
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
// 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
// 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);
}
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);
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,
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