PageRenderTime 94ms CodeModel.GetById 17ms app.highlight 64ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/ntp/ntpd/refclock_arc.c

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