refclock_true.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* refclock_true - clock driver for the Kinemetrics Truetime receivers
* Receiver Version 3.0C - tested plain, with CLKLDISC
* Developement work being done:
* - Properly handle varying satellite positions (more acurately)
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
/* This should be an atom clock but those are very hard to build.
*
* The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
* of TTL input and output pins, all brought out to the back panel. If you
* wire a PPS signal (such as the TTL PPS coming out of a GOES or other
* Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
* 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
* number of uSecs since the last PPS upward swing, mediated by reading OUT0
* to find out if the counter has wrapped around (this happens if more than
* 65535us (65ms) elapses between the PPS event and our being called.)
*/
#ifdef PPS720
# define PCL720_CTR 0 /* XXX */
#endif
/*
* Support for Kinemetrics Truetime Receivers
* GOES
* OM-DC: getting stale ("OMEGA")
*
* Most of this code is originally from refclock_wwvb.c with thanks.
* It has been so mangled that wwvb is not a recognizable ancestor.
*
* Timcode format: ADDD:HH:MM:SSQCL
* A - control A (this is stripped before we see it)
* Q - Quality indication (see below)
* C - Carriage return
* L - Line feed
*
* Quality codes indicate possible error of
* 468-DC GOES Receiver:
* ? +/- 500 milliseconds # +/- 50 milliseconds
* * +/- 5 milliseconds . +/- 1 millisecond
* space less than 1 millisecond
* OM-DC OMEGA Receiver:
* > >+- 5 seconds
* ? >+/- 500 milliseconds # >+/- 50 milliseconds
* * >+/- 5 milliseconds . >+/- 1 millisecond
* A-H less than 1 millisecond. Character indicates which station
* is being received as follows:
* A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
* E = La Reunion, F = Argentina, G = Australia, H = Japan.
*
* The carriage return start bit begins on 0 seconds and extends to 1 bit time.
*
* Notes on 468-DC and OMEGA receiver:
*
* Send the clock a 'R' or 'C' and once per second a timestamp will
* appear. Send a 'P' to get the satellite position once (GOES only.)
*
* Notes on the 468-DC receiver:
*
* set your clock propagation delay settings correctly and still use
* automatic mode. The manual says to use a compromise when setting the
* switches. This results in significant errors. The solution; use fudge
* time1 and time2 to incorporate corrections. If your clock is set for
* 50 and it should be 58 for using the west and 46 for using the east,
* use the line
*
* fudge 127.127.5.0 time1 +0.008 time2 -0.004
*
* This corrects the 4 milliseconds advance and 8 milliseconds retard
* needed. The software will ask the clock which satellite it sees.
*
* Ntp.conf parameters:
* time1 - offset applied to samples when reading WEST satellite (default = 0)
* time2 - offset applied to samples when reading EAST satellite (default = 0)
* val1 - stratum to assign to this clock (default = 0)
* val2 - refid assigned to this clock (default = "TRUE", see below)
* flag1 - will silence the clock side of xntpd, just reading the clock
* without trying to write to it. (default = 0)
* flag3 - enable ppsclock streams module
*/
/*
* Definitions
*/
/*
* Radio interface parameters
*/
#define DESCRIPTION "Kinemetrics/TrueTime Receiver"
/*
* Tags which station (satellite) we see
*/
#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
/*
* used by the state machine
*/
"Poll", "Location", "TS"};
"Init", "F18", "F50", "Start", "Auto"};
/*
* Imported from the timer module
*/
extern u_long current_time;
/*
* Imported from ntpd module
*/
extern int debug; /* global debug flag */
/*
* unit control structure
*/
struct true_unit {
unsigned int pollcnt; /* poll message counter */
unsigned int station; /* which station we are on */
unsigned int polled; /* Hand in a time sample? */
int unit; /* save an extra copy of this */
#ifdef PPS720
int pcl720init; /* init flag for PCL 720 */
#endif
};
/*
* Function prototypes
*/
static int true_start P((int, struct peer *));
static void true_shutdown P((int, struct peer *));
static void true_receive P((struct recvbuf *));
#ifdef PPS720
static u_long true_sample720 P((void));
#endif
/*
* Transfer vector
*/
struct refclock refclock_true = {
true_start, /* start up driver */
true_shutdown, /* shut down driver */
true_poll, /* transmit poll message */
noentry, /* not used (old true_control) */
noentry, /* initialize driver (not used) */
noentry, /* not used (old true_buginfo) */
NOFLAGS /* not used */
};
#if !defined(__STDC__)
# define true_debug (void)
#else
static void
int want_debugging, now_debugging;
struct refclockproc *pp;
if (want_debugging != now_debugging)
if (want_debugging) {
char filename[20];
#ifdef HAVE_SETVBUF
#else
#endif
}
} else {
}
}
}
#endif /*STDC*/
/*
* true_start - open the devices and initialize data for processing
*/
static int
int unit;
{
struct refclockproc *pp;
char device[20];
int fd;
/*
* Open serial port
*/
#ifdef TTYCLK
#else
#endif /* TTYCLK */
return (0);
/*
* Allocate and initialize unit structure
*/
return (0);
}
return (0);
}
/*
* Initialize miscellaneous variables
*/
return (1);
}
/*
* true_shutdown - shut down the clock
*/
static void
int unit;
{
struct refclockproc *pp;
}
/*
* true_receive - receive data from the serial interface on a clock
*/
static void
{
struct refclockproc *pp;
char sync;
int i;
/*
* Get the clock this applies to and pointers to the data.
*/
/*
* Read clock output. Automatically handles STREAMS, CLKLDISC.
*/
/*
* There is a case where <cr><lf> generates 2 timestamps.
*/
return;
/*
* We get down to business, check the timecode format and decode
* its contents. This code decodes a multitude of different
* clock messages. Timecodes are processed if needed. All replies
* will be run through the state machine to tweak driver options
* and program the clock.
*/
/*
* Clock misunderstood our last command?
*/
return;
}
/*
* Timecode: "nnnnn+nnn-nnn"
* (from GOES clock when asked about satellite position)
*/
)
{
const char *label = "Botch!";
/*
* This is less than perfect. Call the (satellite)
* either EAST or WEST and adjust slop accodingly
* Perfectionists would recalculate the exact delay
* and adjust accordingly...
*/
if (lon < 10000) {
label = "EAST";
} else {
label = "WEST";
}
}
}
else {
label = "UNKNOWN";
}
return;
}
/*
* Timecode: "Fnn"
*/
switch (i) {
case 50:
break;
case 51:
break;
default:
break;
}
return;
}
/*
* Timecode: " TRUETIME Mk III"
*/
}
return;
}
/*
* Timecode: "N03726428W12209421+000033"
* 1 2
* 0123456789012345678901234
* (from a TCU during initialization)
*/
}
return;
}
/*
* Timecode: "ddd:hh:mm:ssQ"
* (from all clocks supported by this driver.)
*/
/*
* Adjust the synchronize indicator according to timecode
*/
else {
}
#ifdef PPS720
/* If it's taken more than 65ms to get here, we'll lose. */
#ifdef ATOM
/*
* find out what time it really is. Include
* the count from the PCL720
*/
return;
}
#endif
#ifdef ATOM
#endif
/*
* Stomp all over the timestamp that was pulled out
* of the input stream. It's irrelevant since we've
* adjusted the input time to reflect now (via pp->usec)
* rather than when the data was collected.
*/
#ifdef ATOM
/*
* Create a true offset for feeding to pps_sample()
*/
pps_sample(&off);
#endif
}
#endif
/*
* The clock will blurt a timecode every second but we only
* want one when polled. If we haven't been polled, or if we
* don't know the clock type yet, bail out.
*/
return;
/*
* Process the new sample in the median filter and determine
* the reference clock offset and dispersion. 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.
*/
return;
}
/*
* We have succedded in answering the poll.
* Turn off the flag and return
*/
return;
}
/*
* No match to known timecodes, report failure and return
*/
return;
}
/*
* true_send - time to send the clock a signal to cough up a time sample
*/
static void
char *cmd;
{
struct refclockproc *pp;
else
}
}
/*
* state machine for initializing and controlling a clock
*/
static void
enum true_event event;
{
struct refclockproc *pp;
}
}
case t_goes:
switch (event) {
case e_Init: /* FALLTHROUGH */
case e_Satellite:
/*
* Switch back to on-second time codes and return.
*/
break;
case e_Poll:
/*
* After each poll, check the station (satellite).
*/
/* No state change needed. */
break;
default:
break;
}
/* FALLTHROUGH */
case t_omega:
switch (event) {
case e_Init:
break;
case e_TS:
break;
}
break;
default:
break;
}
break;
case t_tm:
switch (event) {
case e_Init:
break;
case e_F18:
break;
case e_F50:
break;
case e_F51:
break;
case e_TS:
break;
}
break;
default:
break;
}
break;
case t_tcu:
switch (event) {
case e_Init:
break;
case e_TS:
break;
}
break;
default:
break;
}
break;
case t_unknown:
case s_Base:
abort();
break;
case s_InqGOES:
switch (event) {
case e_Satellite:
break;
case e_Init: /*FALLTHROUGH*/
case e_Huh: /*FALLTHROUGH*/
case e_TS:
break;
default:
abort();
}
break;
case s_InqOmega:
switch (event) {
case e_TS:
break;
case e_Init: /*FALLTHROUGH*/
case e_Huh:
break;
default:
abort();
}
break;
case s_InqTM:
switch (event) {
case e_F18:
break;
case e_Init: /*FALLTHROUGH*/
case e_Huh:
break;
default:
abort();
}
break;
case s_InqTCU:
switch (event) {
case e_Location:
break;
case e_Init: /*FALLTHROUGH*/
case e_Huh:
break;
default:
abort();
}
break;
/*
* An expedient hack to prevent lint complaints,
* these don't actually need to be used here...
*/
case s_Init:
case s_F18:
case s_F50:
case s_Start:
case s_Auto:
case s_Max:
}
break;
default:
abort();
/* NOTREACHED */
}
#ifdef PPS720
/* Make counter trigger on gate0, count down from 65535. */
/*
* (These constants are OK since
* they represent hardware maximums.)
*/
}
up->pcl720init++;
}
#endif
}
/*
* true_poll - called by the transmit procedure
*/
static void
int unit;
{
struct refclockproc *pp;
/*
* You don't need to poll this clock. It puts out timecodes
* once per second. If asked for a timestamp, take note.
* The next time a timecode comes in, it will be fed back.
*/
else {
}
/*
* polled every 64 seconds. Ask true_receive to hand in a
* timestamp.
*/
}
#ifdef PPS720
/*
* true_sample720 - sample the PCL-720
*/
static u_long
{
unsigned long f;
/* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
* If it is not being held low now, we did not get called
* within 65535us.
*/
}
return (0);
}
#ifdef PPS720_DEBUG
#endif
return (f);
}
#endif
#else /* not (REFCLOCK && TRUETIME) */
int refclock_true_bs;
#endif /* not (REFCLOCK && TRUETIME) */