xref: /freebsd/contrib/ntp/ntpd/refclock_arc.c (revision c0b746e5e8d9479f05b3749cbf1f73b8928719bd)
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 
306c0b746e5SOllivier Robert 
307c0b746e5SOllivier Robert #include <stdio.h>
308c0b746e5SOllivier Robert #include <ctype.h>
309c0b746e5SOllivier Robert #include <sys/time.h>
310c0b746e5SOllivier Robert 
311c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS)
312c0b746e5SOllivier Robert #include <sgtty.h>
313c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */
314c0b746e5SOllivier Robert 
315c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS)
316c0b746e5SOllivier Robert #include <termio.h>
317c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */
318c0b746e5SOllivier Robert 
319c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS)
320c0b746e5SOllivier Robert #include <termios.h>
321c0b746e5SOllivier Robert #endif
322c0b746e5SOllivier Robert 
323c0b746e5SOllivier Robert #include "ntpd.h"
324c0b746e5SOllivier Robert #include "ntp_io.h"
325c0b746e5SOllivier Robert #include "ntp_refclock.h"
326c0b746e5SOllivier Robert #include "ntp_stdlib.h"
327c0b746e5SOllivier Robert 
328c0b746e5SOllivier Robert /*
329c0b746e5SOllivier Robert  * This driver supports the ARCRON MSF Radio Controlled Clock
330c0b746e5SOllivier Robert  */
331c0b746e5SOllivier Robert 
332c0b746e5SOllivier Robert /*
333c0b746e5SOllivier Robert  * Interface definitions
334c0b746e5SOllivier Robert  */
335c0b746e5SOllivier Robert #define DEVICE          "/dev/arc%d"    /* Device name and unit. */
336c0b746e5SOllivier Robert #define SPEED           B300            /* UART speed (300 baud) */
337c0b746e5SOllivier Robert #define PRECISION       (-4)            /* Precision  (~63 ms). */
338c0b746e5SOllivier Robert #define HIGHPRECISION   (-5)            /* If things are going well... */
339c0b746e5SOllivier Robert #define REFID           "MSFa"          /* Reference ID. */
340c0b746e5SOllivier Robert #define DESCRIPTION     "ARCRON MSF Receiver"
341c0b746e5SOllivier Robert 
342c0b746e5SOllivier Robert #define NSAMPLESLONG    8               /* Stages of long filter. */
343c0b746e5SOllivier Robert 
344c0b746e5SOllivier Robert #define LENARC          16              /* Format `o' timecode length. */
345c0b746e5SOllivier Robert 
346c0b746e5SOllivier Robert #define BITSPERCHAR     11              /* Bits per character. */
347c0b746e5SOllivier Robert #define BITTIME         0x0DA740E       /* Time for 1 bit at 300bps. */
348c0b746e5SOllivier Robert #define CHARTIME10      0x8888888       /* Time for 10-bit char at 300bps. */
349c0b746e5SOllivier Robert #define CHARTIME11      0x962FC96       /* Time for 11-bit char at 300bps. */
350c0b746e5SOllivier Robert #define CHARTIME                        /* Time for char at 300bps. */ \
351c0b746e5SOllivier Robert ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
352c0b746e5SOllivier Robert 				       (BITSPERCHAR * BITTIME) ) )
353c0b746e5SOllivier Robert 
354c0b746e5SOllivier Robert      /* Allow for UART to accept char half-way through final stop bit. */
355c0b746e5SOllivier Robert #define INITIALOFFSET (u_int32)(-BITTIME/2)
356c0b746e5SOllivier Robert 
357c0b746e5SOllivier Robert      /*
358c0b746e5SOllivier Robert     charoffsets[x] is the time after the start of the second that byte
359c0b746e5SOllivier Robert     x (with the first byte being byte 1) is received by the UART,
360c0b746e5SOllivier Robert     assuming that the initial edge of the start bit of the first byte
361c0b746e5SOllivier Robert     is on-time.  The values are represented as the fractional part of
362c0b746e5SOllivier Robert     an l_fp.
363c0b746e5SOllivier Robert 
364c0b746e5SOllivier Robert     We store enough values to have the offset of each byte including
365c0b746e5SOllivier Robert     the trailing \r, on the assumption that the bytes follow one
366c0b746e5SOllivier Robert     another without gaps.
367c0b746e5SOllivier Robert     */
368c0b746e5SOllivier Robert      static const u_int32 charoffsets[LENARC+1] = {
369c0b746e5SOllivier Robert #if BITSPERCHAR == 11 /* Usual case. */
370c0b746e5SOllivier Robert 	     /* Offsets computed as accurately as possible... */
371c0b746e5SOllivier Robert 	     0,
372c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
373c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
374c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
375c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
376c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
377c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
378c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
379c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
380c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
381c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
382c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
383c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
384c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
385c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
386c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
387c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
388c0b746e5SOllivier Robert #else
389c0b746e5SOllivier Robert 	     /* Offsets computed with a small rounding error... */
390c0b746e5SOllivier Robert 	     0,
391c0b746e5SOllivier Robert 	     INITIALOFFSET +  1 * CHARTIME,
392c0b746e5SOllivier Robert 	     INITIALOFFSET +  2 * CHARTIME,
393c0b746e5SOllivier Robert 	     INITIALOFFSET +  3 * CHARTIME,
394c0b746e5SOllivier Robert 	     INITIALOFFSET +  4 * CHARTIME,
395c0b746e5SOllivier Robert 	     INITIALOFFSET +  5 * CHARTIME,
396c0b746e5SOllivier Robert 	     INITIALOFFSET +  6 * CHARTIME,
397c0b746e5SOllivier Robert 	     INITIALOFFSET +  7 * CHARTIME,
398c0b746e5SOllivier Robert 	     INITIALOFFSET +  8 * CHARTIME,
399c0b746e5SOllivier Robert 	     INITIALOFFSET +  9 * CHARTIME,
400c0b746e5SOllivier Robert 	     INITIALOFFSET + 10 * CHARTIME,
401c0b746e5SOllivier Robert 	     INITIALOFFSET + 11 * CHARTIME,
402c0b746e5SOllivier Robert 	     INITIALOFFSET + 12 * CHARTIME,
403c0b746e5SOllivier Robert 	     INITIALOFFSET + 13 * CHARTIME,
404c0b746e5SOllivier Robert 	     INITIALOFFSET + 14 * CHARTIME,
405c0b746e5SOllivier Robert 	     INITIALOFFSET + 15 * CHARTIME,
406c0b746e5SOllivier Robert 	     INITIALOFFSET + 16 * CHARTIME
407c0b746e5SOllivier Robert #endif
408c0b746e5SOllivier Robert      };
409c0b746e5SOllivier Robert 
410c0b746e5SOllivier Robert /* Chose filter length dependent on fudge flag 4. */
411c0b746e5SOllivier Robert #define CHOSENSAMPLES(pp) \
412c0b746e5SOllivier Robert (((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES)
413c0b746e5SOllivier Robert      /*
414c0b746e5SOllivier Robert Chose how many filter samples to keep.  Several factors are in play.
415c0b746e5SOllivier Robert 
416c0b746e5SOllivier Robert  1) Discard at least one sample to allow a spike value to be
417c0b746e5SOllivier Robert     discarded.
418c0b746e5SOllivier Robert 
419c0b746e5SOllivier Robert  2) Discard about 1-in-8 to 1-in-30 samples to handle spikes.
420c0b746e5SOllivier Robert 
421c0b746e5SOllivier Robert  3) Keep an odd number of samples to avoid median value being biased
422c0b746e5SOllivier Robert     high or low.
423c0b746e5SOllivier Robert */
424c0b746e5SOllivier Robert #define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1)
425c0b746e5SOllivier Robert 
426c0b746e5SOllivier Robert #define DEFAULT_RESYNC_TIME  (57*60)    /* Gap between resync attempts (s). */
427c0b746e5SOllivier Robert #define RETRY_RESYNC_TIME    (27*60)    /* Gap to emergency resync attempt. */
428c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
429c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 500        /* Delay before first resync. */
430c0b746e5SOllivier Robert #else
431c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 50         /* Delay before first resync. */
432c0b746e5SOllivier Robert #endif
433c0b746e5SOllivier Robert 
434c0b746e5SOllivier Robert      static const int moff[12] =
435c0b746e5SOllivier Robert { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
436c0b746e5SOllivier Robert /* Flags for a raw open() of the clock serial device. */
437c0b746e5SOllivier Robert #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
438c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR | O_NOCTTY)
439c0b746e5SOllivier Robert #else           /* Oh well, it may not matter... */
440c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR)
441c0b746e5SOllivier Robert #endif
442c0b746e5SOllivier Robert 
443c0b746e5SOllivier Robert 
444c0b746e5SOllivier Robert /* Length of queue of command bytes to be sent. */
445c0b746e5SOllivier Robert #define CMDQUEUELEN 4                   /* Enough for two cmds + each \r. */
446c0b746e5SOllivier Robert /* Queue tick time; interval in seconds between chars taken off queue. */
447c0b746e5SOllivier Robert /* Must be >= 2 to allow o\r response to come back uninterrupted. */
448c0b746e5SOllivier Robert #define QUEUETICK   2                   /* Allow o\r reply to finish. */
449c0b746e5SOllivier Robert 
450c0b746e5SOllivier Robert /*
451c0b746e5SOllivier Robert  * ARC unit control structure
452c0b746e5SOllivier Robert  */
453c0b746e5SOllivier Robert struct arcunit {
454c0b746e5SOllivier Robert 	l_fp lastrec;       /* Time tag for the receive time (system). */
455c0b746e5SOllivier Robert 	int status;         /* Clock status. */
456c0b746e5SOllivier Robert 
457c0b746e5SOllivier Robert 	int quality;        /* Quality of reception 0--5 for unit. */
458c0b746e5SOllivier Robert 	/* We may also use the values -1 or 6 internally. */
459c0b746e5SOllivier Robert 
460c0b746e5SOllivier Robert 	u_long next_resync; /* Next resync time (s) compared to current_time. */
461c0b746e5SOllivier Robert 	int resyncing;      /* Resync in progress if true. */
462c0b746e5SOllivier Robert 
463c0b746e5SOllivier Robert 	/* In the outgoing queue, cmdqueue[0] is next to be sent. */
464c0b746e5SOllivier Robert 	char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
465c0b746e5SOllivier Robert 
466c0b746e5SOllivier Robert 	u_long saved_flags; /* Saved fudge flags. */
467c0b746e5SOllivier Robert };
468c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
469c0b746e5SOllivier Robert /* The flag `possible_leap' is set non-zero when any MSF unit
470c0b746e5SOllivier Robert        thinks a leap-second may have happened.
471c0b746e5SOllivier Robert 
472c0b746e5SOllivier Robert        Set whenever we receive a valid time sample in the first hour of
473c0b746e5SOllivier Robert        the first day of the first/seventh months.
474c0b746e5SOllivier Robert 
475c0b746e5SOllivier Robert        Outside the special hour this value is unconditionally set
476c0b746e5SOllivier Robert        to zero by the receive routine.
477c0b746e5SOllivier Robert 
478c0b746e5SOllivier Robert        On finding itself in this timeslot, as long as the value is
479c0b746e5SOllivier Robert        non-negative, the receive routine sets it to a positive value to
480c0b746e5SOllivier Robert        indicate a resync to MSF should be performed.
481c0b746e5SOllivier Robert 
482c0b746e5SOllivier Robert        In the poll routine, if this value is positive and we are not
483c0b746e5SOllivier Robert        already resyncing (eg from a sync that started just before
484c0b746e5SOllivier Robert        midnight), start resyncing and set this value negative to
485c0b746e5SOllivier Robert        indicate that a leap-triggered resync has been started.  Having
486c0b746e5SOllivier Robert        set this negative prevents the receive routine setting it
487c0b746e5SOllivier Robert        positive and thus prevents multiple resyncs during the witching
488c0b746e5SOllivier Robert        hour.
489c0b746e5SOllivier Robert      */
490c0b746e5SOllivier Robert static int possible_leap = 0;       /* No resync required by default. */
491c0b746e5SOllivier Robert #endif
492c0b746e5SOllivier Robert 
493c0b746e5SOllivier Robert #if 0
494c0b746e5SOllivier Robert static void dummy_event_handler P((struct peer *));
495c0b746e5SOllivier Robert static void   arc_event_handler P((struct peer *));
496c0b746e5SOllivier Robert #endif /* 0 */
497c0b746e5SOllivier Robert 
498c0b746e5SOllivier Robert #define QUALITY_UNKNOWN     -1 /* Indicates unknown clock quality. */
499c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
500c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
501c0b746e5SOllivier Robert #define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
502c0b746e5SOllivier Robert 
503c0b746e5SOllivier Robert /*
504c0b746e5SOllivier Robert  * Function prototypes
505c0b746e5SOllivier Robert  */
506c0b746e5SOllivier Robert static  int     arc_start       P((int, struct peer *));
507c0b746e5SOllivier Robert static  void    arc_shutdown    P((int, struct peer *));
508c0b746e5SOllivier Robert static  void    arc_receive     P((struct recvbuf *));
509c0b746e5SOllivier Robert static  void    arc_poll        P((int, struct peer *));
510c0b746e5SOllivier Robert 
511c0b746e5SOllivier Robert /*
512c0b746e5SOllivier Robert  * Transfer vector
513c0b746e5SOllivier Robert  */
514c0b746e5SOllivier Robert struct  refclock refclock_arc = {
515c0b746e5SOllivier Robert 	arc_start,              /* start up driver */
516c0b746e5SOllivier Robert 	arc_shutdown,           /* shut down driver */
517c0b746e5SOllivier Robert 	arc_poll,               /* transmit poll message */
518c0b746e5SOllivier Robert 	noentry,                /* not used (old arc_control) */
519c0b746e5SOllivier Robert 	noentry,                /* initialize driver (not used) */
520c0b746e5SOllivier Robert 	noentry,                /* not used (old arc_buginfo) */
521c0b746e5SOllivier Robert 	NOFLAGS                 /* not used */
522c0b746e5SOllivier Robert };
523c0b746e5SOllivier Robert 
524c0b746e5SOllivier Robert /* Queue us up for the next tick. */
525c0b746e5SOllivier Robert #define ENQUEUE(up) \
526c0b746e5SOllivier Robert 	do { \
527c0b746e5SOllivier Robert 	     if((up)->ev.next != 0) { break; } /* WHOOPS! */ \
528c0b746e5SOllivier Robert 	     peer->nextdate = current_time + QUEUETICK; \
529c0b746e5SOllivier Robert 	} while(0)
530c0b746e5SOllivier Robert 
531c0b746e5SOllivier Robert #if 0
532c0b746e5SOllivier Robert /* Placeholder event handler---does nothing safely---soaks up lose tick. */
533c0b746e5SOllivier Robert static void
534c0b746e5SOllivier Robert dummy_event_handler(
535c0b746e5SOllivier Robert 	struct peer *peer
536c0b746e5SOllivier Robert 	)
537c0b746e5SOllivier Robert {
538c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
539c0b746e5SOllivier Robert 	if(debug) { printf("arc: dummy_event_handler() called.\n"); }
540c0b746e5SOllivier Robert #endif
541c0b746e5SOllivier Robert }
542c0b746e5SOllivier Robert 
543c0b746e5SOllivier Robert /*
544c0b746e5SOllivier Robert Normal event handler.
545c0b746e5SOllivier Robert 
546c0b746e5SOllivier Robert Take first character off queue and send to clock if not a null.
547c0b746e5SOllivier Robert 
548c0b746e5SOllivier Robert Shift characters down and put a null on the end.
549c0b746e5SOllivier Robert 
550c0b746e5SOllivier Robert We assume that there is no parallelism so no race condition, but even
551c0b746e5SOllivier Robert if there is nothing bad will happen except that we might send some bad
552c0b746e5SOllivier Robert data to the clock once in a while.
553c0b746e5SOllivier Robert */
554c0b746e5SOllivier Robert static void
555c0b746e5SOllivier Robert arc_event_handler(
556c0b746e5SOllivier Robert 	struct peer *peer
557c0b746e5SOllivier Robert 	)
558c0b746e5SOllivier Robert {
559c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
560c0b746e5SOllivier Robert 	register struct arcunit *up = (struct arcunit *)pp->unitptr;
561c0b746e5SOllivier Robert 	int i;
562c0b746e5SOllivier Robert 	char c;
563c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
564c0b746e5SOllivier Robert 	if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
565c0b746e5SOllivier Robert #endif
566c0b746e5SOllivier Robert 
567c0b746e5SOllivier Robert 	c = up->cmdqueue[0];       /* Next char to be sent. */
568c0b746e5SOllivier Robert 	/* Shift down characters, shifting trailing \0 in at end. */
569c0b746e5SOllivier Robert 	for(i = 0; i < CMDQUEUELEN; ++i)
570c0b746e5SOllivier Robert 	{ up->cmdqueue[i] = up->cmdqueue[i+1]; }
571c0b746e5SOllivier Robert 
572c0b746e5SOllivier Robert 	/* Don't send '\0' characters. */
573c0b746e5SOllivier Robert 	if(c != '\0') {
574c0b746e5SOllivier Robert 		if(write(pp->io.fd, &c, 1) != 1) {
575c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
576c0b746e5SOllivier Robert 		}
577c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
578c0b746e5SOllivier Robert 		else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
579c0b746e5SOllivier Robert #endif
580c0b746e5SOllivier Robert 	}
581c0b746e5SOllivier Robert }
582c0b746e5SOllivier Robert #endif /* 0 */
583c0b746e5SOllivier Robert 
584c0b746e5SOllivier Robert /*
585c0b746e5SOllivier Robert  * arc_start - open the devices and initialize data for processing
586c0b746e5SOllivier Robert  */
587c0b746e5SOllivier Robert static int
588c0b746e5SOllivier Robert arc_start(
589c0b746e5SOllivier Robert 	int unit,
590c0b746e5SOllivier Robert 	struct peer *peer
591c0b746e5SOllivier Robert 	)
592c0b746e5SOllivier Robert {
593c0b746e5SOllivier Robert 	register struct arcunit *up;
594c0b746e5SOllivier Robert 	struct refclockproc *pp;
595c0b746e5SOllivier Robert 	int fd;
596c0b746e5SOllivier Robert 	char device[20];
597c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
598c0b746e5SOllivier Robert 	struct termios arg;
599c0b746e5SOllivier Robert #endif
600c0b746e5SOllivier Robert 
601c0b746e5SOllivier Robert 	msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
602c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
603c0b746e5SOllivier Robert 	if(debug) {
604c0b746e5SOllivier Robert 		printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
605c0b746e5SOllivier Robert 	}
606c0b746e5SOllivier Robert #endif
607c0b746e5SOllivier Robert 
608c0b746e5SOllivier Robert 	/* Prevent a ridiculous device number causing overflow of device[]. */
609c0b746e5SOllivier Robert 	if((unit < 0) || (unit > 255)) { return(0); }
610c0b746e5SOllivier Robert 
611c0b746e5SOllivier Robert 	/*
612c0b746e5SOllivier Robert 	 * Open serial port. Use CLK line discipline, if available.
613c0b746e5SOllivier Robert 	 */
614c0b746e5SOllivier Robert 	(void)sprintf(device, DEVICE, unit);
615c0b746e5SOllivier Robert 	if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
616c0b746e5SOllivier Robert 		return(0);
617c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
618c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d using open().\n", unit); }
619c0b746e5SOllivier Robert #endif
620c0b746e5SOllivier Robert 	fd = open(device, OPEN_FLAGS);
621c0b746e5SOllivier Robert 	if(fd < 0) {
622c0b746e5SOllivier Robert #ifdef DEBUG
623c0b746e5SOllivier Robert 		if(debug) { printf("arc: failed [open()] to open %s.\n", device); }
624c0b746e5SOllivier Robert #endif
625c0b746e5SOllivier Robert 		return(0);
626c0b746e5SOllivier Robert 	}
627c0b746e5SOllivier Robert 
628c0b746e5SOllivier Robert 	fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
629c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
630c0b746e5SOllivier Robert 	if(debug)
631c0b746e5SOllivier Robert 	{ printf("Opened RS232 port with file descriptor %d.\n", fd); }
632c0b746e5SOllivier Robert #endif
633c0b746e5SOllivier Robert 
634c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
635c0b746e5SOllivier Robert 
636c0b746e5SOllivier Robert 	arg.c_iflag = IGNBRK | ISTRIP;
637c0b746e5SOllivier Robert 	arg.c_oflag = 0;
638c0b746e5SOllivier Robert 	arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
639c0b746e5SOllivier Robert 	arg.c_lflag = 0;
640c0b746e5SOllivier Robert 	arg.c_cc[VMIN] = 1;
641c0b746e5SOllivier Robert 	arg.c_cc[VTIME] = 0;
642c0b746e5SOllivier Robert 
643c0b746e5SOllivier Robert 	tcsetattr(fd, TCSANOW, &arg);
644c0b746e5SOllivier Robert 
645c0b746e5SOllivier Robert #else
646c0b746e5SOllivier Robert 
647c0b746e5SOllivier Robert 	msyslog(LOG_ERR, "ARCRON: termios not supported in this driver");
648c0b746e5SOllivier Robert 	(void)close(fd);
649c0b746e5SOllivier Robert 
650c0b746e5SOllivier Robert 	return 0;
651c0b746e5SOllivier Robert 
652c0b746e5SOllivier Robert #endif
653c0b746e5SOllivier Robert 
654c0b746e5SOllivier Robert 	up = (struct arcunit *) emalloc(sizeof(struct arcunit));
655c0b746e5SOllivier Robert 	if(!up) { (void) close(fd); return(0); }
656c0b746e5SOllivier Robert 	/* Set structure to all zeros... */
657c0b746e5SOllivier Robert 	memset((char *)up, 0, sizeof(struct arcunit));
658c0b746e5SOllivier Robert 	pp = peer->procptr;
659c0b746e5SOllivier Robert 	pp->io.clock_recv = arc_receive;
660c0b746e5SOllivier Robert 	pp->io.srcclock = (caddr_t)peer;
661c0b746e5SOllivier Robert 	pp->io.datalen = 0;
662c0b746e5SOllivier Robert 	pp->io.fd = fd;
663c0b746e5SOllivier Robert 	if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); }
664c0b746e5SOllivier Robert 	pp->unitptr = (caddr_t)up;
665c0b746e5SOllivier Robert 
666c0b746e5SOllivier Robert 	/*
667c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
668c0b746e5SOllivier Robert 	 */
669c0b746e5SOllivier Robert 	peer->precision = PRECISION;
670c0b746e5SOllivier Robert 	peer->stratum = 2;              /* Default to stratum 2 not 0. */
671c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
672c0b746e5SOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
673c0b746e5SOllivier Robert 	/* Spread out resyncs so that they should remain separated. */
674c0b746e5SOllivier Robert 	up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
675c0b746e5SOllivier Robert 
676c0b746e5SOllivier Robert #if 0 /* Not needed because of zeroing of arcunit structure... */
677c0b746e5SOllivier Robert 	up->resyncing = 0;              /* Not resyncing yet. */
678c0b746e5SOllivier Robert 	up->saved_flags = 0;            /* Default is all flags off. */
679c0b746e5SOllivier Robert 	/* Clear send buffer out... */
680c0b746e5SOllivier Robert 	{
681c0b746e5SOllivier Robert 		int i;
682c0b746e5SOllivier Robert 		for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
683c0b746e5SOllivier Robert 	}
684c0b746e5SOllivier Robert #endif
685c0b746e5SOllivier Robert 
686c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
687c0b746e5SOllivier Robert 	up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
688c0b746e5SOllivier Robert #else
689c0b746e5SOllivier Robert 	up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
690c0b746e5SOllivier Robert #endif
691c0b746e5SOllivier Robert 	return(1);
692c0b746e5SOllivier Robert }
693c0b746e5SOllivier Robert 
694c0b746e5SOllivier Robert 
695c0b746e5SOllivier Robert /*
696c0b746e5SOllivier Robert  * arc_shutdown - shut down the clock
697c0b746e5SOllivier Robert  */
698c0b746e5SOllivier Robert static void
699c0b746e5SOllivier Robert arc_shutdown(
700c0b746e5SOllivier Robert 	int unit,
701c0b746e5SOllivier Robert 	struct peer *peer
702c0b746e5SOllivier Robert 	)
703c0b746e5SOllivier Robert {
704c0b746e5SOllivier Robert 	register struct arcunit *up;
705c0b746e5SOllivier Robert 	struct refclockproc *pp;
706c0b746e5SOllivier Robert 
707c0b746e5SOllivier Robert 	pp = peer->procptr;
708c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
709c0b746e5SOllivier Robert 	io_closeclock(&pp->io);
710c0b746e5SOllivier Robert 	free(up);
711c0b746e5SOllivier Robert }
712c0b746e5SOllivier Robert 
713c0b746e5SOllivier Robert /*
714c0b746e5SOllivier Robert Compute space left in output buffer.
715c0b746e5SOllivier Robert */
716c0b746e5SOllivier Robert static int
717c0b746e5SOllivier Robert space_left(
718c0b746e5SOllivier Robert 	register struct arcunit *up
719c0b746e5SOllivier Robert 	)
720c0b746e5SOllivier Robert {
721c0b746e5SOllivier Robert 	int spaceleft;
722c0b746e5SOllivier Robert 
723c0b746e5SOllivier Robert 	/* Compute space left in buffer after any pending output. */
724c0b746e5SOllivier Robert 	for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
725c0b746e5SOllivier Robert 	{ if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
726c0b746e5SOllivier Robert 	return(spaceleft);
727c0b746e5SOllivier Robert }
728c0b746e5SOllivier Robert 
729c0b746e5SOllivier Robert /*
730c0b746e5SOllivier Robert Send command by copying into command buffer as far forward as possible,
731c0b746e5SOllivier Robert after any pending output.
732c0b746e5SOllivier Robert 
733c0b746e5SOllivier Robert Indicate an error by returning 0 if there is not space for the command.
734c0b746e5SOllivier Robert */
735c0b746e5SOllivier Robert static int
736c0b746e5SOllivier Robert send_slow(
737c0b746e5SOllivier Robert 	register struct arcunit *up,
738c0b746e5SOllivier Robert 	int fd,
739c0b746e5SOllivier Robert 	const char *s
740c0b746e5SOllivier Robert 	)
741c0b746e5SOllivier Robert {
742c0b746e5SOllivier Robert 	int sl = strlen(s);
743c0b746e5SOllivier Robert 	int spaceleft = space_left(up);
744c0b746e5SOllivier Robert 
745c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
746c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
747c0b746e5SOllivier Robert #endif
748c0b746e5SOllivier Robert 	if(spaceleft < sl) { /* Should not normally happen... */
749c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
750c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
751c0b746e5SOllivier Robert 		       sl, spaceleft);
752c0b746e5SOllivier Robert #endif
753c0b746e5SOllivier Robert 		return(0);                      /* FAILED! */
754c0b746e5SOllivier Robert 	}
755c0b746e5SOllivier Robert 
756c0b746e5SOllivier Robert 	/* Copy in the command to be sent. */
757c0b746e5SOllivier Robert 	while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
758c0b746e5SOllivier Robert 
759c0b746e5SOllivier Robert 	return(1);
760c0b746e5SOllivier Robert }
761c0b746e5SOllivier Robert 
762c0b746e5SOllivier Robert 
763c0b746e5SOllivier Robert /* Macro indicating action we will take for different quality values. */
764c0b746e5SOllivier Robert #define quality_action(q) \
765c0b746e5SOllivier Robert (((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
766c0b746e5SOllivier Robert  (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
767c0b746e5SOllivier Robert   "OK, will use clock"))
768c0b746e5SOllivier Robert 
769c0b746e5SOllivier Robert      /*
770c0b746e5SOllivier Robert  * arc_receive - receive data from the serial interface
771c0b746e5SOllivier Robert  */
772c0b746e5SOllivier Robert      static void
773c0b746e5SOllivier Robert arc_receive(
774c0b746e5SOllivier Robert 	struct recvbuf *rbufp
775c0b746e5SOllivier Robert 	)
776c0b746e5SOllivier Robert {
777c0b746e5SOllivier Robert 	register struct arcunit *up;
778c0b746e5SOllivier Robert 	struct refclockproc *pp;
779c0b746e5SOllivier Robert 	struct peer *peer;
780c0b746e5SOllivier Robert 	char c;
781c0b746e5SOllivier Robert 	int i, n, wday, month, bst, status;
782c0b746e5SOllivier Robert 	int arc_last_offset;
783c0b746e5SOllivier Robert 
784c0b746e5SOllivier Robert 	/*
785c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp
786c0b746e5SOllivier Robert 	 */
787c0b746e5SOllivier Robert 	peer = (struct peer *)rbufp->recv_srcclock;
788c0b746e5SOllivier Robert 	pp = peer->procptr;
789c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
790c0b746e5SOllivier Robert 
791c0b746e5SOllivier Robert 
792c0b746e5SOllivier Robert 	/*
793c0b746e5SOllivier Robert 	  If the command buffer is empty, and we are resyncing, insert a
794c0b746e5SOllivier Robert 	  g\r quality request into it to poll for signal quality again.
795c0b746e5SOllivier Robert 	*/
796c0b746e5SOllivier Robert 	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
797c0b746e5SOllivier Robert #ifdef DEBUG
798c0b746e5SOllivier Robert 		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
799c0b746e5SOllivier Robert #endif
800c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "g\r");
801c0b746e5SOllivier Robert 	}
802c0b746e5SOllivier Robert 
803c0b746e5SOllivier Robert 	/*
804c0b746e5SOllivier Robert 	  The `arc_last_offset' is the offset in lastcode[] of the last byte
805c0b746e5SOllivier Robert 	  received, and which we assume actually received the input
806c0b746e5SOllivier Robert 	  timestamp.
807c0b746e5SOllivier Robert 
808c0b746e5SOllivier Robert 	  (When we get round to using tty_clk and it is available, we
809c0b746e5SOllivier Robert 	  assume that we will receive the whole timecode with the
810c0b746e5SOllivier Robert 	  trailing \r, and that that \r will be timestamped.  But this
811c0b746e5SOllivier Robert 	  assumption also works if receive the characters one-by-one.)
812c0b746e5SOllivier Robert 	*/
813c0b746e5SOllivier Robert 	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
814c0b746e5SOllivier Robert 
815c0b746e5SOllivier Robert 	/*
816c0b746e5SOllivier Robert 	  We catch a timestamp iff:
817c0b746e5SOllivier Robert 
818c0b746e5SOllivier Robert 	  * The command code is `o' for a timestamp.
819c0b746e5SOllivier Robert 
820c0b746e5SOllivier Robert 	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
821c0b746e5SOllivier Robert 	  exactly char in the buffer (the command code) so that we
822c0b746e5SOllivier Robert 	  only sample the first character of the timecode as our
823c0b746e5SOllivier Robert 	  `on-time' character.
824c0b746e5SOllivier Robert 
825c0b746e5SOllivier Robert 	  * The first character in the buffer is not the echoed `\r'
826c0b746e5SOllivier Robert 	  from the `o` command (so if we are to timestamp an `\r' it
827c0b746e5SOllivier Robert 	  must not be first in the receive buffer with lencode==1.
828c0b746e5SOllivier Robert 	  (Even if we had other characters following it, we probably
829c0b746e5SOllivier Robert 	  would have a premature timestamp on the '\r'.)
830c0b746e5SOllivier Robert 
831c0b746e5SOllivier Robert 	  * We have received at least one character (I cannot imagine
832c0b746e5SOllivier Robert 	  how it could be otherwise, but anyway...).
833c0b746e5SOllivier Robert 	*/
834c0b746e5SOllivier Robert 	c = rbufp->recv_buffer[0];
835c0b746e5SOllivier Robert 	if((pp->a_lastcode[0] == 'o') &&
836c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES
837c0b746e5SOllivier Robert 	   (pp->lencode == 1) &&
838c0b746e5SOllivier Robert #endif
839c0b746e5SOllivier Robert 	   ((pp->lencode != 1) || (c != '\r')) &&
840c0b746e5SOllivier Robert 	   (arc_last_offset >= 1)) {
841c0b746e5SOllivier Robert 		/* Note that the timestamp should be corrected if >1 char rcvd. */
842c0b746e5SOllivier Robert 		l_fp timestamp;
843c0b746e5SOllivier Robert 		timestamp = rbufp->recv_time;
844c0b746e5SOllivier Robert #ifdef DEBUG
845c0b746e5SOllivier Robert 		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
846c0b746e5SOllivier Robert 			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
847c0b746e5SOllivier Robert 			       ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
848c0b746e5SOllivier Robert 			       rbufp->recv_length);
849c0b746e5SOllivier Robert 		}
850c0b746e5SOllivier Robert #endif
851c0b746e5SOllivier Robert 
852c0b746e5SOllivier Robert 		/*
853c0b746e5SOllivier Robert 		  Now correct timestamp by offset of last byte received---we
854c0b746e5SOllivier Robert 		  subtract from the receive time the delay implied by the
855c0b746e5SOllivier Robert 		  extra characters received.
856c0b746e5SOllivier Robert 
857c0b746e5SOllivier Robert 		  Reject the input if the resulting code is too long, but
858c0b746e5SOllivier Robert 		  allow for the trailing \r, normally not used but a good
859c0b746e5SOllivier Robert 		  handle for tty_clk or somesuch kernel timestamper.
860c0b746e5SOllivier Robert 		*/
861c0b746e5SOllivier Robert 		if(arc_last_offset > LENARC) {
862c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
863c0b746e5SOllivier Robert 			if(debug) {
864c0b746e5SOllivier Robert 				printf("arc: input code too long (%d cf %d); rejected.\n",
865c0b746e5SOllivier Robert 				       arc_last_offset, LENARC);
866c0b746e5SOllivier Robert 			}
867c0b746e5SOllivier Robert #endif
868c0b746e5SOllivier Robert 			pp->lencode = 0;
869c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
870c0b746e5SOllivier Robert 			return;
871c0b746e5SOllivier Robert 		}
872c0b746e5SOllivier Robert 
873c0b746e5SOllivier Robert 		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
874c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
875c0b746e5SOllivier Robert 		if(debug > 1) {
876c0b746e5SOllivier Robert 			printf(
877c0b746e5SOllivier Robert 				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
878c0b746e5SOllivier Robert 				((rbufp->recv_length > 1) ? "*** " : ""),
879c0b746e5SOllivier Robert 				rbufp->recv_length,
880c0b746e5SOllivier Robert 				arc_last_offset,
881c0b746e5SOllivier Robert 				mfptoms((unsigned long)0,
882c0b746e5SOllivier Robert 					charoffsets[arc_last_offset],
883c0b746e5SOllivier Robert 					1));
884c0b746e5SOllivier Robert 		}
885c0b746e5SOllivier Robert #endif
886c0b746e5SOllivier Robert 
887c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
888c0b746e5SOllivier Robert 		/*
889c0b746e5SOllivier Robert 		  If taking multiple samples, capture the current adjusted
890c0b746e5SOllivier Robert 		  sample iff:
891c0b746e5SOllivier Robert 
892c0b746e5SOllivier Robert 		  * No timestamp has yet been captured (it is zero), OR
893c0b746e5SOllivier Robert 
894c0b746e5SOllivier Robert 		  * This adjusted timestamp is earlier than the one already
895c0b746e5SOllivier Robert 		  captured, on the grounds that this one suffered less
896c0b746e5SOllivier Robert 		  delay in being delivered to us and is more accurate.
897c0b746e5SOllivier Robert 
898c0b746e5SOllivier Robert 		*/
899c0b746e5SOllivier Robert 		if(L_ISZERO(&(up->lastrec)) ||
900c0b746e5SOllivier Robert 		   L_ISGEQ(&(up->lastrec), &timestamp))
901c0b746e5SOllivier Robert #endif
902c0b746e5SOllivier Robert 		{
903c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
904c0b746e5SOllivier Robert 			if(debug > 1) {
905c0b746e5SOllivier Robert 				printf("arc: system timestamp captured.\n");
906c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
907c0b746e5SOllivier Robert 				if(!L_ISZERO(&(up->lastrec))) {
908c0b746e5SOllivier Robert 					l_fp diff;
909c0b746e5SOllivier Robert 					diff = up->lastrec;
910c0b746e5SOllivier Robert 					L_SUB(&diff, &timestamp);
911c0b746e5SOllivier Robert 					printf("arc: adjusted timestamp by -%sms.\n",
912c0b746e5SOllivier Robert 					       mfptoms(diff.l_i, diff.l_f, 3));
913c0b746e5SOllivier Robert 				}
914c0b746e5SOllivier Robert #endif
915c0b746e5SOllivier Robert 			}
916c0b746e5SOllivier Robert #endif
917c0b746e5SOllivier Robert 			up->lastrec = timestamp;
918c0b746e5SOllivier Robert 		}
919c0b746e5SOllivier Robert 
920c0b746e5SOllivier Robert 	}
921c0b746e5SOllivier Robert 
922c0b746e5SOllivier Robert 	/* Just in case we still have lots of rubbish in the buffer... */
923c0b746e5SOllivier Robert 	/* ...and to avoid the same timestamp being reused by mistake, */
924c0b746e5SOllivier Robert 	/* eg on receipt of the \r coming in on its own after the      */
925c0b746e5SOllivier Robert 	/* timecode.                                                   */
926c0b746e5SOllivier Robert 	if(pp->lencode >= LENARC) {
927c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
928c0b746e5SOllivier Robert 		if(debug && (rbufp->recv_buffer[0] != '\r'))
929c0b746e5SOllivier Robert 		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
930c0b746e5SOllivier Robert #endif
931c0b746e5SOllivier Robert 		pp->lencode = 0;
932c0b746e5SOllivier Robert 		return;
933c0b746e5SOllivier Robert 	}
934c0b746e5SOllivier Robert 
935c0b746e5SOllivier Robert 	/* Append input to code buffer, avoiding overflow. */
936c0b746e5SOllivier Robert 	for(i = 0; i < rbufp->recv_length; i++) {
937c0b746e5SOllivier Robert 		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
938c0b746e5SOllivier Robert 		c = rbufp->recv_buffer[i];
939c0b746e5SOllivier Robert 
940c0b746e5SOllivier Robert 		/* Drop trailing '\r's and drop `h' command echo totally. */
941c0b746e5SOllivier Robert 		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
942c0b746e5SOllivier Robert 
943c0b746e5SOllivier Robert 		/*
944c0b746e5SOllivier Robert 		  If we've just put an `o' in the lastcode[0], clear the
945c0b746e5SOllivier Robert 		  timestamp in anticipation of a timecode arriving soon.
946c0b746e5SOllivier Robert 
947c0b746e5SOllivier Robert 		  We would expect to get to process this before any of the
948c0b746e5SOllivier Robert 		  timecode arrives.
949c0b746e5SOllivier Robert 		*/
950c0b746e5SOllivier Robert 		if((c == 'o') && (pp->lencode == 1)) {
951c0b746e5SOllivier Robert 			L_CLR(&(up->lastrec));
952c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
953c0b746e5SOllivier Robert 			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
954c0b746e5SOllivier Robert #endif
955c0b746e5SOllivier Robert 		}
956c0b746e5SOllivier Robert 	}
957c0b746e5SOllivier Robert 
958c0b746e5SOllivier Robert 	/* Handle a quality message. */
959c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] == 'g') {
960c0b746e5SOllivier Robert 		int r, q;
961c0b746e5SOllivier Robert 
962c0b746e5SOllivier Robert 		if(pp->lencode < 3) { return; } /* Need more data... */
963c0b746e5SOllivier Robert 		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
964c0b746e5SOllivier Robert 		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
965c0b746e5SOllivier Robert 		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
966c0b746e5SOllivier Robert 		   ((r & 0x70) != 0x30)) {
967c0b746e5SOllivier Robert 			/* Badly formatted response. */
968c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
969c0b746e5SOllivier Robert 			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
970c0b746e5SOllivier Robert #endif
971c0b746e5SOllivier Robert 			return;
972c0b746e5SOllivier Robert 		}
973c0b746e5SOllivier Robert 		if(r == '3') { /* Only use quality value whilst sync in progress. */
974c0b746e5SOllivier Robert 			up->quality = (q & 0xf);
975c0b746e5SOllivier Robert #ifdef DEBUG
976c0b746e5SOllivier Robert 			if(debug) { printf("arc: signal quality %d.\n", up->quality); }
977c0b746e5SOllivier Robert #endif
978c0b746e5SOllivier Robert 		} else if( /* (r == '2') && */ up->resyncing) {
979c0b746e5SOllivier Robert #ifdef DEBUG
980c0b746e5SOllivier Robert 			if(debug)
981c0b746e5SOllivier Robert 			{
982c0b746e5SOllivier Robert 				printf("arc: sync finished, signal quality %d: %s\n",
983c0b746e5SOllivier Robert 				       up->quality,
984c0b746e5SOllivier Robert 				       quality_action(up->quality));
985c0b746e5SOllivier Robert 			}
986c0b746e5SOllivier Robert #endif
987c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE,
988c0b746e5SOllivier Robert 			       "ARCRON: sync finished, signal quality %d: %s",
989c0b746e5SOllivier Robert 			       up->quality,
990c0b746e5SOllivier Robert 			       quality_action(up->quality));
991c0b746e5SOllivier Robert 			up->resyncing = 0; /* Resync is over. */
992c0b746e5SOllivier Robert 
993c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
994c0b746e5SOllivier Robert 			/* Clock quality dubious; resync earlier than usual. */
995c0b746e5SOllivier Robert 			if((up->quality == QUALITY_UNKNOWN) ||
996c0b746e5SOllivier Robert 			   (up->quality < MIN_CLOCK_QUALITY_OK))
997c0b746e5SOllivier Robert 			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
998c0b746e5SOllivier Robert #endif
999c0b746e5SOllivier Robert 		}
1000c0b746e5SOllivier Robert 		pp->lencode = 0;
1001c0b746e5SOllivier Robert 		return;
1002c0b746e5SOllivier Robert 	}
1003c0b746e5SOllivier Robert 
1004c0b746e5SOllivier Robert 	/* Stop now if this is not a timecode message. */
1005c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] != 'o') {
1006c0b746e5SOllivier Robert 		pp->lencode = 0;
1007c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1008c0b746e5SOllivier Robert 		return;
1009c0b746e5SOllivier Robert 	}
1010c0b746e5SOllivier Robert 
1011c0b746e5SOllivier Robert 	/* If we don't have enough data, wait for more... */
1012c0b746e5SOllivier Robert 	if(pp->lencode < LENARC) { return; }
1013c0b746e5SOllivier Robert 
1014c0b746e5SOllivier Robert 
1015c0b746e5SOllivier Robert 	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
1016c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1017c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1018c0b746e5SOllivier Robert #endif
1019c0b746e5SOllivier Robert 
1020c0b746e5SOllivier Robert 	/* But check that we actually captured a system timestamp on it. */
1021c0b746e5SOllivier Robert 	if(L_ISZERO(&(up->lastrec))) {
1022c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1023c0b746e5SOllivier Robert 		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1024c0b746e5SOllivier Robert #endif
1025c0b746e5SOllivier Robert 		pp->lencode = 0;
1026c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1027c0b746e5SOllivier Robert 		return;
1028c0b746e5SOllivier Robert 	}
1029c0b746e5SOllivier Robert 	/*
1030c0b746e5SOllivier Robert 	  Append a mark of the clock's received signal quality for the
1031c0b746e5SOllivier Robert 	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1032c0b746e5SOllivier Robert 	  quality value to `6' for his s/w) and terminate the string for
1033c0b746e5SOllivier Robert 	  sure.  This should not go off the buffer end.
1034c0b746e5SOllivier Robert 	*/
1035c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1036c0b746e5SOllivier Robert 				       '6' : ('0' + up->quality));
1037c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1038c0b746e5SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
1039c0b746e5SOllivier Robert 
1040c0b746e5SOllivier Robert 	/* We don't use the micro-/milli- second part... */
1041c0b746e5SOllivier Robert 	pp->usec = 0;
1042c0b746e5SOllivier Robert 	pp->msec = 0;
1043c0b746e5SOllivier Robert 
1044c0b746e5SOllivier Robert 	n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d",
1045c0b746e5SOllivier Robert 		   &pp->hour, &pp->minute, &pp->second,
1046c0b746e5SOllivier Robert 		   &wday, &pp->day, &month, &pp->year, &bst, &status);
1047c0b746e5SOllivier Robert 
1048c0b746e5SOllivier Robert 	/* Validate format and numbers. */
1049c0b746e5SOllivier Robert 	if(n != 9) {
1050c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1051c0b746e5SOllivier Robert 		/* Would expect to have caught major problems already... */
1052c0b746e5SOllivier Robert 		if(debug) { printf("arc: badly formatted data.\n"); }
1053c0b746e5SOllivier Robert #endif
1054c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1055c0b746e5SOllivier Robert 		return;
1056c0b746e5SOllivier Robert 	}
1057c0b746e5SOllivier Robert 	/*
1058c0b746e5SOllivier Robert 	  Validate received values at least enough to prevent internal
1059c0b746e5SOllivier Robert 	  array-bounds problems, etc.
1060c0b746e5SOllivier Robert 	*/
1061c0b746e5SOllivier Robert 	if((pp->hour < 0) || (pp->hour > 23) ||
1062c0b746e5SOllivier Robert 	   (pp->minute < 0) || (pp->minute > 59) ||
1063c0b746e5SOllivier Robert 	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1064c0b746e5SOllivier Robert 	   (wday < 1) || (wday > 7) ||
1065c0b746e5SOllivier Robert 	   (pp->day < 1) || (pp->day > 31) ||
1066c0b746e5SOllivier Robert 	   (month < 1) || (month > 12) ||
1067c0b746e5SOllivier Robert 	   (pp->year < 0) || (pp->year > 99)) {
1068c0b746e5SOllivier Robert 		/* Data out of range. */
1069c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1070c0b746e5SOllivier Robert 		return;
1071c0b746e5SOllivier Robert 	}
1072c0b746e5SOllivier Robert 	/* Check that BST/UTC bits are the complement of one another. */
1073c0b746e5SOllivier Robert 	if(!(bst & 2) == !(bst & 4)) {
1074c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1075c0b746e5SOllivier Robert 		return;
1076c0b746e5SOllivier Robert 	}
1077c0b746e5SOllivier Robert 
1078c0b746e5SOllivier Robert 	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1079c0b746e5SOllivier Robert 
1080c0b746e5SOllivier Robert 	/* Year-2000 alert! */
1081c0b746e5SOllivier Robert 	/* Attempt to wrap 2-digit date into sensible window. */
1082c0b746e5SOllivier Robert 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
1083c0b746e5SOllivier Robert 	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
1084c0b746e5SOllivier Robert 	/*
1085c0b746e5SOllivier Robert 	  Attempt to do the right thing by screaming that the code will
1086c0b746e5SOllivier Robert 	  soon break when we get to the end of its useful life.  What a
1087c0b746e5SOllivier Robert 	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1088c0b746e5SOllivier Robert 	*/
1089c0b746e5SOllivier Robert 	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
1090c0b746e5SOllivier Robert 		/*This should get attention B^> */
1091c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,
1092c0b746e5SOllivier Robert 		       "ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1093c0b746e5SOllivier Robert 	}
1094c0b746e5SOllivier Robert #ifdef DEBUG
1095c0b746e5SOllivier Robert 	if(debug) {
1096c0b746e5SOllivier Robert 		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1097c0b746e5SOllivier Robert 		       n,
1098c0b746e5SOllivier Robert 		       pp->hour, pp->minute, pp->second,
1099c0b746e5SOllivier Robert 		       pp->day, month, pp->year, bst, status);
1100c0b746e5SOllivier Robert 	}
1101c0b746e5SOllivier Robert #endif
1102c0b746e5SOllivier Robert 
1103c0b746e5SOllivier Robert 	/*
1104c0b746e5SOllivier Robert 	  The status value tested for is not strictly supported by the
1105c0b746e5SOllivier Robert 	  clock spec since the value of bit 2 (0x4) is claimed to be
1106c0b746e5SOllivier Robert 	  undefined for MSF, yet does seem to indicate if the last resync
1107c0b746e5SOllivier Robert 	  was successful or not.
1108c0b746e5SOllivier Robert 	*/
1109c0b746e5SOllivier Robert 	pp->leap = LEAP_NOWARNING;
1110c0b746e5SOllivier Robert 	status &= 0x7;
1111c0b746e5SOllivier Robert 	if(status == 0x3) {
1112c0b746e5SOllivier Robert 		if(status != up->status)
1113c0b746e5SOllivier Robert 		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1114c0b746e5SOllivier Robert 	} else {
1115c0b746e5SOllivier Robert 		if(status != up->status) {
1116c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: signal lost");
1117c0b746e5SOllivier Robert 			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1118c0b746e5SOllivier Robert 			up->status = status;
1119c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_FAULT);
1120c0b746e5SOllivier Robert 			return;
1121c0b746e5SOllivier Robert 		}
1122c0b746e5SOllivier Robert 	}
1123c0b746e5SOllivier Robert 	up->status = status;
1124c0b746e5SOllivier Robert 
1125c0b746e5SOllivier Robert 	pp->day += moff[month - 1];
1126c0b746e5SOllivier Robert 
1127c0b746e5SOllivier Robert 	if(isleap_4(pp->year) && month > 2) { pp->day++; }	/* Y2KFixes */
1128c0b746e5SOllivier Robert 
1129c0b746e5SOllivier Robert 	/* Convert to UTC if required */
1130c0b746e5SOllivier Robert 	if(bst & 2) {
1131c0b746e5SOllivier Robert 		pp->hour--;
1132c0b746e5SOllivier Robert 		if (pp->hour < 0) {
1133c0b746e5SOllivier Robert 			pp->hour = 23;
1134c0b746e5SOllivier Robert 			pp->day--;
1135c0b746e5SOllivier Robert 			/* If we try to wrap round the year (BST on 1st Jan), reject.*/
1136c0b746e5SOllivier Robert 			if(pp->day < 0) {
1137c0b746e5SOllivier Robert 				refclock_report(peer, CEVNT_BADTIME);
1138c0b746e5SOllivier Robert 				return;
1139c0b746e5SOllivier Robert 			}
1140c0b746e5SOllivier Robert 		}
1141c0b746e5SOllivier Robert 	}
1142c0b746e5SOllivier Robert 
1143c0b746e5SOllivier Robert 	/* If clock signal quality is unknown, revert to default PRECISION...*/
1144c0b746e5SOllivier Robert 	if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; }
1145c0b746e5SOllivier Robert 	/* ...else improve precision if flag3 is set... */
1146c0b746e5SOllivier Robert 	else {
1147c0b746e5SOllivier Robert 		peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1148c0b746e5SOllivier Robert 				   HIGHPRECISION : PRECISION);
1149c0b746e5SOllivier Robert 	}
1150c0b746e5SOllivier Robert 
1151c0b746e5SOllivier Robert 	/* Notice and log any change (eg from initial defaults) for flags. */
1152c0b746e5SOllivier Robert 	if(up->saved_flags != pp->sloppyclockflag) {
1153c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1154c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1155c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1156c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1157c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1158c0b746e5SOllivier Robert 		       ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1159c0b746e5SOllivier Robert 		/* Note effects of flags changing... */
1160c0b746e5SOllivier Robert 		if(debug) {
1161c0b746e5SOllivier Robert 			printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp));
1162c0b746e5SOllivier Robert 			printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp));
1163c0b746e5SOllivier Robert 			printf("arc: PRECISION = %d.\n", peer->precision);
1164c0b746e5SOllivier Robert 		}
1165c0b746e5SOllivier Robert #endif
1166c0b746e5SOllivier Robert 		up->saved_flags = pp->sloppyclockflag;
1167c0b746e5SOllivier Robert 	}
1168c0b746e5SOllivier Robert 
1169c0b746e5SOllivier Robert 	/* Note time of last believable timestamp. */
1170c0b746e5SOllivier Robert 	pp->lastrec = up->lastrec;
1171c0b746e5SOllivier Robert 
1172c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1173c0b746e5SOllivier Robert 	/* Find out if a leap-second might just have happened...
1174c0b746e5SOllivier Robert 	   (ie is this the first hour of the first day of Jan or Jul?)
1175c0b746e5SOllivier Robert 	*/
1176c0b746e5SOllivier Robert 	if((pp->hour == 0) &&
1177c0b746e5SOllivier Robert 	   (pp->day == 1) &&
1178c0b746e5SOllivier Robert 	   ((month == 1) || (month == 7))) {
1179c0b746e5SOllivier Robert 		if(possible_leap >= 0) {
1180c0b746e5SOllivier Robert 			/* A leap may have happened, and no resync has started yet...*/
1181c0b746e5SOllivier Robert 			possible_leap = 1;
1182c0b746e5SOllivier Robert 		}
1183c0b746e5SOllivier Robert 	} else {
1184c0b746e5SOllivier Robert 		/* Definitely not leap-second territory... */
1185c0b746e5SOllivier Robert 		possible_leap = 0;
1186c0b746e5SOllivier Robert 	}
1187c0b746e5SOllivier Robert #endif
1188c0b746e5SOllivier Robert 
1189c0b746e5SOllivier Robert 	if (!refclock_process(pp)) {
1190c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
1191c0b746e5SOllivier Robert 		return;
1192c0b746e5SOllivier Robert 	}
1193c0b746e5SOllivier Robert 	refclock_receive(peer);
1194c0b746e5SOllivier Robert }
1195c0b746e5SOllivier Robert 
1196c0b746e5SOllivier Robert 
1197c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */
1198c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */
1199c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */
1200c0b746e5SOllivier Robert static  void    request_time    P((int, struct peer *));
1201c0b746e5SOllivier Robert static void
1202c0b746e5SOllivier Robert request_time(
1203c0b746e5SOllivier Robert 	int unit,
1204c0b746e5SOllivier Robert 	struct peer *peer
1205c0b746e5SOllivier Robert 	)
1206c0b746e5SOllivier Robert {
1207c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
1208c0b746e5SOllivier Robert 	register struct arcunit *up = (struct arcunit *)pp->unitptr;
1209c0b746e5SOllivier Robert #ifdef DEBUG
1210c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1211c0b746e5SOllivier Robert #endif
1212c0b746e5SOllivier Robert 	if (!send_slow(up, pp->io.fd, "o\r")) {
1213c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG
1214c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit);
1215c0b746e5SOllivier Robert #endif
1216c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1217c0b746e5SOllivier Robert 		return;
1218c0b746e5SOllivier Robert 	}
1219c0b746e5SOllivier Robert 	pp->polls++;
1220c0b746e5SOllivier Robert }
1221c0b746e5SOllivier Robert 
1222c0b746e5SOllivier Robert /*
1223c0b746e5SOllivier Robert  * arc_poll - called by the transmit procedure
1224c0b746e5SOllivier Robert  */
1225c0b746e5SOllivier Robert static void
1226c0b746e5SOllivier Robert arc_poll(
1227c0b746e5SOllivier Robert 	int unit,
1228c0b746e5SOllivier Robert 	struct peer *peer
1229c0b746e5SOllivier Robert 	)
1230c0b746e5SOllivier Robert {
1231c0b746e5SOllivier Robert 	register struct arcunit *up;
1232c0b746e5SOllivier Robert 	struct refclockproc *pp;
1233c0b746e5SOllivier Robert 	int resync_needed;              /* Should we start a resync? */
1234c0b746e5SOllivier Robert 
1235c0b746e5SOllivier Robert 	pp = peer->procptr;
1236c0b746e5SOllivier Robert 	up = (struct arcunit *)pp->unitptr;
1237c0b746e5SOllivier Robert 	pp->lencode = 0;
1238c0b746e5SOllivier Robert 	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
1239c0b746e5SOllivier Robert 
1240c0b746e5SOllivier Robert #if 0
1241c0b746e5SOllivier Robert 	/* Flush input. */
1242c0b746e5SOllivier Robert 	tcflush(pp->io.fd, TCIFLUSH);
1243c0b746e5SOllivier Robert #endif
1244c0b746e5SOllivier Robert 
1245c0b746e5SOllivier Robert 	/* Resync if our next scheduled resync time is here or has passed. */
1246c0b746e5SOllivier Robert 	resync_needed = (up->next_resync <= current_time);
1247c0b746e5SOllivier Robert 
1248c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1249c0b746e5SOllivier Robert 	/*
1250c0b746e5SOllivier Robert 	  Try to catch a potential leap-second insertion or deletion quickly.
1251c0b746e5SOllivier Robert 
1252c0b746e5SOllivier Robert 	  In addition to the normal NTP fun of clocks that don't report
1253c0b746e5SOllivier Robert 	  leap-seconds spooking their hosts, this clock does not even
1254c0b746e5SOllivier Robert 	  sample the radio sugnal the whole time, so may miss a
1255c0b746e5SOllivier Robert 	  leap-second insertion or deletion for up to a whole sample
1256c0b746e5SOllivier Robert 	  time.
1257c0b746e5SOllivier Robert 
1258c0b746e5SOllivier Robert 	  To try to minimise this effect, if in the first few minutes of
1259c0b746e5SOllivier Robert 	  the day immediately following a leap-second-insertion point
1260c0b746e5SOllivier Robert 	  (ie in the first hour of the first day of the first and sixth
1261c0b746e5SOllivier Robert 	  months), and if the last resync was in the previous day, and a
1262c0b746e5SOllivier Robert 	  resync is not already in progress, resync the clock
1263c0b746e5SOllivier Robert 	  immediately.
1264c0b746e5SOllivier Robert 
1265c0b746e5SOllivier Robert 	*/
1266c0b746e5SOllivier Robert 	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1267c0b746e5SOllivier Robert 	   (!up->resyncing)) {          /* No resync in progress yet. */
1268c0b746e5SOllivier Robert 		resync_needed = 1;
1269c0b746e5SOllivier Robert 		possible_leap = -1;          /* Prevent multiple resyncs. */
1270c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1271c0b746e5SOllivier Robert 	}
1272c0b746e5SOllivier Robert #endif
1273c0b746e5SOllivier Robert 
1274c0b746e5SOllivier Robert 	/* Do a resync if required... */
1275c0b746e5SOllivier Robert 	if(resync_needed) {
1276c0b746e5SOllivier Robert 		/* First, reset quality value to `unknown' so we can detect */
1277c0b746e5SOllivier Robert 		/* when a quality message has been responded to by this     */
1278c0b746e5SOllivier Robert 		/* being set to some other value.                           */
1279c0b746e5SOllivier Robert 		up->quality = QUALITY_UNKNOWN;
1280c0b746e5SOllivier Robert 
1281c0b746e5SOllivier Robert 		/* Note that we are resyncing... */
1282c0b746e5SOllivier Robert 		up->resyncing = 1;
1283c0b746e5SOllivier Robert 
1284c0b746e5SOllivier Robert 		/* Now actually send the resync command and an immediate poll. */
1285c0b746e5SOllivier Robert #ifdef DEBUG
1286c0b746e5SOllivier Robert 		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1287c0b746e5SOllivier Robert #endif
1288c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1289c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "h\r");
1290c0b746e5SOllivier Robert 
1291c0b746e5SOllivier Robert 		/* Schedule our next resync... */
1292c0b746e5SOllivier Robert 		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1293c0b746e5SOllivier Robert 
1294c0b746e5SOllivier Robert 		/* Drop through to request time if appropriate. */
1295c0b746e5SOllivier Robert 	}
1296c0b746e5SOllivier Robert 
1297c0b746e5SOllivier Robert 	/* If clock quality is too poor to trust, indicate a fault. */
1298c0b746e5SOllivier Robert 	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1299c0b746e5SOllivier Robert 	/* we'll cross our fingers and just hope that the thing     */
1300c0b746e5SOllivier Robert 	/* synced so quickly we did not catch it---we'll            */
1301c0b746e5SOllivier Robert 	/* double-check the clock is OK elsewhere.                  */
1302c0b746e5SOllivier Robert 	if(
1303c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1304c0b746e5SOllivier Robert 		(up->quality != QUALITY_UNKNOWN) &&
1305c0b746e5SOllivier Robert #else
1306c0b746e5SOllivier Robert 		(up->quality == QUALITY_UNKNOWN) ||
1307c0b746e5SOllivier Robert #endif
1308c0b746e5SOllivier Robert 		(up->quality < MIN_CLOCK_QUALITY_OK)) {
1309c0b746e5SOllivier Robert #ifdef DEBUG
1310c0b746e5SOllivier Robert 		if(debug) {
1311c0b746e5SOllivier Robert 			printf("arc: clock quality %d too poor.\n", up->quality);
1312c0b746e5SOllivier Robert 		}
1313c0b746e5SOllivier Robert #endif
1314c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1315c0b746e5SOllivier Robert 		return;
1316c0b746e5SOllivier Robert 	}
1317c0b746e5SOllivier Robert 	/* This is the normal case: request a timestamp. */
1318c0b746e5SOllivier Robert 	request_time(unit, peer);
1319c0b746e5SOllivier Robert }
1320c0b746e5SOllivier Robert 
1321c0b746e5SOllivier Robert #else
1322c0b746e5SOllivier Robert int refclock_arc_bs;
1323c0b746e5SOllivier Robert #endif
1324