4 * refclock_true - clock driver for the Kinemetrics Truetime receivers
5 * Receiver Version 3.0C - tested plain, with CLKLDISC
6 * Developement work being done:
7 * - Properly handle varying satellite positions (more acurately)
8 * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
15 #if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
19 #include "ntp_refclock.h"
20 #include "ntp_unixtime.h"
21 #include "ntp_stdlib.h"
26 /* This should be an atom clock but those are very hard to build.
28 * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
29 * of TTL input and output pins, all brought out to the back panel. If you
30 * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
31 * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
32 * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
33 * number of uSecs since the last PPS upward swing, mediated by reading OUT0
34 * to find out if the counter has wrapped around (this happens if more than
35 * 65535us (65ms) elapses between the PPS event and our being called.)
40 # include <machine/inline.h>
41 # include <sys/pcl720.h>
42 # include <sys/i8253.h>
43 # define PCL720_IOB 0x2a0 /* XXX */
44 # define PCL720_CTR 0 /* XXX */
48 * Support for Kinemetrics Truetime Receivers
51 * XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD)
52 * GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module)
53 * OM-DC: getting stale ("OMEGA")
55 * Most of this code is originally from refclock_wwvb.c with thanks.
56 * It has been so mangled that wwvb is not a recognizable ancestor.
58 * Timcode format: ADDD:HH:MM:SSQCL
59 * A - control A (this is stripped before we see it)
60 * Q - Quality indication (see below)
64 * Quality codes indicate possible error of
65 * 468-DC GOES Receiver:
66 * GPS-TM/TMD Receiver: (default quality codes for XL-DC)
67 * ? +/- 1 milliseconds # +/- 100 microseconds
68 * * +/- 10 microseconds . +/- 1 microsecond
69 * space less than 1 microsecond
70 * OM-DC OMEGA Receiver: (default quality codes for OMEGA)
71 * WARNING OMEGA navigation system is no longer existent
73 * ? >+/- 500 milliseconds # >+/- 50 milliseconds
74 * * >+/- 5 milliseconds . >+/- 1 millisecond
75 * A-H less than 1 millisecond. Character indicates which station
76 * is being received as follows:
77 * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
78 * E = La Reunion, F = Argentina, G = Australia, H = Japan.
80 * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
82 * Notes on 468-DC and OMEGA receiver:
84 * Send the clock a 'R' or 'C' and once per second a timestamp will
85 * appear. Send a 'P' to get the satellite position once (GOES only.)
87 * Notes on the 468-DC receiver:
89 * Since the old east/west satellite locations are only historical, you can't
90 * set your clock propagation delay settings correctly and still use
91 * automatic mode. The manual says to use a compromise when setting the
92 * switches. This results in significant errors. The solution; use fudge
93 * time1 and time2 to incorporate corrections. If your clock is set for
94 * 50 and it should be 58 for using the west and 46 for using the east,
97 * fudge 127.127.5.0 time1 +0.008 time2 -0.004
99 * This corrects the 4 milliseconds advance and 8 milliseconds retard
100 * needed. The software will ask the clock which satellite it sees.
102 * Ntp.conf parameters:
103 * time1 - offset applied to samples when reading WEST satellite (default = 0)
104 * time2 - offset applied to samples when reading EAST satellite (default = 0)
105 * val1 - stratum to assign to this clock (default = 0)
106 * val2 - refid assigned to this clock (default = "TRUE", see below)
107 * flag1 - will silence the clock side of ntpd, just reading the clock
108 * without trying to write to it. (default = 0)
109 * flag2 - generate a debug file /tmp/true%d.
110 * flag3 - enable ppsclock streams module
111 * flag4 - use the PCL-720 (BSD/OS only)
118 #define DEVICE "/dev/true%d"
119 #define SPEED232 B9600 /* 9600 baud */
122 * Radio interface parameters
124 #define PRECISION (-10) /* precision assumed (about 1 ms) */
125 #define REFID "TRUE" /* reference id */
126 #define DESCRIPTION "Kinemetrics/TrueTime Receiver"
129 * Tags which station (satellite) we see
131 #define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
132 #define GOES_EAST 1 /* until you discover otherwise */
135 * used by the state machine
137 enum true_event
{e_Init
, e_Huh
, e_F18
, e_F50
, e_F51
, e_Satellite
,
138 e_Poll
, e_Location
, e_TS
, e_Max
};
139 const char *events
[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
140 "Poll", "Location", "TS"};
141 #define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
143 enum true_state
{s_Base
, s_InqTM
, s_InqTCU
, s_InqOmega
, s_InqGOES
,
144 s_Init
, s_F18
, s_F50
, s_Start
, s_Auto
, s_Max
};
145 const char *states
[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
146 "Init", "F18", "F50", "Start", "Auto"};
147 #define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
149 enum true_type
{t_unknown
, t_goes
, t_tm
, t_tcu
, t_omega
, t_Max
};
150 const char *types
[] = {"unknown", "goes", "tm", "tcu", "omega"};
151 #define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
154 * unit control structure
157 unsigned int pollcnt
; /* poll message counter */
158 unsigned int station
; /* which station we are on */
159 unsigned int polled
; /* Hand in a time sample? */
160 enum true_state state
; /* state machine */
161 enum true_type type
; /* what kind of clock is it? */
162 int unit
; /* save an extra copy of this */
163 FILE *debug
; /* debug logging file */
165 int pcl720init
; /* init flag for PCL 720 */
170 * Function prototypes
172 static int true_start
P((int, struct peer
*));
173 static void true_shutdown
P((int, struct peer
*));
174 static void true_receive
P((struct recvbuf
*));
175 static void true_poll
P((int, struct peer
*));
176 static void true_send
P((struct peer
*, const char *));
177 static void true_doevent
P((struct peer
*, enum true_event
));
180 static u_long true_sample720
P((void));
186 struct refclock refclock_true
= {
187 true_start
, /* start up driver */
188 true_shutdown
, /* shut down driver */
189 true_poll
, /* transmit poll message */
190 noentry
, /* not used (old true_control) */
191 noentry
, /* initialize driver (not used) */
192 noentry
, /* not used (old true_buginfo) */
193 NOFLAGS
/* not used */
197 #if !defined(__STDC__)
198 # define true_debug (void)
201 true_debug(struct peer
*peer
, const char *fmt
, ...)
204 int want_debugging
, now_debugging
;
205 struct refclockproc
*pp
;
206 struct true_unit
*up
;
210 up
= (struct true_unit
*)pp
->unitptr
;
212 want_debugging
= (pp
->sloppyclockflag
& CLK_FLAG2
) != 0;
213 now_debugging
= (up
->debug
!= NULL
);
214 if (want_debugging
!= now_debugging
)
216 if (want_debugging
) {
220 snprintf(filename
, sizeof(filename
), "/tmp/true%d.debug", up
->unit
);
221 fd
= open(filename
, O_CREAT
| O_WRONLY
| O_EXCL
, 0600);
222 if (fd
>= 0 && (up
->debug
= fdopen(fd
, "r+"))) {
224 static char buf
[BUFSIZ
];
225 setvbuf(up
->debug
, buf
, _IOLBF
, BUFSIZ
);
227 setlinebuf(up
->debug
);
237 fprintf(up
->debug
, "true%d: ", up
->unit
);
238 vfprintf(up
->debug
, fmt
, ap
);
244 * true_start - open the devices and initialize data for processing
252 register struct true_unit
*up
;
253 struct refclockproc
*pp
;
260 (void)snprintf(device
, sizeof(device
), DEVICE
, unit
);
261 if (!(fd
= refclock_open(device
, SPEED232
, LDISC_CLK
)))
265 * Allocate and initialize unit structure
267 if (!(up
= (struct true_unit
*)
268 emalloc(sizeof(struct true_unit
)))) {
272 memset((char *)up
, 0, sizeof(struct true_unit
));
274 pp
->io
.clock_recv
= true_receive
;
275 pp
->io
.srcclock
= (caddr_t
)peer
;
278 if (!io_addclock(&pp
->io
)) {
283 pp
->unitptr
= (caddr_t
)up
;
286 * Initialize miscellaneous variables
288 peer
->precision
= PRECISION
;
289 pp
->clockdesc
= DESCRIPTION
;
290 memcpy((char *)&pp
->refid
, REFID
, 4);
292 up
->type
= t_unknown
;
294 true_doevent(peer
, e_Init
);
299 * true_shutdown - shut down the clock
307 register struct true_unit
*up
;
308 struct refclockproc
*pp
;
311 up
= (struct true_unit
*)pp
->unitptr
;
312 io_closeclock(&pp
->io
);
318 * true_receive - receive data from the serial interface on a clock
322 struct recvbuf
*rbufp
325 register struct true_unit
*up
;
326 struct refclockproc
*pp
;
331 int lat
, lon
, off
; /* GOES Satellite position */
332 /* Use these variable to hold data until we decide its worth keeping */
333 char rd_lastcode
[BMAX
];
338 * Get the clock this applies to and pointers to the data.
340 peer
= (struct peer
*)rbufp
->recv_srcclock
;
342 up
= (struct true_unit
*)pp
->unitptr
;
345 * Read clock output. Automatically handles STREAMS, CLKLDISC.
347 rd_lencode
= refclock_gtlin(rbufp
, rd_lastcode
, BMAX
, &rd_tmp
);
348 rd_lastcode
[rd_lencode
] = '\0';
351 * There is a case where <cr><lf> generates 2 timestamps.
355 pp
->lencode
= rd_lencode
;
356 strcpy(pp
->a_lastcode
, rd_lastcode
);
357 pp
->lastrec
= rd_tmp
;
358 true_debug(peer
, "receive(%s) [%d]\n", pp
->a_lastcode
, pp
->lencode
);
361 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
364 * We get down to business, check the timecode format and decode
365 * its contents. This code decodes a multitude of different
366 * clock messages. Timecodes are processed if needed. All replies
367 * will be run through the state machine to tweak driver options
368 * and program the clock.
372 * Clock misunderstood our last command?
374 if (pp
->a_lastcode
[0] == '?' ||
375 strcmp(pp
->a_lastcode
, "ERROR 05 NO SUCH FUNCTION") == 0) {
376 true_doevent(peer
, e_Huh
);
381 * Timecode: "nnnnn+nnn-nnn"
382 * (from GOES clock when asked about satellite position)
384 if ((pp
->a_lastcode
[5] == '+' || pp
->a_lastcode
[5] == '-') &&
385 (pp
->a_lastcode
[9] == '+' || pp
->a_lastcode
[9] == '-') &&
386 sscanf(pp
->a_lastcode
, "%5d%*c%3d%*c%3d", &lon
, &lat
, &off
) == 3
388 const char *label
= "Botch!";
391 * This is less than perfect. Call the (satellite)
392 * either EAST or WEST and adjust slop accodingly
393 * Perfectionists would recalculate the exact delay
394 * and adjust accordingly...
396 if (lon
> 7000 && lon
< 14000) {
398 new_station
= GOES_EAST
;
401 new_station
= GOES_WEST
;
405 if (new_station
!= up
->station
) {
408 dtemp
= pp
->fudgetime1
;
409 pp
->fudgetime1
= pp
->fudgetime2
;
410 pp
->fudgetime2
= dtemp
;
411 up
->station
= new_station
;
415 /*refclock_report(peer, CEVNT_BADREPLY);*/
418 true_debug(peer
, "GOES: station %s\n", label
);
419 true_doevent(peer
, e_Satellite
);
425 * (from TM/TMD clock when it wants to tell us what it's up to.)
427 if (sscanf(pp
->a_lastcode
, "F%2d", &i
) == 1 && i
> 0 && i
< 80) {
430 true_doevent(peer
, e_F50
);
433 true_doevent(peer
, e_F51
);
436 true_debug(peer
, "got F%02d - ignoring\n", i
);
443 * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
444 * (from a TM/TMD/XL clock during initialization.)
446 if (strcmp(pp
->a_lastcode
, " TRUETIME Mk III") == 0 ||
447 strncmp(pp
->a_lastcode
, " TRUETIME XL", 12) == 0) {
448 true_doevent(peer
, e_F18
);
449 NLOG(NLOG_CLOCKSTATUS
) {
450 msyslog(LOG_INFO
, "TM/TMD/XL: %s", pp
->a_lastcode
);
456 * Timecode: "N03726428W12209421+000033"
458 * 0123456789012345678901234
459 * (from a TCU during initialization)
461 if ((pp
->a_lastcode
[0] == 'N' || pp
->a_lastcode
[0] == 'S') &&
462 (pp
->a_lastcode
[9] == 'W' || pp
->a_lastcode
[9] == 'E') &&
463 pp
->a_lastcode
[18] == '+') {
464 true_doevent(peer
, e_Location
);
465 NLOG(NLOG_CLOCKSTATUS
) {
466 msyslog(LOG_INFO
, "TCU-800: %s", pp
->a_lastcode
);
471 * Timecode: "ddd:hh:mm:ssQ"
472 * (from all clocks supported by this driver.)
474 if (pp
->a_lastcode
[3] == ':' &&
475 pp
->a_lastcode
[6] == ':' &&
476 pp
->a_lastcode
[9] == ':' &&
477 sscanf(pp
->a_lastcode
, "%3d:%2d:%2d:%2d%c",
478 &pp
->day
, &pp
->hour
, &pp
->minute
,
479 &pp
->second
, &synced
) == 5) {
482 * Adjust the synchronize indicator according to timecode
483 * say were OK, and then say not if we really are not OK
485 if (synced
== '>' || synced
== '#' || synced
== '?')
486 pp
->leap
= LEAP_NOTINSYNC
;
488 pp
->leap
= LEAP_NOWARNING
;
490 true_doevent(peer
, e_TS
);
493 /* If it's taken more than 65ms to get here, we'll lose. */
494 if ((pp
->sloppyclockflag
& CLK_FLAG4
) && up
->pcl720init
) {
499 * find out what time it really is. Include
500 * the count from the PCL720
502 if (!clocktime(pp
->day
, pp
->hour
, pp
->minute
,
503 pp
->second
, GMT
, pp
->lastrec
.l_ui
,
504 &pp
->yearstart
, &off
.l_ui
)) {
505 refclock_report(peer
, CEVNT_BADTIME
);
511 pp
->usec
= true_sample720();
513 TVUTOTSF(pp
->usec
, off
.l_uf
);
517 * Stomp all over the timestamp that was pulled out
518 * of the input stream. It's irrelevant since we've
519 * adjusted the input time to reflect now (via pp->usec)
520 * rather than when the data was collected.
522 get_systime(&pp
->lastrec
);
525 * Create a true offset for feeding to pps_sample()
527 L_SUB(&off
, &pp
->lastrec
);
529 pps_sample(peer
, &off
);
531 true_debug(peer
, "true_sample720: %luus\n", pp
->usec
);
536 * The clock will blurt a timecode every second but we only
537 * want one when polled. If we havn't been polled, bail out.
542 true_doevent(peer
, e_Poll
);
543 if (!refclock_process(pp
)) {
544 refclock_report(peer
, CEVNT_BADTIME
);
548 * If clock is good we send a NOMINAL message so that
549 * any previous BAD messages are nullified
551 pp
->lastref
= pp
->lastrec
;
552 refclock_receive(peer
);
553 refclock_report(peer
, CEVNT_NOMINAL
);
556 * We have succedded in answering the poll.
557 * Turn off the flag and return
565 * No match to known timecodes, report failure and return
567 refclock_report(peer
, CEVNT_BADREPLY
);
573 * true_send - time to send the clock a signal to cough up a time sample
581 struct refclockproc
*pp
;
584 if (!(pp
->sloppyclockflag
& CLK_FLAG1
)) {
585 register int len
= strlen(cmd
);
587 true_debug(peer
, "Send '%s'\n", cmd
);
588 if (write(pp
->io
.fd
, cmd
, (unsigned)len
) != len
)
589 refclock_report(peer
, CEVNT_FAULT
);
597 * state machine for initializing and controlling a clock
602 enum true_event event
605 struct true_unit
*up
;
606 struct refclockproc
*pp
;
609 up
= (struct true_unit
*)pp
->unitptr
;
611 NLOG(NLOG_CLOCKSTATUS
) {
612 msyslog(LOG_INFO
, "TRUE: clock %s, state %s, event %s",
618 true_debug(peer
, "clock %s, state %s, event %s\n",
619 typeStr(up
->type
), stateStr(up
->state
), eventStr(event
));
623 case e_Init
: /* FALLTHROUGH */
626 * Switch back to on-second time codes and return.
628 true_send(peer
, "C");
633 * After each poll, check the station (satellite).
635 true_send(peer
, "P");
636 /* No state change needed. */
645 true_send(peer
, "C");
649 if (up
->state
!= s_Start
&& up
->state
!= s_Auto
) {
650 true_send(peer
, "\03\r");
662 true_send(peer
, "F18\r");
666 true_send(peer
, "F50\r");
670 true_send(peer
, "F51\r");
674 true_send(peer
, "F08\r");
678 if (up
->state
!= s_Start
&& up
->state
!= s_Auto
) {
679 true_send(peer
, "\03\r");
691 true_send(peer
, "MD3\r"); /* GPS Synch'd Gen. */
692 true_send(peer
, "TSU\r"); /* UTC, not GPS. */
693 true_send(peer
, "AU\r"); /* Auto Timestamps. */
697 if (up
->state
!= s_Start
&& up
->state
!= s_Auto
) {
698 true_send(peer
, "\03\r");
712 true_send(peer
, "P\r");
713 up
->state
= s_InqGOES
;
719 true_doevent(peer
, e_Init
);
721 case e_Init
: /*FALLTHROUGH*/
722 case e_Huh
: /*FALLTHROUGH*/
724 up
->state
= s_InqOmega
;
725 true_send(peer
, "C\r");
735 up
->state
= s_Auto
; /* Inq side-effect. */
737 case e_Init
: /*FALLTHROUGH*/
740 true_send(peer
, "F18\r");
750 true_doevent(peer
, e_Init
);
752 case e_Init
: /*FALLTHROUGH*/
754 true_send(peer
, "PO\r");
755 up
->state
= s_InqTCU
;
765 true_doevent(peer
, e_Init
);
767 case e_Init
: /*FALLTHROUGH*/
777 * An expedient hack to prevent lint complaints,
778 * these don't actually need to be used here...
786 msyslog(LOG_INFO
, "TRUE: state %s is unexpected!", stateStr(up
->state
));
795 if ((pp
->sloppyclockflag
& CLK_FLAG4
) && !up
->pcl720init
) {
796 /* Make counter trigger on gate0, count down from 65535. */
797 pcl720_load(PCL720_IOB
, PCL720_CTR
, i8253_oneshot
, 65535);
799 * (These constants are OK since
800 * they represent hardware maximums.)
802 NLOG(NLOG_CLOCKINFO
) {
803 msyslog(LOG_NOTICE
, "PCL-720 initialized");
813 * true_poll - called by the transmit procedure
821 struct true_unit
*up
;
822 struct refclockproc
*pp
;
825 * You don't need to poll this clock. It puts out timecodes
826 * once per second. If asked for a timestamp, take note.
827 * The next time a timecode comes in, it will be fed back.
830 up
= (struct true_unit
*)pp
->unitptr
;
834 true_doevent(peer
, e_Init
);
835 refclock_report(peer
, CEVNT_TIMEOUT
);
839 * polled every 64 seconds. Ask true_receive to hand in a
848 * true_sample720 - sample the PCL-720
855 /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
856 * If it is not being held low now, we did not get called
859 if (inb(pcl720_data_16_23(PCL720_IOB
)) & 0x01) {
860 NLOG(NLOG_CLOCKINFO
) {
861 msyslog(LOG_NOTICE
, "PCL-720 out of synch");
865 f
= (65536 - pcl720_read(PCL720_IOB
, PCL720_CTR
));
867 msyslog(LOG_DEBUG
, "PCL-720: %luus", f
);
874 int refclock_true_bs
;
875 #endif /* REFCLOCK */