refclock_chu.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright (c) 1996 by Sun Microsystems, Inc.
* All Rights Reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* refclock_chu - clock driver for the CHU time code
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.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.
*/
/*
* CHU definitions
*/
#ifndef CHULDISC
#endif
/*
* 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
/*
* Default fudge factors
*/
/*
* Hacks to avoid excercising the multiplier. I have no pride.
*/
/*
* 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";
/*
* Unit control structure.
*/
struct chuunit {
int pollcnt; /* poll message counter */
};
/*
* 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[].
*/
#define CHARDELAY 0x081b4e82
};
/*
* Imported from the timer module
*/
extern u_long current_time;
extern struct event timerqueue[];
/*
* Imported from ntpd module
*/
extern int debug; /* global debug flag */
/*
* Function prototypes
*/
static void chu_shutdown P((int, struct peer *));
static void chu_receive P((struct recvbuf *));
static void chu_process P((struct chuunit *));
static void chu_timeout P((struct peer *));
/*
* Transfer vector
*/
struct refclock refclock_chu = {
chu_start, /* start up driver */
chu_shutdown, /* shut down driver */
chu_poll, /* transmit poll message */
noentry, /* not used (old chu_control) */
noentry, /* initialize driver (not used) */
noentry, /* not used (old chu_buginfo) */
NOFLAGS /* not used */
};
/*
* chu_start - open the CHU device and initialize data for processing
*/
static int
int unit;
{
struct refclockproc *pp;
int fd;
char device[20];
/*
* Open serial port and set CHU line discipline
*/
return (0);
/*
* Allocate and initialize unit structure
*/
return (0);
}
return (0);
}
/*
* Initialize miscellaneous variables
*/
return (1);
}
/*
* chu_shutdown - shut down the clock
*/
static void
int unit;
{
struct refclockproc *pp;
}
/*
* chu_receive - receive data from a CHU clock, do format checks and compute
* an estimate from the sample data
*/
static void
{
struct refclockproc *pp;
int i;
int isneg;
/*
* Do a length check on the data. Should be what we asked for.
*/
"chu_receive: received %d bytes, expected %d",
return;
}
/*
* Get the clock this applies to and a pointer to the data
*/
/*
* Just for fun, we can debug the whole frame if
* we want.
*/
for (i = 0; i < NCHUCHARS; i++) {
0xf];
>> 4];
}
#ifdef DEBUG
if (debug > 3) {
"year":"time");
for (i = 0; i < NCHUCHARS; i++) {
char c[64];
sprintf(c,"%c%c %s",
printf("chu: %s .%06ld\n", c,
}
}
#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.
*/
{
/*
* Break out the code into the BCD nibbles.
* Put it in the half of lastcode.
*/
for (i = 0; i < NCHUCHARS; i++) {
}
/*
* Now make sure that the leap nibble
* is even parity.
*/
if (parity)
{
return;
}
/*
* This just happens to work. :-)
*/
return;
}
{
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.
*/
for (i = 0; i < NCHUCHARS; i++) {
}
/*
* 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++)
return;
}
/*
* If the first nibble isn't a 6, we're up the creek
*/
if (*code++ != 6) {
return;
}
/*
* Collect the day, the hour, the minute and the second.
*/
/*
* Sanity check the day and time. Note that this
* only occurs on the 32st through the 39th second
* of the minute.
*/
} else {
}
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.
*/
return;
}
/*
* 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++) {
}
if (!pp->sloppyclockflag) {
/*
* 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++)
/*
* Sort them into order. Reuse variables with abandon.
*/
}
}
}
/*
* 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.
*/
for (i = 5; i <= 7; i++)
/*
* Round properly, then right shift two bits for the
* divide by four.
*/
if (tmp & 0x2)
} 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++)
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.
*/
/*
* This code knows the low order bit in 0.1 is zero
*/
for (i = 1; i < NZPOBITS; i++) {
if (ZEROPTONE & (1<<i))
}
/*
* Done, round it correctly. Prod_ui contains the
* fraction.
*/
if (prod_uf & 0x80000000)
prod_ui++;
if (isneg)
date_ui = 0xffffffff;
else
date_ui = 0;
/*
* date_ui is integral part, tmp is fraction.
*/
} else {
if (isneg)
else
highbits = 0;
/*
* This code knows the low order bit in 0.1 is zero
*/
for (i = 1; i < NZPOBITS; i++) {
if (ZEROPTONE & (1<<i))
}
if (prod_uf & 0x80000000)
}
}
/*
* 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.
*/
/*
* This shouldn't actually happen, but might if a single
* bit error occurred in the code which fooled us.
* Throw away all previous data.
*/
up->haveoffset = 0;
}
}
/*
* Got a full second's worth. Dequeue timer and
* process this.
*/
}
/*
* 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_timeout - process a timeout event
*/
static void
{
/*
* 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.
*/
}
/*
* chu_process - process the raw offset estimates we have and pass
* the results on to the NTP clock filters.
*/
static void
{
struct refclockproc *pp;
int i;
int imax;
/*
* The most positive offset.
*/
for (i = 0; i < NCHUCODES; i++)
imax = i;
/*
* The most positive estimate is our best bet. Go through
* the list again computing the dispersion.
*/
dispersion = 0;
for (i = 0; i < NCHUCODES; i++) {
} else {
}
}
/*
* Zero out unit for next code series
*/
up->haveoffset = 0;
}
/*
* chu_poll - called by the transmit procedure
*/
static void
int unit;
{
struct refclockproc *pp;
else
}
#else /* not (REFCLOCK && CHUCLK) */
int refclock_chu_bs;
#endif /* not (REFCLOCK && CHUCLK) */