source: trunk/third/xntp/xntpd/refclock_trak.c @ 10832

Revision 10832, 9.5 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10831, which included commits to RCS files with non-trunk default branches.
Line 
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 xntp3.4e or later
7 */
8
9#ifdef HAVE_CONFIG_H
10#include <config.h>
11#endif
12
13#if defined(REFCLOCK) && defined(TRAK)
14
15#include <stdio.h>
16#include <ctype.h>
17#include <sys/time.h>
18
19#include "ntpd.h"
20#include "ntp_io.h"
21#include "ntp_refclock.h"
22#include "ntp_stdlib.h"
23#include "ntp_unixtime.h"
24
25#ifdef PPS
26#include <sys/ppsclock.h>
27#endif /* PPS */
28
29/*
30 * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
31 * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
32 * signal; however, in most cases the actual accuracy is limited by the
33 * precision of the timecode and the latencies of the serial interface
34 * and operating system.
35 *
36 * For best accuracy, this radio requires the LDISC_ACTS line
37 * discipline, which captures a timestamp at the '*' on-time character
38 * of the timecode. Using this discipline the jitter is in the order of
39 * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
40 * timestamp is used, which is captured at the \r ending the timecode
41 * message. This introduces a systematic error of 23 character times, or
42 * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
43 * IPC-class machines.
44 *
45 * Using the memus, the radio should be set for 9600 bps, one stop bit
46 * and no parity. It should be set to operate in computer (no echo)
47 * mode. The timecode format includes neither the year nor leap-second
48 * warning. No provisions are included in this preliminary version of
49 * the driver to read and record detailed internal radio status.
50 *
51 * In operation, this driver sends a RQTS\r request to the radio at
52 * initialization in order to put it in continuous time output mode. The
53 * radio then sends the following message once each second:
54 *
55 *      *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
56 *
57 *      on-time = '*' *        ddd = day of year
58 *      hh:mm:ss = hours, minutes, seconds
59 *      q = quality indicator (phase error), 0-6:
60 *              0 > 20 us
61 *              6 > 10 us
62 *              5 > 1 us
63 *              4 > 100 ns
64 *              3 > 10 ns
65 *              2 < 10 ns
66 *
67 * The alarm condition is indicated by '0' at Q, which means the radio
68 * has a phase error than 20 usec relative to the broadcast time. The
69 * absence of year, DST and leap-second warning in this format is also
70 * alarming.
71 *
72 * The continuous time mode is disabled using the RQTX<cr> request,
73 * following which the radio sends a RQTX DONE<cr><lf> response. In the
74 * normal mode, other control and status requests are effective,
75 * including the leap-second status request RQLS<cr>. The radio responds
76 * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
77 * day. Presumably, this gives the epoch of the next leap second,
78 * RQLS 00,00,00 if none is specified in the GPS message. Specified in
79 * this form, the information is generally useless and is ignored by
80 * the driver.
81 *
82 * Fudge Factors
83 *
84 * There are no special fudge factors other than the generic.
85 */
86
87/*
88 * Interface definitions
89 */
90#define DEVICE          "/dev/trak%d" /* device name and unit */
91#define SPEED232        B9600   /* uart speed (9600 baud) */
92#define PRECISION       (-20)   /* precision assumed (about 1 us) */
93#define REFID           "GPS\0" /* reference ID */
94#define DESCRIPTION     "TRACK 8810/8820 Station Clock" /* WRU */
95
96#define NSAMPLES        3       /* stages of median filter */
97#define LENTRAK         24      /* timecode length */
98#define C_CTO           "RQTS\r" /* start continuous time output */
99
100/*
101 * Imported from ntp_timer module
102 */
103extern  u_long  current_time;   /* current time (s) */
104
105#ifdef PPS
106/*
107 * Imported from ntp_loopfilter module
108 */
109extern int fdpps;               /* pps file descriptor */
110#endif /* PPS */
111
112/*
113 * Imported from ntpd module
114 */
115extern  int     debug;          /* global debug flag */
116
117/*
118 * Unit control structure
119 */
120struct trakunit {
121        int     polled;         /* poll message flag */
122        l_fp    tstamp;         /* timestamp of last poll */
123};
124
125/*
126 * Function prototypes
127 */
128static  int     trak_start      P((int, struct peer *));
129static  void    trak_shutdown   P((int, struct peer *));
130static  void    trak_receive    P((struct recvbuf *));
131static  void    trak_poll       P((int, struct peer *));
132
133/*
134 * Transfer vector
135 */
136struct  refclock refclock_trak = {
137        trak_start,             /* start up driver */
138        trak_shutdown,          /* shut down driver */
139        trak_poll,              /* transmit poll message */
140        noentry,                /* not used (old trak_control) */
141        noentry,                /* initialize driver (not used) */
142        noentry,                /* not used (old trak_buginfo) */
143        NOFLAGS                 /* not used */
144};
145
146
147/*
148 * trak_start - open the devices and initialize data for processing
149 */
150static int
151trak_start(unit, peer)
152        int unit;
153        struct peer *peer;
154{
155        register struct trakunit *up;
156        struct refclockproc *pp;
157        int fd;
158        char device[20];
159
160        /*
161         * Open serial port. The LDISC_ACTS line discipline inserts a
162         * timestamp following the "*" on-time character of the
163         * timecode.
164         */
165        (void)sprintf(device, DEVICE, unit);
166#ifdef PPS
167        if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
168#else
169        if (!(fd = refclock_open(device, SPEED232, 0)))
170#endif /* PPS */
171                return (0);
172
173        /*
174         * Allocate and initialize unit structure
175         */
176        if (!(up = (struct trakunit *)
177            emalloc(sizeof(struct trakunit)))) {
178                (void) close(fd);
179                return (0);
180        }
181        memset((char *)up, 0, sizeof(struct trakunit));
182        pp = peer->procptr;
183        pp->io.clock_recv = trak_receive;
184        pp->io.srcclock = (caddr_t)peer;
185        pp->io.datalen = 0;
186        pp->io.fd = fd;
187        if (!io_addclock(&pp->io)) {
188                (void) close(fd);
189                free(up);
190                return (0);
191        }
192        pp->unitptr = (caddr_t)up;
193
194        /*
195         * Initialize miscellaneous variables
196         */
197        peer->precision = PRECISION;
198        pp->clockdesc = DESCRIPTION;
199        memcpy((char *)&pp->refid, REFID, 4);
200        up->polled = 0;
201
202        /*
203         * Start continuous time output. If something breaks, fold the
204         * tent and go home.
205         */
206        if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
207                refclock_report(peer, CEVNT_FAULT);
208                (void) close(fd);
209                free(up);
210                return (0);
211        }
212        return (1);
213}
214
215
216/*
217 * trak_shutdown - shut down the clock
218 */
219static void
220trak_shutdown(unit, peer)
221        int unit;
222        struct peer *peer;
223{
224        register struct trakunit *up;
225        struct refclockproc *pp;
226
227        pp = peer->procptr;
228        up = (struct trakunit *)pp->unitptr;
229        io_closeclock(&pp->io);
230        free(up);
231}
232
233
234/*
235 * trak_receive - receive data from the serial interface
236 */
237static void
238trak_receive(rbufp)
239        struct recvbuf *rbufp;
240{
241        register struct trakunit *up;
242        struct refclockproc *pp;
243        struct peer *peer;
244        l_fp trtmp;
245        char *dpt, *dpend;
246        char qchar;
247#ifdef PPS
248        struct ppsclockev ppsev;
249#endif /* PPS */
250
251        /*
252         * Initialize pointers and read the timecode and timestamp. We
253         * then chuck out everything, including runts, except one
254         * message each poll interval.
255         */
256        peer = (struct peer *)rbufp->recv_srcclock;
257        pp = peer->procptr;
258        up = (struct trakunit *)pp->unitptr;
259        pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
260            &pp->lastrec);
261
262        /*
263         * We get a buffer and timestamp following the '*' on-time
264         * character. If a valid timestamp, we use that in place of the
265         * buffer timestamp and edit out the timestamp for prettyprint
266         * billboards.
267         */
268        dpt = pp->a_lastcode;
269        dpend = dpt + pp->lencode;
270        if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
271                if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
272                    pp->lastrec.l_i + 1) {
273                        pp->lastrec = trtmp;
274                        dpt += 9;
275                        while (dpt < dpend)
276                                *(dpt - 8) = *dpt++;
277                }
278        }
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
290
291        /*
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.
295         */
296        if (pp->lencode < LENTRAK) {
297                refclock_report(peer, CEVNT_BADREPLY);
298                return;
299        }
300
301        /*
302         * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
303         */
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;
308        }
309
310        /*
311         * Decode quality and leap characters. If unsynchronized, set
312         * the leap bits accordingly and exit.
313         */
314        if (qchar == '0') {
315                pp->leap = LEAP_NOTINSYNC;
316                return;
317        }
318        else {
319                pp->leap = 0;
320                pp->lasttime = current_time;
321        }
322#ifdef PPS
323        if(ioctl(fdpps,CIOGETEV,(caddr_t) &ppsev) >=0) {
324                ppsev.tv.tv_sec += (u_int32) JAN_1970;
325                TVTOTS(&ppsev.tv,&up->tstamp);
326        }
327#endif /* PPS */
328        /* record the last ppsclock event time stamp */
329        pp->lastrec = up->tstamp;
330
331        /*
332         * Process the new sample in the median filter and determine the
333         * reference clock offset and dispersion. We use lastrec as both
334         * the reference time and receive time in order to avoid being
335         * cute, like setting the reference time later than the receive
336         * time, which may cause a paranoid protocol module to chuck out
337         * the data.
338         */
339        if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
340                refclock_report(peer, CEVNT_BADTIME);
341                return;
342        }
343        refclock_receive(peer, &pp->offset, 0, pp->dispersion,
344            &pp->lastrec, &pp->lastrec, pp->leap);
345}
346
347
348/*
349 * trak_poll - called by the transmit procedure
350 */
351static void
352trak_poll(unit, peer)
353        int unit;
354        struct peer *peer;
355{
356        register struct trakunit *up;
357        struct refclockproc *pp;
358
359        /*
360         * We don't really do anything here, except arm the receiving
361         * side to capture a sample and check for timeouts.
362         */
363        pp = peer->procptr;
364        up = (struct trakunit *)pp->unitptr;
365        if (up->polled)
366                refclock_report(peer, CEVNT_TIMEOUT);
367        pp->polls++;
368        up->polled = 1;
369}
370
371#endif
Note: See TracBrowser for help on using the repository browser.