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: &mchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
! 241: &daychar, junque, &pp->day, info, <emp) != 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>