Obfuscate RCS ID matching so that CVS doesn't expand it.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_trak.c
blobc078b2e848d85963eb1b90b9499e5e66e7e9f6bb
1 /* $NetBSD$ */
3 /*
4 * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
6 * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
7 * original version Dec, 1993
8 * revised version Sep, 1994 for ntp3.4e or later
9 */
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
15 #if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_refclock.h"
20 #include "ntp_stdlib.h"
21 #include "ntp_unixtime.h"
23 #include <stdio.h>
24 #include <ctype.h>
26 #ifdef HAVE_SYS_TERMIOS_H
27 # include <sys/termios.h>
28 #endif
29 #ifdef HAVE_SYS_PPSCLOCK_H
30 # include <sys/ppsclock.h>
31 #endif
34 * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
35 * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
36 * signal; however, in most cases the actual accuracy is limited by the
37 * precision of the timecode and the latencies of the serial interface
38 * and operating system.
40 * For best accuracy, this radio requires the LDISC_ACTS line
41 * discipline, which captures a timestamp at the '*' on-time character
42 * of the timecode. Using this discipline the jitter is in the order of
43 * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
44 * timestamp is used, which is captured at the \r ending the timecode
45 * message. This introduces a systematic error of 23 character times, or
46 * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
47 * IPC-class machines.
49 * Using the memus, the radio should be set for 9600 bps, one stop bit
50 * and no parity. It should be set to operate in computer (no echo)
51 * mode. The timecode format includes neither the year nor leap-second
52 * warning. No provisions are included in this preliminary version of
53 * the driver to read and record detailed internal radio status.
55 * In operation, this driver sends a RQTS\r request to the radio at
56 * initialization in order to put it in continuous time output mode. The
57 * radio then sends the following message once each second:
59 * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
61 * on-time = '*'\v * ddd = day of year
62 * hh:mm:ss = hours, minutes, seconds
63 * q = quality indicator (phase error), 0-6:
64 * 0 > 20 us
65 * 6 > 10 us
66 * 5 > 1 us
67 * 4 > 100 ns
68 * 3 > 10 ns
69 * 2 < 10 ns
71 * The alarm condition is indicated by '0' at Q, which means the radio
72 * has a phase error than 20 usec relative to the broadcast time. The
73 * absence of year, DST and leap-second warning in this format is also
74 * alarming.
76 * The continuous time mode is disabled using the RQTX<cr> request,
77 * following which the radio sends a RQTX DONE<cr><lf> response. In the
78 * normal mode, other control and status requests are effective,
79 * including the leap-second status request RQLS<cr>. The radio responds
80 * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
81 * day. Presumably, this gives the epoch of the next leap second,
82 * RQLS 00,00,00 if none is specified in the GPS message. Specified in
83 * this form, the information is generally useless and is ignored by
84 * the driver.
86 * Fudge Factors
88 * There are no special fudge factors other than the generic.
92 * Interface definitions
94 #define DEVICE "/dev/trak%d" /* device name and unit */
95 #define SPEED232 B9600 /* uart speed (9600 baud) */
96 #define PRECISION (-20) /* precision assumed (about 1 us) */
97 #define REFID "GPS\0" /* reference ID */
98 #define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
100 #define LENTRAK 24 /* timecode length */
101 #define C_CTO "RQTS\r" /* start continuous time output */
104 * Unit control structure
106 struct trakunit {
107 int polled; /* poll message flag */
108 l_fp tstamp; /* timestamp of last poll */
112 * Function prototypes
114 static int trak_start P((int, struct peer *));
115 static void trak_shutdown P((int, struct peer *));
116 static void trak_receive P((struct recvbuf *));
117 static void trak_poll P((int, struct peer *));
120 * Transfer vector
122 struct refclock refclock_trak = {
123 trak_start, /* start up driver */
124 trak_shutdown, /* shut down driver */
125 trak_poll, /* transmit poll message */
126 noentry, /* not used (old trak_control) */
127 noentry, /* initialize driver (not used) */
128 noentry, /* not used (old trak_buginfo) */
129 NOFLAGS /* not used */
134 * trak_start - open the devices and initialize data for processing
136 static int
137 trak_start(
138 int unit,
139 struct peer *peer
142 register struct trakunit *up;
143 struct refclockproc *pp;
144 int fd;
145 char device[20];
148 * Open serial port. The LDISC_ACTS line discipline inserts a
149 * timestamp following the "*" on-time character of the
150 * timecode.
152 (void)sprintf(device, DEVICE, unit);
153 if (
154 #ifdef PPS
155 !(fd = refclock_open(device, SPEED232, LDISC_CLK))
156 #else
157 !(fd = refclock_open(device, SPEED232, 0))
158 #endif /* PPS */
160 return (0);
163 * Allocate and initialize unit structure
165 if (!(up = (struct trakunit *)
166 emalloc(sizeof(struct trakunit)))) {
167 (void) close(fd);
168 return (0);
170 memset((char *)up, 0, sizeof(struct trakunit));
171 pp = peer->procptr;
172 pp->io.clock_recv = trak_receive;
173 pp->io.srcclock = (caddr_t)peer;
174 pp->io.datalen = 0;
175 pp->io.fd = fd;
176 if (!io_addclock(&pp->io)) {
177 (void) close(fd);
178 free(up);
179 return (0);
181 pp->unitptr = (caddr_t)up;
184 * Initialize miscellaneous variables
186 peer->precision = PRECISION;
187 pp->clockdesc = DESCRIPTION;
188 memcpy((char *)&pp->refid, REFID, 4);
189 up->polled = 0;
192 * Start continuous time output. If something breaks, fold the
193 * tent and go home.
195 if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
196 refclock_report(peer, CEVNT_FAULT);
197 (void) close(fd);
198 free(up);
199 return (0);
201 return (1);
206 * trak_shutdown - shut down the clock
208 static void
209 trak_shutdown(
210 int unit,
211 struct peer *peer
214 register struct trakunit *up;
215 struct refclockproc *pp;
217 pp = peer->procptr;
218 up = (struct trakunit *)pp->unitptr;
219 io_closeclock(&pp->io);
220 free(up);
225 * trak_receive - receive data from the serial interface
227 static void
228 trak_receive(
229 struct recvbuf *rbufp
232 register struct trakunit *up;
233 struct refclockproc *pp;
234 struct peer *peer;
235 l_fp trtmp;
236 char *dpt, *dpend;
237 char qchar;
238 #ifdef PPS
239 struct ppsclockev ppsev;
240 int request;
241 #ifdef HAVE_CIOGETEV
242 request = CIOGETEV;
243 #endif
244 #ifdef HAVE_TIOCGPPSEV
245 request = TIOCGPPSEV;
246 #endif
247 #endif /* PPS */
250 * Initialize pointers and read the timecode and timestamp. We
251 * then chuck out everything, including runts, except one
252 * message each poll interval.
254 peer = (struct peer *)rbufp->recv_srcclock;
255 pp = peer->procptr;
256 up = (struct trakunit *)pp->unitptr;
257 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
258 &pp->lastrec);
261 * We get a buffer and timestamp following the '*' on-time
262 * character. If a valid timestamp, we use that in place of the
263 * buffer timestamp and edit out the timestamp for prettyprint
264 * billboards.
266 dpt = pp->a_lastcode;
267 dpend = dpt + pp->lencode;
268 if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
269 if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
270 pp->lastrec.l_i + 1) {
271 pp->lastrec = trtmp;
272 dpt += 9;
273 while (dpt < dpend) {
274 *(dpt - 8) = *dpt;
275 ++dpt;
279 if (up->polled == 0) return;
280 up->polled = 0;
281 #ifndef PPS
282 get_systime(&up->tstamp);
283 #endif
284 record_clock_stats(&peer->srcadr, pp->a_lastcode);
285 #ifdef DEBUG
286 if (debug)
287 printf("trak: timecode %d %s\n", pp->lencode,
288 pp->a_lastcode);
289 #endif
292 * We get down to business, check the timecode format and decode
293 * its contents. If the timecode has invalid length or is not in
294 * proper format, we declare bad format and exit.
296 if (pp->lencode < LENTRAK) {
297 refclock_report(peer, CEVNT_BADREPLY);
298 return;
302 * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
304 if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
305 &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
306 refclock_report(peer, CEVNT_BADREPLY);
307 return;
311 * Decode quality and leap characters. If unsynchronized, set
312 * the leap bits accordingly and exit.
314 if (qchar == '0') {
315 pp->leap = LEAP_NOTINSYNC;
316 return;
318 #ifdef PPS
319 if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
320 ppsev.tv.tv_sec += (u_int32) JAN_1970;
321 TVTOTS(&ppsev.tv,&up->tstamp);
323 #endif /* PPS */
324 /* record the last ppsclock event time stamp */
325 pp->lastrec = up->tstamp;
326 if (!refclock_process(pp)) {
327 refclock_report(peer, CEVNT_BADTIME);
328 return;
330 pp->lastref = pp->lastrec;
331 refclock_receive(peer);
336 * trak_poll - called by the transmit procedure
338 static void
339 trak_poll(
340 int unit,
341 struct peer *peer
344 register struct trakunit *up;
345 struct refclockproc *pp;
348 * We don't really do anything here, except arm the receiving
349 * side to capture a sample and check for timeouts.
351 pp = peer->procptr;
352 up = (struct trakunit *)pp->unitptr;
353 if (up->polled)
354 refclock_report(peer, CEVNT_TIMEOUT);
355 pp->polls++;
356 up->polled = 1;
359 #else
360 int refclock_trak_bs;
361 #endif /* REFCLOCK */