1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
= How to Write a Reference Clock Driver
include::include-html.ad[]
[cols="10%,90%",frame="none",grid="none",style="verse"]
|==============================
|image:pic/pogo4.gif[]|
{millshome}pictures.html[from 'Pogo', Walt Kelly]
You need a little magic.
|==============================
== Related Links
include::includes/misc.adoc[]
== Table of Contents
* link:#desc[Description]
* link:#file[Files Which Need to be Changed]
* link:#intf[Interface Routine Overview]
* link:#pps[Pulse-per-Second Interface]
'''''
== When Not to Write a Driver
If the device you are trying to support is an exotic GPS, you should
probably not write an +ntpd+ driver for it. Instead, check to see if
it is already supported by {GPSD}, a project with which NTPsec
cooperates closely. The GPSD people are specialists in managing GPSes
and better at it than we are, supporting a much broader range of
devices, and GPSD is designed to feed clock samples to +ntpd+ from any
of them. If you need to write a driver for a GPS, they'll take it and
should have it.
If you have a non-GPS time source (like a time radio or GPSDO) that
you want to support, consider link:generic_howto.html[writing a mode
for it in the generic driver] rather than a full driver of its own;
this will be easier. The generic driver is so called because it
factors out a lot of I/O and housekeeping code common to all drivers,
allowing you support a new device type by writing only a parser for
its sentences.
[[desc]]
== Structure of a Driver
NTP reference clock support maintains the fiction that the clock is
actually an ordinary server in the NTP tradition, but operating at a
synthetic stratum of zero. The entire suite of algorithms filter the
received data and select the best sources to correct the system clock.
No packets are exchanged with a reference clock; however, the transmit,
receive and packet procedures are replaced with code to simulate them.
The driver assumes three timescales: standard time maintained by a
distant laboratory such as USNO or NIST, reference time maintained by
the external radio and the system time maintained by NTP. The radio
synchronizes reference time via radio, satellite or modem. As the
transmission means may not always be reliable, most radios continue to
provide clock updates for some time after signal loss using an internal
reference oscillator. In such cases the radio may or may not reveal the
time since last synchronized or the estimated time error.
All three timescales run only in Coordinated Universal Time (UTC) and
are not adjusted for local timezone or standard/daylight time. The local
timezone, standard/daylight indicator and year, if provided, are
ignored. However, it is important to determine whether a leap second is
to be inserted in the UTC timescale in the near future so NTP can insert
it in the system timescale at the appropriate epoch.
The interface routines in the +ntp_refclock.c+ source file call the
following driver routines via a transfer vector:
+startup+::
The association has just been mobilized. The driver may allocate a
private structure and open the device(s) required.
+shutdown+::
The association is about to be demobilized. The driver should close
all device(s) and free private structures.
+receive+::
A timecode string is ready for retrieval using the +refclock_gtlin()+
or +refclock_gtraw()+ routines and provide clock updates.
+poll+::
Called at poll timeout, by default 64 s. Ordinarily, the driver will
send a poll sequence to the radio as required.
+timer+::
Called once per second. This can be used for housekeeping functions.
In the case with pulse-per-second (PPS) signals, this can be used to
process the signals and provide clock updates.
The receive routine retrieves a timecode string via serial or parallel
port, PPS signal or other means. It decodes the timecode in days, hours,
minutes, seconds and nanoseconds and checks for errors. It provides
these data along with the on-time timestamp to the +refclock_process+
routine, which saves the computed offset in a 60-sample circular buffer.
On occasion, either by timeout, sample count or call to the poll
routine, the driver calls +refclock_receive+ to process the circular
buffer samples and update the system clock.
The best way to understand how the clock drivers work is to study one of
the drivers already implemented, such as +refclock_spectracom.c+. The
main interface is the +refclockproc+ structure, which contains for most
drivers the decoded timecode, on-time timestamp, reference timestamp,
exception reports and statistics tallies, etc. The support routines are
passed a pointer to the +peer+ structure, which contains a pointer to
the +refclockproc+ structure, which in turn contains a pointer to the
unit structure, if used. For legacy purposes, a table
+typeunit[type][unit]+ contains the peer structure pointer for each
configured clock type and unit. This structure should not be used for
new implementations.
Radio and modem reference clocks by convention have addresses of the
form +127.127.t.u+, where _t_ is the clock type and _u_ in the range 0-3
is used to distinguish multiple instances of clocks of the same type.
These addresses used to be exposed as part of the refclock
configuration syntax, but are no longer. Nothing in ntpd now actually
requires this form of address for clocks, but it is still generated
so as not to hand surprises to legacy +ntpq+ instances that still make
the assumption.
Most clocks require a serial or parallel port or special bus peripheral.
The particular device is normally specified by adding a soft link
+/dev/deviceu+ to the particular hardware device.
By convention, reference clock drivers are named in the form
+refclock_xxxx.c+, where +xxxx+ is a unique string. Each driver is
assigned a unique long-form driver name, short-form driver name and
device name. The existing assignments are in the
link:refclock.html[Reference Clock Drivers] page and its dependencies.
== Conventions, Fudge Factors and Flags
Most drivers support manual or automatic calibration for systematic
offset bias using values encoded in the +refclock+ configuration command.
By convention, the +time1+ value defines the calibration offset in
seconds. For those drivers that support statistics collection using the
+filegen+ utility and the +clockstats+ file, the +flag4+ switch enables
the utility.
If the calibration feature has been enabled, the +flag1+ switch is set
and the PPS signal is actively disciplining the system time, the +time1+
value is automatically adjusted to maintain a residual offset of zero.
Once the its value has stabilized, the value can be inserted in the
configuration file and the calibration feature disabled.
[[file]]
== Files Which Need to be Changed
When a new reference clock driver is installed, the following files need
to be edited. Note that changes are also necessary to properly integrate
the driver in the configuration and makefile scripts, but these are
decidedly beyond the scope of this page.
+./include/ntp.h+::
The reference clock type defines are used in many places. Each driver
is assigned a unique type number. Unused numbers are clearly marked in
the list. A unique +REFCLK_xxxx+ identification code should be
recorded in the list opposite its assigned type number.
+./libntp/clocktypes.c+::
The +./libntp/clktype+ array is used by certain display functions. A
unique short-form name of the driver should be entered together with
its assigned identification code.
+./ntpd/ntp_control.c+::
The +clocktypes+ array is used for certain control message displays
functions. It should be initialized with the reference clock class
assigned to the driver, as per the NTP specification RFC 1305. See the
+./include/ntp_control.h+ header file for the assigned classes.
+./ntpd/refclock_conf.c+::
This file contains a list of external structure definitions which are
conditionally defined. A new set of entries should be installed
similar to those already in the table. The +refclock_conf+ array is a
set of pointers to transfer vectors in the individual drivers. The
external name of the transfer vector should be initialized in
correspondence with the type number.
[[intf]]
== Interface Routine Overview
+refclock_newpeer+ - initialize and start a reference clock.::
Allocates and initializes the interface structure which
supports a reference clock in the form of an ordinary NTP peer. A
driver-specific support routine completes the initialization, if used.
Default peer variables which identify the clock and establish its
reference ID and stratum are set here. It returns one if success and
zero if the clock address is invalid or already running, insufficient
resources are available or the driver declares a bum rap.
+refclock_unpeer+ - shut down a clock::
Shuts down a clock and returns its resources to the system.
+refclock_transmit+ - simulate the transmit procedure::
Implements the NTP transmit procedure for a reference
clock. This provides a mechanism to call the driver at the NTP poll
interval, as well as provides a reachability mechanism to detect a
broken radio or other madness.
+refclock_process+ - insert a sample in the circular buffer::
Saves the offset computed from the on-time timestamp and
the days, hours, minutes, seconds and nanoseconds in the circular
buffer. Note that no provision is included for the year, as provided
by some (but not all) radio clocks. Ordinarily, the year is implicit
in the Unix file system and hardware/software clock support, so this
is ordinarily not a problem.
+refclock_receive+ - simulate the receive and packet procedures::
Simulates the NTP receive and packet procedures for a
reference clock. This provides a mechanism in which the ordinary NTP
filter, selection and combining algorithms can be used to suppress
misbehaving radios and to mitigate between them when more than one is
available for backup.
+refclock_gtraw+, +refclock_gtlin+ - read the buffer and on-time timestamp::
These routines return the data received from the clock and the on-time
timestamp. The +refclock_gtraw+ routine returns a batch of one or more
characters returned by the Unix terminal routines in raw mode. The
+refclock_gtlin+ routine removes the parity bit and control characters
and returns all the characters up to and including the line
terminator. Either routine returns the number of characters delivered.
+refclock_open+ - open a serial port for reference clock::
Opens a serial port for I/O and sets default options. It
returns the file descriptor if success and zero if failure.
+refclock_ioctl+ - set serial port control functions::
Attempts to hide the internal, system-specific details of
serial ports. It can handle POSIX (+termios+), SYSV (+termio+) and BSD
(+sgtty+) interfaces with varying degrees of success. The routine
returns one if success and zero if failure.
+refclock_ppsapi+::
Initializes the Pulse-per-Second interface (see below).
+refclock_pps+::
Called once per second to read the latest PPS offset
and save it in the circular buffer (see below).
[[pps]]
== Pulse-per-Second Interface
When the Pulse-per-Second Application Interface (RFC 2783) is present, a
compact PPS interface is available to all drivers. See the
link:prefer.html[Mitigation Rules and the Prefer Peer] page for further
information. To use this interface, include the +timeppps.h+ and
+refclock_pps.h+ header files and define the +refclock_ppsctl+ structure
in the driver private storage. The +timepps.h+ file is specific to each
operating system and may not be available for some systems.
To use the interface, call +refclock_ppsapi+ from the startup routine
passing the device file descriptor and +refclock_ppsctl+ structure
pointer. Then, call +refclock_pps+ from the timer routine passing the
association pointer and +refclock_ppsctl+ structure pointer. See the
+refclock_pps.c+ file for examples and calling sequences. If the PPS
signal is valid, the offset sample will be save in the circular buffer
and a bit set in the association flags word indicating the sample is
valid and the driver an be selected as a PPS peer. If this bit is set
when the poll routine is called, the driver calls the
+refclock_receive+ routine to process the samples in the circular
buffer and update the system clock.
'''''
image:pic/pogo1a.gif[]
include::includes/footer.adoc[]
|