Commit | Line | Data |
---|---|---|
9e8e5516 C |
1 | #ifndef lint |
2 | static char *RCSid = "$Header: /f/osi/others/ntp/RCS/ntp_adjust.c,v 7.1 91/02/22 09:33:46 mrose Interim $"; | |
3 | #endif | |
4 | ||
5 | /* | |
6 | * This module implemenets the logical Local Clock, as described in section | |
7 | * 5. of the NTP specification. | |
8 | * based on the ntp 3.4 code, but modified for OSI etc. | |
9 | * | |
10 | * $Log: ntp_adjust.c,v $ | |
11 | * Revision 7.1 91/02/22 09:33:46 mrose | |
12 | * Interim 6.8 | |
13 | * | |
14 | * Revision 7.0 90/12/10 17:21:27 mrose | |
15 | * *** empty log message *** | |
16 | * | |
17 | * Revision 1.1 89/06/15 20:36:55 jpo | |
18 | * Initial revision | |
19 | * | |
20 | * | |
21 | */ | |
22 | ||
23 | #include "ntp.h" | |
24 | ||
25 | #ifdef DEBUG | |
26 | extern int debug; | |
27 | #endif | |
28 | ||
29 | extern int doset; | |
30 | extern int kern_tickadj; | |
31 | extern char *ntoa(); | |
32 | extern struct sysdata sys; | |
33 | extern LLog *pgm_log; | |
34 | ||
35 | double drift_comp = 0.0, | |
36 | compliance, | |
37 | clock_adjust; | |
38 | long update_timer = 0; | |
39 | ||
40 | int adj_precision; | |
41 | double adj_residual; | |
42 | int firstpass = 1; | |
43 | ||
44 | #define abs(x) ((x) < 0 ? -(x) : (x)) | |
45 | ||
46 | void | |
47 | init_logical_clock() | |
48 | { | |
49 | if (kern_tickadj) | |
50 | adj_precision = kern_tickadj; | |
51 | else | |
52 | adj_precision = 1; | |
53 | /* | |
54 | * If you have the "fix" for adjtime() installed in you kernel, you'll | |
55 | * have to make sure that adj_precision is set to 1 here. | |
56 | */ | |
57 | } | |
58 | ||
59 | ||
60 | /* | |
61 | * 5.0 Logical clock procedure | |
62 | * | |
63 | * Only paramter is an offset to vary the clock by, in seconds. We'll either | |
64 | * arrange for the clock to slew to accomodate the adjustment, or just preform | |
65 | * a step adjustment if the offset is too large. | |
66 | * | |
67 | * The update which is to be performed is left in the external | |
68 | * clock_adjust. | |
69 | * | |
70 | * Returns non-zero if clock was reset rather than slewed. | |
71 | * | |
72 | * Many thanks for Dennis Ferguson <dennis@gw.ccie.utoronto.ca> for his | |
73 | * corrections to my code. | |
74 | */ | |
75 | ||
76 | int | |
77 | adj_logical(offset) | |
78 | double offset; | |
79 | { | |
80 | struct timeval tv1, tv2; | |
81 | #ifdef XADJTIME2 | |
82 | struct timeval delta, olddelta; | |
83 | #endif | |
84 | ||
85 | /* | |
86 | * Now adjust the logical clock | |
87 | */ | |
88 | if (!doset) | |
89 | return 0; | |
90 | ||
91 | adj_residual = 0.0; | |
92 | if (offset > CLOCK_MAX || offset < -CLOCK_MAX) { | |
93 | double steptime = offset; | |
94 | ||
95 | (void) gettimeofday(&tv2, (struct timezone *) 0); | |
96 | steptime += tv2.tv_sec; | |
97 | steptime += tv2.tv_usec / 1000000.0; | |
98 | tv1.tv_sec = steptime; | |
99 | tv1.tv_usec = (steptime - tv1.tv_sec) * 1000000; | |
100 | #ifdef DEBUG | |
101 | if (debug > 2) { | |
102 | steptime = (tv1.tv_sec + tv1.tv_usec/1000000.0) - | |
103 | (tv2.tv_sec + tv2.tv_usec/1000000.0); | |
104 | TRACE (2, ("adj_logical: %f %f", offset, steptime)); | |
105 | } | |
106 | #endif | |
107 | if (settimeofday(&tv1, (struct timezone *) 0) < 0) { | |
108 | advise (LLOG_EXCEPTIONS, NULLCP, "Can't set time: %m"); | |
109 | return(-1); | |
110 | } | |
111 | else { | |
112 | TRACE (1, ("set time of day")); | |
113 | } | |
114 | clock_adjust = 0.0; | |
115 | firstpass = 1; | |
116 | update_timer = 0; | |
117 | return (1); /* indicate that step adjustment was done */ | |
118 | } else { | |
119 | double ai; | |
120 | ||
121 | /* | |
122 | * If this is our very first adjustment, don't touch | |
123 | * the drift compensation (this is f in the spec | |
124 | * equations), else update using the *old* value | |
125 | * of the compliance. | |
126 | */ | |
127 | clock_adjust = offset; | |
128 | if (firstpass) | |
129 | firstpass = 0; | |
130 | else if (update_timer > 0) { | |
131 | ai = abs(compliance); | |
132 | ai = (double)(1<<CLOCK_COMP) - | |
133 | (double)(1<<CLOCK_FACTOR) * ai; | |
134 | if (ai < 1.0) /* max(... , 1.0) */ | |
135 | ai = 1.0; | |
136 | drift_comp += offset / (ai * (double)update_timer); | |
137 | } | |
138 | ||
139 | /* | |
140 | * Set the timer to zero. adj_host_clock() increments it | |
141 | * so we can tell the period between updates. | |
142 | */ | |
143 | update_timer = 0; | |
144 | ||
145 | /* | |
146 | * Now update the compliance. The compliance is h in the | |
147 | * equations. | |
148 | */ | |
149 | compliance += (offset - compliance)/(double)(1<<CLOCK_TRACK); | |
150 | ||
151 | #ifdef XADJTIME2 | |
152 | delta.tv_sec = offset; | |
153 | delta.tv_usec = (offset - delta.tv_sec) * 1000; | |
154 | (void) adjtime2(&delta, &olddelta); | |
155 | #endif | |
156 | return(0); | |
157 | } | |
158 | } | |
159 | ||
160 | #ifndef XADJTIME2 | |
161 | extern int adjtime(); | |
162 | ||
163 | /* | |
164 | * This is that routine that performs the periodic clock adjustment. | |
165 | * The procedure is best described in the the NTP document. In a | |
166 | * nutshell, we prefer to do lots of small evenly spaced adjustments. | |
167 | * The alternative, one large adjustment, creates two much of a | |
168 | * clock disruption and as a result oscillation. | |
169 | * | |
170 | * This function is called every 2**CLOCK_ADJ seconds. | |
171 | * | |
172 | */ | |
173 | ||
174 | /* | |
175 | * global for debugging? | |
176 | */ | |
177 | double adjustment; | |
178 | ||
179 | void | |
180 | adj_host_clock(n) | |
181 | int n; | |
182 | { | |
183 | ||
184 | struct timeval delta, olddelta; | |
185 | ||
186 | if (!doset) | |
187 | return; | |
188 | ||
189 | /* | |
190 | * Add update period into timer so we know how long it | |
191 | * took between the last update and the next one. | |
192 | */ | |
193 | update_timer += n; | |
194 | /* | |
195 | * Should check to see if update_timer > 1 day here? | |
196 | */ | |
197 | ||
198 | /* | |
199 | * Compute phase part of adjustment here and update clock_adjust. | |
200 | * Note that the equations used here are implicit in the last | |
201 | * two equations in the spec (in particular, look at the equation | |
202 | * for g and figure out how to find the k==1 term given the k==0 term.) | |
203 | */ | |
204 | adjustment = clock_adjust / (double)(1<<CLOCK_PHASE); | |
205 | clock_adjust -= adjustment; | |
206 | ||
207 | /* | |
208 | * Now add in the frequency component. Be careful to note that | |
209 | * the ni occurs in the last equation since those equations take | |
210 | * you from 64 second update to 64 second update (ei is the total | |
211 | * adjustment done over 64 seconds) and we're only deal in the | |
212 | * little 4 second adjustment interval here. | |
213 | */ | |
214 | adjustment += drift_comp / (double)(1<<CLOCK_FREQ); | |
215 | ||
216 | /* | |
217 | * Add in old adjustment residual | |
218 | */ | |
219 | adjustment += adj_residual; | |
220 | ||
221 | /* | |
222 | * Simplify. Adjustment shouldn't be bigger than 2 ms. Hope | |
223 | * writer of spec was truth telling. | |
224 | */ | |
225 | #ifdef DEBUG | |
226 | delta.tv_sec = adjustment; | |
227 | if (debug && delta.tv_sec) abort(); | |
228 | #else | |
229 | delta.tv_sec = 0; | |
230 | #endif | |
231 | delta.tv_usec = ((long)(adjustment * 1000000.0) / adj_precision) | |
232 | * adj_precision; | |
233 | ||
234 | adj_residual = adjustment - (double) delta.tv_usec / 1000000.0; | |
235 | ||
236 | if (delta.tv_usec == 0) | |
237 | return; | |
238 | ||
239 | if (adjtime(&delta, &olddelta) < 0) | |
240 | advise (LLOG_EXCEPTIONS, NULLCP, "Can't adjust time: %m"); | |
241 | ||
242 | TRACE (2, ("adj: %ld us %f %f", | |
243 | delta.tv_usec, drift_comp, clock_adjust)); | |
244 | } | |
245 | #endif |