Compare commits

...

11 Commits

Author SHA1 Message Date
Miroslav Lichvar
db286ca6ea doc: update NEWS 2016-11-21 12:03:45 +01:00
Miroslav Lichvar
85fbfd9b15 sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-21 12:03:45 +01:00
Miroslav Lichvar
b819c7fe55 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 0a848e2528.
2016-11-21 12:03:27 +01:00
Miroslav Lichvar
2b5c86b9a3 refclock: fix check for old samples
The fix in commit 0a848e2528 was
incorrect.
2016-11-21 12:03:15 +01:00
Miroslav Lichvar
0a848e2528 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
b443ec5ea5 test: add smooth unit test 2016-11-21 12:02:51 +01:00
Miroslav Lichvar
37d1467368 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
1d9d19d76b client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
9603f0552a client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
12befc2afd ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
78f20f7b3e conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-11-21 12:02:51 +01:00
8 changed files with 139 additions and 28 deletions

10
NEWS
View File

@@ -1,3 +1,13 @@
New in version 2.4.1
====================
Bug fixes
---------
* Fix processing of kernel timestamps on non-Linux systems
* Fix crash with smoothtime directive
* Fix validation of refclock sample times
* Fix parsing of refclock directive
New in version 2.4
==================

View File

@@ -125,6 +125,7 @@ read_line(void)
return( line );
#else
printf("%s", prompt);
fflush(stdout);
#endif
}
if (fgets(line, sizeof(line), stdin)) {
@@ -2007,7 +2008,7 @@ process_cmd_sources(char *line)
print_report("%c%c %-27s %2d %2d %3o %I %+S[%+S] +/- %S\n",
mode_ch, state_ch, name,
ntohs(reply.data.source_data.stratum),
ntohs(reply.data.source_data.poll),
(int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability),
(unsigned long)ntohl(reply.data.source_data.since_sample),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),

7
conf.c
View File

@@ -696,9 +696,9 @@ parse_refclock(char *line)
line = CPS_SplitWord(line);
param = Strdup(p);
while (*line) {
cmd = line;
for (cmd = line; *cmd; line += n, cmd = line) {
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
@@ -756,10 +756,9 @@ parse_refclock(char *line)
other_parse_error("Invalid refclock option");
return;
}
line += n;
}
if (*line) {
if (*cmd) {
command_parse_error();
return;
}

View File

@@ -556,7 +556,7 @@ read_from_socket(void *anything)
#endif
#ifdef SO_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));

View File

@@ -92,7 +92,7 @@ static ARR_Instance refclocks;
static LOG_FileID logfileid;
static int valid_sample_time(RCL_Instance instance, struct timeval *tv);
static int valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked);
static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
@@ -106,6 +106,7 @@ static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
@@ -372,7 +373,7 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time))
!valid_sample_time(instance, sample_time, &cooked_time))
return 0;
switch (leap) {
@@ -412,7 +413,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time))
!valid_sample_time(instance, pulse_time, &cooked_time))
return 0;
rate = instance->pps_rate;
@@ -503,18 +504,25 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
}
static int
valid_sample_time(RCL_Instance instance, struct timeval *tv)
valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked)
{
struct timeval raw_time;
double diff;
struct timeval raw_time, last_sample_time;
double diff, last_offset, last_dispersion;
LCL_ReadRawTime(&raw_time);
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f tv=%s",
UTI_RefidToString(instance->ref_id), diff, UTI_TimevalToString(tv));
UTI_DiffTimevalsToDouble(&diff, &raw_time, raw);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimevals(&last_sample_time, cooked) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
UTI_RefidToString(instance->ref_id), diff,
UTI_TimevalToString(raw), UTI_TimevalToString(cooked));
return 0;
}
return 1;
}
@@ -719,6 +727,12 @@ filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time,
return 1;
}
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array;
static int

View File

@@ -137,7 +137,7 @@ get_smoothing(struct timeval *now, double *poffset, double *pfreq,
static void
update_stages(void)
{
double s1, s2, s, l1, l2, l3, lc, f, f2;
double s1, s2, s, l1, l2, l3, lc, f, f2, l1t[2], l3t[2], err[2];
int i, dir;
/* Prepare the three stages so that the integral of the frequency offset
@@ -146,22 +146,41 @@ update_stages(void)
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
l1 = l2 = l3 = 0.0;
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. If length of the 1st stage comes out negative, switch
its direction. */
for (dir = -1; dir <= 1; dir += 2) {
frequency limit. The direction of the 1st stage is selected so that
the lengths will not be negative. With extremely small offsets both
directions may give a negative length due to numerical errors, so select
the one which gives a smaller error. */
for (i = 0, dir = -1; i <= 1; i++, dir += 2) {
err[i] = 0.0;
s = dir * s1 + s2;
if (s >= 0.0) {
l3 = sqrt(s);
l1 = l3 - dir * smooth_freq / max_wander;
if (l1 >= 0.0)
break;
if (s < 0.0) {
err[i] += -s;
s = 0.0;
}
l3t[i] = sqrt(s);
l1t[i] = l3t[i] - dir * smooth_freq / max_wander;
if (l1t[i] < 0.0) {
err[i] += l1t[i] * l1t[i];
l1t[i] = 0.0;
}
}
assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0);
if (err[0] < err[1]) {
l1 = l1t[0];
l3 = l3t[0];
dir = -1;
} else {
l1 = l1t[1];
l3 = l3t[1];
dir = 1;
}
l2 = 0.0;
/* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */
f = dir * smooth_freq + l1 * max_wander - max_freq;

View File

@@ -74,6 +74,7 @@ typedef enum {
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
SRC_STALE, /* Has older samples than others */
SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */
SRC_UNTRUSTED, /* Overlaps trusted sources */
SRC_FALSETICKER, /* Doesn't agree with others */
SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */
SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */
@@ -890,6 +891,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0;
} else if (sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) {
sources[i]->status = SRC_UNTRUSTED;
} else {
sources[i]->status = SRC_FALSETICKER;
}
@@ -1318,6 +1322,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_JITTERY:
report->state = RPT_JITTERY;
break;
case SRC_UNTRUSTED:
case SRC_WAITS_SOURCES:
case SRC_NONPREFERRED:
case SRC_WAITS_UPDATE:

63
test/unit/smooth.c Normal file
View File

@@ -0,0 +1,63 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
*/
#include <smooth.c>
#include "test.h"
void
test_unit(void)
{
int i, j;
struct timeval tv;
double offset, freq, wander;
char conf[] = "smoothtime 300 0.01";
CNF_Initialise(0);
CNF_ParseLine(NULL, 1, conf);
LCL_Initialise();
SMT_Initialise();
locked = 0;
for (i = 0; i < 500; i++) {
tv.tv_sec = tv.tv_usec = 0;
SMT_Reset(&tv);
DEBUG_LOG(0, "iteration %d", i);
offset = (random() % 1000000 - 500000) / 1.0e6;
freq = (random() % 1000000 - 500000) / 1.0e9;
update_smoothing(&tv, offset, freq);
for (j = 0; j < 10000; j++) {
update_smoothing(&tv, 0.0, 0.0);
UTI_AddDoubleToTimeval(&tv, 16.0, &tv);
get_smoothing(&tv, &offset, &freq, &wander);
}
TEST_CHECK(fabs(offset) < 1e-12);
TEST_CHECK(fabs(freq) < 1e-12);
TEST_CHECK(fabs(wander) < 1e-12);
}
SMT_Finalise();
LCL_Finalise();
CNF_Finalise();
}