]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_trak.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ntp / ntpd / refclock_trak.c
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 = '*'\v *        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         (void)sprintf(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         if (!(up = (struct trakunit *)
164               emalloc(sizeof(struct trakunit)))) {
165                 (void) close(fd);
166                 return (0);
167         }
168         memset((char *)up, 0, sizeof(struct trakunit));
169         pp = peer->procptr;
170         pp->io.clock_recv = trak_receive;
171         pp->io.srcclock = (caddr_t)peer;
172         pp->io.datalen = 0;
173         pp->io.fd = fd;
174         if (!io_addclock(&pp->io)) {
175                 (void) close(fd);
176                 free(up);
177                 return (0);
178         }
179         pp->unitptr = (caddr_t)up;
180
181         /*
182          * Initialize miscellaneous variables
183          */
184         peer->precision = PRECISION;
185         pp->clockdesc = DESCRIPTION;
186         memcpy((char *)&pp->refid, REFID, 4);
187         up->polled = 0;
188
189         /*
190          * Start continuous time output. If something breaks, fold the
191          * tent and go home.
192          */
193         if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
194                 refclock_report(peer, CEVNT_FAULT);
195                 (void) close(fd);
196                 free(up);
197                 return (0);
198         }
199         return (1);
200 }
201
202
203 /*
204  * trak_shutdown - shut down the clock
205  */
206 static void
207 trak_shutdown(
208         int unit,
209         struct peer *peer
210         )
211 {
212         register struct trakunit *up;
213         struct refclockproc *pp;
214
215         pp = peer->procptr;
216         up = (struct trakunit *)pp->unitptr;
217         io_closeclock(&pp->io);
218         free(up);
219 }
220
221
222 /*
223  * trak_receive - receive data from the serial interface
224  */
225 static void
226 trak_receive(
227         struct recvbuf *rbufp
228         )
229 {
230         register struct trakunit *up;
231         struct refclockproc *pp;
232         struct peer *peer;
233         l_fp trtmp;
234         char *dpt, *dpend;
235         char qchar;
236 #ifdef PPS
237         struct ppsclockev ppsev;
238         int request;
239 #ifdef HAVE_CIOGETEV
240         request = CIOGETEV;
241 #endif
242 #ifdef HAVE_TIOCGPPSEV
243         request = TIOCGPPSEV;
244 #endif
245 #endif /* PPS */
246
247         /*
248          * Initialize pointers and read the timecode and timestamp. We
249          * then chuck out everything, including runts, except one
250          * message each poll interval.
251          */
252         peer = (struct peer *)rbufp->recv_srcclock;
253         pp = peer->procptr;
254         up = (struct trakunit *)pp->unitptr;
255         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
256                                      &pp->lastrec);
257
258         /*
259          * We get a buffer and timestamp following the '*' on-time
260          * character. If a valid timestamp, we use that in place of the
261          * buffer timestamp and edit out the timestamp for prettyprint
262          * billboards.
263          */
264         dpt = pp->a_lastcode;
265         dpend = dpt + pp->lencode;
266         if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
267                 if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
268                     pp->lastrec.l_i + 1) {
269                         pp->lastrec = trtmp;
270                         dpt += 9;
271                         while (dpt < dpend) {
272                                 *(dpt - 8) = *dpt;
273                                 ++dpt;
274                         }
275                 }
276         }
277         if (up->polled == 0) return;
278         up->polled = 0;
279 #ifndef PPS
280         get_systime(&up->tstamp);
281 #endif
282         record_clock_stats(&peer->srcadr, pp->a_lastcode);
283 #ifdef DEBUG
284         if (debug)
285             printf("trak: timecode %d %s\n", pp->lencode,
286                    pp->a_lastcode);
287 #endif
288
289         /*
290          * We get down to business, check the timecode format and decode
291          * its contents. If the timecode has invalid length or is not in
292          * proper format, we declare bad format and exit.
293          */
294         if (pp->lencode < LENTRAK) {
295                 refclock_report(peer, CEVNT_BADREPLY);
296                 return;
297         }
298
299         /*
300          * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
301          */
302         if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
303                    &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
304                 refclock_report(peer, CEVNT_BADREPLY);
305                 return;
306         }
307
308         /*
309          * Decode quality and leap characters. If unsynchronized, set
310          * the leap bits accordingly and exit.
311          */
312         if (qchar == '0') {
313                 pp->leap = LEAP_NOTINSYNC;
314                 return;
315         }
316 #ifdef PPS
317         if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
318                 ppsev.tv.tv_sec += (u_int32) JAN_1970;
319                 TVTOTS(&ppsev.tv,&up->tstamp);
320         }
321 #endif /* PPS */
322         /* record the last ppsclock event time stamp */
323         pp->lastrec = up->tstamp;
324         if (!refclock_process(pp)) {
325                 refclock_report(peer, CEVNT_BADTIME);
326                 return;
327         }
328         pp->lastref = pp->lastrec;
329         refclock_receive(peer);
330 }
331
332
333 /*
334  * trak_poll - called by the transmit procedure
335  */
336 static void
337 trak_poll(
338         int unit,
339         struct peer *peer
340         )
341 {
342         register struct trakunit *up;
343         struct refclockproc *pp;
344
345         /*
346          * We don't really do anything here, except arm the receiving
347          * side to capture a sample and check for timeouts.
348          */
349         pp = peer->procptr;
350         up = (struct trakunit *)pp->unitptr;
351         if (up->polled)
352             refclock_report(peer, CEVNT_TIMEOUT);
353         pp->polls++;
354         up->polled = 1;
355 }
356
357 #else
358 int refclock_trak_bs;
359 #endif /* REFCLOCK */