265 lines
10 KiB
HTML
265 lines
10 KiB
HTML
<!-- $NetBSD: parsenew.html,v 1.1 1998/12/30 20:20:36 mcr Exp $ -->
|
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML Strict//EN">
|
|
<TITLE>Making PARSE Clocks</TITLE>
|
|
<h1>How to build new PARSE clocks</h1>
|
|
|
|
<p>Here is an attempt to sketch out what you need to do in order to
|
|
add another clock to the parse driver:
|
|
|
|
|
|
<p>Prerequisites:
|
|
<ul>
|
|
<li>Does the system you want the clock connect to have the include files
|
|
termio.h or termios.h ? (You need that for the parse driver)
|
|
</ul>
|
|
|
|
|
|
<p>What to do:
|
|
|
|
|
|
<p>Make a conversion module (libparse/clk_*.c)
|
|
|
|
<ol>
|
|
<li>What ist the time code format ?
|
|
<ul>
|
|
<li> 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:
|
|
<pre>
|
|
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 */
|
|
};
|
|
</pre>
|
|
|
|
<p>Conversion is usually simple and straight forward. For the flags following
|
|
values can be OR'ed together:
|
|
<PRE>
|
|
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)
|
|
</PRE>
|
|
|
|
<p>These are feature flags denoting items that are supported by the clock:
|
|
<PRE>
|
|
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)
|
|
</PRE>
|
|
|
|
<p>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.
|
|
|
|
|
|
<p>Conversion is done in the cvt_* routine in parse/clk_*.c files. look in
|
|
them for examples. The basic structure is:
|
|
|
|
<PRE>
|
|
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;
|
|
}
|
|
}
|
|
</PRE>
|
|
|
|
|
|
<p>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.
|
|
|
|
<PRE>
|
|
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 */
|
|
};
|
|
</PRE>
|
|
|
|
|
|
<p>The flags:
|
|
<PRE>
|
|
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)
|
|
</PRE>
|
|
|
|
|
|
<p>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.
|
|
|
|
|
|
<p>In order to make your clk_*.c file usable a reference to the clockformat
|
|
structure must be put into parse_conf.c.
|
|
</ul>
|
|
<li>TTY setup and initialisation/configuration will be done in
|
|
xntpd/refclock_parse.c.
|
|
<ul>
|
|
<li>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.
|
|
<li>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)
|
|
<PRE>
|
|
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_event)(); /* special event handling (e.g. reset clock) */
|
|
What to do, when an event happens - used to re-initialize clocks on timeout.
|
|
|
|
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 rootdelay estimate (usually 0)
|
|
|
|
u_long cl_basedelay; /* current offset - unsigned l_fp
|
|
fractional part (fraction) by
|
|
which the RS232 time code is
|
|
delayed from the actual time. */
|
|
|
|
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_speed; /* terminal input & output baudrate */
|
|
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.
|
|
|
|
u_long cl_samples; /* samples for median filter */
|
|
u_long cl_keep; /* samples for median filter to keep */
|
|
median filter parameters - smoothing and rejection of bad samples
|
|
} clockinfo[] = {
|
|
...,<other clocks>,...
|
|
{ < your parameters> },
|
|
};
|
|
|
|
</PRE>
|
|
</ul>
|
|
</ol>
|
|
|
|
<p>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.
|
|
|
|
<p>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).
|
|
|
|
<p>For questions: <a href="mailto: kardel@informatik.uni-erlangen.de">kardel@informatik.uni-erlangen.de</a>.
|
|
|
|
<p>Please include an exact description on how your clock works. (initialisation,
|
|
TTY modes, strings to be sent to it, responses received from the clock).
|
|
<hr><p>
|
|
<a href="http://www4.informatik.uni-erlangen.de/~kardel">Frank Kardel</a>
|