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>