Annotation of embedaddon/ntp/ntpd/refclock_arbiter.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>