#include "array.h"
#include "clientlog.h"
#include "conf.h"
+#include "local.h"
#include "memory.h"
#include "ntp.h"
#include "reports.h"
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
- uint32_t flags;
+ uint16_t flags;
+ uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
uint32_t max_size;
uint32_t cached_index;
uint64_t cached_rx_ts;
+ uint16_t slew_epoch;
+ double slew_offset;
} NtpTimestampMap;
static NtpTimestampMap ntp_ts_map;
/* ================================================== */
static int expand_hashtable(void);
+static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+ double doffset, LCL_ChangeType change_type, void *anything);
/* ================================================== */
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
ntp_ts_map.cached_index = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
+ ntp_ts_map.slew_epoch = 0;
+ ntp_ts_map.slew_offset = 0.0;
+
+ LCL_AddParameterChangeHandler(handle_slew, NULL);
}
/* ================================================== */
ARR_DestroyInstance(records);
if (ntp_ts_map.timestamps)
ARR_DestroyInstance(ntp_ts_map.timestamps);
+
+ LCL_RemoveParameterChangeHandler(handle_slew, NULL);
}
/* ================================================== */
tss = get_ntp_tss(index);
tss->rx_ts = rx;
tss->flags = 0;
+ tss->slew_epoch = ntp_ts_map.slew_epoch;
set_ntp_tx_offset(tss, rx_ts, tx_ts);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
/* ================================================== */
+static void
+handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
+ double doffset, LCL_ChangeType change_type, void *anything)
+{
+ /* Drop all timestamps on unknown step */
+ if (change_type == LCL_ChangeUnknownStep) {
+ ntp_ts_map.size = 0;
+ ntp_ts_map.cached_rx_ts = 0ULL;
+ }
+
+ ntp_ts_map.slew_epoch++;
+ ntp_ts_map.slew_offset = doffset;
+}
+
+/* ================================================== */
+
+void
+CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
+{
+ uint32_t index;
+
+ if (!ntp_ts_map.timestamps)
+ return;
+
+ if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
+ return;
+
+ /* If the RX timestamp was captured before the last correction of the clock,
+ remove the adjustment from the TX timestamp */
+ if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
+ UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
+}
+
+/* ================================================== */
+
void
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
/* Functions to save and retrieve timestamps for server interleaved mode */
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
- NTP_Local_Timestamp local_tx;
+ NTP_Local_Timestamp old_tx, new_tx;
NTP_int64 *local_ntp_rx;
NTP_PacketInfo info;
UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
local_ntp_rx = &message->receive_ts;
+ new_tx = *tx_ts;
- if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &local_tx.ts))
+ if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
return;
- update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
+ /* Undo a clock adjustment between the RX and TX timestamps to minimise error
+ in the delay measured by the client */
+ CLG_UndoNtpTxTimestampSlew(local_ntp_rx, &new_tx.ts);
+
+ update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
- CLG_UpdateNtpTxTimestamp(local_ntp_rx, &local_tx.ts);
+ CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
}
/* ================================================== */
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
+ LCL_Initialise();
CLG_Initialise();
TEST_CHECK(ARR_GetSize(records) == 16);
ntp_ts_map.first = i % ntp_ts_map.max_size;
ntp_ts_map.size = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
+ ntp_ts_map.slew_epoch = i * 400;
for (j = 0; j < 500; j++) {
do {
TEST_CHECK(ntp_ts_map.size == ntp_ts_map.max_size);
TEST_CHECK(ntp_ts_map.first == (i + j + ntp_ts_map.size + 1) % ntp_ts_map.max_size);
}
+ TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1);
+ TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
for (k = random() % 4; k > 0; k--) {
- int64_to_ntp64(get_ntp_tss(random() % ntp_ts_map.size)->rx_ts, &ntp_ts);
+ index2 = random() % ntp_ts_map.size;
+ int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
if (random() % 2)
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
+
+ ts2 = ts;
+ CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
+ if ((get_ntp_tss(index2)->slew_epoch + 1) % (1U << 16) != ntp_ts_map.slew_epoch) {
+ TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+ } else {
+ TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - ntp_ts_map.slew_offset) <
+ 1.0e-9);
+ }
+
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+ if (random() % 2) {
+ uint16_t prev_epoch = ntp_ts_map.slew_epoch;
+ handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e-5, 1.0e-5),
+ LCL_ChangeAdjust, NULL);
+ TEST_CHECK((prev_epoch + 1) % (1U << 16) == ntp_ts_map.slew_epoch);
+ }
+
if (ntp_ts_map.size > 1) {
index = random() % (ntp_ts_map.size - 1);
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
}
CLG_GetNtpTxTimestamp(&ntp_ts, &ts);
}
}
+
+ if (random() % 2) {
+ handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e9, 1.0e9),
+ LCL_ChangeUnknownStep, NULL);
+ TEST_CHECK(ntp_ts_map.size == 0);
+ TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+ CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ }
}
CLG_Finalise();
+ LCL_Finalise();
CNF_Finalise();
}
#else