freebsd-dev/usr.sbin/xntpd/xntpd/refclock_chu.c
1994-04-21 00:33:33 +00:00

1160 lines
31 KiB
C

/*
* refclock_chu - clock driver for the CHU time code
*/
#if defined(REFCLOCK) && (defined(CHU) || defined(CHUCLK) || defined(CHUPPS))
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#if defined(HAVE_BSD_TTYS)
#include <sgtty.h>
#endif /* HAVE_BSD_TTYS */
#if defined(HAVE_SYSV_TTYS)
#include <termio.h>
#endif /* HAVE_SYSV_TTYS */
#if defined(HAVE_TERMIOS)
#include <termios.h>
#endif
#if defined(STREAM)
#include <stropts.h>
#endif /* STREAM */
#if defined (CHUPPS)
#include <sys/ppsclock.h>
#endif /* CHUPPS */
#include <sys/chudefs.h>
#include "ntp_stdlib.h"
/*
* The CHU time signal includes a time code which is modulated at the
* standard Bell 103 frequencies (i.e. mark=2225Hz, space=2025Hz).
* and formatted into 8 bit characters with one start bit and two
* stop bits. The time code is composed of 10 8-bit characters.
* The second 5 bytes of the timecode are a redundancy check, and
* are a copy of the first 5 bytes.
*
* It is assumed that you have built or modified a Bell 103 standard
* modem, attached the input to the output of a radio and cabled the
* output to a serial port on your computer, i.e. what you are receiving
* is essentially the output of your radio. It is also assumed you have
* installed a special CHU line discipline to condition the output from
* the terminal driver and take accurate time stamps.
*
* There are two types of timecodes. One is sent in the 32nd
* through 39th second of the minute.
*
* 6dddhhmmss6dddhhmmss
*
* where ddd is the day of the year, hh is the hour (in UTC), mm is
* the minute and ss the second. The 6 is a constant. Note that
* the code is sent twice.
*
* The second sort of timecode is sent only during the 31st second
* past the minute.
*
* xdyyyyttabXDYYYYTTAB
*
* In this case, the second part of the code is the one's complement
* of the code. This differentiates it from the other timecode
* format.
*
* d is the absolute value of DUT (in tenths of a second). yyyy
* is the year. tt is the difference between UTC and TAI. a is
* a canadian daylight time flag and b is a serial number.
* x is a bitwise field. The least significant bit of x is
* one if DUT is negative. The 2nd bit is set if a leap second
* will be added at the next opportunity. The 3rd bit is set if
* a leap second will be deleted at the next opportunity.
* The 4th bit is an even parity bit for the other three bits
* in this nibble.
*
* The start bit in each character has a precise relationship to
* the on-time second. Most often UART's synchronize themselves to the
* start bit and will post an interrupt at the center of the first stop
* bit. Thus each character's interrupt should occur at a fixed offset
* from the on-time second. This means that a timestamp taken at the
* arrival of each character in the code will provide an independent
* estimate of the offset. Since there are 10 characters in the time
* code and the code is sent 9 times per minute, this means you potentially
* get 90 offset samples per minute. Much of the code in here is dedicated
* to producing a single offset estimate from these samples.
*
* A note about the line discipline. It is possible to receive the
* CHU time code in raw mode, but this has disadvantages. In particular,
* this puts a lot of code between the interrupt and the time you freeze
* a time stamp, decreasing precision. It is also expensive in terms of
* context switches, and made even more expensive by the way I do I/O.
* Worse, since you are listening directly to the output of your radio,
* CHU is noisy and will make you spend a lot of time receiving noise.
*
* The line discipline fixes a lot of this. It knows that the CHU time
* code consists of 10 bytes which arrive with an intercharacter
* spacing of about 37 ms, and that the data is BCD, and filters on this
* basis. It delivers block of ten characters plus their associated time
* stamps all at once. The time stamps are hence about as accurate as
* a Unix machine can get them, and much of the noise disappears in the
* kernel with no context switching cost.
*
* The kernel module also will insure that the packets that are
* delivered have the correct redundancy bytes, and will return
* a flag in chutype to differentiate one sort of packet from
* the other.
*/
/*
* Definitions
*/
#define MAXUNITS 4 /* maximum number of CHU units permitted */
#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
#define SPEED232 B300 /* uart speed (300 baud) */
#define NCHUCODES 8 /* expect 8 CHU codes per minute */
#define CHULDISC 10 /* XXX temp CHU line discipline */
/*
* To compute a quality for the estimate (a pseudo dispersion) we add a
* fixed 10 ms for each missing code in the minute and add to this
* the sum of the differences between the remaining offsets and the
* estimated sample offset.
*/
#define CHUDELAYPENALTY 0x0000028f
/*
* Other constant stuff
*/
#define CHUPRECISION (-9) /* what the heck */
#define CHUREFID "CHU\0"
#define CHUDESCRIPTION "Direct synchronized to CHU timecode"
#define CHUHSREFID 0x7f7f070a /* 127.127.7.10 refid for hi strata */
/*
* Default fudge factors
*/
#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
/*
* Hacks to avoid excercising the multiplier. I have no pride.
*/
#define MULBY10(x) (((x)<<3) + ((x)<<1))
#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
#define MULBY24(x) (((x)<<4) + ((x)<<3))
/*
* Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
* as an l_fp fraction, NZPOBITS is the number of significant bits
* in ZEROPTONE.
*/
#define ZEROPTONE 0x1999999a
#define NZPOBITS 29
static char hexstring[]="0123456789abcdef";
/*
* CHU unit control structure.
*/
struct chuunit {
struct peer *peer; /* associated peer structure */
struct event chutimer; /* timeout timer structure */
struct refclockio chuio; /* given to the I/O handler */
l_fp offsets[NCHUCODES]; /* offsets computed from each code */
l_fp rectimes[NCHUCODES]; /* times we received this stuff */
U_LONG reftimes[NCHUCODES]; /* time of last code received */
u_char lastcode[NCHUCHARS*4]; /* last code we received */
u_char asciicode[NCHUCHARS*4+1]; /* last code translated to ascii */
u_char expect; /* the next offset expected */
u_char unit; /* unit number for this guy */
u_short haveoffset; /* flag word indicating valid offsets */
u_short flags; /* operational flags */
u_char status; /* clock status */
u_char lastevent; /* last clock event */
u_char unused[2];
U_LONG lastupdate; /* last time data received */
U_LONG responses; /* number of responses */
U_LONG badformat; /* number of bad format responses */
U_LONG baddata; /* number of invalid time codes */
U_LONG timestarted; /* time we started this */
u_char leap; /* leap status */
};
#define CHUTIMERSET 0x1 /* timer is set to fire */
/*
* The CHU table. This gives the expected time of arrival of each
* character after the on-time second and is computed as follows:
* The CHU time code is sent at 300 bps. Your average UART will
* synchronize at the edge of the start bit and will consider the
* character complete at the middle of the first stop bit, i.e.
* 0.031667 ms later (some UARTS may complete the character at the
* end of the stop bit instead of the middle, but you can fudge this).
* Thus the expected time of each interrupt is the start bit time plus
* 0.031667 seconds. These times are in chutable[]. To this we add
* such things as propagation delay and delay fudge factor.
*/
#define CHARDELAY 0x081b4e82
static U_LONG chutable[NCHUCHARS] = {
0x22222222 + CHARDELAY, /* 0.1333333333 */
0x2b851eb8 + CHARDELAY, /* 0.170 (exactly) */
0x34e81b4e + CHARDELAY, /* 0.2066666667 */
0x3f92c5f9 + CHARDELAY, /* 0.2483333333 */
0x47ae147b + CHARDELAY, /* 0.280 (exactly) */
0x51111111 + CHARDELAY, /* 0.3166666667 */
0x5a740da7 + CHARDELAY, /* 0.3533333333 */
0x63d70a3d + CHARDELAY, /* 0.390 (exactly) */
0x6d3a06d4 + CHARDELAY, /* 0.4266666667 */
0x769d0370 + CHARDELAY, /* 0.4633333333 */
};
/*
* Data space for the unit structures. Note that we allocate these on
* the fly, but never give them back.
*/
static struct chuunit *chuunits[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 propagation_delay[MAXUNITS];
static l_fp fudgefactor[MAXUNITS];
static l_fp offset_fudge[MAXUNITS];
static u_char stratumtouse[MAXUNITS];
static u_char sloppyclockflag[MAXUNITS];
/*
* We keep track of the start of the year, watching for changes.
* We also keep track of whether the year is a leap year or not.
* All because stupid CHU doesn't include the year in the time code.
*/
static U_LONG yearstart;
/*
* Imported from the timer module
*/
extern U_LONG current_time;
extern struct event timerqueue[];
/*
* Imported from ntp_loopfilter module
*/
extern int fdpps; /* pps file descriptor */
/*
* Imported from ntpd module
*/
extern int debug; /* global debug flag */
/*
* Event reporting. This optimizes things a little.
*/
#define chu_event(chu, evcode) \
do { \
if ((chu)->status != (u_char)(evcode)) \
chu_report_event((chu), (evcode)); \
} while (0)
static void chu_init P((void));
static int chu_start P((u_int, struct peer *));
static void chu_shutdown P((int));
static void chu_report_event P((struct chuunit *, int));
static void chu_receive P((struct recvbuf *));
static void chu_process P((struct chuunit *));
static void chu_poll P((int, struct peer *));
static void chu_control P((u_int, struct refclockstat *, struct refclockstat *));
static void chu_timeout P((struct peer *));
/*
* Transfer vector
*/
struct refclock refclock_chu = {
chu_start, chu_shutdown, chu_poll,
chu_control, chu_init, noentry, NOFLAGS
};
/*
* chu_init - initialize internal chu driver data
*/
static void
chu_init()
{
register int i;
/*
* Just zero the data arrays
*/
memset((char *)chuunits, 0, sizeof chuunits);
memset((char *)unitinuse, 0, sizeof unitinuse);
/*
* Initialize fudge factors to default.
*/
for (i = 0; i < MAXUNITS; i++) {
propagation_delay[i].l_ui = 0;
propagation_delay[i].l_uf = DEFPROPDELAY;
fudgefactor[i].l_ui = 0;
fudgefactor[i].l_uf = DEFFILTFUDGE;
offset_fudge[i] = propagation_delay[i];
L_ADD(&offset_fudge[i], &fudgefactor[i]);
stratumtouse[i] = 0;
sloppyclockflag[i] = 0;
}
}
/*
* chu_start - open the CHU device and initialize data for processing
*/
static int
chu_start(unit, peer)
u_int unit;
struct peer *peer;
{
register struct chuunit *chu;
register int i;
int fd232;
char chudev[20];
l_fp ts;
/*
* Check configuration info
*/
if (unit >= MAXUNITS) {
syslog(LOG_ERR, "chu_start: unit %d invalid", unit);
return 0;
}
if (unitinuse[unit]) {
syslog(LOG_ERR, "chu_start: unit %d in use", unit);
return 0;
}
/*
* Open serial port
*/
(void) sprintf(chudev, CHUDEV, unit);
fd232 = open(chudev, O_RDONLY, 0777);
if (fd232 == -1) {
syslog(LOG_ERR, "chu_start: open of %s: %m", chudev);
return 0;
}
#if defined(HAVE_SYSV_TTYS)
/*
* System V serial line parameters (termio interface)
*/
CHU SUPPORT NOT AVAILABLE IN TERMIO INTERFACE
#endif /* HAVE_SYSV_TTYS */
#if defined(HAVE_TERMIOS)
/*
* POSIX serial line parameters (termios interface)
*
* The CHUCLK support uses a 300-baud modem and level converter
* (gadget box). It requires the chu_clk streams module and
* SunOS 4.1.1 or later.
*
* The CHUPPS option provides timestamping at the driver level.
* It uses a 1-pps signal and level converter (gadget box) and
* requires the ppsclock streams module and SunOS 4.1.1 or
* later.
*/
{ struct termios ttyb, *ttyp;
ttyp = &ttyb;
if (tcgetattr(fd232, ttyp) < 0) {
syslog(LOG_ERR,
"chu_start: tcgetattr(%s): %m", chudev);
goto screwed;
}
ttyp->c_iflag = IGNBRK|IGNPAR;
ttyp->c_oflag = 0;
ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
ttyp->c_lflag = 0;
ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
ttyp->c_cc[VMIN] = 1;
ttyp->c_cc[VTIME] = 0;
if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
syslog(LOG_ERR,
"chu_start: tcsetattr(%s): %m", chudev);
goto screwed;
}
if (tcflush(fd232, TCIOFLUSH) < 0) {
syslog(LOG_ERR,
"chu_start: tcflush(%s): %m", chudev);
goto screwed;
}
}
#endif /* HAVE_TERMIOS */
#ifdef STREAM
while (ioctl(fd232, I_POP, 0 ) >= 0) ;
if (ioctl(fd232, I_PUSH, "chu" ) < 0) {
syslog(LOG_ERR,
"chu_start: ioctl(%s, I_PUSH, chu): %m", chudev);
goto screwed;
}
#if defined(CHUPPS)
if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
syslog(LOG_ERR,
"chu_start: ioctl(%s, I_PUSH, ppsclock): %m", chudev);
else
fdpps = fd232;
#endif /* CHUPPS */
#endif /* STREAM */
#if defined(HAVE_BSD_TTYS)
/*
* 4.3bsd serial line parameters (sgttyb interface)
*
* The CHUCLK support uses a 300-baud modem and level converter
* (gadget box). It requires the chu_clk streams module and
* 4.3bsd or later.
*/
{ struct sgttyb ttyb;
int ldisc = CHULDISC;
if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
syslog(LOG_ERR,
"chu_start: ioctl(%s, TIOCGETP): %m", chudev);
goto screwed;
}
ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
ttyb.sg_erase = ttyb.sg_kill = '\r';
ttyb.sg_flags = RAW;
if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
syslog(LOG_ERR,
"chu_start: ioctl(%s, TIOCSETP): %m", chudev);
goto screwed;
}
if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
syslog(LOG_ERR,
"chu_start: ioctl(%s, TIOCSETD): %m",chudev);
goto screwed;
}
}
#endif /* HAVE_BSD_TTYS */
/*
* Allocate unit structure
*/
if (chuunits[unit] != 0) {
chu = chuunits[unit]; /* The one we want is okay */
} else {
for (i = 0; i < MAXUNITS; i++) {
if (!unitinuse[i] && chuunits[i] != 0)
break;
}
if (i < MAXUNITS) {
/*
* Reclaim this one
*/
chu = chuunits[i];
chuunits[i] = 0;
} else {
chu = (struct chuunit *)emalloc(sizeof(struct chuunit));
}
}
memset((char *)chu, 0, sizeof(struct chuunit));
chuunits[unit] = chu;
/*
* Set up the structure
*/
chu->peer = peer;
chu->unit = (u_char)unit;
chu->timestarted = current_time;
chu->chutimer.peer = (struct peer *)chu;
chu->chutimer.event_handler = chu_timeout;
chu->chuio.clock_recv = chu_receive;
chu->chuio.srcclock = (caddr_t)chu;
chu->chuio.datalen = sizeof(struct chucode);
chu->chuio.fd = fd232;
/*
* Initialize the year from the system time in case this is the
* first open.
*/
get_systime(&ts);
yearstart = calyearstart(ts.l_ui);
if (!io_addclock(&chu->chuio)) {
goto screwed;
}
/*
* All done. Initialize a few random peer variables, then
* return success.
*/
peer->precision = CHUPRECISION;
peer->rootdelay = 0;
peer->rootdispersion = 0;
peer->stratum = stratumtouse[unit];
if (stratumtouse[unit] <= 1)
memmove((char *)&peer->refid, CHUREFID, 4);
else
peer->refid = htonl(CHUHSREFID);
unitinuse[unit] = 1;
return (1);
/*
* Something broke; abandon ship.
*/
screwed:
(void) close(fd232);
return (0);
}
/*
* chu_shutdown - shut down a CHU clock
*/
static void
chu_shutdown(unit)
int unit;
{
register struct chuunit *chu;
if (unit >= MAXUNITS) {
syslog(LOG_ERR, "chu_shutdown: unit %d invalid", unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR, "chu_shutdown: unit %d not in use", unit);
return;
}
/*
* Tell the I/O module to turn us off, and dequeue timer
* if any. We're history.
*/
chu = chuunits[unit];
if (chu->flags & CHUTIMERSET)
TIMER_DEQUEUE(&chu->chutimer);
io_closeclock(&chu->chuio);
unitinuse[unit] = 0;
}
/*
* chu_report_event - record an event and report it
*/
static void
chu_report_event(chu, code)
struct chuunit *chu;
int code;
{
/*
* Trap support isn't up to handling this, so just
* record it.
*/
if (chu->status != (u_char)code) {
chu->status = (u_char)code;
if (code != CEVNT_NOMINAL)
chu->lastevent = (u_char)code;
}
}
/*
* chu_receive - receive data from a CHU clock, do format checks and compute
* an estimate from the sample data
*/
static void
chu_receive(rbufp)
struct recvbuf *rbufp;
{
register int i;
register U_LONG date_ui;
register U_LONG tmp;
register u_char *code;
register struct chuunit *chu;
register struct chucode *chuc;
int isneg;
U_LONG reftime;
l_fp off[NCHUCHARS];
int day, hour, minute, second;
/*
* Do a length check on the data. Should be what we asked for.
*/
if (rbufp->recv_length != sizeof(struct chucode)) {
syslog(LOG_ERR, "chu_receive: received %d bytes, expected %d",
rbufp->recv_length, sizeof(struct chucode));
return;
}
/*
* Get the clock this applies to and a pointer to the data
*/
chu = (struct chuunit *)rbufp->recv_srcclock;
chuc = (struct chucode *)&rbufp->recv_space;
chu->responses++;
chu->lastupdate = current_time;
/*
* Just for fun, we can debug the whole frame if
* we want.
*/
#ifdef CHU_DEBUG
syslog(LOG_DEBUG, "CHU %s packet:", (chuc->chutype == CHU_YEAR)?
"year":"time");
for (i=0; i < NCHUCHARS; i++) {
char c[64];
sprintf(c,"%c%c %s",hexstring[chuc->codechars[i]&0xf],
hexstring[chuc->codechars[i]>>4],
ctime(&(chuc->codetimes[i].tv_sec)));
c[strlen(c)-1]=0; /* ctime() adds a damn \n */
syslog(LOG_DEBUG, "%s .%06d", c, chuc->codetimes[i].tv_usec);
}
#endif
/*
* At this point we're assured that both halves of the
* data match because of what the kernel has done.
* But there's more than one data format. We need to
* check chutype to see what to do now. If it's a
* year packet, then we fiddle with it specially.
*/
if (chuc->chutype == CHU_YEAR)
{
u_char leapbits,parity;
/*
* Break out the code into the BCD nibbles.
* Put it in the half of lastcode.
*/
code = chu->lastcode;
code += 2*NCHUCHARS;
for (i = 0; i < NCHUCHARS; i++) {
*code++ = chuc->codechars[i] & 0xf;
*code++ = (chuc->codechars[i] >> 4) & 0xf;
}
leapbits = chuc->codechars[0]&0xf;
/*
* Now make sure that the leap nibble
* is even parity.
*/
parity = (leapbits ^ (leapbits >> 2))&0x3;
parity = (parity ^ (parity>>1))&0x1;
if (parity)
{
chu->badformat++;
chu_event(chu, CEVNT_BADREPLY);
return;
}
/*
* This just happens to work. :-)
*/
chu->leap = (leapbits >> 1) & 0x3;
return;
}
if (chuc->chutype != CHU_TIME)
{
chu->badformat++;
chu_event(chu, CEVNT_BADREPLY);
return;
}
/*
* Break out the code into the BCD nibbles. Only need to fiddle
* with the first half since both are identical. Note the first
* BCD character is the low order nibble, the second the high order.
*/
code = chu->lastcode;
for (i = 0; i < NCHUCHARS; i++) {
*code++ = chuc->codechars[i] & 0xf;
*code++ = (chuc->codechars[i] >> 4) & 0xf;
}
/*
* Format check. Make sure the two halves match.
* There's really no need for this, but it can't hurt.
*/
for (i = 0; i < NCHUCHARS/2; i++)
if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
chu->badformat++;
chu_event(chu, CEVNT_BADREPLY);
return;
}
/*
* If the first nibble isn't a 6, we're up the creek
*/
code = chu->lastcode;
if (*code++ != 6) {
chu->badformat++;
chu_event(chu, CEVNT_BADREPLY);
return;
}
/*
* Collect the day, the hour, the minute and the second.
*/
day = *code++;
day = MULBY10(day) + *code++;
day = MULBY10(day) + *code++;
hour = *code++;
hour = MULBY10(hour) + *code++;
minute = *code++;
minute = MULBY10(minute) + *code++;
second = *code++;
second = MULBY10(second) + *code++;
/*
* Sanity check the day and time. Note that this
* only occurs on the 32st through the 39th second
* of the minute.
*/
if (day < 1 || day > 366
|| hour > 23 || minute > 59
|| second < 32 || second > 39) {
chu->baddata++;
if (day < 1 || day > 366) {
chu_event(chu, CEVNT_BADDATE);
} else {
chu_event(chu, CEVNT_BADTIME);
}
return;
}
/*
* Compute the NTP date from the input data and the
* receive timestamp. If this doesn't work, mark the
* date as bad and forget it.
*/
if (!clocktime(day, hour, minute, second, 0,
rbufp->recv_time.l_ui, &yearstart, &reftime)) {
chu_event(chu, CEVNT_BADDATE);
return;
}
date_ui = reftime;;
/*
* We've now got the integral seconds part of the time code (we hope).
* The fractional part comes from the table. We next compute
* the offsets for each character.
*/
for (i = 0; i < NCHUCHARS; i++) {
register U_LONG tmp2;
off[i].l_ui = date_ui;
off[i].l_uf = chutable[i];
tmp = chuc->codetimes[i].tv_sec + JAN_1970;
TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
}
if (!sloppyclockflag[chu->unit]) {
u_short ord[NCHUCHARS];
/*
* In here we assume the clock has adequate bits
* to take timestamps with reasonable accuracy.
* Note that the time stamps may contain errors
* for a couple of reasons. Timing is actually
* referenced to the start bit in each character
* in the time code. If this is obscured by static
* you can still get a valid character but have the
* timestamp offset by +-1.5 ms. Also, we may suffer
* from interrupt delays if the interrupt is being
* held off when the character arrives. Note the
* latter error is always in the form of a delay.
*
* After fiddling I arrived at the following scheme.
* We sort the times into order by offset. We then
* drop the most positive 2 offset values (which may
* correspond to a character arriving early due to
* static) and the most negative 4 (which may correspond
* to delayed characters, either from static or from
* interrupt latency). We then take the mean of the
* remaining 4 offsets as our estimate.
*/
/*
* Set up the order array.
*/
for (i = 0; i < NCHUCHARS; i++)
ord[i] = (u_short)i;
/*
* Sort them into order. Reuse variables with abandon.
*/
for (tmp = 0; tmp < (NCHUCHARS-1); tmp++) {
for (i = (int)tmp+1; i < NCHUCHARS; i++) {
if (!L_ISGEQ(&off[ord[i]], &off[ord[tmp]])) {
date_ui = (U_LONG)ord[i];
ord[i] = ord[tmp];
ord[tmp] = (u_short)date_ui;
}
}
}
/*
* Done the sort. We drop 0, 1, 2 and 3 at the negative
* end, and 8 and 9 at the positive. Take the sum of
* 4, 5, 6 and 7.
*/
date_ui = off[ord[4]].l_ui;
tmp = off[ord[4]].l_uf;
for (i = 5; i <= 7; i++)
M_ADD(date_ui, tmp, off[ord[i]].l_ui, off[ord[i]].l_uf);
/*
* Round properly, then right shift two bits for the
* divide by four.
*/
if (tmp & 0x2)
M_ADDUF(date_ui, tmp, 0x4);
M_RSHIFT(date_ui, tmp);
M_RSHIFT(date_ui, tmp);
} else {
/*
* Here is a *big* problem. On a machine where the
* low order bit in the clock is on the order of half
* a millisecond or more we don't really have enough
* precision to make intelligent choices about which
* samples might be in error and which aren't. More
* than this, in the case of error free data we can
* pick up a few bits of precision by taking the mean
* of the whole bunch. This is what we do. The problem
* comes when it comes time to divide the 64 bit sum of
* the 10 samples by 10, a procedure which really sucks.
* Oh, well, grin and bear it. Compute the sum first.
*/
date_ui = 0;
tmp = 0;
for (i = 0; i < NCHUCHARS; i++)
M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
if (M_ISNEG(date_ui, tmp))
isneg = 1;
else
isneg = 0;
/*
* Here is a multiply-by-0.1 optimization that should apply
* just about everywhere. If the magnitude of the sum
* is less than 9 we don't have to worry about overflow
* out of a 64 bit product, even after rounding.
*/
if (date_ui < 9 || date_ui > 0xfffffff7) {
register U_LONG prod_ui;
register U_LONG prod_uf;
prod_ui = prod_uf = 0;
/*
* This code knows the low order bit in 0.1 is zero
*/
for (i = 1; i < NZPOBITS; i++) {
M_LSHIFT(date_ui, tmp);
if (ZEROPTONE & (1<<i))
M_ADD(prod_ui, prod_uf, date_ui, tmp);
}
/*
* Done, round it correctly. Prod_ui contains the
* fraction.
*/
if (prod_uf & 0x80000000)
prod_ui++;
if (isneg)
date_ui = 0xffffffff;
else
date_ui = 0;
tmp = prod_ui;
/*
* date_ui is integral part, tmp is fraction.
*/
} else {
register U_LONG prod_ovr;
register U_LONG prod_ui;
register U_LONG prod_uf;
register U_LONG highbits;
prod_ovr = prod_ui = prod_uf = 0;
if (isneg)
highbits = 0xffffffff; /* sign extend */
else
highbits = 0;
/*
* This code knows the low order bit in 0.1 is zero
*/
for (i = 1; i < NZPOBITS; i++) {
M_LSHIFT3(highbits, date_ui, tmp);
if (ZEROPTONE & (1<<i))
M_ADD3(prod_ovr, prod_uf, prod_ui,
highbits, date_ui, tmp);
}
if (prod_uf & 0x80000000)
M_ADDUF(prod_ovr, prod_ui, (U_LONG)1);
date_ui = prod_ovr;
tmp = prod_ui;
}
}
/*
* At this point we have the mean offset, with the integral
* part in date_ui and the fractional part in tmp. Store
* it in the structure.
*/
i = second - 32; /* gives a value 0 through 8 */
if (i < (int)chu->expect) {
/*
* This shouldn't actually happen, but might if a single
* bit error occurred in the code which fooled us.
* Throw away all previous data.
*/
chu->expect = 0;
chu->haveoffset = 0;
if (chu->flags & CHUTIMERSET) {
TIMER_DEQUEUE(&chu->chutimer);
chu->flags &= ~CHUTIMERSET;
}
}
/*
* Add in fudge factor.
*/
M_ADD(date_ui, tmp, offset_fudge[chu->unit].l_ui,
offset_fudge[chu->unit].l_uf);
chu->offsets[i].l_ui = date_ui;
chu->offsets[i].l_uf = tmp;
chu->rectimes[i] = rbufp->recv_time;
chu->reftimes[i] = reftime;
chu->expect = i + 1;
chu->haveoffset |= (1<<i);
if (chu->expect >= NCHUCODES) {
/*
* Got a full second's worth. Dequeue timer and
* process this.
*/
if (chu->flags & CHUTIMERSET) {
TIMER_DEQUEUE(&chu->chutimer);
chu->flags &= ~CHUTIMERSET;
}
chu_process(chu);
} else if (!(chu->flags & CHUTIMERSET)) {
/*
* Try to take an interrupt sometime after the
* 42 second mark (leaves an extra 2 seconds for
* slop). Round it up to an even multiple of
* 4 seconds.
*/
chu->chutimer.event_time =
current_time + (U_LONG)(10 - i) + (1<<EVENT_TIMEOUT);
chu->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1);
TIMER_INSERT(timerqueue, &chu->chutimer);
chu->flags |= CHUTIMERSET;
}
}
/*
* chu_timeout - process a timeout event
*/
static void
chu_timeout(fakepeer)
struct peer *fakepeer;
{
/*
* If we got here it means we received some time codes
* but didn't get the one which should have arrived on
* the 39th second. Process what we have.
*/
((struct chuunit *)fakepeer)->flags &= ~CHUTIMERSET;
chu_process((struct chuunit *)fakepeer);
}
/*
* chu_process - process the raw offset estimates we have and pass
* the results on to the NTP clock filters.
*/
static void
chu_process(chu)
register struct chuunit *chu;
{
register int i;
register s_fp bestoff;
register s_fp tmpoff;
u_fp dispersion;
int imax;
l_fp ts;
/*
* The most positive offset.
*/
imax = NCHUCODES;
for (i = 0; i < NCHUCODES; i++)
if (chu->haveoffset & (1<<i))
if (i < imax || L_ISGEQ(&chu->offsets[i],
&chu->offsets[imax]))
imax = i;
/*
* The most positive estimate is our best bet. Go through
* the list again computing the dispersion.
*/
bestoff = LFPTOFP(&chu->offsets[imax]);
dispersion = 0;
for (i = 0; i < NCHUCODES; i++) {
if (chu->haveoffset & (1<<i)) {
tmpoff = LFPTOFP(&chu->offsets[i]);
dispersion += (bestoff - tmpoff);
} else {
dispersion += CHUDELAYPENALTY;
}
}
/*
* Make up a reference time stamp, then give it to the
* reference clock support code for further processing.
*/
ts.l_ui = chu->reftimes[imax];
ts.l_uf = chutable[NCHUCHARS-1];
for (i = 0; i < NCHUCHARS*4; i++) {
chu->asciicode[i] = hexstring[chu->lastcode[i]];
}
chu->asciicode[i] = '\0';
record_clock_stats(&(chu->peer->srcadr), chu->asciicode);
refclock_receive(chu->peer, &chu->offsets[imax], 0,
dispersion, &ts, &chu->rectimes[imax], chu->leap);
/*
* Zero out unit for next code series
*/
chu->haveoffset = 0;
chu->expect = 0;
chu_event(chu, CEVNT_NOMINAL);
}
/*
* chu_poll - called by the transmit procedure
*/
static void
chu_poll(unit, peer)
int unit;
struct peer *peer;
{
if (unit >= MAXUNITS) {
syslog(LOG_ERR, "chu_poll: unit %d invalid", unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR, "chu_poll: unit %d not in use", unit);
return;
}
if ((current_time - chuunits[unit]->lastupdate) > 150) {
chu_event(chuunits[unit], CEVNT_PROP);
}
}
/*
* chu_control - set fudge factors, return statistics
*/
static void
chu_control(unit, in, out)
u_int unit;
struct refclockstat *in;
struct refclockstat *out;
{
register struct chuunit *chu;
U_LONG npolls;
if (unit >= MAXUNITS) {
syslog(LOG_ERR, "chu_control: unit %d invalid", unit);
return;
}
if (in != 0) {
if (in->haveflags & CLK_HAVETIME1)
propagation_delay[unit] = in->fudgetime1;
if (in->haveflags & CLK_HAVETIME2)
fudgefactor[unit] = in->fudgetime2;
offset_fudge[unit] = propagation_delay[unit];
L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
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
*/
peer = chuunits[unit]->peer;
peer->stratum = stratumtouse[unit];
if (stratumtouse[unit] <= 1)
memmove((char *)&peer->refid,
CHUREFID, 4);
else
peer->refid = htonl(CHUHSREFID);
}
}
if (in->haveflags & CLK_HAVEFLAG1) {
sloppyclockflag[unit] = in->flags & CLK_FLAG1;
}
}
if (out != 0) {
out->type = REFCLK_CHU;
out->flags = 0;
out->haveflags
= CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|
CLK_HAVEVAL2|CLK_HAVEFLAG1;
out->clockdesc = CHUDESCRIPTION;
out->fudgetime1 = propagation_delay[unit];
out->fudgetime2 = fudgefactor[unit];
out->fudgeval1 = (long)stratumtouse[unit];
out->flags = sloppyclockflag[unit];
if (unitinuse[unit]) {
chu = chuunits[unit];
out->lencode = NCHUCHARS*4;
out->fudgeval2 = chu->lastcode[2*NCHUCHARS+1];
out->fudgeval2 *= (chu->lastcode[2*NCHUCHARS]&1)?-1:1;
out->lastcode = chu->asciicode;
out->timereset = current_time - chu->timestarted;
npolls = out->timereset / 6; /* **divide** */
out->polls = npolls;
out->noresponse = (npolls - chu->responses);
out->badformat = chu->badformat;
out->baddata = chu->baddata;
out->lastevent = chu->lastevent;
out->currentstatus = chu->status;
} else {
out->fudgeval2 = 0;
out->lencode = 0;
out->lastcode = "";
out->polls = out->noresponse = 0;
out->badformat = out->baddata = 0;
out->timereset = 0;
out->currentstatus = out->lastevent = CEVNT_NOMINAL;
}
}
}
#endif