diff --git a/candm.h b/candm.h index b1a191d..956db9e 100644 --- a/candm.h +++ b/candm.h @@ -89,7 +89,8 @@ #define REQ_RESELECT 48 #define REQ_RESELECTDISTANCE 49 #define REQ_MODIFY_MAKESTEP 50 -#define N_REQUEST_TYPES 51 +#define REQ_SMOOTHING 51 +#define N_REQUEST_TYPES 52 /* Special utoken value used to log on with first exchange being the password. (This time value has long since gone by) */ @@ -422,7 +423,8 @@ typedef struct { #define RPY_CLIENT_ACCESSES_BY_INDEX 10 #define RPY_MANUAL_LIST 11 #define RPY_ACTIVITY 12 -#define N_REPLY_TYPES 13 +#define RPY_SMOOTHING 13 +#define N_REPLY_TYPES 14 /* Status codes */ #define STT_SUCCESS 0 @@ -577,6 +579,19 @@ typedef struct { int32_t EOR; } RPY_Activity; +#define RPY_SMT_FLAG_ACTIVE 0x1 +#define RPY_SMT_FLAG_LEAPONLY 0x2 + +typedef struct { + uint32_t flags; + Float offset; + Float freq_ppm; + Float wander_ppm; + Float last_update_ago; + Float remaining_time; + int32_t EOR; +} RPY_Smoothing; + typedef struct { uint8_t version; uint8_t pkt_type; @@ -603,6 +618,7 @@ typedef struct { RPY_ClientAccessesByIndex client_accesses_by_index; RPY_ManualList manual_list; RPY_Activity activity; + RPY_Smoothing smoothing; } data; /* Reply specific parameters */ /* authentication of the packet, there is no hole after the actual data diff --git a/cmdmon.c b/cmdmon.c index c569a80..2d71b8d 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -37,6 +37,7 @@ #include "keys.h" #include "ntp_sources.h" #include "ntp_core.h" +#include "smooth.h" #include "sources.h" #include "sourcestats.h" #include "reference.h" @@ -162,6 +163,7 @@ static const char permissions[] = { PERMIT_AUTH, /* RESELECT */ PERMIT_AUTH, /* RESELECTDISTANCE */ PERMIT_AUTH, /* MODIFY_MAKESTEP */ + PERMIT_OPEN, /* SMOOTHING */ }; /* ================================================== */ @@ -1223,6 +1225,31 @@ handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message) /* ================================================== */ +static void +handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message) +{ + RPT_SmoothingReport report; + struct timeval now; + + SCH_GetLastEventTime(&now, NULL, NULL); + + if (!SMT_GetSmoothingReport(&report, &now)) { + tx_message->status = htons(STT_NOTENABLED); + return; + } + + tx_message->reply = htons(RPY_SMOOTHING); + tx_message->data.smoothing.flags = htonl((report.active ? RPY_SMT_FLAG_ACTIVE : 0) | + (report.leap_only ? RPY_SMT_FLAG_LEAPONLY : 0)); + tx_message->data.smoothing.offset = UTI_FloatHostToNetwork(report.offset); + tx_message->data.smoothing.freq_ppm = UTI_FloatHostToNetwork(report.freq_ppm); + tx_message->data.smoothing.wander_ppm = UTI_FloatHostToNetwork(report.wander_ppm); + tx_message->data.smoothing.last_update_ago = UTI_FloatHostToNetwork(report.last_update_ago); + tx_message->data.smoothing.remaining_time = UTI_FloatHostToNetwork(report.remaining_time); +} + +/* ================================================== */ + static void handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message) { @@ -1889,6 +1916,10 @@ read_from_cmd_socket(void *anything) handle_tracking(&rx_message, &tx_message); break; + case REQ_SMOOTHING: + handle_smoothing(&rx_message, &tx_message); + break; + case REQ_SOURCESTATS: handle_sourcestats(&rx_message, &tx_message); break; diff --git a/pktlength.c b/pktlength.c index 3b12517..72c3171 100644 --- a/pktlength.c +++ b/pktlength.c @@ -146,6 +146,8 @@ command_unpadded_length(CMD_Request *r) return offsetof(CMD_Request, data.modify_minstratum.EOR); case REQ_MODIFY_POLLTARGET: return offsetof(CMD_Request, data.modify_polltarget.EOR); + case REQ_SMOOTHING: + return offsetof(CMD_Request, data.null.EOR); default: /* If we fall through the switch, it most likely means we've forgotten to implement a new case */ assert(0); @@ -296,6 +298,8 @@ PKL_CommandPaddingLength(CMD_Request *r) return PADDING_LENGTH(data.modify_minstratum.EOR, data.null.EOR); case REQ_MODIFY_POLLTARGET: return PADDING_LENGTH(data.modify_polltarget.EOR, data.null.EOR); + case REQ_SMOOTHING: + return PADDING_LENGTH(data.null.EOR, data.smoothing.EOR); default: /* If we fall through the switch, it most likely means we've forgotten to implement a new case */ assert(0); @@ -359,7 +363,8 @@ PKL_ReplyLength(CMD_Reply *r) } case RPY_ACTIVITY: return offsetof(CMD_Reply, data.activity.EOR); - + case RPY_SMOOTHING: + return offsetof(CMD_Reply, data.smoothing.EOR); default: assert(0); } diff --git a/reports.h b/reports.h index 776276a..1860e78 100644 --- a/reports.h +++ b/reports.h @@ -112,4 +112,14 @@ typedef struct { int unresolved; } RPT_ActivityReport; +typedef struct { + int active; + int leap_only; + double offset; + double freq_ppm; + double wander_ppm; + double last_update_ago; + double remaining_time; +} RPT_SmoothingReport; + #endif /* GOT_REPORTS_H */ diff --git a/smooth.c b/smooth.c index 519b2ba..bfd7529 100644 --- a/smooth.c +++ b/smooth.c @@ -97,15 +97,17 @@ static struct timeval last_update; static void -get_offset_freq(struct timeval *now, double *offset, double *freq) +get_smoothing(struct timeval *now, double *poffset, double *pfreq, + double *pwander) { - double elapsed, length; + double elapsed, length, offset, freq, wander; int i; UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); - *offset = smooth_offset; - *freq = smooth_freq; + offset = smooth_offset; + freq = smooth_freq; + wander = 0.0; for (i = 0; i < NUM_STAGES; i++) { if (elapsed <= 0.0) @@ -115,13 +117,21 @@ get_offset_freq(struct timeval *now, double *offset, double *freq) if (length >= elapsed) length = elapsed; - *offset -= length * (2.0 * *freq + stages[i].wander * length) / 2.0; - *freq += stages[i].wander * length; + wander = stages[i].wander; + offset -= length * (2.0 * freq + wander * length) / 2.0; + freq += wander * length; elapsed -= length; } - if (elapsed > 0.0) - *offset -= elapsed * *freq; + if (elapsed > 0.0) { + wander = 0.0; + offset -= elapsed * freq; + } + + *poffset = offset; + *pfreq = freq; + if (pwander) + *pwander = wander; } static void @@ -193,11 +203,12 @@ update_smoothing(struct timeval *now, double offset, double freq) LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ? " (leap seconds only)" : ""); locked = 0; + last_update = *now; } return; } - get_offset_freq(now, &smooth_offset, &smooth_freq); + get_smoothing(now, &smooth_offset, &smooth_freq, NULL); smooth_offset += offset; smooth_freq = (smooth_freq - freq) / (1.0 - freq); last_update = *now; @@ -258,7 +269,7 @@ SMT_GetOffset(struct timeval *now) if (!enabled) return 0.0; - get_offset_freq(now, &offset, &freq); + get_smoothing(now, &offset, &freq, NULL); return offset; } @@ -289,3 +300,35 @@ SMT_Leap(struct timeval *now, int leap) update_smoothing(now, leap, 0.0); } + +int +SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now) +{ + double length, elapsed; + int i; + + if (!enabled) + return 0; + + report->active = !locked; + report->leap_only = leap_only_mode; + + get_smoothing(now, &report->offset, &report->freq_ppm, &report->wander_ppm); + + /* Convert to ppm and negate (positive values mean faster/speeding up) */ + report->freq_ppm *= -1.0e6; + report->wander_ppm *= -1.0e6; + + UTI_DiffTimevalsToDouble(&elapsed, now, &last_update); + if (!locked && elapsed >= 0.0) { + for (i = 0, length = 0.0; i < NUM_STAGES; i++) + length += stages[i].length; + report->last_update_ago = elapsed; + report->remaining_time = elapsed < length ? length - elapsed : 0.0; + } else { + report->last_update_ago = 0.0; + report->remaining_time = 0.0; + } + + return 1; +} diff --git a/smooth.h b/smooth.h index 4cb72d7..1cf6814 100644 --- a/smooth.h +++ b/smooth.h @@ -27,6 +27,8 @@ #ifndef GOT_SMOOTH_H #define GOT_SMOOTH_H +#include "reports.h" + extern void SMT_Initialise(void); extern void SMT_Finalise(void); @@ -39,4 +41,6 @@ extern void SMT_Reset(struct timeval *now); extern void SMT_Leap(struct timeval *now, int leap); +extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now); + #endif