Annotation of embedaddon/ntp/ntpd/refclock_trak.c, revision 1.1.1.1
1.1 misho 1: /*
2: * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
3: *
4: * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
5: * original version Dec, 1993
6: * revised version Sep, 1994 for ntp3.4e or later
7: */
8:
9: #ifdef HAVE_CONFIG_H
10: #include <config.h>
11: #endif
12:
13: #if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
14:
15: #include "ntpd.h"
16: #include "ntp_io.h"
17: #include "ntp_refclock.h"
18: #include "ntp_stdlib.h"
19: #include "ntp_unixtime.h"
20:
21: #include <stdio.h>
22: #include <ctype.h>
23:
24: #ifdef HAVE_SYS_TERMIOS_H
25: # include <sys/termios.h>
26: #endif
27: #ifdef HAVE_SYS_PPSCLOCK_H
28: # include <sys/ppsclock.h>
29: #endif
30:
31: /*
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.
37: *
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
45: * IPC-class machines.
46: *
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.
52: *
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:
56: *
57: * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
58: *
59: * on-time = '*' * ddd = day of year
60: * hh:mm:ss = hours, minutes, seconds
61: * q = quality indicator (phase error), 0-6:
62: * 0 > 20 us
63: * 6 > 10 us
64: * 5 > 1 us
65: * 4 > 100 ns
66: * 3 > 10 ns
67: * 2 < 10 ns
68: *
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
72: * alarming.
73: *
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
82: * the driver.
83: *
84: * Fudge Factors
85: *
86: * There are no special fudge factors other than the generic.
87: */
88:
89: /*
90: * Interface definitions
91: */
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 */
97:
98: #define LENTRAK 24 /* timecode length */
99: #define C_CTO "RQTS\r" /* start continuous time output */
100:
101: /*
102: * Unit control structure
103: */
104: struct trakunit {
105: int polled; /* poll message flag */
106: l_fp tstamp; /* timestamp of last poll */
107: };
108:
109: /*
110: * Function prototypes
111: */
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 *));
116:
117: /*
118: * Transfer vector
119: */
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 */
128: };
129:
130:
131: /*
132: * trak_start - open the devices and initialize data for processing
133: */
134: static int
135: trak_start(
136: int unit,
137: struct peer *peer
138: )
139: {
140: register struct trakunit *up;
141: struct refclockproc *pp;
142: int fd;
143: char device[20];
144:
145: /*
146: * Open serial port. The LDISC_ACTS line discipline inserts a
147: * timestamp following the "*" on-time character of the
148: * timecode.
149: */
150: snprintf(device, sizeof(device), DEVICE, unit);
151: if (
152: #ifdef PPS
153: !(fd = refclock_open(device, SPEED232, LDISC_CLK))
154: #else
155: !(fd = refclock_open(device, SPEED232, 0))
156: #endif /* PPS */
157: )
158: return (0);
159:
160: /*
161: * Allocate and initialize unit structure
162: */
163: up = emalloc(sizeof(*up));
164: memset(up, 0, sizeof(*up));
165: pp = peer->procptr;
166: pp->io.clock_recv = trak_receive;
167: pp->io.srcclock = (caddr_t)peer;
168: pp->io.datalen = 0;
169: pp->io.fd = fd;
170: if (!io_addclock(&pp->io)) {
171: (void) close(fd);
172: pp->io.fd = -1;
173: free(up);
174: return (0);
175: }
176: pp->unitptr = (caddr_t)up;
177:
178: /*
179: * Initialize miscellaneous variables
180: */
181: peer->precision = PRECISION;
182: pp->clockdesc = DESCRIPTION;
183: memcpy((char *)&pp->refid, REFID, 4);
184: up->polled = 0;
185:
186: /*
187: * Start continuous time output. If something breaks, fold the
188: * tent and go home.
189: */
190: if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
191: refclock_report(peer, CEVNT_FAULT);
192: (void) close(fd);
193: free(up);
194: return (0);
195: }
196: return (1);
197: }
198:
199:
200: /*
201: * trak_shutdown - shut down the clock
202: */
203: static void
204: trak_shutdown(
205: int unit,
206: struct peer *peer
207: )
208: {
209: register struct trakunit *up;
210: struct refclockproc *pp;
211:
212: pp = peer->procptr;
213: up = (struct trakunit *)pp->unitptr;
214: if (-1 != pp->io.fd)
215: io_closeclock(&pp->io);
216: if (NULL != up)
217: free(up);
218: }
219:
220:
221: /*
222: * trak_receive - receive data from the serial interface
223: */
224: static void
225: trak_receive(
226: struct recvbuf *rbufp
227: )
228: {
229: register struct trakunit *up;
230: struct refclockproc *pp;
231: struct peer *peer;
232: l_fp trtmp;
233: char *dpt, *dpend;
234: char qchar;
235: #ifdef PPS
236: struct ppsclockev ppsev;
237: int request;
238: #ifdef HAVE_CIOGETEV
239: request = CIOGETEV;
240: #endif
241: #ifdef HAVE_TIOCGPPSEV
242: request = TIOCGPPSEV;
243: #endif
244: #endif /* PPS */
245:
246: /*
247: * Initialize pointers and read the timecode and timestamp. We
248: * then chuck out everything, including runts, except one
249: * message each poll interval.
250: */
251: peer = (struct peer *)rbufp->recv_srcclock;
252: pp = peer->procptr;
253: up = (struct trakunit *)pp->unitptr;
254: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
255: &pp->lastrec);
256:
257: /*
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
261: * billboards.
262: */
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) {
268: pp->lastrec = trtmp;
269: dpt += 9;
270: while (dpt < dpend) {
271: *(dpt - 8) = *dpt;
272: ++dpt;
273: }
274: }
275: }
276: if (up->polled == 0) return;
277: up->polled = 0;
278: #ifndef PPS
279: get_systime(&up->tstamp);
280: #endif
281: record_clock_stats(&peer->srcadr, pp->a_lastcode);
282: #ifdef DEBUG
283: if (debug)
284: printf("trak: timecode %d %s\n", pp->lencode,
285: pp->a_lastcode);
286: #endif
287:
288: /*
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.
292: */
293: if (pp->lencode < LENTRAK) {
294: refclock_report(peer, CEVNT_BADREPLY);
295: return;
296: }
297:
298: /*
299: * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
300: */
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);
304: return;
305: }
306:
307: /*
308: * Decode quality and leap characters. If unsynchronized, set
309: * the leap bits accordingly and exit.
310: */
311: if (qchar == '0') {
312: pp->leap = LEAP_NOTINSYNC;
313: return;
314: }
315: #ifdef PPS
316: if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
317: ppsev.tv.tv_sec += (u_int32) JAN_1970;
318: TVTOTS(&ppsev.tv,&up->tstamp);
319: }
320: #endif /* PPS */
321: /* record the last ppsclock event time stamp */
322: pp->lastrec = up->tstamp;
323: if (!refclock_process(pp)) {
324: refclock_report(peer, CEVNT_BADTIME);
325: return;
326: }
327: pp->lastref = pp->lastrec;
328: refclock_receive(peer);
329: }
330:
331:
332: /*
333: * trak_poll - called by the transmit procedure
334: */
335: static void
336: trak_poll(
337: int unit,
338: struct peer *peer
339: )
340: {
341: register struct trakunit *up;
342: struct refclockproc *pp;
343:
344: /*
345: * We don't really do anything here, except arm the receiving
346: * side to capture a sample and check for timeouts.
347: */
348: pp = peer->procptr;
349: up = (struct trakunit *)pp->unitptr;
350: if (up->polled)
351: refclock_report(peer, CEVNT_TIMEOUT);
352: pp->polls++;
353: up->polled = 1;
354: }
355:
356: #else
357: int refclock_trak_bs;
358: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>