home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
x
/
xntp3.zip
/
xntpd
/
refclock_wwvb.
< prev
next >
Wrap
Text File
|
1992-08-29
|
25KB
|
916 lines
/*
* refclock_wwvb - clock driver for the Spectracom WWVB receiver
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#if defined(HPUX)
#include <termio.h>
#else
#include <sgtty.h>
#endif
#include "ntp_syslog.h"
#include "ntp_fp.h"
#include "ntp.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#if defined(REFCLOCK) && defined(WWVB)
/*
* This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
* Synchronized Clock under Unix and on a Gizmo board. There are two
* formats used by these clocks. Format 0 (zero), which is available
* with both the Netclock/2 and 8170, is in the following
* format:
*
* <cr><lf>I<sp><sp>ddd<sp>hh:mm:ss<sp><sp>TZ=nn<cr><lf>
*
* The ddd, hh, mm and ss fields show the day of year, hours, minutes
* and seconds, respectively. The nn field shows the local hour offset
* relative to UTC and should always be set to 00. The I is normally
* <sp> when the clock is synchronized and '?' when it isn't (it could
* also be a '*' if we set the time manually, but this is forbidden.
*
* Format 2 (two), which is available only with the Netclock/2 and
* specially modified 8170, is in the following format:
*
* <cr><lf>IQyy<sp>ddd<sp>hh:mm:ss.mmm<sp>LD
*
* The ddd, hh and ss fields and I are as in format 0. The yy field
* shows the year and mmm the milliseconds, respectively. The Q is
* normally <sp> when the time error is less than 1 ms and and a
* character in the set 'A'...'D' when the time error is less than 10,
* 100, 500 and greater than 500 ms respectively. The L is normally
* <sp>, but is set to 'L' early in the month of an upcoming UTC
* leap second and reset to <sp> on the first day of the following
* month. The D is set to 'S' for standard time 'I' on the day
* preceding a switch to daylight time, 'D' for daylight time and 'O'
* on the day preceding a switch to standard time. The start bit of the
* first <cr> is supposed to be synchronized to the on-time second.
*
* This driver does not need to be told which format is in use - it
* figures out which one from the length of the message. It also
* corrects for the UART delay by measuring the delay between the <cr>
* and <lf> characters. A three-stage median filter is used to reduce
* jitter and provide a dispersion measure. The driver makes no attempt
* to correct for the intrinsic jitter of the radio itself, which is a
* known problem with the older 8170 radios. When necessary, jitter
* correction can be provided by a 1-pps signal using facilities built
* into the ntp_loopfilter module.
*
* Bugs:
* The year indication so carefully provided in format 2 is not used.
*/
/*
* Definitions
*/
#define MAXUNITS 4 /* max number of WWVB units */
#ifdef GIZMO
#define WWVB232 "/dev/tty%d" /* %d is the unit number */
#else
#define WWVB232 "/dev/wwvb%d"
#define SPEED232 B9600 /* 9600 baud */
#endif
/*
* Radio interface parameters
*/
#define WWVBMAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
#define WWVBSKEWFACTOR 17 /* skew factor (for about 32 ppm) */
#define WWVBPRECISION (-13) /* precision assumed (about 100 us) */
#define WWVBREFID "WWVB" /* reference id */
#define WWVBDESCRIPTION "Spectracom WWVB Receiver" /* who we are */
#define WWVBHSREFID 0x7f7f040a /* 127.127.4.10 refid hi strata */
#define GMT 0 /* hour offset from Greenwich */
#define NCODES 3 /* stages of median filter */
#define LENWWVB0 22 /* format 0 timecode length */
#define LENWWVB2 24 /* format 2 timecode length */
#define FMTWWVBU 0 /* unknown format timecode id */
#define FMTWWVB0 1 /* format 0 timecode id */
#define FMTWWVB2 2 /* format 2 timecode id */
#define DEFFUDGETIME 0 /* default fudge time (ms) */
#define BMAX 50 /* timecode buffer length */
/*
* Hack to avoid excercising the multiplier. I have no pride.
*/
#define MULBY10(x) (((x)<<3) + ((x)<<1))
#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */
/*
* Time conversion tables imported from the library
*/
extern u_long msutotsflo[];
extern u_long msutotsfhi[];
extern u_long ustotslo[];
extern u_long ustotsmid[];
extern u_long ustotshi[];
/*
* Imported from the timer module
*/
extern u_long current_time;
extern struct event timerqueue[];
/*
* Debug flag
*/
extern int debug;
/*
* WWVB unit control structure.
*/
struct wwvbunit {
struct peer *peer; /* associated peer structure */
struct refclockio io; /* given to the I/O handler */
l_fp lastrec; /* last receive time */
l_fp lastref; /* last timecode time */
l_fp offset[NCODES]; /* recent sample offsets */
char lastcode[BMAX]; /* last timecode received */
u_char format; /* timecode format */
u_char tcswitch; /* timecode switch */
u_char lencode; /* length of last timecode */
u_long lasttime; /* last time clock heard from */
u_char unit; /* unit number for this guy */
u_char status; /* clock status */
u_char lastevent; /* last clock event */
u_char reason; /* reason for last abort */
u_char year; /* year of eternity */
u_short day; /* day of year */
u_char hour; /* hour of day */
u_char minute; /* minute of hour */
u_char second; /* seconds of minute */
u_char leap; /* leap indicators */
u_short msec; /* millisecond of second */
u_char quality; /* quality char from format 2 */
u_long yearstart; /* start of current year */
/*
* Status tallies
*/
u_long polls; /* polls sent */
u_long noreply; /* no replies to polls */
u_long coderecv; /* timecodes received */
u_long badformat; /* bad format */
u_long baddata; /* bad data */
u_long timestarted; /* time we started this */
};
/*
* Data space for the unit structures. Note that we allocate these on
* the fly, but never give them back.
*/
static struct wwvbunit *wwvbunits[MAXUNITS];
static u_char unitinuse[MAXUNITS];
/*
* Keep the fudge factors separately so they can be set even
* when no clock is configured.
*/
static l_fp fudgefactor[MAXUNITS];
static u_char stratumtouse[MAXUNITS];
static u_char sloppyclockflag[MAXUNITS];
/*
* wwvb_init - initialize internal wwvb driver data
*/
void
wwvb_init()
{
register int i;
/*
* Just zero the data arrays
*/
bzero((char *)wwvbunits, sizeof wwvbunits);
bzero((char *)unitinuse, sizeof unitinuse);
/*
* Initialize fudge factors to default.
*/
for (i = 0; i < MAXUNITS; i++) {
fudgefactor[i].l_ui = 0;
fudgefactor[i].l_uf = DEFFUDGETIME;
stratumtouse[i] = 0;
sloppyclockflag[i] = 0;
}
}
/*
* wwvb_start - open the WWVB devices and initialize data for processing
*/
int
wwvb_start(unit, peer)
u_int unit;
struct peer *peer;
{
register struct wwvbunit *wwvb;
register int i;
int fd232;
char wwvbdev[20];
unsigned int ldisc;
#if defined(HPUX)
struct termio ttyb;
#else
struct sgttyb ttyb;
#endif
void wwvb_receive();
extern int io_addclock();
extern void io_closeclock();
extern char *emalloc();
/*
* Check configuration info.
*/
if (unit >= MAXUNITS) {
syslog(LOG_ERR,
"wwvb clock: unit number %d invalid (max 3)", unit);
return 0;
}
if (unitinuse[unit]) {
syslog(LOG_ERR,
"wwvb clock: unit number %d in use", unit);
return 0;
}
/*
* Open serial port.
*/
(void) sprintf(wwvbdev, WWVB232, unit);
fd232 = open(wwvbdev, O_RDWR, 0777);
if (fd232 == -1) {
syslog(LOG_ERR,
"wwvb clock: open of %s failed: %m", wwvbdev);
return 0;
}
#if defined(HPUX)
/*
* Don't have exclusive use, but we can do cooked mode and baud rate.
*/
if (ioctl(fd232, TCGETA, (char *)&ttyb) < 0) {
syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCGETA): %m", wwvbdev);
return 0;
}
ttyb.c_iflag = (IGNBRK|ICRNL);
ttyb.c_oflag = (OPOST|ONLCR);
ttyb.c_cflag = (SPEED232|CS8|CLOCAL|CREAD|CSTOPB);
ttyb.c_lflag = ICANON;
ttyb.c_cc[VEOF] = '\0';
ttyb.c_cc[VERASE] = '\0';
ttyb.c_cc[VKILL] = '\0';
if (ioctl(fd232, TCSETA, (char *)&ttyb) < 0) {
syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCSETA): %m", wwvbdev);
return 0;
}
#else
/*
* Set for exclusive use, cooked mode and baud rate.
*/
if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
syslog(LOG_ERR,
"wwvb clock: ioctl(%s, TIOCEXCL): %m", wwvbdev);
goto screwed;
}
ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
ttyb.sg_flags = EVENP|ODDP|CRMOD|NOHANG;
if (ioctl(fd232, TIOCSETP, (char *)&ttyb) < 0) {
syslog(LOG_ERR,
"wwvb clock: ioctl(%s, TIOCSETP): %m", wwvbdev);
goto screwed;
}
#endif
/*
* Looks like this might succeed. Find memory for the
* structure. Look to see if there are any unused ones, if not
* we malloc() one.
*/
if (wwvbunits[unit] != 0) {
wwvb = wwvbunits[unit]; /* The one we want is okay */
} else {
for (i = 0; i < MAXUNITS; i++) {
if (!unitinuse[i] && wwvbunits[i] != 0)
break;
}
if (i < MAXUNITS) {
/*
* Reclaim this one
*/
wwvb = wwvbunits[i];
wwvbunits[i] = 0;
} else {
wwvb = (struct wwvbunit *)
emalloc(sizeof(struct wwvbunit));
}
}
bzero((char *)wwvb, sizeof(struct wwvbunit));
wwvbunits[unit] = wwvb;
/*
* Set up the structures
*/
wwvb->peer = peer;
wwvb->unit = (u_char)unit;
wwvb->timestarted = current_time;
wwvb->tcswitch = 0;
wwvb->io.clock_recv = wwvb_receive;
wwvb->io.srcclock = (caddr_t)wwvb;
wwvb->io.datalen = 0;
wwvb->io.fd = fd232;
/*
* Flush buffers and link in I/O list.
*/
ldisc = 0;
#if defined(HPUX)
if (ioctl(fd232, TCFLSH, 2) < 0) {
syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCFLSH): %m", wwvbdev);
#else
if (ioctl(fd232, TIOCFLUSH, (char *)&ldisc) < 0) {
syslog(LOG_ERR,
"wwvb clock: ioctl(%s, TIOCFLUSH): %m", wwvbdev);
#endif
goto screwed;
}
if (!io_addclock(&wwvb->io)) {
goto screwed;
}
/*
* All done. Initialize a few random peer variables, then
* return success.
*/
peer->precision = WWVBPRECISION;
peer->rootdelay = 0;
peer->rootdispersion = 0;
peer->stratum = stratumtouse[unit];
if (stratumtouse[unit] <= 1)
bcopy(WWVBREFID, (char *)&peer->refid, 4);
else
peer->refid = htonl(WWVBHSREFID);
unitinuse[unit] = 1;
return 1;
/*
* Something broke; abandon ship
*/
screwed:
(void) close(fd232);
return 0;
}
/*
* wwvb_shutdown - shut down a WWVB clock
*/
void
wwvb_shutdown(unit)
int unit;
{
register struct wwvbunit *wwvb;
extern void io_closeclock();
if (unit >= MAXUNITS) {
syslog(LOG_ERR,
"wwvb clock: INTERNAL ERROR, unit number %d invalid (max 4)",
unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR,
"wwvb clock: INTERNAL ERROR, unit number %d not in use", unit);
return;
}
/*
* Tell the I/O module to turn us off. We're history.
*/
wwvb = wwvbunits[unit];
io_closeclock(&wwvb->io);
unitinuse[unit] = 0;
}
/*
* wwvb_report_event - note the occurance of an event
*/
void
wwvb_report_event(wwvb, code)
struct wwvbunit *wwvb;
int code;
{
struct peer *peer;
peer = wwvb->peer;
if (wwvb->status != (u_char)code) {
wwvb->status = (u_char)code;
if (code != CEVNT_NOMINAL)
wwvb->lastevent = (u_char)code;
/*
* Should report event to trap handler in here.
* Soon...
*/
syslog(LOG_INFO, "clock %s event %x\n",
ntoa(&peer->srcadr), code);
}
}
/*
* wwvb_receive - receive data from the serial interface on a Spectracom
* clock
*/
void
wwvb_receive(rbufp)
struct recvbuf *rbufp;
{
register int i;
register struct wwvbunit *wwvb;
register u_char *dpt;
register char *cp;
register u_char *dpend;
l_fp tstmp;
u_fp dispersion;
char wwvb_process();
extern void refclock_receive();
char *ulfptoa();
/*
* Get the clock this applies to and a pointers to the data
*/
wwvb = (struct wwvbunit *)rbufp->recv_srcclock;
dpt = (u_char *)&rbufp->recv_space;
/*
* Note we get a buffer and timestamp for both a <cr> and <lf>.
* We use the difference between the <cr> and <lf> timestamps to
* determine the character time and correct the <cr> timestamp
* time to the middle of the <cr> start bit. The error is half a
* bit time +-jitter, or 52 +-13 us at 9600 bps. Note: the start
* bit is delayed 50-150 us relative to the pps pulse on a
* Netclock/2 in format 0.
*/
if (rbufp->recv_length == 1) {
if (wwvb->tcswitch == 0) {
wwvb->tcswitch = 1;
wwvb->lastrec = rbufp->recv_time;
} else {
wwvb->tcswitch = 0;
tstmp = rbufp->recv_time;
L_SUB(&tstmp, &wwvb->lastrec);
L_SUB(&wwvb->lastrec, &tstmp);
#ifdef DEBUG
if (debug)
printf("wwvb: timestamp lag %s\n",
ulfptoa(&tstmp, 6));
#endif
}
return;
}
tstmp = wwvb->lastrec;
wwvb->lastrec = rbufp->recv_time;
wwvb->tcswitch = 1;
/*
* Edit timecode to remove control chars
*/
dpend = dpt + rbufp->recv_length;
cp = wwvb->lastcode;
while (dpt < dpend)
if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
*cp = '\0';
wwvb->lencode = cp - wwvb->lastcode;
if (wwvb->lencode == 0) return;
/*
* Note the receive timestamp is determined at the first <cr>;
* however, we don't get the timecode for that timestamp until
* the next <cr>. We assume that, if we happen to come up
* during a timestamp, the format and data checks will toss it
* out.
*/
#ifdef DEBUG
if (debug)
printf("wwvb: timecode %d %s\n",
wwvb->lencode, wwvb->lastcode);
#endif
/*
* We get down to business, check the timecode format and decode
* its contents. This code checks for and decodes both format 0
* and format 2 and need not be told which in advance.
*/
cp = wwvb->lastcode;
wwvb->leap = 0;
wwvb->format = FMTWWVBU;
if (wwvb->lencode == LENWWVB0) {
/*
* Check timecode format 0
*/
if (cp[1] != ' ' || /* <sp> separator */
cp[2] != ' ' || /* <sp> separator */
!isdigit(cp[3]) || /* day of year */
!isdigit(cp[4]) ||
!isdigit(cp[5]) ||
cp[6] != ' ' || /* <sp> */
!isdigit(cp[7]) || /* hours */
!isdigit(cp[8]) ||
cp[9] != ':' || /* : separator */
!isdigit(cp[10]) || /* minutes */
!isdigit(cp[11]) ||
cp[12] != ':' || /* : separator */
!isdigit(cp[13]) || /* seconds */
!isdigit(cp[14]) ||
cp[15] != ' ' || /* <sp> separator */
cp[16] != ' ' || /* <sp> separator */
cp[17] != 'T' || /* T separator */
cp[18] != 'Z' || /* Z separator */
cp[19] != '=' || /* = separator */
!isdigit(cp[20]) || /* time zone */
!isdigit(cp[21])) {
wwvb->badformat++;
wwvb_report_event(wwvb, CEVNT_BADREPLY);
return;
}
else wwvb->format = FMTWWVB0;
/*
* Convert format 0 and check values
*/
wwvb->year = 0; /* fake */
wwvb->day = cp[3] - '0';
wwvb->day = MULBY10(wwvb->day) + cp[4] - '0';
wwvb->day = MULBY10(wwvb->day) + cp[5] - '0';
wwvb->hour = MULBY10(cp[7] - '0') + cp[8] - '0';
wwvb->minute = MULBY10(cp[10] - '0') + cp[11] - '0';
wwvb->second = MULBY10(cp[13] - '0') + cp[14] - '0';
wwvb->msec = 0;
if (cp[0] != ' ')
wwvb->leap = LEAP_NOTINSYNC;
else
wwvb->lasttime = current_time;
if (wwvb->day < 1 || wwvb->day > 366) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADDATE);
return;
}
if (wwvb->hour > 23 || wwvb->minute > 59
|| wwvb->second > 59) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADTIME);
return;
}
} else if (wwvb->lencode == LENWWVB2) {
/*
* Check timecode format 2
*/
if (!isdigit(cp[2]) || /* year of century */
!isdigit(cp[3]) ||
cp[4] != ' ' || /* <sp> separator */
!isdigit(cp[5]) || /* day of year */
!isdigit(cp[6]) ||
!isdigit(cp[7]) ||
cp[8] != ' ' || /* <sp> separator */
!isdigit(cp[9]) || /* hour */
!isdigit(cp[10]) ||
cp[11] != ':' || /* : separator */
!isdigit(cp[12]) || /* minute */
!isdigit(cp[13]) ||
cp[14] != ':' || /* : separator */
!isdigit(cp[15]) || /* second */
!isdigit(cp[16]) ||
cp[17] != '.' || /* . separator */
!isdigit(cp[18]) || /* millisecond */
!isdigit(cp[19]) ||
!isdigit(cp[20]) ||
cp[21] != ' ') { /* <sp> separator */
wwvb->badformat++;
wwvb_report_event(wwvb, CEVNT_BADREPLY);
return;
}
else wwvb->format = FMTWWVB2;
/*
* Convert format 2 and check values
*/
wwvb->year = MULBY10(cp[2] - '0') + cp[3] - '0';
wwvb->day = cp[5] - '0';
wwvb->day = MULBY10(wwvb->day) + cp[6] - '0';
wwvb->day = MULBY10(wwvb->day) + cp[7] - '0';
wwvb->hour = MULBY10(cp[9] - '0') + cp[10] - '0';
wwvb->minute = MULBY10(cp[12] - '0') + cp[13] - '0';
wwvb->second = MULBY10(cp[15] - '0') + cp[16] - '0';
wwvb->msec = cp[18] - '0';
wwvb->msec = MULBY10(wwvb->msec) + cp[19] - '0';
wwvb->msec = MULBY10(wwvb->msec) + cp[20] - '0';
wwvb->quality = cp[1];
if (cp[0] != ' ')
wwvb->leap = LEAP_NOTINSYNC;
else if (cp[1] == ' ')
wwvb->lasttime = current_time;
if (cp[22] == 'L')
wwvb->leap = LEAP_ADDSECOND;
if (wwvb->day < 1 || wwvb->day > 366) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADDATE);
return;
}
if (wwvb->hour > 23 || wwvb->minute > 59
|| wwvb->second > 59) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADTIME);
return;
}
} else {
wwvb->badformat++;
wwvb_report_event(wwvb, CEVNT_BADREPLY);
return;
}
/*
* Now, compute the reference time value. Use the heavy
* machinery for the seconds and the millisecond field for the
* fraction when present.
*
* this code does not yet know how to do the years
*/
if (!clocktime(wwvb->day, wwvb->hour, wwvb->minute,
wwvb->second, GMT, tstmp.l_ui,
&wwvb->yearstart, &wwvb->lastref.l_ui)) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADTIME);
return;
}
MSUTOTSF(wwvb->msec, wwvb->lastref.l_uf);
i = ((int)(wwvb->coderecv)) % NCODES;
wwvb->offset[i] = wwvb->lastref;
L_SUB(&wwvb->offset[i], &tstmp);
if (wwvb->coderecv == 0)
for (i = 1; i < NCODES; i++)
wwvb->offset[i] = wwvb->offset[0];
wwvb->coderecv++;
/*
* Process the median filter, add the fudge factor and pass the
* offset and dispersion along. We use lastrec as both the
* reference time and receive time in order to avoid being cute,
* like setting the reference time later than the receive time,
* which may cause a paranoid protocol module to chuck out the
* data.
*/
if (!wwvb_process(wwvb, &tstmp, &dispersion)) {
wwvb->baddata++;
wwvb_report_event(wwvb, CEVNT_BADTIME);
return;
}
L_ADD(&tstmp, &(fudgefactor[wwvb->unit]));
refclock_receive(wwvb->peer, &tstmp, GMT, dispersion,
&wwvb->lastrec, &wwvb->lastrec, wwvb->leap);
}
/*
* wwvb_process - process a pile of samples from the clock
*/
char
wwvb_process(wwvb, offset, dispersion)
struct wwvbunit *wwvb;
l_fp *offset;
u_fp *dispersion;
{
register int i, j;
register u_long tmp_ui, tmp_uf;
int not_median1, not_median2, median;
u_fp disp_tmp, disp_tmp2;
/*
* This code implements a three-stage median filter. First, we
* check if the samples are within 125 ms of each other. If not,
* dump the sample set. We take the median of the three offsets
* and use that as the sample offset. We take the maximum
* difference and use that as the sample dispersion. There
* probably is not much to be gained by a longer filter, since
* the clock filter in ntp_proto should do its thing.
*/
disp_tmp2 = 0;
for (i = 0; i < NCODES-1; i++) {
for (j = i+1; j < NCODES; j++) {
tmp_ui = wwvb->offset[i].l_ui;
tmp_uf = wwvb->offset[i].l_uf;
M_SUB(tmp_ui, tmp_uf, wwvb->offset[j].l_ui,
wwvb->offset[j].l_uf);
if (M_ISNEG(tmp_ui, tmp_uf)) {
M_NEG(tmp_ui, tmp_uf);
}
if (tmp_ui != 0 || tmp_uf > CODEDIFF) {
return 0;
}
disp_tmp = MFPTOFP(0, tmp_uf);
if (disp_tmp > disp_tmp2) {
disp_tmp2 = disp_tmp;
not_median1 = i;
not_median2 = j;
}
}
}
/*
* It seems as if all are within 125 ms of each other.
* Now to determine the median of the three. Whlie the
* 125 ms check was going on, we also subtly catch the
* dispersion and set-up for a very easy median calculation.
* The largest difference between any two samples constitutes
* the dispersion. The sample not involve in the dispersion is
* the median sample. EASY!
*/
if (wwvb->lasttime == 0 || disp_tmp2 > WWVBMAXDISPERSE)
disp_tmp2 = WWVBMAXDISPERSE;
if (not_median1 == 0) {
if (not_median2 == 1)
median = 2;
else
median = 1;
} else {
median = 0;
}
*offset = wwvb->offset[median];
*dispersion = disp_tmp2;
return 1;
}
/*
* wwvb_poll - called by the transmit procedure
*/
void
wwvb_poll(unit, peer)
int unit;
char *peer;
{
struct wwvbunit *wwvb;
/*
* Time to request a time code. The Spectracom clock responds
* to a "T" sent to it by returning a time code as stated in the
* comments in the header. 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 (unit >= MAXUNITS) {
syslog(LOG_ERR,
"wwvb clock poll: INTERNAL: unit %d invalid", unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR,
"wwvb clock poll: INTERNAL: unit %d unused", unit);
return;
}
wwvb = wwvbunits[unit];
if ((current_time - wwvb->lasttime) > 150) {
wwvb->noreply++;
wwvb_report_event(wwvbunits[unit], CEVNT_TIMEOUT);
}
if (write(wwvb->io.fd, "T", 1) != 1) {
syslog(LOG_ERR,
"wwvb clock: write fails to unit %d: %m", wwvb->unit);
wwvb_report_event(wwvb, CEVNT_FAULT);
} else {
wwvb->polls++;
}
}
/*
* wwvb_control - set fudge factors, return statistics
*/
void
wwvb_control(unit, in, out)
u_int unit;
struct refclockstat *in;
struct refclockstat *out;
{
register struct wwvbunit *wwvb;
if (unit >= MAXUNITS) {
syslog(LOG_ERR,
"wwvb clock: unit %d invalid (max %d)", unit, MAXUNITS-1);
return;
}
if (in != 0) {
if (in->haveflags & CLK_HAVETIME1)
fudgefactor[unit] = in->fudgetime1;
if (in->haveflags & CLK_HAVEVAL1) {
stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
if (unitinuse[unit]) {
struct peer *peer;
/*
* Should actually reselect clock, but
* will wait for the next timecode
*/
wwvb = wwvbunits[unit];
peer = wwvb->peer;
peer->stratum = stratumtouse[unit];
if (stratumtouse[unit] <= 1)
bcopy(WWVBREFID, (char *)&peer->refid,
4);
else
peer->refid = htonl(WWVBHSREFID);
}
}
if (in->haveflags & CLK_HAVEFLAG1) {
sloppyclockflag[unit] = in->flags & CLK_FLAG1;
}
}
if (out != 0) {
out->type = REFCLK_WWVB_SPECTRACOM;
out->haveflags
= CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
out->clockdesc = WWVBDESCRIPTION;
out->fudgetime1 = fudgefactor[unit];
out->fudgetime2.l_ui = 0;
out->fudgetime2.l_uf = 0;
out->fudgeval1 = (long)stratumtouse[unit];
out->fudgeval2 = 0;
out->flags = sloppyclockflag[unit];
if (unitinuse[unit]) {
wwvb = wwvbunits[unit];
out->lencode = wwvb->lencode;
out->lastcode = wwvb->lastcode;
out->timereset = current_time - wwvb->timestarted;
out->polls = wwvb->polls;
out->noresponse = wwvb->noreply;
out->badformat = wwvb->badformat;
out->baddata = wwvb->baddata;
out->lastevent = wwvb->lastevent;
out->currentstatus = wwvb->status;
} else {
out->lencode = 0;
out->lastcode = "";
out->polls = out->noresponse = 0;
out->badformat = out->baddata = 0;
out->timereset = 0;
out->currentstatus = out->lastevent = CEVNT_NOMINAL;
}
}
}
/*
* wwvb_buginfo - return clock dependent debugging info
*/
void
wwvb_buginfo(unit, bug)
int unit;
register struct refclockbug *bug;
{
register struct wwvbunit *wwvb;
register int i;
register int n;
if (unit >= MAXUNITS) {
syslog(LOG_ERR, "wwvb clock: unit %d invalid (max %d)",
unit, MAXUNITS-1);
return;
}
if (!unitinuse[unit])
return;
wwvb = wwvbunits[unit];
bug->nvalues = 11;
bug->ntimes = 5;
if (wwvb->lasttime != 0)
bug->values[0] = current_time - wwvb->lasttime;
else
bug->values[0] = 0;
bug->values[1] = (u_long)wwvb->reason;
bug->values[2] = (u_long)wwvb->year;
bug->values[3] = (u_long)wwvb->day;
bug->values[4] = (u_long)wwvb->hour;
bug->values[5] = (u_long)wwvb->minute;
bug->values[6] = (u_long)wwvb->second;
bug->values[7] = (u_long)wwvb->msec;
bug->values[8] = wwvb->noreply;
bug->values[9] = wwvb->yearstart;
bug->values[10] = wwvb->quality;
bug->stimes = 0x1c;
bug->times[0] = wwvb->lastref;
bug->times[1] = wwvb->lastrec;
bug->times[2] = wwvb->offset[0];
bug->times[3] = wwvb->offset[1];
bug->times[4] = wwvb->offset[2];
}
#endif