1 | /* |
---|
2 | ** refclock_datum - clock driver for the Datum Programmable Time Server |
---|
3 | ** |
---|
4 | ** Important note: This driver assumes that you have termios. If you have |
---|
5 | ** a system that does not have termios, you will have to modify this driver. |
---|
6 | ** |
---|
7 | ** Sorry, I have only tested this driver on SUN and HP platforms. |
---|
8 | */ |
---|
9 | |
---|
10 | #ifdef HAVE_CONFIG_H |
---|
11 | #include <config.h> |
---|
12 | #endif |
---|
13 | |
---|
14 | #if defined(REFCLOCK) && defined(DATUM) |
---|
15 | |
---|
16 | /* |
---|
17 | ** Include Files |
---|
18 | */ |
---|
19 | |
---|
20 | #include <stdio.h> |
---|
21 | #include <ctype.h> |
---|
22 | #include <sys/time.h> |
---|
23 | #include <sys/errno.h> |
---|
24 | |
---|
25 | #include "ntpd.h" |
---|
26 | #include "ntp_io.h" |
---|
27 | #include "ntp_refclock.h" |
---|
28 | #include "ntp_unixtime.h" |
---|
29 | |
---|
30 | #if defined(HAVE_BSD_TTYS) |
---|
31 | #include <sgtty.h> |
---|
32 | #endif /* HAVE_BSD_TTYS */ |
---|
33 | |
---|
34 | #if defined(HAVE_SYSV_TTYS) |
---|
35 | #include <termio.h> |
---|
36 | #endif /* HAVE_SYSV_TTYS */ |
---|
37 | |
---|
38 | #if defined(HAVE_TERMIOS) |
---|
39 | #include <termios.h> |
---|
40 | #endif |
---|
41 | #if defined(STREAM) |
---|
42 | #include <stropts.h> |
---|
43 | #if defined(WWVBCLK) |
---|
44 | #include <sys/clkdefs.h> |
---|
45 | #endif /* WWVBCLK */ |
---|
46 | #endif /* STREAM */ |
---|
47 | |
---|
48 | #if defined (WWVBPPS) |
---|
49 | #include <sys/ppsclock.h> |
---|
50 | #endif /* WWVBPPS */ |
---|
51 | |
---|
52 | #include "ntp_stdlib.h" |
---|
53 | |
---|
54 | /* |
---|
55 | ** This driver supports the Datum Programmable Time System (PTS) clock. |
---|
56 | ** The clock works in very straight forward manner. When it receives a |
---|
57 | ** time code request (e.g., the ascii string "//k/mn"), it responds with |
---|
58 | ** a seven byte BCD time code. This clock only responds with a |
---|
59 | ** time code after it first receives the "//k/mn" message. It does not |
---|
60 | ** periodically send time codes back at some rate once it is started. |
---|
61 | ** the returned time code can be broken down into the following fields. |
---|
62 | ** |
---|
63 | ** _______________________________ |
---|
64 | ** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|
65 | ** =============================== |
---|
66 | ** byte 0: | - - - - | H D | |
---|
67 | ** =============================== |
---|
68 | ** byte 1: | T D | U D | |
---|
69 | ** =============================== |
---|
70 | ** byte 2: | - - | T H | U H | |
---|
71 | ** =============================== |
---|
72 | ** byte 3: | - | T M | U M | |
---|
73 | ** =============================== |
---|
74 | ** byte 4: | - | T S | U S | |
---|
75 | ** =============================== |
---|
76 | ** byte 5: | t S | h S | |
---|
77 | ** =============================== |
---|
78 | ** byte 6: | m S | - - - - | |
---|
79 | ** =============================== |
---|
80 | ** |
---|
81 | ** In the table above: |
---|
82 | ** |
---|
83 | ** "-" means don't care |
---|
84 | ** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days |
---|
85 | ** "T H", and "UH" means Tens and Units of Hours |
---|
86 | ** "T M", and "U M" means Tens and Units of Minutes |
---|
87 | ** "T S", and "U S" means Tens and Units of Seconds |
---|
88 | ** "t S", "h S", and "m S" means tenths, hundredths, and thousandths |
---|
89 | ** of seconds |
---|
90 | ** |
---|
91 | ** The Datum PTS communicates throught the RS232 port on your machine. |
---|
92 | ** Right now, it assumes that you have termios. This driver has been tested |
---|
93 | ** on SUN and HP workstations. The Datum PTS supports various IRIG and |
---|
94 | ** NASA input codes. This driver assumes that the name of the device is |
---|
95 | ** /dev/datum. You will need to make a soft link to your RS232 device or |
---|
96 | ** create a new driver to use this refclock. |
---|
97 | */ |
---|
98 | |
---|
99 | /* |
---|
100 | ** Datum PTS defines |
---|
101 | */ |
---|
102 | |
---|
103 | /* |
---|
104 | ** Note that if GMT is defined, then the Datum PTS must use Greenwich |
---|
105 | ** time. Otherwise, this driver allows the Datum PTS to use the current |
---|
106 | ** wall clock for its time. It determines the time zone offset by minimizing |
---|
107 | ** the error after trying several time zone offsets. If the Datum PTS |
---|
108 | ** time is Greenwich time and GMT is not defined, everything should still |
---|
109 | ** work since the time zone will be found to be 0. What this really means |
---|
110 | ** is that your system time (at least to start with) must be within the |
---|
111 | ** correct time by less than +- 30 minutes. The default is for GMT to not |
---|
112 | ** defined. If you really want to force GMT without the funny +- 30 minute |
---|
113 | ** stuff then you must define (uncomment) GMT below. |
---|
114 | */ |
---|
115 | |
---|
116 | /* |
---|
117 | #define GMT |
---|
118 | #define DEBUG_DATUM_PTC |
---|
119 | #define LOG_TIME_ERRORS |
---|
120 | */ |
---|
121 | |
---|
122 | |
---|
123 | #define PTSPRECISION (-10) /* precision assumed 1/1024 ms */ |
---|
124 | #define DATMREFID "DATM" /* reference id */ |
---|
125 | #define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */ |
---|
126 | #define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */ |
---|
127 | |
---|
128 | #define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR) |
---|
129 | |
---|
130 | /* |
---|
131 | ** External Variables |
---|
132 | */ |
---|
133 | |
---|
134 | extern u_long current_time; /* current time (s) - not really used */ |
---|
135 | extern int debug; /* global debug flag - not relly used */ |
---|
136 | |
---|
137 | /* |
---|
138 | ** The Datum PTS structure |
---|
139 | */ |
---|
140 | |
---|
141 | /* |
---|
142 | ** I don't use a fixed array of MAXUNITS like everyone else just because |
---|
143 | ** I don't like to program that way. Sorry if this bothers anyone. I assume |
---|
144 | ** that you can use any id for your unit and I will search for it in a |
---|
145 | ** dynamic array of units until I find it. I was worried that users might |
---|
146 | ** enter a bad id in their configuration file (larger than MAXUNITS) and |
---|
147 | ** besides, it is just cleaner not to have to assume that you have a fixed |
---|
148 | ** number of anything in a program. |
---|
149 | */ |
---|
150 | |
---|
151 | struct datum_pts_unit { |
---|
152 | struct peer *peer; /* peer used by xntp */ |
---|
153 | struct refclockio io; /* io structure used by xntp */ |
---|
154 | int PTS_fd; /* file descriptor for PTS */ |
---|
155 | u_int unit; /* id for unit */ |
---|
156 | u_long timestarted; /* time started */ |
---|
157 | l_fp lastrec; /* time tag for the receive time (system) */ |
---|
158 | l_fp lastref; /* reference time (Datum time) */ |
---|
159 | u_long yearstart; /* the year that this clock started */ |
---|
160 | int coderecv; /* number of time codes received */ |
---|
161 | int day; /* day */ |
---|
162 | int hour; /* hour */ |
---|
163 | int minute; /* minutes */ |
---|
164 | int second; /* seconds */ |
---|
165 | int msec; /* miliseconds */ |
---|
166 | int usec; /* miliseconds */ |
---|
167 | u_char leap; /* funny leap character code */ |
---|
168 | char retbuf[8]; /* returned time from the datum pts */ |
---|
169 | char nbytes; /* number of bytes received from datum pts */ |
---|
170 | double sigma2; /* average squared error (roughly) */ |
---|
171 | int tzoff; /* time zone offest from GMT */ |
---|
172 | }; |
---|
173 | |
---|
174 | /* |
---|
175 | ** PTS static constant variables for internal use |
---|
176 | */ |
---|
177 | |
---|
178 | static char TIME_REQUEST[6]; /* request message sent to datum for time */ |
---|
179 | static int nunits; /* number of active units */ |
---|
180 | static struct datum_pts_unit |
---|
181 | **datum_pts_unit; /* dynamic array of datum PTS structures */ |
---|
182 | |
---|
183 | /* |
---|
184 | ** Callback function prototypes that xntpd needs to know about. |
---|
185 | */ |
---|
186 | |
---|
187 | static int datum_pts_start P((int, struct peer *)); |
---|
188 | static void datum_pts_shutdown P((int, struct peer *)); |
---|
189 | static void datum_pts_poll P((int, struct peer *)); |
---|
190 | static void datum_pts_control P((int, struct refclockstat *, |
---|
191 | struct refclockstat *)); |
---|
192 | static void datum_pts_init P((void)); |
---|
193 | static void datum_pts_buginfo P((int, struct refclockbug *)); |
---|
194 | |
---|
195 | /* |
---|
196 | ** This is the call back function structure that xntpd actually uses for |
---|
197 | ** this refclock. |
---|
198 | */ |
---|
199 | |
---|
200 | struct refclock refclock_datum = { |
---|
201 | datum_pts_start, /* start up a new Datum refclock */ |
---|
202 | datum_pts_shutdown, /* shutdown a Datum refclock */ |
---|
203 | datum_pts_poll, /* sends out the time request */ |
---|
204 | datum_pts_control, /* not used */ |
---|
205 | datum_pts_init, /* initialization (called first) */ |
---|
206 | datum_pts_buginfo, /* not used */ |
---|
207 | NOFLAGS /* we are not setting any special flags */ |
---|
208 | }; |
---|
209 | |
---|
210 | /* |
---|
211 | ** The datum_pts_receive callback function is handled differently from the |
---|
212 | ** rest. It is passed to the xntpd io data structure. Basically, every |
---|
213 | ** 64 seconds, the datum_pts_poll() routine is called. It sends out the time |
---|
214 | ** request message to the Datum Programmable Time System. Then, xntpd |
---|
215 | ** waits on a select() call to receive data back. The datum_pts_receive() |
---|
216 | ** function is called as data comes back. We expect a seven byte time |
---|
217 | ** code to be returned but the datum_pts_receive() function may only get |
---|
218 | ** a few bytes passed to it at a time. In other words, this routine may |
---|
219 | ** get called by the io stuff in xntpd a few times before we get all seven |
---|
220 | ** bytes. Once the last byte is received, we process it and then pass the |
---|
221 | ** new time measurement to xntpd for updating the system time. For now, |
---|
222 | ** there is no 3 state filtering done on the time measurements. The |
---|
223 | ** jitter may be a little high but at least for its current use, it is not |
---|
224 | ** a problem. We have tried to keep things as simple as possible. This |
---|
225 | ** clock should not jitter more than 1 or 2 mseconds at the most once |
---|
226 | ** things settle down. It is important to get the right drift calibrated |
---|
227 | ** in the xntpd.drift file as well as getting the right tick set up right |
---|
228 | ** using tickadj for SUNs. Tickadj is not used for the HP but you need to |
---|
229 | ** remember to bring up the adjtime daemon because HP does not support |
---|
230 | ** the adjtime() call. |
---|
231 | */ |
---|
232 | |
---|
233 | static void datum_pts_receive P((struct recvbuf *)); |
---|
234 | |
---|
235 | /*......................................................................*/ |
---|
236 | /* datum_pts_start - start up the datum PTS. This means open the */ |
---|
237 | /* RS232 device and set up the data structure for my unit. */ |
---|
238 | /*......................................................................*/ |
---|
239 | |
---|
240 | static int datum_pts_start(unit, peer) |
---|
241 | int unit; |
---|
242 | struct peer *peer; |
---|
243 | { |
---|
244 | struct datum_pts_unit **temp_datum_pts_unit; |
---|
245 | struct datum_pts_unit *datum_pts; |
---|
246 | |
---|
247 | #ifdef HAVE_TERMIOS |
---|
248 | struct termios arg; |
---|
249 | #endif |
---|
250 | |
---|
251 | #ifdef DEBUG_DATUM_PTC |
---|
252 | if (debug) |
---|
253 | printf("Starting Datum PTS unit %d\n", unit); |
---|
254 | #endif |
---|
255 | |
---|
256 | /* |
---|
257 | ** Create the memory for the new unit |
---|
258 | */ |
---|
259 | |
---|
260 | temp_datum_pts_unit = (struct datum_pts_unit **) |
---|
261 | malloc((nunits+1)*sizeof(struct datum_pts_unit *)); |
---|
262 | if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit, |
---|
263 | nunits*sizeof(struct datum_pts_unit *)); |
---|
264 | free(datum_pts_unit); |
---|
265 | datum_pts_unit = temp_datum_pts_unit; |
---|
266 | datum_pts_unit[nunits] = (struct datum_pts_unit *) |
---|
267 | malloc(sizeof(struct datum_pts_unit)); |
---|
268 | datum_pts = datum_pts_unit[nunits]; |
---|
269 | |
---|
270 | datum_pts->unit = unit; /* set my unit id */ |
---|
271 | datum_pts->yearstart = 0; /* initialize the yearstart to 0 */ |
---|
272 | datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */ |
---|
273 | |
---|
274 | /* |
---|
275 | ** Open the Datum PTS device |
---|
276 | */ |
---|
277 | |
---|
278 | datum_pts->PTS_fd = open("/dev/datum",O_RDWR); |
---|
279 | |
---|
280 | fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */ |
---|
281 | |
---|
282 | #ifdef DEBUG_DATUM_PTC |
---|
283 | if (debug) |
---|
284 | printf("Opening RS232 port with file descriptor %d\n", |
---|
285 | datum_pts->PTS_fd); |
---|
286 | #endif |
---|
287 | |
---|
288 | /* |
---|
289 | ** Set up the RS232 terminal device information. Note that we assume that |
---|
290 | ** we have termios. This code has only been tested on SUNs and HPs. If your |
---|
291 | ** machine does not have termios this driver cannot be initialized. You can change this |
---|
292 | ** if you want by editing this source. Please give the changes back to the |
---|
293 | ** xntp folks so that it can become part of their regular distribution. |
---|
294 | */ |
---|
295 | |
---|
296 | #ifdef HAVE_TERMIOS |
---|
297 | |
---|
298 | arg.c_iflag = IGNBRK; |
---|
299 | arg.c_oflag = 0; |
---|
300 | arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL; |
---|
301 | arg.c_lflag = 0; |
---|
302 | arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */ |
---|
303 | arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */ |
---|
304 | |
---|
305 | tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg); |
---|
306 | |
---|
307 | #else |
---|
308 | |
---|
309 | msyslog(LOG_ERR, "Datum_PTS: Termios not supported in this driver"); |
---|
310 | (void)close(datum_pts->PTS_fd); |
---|
311 | |
---|
312 | return 0; |
---|
313 | |
---|
314 | #endif |
---|
315 | |
---|
316 | /* |
---|
317 | ** Initialize the xntpd IO structure |
---|
318 | */ |
---|
319 | |
---|
320 | datum_pts->peer = peer; |
---|
321 | datum_pts->timestarted = current_time; |
---|
322 | |
---|
323 | datum_pts->io.clock_recv = datum_pts_receive; |
---|
324 | datum_pts->io.srcclock = (caddr_t)datum_pts; |
---|
325 | datum_pts->io.datalen = 0; |
---|
326 | datum_pts->io.fd = datum_pts->PTS_fd; |
---|
327 | |
---|
328 | if (!io_addclock(&(datum_pts->io))) { |
---|
329 | |
---|
330 | #ifdef DEBUG_DATUM_PTC |
---|
331 | if (debug) |
---|
332 | printf("Problem adding clock\n"); |
---|
333 | #endif |
---|
334 | |
---|
335 | msyslog(LOG_ERR, "Datum_PTS: Problem adding clock"); |
---|
336 | (void)close(datum_pts->PTS_fd); |
---|
337 | |
---|
338 | return 0; |
---|
339 | } |
---|
340 | |
---|
341 | peer->precision = PTSPRECISION; |
---|
342 | peer->rootdelay = 0; |
---|
343 | peer->rootdispersion = 0; |
---|
344 | peer->stratum = 0; |
---|
345 | memcpy((char *)&peer->refid, DATMREFID, 4); |
---|
346 | |
---|
347 | /* |
---|
348 | ** Now add one to the number of units and return a successful code |
---|
349 | */ |
---|
350 | |
---|
351 | nunits++; |
---|
352 | return 1; |
---|
353 | |
---|
354 | } |
---|
355 | |
---|
356 | |
---|
357 | /*......................................................................*/ |
---|
358 | /* datum_pts_shutdown - this routine shuts doen the device and */ |
---|
359 | /* removes the memory for the unit. */ |
---|
360 | /*......................................................................*/ |
---|
361 | |
---|
362 | static void datum_pts_shutdown(unit, peer) |
---|
363 | int unit; |
---|
364 | struct peer *peer; |
---|
365 | { |
---|
366 | int i,j; |
---|
367 | struct datum_pts_unit **temp_datum_pts_unit; |
---|
368 | |
---|
369 | #ifdef DEBUG_DATUM_PTC |
---|
370 | if (debug) |
---|
371 | printf("Shutdown Datum PTS\n"); |
---|
372 | #endif |
---|
373 | |
---|
374 | msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS"); |
---|
375 | |
---|
376 | /* |
---|
377 | ** First we have to find the right unit (i.e., the one with the same id). |
---|
378 | ** We do this by looping through the dynamic array of units intil we find |
---|
379 | ** it. Note, that I don't simply use an array with a maximimum number of |
---|
380 | ** Datum PTS units. Everything is completely dynamic. |
---|
381 | */ |
---|
382 | |
---|
383 | for (i=0; i<nunits; i++) { |
---|
384 | if (datum_pts_unit[i]->unit == unit) { |
---|
385 | |
---|
386 | /* |
---|
387 | ** We found the unit so close the file descriptor and free up the memory used |
---|
388 | ** by the structure. |
---|
389 | */ |
---|
390 | |
---|
391 | io_closeclock(&datum_pts_unit[i]->io); |
---|
392 | close(datum_pts_unit[i]->PTS_fd); |
---|
393 | free(datum_pts_unit[i]); |
---|
394 | |
---|
395 | /* |
---|
396 | ** Now clean up the datum_pts_unit dynamic array so that there are no holes. |
---|
397 | ** This may mean moving pointers around, etc., to keep things compact. |
---|
398 | */ |
---|
399 | |
---|
400 | if (nunits > 1) { |
---|
401 | |
---|
402 | temp_datum_pts_unit = (struct datum_pts_unit **) |
---|
403 | malloc((nunits-1)*sizeof(struct datum_pts_unit *)); |
---|
404 | if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit, |
---|
405 | i*sizeof(struct datum_pts_unit *)); |
---|
406 | |
---|
407 | for (j=i+1; j<nunits; j++) { |
---|
408 | temp_datum_pts_unit[j-1] = datum_pts_unit[j]; |
---|
409 | } |
---|
410 | |
---|
411 | free(datum_pts_unit); |
---|
412 | datum_pts_unit = temp_datum_pts_unit; |
---|
413 | |
---|
414 | }else{ |
---|
415 | |
---|
416 | free(datum_pts_unit); |
---|
417 | datum_pts_unit = NULL; |
---|
418 | |
---|
419 | } |
---|
420 | |
---|
421 | return; |
---|
422 | |
---|
423 | } |
---|
424 | } |
---|
425 | |
---|
426 | #ifdef DEBUG_DATUM_PTC |
---|
427 | if (debug) |
---|
428 | printf("Error, could not shut down unit %d\n",unit); |
---|
429 | #endif |
---|
430 | |
---|
431 | msyslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit); |
---|
432 | |
---|
433 | } |
---|
434 | |
---|
435 | /*......................................................................*/ |
---|
436 | /* datum_pts_poll - this routine sends out the time request to the */ |
---|
437 | /* Datum PTS device. The time will be passed back in the */ |
---|
438 | /* datum_pts_receive() routine. */ |
---|
439 | /*......................................................................*/ |
---|
440 | |
---|
441 | static void datum_pts_poll(unit, peer) |
---|
442 | int unit; |
---|
443 | struct peer *peer; |
---|
444 | { |
---|
445 | int i; |
---|
446 | int index; |
---|
447 | int error_code; |
---|
448 | struct datum_pts_unit *datum_pts; |
---|
449 | |
---|
450 | #ifdef DEBUG_DATUM_PTC |
---|
451 | if (debug) |
---|
452 | printf("Poll Datum PTS\n"); |
---|
453 | #endif |
---|
454 | |
---|
455 | /* |
---|
456 | ** Find the right unit and send out a time request once it is found. |
---|
457 | */ |
---|
458 | |
---|
459 | index = -1; |
---|
460 | for (i=0; i<nunits; i++) { |
---|
461 | if (datum_pts_unit[i]->unit == unit) { |
---|
462 | index = i; |
---|
463 | datum_pts = datum_pts_unit[i]; |
---|
464 | error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6); |
---|
465 | if (error_code != 6) perror("TIME_REQUEST"); |
---|
466 | datum_pts->nbytes = 0; |
---|
467 | break; |
---|
468 | } |
---|
469 | } |
---|
470 | |
---|
471 | /* |
---|
472 | ** Print out an error message if we could not find the right unit. |
---|
473 | */ |
---|
474 | |
---|
475 | if (index == -1) { |
---|
476 | |
---|
477 | #ifdef DEBUG_DATUM_PTC |
---|
478 | if (debug) |
---|
479 | printf("Error, could not poll unit %d\n",unit); |
---|
480 | #endif |
---|
481 | |
---|
482 | msyslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit); |
---|
483 | return; |
---|
484 | |
---|
485 | } |
---|
486 | |
---|
487 | } |
---|
488 | |
---|
489 | |
---|
490 | /*......................................................................*/ |
---|
491 | /* datum_pts_control - not used */ |
---|
492 | /*......................................................................*/ |
---|
493 | |
---|
494 | static void datum_pts_control(unit, in, out) |
---|
495 | int unit; |
---|
496 | struct refclockstat *in; |
---|
497 | struct refclockstat *out; |
---|
498 | { |
---|
499 | |
---|
500 | #ifdef DEBUG_DATUM_PTC |
---|
501 | if (debug) |
---|
502 | printf("Control Datum PTS\n"); |
---|
503 | #endif |
---|
504 | |
---|
505 | } |
---|
506 | |
---|
507 | |
---|
508 | /*......................................................................*/ |
---|
509 | /* datum_pts_init - initializes things for all possible Datum */ |
---|
510 | /* time code generators that might be used. In practice, this is */ |
---|
511 | /* only called once at the beginning before anything else is */ |
---|
512 | /* called. */ |
---|
513 | /*......................................................................*/ |
---|
514 | |
---|
515 | static void datum_pts_init() |
---|
516 | { |
---|
517 | |
---|
518 | /* */ |
---|
519 | /*...... open up the log file if we are debugging ......................*/ |
---|
520 | /* */ |
---|
521 | |
---|
522 | /* |
---|
523 | ** Open up the log file if we are debugging. For now, send data out to the |
---|
524 | ** screen (stdout). |
---|
525 | */ |
---|
526 | |
---|
527 | #ifdef DEBUG_DATUM_PTC |
---|
528 | if (debug) |
---|
529 | printf("Init Datum PTS\n"); |
---|
530 | #endif |
---|
531 | |
---|
532 | /* |
---|
533 | ** Initialize the time request command string. This is the only message |
---|
534 | ** that we ever have to send to the Datum PTS (although others are defined). |
---|
535 | */ |
---|
536 | |
---|
537 | memcpy(TIME_REQUEST, "//k/mn",6); |
---|
538 | |
---|
539 | /* |
---|
540 | ** Initialize the number of units to 0 and set the dynamic array of units to |
---|
541 | ** NULL since there are no units defined yet. |
---|
542 | */ |
---|
543 | |
---|
544 | datum_pts_unit = NULL; |
---|
545 | nunits = 0; |
---|
546 | |
---|
547 | } |
---|
548 | |
---|
549 | |
---|
550 | /*......................................................................*/ |
---|
551 | /* datum_pts_buginfo - not used */ |
---|
552 | /*......................................................................*/ |
---|
553 | |
---|
554 | static void datum_pts_buginfo(unit, bug) |
---|
555 | int unit; |
---|
556 | register struct refclockbug *bug; |
---|
557 | { |
---|
558 | |
---|
559 | #ifdef DEBUG_DATUM_PTC |
---|
560 | if (debug) |
---|
561 | printf("Buginfo Datum PTS\n"); |
---|
562 | #endif |
---|
563 | |
---|
564 | } |
---|
565 | |
---|
566 | |
---|
567 | /*......................................................................*/ |
---|
568 | /* datum_pts_receive - receive the time buffer that was read in */ |
---|
569 | /* by the xntpd io handling routines. When 7 bytes have been */ |
---|
570 | /* received (it may take several tries before all 7 bytes are */ |
---|
571 | /* received), then the time code must be unpacked and sent to */ |
---|
572 | /* the xntpd clock_receive() routine which causes the systems */ |
---|
573 | /* clock to be updated (several layers down). */ |
---|
574 | /*......................................................................*/ |
---|
575 | |
---|
576 | static void datum_pts_receive(rbufp) |
---|
577 | struct recvbuf *rbufp; |
---|
578 | { |
---|
579 | int i; |
---|
580 | l_fp tstmp; |
---|
581 | struct datum_pts_unit *datum_pts; |
---|
582 | char *dpt; |
---|
583 | int dpend; |
---|
584 | int tzoff; |
---|
585 | int timerr; |
---|
586 | double ftimerr, abserr; |
---|
587 | u_fp dispersion; |
---|
588 | int goodtime; |
---|
589 | |
---|
590 | /* |
---|
591 | ** Get the time code (maybe partial) message out of the rbufp buffer. |
---|
592 | */ |
---|
593 | |
---|
594 | datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock; |
---|
595 | dpt = (char *)&rbufp->recv_space; |
---|
596 | dpend = rbufp->recv_length; |
---|
597 | |
---|
598 | #ifdef DEBUG_DATUM_PTC |
---|
599 | if (debug) |
---|
600 | printf("Receive Datum PTS: %d bytes\n", dpend); |
---|
601 | #endif |
---|
602 | |
---|
603 | /* */ |
---|
604 | /*...... save the ntp system time when the first byte is received ......*/ |
---|
605 | /* */ |
---|
606 | |
---|
607 | /* |
---|
608 | ** Save the ntp system time when the first byte is received. Note that |
---|
609 | ** because it may take several calls to this routine before all seven |
---|
610 | ** bytes of our return message are finally received by the io handlers in |
---|
611 | ** xntpd, we really do want to use the time tag when the first byte is |
---|
612 | ** received to reduce the jitter. |
---|
613 | */ |
---|
614 | |
---|
615 | if (datum_pts->nbytes == 0) { |
---|
616 | datum_pts->lastrec = rbufp->recv_time; |
---|
617 | } |
---|
618 | |
---|
619 | /* |
---|
620 | ** Increment our count to the number of bytes received so far. Return if we |
---|
621 | ** haven't gotten all seven bytes yet. |
---|
622 | */ |
---|
623 | |
---|
624 | for (i=0; i<dpend; i++) { |
---|
625 | datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i]; |
---|
626 | } |
---|
627 | |
---|
628 | datum_pts->nbytes += dpend; |
---|
629 | |
---|
630 | if (datum_pts->nbytes != 7) { |
---|
631 | return; |
---|
632 | } |
---|
633 | |
---|
634 | /* |
---|
635 | ** Convert the seven bytes received in our time buffer to day, hour, minute, |
---|
636 | ** second, and msecond values. The usec value is not used for anything |
---|
637 | ** currently. It is just the fractional part of the time stored in units |
---|
638 | ** of microseconds. |
---|
639 | */ |
---|
640 | |
---|
641 | datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) + |
---|
642 | 10*((datum_pts->retbuf[1] & 0xf0)>>4) + |
---|
643 | (datum_pts->retbuf[1] & 0x0f); |
---|
644 | |
---|
645 | datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) + |
---|
646 | (datum_pts->retbuf[2] & 0x0f); |
---|
647 | |
---|
648 | datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) + |
---|
649 | (datum_pts->retbuf[3] & 0x0f); |
---|
650 | |
---|
651 | datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) + |
---|
652 | (datum_pts->retbuf[4] & 0x0f); |
---|
653 | |
---|
654 | datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) + |
---|
655 | 10*(datum_pts->retbuf[5] & 0x0f) + |
---|
656 | ((datum_pts->retbuf[6] & 0xf0)>>4); |
---|
657 | |
---|
658 | datum_pts->usec = 1000*datum_pts->msec; |
---|
659 | |
---|
660 | #ifdef DEBUG_DATUM_PTC |
---|
661 | if (debug) |
---|
662 | printf("day %d, hour %d, minute %d, second %d, msec %d\n", |
---|
663 | datum_pts->day, |
---|
664 | datum_pts->hour, |
---|
665 | datum_pts->minute, |
---|
666 | datum_pts->second, |
---|
667 | datum_pts->msec); |
---|
668 | #endif |
---|
669 | |
---|
670 | /* |
---|
671 | ** Get the GMT time zone offset. Note that GMT should be zero if the Datum |
---|
672 | ** reference time is using GMT as its time base. Otherwise we have to |
---|
673 | ** determine the offset if the Datum PTS is using time of day as its time |
---|
674 | ** base. |
---|
675 | */ |
---|
676 | |
---|
677 | goodtime = 0; /* We are not sure about the time and offset yet */ |
---|
678 | |
---|
679 | #ifdef GMT |
---|
680 | |
---|
681 | /* |
---|
682 | ** This is the case where the Datum PTS is using GMT so there is no time |
---|
683 | ** zone offset. |
---|
684 | */ |
---|
685 | |
---|
686 | tzoff = 0; /* set time zone offset to 0 */ |
---|
687 | |
---|
688 | #else |
---|
689 | |
---|
690 | /* |
---|
691 | ** This is the case where the Datum PTS is using regular time of day for its |
---|
692 | ** time so we must compute the time zone offset. The way we do it is kind of |
---|
693 | ** funny but it works. We loop through different time zones (0 to 24) and |
---|
694 | ** pick the one that gives the smallest error (+- one half hour). The time |
---|
695 | ** zone offset is stored in the datum_pts structure for future use. Normally, |
---|
696 | ** the clocktime() routine is only called once (unless the time zone offset |
---|
697 | ** changes due to daylight savings) since the goodtime flag is set when a |
---|
698 | ** good time is found (with a good offset). Note that even if the Datum |
---|
699 | ** PTS is using GMT, this mechanism will still work since it should come up |
---|
700 | ** with a value for tzoff = 0 (assuming that your system clock is within |
---|
701 | ** a half hour of the Datum time (even with time zone differences). |
---|
702 | */ |
---|
703 | |
---|
704 | for (tzoff=0; tzoff<24; tzoff++) { |
---|
705 | if (clocktime( datum_pts->day, |
---|
706 | datum_pts->hour, |
---|
707 | datum_pts->minute, |
---|
708 | datum_pts->second, |
---|
709 | (tzoff + datum_pts->tzoff) % 24, |
---|
710 | datum_pts->lastrec.l_ui, |
---|
711 | &datum_pts->yearstart, |
---|
712 | &datum_pts->lastref.l_ui) ) { |
---|
713 | |
---|
714 | error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui; |
---|
715 | |
---|
716 | #ifdef DEBUG_DATUM_PTC |
---|
717 | printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error); |
---|
718 | #endif |
---|
719 | |
---|
720 | if ((error < 1799) && (error > -1799)) { |
---|
721 | tzoff = (tzoff + datum_pts->tzoff) % 24; |
---|
722 | datum_pts->tzoff = tzoff; |
---|
723 | goodtime = 1; |
---|
724 | |
---|
725 | #ifdef DEBUG_DATUM_PTC |
---|
726 | printf("Time Zone found (clocktime method) = %d\n",tzoff); |
---|
727 | #endif |
---|
728 | |
---|
729 | break; |
---|
730 | } |
---|
731 | |
---|
732 | } |
---|
733 | } |
---|
734 | |
---|
735 | #endif |
---|
736 | |
---|
737 | /* |
---|
738 | ** Make sure that we have a good time from the Datum PTS. Clocktime() also |
---|
739 | ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e., |
---|
740 | ** the fraction of a second) stuff later. |
---|
741 | */ |
---|
742 | |
---|
743 | if (!goodtime) { |
---|
744 | |
---|
745 | if (!clocktime( datum_pts->day, |
---|
746 | datum_pts->hour, |
---|
747 | datum_pts->minute, |
---|
748 | datum_pts->second, |
---|
749 | tzoff, |
---|
750 | datum_pts->lastrec.l_ui, |
---|
751 | &datum_pts->yearstart, |
---|
752 | &datum_pts->lastref.l_ui) ) { |
---|
753 | |
---|
754 | #ifdef DEBUG_DATUM_PTC |
---|
755 | if (debug) |
---|
756 | { |
---|
757 | printf("Error: bad clocktime\n"); |
---|
758 | printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n", |
---|
759 | tzoff, |
---|
760 | datum_pts->lastrec.l_ui, |
---|
761 | datum_pts->yearstart, |
---|
762 | datum_pts->lastref.l_ui); |
---|
763 | } |
---|
764 | #endif |
---|
765 | |
---|
766 | msyslog(LOG_ERR, "Datum_PTS: Bad clocktime"); |
---|
767 | |
---|
768 | return; |
---|
769 | |
---|
770 | }else{ |
---|
771 | |
---|
772 | #ifdef DEBUG_DATUM_PTC |
---|
773 | if (debug) |
---|
774 | printf("Good clocktime\n"); |
---|
775 | #endif |
---|
776 | |
---|
777 | } |
---|
778 | |
---|
779 | } |
---|
780 | |
---|
781 | /* |
---|
782 | ** We have datum_pts->lastref.l_ui set (which is the integer part of the |
---|
783 | ** time. Now set the microseconds field. |
---|
784 | */ |
---|
785 | |
---|
786 | TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf); |
---|
787 | |
---|
788 | /* |
---|
789 | ** Compute the time correction as the difference between the reference |
---|
790 | ** time (i.e., the Datum time) minus the receive time (system time). |
---|
791 | */ |
---|
792 | |
---|
793 | tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */ |
---|
794 | L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */ |
---|
795 | datum_pts->coderecv++; /* increment a counter */ |
---|
796 | |
---|
797 | dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */ |
---|
798 | |
---|
799 | #ifdef DEBUG_DATUM_PTC |
---|
800 | ftimerr = dispersion; |
---|
801 | ftimerr /= (1024.0 * 64.0); |
---|
802 | if (debug) |
---|
803 | printf("dispersion = %d, %f\n", dispersion, ftimerr); |
---|
804 | #endif |
---|
805 | |
---|
806 | /* |
---|
807 | ** Pass the new time to xntpd through the refclock_receive function. Note |
---|
808 | ** that we are not trying to make any corrections due to the time it takes |
---|
809 | ** for the Datum PTS to send the message back. I am (erroneously) assuming |
---|
810 | ** that the time for the Datum PTS to send the time back to us is negligable. |
---|
811 | ** I suspect that this time delay may be as much as 15 ms or so (but probably |
---|
812 | ** less). For our needs at JPL, this kind of error is ok so it is not |
---|
813 | ** necessary to use fudge factors in the ntp.conf file. Maybe later we will. |
---|
814 | */ |
---|
815 | |
---|
816 | refclock_receive( datum_pts->peer, |
---|
817 | &tstmp, |
---|
818 | tzoff, |
---|
819 | dispersion, |
---|
820 | &datum_pts->lastrec, |
---|
821 | &datum_pts->lastrec, |
---|
822 | datum_pts->leap ); |
---|
823 | |
---|
824 | /* |
---|
825 | ** Compute sigma squared (not used currently). Maybe later, this could be |
---|
826 | ** used for the dispersion estimate. The problem is that xntpd does not link |
---|
827 | ** in the math library so sqrt() is not available. Anyway, this is useful |
---|
828 | ** for debugging. Maybe later I will just use absolute values for the time |
---|
829 | ** error to come up with my dispersion estimate. Anyway, for now my dispersion |
---|
830 | ** is set to 0. |
---|
831 | */ |
---|
832 | |
---|
833 | timerr = tstmp.l_ui<<20; |
---|
834 | timerr |= (tstmp.l_uf>>12) & 0x000fffff; |
---|
835 | ftimerr = timerr; |
---|
836 | ftimerr /= 1024*1024; |
---|
837 | abserr = ftimerr; |
---|
838 | if (ftimerr < 0.0) abserr = -ftimerr; |
---|
839 | |
---|
840 | if (datum_pts->sigma2 == 0.0) { |
---|
841 | if (abserr < DATUM_MAX_ERROR) { |
---|
842 | datum_pts->sigma2 = abserr*abserr; |
---|
843 | }else{ |
---|
844 | datum_pts->sigma2 = DATUM_MAX_ERROR2; |
---|
845 | } |
---|
846 | }else{ |
---|
847 | if (abserr < DATUM_MAX_ERROR) { |
---|
848 | datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr; |
---|
849 | }else{ |
---|
850 | datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2; |
---|
851 | } |
---|
852 | } |
---|
853 | |
---|
854 | #ifdef DEBUG_DATUM_PTC |
---|
855 | if (debug) |
---|
856 | printf("Time error = %f seconds\n", ftimerr); |
---|
857 | #endif |
---|
858 | |
---|
859 | #if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) |
---|
860 | if (debug) |
---|
861 | printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n", |
---|
862 | datum_pts->day, |
---|
863 | datum_pts->hour, |
---|
864 | datum_pts->minute, |
---|
865 | datum_pts->second, |
---|
866 | datum_pts->msec, |
---|
867 | ftimerr); |
---|
868 | #endif |
---|
869 | |
---|
870 | } |
---|
871 | #endif |
---|