refclock_arc.c revision 54359
154359Sroberto/*
254359Sroberto * refclock_arc - clock driver for ARCRON MSF receivers
354359Sroberto */
454359Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
1054359Srobertostatic const char arc_version[] = { "V1.1 1997/06/23" };
1154359Sroberto
1254359Sroberto#undef ARCRON_DEBUG /* Define only while in development... */
1354359Sroberto
1454359Sroberto#ifndef ARCRON_NOT_KEEN
1554359Sroberto#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
1654359Sroberto#endif
1754359Sroberto
1854359Sroberto#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
1954359Sroberto#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
2054359Sroberto#endif
2154359Sroberto
2254359Sroberto#ifndef ARCRON_NOT_LEAPSECOND_KEEN
2354359Sroberto#ifndef ARCRON_LEAPSECOND_KEEN
2454359Sroberto#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
2554359Sroberto#endif
2654359Sroberto#endif
2754359Sroberto
2854359Sroberto/*
2954359SrobertoCode by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
3054359SrobertoModifications by Damon Hart-Davis, <d@hd.org>, 1997.
3154359Sroberto
3254359SrobertoTHIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
3354359SrobertoYOUR OWN RISK.
3454359Sroberto
3554359SrobertoOrginally developed and used with ntp3-5.85 by Derek Mulcahy.
3654359Sroberto
3754359SrobertoBuilt against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
3854359Sroberto
3954359SrobertoThis code may be freely copied and used and incorporated in other
4054359Srobertosystems providing the disclaimer and notice of authorship are
4154359Srobertoreproduced.
4254359Sroberto
4354359Sroberto-------------------------------------------------------------------------------
4454359Sroberto
4554359SrobertoAuthor's original note:
4654359Sroberto
4754359SrobertoI enclose my ntp driver for the Galleon Systems Arc MSF receiver.
4854359Sroberto
4954359SrobertoIt works (after a fashion) on both Solaris-1 and Solaris-2.
5054359Sroberto
5154359SrobertoI am currently using ntp3-5.85.  I have been running the code for
5254359Srobertoabout 7 months without any problems.  Even coped with the change to BST!
5354359Sroberto
5454359SrobertoI had to do some funky things to read from the clock because it uses the
5554359Srobertopower from the receive lines to drive the transmit lines.  This makes the
5654359Srobertocode look a bit stupid but it works.  I also had to put in some delays to
5754359Srobertoallow for the turnaround time from receive to transmit.  These delays
5854359Srobertoare between characters when requesting a time stamp so that shouldn't affect
5954359Srobertothe results too drastically.
6054359Sroberto
6154359Sroberto...
6254359Sroberto
6354359SrobertoThe bottom line is that it works but could easily be improved.  You are
6454359Srobertofree to do what you will with the code.  I haven't been able to determine
6554359Srobertohow good the clock is.  I think that this requires a known good clock
6654359Srobertoto compare it against.
6754359Sroberto
6854359Sroberto-------------------------------------------------------------------------------
6954359Sroberto
7054359SrobertoDamon's notes for adjustments:
7154359Sroberto
7254359SrobertoMAJOR CHANGES SINCE V1.0
7354359Sroberto========================
7454359Sroberto 1) Removal of pollcnt variable that made the clock go permanently
7554359Sroberto    off-line once two time polls failed to gain responses.
7654359Sroberto
7754359Sroberto 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
7854359Sroberto    terminal of the process when we do a low-level open().
7954359Sroberto
8054359Sroberto 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
8154359Sroberto    defined) to try to resync quickly after a potential leap-second
8254359Sroberto    insertion or deletion.
8354359Sroberto
8454359Sroberto 4) Code significantly slimmer at run-time than V1.0.
8554359Sroberto
8654359Sroberto
8754359SrobertoGENERAL
8854359Sroberto=======
8954359Sroberto
9054359Sroberto 1) The C preprocessor symbol to have the clock built has been changed
9154359Sroberto    from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
9254359Sroberto    possiblity of clashes with other symbols in the future.
9354359Sroberto
9454359Sroberto 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
9554359Sroberto
9654359Sroberto     a) The ARC documentation claims the internal clock is (only)
9754359Sroberto        accurate to about 20ms relative to Rugby (plus there must be
9854359Sroberto        noticable drift and delay in the ms range due to transmission
9954359Sroberto        delays and changing atmospheric effects).  This clock is not
10054359Sroberto        designed for ms accuracy as NTP has spoilt us all to expect.
10154359Sroberto
10254359Sroberto     b) The clock oscillator looks like a simple uncompensated quartz
10354359Sroberto        crystal of the sort used in digital watches (ie 32768Hz) which
10454359Sroberto        can have large temperature coefficients and drifts; it is not
10554359Sroberto        clear if this oscillator is properly disciplined to the MSF
10654359Sroberto        transmission, but as the default is to resync only once per
10754359Sroberto        *day*, we can imagine that it is not, and is free-running.  We
10854359Sroberto        can minimise drift by resyncing more often (at the cost of
10954359Sroberto        reduced battery life), but drift/wander may still be
11054359Sroberto        significant.
11154359Sroberto
11254359Sroberto     c) Note that the bit time of 3.3ms adds to the potential error in
11354359Sroberto        the the clock timestamp, since the bit clock of the serial link
11454359Sroberto        may effectively be free-running with respect to the host clock
11554359Sroberto        and the MSF clock.  Actually, the error is probably 1/16th of
11654359Sroberto        the above, since the input data is probably sampled at at least
11754359Sroberto        16x the bit rate.
11854359Sroberto
11954359Sroberto    By keeping the clock marked as not very precise, it will have a
12054359Sroberto    fairly large dispersion, and thus will tend to be used as a
12154359Sroberto    `backup' time source and sanity checker, which this clock is
12254359Sroberto    probably ideal for.  For an isolated network without other time
12354359Sroberto    sources, this clock can probably be expected to provide *much*
12454359Sroberto    better than 1s accuracy, which will be fine.
12554359Sroberto
12654359Sroberto    By default, PRECISION is set to -4, but experience, especially at a
12754359Sroberto    particular geographic location with a particular clock, may allow
12854359Sroberto    this to be altered to -5.  (Note that skews of +/- 10ms are to be
12954359Sroberto    expected from the clock from time-to-time.)  This improvement of
13054359Sroberto    reported precision can be instigated by setting flag3 to 1, though
13154359Sroberto    the PRECISION will revert to the normal value while the clock
13254359Sroberto    signal quality is unknown whatever the flag3 setting.
13354359Sroberto
13454359Sroberto    IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
13554359Sroberto    ANY RESIDUAL SKEW, eg:
13654359Sroberto
13754359Sroberto        server 127.127.27.0 # ARCRON MSF radio clock unit 0.
13854359Sroberto        # Fudge timestamps by about 20ms.
13954359Sroberto        fudge 127.127.27.0 time1 0.020
14054359Sroberto
14154359Sroberto    You will need to observe your system's behaviour, assuming you have
14254359Sroberto    some other NTP source to compare it with, to work out what the
14354359Sroberto    fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
14454359Sroberto    my MSF clock with my distance from the MSF transmitter, +20ms
14554359Sroberto    seemed about right, after some observation.
14654359Sroberto
14754359Sroberto 3) REFID has been made "MSFa" to reflect the MSF time source and the
14854359Sroberto    ARCRON receiver.
14954359Sroberto
15054359Sroberto 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
15154359Sroberto    forcing a resync since the last attempt.  This is picked to give a
15254359Sroberto    little less than an hour between resyncs and to try to avoid
15354359Sroberto    clashing with any regular event at a regular time-past-the-hour
15454359Sroberto    which might cause systematic errors.
15554359Sroberto
15654359Sroberto    The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
15754359Sroberto    running down its batteries unnecesarily if ntpd is going to crash
15854359Sroberto    or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
15954359Sroberto    then this period is long enough for (with normal polling rates)
16054359Sroberto    enough time samples to have been taken to allow ntpd to sync to
16154359Sroberto    the clock before the interruption for the clock to resync to MSF.
16254359Sroberto    This avoids ntpd syncing to another peer first and then
16354359Sroberto    almost immediately hopping to the MSF clock.
16454359Sroberto
16554359Sroberto    The RETRY_RESYNC_TIME is used before rescheduling a resync after a
16654359Sroberto    resync failed to reveal a statisfatory signal quality (too low or
16754359Sroberto    unknown).
16854359Sroberto
16954359Sroberto 5) The clock seems quite jittery, so I have increased the
17054359Sroberto    median-filter size from the typical (previous) value of 3.  I
17154359Sroberto    discard up to half the results in the filter.  It looks like maybe
17254359Sroberto    1 sample in 10 or so (maybe less) is a spike, so allow the median
17354359Sroberto    filter to discard at least 10% of its entries or 1 entry, whichever
17454359Sroberto    is greater.
17554359Sroberto
17654359Sroberto 6) Sleeping *before* each character sent to the unit to allow required
17754359Sroberto    inter-character time but without introducting jitter and delay in
17854359Sroberto    handling the response if possible.
17954359Sroberto
18054359Sroberto 7) If the flag ARCRON_KEEN is defined, take time samples whenever
18154359Sroberto    possible, even while resyncing, etc.  We rely, in this case, on the
18254359Sroberto    clock always giving us a reasonable time or else telling us in the
18354359Sroberto    status byte at the end of the timestamp that it failed to sync to
18454359Sroberto    MSF---thus we should never end up syncing to completely the wrong
18554359Sroberto    time.
18654359Sroberto
18754359Sroberto 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
18854359Sroberto    refclock median-filter routines to get round small bug in 3-5.90
18954359Sroberto    code which does not return the median offset. XXX Removed this
19054359Sroberto    bit due NTP Version 4 upgrade - dlm.
19154359Sroberto
19254359Sroberto 9) We would appear to have a year-2000 problem with this clock since
19354359Sroberto    it returns only the two least-significant digits of the year.  But
19454359Sroberto    ntpd ignores the year and uses the local-system year instead, so
19554359Sroberto    this is in fact not a problem.  Nevertheless, we attempt to do a
19654359Sroberto    sensible thing with the dates, wrapping them into a 100-year
19754359Sroberto    window.
19854359Sroberto
19954359Sroberto 10)Logs stats information that can be used by Derek's Tcl/Tk utility
20054359Sroberto    to show the status of the clock.
20154359Sroberto
20254359Sroberto 11)The clock documentation insists that the number of bits per
20354359Sroberto    character to be sent to the clock, and sent by it, is 11, including
20454359Sroberto    one start bit and two stop bits.  The data format is either 7+even
20554359Sroberto    or 8+none.
20654359Sroberto
20754359Sroberto
20854359SrobertoTO-DO LIST
20954359Sroberto==========
21054359Sroberto
21154359Sroberto  * Eliminate use of scanf(), and maybe sprintf().
21254359Sroberto
21354359Sroberto  * Allow user setting of resync interval to trade battery life for
21454359Sroberto    accuracy; maybe could be done via fudge factor or unit number.
21554359Sroberto
21654359Sroberto  * Possibly note the time since the last resync of the MSF clock to
21754359Sroberto    MSF as the age of the last reference timestamp, ie trust the
21854359Sroberto    clock's oscillator not very much...
21954359Sroberto
22054359Sroberto  * Add very slow auto-adjustment up to a value of +/- time2 to correct
22154359Sroberto    for long-term errors in the clock value (time2 defaults to 0 so the
22254359Sroberto    correction would be disabled by default).
22354359Sroberto
22454359Sroberto  * Consider trying to use the tty_clk/ppsclock support.
22554359Sroberto
22654359Sroberto  * Possibly use average or maximum signal quality reported during
22754359Sroberto    resync, rather than just the last one, which may be atypical.
22854359Sroberto
22954359Sroberto*/
23054359Sroberto
23154359Sroberto
23254359Sroberto/* Notes for HKW Elektronik GmBH Radio clock driver */
23354359Sroberto/* Author Lyndon David, Sentinet Ltd, Feb 1997      */
23454359Sroberto/* These notes seem also to apply usefully to the ARCRON clock. */
23554359Sroberto
23654359Sroberto/* The HKW clock module is a radio receiver tuned into the Rugby */
23754359Sroberto/* MSF time signal tranmitted on 60 kHz. The clock module connects */
23854359Sroberto/* to the computer via a serial line and transmits the time encoded */
23954359Sroberto/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
24054359Sroberto
24154359Sroberto/* Clock communications, from the datasheet */
24254359Sroberto/* All characters sent to the clock are echoed back to the controlling */
24354359Sroberto/* device. */
24454359Sroberto/* Transmit time/date information */
24554359Sroberto/* syntax ASCII o<cr> */
24654359Sroberto/* Character o may be replaced if neccesary by a character whose code */
24754359Sroberto/* contains the lowest four bits f(hex) eg */
24854359Sroberto/* syntax binary: xxxx1111 00001101 */
24954359Sroberto
25054359Sroberto/* DHD note:
25154359SrobertoYou have to wait for character echo + 10ms before sending next character.
25254359Sroberto*/
25354359Sroberto
25454359Sroberto/* The clock replies to this command with a sequence of 15 characters */
25554359Sroberto/* which contain the complete time and a final <cr> making 16 characters */
25654359Sroberto/* in total. */
25754359Sroberto/* The RC computer clock will not reply immediately to this command because */
25854359Sroberto/* the start bit edge of the first reply character marks the beginning of */
25954359Sroberto/* the second. So the RC Computer Clock will reply to this command at the */
26054359Sroberto/* start of the next second */
26154359Sroberto/* The characters have the following meaning */
26254359Sroberto/* 1. hours tens   */
26354359Sroberto/* 2. hours units  */
26454359Sroberto/* 3. minutes tens */
26554359Sroberto/* 4. minutes units */
26654359Sroberto/* 5. seconds tens  */
26754359Sroberto/* 6. seconds units */
26854359Sroberto/* 7. day of week 1-monday 7-sunday */
26954359Sroberto/* 8. day of month tens */
27054359Sroberto/* 9. day of month units */
27154359Sroberto/* 10. month tens */
27254359Sroberto/* 11. month units */
27354359Sroberto/* 12. year tens */
27454359Sroberto/* 13. year units */
27554359Sroberto/* 14. BST/UTC status */
27654359Sroberto/*      bit 7   parity */
27754359Sroberto/*      bit 6   always 0 */
27854359Sroberto/*      bit 5   always 1 */
27954359Sroberto/*      bit 4   always 1 */
28054359Sroberto/*      bit 3   always 0 */
28154359Sroberto/*      bit 2   =1 if UTC is in effect, complementary to the BST bit */
28254359Sroberto/*      bit 1   =1 if BST is in effect, according to the BST bit     */
28354359Sroberto/*      bit 0   BST/UTC change impending bit=1 in case of change impending */
28454359Sroberto/* 15. status */
28554359Sroberto/*      bit 7   parity */
28654359Sroberto/*      bit 6   always 0 */
28754359Sroberto/*      bit 5   always 1 */
28854359Sroberto/*      bit 4   always 1 */
28954359Sroberto/*      bit 3   =1 if low battery is detected */
29054359Sroberto/*      bit 2   =1 if the very last reception attempt failed and a valid */
29154359Sroberto/*              time information already exists (bit0=1) */
29254359Sroberto/*              =0 if the last reception attempt was successful */
29354359Sroberto/*      bit 1   =1 if at least one reception since 2:30 am was successful */
29454359Sroberto/*              =0 if no reception attempt since 2:30 am was successful */
29554359Sroberto/*      bit 0   =1 if the RC Computer Clock contains valid time information */
29654359Sroberto/*              This bit is zero after reset and one after the first */
29754359Sroberto/*              successful reception attempt */
29854359Sroberto
29954359Sroberto/* DHD note:
30054359SrobertoAlso note g<cr> command which confirms that a resync is in progress, and
30154359Srobertoif so what signal quality (0--5) is available.
30254359SrobertoAlso note h<cr> command which starts a resync to MSF signal.
30354359Sroberto*/
30454359Sroberto
30554359Sroberto
30654359Sroberto
30754359Sroberto#include <stdio.h>
30854359Sroberto#include <ctype.h>
30954359Sroberto#include <sys/time.h>
31054359Sroberto
31154359Sroberto#if defined(HAVE_BSD_TTYS)
31254359Sroberto#include <sgtty.h>
31354359Sroberto#endif /* HAVE_BSD_TTYS */
31454359Sroberto
31554359Sroberto#if defined(HAVE_SYSV_TTYS)
31654359Sroberto#include <termio.h>
31754359Sroberto#endif /* HAVE_SYSV_TTYS */
31854359Sroberto
31954359Sroberto#if defined(HAVE_TERMIOS)
32054359Sroberto#include <termios.h>
32154359Sroberto#endif
32254359Sroberto
32354359Sroberto#include "ntpd.h"
32454359Sroberto#include "ntp_io.h"
32554359Sroberto#include "ntp_refclock.h"
32654359Sroberto#include "ntp_stdlib.h"
32754359Sroberto
32854359Sroberto/*
32954359Sroberto * This driver supports the ARCRON MSF Radio Controlled Clock
33054359Sroberto */
33154359Sroberto
33254359Sroberto/*
33354359Sroberto * Interface definitions
33454359Sroberto */
33554359Sroberto#define DEVICE          "/dev/arc%d"    /* Device name and unit. */
33654359Sroberto#define SPEED           B300            /* UART speed (300 baud) */
33754359Sroberto#define PRECISION       (-4)            /* Precision  (~63 ms). */
33854359Sroberto#define HIGHPRECISION   (-5)            /* If things are going well... */
33954359Sroberto#define REFID           "MSFa"          /* Reference ID. */
34054359Sroberto#define DESCRIPTION     "ARCRON MSF Receiver"
34154359Sroberto
34254359Sroberto#define NSAMPLESLONG    8               /* Stages of long filter. */
34354359Sroberto
34454359Sroberto#define LENARC          16              /* Format `o' timecode length. */
34554359Sroberto
34654359Sroberto#define BITSPERCHAR     11              /* Bits per character. */
34754359Sroberto#define BITTIME         0x0DA740E       /* Time for 1 bit at 300bps. */
34854359Sroberto#define CHARTIME10      0x8888888       /* Time for 10-bit char at 300bps. */
34954359Sroberto#define CHARTIME11      0x962FC96       /* Time for 11-bit char at 300bps. */
35054359Sroberto#define CHARTIME                        /* Time for char at 300bps. */ \
35154359Sroberto( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
35254359Sroberto				       (BITSPERCHAR * BITTIME) ) )
35354359Sroberto
35454359Sroberto     /* Allow for UART to accept char half-way through final stop bit. */
35554359Sroberto#define INITIALOFFSET (u_int32)(-BITTIME/2)
35654359Sroberto
35754359Sroberto     /*
35854359Sroberto    charoffsets[x] is the time after the start of the second that byte
35954359Sroberto    x (with the first byte being byte 1) is received by the UART,
36054359Sroberto    assuming that the initial edge of the start bit of the first byte
36154359Sroberto    is on-time.  The values are represented as the fractional part of
36254359Sroberto    an l_fp.
36354359Sroberto
36454359Sroberto    We store enough values to have the offset of each byte including
36554359Sroberto    the trailing \r, on the assumption that the bytes follow one
36654359Sroberto    another without gaps.
36754359Sroberto    */
36854359Sroberto     static const u_int32 charoffsets[LENARC+1] = {
36954359Sroberto#if BITSPERCHAR == 11 /* Usual case. */
37054359Sroberto	     /* Offsets computed as accurately as possible... */
37154359Sroberto	     0,
37254359Sroberto	     INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
37354359Sroberto	     INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
37454359Sroberto	     INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
37554359Sroberto	     INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
37654359Sroberto	     INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
37754359Sroberto	     INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
37854359Sroberto	     INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
37954359Sroberto	     INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
38054359Sroberto	     INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
38154359Sroberto	     INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
38254359Sroberto	     INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
38354359Sroberto	     INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
38454359Sroberto	     INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
38554359Sroberto	     INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
38654359Sroberto	     INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
38754359Sroberto	     INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
38854359Sroberto#else
38954359Sroberto	     /* Offsets computed with a small rounding error... */
39054359Sroberto	     0,
39154359Sroberto	     INITIALOFFSET +  1 * CHARTIME,
39254359Sroberto	     INITIALOFFSET +  2 * CHARTIME,
39354359Sroberto	     INITIALOFFSET +  3 * CHARTIME,
39454359Sroberto	     INITIALOFFSET +  4 * CHARTIME,
39554359Sroberto	     INITIALOFFSET +  5 * CHARTIME,
39654359Sroberto	     INITIALOFFSET +  6 * CHARTIME,
39754359Sroberto	     INITIALOFFSET +  7 * CHARTIME,
39854359Sroberto	     INITIALOFFSET +  8 * CHARTIME,
39954359Sroberto	     INITIALOFFSET +  9 * CHARTIME,
40054359Sroberto	     INITIALOFFSET + 10 * CHARTIME,
40154359Sroberto	     INITIALOFFSET + 11 * CHARTIME,
40254359Sroberto	     INITIALOFFSET + 12 * CHARTIME,
40354359Sroberto	     INITIALOFFSET + 13 * CHARTIME,
40454359Sroberto	     INITIALOFFSET + 14 * CHARTIME,
40554359Sroberto	     INITIALOFFSET + 15 * CHARTIME,
40654359Sroberto	     INITIALOFFSET + 16 * CHARTIME
40754359Sroberto#endif
40854359Sroberto     };
40954359Sroberto
41054359Sroberto/* Chose filter length dependent on fudge flag 4. */
41154359Sroberto#define CHOSENSAMPLES(pp) \
41254359Sroberto(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES)
41354359Sroberto     /*
41454359SrobertoChose how many filter samples to keep.  Several factors are in play.
41554359Sroberto
41654359Sroberto 1) Discard at least one sample to allow a spike value to be
41754359Sroberto    discarded.
41854359Sroberto
41954359Sroberto 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes.
42054359Sroberto
42154359Sroberto 3) Keep an odd number of samples to avoid median value being biased
42254359Sroberto    high or low.
42354359Sroberto*/
42454359Sroberto#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1)
42554359Sroberto
42654359Sroberto#define DEFAULT_RESYNC_TIME  (57*60)    /* Gap between resync attempts (s). */
42754359Sroberto#define RETRY_RESYNC_TIME    (27*60)    /* Gap to emergency resync attempt. */
42854359Sroberto#ifdef ARCRON_KEEN
42954359Sroberto#define INITIAL_RESYNC_DELAY 500        /* Delay before first resync. */
43054359Sroberto#else
43154359Sroberto#define INITIAL_RESYNC_DELAY 50         /* Delay before first resync. */
43254359Sroberto#endif
43354359Sroberto
43454359Sroberto     static const int moff[12] =
43554359Sroberto{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
43654359Sroberto/* Flags for a raw open() of the clock serial device. */
43754359Sroberto#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
43854359Sroberto#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
43954359Sroberto#else           /* Oh well, it may not matter... */
44054359Sroberto#define OPEN_FLAGS (O_RDWR)
44154359Sroberto#endif
44254359Sroberto
44354359Sroberto
44454359Sroberto/* Length of queue of command bytes to be sent. */
44554359Sroberto#define CMDQUEUELEN 4                   /* Enough for two cmds + each \r. */
44654359Sroberto/* Queue tick time; interval in seconds between chars taken off queue. */
44754359Sroberto/* Must be >= 2 to allow o\r response to come back uninterrupted. */
44854359Sroberto#define QUEUETICK   2                   /* Allow o\r reply to finish. */
44954359Sroberto
45054359Sroberto/*
45154359Sroberto * ARC unit control structure
45254359Sroberto */
45354359Srobertostruct arcunit {
45454359Sroberto	l_fp lastrec;       /* Time tag for the receive time (system). */
45554359Sroberto	int status;         /* Clock status. */
45654359Sroberto
45754359Sroberto	int quality;        /* Quality of reception 0--5 for unit. */
45854359Sroberto	/* We may also use the values -1 or 6 internally. */
45954359Sroberto
46054359Sroberto	u_long next_resync; /* Next resync time (s) compared to current_time. */
46154359Sroberto	int resyncing;      /* Resync in progress if true. */
46254359Sroberto
46354359Sroberto	/* In the outgoing queue, cmdqueue[0] is next to be sent. */
46454359Sroberto	char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
46554359Sroberto
46654359Sroberto	u_long saved_flags; /* Saved fudge flags. */
46754359Sroberto};
46854359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
46954359Sroberto/* The flag `possible_leap' is set non-zero when any MSF unit
47054359Sroberto       thinks a leap-second may have happened.
47154359Sroberto
47254359Sroberto       Set whenever we receive a valid time sample in the first hour of
47354359Sroberto       the first day of the first/seventh months.
47454359Sroberto
47554359Sroberto       Outside the special hour this value is unconditionally set
47654359Sroberto       to zero by the receive routine.
47754359Sroberto
47854359Sroberto       On finding itself in this timeslot, as long as the value is
47954359Sroberto       non-negative, the receive routine sets it to a positive value to
48054359Sroberto       indicate a resync to MSF should be performed.
48154359Sroberto
48254359Sroberto       In the poll routine, if this value is positive and we are not
48354359Sroberto       already resyncing (eg from a sync that started just before
48454359Sroberto       midnight), start resyncing and set this value negative to
48554359Sroberto       indicate that a leap-triggered resync has been started.  Having
48654359Sroberto       set this negative prevents the receive routine setting it
48754359Sroberto       positive and thus prevents multiple resyncs during the witching
48854359Sroberto       hour.
48954359Sroberto     */
49054359Srobertostatic int possible_leap = 0;       /* No resync required by default. */
49154359Sroberto#endif
49254359Sroberto
49354359Sroberto#if 0
49454359Srobertostatic void dummy_event_handler P((struct peer *));
49554359Srobertostatic void   arc_event_handler P((struct peer *));
49654359Sroberto#endif /* 0 */
49754359Sroberto
49854359Sroberto#define QUALITY_UNKNOWN     -1 /* Indicates unknown clock quality. */
49954359Sroberto#define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
50054359Sroberto#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
50154359Sroberto#define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
50254359Sroberto
50354359Sroberto/*
50454359Sroberto * Function prototypes
50554359Sroberto */
50654359Srobertostatic  int     arc_start       P((int, struct peer *));
50754359Srobertostatic  void    arc_shutdown    P((int, struct peer *));
50854359Srobertostatic  void    arc_receive     P((struct recvbuf *));
50954359Srobertostatic  void    arc_poll        P((int, struct peer *));
51054359Sroberto
51154359Sroberto/*
51254359Sroberto * Transfer vector
51354359Sroberto */
51454359Srobertostruct  refclock refclock_arc = {
51554359Sroberto	arc_start,              /* start up driver */
51654359Sroberto	arc_shutdown,           /* shut down driver */
51754359Sroberto	arc_poll,               /* transmit poll message */
51854359Sroberto	noentry,                /* not used (old arc_control) */
51954359Sroberto	noentry,                /* initialize driver (not used) */
52054359Sroberto	noentry,                /* not used (old arc_buginfo) */
52154359Sroberto	NOFLAGS                 /* not used */
52254359Sroberto};
52354359Sroberto
52454359Sroberto/* Queue us up for the next tick. */
52554359Sroberto#define ENQUEUE(up) \
52654359Sroberto	do { \
52754359Sroberto	     if((up)->ev.next != 0) { break; } /* WHOOPS! */ \
52854359Sroberto	     peer->nextdate = current_time + QUEUETICK; \
52954359Sroberto	} while(0)
53054359Sroberto
53154359Sroberto#if 0
53254359Sroberto/* Placeholder event handler---does nothing safely---soaks up lose tick. */
53354359Srobertostatic void
53454359Srobertodummy_event_handler(
53554359Sroberto	struct peer *peer
53654359Sroberto	)
53754359Sroberto{
53854359Sroberto#ifdef ARCRON_DEBUG
53954359Sroberto	if(debug) { printf("arc: dummy_event_handler() called.\n"); }
54054359Sroberto#endif
54154359Sroberto}
54254359Sroberto
54354359Sroberto/*
54454359SrobertoNormal event handler.
54554359Sroberto
54654359SrobertoTake first character off queue and send to clock if not a null.
54754359Sroberto
54854359SrobertoShift characters down and put a null on the end.
54954359Sroberto
55054359SrobertoWe assume that there is no parallelism so no race condition, but even
55154359Srobertoif there is nothing bad will happen except that we might send some bad
55254359Srobertodata to the clock once in a while.
55354359Sroberto*/
55454359Srobertostatic void
55554359Srobertoarc_event_handler(
55654359Sroberto	struct peer *peer
55754359Sroberto	)
55854359Sroberto{
55954359Sroberto	struct refclockproc *pp = peer->procptr;
56054359Sroberto	register struct arcunit *up = (struct arcunit *)pp->unitptr;
56154359Sroberto	int i;
56254359Sroberto	char c;
56354359Sroberto#ifdef ARCRON_DEBUG
56454359Sroberto	if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
56554359Sroberto#endif
56654359Sroberto
56754359Sroberto	c = up->cmdqueue[0];       /* Next char to be sent. */
56854359Sroberto	/* Shift down characters, shifting trailing \0 in at end. */
56954359Sroberto	for(i = 0; i < CMDQUEUELEN; ++i)
57054359Sroberto	{ up->cmdqueue[i] = up->cmdqueue[i+1]; }
57154359Sroberto
57254359Sroberto	/* Don't send '\0' characters. */
57354359Sroberto	if(c != '\0') {
57454359Sroberto		if(write(pp->io.fd, &c, 1) != 1) {
57554359Sroberto			msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
57654359Sroberto		}
57754359Sroberto#ifdef ARCRON_DEBUG
57854359Sroberto		else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
57954359Sroberto#endif
58054359Sroberto	}
58154359Sroberto}
58254359Sroberto#endif /* 0 */
58354359Sroberto
58454359Sroberto/*
58554359Sroberto * arc_start - open the devices and initialize data for processing
58654359Sroberto */
58754359Srobertostatic int
58854359Srobertoarc_start(
58954359Sroberto	int unit,
59054359Sroberto	struct peer *peer
59154359Sroberto	)
59254359Sroberto{
59354359Sroberto	register struct arcunit *up;
59454359Sroberto	struct refclockproc *pp;
59554359Sroberto	int fd;
59654359Sroberto	char device[20];
59754359Sroberto#ifdef HAVE_TERMIOS
59854359Sroberto	struct termios arg;
59954359Sroberto#endif
60054359Sroberto
60154359Sroberto	msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
60254359Sroberto#ifdef ARCRON_DEBUG
60354359Sroberto	if(debug) {
60454359Sroberto		printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
60554359Sroberto	}
60654359Sroberto#endif
60754359Sroberto
60854359Sroberto	/* Prevent a ridiculous device number causing overflow of device[]. */
60954359Sroberto	if((unit < 0) || (unit > 255)) { return(0); }
61054359Sroberto
61154359Sroberto	/*
61254359Sroberto	 * Open serial port. Use CLK line discipline, if available.
61354359Sroberto	 */
61454359Sroberto	(void)sprintf(device, DEVICE, unit);
61554359Sroberto	if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
61654359Sroberto		return(0);
61754359Sroberto#ifdef ARCRON_DEBUG
61854359Sroberto	if(debug) { printf("arc: unit %d using open().\n", unit); }
61954359Sroberto#endif
62054359Sroberto	fd = open(device, OPEN_FLAGS);
62154359Sroberto	if(fd < 0) {
62254359Sroberto#ifdef DEBUG
62354359Sroberto		if(debug) { printf("arc: failed [open()] to open %s.\n", device); }
62454359Sroberto#endif
62554359Sroberto		return(0);
62654359Sroberto	}
62754359Sroberto
62854359Sroberto	fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
62954359Sroberto#ifdef ARCRON_DEBUG
63054359Sroberto	if(debug)
63154359Sroberto	{ printf("Opened RS232 port with file descriptor %d.\n", fd); }
63254359Sroberto#endif
63354359Sroberto
63454359Sroberto#ifdef HAVE_TERMIOS
63554359Sroberto
63654359Sroberto	arg.c_iflag = IGNBRK | ISTRIP;
63754359Sroberto	arg.c_oflag = 0;
63854359Sroberto	arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
63954359Sroberto	arg.c_lflag = 0;
64054359Sroberto	arg.c_cc[VMIN] = 1;
64154359Sroberto	arg.c_cc[VTIME] = 0;
64254359Sroberto
64354359Sroberto	tcsetattr(fd, TCSANOW, &arg);
64454359Sroberto
64554359Sroberto#else
64654359Sroberto
64754359Sroberto	msyslog(LOG_ERR, "ARCRON: termios not supported in this driver");
64854359Sroberto	(void)close(fd);
64954359Sroberto
65054359Sroberto	return 0;
65154359Sroberto
65254359Sroberto#endif
65354359Sroberto
65454359Sroberto	up = (struct arcunit *) emalloc(sizeof(struct arcunit));
65554359Sroberto	if(!up) { (void) close(fd); return(0); }
65654359Sroberto	/* Set structure to all zeros... */
65754359Sroberto	memset((char *)up, 0, sizeof(struct arcunit));
65854359Sroberto	pp = peer->procptr;
65954359Sroberto	pp->io.clock_recv = arc_receive;
66054359Sroberto	pp->io.srcclock = (caddr_t)peer;
66154359Sroberto	pp->io.datalen = 0;
66254359Sroberto	pp->io.fd = fd;
66354359Sroberto	if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); }
66454359Sroberto	pp->unitptr = (caddr_t)up;
66554359Sroberto
66654359Sroberto	/*
66754359Sroberto	 * Initialize miscellaneous variables
66854359Sroberto	 */
66954359Sroberto	peer->precision = PRECISION;
67054359Sroberto	peer->stratum = 2;              /* Default to stratum 2 not 0. */
67154359Sroberto	pp->clockdesc = DESCRIPTION;
67254359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
67354359Sroberto	/* Spread out resyncs so that they should remain separated. */
67454359Sroberto	up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
67554359Sroberto
67654359Sroberto#if 0 /* Not needed because of zeroing of arcunit structure... */
67754359Sroberto	up->resyncing = 0;              /* Not resyncing yet. */
67854359Sroberto	up->saved_flags = 0;            /* Default is all flags off. */
67954359Sroberto	/* Clear send buffer out... */
68054359Sroberto	{
68154359Sroberto		int i;
68254359Sroberto		for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
68354359Sroberto	}
68454359Sroberto#endif
68554359Sroberto
68654359Sroberto#ifdef ARCRON_KEEN
68754359Sroberto	up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
68854359Sroberto#else
68954359Sroberto	up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
69054359Sroberto#endif
69154359Sroberto	return(1);
69254359Sroberto}
69354359Sroberto
69454359Sroberto
69554359Sroberto/*
69654359Sroberto * arc_shutdown - shut down the clock
69754359Sroberto */
69854359Srobertostatic void
69954359Srobertoarc_shutdown(
70054359Sroberto	int unit,
70154359Sroberto	struct peer *peer
70254359Sroberto	)
70354359Sroberto{
70454359Sroberto	register struct arcunit *up;
70554359Sroberto	struct refclockproc *pp;
70654359Sroberto
70754359Sroberto	pp = peer->procptr;
70854359Sroberto	up = (struct arcunit *)pp->unitptr;
70954359Sroberto	io_closeclock(&pp->io);
71054359Sroberto	free(up);
71154359Sroberto}
71254359Sroberto
71354359Sroberto/*
71454359SrobertoCompute space left in output buffer.
71554359Sroberto*/
71654359Srobertostatic int
71754359Srobertospace_left(
71854359Sroberto	register struct arcunit *up
71954359Sroberto	)
72054359Sroberto{
72154359Sroberto	int spaceleft;
72254359Sroberto
72354359Sroberto	/* Compute space left in buffer after any pending output. */
72454359Sroberto	for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
72554359Sroberto	{ if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
72654359Sroberto	return(spaceleft);
72754359Sroberto}
72854359Sroberto
72954359Sroberto/*
73054359SrobertoSend command by copying into command buffer as far forward as possible,
73154359Srobertoafter any pending output.
73254359Sroberto
73354359SrobertoIndicate an error by returning 0 if there is not space for the command.
73454359Sroberto*/
73554359Srobertostatic int
73654359Srobertosend_slow(
73754359Sroberto	register struct arcunit *up,
73854359Sroberto	int fd,
73954359Sroberto	const char *s
74054359Sroberto	)
74154359Sroberto{
74254359Sroberto	int sl = strlen(s);
74354359Sroberto	int spaceleft = space_left(up);
74454359Sroberto
74554359Sroberto#ifdef ARCRON_DEBUG
74654359Sroberto	if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
74754359Sroberto#endif
74854359Sroberto	if(spaceleft < sl) { /* Should not normally happen... */
74954359Sroberto#ifdef ARCRON_DEBUG
75054359Sroberto		msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
75154359Sroberto		       sl, spaceleft);
75254359Sroberto#endif
75354359Sroberto		return(0);                      /* FAILED! */
75454359Sroberto	}
75554359Sroberto
75654359Sroberto	/* Copy in the command to be sent. */
75754359Sroberto	while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
75854359Sroberto
75954359Sroberto	return(1);
76054359Sroberto}
76154359Sroberto
76254359Sroberto
76354359Sroberto/* Macro indicating action we will take for different quality values. */
76454359Sroberto#define quality_action(q) \
76554359Sroberto(((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
76654359Sroberto (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
76754359Sroberto  "OK, will use clock"))
76854359Sroberto
76954359Sroberto     /*
77054359Sroberto * arc_receive - receive data from the serial interface
77154359Sroberto */
77254359Sroberto     static void
77354359Srobertoarc_receive(
77454359Sroberto	struct recvbuf *rbufp
77554359Sroberto	)
77654359Sroberto{
77754359Sroberto	register struct arcunit *up;
77854359Sroberto	struct refclockproc *pp;
77954359Sroberto	struct peer *peer;
78054359Sroberto	char c;
78154359Sroberto	int i, n, wday, month, bst, status;
78254359Sroberto	int arc_last_offset;
78354359Sroberto
78454359Sroberto	/*
78554359Sroberto	 * Initialize pointers and read the timecode and timestamp
78654359Sroberto	 */
78754359Sroberto	peer = (struct peer *)rbufp->recv_srcclock;
78854359Sroberto	pp = peer->procptr;
78954359Sroberto	up = (struct arcunit *)pp->unitptr;
79054359Sroberto
79154359Sroberto
79254359Sroberto	/*
79354359Sroberto	  If the command buffer is empty, and we are resyncing, insert a
79454359Sroberto	  g\r quality request into it to poll for signal quality again.
79554359Sroberto	*/
79654359Sroberto	if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
79754359Sroberto#ifdef DEBUG
79854359Sroberto		if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
79954359Sroberto#endif
80054359Sroberto		send_slow(up, pp->io.fd, "g\r");
80154359Sroberto	}
80254359Sroberto
80354359Sroberto	/*
80454359Sroberto	  The `arc_last_offset' is the offset in lastcode[] of the last byte
80554359Sroberto	  received, and which we assume actually received the input
80654359Sroberto	  timestamp.
80754359Sroberto
80854359Sroberto	  (When we get round to using tty_clk and it is available, we
80954359Sroberto	  assume that we will receive the whole timecode with the
81054359Sroberto	  trailing \r, and that that \r will be timestamped.  But this
81154359Sroberto	  assumption also works if receive the characters one-by-one.)
81254359Sroberto	*/
81354359Sroberto	arc_last_offset = pp->lencode+rbufp->recv_length - 1;
81454359Sroberto
81554359Sroberto	/*
81654359Sroberto	  We catch a timestamp iff:
81754359Sroberto
81854359Sroberto	  * The command code is `o' for a timestamp.
81954359Sroberto
82054359Sroberto	  * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
82154359Sroberto	  exactly char in the buffer (the command code) so that we
82254359Sroberto	  only sample the first character of the timecode as our
82354359Sroberto	  `on-time' character.
82454359Sroberto
82554359Sroberto	  * The first character in the buffer is not the echoed `\r'
82654359Sroberto	  from the `o` command (so if we are to timestamp an `\r' it
82754359Sroberto	  must not be first in the receive buffer with lencode==1.
82854359Sroberto	  (Even if we had other characters following it, we probably
82954359Sroberto	  would have a premature timestamp on the '\r'.)
83054359Sroberto
83154359Sroberto	  * We have received at least one character (I cannot imagine
83254359Sroberto	  how it could be otherwise, but anyway...).
83354359Sroberto	*/
83454359Sroberto	c = rbufp->recv_buffer[0];
83554359Sroberto	if((pp->a_lastcode[0] == 'o') &&
83654359Sroberto#ifndef ARCRON_MULTIPLE_SAMPLES
83754359Sroberto	   (pp->lencode == 1) &&
83854359Sroberto#endif
83954359Sroberto	   ((pp->lencode != 1) || (c != '\r')) &&
84054359Sroberto	   (arc_last_offset >= 1)) {
84154359Sroberto		/* Note that the timestamp should be corrected if >1 char rcvd. */
84254359Sroberto		l_fp timestamp;
84354359Sroberto		timestamp = rbufp->recv_time;
84454359Sroberto#ifdef DEBUG
84554359Sroberto		if(debug) { /* Show \r as `R', other non-printing char as `?'. */
84654359Sroberto			printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
84754359Sroberto			       ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
84854359Sroberto			       rbufp->recv_length);
84954359Sroberto		}
85054359Sroberto#endif
85154359Sroberto
85254359Sroberto		/*
85354359Sroberto		  Now correct timestamp by offset of last byte received---we
85454359Sroberto		  subtract from the receive time the delay implied by the
85554359Sroberto		  extra characters received.
85654359Sroberto
85754359Sroberto		  Reject the input if the resulting code is too long, but
85854359Sroberto		  allow for the trailing \r, normally not used but a good
85954359Sroberto		  handle for tty_clk or somesuch kernel timestamper.
86054359Sroberto		*/
86154359Sroberto		if(arc_last_offset > LENARC) {
86254359Sroberto#ifdef ARCRON_DEBUG
86354359Sroberto			if(debug) {
86454359Sroberto				printf("arc: input code too long (%d cf %d); rejected.\n",
86554359Sroberto				       arc_last_offset, LENARC);
86654359Sroberto			}
86754359Sroberto#endif
86854359Sroberto			pp->lencode = 0;
86954359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
87054359Sroberto			return;
87154359Sroberto		}
87254359Sroberto
87354359Sroberto		L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
87454359Sroberto#ifdef ARCRON_DEBUG
87554359Sroberto		if(debug > 1) {
87654359Sroberto			printf(
87754359Sroberto				"arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
87854359Sroberto				((rbufp->recv_length > 1) ? "*** " : ""),
87954359Sroberto				rbufp->recv_length,
88054359Sroberto				arc_last_offset,
88154359Sroberto				mfptoms((unsigned long)0,
88254359Sroberto					charoffsets[arc_last_offset],
88354359Sroberto					1));
88454359Sroberto		}
88554359Sroberto#endif
88654359Sroberto
88754359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES
88854359Sroberto		/*
88954359Sroberto		  If taking multiple samples, capture the current adjusted
89054359Sroberto		  sample iff:
89154359Sroberto
89254359Sroberto		  * No timestamp has yet been captured (it is zero), OR
89354359Sroberto
89454359Sroberto		  * This adjusted timestamp is earlier than the one already
89554359Sroberto		  captured, on the grounds that this one suffered less
89654359Sroberto		  delay in being delivered to us and is more accurate.
89754359Sroberto
89854359Sroberto		*/
89954359Sroberto		if(L_ISZERO(&(up->lastrec)) ||
90054359Sroberto		   L_ISGEQ(&(up->lastrec), &timestamp))
90154359Sroberto#endif
90254359Sroberto		{
90354359Sroberto#ifdef ARCRON_DEBUG
90454359Sroberto			if(debug > 1) {
90554359Sroberto				printf("arc: system timestamp captured.\n");
90654359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES
90754359Sroberto				if(!L_ISZERO(&(up->lastrec))) {
90854359Sroberto					l_fp diff;
90954359Sroberto					diff = up->lastrec;
91054359Sroberto					L_SUB(&diff, &timestamp);
91154359Sroberto					printf("arc: adjusted timestamp by -%sms.\n",
91254359Sroberto					       mfptoms(diff.l_i, diff.l_f, 3));
91354359Sroberto				}
91454359Sroberto#endif
91554359Sroberto			}
91654359Sroberto#endif
91754359Sroberto			up->lastrec = timestamp;
91854359Sroberto		}
91954359Sroberto
92054359Sroberto	}
92154359Sroberto
92254359Sroberto	/* Just in case we still have lots of rubbish in the buffer... */
92354359Sroberto	/* ...and to avoid the same timestamp being reused by mistake, */
92454359Sroberto	/* eg on receipt of the \r coming in on its own after the      */
92554359Sroberto	/* timecode.                                                   */
92654359Sroberto	if(pp->lencode >= LENARC) {
92754359Sroberto#ifdef ARCRON_DEBUG
92854359Sroberto		if(debug && (rbufp->recv_buffer[0] != '\r'))
92954359Sroberto		{ printf("arc: rubbish in pp->a_lastcode[].\n"); }
93054359Sroberto#endif
93154359Sroberto		pp->lencode = 0;
93254359Sroberto		return;
93354359Sroberto	}
93454359Sroberto
93554359Sroberto	/* Append input to code buffer, avoiding overflow. */
93654359Sroberto	for(i = 0; i < rbufp->recv_length; i++) {
93754359Sroberto		if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
93854359Sroberto		c = rbufp->recv_buffer[i];
93954359Sroberto
94054359Sroberto		/* Drop trailing '\r's and drop `h' command echo totally. */
94154359Sroberto		if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
94254359Sroberto
94354359Sroberto		/*
94454359Sroberto		  If we've just put an `o' in the lastcode[0], clear the
94554359Sroberto		  timestamp in anticipation of a timecode arriving soon.
94654359Sroberto
94754359Sroberto		  We would expect to get to process this before any of the
94854359Sroberto		  timecode arrives.
94954359Sroberto		*/
95054359Sroberto		if((c == 'o') && (pp->lencode == 1)) {
95154359Sroberto			L_CLR(&(up->lastrec));
95254359Sroberto#ifdef ARCRON_DEBUG
95354359Sroberto			if(debug > 1) { printf("arc: clearing timestamp.\n"); }
95454359Sroberto#endif
95554359Sroberto		}
95654359Sroberto	}
95754359Sroberto
95854359Sroberto	/* Handle a quality message. */
95954359Sroberto	if(pp->a_lastcode[0] == 'g') {
96054359Sroberto		int r, q;
96154359Sroberto
96254359Sroberto		if(pp->lencode < 3) { return; } /* Need more data... */
96354359Sroberto		r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
96454359Sroberto		q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
96554359Sroberto		if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
96654359Sroberto		   ((r & 0x70) != 0x30)) {
96754359Sroberto			/* Badly formatted response. */
96854359Sroberto#ifdef ARCRON_DEBUG
96954359Sroberto			if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
97054359Sroberto#endif
97154359Sroberto			return;
97254359Sroberto		}
97354359Sroberto		if(r == '3') { /* Only use quality value whilst sync in progress. */
97454359Sroberto			up->quality = (q & 0xf);
97554359Sroberto#ifdef DEBUG
97654359Sroberto			if(debug) { printf("arc: signal quality %d.\n", up->quality); }
97754359Sroberto#endif
97854359Sroberto		} else if( /* (r == '2') && */ up->resyncing) {
97954359Sroberto#ifdef DEBUG
98054359Sroberto			if(debug)
98154359Sroberto			{
98254359Sroberto				printf("arc: sync finished, signal quality %d: %s\n",
98354359Sroberto				       up->quality,
98454359Sroberto				       quality_action(up->quality));
98554359Sroberto			}
98654359Sroberto#endif
98754359Sroberto			msyslog(LOG_NOTICE,
98854359Sroberto			       "ARCRON: sync finished, signal quality %d: %s",
98954359Sroberto			       up->quality,
99054359Sroberto			       quality_action(up->quality));
99154359Sroberto			up->resyncing = 0; /* Resync is over. */
99254359Sroberto
99354359Sroberto#ifdef ARCRON_KEEN
99454359Sroberto			/* Clock quality dubious; resync earlier than usual. */
99554359Sroberto			if((up->quality == QUALITY_UNKNOWN) ||
99654359Sroberto			   (up->quality < MIN_CLOCK_QUALITY_OK))
99754359Sroberto			{ up->next_resync = current_time + RETRY_RESYNC_TIME; }
99854359Sroberto#endif
99954359Sroberto		}
100054359Sroberto		pp->lencode = 0;
100154359Sroberto		return;
100254359Sroberto	}
100354359Sroberto
100454359Sroberto	/* Stop now if this is not a timecode message. */
100554359Sroberto	if(pp->a_lastcode[0] != 'o') {
100654359Sroberto		pp->lencode = 0;
100754359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
100854359Sroberto		return;
100954359Sroberto	}
101054359Sroberto
101154359Sroberto	/* If we don't have enough data, wait for more... */
101254359Sroberto	if(pp->lencode < LENARC) { return; }
101354359Sroberto
101454359Sroberto
101554359Sroberto	/* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
101654359Sroberto#ifdef ARCRON_DEBUG
101754359Sroberto	if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
101854359Sroberto#endif
101954359Sroberto
102054359Sroberto	/* But check that we actually captured a system timestamp on it. */
102154359Sroberto	if(L_ISZERO(&(up->lastrec))) {
102254359Sroberto#ifdef ARCRON_DEBUG
102354359Sroberto		if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
102454359Sroberto#endif
102554359Sroberto		pp->lencode = 0;
102654359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
102754359Sroberto		return;
102854359Sroberto	}
102954359Sroberto	/*
103054359Sroberto	  Append a mark of the clock's received signal quality for the
103154359Sroberto	  benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
103254359Sroberto	  quality value to `6' for his s/w) and terminate the string for
103354359Sroberto	  sure.  This should not go off the buffer end.
103454359Sroberto	*/
103554359Sroberto	pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
103654359Sroberto				       '6' : ('0' + up->quality));
103754359Sroberto	pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
103854359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
103954359Sroberto
104054359Sroberto	/* We don't use the micro-/milli- second part... */
104154359Sroberto	pp->usec = 0;
104254359Sroberto	pp->msec = 0;
104354359Sroberto
104454359Sroberto	n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d",
104554359Sroberto		   &pp->hour, &pp->minute, &pp->second,
104654359Sroberto		   &wday, &pp->day, &month, &pp->year, &bst, &status);
104754359Sroberto
104854359Sroberto	/* Validate format and numbers. */
104954359Sroberto	if(n != 9) {
105054359Sroberto#ifdef ARCRON_DEBUG
105154359Sroberto		/* Would expect to have caught major problems already... */
105254359Sroberto		if(debug) { printf("arc: badly formatted data.\n"); }
105354359Sroberto#endif
105454359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
105554359Sroberto		return;
105654359Sroberto	}
105754359Sroberto	/*
105854359Sroberto	  Validate received values at least enough to prevent internal
105954359Sroberto	  array-bounds problems, etc.
106054359Sroberto	*/
106154359Sroberto	if((pp->hour < 0) || (pp->hour > 23) ||
106254359Sroberto	   (pp->minute < 0) || (pp->minute > 59) ||
106354359Sroberto	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
106454359Sroberto	   (wday < 1) || (wday > 7) ||
106554359Sroberto	   (pp->day < 1) || (pp->day > 31) ||
106654359Sroberto	   (month < 1) || (month > 12) ||
106754359Sroberto	   (pp->year < 0) || (pp->year > 99)) {
106854359Sroberto		/* Data out of range. */
106954359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
107054359Sroberto		return;
107154359Sroberto	}
107254359Sroberto	/* Check that BST/UTC bits are the complement of one another. */
107354359Sroberto	if(!(bst & 2) == !(bst & 4)) {
107454359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
107554359Sroberto		return;
107654359Sroberto	}
107754359Sroberto
107854359Sroberto	if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
107954359Sroberto
108054359Sroberto	/* Year-2000 alert! */
108154359Sroberto	/* Attempt to wrap 2-digit date into sensible window. */
108254359Sroberto	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* Y2KFixes */
108354359Sroberto	pp->year += 1900;	/* use full four-digit year */	/* Y2KFixes */
108454359Sroberto	/*
108554359Sroberto	  Attempt to do the right thing by screaming that the code will
108654359Sroberto	  soon break when we get to the end of its useful life.  What a
108754359Sroberto	  hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
108854359Sroberto	*/
108954359Sroberto	if(pp->year >= YEAR_PIVOT+2000-2 ) {  			/* Y2KFixes */
109054359Sroberto		/*This should get attention B^> */
109154359Sroberto		msyslog(LOG_NOTICE,
109254359Sroberto		       "ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
109354359Sroberto	}
109454359Sroberto#ifdef DEBUG
109554359Sroberto	if(debug) {
109654359Sroberto		printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
109754359Sroberto		       n,
109854359Sroberto		       pp->hour, pp->minute, pp->second,
109954359Sroberto		       pp->day, month, pp->year, bst, status);
110054359Sroberto	}
110154359Sroberto#endif
110254359Sroberto
110354359Sroberto	/*
110454359Sroberto	  The status value tested for is not strictly supported by the
110554359Sroberto	  clock spec since the value of bit 2 (0x4) is claimed to be
110654359Sroberto	  undefined for MSF, yet does seem to indicate if the last resync
110754359Sroberto	  was successful or not.
110854359Sroberto	*/
110954359Sroberto	pp->leap = LEAP_NOWARNING;
111054359Sroberto	status &= 0x7;
111154359Sroberto	if(status == 0x3) {
111254359Sroberto		if(status != up->status)
111354359Sroberto		{ msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
111454359Sroberto	} else {
111554359Sroberto		if(status != up->status) {
111654359Sroberto			msyslog(LOG_NOTICE, "ARCRON: signal lost");
111754359Sroberto			pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
111854359Sroberto			up->status = status;
111954359Sroberto			refclock_report(peer, CEVNT_FAULT);
112054359Sroberto			return;
112154359Sroberto		}
112254359Sroberto	}
112354359Sroberto	up->status = status;
112454359Sroberto
112554359Sroberto	pp->day += moff[month - 1];
112654359Sroberto
112754359Sroberto	if(isleap_4(pp->year) && month > 2) { pp->day++; }	/* Y2KFixes */
112854359Sroberto
112954359Sroberto	/* Convert to UTC if required */
113054359Sroberto	if(bst & 2) {
113154359Sroberto		pp->hour--;
113254359Sroberto		if (pp->hour < 0) {
113354359Sroberto			pp->hour = 23;
113454359Sroberto			pp->day--;
113554359Sroberto			/* If we try to wrap round the year (BST on 1st Jan), reject.*/
113654359Sroberto			if(pp->day < 0) {
113754359Sroberto				refclock_report(peer, CEVNT_BADTIME);
113854359Sroberto				return;
113954359Sroberto			}
114054359Sroberto		}
114154359Sroberto	}
114254359Sroberto
114354359Sroberto	/* If clock signal quality is unknown, revert to default PRECISION...*/
114454359Sroberto	if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; }
114554359Sroberto	/* ...else improve precision if flag3 is set... */
114654359Sroberto	else {
114754359Sroberto		peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
114854359Sroberto				   HIGHPRECISION : PRECISION);
114954359Sroberto	}
115054359Sroberto
115154359Sroberto	/* Notice and log any change (eg from initial defaults) for flags. */
115254359Sroberto	if(up->saved_flags != pp->sloppyclockflag) {
115354359Sroberto#ifdef ARCRON_DEBUG
115454359Sroberto		msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
115554359Sroberto		       ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
115654359Sroberto		       ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
115754359Sroberto		       ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
115854359Sroberto		       ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
115954359Sroberto		/* Note effects of flags changing... */
116054359Sroberto		if(debug) {
116154359Sroberto			printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp));
116254359Sroberto			printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp));
116354359Sroberto			printf("arc: PRECISION = %d.\n", peer->precision);
116454359Sroberto		}
116554359Sroberto#endif
116654359Sroberto		up->saved_flags = pp->sloppyclockflag;
116754359Sroberto	}
116854359Sroberto
116954359Sroberto	/* Note time of last believable timestamp. */
117054359Sroberto	pp->lastrec = up->lastrec;
117154359Sroberto
117254359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
117354359Sroberto	/* Find out if a leap-second might just have happened...
117454359Sroberto	   (ie is this the first hour of the first day of Jan or Jul?)
117554359Sroberto	*/
117654359Sroberto	if((pp->hour == 0) &&
117754359Sroberto	   (pp->day == 1) &&
117854359Sroberto	   ((month == 1) || (month == 7))) {
117954359Sroberto		if(possible_leap >= 0) {
118054359Sroberto			/* A leap may have happened, and no resync has started yet...*/
118154359Sroberto			possible_leap = 1;
118254359Sroberto		}
118354359Sroberto	} else {
118454359Sroberto		/* Definitely not leap-second territory... */
118554359Sroberto		possible_leap = 0;
118654359Sroberto	}
118754359Sroberto#endif
118854359Sroberto
118954359Sroberto	if (!refclock_process(pp)) {
119054359Sroberto		refclock_report(peer, CEVNT_BADTIME);
119154359Sroberto		return;
119254359Sroberto	}
119354359Sroberto	refclock_receive(peer);
119454359Sroberto}
119554359Sroberto
119654359Sroberto
119754359Sroberto/* request_time() sends a time request to the clock with given peer. */
119854359Sroberto/* This automatically reports a fault if necessary. */
119954359Sroberto/* No data should be sent after this until arc_poll() returns. */
120054359Srobertostatic  void    request_time    P((int, struct peer *));
120154359Srobertostatic void
120254359Srobertorequest_time(
120354359Sroberto	int unit,
120454359Sroberto	struct peer *peer
120554359Sroberto	)
120654359Sroberto{
120754359Sroberto	struct refclockproc *pp = peer->procptr;
120854359Sroberto	register struct arcunit *up = (struct arcunit *)pp->unitptr;
120954359Sroberto#ifdef DEBUG
121054359Sroberto	if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
121154359Sroberto#endif
121254359Sroberto	if (!send_slow(up, pp->io.fd, "o\r")) {
121354359Sroberto#ifdef ARCRON_DEBUG
121454359Sroberto		msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit);
121554359Sroberto#endif
121654359Sroberto		refclock_report(peer, CEVNT_FAULT);
121754359Sroberto		return;
121854359Sroberto	}
121954359Sroberto	pp->polls++;
122054359Sroberto}
122154359Sroberto
122254359Sroberto/*
122354359Sroberto * arc_poll - called by the transmit procedure
122454359Sroberto */
122554359Srobertostatic void
122654359Srobertoarc_poll(
122754359Sroberto	int unit,
122854359Sroberto	struct peer *peer
122954359Sroberto	)
123054359Sroberto{
123154359Sroberto	register struct arcunit *up;
123254359Sroberto	struct refclockproc *pp;
123354359Sroberto	int resync_needed;              /* Should we start a resync? */
123454359Sroberto
123554359Sroberto	pp = peer->procptr;
123654359Sroberto	up = (struct arcunit *)pp->unitptr;
123754359Sroberto	pp->lencode = 0;
123854359Sroberto	memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
123954359Sroberto
124054359Sroberto#if 0
124154359Sroberto	/* Flush input. */
124254359Sroberto	tcflush(pp->io.fd, TCIFLUSH);
124354359Sroberto#endif
124454359Sroberto
124554359Sroberto	/* Resync if our next scheduled resync time is here or has passed. */
124654359Sroberto	resync_needed = (up->next_resync <= current_time);
124754359Sroberto
124854359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN
124954359Sroberto	/*
125054359Sroberto	  Try to catch a potential leap-second insertion or deletion quickly.
125154359Sroberto
125254359Sroberto	  In addition to the normal NTP fun of clocks that don't report
125354359Sroberto	  leap-seconds spooking their hosts, this clock does not even
125454359Sroberto	  sample the radio sugnal the whole time, so may miss a
125554359Sroberto	  leap-second insertion or deletion for up to a whole sample
125654359Sroberto	  time.
125754359Sroberto
125854359Sroberto	  To try to minimise this effect, if in the first few minutes of
125954359Sroberto	  the day immediately following a leap-second-insertion point
126054359Sroberto	  (ie in the first hour of the first day of the first and sixth
126154359Sroberto	  months), and if the last resync was in the previous day, and a
126254359Sroberto	  resync is not already in progress, resync the clock
126354359Sroberto	  immediately.
126454359Sroberto
126554359Sroberto	*/
126654359Sroberto	if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
126754359Sroberto	   (!up->resyncing)) {          /* No resync in progress yet. */
126854359Sroberto		resync_needed = 1;
126954359Sroberto		possible_leap = -1;          /* Prevent multiple resyncs. */
127054359Sroberto		msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
127154359Sroberto	}
127254359Sroberto#endif
127354359Sroberto
127454359Sroberto	/* Do a resync if required... */
127554359Sroberto	if(resync_needed) {
127654359Sroberto		/* First, reset quality value to `unknown' so we can detect */
127754359Sroberto		/* when a quality message has been responded to by this     */
127854359Sroberto		/* being set to some other value.                           */
127954359Sroberto		up->quality = QUALITY_UNKNOWN;
128054359Sroberto
128154359Sroberto		/* Note that we are resyncing... */
128254359Sroberto		up->resyncing = 1;
128354359Sroberto
128454359Sroberto		/* Now actually send the resync command and an immediate poll. */
128554359Sroberto#ifdef DEBUG
128654359Sroberto		if(debug) { printf("arc: sending resync command (h\\r).\n"); }
128754359Sroberto#endif
128854359Sroberto		msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
128954359Sroberto		send_slow(up, pp->io.fd, "h\r");
129054359Sroberto
129154359Sroberto		/* Schedule our next resync... */
129254359Sroberto		up->next_resync = current_time + DEFAULT_RESYNC_TIME;
129354359Sroberto
129454359Sroberto		/* Drop through to request time if appropriate. */
129554359Sroberto	}
129654359Sroberto
129754359Sroberto	/* If clock quality is too poor to trust, indicate a fault. */
129854359Sroberto	/* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
129954359Sroberto	/* we'll cross our fingers and just hope that the thing     */
130054359Sroberto	/* synced so quickly we did not catch it---we'll            */
130154359Sroberto	/* double-check the clock is OK elsewhere.                  */
130254359Sroberto	if(
130354359Sroberto#ifdef ARCRON_KEEN
130454359Sroberto		(up->quality != QUALITY_UNKNOWN) &&
130554359Sroberto#else
130654359Sroberto		(up->quality == QUALITY_UNKNOWN) ||
130754359Sroberto#endif
130854359Sroberto		(up->quality < MIN_CLOCK_QUALITY_OK)) {
130954359Sroberto#ifdef DEBUG
131054359Sroberto		if(debug) {
131154359Sroberto			printf("arc: clock quality %d too poor.\n", up->quality);
131254359Sroberto		}
131354359Sroberto#endif
131454359Sroberto		refclock_report(peer, CEVNT_FAULT);
131554359Sroberto		return;
131654359Sroberto	}
131754359Sroberto	/* This is the normal case: request a timestamp. */
131854359Sroberto	request_time(unit, peer);
131954359Sroberto}
132054359Sroberto
132154359Sroberto#else
132254359Srobertoint refclock_arc_bs;
132354359Sroberto#endif
1324