2 * Copyright (c) 2005 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/usr.sbin/dntpd/client.c,v 1.4 2005/04/24 09:39:27 dillon Exp $
45 client_main(struct server_info **info_ary, int count)
47 struct server_info *best_off;
48 struct server_info *best_freq;
55 * Poll clients and accumulate data
57 for (i = 0; i < count; ++i)
58 client_poll(info_ary[i]);
61 * Find the best client (or synthesize one). A different client
62 * can be chosen for frequency and offset. Note in particular
63 * that offset counters and averaging code gets reset when an
64 * offset correction is made (otherwise the averaging history will
65 * cause later corrections to overshoot).
67 * The regression used to calculate the frequency is a much
68 * longer-term entity and is NOT reset, so it is still possible
69 * for the offset correction code to make minor adjustments to
70 * the frequency if it so desires.
72 * client_check may replace the server_info pointer with a new
77 for (i = 0; i < count; ++i)
78 client_check(&info_ary[i], &best_off, &best_freq);
83 * XXX it might not be a good idea to issue an offset correction if
84 * a prior offset correction is still in progress as this will skew
85 * the offset calculation. XXX either figure out how to correct the
86 * skew or do not issue a correction.
89 offset = best_off->lin_sumoffset / best_off->lin_countoffset;
90 lin_resetalloffsets(info_ary, count);
91 freq = sysntp_correct_offset(offset);
97 * Frequency correction (throw away minor freq adjusts from the
98 * offset code if we can't do a frequency correction here).
101 sysntp_correct_freq(best_freq->lin_cache_freq + freq);
105 usleep(debug_sleep * 1000000 + random() % 100000);
107 usleep(60 * 1000000 + random() % 100000);
112 client_poll(server_info_t info)
120 fprintf(stderr, "%s: poll, ", info->target);
123 if (udp_ntptimereq(info->fd, &rtv, <v, &lbtv) < 0) {
125 fprintf(stderr, "no response\n");
130 * Figure out the offset (the difference between the reported
131 * time and our current time) for linear regression purposes.
133 offset = tv_delta_double(&rtv, <v);
146 strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tp);
147 fprintf(stderr, "%s.%03ld ",
148 buf, rtv.tv_usec / 1000);
150 lin_regress(info, <v, &lbtv, offset);
151 info = info->altinfo;
152 if (info && debug_opt) {
153 fprintf(stderr, "%*.*s: poll, ",
154 (int)strlen(info->target),
155 (int)strlen(info->target),
163 * Find the best client (or synthesize a fake info structure to return).
164 * We can find separate best clients for offset and frequency.
167 client_check(struct server_info **checkp,
168 struct server_info **best_off,
169 struct server_info **best_freq)
171 struct server_info *check = *checkp;
172 struct server_info *info;
175 * Start an alternate linear regression once our current one
176 * has passed a certain point.
178 if (check->lin_count >= LIN_RESTART / 2 && check->altinfo == NULL) {
179 info = malloc(sizeof(*info));
180 assert(info != NULL);
181 bcopy(check, info, sizeof(*info));
182 check->altinfo = info;
187 * Replace our current linear regression with the alternate once
188 * the current one has hit its limit (beyond a certain point the
189 * linear regression starts to work against us, preventing us from
190 * reacting to changing conditions).
192 * Report any significant change in the offset or ppm.
194 if (check->lin_count >= LIN_RESTART) {
195 if ((info = check->altinfo) && info->lin_count >= LIN_RESTART / 2) {
199 freq_diff = info->lin_cache_freq - check->lin_cache_freq;
200 printf("%s: Switching to alternate, Frequence difference is %6.3f ppm\n",
201 info->target, freq_diff * 1.0E+6);
210 * BEST CLIENT FOR FREQUENCY CORRECTION:
212 * 8 samples and a correllation > 0.99, or
213 * 16 samples and a correllation > 0.96
216 if ((check->lin_count >= 8 && fabs(check->lin_cache_corr) >= 0.99) ||
217 (check->lin_count >= 16 && fabs(check->lin_cache_corr) >= 0.96)
220 fabs(check->lin_cache_corr) > fabs(info->lin_cache_corr)
229 * BEST CLIENT FOR OFFSET CORRECTION:
231 * Use the standard-deviation and require at least 4 samples. An
232 * offset correction is valid if the standard deviation is less then
233 * the average offset divided by 4.
236 if (check->lin_countoffset >= 4 &&
237 check->lin_cache_stddev < fabs(check->lin_sumoffset / check->lin_countoffset / 4)) {
239 fabs(check->lin_cache_stddev) < fabs(info->lin_cache_stddev)
250 * ltv local time as of when the offset error was calculated between
251 * local time and remote time.
253 * lbtv base time as of when local time was obtained. Used to
254 * calculate the cumulative corrections made to the system's
255 * real time clock so we can de-correct the offset for the
258 * X is the time axis, in seconds.
259 * Y is the uncorrected offset, in seconds.
262 lin_regress(server_info_t info, struct timeval *ltv, struct timeval *lbtv,
266 double uncorrected_offset;
269 * De-correcting the offset:
271 * The passed offset is (our_real_time - remote_real_time). To remove
272 * corrections from our_real_time we take the difference in the basetime
273 * (new_base_time - old_base_time) and subtract that from the offset.
274 * That is, if the basetime goesup, the uncorrected offset goes down.
276 if (info->lin_count == 0) {
278 info->lin_btv = *lbtv;
280 uncorrected_offset = offset;
282 time_axis = tv_delta_double(&info->lin_tv, ltv);
283 uncorrected_offset = offset - tv_delta_double(&info->lin_btv, lbtv);
287 * We have to use the uncorrected offset for frequency calculations.
290 info->lin_sumx += time_axis;
291 info->lin_sumx2 += time_axis * time_axis;
292 info->lin_sumy += uncorrected_offset;
293 info->lin_sumy2 += uncorrected_offset * uncorrected_offset;
294 info->lin_sumxy += time_axis * uncorrected_offset;
297 * We have to use the corrected offset for offset calculations.
299 ++info->lin_countoffset;
300 info->lin_sumoffset += offset;
301 info->lin_sumoffset2 += offset * offset;
304 * Calculate various derived values. This gets us slope, y-intercept,
305 * and correllation from the linear regression.
307 if (info->lin_count > 1) {
308 info->lin_cache_slope =
309 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
310 (info->lin_count * info->lin_sumx2 - info->lin_sumx * info->lin_sumx);
312 info->lin_cache_yint =
313 (info->lin_sumy - info->lin_cache_slope * info->lin_sumx) /
316 info->lin_cache_corr =
317 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
318 sqrt((info->lin_count * info->lin_sumx2 -
319 info->lin_sumx * info->lin_sumx) *
320 (info->lin_count * info->lin_sumy2 -
321 info->lin_sumy * info->lin_sumy)
326 * Calculate more derived values. This gets us the standard-deviation
327 * of offsets. The standard deviation approximately means that 68%
328 * of the samples fall within the calculated stddev of the mean.
330 if (info->lin_countoffset > 1) {
331 info->lin_cache_stddev =
332 sqrt((info->lin_sumoffset2 -
333 ((info->lin_sumoffset * info->lin_sumoffset /
334 info->lin_countoffset))) /
335 (info->lin_countoffset - 1.0));
339 * Save the most recent offset, we might use it in the future.
340 * Save the frequency correction (we might scale the slope later so
341 * we have a separate field for the actual frequency correction in
342 * seconds per second).
344 info->lin_cache_offset = offset;
345 info->lin_cache_freq = info->lin_cache_slope;
348 fprintf(stderr, "iter=%2d time=%7.3f off=%.6f uoff=%.6f",
349 (int)info->lin_count,
350 time_axis, offset, uncorrected_offset);
351 if (info->lin_count > 1) {
352 fprintf(stderr, " slope %7.6f"
353 " yint %3.2f corr %7.6f freq_ppm %4.2f",
354 info->lin_cache_slope,
355 info->lin_cache_yint,
356 info->lin_cache_corr,
357 info->lin_cache_freq * 1000000.0);
359 if (info->lin_countoffset > 1) {
360 fprintf(stderr, " stddev %7.6f", info->lin_cache_stddev);
362 fprintf(stderr, "\n");
367 * Reset the linear regression data. The info structure will not again be
368 * a candidate for frequency or offset correction until sufficient data
369 * has been accumulated to make a decision.
372 lin_reset(server_info_t info)
381 info->lin_countoffset = 0;
382 info->lin_sumoffset = 0;
383 info->lin_sumoffset2 = 0;
385 info->lin_cache_slope = 0;
386 info->lin_cache_yint = 0;
387 info->lin_cache_corr = 0;
388 info->lin_cache_offset = 0;
389 info->lin_cache_freq = 0;
393 * Sometimes we want to clean out the offset calculations without
394 * destroying the linear regression used to figure out the frequency
395 * correction. This usually occurs whenever we issue an offset
396 * adjustment to the system, which invalidates any offset data accumulated
400 lin_resetalloffsets(struct server_info **info_ary, int count)
405 for (i = 0; i < count; ++i) {
406 for (info = info_ary[i]; info; info = info->altinfo)
407 lin_resetoffsets(info);
412 lin_resetoffsets(server_info_t info)
414 info->lin_countoffset = 0;
415 info->lin_sumoffset = 0;
416 info->lin_sumoffset2 = 0;