xref: /freebsd/contrib/ntp/ntpd/refclock_arc.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
29c2daa00SOllivier Robert  * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert 
5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
6c0b746e5SOllivier Robert #include <config.h>
7c0b746e5SOllivier Robert #endif
8c0b746e5SOllivier Robert 
92b15cb3dSCy Schubert #include "ntp_types.h"
102b15cb3dSCy Schubert 
11c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
12ea906c41SOllivier Robert 
139c2daa00SOllivier Robert static const char arc_version[] = { "V1.3 2003/02/21" };
14c0b746e5SOllivier Robert 
159c2daa00SOllivier Robert /* define PRE_NTP420 for compatibility to previous versions of NTP (at least
169c2daa00SOllivier Robert    to 4.1.0 */
179c2daa00SOllivier Robert #undef PRE_NTP420
18c0b746e5SOllivier Robert 
19c0b746e5SOllivier Robert #ifndef ARCRON_NOT_KEEN
20c0b746e5SOllivier Robert #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
21c0b746e5SOllivier Robert #endif
22c0b746e5SOllivier Robert 
23c0b746e5SOllivier Robert #ifndef ARCRON_NOT_MULTIPLE_SAMPLES
24c0b746e5SOllivier Robert #define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
25c0b746e5SOllivier Robert #endif
26c0b746e5SOllivier Robert 
27c0b746e5SOllivier Robert #ifndef ARCRON_NOT_LEAPSECOND_KEEN
28c0b746e5SOllivier Robert #ifndef ARCRON_LEAPSECOND_KEEN
29c0b746e5SOllivier Robert #undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
30c0b746e5SOllivier Robert #endif
31c0b746e5SOllivier Robert #endif
32c0b746e5SOllivier Robert 
33c0b746e5SOllivier Robert /*
34c0b746e5SOllivier Robert Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
35c0b746e5SOllivier Robert Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
369c2daa00SOllivier Robert Modifications by Paul Alfille, <palfille@partners.org>, 2003.
379c2daa00SOllivier Robert Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
38ea906c41SOllivier Robert Modifications by Nigel Roles <nigel@9fs.org>, 2003.
399c2daa00SOllivier Robert 
40c0b746e5SOllivier Robert 
41c0b746e5SOllivier Robert THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
42c0b746e5SOllivier Robert YOUR OWN RISK.
43c0b746e5SOllivier Robert 
44c0b746e5SOllivier Robert Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
45c0b746e5SOllivier Robert 
46c0b746e5SOllivier Robert Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
47c0b746e5SOllivier Robert 
48c0b746e5SOllivier Robert This code may be freely copied and used and incorporated in other
49c0b746e5SOllivier Robert systems providing the disclaimer and notice of authorship are
50c0b746e5SOllivier Robert reproduced.
51c0b746e5SOllivier Robert 
52c0b746e5SOllivier Robert -------------------------------------------------------------------------------
53c0b746e5SOllivier Robert 
54ea906c41SOllivier Robert Nigel's notes:
55ea906c41SOllivier Robert 
56ea906c41SOllivier Robert 1) Called tcgetattr() before modifying, so that fields correctly initialised
57ea906c41SOllivier Robert    for all operating systems
58ea906c41SOllivier Robert 
59ea906c41SOllivier Robert 2) Altered parsing of timestamp line so that it copes with fields which are
60ea906c41SOllivier Robert    not always ASCII digits (e.g. status field when battery low)
61ea906c41SOllivier Robert 
62ea906c41SOllivier Robert -------------------------------------------------------------------------------
63ea906c41SOllivier Robert 
649c2daa00SOllivier Robert Christopher's notes:
659c2daa00SOllivier Robert 
669c2daa00SOllivier Robert MAJOR CHANGES SINCE V1.2
679c2daa00SOllivier Robert ========================
689c2daa00SOllivier Robert  1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
699c2daa00SOllivier Robert     2001-02-17 comp.protocols.time.ntp
709c2daa00SOllivier Robert 
719c2daa00SOllivier Robert  2) Added WWVB support via clock mode command, localtime/UTC time configured
729c2daa00SOllivier Robert     via flag1=(0=UTC, 1=localtime)
739c2daa00SOllivier Robert 
749c2daa00SOllivier Robert  3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
759c2daa00SOllivier Robert 
769c2daa00SOllivier Robert  4) Added simplified conversion from localtime to UTC with dst/bst translation
779c2daa00SOllivier Robert 
789c2daa00SOllivier Robert  5) Added average signal quality poll
799c2daa00SOllivier Robert 
809c2daa00SOllivier Robert  6) Fixed a badformat error when no code is available due to stripping
819c2daa00SOllivier Robert     \n & \r's
829c2daa00SOllivier Robert 
839c2daa00SOllivier Robert  7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
849c2daa00SOllivier Robert     routine
859c2daa00SOllivier Robert 
869c2daa00SOllivier Robert  8) Lots of code cleanup, including standardized DEBUG macros and removal
879c2daa00SOllivier Robert     of unused code
889c2daa00SOllivier Robert 
899c2daa00SOllivier Robert -------------------------------------------------------------------------------
909c2daa00SOllivier Robert 
91c0b746e5SOllivier Robert Author's original note:
92c0b746e5SOllivier Robert 
93c0b746e5SOllivier Robert I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
94c0b746e5SOllivier Robert 
95c0b746e5SOllivier Robert It works (after a fashion) on both Solaris-1 and Solaris-2.
96c0b746e5SOllivier Robert 
97c0b746e5SOllivier Robert I am currently using ntp3-5.85.  I have been running the code for
98c0b746e5SOllivier Robert about 7 months without any problems.  Even coped with the change to BST!
99c0b746e5SOllivier Robert 
100c0b746e5SOllivier Robert I had to do some funky things to read from the clock because it uses the
101c0b746e5SOllivier Robert power from the receive lines to drive the transmit lines.  This makes the
102c0b746e5SOllivier Robert code look a bit stupid but it works.  I also had to put in some delays to
103c0b746e5SOllivier Robert allow for the turnaround time from receive to transmit.  These delays
104c0b746e5SOllivier Robert are between characters when requesting a time stamp so that shouldn't affect
105c0b746e5SOllivier Robert the results too drastically.
106c0b746e5SOllivier Robert 
107c0b746e5SOllivier Robert ...
108c0b746e5SOllivier Robert 
109c0b746e5SOllivier Robert The bottom line is that it works but could easily be improved.  You are
110c0b746e5SOllivier Robert free to do what you will with the code.  I haven't been able to determine
111c0b746e5SOllivier Robert how good the clock is.  I think that this requires a known good clock
112c0b746e5SOllivier Robert to compare it against.
113c0b746e5SOllivier Robert 
114c0b746e5SOllivier Robert -------------------------------------------------------------------------------
115c0b746e5SOllivier Robert 
116c0b746e5SOllivier Robert Damon's notes for adjustments:
117c0b746e5SOllivier Robert 
118c0b746e5SOllivier Robert MAJOR CHANGES SINCE V1.0
119c0b746e5SOllivier Robert ========================
120c0b746e5SOllivier Robert  1) Removal of pollcnt variable that made the clock go permanently
121c0b746e5SOllivier Robert     off-line once two time polls failed to gain responses.
122c0b746e5SOllivier Robert 
123c0b746e5SOllivier Robert  2) Avoiding (at least on Solaris-2) terminal becoming the controlling
124c0b746e5SOllivier Robert     terminal of the process when we do a low-level open().
125c0b746e5SOllivier Robert 
126c0b746e5SOllivier Robert  3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
127c0b746e5SOllivier Robert     defined) to try to resync quickly after a potential leap-second
128c0b746e5SOllivier Robert     insertion or deletion.
129c0b746e5SOllivier Robert 
130c0b746e5SOllivier Robert  4) Code significantly slimmer at run-time than V1.0.
131c0b746e5SOllivier Robert 
132c0b746e5SOllivier Robert 
133c0b746e5SOllivier Robert GENERAL
134c0b746e5SOllivier Robert =======
135c0b746e5SOllivier Robert 
136c0b746e5SOllivier Robert  1) The C preprocessor symbol to have the clock built has been changed
137c0b746e5SOllivier Robert     from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
138c0b746e5SOllivier Robert     possiblity of clashes with other symbols in the future.
139c0b746e5SOllivier Robert 
140c0b746e5SOllivier Robert  2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
141c0b746e5SOllivier Robert 
142c0b746e5SOllivier Robert      a) The ARC documentation claims the internal clock is (only)
143c0b746e5SOllivier Robert 	accurate to about 20ms relative to Rugby (plus there must be
144c0b746e5SOllivier Robert 	noticable drift and delay in the ms range due to transmission
145c0b746e5SOllivier Robert 	delays and changing atmospheric effects).  This clock is not
146c0b746e5SOllivier Robert 	designed for ms accuracy as NTP has spoilt us all to expect.
147c0b746e5SOllivier Robert 
148c0b746e5SOllivier Robert      b) The clock oscillator looks like a simple uncompensated quartz
149c0b746e5SOllivier Robert 	crystal of the sort used in digital watches (ie 32768Hz) which
150c0b746e5SOllivier Robert 	can have large temperature coefficients and drifts; it is not
151c0b746e5SOllivier Robert 	clear if this oscillator is properly disciplined to the MSF
152c0b746e5SOllivier Robert 	transmission, but as the default is to resync only once per
153c0b746e5SOllivier Robert 	*day*, we can imagine that it is not, and is free-running.  We
154c0b746e5SOllivier Robert 	can minimise drift by resyncing more often (at the cost of
155c0b746e5SOllivier Robert 	reduced battery life), but drift/wander may still be
156c0b746e5SOllivier Robert 	significant.
157c0b746e5SOllivier Robert 
158c0b746e5SOllivier Robert      c) Note that the bit time of 3.3ms adds to the potential error in
159c0b746e5SOllivier Robert 	the the clock timestamp, since the bit clock of the serial link
160c0b746e5SOllivier Robert 	may effectively be free-running with respect to the host clock
161c0b746e5SOllivier Robert 	and the MSF clock.  Actually, the error is probably 1/16th of
162c0b746e5SOllivier Robert 	the above, since the input data is probably sampled at at least
163c0b746e5SOllivier Robert 	16x the bit rate.
164c0b746e5SOllivier Robert 
165c0b746e5SOllivier Robert     By keeping the clock marked as not very precise, it will have a
166c0b746e5SOllivier Robert     fairly large dispersion, and thus will tend to be used as a
167c0b746e5SOllivier Robert     `backup' time source and sanity checker, which this clock is
168c0b746e5SOllivier Robert     probably ideal for.  For an isolated network without other time
169c0b746e5SOllivier Robert     sources, this clock can probably be expected to provide *much*
170c0b746e5SOllivier Robert     better than 1s accuracy, which will be fine.
171c0b746e5SOllivier Robert 
172c0b746e5SOllivier Robert     By default, PRECISION is set to -4, but experience, especially at a
173c0b746e5SOllivier Robert     particular geographic location with a particular clock, may allow
174c0b746e5SOllivier Robert     this to be altered to -5.  (Note that skews of +/- 10ms are to be
175c0b746e5SOllivier Robert     expected from the clock from time-to-time.)  This improvement of
176c0b746e5SOllivier Robert     reported precision can be instigated by setting flag3 to 1, though
177c0b746e5SOllivier Robert     the PRECISION will revert to the normal value while the clock
178c0b746e5SOllivier Robert     signal quality is unknown whatever the flag3 setting.
179c0b746e5SOllivier Robert 
180c0b746e5SOllivier Robert     IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
181c0b746e5SOllivier Robert     ANY RESIDUAL SKEW, eg:
182c0b746e5SOllivier Robert 
183c0b746e5SOllivier Robert 	server 127.127.27.0 # ARCRON MSF radio clock unit 0.
184c0b746e5SOllivier Robert 	# Fudge timestamps by about 20ms.
185c0b746e5SOllivier Robert 	fudge 127.127.27.0 time1 0.020
186c0b746e5SOllivier Robert 
187c0b746e5SOllivier Robert     You will need to observe your system's behaviour, assuming you have
188c0b746e5SOllivier Robert     some other NTP source to compare it with, to work out what the
189c0b746e5SOllivier Robert     fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
190c0b746e5SOllivier Robert     my MSF clock with my distance from the MSF transmitter, +20ms
191c0b746e5SOllivier Robert     seemed about right, after some observation.
192c0b746e5SOllivier Robert 
193c0b746e5SOllivier Robert  3) REFID has been made "MSFa" to reflect the MSF time source and the
194c0b746e5SOllivier Robert     ARCRON receiver.
195c0b746e5SOllivier Robert 
196c0b746e5SOllivier Robert  4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
197c0b746e5SOllivier Robert     forcing a resync since the last attempt.  This is picked to give a
198c0b746e5SOllivier Robert     little less than an hour between resyncs and to try to avoid
199c0b746e5SOllivier Robert     clashing with any regular event at a regular time-past-the-hour
200c0b746e5SOllivier Robert     which might cause systematic errors.
201c0b746e5SOllivier Robert 
202c0b746e5SOllivier Robert     The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
203c0b746e5SOllivier Robert     running down its batteries unnecesarily if ntpd is going to crash
204c0b746e5SOllivier Robert     or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
205c0b746e5SOllivier Robert     then this period is long enough for (with normal polling rates)
206c0b746e5SOllivier Robert     enough time samples to have been taken to allow ntpd to sync to
207c0b746e5SOllivier Robert     the clock before the interruption for the clock to resync to MSF.
208c0b746e5SOllivier Robert     This avoids ntpd syncing to another peer first and then
209c0b746e5SOllivier Robert     almost immediately hopping to the MSF clock.
210c0b746e5SOllivier Robert 
211c0b746e5SOllivier Robert     The RETRY_RESYNC_TIME is used before rescheduling a resync after a
212c0b746e5SOllivier Robert     resync failed to reveal a statisfatory signal quality (too low or
213c0b746e5SOllivier Robert     unknown).
214c0b746e5SOllivier Robert 
215c0b746e5SOllivier Robert  5) The clock seems quite jittery, so I have increased the
216c0b746e5SOllivier Robert     median-filter size from the typical (previous) value of 3.  I
217c0b746e5SOllivier Robert     discard up to half the results in the filter.  It looks like maybe
218c0b746e5SOllivier Robert     1 sample in 10 or so (maybe less) is a spike, so allow the median
219c0b746e5SOllivier Robert     filter to discard at least 10% of its entries or 1 entry, whichever
220c0b746e5SOllivier Robert     is greater.
221c0b746e5SOllivier Robert 
222c0b746e5SOllivier Robert  6) Sleeping *before* each character sent to the unit to allow required
223c0b746e5SOllivier Robert     inter-character time but without introducting jitter and delay in
224c0b746e5SOllivier Robert     handling the response if possible.
225c0b746e5SOllivier Robert 
226c0b746e5SOllivier Robert  7) If the flag ARCRON_KEEN is defined, take time samples whenever
227c0b746e5SOllivier Robert     possible, even while resyncing, etc.  We rely, in this case, on the
228c0b746e5SOllivier Robert     clock always giving us a reasonable time or else telling us in the
229c0b746e5SOllivier Robert     status byte at the end of the timestamp that it failed to sync to
230c0b746e5SOllivier Robert     MSF---thus we should never end up syncing to completely the wrong
231c0b746e5SOllivier Robert     time.
232c0b746e5SOllivier Robert 
233c0b746e5SOllivier Robert  8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
234c0b746e5SOllivier Robert     refclock median-filter routines to get round small bug in 3-5.90
235c0b746e5SOllivier Robert     code which does not return the median offset. XXX Removed this
236c0b746e5SOllivier Robert     bit due NTP Version 4 upgrade - dlm.
237c0b746e5SOllivier Robert 
238c0b746e5SOllivier Robert  9) We would appear to have a year-2000 problem with this clock since
239c0b746e5SOllivier Robert     it returns only the two least-significant digits of the year.  But
240c0b746e5SOllivier Robert     ntpd ignores the year and uses the local-system year instead, so
241c0b746e5SOllivier Robert     this is in fact not a problem.  Nevertheless, we attempt to do a
242c0b746e5SOllivier Robert     sensible thing with the dates, wrapping them into a 100-year
243c0b746e5SOllivier Robert     window.
244c0b746e5SOllivier Robert 
245c0b746e5SOllivier Robert  10)Logs stats information that can be used by Derek's Tcl/Tk utility
246c0b746e5SOllivier Robert     to show the status of the clock.
247c0b746e5SOllivier Robert 
248c0b746e5SOllivier Robert  11)The clock documentation insists that the number of bits per
249c0b746e5SOllivier Robert     character to be sent to the clock, and sent by it, is 11, including
250c0b746e5SOllivier Robert     one start bit and two stop bits.  The data format is either 7+even
251c0b746e5SOllivier Robert     or 8+none.
252c0b746e5SOllivier Robert 
253c0b746e5SOllivier Robert 
254c0b746e5SOllivier Robert TO-DO LIST
255c0b746e5SOllivier Robert ==========
256c0b746e5SOllivier Robert 
257c0b746e5SOllivier Robert   * Eliminate use of scanf(), and maybe sprintf().
258c0b746e5SOllivier Robert 
259c0b746e5SOllivier Robert   * Allow user setting of resync interval to trade battery life for
260c0b746e5SOllivier Robert     accuracy; maybe could be done via fudge factor or unit number.
261c0b746e5SOllivier Robert 
262c0b746e5SOllivier Robert   * Possibly note the time since the last resync of the MSF clock to
263c0b746e5SOllivier Robert     MSF as the age of the last reference timestamp, ie trust the
264c0b746e5SOllivier Robert     clock's oscillator not very much...
265c0b746e5SOllivier Robert 
266c0b746e5SOllivier Robert   * Add very slow auto-adjustment up to a value of +/- time2 to correct
267c0b746e5SOllivier Robert     for long-term errors in the clock value (time2 defaults to 0 so the
268c0b746e5SOllivier Robert     correction would be disabled by default).
269c0b746e5SOllivier Robert 
270c0b746e5SOllivier Robert   * Consider trying to use the tty_clk/ppsclock support.
271c0b746e5SOllivier Robert 
272c0b746e5SOllivier Robert   * Possibly use average or maximum signal quality reported during
273c0b746e5SOllivier Robert     resync, rather than just the last one, which may be atypical.
274c0b746e5SOllivier Robert 
275c0b746e5SOllivier Robert */
276c0b746e5SOllivier Robert 
277c0b746e5SOllivier Robert 
278c0b746e5SOllivier Robert /* Notes for HKW Elektronik GmBH Radio clock driver */
279c0b746e5SOllivier Robert /* Author Lyndon David, Sentinet Ltd, Feb 1997      */
280c0b746e5SOllivier Robert /* These notes seem also to apply usefully to the ARCRON clock. */
281c0b746e5SOllivier Robert 
282c0b746e5SOllivier Robert /* The HKW clock module is a radio receiver tuned into the Rugby */
283c0b746e5SOllivier Robert /* MSF time signal tranmitted on 60 kHz. The clock module connects */
284c0b746e5SOllivier Robert /* to the computer via a serial line and transmits the time encoded */
285c0b746e5SOllivier Robert /* in 15 bytes at 300 baud 7 bits two stop bits even parity */
286c0b746e5SOllivier Robert 
287c0b746e5SOllivier Robert /* Clock communications, from the datasheet */
288c0b746e5SOllivier Robert /* All characters sent to the clock are echoed back to the controlling */
289c0b746e5SOllivier Robert /* device. */
290c0b746e5SOllivier Robert /* Transmit time/date information */
291c0b746e5SOllivier Robert /* syntax ASCII o<cr> */
292c0b746e5SOllivier Robert /* Character o may be replaced if neccesary by a character whose code */
293c0b746e5SOllivier Robert /* contains the lowest four bits f(hex) eg */
294c0b746e5SOllivier Robert /* syntax binary: xxxx1111 00001101 */
295c0b746e5SOllivier Robert 
296c0b746e5SOllivier Robert /* DHD note:
297c0b746e5SOllivier Robert You have to wait for character echo + 10ms before sending next character.
298c0b746e5SOllivier Robert */
299c0b746e5SOllivier Robert 
300c0b746e5SOllivier Robert /* The clock replies to this command with a sequence of 15 characters */
301c0b746e5SOllivier Robert /* which contain the complete time and a final <cr> making 16 characters */
302c0b746e5SOllivier Robert /* in total. */
303c0b746e5SOllivier Robert /* The RC computer clock will not reply immediately to this command because */
304c0b746e5SOllivier Robert /* the start bit edge of the first reply character marks the beginning of */
305c0b746e5SOllivier Robert /* the second. So the RC Computer Clock will reply to this command at the */
306c0b746e5SOllivier Robert /* start of the next second */
307c0b746e5SOllivier Robert /* The characters have the following meaning */
308c0b746e5SOllivier Robert /* 1. hours tens   */
309c0b746e5SOllivier Robert /* 2. hours units  */
310c0b746e5SOllivier Robert /* 3. minutes tens */
311c0b746e5SOllivier Robert /* 4. minutes units */
312c0b746e5SOllivier Robert /* 5. seconds tens  */
313c0b746e5SOllivier Robert /* 6. seconds units */
314c0b746e5SOllivier Robert /* 7. day of week 1-monday 7-sunday */
315c0b746e5SOllivier Robert /* 8. day of month tens */
316c0b746e5SOllivier Robert /* 9. day of month units */
317c0b746e5SOllivier Robert /* 10. month tens */
318c0b746e5SOllivier Robert /* 11. month units */
319c0b746e5SOllivier Robert /* 12. year tens */
320c0b746e5SOllivier Robert /* 13. year units */
321c0b746e5SOllivier Robert /* 14. BST/UTC status */
322c0b746e5SOllivier Robert /*	bit 7	parity */
323c0b746e5SOllivier Robert /*	bit 6	always 0 */
324c0b746e5SOllivier Robert /*	bit 5	always 1 */
325c0b746e5SOllivier Robert /*	bit 4	always 1 */
326c0b746e5SOllivier Robert /*	bit 3	always 0 */
327c0b746e5SOllivier Robert /*	bit 2	=1 if UTC is in effect, complementary to the BST bit */
328c0b746e5SOllivier Robert /*	bit 1	=1 if BST is in effect, according to the BST bit     */
329c0b746e5SOllivier Robert /*	bit 0	BST/UTC change impending bit=1 in case of change impending */
330c0b746e5SOllivier Robert /* 15. status */
331c0b746e5SOllivier Robert /*	bit 7	parity */
332c0b746e5SOllivier Robert /*	bit 6	always 0 */
333c0b746e5SOllivier Robert /*	bit 5	always 1 */
334c0b746e5SOllivier Robert /*	bit 4	always 1 */
335c0b746e5SOllivier Robert /*	bit 3	=1 if low battery is detected */
336c0b746e5SOllivier Robert /*	bit 2	=1 if the very last reception attempt failed and a valid */
337c0b746e5SOllivier Robert /*		time information already exists (bit0=1) */
338c0b746e5SOllivier Robert /*		=0 if the last reception attempt was successful */
339c0b746e5SOllivier Robert /*	bit 1	=1 if at least one reception since 2:30 am was successful */
340c0b746e5SOllivier Robert /*		=0 if no reception attempt since 2:30 am was successful */
341c0b746e5SOllivier Robert /*	bit 0	=1 if the RC Computer Clock contains valid time information */
342c0b746e5SOllivier Robert /*		This bit is zero after reset and one after the first */
343c0b746e5SOllivier Robert /*		successful reception attempt */
344c0b746e5SOllivier Robert 
345c0b746e5SOllivier Robert /* DHD note:
346c0b746e5SOllivier Robert Also note g<cr> command which confirms that a resync is in progress, and
347c0b746e5SOllivier Robert if so what signal quality (0--5) is available.
348c0b746e5SOllivier Robert Also note h<cr> command which starts a resync to MSF signal.
349c0b746e5SOllivier Robert */
350c0b746e5SOllivier Robert 
351c0b746e5SOllivier Robert 
352224ba2bdSOllivier Robert #include "ntpd.h"
353224ba2bdSOllivier Robert #include "ntp_io.h"
354224ba2bdSOllivier Robert #include "ntp_refclock.h"
3559c2daa00SOllivier Robert #include "ntp_calendar.h"
356224ba2bdSOllivier Robert #include "ntp_stdlib.h"
357c0b746e5SOllivier Robert 
358c0b746e5SOllivier Robert #include <stdio.h>
359c0b746e5SOllivier Robert #include <ctype.h>
360c0b746e5SOllivier Robert 
361c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS)
362c0b746e5SOllivier Robert #include <sgtty.h>
363c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */
364c0b746e5SOllivier Robert 
365c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS)
366c0b746e5SOllivier Robert #include <termio.h>
367c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */
368c0b746e5SOllivier Robert 
369c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS)
370c0b746e5SOllivier Robert #include <termios.h>
371c0b746e5SOllivier Robert #endif
372c0b746e5SOllivier Robert 
373c0b746e5SOllivier Robert /*
3749c2daa00SOllivier Robert  * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
375c0b746e5SOllivier Robert  */
376c0b746e5SOllivier Robert 
377c0b746e5SOllivier Robert /*
378c0b746e5SOllivier Robert  * Interface definitions
379c0b746e5SOllivier Robert  */
380c0b746e5SOllivier Robert #define DEVICE		"/dev/arc%d"	/* Device name and unit. */
381c0b746e5SOllivier Robert #define SPEED		B300		/* UART speed (300 baud) */
382c0b746e5SOllivier Robert #define PRECISION	(-4)		/* Precision  (~63 ms). */
383c0b746e5SOllivier Robert #define HIGHPRECISION	(-5)		/* If things are going well... */
384c0b746e5SOllivier Robert #define REFID		"MSFa"		/* Reference ID. */
3859c2daa00SOllivier Robert #define REFID_MSF	"MSF"		/* Reference ID. */
3869c2daa00SOllivier Robert #define REFID_DCF77	"DCF"		/* Reference ID. */
3879c2daa00SOllivier Robert #define REFID_WWVB	"WWVB"		/* Reference ID. */
3889c2daa00SOllivier Robert #define DESCRIPTION	"ARCRON MSF/DCF/WWVB Receiver"
389c0b746e5SOllivier Robert 
3909c2daa00SOllivier Robert #ifdef PRE_NTP420
3919c2daa00SOllivier Robert #define MODE ttlmax
3929c2daa00SOllivier Robert #else
3939c2daa00SOllivier Robert #define MODE ttl
3949c2daa00SOllivier Robert #endif
395c0b746e5SOllivier Robert 
396c0b746e5SOllivier Robert #define LENARC		16		/* Format `o' timecode length. */
397c0b746e5SOllivier Robert 
398c0b746e5SOllivier Robert #define BITSPERCHAR	11		/* Bits per character. */
399c0b746e5SOllivier Robert #define BITTIME		0x0DA740E	/* Time for 1 bit at 300bps. */
400c0b746e5SOllivier Robert #define CHARTIME10	0x8888888	/* Time for 10-bit char at 300bps. */
401c0b746e5SOllivier Robert #define CHARTIME11	0x962FC96	/* Time for 11-bit char at 300bps. */
402c0b746e5SOllivier Robert #define CHARTIME			/* Time for char at 300bps. */ \
403c0b746e5SOllivier Robert ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
404c0b746e5SOllivier Robert 				       (BITSPERCHAR * BITTIME) ) )
405c0b746e5SOllivier Robert 
406c0b746e5SOllivier Robert      /* Allow for UART to accept char half-way through final stop bit. */
4072b15cb3dSCy Schubert #define INITIALOFFSET ((u_int32)(-BITTIME/2))
408c0b746e5SOllivier Robert 
409c0b746e5SOllivier Robert      /*
410c0b746e5SOllivier Robert     charoffsets[x] is the time after the start of the second that byte
411c0b746e5SOllivier Robert     x (with the first byte being byte 1) is received by the UART,
412c0b746e5SOllivier Robert     assuming that the initial edge of the start bit of the first byte
413c0b746e5SOllivier Robert     is on-time.  The values are represented as the fractional part of
414c0b746e5SOllivier Robert     an l_fp.
415c0b746e5SOllivier Robert 
416c0b746e5SOllivier Robert     We store enough values to have the offset of each byte including
417c0b746e5SOllivier Robert     the trailing \r, on the assumption that the bytes follow one
418c0b746e5SOllivier Robert     another without gaps.
419c0b746e5SOllivier Robert     */
420c0b746e5SOllivier Robert      static const u_int32 charoffsets[LENARC+1] = {
421c0b746e5SOllivier Robert #if BITSPERCHAR == 11 /* Usual case. */
422c0b746e5SOllivier Robert 	     /* Offsets computed as accurately as possible... */
423c0b746e5SOllivier Robert 	     0,
424c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
425c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
426c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
427c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
428c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
429c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
430c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
431c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
432c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
433c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
434c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
435c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
436c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
437c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
438c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
439c0b746e5SOllivier Robert 	     INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
440c0b746e5SOllivier Robert #else
441c0b746e5SOllivier Robert 	     /* Offsets computed with a small rounding error... */
442c0b746e5SOllivier Robert 	     0,
443c0b746e5SOllivier Robert 	     INITIALOFFSET +  1 * CHARTIME,
444c0b746e5SOllivier Robert 	     INITIALOFFSET +  2 * CHARTIME,
445c0b746e5SOllivier Robert 	     INITIALOFFSET +  3 * CHARTIME,
446c0b746e5SOllivier Robert 	     INITIALOFFSET +  4 * CHARTIME,
447c0b746e5SOllivier Robert 	     INITIALOFFSET +  5 * CHARTIME,
448c0b746e5SOllivier Robert 	     INITIALOFFSET +  6 * CHARTIME,
449c0b746e5SOllivier Robert 	     INITIALOFFSET +  7 * CHARTIME,
450c0b746e5SOllivier Robert 	     INITIALOFFSET +  8 * CHARTIME,
451c0b746e5SOllivier Robert 	     INITIALOFFSET +  9 * CHARTIME,
452c0b746e5SOllivier Robert 	     INITIALOFFSET + 10 * CHARTIME,
453c0b746e5SOllivier Robert 	     INITIALOFFSET + 11 * CHARTIME,
454c0b746e5SOllivier Robert 	     INITIALOFFSET + 12 * CHARTIME,
455c0b746e5SOllivier Robert 	     INITIALOFFSET + 13 * CHARTIME,
456c0b746e5SOllivier Robert 	     INITIALOFFSET + 14 * CHARTIME,
457c0b746e5SOllivier Robert 	     INITIALOFFSET + 15 * CHARTIME,
458c0b746e5SOllivier Robert 	     INITIALOFFSET + 16 * CHARTIME
459c0b746e5SOllivier Robert #endif
460c0b746e5SOllivier Robert      };
461c0b746e5SOllivier Robert 
462c0b746e5SOllivier Robert #define DEFAULT_RESYNC_TIME  (57*60)	/* Gap between resync attempts (s). */
463c0b746e5SOllivier Robert #define RETRY_RESYNC_TIME    (27*60)	/* Gap to emergency resync attempt. */
464c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
465c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 500	/* Delay before first resync. */
466c0b746e5SOllivier Robert #else
467c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 50		/* Delay before first resync. */
468c0b746e5SOllivier Robert #endif
469c0b746e5SOllivier Robert 
470c0b746e5SOllivier Robert      static const int moff[12] =
471c0b746e5SOllivier Robert { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
472c0b746e5SOllivier Robert /* Flags for a raw open() of the clock serial device. */
473c0b746e5SOllivier Robert #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
474c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR | O_NOCTTY)
475c0b746e5SOllivier Robert #else		/* Oh well, it may not matter... */
476c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR)
477c0b746e5SOllivier Robert #endif
478c0b746e5SOllivier Robert 
479c0b746e5SOllivier Robert 
480c0b746e5SOllivier Robert /* Length of queue of command bytes to be sent. */
481c0b746e5SOllivier Robert #define CMDQUEUELEN 4			/* Enough for two cmds + each \r. */
482c0b746e5SOllivier Robert /* Queue tick time; interval in seconds between chars taken off queue. */
483c0b746e5SOllivier Robert /* Must be >= 2 to allow o\r response to come back uninterrupted. */
484c0b746e5SOllivier Robert #define QUEUETICK   2			/* Allow o\r reply to finish. */
485c0b746e5SOllivier Robert 
486c0b746e5SOllivier Robert /*
487c0b746e5SOllivier Robert  * ARC unit control structure
488c0b746e5SOllivier Robert  */
489c0b746e5SOllivier Robert struct arcunit {
490c0b746e5SOllivier Robert 	l_fp lastrec;	    /* Time tag for the receive time (system). */
491c0b746e5SOllivier Robert 	int status;	    /* Clock status. */
492c0b746e5SOllivier Robert 
493c0b746e5SOllivier Robert 	int quality;	    /* Quality of reception 0--5 for unit. */
494c0b746e5SOllivier Robert 	/* We may also use the values -1 or 6 internally. */
4959c2daa00SOllivier Robert 	u_long quality_stamp; /* Next time to reset quality average. */
496c0b746e5SOllivier Robert 
497c0b746e5SOllivier Robert 	u_long next_resync; /* Next resync time (s) compared to current_time. */
498c0b746e5SOllivier Robert 	int resyncing;	    /* Resync in progress if true. */
499c0b746e5SOllivier Robert 
500c0b746e5SOllivier Robert 	/* In the outgoing queue, cmdqueue[0] is next to be sent. */
501c0b746e5SOllivier Robert 	char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
502c0b746e5SOllivier Robert 
503c0b746e5SOllivier Robert 	u_long saved_flags; /* Saved fudge flags. */
504c0b746e5SOllivier Robert };
5059c2daa00SOllivier Robert 
506c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
507c0b746e5SOllivier Robert /* The flag `possible_leap' is set non-zero when any MSF unit
508c0b746e5SOllivier Robert        thinks a leap-second may have happened.
509c0b746e5SOllivier Robert 
510c0b746e5SOllivier Robert        Set whenever we receive a valid time sample in the first hour of
511c0b746e5SOllivier Robert        the first day of the first/seventh months.
512c0b746e5SOllivier Robert 
513c0b746e5SOllivier Robert        Outside the special hour this value is unconditionally set
514c0b746e5SOllivier Robert        to zero by the receive routine.
515c0b746e5SOllivier Robert 
516c0b746e5SOllivier Robert        On finding itself in this timeslot, as long as the value is
517c0b746e5SOllivier Robert        non-negative, the receive routine sets it to a positive value to
518c0b746e5SOllivier Robert        indicate a resync to MSF should be performed.
519c0b746e5SOllivier Robert 
520c0b746e5SOllivier Robert        In the poll routine, if this value is positive and we are not
521c0b746e5SOllivier Robert        already resyncing (eg from a sync that started just before
522c0b746e5SOllivier Robert        midnight), start resyncing and set this value negative to
523c0b746e5SOllivier Robert        indicate that a leap-triggered resync has been started.  Having
524c0b746e5SOllivier Robert        set this negative prevents the receive routine setting it
525c0b746e5SOllivier Robert        positive and thus prevents multiple resyncs during the witching
526c0b746e5SOllivier Robert        hour.
527c0b746e5SOllivier Robert      */
528c0b746e5SOllivier Robert static int possible_leap = 0;       /* No resync required by default. */
529c0b746e5SOllivier Robert #endif
530c0b746e5SOllivier Robert 
531c0b746e5SOllivier Robert #if 0
5322b15cb3dSCy Schubert static void dummy_event_handler (struct peer *);
5332b15cb3dSCy Schubert static void   arc_event_handler (struct peer *);
534c0b746e5SOllivier Robert #endif /* 0 */
535c0b746e5SOllivier Robert 
536c0b746e5SOllivier Robert #define QUALITY_UNKNOWN	    -1 /* Indicates unknown clock quality. */
537c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
538c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
539c0b746e5SOllivier Robert #define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
540c0b746e5SOllivier Robert 
541c0b746e5SOllivier Robert /*
542c0b746e5SOllivier Robert  * Function prototypes
543c0b746e5SOllivier Robert  */
5442b15cb3dSCy Schubert static	int	arc_start	(int, struct peer *);
5452b15cb3dSCy Schubert static	void	arc_shutdown	(int, struct peer *);
5462b15cb3dSCy Schubert static	void	arc_receive	(struct recvbuf *);
5472b15cb3dSCy Schubert static	void	arc_poll	(int, struct peer *);
548c0b746e5SOllivier Robert 
549c0b746e5SOllivier Robert /*
550c0b746e5SOllivier Robert  * Transfer vector
551c0b746e5SOllivier Robert  */
552c0b746e5SOllivier Robert struct  refclock refclock_arc = {
553c0b746e5SOllivier Robert 	arc_start,		/* start up driver */
554c0b746e5SOllivier Robert 	arc_shutdown,		/* shut down driver */
555c0b746e5SOllivier Robert 	arc_poll,		/* transmit poll message */
556c0b746e5SOllivier Robert 	noentry,		/* not used (old arc_control) */
557c0b746e5SOllivier Robert 	noentry,		/* initialize driver (not used) */
558c0b746e5SOllivier Robert 	noentry,		/* not used (old arc_buginfo) */
559c0b746e5SOllivier Robert 	NOFLAGS			/* not used */
560c0b746e5SOllivier Robert };
561c0b746e5SOllivier Robert 
562c0b746e5SOllivier Robert /* Queue us up for the next tick. */
563c0b746e5SOllivier Robert #define ENQUEUE(up) \
564c0b746e5SOllivier Robert 	do { \
5652b15cb3dSCy Schubert 	     peer->procptr->nextaction = current_time + QUEUETICK; \
566c0b746e5SOllivier Robert 	} while(0)
567c0b746e5SOllivier Robert 
5689c2daa00SOllivier Robert /* Placeholder event handler---does nothing safely---soaks up loose tick. */
569c0b746e5SOllivier Robert static void
570c0b746e5SOllivier Robert dummy_event_handler(
571c0b746e5SOllivier Robert 	struct peer *peer
572c0b746e5SOllivier Robert 	)
573c0b746e5SOllivier Robert {
5749c2daa00SOllivier Robert #ifdef DEBUG
575c0b746e5SOllivier Robert 	if(debug) { printf("arc: dummy_event_handler() called.\n"); }
576c0b746e5SOllivier Robert #endif
577c0b746e5SOllivier Robert }
578c0b746e5SOllivier Robert 
579c0b746e5SOllivier Robert /*
580c0b746e5SOllivier Robert Normal event handler.
581c0b746e5SOllivier Robert 
582c0b746e5SOllivier Robert Take first character off queue and send to clock if not a null.
583c0b746e5SOllivier Robert 
584c0b746e5SOllivier Robert Shift characters down and put a null on the end.
585c0b746e5SOllivier Robert 
586c0b746e5SOllivier Robert We assume that there is no parallelism so no race condition, but even
587c0b746e5SOllivier Robert if there is nothing bad will happen except that we might send some bad
588c0b746e5SOllivier Robert data to the clock once in a while.
589c0b746e5SOllivier Robert */
590c0b746e5SOllivier Robert static void
591c0b746e5SOllivier Robert arc_event_handler(
592c0b746e5SOllivier Robert 	struct peer *peer
593c0b746e5SOllivier Robert 	)
594c0b746e5SOllivier Robert {
595c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
5962b15cb3dSCy Schubert 	register struct arcunit *up = pp->unitptr;
597c0b746e5SOllivier Robert 	int i;
598c0b746e5SOllivier Robert 	char c;
5999c2daa00SOllivier Robert #ifdef DEBUG
600c0b746e5SOllivier Robert 	if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
601c0b746e5SOllivier Robert #endif
602c0b746e5SOllivier Robert 
603c0b746e5SOllivier Robert 	c = up->cmdqueue[0];       /* Next char to be sent. */
604c0b746e5SOllivier Robert 	/* Shift down characters, shifting trailing \0 in at end. */
605c0b746e5SOllivier Robert 	for(i = 0; i < CMDQUEUELEN; ++i)
606c0b746e5SOllivier Robert 	{ up->cmdqueue[i] = up->cmdqueue[i+1]; }
607c0b746e5SOllivier Robert 
608c0b746e5SOllivier Robert 	/* Don't send '\0' characters. */
609c0b746e5SOllivier Robert 	if(c != '\0') {
610c0b746e5SOllivier Robert 		if(write(pp->io.fd, &c, 1) != 1) {
611c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
612c0b746e5SOllivier Robert 		}
6139c2daa00SOllivier Robert #ifdef DEBUG
614c0b746e5SOllivier Robert 		else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
615c0b746e5SOllivier Robert #endif
616c0b746e5SOllivier Robert 	}
6179c2daa00SOllivier Robert 
6189c2daa00SOllivier Robert 	ENQUEUE(up);
619c0b746e5SOllivier Robert }
620c0b746e5SOllivier Robert 
621c0b746e5SOllivier Robert /*
622c0b746e5SOllivier Robert  * arc_start - open the devices and initialize data for processing
623c0b746e5SOllivier Robert  */
624c0b746e5SOllivier Robert static int
625c0b746e5SOllivier Robert arc_start(
626c0b746e5SOllivier Robert 	int unit,
627c0b746e5SOllivier Robert 	struct peer *peer
628c0b746e5SOllivier Robert 	)
629c0b746e5SOllivier Robert {
630c0b746e5SOllivier Robert 	register struct arcunit *up;
631c0b746e5SOllivier Robert 	struct refclockproc *pp;
6322b15cb3dSCy Schubert 	int temp_fd;
633c0b746e5SOllivier Robert 	int fd;
634c0b746e5SOllivier Robert 	char device[20];
635c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
636c0b746e5SOllivier Robert 	struct termios arg;
637c0b746e5SOllivier Robert #endif
638c0b746e5SOllivier Robert 
6392b15cb3dSCy Schubert 	msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
6402b15cb3dSCy Schubert 		arc_version, unit);
6412b15cb3dSCy Schubert 	DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
6422b15cb3dSCy Schubert 		unit));
643c0b746e5SOllivier Robert 
644c0b746e5SOllivier Robert 	/*
645c0b746e5SOllivier Robert 	 * Open serial port. Use CLK line discipline, if available.
646c0b746e5SOllivier Robert 	 */
6472b15cb3dSCy Schubert 	snprintf(device, sizeof(device), DEVICE, unit);
648a466cc55SCy Schubert 	temp_fd = refclock_open(&peer->srcadr, device, SPEED, LDISC_CLK);
6492b15cb3dSCy Schubert 	if (temp_fd <= 0)
6502b15cb3dSCy Schubert 		return 0;
6512b15cb3dSCy Schubert 	DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
6522b15cb3dSCy Schubert 	fd = tty_open(device, OPEN_FLAGS, 0777);
653c0b746e5SOllivier Robert 	if (fd < 0) {
6542b15cb3dSCy Schubert 		msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.",
6552b15cb3dSCy Schubert 			unit, device);
6562b15cb3dSCy Schubert 		close(temp_fd);
6572b15cb3dSCy Schubert 		return 0;
658c0b746e5SOllivier Robert 	}
6592b15cb3dSCy Schubert 	close(temp_fd);
6609034852cSGleb Smirnoff 	temp_fd = -1;		/* not used after this, at *this* time. */
661c0b746e5SOllivier Robert 
6622b15cb3dSCy Schubert #ifndef SYS_WINNT
6632b15cb3dSCy Schubert 	if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */
6642b15cb3dSCy Schubert 		msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
6652b15cb3dSCy Schubert 			unit);
6662b15cb3dSCy Schubert 
667c0b746e5SOllivier Robert #endif
6682b15cb3dSCy Schubert 	DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
669c0b746e5SOllivier Robert 
670c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS
671c0b746e5SOllivier Robert 
6722b15cb3dSCy Schubert 	if (tcgetattr(fd, &arg) < 0) {
6732b15cb3dSCy Schubert 		msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.",
6742b15cb3dSCy Schubert 			unit, device);
6752b15cb3dSCy Schubert 		close(fd);
6762b15cb3dSCy Schubert 		return 0;
6772b15cb3dSCy Schubert 	}
678ea906c41SOllivier Robert 
679c0b746e5SOllivier Robert 	arg.c_iflag = IGNBRK | ISTRIP;
680c0b746e5SOllivier Robert 	arg.c_oflag = 0;
681c0b746e5SOllivier Robert 	arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
682c0b746e5SOllivier Robert 	arg.c_lflag = 0;
683c0b746e5SOllivier Robert 	arg.c_cc[VMIN] = 1;
684c0b746e5SOllivier Robert 	arg.c_cc[VTIME] = 0;
685c0b746e5SOllivier Robert 
6862b15cb3dSCy Schubert 	if (tcsetattr(fd, TCSANOW, &arg) < 0) {
6872b15cb3dSCy Schubert 		msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.",
6882b15cb3dSCy Schubert 			unit, device);
6892b15cb3dSCy Schubert 		close(fd);
6902b15cb3dSCy Schubert 		return 0;
6912b15cb3dSCy Schubert 	}
692c0b746e5SOllivier Robert 
693c0b746e5SOllivier Robert #else
694c0b746e5SOllivier Robert 
6952b15cb3dSCy Schubert 	msyslog(LOG_ERR, "ARCRON: termios required by this driver");
696c0b746e5SOllivier Robert 	(void)close(fd);
697c0b746e5SOllivier Robert 
698c0b746e5SOllivier Robert 	return 0;
699c0b746e5SOllivier Robert 
700c0b746e5SOllivier Robert #endif
701c0b746e5SOllivier Robert 
702c0b746e5SOllivier Robert 	/* Set structure to all zeros... */
7032b15cb3dSCy Schubert 	up = emalloc_zero(sizeof(*up));
704c0b746e5SOllivier Robert 	pp = peer->procptr;
705c0b746e5SOllivier Robert 	pp->io.clock_recv = arc_receive;
7062b15cb3dSCy Schubert 	pp->io.srcclock = peer;
707c0b746e5SOllivier Robert 	pp->io.datalen = 0;
708c0b746e5SOllivier Robert 	pp->io.fd = fd;
7092b15cb3dSCy Schubert 	if (!io_addclock(&pp->io)) {
7102b15cb3dSCy Schubert 		close(fd);
7112b15cb3dSCy Schubert 		pp->io.fd = -1;
7122b15cb3dSCy Schubert 		free(up);
7132b15cb3dSCy Schubert 		return(0);
7142b15cb3dSCy Schubert 	}
7152b15cb3dSCy Schubert 	pp->unitptr = up;
716c0b746e5SOllivier Robert 
717c0b746e5SOllivier Robert 	/*
718c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
719c0b746e5SOllivier Robert 	 */
720c0b746e5SOllivier Robert 	peer->precision = PRECISION;
721c0b746e5SOllivier Robert 	peer->stratum = 2;              /* Default to stratum 2 not 0. */
722c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
7239c2daa00SOllivier Robert 	if (peer->MODE > 3) {
7249c2daa00SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
7259c2daa00SOllivier Robert 		return 0;
7269c2daa00SOllivier Robert 	}
7279c2daa00SOllivier Robert #ifdef DEBUG
7289c2daa00SOllivier Robert 	if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
7299c2daa00SOllivier Robert #endif
7309c2daa00SOllivier Robert 	switch (peer->MODE) {
7319c2daa00SOllivier Robert 	    case 1:
7329c2daa00SOllivier Robert 		memcpy((char *)&pp->refid, REFID_MSF, 4);
7339c2daa00SOllivier Robert 		break;
7349c2daa00SOllivier Robert 	    case 2:
7359c2daa00SOllivier Robert 		memcpy((char *)&pp->refid, REFID_DCF77, 4);
7369c2daa00SOllivier Robert 		break;
7379c2daa00SOllivier Robert 	    case 3:
7389c2daa00SOllivier Robert 		memcpy((char *)&pp->refid, REFID_WWVB, 4);
7399c2daa00SOllivier Robert 		break;
7409c2daa00SOllivier Robert 	    default:
741c0b746e5SOllivier Robert 		memcpy((char *)&pp->refid, REFID, 4);
7429c2daa00SOllivier Robert 		break;
7439c2daa00SOllivier Robert 	}
744c0b746e5SOllivier Robert 	/* Spread out resyncs so that they should remain separated. */
745c0b746e5SOllivier Robert 	up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
746c0b746e5SOllivier Robert 
747c0b746e5SOllivier Robert #if 0 /* Not needed because of zeroing of arcunit structure... */
748c0b746e5SOllivier Robert 	up->resyncing = 0;              /* Not resyncing yet. */
749c0b746e5SOllivier Robert 	up->saved_flags = 0;            /* Default is all flags off. */
750c0b746e5SOllivier Robert 	/* Clear send buffer out... */
751c0b746e5SOllivier Robert 	{
752c0b746e5SOllivier Robert 		int i;
753c0b746e5SOllivier Robert 		for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
754c0b746e5SOllivier Robert 	}
755c0b746e5SOllivier Robert #endif
756c0b746e5SOllivier Robert 
757c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
758c0b746e5SOllivier Robert 	up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
759c0b746e5SOllivier Robert #else
760c0b746e5SOllivier Robert 	up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
761c0b746e5SOllivier Robert #endif
7629c2daa00SOllivier Robert 
7632b15cb3dSCy Schubert 	peer->procptr->action = arc_event_handler;
7649c2daa00SOllivier Robert 
7659c2daa00SOllivier Robert 	ENQUEUE(up);
7669c2daa00SOllivier Robert 
767c0b746e5SOllivier Robert 	return(1);
768c0b746e5SOllivier Robert }
769c0b746e5SOllivier Robert 
770c0b746e5SOllivier Robert 
771c0b746e5SOllivier Robert /*
772c0b746e5SOllivier Robert  * arc_shutdown - shut down the clock
773c0b746e5SOllivier Robert  */
774c0b746e5SOllivier Robert static void
775c0b746e5SOllivier Robert arc_shutdown(
776c0b746e5SOllivier Robert 	int unit,
777c0b746e5SOllivier Robert 	struct peer *peer
778c0b746e5SOllivier Robert 	)
779c0b746e5SOllivier Robert {
780c0b746e5SOllivier Robert 	register struct arcunit *up;
781c0b746e5SOllivier Robert 	struct refclockproc *pp;
782c0b746e5SOllivier Robert 
7832b15cb3dSCy Schubert 	peer->procptr->action = dummy_event_handler;
7849c2daa00SOllivier Robert 
785c0b746e5SOllivier Robert 	pp = peer->procptr;
7862b15cb3dSCy Schubert 	up = pp->unitptr;
7872b15cb3dSCy Schubert 	if (-1 != pp->io.fd)
788c0b746e5SOllivier Robert 		io_closeclock(&pp->io);
7892b15cb3dSCy Schubert 	if (NULL != up)
790c0b746e5SOllivier Robert 		free(up);
791c0b746e5SOllivier Robert }
792c0b746e5SOllivier Robert 
793c0b746e5SOllivier Robert /*
794c0b746e5SOllivier Robert Compute space left in output buffer.
795c0b746e5SOllivier Robert */
796c0b746e5SOllivier Robert static int
797c0b746e5SOllivier Robert space_left(
798c0b746e5SOllivier Robert 	register struct arcunit *up
799c0b746e5SOllivier Robert 	)
800c0b746e5SOllivier Robert {
801c0b746e5SOllivier Robert 	int spaceleft;
802c0b746e5SOllivier Robert 
803c0b746e5SOllivier Robert 	/* Compute space left in buffer after any pending output. */
804c0b746e5SOllivier Robert 	for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
805c0b746e5SOllivier Robert 	{ if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
806c0b746e5SOllivier Robert 	return(spaceleft);
807c0b746e5SOllivier Robert }
808c0b746e5SOllivier Robert 
809c0b746e5SOllivier Robert /*
810c0b746e5SOllivier Robert Send command by copying into command buffer as far forward as possible,
811c0b746e5SOllivier Robert after any pending output.
812c0b746e5SOllivier Robert 
813c0b746e5SOllivier Robert Indicate an error by returning 0 if there is not space for the command.
814c0b746e5SOllivier Robert */
815c0b746e5SOllivier Robert static int
816c0b746e5SOllivier Robert send_slow(
817c0b746e5SOllivier Robert 	register struct arcunit *up,
818c0b746e5SOllivier Robert 	int fd,
819c0b746e5SOllivier Robert 	const char *s
820c0b746e5SOllivier Robert 	)
821c0b746e5SOllivier Robert {
822c0b746e5SOllivier Robert 	int sl = strlen(s);
823c0b746e5SOllivier Robert 	int spaceleft = space_left(up);
824c0b746e5SOllivier Robert 
8259c2daa00SOllivier Robert #ifdef DEBUG
826c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
827c0b746e5SOllivier Robert #endif
828c0b746e5SOllivier Robert 	if(spaceleft < sl) { /* Should not normally happen... */
8299c2daa00SOllivier Robert #ifdef DEBUG
830c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
831c0b746e5SOllivier Robert 			sl, spaceleft);
832c0b746e5SOllivier Robert #endif
833c0b746e5SOllivier Robert 		return(0);			/* FAILED! */
834c0b746e5SOllivier Robert 	}
835c0b746e5SOllivier Robert 
836c0b746e5SOllivier Robert 	/* Copy in the command to be sent. */
837ea906c41SOllivier Robert 	while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
838c0b746e5SOllivier Robert 
839c0b746e5SOllivier Robert 	return(1);
840c0b746e5SOllivier Robert }
841c0b746e5SOllivier Robert 
842c0b746e5SOllivier Robert 
843ea906c41SOllivier Robert static int
844ea906c41SOllivier Robert get2(char *p, int *val)
845ea906c41SOllivier Robert {
8462b15cb3dSCy Schubert   if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0;
847ea906c41SOllivier Robert   *val = (p[0] - '0') * 10 + p[1] - '0';
848ea906c41SOllivier Robert   return 1;
849ea906c41SOllivier Robert }
850ea906c41SOllivier Robert 
851ea906c41SOllivier Robert static int
852ea906c41SOllivier Robert get1(char *p, int *val)
853ea906c41SOllivier Robert {
8542b15cb3dSCy Schubert   if (!isdigit((unsigned char)p[0])) return 0;
855ea906c41SOllivier Robert   *val = p[0] - '0';
856ea906c41SOllivier Robert   return 1;
857ea906c41SOllivier Robert }
858ea906c41SOllivier Robert 
859c0b746e5SOllivier Robert /* Macro indicating action we will take for different quality values. */
860c0b746e5SOllivier Robert #define quality_action(q) \
861c0b746e5SOllivier Robert (((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
862c0b746e5SOllivier Robert  (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
863c0b746e5SOllivier Robert   "OK, will use clock"))
864c0b746e5SOllivier Robert 
865c0b746e5SOllivier Robert /*
866c0b746e5SOllivier Robert  * arc_receive - receive data from the serial interface
867c0b746e5SOllivier Robert  */
868c0b746e5SOllivier Robert static void
869c0b746e5SOllivier Robert arc_receive(
870c0b746e5SOllivier Robert 	struct recvbuf *rbufp
871c0b746e5SOllivier Robert 	)
872c0b746e5SOllivier Robert {
873*f5f40dd6SCy Schubert 	static int quality_average = 0;
874*f5f40dd6SCy Schubert 	static int quality_sum = 0;
875*f5f40dd6SCy Schubert 	static int quality_polls = 0;
876c0b746e5SOllivier Robert 	register struct arcunit *up;
877c0b746e5SOllivier Robert 	struct refclockproc *pp;
878c0b746e5SOllivier Robert 	struct peer *peer;
879c0b746e5SOllivier Robert 	char c;
880*f5f40dd6SCy Schubert 	int i, wday, month, flags, status;
881c0b746e5SOllivier Robert 	int arc_last_offset;
882*f5f40dd6SCy Schubert     #ifdef DEBUG
883*f5f40dd6SCy Schubert 	int n;
884*f5f40dd6SCy Schubert     #endif
885c0b746e5SOllivier Robert 
886c0b746e5SOllivier Robert 	/*
887c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp
888c0b746e5SOllivier Robert 	 */
8892b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
890c0b746e5SOllivier Robert 	pp = peer->procptr;
8912b15cb3dSCy Schubert 	up = pp->unitptr;
892c0b746e5SOllivier Robert 
893c0b746e5SOllivier Robert 
894c0b746e5SOllivier Robert 	/*
895c0b746e5SOllivier Robert 	  If the command buffer is empty, and we are resyncing, insert a
896c0b746e5SOllivier Robert 	  g\r quality request into it to poll for signal quality again.
897c0b746e5SOllivier Robert 	*/
898c0b746e5SOllivier Robert 	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
899c0b746e5SOllivier Robert #ifdef DEBUG
900c0b746e5SOllivier Robert 		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
901c0b746e5SOllivier Robert #endif
902c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "g\r");
903c0b746e5SOllivier Robert 	}
904c0b746e5SOllivier Robert 
905c0b746e5SOllivier Robert 	/*
906c0b746e5SOllivier Robert 	  The `arc_last_offset' is the offset in lastcode[] of the last byte
907c0b746e5SOllivier Robert 	  received, and which we assume actually received the input
908c0b746e5SOllivier Robert 	  timestamp.
909c0b746e5SOllivier Robert 
910c0b746e5SOllivier Robert 	  (When we get round to using tty_clk and it is available, we
911c0b746e5SOllivier Robert 	  assume that we will receive the whole timecode with the
912c0b746e5SOllivier Robert 	  trailing \r, and that that \r will be timestamped.  But this
913c0b746e5SOllivier Robert 	  assumption also works if receive the characters one-by-one.)
914c0b746e5SOllivier Robert 	*/
915c0b746e5SOllivier Robert 	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
916c0b746e5SOllivier Robert 
917c0b746e5SOllivier Robert 	/*
918c0b746e5SOllivier Robert 	  We catch a timestamp iff:
919c0b746e5SOllivier Robert 
920c0b746e5SOllivier Robert 	  * The command code is `o' for a timestamp.
921c0b746e5SOllivier Robert 
922c0b746e5SOllivier Robert 	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
923c0b746e5SOllivier Robert 	  exactly char in the buffer (the command code) so that we
924c0b746e5SOllivier Robert 	  only sample the first character of the timecode as our
925c0b746e5SOllivier Robert 	  `on-time' character.
926c0b746e5SOllivier Robert 
927c0b746e5SOllivier Robert 	  * The first character in the buffer is not the echoed `\r'
928c0b746e5SOllivier Robert 	  from the `o` command (so if we are to timestamp an `\r' it
929c0b746e5SOllivier Robert 	  must not be first in the receive buffer with lencode==1.
930c0b746e5SOllivier Robert 	  (Even if we had other characters following it, we probably
931c0b746e5SOllivier Robert 	  would have a premature timestamp on the '\r'.)
932c0b746e5SOllivier Robert 
933c0b746e5SOllivier Robert 	  * We have received at least one character (I cannot imagine
934c0b746e5SOllivier Robert 	  how it could be otherwise, but anyway...).
935c0b746e5SOllivier Robert 	*/
936c0b746e5SOllivier Robert 	c = rbufp->recv_buffer[0];
937c0b746e5SOllivier Robert 	if((pp->a_lastcode[0] == 'o') &&
938c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES
939c0b746e5SOllivier Robert 	   (pp->lencode == 1) &&
940c0b746e5SOllivier Robert #endif
941c0b746e5SOllivier Robert 	   ((pp->lencode != 1) || (c != '\r')) &&
942c0b746e5SOllivier Robert 	   (arc_last_offset >= 1)) {
943c0b746e5SOllivier Robert 		/* Note that the timestamp should be corrected if >1 char rcvd. */
944c0b746e5SOllivier Robert 		l_fp timestamp;
945c0b746e5SOllivier Robert 		timestamp = rbufp->recv_time;
946c0b746e5SOllivier Robert #ifdef DEBUG
947c0b746e5SOllivier Robert 		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
948c0b746e5SOllivier Robert 			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
9492b15cb3dSCy Schubert 			       ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')),
950c0b746e5SOllivier Robert 			       rbufp->recv_length);
951c0b746e5SOllivier Robert 		}
952c0b746e5SOllivier Robert #endif
953c0b746e5SOllivier Robert 
954c0b746e5SOllivier Robert 		/*
955c0b746e5SOllivier Robert 		  Now correct timestamp by offset of last byte received---we
956c0b746e5SOllivier Robert 		  subtract from the receive time the delay implied by the
957c0b746e5SOllivier Robert 		  extra characters received.
958c0b746e5SOllivier Robert 
959c0b746e5SOllivier Robert 		  Reject the input if the resulting code is too long, but
960c0b746e5SOllivier Robert 		  allow for the trailing \r, normally not used but a good
961c0b746e5SOllivier Robert 		  handle for tty_clk or somesuch kernel timestamper.
962c0b746e5SOllivier Robert 		*/
963c0b746e5SOllivier Robert 		if(arc_last_offset > LENARC) {
9649c2daa00SOllivier Robert #ifdef DEBUG
965c0b746e5SOllivier Robert 			if(debug) {
966c0b746e5SOllivier Robert 				printf("arc: input code too long (%d cf %d); rejected.\n",
967c0b746e5SOllivier Robert 				       arc_last_offset, LENARC);
968c0b746e5SOllivier Robert 			}
969c0b746e5SOllivier Robert #endif
970c0b746e5SOllivier Robert 			pp->lencode = 0;
971c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
972c0b746e5SOllivier Robert 			return;
973c0b746e5SOllivier Robert 		}
974c0b746e5SOllivier Robert 
975c0b746e5SOllivier Robert 		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
9769c2daa00SOllivier Robert #ifdef DEBUG
977c0b746e5SOllivier Robert 		if(debug > 1) {
978c0b746e5SOllivier Robert 			printf(
979c0b746e5SOllivier Robert 				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
980c0b746e5SOllivier Robert 				((rbufp->recv_length > 1) ? "*** " : ""),
981c0b746e5SOllivier Robert 				rbufp->recv_length,
982c0b746e5SOllivier Robert 				arc_last_offset,
983c0b746e5SOllivier Robert 				mfptoms((unsigned long)0,
984c0b746e5SOllivier Robert 					charoffsets[arc_last_offset],
985c0b746e5SOllivier Robert 					1));
986c0b746e5SOllivier Robert 		}
987c0b746e5SOllivier Robert #endif
988c0b746e5SOllivier Robert 
989c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
990c0b746e5SOllivier Robert 		/*
991c0b746e5SOllivier Robert 		  If taking multiple samples, capture the current adjusted
992c0b746e5SOllivier Robert 		  sample iff:
993c0b746e5SOllivier Robert 
994c0b746e5SOllivier Robert 		  * No timestamp has yet been captured (it is zero), OR
995c0b746e5SOllivier Robert 
996c0b746e5SOllivier Robert 		  * This adjusted timestamp is earlier than the one already
997c0b746e5SOllivier Robert 		  captured, on the grounds that this one suffered less
998c0b746e5SOllivier Robert 		  delay in being delivered to us and is more accurate.
999c0b746e5SOllivier Robert 
1000c0b746e5SOllivier Robert 		*/
1001c0b746e5SOllivier Robert 		if(L_ISZERO(&(up->lastrec)) ||
1002c0b746e5SOllivier Robert 		   L_ISGEQ(&(up->lastrec), &timestamp))
1003c0b746e5SOllivier Robert #endif
1004c0b746e5SOllivier Robert 		{
10059c2daa00SOllivier Robert #ifdef DEBUG
1006c0b746e5SOllivier Robert 			if(debug > 1) {
1007c0b746e5SOllivier Robert 				printf("arc: system timestamp captured.\n");
1008c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
1009c0b746e5SOllivier Robert 				if(!L_ISZERO(&(up->lastrec))) {
1010c0b746e5SOllivier Robert 					l_fp diff;
1011c0b746e5SOllivier Robert 					diff = up->lastrec;
1012c0b746e5SOllivier Robert 					L_SUB(&diff, &timestamp);
1013c0b746e5SOllivier Robert 					printf("arc: adjusted timestamp by -%sms.\n",
10142b15cb3dSCy Schubert 					       mfptoms(diff.l_ui, diff.l_uf, 3));
1015c0b746e5SOllivier Robert 				}
1016c0b746e5SOllivier Robert #endif
1017c0b746e5SOllivier Robert 			}
1018c0b746e5SOllivier Robert #endif
1019c0b746e5SOllivier Robert 			up->lastrec = timestamp;
1020c0b746e5SOllivier Robert 		}
1021c0b746e5SOllivier Robert 
1022c0b746e5SOllivier Robert 	}
1023c0b746e5SOllivier Robert 
1024c0b746e5SOllivier Robert 	/* Just in case we still have lots of rubbish in the buffer... */
1025c0b746e5SOllivier Robert 	/* ...and to avoid the same timestamp being reused by mistake, */
1026c0b746e5SOllivier Robert 	/* eg on receipt of the \r coming in on its own after the      */
1027c0b746e5SOllivier Robert 	/* timecode.						       */
1028c0b746e5SOllivier Robert 	if(pp->lencode >= LENARC) {
10299c2daa00SOllivier Robert #ifdef DEBUG
1030c0b746e5SOllivier Robert 		if(debug && (rbufp->recv_buffer[0] != '\r'))
1031c0b746e5SOllivier Robert 		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
1032c0b746e5SOllivier Robert #endif
1033c0b746e5SOllivier Robert 		pp->lencode = 0;
1034c0b746e5SOllivier Robert 		return;
1035c0b746e5SOllivier Robert 	}
1036c0b746e5SOllivier Robert 
1037c0b746e5SOllivier Robert 	/* Append input to code buffer, avoiding overflow. */
1038c0b746e5SOllivier Robert 	for(i = 0; i < rbufp->recv_length; i++) {
1039c0b746e5SOllivier Robert 		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
1040c0b746e5SOllivier Robert 		c = rbufp->recv_buffer[i];
1041c0b746e5SOllivier Robert 
1042c0b746e5SOllivier Robert 		/* Drop trailing '\r's and drop `h' command echo totally. */
1043c0b746e5SOllivier Robert 		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
1044c0b746e5SOllivier Robert 
1045c0b746e5SOllivier Robert 		/*
1046c0b746e5SOllivier Robert 		  If we've just put an `o' in the lastcode[0], clear the
1047c0b746e5SOllivier Robert 		  timestamp in anticipation of a timecode arriving soon.
1048c0b746e5SOllivier Robert 
1049c0b746e5SOllivier Robert 		  We would expect to get to process this before any of the
1050c0b746e5SOllivier Robert 		  timecode arrives.
1051c0b746e5SOllivier Robert 		*/
1052c0b746e5SOllivier Robert 		if((c == 'o') && (pp->lencode == 1)) {
1053c0b746e5SOllivier Robert 			L_CLR(&(up->lastrec));
10549c2daa00SOllivier Robert #ifdef DEBUG
1055c0b746e5SOllivier Robert 			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
1056c0b746e5SOllivier Robert #endif
1057c0b746e5SOllivier Robert 		}
1058c0b746e5SOllivier Robert 	}
10599c2daa00SOllivier Robert 	if (pp->lencode == 0) return;
1060c0b746e5SOllivier Robert 
1061c0b746e5SOllivier Robert 	/* Handle a quality message. */
1062c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] == 'g') {
1063c0b746e5SOllivier Robert 		int r, q;
1064c0b746e5SOllivier Robert 
1065c0b746e5SOllivier Robert 		if(pp->lencode < 3) { return; } /* Need more data... */
1066c0b746e5SOllivier Robert 		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
1067c0b746e5SOllivier Robert 		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
1068c0b746e5SOllivier Robert 		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
1069c0b746e5SOllivier Robert 		   ((r & 0x70) != 0x30)) {
1070c0b746e5SOllivier Robert 			/* Badly formatted response. */
10719c2daa00SOllivier Robert #ifdef DEBUG
1072c0b746e5SOllivier Robert 			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
1073c0b746e5SOllivier Robert #endif
1074c0b746e5SOllivier Robert 			return;
1075c0b746e5SOllivier Robert 		}
1076c0b746e5SOllivier Robert 		if(r == '3') { /* Only use quality value whilst sync in progress. */
10779c2daa00SOllivier Robert 			if (up->quality_stamp < current_time) {
10789c2daa00SOllivier Robert 				struct calendar cal;
10799c2daa00SOllivier Robert 				l_fp new_stamp;
10809c2daa00SOllivier Robert 
10819c2daa00SOllivier Robert 				get_systime (&new_stamp);
10829c2daa00SOllivier Robert 				caljulian (new_stamp.l_ui, &cal);
10839c2daa00SOllivier Robert 				up->quality_stamp =
10849c2daa00SOllivier Robert 					current_time + 60 - cal.second + 5;
10859c2daa00SOllivier Robert 				quality_sum = 0;
10869c2daa00SOllivier Robert 				quality_polls = 0;
10879c2daa00SOllivier Robert 			}
10889c2daa00SOllivier Robert 			quality_sum += (q & 0xf);
10899c2daa00SOllivier Robert 			quality_polls++;
10909c2daa00SOllivier Robert 			quality_average = (quality_sum / quality_polls);
1091c0b746e5SOllivier Robert #ifdef DEBUG
10929c2daa00SOllivier Robert 			if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
1093c0b746e5SOllivier Robert #endif
1094c0b746e5SOllivier Robert 		} else if( /* (r == '2') && */ up->resyncing) {
10959c2daa00SOllivier Robert 			up->quality = quality_average;
1096c0b746e5SOllivier Robert #ifdef DEBUG
1097c0b746e5SOllivier Robert 			if(debug)
1098c0b746e5SOllivier Robert 			{
1099c0b746e5SOllivier Robert 				printf("arc: sync finished, signal quality %d: %s\n",
1100c0b746e5SOllivier Robert 				       up->quality,
1101c0b746e5SOllivier Robert 				       quality_action(up->quality));
1102c0b746e5SOllivier Robert 			}
1103c0b746e5SOllivier Robert #endif
1104c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE,
1105c0b746e5SOllivier Robert 				"ARCRON: sync finished, signal quality %d: %s",
1106c0b746e5SOllivier Robert 				up->quality,
1107c0b746e5SOllivier Robert 				quality_action(up->quality));
1108c0b746e5SOllivier Robert 			up->resyncing = 0; /* Resync is over. */
11099c2daa00SOllivier Robert 			quality_average = 0;
11109c2daa00SOllivier Robert 			quality_sum = 0;
11119c2daa00SOllivier Robert 			quality_polls = 0;
1112c0b746e5SOllivier Robert 
1113c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1114c0b746e5SOllivier Robert 			/* Clock quality dubious; resync earlier than usual. */
1115c0b746e5SOllivier Robert 			if((up->quality == QUALITY_UNKNOWN) ||
1116c0b746e5SOllivier Robert 			   (up->quality < MIN_CLOCK_QUALITY_OK))
1117c0b746e5SOllivier Robert 			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
1118c0b746e5SOllivier Robert #endif
1119c0b746e5SOllivier Robert 		}
1120c0b746e5SOllivier Robert 		pp->lencode = 0;
1121c0b746e5SOllivier Robert 		return;
1122c0b746e5SOllivier Robert 	}
1123c0b746e5SOllivier Robert 
1124c0b746e5SOllivier Robert 	/* Stop now if this is not a timecode message. */
1125c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] != 'o') {
1126c0b746e5SOllivier Robert 		pp->lencode = 0;
1127c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1128c0b746e5SOllivier Robert 		return;
1129c0b746e5SOllivier Robert 	}
1130c0b746e5SOllivier Robert 
1131c0b746e5SOllivier Robert 	/* If we don't have enough data, wait for more... */
1132c0b746e5SOllivier Robert 	if(pp->lencode < LENARC) { return; }
1133c0b746e5SOllivier Robert 
1134c0b746e5SOllivier Robert 
1135c0b746e5SOllivier Robert 	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
11369c2daa00SOllivier Robert #ifdef DEBUG
1137c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1138c0b746e5SOllivier Robert #endif
1139c0b746e5SOllivier Robert 
1140c0b746e5SOllivier Robert 	/* But check that we actually captured a system timestamp on it. */
1141c0b746e5SOllivier Robert 	if(L_ISZERO(&(up->lastrec))) {
11429c2daa00SOllivier Robert #ifdef DEBUG
1143c0b746e5SOllivier Robert 		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1144c0b746e5SOllivier Robert #endif
1145c0b746e5SOllivier Robert 		pp->lencode = 0;
1146c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1147c0b746e5SOllivier Robert 		return;
1148c0b746e5SOllivier Robert 	}
1149c0b746e5SOllivier Robert 	/*
1150c0b746e5SOllivier Robert 	  Append a mark of the clock's received signal quality for the
1151c0b746e5SOllivier Robert 	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1152c0b746e5SOllivier Robert 	  quality value to `6' for his s/w) and terminate the string for
1153c0b746e5SOllivier Robert 	  sure.  This should not go off the buffer end.
1154c0b746e5SOllivier Robert 	*/
1155c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1156c0b746e5SOllivier Robert 				       '6' : ('0' + up->quality));
1157c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1158c0b746e5SOllivier Robert 
11599c2daa00SOllivier Robert #ifdef PRE_NTP420
1160c0b746e5SOllivier Robert 	/* We don't use the micro-/milli- second part... */
1161c0b746e5SOllivier Robert 	pp->usec = 0;
1162c0b746e5SOllivier Robert 	pp->msec = 0;
11639c2daa00SOllivier Robert #else
11649c2daa00SOllivier Robert 	/* We don't use the nano-second part... */
11659c2daa00SOllivier Robert 	pp->nsec = 0;
11669c2daa00SOllivier Robert #endif
1167c0b746e5SOllivier Robert 	/* Validate format and numbers. */
1168ea906c41SOllivier Robert 	if (pp->a_lastcode[0] != 'o'
1169ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 1, &pp->hour)
1170ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 3, &pp->minute)
1171ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 5, &pp->second)
1172ea906c41SOllivier Robert 		|| !get1(pp->a_lastcode + 7, &wday)
1173ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 8, &pp->day)
1174ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 10, &month)
1175ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 12, &pp->year)) {
11769c2daa00SOllivier Robert #ifdef DEBUG
1177c0b746e5SOllivier Robert 		/* Would expect to have caught major problems already... */
1178c0b746e5SOllivier Robert 		if(debug) { printf("arc: badly formatted data.\n"); }
1179c0b746e5SOllivier Robert #endif
11809c2daa00SOllivier Robert 		pp->lencode = 0;
1181c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1182c0b746e5SOllivier Robert 		return;
1183c0b746e5SOllivier Robert 	}
1184ea906c41SOllivier Robert 	flags = pp->a_lastcode[14];
1185ea906c41SOllivier Robert 	status = pp->a_lastcode[15];
1186ea906c41SOllivier Robert #ifdef DEBUG
1187ea906c41SOllivier Robert 	if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
1188ea906c41SOllivier Robert 	n = 9;
1189*f5f40dd6SCy Schubert #endif
1190ea906c41SOllivier Robert 
1191c0b746e5SOllivier Robert 	/*
1192c0b746e5SOllivier Robert 	  Validate received values at least enough to prevent internal
1193c0b746e5SOllivier Robert 	  array-bounds problems, etc.
1194c0b746e5SOllivier Robert 	*/
1195c0b746e5SOllivier Robert 	if((pp->hour < 0) || (pp->hour > 23) ||
1196c0b746e5SOllivier Robert 	   (pp->minute < 0) || (pp->minute > 59) ||
1197c0b746e5SOllivier Robert 	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1198c0b746e5SOllivier Robert 	   (wday < 1) || (wday > 7) ||
1199c0b746e5SOllivier Robert 	   (pp->day < 1) || (pp->day > 31) ||
1200c0b746e5SOllivier Robert 	   (month < 1) || (month > 12) ||
1201c0b746e5SOllivier Robert 	   (pp->year < 0) || (pp->year > 99)) {
1202c0b746e5SOllivier Robert 		/* Data out of range. */
12039c2daa00SOllivier Robert 		pp->lencode = 0;
1204c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1205c0b746e5SOllivier Robert 		return;
1206c0b746e5SOllivier Robert 	}
1207c0b746e5SOllivier Robert 
12089c2daa00SOllivier Robert 
12099c2daa00SOllivier Robert 	if(peer->MODE == 0) { /* compatiblity to original version */
12109c2daa00SOllivier Robert 		int bst = flags;
12119c2daa00SOllivier Robert 		/* Check that BST/UTC bits are the complement of one another. */
12129c2daa00SOllivier Robert 		if(!(bst & 2) == !(bst & 4)) {
12139c2daa00SOllivier Robert 			pp->lencode = 0;
12149c2daa00SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
12159c2daa00SOllivier Robert 			return;
12169c2daa00SOllivier Robert 		}
12179c2daa00SOllivier Robert 	}
1218c0b746e5SOllivier Robert 	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1219c0b746e5SOllivier Robert 
1220c0b746e5SOllivier Robert 	/* Year-2000 alert! */
1221c0b746e5SOllivier Robert 	/* Attempt to wrap 2-digit date into sensible window. */
1222c0b746e5SOllivier Robert 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
1223c0b746e5SOllivier Robert 	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
1224c0b746e5SOllivier Robert 	/*
1225c0b746e5SOllivier Robert 	  Attempt to do the right thing by screaming that the code will
1226c0b746e5SOllivier Robert 	  soon break when we get to the end of its useful life.  What a
1227c0b746e5SOllivier Robert 	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1228c0b746e5SOllivier Robert 	*/
1229c0b746e5SOllivier Robert 	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
1230c0b746e5SOllivier Robert 		/*This should get attention B^> */
1231c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,
1232c0b746e5SOllivier Robert 			"ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1233c0b746e5SOllivier Robert 	}
1234c0b746e5SOllivier Robert #ifdef DEBUG
1235c0b746e5SOllivier Robert 	if(debug) {
1236c0b746e5SOllivier Robert 		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1237c0b746e5SOllivier Robert 		       n,
1238c0b746e5SOllivier Robert 		       pp->hour, pp->minute, pp->second,
12399c2daa00SOllivier Robert 		       pp->day, month, pp->year, flags, status);
1240c0b746e5SOllivier Robert 	}
1241c0b746e5SOllivier Robert #endif
1242c0b746e5SOllivier Robert 
1243c0b746e5SOllivier Robert 	/*
1244c0b746e5SOllivier Robert 	  The status value tested for is not strictly supported by the
1245c0b746e5SOllivier Robert 	  clock spec since the value of bit 2 (0x4) is claimed to be
1246c0b746e5SOllivier Robert 	  undefined for MSF, yet does seem to indicate if the last resync
1247c0b746e5SOllivier Robert 	  was successful or not.
1248c0b746e5SOllivier Robert 	*/
1249c0b746e5SOllivier Robert 	pp->leap = LEAP_NOWARNING;
1250c0b746e5SOllivier Robert 	status &= 0x7;
1251c0b746e5SOllivier Robert 	if(status == 0x3) {
1252c0b746e5SOllivier Robert 		if(status != up->status)
1253c0b746e5SOllivier Robert 		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1254c0b746e5SOllivier Robert 	} else {
1255c0b746e5SOllivier Robert 		if(status != up->status) {
1256c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: signal lost");
1257c0b746e5SOllivier Robert 			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1258c0b746e5SOllivier Robert 			up->status = status;
12599c2daa00SOllivier Robert 			pp->lencode = 0;
1260c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_FAULT);
1261c0b746e5SOllivier Robert 			return;
1262c0b746e5SOllivier Robert 		}
1263c0b746e5SOllivier Robert 	}
1264c0b746e5SOllivier Robert 	up->status = status;
1265c0b746e5SOllivier Robert 
12669c2daa00SOllivier Robert 	if (peer->MODE == 0) { /* compatiblity to original version */
12679c2daa00SOllivier Robert 		int bst = flags;
12689c2daa00SOllivier Robert 
1269c0b746e5SOllivier Robert 		pp->day += moff[month - 1];
1270c0b746e5SOllivier Robert 
1271c0b746e5SOllivier Robert 		if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
1272c0b746e5SOllivier Robert 
1273c0b746e5SOllivier Robert 		/* Convert to UTC if required */
1274c0b746e5SOllivier Robert 		if(bst & 2) {
1275c0b746e5SOllivier Robert 			pp->hour--;
1276c0b746e5SOllivier Robert 			if (pp->hour < 0) {
1277c0b746e5SOllivier Robert 				pp->hour = 23;
1278c0b746e5SOllivier Robert 				pp->day--;
12799c2daa00SOllivier Robert 				/* If we try to wrap round the year
12809c2daa00SOllivier Robert 				 * (BST on 1st Jan), reject.*/
1281c0b746e5SOllivier Robert 				if(pp->day < 0) {
12829c2daa00SOllivier Robert 					pp->lencode = 0;
1283c0b746e5SOllivier Robert 					refclock_report(peer, CEVNT_BADTIME);
1284c0b746e5SOllivier Robert 					return;
1285c0b746e5SOllivier Robert 				}
1286c0b746e5SOllivier Robert 			}
1287c0b746e5SOllivier Robert 		}
12889c2daa00SOllivier Robert 	}
1289c0b746e5SOllivier Robert 
12909c2daa00SOllivier Robert 	if(peer->MODE > 0) {
12919c2daa00SOllivier Robert 		if(pp->sloppyclockflag & CLK_FLAG1) {
12929c2daa00SOllivier Robert 			struct tm  local;
12939c2daa00SOllivier Robert 			struct tm *gmtp;
12949c2daa00SOllivier Robert 			time_t	   unixtime;
12959c2daa00SOllivier Robert 
12969c2daa00SOllivier Robert 			/*
12979c2daa00SOllivier Robert 			 * Convert to GMT for sites that distribute localtime.
12989c2daa00SOllivier Robert 			 * This means we have to do Y2K conversion on the
12999c2daa00SOllivier Robert 			 * 2-digit year; otherwise, we get the time wrong.
13009c2daa00SOllivier Robert 			 */
13019c2daa00SOllivier Robert 
13022b15cb3dSCy Schubert 			memset(&local, 0, sizeof(local));
13032b15cb3dSCy Schubert 
13049c2daa00SOllivier Robert 			local.tm_year  = pp->year-1900;
13059c2daa00SOllivier Robert 			local.tm_mon   = month-1;
13069c2daa00SOllivier Robert 			local.tm_mday  = pp->day;
13079c2daa00SOllivier Robert 			local.tm_hour  = pp->hour;
13089c2daa00SOllivier Robert 			local.tm_min   = pp->minute;
13099c2daa00SOllivier Robert 			local.tm_sec   = pp->second;
13109c2daa00SOllivier Robert 			switch (peer->MODE) {
13119c2daa00SOllivier Robert 			    case 1:
13129c2daa00SOllivier Robert 				local.tm_isdst = (flags & 2);
13139c2daa00SOllivier Robert 				break;
13149c2daa00SOllivier Robert 			    case 2:
13159c2daa00SOllivier Robert 				local.tm_isdst = (flags & 2);
13169c2daa00SOllivier Robert 				break;
13179c2daa00SOllivier Robert 			    case 3:
13189c2daa00SOllivier Robert 				switch (flags & 3) {
13199c2daa00SOllivier Robert 				    case 0: /* It is unclear exactly when the
13209c2daa00SOllivier Robert 					       Arcron changes from DST->ST and
13219c2daa00SOllivier Robert 					       ST->DST. Testing has shown this
13229c2daa00SOllivier Robert 					       to be irregular. For the time
13239c2daa00SOllivier Robert 					       being, let the OS decide. */
13249c2daa00SOllivier Robert 					local.tm_isdst = 0;
13259c2daa00SOllivier Robert #ifdef DEBUG
13269c2daa00SOllivier Robert 					if (debug)
13279c2daa00SOllivier Robert 					    printf ("arc: DST = 00 (0)\n");
13289c2daa00SOllivier Robert #endif
13299c2daa00SOllivier Robert 					break;
13309c2daa00SOllivier Robert 				    case 1: /* dst->st time */
13319c2daa00SOllivier Robert 					local.tm_isdst = -1;
13329c2daa00SOllivier Robert #ifdef DEBUG
13339c2daa00SOllivier Robert 					if (debug)
13349c2daa00SOllivier Robert 					    printf ("arc: DST = 01 (1)\n");
13359c2daa00SOllivier Robert #endif
13369c2daa00SOllivier Robert 					break;
13379c2daa00SOllivier Robert 				    case 2: /* st->dst time */
13389c2daa00SOllivier Robert 					local.tm_isdst = -1;
13399c2daa00SOllivier Robert #ifdef DEBUG
13409c2daa00SOllivier Robert 					if (debug)
13419c2daa00SOllivier Robert 					    printf ("arc: DST = 10 (2)\n");
13429c2daa00SOllivier Robert #endif
13439c2daa00SOllivier Robert 					break;
13449c2daa00SOllivier Robert 				    case 3: /* dst time */
13459c2daa00SOllivier Robert 				        local.tm_isdst = 1;
13469c2daa00SOllivier Robert #ifdef DEBUG
13479c2daa00SOllivier Robert 					if (debug)
13489c2daa00SOllivier Robert 					    printf ("arc: DST = 11 (3)\n");
13499c2daa00SOllivier Robert #endif
13509c2daa00SOllivier Robert 					break;
13519c2daa00SOllivier Robert 				}
13529c2daa00SOllivier Robert 				break;
13539c2daa00SOllivier Robert 			    default:
13549c2daa00SOllivier Robert 				msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
13559c2daa00SOllivier Robert 					peer->MODE);
13569c2daa00SOllivier Robert 				return;
13579c2daa00SOllivier Robert 				break;
13589c2daa00SOllivier Robert 			}
13599c2daa00SOllivier Robert 			unixtime = mktime (&local);
13609c2daa00SOllivier Robert 			if ((gmtp = gmtime (&unixtime)) == NULL)
13619c2daa00SOllivier Robert 			{
13629c2daa00SOllivier Robert 				pp->lencode = 0;
13639c2daa00SOllivier Robert 				refclock_report (peer, CEVNT_FAULT);
13649c2daa00SOllivier Robert 				return;
13659c2daa00SOllivier Robert 			}
13669c2daa00SOllivier Robert 			pp->year = gmtp->tm_year+1900;
13679c2daa00SOllivier Robert 			month = gmtp->tm_mon+1;
13689c2daa00SOllivier Robert 			pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
13699c2daa00SOllivier Robert 			/* pp->day = gmtp->tm_yday; */
13709c2daa00SOllivier Robert 			pp->hour = gmtp->tm_hour;
13719c2daa00SOllivier Robert 			pp->minute = gmtp->tm_min;
13729c2daa00SOllivier Robert 			pp->second = gmtp->tm_sec;
13739c2daa00SOllivier Robert #ifdef DEBUG
13749c2daa00SOllivier Robert 			if (debug)
13759c2daa00SOllivier Robert 			{
13769c2daa00SOllivier Robert 				printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
13779c2daa00SOllivier Robert 					pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
13789c2daa00SOllivier Robert 					pp->second);
13799c2daa00SOllivier Robert 			}
13809c2daa00SOllivier Robert #endif
13819c2daa00SOllivier Robert 		} else
13829c2daa00SOllivier Robert 		{
13839c2daa00SOllivier Robert 			/*
13849c2daa00SOllivier Robert 			* For more rational sites distributing UTC
13859c2daa00SOllivier Robert 			*/
13869c2daa00SOllivier Robert 			pp->day    = ymd2yd(pp->year,month,pp->day);
13879c2daa00SOllivier Robert 		}
13889c2daa00SOllivier Robert 	}
13899c2daa00SOllivier Robert 
13909c2daa00SOllivier Robert 	if (peer->MODE == 0) { /* compatiblity to original version */
13919c2daa00SOllivier Robert 				/* If clock signal quality is
13929c2daa00SOllivier Robert 				 * unknown, revert to default PRECISION...*/
13939c2daa00SOllivier Robert 		if(up->quality == QUALITY_UNKNOWN) {
13949c2daa00SOllivier Robert 			peer->precision = PRECISION;
13959c2daa00SOllivier Robert 		} else { /* ...else improve precision if flag3 is set... */
1396c0b746e5SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1397c0b746e5SOllivier Robert 					   HIGHPRECISION : PRECISION);
1398c0b746e5SOllivier Robert 		}
13999c2daa00SOllivier Robert 	} else {
14009c2daa00SOllivier Robert 		if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
14019c2daa00SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
14029c2daa00SOllivier Robert 					   HIGHPRECISION : PRECISION);
14039c2daa00SOllivier Robert 		} else if (up->quality == QUALITY_UNKNOWN) {
14049c2daa00SOllivier Robert 			peer->precision = PRECISION;
14059c2daa00SOllivier Robert 		} else {
14069c2daa00SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
14079c2daa00SOllivier Robert 					   HIGHPRECISION : PRECISION);
14089c2daa00SOllivier Robert 		}
14099c2daa00SOllivier Robert 	}
1410c0b746e5SOllivier Robert 
1411c0b746e5SOllivier Robert 	/* Notice and log any change (eg from initial defaults) for flags. */
1412c0b746e5SOllivier Robert 	if(up->saved_flags != pp->sloppyclockflag) {
14139c2daa00SOllivier Robert #ifdef DEBUG
1414c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1415c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1416c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1417c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1418c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1419c0b746e5SOllivier Robert 		/* Note effects of flags changing... */
1420c0b746e5SOllivier Robert 		if(debug) {
1421c0b746e5SOllivier Robert 			printf("arc: PRECISION = %d.\n", peer->precision);
1422c0b746e5SOllivier Robert 		}
1423c0b746e5SOllivier Robert #endif
1424c0b746e5SOllivier Robert 		up->saved_flags = pp->sloppyclockflag;
1425c0b746e5SOllivier Robert 	}
1426c0b746e5SOllivier Robert 
1427c0b746e5SOllivier Robert 	/* Note time of last believable timestamp. */
1428c0b746e5SOllivier Robert 	pp->lastrec = up->lastrec;
1429c0b746e5SOllivier Robert 
1430c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1431c0b746e5SOllivier Robert 	/* Find out if a leap-second might just have happened...
1432c0b746e5SOllivier Robert 	   (ie is this the first hour of the first day of Jan or Jul?)
1433c0b746e5SOllivier Robert 	*/
1434c0b746e5SOllivier Robert 	if((pp->hour == 0) &&
1435c0b746e5SOllivier Robert 	   (pp->day == 1) &&
1436c0b746e5SOllivier Robert 	   ((month == 1) || (month == 7))) {
1437c0b746e5SOllivier Robert 		if(possible_leap >= 0) {
1438c0b746e5SOllivier Robert 			/* A leap may have happened, and no resync has started yet...*/
1439c0b746e5SOllivier Robert 			possible_leap = 1;
1440c0b746e5SOllivier Robert 		}
1441c0b746e5SOllivier Robert 	} else {
1442c0b746e5SOllivier Robert 		/* Definitely not leap-second territory... */
1443c0b746e5SOllivier Robert 		possible_leap = 0;
1444c0b746e5SOllivier Robert 	}
1445c0b746e5SOllivier Robert #endif
1446c0b746e5SOllivier Robert 
1447c0b746e5SOllivier Robert 	if (!refclock_process(pp)) {
14489c2daa00SOllivier Robert 		pp->lencode = 0;
1449c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
1450c0b746e5SOllivier Robert 		return;
1451c0b746e5SOllivier Robert 	}
14529c2daa00SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
1453c0b746e5SOllivier Robert 	refclock_receive(peer);
1454c0b746e5SOllivier Robert }
1455c0b746e5SOllivier Robert 
1456c0b746e5SOllivier Robert 
1457c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */
1458c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */
1459c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */
14602b15cb3dSCy Schubert static  void    request_time    (int, struct peer *);
1461c0b746e5SOllivier Robert static void
1462c0b746e5SOllivier Robert request_time(
1463c0b746e5SOllivier Robert 	int unit,
1464c0b746e5SOllivier Robert 	struct peer *peer
1465c0b746e5SOllivier Robert 	)
1466c0b746e5SOllivier Robert {
1467c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
14682b15cb3dSCy Schubert 	register struct arcunit *up = pp->unitptr;
1469c0b746e5SOllivier Robert #ifdef DEBUG
1470c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1471c0b746e5SOllivier Robert #endif
1472c0b746e5SOllivier Robert 	if (!send_slow(up, pp->io.fd, "o\r")) {
14739c2daa00SOllivier Robert #ifdef DEBUG
14749c2daa00SOllivier Robert 		if (debug) {
14759c2daa00SOllivier Robert 			printf("arc: unit %d: problem sending", unit);
14769c2daa00SOllivier Robert 		}
1477c0b746e5SOllivier Robert #endif
14789c2daa00SOllivier Robert 		pp->lencode = 0;
1479c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1480c0b746e5SOllivier Robert 		return;
1481c0b746e5SOllivier Robert 	}
1482c0b746e5SOllivier Robert 	pp->polls++;
1483c0b746e5SOllivier Robert }
1484c0b746e5SOllivier Robert 
1485c0b746e5SOllivier Robert /*
1486c0b746e5SOllivier Robert  * arc_poll - called by the transmit procedure
1487c0b746e5SOllivier Robert  */
1488c0b746e5SOllivier Robert static void
1489c0b746e5SOllivier Robert arc_poll(
1490c0b746e5SOllivier Robert 	int unit,
1491c0b746e5SOllivier Robert 	struct peer *peer
1492c0b746e5SOllivier Robert 	)
1493c0b746e5SOllivier Robert {
1494c0b746e5SOllivier Robert 	register struct arcunit *up;
1495c0b746e5SOllivier Robert 	struct refclockproc *pp;
1496c0b746e5SOllivier Robert 	int resync_needed;              /* Should we start a resync? */
1497c0b746e5SOllivier Robert 
1498c0b746e5SOllivier Robert 	pp = peer->procptr;
14992b15cb3dSCy Schubert 	up = pp->unitptr;
15009c2daa00SOllivier Robert #if 0
1501c0b746e5SOllivier Robert 	pp->lencode = 0;
1502c0b746e5SOllivier Robert 	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
15039c2daa00SOllivier Robert #endif
1504c0b746e5SOllivier Robert 
1505c0b746e5SOllivier Robert #if 0
1506c0b746e5SOllivier Robert 	/* Flush input. */
1507c0b746e5SOllivier Robert 	tcflush(pp->io.fd, TCIFLUSH);
1508c0b746e5SOllivier Robert #endif
1509c0b746e5SOllivier Robert 
1510c0b746e5SOllivier Robert 	/* Resync if our next scheduled resync time is here or has passed. */
15119c2daa00SOllivier Robert 	resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
15129c2daa00SOllivier Robert 			  (up->next_resync <= current_time) );
1513c0b746e5SOllivier Robert 
1514c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1515c0b746e5SOllivier Robert 	/*
1516c0b746e5SOllivier Robert 	  Try to catch a potential leap-second insertion or deletion quickly.
1517c0b746e5SOllivier Robert 
1518c0b746e5SOllivier Robert 	  In addition to the normal NTP fun of clocks that don't report
1519c0b746e5SOllivier Robert 	  leap-seconds spooking their hosts, this clock does not even
1520c0b746e5SOllivier Robert 	  sample the radio sugnal the whole time, so may miss a
1521c0b746e5SOllivier Robert 	  leap-second insertion or deletion for up to a whole sample
1522c0b746e5SOllivier Robert 	  time.
1523c0b746e5SOllivier Robert 
1524c0b746e5SOllivier Robert 	  To try to minimise this effect, if in the first few minutes of
1525c0b746e5SOllivier Robert 	  the day immediately following a leap-second-insertion point
1526c0b746e5SOllivier Robert 	  (ie in the first hour of the first day of the first and sixth
1527c0b746e5SOllivier Robert 	  months), and if the last resync was in the previous day, and a
1528c0b746e5SOllivier Robert 	  resync is not already in progress, resync the clock
1529c0b746e5SOllivier Robert 	  immediately.
1530c0b746e5SOllivier Robert 
1531c0b746e5SOllivier Robert 	*/
1532c0b746e5SOllivier Robert 	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1533c0b746e5SOllivier Robert 	   (!up->resyncing)) {          /* No resync in progress yet. */
1534c0b746e5SOllivier Robert 		resync_needed = 1;
1535c0b746e5SOllivier Robert 		possible_leap = -1;          /* Prevent multiple resyncs. */
1536c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1537c0b746e5SOllivier Robert 	}
1538c0b746e5SOllivier Robert #endif
1539c0b746e5SOllivier Robert 
1540c0b746e5SOllivier Robert 	/* Do a resync if required... */
1541c0b746e5SOllivier Robert 	if(resync_needed) {
1542c0b746e5SOllivier Robert 		/* First, reset quality value to `unknown' so we can detect */
1543c0b746e5SOllivier Robert 		/* when a quality message has been responded to by this     */
1544c0b746e5SOllivier Robert 		/* being set to some other value.                           */
1545c0b746e5SOllivier Robert 		up->quality = QUALITY_UNKNOWN;
1546c0b746e5SOllivier Robert 
1547c0b746e5SOllivier Robert 		/* Note that we are resyncing... */
1548c0b746e5SOllivier Robert 		up->resyncing = 1;
1549c0b746e5SOllivier Robert 
1550c0b746e5SOllivier Robert 		/* Now actually send the resync command and an immediate poll. */
1551c0b746e5SOllivier Robert #ifdef DEBUG
1552c0b746e5SOllivier Robert 		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1553c0b746e5SOllivier Robert #endif
1554c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1555c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "h\r");
1556c0b746e5SOllivier Robert 
1557c0b746e5SOllivier Robert 		/* Schedule our next resync... */
1558c0b746e5SOllivier Robert 		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1559c0b746e5SOllivier Robert 
1560c0b746e5SOllivier Robert 		/* Drop through to request time if appropriate. */
1561c0b746e5SOllivier Robert 	}
1562c0b746e5SOllivier Robert 
1563c0b746e5SOllivier Robert 	/* If clock quality is too poor to trust, indicate a fault. */
1564c0b746e5SOllivier Robert 	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1565c0b746e5SOllivier Robert 	/* we'll cross our fingers and just hope that the thing     */
1566c0b746e5SOllivier Robert 	/* synced so quickly we did not catch it---we'll            */
1567c0b746e5SOllivier Robert 	/* double-check the clock is OK elsewhere.                  */
1568c0b746e5SOllivier Robert 	if(
1569c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1570c0b746e5SOllivier Robert 		(up->quality != QUALITY_UNKNOWN) &&
1571c0b746e5SOllivier Robert #else
1572c0b746e5SOllivier Robert 		(up->quality == QUALITY_UNKNOWN) ||
1573c0b746e5SOllivier Robert #endif
1574c0b746e5SOllivier Robert 		(up->quality < MIN_CLOCK_QUALITY_OK)) {
1575c0b746e5SOllivier Robert #ifdef DEBUG
1576c0b746e5SOllivier Robert 		if(debug) {
1577c0b746e5SOllivier Robert 			printf("arc: clock quality %d too poor.\n", up->quality);
1578c0b746e5SOllivier Robert 		}
1579c0b746e5SOllivier Robert #endif
15809c2daa00SOllivier Robert 		pp->lencode = 0;
1581c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1582c0b746e5SOllivier Robert 		return;
1583c0b746e5SOllivier Robert 	}
1584c0b746e5SOllivier Robert 	/* This is the normal case: request a timestamp. */
1585c0b746e5SOllivier Robert 	request_time(unit, peer);
1586c0b746e5SOllivier Robert }
1587c0b746e5SOllivier Robert 
1588c0b746e5SOllivier Robert #else
15892b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT
1590c0b746e5SOllivier Robert #endif
1591