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