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