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.3 2005/04/24 06:24:42 dillon Exp $
45 client_main(struct server_info **info_ary, int count)
47 struct server_info *best;
54 * Poll clients and accumulate data
56 for (i = 0; i < count; ++i)
57 client_poll(info_ary[i]);
60 * Find the best client (or synthesize one). If we find one
61 * then program the frequency correction and offset.
63 * client_check may replace the server_info pointer with a new
67 for (i = 0; i < count; ++i)
68 best = client_check(&info_ary[i], best);
70 offset = best->lin_sumoffset / best->lin_count;
71 freq = sysntp_correct_offset(offset);
72 sysntp_correct_freq(best->lin_cache_freq + freq);
75 usleep(debug_sleep * 1000000 + random() % 100000);
77 usleep(60 * 1000000 + random() % 100000);
82 client_poll(server_info_t info)
90 fprintf(stderr, "%s: poll, ", info->target);
93 if (udp_ntptimereq(info->fd, &rtv, <v, &lbtv) < 0) {
95 fprintf(stderr, "no response\n");
100 * Figure out the offset (the difference between the reported
101 * time and our current time) for linear regression purposes.
103 offset = tv_delta_micro(&rtv, <v) / 1000000.0;
116 strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tp);
117 fprintf(stderr, "%s.%03ld ",
118 buf, rtv.tv_usec / 1000);
120 lin_regress(info, <v, &lbtv, offset);
121 info = info->altinfo;
122 if (info && debug_opt) {
123 fprintf(stderr, "%*.*s: poll, ",
124 (int)strlen(info->target),
125 (int)strlen(info->target),
133 * Find the best client (or synthesize a fake info structure to return)
136 client_check(struct server_info **checkp, struct server_info *best)
138 struct server_info *check = *checkp;
139 struct server_info *info;
142 * Start an alternate linear regression once our current one
143 * has passed a certain point.
145 if (check->lin_count >= LIN_RESTART / 2 && check->altinfo == NULL) {
146 info = malloc(sizeof(*info));
147 assert(info != NULL);
148 bcopy(check, info, sizeof(*info));
149 check->altinfo = info;
154 * Replace our current linear regression with the alternate once
155 * the current one has hit its limit (beyond a certain point the
156 * linear regression starts to work against us, preventing us from
157 * reacting to changing conditions).
159 * Report any significant change in the offset or ppm.
161 if (check->lin_count >= LIN_RESTART) {
162 if ((info = check->altinfo) && info->lin_count >= LIN_RESTART / 2) {
166 freq_diff = info->lin_cache_freq - check->lin_cache_freq;
167 printf("%s: Switching to alternate, Frequence difference is %6.3f ppm\n",
168 info->target, freq_diff * 1.0E+6);
177 * Required for selection:
179 * 8 samples and a correllation > 0.99, or
180 * 16 samples and a correllation > 0.96
182 if ((check->lin_count >= 8 && fabs(check->lin_cache_corr) >= 0.99) ||
183 (check->lin_count >= 16 && fabs(check->lin_cache_corr) >= 0.96)
186 fabs(check->lin_cache_corr) > fabs(best->lin_cache_corr)) {
197 * ltv local time as of when the offset error was calculated between
198 * local time and remote time.
200 * lbtv base time as of when local time was obtained. Used to
201 * calculate the cumulative corrections made to the system's
202 * real time clock so we can de-correct the offset for the
205 * X is the time axis, in seconds.
206 * Y is the uncorrected offset, in seconds.
209 lin_regress(server_info_t info, struct timeval *ltv, struct timeval *lbtv,
213 double uncorrected_offset;
215 if (info->lin_count == 0) {
217 info->lin_btv = *lbtv;
219 uncorrected_offset = offset;
221 time_axis = tv_delta_micro(&info->lin_tv, ltv) / 1000000.0;
222 uncorrected_offset = offset -
223 tv_delta_micro(&info->lin_btv, lbtv) / 1000000.0;
226 info->lin_sumx += time_axis;
227 info->lin_sumx2 += time_axis * time_axis;
228 info->lin_sumy += uncorrected_offset;
229 info->lin_sumy2 += uncorrected_offset * uncorrected_offset;
230 info->lin_sumxy += time_axis * uncorrected_offset;
231 info->lin_sumoffset += uncorrected_offset;
234 * Calculate various derived values (from statistics).
237 if (info->lin_count > 1) {
238 info->lin_cache_slope =
239 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
240 (info->lin_count * info->lin_sumx2 - info->lin_sumx * info->lin_sumx);
242 info->lin_cache_yint =
243 (info->lin_sumy - info->lin_cache_slope * info->lin_sumx) /
246 info->lin_cache_corr =
247 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
248 sqrt((info->lin_count * info->lin_sumx2 -
249 info->lin_sumx * info->lin_sumx) *
250 (info->lin_count * info->lin_sumy2 -
251 info->lin_sumy * info->lin_sumy)
256 * Calculate the offset and frequency correction
258 info->lin_cache_offset = offset;
259 info->lin_cache_freq = info->lin_cache_slope;
262 fprintf(stderr, "iter=%2d time=%7.3f off=%.6f uoff=%.6f",
263 (int)info->lin_count,
264 time_axis, offset, uncorrected_offset);
265 if (info->lin_count > 1) {
266 fprintf(stderr, " slope %7.6f"
267 " yint %3.2f corr %7.6f freq_ppm %4.2f",
268 info->lin_cache_slope,
269 info->lin_cache_yint,
270 info->lin_cache_corr,
271 info->lin_cache_freq * 1000000.0);
273 fprintf(stderr, "\n");
278 lin_reset(server_info_t info)
286 info->lin_sumoffset = 0;
288 info->lin_cache_slope = 0;
289 info->lin_cache_yint = 0;
290 info->lin_cache_corr = 0;
291 info->lin_cache_offset = 0;
292 info->lin_cache_freq = 0;