xref: /freebsd/contrib/ntp/ntpd/refclock_arc.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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);
648*a466cc55SCy 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 {
873c0b746e5SOllivier Robert 	register struct arcunit *up;
874c0b746e5SOllivier Robert 	struct refclockproc *pp;
875c0b746e5SOllivier Robert 	struct peer *peer;
876c0b746e5SOllivier Robert 	char c;
8779c2daa00SOllivier Robert 	int i, n, wday, month, flags, status;
878c0b746e5SOllivier Robert 	int arc_last_offset;
8799c2daa00SOllivier Robert 	static int quality_average = 0;
8809c2daa00SOllivier Robert 	static int quality_sum = 0;
8819c2daa00SOllivier Robert 	static int quality_polls = 0;
882c0b746e5SOllivier Robert 
883c0b746e5SOllivier Robert 	/*
884c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp
885c0b746e5SOllivier Robert 	 */
8862b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
887c0b746e5SOllivier Robert 	pp = peer->procptr;
8882b15cb3dSCy Schubert 	up = pp->unitptr;
889c0b746e5SOllivier Robert 
890c0b746e5SOllivier Robert 
891c0b746e5SOllivier Robert 	/*
892c0b746e5SOllivier Robert 	  If the command buffer is empty, and we are resyncing, insert a
893c0b746e5SOllivier Robert 	  g\r quality request into it to poll for signal quality again.
894c0b746e5SOllivier Robert 	*/
895c0b746e5SOllivier Robert 	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
896c0b746e5SOllivier Robert #ifdef DEBUG
897c0b746e5SOllivier Robert 		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
898c0b746e5SOllivier Robert #endif
899c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "g\r");
900c0b746e5SOllivier Robert 	}
901c0b746e5SOllivier Robert 
902c0b746e5SOllivier Robert 	/*
903c0b746e5SOllivier Robert 	  The `arc_last_offset' is the offset in lastcode[] of the last byte
904c0b746e5SOllivier Robert 	  received, and which we assume actually received the input
905c0b746e5SOllivier Robert 	  timestamp.
906c0b746e5SOllivier Robert 
907c0b746e5SOllivier Robert 	  (When we get round to using tty_clk and it is available, we
908c0b746e5SOllivier Robert 	  assume that we will receive the whole timecode with the
909c0b746e5SOllivier Robert 	  trailing \r, and that that \r will be timestamped.  But this
910c0b746e5SOllivier Robert 	  assumption also works if receive the characters one-by-one.)
911c0b746e5SOllivier Robert 	*/
912c0b746e5SOllivier Robert 	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
913c0b746e5SOllivier Robert 
914c0b746e5SOllivier Robert 	/*
915c0b746e5SOllivier Robert 	  We catch a timestamp iff:
916c0b746e5SOllivier Robert 
917c0b746e5SOllivier Robert 	  * The command code is `o' for a timestamp.
918c0b746e5SOllivier Robert 
919c0b746e5SOllivier Robert 	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
920c0b746e5SOllivier Robert 	  exactly char in the buffer (the command code) so that we
921c0b746e5SOllivier Robert 	  only sample the first character of the timecode as our
922c0b746e5SOllivier Robert 	  `on-time' character.
923c0b746e5SOllivier Robert 
924c0b746e5SOllivier Robert 	  * The first character in the buffer is not the echoed `\r'
925c0b746e5SOllivier Robert 	  from the `o` command (so if we are to timestamp an `\r' it
926c0b746e5SOllivier Robert 	  must not be first in the receive buffer with lencode==1.
927c0b746e5SOllivier Robert 	  (Even if we had other characters following it, we probably
928c0b746e5SOllivier Robert 	  would have a premature timestamp on the '\r'.)
929c0b746e5SOllivier Robert 
930c0b746e5SOllivier Robert 	  * We have received at least one character (I cannot imagine
931c0b746e5SOllivier Robert 	  how it could be otherwise, but anyway...).
932c0b746e5SOllivier Robert 	*/
933c0b746e5SOllivier Robert 	c = rbufp->recv_buffer[0];
934c0b746e5SOllivier Robert 	if((pp->a_lastcode[0] == 'o') &&
935c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES
936c0b746e5SOllivier Robert 	   (pp->lencode == 1) &&
937c0b746e5SOllivier Robert #endif
938c0b746e5SOllivier Robert 	   ((pp->lencode != 1) || (c != '\r')) &&
939c0b746e5SOllivier Robert 	   (arc_last_offset >= 1)) {
940c0b746e5SOllivier Robert 		/* Note that the timestamp should be corrected if >1 char rcvd. */
941c0b746e5SOllivier Robert 		l_fp timestamp;
942c0b746e5SOllivier Robert 		timestamp = rbufp->recv_time;
943c0b746e5SOllivier Robert #ifdef DEBUG
944c0b746e5SOllivier Robert 		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
945c0b746e5SOllivier Robert 			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
9462b15cb3dSCy Schubert 			       ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')),
947c0b746e5SOllivier Robert 			       rbufp->recv_length);
948c0b746e5SOllivier Robert 		}
949c0b746e5SOllivier Robert #endif
950c0b746e5SOllivier Robert 
951c0b746e5SOllivier Robert 		/*
952c0b746e5SOllivier Robert 		  Now correct timestamp by offset of last byte received---we
953c0b746e5SOllivier Robert 		  subtract from the receive time the delay implied by the
954c0b746e5SOllivier Robert 		  extra characters received.
955c0b746e5SOllivier Robert 
956c0b746e5SOllivier Robert 		  Reject the input if the resulting code is too long, but
957c0b746e5SOllivier Robert 		  allow for the trailing \r, normally not used but a good
958c0b746e5SOllivier Robert 		  handle for tty_clk or somesuch kernel timestamper.
959c0b746e5SOllivier Robert 		*/
960c0b746e5SOllivier Robert 		if(arc_last_offset > LENARC) {
9619c2daa00SOllivier Robert #ifdef DEBUG
962c0b746e5SOllivier Robert 			if(debug) {
963c0b746e5SOllivier Robert 				printf("arc: input code too long (%d cf %d); rejected.\n",
964c0b746e5SOllivier Robert 				       arc_last_offset, LENARC);
965c0b746e5SOllivier Robert 			}
966c0b746e5SOllivier Robert #endif
967c0b746e5SOllivier Robert 			pp->lencode = 0;
968c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
969c0b746e5SOllivier Robert 			return;
970c0b746e5SOllivier Robert 		}
971c0b746e5SOllivier Robert 
972c0b746e5SOllivier Robert 		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
9739c2daa00SOllivier Robert #ifdef DEBUG
974c0b746e5SOllivier Robert 		if(debug > 1) {
975c0b746e5SOllivier Robert 			printf(
976c0b746e5SOllivier Robert 				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
977c0b746e5SOllivier Robert 				((rbufp->recv_length > 1) ? "*** " : ""),
978c0b746e5SOllivier Robert 				rbufp->recv_length,
979c0b746e5SOllivier Robert 				arc_last_offset,
980c0b746e5SOllivier Robert 				mfptoms((unsigned long)0,
981c0b746e5SOllivier Robert 					charoffsets[arc_last_offset],
982c0b746e5SOllivier Robert 					1));
983c0b746e5SOllivier Robert 		}
984c0b746e5SOllivier Robert #endif
985c0b746e5SOllivier Robert 
986c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
987c0b746e5SOllivier Robert 		/*
988c0b746e5SOllivier Robert 		  If taking multiple samples, capture the current adjusted
989c0b746e5SOllivier Robert 		  sample iff:
990c0b746e5SOllivier Robert 
991c0b746e5SOllivier Robert 		  * No timestamp has yet been captured (it is zero), OR
992c0b746e5SOllivier Robert 
993c0b746e5SOllivier Robert 		  * This adjusted timestamp is earlier than the one already
994c0b746e5SOllivier Robert 		  captured, on the grounds that this one suffered less
995c0b746e5SOllivier Robert 		  delay in being delivered to us and is more accurate.
996c0b746e5SOllivier Robert 
997c0b746e5SOllivier Robert 		*/
998c0b746e5SOllivier Robert 		if(L_ISZERO(&(up->lastrec)) ||
999c0b746e5SOllivier Robert 		   L_ISGEQ(&(up->lastrec), &timestamp))
1000c0b746e5SOllivier Robert #endif
1001c0b746e5SOllivier Robert 		{
10029c2daa00SOllivier Robert #ifdef DEBUG
1003c0b746e5SOllivier Robert 			if(debug > 1) {
1004c0b746e5SOllivier Robert 				printf("arc: system timestamp captured.\n");
1005c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES
1006c0b746e5SOllivier Robert 				if(!L_ISZERO(&(up->lastrec))) {
1007c0b746e5SOllivier Robert 					l_fp diff;
1008c0b746e5SOllivier Robert 					diff = up->lastrec;
1009c0b746e5SOllivier Robert 					L_SUB(&diff, &timestamp);
1010c0b746e5SOllivier Robert 					printf("arc: adjusted timestamp by -%sms.\n",
10112b15cb3dSCy Schubert 					       mfptoms(diff.l_ui, diff.l_uf, 3));
1012c0b746e5SOllivier Robert 				}
1013c0b746e5SOllivier Robert #endif
1014c0b746e5SOllivier Robert 			}
1015c0b746e5SOllivier Robert #endif
1016c0b746e5SOllivier Robert 			up->lastrec = timestamp;
1017c0b746e5SOllivier Robert 		}
1018c0b746e5SOllivier Robert 
1019c0b746e5SOllivier Robert 	}
1020c0b746e5SOllivier Robert 
1021c0b746e5SOllivier Robert 	/* Just in case we still have lots of rubbish in the buffer... */
1022c0b746e5SOllivier Robert 	/* ...and to avoid the same timestamp being reused by mistake, */
1023c0b746e5SOllivier Robert 	/* eg on receipt of the \r coming in on its own after the      */
1024c0b746e5SOllivier Robert 	/* timecode.						       */
1025c0b746e5SOllivier Robert 	if(pp->lencode >= LENARC) {
10269c2daa00SOllivier Robert #ifdef DEBUG
1027c0b746e5SOllivier Robert 		if(debug && (rbufp->recv_buffer[0] != '\r'))
1028c0b746e5SOllivier Robert 		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
1029c0b746e5SOllivier Robert #endif
1030c0b746e5SOllivier Robert 		pp->lencode = 0;
1031c0b746e5SOllivier Robert 		return;
1032c0b746e5SOllivier Robert 	}
1033c0b746e5SOllivier Robert 
1034c0b746e5SOllivier Robert 	/* Append input to code buffer, avoiding overflow. */
1035c0b746e5SOllivier Robert 	for(i = 0; i < rbufp->recv_length; i++) {
1036c0b746e5SOllivier Robert 		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
1037c0b746e5SOllivier Robert 		c = rbufp->recv_buffer[i];
1038c0b746e5SOllivier Robert 
1039c0b746e5SOllivier Robert 		/* Drop trailing '\r's and drop `h' command echo totally. */
1040c0b746e5SOllivier Robert 		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
1041c0b746e5SOllivier Robert 
1042c0b746e5SOllivier Robert 		/*
1043c0b746e5SOllivier Robert 		  If we've just put an `o' in the lastcode[0], clear the
1044c0b746e5SOllivier Robert 		  timestamp in anticipation of a timecode arriving soon.
1045c0b746e5SOllivier Robert 
1046c0b746e5SOllivier Robert 		  We would expect to get to process this before any of the
1047c0b746e5SOllivier Robert 		  timecode arrives.
1048c0b746e5SOllivier Robert 		*/
1049c0b746e5SOllivier Robert 		if((c == 'o') && (pp->lencode == 1)) {
1050c0b746e5SOllivier Robert 			L_CLR(&(up->lastrec));
10519c2daa00SOllivier Robert #ifdef DEBUG
1052c0b746e5SOllivier Robert 			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
1053c0b746e5SOllivier Robert #endif
1054c0b746e5SOllivier Robert 		}
1055c0b746e5SOllivier Robert 	}
10569c2daa00SOllivier Robert 	if (pp->lencode == 0) return;
1057c0b746e5SOllivier Robert 
1058c0b746e5SOllivier Robert 	/* Handle a quality message. */
1059c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] == 'g') {
1060c0b746e5SOllivier Robert 		int r, q;
1061c0b746e5SOllivier Robert 
1062c0b746e5SOllivier Robert 		if(pp->lencode < 3) { return; } /* Need more data... */
1063c0b746e5SOllivier Robert 		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
1064c0b746e5SOllivier Robert 		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
1065c0b746e5SOllivier Robert 		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
1066c0b746e5SOllivier Robert 		   ((r & 0x70) != 0x30)) {
1067c0b746e5SOllivier Robert 			/* Badly formatted response. */
10689c2daa00SOllivier Robert #ifdef DEBUG
1069c0b746e5SOllivier Robert 			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
1070c0b746e5SOllivier Robert #endif
1071c0b746e5SOllivier Robert 			return;
1072c0b746e5SOllivier Robert 		}
1073c0b746e5SOllivier Robert 		if(r == '3') { /* Only use quality value whilst sync in progress. */
10749c2daa00SOllivier Robert 			if (up->quality_stamp < current_time) {
10759c2daa00SOllivier Robert 				struct calendar cal;
10769c2daa00SOllivier Robert 				l_fp new_stamp;
10779c2daa00SOllivier Robert 
10789c2daa00SOllivier Robert 				get_systime (&new_stamp);
10799c2daa00SOllivier Robert 				caljulian (new_stamp.l_ui, &cal);
10809c2daa00SOllivier Robert 				up->quality_stamp =
10819c2daa00SOllivier Robert 					current_time + 60 - cal.second + 5;
10829c2daa00SOllivier Robert 				quality_sum = 0;
10839c2daa00SOllivier Robert 				quality_polls = 0;
10849c2daa00SOllivier Robert 			}
10859c2daa00SOllivier Robert 			quality_sum += (q & 0xf);
10869c2daa00SOllivier Robert 			quality_polls++;
10879c2daa00SOllivier Robert 			quality_average = (quality_sum / quality_polls);
1088c0b746e5SOllivier Robert #ifdef DEBUG
10899c2daa00SOllivier Robert 			if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
1090c0b746e5SOllivier Robert #endif
1091c0b746e5SOllivier Robert 		} else if( /* (r == '2') && */ up->resyncing) {
10929c2daa00SOllivier Robert 			up->quality = quality_average;
1093c0b746e5SOllivier Robert #ifdef DEBUG
1094c0b746e5SOllivier Robert 			if(debug)
1095c0b746e5SOllivier Robert 			{
1096c0b746e5SOllivier Robert 				printf("arc: sync finished, signal quality %d: %s\n",
1097c0b746e5SOllivier Robert 				       up->quality,
1098c0b746e5SOllivier Robert 				       quality_action(up->quality));
1099c0b746e5SOllivier Robert 			}
1100c0b746e5SOllivier Robert #endif
1101c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE,
1102c0b746e5SOllivier Robert 				"ARCRON: sync finished, signal quality %d: %s",
1103c0b746e5SOllivier Robert 				up->quality,
1104c0b746e5SOllivier Robert 				quality_action(up->quality));
1105c0b746e5SOllivier Robert 			up->resyncing = 0; /* Resync is over. */
11069c2daa00SOllivier Robert 			quality_average = 0;
11079c2daa00SOllivier Robert 			quality_sum = 0;
11089c2daa00SOllivier Robert 			quality_polls = 0;
1109c0b746e5SOllivier Robert 
1110c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1111c0b746e5SOllivier Robert 			/* Clock quality dubious; resync earlier than usual. */
1112c0b746e5SOllivier Robert 			if((up->quality == QUALITY_UNKNOWN) ||
1113c0b746e5SOllivier Robert 			   (up->quality < MIN_CLOCK_QUALITY_OK))
1114c0b746e5SOllivier Robert 			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
1115c0b746e5SOllivier Robert #endif
1116c0b746e5SOllivier Robert 		}
1117c0b746e5SOllivier Robert 		pp->lencode = 0;
1118c0b746e5SOllivier Robert 		return;
1119c0b746e5SOllivier Robert 	}
1120c0b746e5SOllivier Robert 
1121c0b746e5SOllivier Robert 	/* Stop now if this is not a timecode message. */
1122c0b746e5SOllivier Robert 	if(pp->a_lastcode[0] != 'o') {
1123c0b746e5SOllivier Robert 		pp->lencode = 0;
1124c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1125c0b746e5SOllivier Robert 		return;
1126c0b746e5SOllivier Robert 	}
1127c0b746e5SOllivier Robert 
1128c0b746e5SOllivier Robert 	/* If we don't have enough data, wait for more... */
1129c0b746e5SOllivier Robert 	if(pp->lencode < LENARC) { return; }
1130c0b746e5SOllivier Robert 
1131c0b746e5SOllivier Robert 
1132c0b746e5SOllivier Robert 	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
11339c2daa00SOllivier Robert #ifdef DEBUG
1134c0b746e5SOllivier Robert 	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1135c0b746e5SOllivier Robert #endif
1136c0b746e5SOllivier Robert 
1137c0b746e5SOllivier Robert 	/* But check that we actually captured a system timestamp on it. */
1138c0b746e5SOllivier Robert 	if(L_ISZERO(&(up->lastrec))) {
11399c2daa00SOllivier Robert #ifdef DEBUG
1140c0b746e5SOllivier Robert 		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1141c0b746e5SOllivier Robert #endif
1142c0b746e5SOllivier Robert 		pp->lencode = 0;
1143c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1144c0b746e5SOllivier Robert 		return;
1145c0b746e5SOllivier Robert 	}
1146c0b746e5SOllivier Robert 	/*
1147c0b746e5SOllivier Robert 	  Append a mark of the clock's received signal quality for the
1148c0b746e5SOllivier Robert 	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1149c0b746e5SOllivier Robert 	  quality value to `6' for his s/w) and terminate the string for
1150c0b746e5SOllivier Robert 	  sure.  This should not go off the buffer end.
1151c0b746e5SOllivier Robert 	*/
1152c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1153c0b746e5SOllivier Robert 				       '6' : ('0' + up->quality));
1154c0b746e5SOllivier Robert 	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1155c0b746e5SOllivier Robert 
11569c2daa00SOllivier Robert #ifdef PRE_NTP420
1157c0b746e5SOllivier Robert 	/* We don't use the micro-/milli- second part... */
1158c0b746e5SOllivier Robert 	pp->usec = 0;
1159c0b746e5SOllivier Robert 	pp->msec = 0;
11609c2daa00SOllivier Robert #else
11619c2daa00SOllivier Robert 	/* We don't use the nano-second part... */
11629c2daa00SOllivier Robert 	pp->nsec = 0;
11639c2daa00SOllivier Robert #endif
1164c0b746e5SOllivier Robert 	/* Validate format and numbers. */
1165ea906c41SOllivier Robert 	if (pp->a_lastcode[0] != 'o'
1166ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 1, &pp->hour)
1167ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 3, &pp->minute)
1168ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 5, &pp->second)
1169ea906c41SOllivier Robert 		|| !get1(pp->a_lastcode + 7, &wday)
1170ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 8, &pp->day)
1171ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 10, &month)
1172ea906c41SOllivier Robert 		|| !get2(pp->a_lastcode + 12, &pp->year)) {
11739c2daa00SOllivier Robert #ifdef DEBUG
1174c0b746e5SOllivier Robert 		/* Would expect to have caught major problems already... */
1175c0b746e5SOllivier Robert 		if(debug) { printf("arc: badly formatted data.\n"); }
1176c0b746e5SOllivier Robert #endif
11779c2daa00SOllivier Robert 		pp->lencode = 0;
1178c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1179c0b746e5SOllivier Robert 		return;
1180c0b746e5SOllivier Robert 	}
1181ea906c41SOllivier Robert 	flags = pp->a_lastcode[14];
1182ea906c41SOllivier Robert 	status = pp->a_lastcode[15];
1183ea906c41SOllivier Robert #ifdef DEBUG
1184ea906c41SOllivier Robert 	if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
1185ea906c41SOllivier Robert #endif
1186ea906c41SOllivier Robert 	n = 9;
1187ea906c41SOllivier Robert 
1188c0b746e5SOllivier Robert 	/*
1189c0b746e5SOllivier Robert 	  Validate received values at least enough to prevent internal
1190c0b746e5SOllivier Robert 	  array-bounds problems, etc.
1191c0b746e5SOllivier Robert 	*/
1192c0b746e5SOllivier Robert 	if((pp->hour < 0) || (pp->hour > 23) ||
1193c0b746e5SOllivier Robert 	   (pp->minute < 0) || (pp->minute > 59) ||
1194c0b746e5SOllivier Robert 	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1195c0b746e5SOllivier Robert 	   (wday < 1) || (wday > 7) ||
1196c0b746e5SOllivier Robert 	   (pp->day < 1) || (pp->day > 31) ||
1197c0b746e5SOllivier Robert 	   (month < 1) || (month > 12) ||
1198c0b746e5SOllivier Robert 	   (pp->year < 0) || (pp->year > 99)) {
1199c0b746e5SOllivier Robert 		/* Data out of range. */
12009c2daa00SOllivier Robert 		pp->lencode = 0;
1201c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
1202c0b746e5SOllivier Robert 		return;
1203c0b746e5SOllivier Robert 	}
1204c0b746e5SOllivier Robert 
12059c2daa00SOllivier Robert 
12069c2daa00SOllivier Robert 	if(peer->MODE == 0) { /* compatiblity to original version */
12079c2daa00SOllivier Robert 		int bst = flags;
12089c2daa00SOllivier Robert 		/* Check that BST/UTC bits are the complement of one another. */
12099c2daa00SOllivier Robert 		if(!(bst & 2) == !(bst & 4)) {
12109c2daa00SOllivier Robert 			pp->lencode = 0;
12119c2daa00SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
12129c2daa00SOllivier Robert 			return;
12139c2daa00SOllivier Robert 		}
12149c2daa00SOllivier Robert 	}
1215c0b746e5SOllivier Robert 	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1216c0b746e5SOllivier Robert 
1217c0b746e5SOllivier Robert 	/* Year-2000 alert! */
1218c0b746e5SOllivier Robert 	/* Attempt to wrap 2-digit date into sensible window. */
1219c0b746e5SOllivier Robert 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
1220c0b746e5SOllivier Robert 	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
1221c0b746e5SOllivier Robert 	/*
1222c0b746e5SOllivier Robert 	  Attempt to do the right thing by screaming that the code will
1223c0b746e5SOllivier Robert 	  soon break when we get to the end of its useful life.  What a
1224c0b746e5SOllivier Robert 	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1225c0b746e5SOllivier Robert 	*/
1226c0b746e5SOllivier Robert 	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
1227c0b746e5SOllivier Robert 		/*This should get attention B^> */
1228c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,
1229c0b746e5SOllivier Robert 			"ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1230c0b746e5SOllivier Robert 	}
1231c0b746e5SOllivier Robert #ifdef DEBUG
1232c0b746e5SOllivier Robert 	if(debug) {
1233c0b746e5SOllivier Robert 		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1234c0b746e5SOllivier Robert 		       n,
1235c0b746e5SOllivier Robert 		       pp->hour, pp->minute, pp->second,
12369c2daa00SOllivier Robert 		       pp->day, month, pp->year, flags, status);
1237c0b746e5SOllivier Robert 	}
1238c0b746e5SOllivier Robert #endif
1239c0b746e5SOllivier Robert 
1240c0b746e5SOllivier Robert 	/*
1241c0b746e5SOllivier Robert 	  The status value tested for is not strictly supported by the
1242c0b746e5SOllivier Robert 	  clock spec since the value of bit 2 (0x4) is claimed to be
1243c0b746e5SOllivier Robert 	  undefined for MSF, yet does seem to indicate if the last resync
1244c0b746e5SOllivier Robert 	  was successful or not.
1245c0b746e5SOllivier Robert 	*/
1246c0b746e5SOllivier Robert 	pp->leap = LEAP_NOWARNING;
1247c0b746e5SOllivier Robert 	status &= 0x7;
1248c0b746e5SOllivier Robert 	if(status == 0x3) {
1249c0b746e5SOllivier Robert 		if(status != up->status)
1250c0b746e5SOllivier Robert 		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1251c0b746e5SOllivier Robert 	} else {
1252c0b746e5SOllivier Robert 		if(status != up->status) {
1253c0b746e5SOllivier Robert 			msyslog(LOG_NOTICE, "ARCRON: signal lost");
1254c0b746e5SOllivier Robert 			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1255c0b746e5SOllivier Robert 			up->status = status;
12569c2daa00SOllivier Robert 			pp->lencode = 0;
1257c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_FAULT);
1258c0b746e5SOllivier Robert 			return;
1259c0b746e5SOllivier Robert 		}
1260c0b746e5SOllivier Robert 	}
1261c0b746e5SOllivier Robert 	up->status = status;
1262c0b746e5SOllivier Robert 
12639c2daa00SOllivier Robert 	if (peer->MODE == 0) { /* compatiblity to original version */
12649c2daa00SOllivier Robert 		int bst = flags;
12659c2daa00SOllivier Robert 
1266c0b746e5SOllivier Robert 		pp->day += moff[month - 1];
1267c0b746e5SOllivier Robert 
1268c0b746e5SOllivier Robert 		if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
1269c0b746e5SOllivier Robert 
1270c0b746e5SOllivier Robert 		/* Convert to UTC if required */
1271c0b746e5SOllivier Robert 		if(bst & 2) {
1272c0b746e5SOllivier Robert 			pp->hour--;
1273c0b746e5SOllivier Robert 			if (pp->hour < 0) {
1274c0b746e5SOllivier Robert 				pp->hour = 23;
1275c0b746e5SOllivier Robert 				pp->day--;
12769c2daa00SOllivier Robert 				/* If we try to wrap round the year
12779c2daa00SOllivier Robert 				 * (BST on 1st Jan), reject.*/
1278c0b746e5SOllivier Robert 				if(pp->day < 0) {
12799c2daa00SOllivier Robert 					pp->lencode = 0;
1280c0b746e5SOllivier Robert 					refclock_report(peer, CEVNT_BADTIME);
1281c0b746e5SOllivier Robert 					return;
1282c0b746e5SOllivier Robert 				}
1283c0b746e5SOllivier Robert 			}
1284c0b746e5SOllivier Robert 		}
12859c2daa00SOllivier Robert 	}
1286c0b746e5SOllivier Robert 
12879c2daa00SOllivier Robert 	if(peer->MODE > 0) {
12889c2daa00SOllivier Robert 		if(pp->sloppyclockflag & CLK_FLAG1) {
12899c2daa00SOllivier Robert 			struct tm  local;
12909c2daa00SOllivier Robert 			struct tm *gmtp;
12919c2daa00SOllivier Robert 			time_t	   unixtime;
12929c2daa00SOllivier Robert 
12939c2daa00SOllivier Robert 			/*
12949c2daa00SOllivier Robert 			 * Convert to GMT for sites that distribute localtime.
12959c2daa00SOllivier Robert 			 * This means we have to do Y2K conversion on the
12969c2daa00SOllivier Robert 			 * 2-digit year; otherwise, we get the time wrong.
12979c2daa00SOllivier Robert 			 */
12989c2daa00SOllivier Robert 
12992b15cb3dSCy Schubert 			memset(&local, 0, sizeof(local));
13002b15cb3dSCy Schubert 
13019c2daa00SOllivier Robert 			local.tm_year  = pp->year-1900;
13029c2daa00SOllivier Robert 			local.tm_mon   = month-1;
13039c2daa00SOllivier Robert 			local.tm_mday  = pp->day;
13049c2daa00SOllivier Robert 			local.tm_hour  = pp->hour;
13059c2daa00SOllivier Robert 			local.tm_min   = pp->minute;
13069c2daa00SOllivier Robert 			local.tm_sec   = pp->second;
13079c2daa00SOllivier Robert 			switch (peer->MODE) {
13089c2daa00SOllivier Robert 			    case 1:
13099c2daa00SOllivier Robert 				local.tm_isdst = (flags & 2);
13109c2daa00SOllivier Robert 				break;
13119c2daa00SOllivier Robert 			    case 2:
13129c2daa00SOllivier Robert 				local.tm_isdst = (flags & 2);
13139c2daa00SOllivier Robert 				break;
13149c2daa00SOllivier Robert 			    case 3:
13159c2daa00SOllivier Robert 				switch (flags & 3) {
13169c2daa00SOllivier Robert 				    case 0: /* It is unclear exactly when the
13179c2daa00SOllivier Robert 					       Arcron changes from DST->ST and
13189c2daa00SOllivier Robert 					       ST->DST. Testing has shown this
13199c2daa00SOllivier Robert 					       to be irregular. For the time
13209c2daa00SOllivier Robert 					       being, let the OS decide. */
13219c2daa00SOllivier Robert 					local.tm_isdst = 0;
13229c2daa00SOllivier Robert #ifdef DEBUG
13239c2daa00SOllivier Robert 					if (debug)
13249c2daa00SOllivier Robert 					    printf ("arc: DST = 00 (0)\n");
13259c2daa00SOllivier Robert #endif
13269c2daa00SOllivier Robert 					break;
13279c2daa00SOllivier Robert 				    case 1: /* dst->st time */
13289c2daa00SOllivier Robert 					local.tm_isdst = -1;
13299c2daa00SOllivier Robert #ifdef DEBUG
13309c2daa00SOllivier Robert 					if (debug)
13319c2daa00SOllivier Robert 					    printf ("arc: DST = 01 (1)\n");
13329c2daa00SOllivier Robert #endif
13339c2daa00SOllivier Robert 					break;
13349c2daa00SOllivier Robert 				    case 2: /* st->dst time */
13359c2daa00SOllivier Robert 					local.tm_isdst = -1;
13369c2daa00SOllivier Robert #ifdef DEBUG
13379c2daa00SOllivier Robert 					if (debug)
13389c2daa00SOllivier Robert 					    printf ("arc: DST = 10 (2)\n");
13399c2daa00SOllivier Robert #endif
13409c2daa00SOllivier Robert 					break;
13419c2daa00SOllivier Robert 				    case 3: /* dst time */
13429c2daa00SOllivier Robert 				        local.tm_isdst = 1;
13439c2daa00SOllivier Robert #ifdef DEBUG
13449c2daa00SOllivier Robert 					if (debug)
13459c2daa00SOllivier Robert 					    printf ("arc: DST = 11 (3)\n");
13469c2daa00SOllivier Robert #endif
13479c2daa00SOllivier Robert 					break;
13489c2daa00SOllivier Robert 				}
13499c2daa00SOllivier Robert 				break;
13509c2daa00SOllivier Robert 			    default:
13519c2daa00SOllivier Robert 				msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
13529c2daa00SOllivier Robert 					peer->MODE);
13539c2daa00SOllivier Robert 				return;
13549c2daa00SOllivier Robert 				break;
13559c2daa00SOllivier Robert 			}
13569c2daa00SOllivier Robert 			unixtime = mktime (&local);
13579c2daa00SOllivier Robert 			if ((gmtp = gmtime (&unixtime)) == NULL)
13589c2daa00SOllivier Robert 			{
13599c2daa00SOllivier Robert 				pp->lencode = 0;
13609c2daa00SOllivier Robert 				refclock_report (peer, CEVNT_FAULT);
13619c2daa00SOllivier Robert 				return;
13629c2daa00SOllivier Robert 			}
13639c2daa00SOllivier Robert 			pp->year = gmtp->tm_year+1900;
13649c2daa00SOllivier Robert 			month = gmtp->tm_mon+1;
13659c2daa00SOllivier Robert 			pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
13669c2daa00SOllivier Robert 			/* pp->day = gmtp->tm_yday; */
13679c2daa00SOllivier Robert 			pp->hour = gmtp->tm_hour;
13689c2daa00SOllivier Robert 			pp->minute = gmtp->tm_min;
13699c2daa00SOllivier Robert 			pp->second = gmtp->tm_sec;
13709c2daa00SOllivier Robert #ifdef DEBUG
13719c2daa00SOllivier Robert 			if (debug)
13729c2daa00SOllivier Robert 			{
13739c2daa00SOllivier Robert 				printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
13749c2daa00SOllivier Robert 					pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
13759c2daa00SOllivier Robert 					pp->second);
13769c2daa00SOllivier Robert 			}
13779c2daa00SOllivier Robert #endif
13789c2daa00SOllivier Robert 		} else
13799c2daa00SOllivier Robert 		{
13809c2daa00SOllivier Robert 			/*
13819c2daa00SOllivier Robert 			* For more rational sites distributing UTC
13829c2daa00SOllivier Robert 			*/
13839c2daa00SOllivier Robert 			pp->day    = ymd2yd(pp->year,month,pp->day);
13849c2daa00SOllivier Robert 		}
13859c2daa00SOllivier Robert 	}
13869c2daa00SOllivier Robert 
13879c2daa00SOllivier Robert 	if (peer->MODE == 0) { /* compatiblity to original version */
13889c2daa00SOllivier Robert 				/* If clock signal quality is
13899c2daa00SOllivier Robert 				 * unknown, revert to default PRECISION...*/
13909c2daa00SOllivier Robert 		if(up->quality == QUALITY_UNKNOWN) {
13919c2daa00SOllivier Robert 			peer->precision = PRECISION;
13929c2daa00SOllivier Robert 		} else { /* ...else improve precision if flag3 is set... */
1393c0b746e5SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1394c0b746e5SOllivier Robert 					   HIGHPRECISION : PRECISION);
1395c0b746e5SOllivier Robert 		}
13969c2daa00SOllivier Robert 	} else {
13979c2daa00SOllivier Robert 		if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
13989c2daa00SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
13999c2daa00SOllivier Robert 					   HIGHPRECISION : PRECISION);
14009c2daa00SOllivier Robert 		} else if (up->quality == QUALITY_UNKNOWN) {
14019c2daa00SOllivier Robert 			peer->precision = PRECISION;
14029c2daa00SOllivier Robert 		} else {
14039c2daa00SOllivier Robert 			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
14049c2daa00SOllivier Robert 					   HIGHPRECISION : PRECISION);
14059c2daa00SOllivier Robert 		}
14069c2daa00SOllivier Robert 	}
1407c0b746e5SOllivier Robert 
1408c0b746e5SOllivier Robert 	/* Notice and log any change (eg from initial defaults) for flags. */
1409c0b746e5SOllivier Robert 	if(up->saved_flags != pp->sloppyclockflag) {
14109c2daa00SOllivier Robert #ifdef DEBUG
1411c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1412c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1413c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1414c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1415c0b746e5SOllivier Robert 			((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1416c0b746e5SOllivier Robert 		/* Note effects of flags changing... */
1417c0b746e5SOllivier Robert 		if(debug) {
1418c0b746e5SOllivier Robert 			printf("arc: PRECISION = %d.\n", peer->precision);
1419c0b746e5SOllivier Robert 		}
1420c0b746e5SOllivier Robert #endif
1421c0b746e5SOllivier Robert 		up->saved_flags = pp->sloppyclockflag;
1422c0b746e5SOllivier Robert 	}
1423c0b746e5SOllivier Robert 
1424c0b746e5SOllivier Robert 	/* Note time of last believable timestamp. */
1425c0b746e5SOllivier Robert 	pp->lastrec = up->lastrec;
1426c0b746e5SOllivier Robert 
1427c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1428c0b746e5SOllivier Robert 	/* Find out if a leap-second might just have happened...
1429c0b746e5SOllivier Robert 	   (ie is this the first hour of the first day of Jan or Jul?)
1430c0b746e5SOllivier Robert 	*/
1431c0b746e5SOllivier Robert 	if((pp->hour == 0) &&
1432c0b746e5SOllivier Robert 	   (pp->day == 1) &&
1433c0b746e5SOllivier Robert 	   ((month == 1) || (month == 7))) {
1434c0b746e5SOllivier Robert 		if(possible_leap >= 0) {
1435c0b746e5SOllivier Robert 			/* A leap may have happened, and no resync has started yet...*/
1436c0b746e5SOllivier Robert 			possible_leap = 1;
1437c0b746e5SOllivier Robert 		}
1438c0b746e5SOllivier Robert 	} else {
1439c0b746e5SOllivier Robert 		/* Definitely not leap-second territory... */
1440c0b746e5SOllivier Robert 		possible_leap = 0;
1441c0b746e5SOllivier Robert 	}
1442c0b746e5SOllivier Robert #endif
1443c0b746e5SOllivier Robert 
1444c0b746e5SOllivier Robert 	if (!refclock_process(pp)) {
14459c2daa00SOllivier Robert 		pp->lencode = 0;
1446c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
1447c0b746e5SOllivier Robert 		return;
1448c0b746e5SOllivier Robert 	}
14499c2daa00SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
1450c0b746e5SOllivier Robert 	refclock_receive(peer);
1451c0b746e5SOllivier Robert }
1452c0b746e5SOllivier Robert 
1453c0b746e5SOllivier Robert 
1454c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */
1455c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */
1456c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */
14572b15cb3dSCy Schubert static  void    request_time    (int, struct peer *);
1458c0b746e5SOllivier Robert static void
1459c0b746e5SOllivier Robert request_time(
1460c0b746e5SOllivier Robert 	int unit,
1461c0b746e5SOllivier Robert 	struct peer *peer
1462c0b746e5SOllivier Robert 	)
1463c0b746e5SOllivier Robert {
1464c0b746e5SOllivier Robert 	struct refclockproc *pp = peer->procptr;
14652b15cb3dSCy Schubert 	register struct arcunit *up = pp->unitptr;
1466c0b746e5SOllivier Robert #ifdef DEBUG
1467c0b746e5SOllivier Robert 	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1468c0b746e5SOllivier Robert #endif
1469c0b746e5SOllivier Robert 	if (!send_slow(up, pp->io.fd, "o\r")) {
14709c2daa00SOllivier Robert #ifdef DEBUG
14719c2daa00SOllivier Robert 		if (debug) {
14729c2daa00SOllivier Robert 			printf("arc: unit %d: problem sending", unit);
14739c2daa00SOllivier Robert 		}
1474c0b746e5SOllivier Robert #endif
14759c2daa00SOllivier Robert 		pp->lencode = 0;
1476c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1477c0b746e5SOllivier Robert 		return;
1478c0b746e5SOllivier Robert 	}
1479c0b746e5SOllivier Robert 	pp->polls++;
1480c0b746e5SOllivier Robert }
1481c0b746e5SOllivier Robert 
1482c0b746e5SOllivier Robert /*
1483c0b746e5SOllivier Robert  * arc_poll - called by the transmit procedure
1484c0b746e5SOllivier Robert  */
1485c0b746e5SOllivier Robert static void
1486c0b746e5SOllivier Robert arc_poll(
1487c0b746e5SOllivier Robert 	int unit,
1488c0b746e5SOllivier Robert 	struct peer *peer
1489c0b746e5SOllivier Robert 	)
1490c0b746e5SOllivier Robert {
1491c0b746e5SOllivier Robert 	register struct arcunit *up;
1492c0b746e5SOllivier Robert 	struct refclockproc *pp;
1493c0b746e5SOllivier Robert 	int resync_needed;              /* Should we start a resync? */
1494c0b746e5SOllivier Robert 
1495c0b746e5SOllivier Robert 	pp = peer->procptr;
14962b15cb3dSCy Schubert 	up = pp->unitptr;
14979c2daa00SOllivier Robert #if 0
1498c0b746e5SOllivier Robert 	pp->lencode = 0;
1499c0b746e5SOllivier Robert 	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
15009c2daa00SOllivier Robert #endif
1501c0b746e5SOllivier Robert 
1502c0b746e5SOllivier Robert #if 0
1503c0b746e5SOllivier Robert 	/* Flush input. */
1504c0b746e5SOllivier Robert 	tcflush(pp->io.fd, TCIFLUSH);
1505c0b746e5SOllivier Robert #endif
1506c0b746e5SOllivier Robert 
1507c0b746e5SOllivier Robert 	/* Resync if our next scheduled resync time is here or has passed. */
15089c2daa00SOllivier Robert 	resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
15099c2daa00SOllivier Robert 			  (up->next_resync <= current_time) );
1510c0b746e5SOllivier Robert 
1511c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN
1512c0b746e5SOllivier Robert 	/*
1513c0b746e5SOllivier Robert 	  Try to catch a potential leap-second insertion or deletion quickly.
1514c0b746e5SOllivier Robert 
1515c0b746e5SOllivier Robert 	  In addition to the normal NTP fun of clocks that don't report
1516c0b746e5SOllivier Robert 	  leap-seconds spooking their hosts, this clock does not even
1517c0b746e5SOllivier Robert 	  sample the radio sugnal the whole time, so may miss a
1518c0b746e5SOllivier Robert 	  leap-second insertion or deletion for up to a whole sample
1519c0b746e5SOllivier Robert 	  time.
1520c0b746e5SOllivier Robert 
1521c0b746e5SOllivier Robert 	  To try to minimise this effect, if in the first few minutes of
1522c0b746e5SOllivier Robert 	  the day immediately following a leap-second-insertion point
1523c0b746e5SOllivier Robert 	  (ie in the first hour of the first day of the first and sixth
1524c0b746e5SOllivier Robert 	  months), and if the last resync was in the previous day, and a
1525c0b746e5SOllivier Robert 	  resync is not already in progress, resync the clock
1526c0b746e5SOllivier Robert 	  immediately.
1527c0b746e5SOllivier Robert 
1528c0b746e5SOllivier Robert 	*/
1529c0b746e5SOllivier Robert 	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1530c0b746e5SOllivier Robert 	   (!up->resyncing)) {          /* No resync in progress yet. */
1531c0b746e5SOllivier Robert 		resync_needed = 1;
1532c0b746e5SOllivier Robert 		possible_leap = -1;          /* Prevent multiple resyncs. */
1533c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1534c0b746e5SOllivier Robert 	}
1535c0b746e5SOllivier Robert #endif
1536c0b746e5SOllivier Robert 
1537c0b746e5SOllivier Robert 	/* Do a resync if required... */
1538c0b746e5SOllivier Robert 	if(resync_needed) {
1539c0b746e5SOllivier Robert 		/* First, reset quality value to `unknown' so we can detect */
1540c0b746e5SOllivier Robert 		/* when a quality message has been responded to by this     */
1541c0b746e5SOllivier Robert 		/* being set to some other value.                           */
1542c0b746e5SOllivier Robert 		up->quality = QUALITY_UNKNOWN;
1543c0b746e5SOllivier Robert 
1544c0b746e5SOllivier Robert 		/* Note that we are resyncing... */
1545c0b746e5SOllivier Robert 		up->resyncing = 1;
1546c0b746e5SOllivier Robert 
1547c0b746e5SOllivier Robert 		/* Now actually send the resync command and an immediate poll. */
1548c0b746e5SOllivier Robert #ifdef DEBUG
1549c0b746e5SOllivier Robert 		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1550c0b746e5SOllivier Robert #endif
1551c0b746e5SOllivier Robert 		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1552c0b746e5SOllivier Robert 		send_slow(up, pp->io.fd, "h\r");
1553c0b746e5SOllivier Robert 
1554c0b746e5SOllivier Robert 		/* Schedule our next resync... */
1555c0b746e5SOllivier Robert 		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1556c0b746e5SOllivier Robert 
1557c0b746e5SOllivier Robert 		/* Drop through to request time if appropriate. */
1558c0b746e5SOllivier Robert 	}
1559c0b746e5SOllivier Robert 
1560c0b746e5SOllivier Robert 	/* If clock quality is too poor to trust, indicate a fault. */
1561c0b746e5SOllivier Robert 	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1562c0b746e5SOllivier Robert 	/* we'll cross our fingers and just hope that the thing     */
1563c0b746e5SOllivier Robert 	/* synced so quickly we did not catch it---we'll            */
1564c0b746e5SOllivier Robert 	/* double-check the clock is OK elsewhere.                  */
1565c0b746e5SOllivier Robert 	if(
1566c0b746e5SOllivier Robert #ifdef ARCRON_KEEN
1567c0b746e5SOllivier Robert 		(up->quality != QUALITY_UNKNOWN) &&
1568c0b746e5SOllivier Robert #else
1569c0b746e5SOllivier Robert 		(up->quality == QUALITY_UNKNOWN) ||
1570c0b746e5SOllivier Robert #endif
1571c0b746e5SOllivier Robert 		(up->quality < MIN_CLOCK_QUALITY_OK)) {
1572c0b746e5SOllivier Robert #ifdef DEBUG
1573c0b746e5SOllivier Robert 		if(debug) {
1574c0b746e5SOllivier Robert 			printf("arc: clock quality %d too poor.\n", up->quality);
1575c0b746e5SOllivier Robert 		}
1576c0b746e5SOllivier Robert #endif
15779c2daa00SOllivier Robert 		pp->lencode = 0;
1578c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
1579c0b746e5SOllivier Robert 		return;
1580c0b746e5SOllivier Robert 	}
1581c0b746e5SOllivier Robert 	/* This is the normal case: request a timestamp. */
1582c0b746e5SOllivier Robert 	request_time(unit, peer);
1583c0b746e5SOllivier Robert }
1584c0b746e5SOllivier Robert 
1585c0b746e5SOllivier Robert #else
15862b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT
1587c0b746e5SOllivier Robert #endif
1588