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

1.1     ! misho       1: /*
        !             2:  * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
        !             3:  */
        !             4: 
        !             5: #ifdef HAVE_CONFIG_H
        !             6: #include <config.h>
        !             7: #endif
        !             8: 
        !             9: #if defined(REFCLOCK) && defined(CLOCK_PST)
        !            10: 
        !            11: #include "ntpd.h"
        !            12: #include "ntp_io.h"
        !            13: #include "ntp_refclock.h"
        !            14: #include "ntp_stdlib.h"
        !            15: 
        !            16: #include <stdio.h>
        !            17: #include <ctype.h>
        !            18: 
        !            19: /*
        !            20:  * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
        !            21:  * Receivers. No specific claim of accuracy is made for these receiver,
        !            22:  * but actual experience suggests that 10 ms would be a conservative
        !            23:  * assumption.
        !            24:  * 
        !            25:  * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
        !            26:  * of-year format and UTC time zone. Automatic correction for DST should
        !            27:  * be disabled. It is very important that the year be set correctly in
        !            28:  * the DIPswitches; otherwise, the day of year will be incorrect after
        !            29:  * 28 April of a normal or leap year. The propagation delay DIPswitches
        !            30:  * should be set according to the distance from the transmitter for both
        !            31:  * WWV and WWVH, as described in the instructions. While the delay can
        !            32:  * be set only to within 11 ms, the fudge time1 parameter can be used
        !            33:  * for vernier corrections.
        !            34:  *
        !            35:  * Using the poll sequence QTQDQM, the response timecode is in three
        !            36:  * sections totalling 50 ASCII printing characters, as concatenated by
        !            37:  * the driver, in the following format:
        !            38:  *
        !            39:  * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
        !            40:  *
        !            41:  *     on-time = first <cr>
        !            42:  *     hh:mm:ss.fff = hours, minutes, seconds, milliseconds
        !            43:  *     a = AM/PM indicator (' ' for 24-hour mode)
        !            44:  *     yy = year (from internal switches)
        !            45:  *     dd/mm/ddd = day of month, month, day of year
        !            46:  *     s = daylight-saving indicator (' ' for 24-hour mode)
        !            47:  *     f = frequency enable (O = all frequencies enabled)
        !            48:  *     r = baud rate (3 = 1200, 6 = 9600)
        !            49:  *     d = features indicator (@ = month/day display enabled)
        !            50:  *     z = time zone (0 = UTC)
        !            51:  *     y = year (5 = 91)
        !            52:  *     cc = WWV propagation delay (52 = 22 ms)
        !            53:  *     hh = WWVH propagation delay (81 = 33 ms)
        !            54:  *     SS = status (80 or 82 = operating correctly)
        !            55:  *     F = current receive frequency (4 = 15 MHz)
        !            56:  *     T = transmitter (C = WWV, H = WWVH)
        !            57:  *     tttt = time since last update (0000 = minutes)
        !            58:  *     uu = flush character (03 = ^c)
        !            59:  *     xx = 94 (unknown)
        !            60:  *
        !            61:  * The alarm condition is indicated by other than '8' at A, which occurs
        !            62:  * during initial synchronization and when received signal is lost for
        !            63:  * an extended period; unlock condition is indicated by other than
        !            64:  * "0000" in the tttt subfield at Q.
        !            65:  *
        !            66:  * Fudge Factors
        !            67:  *
        !            68:  * There are no special fudge factors other than the generic.
        !            69:  */
        !            70: 
        !            71: /*
        !            72:  * Interface definitions
        !            73:  */
        !            74: #define        DEVICE          "/dev/wwv%d" /* device name and unit */
        !            75: #define        SPEED232        B9600   /* uart speed (9600 baud) */
        !            76: #define        PRECISION       (-10)   /* precision assumed (about 1 ms) */
        !            77: #define        WWVREFID        "WWV\0" /* WWV reference ID */
        !            78: #define        WWVHREFID       "WWVH"  /* WWVH reference ID */
        !            79: #define        DESCRIPTION     "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
        !            80: #define PST_PHI                (10e-6) /* max clock oscillator offset */
        !            81: #define LENPST         46      /* min timecode length */
        !            82: 
        !            83: /*
        !            84:  * Unit control structure
        !            85:  */
        !            86: struct pstunit {
        !            87:        int     tcswitch;       /* timecode switch */
        !            88:        char    *lastptr;       /* pointer to timecode data */
        !            89: };
        !            90: 
        !            91: /*
        !            92:  * Function prototypes
        !            93:  */
        !            94: static int     pst_start       (int, struct peer *);
        !            95: static void    pst_shutdown    (int, struct peer *);
        !            96: static void    pst_receive     (struct recvbuf *);
        !            97: static void    pst_poll        (int, struct peer *);
        !            98: 
        !            99: /*
        !           100:  * Transfer vector
        !           101:  */
        !           102: struct refclock refclock_pst = {
        !           103:        pst_start,              /* start up driver */
        !           104:        pst_shutdown,           /* shut down driver */
        !           105:        pst_poll,               /* transmit poll message */
        !           106:        noentry,                /* not used (old pst_control) */
        !           107:        noentry,                /* initialize driver */
        !           108:        noentry,                /* not used (old pst_buginfo) */
        !           109:        NOFLAGS                 /* not used */
        !           110: };
        !           111: 
        !           112: 
        !           113: /*
        !           114:  * pst_start - open the devices and initialize data for processing
        !           115:  */
        !           116: static int
        !           117: pst_start(
        !           118:        int unit,
        !           119:        struct peer *peer
        !           120:        )
        !           121: {
        !           122:        register struct pstunit *up;
        !           123:        struct refclockproc *pp;
        !           124:        int fd;
        !           125:        char device[20];
        !           126: 
        !           127:        /*
        !           128:         * Open serial port. Use CLK line discipline, if available.
        !           129:         */
        !           130:        snprintf(device, sizeof(device), DEVICE, unit);
        !           131:        if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
        !           132:                return (0);
        !           133: 
        !           134:        /*
        !           135:         * Allocate and initialize unit structure
        !           136:         */
        !           137:        up = emalloc(sizeof(*up));
        !           138:        memset(up, 0, sizeof(*up));
        !           139:        pp = peer->procptr;
        !           140:        pp->io.clock_recv = pst_receive;
        !           141:        pp->io.srcclock = (caddr_t)peer;
        !           142:        pp->io.datalen = 0;
        !           143:        pp->io.fd = fd;
        !           144:        if (!io_addclock(&pp->io)) {
        !           145:                close(fd);
        !           146:                pp->io.fd = -1;
        !           147:                free(up);
        !           148:                return (0);
        !           149:        }
        !           150:        pp->unitptr = (caddr_t)up;
        !           151: 
        !           152:        /*
        !           153:         * Initialize miscellaneous variables
        !           154:         */
        !           155:        peer->precision = PRECISION;
        !           156:        pp->clockdesc = DESCRIPTION;
        !           157:        memcpy((char *)&pp->refid, WWVREFID, 4);
        !           158:        peer->burst = MAXSTAGE;
        !           159:        return (1);
        !           160: }
        !           161: 
        !           162: 
        !           163: /*
        !           164:  * pst_shutdown - shut down the clock
        !           165:  */
        !           166: static void
        !           167: pst_shutdown(
        !           168:        int unit,
        !           169:        struct peer *peer
        !           170:        )
        !           171: {
        !           172:        register struct pstunit *up;
        !           173:        struct refclockproc *pp;
        !           174: 
        !           175:        pp = peer->procptr;
        !           176:        up = (struct pstunit *)pp->unitptr;
        !           177:        if (-1 != pp->io.fd)
        !           178:                io_closeclock(&pp->io);
        !           179:        if (NULL != up)
        !           180:                free(up);
        !           181: }
        !           182: 
        !           183: 
        !           184: /*
        !           185:  * pst_receive - receive data from the serial interface
        !           186:  */
        !           187: static void
        !           188: pst_receive(
        !           189:        struct recvbuf *rbufp
        !           190:        )
        !           191: {
        !           192:        register struct pstunit *up;
        !           193:        struct refclockproc *pp;
        !           194:        struct peer *peer;
        !           195:        l_fp trtmp;
        !           196:        u_long ltemp;
        !           197:        char ampmchar;          /* AM/PM indicator */
        !           198:        char daychar;           /* standard/daylight indicator */
        !           199:        char junque[10];        /* "yy/dd/mm/" discard */
        !           200:        char info[14];          /* "frdzycchhSSFT" clock info */
        !           201: 
        !           202:        /*
        !           203:         * Initialize pointers and read the timecode and timestamp
        !           204:         */
        !           205:        peer = (struct peer *)rbufp->recv_srcclock;
        !           206:        pp = peer->procptr;
        !           207:        up = (struct pstunit *)pp->unitptr;
        !           208:        up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
        !           209:            + BMAX - 2 - up->lastptr, &trtmp);
        !           210:        *up->lastptr++ = ' ';
        !           211:        *up->lastptr = '\0';
        !           212: 
        !           213:        /*
        !           214:         * Note we get a buffer and timestamp for each <cr>, but only
        !           215:         * the first timestamp is retained.
        !           216:         */
        !           217:        if (up->tcswitch == 0)
        !           218:                pp->lastrec = trtmp;
        !           219:        up->tcswitch++;
        !           220:        pp->lencode = up->lastptr - pp->a_lastcode;
        !           221:        if (up->tcswitch < 3)
        !           222:                return;
        !           223: 
        !           224:        /*
        !           225:         * We get down to business, check the timecode format and decode
        !           226:         * its contents. If the timecode has invalid length or is not in
        !           227:         * proper format, we declare bad format and exit.
        !           228:         */
        !           229:        if (pp->lencode < LENPST) {
        !           230:                refclock_report(peer, CEVNT_BADREPLY);
        !           231:                return;
        !           232:        }
        !           233: 
        !           234:        /*
        !           235:         * Timecode format:
        !           236:         * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
        !           237:         */
        !           238:        if (sscanf(pp->a_lastcode,
        !           239:            "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
        !           240:            &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
        !           241:            &daychar, junque, &pp->day, info, &ltemp) != 10) {
        !           242:                refclock_report(peer, CEVNT_BADREPLY);
        !           243:                return;
        !           244:        }
        !           245:        pp->nsec *= 1000000;
        !           246: 
        !           247:        /*
        !           248:         * Decode synchronization, quality and last update. If
        !           249:         * unsynchronized, set the leap bits accordingly and exit. Once
        !           250:         * synchronized, the dispersion depends only on when the clock
        !           251:         * was last heard, which depends on the time since last update,
        !           252:         * as reported by the clock.
        !           253:         */
        !           254:        if (info[9] != '8')
        !           255:                pp->leap = LEAP_NOTINSYNC;
        !           256:        if (info[12] == 'H')
        !           257:                memcpy((char *)&pp->refid, WWVHREFID, 4);
        !           258:        else
        !           259:                memcpy((char *)&pp->refid, WWVREFID, 4);
        !           260:        if (peer->stratum <= 1)
        !           261:                peer->refid = pp->refid;
        !           262:        if (ltemp == 0)
        !           263:                pp->lastref = pp->lastrec;
        !           264:        pp->disp = PST_PHI * ltemp * 60;
        !           265: 
        !           266:        /*
        !           267:         * Process the new sample in the median filter and determine the
        !           268:         * timecode timestamp.
        !           269:         */
        !           270:        if (!refclock_process(pp))
        !           271:                refclock_report(peer, CEVNT_BADTIME);
        !           272:        else if (peer->disp > MAXDISTANCE)
        !           273:                refclock_receive(peer);
        !           274: }
        !           275: 
        !           276: 
        !           277: /*
        !           278:  * pst_poll - called by the transmit procedure
        !           279:  */
        !           280: static void
        !           281: pst_poll(
        !           282:        int unit,
        !           283:        struct peer *peer
        !           284:        )
        !           285: {
        !           286:        register struct pstunit *up;
        !           287:        struct refclockproc *pp;
        !           288: 
        !           289:        /*
        !           290:         * Time to poll the clock. The PSTI/Traconex clock responds to a
        !           291:         * "QTQDQMT" by returning a timecode in the format specified
        !           292:         * above. Note there is no checking on state, since this may not
        !           293:         * be the only customer reading the clock. Only one customer
        !           294:         * need poll the clock; all others just listen in. If the clock
        !           295:         * becomes unreachable, declare a timeout and keep going.
        !           296:         */
        !           297:        pp = peer->procptr;
        !           298:        up = (struct pstunit *)pp->unitptr;
        !           299:        up->tcswitch = 0;
        !           300:        up->lastptr = pp->a_lastcode;
        !           301:        if (write(pp->io.fd, "QTQDQMT", 6) != 6)
        !           302:                refclock_report(peer, CEVNT_FAULT);
        !           303:        if (peer->burst > 0)
        !           304:                return;
        !           305:        if (pp->coderecv == pp->codeproc) {
        !           306:                refclock_report(peer, CEVNT_TIMEOUT);
        !           307:                return;
        !           308:        }
        !           309:        refclock_receive(peer);
        !           310:        record_clock_stats(&peer->srcadr, pp->a_lastcode);
        !           311: #ifdef DEBUG
        !           312:        if (debug)
        !           313:                printf("pst: timecode %d %s\n", pp->lencode,
        !           314:                    pp->a_lastcode);
        !           315: #endif
        !           316:        peer->burst = MAXSTAGE;
        !           317:        pp->polls++;
        !           318: }
        !           319: 
        !           320: #else
        !           321: int refclock_pst_int;
        !           322: #endif /* REFCLOCK */

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