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