diff options
author | roberto <roberto@FreeBSD.org> | 2008-08-17 17:37:33 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 2008-08-17 17:37:33 +0000 |
commit | 4ded1c1fa0bc21c61f91a2dbe864835986745121 (patch) | |
tree | 16d100fbc9dae63888d48b464e471ba0e5065193 /ntpd/refclock_pst.c | |
parent | 8b5a86d4fda08a9c68231415812edcb26be52f79 (diff) | |
download | FreeBSD-src-4ded1c1fa0bc21c61f91a2dbe864835986745121.zip FreeBSD-src-4ded1c1fa0bc21c61f91a2dbe864835986745121.tar.gz |
Flatten the dist and various 4.n.n trees in preparation of future ntp imports.
Diffstat (limited to 'ntpd/refclock_pst.c')
-rw-r--r-- | ntpd/refclock_pst.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/ntpd/refclock_pst.c b/ntpd/refclock_pst.c new file mode 100644 index 0000000..2443b2c --- /dev/null +++ b/ntpd/refclock_pst.c @@ -0,0 +1,321 @@ +/* + * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_PST) + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +#include <stdio.h> +#include <ctype.h> + +/* + * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH + * Receivers. No specific claim of accuracy is made for these receiver, + * but actual experience suggests that 10 ms would be a conservative + * assumption. + * + * The DIPswitches should be set for 9600 bps line speed, 24-hour day- + * of-year format and UTC time zone. Automatic correction for DST should + * be disabled. It is very important that the year be set correctly in + * the DIPswitches; otherwise, the day of year will be incorrect after + * 28 April of a normal or leap year. The propagation delay DIPswitches + * should be set according to the distance from the transmitter for both + * WWV and WWVH, as described in the instructions. While the delay can + * be set only to within 11 ms, the fudge time1 parameter can be used + * for vernier corrections. + * + * Using the poll sequence QTQDQM, the response timecode is in three + * sections totalling 50 ASCII printing characters, as concatenated by + * the driver, in the following format: + * + * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr> + * + * on-time = first <cr> + * hh:mm:ss.fff = hours, minutes, seconds, milliseconds + * a = AM/PM indicator (' ' for 24-hour mode) + * yy = year (from internal switches) + * dd/mm/ddd = day of month, month, day of year + * s = daylight-saving indicator (' ' for 24-hour mode) + * f = frequency enable (O = all frequencies enabled) + * r = baud rate (3 = 1200, 6 = 9600) + * d = features indicator (@ = month/day display enabled) + * z = time zone (0 = UTC) + * y = year (5 = 91) + * cc = WWV propagation delay (52 = 22 ms) + * hh = WWVH propagation delay (81 = 33 ms) + * SS = status (80 or 82 = operating correctly) + * F = current receive frequency (4 = 15 MHz) + * T = transmitter (C = WWV, H = WWVH) + * tttt = time since last update (0000 = minutes) + * uu = flush character (03 = ^c) + * xx = 94 (unknown) + * + * The alarm condition is indicated by other than '8' at A, which occurs + * during initial synchronization and when received signal is lost for + * an extended period; unlock condition is indicated by other than + * "0000" in the tttt subfield at Q. + * + * Fudge Factors + * + * There are no special fudge factors other than the generic. + */ + +/* + * Interface definitions + */ +#define DEVICE "/dev/wwv%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define WWVREFID "WWV\0" /* WWV reference ID */ +#define WWVHREFID "WWVH" /* WWVH reference ID */ +#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */ +#define PST_PHI (10e-6) /* max clock oscillator offset */ +#define LENPST 46 /* min timecode length */ + +/* + * Unit control structure + */ +struct pstunit { + int tcswitch; /* timecode switch */ + char *lastptr; /* pointer to timecode data */ +}; + +/* + * Function prototypes + */ +static int pst_start P((int, struct peer *)); +static void pst_shutdown P((int, struct peer *)); +static void pst_receive P((struct recvbuf *)); +static void pst_poll P((int, struct peer *)); + +/* + * Transfer vector + */ +struct refclock refclock_pst = { + pst_start, /* start up driver */ + pst_shutdown, /* shut down driver */ + pst_poll, /* transmit poll message */ + noentry, /* not used (old pst_control) */ + noentry, /* initialize driver */ + noentry, /* not used (old pst_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * pst_start - open the devices and initialize data for processing + */ +static int +pst_start( + int unit, + struct peer *peer + ) +{ + register struct pstunit *up; + struct refclockproc *pp; + int fd; + char device[20]; + + /* + * Open serial port. Use CLK line discipline, if available. + */ + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct pstunit *)emalloc(sizeof(struct pstunit)))) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct pstunit)); + pp = peer->procptr; + pp->io.clock_recv = pst_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); + } + pp->unitptr = (caddr_t)up; + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, WWVREFID, 4); + peer->burst = MAXSTAGE; + return (1); +} + + +/* + * pst_shutdown - shut down the clock + */ +static void +pst_shutdown( + int unit, + struct peer *peer + ) +{ + register struct pstunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + + +/* + * pst_receive - receive data from the serial interface + */ +static void +pst_receive( + struct recvbuf *rbufp + ) +{ + register struct pstunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp trtmp; + u_long ltemp; + char ampmchar; /* AM/PM indicator */ + char daychar; /* standard/daylight indicator */ + char junque[10]; /* "yy/dd/mm/" discard */ + char info[14]; /* "frdzycchhSSFT" clock info */ + + /* + * Initialize pointers and read the timecode and timestamp + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode + + BMAX - 2 - up->lastptr, &trtmp); + *up->lastptr++ = ' '; + *up->lastptr = '\0'; + + /* + * Note we get a buffer and timestamp for each <cr>, but only + * the first timestamp is retained. + */ + if (up->tcswitch == 0) + pp->lastrec = trtmp; + up->tcswitch++; + pp->lencode = up->lastptr - pp->a_lastcode; + if (up->tcswitch < 3) + return; + + /* + * We get down to business, check the timecode format and decode + * its contents. If the timecode has invalid length or is not in + * proper format, we declare bad format and exit. + */ + if (pp->lencode < LENPST) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Timecode format: + * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx" + */ + if (sscanf(pp->a_lastcode, + "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld", + &mchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec, + &daychar, junque, &pp->day, info, <emp) != 10) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + pp->nsec *= 1000000; + + /* + * Decode synchronization, quality and last update. If + * unsynchronized, set the leap bits accordingly and exit. Once + * synchronized, the dispersion depends only on when the clock + * was last heard, which depends on the time since last update, + * as reported by the clock. + */ + if (info[9] != '8') + pp->leap = LEAP_NOTINSYNC; + if (info[12] == 'H') + memcpy((char *)&pp->refid, WWVHREFID, 4); + else + memcpy((char *)&pp->refid, WWVREFID, 4); + if (peer->stratum <= 1) + peer->refid = pp->refid; + if (ltemp == 0) + pp->lastref = pp->lastrec; + pp->disp = PST_PHI * ltemp * 60; + + /* + * Process the new sample in the median filter and determine the + * timecode timestamp. + */ + if (!refclock_process(pp)) + refclock_report(peer, CEVNT_BADTIME); + +} + + +/* + * pst_poll - called by the transmit procedure + */ +static void +pst_poll( + int unit, + struct peer *peer + ) +{ + register struct pstunit *up; + struct refclockproc *pp; + + /* + * Time to poll the clock. The PSTI/Traconex clock responds to a + * "QTQDQMT" by returning a timecode in the format specified + * above. Note there is no checking on state, since this may not + * be the only customer reading the clock. Only one customer + * need poll the clock; all others just listen in. If the clock + * becomes unreachable, declare a timeout and keep going. + */ + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + up->tcswitch = 0; + up->lastptr = pp->a_lastcode; + if (write(pp->io.fd, "QTQDQMT", 6) != 6) + refclock_report(peer, CEVNT_FAULT); + if (peer->burst > 0) + return; + if (pp->coderecv == pp->codeproc) { + refclock_report(peer, CEVNT_TIMEOUT); + return; + } + refclock_receive(peer); + record_clock_stats(&peer->srcadr, pp->a_lastcode); +#ifdef DEBUG + if (debug) + printf("pst: timecode %d %s\n", pp->lencode, + pp->a_lastcode); +#endif + peer->burst = MAXSTAGE; + pp->polls++; +} + +#else +int refclock_pst_int; +#endif /* REFCLOCK */ |