* broadcast directive:: Make chronyd act as an NTP broadcast server
* cmdallow directive:: Give control access to chronyc on other computers
* cmddeny directive:: Deny control access to chronyc on other computers
+* combinelimit directive:: Limit sources included in combining algorithm
* commandkey directive:: Set runtime command key
* corrtimeratio directive:: Set correction time ratio
* cmdport directive:: Set port to use for runtime commanding
There is also a @code{cmddeny all} directive with similar behaviour to the
@code{cmdallow all} directive.
@c }}}
+@c {{{ combinelimit
+@node combinelimit directive
+@subsection combinelimit
+When @code{chronyd} has multiple sources available for synchronization, it has
+to select one source as the synchronization source. The measured offsets and
+frequencies of the system clock relative to the other sources, however, can be
+combined with the selected source to improve the accuracy of the system clock.
+
+The @code{combinelimit} directive limits which sources are included in the
+combining algorithm. Their synchronization distance has to be shorter than the
+distance of the selected source multiplied by the value of the limit. Also,
+their measured frequencies have to be close to the frequency of the selected
+source.
+
+By default, the limit is 3. Setting the limit to 0 effectively disables the
+source combining algorithm and only the selected source will be used to
+control the system clock.
+
+The syntax is
+
+@example
+combinelimit <limit>
+@end example
+@c }}}
@c {{{ commandkey
@node commandkey directive
@subsection commandkey
@item S
This column indicates the state of the sources. @code{*} indicates the
-source to which @code{chronyd} is current synchronised. @code{+} indicates
-other acceptable sources. @code{?} indicates sources to which
-connectivity has been lost. @code{x} indicates a clock which @code{chronyd}
+source to which @code{chronyd} is currently synchronised. @code{+}
+indicates acceptable sources which are combined with the selected
+source. @code{-} indicates acceptable sources which are excluded by
+the combining algorithm. @code{?} indicates sources to which
+connectivity has been lost or whose packets don't pass all tests.
+@code{x} indicates a clock which @code{chronyd}
thinks is is a falseticker (i.e. its time is inconsistent with a
majority of other sources). @code{~} indicates a source whose time
-appears to have too much variability. The @code{~} condition is also
+appears to have too much variability. The @code{?} condition is also
shown at start-up, until at least 3 samples have been gathered from it.
@item Name/IP address
if (verbose) {
printf("\n");
printf(" .-- Source mode '^' = server, '=' = peer, '#' = local clock.\n");
- printf(" / .- Source state '*' = current synced, '+' = OK for sync, '?' = unreachable,\n");
- printf("| / 'x' = time may be in error, '~' = time is too variable.\n");
+ printf(" / .- Source state '*' = current synced, '+' = combined , '-' = not combined,\n");
+ printf("| / '?' = unreachable, 'x' = time may be in error, '~' = time too variable.\n");
printf("|| .- xxxx [ yyyy ] +/- zzzz\n");
printf("|| / xxxx = adjusted offset,\n");
printf("|| Log2(Polling interval) -. | yyyy = measured offset,\n");
case RPT_CANDIDATE:
tx_message->data.source_data.state = htons(RPY_SD_ST_CANDIDATE);
break;
+ case RPT_OUTLYER:
+ tx_message->data.source_data.state = htons(RPY_SD_ST_OUTLYER);
+ break;
}
switch (report.mode) {
case RPT_NTP_CLIENT:
static void parse_cmdallow(char *);
static void parse_cmddeny(char *);
static void parse_cmdport(char *);
+static void parse_combinelimit(char *);
static void parse_commandkey(char *);
static void parse_corrtimeratio(char *);
static void parse_deny(char *);
static double reselect_distance = 1e-4;
static double stratum_weight = 1.0;
+static double combine_limit = 3.0;
static int cmd_port = DEFAULT_CANDM_PORT;
parse_cmddeny(p);
} else if (!strcasecmp(command, "cmdport")) {
parse_cmdport(p);
+ } else if (!strcasecmp(command, "combinelimit")) {
+ parse_combinelimit(p);
} else if (!strcasecmp(command, "commandkey")) {
parse_commandkey(p);
} else if (!strcasecmp(command, "corrtimeratio")) {
/* ================================================== */
+static void
+parse_combinelimit(char *line)
+{
+ check_number_of_args(line, 1);
+ if (sscanf(line, "%lf", &combine_limit) != 1) {
+ command_parse_error();
+ }
+}
+
+/* ================================================== */
+
static void
parse_driftfile(char *line)
{
/* ================================================== */
+double
+CNF_GetCombineLimit(void)
+{
+ return combine_limit;
+}
+
+/* ================================================== */
+
int
CNF_GetManualEnabled(void)
{
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
+extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum);
int stratum;
int poll;
enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode;
- enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE} state;
+ enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLYER} state;
enum {RPT_NORMAL, RPT_PREFER, RPT_NOSELECT} sel_option;
int reachability;
/* Reachability register */
int reachability;
+ /* Updates left before resetting outlyer status */
+ int outlyer;
+
/* Flag indicating the status of the source */
SRC_Status status;
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
+/* Number of updates needed to reset the outlyer status */
+#define OUTLYER_PENALTY 32
+
static double reselect_distance;
static double stratum_weight;
+static double combine_limit;
/* ================================================== */
/* Forward prototype */
selected_source_index = INVALID_SOURCE;
reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight();
+ combine_limit = CNF_GetCombineLimit();
initialised = 1;
LCL_AddParameterChangeHandler(slew_sources, NULL);
result->ip_addr = addr;
result->selectable = 0;
result->reachability = 0;
+ result->outlyer = 0;
result->status = SRC_BAD_STATS;
result->type = type;
result->sel_score = 1.0;
&src_frequency, &src_skew,
&src_root_delay, &src_root_dispersion);
+ /* Don't include this source if its distance is longer than the distance of
+ the selected source multiplied by the limit, their estimated frequencies
+ are not close, or it was recently marked as outlyer */
+
+ if (index != selected_source_index &&
+ (sources[index]->sel_info.root_distance > combine_limit *
+ (reselect_distance + sources[selected_source_index]->sel_info.root_distance) ||
+ fabs(*frequency - src_frequency) >
+ combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) {
+ sources[index]->outlyer = OUTLYER_PENALTY;
+ }
+
+ if (sources[index]->outlyer) {
+ sources[index]->outlyer--;
+ continue;
+ }
+
UTI_DiffTimevalsToDouble(&elapsed, ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
weight = 1.0 / sources[index]->sel_info.root_distance;
/* Reset score for non-selectable sources */
if (sources[i]->status != SRC_SELECTABLE) {
sources[i]->sel_score = 1.0;
+ sources[i]->outlyer = OUTLYER_PENALTY;
continue;
}
/* New source has been selected, reset all scores */
for (i=0; i < n_sources; i++) {
sources[i]->sel_score = 1.0;
+ sources[i]->outlyer = 0;
}
}
report->state = RPT_FALSETICKER;
break;
case SRC_SELECTABLE:
- report->state = RPT_CANDIDATE;
+ report->state = src->outlyer ? RPT_OUTLYER : RPT_CANDIDATE;
break;
default:
assert(0);