154359Sroberto/*
2132451Sroberto * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
354359Sroberto */
454359Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
9290001Sglebius#include "ntp_types.h"
10290001Sglebius
1154359Sroberto#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
12182007Sroberto
13132451Srobertostatic const char arc_version[] = { "V1.3 2003/02/21" };
1454359Sroberto
15132451Sroberto/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
16132451Sroberto   to 4.1.0 */
17132451Sroberto#undef PRE_NTP420
1854359Sroberto
1954359Sroberto#ifndef ARCRON_NOT_KEEN
2054359Sroberto#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
2154359Sroberto#endif
2254359Sroberto
2354359Sroberto#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
2454359Sroberto#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
2554359Sroberto#endif
2654359Sroberto
2754359Sroberto#ifndef ARCRON_NOT_LEAPSECOND_KEEN
2854359Sroberto#ifndef ARCRON_LEAPSECOND_KEEN
2954359Sroberto#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
3054359Sroberto#endif
3154359Sroberto#endif
3254359Sroberto
3354359Sroberto/*
3454359SrobertoCode by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
3554359SrobertoModifications by Damon Hart-Davis, <d@hd.org>, 1997.
36132451SrobertoModifications by Paul Alfille, <palfille@partners.org>, 2003.
37132451SrobertoModifications by Christopher Price, <cprice@cs-home.com>, 2003.
38182007SrobertoModifications by Nigel Roles <nigel@9fs.org>, 2003.
3954359Sroberto
40132451Sroberto
4154359SrobertoTHIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
4254359SrobertoYOUR OWN RISK.
4354359Sroberto
4454359SrobertoOrginally developed and used with ntp3-5.85 by Derek Mulcahy.
4554359Sroberto
4654359SrobertoBuilt against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
4754359Sroberto
4854359SrobertoThis code may be freely copied and used and incorporated in other
4954359Srobertosystems providing the disclaimer and notice of authorship are
5054359Srobertoreproduced.
5154359Sroberto
5254359Sroberto-------------------------------------------------------------------------------
5354359Sroberto
54182007SrobertoNigel's notes:
55182007Sroberto
56182007Sroberto1) Called tcgetattr() before modifying, so that fields correctly initialised
57182007Sroberto   for all operating systems
58182007Sroberto
59182007Sroberto2) Altered parsing of timestamp line so that it copes with fields which are
60182007Sroberto   not always ASCII digits (e.g. status field when battery low)
61182007Sroberto
62182007Sroberto-------------------------------------------------------------------------------
63182007Sroberto
64132451SrobertoChristopher's notes:
65132451Sroberto
66132451SrobertoMAJOR CHANGES SINCE V1.2
67132451Sroberto========================
68132451Sroberto 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
69132451Sroberto    2001-02-17 comp.protocols.time.ntp
70132451Sroberto
71132451Sroberto 2) Added WWVB support via clock mode command, localtime/UTC time configured
72132451Sroberto    via flag1=(0=UTC, 1=localtime)
73132451Sroberto
74132451Sroberto 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
75132451Sroberto
76132451Sroberto 4) Added simplified conversion from localtime to UTC with dst/bst translation
77132451Sroberto
78132451Sroberto 5) Added average signal quality poll
79132451Sroberto
80132451Sroberto 6) Fixed a badformat error when no code is available due to stripping
81132451Sroberto    \n & \r's
82132451Sroberto
83132451Sroberto 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
84132451Sroberto    routine
85132451Sroberto
86132451Sroberto 8) Lots of code cleanup, including standardized DEBUG macros and removal
87132451Sroberto    of unused code
88132451Sroberto
89132451Sroberto-------------------------------------------------------------------------------
90132451Sroberto
9154359SrobertoAuthor's original note:
9254359Sroberto
9354359SrobertoI enclose my ntp driver for the Galleon Systems Arc MSF receiver.
9454359Sroberto
9554359SrobertoIt works (after a fashion) on both Solaris-1 and Solaris-2.
9654359Sroberto
9754359SrobertoI am currently using ntp3-5.85.  I have been running the code for
9854359Srobertoabout 7 months without any problems.  Even coped with the change to BST!
9954359Sroberto
10054359SrobertoI had to do some funky things to read from the clock because it uses the
10154359Srobertopower from the receive lines to drive the transmit lines.  This makes the
10254359Srobertocode look a bit stupid but it works.  I also had to put in some delays to
10354359Srobertoallow for the turnaround time from receive to transmit.  These delays
10454359Srobertoare between characters when requesting a time stamp so that shouldn't affect
10554359Srobertothe results too drastically.
10654359Sroberto
10754359Sroberto...
10854359Sroberto
10954359SrobertoThe bottom line is that it works but could easily be improved.  You are
11054359Srobertofree to do what you will with the code.  I haven't been able to determine
11154359Srobertohow good the clock is.  I think that this requires a known good clock
11254359Srobertoto compare it against.
11354359Sroberto
11454359Sroberto-------------------------------------------------------------------------------
11554359Sroberto
11654359SrobertoDamon's notes for adjustments:
11754359Sroberto
11854359SrobertoMAJOR CHANGES SINCE V1.0
11954359Sroberto========================
12054359Sroberto 1) Removal of pollcnt variable that made the clock go permanently
12154359Sroberto    off-line once two time polls failed to gain responses.
12254359Sroberto
12354359Sroberto 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
12454359Sroberto    terminal of the process when we do a low-level open().
12554359Sroberto
12654359Sroberto 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
12754359Sroberto    defined) to try to resync quickly after a potential leap-second
12854359Sroberto    insertion or deletion.
12954359Sroberto
13054359Sroberto 4) Code significantly slimmer at run-time than V1.0.
13154359Sroberto
13254359Sroberto
13354359SrobertoGENERAL
13454359Sroberto=======
13554359Sroberto
13654359Sroberto 1) The C preprocessor symbol to have the clock built has been changed
13754359Sroberto    from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
13854359Sroberto    possiblity of clashes with other symbols in the future.
13954359Sroberto
14054359Sroberto 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
14154359Sroberto
14254359Sroberto     a) The ARC documentation claims the internal clock is (only)
143290001Sglebius	accurate to about 20ms relative to Rugby (plus there must be
144290001Sglebius	noticable drift and delay in the ms range due to transmission
145290001Sglebius	delays and changing atmospheric effects).  This clock is not
146290001Sglebius	designed for ms accuracy as NTP has spoilt us all to expect.
14754359Sroberto
14854359Sroberto     b) The clock oscillator looks like a simple uncompensated quartz
149290001Sglebius	crystal of the sort used in digital watches (ie 32768Hz) which
150290001Sglebius	can have large temperature coefficients and drifts; it is not
151290001Sglebius	clear if this oscillator is properly disciplined to the MSF
152290001Sglebius	transmission, but as the default is to resync only once per
153290001Sglebius	*day*, we can imagine that it is not, and is free-running.  We
154290001Sglebius	can minimise drift by resyncing more often (at the cost of
155290001Sglebius	reduced battery life), but drift/wander may still be
156290001Sglebius	significant.
15754359Sroberto
15854359Sroberto     c) Note that the bit time of 3.3ms adds to the potential error in
159290001Sglebius	the the clock timestamp, since the bit clock of the serial link
160290001Sglebius	may effectively be free-running with respect to the host clock
161290001Sglebius	and the MSF clock.  Actually, the error is probably 1/16th of
162290001Sglebius	the above, since the input data is probably sampled at at least
163290001Sglebius	16x the bit rate.
16454359Sroberto
16554359Sroberto    By keeping the clock marked as not very precise, it will have a
16654359Sroberto    fairly large dispersion, and thus will tend to be used as a
16754359Sroberto    `backup' time source and sanity checker, which this clock is
16854359Sroberto    probably ideal for.  For an isolated network without other time
16954359Sroberto    sources, this clock can probably be expected to provide *much*
17054359Sroberto    better than 1s accuracy, which will be fine.
17154359Sroberto
17254359Sroberto    By default, PRECISION is set to -4, but experience, especially at a
17354359Sroberto    particular geographic location with a particular clock, may allow
17454359Sroberto    this to be altered to -5.  (Note that skews of +/- 10ms are to be
17554359Sroberto    expected from the clock from time-to-time.)  This improvement of
17654359Sroberto    reported precision can be instigated by setting flag3 to 1, though
17754359Sroberto    the PRECISION will revert to the normal value while the clock
17854359Sroberto    signal quality is unknown whatever the flag3 setting.
17954359Sroberto
18054359Sroberto    IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
18154359Sroberto    ANY RESIDUAL SKEW, eg:
18254359Sroberto
183290001Sglebius	server 127.127.27.0 # ARCRON MSF radio clock unit 0.
184290001Sglebius	# Fudge timestamps by about 20ms.
185290001Sglebius	fudge 127.127.27.0 time1 0.020
18654359Sroberto
18754359Sroberto    You will need to observe your system's behaviour, assuming you have
18854359Sroberto    some other NTP source to compare it with, to work out what the
18954359Sroberto    fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
19054359Sroberto    my MSF clock with my distance from the MSF transmitter, +20ms
19154359Sroberto    seemed about right, after some observation.
19254359Sroberto
19354359Sroberto 3) REFID has been made "MSFa" to reflect the MSF time source and the
19454359Sroberto    ARCRON receiver.
19554359Sroberto
19654359Sroberto 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
19754359Sroberto    forcing a resync since the last attempt.  This is picked to give a
19854359Sroberto    little less than an hour between resyncs and to try to avoid
19954359Sroberto    clashing with any regular event at a regular time-past-the-hour
20054359Sroberto    which might cause systematic errors.
20154359Sroberto
20254359Sroberto    The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
20354359Sroberto    running down its batteries unnecesarily if ntpd is going to crash
20454359Sroberto    or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
20554359Sroberto    then this period is long enough for (with normal polling rates)
20654359Sroberto    enough time samples to have been taken to allow ntpd to sync to
20754359Sroberto    the clock before the interruption for the clock to resync to MSF.
20854359Sroberto    This avoids ntpd syncing to another peer first and then
20954359Sroberto    almost immediately hopping to the MSF clock.
21054359Sroberto
21154359Sroberto    The RETRY_RESYNC_TIME is used before rescheduling a resync after a
21254359Sroberto    resync failed to reveal a statisfatory signal quality (too low or
21354359Sroberto    unknown).
21454359Sroberto
21554359Sroberto 5) The clock seems quite jittery, so I have increased the
21654359Sroberto    median-filter size from the typical (previous) value of 3.  I
21754359Sroberto    discard up to half the results in the filter.  It looks like maybe
21854359Sroberto    1 sample in 10 or so (maybe less) is a spike, so allow the median
21954359Sroberto    filter to discard at least 10% of its entries or 1 entry, whichever
22054359Sroberto    is greater.
22154359Sroberto
22254359Sroberto 6) Sleeping *before* each character sent to the unit to allow required
22354359Sroberto    inter-character time but without introducting jitter and delay in
22454359Sroberto    handling the response if possible.
22554359Sroberto
22654359Sroberto 7) If the flag ARCRON_KEEN is defined, take time samples whenever
22754359Sroberto    possible, even while resyncing, etc.  We rely, in this case, on the
22854359Sroberto    clock always giving us a reasonable time or else telling us in the
22954359Sroberto    status byte at the end of the timestamp that it failed to sync to
23054359Sroberto    MSF---thus we should never end up syncing to completely the wrong
23154359Sroberto    time.
23254359Sroberto
23354359Sroberto 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
23454359Sroberto    refclock median-filter routines to get round small bug in 3-5.90
23554359Sroberto    code which does not return the median offset. XXX Removed this
23654359Sroberto    bit due NTP Version 4 upgrade - dlm.
23754359Sroberto
23854359Sroberto 9) We would appear to have a year-2000 problem with this clock since
23954359Sroberto    it returns only the two least-significant digits of the year.  But
24054359Sroberto    ntpd ignores the year and uses the local-system year instead, so
24154359Sroberto    this is in fact not a problem.  Nevertheless, we attempt to do a
24254359Sroberto    sensible thing with the dates, wrapping them into a 100-year
24354359Sroberto    window.
24454359Sroberto
24554359Sroberto 10)Logs stats information that can be used by Derek's Tcl/Tk utility
24654359Sroberto    to show the status of the clock.
24754359Sroberto
24854359Sroberto 11)The clock documentation insists that the number of bits per
24954359Sroberto    character to be sent to the clock, and sent by it, is 11, including
25054359Sroberto    one start bit and two stop bits.  The data format is either 7+even
25154359Sroberto    or 8+none.
25254359Sroberto
25354359Sroberto
25454359SrobertoTO-DO LIST
25554359Sroberto==========
25654359Sroberto
25754359Sroberto  * Eliminate use of scanf(), and maybe sprintf().
25854359Sroberto
25954359Sroberto  * Allow user setting of resync interval to trade battery life for
26054359Sroberto    accuracy; maybe could be done via fudge factor or unit number.
26154359Sroberto
26254359Sroberto  * Possibly note the time since the last resync of the MSF clock to
26354359Sroberto    MSF as the age of the last reference timestamp, ie trust the
26454359Sroberto    clock's oscillator not very much...
26554359Sroberto
26654359Sroberto  * Add very slow auto-adjustment up to a value of +/- time2 to correct
26754359Sroberto    for long-term errors in the clock value (time2 defaults to 0 so the
26854359Sroberto    correction would be disabled by default).
26954359Sroberto
27054359Sroberto  * Consider trying to use the tty_clk/ppsclock support.
27154359Sroberto
27254359Sroberto  * Possibly use average or maximum signal quality reported during
27354359Sroberto    resync, rather than just the last one, which may be atypical.
27454359Sroberto
27554359Sroberto*/
27654359Sroberto
27754359Sroberto
27854359Sroberto/* Notes for HKW Elektronik GmBH Radio clock driver */
27954359Sroberto/* Author Lyndon David, Sentinet Ltd, Feb 1997      */
28054359Sroberto/* These notes seem also to apply usefully to the ARCRON clock. */
28154359Sroberto
28254359Sroberto/* The HKW clock module is a radio receiver tuned into the Rugby */
28354359Sroberto/* MSF time signal tranmitted on 60 kHz. The clock module connects */
28454359Sroberto/* to the computer via a serial line and transmits the time encoded */
28554359Sroberto/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
28654359Sroberto
28754359Sroberto/* Clock communications, from the datasheet */
28854359Sroberto/* All characters sent to the clock are echoed back to the controlling */
28954359Sroberto/* device. */
29054359Sroberto/* Transmit time/date information */
29154359Sroberto/* syntax ASCII o<cr> */
29254359Sroberto/* Character o may be replaced if neccesary by a character whose code */
29354359Sroberto/* contains the lowest four bits f(hex) eg */
29454359Sroberto/* syntax binary: xxxx1111 00001101 */
29554359Sroberto
29654359Sroberto/* DHD note:
29754359SrobertoYou have to wait for character echo + 10ms before sending next character.
29854359Sroberto*/
29954359Sroberto
30054359Sroberto/* The clock replies to this command with a sequence of 15 characters */
30154359Sroberto/* which contain the complete time and a final <cr> making 16 characters */
30254359Sroberto/* in total. */
30354359Sroberto/* The RC computer clock will not reply immediately to this command because */
30454359Sroberto/* the start bit edge of the first reply character marks the beginning of */
30554359Sroberto/* the second. So the RC Computer Clock will reply to this command at the */
30654359Sroberto/* start of the next second */
30754359Sroberto/* The characters have the following meaning */
30854359Sroberto/* 1. hours tens   */
30954359Sroberto/* 2. hours units  */
31054359Sroberto/* 3. minutes tens */
31154359Sroberto/* 4. minutes units */
31254359Sroberto/* 5. seconds tens  */
31354359Sroberto/* 6. seconds units */
31454359Sroberto/* 7. day of week 1-monday 7-sunday */
31554359Sroberto/* 8. day of month tens */
31654359Sroberto/* 9. day of month units */
31754359Sroberto/* 10. month tens */
31854359Sroberto/* 11. month units */
31954359Sroberto/* 12. year tens */
32054359Sroberto/* 13. year units */
32154359Sroberto/* 14. BST/UTC status */
322290001Sglebius/*	bit 7	parity */
323290001Sglebius/*	bit 6	always 0 */
324290001Sglebius/*	bit 5	always 1 */
325290001Sglebius/*	bit 4	always 1 */
326290001Sglebius/*	bit 3	always 0 */
327290001Sglebius/*	bit 2	=1 if UTC is in effect, complementary to the BST bit */
328290001Sglebius/*	bit 1	=1 if BST is in effect, according to the BST bit     */
329290001Sglebius/*	bit 0	BST/UTC change impending bit=1 in case of change impending */
33054359Sroberto/* 15. status */
331290001Sglebius/*	bit 7	parity */
332290001Sglebius/*	bit 6	always 0 */
333290001Sglebius/*	bit 5	always 1 */
334290001Sglebius/*	bit 4	always 1 */
335290001Sglebius/*	bit 3	=1 if low battery is detected */
336290001Sglebius/*	bit 2	=1 if the very last reception attempt failed and a valid */
337290001Sglebius/*		time information already exists (bit0=1) */
338290001Sglebius/*		=0 if the last reception attempt was successful */
339290001Sglebius/*	bit 1	=1 if at least one reception since 2:30 am was successful */
340290001Sglebius/*		=0 if no reception attempt since 2:30 am was successful */
341290001Sglebius/*	bit 0	=1 if the RC Computer Clock contains valid time information */
342290001Sglebius/*		This bit is zero after reset and one after the first */
343290001Sglebius/*		successful reception attempt */
34454359Sroberto
34554359Sroberto/* DHD note:
34654359SrobertoAlso note g<cr> command which confirms that a resync is in progress, and
34754359Srobertoif so what signal quality (0--5) is available.
34854359SrobertoAlso note h<cr> command which starts a resync to MSF signal.
34954359Sroberto*/
35054359Sroberto
35154359Sroberto
35282498Sroberto#include "ntpd.h"
35382498Sroberto#include "ntp_io.h"
35482498Sroberto#include "ntp_refclock.h"
355132451Sroberto#include "ntp_calendar.h"
35682498Sroberto#include "ntp_stdlib.h"
35754359Sroberto
35854359Sroberto#include <stdio.h>
35954359Sroberto#include <ctype.h>
36054359Sroberto
36154359Sroberto#if defined(HAVE_BSD_TTYS)
36254359Sroberto#include <sgtty.h>
36354359Sroberto#endif /* HAVE_BSD_TTYS */
36454359Sroberto
36554359Sroberto#if defined(HAVE_SYSV_TTYS)
36654359Sroberto#include <termio.h>
36754359Sroberto#endif /* HAVE_SYSV_TTYS */
36854359Sroberto
36954359Sroberto#if defined(HAVE_TERMIOS)
37054359Sroberto#include <termios.h>
37154359Sroberto#endif
37254359Sroberto
37354359Sroberto/*
374132451Sroberto * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
37554359Sroberto */
37654359Sroberto
37754359Sroberto/*
37854359Sroberto * Interface definitions
37954359Sroberto */
380290001Sglebius#define DEVICE		"/dev/arc%d"	/* Device name and unit. */
381290001Sglebius#define SPEED		B300		/* UART speed (300 baud) */
382290001Sglebius#define PRECISION	(-4)		/* Precision  (~63 ms). */
383290001Sglebius#define HIGHPRECISION	(-5)		/* If things are going well... */
384290001Sglebius#define REFID		"MSFa"		/* Reference ID. */
385290001Sglebius#define REFID_MSF	"MSF"		/* Reference ID. */
386290001Sglebius#define REFID_DCF77	"DCF"		/* Reference ID. */
387290001Sglebius#define REFID_WWVB	"WWVB"		/* Reference ID. */
388290001Sglebius#define DESCRIPTION	"ARCRON MSF/DCF/WWVB Receiver"
38954359Sroberto
390132451Sroberto#ifdef PRE_NTP420
391132451Sroberto#define MODE ttlmax
392132451Sroberto#else
393132451Sroberto#define MODE ttl
394132451Sroberto#endif
39554359Sroberto
396290001Sglebius#define LENARC		16		/* Format `o' timecode length. */
39754359Sroberto
398290001Sglebius#define BITSPERCHAR	11		/* Bits per character. */
399290001Sglebius#define BITTIME		0x0DA740E	/* Time for 1 bit at 300bps. */
400290001Sglebius#define CHARTIME10	0x8888888	/* Time for 10-bit char at 300bps. */
401290001Sglebius#define CHARTIME11	0x962FC96	/* Time for 11-bit char at 300bps. */
402290001Sglebius#define CHARTIME			/* Time for char at 300bps. */ \
40354359Sroberto( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
40454359Sroberto				       (BITSPERCHAR * BITTIME) ) )
40554359Sroberto
40654359Sroberto     /* Allow for UART to accept char half-way through final stop bit. */
407290001Sglebius#define INITIALOFFSET ((u_int32)(-BITTIME/2))
40854359Sroberto
40954359Sroberto     /*
41054359Sroberto    charoffsets[x] is the time after the start of the second that byte
41154359Sroberto    x (with the first byte being byte 1) is received by the UART,
41254359Sroberto    assuming that the initial edge of the start bit of the first byte
41354359Sroberto    is on-time.  The values are represented as the fractional part of
41454359Sroberto    an l_fp.
41554359Sroberto
41654359Sroberto    We store enough values to have the offset of each byte including
41754359Sroberto    the trailing \r, on the assumption that the bytes follow one
41854359Sroberto    another without gaps.
41954359Sroberto    */
42054359Sroberto     static const u_int32 charoffsets[LENARC+1] = {
42154359Sroberto#if BITSPERCHAR == 11 /* Usual case. */
42254359Sroberto	     /* Offsets computed as accurately as possible... */
42354359Sroberto	     0,
42454359Sroberto	     INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
42554359Sroberto	     INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
42654359Sroberto	     INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
42754359Sroberto	     INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
42854359Sroberto	     INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
42954359Sroberto	     INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
43054359Sroberto	     INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
43154359Sroberto	     INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
43254359Sroberto	     INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
43354359Sroberto	     INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
43454359Sroberto	     INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
43554359Sroberto	     INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
43654359Sroberto	     INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
43754359Sroberto	     INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
43854359Sroberto	     INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
43954359Sroberto	     INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
44054359Sroberto#else
44154359Sroberto	     /* Offsets computed with a small rounding error... */
44254359Sroberto	     0,
44354359Sroberto	     INITIALOFFSET +  1 * CHARTIME,
44454359Sroberto	     INITIALOFFSET +  2 * CHARTIME,
44554359Sroberto	     INITIALOFFSET +  3 * CHARTIME,
44654359Sroberto	     INITIALOFFSET +  4 * CHARTIME,
44754359Sroberto	     INITIALOFFSET +  5 * CHARTIME,
44854359Sroberto	     INITIALOFFSET +  6 * CHARTIME,
44954359Sroberto	     INITIALOFFSET +  7 * CHARTIME,
45054359Sroberto	     INITIALOFFSET +  8 * CHARTIME,
45154359Sroberto	     INITIALOFFSET +  9 * CHARTIME,
45254359Sroberto	     INITIALOFFSET + 10 * CHARTIME,
45354359Sroberto	     INITIALOFFSET + 11 * CHARTIME,
45454359Sroberto	     INITIALOFFSET + 12 * CHARTIME,
45554359Sroberto	     INITIALOFFSET + 13 * CHARTIME,
45654359Sroberto	     INITIALOFFSET + 14 * CHARTIME,
45754359Sroberto	     INITIALOFFSET + 15 * CHARTIME,
45854359Sroberto	     INITIALOFFSET + 16 * CHARTIME
45954359Sroberto#endif
46054359Sroberto     };
46154359Sroberto
462290001Sglebius#define DEFAULT_RESYNC_TIME  (57*60)	/* Gap between resync attempts (s). */
463290001Sglebius#define RETRY_RESYNC_TIME    (27*60)	/* Gap to emergency resync attempt. */
46454359Sroberto#ifdef ARCRON_KEEN
465290001Sglebius#define INITIAL_RESYNC_DELAY 500	/* Delay before first resync. */
46654359Sroberto#else
467290001Sglebius#define INITIAL_RESYNC_DELAY 50		/* Delay before first resync. */
46854359Sroberto#endif
46954359Sroberto
47054359Sroberto     static const int moff[12] =
47154359Sroberto{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
47254359Sroberto/* Flags for a raw open() of the clock serial device. */
47354359Sroberto#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
47454359Sroberto#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
475290001Sglebius#else		/* Oh well, it may not matter... */
47654359Sroberto#define OPEN_FLAGS (O_RDWR)
47754359Sroberto#endif
47854359Sroberto
47954359Sroberto
48054359Sroberto/* Length of queue of command bytes to be sent. */
481290001Sglebius#define CMDQUEUELEN 4			/* Enough for two cmds + each \r. */
48254359Sroberto/* Queue tick time; interval in seconds between chars taken off queue. */
48354359Sroberto/* Must be >= 2 to allow o\r response to come back uninterrupted. */
484290001Sglebius#define QUEUETICK   2			/* Allow o\r reply to finish. */
48554359Sroberto
48654359Sroberto/*
48754359Sroberto * ARC unit control structure
48854359Sroberto */
48954359Srobertostruct arcunit {
490290001Sglebius	l_fp lastrec;	    /* Time tag for the receive time (system). */
491290001Sglebius	int status;	    /* Clock status. */
49254359Sroberto
493290001Sglebius	int quality;	    /* Quality of reception 0--5 for unit. */
49454359Sroberto	/* We may also use the values -1 or 6 internally. */
495132451Sroberto	u_long quality_stamp; /* Next time to reset quality average. */
49654359Sroberto
49754359Sroberto	u_long next_resync; /* Next resync time (s) compared to current_time. */
498290001Sglebius	int resyncing;	    /* Resync in progress if true. */
49954359Sroberto
50054359Sroberto	/* In the outgoing queue, cmdqueue[0] is next to be sent. */
50154359Sroberto	char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
50254359Sroberto
50354359Sroberto	u_long saved_flags; /* Saved fudge flags. */
50454359Sroberto};
505132451Sroberto
50654359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
50754359Sroberto/* The flag `possible_leap' is set non-zero when any MSF unit
50854359Sroberto       thinks a leap-second may have happened.
50954359Sroberto
51054359Sroberto       Set whenever we receive a valid time sample in the first hour of
51154359Sroberto       the first day of the first/seventh months.
51254359Sroberto
51354359Sroberto       Outside the special hour this value is unconditionally set
51454359Sroberto       to zero by the receive routine.
51554359Sroberto
51654359Sroberto       On finding itself in this timeslot, as long as the value is
51754359Sroberto       non-negative, the receive routine sets it to a positive value to
51854359Sroberto       indicate a resync to MSF should be performed.
51954359Sroberto
52054359Sroberto       In the poll routine, if this value is positive and we are not
52154359Sroberto       already resyncing (eg from a sync that started just before
52254359Sroberto       midnight), start resyncing and set this value negative to
52354359Sroberto       indicate that a leap-triggered resync has been started.  Having
52454359Sroberto       set this negative prevents the receive routine setting it
52554359Sroberto       positive and thus prevents multiple resyncs during the witching
52654359Sroberto       hour.
52754359Sroberto     */
52854359Srobertostatic int possible_leap = 0;       /* No resync required by default. */
52954359Sroberto#endif
53054359Sroberto
53154359Sroberto#if 0
532290001Sglebiusstatic void dummy_event_handler (struct peer *);
533290001Sglebiusstatic void   arc_event_handler (struct peer *);
53454359Sroberto#endif /* 0 */
53554359Sroberto
536290001Sglebius#define QUALITY_UNKNOWN	    -1 /* Indicates unknown clock quality. */
53754359Sroberto#define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
53854359Sroberto#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
53954359Sroberto#define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
54054359Sroberto
54154359Sroberto/*
54254359Sroberto * Function prototypes
54354359Sroberto */
544290001Sglebiusstatic	int	arc_start	(int, struct peer *);
545290001Sglebiusstatic	void	arc_shutdown	(int, struct peer *);
546290001Sglebiusstatic	void	arc_receive	(struct recvbuf *);
547290001Sglebiusstatic	void	arc_poll	(int, struct peer *);
54854359Sroberto
54954359Sroberto/*
55054359Sroberto * Transfer vector
55154359Sroberto */
55254359Srobertostruct  refclock refclock_arc = {
553290001Sglebius	arc_start,		/* start up driver */
554290001Sglebius	arc_shutdown,		/* shut down driver */
555290001Sglebius	arc_poll,		/* transmit poll message */
556290001Sglebius	noentry,		/* not used (old arc_control) */
557290001Sglebius	noentry,		/* initialize driver (not used) */
558290001Sglebius	noentry,		/* not used (old arc_buginfo) */
559290001Sglebius	NOFLAGS			/* not used */
56054359Sroberto};
56154359Sroberto
56254359Sroberto/* Queue us up for the next tick. */
56354359Sroberto#define ENQUEUE(up) \
56454359Sroberto	do { \
565290001Sglebius	     peer->procptr->nextaction = current_time + QUEUETICK; \
56654359Sroberto	} while(0)
56754359Sroberto
568132451Sroberto/* Placeholder event handler---does nothing safely---soaks up loose tick. */
56954359Srobertostatic void
57054359Srobertodummy_event_handler(
57154359Sroberto	struct peer *peer
57254359Sroberto	)
57354359Sroberto{
574132451Sroberto#ifdef DEBUG
57554359Sroberto	if(debug) { printf("arc: dummy_event_handler() called.\n"); }
57654359Sroberto#endif
57754359Sroberto}
57854359Sroberto
57954359Sroberto/*
58054359SrobertoNormal event handler.
58154359Sroberto
58254359SrobertoTake first character off queue and send to clock if not a null.
58354359Sroberto
58454359SrobertoShift characters down and put a null on the end.
58554359Sroberto
58654359SrobertoWe assume that there is no parallelism so no race condition, but even
58754359Srobertoif there is nothing bad will happen except that we might send some bad
58854359Srobertodata to the clock once in a while.
58954359Sroberto*/
59054359Srobertostatic void
59154359Srobertoarc_event_handler(
59254359Sroberto	struct peer *peer
59354359Sroberto	)
59454359Sroberto{
59554359Sroberto	struct refclockproc *pp = peer->procptr;
596290001Sglebius	register struct arcunit *up = pp->unitptr;
59754359Sroberto	int i;
59854359Sroberto	char c;
599132451Sroberto#ifdef DEBUG
60054359Sroberto	if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
60154359Sroberto#endif
60254359Sroberto
60354359Sroberto	c = up->cmdqueue[0];       /* Next char to be sent. */
60454359Sroberto	/* Shift down characters, shifting trailing \0 in at end. */
60554359Sroberto	for(i = 0; i < CMDQUEUELEN; ++i)
60654359Sroberto	{ up->cmdqueue[i] = up->cmdqueue[i+1]; }
60754359Sroberto
60854359Sroberto	/* Don't send '\0' characters. */
60954359Sroberto	if(c != '\0') {
61054359Sroberto		if(write(pp->io.fd, &c, 1) != 1) {
61154359Sroberto			msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
61254359Sroberto		}
613132451Sroberto#ifdef DEBUG
61454359Sroberto		else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
61554359Sroberto#endif
61654359Sroberto	}
617132451Sroberto
618132451Sroberto	ENQUEUE(up);
61954359Sroberto}
62054359Sroberto
62154359Sroberto/*
62254359Sroberto * arc_start - open the devices and initialize data for processing
62354359Sroberto */
62454359Srobertostatic int
62554359Srobertoarc_start(
62654359Sroberto	int unit,
62754359Sroberto	struct peer *peer
62854359Sroberto	)
62954359Sroberto{
63054359Sroberto	register struct arcunit *up;
63154359Sroberto	struct refclockproc *pp;
632290001Sglebius	int temp_fd;
63354359Sroberto	int fd;
63454359Sroberto	char device[20];
63554359Sroberto#ifdef HAVE_TERMIOS
63654359Sroberto	struct termios arg;
63754359Sroberto#endif
63854359Sroberto
639290001Sglebius	msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
640290001Sglebius		arc_version, unit);
641290001Sglebius	DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
642290001Sglebius		unit));
64354359Sroberto
64454359Sroberto	/*
64554359Sroberto	 * Open serial port. Use CLK line discipline, if available.
64654359Sroberto	 */
647290001Sglebius	snprintf(device, sizeof(device), DEVICE, unit);
648290001Sglebius	temp_fd = refclock_open(device, SPEED, LDISC_CLK);
649290001Sglebius	if (temp_fd <= 0)
650290001Sglebius		return 0;
651290001Sglebius	DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
652290001Sglebius	fd = tty_open(device, OPEN_FLAGS, 0777);
653290001Sglebius	if (fd < 0) {
654290001Sglebius		msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.",
655290001Sglebius			unit, device);
656290001Sglebius		close(temp_fd);
657290001Sglebius		return 0;
65854359Sroberto	}
659290001Sglebius	close(temp_fd);
660290001Sglebius	temp_fd = -1;		/* not used after this, at *this* time. */
66154359Sroberto
662290001Sglebius#ifndef SYS_WINNT
663290001Sglebius	if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */
664290001Sglebius		msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
665290001Sglebius			unit);
666290001Sglebius
66754359Sroberto#endif
668290001Sglebius	DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
66954359Sroberto
67054359Sroberto#ifdef HAVE_TERMIOS
67154359Sroberto
672290001Sglebius	if (tcgetattr(fd, &arg) < 0) {
673290001Sglebius		msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.",
674290001Sglebius			unit, device);
675290001Sglebius		close(fd);
676290001Sglebius		return 0;
677290001Sglebius	}
678182007Sroberto
67954359Sroberto	arg.c_iflag = IGNBRK | ISTRIP;
68054359Sroberto	arg.c_oflag = 0;
68154359Sroberto	arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
68254359Sroberto	arg.c_lflag = 0;
68354359Sroberto	arg.c_cc[VMIN] = 1;
68454359Sroberto	arg.c_cc[VTIME] = 0;
68554359Sroberto
686290001Sglebius	if (tcsetattr(fd, TCSANOW, &arg) < 0) {
687290001Sglebius		msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.",
688290001Sglebius			unit, device);
689290001Sglebius		close(fd);
690290001Sglebius		return 0;
691290001Sglebius	}
69254359Sroberto
69354359Sroberto#else
69454359Sroberto
695290001Sglebius	msyslog(LOG_ERR, "ARCRON: termios required by this driver");
69654359Sroberto	(void)close(fd);
69754359Sroberto
69854359Sroberto	return 0;
69954359Sroberto
70054359Sroberto#endif
70154359Sroberto
70254359Sroberto	/* Set structure to all zeros... */
703290001Sglebius	up = emalloc_zero(sizeof(*up));
70454359Sroberto	pp = peer->procptr;
70554359Sroberto	pp->io.clock_recv = arc_receive;
706290001Sglebius	pp->io.srcclock = peer;
70754359Sroberto	pp->io.datalen = 0;
70854359Sroberto	pp->io.fd = fd;
709290001Sglebius	if (!io_addclock(&pp->io)) {
710290001Sglebius		close(fd);
711290001Sglebius		pp->io.fd = -1;
712290001Sglebius		free(up);
713290001Sglebius		return(0);
714290001Sglebius	}
715290001Sglebius	pp->unitptr = up;
71654359Sroberto
71754359Sroberto	/*
71854359Sroberto	 * Initialize miscellaneous variables
71954359Sroberto	 */
72054359Sroberto	peer->precision = PRECISION;
72154359Sroberto	peer->stratum = 2;              /* Default to stratum 2 not 0. */
72254359Sroberto	pp->clockdesc = DESCRIPTION;
723132451Sroberto	if (peer->MODE > 3) {
724132451Sroberto		msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
725132451Sroberto		return 0;
726132451Sroberto	}
727132451Sroberto#ifdef DEBUG
728132451Sroberto	if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
729132451Sroberto#endif
730132451Sroberto	switch (peer->MODE) {
731132451Sroberto	    case 1:
732132451Sroberto		memcpy((char *)&pp->refid, REFID_MSF, 4);
733132451Sroberto		break;
734132451Sroberto	    case 2:
735132451Sroberto		memcpy((char *)&pp->refid, REFID_DCF77, 4);
736132451Sroberto		break;
737132451Sroberto	    case 3:
738132451Sroberto		memcpy((char *)&pp->refid, REFID_WWVB, 4);
739132451Sroberto		break;
740132451Sroberto	    default:
741132451Sroberto		memcpy((char *)&pp->refid, REFID, 4);
742132451Sroberto		break;
743132451Sroberto	}
74454359Sroberto	/* Spread out resyncs so that they should remain separated. */
74554359Sroberto	up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
74654359Sroberto
74754359Sroberto#if 0 /* Not needed because of zeroing of arcunit structure... */
74854359Sroberto	up->resyncing = 0;              /* Not resyncing yet. */
74954359Sroberto	up->saved_flags = 0;            /* Default is all flags off. */
75054359Sroberto	/* Clear send buffer out... */
75154359Sroberto	{
75254359Sroberto		int i;
75354359Sroberto		for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
75454359Sroberto	}
75554359Sroberto#endif
75654359Sroberto
75754359Sroberto#ifdef ARCRON_KEEN
75854359Sroberto	up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
75954359Sroberto#else
76054359Sroberto	up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
76154359Sroberto#endif
762132451Sroberto
763290001Sglebius	peer->procptr->action = arc_event_handler;
764132451Sroberto
765132451Sroberto	ENQUEUE(up);
766132451Sroberto
76754359Sroberto	return(1);
76854359Sroberto}
76954359Sroberto
77054359Sroberto
77154359Sroberto/*
77254359Sroberto * arc_shutdown - shut down the clock
77354359Sroberto */
77454359Srobertostatic void
77554359Srobertoarc_shutdown(
77654359Sroberto	int unit,
77754359Sroberto	struct peer *peer
77854359Sroberto	)
77954359Sroberto{
78054359Sroberto	register struct arcunit *up;
78154359Sroberto	struct refclockproc *pp;
78254359Sroberto
783290001Sglebius	peer->procptr->action = dummy_event_handler;
784132451Sroberto
78554359Sroberto	pp = peer->procptr;
786290001Sglebius	up = pp->unitptr;
787290001Sglebius	if (-1 != pp->io.fd)
788290001Sglebius		io_closeclock(&pp->io);
789290001Sglebius	if (NULL != up)
790290001Sglebius		free(up);
79154359Sroberto}
79254359Sroberto
79354359Sroberto/*
79454359SrobertoCompute space left in output buffer.
79554359Sroberto*/
79654359Srobertostatic int
79754359Srobertospace_left(
79854359Sroberto	register struct arcunit *up
79954359Sroberto	)
80054359Sroberto{
80154359Sroberto	int spaceleft;
80254359Sroberto
80354359Sroberto	/* Compute space left in buffer after any pending output. */
80454359Sroberto	for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
80554359Sroberto	{ if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
80654359Sroberto	return(spaceleft);
80754359Sroberto}
80854359Sroberto
80954359Sroberto/*
81054359SrobertoSend command by copying into command buffer as far forward as possible,
81154359Srobertoafter any pending output.
81254359Sroberto
81354359SrobertoIndicate an error by returning 0 if there is not space for the command.
81454359Sroberto*/
81554359Srobertostatic int
81654359Srobertosend_slow(
81754359Sroberto	register struct arcunit *up,
81854359Sroberto	int fd,
81954359Sroberto	const char *s
82054359Sroberto	)
82154359Sroberto{
82254359Sroberto	int sl = strlen(s);
82354359Sroberto	int spaceleft = space_left(up);
82454359Sroberto
825132451Sroberto#ifdef DEBUG
82654359Sroberto	if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
82754359Sroberto#endif
82854359Sroberto	if(spaceleft < sl) { /* Should not normally happen... */
829132451Sroberto#ifdef DEBUG
83054359Sroberto		msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
831290001Sglebius			sl, spaceleft);
83254359Sroberto#endif
833290001Sglebius		return(0);			/* FAILED! */
83454359Sroberto	}
83554359Sroberto
83654359Sroberto	/* Copy in the command to be sent. */
837182007Sroberto	while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
83854359Sroberto
83954359Sroberto	return(1);
84054359Sroberto}
84154359Sroberto
84254359Sroberto
843182007Srobertostatic int
844182007Srobertoget2(char *p, int *val)
845182007Sroberto{
846290001Sglebius  if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0;
847182007Sroberto  *val = (p[0] - '0') * 10 + p[1] - '0';
848182007Sroberto  return 1;
849182007Sroberto}
850182007Sroberto
851182007Srobertostatic int
852182007Srobertoget1(char *p, int *val)
853182007Sroberto{
854290001Sglebius  if (!isdigit((unsigned char)p[0])) return 0;
855182007Sroberto  *val = p[0] - '0';
856182007Sroberto  return 1;
857182007Sroberto}
858182007Sroberto
85954359Sroberto/* Macro indicating action we will take for different quality values. */
86054359Sroberto#define quality_action(q) \
86154359Sroberto(((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
86254359Sroberto (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
86354359Sroberto  "OK, will use clock"))
86454359Sroberto
865290001Sglebius/*
86654359Sroberto * arc_receive - receive data from the serial interface
86754359Sroberto */
868290001Sglebiusstatic void
86954359Srobertoarc_receive(
87054359Sroberto	struct recvbuf *rbufp
87154359Sroberto	)
87254359Sroberto{
87354359Sroberto	register struct arcunit *up;
87454359Sroberto	struct refclockproc *pp;
87554359Sroberto	struct peer *peer;
87654359Sroberto	char c;
877132451Sroberto	int i, n, wday, month, flags, status;
87854359Sroberto	int arc_last_offset;
879132451Sroberto	static int quality_average = 0;
880132451Sroberto	static int quality_sum = 0;
881132451Sroberto	static int quality_polls = 0;
88254359Sroberto
88354359Sroberto	/*
88454359Sroberto	 * Initialize pointers and read the timecode and timestamp
88554359Sroberto	 */
886290001Sglebius	peer = rbufp->recv_peer;
88754359Sroberto	pp = peer->procptr;
888290001Sglebius	up = pp->unitptr;
88954359Sroberto
89054359Sroberto
89154359Sroberto	/*
89254359Sroberto	  If the command buffer is empty, and we are resyncing, insert a
89354359Sroberto	  g\r quality request into it to poll for signal quality again.
89454359Sroberto	*/
89554359Sroberto	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
89654359Sroberto#ifdef DEBUG
89754359Sroberto		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
89854359Sroberto#endif
89954359Sroberto		send_slow(up, pp->io.fd, "g\r");
90054359Sroberto	}
90154359Sroberto
90254359Sroberto	/*
90354359Sroberto	  The `arc_last_offset' is the offset in lastcode[] of the last byte
90454359Sroberto	  received, and which we assume actually received the input
90554359Sroberto	  timestamp.
90654359Sroberto
90754359Sroberto	  (When we get round to using tty_clk and it is available, we
90854359Sroberto	  assume that we will receive the whole timecode with the
90954359Sroberto	  trailing \r, and that that \r will be timestamped.  But this
91054359Sroberto	  assumption also works if receive the characters one-by-one.)
91154359Sroberto	*/
91254359Sroberto	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
91354359Sroberto
91454359Sroberto	/*
91554359Sroberto	  We catch a timestamp iff:
91654359Sroberto
91754359Sroberto	  * The command code is `o' for a timestamp.
91854359Sroberto
91954359Sroberto	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
92054359Sroberto	  exactly char in the buffer (the command code) so that we
92154359Sroberto	  only sample the first character of the timecode as our
92254359Sroberto	  `on-time' character.
92354359Sroberto
92454359Sroberto	  * The first character in the buffer is not the echoed `\r'
92554359Sroberto	  from the `o` command (so if we are to timestamp an `\r' it
92654359Sroberto	  must not be first in the receive buffer with lencode==1.
92754359Sroberto	  (Even if we had other characters following it, we probably
92854359Sroberto	  would have a premature timestamp on the '\r'.)
92954359Sroberto
93054359Sroberto	  * We have received at least one character (I cannot imagine
93154359Sroberto	  how it could be otherwise, but anyway...).
93254359Sroberto	*/
93354359Sroberto	c = rbufp->recv_buffer[0];
93454359Sroberto	if((pp->a_lastcode[0] == 'o') &&
93554359Sroberto#ifndef ARCRON_MULTIPLE_SAMPLES
93654359Sroberto	   (pp->lencode == 1) &&
93754359Sroberto#endif
93854359Sroberto	   ((pp->lencode != 1) || (c != '\r')) &&
93954359Sroberto	   (arc_last_offset >= 1)) {
94054359Sroberto		/* Note that the timestamp should be corrected if >1 char rcvd. */
94154359Sroberto		l_fp timestamp;
94254359Sroberto		timestamp = rbufp->recv_time;
94354359Sroberto#ifdef DEBUG
94454359Sroberto		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
94554359Sroberto			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
946290001Sglebius			       ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')),
94754359Sroberto			       rbufp->recv_length);
94854359Sroberto		}
94954359Sroberto#endif
95054359Sroberto
95154359Sroberto		/*
95254359Sroberto		  Now correct timestamp by offset of last byte received---we
95354359Sroberto		  subtract from the receive time the delay implied by the
95454359Sroberto		  extra characters received.
95554359Sroberto
95654359Sroberto		  Reject the input if the resulting code is too long, but
95754359Sroberto		  allow for the trailing \r, normally not used but a good
95854359Sroberto		  handle for tty_clk or somesuch kernel timestamper.
95954359Sroberto		*/
96054359Sroberto		if(arc_last_offset > LENARC) {
961132451Sroberto#ifdef DEBUG
96254359Sroberto			if(debug) {
96354359Sroberto				printf("arc: input code too long (%d cf %d); rejected.\n",
96454359Sroberto				       arc_last_offset, LENARC);
96554359Sroberto			}
96654359Sroberto#endif
96754359Sroberto			pp->lencode = 0;
96854359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
96954359Sroberto			return;
97054359Sroberto		}
97154359Sroberto
97254359Sroberto		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
973132451Sroberto#ifdef DEBUG
97454359Sroberto		if(debug > 1) {
97554359Sroberto			printf(
97654359Sroberto				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
97754359Sroberto				((rbufp->recv_length > 1) ? "*** " : ""),
97854359Sroberto				rbufp->recv_length,
97954359Sroberto				arc_last_offset,
98054359Sroberto				mfptoms((unsigned long)0,
98154359Sroberto					charoffsets[arc_last_offset],
98254359Sroberto					1));
98354359Sroberto		}
98454359Sroberto#endif
98554359Sroberto
98654359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES
98754359Sroberto		/*
98854359Sroberto		  If taking multiple samples, capture the current adjusted
98954359Sroberto		  sample iff:
99054359Sroberto
99154359Sroberto		  * No timestamp has yet been captured (it is zero), OR
99254359Sroberto
99354359Sroberto		  * This adjusted timestamp is earlier than the one already
99454359Sroberto		  captured, on the grounds that this one suffered less
99554359Sroberto		  delay in being delivered to us and is more accurate.
99654359Sroberto
99754359Sroberto		*/
99854359Sroberto		if(L_ISZERO(&(up->lastrec)) ||
99954359Sroberto		   L_ISGEQ(&(up->lastrec), &timestamp))
100054359Sroberto#endif
100154359Sroberto		{
1002132451Sroberto#ifdef DEBUG
100354359Sroberto			if(debug > 1) {
100454359Sroberto				printf("arc: system timestamp captured.\n");
100554359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES
100654359Sroberto				if(!L_ISZERO(&(up->lastrec))) {
100754359Sroberto					l_fp diff;
100854359Sroberto					diff = up->lastrec;
100954359Sroberto					L_SUB(&diff, &timestamp);
101054359Sroberto					printf("arc: adjusted timestamp by -%sms.\n",
1011290001Sglebius					       mfptoms(diff.l_ui, diff.l_uf, 3));
101254359Sroberto				}
101354359Sroberto#endif
101454359Sroberto			}
101554359Sroberto#endif
101654359Sroberto			up->lastrec = timestamp;
101754359Sroberto		}
101854359Sroberto
101954359Sroberto	}
102054359Sroberto
102154359Sroberto	/* Just in case we still have lots of rubbish in the buffer... */
102254359Sroberto	/* ...and to avoid the same timestamp being reused by mistake, */
102354359Sroberto	/* eg on receipt of the \r coming in on its own after the      */
1024290001Sglebius	/* timecode.						       */
102554359Sroberto	if(pp->lencode >= LENARC) {
1026132451Sroberto#ifdef DEBUG
102754359Sroberto		if(debug && (rbufp->recv_buffer[0] != '\r'))
102854359Sroberto		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
102954359Sroberto#endif
103054359Sroberto		pp->lencode = 0;
103154359Sroberto		return;
103254359Sroberto	}
103354359Sroberto
103454359Sroberto	/* Append input to code buffer, avoiding overflow. */
103554359Sroberto	for(i = 0; i < rbufp->recv_length; i++) {
103654359Sroberto		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
103754359Sroberto		c = rbufp->recv_buffer[i];
103854359Sroberto
103954359Sroberto		/* Drop trailing '\r's and drop `h' command echo totally. */
104054359Sroberto		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
104154359Sroberto
104254359Sroberto		/*
104354359Sroberto		  If we've just put an `o' in the lastcode[0], clear the
104454359Sroberto		  timestamp in anticipation of a timecode arriving soon.
104554359Sroberto
104654359Sroberto		  We would expect to get to process this before any of the
104754359Sroberto		  timecode arrives.
104854359Sroberto		*/
104954359Sroberto		if((c == 'o') && (pp->lencode == 1)) {
105054359Sroberto			L_CLR(&(up->lastrec));
1051132451Sroberto#ifdef DEBUG
105254359Sroberto			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
105354359Sroberto#endif
105454359Sroberto		}
105554359Sroberto	}
1056132451Sroberto	if (pp->lencode == 0) return;
105754359Sroberto
105854359Sroberto	/* Handle a quality message. */
105954359Sroberto	if(pp->a_lastcode[0] == 'g') {
106054359Sroberto		int r, q;
106154359Sroberto
106254359Sroberto		if(pp->lencode < 3) { return; } /* Need more data... */
106354359Sroberto		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
106454359Sroberto		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
106554359Sroberto		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
106654359Sroberto		   ((r & 0x70) != 0x30)) {
106754359Sroberto			/* Badly formatted response. */
1068132451Sroberto#ifdef DEBUG
106954359Sroberto			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
107054359Sroberto#endif
107154359Sroberto			return;
107254359Sroberto		}
107354359Sroberto		if(r == '3') { /* Only use quality value whilst sync in progress. */
1074132451Sroberto			if (up->quality_stamp < current_time) {
1075132451Sroberto				struct calendar cal;
1076132451Sroberto				l_fp new_stamp;
1077132451Sroberto
1078132451Sroberto				get_systime (&new_stamp);
1079132451Sroberto				caljulian (new_stamp.l_ui, &cal);
1080132451Sroberto				up->quality_stamp =
1081132451Sroberto					current_time + 60 - cal.second + 5;
1082132451Sroberto				quality_sum = 0;
1083132451Sroberto				quality_polls = 0;
1084132451Sroberto			}
1085132451Sroberto			quality_sum += (q & 0xf);
1086132451Sroberto			quality_polls++;
1087132451Sroberto			quality_average = (quality_sum / quality_polls);
108854359Sroberto#ifdef DEBUG
1089132451Sroberto			if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
109054359Sroberto#endif
109154359Sroberto		} else if( /* (r == '2') && */ up->resyncing) {
1092132451Sroberto			up->quality = quality_average;
109354359Sroberto#ifdef DEBUG
109454359Sroberto			if(debug)
109554359Sroberto			{
109654359Sroberto				printf("arc: sync finished, signal quality %d: %s\n",
109754359Sroberto				       up->quality,
109854359Sroberto				       quality_action(up->quality));
109954359Sroberto			}
110054359Sroberto#endif
110154359Sroberto			msyslog(LOG_NOTICE,
1102290001Sglebius				"ARCRON: sync finished, signal quality %d: %s",
1103290001Sglebius				up->quality,
1104290001Sglebius				quality_action(up->quality));
110554359Sroberto			up->resyncing = 0; /* Resync is over. */
1106132451Sroberto			quality_average = 0;
1107132451Sroberto			quality_sum = 0;
1108132451Sroberto			quality_polls = 0;
110954359Sroberto
111054359Sroberto#ifdef ARCRON_KEEN
111154359Sroberto			/* Clock quality dubious; resync earlier than usual. */
111254359Sroberto			if((up->quality == QUALITY_UNKNOWN) ||
111354359Sroberto			   (up->quality < MIN_CLOCK_QUALITY_OK))
111454359Sroberto			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
111554359Sroberto#endif
111654359Sroberto		}
111754359Sroberto		pp->lencode = 0;
111854359Sroberto		return;
111954359Sroberto	}
112054359Sroberto
112154359Sroberto	/* Stop now if this is not a timecode message. */
112254359Sroberto	if(pp->a_lastcode[0] != 'o') {
112354359Sroberto		pp->lencode = 0;
112454359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
112554359Sroberto		return;
112654359Sroberto	}
112754359Sroberto
112854359Sroberto	/* If we don't have enough data, wait for more... */
112954359Sroberto	if(pp->lencode < LENARC) { return; }
113054359Sroberto
113154359Sroberto
113254359Sroberto	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
1133132451Sroberto#ifdef DEBUG
113454359Sroberto	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
113554359Sroberto#endif
113654359Sroberto
113754359Sroberto	/* But check that we actually captured a system timestamp on it. */
113854359Sroberto	if(L_ISZERO(&(up->lastrec))) {
1139132451Sroberto#ifdef DEBUG
114054359Sroberto		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
114154359Sroberto#endif
114254359Sroberto		pp->lencode = 0;
114354359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
114454359Sroberto		return;
114554359Sroberto	}
114654359Sroberto	/*
114754359Sroberto	  Append a mark of the clock's received signal quality for the
114854359Sroberto	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
114954359Sroberto	  quality value to `6' for his s/w) and terminate the string for
115054359Sroberto	  sure.  This should not go off the buffer end.
115154359Sroberto	*/
115254359Sroberto	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
115354359Sroberto				       '6' : ('0' + up->quality));
115454359Sroberto	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
115554359Sroberto
1156132451Sroberto#ifdef PRE_NTP420
115754359Sroberto	/* We don't use the micro-/milli- second part... */
115854359Sroberto	pp->usec = 0;
115954359Sroberto	pp->msec = 0;
1160132451Sroberto#else
1161132451Sroberto	/* We don't use the nano-second part... */
1162132451Sroberto	pp->nsec = 0;
1163132451Sroberto#endif
116454359Sroberto	/* Validate format and numbers. */
1165182007Sroberto	if (pp->a_lastcode[0] != 'o'
1166182007Sroberto		|| !get2(pp->a_lastcode + 1, &pp->hour)
1167182007Sroberto		|| !get2(pp->a_lastcode + 3, &pp->minute)
1168182007Sroberto		|| !get2(pp->a_lastcode + 5, &pp->second)
1169182007Sroberto		|| !get1(pp->a_lastcode + 7, &wday)
1170182007Sroberto		|| !get2(pp->a_lastcode + 8, &pp->day)
1171182007Sroberto		|| !get2(pp->a_lastcode + 10, &month)
1172182007Sroberto		|| !get2(pp->a_lastcode + 12, &pp->year)) {
1173132451Sroberto#ifdef DEBUG
117454359Sroberto		/* Would expect to have caught major problems already... */
117554359Sroberto		if(debug) { printf("arc: badly formatted data.\n"); }
117654359Sroberto#endif
1177132451Sroberto		pp->lencode = 0;
117854359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
117954359Sroberto		return;
118054359Sroberto	}
1181182007Sroberto	flags = pp->a_lastcode[14];
1182182007Sroberto	status = pp->a_lastcode[15];
1183182007Sroberto#ifdef DEBUG
1184182007Sroberto	if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
1185182007Sroberto#endif
1186182007Sroberto	n = 9;
1187182007Sroberto
118854359Sroberto	/*
118954359Sroberto	  Validate received values at least enough to prevent internal
119054359Sroberto	  array-bounds problems, etc.
119154359Sroberto	*/
119254359Sroberto	if((pp->hour < 0) || (pp->hour > 23) ||
119354359Sroberto	   (pp->minute < 0) || (pp->minute > 59) ||
119454359Sroberto	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
119554359Sroberto	   (wday < 1) || (wday > 7) ||
119654359Sroberto	   (pp->day < 1) || (pp->day > 31) ||
119754359Sroberto	   (month < 1) || (month > 12) ||
119854359Sroberto	   (pp->year < 0) || (pp->year > 99)) {
119954359Sroberto		/* Data out of range. */
1200132451Sroberto		pp->lencode = 0;
120154359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
120254359Sroberto		return;
120354359Sroberto	}
1204132451Sroberto
1205132451Sroberto
1206132451Sroberto	if(peer->MODE == 0) { /* compatiblity to original version */
1207132451Sroberto		int bst = flags;
1208132451Sroberto		/* Check that BST/UTC bits are the complement of one another. */
1209132451Sroberto		if(!(bst & 2) == !(bst & 4)) {
1210132451Sroberto			pp->lencode = 0;
1211132451Sroberto			refclock_report(peer, CEVNT_BADREPLY);
1212132451Sroberto			return;
1213132451Sroberto		}
121454359Sroberto	}
121554359Sroberto	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
121654359Sroberto
121754359Sroberto	/* Year-2000 alert! */
121854359Sroberto	/* Attempt to wrap 2-digit date into sensible window. */
121954359Sroberto	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
122054359Sroberto	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
122154359Sroberto	/*
122254359Sroberto	  Attempt to do the right thing by screaming that the code will
122354359Sroberto	  soon break when we get to the end of its useful life.  What a
122454359Sroberto	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
122554359Sroberto	*/
122654359Sroberto	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
122754359Sroberto		/*This should get attention B^> */
122854359Sroberto		msyslog(LOG_NOTICE,
1229290001Sglebius			"ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
123054359Sroberto	}
123154359Sroberto#ifdef DEBUG
123254359Sroberto	if(debug) {
123354359Sroberto		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
123454359Sroberto		       n,
123554359Sroberto		       pp->hour, pp->minute, pp->second,
1236132451Sroberto		       pp->day, month, pp->year, flags, status);
123754359Sroberto	}
123854359Sroberto#endif
123954359Sroberto
124054359Sroberto	/*
124154359Sroberto	  The status value tested for is not strictly supported by the
124254359Sroberto	  clock spec since the value of bit 2 (0x4) is claimed to be
124354359Sroberto	  undefined for MSF, yet does seem to indicate if the last resync
124454359Sroberto	  was successful or not.
124554359Sroberto	*/
124654359Sroberto	pp->leap = LEAP_NOWARNING;
124754359Sroberto	status &= 0x7;
124854359Sroberto	if(status == 0x3) {
124954359Sroberto		if(status != up->status)
125054359Sroberto		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
125154359Sroberto	} else {
125254359Sroberto		if(status != up->status) {
125354359Sroberto			msyslog(LOG_NOTICE, "ARCRON: signal lost");
125454359Sroberto			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
125554359Sroberto			up->status = status;
1256132451Sroberto			pp->lencode = 0;
125754359Sroberto			refclock_report(peer, CEVNT_FAULT);
125854359Sroberto			return;
125954359Sroberto		}
126054359Sroberto	}
126154359Sroberto	up->status = status;
126254359Sroberto
1263132451Sroberto	if (peer->MODE == 0) { /* compatiblity to original version */
1264132451Sroberto		int bst = flags;
126554359Sroberto
1266132451Sroberto		pp->day += moff[month - 1];
126754359Sroberto
1268132451Sroberto		if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
1269132451Sroberto
1270132451Sroberto		/* Convert to UTC if required */
1271132451Sroberto		if(bst & 2) {
1272132451Sroberto			pp->hour--;
1273132451Sroberto			if (pp->hour < 0) {
1274132451Sroberto				pp->hour = 23;
1275132451Sroberto				pp->day--;
1276132451Sroberto				/* If we try to wrap round the year
1277132451Sroberto				 * (BST on 1st Jan), reject.*/
1278132451Sroberto				if(pp->day < 0) {
1279132451Sroberto					pp->lencode = 0;
1280132451Sroberto					refclock_report(peer, CEVNT_BADTIME);
1281132451Sroberto					return;
1282132451Sroberto				}
1283132451Sroberto			}
1284132451Sroberto		}
1285132451Sroberto	}
1286132451Sroberto
1287132451Sroberto	if(peer->MODE > 0) {
1288132451Sroberto		if(pp->sloppyclockflag & CLK_FLAG1) {
1289132451Sroberto			struct tm  local;
1290290001Sglebius			struct tm *gmtp;
1291290001Sglebius			time_t	   unixtime;
1292132451Sroberto
1293290001Sglebius			/*
1294290001Sglebius			 * Convert to GMT for sites that distribute localtime.
1295132451Sroberto			 * This means we have to do Y2K conversion on the
1296132451Sroberto			 * 2-digit year; otherwise, we get the time wrong.
1297290001Sglebius			 */
1298290001Sglebius
1299290001Sglebius			memset(&local, 0, sizeof(local));
1300290001Sglebius
1301132451Sroberto			local.tm_year  = pp->year-1900;
1302290001Sglebius			local.tm_mon   = month-1;
1303290001Sglebius			local.tm_mday  = pp->day;
1304290001Sglebius			local.tm_hour  = pp->hour;
1305290001Sglebius			local.tm_min   = pp->minute;
1306290001Sglebius			local.tm_sec   = pp->second;
1307290001Sglebius			switch (peer->MODE) {
1308132451Sroberto			    case 1:
1309132451Sroberto				local.tm_isdst = (flags & 2);
1310132451Sroberto				break;
1311132451Sroberto			    case 2:
1312290001Sglebius				local.tm_isdst = (flags & 2);
1313132451Sroberto				break;
1314132451Sroberto			    case 3:
1315132451Sroberto				switch (flags & 3) {
1316132451Sroberto				    case 0: /* It is unclear exactly when the
1317290001Sglebius					       Arcron changes from DST->ST and
1318132451Sroberto					       ST->DST. Testing has shown this
1319132451Sroberto					       to be irregular. For the time
1320132451Sroberto					       being, let the OS decide. */
1321290001Sglebius					local.tm_isdst = 0;
1322132451Sroberto#ifdef DEBUG
1323132451Sroberto					if (debug)
1324132451Sroberto					    printf ("arc: DST = 00 (0)\n");
1325132451Sroberto#endif
1326132451Sroberto					break;
1327132451Sroberto				    case 1: /* dst->st time */
1328290001Sglebius					local.tm_isdst = -1;
1329132451Sroberto#ifdef DEBUG
1330132451Sroberto					if (debug)
1331132451Sroberto					    printf ("arc: DST = 01 (1)\n");
1332132451Sroberto#endif
1333132451Sroberto					break;
1334132451Sroberto				    case 2: /* st->dst time */
1335290001Sglebius					local.tm_isdst = -1;
1336132451Sroberto#ifdef DEBUG
1337132451Sroberto					if (debug)
1338132451Sroberto					    printf ("arc: DST = 10 (2)\n");
1339132451Sroberto#endif
1340132451Sroberto					break;
1341132451Sroberto				    case 3: /* dst time */
1342132451Sroberto				        local.tm_isdst = 1;
1343132451Sroberto#ifdef DEBUG
1344132451Sroberto					if (debug)
1345132451Sroberto					    printf ("arc: DST = 11 (3)\n");
1346132451Sroberto#endif
1347132451Sroberto					break;
1348132451Sroberto				}
1349132451Sroberto				break;
1350132451Sroberto			    default:
1351132451Sroberto				msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
1352290001Sglebius					peer->MODE);
135354359Sroberto				return;
1354132451Sroberto				break;
135554359Sroberto			}
1356290001Sglebius			unixtime = mktime (&local);
1357290001Sglebius			if ((gmtp = gmtime (&unixtime)) == NULL)
1358290001Sglebius			{
1359132451Sroberto				pp->lencode = 0;
1360290001Sglebius				refclock_report (peer, CEVNT_FAULT);
1361290001Sglebius				return;
1362290001Sglebius			}
1363132451Sroberto			pp->year = gmtp->tm_year+1900;
1364290001Sglebius			month = gmtp->tm_mon+1;
1365290001Sglebius			pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
1366290001Sglebius			/* pp->day = gmtp->tm_yday; */
1367290001Sglebius			pp->hour = gmtp->tm_hour;
1368290001Sglebius			pp->minute = gmtp->tm_min;
1369290001Sglebius			pp->second = gmtp->tm_sec;
1370132451Sroberto#ifdef DEBUG
1371290001Sglebius			if (debug)
1372132451Sroberto			{
1373132451Sroberto				printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
1374132451Sroberto					pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
1375132451Sroberto					pp->second);
1376132451Sroberto			}
1377132451Sroberto#endif
1378132451Sroberto		} else
1379132451Sroberto		{
1380290001Sglebius			/*
1381290001Sglebius			* For more rational sites distributing UTC
1382290001Sglebius			*/
1383290001Sglebius			pp->day    = ymd2yd(pp->year,month,pp->day);
138454359Sroberto		}
138554359Sroberto	}
138654359Sroberto
1387132451Sroberto	if (peer->MODE == 0) { /* compatiblity to original version */
1388132451Sroberto				/* If clock signal quality is
1389132451Sroberto				 * unknown, revert to default PRECISION...*/
1390132451Sroberto		if(up->quality == QUALITY_UNKNOWN) {
1391132451Sroberto			peer->precision = PRECISION;
1392132451Sroberto		} else { /* ...else improve precision if flag3 is set... */
1393132451Sroberto			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1394132451Sroberto					   HIGHPRECISION : PRECISION);
1395132451Sroberto		}
1396132451Sroberto	} else {
1397132451Sroberto		if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
1398132451Sroberto			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1399132451Sroberto					   HIGHPRECISION : PRECISION);
1400132451Sroberto		} else if (up->quality == QUALITY_UNKNOWN) {
1401132451Sroberto			peer->precision = PRECISION;
1402132451Sroberto		} else {
1403132451Sroberto			peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1404132451Sroberto					   HIGHPRECISION : PRECISION);
1405132451Sroberto		}
140654359Sroberto	}
140754359Sroberto
140854359Sroberto	/* Notice and log any change (eg from initial defaults) for flags. */
140954359Sroberto	if(up->saved_flags != pp->sloppyclockflag) {
1410132451Sroberto#ifdef DEBUG
141154359Sroberto		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1412290001Sglebius			((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1413290001Sglebius			((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1414290001Sglebius			((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1415290001Sglebius			((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
141654359Sroberto		/* Note effects of flags changing... */
141754359Sroberto		if(debug) {
141854359Sroberto			printf("arc: PRECISION = %d.\n", peer->precision);
141954359Sroberto		}
142054359Sroberto#endif
142154359Sroberto		up->saved_flags = pp->sloppyclockflag;
142254359Sroberto	}
142354359Sroberto
142454359Sroberto	/* Note time of last believable timestamp. */
142554359Sroberto	pp->lastrec = up->lastrec;
142654359Sroberto
142754359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
142854359Sroberto	/* Find out if a leap-second might just have happened...
142954359Sroberto	   (ie is this the first hour of the first day of Jan or Jul?)
143054359Sroberto	*/
143154359Sroberto	if((pp->hour == 0) &&
143254359Sroberto	   (pp->day == 1) &&
143354359Sroberto	   ((month == 1) || (month == 7))) {
143454359Sroberto		if(possible_leap >= 0) {
143554359Sroberto			/* A leap may have happened, and no resync has started yet...*/
143654359Sroberto			possible_leap = 1;
143754359Sroberto		}
143854359Sroberto	} else {
143954359Sroberto		/* Definitely not leap-second territory... */
144054359Sroberto		possible_leap = 0;
144154359Sroberto	}
144254359Sroberto#endif
144354359Sroberto
144454359Sroberto	if (!refclock_process(pp)) {
1445132451Sroberto		pp->lencode = 0;
144654359Sroberto		refclock_report(peer, CEVNT_BADTIME);
144754359Sroberto		return;
144854359Sroberto	}
1449132451Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
145054359Sroberto	refclock_receive(peer);
145154359Sroberto}
145254359Sroberto
145354359Sroberto
145454359Sroberto/* request_time() sends a time request to the clock with given peer. */
145554359Sroberto/* This automatically reports a fault if necessary. */
145654359Sroberto/* No data should be sent after this until arc_poll() returns. */
1457290001Sglebiusstatic  void    request_time    (int, struct peer *);
145854359Srobertostatic void
145954359Srobertorequest_time(
146054359Sroberto	int unit,
146154359Sroberto	struct peer *peer
146254359Sroberto	)
146354359Sroberto{
146454359Sroberto	struct refclockproc *pp = peer->procptr;
1465290001Sglebius	register struct arcunit *up = pp->unitptr;
146654359Sroberto#ifdef DEBUG
146754359Sroberto	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
146854359Sroberto#endif
146954359Sroberto	if (!send_slow(up, pp->io.fd, "o\r")) {
1470132451Sroberto#ifdef DEBUG
1471132451Sroberto		if (debug) {
1472132451Sroberto			printf("arc: unit %d: problem sending", unit);
1473132451Sroberto		}
147454359Sroberto#endif
1475132451Sroberto		pp->lencode = 0;
147654359Sroberto		refclock_report(peer, CEVNT_FAULT);
147754359Sroberto		return;
147854359Sroberto	}
147954359Sroberto	pp->polls++;
148054359Sroberto}
148154359Sroberto
148254359Sroberto/*
148354359Sroberto * arc_poll - called by the transmit procedure
148454359Sroberto */
148554359Srobertostatic void
148654359Srobertoarc_poll(
148754359Sroberto	int unit,
148854359Sroberto	struct peer *peer
148954359Sroberto	)
149054359Sroberto{
149154359Sroberto	register struct arcunit *up;
149254359Sroberto	struct refclockproc *pp;
149354359Sroberto	int resync_needed;              /* Should we start a resync? */
149454359Sroberto
149554359Sroberto	pp = peer->procptr;
1496290001Sglebius	up = pp->unitptr;
1497132451Sroberto#if 0
149854359Sroberto	pp->lencode = 0;
149954359Sroberto	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
1500132451Sroberto#endif
150154359Sroberto
150254359Sroberto#if 0
150354359Sroberto	/* Flush input. */
150454359Sroberto	tcflush(pp->io.fd, TCIFLUSH);
150554359Sroberto#endif
150654359Sroberto
150754359Sroberto	/* Resync if our next scheduled resync time is here or has passed. */
1508132451Sroberto	resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
1509132451Sroberto			  (up->next_resync <= current_time) );
151054359Sroberto
151154359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
151254359Sroberto	/*
151354359Sroberto	  Try to catch a potential leap-second insertion or deletion quickly.
151454359Sroberto
151554359Sroberto	  In addition to the normal NTP fun of clocks that don't report
151654359Sroberto	  leap-seconds spooking their hosts, this clock does not even
151754359Sroberto	  sample the radio sugnal the whole time, so may miss a
151854359Sroberto	  leap-second insertion or deletion for up to a whole sample
151954359Sroberto	  time.
152054359Sroberto
152154359Sroberto	  To try to minimise this effect, if in the first few minutes of
152254359Sroberto	  the day immediately following a leap-second-insertion point
152354359Sroberto	  (ie in the first hour of the first day of the first and sixth
152454359Sroberto	  months), and if the last resync was in the previous day, and a
152554359Sroberto	  resync is not already in progress, resync the clock
152654359Sroberto	  immediately.
152754359Sroberto
152854359Sroberto	*/
152954359Sroberto	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
153054359Sroberto	   (!up->resyncing)) {          /* No resync in progress yet. */
153154359Sroberto		resync_needed = 1;
153254359Sroberto		possible_leap = -1;          /* Prevent multiple resyncs. */
153354359Sroberto		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
153454359Sroberto	}
153554359Sroberto#endif
153654359Sroberto
153754359Sroberto	/* Do a resync if required... */
153854359Sroberto	if(resync_needed) {
153954359Sroberto		/* First, reset quality value to `unknown' so we can detect */
154054359Sroberto		/* when a quality message has been responded to by this     */
154154359Sroberto		/* being set to some other value.                           */
154254359Sroberto		up->quality = QUALITY_UNKNOWN;
154354359Sroberto
154454359Sroberto		/* Note that we are resyncing... */
154554359Sroberto		up->resyncing = 1;
154654359Sroberto
154754359Sroberto		/* Now actually send the resync command and an immediate poll. */
154854359Sroberto#ifdef DEBUG
154954359Sroberto		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
155054359Sroberto#endif
155154359Sroberto		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
155254359Sroberto		send_slow(up, pp->io.fd, "h\r");
155354359Sroberto
155454359Sroberto		/* Schedule our next resync... */
155554359Sroberto		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
155654359Sroberto
155754359Sroberto		/* Drop through to request time if appropriate. */
155854359Sroberto	}
155954359Sroberto
156054359Sroberto	/* If clock quality is too poor to trust, indicate a fault. */
156154359Sroberto	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
156254359Sroberto	/* we'll cross our fingers and just hope that the thing     */
156354359Sroberto	/* synced so quickly we did not catch it---we'll            */
156454359Sroberto	/* double-check the clock is OK elsewhere.                  */
156554359Sroberto	if(
156654359Sroberto#ifdef ARCRON_KEEN
156754359Sroberto		(up->quality != QUALITY_UNKNOWN) &&
156854359Sroberto#else
156954359Sroberto		(up->quality == QUALITY_UNKNOWN) ||
157054359Sroberto#endif
157154359Sroberto		(up->quality < MIN_CLOCK_QUALITY_OK)) {
157254359Sroberto#ifdef DEBUG
157354359Sroberto		if(debug) {
157454359Sroberto			printf("arc: clock quality %d too poor.\n", up->quality);
157554359Sroberto		}
157654359Sroberto#endif
1577132451Sroberto		pp->lencode = 0;
157854359Sroberto		refclock_report(peer, CEVNT_FAULT);
157954359Sroberto		return;
158054359Sroberto	}
158154359Sroberto	/* This is the normal case: request a timestamp. */
158254359Sroberto	request_time(unit, peer);
158354359Sroberto}
158454359Sroberto
158554359Sroberto#else
1586290001SglebiusNONEMPTY_TRANSLATION_UNIT
158754359Sroberto#endif
1588