Annotation of embedaddon/ntp/ntpd/refclock_trak.c, revision 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>