xref: /freebsd/contrib/ntp/ntpd/refclock_arc.c (revision 224ba2bd37e182b64f7d78defef8a6cacaad3415)
1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert  * refclock_arc - clock driver for ARCRON MSF receivers
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert 
5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
6c0b746e5SOllivier Robert #include <config.h>
7c0b746e5SOllivier Robert #endif
8c0b746e5SOllivier Robert 
9c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
10c0b746e5SOllivier Robert static const char arc_version[] = { "V1.1 1997/06/23" };
11c0b746e5SOllivier Robert 
12c0b746e5SOllivier Robert #undef ARCRON_DEBUG /* Define only while in development... */
13c0b746e5SOllivier Robert 
14c0b746e5SOllivier Robert #ifndef ARCRON_NOT_KEEN
15c0b746e5SOllivier Robert #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
16c0b746e5SOllivier Robert #endif
17c0b746e5SOllivier Robert 
18c0b746e5SOllivier Robert #ifndef ARCRON_NOT_MULTIPLE_SAMPLES
19c0b746e5SOllivier Robert #define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
20c0b746e5SOllivier Robert #endif
21c0b746e5SOllivier Robert 
22c0b746e5SOllivier Robert #ifndef ARCRON_NOT_LEAPSECOND_KEEN
23c0b746e5SOllivier Robert #ifndef ARCRON_LEAPSECOND_KEEN
24c0b746e5SOllivier Robert #undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
25c0b746e5SOllivier Robert #endif
26c0b746e5SOllivier Robert #endif
27c0b746e5SOllivier Robert 
28c0b746e5SOllivier Robert /*
29c0b746e5SOllivier Robert Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
30c0b746e5SOllivier Robert Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
31c0b746e5SOllivier Robert 
32c0b746e5SOllivier Robert THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
33c0b746e5SOllivier Robert YOUR OWN RISK.
34c0b746e5SOllivier Robert 
35c0b746e5SOllivier Robert Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
36c0b746e5SOllivier Robert 
37c0b746e5SOllivier Robert Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
38c0b746e5SOllivier Robert 
39c0b746e5SOllivier Robert This code may be freely copied and used and incorporated in other
40c0b746e5SOllivier Robert systems providing the disclaimer and notice of authorship are
41c0b746e5SOllivier Robert reproduced.
42c0b746e5SOllivier Robert 
43c0b746e5SOllivier Robert -------------------------------------------------------------------------------
44c0b746e5SOllivier Robert 
45c0b746e5SOllivier Robert Author's original note:
46c0b746e5SOllivier Robert 
47c0b746e5SOllivier Robert I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
48c0b746e5SOllivier Robert 
49c0b746e5SOllivier Robert It works (after a fashion) on both Solaris-1 and Solaris-2.
50c0b746e5SOllivier Robert 
51c0b746e5SOllivier Robert I am currently using ntp3-5.85.  I have been running the code for
52c0b746e5SOllivier Robert about 7 months without any problems.  Even coped with the change to BST!
53c0b746e5SOllivier Robert 
54c0b746e5SOllivier Robert I had to do some funky things to read from the clock because it uses the
55c0b746e5SOllivier Robert power from the receive lines to drive the transmit lines.  This makes the
56c0b746e5SOllivier Robert code look a bit stupid but it works.  I also had to put in some delays to
57c0b746e5SOllivier Robert allow for the turnaround time from receive to transmit.  These delays
58c0b746e5SOllivier Robert are between characters when requesting a time stamp so that shouldn't affect
59c0b746e5SOllivier Robert the results too drastically.
60c0b746e5SOllivier Robert 
61c0b746e5SOllivier Robert ...
62c0b746e5SOllivier Robert 
63c0b746e5SOllivier Robert The bottom line is that it works but could easily be improved.  You are
64c0b746e5SOllivier Robert free to do what you will with the code.  I haven't been able to determine
65c0b746e5SOllivier Robert how good the clock is.  I think that this requires a known good clock
66c0b746e5SOllivier Robert to compare it against.
67c0b746e5SOllivier Robert 
68c0b746e5SOllivier Robert -------------------------------------------------------------------------------
69c0b746e5SOllivier Robert 
70c0b746e5SOllivier Robert Damon's notes for adjustments:
71c0b746e5SOllivier Robert 
72c0b746e5SOllivier Robert MAJOR CHANGES SINCE V1.0
73c0b746e5SOllivier Robert ========================
74c0b746e5SOllivier Robert  1) Removal of pollcnt variable that made the clock go permanently
75c0b746e5SOllivier Robert     off-line once two time polls failed to gain responses.
76c0b746e5SOllivier Robert 
77c0b746e5SOllivier Robert  2) Avoiding (at least on Solaris-2) terminal becoming the controlling
78c0b746e5SOllivier Robert     terminal of the process when we do a low-level open().
79c0b746e5SOllivier Robert 
80c0b746e5SOllivier Robert  3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
81c0b746e5SOllivier Robert     defined) to try to resync quickly after a potential leap-second
82c0b746e5SOllivier Robert     insertion or deletion.
83c0b746e5SOllivier Robert 
84c0b746e5SOllivier Robert  4) Code significantly slimmer at run-time than V1.0.
85c0b746e5SOllivier Robert 
86c0b746e5SOllivier Robert 
87c0b746e5SOllivier Robert GENERAL
88c0b746e5SOllivier Robert =======
89c0b746e5SOllivier Robert 
90c0b746e5SOllivier Robert  1) The C preprocessor symbol to have the clock built has been changed
91c0b746e5SOllivier Robert     from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
92c0b746e5SOllivier Robert     possiblity of clashes with other symbols in the future.
93c0b746e5SOllivier Robert 
94c0b746e5SOllivier Robert  2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
95c0b746e5SOllivier Robert 
96c0b746e5SOllivier Robert      a) The ARC documentation claims the internal clock is (only)
97c0b746e5SOllivier Robert         accurate to about 20ms relative to Rugby (plus there must be
98c0b746e5SOllivier Robert         noticable drift and delay in the ms range due to transmission
99c0b746e5SOllivier Robert         delays and changing atmospheric effects).  This clock is not
100c0b746e5SOllivier Robert         designed for ms accuracy as NTP has spoilt us all to expect.
101c0b746e5SOllivier Robert 
102c0b746e5SOllivier Robert      b) The clock oscillator looks like a simple uncompensated quartz
103c0b746e5SOllivier Robert         crystal of the sort used in digital watches (ie 32768Hz) which
104c0b746e5SOllivier Robert         can have large temperature coefficients and drifts; it is not
105c0b746e5SOllivier Robert         clear if this oscillator is properly disciplined to the MSF
106c0b746e5SOllivier Robert         transmission, but as the default is to resync only once per
107c0b746e5SOllivier Robert         *day*, we can imagine that it is not, and is free-running.  We
108c0b746e5SOllivier Robert         can minimise drift by resyncing more often (at the cost of
109c0b746e5SOllivier Robert         reduced battery life), but drift/wander may still be
110c0b746e5SOllivier Robert         significant.
111c0b746e5SOllivier Robert 
112c0b746e5SOllivier Robert      c) Note that the bit time of 3.3ms adds to the potential error in
113c0b746e5SOllivier Robert         the the clock timestamp, since the bit clock of the serial link
114c0b746e5SOllivier Robert         may effectively be free-running with respect to the host clock
115c0b746e5SOllivier Robert         and the MSF clock.  Actually, the error is probably 1/16th of
116c0b746e5SOllivier Robert         the above, since the input data is probably sampled at at least
117c0b746e5SOllivier Robert         16x the bit rate.
118c0b746e5SOllivier Robert 
119c0b746e5SOllivier Robert     By keeping the clock marked as not very precise, it will have a
120c0b746e5SOllivier Robert     fairly large dispersion, and thus will tend to be used as a
121c0b746e5SOllivier Robert     `backup' time source and sanity checker, which this clock is
122c0b746e5SOllivier Robert     probably ideal for.  For an isolated network without other time
123c0b746e5SOllivier Robert     sources, this clock can probably be expected to provide *much*
124c0b746e5SOllivier Robert     better than 1s accuracy, which will be fine.
125c0b746e5SOllivier Robert 
126c0b746e5SOllivier Robert     By default, PRECISION is set to -4, but experience, especially at a
127c0b746e5SOllivier Robert     particular geographic location with a particular clock, may allow
128c0b746e5SOllivier Robert     this to be altered to -5.  (Note that skews of +/- 10ms are to be
129c0b746e5SOllivier Robert     expected from the clock from time-to-time.)  This improvement of
130c0b746e5SOllivier Robert     reported precision can be instigated by setting flag3 to 1, though
131c0b746e5SOllivier Robert     the PRECISION will revert to the normal value while the clock
132c0b746e5SOllivier Robert     signal quality is unknown whatever the flag3 setting.
133c0b746e5SOllivier Robert 
134c0b746e5SOllivier Robert     IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
135c0b746e5SOllivier Robert     ANY RESIDUAL SKEW, eg:
136c0b746e5SOllivier Robert 
137c0b746e5SOllivier Robert         server 127.127.27.0 # ARCRON MSF radio clock unit 0.
138c0b746e5SOllivier Robert         # Fudge timestamps by about 20ms.
139c0b746e5SOllivier Robert         fudge 127.127.27.0 time1 0.020
140c0b746e5SOllivier Robert 
141c0b746e5SOllivier Robert     You will need to observe your system's behaviour, assuming you have
142c0b746e5SOllivier Robert     some other NTP source to compare it with, to work out what the
143c0b746e5SOllivier Robert     fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
144c0b746e5SOllivier Robert     my MSF clock with my distance from the MSF transmitter, +20ms
145c0b746e5SOllivier Robert     seemed about right, after some observation.
146c0b746e5SOllivier Robert 
147c0b746e5SOllivier Robert  3) REFID has been made "MSFa" to reflect the MSF time source and the
148c0b746e5SOllivier Robert     ARCRON receiver.
149c0b746e5SOllivier Robert 
150c0b746e5SOllivier Robert  4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
151c0b746e5SOllivier Robert     forcing a resync since the last attempt.  This is picked to give a
152c0b746e5SOllivier Robert     little less than an hour between resyncs and to try to avoid
153c0b746e5SOllivier Robert     clashing with any regular event at a regular time-past-the-hour
154c0b746e5SOllivier Robert     which might cause systematic errors.
155c0b746e5SOllivier Robert 
156c0b746e5SOllivier Robert     The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
157c0b746e5SOllivier Robert     running down its batteries unnecesarily if ntpd is going to crash
158c0b746e5SOllivier Robert     or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
159c0b746e5SOllivier Robert     then this period is long enough for (with normal polling rates)
160c0b746e5SOllivier Robert     enough time samples to have been taken to allow ntpd to sync to
161c0b746e5SOllivier Robert     the clock before the interruption for the clock to resync to MSF.
162c0b746e5SOllivier Robert     This avoids ntpd syncing to another peer first and then
163c0b746e5SOllivier Robert     almost immediately hopping to the MSF clock.
164c0b746e5SOllivier Robert 
165c0b746e5SOllivier Robert     The RETRY_RESYNC_TIME is used before rescheduling a resync after a
166c0b746e5SOllivier Robert     resync failed to reveal a statisfatory signal quality (too low or
167c0b746e5SOllivier Robert     unknown).
168c0b746e5SOllivier Robert 
169c0b746e5SOllivier Robert  5) The clock seems quite jittery, so I have increased the
170c0b746e5SOllivier Robert     median-filter size from the typical (previous) value of 3.  I
171c0b746e5SOllivier Robert     discard up to half the results in the filter.  It looks like maybe
172c0b746e5SOllivier Robert     1 sample in 10 or so (maybe less) is a spike, so allow the median
173c0b746e5SOllivier Robert     filter to discard at least 10% of its entries or 1 entry, whichever
174c0b746e5SOllivier Robert     is greater.
175c0b746e5SOllivier Robert 
176c0b746e5SOllivier Robert  6) Sleeping *before* each character sent to the unit to allow required
177c0b746e5SOllivier Robert     inter-character time but without introducting jitter and delay in
178c0b746e5SOllivier Robert     handling the response if possible.
179c0b746e5SOllivier Robert 
180c0b746e5SOllivier Robert  7) If the flag ARCRON_KEEN is defined, take time samples whenever
181c0b746e5SOllivier Robert     possible, even while resyncing, etc.  We rely, in this case, on the
182c0b746e5SOllivier Robert     clock always giving us a reasonable time or else telling us in the
183c0b746e5SOllivier Robert     status byte at the end of the timestamp that it failed to sync to
184c0b746e5SOllivier Robert     MSF---thus we should never end up syncing to completely the wrong
185c0b746e5SOllivier Robert     time.
186c0b746e5SOllivier Robert 
187c0b746e5SOllivier Robert  8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
188c0b746e5SOllivier Robert     refclock median-filter routines to get round small bug in 3-5.90
189c0b746e5SOllivier Robert     code which does not return the median offset. XXX Removed this
190c0b746e5SOllivier Robert     bit due NTP Version 4 upgrade - dlm.
191c0b746e5SOllivier Robert 
192c0b746e5SOllivier Robert  9) We would appear to have a year-2000 problem with this clock since
193c0b746e5SOllivier Robert     it returns only the two least-significant digits of the year.  But
194c0b746e5SOllivier Robert     ntpd ignores the year and uses the local-system year instead, so
195c0b746e5SOllivier Robert     this is in fact not a problem.  Nevertheless, we attempt to do a
196c0b746e5SOllivier Robert     sensible thing with the dates, wrapping them into a 100-year
197c0b746e5SOllivier Robert     window.
198c0b746e5SOllivier Robert 
199c0b746e5SOllivier Robert  10)Logs stats information that can be used by Derek's Tcl/Tk utility
200c0b746e5SOllivier Robert     to show the status of the clock.
201c0b746e5SOllivier Robert 
202c0b746e5SOllivier Robert  11)The clock documentation insists that the number of bits per
203c0b746e5SOllivier Robert     character to be sent to the clock, and sent by it, is 11, including
204c0b746e5SOllivier Robert     one start bit and two stop bits.  The data format is either 7+even
205c0b746e5SOllivier Robert     or 8+none.
206c0b746e5SOllivier Robert 
207c0b746e5SOllivier Robert 
208c0b746e5SOllivier Robert TO-DO LIST
209c0b746e5SOllivier Robert ==========
210c0b746e5SOllivier Robert 
211c0b746e5SOllivier Robert   * Eliminate use of scanf(), and maybe sprintf().
212c0b746e5SOllivier Robert 
213c0b746e5SOllivier Robert   * Allow user setting of resync interval to trade battery life for
214c0b746e5SOllivier Robert     accuracy; maybe could be done via fudge factor or unit number.
215c0b746e5SOllivier Robert 
216c0b746e5SOllivier Robert   * Possibly note the time since the last resync of the MSF clock to
217c0b746e5SOllivier Robert     MSF as the age of the last reference timestamp, ie trust the
218c0b746e5SOllivier Robert     clock's oscillator not very much...
219c0b746e5SOllivier Robert 
220c0b746e5SOllivier Robert   * Add very slow auto-adjustment up to a value of +/- time2 to correct
221c0b746e5SOllivier Robert     for long-term errors in the clock value (time2 defaults to 0 so the
222c0b746e5SOllivier Robert     correction would be disabled by default).
223c0b746e5SOllivier Robert 
224c0b746e5SOllivier Robert   * Consider trying to use the tty_clk/ppsclock support.
225c0b746e5SOllivier Robert 
226c0b746e5SOllivier Robert   * Possibly use average or maximum signal quality reported during
227c0b746e5SOllivier Robert     resync, rather than just the last one, which may be atypical.
228c0b746e5SOllivier Robert 
229c0b746e5SOllivier Robert */
230c0b746e5SOllivier Robert 
231c0b746e5SOllivier Robert 
232c0b746e5SOllivier Robert /* Notes for HKW Elektronik GmBH Radio clock driver */
233c0b746e5SOllivier Robert /* Author Lyndon David, Sentinet Ltd, Feb 1997      */
234c0b746e5SOllivier Robert /* These notes seem also to apply usefully to the ARCRON clock. */
235c0b746e5SOllivier Robert 
236c0b746e5SOllivier Robert /* The HKW clock module is a radio receiver tuned into the Rugby */
237c0b746e5SOllivier Robert /* MSF time signal tranmitted on 60 kHz. The clock module connects */
238c0b746e5SOllivier Robert /* to the computer via a serial line and transmits the time encoded */
239c0b746e5SOllivier Robert /* in 15 bytes at 300 baud 7 bits two stop bits even parity */
240c0b746e5SOllivier Robert 
241c0b746e5SOllivier Robert /* Clock communications, from the datasheet */
242c0b746e5SOllivier Robert /* All characters sent to the clock are echoed back to the controlling */
243c0b746e5SOllivier Robert /* device. */
244c0b746e5SOllivier Robert /* Transmit time/date information */
245c0b746e5SOllivier Robert /* syntax ASCII o<cr> */
246c0b746e5SOllivier Robert /* Character o may be replaced if neccesary by a character whose code */
247c0b746e5SOllivier Robert /* contains the lowest four bits f(hex) eg */
248c0b746e5SOllivier Robert /* syntax binary: xxxx1111 00001101 */
249c0b746e5SOllivier Robert 
250c0b746e5SOllivier Robert /* DHD note:
251c0b746e5SOllivier Robert You have to wait for character echo + 10ms before sending next character.
252c0b746e5SOllivier Robert */
253c0b746e5SOllivier Robert 
254c0b746e5SOllivier Robert /* The clock replies to this command with a sequence of 15 characters */
255c0b746e5SOllivier Robert /* which contain the complete time and a final <cr> making 16 characters */
256c0b746e5SOllivier Robert /* in total. */
257c0b746e5SOllivier Robert /* The RC computer clock will not reply immediately to this command because */
258c0b746e5SOllivier Robert /* the start bit edge of the first reply character marks the beginning of */
259c0b746e5SOllivier Robert /* the second. So the RC Computer Clock will reply to this command at the */
260c0b746e5SOllivier Robert /* start of the next second */
261c0b746e5SOllivier Robert /* The characters have the following meaning */
262c0b746e5SOllivier Robert /* 1. hours tens   */
263c0b746e5SOllivier Robert /* 2. hours units  */
264c0b746e5SOllivier Robert /* 3. minutes tens */
265c0b746e5SOllivier Robert /* 4. minutes units */
266c0b746e5SOllivier Robert /* 5. seconds tens  */
267c0b746e5SOllivier Robert /* 6. seconds units */
268c0b746e5SOllivier Robert /* 7. day of week 1-monday 7-sunday */
269c0b746e5SOllivier Robert /* 8. day of month tens */
270c0b746e5SOllivier Robert /* 9. day of month units */
271c0b746e5SOllivier Robert /* 10. month tens */
272c0b746e5SOllivier Robert /* 11. month units */
273c0b746e5SOllivier Robert /* 12. year tens */
274c0b746e5SOllivier Robert /* 13. year units */
275c0b746e5SOllivier Robert /* 14. BST/UTC status */
276c0b746e5SOllivier Robert /*      bit 7   parity */
277c0b746e5SOllivier Robert /*      bit 6   always 0 */
278c0b746e5SOllivier Robert /*      bit 5   always 1 */
279c0b746e5SOllivier Robert /*      bit 4   always 1 */
280c0b746e5SOllivier Robert /*      bit 3   always 0 */
281c0b746e5SOllivier Robert /*      bit 2   =1 if UTC is in effect, complementary to the BST bit */
282c0b746e5SOllivier Robert /*      bit 1   =1 if BST is in effect, according to the BST bit     */
283c0b746e5SOllivier Robert /*      bit 0   BST/UTC change impending bit=1 in case of change impending */
284c0b746e5SOllivier Robert /* 15. status */
285c0b746e5SOllivier Robert /*      bit 7   parity */
286c0b746e5SOllivier Robert /*      bit 6   always 0 */
287c0b746e5SOllivier Robert /*      bit 5   always 1 */
288c0b746e5SOllivier Robert /*      bit 4   always 1 */
289c0b746e5SOllivier Robert /*      bit 3   =1 if low battery is detected */
290c0b746e5SOllivier Robert /*      bit 2   =1 if the very last reception attempt failed and a valid */
291c0b746e5SOllivier Robert /*              time information already exists (bit0=1) */
292c0b746e5SOllivier Robert /*              =0 if the last reception attempt was successful */
293c0b746e5SOllivier Robert /*      bit 1   =1 if at least one reception since 2:30 am was successful */
294c0b746e5SOllivier Robert /*              =0 if no reception attempt since 2:30 am was successful */
295c0b746e5SOllivier Robert /*      bit 0   =1 if the RC Computer Clock contains valid time information */
296c0b746e5SOllivier Robert /*              This bit is zero after reset and one after the first */
297c0b746e5SOllivier Robert /*              successful reception attempt */
298c0b746e5SOllivier Robert 
299c0b746e5SOllivier Robert /* DHD note:
300c0b746e5SOllivier Robert Also note g<cr> command which confirms that a resync is in progress, and
301c0b746e5SOllivier Robert if so what signal quality (0--5) is available.
302c0b746e5SOllivier Robert Also note h<cr> command which starts a resync to MSF signal.
303c0b746e5SOllivier Robert */
304c0b746e5SOllivier Robert 
305c0b746e5SOllivier Robert 
306224ba2bdSOllivier Robert #include "ntpd.h"
307224ba2bdSOllivier Robert #include "ntp_io.h"
308224ba2bdSOllivier Robert #include "ntp_refclock.h"
309224ba2bdSOllivier Robert #include "ntp_stdlib.h"
310c0b746e5SOllivier Robert 
311c0b746e5SOllivier Robert #include <stdio.h>
312c0b746e5SOllivier Robert #include <ctype.h>
313c0b746e5SOllivier Robert 
314c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS)
315c0b746e5SOllivier Robert #include <sgtty.h>
316c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */
317c0b746e5SOllivier Robert 
318c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS)
319c0b746e5SOllivier Robert #include <termio.h>
320c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */
321c0b746e5SOllivier Robert 
322c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS)
323c0b746e5SOllivier Robert #include <termios.h>
324c0b746e5SOllivier Robert #endif
325c0b746e5SOllivier Robert 
326c0b746e5SOllivier Robert /*
327c0b746e5SOllivier Robert  * This driver supports the ARCRON MSF Radio Controlled Clock
328c0b746e5SOllivier Robert  */
329c0b746e5SOllivier Robert 
330c0b746e5SOllivier Robert /*
331c0b746e5SOllivier Robert  * Interface definitions
332c0b746e5SOllivier Robert  */
333c0b746e5SOllivier Robert #define DEVICE          "/dev/arc%d"    /* Device name and unit. */
334c0b746e5SOllivier Robert #define SPEED           B300            /* UART speed (300 baud) */
335c0b746e5SOllivier Robert #define PRECISION       (-4)            /* Precision  (~63 ms). */
336c0b746e5SOllivier Robert #define HIGHPRECISION   (-5)            /* If things are going well... */
337c0b746e5SOllivier Robert #define REFID           "MSFa"          /* Reference ID. */
338c0b746e5SOllivier Robert #define DESCRIPTION     "ARCRON MSF Receiver"
339c0b746e5SOllivier Robert 
340c0b746e5SOllivier Robert #define NSAMPLESLONG    8               /* Stages of long filter. */
341c0b746e5SOllivier Robert 
342c0b746e5SOllivier Robert #define LENARC          16              /* Format `o' timecode length. */
343c0b746e5SOllivier Robert 
344c0b746e5SOllivier Robert #define BITSPERCHAR     11              /* Bits per character. */
345c0b746e5SOllivier Robert #define BITTIME         0x0DA740E       /* Time for 1 bit at 300bps. */
346c0b746e5SOllivier Robert #define CHARTIME10      0x8888888       /* Time for 10-bit char at 300bps. */
347c0b746e5SOllivier Robert #define CHARTIME11      0x962FC96       /* Time for 11-bit char at 300bps. */
348c0b746e5SOllivier Robert #define CHARTIME                        /* Time for char at 300bps. */ \
349c0b746e5SOllivier Robert ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
350c0b746e5SOllivier Robert 				       (BITSPERCHAR * BITTIME) ) )
351c0b746e5SOllivier Robert 
352c0b746e5SOllivier Robert      /* Allow for UART to accept char half-way through final stop bit. */
353c0b746e5SOllivier Robert #define INITIALOFFSET (u_int32)(-BITTIME/2)
354c0b746e5SOllivier Robert 
355c0b746e5SOllivier Robert      /*
356c0b746e5SOllivier Robert     charoffsets[x] is the time after the start of the second that byte
357c0b746e5SOllivier Robert     x (with the first byte being byte 1) is received by the UART,
358c0b746e5SOllivier Robert     assuming that the initial edge of the start bit of the first byte
359c0b746e5SOllivier Robert     is on-time.  The values are represented as the fractional part of
360c0b746e5SOllivier Robert     an l_fp.
361c0b746e5SOllivier Robert 
362c0b746e5SOllivier Robert     We store enough values to have the offset of each byte including
363c0b746e5SOllivier Robert     the trailing \r, on the assumption that the bytes follow one
364c0b746e5SOllivier Robert     another without gaps.
365c0b746e5SOllivier Robert     */
366c0b746e5SOllivier Robert      static const u_int32 charoffsets[LENARC+1] = {
367c0b746e5SOllivier Robert #if BITSPERCHAR == 11 /* Usual case. */
368c0b746e5SOllivier Robert 	     /* Offsets computed as accurately as possible... */
369c0b746e5SOllivier Robert 	     0,
370c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
371c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
372c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
373c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
374c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
375c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
376c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
377c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
378c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
379c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
380c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
381c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
382c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
383c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
384c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
385c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
386c0b746e5SOllivier Robert #else
387c0b746e5SOllivier Robert 	     /* Offsets computed with a small rounding error... */
388c0b746e5SOllivier Robert 	     0,
389c0b746e5SOllivier Robert 	     INITIALOFFSET +  1 * CHARTIME,
390c0b746e5SOllivier Robert 	     INITIALOFFSET +  2 * CHARTIME,
391c0b746e5SOllivier Robert 	     INITIALOFFSET +  3 * CHARTIME,
392c0b746e5SOllivier Robert 	     INITIALOFFSET +  4 * CHARTIME,
393c0b746e5SOllivier Robert 	     INITIALOFFSET +  5 * CHARTIME,
394c0b746e5SOllivier Robert 	     INITIALOFFSET +  6 * CHARTIME,
395c0b746e5SOllivier Robert 	     INITIALOFFSET +  7 * CHARTIME,
396c0b746e5SOllivier Robert 	     INITIALOFFSET +  8 * CHARTIME,
397c0b746e5SOllivier Robert 	     INITIALOFFSET +  9 * CHARTIME,
398c0b746e5SOllivier Robert 	     INITIALOFFSET + 10 * CHARTIME,
399c0b746e5SOllivier Robert 	     INITIALOFFSET + 11 * CHARTIME,
400c0b746e5SOllivier Robert 	     INITIALOFFSET + 12 * CHARTIME,
401c0b746e5SOllivier Robert 	     INITIALOFFSET + 13 * CHARTIME,
402c0b746e5SOllivier Robert 	     INITIALOFFSET + 14 * CHARTIME,
403c0b746e5SOllivier Robert 	     INITIALOFFSET + 15 * CHARTIME,
404c0b746e5SOllivier Robert 	     INITIALOFFSET + 16 * CHARTIME
405c0b746e5SOllivier Robert #endif
406c0b746e5SOllivier Robert      };
407c0b746e5SOllivier Robert 
408c0b746e5SOllivier Robert /* Chose filter length dependent on fudge flag 4. */
409c0b746e5SOllivier Robert #define CHOSENSAMPLES(pp) \
410c0b746e5SOllivier Robert (((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES)
411c0b746e5SOllivier Robert      /*
412c0b746e5SOllivier Robert Chose how many filter samples to keep.  Several factors are in play.
413c0b746e5SOllivier Robert 
414c0b746e5SOllivier Robert  1) Discard at least one sample to allow a spike value to be
415c0b746e5SOllivier Robert     discarded.
416c0b746e5SOllivier Robert 
417c0b746e5SOllivier Robert  2) Discard about 1-in-8 to 1-in-30 samples to handle spikes.
418c0b746e5SOllivier Robert 
419c0b746e5SOllivier Robert  3) Keep an odd number of samples to avoid median value being biased
420c0b746e5SOllivier Robert     high or low.
421c0b746e5SOllivier Robert */
422c0b746e5SOllivier Robert #define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1)
423c0b746e5SOllivier Robert 
424c0b746e5SOllivier Robert #define DEFAULT_RESYNC_TIME  (57*60)    /* Gap between resync attempts (s). */
425c0b746e5SOllivier Robert #define RETRY_RESYNC_TIME    (27*60)    /* Gap to emergency resync attempt. */
426c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
427c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 500        /* Delay before first resync. */
428c0b746e5SOllivier Robert #else
429c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 50         /* Delay before first resync. */
430c0b746e5SOllivier Robert #endif
431c0b746e5SOllivier Robert 
432c0b746e5SOllivier Robert      static const int moff[12] =
433c0b746e5SOllivier Robert { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
434c0b746e5SOllivier Robert /* Flags for a raw open() of the clock serial device. */
435c0b746e5SOllivier Robert #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
436c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR | O_NOCTTY)
437c0b746e5SOllivier Robert #else           /* Oh well, it may not matter... */
438c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR)
439c0b746e5SOllivier Robert #endif
440c0b746e5SOllivier Robert 
441c0b746e5SOllivier Robert 
442c0b746e5SOllivier Robert /* Length of queue of command bytes to be sent. */
443c0b746e5SOllivier Robert #define CMDQUEUELEN 4                   /* Enough for two cmds + each \r. */
444c0b746e5SOllivier Robert /* Queue tick time; interval in seconds between chars taken off queue. */
445c0b746e5SOllivier Robert /* Must be >= 2 to allow o\r response to come back uninterrupted. */
446c0b746e5SOllivier Robert #define QUEUETICK   2                   /* Allow o\r reply to finish. */
447c0b746e5SOllivier Robert 
448c0b746e5SOllivier Robert /*
449c0b746e5SOllivier Robert  * ARC unit control structure
450c0b746e5SOllivier Robert  */
451c0b746e5SOllivier Robert struct arcunit {
452c0b746e5SOllivier Robert 	l_fp lastrec;       /* Time tag for the receive time (system). */
453c0b746e5SOllivier Robert 	int status;         /* Clock status. */
454c0b746e5SOllivier Robert 
455c0b746e5SOllivier Robert 	int quality;        /* Quality of reception 0--5 for unit. */
456c0b746e5SOllivier Robert 	/* We may also use the values -1 or 6 internally. */
457c0b746e5SOllivier Robert 
458c0b746e5SOllivier Robert 	u_long next_resync; /* Next resync time (s) compared to current_time. */
459c0b746e5SOllivier Robert 	int resyncing;      /* Resync in progress if true. */
460c0b746e5SOllivier Robert 
461c0b746e5SOllivier Robert 	/* In the outgoing queue, cmdqueue[0] is next to be sent. */
462c0b746e5SOllivier Robert 	char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
463c0b746e5SOllivier Robert 
464c0b746e5SOllivier Robert 	u_long saved_flags; /* Saved fudge flags. */
465c0b746e5SOllivier Robert };
466c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
467c0b746e5SOllivier Robert /* The flag `possible_leap' is set non-zero when any MSF unit
468c0b746e5SOllivier Robert        thinks a leap-second may have happened.
469c0b746e5SOllivier Robert 
470c0b746e5SOllivier Robert        Set whenever we receive a valid time sample in the first hour of
471c0b746e5SOllivier Robert        the first day of the first/seventh months.
472c0b746e5SOllivier Robert 
473c0b746e5SOllivier Robert        Outside the special hour this value is unconditionally set
474c0b746e5SOllivier Robert        to zero by the receive routine.
475c0b746e5SOllivier Robert 
476c0b746e5SOllivier Robert        On finding itself in this timeslot, as long as the value is
477c0b746e5SOllivier Robert        non-negative, the receive routine sets it to a positive value to
478c0b746e5SOllivier Robert        indicate a resync to MSF should be performed.
479c0b746e5SOllivier Robert 
480c0b746e5SOllivier Robert        In the poll routine, if this value is positive and we are not
481c0b746e5SOllivier Robert        already resyncing (eg from a sync that started just before
482c0b746e5SOllivier Robert        midnight), start resyncing and set this value negative to
483c0b746e5SOllivier Robert        indicate that a leap-triggered resync has been started.  Having
484c0b746e5SOllivier Robert        set this negative prevents the receive routine setting it
485c0b746e5SOllivier Robert        positive and thus prevents multiple resyncs during the witching
486c0b746e5SOllivier Robert        hour.
487c0b746e5SOllivier Robert      */
488c0b746e5SOllivier Robert static int possible_leap = 0;       /* No resync required by default. */
489c0b746e5SOllivier Robert #endif
490c0b746e5SOllivier Robert 
491c0b746e5SOllivier Robert #if 0
492c0b746e5SOllivier Robert static void dummy_event_handler P((struct peer *));
493c0b746e5SOllivier Robert static void   arc_event_handler P((struct peer *));
494c0b746e5SOllivier Robert #endif /* 0 */
495c0b746e5SOllivier Robert 
496c0b746e5SOllivier Robert #define QUALITY_UNKNOWN     -1 /* Indicates unknown clock quality. */
497c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
498c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
499c0b746e5SOllivier Robert #define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
500c0b746e5SOllivier Robert 
501c0b746e5SOllivier Robert /*
502c0b746e5SOllivier Robert  * Function prototypes
503c0b746e5SOllivier Robert  */
504c0b746e5SOllivier Robert static  int     arc_start       P((int, struct peer *));
505c0b746e5SOllivier Robert static  void    arc_shutdown    P((int, struct peer *));
506c0b746e5SOllivier Robert static  void    arc_receive     P((struct recvbuf *));
507c0b746e5SOllivier Robert static  void    arc_poll        P((int, struct peer *));
508c0b746e5SOllivier Robert 
509c0b746e5SOllivier Robert /*
510c0b746e5SOllivier Robert  * Transfer vector
511c0b746e5SOllivier Robert  */
512c0b746e5SOllivier Robert struct  refclock refclock_arc = {
513c0b746e5SOllivier Robert 	arc_start,              /* start up driver */
514c0b746e5SOllivier Robert 	arc_shutdown,           /* shut down driver */
515c0b746e5SOllivier Robert 	arc_poll,               /* transmit poll message */
516c0b746e5SOllivier Robert 	noentry,                /* not used (old arc_control) */
517c0b746e5SOllivier Robert 	noentry,                /* initialize driver (not used) */
518c0b746e5SOllivier Robert 	noentry,                /* not used (old arc_buginfo) */
519c0b746e5SOllivier Robert 	NOFLAGS                 /* not used */
520c0b746e5SOllivier Robert };
521c0b746e5SOllivier Robert 
522c0b746e5SOllivier Robert /* Queue us up for the next tick. */
523c0b746e5SOllivier Robert #define ENQUEUE(up) \
524c0b746e5SOllivier Robert 	do { \
525c0b746e5SOllivier Robert 	     if((up)->ev.next != 0) { break; } /* WHOOPS! */ \
526c0b746e5SOllivier Robert 	     peer->nextdate = current_time + QUEUETICK; \
527c0b746e5SOllivier Robert 	} while(0)
528c0b746e5SOllivier Robert 
529c0b746e5SOllivier Robert #if 0
530c0b746e5SOllivier Robert /* Placeholder event handler---does nothing safely---soaks up lose tick. */
531c0b746e5SOllivier Robert static void
532c0b746e5SOllivier Robert dummy_event_handler(
533c0b746e5SOllivier Robert 	struct peer *peer
534c0b746e5SOllivier Robert 	)
535c0b746e5SOllivier Robert {
536c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
537c0b746e5SOllivier Robert 	if(debug) { printf("arc: dummy_event_handler() called.\n"); }
538c0b746e5SOllivier Robert #endif
539c0b746e5SOllivier Robert }
540c0b746e5SOllivier Robert 
541c0b746e5SOllivier Robert /*
542c0b746e5SOllivier Robert Normal event handler.
543c0b746e5SOllivier Robert 
544c0b746e5SOllivier Robert Take first character off queue and send to clock if not a null.
545c0b746e5SOllivier Robert 
546c0b746e5SOllivier Robert Shift characters down and put a null on the end.
547c0b746e5SOllivier Robert 
548c0b746e5SOllivier Robert We assume that there is no parallelism so no race condition, but even
549c0b746e5SOllivier Robert if there is nothing bad will happen except that we might send some bad
550c0b746e5SOllivier Robert data to the clock once in a while.
551c0b746e5SOllivier Robert */
552c0b746e5SOllivier Robert static void
553c0b746e5SOllivier Robert arc_event_handler(
554c0b746e5SOllivier Robert 	struct peer *peer
555c0b746e5SOllivier Robert 	)
556c0b746e5SOllivier Robert {
557c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
558c0b746e5SOllivier Robert 	register struct arcunit *up = (struct arcunit *)pp->unitptr;
559c0b746e5SOllivier Robert 	int i;
560c0b746e5SOllivier Robert 	char c;
561c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
562c0b746e5SOllivier Robert 	if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
563c0b746e5SOllivier Robert #endif
564c0b746e5SOllivier Robert 
565c0b746e5SOllivier Robert 	c = up->cmdqueue[0];       /* Next char to be sent. */
566c0b746e5SOllivier Robert 	/* Shift down characters, shifting trailing \0 in at end. */
567c0b746e5SOllivier Robert 	for(i = 0; i < CMDQUEUELEN; ++i)
568c0b746e5SOllivier Robert 	{ up->cmdqueue[i] = up->cmdqueue[i+1]; }
569c0b746e5SOllivier Robert 
570c0b746e5SOllivier Robert 	/* Don't send '\0' characters. */
571c0b746e5SOllivier Robert 	if(c != '\0') {
572c0b746e5SOllivier Robert 		if(write(pp->io.fd, &c, 1) != 1) {
573c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
574c0b746e5SOllivier Robert 		}
575c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
576c0b746e5SOllivier Robert 		else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
577c0b746e5SOllivier Robert #endif
578c0b746e5SOllivier Robert 	}
579c0b746e5SOllivier Robert }
580c0b746e5SOllivier Robert #endif /* 0 */
581c0b746e5SOllivier Robert 
582c0b746e5SOllivier Robert /*
583c0b746e5SOllivier Robert  * arc_start - open the devices and initialize data for processing
584c0b746e5SOllivier Robert  */
585c0b746e5SOllivier Robert static int
586c0b746e5SOllivier Robert arc_start(
587c0b746e5SOllivier Robert 	int unit,
588c0b746e5SOllivier Robert 	struct peer *peer
589c0b746e5SOllivier Robert 	)
590c0b746e5SOllivier Robert {
591c0b746e5SOllivier Robert 	register struct arcunit *up;
592c0b746e5SOllivier Robert 	struct refclockproc *pp;
593c0b746e5SOllivier Robert 	int fd;
594c0b746e5SOllivier Robert 	char device[20];
595c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
596c0b746e5SOllivier Robert 	struct termios arg;
597c0b746e5SOllivier Robert #endif
598c0b746e5SOllivier Robert 
599c0b746e5SOllivier Robert 	msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
600c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
601c0b746e5SOllivier Robert 	if(debug) {
602c0b746e5SOllivier Robert 		printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
603c0b746e5SOllivier Robert 	}
604c0b746e5SOllivier Robert #endif
605c0b746e5SOllivier Robert 
606c0b746e5SOllivier Robert 	/* Prevent a ridiculous device number causing overflow of device[]. */
607c0b746e5SOllivier Robert 	if((unit < 0) || (unit > 255)) { return(0); }
608c0b746e5SOllivier Robert 
609c0b746e5SOllivier Robert 	/*
610c0b746e5SOllivier Robert 	 * Open serial port. Use CLK line discipline, if available.
611c0b746e5SOllivier Robert 	 */
612c0b746e5SOllivier Robert 	(void)sprintf(device, DEVICE, unit);
613c0b746e5SOllivier Robert 	if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
614c0b746e5SOllivier Robert 		return(0);
615c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
616c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d using open().\n", unit); }
617c0b746e5SOllivier Robert #endif
618c0b746e5SOllivier Robert 	fd = open(device, OPEN_FLAGS);
619c0b746e5SOllivier Robert 	if(fd < 0) {
620c0b746e5SOllivier Robert #ifdef DEBUG
621c0b746e5SOllivier Robert 		if(debug) { printf("arc: failed [open()] to open %s.\n", device); }
622c0b746e5SOllivier Robert #endif
623c0b746e5SOllivier Robert 		return(0);
624c0b746e5SOllivier Robert 	}
625c0b746e5SOllivier Robert 
626c0b746e5SOllivier Robert 	fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
627c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
628c0b746e5SOllivier Robert 	if(debug)
629c0b746e5SOllivier Robert 	{ printf("Opened RS232 port with file descriptor %d.\n", fd); }
630c0b746e5SOllivier Robert #endif
631c0b746e5SOllivier Robert 
632c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
633c0b746e5SOllivier Robert 
634c0b746e5SOllivier Robert 	arg.c_iflag = IGNBRK | ISTRIP;
635c0b746e5SOllivier Robert 	arg.c_oflag = 0;
636c0b746e5SOllivier Robert 	arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
637c0b746e5SOllivier Robert 	arg.c_lflag = 0;
638c0b746e5SOllivier Robert 	arg.c_cc[VMIN] = 1;
639c0b746e5SOllivier Robert 	arg.c_cc[VTIME] = 0;
640c0b746e5SOllivier Robert 
641c0b746e5SOllivier Robert 	tcsetattr(fd, TCSANOW, &arg);
642c0b746e5SOllivier Robert 
643c0b746e5SOllivier Robert #else
644c0b746e5SOllivier Robert 
645c0b746e5SOllivier Robert 	msyslog(LOG_ERR, "ARCRON: termios not supported in this driver");
646c0b746e5SOllivier Robert 	(void)close(fd);
647c0b746e5SOllivier Robert 
648c0b746e5SOllivier Robert 	return 0;
649c0b746e5SOllivier Robert 
650c0b746e5SOllivier Robert #endif
651c0b746e5SOllivier Robert 
652c0b746e5SOllivier Robert 	up = (struct arcunit *) emalloc(sizeof(struct arcunit));
653c0b746e5SOllivier Robert 	if(!up) { (void) close(fd); return(0); }
654c0b746e5SOllivier Robert 	/* Set structure to all zeros... */
655c0b746e5SOllivier Robert 	memset((char *)up, 0, sizeof(struct arcunit));
656c0b746e5SOllivier Robert 	pp = peer->procptr;
657c0b746e5SOllivier Robert 	pp->io.clock_recv = arc_receive;
658c0b746e5SOllivier Robert 	pp->io.srcclock = (caddr_t)peer;
659c0b746e5SOllivier Robert 	pp->io.datalen = 0;
660c0b746e5SOllivier Robert 	pp->io.fd = fd;
661c0b746e5SOllivier Robert 	if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); }
662c0b746e5SOllivier Robert 	pp->unitptr = (caddr_t)up;
663c0b746e5SOllivier Robert 
664c0b746e5SOllivier Robert 	/*
665c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
666c0b746e5SOllivier Robert 	 */
667c0b746e5SOllivier Robert 	peer->precision = PRECISION;
668c0b746e5SOllivier Robert 	peer->stratum = 2;              /* Default to stratum 2 not 0. */
669c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
670c0b746e5SOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
671c0b746e5SOllivier Robert 	/* Spread out resyncs so that they should remain separated. */
672c0b746e5SOllivier Robert 	up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
673c0b746e5SOllivier Robert 
674c0b746e5SOllivier Robert #if 0 /* Not needed because of zeroing of arcunit structure... */
675c0b746e5SOllivier Robert 	up->resyncing = 0;              /* Not resyncing yet. */
676c0b746e5SOllivier Robert 	up->saved_flags = 0;            /* Default is all flags off. */
677c0b746e5SOllivier Robert 	/* Clear send buffer out... */
678c0b746e5SOllivier Robert 	{
679c0b746e5SOllivier Robert 		int i;
680c0b746e5SOllivier Robert 		for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
681c0b746e5SOllivier Robert 	}
682c0b746e5SOllivier Robert #endif
683c0b746e5SOllivier Robert 
684c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
685c0b746e5SOllivier Robert 	up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
686c0b746e5SOllivier Robert #else
687c0b746e5SOllivier Robert 	up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
688c0b746e5SOllivier Robert #endif
689c0b746e5SOllivier Robert 	return(1);
690c0b746e5SOllivier Robert }
691c0b746e5SOllivier Robert 
692c0b746e5SOllivier Robert 
693c0b746e5SOllivier Robert /*
694c0b746e5SOllivier Robert  * arc_shutdown - shut down the clock
695c0b746e5SOllivier Robert  */
696c0b746e5SOllivier Robert static void
697c0b746e5SOllivier Robert arc_shutdown(
698c0b746e5SOllivier Robert 	int unit,
699c0b746e5SOllivier Robert 	struct peer *peer
700c0b746e5SOllivier Robert 	)
701c0b746e5SOllivier Robert {
702c0b746e5SOllivier Robert 	register struct arcunit *up;
703c0b746e5SOllivier Robert 	struct refclockproc *pp;
704c0b746e5SOllivier Robert 
705c0b746e5SOllivier Robert 	pp = peer->procptr;
706c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
707c0b746e5SOllivier Robert 	io_closeclock(&pp->io);
708c0b746e5SOllivier Robert 	free(up);
709c0b746e5SOllivier Robert }
710c0b746e5SOllivier Robert 
711c0b746e5SOllivier Robert /*
712c0b746e5SOllivier Robert Compute space left in output buffer.
713c0b746e5SOllivier Robert */
714c0b746e5SOllivier Robert static int
715c0b746e5SOllivier Robert space_left(
716c0b746e5SOllivier Robert 	register struct arcunit *up
717c0b746e5SOllivier Robert 	)
718c0b746e5SOllivier Robert {
719c0b746e5SOllivier Robert 	int spaceleft;
720c0b746e5SOllivier Robert 
721c0b746e5SOllivier Robert 	/* Compute space left in buffer after any pending output. */
722c0b746e5SOllivier Robert 	for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
723c0b746e5SOllivier Robert 	{ if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
724c0b746e5SOllivier Robert 	return(spaceleft);
725c0b746e5SOllivier Robert }
726c0b746e5SOllivier Robert 
727c0b746e5SOllivier Robert /*
728c0b746e5SOllivier Robert Send command by copying into command buffer as far forward as possible,
729c0b746e5SOllivier Robert after any pending output.
730c0b746e5SOllivier Robert 
731c0b746e5SOllivier Robert Indicate an error by returning 0 if there is not space for the command.
732c0b746e5SOllivier Robert */
733c0b746e5SOllivier Robert static int
734c0b746e5SOllivier Robert send_slow(
735c0b746e5SOllivier Robert 	register struct arcunit *up,
736c0b746e5SOllivier Robert 	int fd,
737c0b746e5SOllivier Robert 	const char *s
738c0b746e5SOllivier Robert 	)
739c0b746e5SOllivier Robert {
740c0b746e5SOllivier Robert 	int sl = strlen(s);
741c0b746e5SOllivier Robert 	int spaceleft = space_left(up);
742c0b746e5SOllivier Robert 
743c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
744c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
745c0b746e5SOllivier Robert #endif
746c0b746e5SOllivier Robert 	if(spaceleft < sl) { /* Should not normally happen... */
747c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
748c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
749c0b746e5SOllivier Robert 		       sl, spaceleft);
750c0b746e5SOllivier Robert #endif
751c0b746e5SOllivier Robert 		return(0);                      /* FAILED! */
752c0b746e5SOllivier Robert 	}
753c0b746e5SOllivier Robert 
754c0b746e5SOllivier Robert 	/* Copy in the command to be sent. */
755c0b746e5SOllivier Robert 	while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
756c0b746e5SOllivier Robert 
757c0b746e5SOllivier Robert 	return(1);
758c0b746e5SOllivier Robert }
759c0b746e5SOllivier Robert 
760c0b746e5SOllivier Robert 
761c0b746e5SOllivier Robert /* Macro indicating action we will take for different quality values. */
762c0b746e5SOllivier Robert #define quality_action(q) \
763c0b746e5SOllivier Robert (((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
764c0b746e5SOllivier Robert  (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
765c0b746e5SOllivier Robert   "OK, will use clock"))
766c0b746e5SOllivier Robert 
767c0b746e5SOllivier Robert      /*
768c0b746e5SOllivier Robert  * arc_receive - receive data from the serial interface
769c0b746e5SOllivier Robert  */
770c0b746e5SOllivier Robert      static void
771c0b746e5SOllivier Robert arc_receive(
772c0b746e5SOllivier Robert 	struct recvbuf *rbufp
773c0b746e5SOllivier Robert 	)
774c0b746e5SOllivier Robert {
775c0b746e5SOllivier Robert 	register struct arcunit *up;
776c0b746e5SOllivier Robert 	struct refclockproc *pp;
777c0b746e5SOllivier Robert 	struct peer *peer;
778c0b746e5SOllivier Robert 	char c;
779c0b746e5SOllivier Robert 	int i, n, wday, month, bst, status;
780c0b746e5SOllivier Robert 	int arc_last_offset;
781c0b746e5SOllivier Robert 
782c0b746e5SOllivier Robert 	/*
783c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp
784c0b746e5SOllivier Robert 	 */
785c0b746e5SOllivier Robert 	peer = (struct peer *)rbufp->recv_srcclock;
786c0b746e5SOllivier Robert 	pp = peer->procptr;
787c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
788c0b746e5SOllivier Robert 
789c0b746e5SOllivier Robert 
790c0b746e5SOllivier Robert 	/*
791c0b746e5SOllivier Robert 	  If the command buffer is empty, and we are resyncing, insert a
792c0b746e5SOllivier Robert 	  g\r quality request into it to poll for signal quality again.
793c0b746e5SOllivier Robert 	*/
794c0b746e5SOllivier Robert 	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
795c0b746e5SOllivier Robert #ifdef DEBUG
796c0b746e5SOllivier Robert 		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
797c0b746e5SOllivier Robert #endif
798c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "g\r");
799c0b746e5SOllivier Robert 	}
800c0b746e5SOllivier Robert 
801c0b746e5SOllivier Robert 	/*
802c0b746e5SOllivier Robert 	  The `arc_last_offset' is the offset in lastcode[] of the last byte
803c0b746e5SOllivier Robert 	  received, and which we assume actually received the input
804c0b746e5SOllivier Robert 	  timestamp.
805c0b746e5SOllivier Robert 
806c0b746e5SOllivier Robert 	  (When we get round to using tty_clk and it is available, we
807c0b746e5SOllivier Robert 	  assume that we will receive the whole timecode with the
808c0b746e5SOllivier Robert 	  trailing \r, and that that \r will be timestamped.  But this
809c0b746e5SOllivier Robert 	  assumption also works if receive the characters one-by-one.)
810c0b746e5SOllivier Robert 	*/
811c0b746e5SOllivier Robert 	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
812c0b746e5SOllivier Robert 
813c0b746e5SOllivier Robert 	/*
814c0b746e5SOllivier Robert 	  We catch a timestamp iff:
815c0b746e5SOllivier Robert 
816c0b746e5SOllivier Robert 	  * The command code is `o' for a timestamp.
817c0b746e5SOllivier Robert 
818c0b746e5SOllivier Robert 	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
819c0b746e5SOllivier Robert 	  exactly char in the buffer (the command code) so that we
820c0b746e5SOllivier Robert 	  only sample the first character of the timecode as our
821c0b746e5SOllivier Robert 	  `on-time' character.
822c0b746e5SOllivier Robert 
823c0b746e5SOllivier Robert 	  * The first character in the buffer is not the echoed `\r'
824c0b746e5SOllivier Robert 	  from the `o` command (so if we are to timestamp an `\r' it
825c0b746e5SOllivier Robert 	  must not be first in the receive buffer with lencode==1.
826c0b746e5SOllivier Robert 	  (Even if we had other characters following it, we probably
827c0b746e5SOllivier Robert 	  would have a premature timestamp on the '\r'.)
828c0b746e5SOllivier Robert 
829c0b746e5SOllivier Robert 	  * We have received at least one character (I cannot imagine
830c0b746e5SOllivier Robert 	  how it could be otherwise, but anyway...).
831c0b746e5SOllivier Robert 	*/
832c0b746e5SOllivier Robert 	c = rbufp->recv_buffer[0];
833c0b746e5SOllivier Robert 	if((pp->a_lastcode[0] == 'o') &&
834c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES
835c0b746e5SOllivier Robert 	   (pp->lencode == 1) &&
836c0b746e5SOllivier Robert #endif
837c0b746e5SOllivier Robert 	   ((pp->lencode != 1) || (c != '\r')) &&
838c0b746e5SOllivier Robert 	   (arc_last_offset >= 1)) {
839c0b746e5SOllivier Robert 		/* Note that the timestamp should be corrected if >1 char rcvd. */
840c0b746e5SOllivier Robert 		l_fp timestamp;
841c0b746e5SOllivier Robert 		timestamp = rbufp->recv_time;
842c0b746e5SOllivier Robert #ifdef DEBUG
843c0b746e5SOllivier Robert 		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
844c0b746e5SOllivier Robert 			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
845c0b746e5SOllivier Robert 			       ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
846c0b746e5SOllivier Robert 			       rbufp->recv_length);
847c0b746e5SOllivier Robert 		}
848c0b746e5SOllivier Robert #endif
849c0b746e5SOllivier Robert 
850c0b746e5SOllivier Robert 		/*
851c0b746e5SOllivier Robert 		  Now correct timestamp by offset of last byte received---we
852c0b746e5SOllivier Robert 		  subtract from the receive time the delay implied by the
853c0b746e5SOllivier Robert 		  extra characters received.
854c0b746e5SOllivier Robert 
855c0b746e5SOllivier Robert 		  Reject the input if the resulting code is too long, but
856c0b746e5SOllivier Robert 		  allow for the trailing \r, normally not used but a good
857c0b746e5SOllivier Robert 		  handle for tty_clk or somesuch kernel timestamper.
858c0b746e5SOllivier Robert 		*/
859c0b746e5SOllivier Robert 		if(arc_last_offset > LENARC) {
860c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
861c0b746e5SOllivier Robert 			if(debug) {
862c0b746e5SOllivier Robert 				printf("arc: input code too long (%d cf %d); rejected.\n",
863c0b746e5SOllivier Robert 				       arc_last_offset, LENARC);
864c0b746e5SOllivier Robert 			}
865c0b746e5SOllivier Robert #endif
866c0b746e5SOllivier Robert 			pp->lencode = 0;
867c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
868c0b746e5SOllivier Robert 			return;
869c0b746e5SOllivier Robert 		}
870c0b746e5SOllivier Robert 
871c0b746e5SOllivier Robert 		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
872c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
873c0b746e5SOllivier Robert 		if(debug > 1) {
874c0b746e5SOllivier Robert 			printf(
875c0b746e5SOllivier Robert 				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
876c0b746e5SOllivier Robert 				((rbufp->recv_length > 1) ? "*** " : ""),
877c0b746e5SOllivier Robert 				rbufp->recv_length,
878c0b746e5SOllivier Robert 				arc_last_offset,
879c0b746e5SOllivier Robert 				mfptoms((unsigned long)0,
880c0b746e5SOllivier Robert 					charoffsets[arc_last_offset],
881c0b746e5SOllivier Robert 					1));
882c0b746e5SOllivier Robert 		}
883c0b746e5SOllivier Robert #endif
884c0b746e5SOllivier Robert 
885c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
886c0b746e5SOllivier Robert 		/*
887c0b746e5SOllivier Robert 		  If taking multiple samples, capture the current adjusted
888c0b746e5SOllivier Robert 		  sample iff:
889c0b746e5SOllivier Robert 
890c0b746e5SOllivier Robert 		  * No timestamp has yet been captured (it is zero), OR
891c0b746e5SOllivier Robert 
892c0b746e5SOllivier Robert 		  * This adjusted timestamp is earlier than the one already
893c0b746e5SOllivier Robert 		  captured, on the grounds that this one suffered less
894c0b746e5SOllivier Robert 		  delay in being delivered to us and is more accurate.
895c0b746e5SOllivier Robert 
896c0b746e5SOllivier Robert 		*/
897c0b746e5SOllivier Robert 		if(L_ISZERO(&(up->lastrec)) ||
898c0b746e5SOllivier Robert 		   L_ISGEQ(&(up->lastrec), &timestamp))
899c0b746e5SOllivier Robert #endif
900c0b746e5SOllivier Robert 		{
901c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
902c0b746e5SOllivier Robert 			if(debug > 1) {
903c0b746e5SOllivier Robert 				printf("arc: system timestamp captured.\n");
904c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
905c0b746e5SOllivier Robert 				if(!L_ISZERO(&(up->lastrec))) {
906c0b746e5SOllivier Robert 					l_fp diff;
907c0b746e5SOllivier Robert 					diff = up->lastrec;
908c0b746e5SOllivier Robert 					L_SUB(&diff, &timestamp);
909c0b746e5SOllivier Robert 					printf("arc: adjusted timestamp by -%sms.\n",
910c0b746e5SOllivier Robert 					       mfptoms(diff.l_i, diff.l_f, 3));
911c0b746e5SOllivier Robert 				}
912c0b746e5SOllivier Robert #endif
913c0b746e5SOllivier Robert 			}
914c0b746e5SOllivier Robert #endif
915c0b746e5SOllivier Robert 			up->lastrec = timestamp;
916c0b746e5SOllivier Robert 		}
917c0b746e5SOllivier Robert 
918c0b746e5SOllivier Robert 	}
919c0b746e5SOllivier Robert 
920c0b746e5SOllivier Robert 	/* Just in case we still have lots of rubbish in the buffer... */
921c0b746e5SOllivier Robert 	/* ...and to avoid the same timestamp being reused by mistake, */
922c0b746e5SOllivier Robert 	/* eg on receipt of the \r coming in on its own after the      */
923c0b746e5SOllivier Robert 	/* timecode.                                                   */
924c0b746e5SOllivier Robert 	if(pp->lencode >= LENARC) {
925c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
926c0b746e5SOllivier Robert 		if(debug && (rbufp->recv_buffer[0] != '\r'))
927c0b746e5SOllivier Robert 		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
928c0b746e5SOllivier Robert #endif
929c0b746e5SOllivier Robert 		pp->lencode = 0;
930c0b746e5SOllivier Robert 		return;
931c0b746e5SOllivier Robert 	}
932c0b746e5SOllivier Robert 
933c0b746e5SOllivier Robert 	/* Append input to code buffer, avoiding overflow. */
934c0b746e5SOllivier Robert 	for(i = 0; i < rbufp->recv_length; i++) {
935c0b746e5SOllivier Robert 		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
936c0b746e5SOllivier Robert 		c = rbufp->recv_buffer[i];
937c0b746e5SOllivier Robert 
938c0b746e5SOllivier Robert 		/* Drop trailing '\r's and drop `h' command echo totally. */
939c0b746e5SOllivier Robert 		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
940c0b746e5SOllivier Robert 
941c0b746e5SOllivier Robert 		/*
942c0b746e5SOllivier Robert 		  If we've just put an `o' in the lastcode[0], clear the
943c0b746e5SOllivier Robert 		  timestamp in anticipation of a timecode arriving soon.
944c0b746e5SOllivier Robert 
945c0b746e5SOllivier Robert 		  We would expect to get to process this before any of the
946c0b746e5SOllivier Robert 		  timecode arrives.
947c0b746e5SOllivier Robert 		*/
948c0b746e5SOllivier Robert 		if((c == 'o') && (pp->lencode == 1)) {
949c0b746e5SOllivier Robert 			L_CLR(&(up->lastrec));
950c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
951c0b746e5SOllivier Robert 			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
952c0b746e5SOllivier Robert #endif
953c0b746e5SOllivier Robert 		}
954c0b746e5SOllivier Robert 	}
955c0b746e5SOllivier Robert 
956c0b746e5SOllivier Robert 	/* Handle a quality message. */
957c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] == 'g') {
958c0b746e5SOllivier Robert 		int r, q;
959c0b746e5SOllivier Robert 
960c0b746e5SOllivier Robert 		if(pp->lencode < 3) { return; } /* Need more data... */
961c0b746e5SOllivier Robert 		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
962c0b746e5SOllivier Robert 		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
963c0b746e5SOllivier Robert 		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
964c0b746e5SOllivier Robert 		   ((r & 0x70) != 0x30)) {
965c0b746e5SOllivier Robert 			/* Badly formatted response. */
966c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
967c0b746e5SOllivier Robert 			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
968c0b746e5SOllivier Robert #endif
969c0b746e5SOllivier Robert 			return;
970c0b746e5SOllivier Robert 		}
971c0b746e5SOllivier Robert 		if(r == '3') { /* Only use quality value whilst sync in progress. */
972c0b746e5SOllivier Robert 			up->quality = (q & 0xf);
973c0b746e5SOllivier Robert #ifdef DEBUG
974c0b746e5SOllivier Robert 			if(debug) { printf("arc: signal quality %d.\n", up->quality); }
975c0b746e5SOllivier Robert #endif
976c0b746e5SOllivier Robert 		} else if( /* (r == '2') && */ up->resyncing) {
977c0b746e5SOllivier Robert #ifdef DEBUG
978c0b746e5SOllivier Robert 			if(debug)
979c0b746e5SOllivier Robert 			{
980c0b746e5SOllivier Robert 				printf("arc: sync finished, signal quality %d: %s\n",
981c0b746e5SOllivier Robert 				       up->quality,
982c0b746e5SOllivier Robert 				       quality_action(up->quality));
983c0b746e5SOllivier Robert 			}
984c0b746e5SOllivier Robert #endif
985c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE,
986c0b746e5SOllivier Robert 			       "ARCRON: sync finished, signal quality %d: %s",
987c0b746e5SOllivier Robert 			       up->quality,
988c0b746e5SOllivier Robert 			       quality_action(up->quality));
989c0b746e5SOllivier Robert 			up->resyncing = 0; /* Resync is over. */
990c0b746e5SOllivier Robert 
991c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
992c0b746e5SOllivier Robert 			/* Clock quality dubious; resync earlier than usual. */
993c0b746e5SOllivier Robert 			if((up->quality == QUALITY_UNKNOWN) ||
994c0b746e5SOllivier Robert 			   (up->quality < MIN_CLOCK_QUALITY_OK))
995c0b746e5SOllivier Robert 			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
996c0b746e5SOllivier Robert #endif
997c0b746e5SOllivier Robert 		}
998c0b746e5SOllivier Robert 		pp->lencode = 0;
999c0b746e5SOllivier Robert 		return;
1000c0b746e5SOllivier Robert 	}
1001c0b746e5SOllivier Robert 
1002c0b746e5SOllivier Robert 	/* Stop now if this is not a timecode message. */
1003c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] != 'o') {
1004c0b746e5SOllivier Robert 		pp->lencode = 0;
1005c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1006c0b746e5SOllivier Robert 		return;
1007c0b746e5SOllivier Robert 	}
1008c0b746e5SOllivier Robert 
1009c0b746e5SOllivier Robert 	/* If we don't have enough data, wait for more... */
1010c0b746e5SOllivier Robert 	if(pp->lencode < LENARC) { return; }
1011c0b746e5SOllivier Robert 
1012c0b746e5SOllivier Robert 
1013c0b746e5SOllivier Robert 	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
1014c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1015c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1016c0b746e5SOllivier Robert #endif
1017c0b746e5SOllivier Robert 
1018c0b746e5SOllivier Robert 	/* But check that we actually captured a system timestamp on it. */
1019c0b746e5SOllivier Robert 	if(L_ISZERO(&(up->lastrec))) {
1020c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1021c0b746e5SOllivier Robert 		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1022c0b746e5SOllivier Robert #endif
1023c0b746e5SOllivier Robert 		pp->lencode = 0;
1024c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1025c0b746e5SOllivier Robert 		return;
1026c0b746e5SOllivier Robert 	}
1027c0b746e5SOllivier Robert 	/*
1028c0b746e5SOllivier Robert 	  Append a mark of the clock's received signal quality for the
1029c0b746e5SOllivier Robert 	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1030c0b746e5SOllivier Robert 	  quality value to `6' for his s/w) and terminate the string for
1031c0b746e5SOllivier Robert 	  sure.  This should not go off the buffer end.
1032c0b746e5SOllivier Robert 	*/
1033c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1034c0b746e5SOllivier Robert 				       '6' : ('0' + up->quality));
1035c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1036c0b746e5SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
1037c0b746e5SOllivier Robert 
1038c0b746e5SOllivier Robert 	/* We don't use the micro-/milli- second part... */
1039c0b746e5SOllivier Robert 	pp->usec = 0;
1040c0b746e5SOllivier Robert 	pp->msec = 0;
1041c0b746e5SOllivier Robert 
1042c0b746e5SOllivier Robert 	n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d",
1043c0b746e5SOllivier Robert 		   &pp->hour, &pp->minute, &pp->second,
1044c0b746e5SOllivier Robert 		   &wday, &pp->day, &month, &pp->year, &bst, &status);
1045c0b746e5SOllivier Robert 
1046c0b746e5SOllivier Robert 	/* Validate format and numbers. */
1047c0b746e5SOllivier Robert 	if(n != 9) {
1048c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1049c0b746e5SOllivier Robert 		/* Would expect to have caught major problems already... */
1050c0b746e5SOllivier Robert 		if(debug) { printf("arc: badly formatted data.\n"); }
1051c0b746e5SOllivier Robert #endif
1052c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1053c0b746e5SOllivier Robert 		return;
1054c0b746e5SOllivier Robert 	}
1055c0b746e5SOllivier Robert 	/*
1056c0b746e5SOllivier Robert 	  Validate received values at least enough to prevent internal
1057c0b746e5SOllivier Robert 	  array-bounds problems, etc.
1058c0b746e5SOllivier Robert 	*/
1059c0b746e5SOllivier Robert 	if((pp->hour < 0) || (pp->hour > 23) ||
1060c0b746e5SOllivier Robert 	   (pp->minute < 0) || (pp->minute > 59) ||
1061c0b746e5SOllivier Robert 	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1062c0b746e5SOllivier Robert 	   (wday < 1) || (wday > 7) ||
1063c0b746e5SOllivier Robert 	   (pp->day < 1) || (pp->day > 31) ||
1064c0b746e5SOllivier Robert 	   (month < 1) || (month > 12) ||
1065c0b746e5SOllivier Robert 	   (pp->year < 0) || (pp->year > 99)) {
1066c0b746e5SOllivier Robert 		/* Data out of range. */
1067c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1068c0b746e5SOllivier Robert 		return;
1069c0b746e5SOllivier Robert 	}
1070c0b746e5SOllivier Robert 	/* Check that BST/UTC bits are the complement of one another. */
1071c0b746e5SOllivier Robert 	if(!(bst & 2) == !(bst & 4)) {
1072c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1073c0b746e5SOllivier Robert 		return;
1074c0b746e5SOllivier Robert 	}
1075c0b746e5SOllivier Robert 
1076c0b746e5SOllivier Robert 	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1077c0b746e5SOllivier Robert 
1078c0b746e5SOllivier Robert 	/* Year-2000 alert! */
1079c0b746e5SOllivier Robert 	/* Attempt to wrap 2-digit date into sensible window. */
1080c0b746e5SOllivier Robert 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
1081c0b746e5SOllivier Robert 	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
1082c0b746e5SOllivier Robert 	/*
1083c0b746e5SOllivier Robert 	  Attempt to do the right thing by screaming that the code will
1084c0b746e5SOllivier Robert 	  soon break when we get to the end of its useful life.  What a
1085c0b746e5SOllivier Robert 	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1086c0b746e5SOllivier Robert 	*/
1087c0b746e5SOllivier Robert 	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
1088c0b746e5SOllivier Robert 		/*This should get attention B^> */
1089c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,
1090c0b746e5SOllivier Robert 		       "ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1091c0b746e5SOllivier Robert 	}
1092c0b746e5SOllivier Robert #ifdef DEBUG
1093c0b746e5SOllivier Robert 	if(debug) {
1094c0b746e5SOllivier Robert 		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1095c0b746e5SOllivier Robert 		       n,
1096c0b746e5SOllivier Robert 		       pp->hour, pp->minute, pp->second,
1097c0b746e5SOllivier Robert 		       pp->day, month, pp->year, bst, status);
1098c0b746e5SOllivier Robert 	}
1099c0b746e5SOllivier Robert #endif
1100c0b746e5SOllivier Robert 
1101c0b746e5SOllivier Robert 	/*
1102c0b746e5SOllivier Robert 	  The status value tested for is not strictly supported by the
1103c0b746e5SOllivier Robert 	  clock spec since the value of bit 2 (0x4) is claimed to be
1104c0b746e5SOllivier Robert 	  undefined for MSF, yet does seem to indicate if the last resync
1105c0b746e5SOllivier Robert 	  was successful or not.
1106c0b746e5SOllivier Robert 	*/
1107c0b746e5SOllivier Robert 	pp->leap = LEAP_NOWARNING;
1108c0b746e5SOllivier Robert 	status &= 0x7;
1109c0b746e5SOllivier Robert 	if(status == 0x3) {
1110c0b746e5SOllivier Robert 		if(status != up->status)
1111c0b746e5SOllivier Robert 		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1112c0b746e5SOllivier Robert 	} else {
1113c0b746e5SOllivier Robert 		if(status != up->status) {
1114c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: signal lost");
1115c0b746e5SOllivier Robert 			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1116c0b746e5SOllivier Robert 			up->status = status;
1117c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_FAULT);
1118c0b746e5SOllivier Robert 			return;
1119c0b746e5SOllivier Robert 		}
1120c0b746e5SOllivier Robert 	}
1121c0b746e5SOllivier Robert 	up->status = status;
1122c0b746e5SOllivier Robert 
1123c0b746e5SOllivier Robert 	pp->day += moff[month - 1];
1124c0b746e5SOllivier Robert 
1125c0b746e5SOllivier Robert 	if(isleap_4(pp->year) && month > 2) { pp->day++; }	/* Y2KFixes */
1126c0b746e5SOllivier Robert 
1127c0b746e5SOllivier Robert 	/* Convert to UTC if required */
1128c0b746e5SOllivier Robert 	if(bst & 2) {
1129c0b746e5SOllivier Robert 		pp->hour--;
1130c0b746e5SOllivier Robert 		if (pp->hour < 0) {
1131c0b746e5SOllivier Robert 			pp->hour = 23;
1132c0b746e5SOllivier Robert 			pp->day--;
1133c0b746e5SOllivier Robert 			/* If we try to wrap round the year (BST on 1st Jan), reject.*/
1134c0b746e5SOllivier Robert 			if(pp->day < 0) {
1135c0b746e5SOllivier Robert 				refclock_report(peer, CEVNT_BADTIME);
1136c0b746e5SOllivier Robert 				return;
1137c0b746e5SOllivier Robert 			}
1138c0b746e5SOllivier Robert 		}
1139c0b746e5SOllivier Robert 	}
1140c0b746e5SOllivier Robert 
1141c0b746e5SOllivier Robert 	/* If clock signal quality is unknown, revert to default PRECISION...*/
1142c0b746e5SOllivier Robert 	if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; }
1143c0b746e5SOllivier Robert 	/* ...else improve precision if flag3 is set... */
1144c0b746e5SOllivier Robert 	else {
1145c0b746e5SOllivier Robert 		peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1146c0b746e5SOllivier Robert 				   HIGHPRECISION : PRECISION);
1147c0b746e5SOllivier Robert 	}
1148c0b746e5SOllivier Robert 
1149c0b746e5SOllivier Robert 	/* Notice and log any change (eg from initial defaults) for flags. */
1150c0b746e5SOllivier Robert 	if(up->saved_flags != pp->sloppyclockflag) {
1151c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1152c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1153c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1154c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1155c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1156c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1157c0b746e5SOllivier Robert 		/* Note effects of flags changing... */
1158c0b746e5SOllivier Robert 		if(debug) {
1159c0b746e5SOllivier Robert 			printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp));
1160c0b746e5SOllivier Robert 			printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp));
1161c0b746e5SOllivier Robert 			printf("arc: PRECISION = %d.\n", peer->precision);
1162c0b746e5SOllivier Robert 		}
1163c0b746e5SOllivier Robert #endif
1164c0b746e5SOllivier Robert 		up->saved_flags = pp->sloppyclockflag;
1165c0b746e5SOllivier Robert 	}
1166c0b746e5SOllivier Robert 
1167c0b746e5SOllivier Robert 	/* Note time of last believable timestamp. */
1168c0b746e5SOllivier Robert 	pp->lastrec = up->lastrec;
1169c0b746e5SOllivier Robert 
1170c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1171c0b746e5SOllivier Robert 	/* Find out if a leap-second might just have happened...
1172c0b746e5SOllivier Robert 	   (ie is this the first hour of the first day of Jan or Jul?)
1173c0b746e5SOllivier Robert 	*/
1174c0b746e5SOllivier Robert 	if((pp->hour == 0) &&
1175c0b746e5SOllivier Robert 	   (pp->day == 1) &&
1176c0b746e5SOllivier Robert 	   ((month == 1) || (month == 7))) {
1177c0b746e5SOllivier Robert 		if(possible_leap >= 0) {
1178c0b746e5SOllivier Robert 			/* A leap may have happened, and no resync has started yet...*/
1179c0b746e5SOllivier Robert 			possible_leap = 1;
1180c0b746e5SOllivier Robert 		}
1181c0b746e5SOllivier Robert 	} else {
1182c0b746e5SOllivier Robert 		/* Definitely not leap-second territory... */
1183c0b746e5SOllivier Robert 		possible_leap = 0;
1184c0b746e5SOllivier Robert 	}
1185c0b746e5SOllivier Robert #endif
1186c0b746e5SOllivier Robert 
1187c0b746e5SOllivier Robert 	if (!refclock_process(pp)) {
1188c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
1189c0b746e5SOllivier Robert 		return;
1190c0b746e5SOllivier Robert 	}
1191c0b746e5SOllivier Robert 	refclock_receive(peer);
1192c0b746e5SOllivier Robert }
1193c0b746e5SOllivier Robert 
1194c0b746e5SOllivier Robert 
1195c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */
1196c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */
1197c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */
1198c0b746e5SOllivier Robert static  void    request_time    P((int, struct peer *));
1199c0b746e5SOllivier Robert static void
1200c0b746e5SOllivier Robert request_time(
1201c0b746e5SOllivier Robert 	int unit,
1202c0b746e5SOllivier Robert 	struct peer *peer
1203c0b746e5SOllivier Robert 	)
1204c0b746e5SOllivier Robert {
1205c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
1206c0b746e5SOllivier Robert 	register struct arcunit *up = (struct arcunit *)pp->unitptr;
1207c0b746e5SOllivier Robert #ifdef DEBUG
1208c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1209c0b746e5SOllivier Robert #endif
1210c0b746e5SOllivier Robert 	if (!send_slow(up, pp->io.fd, "o\r")) {
1211c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1212c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit);
1213c0b746e5SOllivier Robert #endif
1214c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1215c0b746e5SOllivier Robert 		return;
1216c0b746e5SOllivier Robert 	}
1217c0b746e5SOllivier Robert 	pp->polls++;
1218c0b746e5SOllivier Robert }
1219c0b746e5SOllivier Robert 
1220c0b746e5SOllivier Robert /*
1221c0b746e5SOllivier Robert  * arc_poll - called by the transmit procedure
1222c0b746e5SOllivier Robert  */
1223c0b746e5SOllivier Robert static void
1224c0b746e5SOllivier Robert arc_poll(
1225c0b746e5SOllivier Robert 	int unit,
1226c0b746e5SOllivier Robert 	struct peer *peer
1227c0b746e5SOllivier Robert 	)
1228c0b746e5SOllivier Robert {
1229c0b746e5SOllivier Robert 	register struct arcunit *up;
1230c0b746e5SOllivier Robert 	struct refclockproc *pp;
1231c0b746e5SOllivier Robert 	int resync_needed;              /* Should we start a resync? */
1232c0b746e5SOllivier Robert 
1233c0b746e5SOllivier Robert 	pp = peer->procptr;
1234c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
1235c0b746e5SOllivier Robert 	pp->lencode = 0;
1236c0b746e5SOllivier Robert 	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
1237c0b746e5SOllivier Robert 
1238c0b746e5SOllivier Robert #if 0
1239c0b746e5SOllivier Robert 	/* Flush input. */
1240c0b746e5SOllivier Robert 	tcflush(pp->io.fd, TCIFLUSH);
1241c0b746e5SOllivier Robert #endif
1242c0b746e5SOllivier Robert 
1243c0b746e5SOllivier Robert 	/* Resync if our next scheduled resync time is here or has passed. */
1244c0b746e5SOllivier Robert 	resync_needed = (up->next_resync <= current_time);
1245c0b746e5SOllivier Robert 
1246c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1247c0b746e5SOllivier Robert 	/*
1248c0b746e5SOllivier Robert 	  Try to catch a potential leap-second insertion or deletion quickly.
1249c0b746e5SOllivier Robert 
1250c0b746e5SOllivier Robert 	  In addition to the normal NTP fun of clocks that don't report
1251c0b746e5SOllivier Robert 	  leap-seconds spooking their hosts, this clock does not even
1252c0b746e5SOllivier Robert 	  sample the radio sugnal the whole time, so may miss a
1253c0b746e5SOllivier Robert 	  leap-second insertion or deletion for up to a whole sample
1254c0b746e5SOllivier Robert 	  time.
1255c0b746e5SOllivier Robert 
1256c0b746e5SOllivier Robert 	  To try to minimise this effect, if in the first few minutes of
1257c0b746e5SOllivier Robert 	  the day immediately following a leap-second-insertion point
1258c0b746e5SOllivier Robert 	  (ie in the first hour of the first day of the first and sixth
1259c0b746e5SOllivier Robert 	  months), and if the last resync was in the previous day, and a
1260c0b746e5SOllivier Robert 	  resync is not already in progress, resync the clock
1261c0b746e5SOllivier Robert 	  immediately.
1262c0b746e5SOllivier Robert 
1263c0b746e5SOllivier Robert 	*/
1264c0b746e5SOllivier Robert 	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1265c0b746e5SOllivier Robert 	   (!up->resyncing)) {          /* No resync in progress yet. */
1266c0b746e5SOllivier Robert 		resync_needed = 1;
1267c0b746e5SOllivier Robert 		possible_leap = -1;          /* Prevent multiple resyncs. */
1268c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1269c0b746e5SOllivier Robert 	}
1270c0b746e5SOllivier Robert #endif
1271c0b746e5SOllivier Robert 
1272c0b746e5SOllivier Robert 	/* Do a resync if required... */
1273c0b746e5SOllivier Robert 	if(resync_needed) {
1274c0b746e5SOllivier Robert 		/* First, reset quality value to `unknown' so we can detect */
1275c0b746e5SOllivier Robert 		/* when a quality message has been responded to by this     */
1276c0b746e5SOllivier Robert 		/* being set to some other value.                           */
1277c0b746e5SOllivier Robert 		up->quality = QUALITY_UNKNOWN;
1278c0b746e5SOllivier Robert 
1279c0b746e5SOllivier Robert 		/* Note that we are resyncing... */
1280c0b746e5SOllivier Robert 		up->resyncing = 1;
1281c0b746e5SOllivier Robert 
1282c0b746e5SOllivier Robert 		/* Now actually send the resync command and an immediate poll. */
1283c0b746e5SOllivier Robert #ifdef DEBUG
1284c0b746e5SOllivier Robert 		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1285c0b746e5SOllivier Robert #endif
1286c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1287c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "h\r");
1288c0b746e5SOllivier Robert 
1289c0b746e5SOllivier Robert 		/* Schedule our next resync... */
1290c0b746e5SOllivier Robert 		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1291c0b746e5SOllivier Robert 
1292c0b746e5SOllivier Robert 		/* Drop through to request time if appropriate. */
1293c0b746e5SOllivier Robert 	}
1294c0b746e5SOllivier Robert 
1295c0b746e5SOllivier Robert 	/* If clock quality is too poor to trust, indicate a fault. */
1296c0b746e5SOllivier Robert 	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1297c0b746e5SOllivier Robert 	/* we'll cross our fingers and just hope that the thing     */
1298c0b746e5SOllivier Robert 	/* synced so quickly we did not catch it---we'll            */
1299c0b746e5SOllivier Robert 	/* double-check the clock is OK elsewhere.                  */
1300c0b746e5SOllivier Robert 	if(
1301c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1302c0b746e5SOllivier Robert 		(up->quality != QUALITY_UNKNOWN) &&
1303c0b746e5SOllivier Robert #else
1304c0b746e5SOllivier Robert 		(up->quality == QUALITY_UNKNOWN) ||
1305c0b746e5SOllivier Robert #endif
1306c0b746e5SOllivier Robert 		(up->quality < MIN_CLOCK_QUALITY_OK)) {
1307c0b746e5SOllivier Robert #ifdef DEBUG
1308c0b746e5SOllivier Robert 		if(debug) {
1309c0b746e5SOllivier Robert 			printf("arc: clock quality %d too poor.\n", up->quality);
1310c0b746e5SOllivier Robert 		}
1311c0b746e5SOllivier Robert #endif
1312c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1313c0b746e5SOllivier Robert 		return;
1314c0b746e5SOllivier Robert 	}
1315c0b746e5SOllivier Robert 	/* This is the normal case: request a timestamp. */
1316c0b746e5SOllivier Robert 	request_time(unit, peer);
1317c0b746e5SOllivier Robert }
1318c0b746e5SOllivier Robert 
1319c0b746e5SOllivier Robert #else
1320c0b746e5SOllivier Robert int refclock_arc_bs;
1321c0b746e5SOllivier Robert #endif
1322