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

Revision 10832, 11.7 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_arbiter - clock driver for Arbiter 1088A/B Satellite
3 *      Controlled Clock
4 */
5
6#ifdef HAVE_CONFIG_H
7#include <config.h>
8#endif
9
10#if defined(REFCLOCK) && defined(ARBITER)
11
12#include <stdio.h>
13#include <ctype.h>
14#include <sys/time.h>
15
16#include "ntpd.h"
17#include "ntp_io.h"
18#include "ntp_refclock.h"
19#include "ntp_stdlib.h"
20
21/*
22 * This driver supports the Arbiter 1088A/B Satellite Controlled Clock.
23 * The claimed accuracy of this clock is 100 ns relative to the PPS
24 * output when receiving four or more satellites.
25 *
26 * The receiver should be configured before starting the NTP daemon, in
27 * order to establish reliable position and operating conditions. It
28 * does not initiate surveying or hold mode. For use with NTP, the
29 * daylight savings time feature should be disables (D0 command) and the
30 * broadcast mode set to operate in UTC (BU command).
31 *
32 * The timecode format supported by this driver is selected by the poll
33 * sequence "B5", which initiates a line in the following format to be
34 * repeated once per second until turned off by the "B0" poll sequence.
35 *
36 * Format B5 (24 ASCII printing characters):
37 *
38 * <cr><lf>i yy ddd hh:mm:ss.000bbb 
39 *
40 *      on-time = <cr>
41 *      i = synchronization flag (' ' = locked, '?' = unlocked)
42 *      yy = year of century
43 *      ddd = day of year
44 *      hh:mm:ss = hours, minutes, seconds
45 *      .000 = fraction of second (not used)
46 *      bbb = tailing spaces for fill
47 *
48 * The alarm condition is indicated by a '?' at i, which indicates the
49 * receiver is not synchronized. In normal operation, a line consisting
50 * of the timecode followed by the time quality character (TQ) followed
51 * by the receiver status string (SR) is written to the clockstats file.
52 * The time quality character is encoded in IEEE P1344 standard:
53 *
54 * Format TQ (IEEE P1344 estimated worst-case time quality)
55 *
56 *      0       clock locked, maximum accuracy
57 *      F       clock failure, time not reliable
58 *      4       clock unlocked, accuracy < 1 us
59 *      5       clock unlocked, accuracy < 10 us
60 *      6       clock unlocked, accuracy < 100 us
61 *      7       clock unlocked, accuracy < 1 ms
62 *      8       clock unlocked, accuracy < 10 ms
63 *      9       clock unlocked, accuracy < 100 ms
64 *      A       clock unlocked, accuracy < 1 s
65 *      B       clock unlocked, accuracy < 10 s
66 *
67 * The status string is encoded as follows:
68 *
69 * Format SR (25 ASCII printing characters)
70 *
71 *      V=vv S=ss T=t P=pdop E=ee
72 *
73 *      vv = satellites visible
74 *      ss = relative signal strength
75 *      t = satellites tracked
76 *      pdop = position dilution of precision (meters)
77 *      ee = hardware errors
78 *
79 * If flag4 is set, an additional line consisting of the receiver
80 * latitude (LA), longitude (LO) and elevation (LH) (meters) is written
81 * to this file. If channel B is enabled for deviation mode and connected
82 * to a 1-PPS signal, the last two numbers on the line are the deviation
83 * and standard deviation averaged over the last 15 seconds.
84 */
85
86/*
87 * Interface definitions
88 */
89#define DEVICE          "/dev/gps%d" /* device name and unit */
90#define SPEED232        B9600   /* uart speed (9600 baud) */
91#define PRECISION       (-10)   /* precision assumed (about 1 ms) */
92#define REFID           "GPS " /* reference ID */
93#define DESCRIPTION     "Arbiter 1088A/B GPS Receiver" /* WRU */
94
95#define NSAMPLES        3       /* stages of median filter */
96#define LENARB          24      /* format B5 timecode length */
97#define MAXSTA          30      /* max length of status string */
98#define MAXPOS          70      /* max length of position string */
99
100/*
101 * Imported from ntp_timer module
102 */
103extern  u_long  current_time;   /* current time (s) */
104
105/*
106 * Imported from ntpd module
107 */
108extern  int     debug;          /* global debug flag */
109
110/*
111 * ARB unit control structure
112 */
113struct arbunit {
114        int     pollcnt;        /* poll message counter */
115        l_fp    laststamp;      /* last receive timestamp */
116        u_char  tcswitch;       /* timecode enable switch */
117        char    qualchar;       /* IEEE P1344 quality (TQ command) */
118        char    status[MAXSTA]; /* receiver status (SR command) */
119        char    latlon[MAXPOS]; /* receiver position (lat/lon/alt) */
120};
121
122/*
123 * Function prototypes
124 */
125static  int     arb_start       P((int, struct peer *));
126static  void    arb_shutdown    P((int, struct peer *));
127static  void    arb_receive     P((struct recvbuf *));
128static  void    arb_poll        P((int, struct peer *));
129
130/*
131 * Transfer vector
132 */
133struct  refclock refclock_arbiter = {
134        arb_start,              /* start up driver */
135        arb_shutdown,           /* shut down driver */
136        arb_poll,               /* transmit poll message */
137        noentry,                /* not used (old arb_control) */
138        noentry,                /* initialize driver (not used) */
139        noentry,                /* not used (old arb_buginfo) */
140        NOFLAGS                 /* not used */
141};
142
143
144/*
145 * arb_start - open the devices and initialize data for processing
146 */
147static int
148arb_start(unit, peer)
149        int unit;
150        struct peer *peer;
151{
152        register struct arbunit *up;
153        struct refclockproc *pp;
154        int fd;
155        char device[20];
156
157        /*
158         * Open serial port. Use CLK line discipline, if available.
159         */
160        (void)sprintf(device, DEVICE, unit);
161#ifdef TTYCLK
162        if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
163#else
164        if (!(fd = refclock_open(device, SPEED232, 0)))
165#endif /* TTYCLK */
166                return (0);
167
168        /*
169         * Allocate and initialize unit structure
170         */
171        if (!(up = (struct arbunit *)
172            emalloc(sizeof(struct arbunit)))) {
173                (void) close(fd);
174                return (0);
175        }
176        memset((char *)up, 0, sizeof(struct arbunit));
177        pp = peer->procptr;
178        pp->io.clock_recv = arb_receive;
179        pp->io.srcclock = (caddr_t)peer;
180        pp->io.datalen = 0;
181        pp->io.fd = fd;
182        if (!io_addclock(&pp->io)) {
183                (void) close(fd);
184                free(up);
185                return (0);
186        }
187        pp->unitptr = (caddr_t)up;
188
189        /*
190         * Initialize miscellaneous variables
191         */
192        peer->precision = PRECISION;
193        pp->clockdesc = DESCRIPTION;
194        memcpy((char *)&pp->refid, REFID, 4);
195        up->pollcnt = 2;
196        return (1);
197}
198
199
200/*
201 * arb_shutdown - shut down the clock
202 */
203static void
204arb_shutdown(unit, peer)
205        int unit;
206        struct peer *peer;
207{
208        register struct arbunit *up;
209        struct refclockproc *pp;
210
211        pp = peer->procptr;
212        up = (struct arbunit *)pp->unitptr;
213        io_closeclock(&pp->io);
214        free(up);
215}
216
217
218/*
219 * arb_receive - receive data from the serial interface
220 */
221static void
222arb_receive(rbufp)
223        struct recvbuf *rbufp;
224{
225        register struct arbunit *up;
226        struct refclockproc *pp;
227        struct peer *peer;
228        l_fp trtmp;
229        u_long ltemp;
230        int temp;
231        u_char  syncchar;       /* synchronization indicator */
232        u_char  leapchar;       /* leap indicator */
233
234        /*
235         * Initialize pointers and read the timecode and timestamp
236         */
237        peer = (struct peer *)rbufp->recv_srcclock;
238        pp = peer->procptr;
239        up = (struct arbunit *)pp->unitptr;
240        temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
241        if (up->tcswitch)
242                return;
243
244        /*
245         * Note we get a buffer and timestamp for both a <cr> and <lf>,
246         * but only the <cr> timestamp is retained. The program first
247         * sends a TQ and expects the echo followed by the time quality
248         * character. It then sends a B5 starting the timecode broadcast
249         * and expects the echo followed some time later by the on-time
250         * character <cr> and then the <lf> beginning the timecode
251         * itself. Finally, at the <cr> beginning the next timecode at
252         * the next second, the program sends a B0 shutting down the the
253         * timecode broadcast.
254         *
255         * If flag4 is set, the program snatches the latitude, longitude
256         * and elevation and writes it to the clockstats file.
257         */
258        if (temp == 0)
259                return;
260        pp->lastrec = up->laststamp;
261        up->laststamp = trtmp;
262        if (temp < 3)
263                return;
264        if (!strncmp(pp->a_lastcode, "TQ", 2)) {
265                up->qualchar = pp->a_lastcode[2];
266                write(pp->io.fd, "SR", 2);
267                return;
268        } else if (!strncmp(pp->a_lastcode, "SR", 2)) {
269                strcpy(up->status, pp->a_lastcode + 2);
270                if (pp->sloppyclockflag & CLK_FLAG4)
271                        write(pp->io.fd, "LA", 2);
272                else
273                        write(pp->io.fd, "B5", 2);
274                return;
275        } else if (!strncmp(pp->a_lastcode, "LA", 2)) {
276                strcpy(up->latlon, pp->a_lastcode + 2);
277                write(pp->io.fd, "LO", 2);
278                return;
279        } else if (!strncmp(pp->a_lastcode, "LO", 2)) {
280                strcat(up->latlon, " ");
281                strcat(up->latlon, pp->a_lastcode + 2);
282                write(pp->io.fd, "LH", 2);
283                return;
284        } else if (!strncmp(pp->a_lastcode, "LH", 2)) {
285                strcat(up->latlon, " ");
286                strcat(up->latlon, pp->a_lastcode + 2);
287                write(pp->io.fd, "DB", 2);
288                return;
289        } else if (!strncmp(pp->a_lastcode, "DB", 2)) {
290                strcat(up->latlon, " ");
291                strcat(up->latlon, pp->a_lastcode + 2);
292                record_clock_stats(&peer->srcadr, up->latlon);
293                write(pp->io.fd, "B5", 2);
294                return;
295        }
296        write(pp->io.fd, "B0", 2);
297        pp->lencode = temp;
298        up->pollcnt = 2;
299        up->tcswitch++;
300
301#ifdef DEBUG
302        if (debug)
303                printf("arbiter: timecode %d %s\n", pp->lencode,
304                    pp->a_lastcode);
305#endif
306
307        /*
308         * We get down to business, check the timecode format and decode
309         * its contents. If the timecode has valid length, but not in
310         * proper format, we declare bad format and exit. If the
311         * timecode has invalid length, which sometimes occurs when the
312         * B0 amputates the broadcast, we just quietly steal away. Note
313         * that the time quality character and receiver status string is
314         * tacked on the end for clockstats display.
315         */
316        if (pp->lencode == LENARB) {
317                /*
318                 * Timecode format B5: "i yy ddd hh:mm:ss.000   "
319                 */
320                pp->a_lastcode[LENARB - 2] = up->qualchar;
321                strcat(pp->a_lastcode, up->status);
322                record_clock_stats(&peer->srcadr, pp->a_lastcode);
323                syncchar = leapchar = ' ';
324                if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d",
325                    &syncchar, &pp->year, &pp->day, &pp->hour,
326                    &pp->minute, &pp->second) != 6) {
327                        refclock_report(peer, CEVNT_BADREPLY);
328                        return;
329                }
330        } else {
331                return;
332        }
333
334        /*
335         * We decode the clock dispersion from the time quality
336         * character. This assumes dispersion increases at about 1 ms
337         * per minute, or about 16 ppm, which may even be a reasonable
338         * figure for the radio clock oscillator. If the clock has not
339         * yet tracked any satellites or failed, the leap bits are
340         * marked unsynchronized.
341         */
342        switch (up->qualchar) {
343
344                case '0':               /* locked, max accuracy */
345                case '4':               /* unlock accuracy < 1 us */
346                case '5':               /* unlock accuracy < 10 us */
347                ltemp = 0;
348                break;
349
350                case '6':               /* unlock accuracy < 100 us */
351                ltemp = 6;
352                break;
353
354                case '7':               /* unlock accuracy < 1 ms */
355                ltemp = 63;
356                break;
357
358                case '8':               /* unlock accuracy < 10 ms */
359                ltemp = 625;
360                break;
361
362                case '9':               /* unlock accuracy < 100 ms */
363                ltemp = 6250;
364                break;
365
366                case 'A':               /* unlock accuracy < 1 s */
367                ltemp = 62500;
368                break;
369
370                case 'B':               /* unlock accuracy < 10 s */
371                case 'F':               /* clock failure */
372                ltemp = NTP_MAXAGE;
373                pp->leap = LEAP_NOTINSYNC;
374                return;
375
376                default:
377                ltemp = NTP_MAXAGE;
378                pp->leap = LEAP_NOTINSYNC;
379                refclock_report(peer, CEVNT_BADREPLY);
380                return;
381        }
382        pp->leap = 0;
383        pp->lasttime = current_time - ltemp;
384
385        /*
386         * Process the new sample in the median filter and determine the
387         * reference clock offset and dispersion. We use lastrec as both
388         * the reference time and receive time in order to avoid being
389         * cute, like setting the reference time later than the receive
390         * time, which may cause a paranoid protocol module to chuck out
391         * the data.
392         */
393        if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
394                refclock_report(peer, CEVNT_BADTIME);
395                return;
396        }
397        trtmp = pp->lastrec;
398        trtmp.l_ui -= ltemp;
399        refclock_receive(peer, &pp->offset, 0, pp->dispersion,
400            &trtmp, &pp->lastrec, pp->leap);
401}
402
403
404/*
405 * arb_poll - called by the transmit procedure
406 */
407static void
408arb_poll(unit, peer)
409        int unit;
410        struct peer *peer;
411{
412        register struct arbunit *up;
413        struct refclockproc *pp;
414
415        /*
416         * Time to poll the clock. The Arbiter clock responds to a "B5"
417         * by returning a timecode in the format specified above.
418         * Transmission occurs once per second, unless turned off by a
419         * "B0". Note there is no checking on state, since this may not
420         * be the only customer reading the clock. Only one customer
421         * need poll the clock; all others just listen in. If nothing is
422         * heard from the clock for two polls, declare a timeout and
423         * keep going.
424         */
425        pp = peer->procptr;
426        up = (struct arbunit *)pp->unitptr;
427        if (up->pollcnt == 0)
428                refclock_report(peer, CEVNT_TIMEOUT);
429        else
430                up->pollcnt--;
431        if (write(pp->io.fd, "TQ", 2) != 2) {
432                refclock_report(peer, CEVNT_FAULT);
433        } else
434                pp->polls++;
435        up->tcswitch = 0;
436       
437}
438
439#endif
Note: See TracBrowser for help on using the repository browser.