README.new_clocks revision 7c478bd95313f5f23a4c958a745db2134aa03244
ident "%Z%%M% %I% %E% SMI"
Here is an attempt to sketch out what you need to do in order to
add another clock to the parse driver:
Prerequisites:
- Does the system you want the clock connect to have
What to do:
Make a conversion module (parse/clk_*.c)
- What ist the time code format ?
- find year, month, day, hour, minute, second, status (synchronised or
not), possibly time zone information (you need to give the offset to UTC)
You will have to convert the data from a string into a struct clocktime:
struct clocktime /* clock time broken up from time code */
{
long day;
long month;
long year;
long hour;
long minute;
long second;
long usecond;
long utcoffset; /* in seconds */
time_t utcoffset; /* true utc time instead of date/time */
long flags; /* current clock status */
};
Conversion is usually simple and straight forward. For the flags following
values can be OR'ed together:
PARSEB_ANNOUNCE switch time zone warning (informational only)
PARSEB_POWERUP no synchronisation - clock confused (must set then)
PARSEB_NOSYNC timecode currently not confirmed (must set then)
usually on reception error when there is still a
chance the the generated time is still ok.
PARSEB_DST DST in effect (informational only)
PARSEB_UTC timecode contains UTC time (informational only)
PARSEB_LEAPADD LEAP addition warning (prior to leap happening - must set when imminent)
also used for time code that do not encode the
direction (as this is currently the default).
PARSEB_LEAPDEL LEAP deletion warning (prior to leap happening - must set when imminent)
PARSEB_ALTERNATE backup transmitter (informational only)
PARSEB_POSITION geographic position available (informational only)
PARSEB_LEAPSECOND actual leap second (this time code is the leap
second - informational only)
These are feature flags denoting items that are supported by the clock:
PARSEB_S_LEAP supports LEAP - might set PARSEB_LEAP
PARSEB_S_ANTENNA supports ANTENNA - might set PARSEB_ALTERNATE
PARSEB_S_PPS supports PPS time stamping
PARSEB_S_POSITION supports position information (GPS)
If the utctime field is non zero this value will be take as
time code value. This allows for conversion routines that
already have the utc time value. The utctime field gives the seconds
since Jan 1st 1970, 0:00:00. The useconds field gives the respective
usec value. The fields for date and time (down to second resolution)
will be ignored.
Conversion is done in the cvt_* routine in parse/clk_*.c files. look in
them for examples. The basic structure is:
struct clockformat <yourclock>_format = {
lots of fields for you to fill out (see below)
};
static cvt_<yourclock>()
...
{
if (<I do not recognize my time code>) {
return CVT_NONE;
} else {
if (<conversion into clockformat is ok>) {
<set all necessary flags>;
return CVT_OK;
} else {
return CVT_FAIL|CVT_BADFMT;
}
}
The struct clockformat is the interface to the rest of the parse
driver - it holds all information necessary for finding the
clock message and doing the appropriate time stamping.
struct clockformat
{
u_long (*convert)();
/* conversion routine - your routine - cvt_<yourclock> */
void (*syncevt)();
/* routine for handling RS232 sync events (time stamps) - usually sync_simple */
u_long (*syncpps)();
/* PPS input routine - usually pps_simple */
u_long (*synth)();
/* time code synthesizer - usually not used - (long (*)())0 */
void *data;
/* local parameters - any parameters/data/configuration info your conversion
routine might need */
char *name;
/* clock format name - Name of the time code */
unsigned short length;
/* maximum length of data packet for your clock format */
u_long flags;
/* information for the parser what to look for */
struct timeval timeout;
/* buffer restart after timeout (us) - some clocks preceede new data by
a longer period of silence - unsually not used */
unsigned char startsym;
/* start symbol - character at the beginning of the clock data */
unsigned char endsym;
/* end symbol - character at the end of the clock data */
unsigned char syncsym;
/* sync symbol - character that is "on time" - where the time stamp should be taken */
};
The flags:
F_START use startsym to find the beginning of the clock data
F_END use endsym to find the end of the clock data
SYNC_TIMEOUT packet restart after timeout in timeout field
SYNC_START packet start is sync event (time stamp at paket start)
SYNC_END packet end is sync event (time stamp at paket end)
SYNC_CHAR special character (syncsym) is sync event
SYNC_ONE PPS synchronize on 'ONE' transition
SYNC_ZERO PPS synchronize on 'ZERO' transition
SYNC_SYNTHESIZE generate intermediate time stamps (very special case!)
CVT_FIXEDONLY convert only in fixed configuration - (data format not
suitable for auto-configuration)
The above should have given you some hints on how to build a clk_*.c
file with the time code conversion. See the examples and pick a clock
closest to yours and tweak the code to match your clock.
In order to make your clk_*.c file usable a reference to the clockformat
structure must be put into parse_conf.c.
TTY setup and initialisation/configuration will be done in
- Find out the exact tty settings for your clock (baud rate, parity,
stop bits, character size, ...) and note them in terms of
termio*.h c_cflag macros.
- in xntpd/refclock_parse.c fill out a new the struct clockinfo element
(that allocates a new "IP" address - see comments)
(see all the other clocks for example)
struct clockinfo
{
u_long cl_flags; /* operation flags (io modes) */
PARSE_F_NOPOLLONLY always do async io - read whenever input comes
PARSE_F_POLLONLY never do async io - only read when expecting data
PARSE_F_PPSPPS use loopfilter PPS code (CIOGETEV)
PARSE_F_PPSONSECOND PPS pulses are on second
usually flags stay 0 as they are used only for special setups
void (*cl_poll)(); /* active poll routine */
The routine to call when the clock needs data sent to it in order to
get a time code from the clock (e.g. Trimble clock)
int (*cl_init)(); /* active poll init routine */
The routine to call for very special initializations.
void (*cl_end)(); /* active poll end routine */
The routine to call to undo any special initialisation (free memory/timers)
void *cl_data; /* local data area for "poll" mechanism */
local data for polling routines
u_fp cl_rootdelay; /* rootdelay */
NTP rottdelay estimate (usually 0)
u_long cl_basedelay; /* current offset - unsigned l_fp fractional par
time (fraction) by which the RS232 time code is delayed from the actual time.
t */
u_long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional
time (fraction) by which the PPS time stamp is delayed (usually 0)
part */
char *cl_id; /* ID code (usually "DCF") */
Refclock id - (max 4 chars)
char *cl_description; /* device name */
Name of this device.
char *cl_format; /* fixed format */
If the data format cann not ne detected automatically this is the name
as in clk_*.c clockformat.
u_char cl_type; /* clock type (ntp control) */
Type if clock as in clock status word (ntp control messages) - usually 0
u_long cl_maxunsync; /* time to trust oscillator after loosing synch
*/
seconds a clock can be trusted after loosing synchronisation.
u_long cl_cflag; /* terminal io flags */
u_long cl_iflag; /* terminal io flags */
u_long cl_oflag; /* terminal io flags */
u_long cl_lflag; /* terminal io flags */
termio*.h tty modes.
} clockinfo[] = {
...,<other clocks>,...
{ < your parameters> },
};
Well, this is very sketchy, i know. But I hope it helps a little bit.
The best way is to look which clock comes closest to your and tweak that
code.
Two sorts of clocks are used with parse. Clocks that automatically send
their time code (once a second) do not need entries in the poll routines because
they send the data all the time. The second sort are the clocks that need a
command sent to them in order to reply with a time code (like the Trimble
clock).
For questions: kardel@informatik.uni-erlangen.de. Please include
an exact description on how your clock works. (initialisation,
TTY modes, strings to be sent to it, responses received from the clock).
Frank Kardel