2 * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
4 * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
5 * original version Dec, 1993
6 * revised version Sep, 1994 for ntp3.4e or later
13 #if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
17 #include "ntp_refclock.h"
18 #include "ntp_stdlib.h"
19 #include "ntp_unixtime.h"
24 #ifdef HAVE_SYS_TERMIOS_H
25 # include <sys/termios.h>
27 #ifdef HAVE_SYS_PPSCLOCK_H
28 # include <sys/ppsclock.h>
32 * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
33 * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
34 * signal; however, in most cases the actual accuracy is limited by the
35 * precision of the timecode and the latencies of the serial interface
36 * and operating system.
38 * For best accuracy, this radio requires the LDISC_ACTS line
39 * discipline, which captures a timestamp at the '*' on-time character
40 * of the timecode. Using this discipline the jitter is in the order of
41 * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
42 * timestamp is used, which is captured at the \r ending the timecode
43 * message. This introduces a systematic error of 23 character times, or
44 * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
47 * Using the memus, the radio should be set for 9600 bps, one stop bit
48 * and no parity. It should be set to operate in computer (no echo)
49 * mode. The timecode format includes neither the year nor leap-second
50 * warning. No provisions are included in this preliminary version of
51 * the driver to read and record detailed internal radio status.
53 * In operation, this driver sends a RQTS\r request to the radio at
54 * initialization in order to put it in continuous time output mode. The
55 * radio then sends the following message once each second:
57 * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
59 * on-time = '*'
\v * ddd = day of year
60 * hh:mm:ss = hours, minutes, seconds
61 * q = quality indicator (phase error), 0-6:
69 * The alarm condition is indicated by '0' at Q, which means the radio
70 * has a phase error than 20 usec relative to the broadcast time. The
71 * absence of year, DST and leap-second warning in this format is also
74 * The continuous time mode is disabled using the RQTX<cr> request,
75 * following which the radio sends a RQTX DONE<cr><lf> response. In the
76 * normal mode, other control and status requests are effective,
77 * including the leap-second status request RQLS<cr>. The radio responds
78 * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
79 * day. Presumably, this gives the epoch of the next leap second,
80 * RQLS 00,00,00 if none is specified in the GPS message. Specified in
81 * this form, the information is generally useless and is ignored by
86 * There are no special fudge factors other than the generic.
90 * Interface definitions
92 #define DEVICE "/dev/trak%d" /* device name and unit */
93 #define SPEED232 B9600 /* uart speed (9600 baud) */
94 #define PRECISION (-20) /* precision assumed (about 1 us) */
95 #define REFID "GPS\0" /* reference ID */
96 #define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
98 #define LENTRAK 24 /* timecode length */
99 #define C_CTO "RQTS\r" /* start continuous time output */
102 * Unit control structure
105 int polled; /* poll message flag */
106 l_fp tstamp; /* timestamp of last poll */
110 * Function prototypes
112 static int trak_start P((int, struct peer *));
113 static void trak_shutdown P((int, struct peer *));
114 static void trak_receive P((struct recvbuf *));
115 static void trak_poll P((int, struct peer *));
120 struct refclock refclock_trak = {
121 trak_start, /* start up driver */
122 trak_shutdown, /* shut down driver */
123 trak_poll, /* transmit poll message */
124 noentry, /* not used (old trak_control) */
125 noentry, /* initialize driver (not used) */
126 noentry, /* not used (old trak_buginfo) */
127 NOFLAGS /* not used */
132 * trak_start - open the devices and initialize data for processing
140 register struct trakunit *up;
141 struct refclockproc *pp;
146 * Open serial port. The LDISC_ACTS line discipline inserts a
147 * timestamp following the "*" on-time character of the
150 snprintf(device, sizeof(device), DEVICE, unit);
153 !(fd = refclock_open(device, SPEED232, LDISC_CLK))
155 !(fd = refclock_open(device, SPEED232, 0))
161 * Allocate and initialize unit structure
163 up = emalloc(sizeof(*up));
164 memset(up, 0, sizeof(*up));
166 pp->io.clock_recv = trak_receive;
167 pp->io.srcclock = (caddr_t)peer;
170 if (!io_addclock(&pp->io)) {
176 pp->unitptr = (caddr_t)up;
179 * Initialize miscellaneous variables
181 peer->precision = PRECISION;
182 pp->clockdesc = DESCRIPTION;
183 memcpy((char *)&pp->refid, REFID, 4);
187 * Start continuous time output. If something breaks, fold the
190 if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
191 refclock_report(peer, CEVNT_FAULT);
201 * trak_shutdown - shut down the clock
209 register struct trakunit *up;
210 struct refclockproc *pp;
213 up = (struct trakunit *)pp->unitptr;
215 io_closeclock(&pp->io);
222 * trak_receive - receive data from the serial interface
226 struct recvbuf *rbufp
229 register struct trakunit *up;
230 struct refclockproc *pp;
236 struct ppsclockev ppsev;
241 #ifdef HAVE_TIOCGPPSEV
242 request = TIOCGPPSEV;
247 * Initialize pointers and read the timecode and timestamp. We
248 * then chuck out everything, including runts, except one
249 * message each poll interval.
251 peer = (struct peer *)rbufp->recv_srcclock;
253 up = (struct trakunit *)pp->unitptr;
254 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
258 * We get a buffer and timestamp following the '*' on-time
259 * character. If a valid timestamp, we use that in place of the
260 * buffer timestamp and edit out the timestamp for prettyprint
263 dpt = pp->a_lastcode;
264 dpend = dpt + pp->lencode;
265 if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
266 if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
267 pp->lastrec.l_i + 1) {
270 while (dpt < dpend) {
276 if (up->polled == 0) return;
279 get_systime(&up->tstamp);
281 record_clock_stats(&peer->srcadr, pp->a_lastcode);
284 printf("trak: timecode %d %s\n", pp->lencode,
289 * We get down to business, check the timecode format and decode
290 * its contents. If the timecode has invalid length or is not in
291 * proper format, we declare bad format and exit.
293 if (pp->lencode < LENTRAK) {
294 refclock_report(peer, CEVNT_BADREPLY);
299 * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
301 if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
302 &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
303 refclock_report(peer, CEVNT_BADREPLY);
308 * Decode quality and leap characters. If unsynchronized, set
309 * the leap bits accordingly and exit.
312 pp->leap = LEAP_NOTINSYNC;
316 if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
317 ppsev.tv.tv_sec += (u_int32) JAN_1970;
318 TVTOTS(&ppsev.tv,&up->tstamp);
321 /* record the last ppsclock event time stamp */
322 pp->lastrec = up->tstamp;
323 if (!refclock_process(pp)) {
324 refclock_report(peer, CEVNT_BADTIME);
327 pp->lastref = pp->lastrec;
328 refclock_receive(peer);
333 * trak_poll - called by the transmit procedure
341 register struct trakunit *up;
342 struct refclockproc *pp;
345 * We don't really do anything here, except arm the receiving
346 * side to capture a sample and check for timeouts.
349 up = (struct trakunit *)pp->unitptr;
351 refclock_report(peer, CEVNT_TIMEOUT);
357 int refclock_trak_bs;
358 #endif /* REFCLOCK */