1 | <html><head><title> |
---|
2 | How to Write a Reference Clock Driver |
---|
3 | </title></head><body><h3> |
---|
4 | How to Write a Reference Clock Driver |
---|
5 | </h3> |
---|
6 | |
---|
7 | <img align=left src=pic/pogo4.gif><a href=http://www.eecis.udel.edu/~mills/pictures.htm>from <i>Pogo</i>, Walt Kelly</a> |
---|
8 | |
---|
9 | <p>You need a little magic. |
---|
10 | <br clear=left><hr> |
---|
11 | |
---|
12 | <h4>Description</h4> |
---|
13 | |
---|
14 | <p>Reference clock support maintains the fiction that the clock is |
---|
15 | actually an ordinary peer in the NTP tradition, but operating at a |
---|
16 | synthetic stratum of zero. The entire suite of algorithms used to filter |
---|
17 | the received data, select the best clocks or peers and combine them to |
---|
18 | produce a local clock correction operate just like ordinary NTP peers. |
---|
19 | In this way, defective clocks can be detected and removed from the peer |
---|
20 | population. As no packets are exchanged with a reference clock; however, |
---|
21 | the transmit, receive and packet procedures are replaced with separate |
---|
22 | code to simulate them. |
---|
23 | |
---|
24 | <p>Radio and modem reference clocks by convention have addresses in the |
---|
25 | form <tt>127.127.<i>t</i>.<i>u</i></tt>, where <i>t</i> is the clock |
---|
26 | type and <i>u</i> in the range 0-3 is used to distinguish multiple |
---|
27 | instances of clocks of the same type. Most clocks require a serial port |
---|
28 | or special bus peripheral. The particular device is normally specified |
---|
29 | by adding a soft link <tt>/dev/device<i>d</i>d</tt> to the particular |
---|
30 | hardware device involved, where <tt><i>d</i></tt> corresponds to the |
---|
31 | unit number. |
---|
32 | |
---|
33 | <p>The best way to understand how the clock drivers work is to study the |
---|
34 | <tt>ntp_refclock.c</tt> module and one of the drivers already |
---|
35 | implemented, such as <tt>refclock_wwvb.c</tt>. Routines |
---|
36 | <tt>refclock_transmit()</tt> and <tt>refclock_receive()</tt> maintain |
---|
37 | the peer variables in a state analogous to a network peer and pass |
---|
38 | received data on through the clock filters. Routines |
---|
39 | <tt>refclock_peer()</tt> and <tt>refclock_unpeer()</tt> are called to |
---|
40 | initialize and terminate reference clock associations, should this ever |
---|
41 | be necessary. A set of utility routines is included to open serial |
---|
42 | devices, process sample data, edit input lines to extract embedded |
---|
43 | timestamps and to perform various debugging functions. |
---|
44 | |
---|
45 | <p>The main interface used by these routines is the |
---|
46 | <tt>refclockproc</tt> structure, which contains for most drivers the |
---|
47 | decimal equivalents of the year, day, month, hour, second and |
---|
48 | millisecond/microsecond decoded from the ASCII timecode. Additional |
---|
49 | information includes the receive timestamp, exception report, statistics |
---|
50 | tallies, etc. The support routines are passed a pointer to the |
---|
51 | <tt>peer</tt> structure, which is used for all peer-specific processing |
---|
52 | and contains a pointer to the <tt>refclockproc</tt> structure, which in |
---|
53 | turn contains a pointer to the unit structure, if used. For legacy |
---|
54 | purposes, a table <tt>typeunit[type][unit]</tt> contains the peer |
---|
55 | structure pointer for each configured clock type and unit. |
---|
56 | |
---|
57 | <p>The reference clock interface supports auxiliary functions to support |
---|
58 | in-stream timestamping, pulse-per-second (PPS) interfacing and precision |
---|
59 | time kernel support. In most cases the drivers do not need to be aware |
---|
60 | of them, since they are detected at autoconfigure time and loaded |
---|
61 | automatically when the device is opened. These include the |
---|
62 | <tt>tty_clk</tt> and <tt>ppsclock</tt> STREAMS modules and |
---|
63 | <tt>ppsapi</tt> PPS interface described in the <a href="ldisc.htm">Line |
---|
64 | Disciplines and Streams Modules</a> page. The <tt>tty_clk</tt> module |
---|
65 | reduces latency errors due to the operating system and serial port code |
---|
66 | in slower systems. The <tt>ppsclock</tt> module is an interface for the |
---|
67 | PPS signal provided by some radios. The <tt>ppsapi</tt> PPS interface |
---|
68 | replaces the <tt>ppsclock</tt> STREAMS module and is expected to become |
---|
69 | the IETF standard cross-platform interface for PPS signals. In either |
---|
70 | case, the PPS signal can be connected via a level converter/pulse |
---|
71 | generator described in the <a href = "gadget.htm"> Gadget Box PPS Level |
---|
72 | Converter and CHU Modem</a> page. |
---|
73 | |
---|
74 | <p>By convention, reference clock drivers are named in the form |
---|
75 | <tt>refclock_<i>xxxx</i>.c</tt>, where <i>xxxx</i> is a unique |
---|
76 | string. Each driver is assigned a unique type number, long-form driver |
---|
77 | name, short-form driver name, and device name. The existing assignments |
---|
78 | are in the <a href="refclock.htm"> Reference Clock Drivers</a> page |
---|
79 | and its dependencies. All drivers supported by the particular hardware |
---|
80 | and operating system are automatically detected in the autoconfigure |
---|
81 | phase and conditionally compiled. They are configured when the daemon is |
---|
82 | started according to the configuration file, as described in the <a |
---|
83 | href="config.htm"> Configuration Options </a> page. |
---|
84 | |
---|
85 | <p>The standard clock driver interface includes a set of common support |
---|
86 | routines some of which do such things as start and stop the device, open |
---|
87 | the serial port, and establish special functions such as PPS signal |
---|
88 | support. Other routines read and write data to the device and process |
---|
89 | time values. Most drivers need only a little customizing code to, for |
---|
90 | instance, transform idiosyncratic timecode formats to standard form, |
---|
91 | poll the device as necessary, and handle exception conditions. A |
---|
92 | standard interface is available for remote debugging and monitoring |
---|
93 | programs, such as <tt>ntpq</tt> and <tt>ntpdc</tt>, as well as |
---|
94 | the <tt>filegen</tt> facility, which can be used to record device |
---|
95 | status on a continuous basis. |
---|
96 | |
---|
97 | <p>The general organization of a typical clock driver includes a |
---|
98 | receive-interrupt routine to read a timecode from the I/O buffer and |
---|
99 | convert to internal format, generally in days, hours, minutes, seconds |
---|
100 | and fraction. Some timecode formats include provisions for leap-second |
---|
101 | warning and determine the clock hardware and software health. The |
---|
102 | interrupt routine then calls <tt>refclock_process()</tt> with these data |
---|
103 | and the timestamp captured at the on-time character of the timecode. |
---|
104 | This routine saves each sample as received in a circular buffer, which |
---|
105 | can store from a few up to 60 samples, in cases where the timecodes |
---|
106 | arrive one per second. |
---|
107 | |
---|
108 | <p>The <tt>refclock_transmit()</tt> routine in the interface is called |
---|
109 | by the system at intervals defined by the poll interval in the peer |
---|
110 | structure, generally 64 s. This routine in turn calls the transmit poll |
---|
111 | routine in the driver. In the intended design, the driver calls the |
---|
112 | <tt>refclock_receive()</tt> to process the offset samples that have |
---|
113 | accumulated since the last poll and produce the final offset and |
---|
114 | variance. The samples are processed by recursively discarding median |
---|
115 | outlyers until about 60 percent of samples remain, then averaging the |
---|
116 | surviving samples. When a reference clock must be explicitly polled to |
---|
117 | produce a timecode, the driver can reset the poll interval so that the |
---|
118 | poll routine is called a specified number of times at 1-s intervals. |
---|
119 | |
---|
120 | <p>The interface code and this documentation have been developed over |
---|
121 | some time and required not a little hard work converting old drivers, |
---|
122 | etc. Should you find success writing a driver for a new radio or modem |
---|
123 | service, please consider contributing it to the common good. Send the |
---|
124 | driver file itself and patches for the other files to Dave Mills |
---|
125 | (mills@udel.edu). |
---|
126 | |
---|
127 | <h4>Conventions, Fudge Factors and Flags</h4> |
---|
128 | |
---|
129 | <p>Most drivers support manual or automatic calibration for systematic |
---|
130 | offset bias using values encoded in the <tt>fudge</tt> configuration |
---|
131 | command. By convention, the <tt>time1</tt> value defines the calibration |
---|
132 | offset in seconds. For those drivers that support statistics collection |
---|
133 | using the <tt>filegen</tt> utility and the <tt>clockstats</tt> file, the |
---|
134 | <tt>flag4</tt> switch enables the utility. When a PPS signal is |
---|
135 | available, a special automatic calibration facility is provided. If the |
---|
136 | <tt>flag1</tt> switch is set and the PPS signal is actively disciplining |
---|
137 | the system time, the calibration value is automatically adjusted to |
---|
138 | maintain a residual offset of zero. Should the PPS signal or the prefer |
---|
139 | peer fail, the adjustment is frozen and the remaining drivers continue |
---|
140 | to discipline the system clock with a minimum of residual error. |
---|
141 | |
---|
142 | <h4>Files Which Need to be Changed</h4> |
---|
143 | |
---|
144 | <p>A new reference clock implementation needs to supply, in addition to |
---|
145 | the driver itself, several changes to existing files. |
---|
146 | |
---|
147 | <dl> |
---|
148 | |
---|
149 | <dt><tt>./include/ntp.h</tt> |
---|
150 | <dd>The reference clock type defines are used in many places. Each |
---|
151 | driver is assigned a unique type number. Unused numbers are clearly |
---|
152 | marked in the list. A unique <tt>REFCLK_<i>xxxx</i></tt> |
---|
153 | identification code should be recorded in the list opposite its assigned |
---|
154 | type number. |
---|
155 | |
---|
156 | <p><dt><tt>./libntp/clocktypes.c</tt> |
---|
157 | <dd>The <tt>./libntp/clktype</tt> array is used by certain display |
---|
158 | functions. A unique short-form name of the driver should be entered |
---|
159 | together with its assigned identification code. |
---|
160 | |
---|
161 | <p><dt><tt>./ntpd/ntp_control.c</tt> |
---|
162 | <dd>The <tt>clocktypes</tt> array is used for certain control |
---|
163 | message displays functions. It should be initialized with the reference |
---|
164 | clock class assigned to the driver, as per the NTP specification |
---|
165 | RFC-1305. See the <tt>./include/ntp_control.h</tt> header file for |
---|
166 | the assigned classes. |
---|
167 | |
---|
168 | <p><dt><tt>./ntpd/refclock_conf.c</tt> |
---|
169 | <dd>This file contains a list of external structure definitions which |
---|
170 | are conditionally defined. A new set of entries should be installed |
---|
171 | similar to those already in the table. The <tt>refclock_conf</tt> |
---|
172 | array is a set of pointers to transfer vectors in the individual |
---|
173 | drivers. The external name of the transfer vector should be initialized |
---|
174 | in correspondence with the type number. |
---|
175 | |
---|
176 | <p><dt><tt>./acconfig.h</tt> |
---|
177 | <dd>This is a configuration file used by the autoconfigure scheme. Add |
---|
178 | two lines in the form: |
---|
179 | |
---|
180 | <p><pre> |
---|
181 | /* Define if we have a FOO clock */ |
---|
182 | #undef FOO |
---|
183 | </pre> |
---|
184 | |
---|
185 | <p>where FOO is the define used to cause the driver to be included in |
---|
186 | the distribution. |
---|
187 | |
---|
188 | <p><dt><tt>./configure.in</tt> |
---|
189 | <dd>This is a configuration file used by the autoconfigure scheme. Add |
---|
190 | lines similar to the following: |
---|
191 | |
---|
192 | <p><pre> |
---|
193 | AC_MSG_CHECKING(FOO clock_description) |
---|
194 | AC_ARG_ENABLE(FOO, [ --enable-FOO clock_description], |
---|
195 | [ntp_ok=$enableval], [ntp_ok=$ntp_eac]) |
---|
196 | if test "$ntp_ok" = "yes"; then |
---|
197 | ntp_refclock=yes |
---|
198 | AC_DEFINE(FOO) |
---|
199 | fi |
---|
200 | AC_MSG_RESULT($ntp_ok) |
---|
201 | </pre> |
---|
202 | |
---|
203 | <p>(Note that <tt>$ntp_eac</tt> is the value from <tt>-- |
---|
204 | {dis,en}able-all-clocks</tt> for non-PARSE clocks and |
---|
205 | <tt>$ntp_eacp</tt> is the value from <tt>--{dis,en}able-parse- |
---|
206 | clocks</tt> for PARSE clocks. See the documentation on the autoconf |
---|
207 | and automake tools from the GNU distributions.) |
---|
208 | |
---|
209 | <p><dt><tt>./ntpd/Makefile.am</tt> |
---|
210 | <dd><p>This is the makefile prototype used by the autoconfigure scheme. |
---|
211 | Add the driver file name to the entries already in the |
---|
212 | <tt>ntpd_SOURCES</tt> list. |
---|
213 | |
---|
214 | <p>Patches to <tt>automake-1.0</tt> are required for the |
---|
215 | autoconfigure scripts to work properly. The file <tt>automake- |
---|
216 | 1.0.patches</tt> can be used for this purpose. |
---|
217 | |
---|
218 | <p><dt><tt>./ntpd/Makefile.am</tt> |
---|
219 | <dd>Do the following sequence of commands: |
---|
220 | |
---|
221 | <p><pre> |
---|
222 | automake |
---|
223 | autoconf |
---|
224 | autoheader |
---|
225 | configure |
---|
226 | </pre> |
---|
227 | |
---|
228 | <p>or simply run <tt>make</tt>, which will do this command sequence |
---|
229 | automatically. |
---|
230 | |
---|
231 | </dl> |
---|
232 | |
---|
233 | <p><h4>Interface Routine Overview</h4> |
---|
234 | |
---|
235 | <dl> |
---|
236 | |
---|
237 | <dt><tt>refclock_newpeer</tt> - initialize and start a reference |
---|
238 | clock |
---|
239 | <dd>This routine allocates and initializes the interface structure which |
---|
240 | supports a reference clock in the form of an ordinary NTP peer. A |
---|
241 | driver-specific support routine completes the initialization, if used. |
---|
242 | Default peer variables which identify the clock and establish its |
---|
243 | reference ID and stratum are set here. It returns one if success and |
---|
244 | zero if the clock address is invalid or already running, insufficient |
---|
245 | resources are available or the driver declares a bum rap. |
---|
246 | <p><dt><tt>refclock_unpeer</tt> - shut down a clock |
---|
247 | <dd>This routine is used to shut down a clock and return its resources |
---|
248 | to the system. |
---|
249 | |
---|
250 | <p><dt><tt>refclock_transmit</tt> - simulate the transmit procedure |
---|
251 | <dd>This routine implements the NTP transmit procedure for a reference |
---|
252 | clock. This provides a mechanism to call the driver at the NTP poll |
---|
253 | interval, as well as provides a reachability mechanism to detect a |
---|
254 | broken radio or other madness. |
---|
255 | |
---|
256 | <p><dt><tt>refclock_sample</tt> - process a pile of samples from the |
---|
257 | clock |
---|
258 | <dd>This routine converts the timecode in the form days, hours, minutes, |
---|
259 | seconds, milliseconds/microseconds to internal timestamp format. It then |
---|
260 | calculates the difference from the receive timestamp and assembles the |
---|
261 | samples in a shift register. It implements a recursive median filter to |
---|
262 | suppress spikes in the data, as well as determine a rough dispersion |
---|
263 | estimate. A configuration constant time adjustment |
---|
264 | <tt>fudgetime1</tt> can be added to the final offset to compensate |
---|
265 | for various systematic errors. The routine returns one if success and |
---|
266 | zero if failure due to invalid timecode data or very noisy offsets. |
---|
267 | |
---|
268 | <p>Note that no provision is included for the year, as provided by some |
---|
269 | (but not all) radio clocks. Ordinarily, the year is implicit in the Unix |
---|
270 | file system and hardware/software clock support, so this is ordinarily |
---|
271 | not a problem. Nevertheless, the absence of the year should be |
---|
272 | considered more a bug than a feature and may be supported in future. |
---|
273 | |
---|
274 | <p><dt><tt>refclock_receive</tt> - simulate the receive and packet |
---|
275 | procedures |
---|
276 | <dd>This routine simulates the NTP receive and packet procedures for a |
---|
277 | reference clock. This provides a mechanism in which the ordinary NTP |
---|
278 | filter, selection and combining algorithms can be used to suppress |
---|
279 | misbehaving radios and to mitigate between them when more than one is |
---|
280 | available for backup. |
---|
281 | |
---|
282 | <p><dt><tt>refclock_gtlin</tt> - groom next input line and extract |
---|
283 | timestamp |
---|
284 | <dd>This routine processes the timecode received from the clock and |
---|
285 | removes the parity bit and control characters. If a timestamp is present |
---|
286 | in the timecode, as produced by the <tt>tty_clk</tt> line |
---|
287 | discipline/streams module, it returns that as the timestamp; otherwise, |
---|
288 | it returns the buffer timestamp. The routine return code is the number |
---|
289 | of characters in the line. |
---|
290 | |
---|
291 | <p><dt><tt>refclock_open</tt> - open serial port for reference clock |
---|
292 | <dd>This routine opens a serial port for I/O and sets default options. |
---|
293 | It returns the file descriptor if success and zero if failure. |
---|
294 | |
---|
295 | <p><dt><tt>refclock_ioctl</tt> - set serial port control functions |
---|
296 | <dd>This routine attempts to hide the internal, system-specific details |
---|
297 | of serial ports. It can handle POSIX (<tt>termios</tt>), SYSV |
---|
298 | (<tt>termio</tt>) and BSD (<tt>sgtty</tt>) interfaces with |
---|
299 | varying degrees of success. The routine sets up the <tt>tty_clk, |
---|
300 | chu_clk</tt> and <tt>ppsclock</tt> streams module/line discipline, |
---|
301 | if compiled in the daemon and requested in the call. The routine returns |
---|
302 | one if success and zero if failure. |
---|
303 | |
---|
304 | <p><dt><tt>refclock_control</tt> - set and/or return clock values |
---|
305 | <dd>This routine is used mainly for debugging. It returns designated |
---|
306 | values from the interface structure that can be displayed using ntpdc |
---|
307 | and the clockstat command. It can also be used to initialize |
---|
308 | configuration variables, such as <tt>fudgetimes, fudgevalues,</tt> |
---|
309 | reference ID and stratum. |
---|
310 | |
---|
311 | <p><dt><tt>refclock_buginfo</tt> - return debugging info |
---|
312 | <dd>This routine is used mainly for debugging. It returns designated |
---|
313 | values from the interface structure that can be displayed using |
---|
314 | <tt>ntpdc</tt> and the <tt>clkbug</tt> command. |
---|
315 | |
---|
316 | </dl> |
---|
317 | |
---|
318 | <hr><a href=index.htm><img align=left src=pic/home.gif></a><address><a |
---|
319 | href=mailto:mills@udel.edu> David L. Mills <mills@udel.edu></a> |
---|
320 | </address></a></body></html> |
---|