Annotation of embedaddon/ntp/ntpd/refclock_arc.c, revision 1.1
1.1 ! misho 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:
! 11: static 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: /*
! 32: Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
! 33: Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
! 34: Modifications by Paul Alfille, <palfille@partners.org>, 2003.
! 35: Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
! 36: Modifications by Nigel Roles <nigel@9fs.org>, 2003.
! 37:
! 38:
! 39: THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT
! 40: YOUR OWN RISK.
! 41:
! 42: Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
! 43:
! 44: Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
! 45:
! 46: This code may be freely copied and used and incorporated in other
! 47: systems providing the disclaimer and notice of authorship are
! 48: reproduced.
! 49:
! 50: -------------------------------------------------------------------------------
! 51:
! 52: Nigel's notes:
! 53:
! 54: 1) Called tcgetattr() before modifying, so that fields correctly initialised
! 55: for all operating systems
! 56:
! 57: 2) 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:
! 62: Christopher's notes:
! 63:
! 64: MAJOR 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:
! 89: Author's original note:
! 90:
! 91: I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
! 92:
! 93: It works (after a fashion) on both Solaris-1 and Solaris-2.
! 94:
! 95: I am currently using ntp3-5.85. I have been running the code for
! 96: about 7 months without any problems. Even coped with the change to BST!
! 97:
! 98: I had to do some funky things to read from the clock because it uses the
! 99: power from the receive lines to drive the transmit lines. This makes the
! 100: code look a bit stupid but it works. I also had to put in some delays to
! 101: allow for the turnaround time from receive to transmit. These delays
! 102: are between characters when requesting a time stamp so that shouldn't affect
! 103: the results too drastically.
! 104:
! 105: ...
! 106:
! 107: The bottom line is that it works but could easily be improved. You are
! 108: free to do what you will with the code. I haven't been able to determine
! 109: how good the clock is. I think that this requires a known good clock
! 110: to compare it against.
! 111:
! 112: -------------------------------------------------------------------------------
! 113:
! 114: Damon's notes for adjustments:
! 115:
! 116: MAJOR 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:
! 131: GENERAL
! 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:
! 252: TO-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:
! 295: You 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:
! 344: Also note g<cr> command which confirms that a resync is in progress, and
! 345: if so what signal quality (0--5) is available.
! 346: Also 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: */
! 487: struct 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: */
! 526: static int possible_leap = 0; /* No resync required by default. */
! 527: #endif
! 528:
! 529: #if 0
! 530: static void dummy_event_handler (struct peer *);
! 531: static void arc_event_handler (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: */
! 542: static int arc_start (int, struct peer *);
! 543: static void arc_shutdown (int, struct peer *);
! 544: static void arc_receive (struct recvbuf *);
! 545: static void arc_poll (int, struct peer *);
! 546:
! 547: /*
! 548: * Transfer vector
! 549: */
! 550: struct 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. */
! 567: static void
! 568: dummy_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: /*
! 578: Normal event handler.
! 579:
! 580: Take first character off queue and send to clock if not a null.
! 581:
! 582: Shift characters down and put a null on the end.
! 583:
! 584: We assume that there is no parallelism so no race condition, but even
! 585: if there is nothing bad will happen except that we might send some bad
! 586: data to the clock once in a while.
! 587: */
! 588: static void
! 589: arc_event_handler(
! 590: struct peer *peer
! 591: )
! 592: {
! 593: struct refclockproc *pp = peer->procptr;
! 594: register struct arcunit *up = 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: */
! 622: static int
! 623: arc_start(
! 624: int unit,
! 625: struct peer *peer
! 626: )
! 627: {
! 628: register struct arcunit *up;
! 629: struct refclockproc *pp;
! 630: int temp_fd;
! 631: int fd;
! 632: char device[20];
! 633: #ifdef HAVE_TERMIOS
! 634: struct termios arg;
! 635: #endif
! 636:
! 637: msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
! 638: arc_version, unit);
! 639: DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
! 640: unit));
! 641:
! 642: /*
! 643: * Open serial port. Use CLK line discipline, if available.
! 644: */
! 645: snprintf(device, sizeof(device), DEVICE, unit);
! 646: temp_fd = refclock_open(device, SPEED, LDISC_CLK);
! 647: if (temp_fd <= 0)
! 648: return 0;
! 649: DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
! 650: fd = tty_open(device, OPEN_FLAGS, 0777);
! 651: if (fd < 0) {
! 652: msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.\n",
! 653: unit, device);
! 654: close(temp_fd);
! 655: return 0;
! 656: }
! 657: close(temp_fd);
! 658: temp_fd = -1;
! 659:
! 660: #ifndef SYS_WINNT
! 661: fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
! 662: #endif
! 663: DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
! 664:
! 665: #ifdef HAVE_TERMIOS
! 666:
! 667: if (tcgetattr(fd, &arg) < 0) {
! 668: msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.\n",
! 669: unit, device);
! 670: close(fd);
! 671: return 0;
! 672: }
! 673:
! 674: arg.c_iflag = IGNBRK | ISTRIP;
! 675: arg.c_oflag = 0;
! 676: arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
! 677: arg.c_lflag = 0;
! 678: arg.c_cc[VMIN] = 1;
! 679: arg.c_cc[VTIME] = 0;
! 680:
! 681: if (tcsetattr(fd, TCSANOW, &arg) < 0) {
! 682: msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.\n",
! 683: unit, device);
! 684: close(fd);
! 685: return 0;
! 686: }
! 687:
! 688: #else
! 689:
! 690: msyslog(LOG_ERR, "ARCRON: termios required by this driver");
! 691: (void)close(fd);
! 692:
! 693: return 0;
! 694:
! 695: #endif
! 696:
! 697: /* Set structure to all zeros... */
! 698: up = emalloc_zero(sizeof(*up));
! 699: pp = peer->procptr;
! 700: pp->io.clock_recv = arc_receive;
! 701: pp->io.srcclock = (caddr_t)peer;
! 702: pp->io.datalen = 0;
! 703: pp->io.fd = fd;
! 704: if (!io_addclock(&pp->io)) {
! 705: close(fd);
! 706: pp->io.fd = -1;
! 707: free(up);
! 708: return(0);
! 709: }
! 710: pp->unitptr = up;
! 711:
! 712: /*
! 713: * Initialize miscellaneous variables
! 714: */
! 715: peer->precision = PRECISION;
! 716: peer->stratum = 2; /* Default to stratum 2 not 0. */
! 717: pp->clockdesc = DESCRIPTION;
! 718: if (peer->MODE > 3) {
! 719: msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
! 720: return 0;
! 721: }
! 722: #ifdef DEBUG
! 723: if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
! 724: #endif
! 725: switch (peer->MODE) {
! 726: case 1:
! 727: memcpy((char *)&pp->refid, REFID_MSF, 4);
! 728: break;
! 729: case 2:
! 730: memcpy((char *)&pp->refid, REFID_DCF77, 4);
! 731: break;
! 732: case 3:
! 733: memcpy((char *)&pp->refid, REFID_WWVB, 4);
! 734: break;
! 735: default:
! 736: memcpy((char *)&pp->refid, REFID, 4);
! 737: break;
! 738: }
! 739: /* Spread out resyncs so that they should remain separated. */
! 740: up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
! 741:
! 742: #if 0 /* Not needed because of zeroing of arcunit structure... */
! 743: up->resyncing = 0; /* Not resyncing yet. */
! 744: up->saved_flags = 0; /* Default is all flags off. */
! 745: /* Clear send buffer out... */
! 746: {
! 747: int i;
! 748: for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
! 749: }
! 750: #endif
! 751:
! 752: #ifdef ARCRON_KEEN
! 753: up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */
! 754: #else
! 755: up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
! 756: #endif
! 757:
! 758: peer->action = arc_event_handler;
! 759:
! 760: ENQUEUE(up);
! 761:
! 762: return(1);
! 763: }
! 764:
! 765:
! 766: /*
! 767: * arc_shutdown - shut down the clock
! 768: */
! 769: static void
! 770: arc_shutdown(
! 771: int unit,
! 772: struct peer *peer
! 773: )
! 774: {
! 775: register struct arcunit *up;
! 776: struct refclockproc *pp;
! 777:
! 778: peer->action = dummy_event_handler;
! 779:
! 780: pp = peer->procptr;
! 781: up = pp->unitptr;
! 782: if (-1 != pp->io.fd)
! 783: io_closeclock(&pp->io);
! 784: if (NULL != up)
! 785: free(up);
! 786: }
! 787:
! 788: /*
! 789: Compute space left in output buffer.
! 790: */
! 791: static int
! 792: space_left(
! 793: register struct arcunit *up
! 794: )
! 795: {
! 796: int spaceleft;
! 797:
! 798: /* Compute space left in buffer after any pending output. */
! 799: for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
! 800: { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
! 801: return(spaceleft);
! 802: }
! 803:
! 804: /*
! 805: Send command by copying into command buffer as far forward as possible,
! 806: after any pending output.
! 807:
! 808: Indicate an error by returning 0 if there is not space for the command.
! 809: */
! 810: static int
! 811: send_slow(
! 812: register struct arcunit *up,
! 813: int fd,
! 814: const char *s
! 815: )
! 816: {
! 817: int sl = strlen(s);
! 818: int spaceleft = space_left(up);
! 819:
! 820: #ifdef DEBUG
! 821: if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
! 822: #endif
! 823: if(spaceleft < sl) { /* Should not normally happen... */
! 824: #ifdef DEBUG
! 825: msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
! 826: sl, spaceleft);
! 827: #endif
! 828: return(0); /* FAILED! */
! 829: }
! 830:
! 831: /* Copy in the command to be sent. */
! 832: while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
! 833:
! 834: return(1);
! 835: }
! 836:
! 837:
! 838: static int
! 839: get2(char *p, int *val)
! 840: {
! 841: if (!isdigit((int)p[0]) || !isdigit((int)p[1])) return 0;
! 842: *val = (p[0] - '0') * 10 + p[1] - '0';
! 843: return 1;
! 844: }
! 845:
! 846: static int
! 847: get1(char *p, int *val)
! 848: {
! 849: if (!isdigit((int)p[0])) return 0;
! 850: *val = p[0] - '0';
! 851: return 1;
! 852: }
! 853:
! 854: /* Macro indicating action we will take for different quality values. */
! 855: #define quality_action(q) \
! 856: (((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \
! 857: (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
! 858: "OK, will use clock"))
! 859:
! 860: /*
! 861: * arc_receive - receive data from the serial interface
! 862: */
! 863: static void
! 864: arc_receive(
! 865: struct recvbuf *rbufp
! 866: )
! 867: {
! 868: register struct arcunit *up;
! 869: struct refclockproc *pp;
! 870: struct peer *peer;
! 871: char c;
! 872: int i, n, wday, month, flags, status;
! 873: int arc_last_offset;
! 874: static int quality_average = 0;
! 875: static int quality_sum = 0;
! 876: static int quality_polls = 0;
! 877:
! 878: /*
! 879: * Initialize pointers and read the timecode and timestamp
! 880: */
! 881: peer = (struct peer *)rbufp->recv_srcclock;
! 882: pp = peer->procptr;
! 883: up = pp->unitptr;
! 884:
! 885:
! 886: /*
! 887: If the command buffer is empty, and we are resyncing, insert a
! 888: g\r quality request into it to poll for signal quality again.
! 889: */
! 890: if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
! 891: #ifdef DEBUG
! 892: if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
! 893: #endif
! 894: send_slow(up, pp->io.fd, "g\r");
! 895: }
! 896:
! 897: /*
! 898: The `arc_last_offset' is the offset in lastcode[] of the last byte
! 899: received, and which we assume actually received the input
! 900: timestamp.
! 901:
! 902: (When we get round to using tty_clk and it is available, we
! 903: assume that we will receive the whole timecode with the
! 904: trailing \r, and that that \r will be timestamped. But this
! 905: assumption also works if receive the characters one-by-one.)
! 906: */
! 907: arc_last_offset = pp->lencode+rbufp->recv_length - 1;
! 908:
! 909: /*
! 910: We catch a timestamp iff:
! 911:
! 912: * The command code is `o' for a timestamp.
! 913:
! 914: * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
! 915: exactly char in the buffer (the command code) so that we
! 916: only sample the first character of the timecode as our
! 917: `on-time' character.
! 918:
! 919: * The first character in the buffer is not the echoed `\r'
! 920: from the `o` command (so if we are to timestamp an `\r' it
! 921: must not be first in the receive buffer with lencode==1.
! 922: (Even if we had other characters following it, we probably
! 923: would have a premature timestamp on the '\r'.)
! 924:
! 925: * We have received at least one character (I cannot imagine
! 926: how it could be otherwise, but anyway...).
! 927: */
! 928: c = rbufp->recv_buffer[0];
! 929: if((pp->a_lastcode[0] == 'o') &&
! 930: #ifndef ARCRON_MULTIPLE_SAMPLES
! 931: (pp->lencode == 1) &&
! 932: #endif
! 933: ((pp->lencode != 1) || (c != '\r')) &&
! 934: (arc_last_offset >= 1)) {
! 935: /* Note that the timestamp should be corrected if >1 char rcvd. */
! 936: l_fp timestamp;
! 937: timestamp = rbufp->recv_time;
! 938: #ifdef DEBUG
! 939: if(debug) { /* Show \r as `R', other non-printing char as `?'. */
! 940: printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
! 941: ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
! 942: rbufp->recv_length);
! 943: }
! 944: #endif
! 945:
! 946: /*
! 947: Now correct timestamp by offset of last byte received---we
! 948: subtract from the receive time the delay implied by the
! 949: extra characters received.
! 950:
! 951: Reject the input if the resulting code is too long, but
! 952: allow for the trailing \r, normally not used but a good
! 953: handle for tty_clk or somesuch kernel timestamper.
! 954: */
! 955: if(arc_last_offset > LENARC) {
! 956: #ifdef DEBUG
! 957: if(debug) {
! 958: printf("arc: input code too long (%d cf %d); rejected.\n",
! 959: arc_last_offset, LENARC);
! 960: }
! 961: #endif
! 962: pp->lencode = 0;
! 963: refclock_report(peer, CEVNT_BADREPLY);
! 964: return;
! 965: }
! 966:
! 967: L_SUBUF(×tamp, charoffsets[arc_last_offset]);
! 968: #ifdef DEBUG
! 969: if(debug > 1) {
! 970: printf(
! 971: "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
! 972: ((rbufp->recv_length > 1) ? "*** " : ""),
! 973: rbufp->recv_length,
! 974: arc_last_offset,
! 975: mfptoms((unsigned long)0,
! 976: charoffsets[arc_last_offset],
! 977: 1));
! 978: }
! 979: #endif
! 980:
! 981: #ifdef ARCRON_MULTIPLE_SAMPLES
! 982: /*
! 983: If taking multiple samples, capture the current adjusted
! 984: sample iff:
! 985:
! 986: * No timestamp has yet been captured (it is zero), OR
! 987:
! 988: * This adjusted timestamp is earlier than the one already
! 989: captured, on the grounds that this one suffered less
! 990: delay in being delivered to us and is more accurate.
! 991:
! 992: */
! 993: if(L_ISZERO(&(up->lastrec)) ||
! 994: L_ISGEQ(&(up->lastrec), ×tamp))
! 995: #endif
! 996: {
! 997: #ifdef DEBUG
! 998: if(debug > 1) {
! 999: printf("arc: system timestamp captured.\n");
! 1000: #ifdef ARCRON_MULTIPLE_SAMPLES
! 1001: if(!L_ISZERO(&(up->lastrec))) {
! 1002: l_fp diff;
! 1003: diff = up->lastrec;
! 1004: L_SUB(&diff, ×tamp);
! 1005: printf("arc: adjusted timestamp by -%sms.\n",
! 1006: mfptoms(diff.l_i, diff.l_f, 3));
! 1007: }
! 1008: #endif
! 1009: }
! 1010: #endif
! 1011: up->lastrec = timestamp;
! 1012: }
! 1013:
! 1014: }
! 1015:
! 1016: /* Just in case we still have lots of rubbish in the buffer... */
! 1017: /* ...and to avoid the same timestamp being reused by mistake, */
! 1018: /* eg on receipt of the \r coming in on its own after the */
! 1019: /* timecode. */
! 1020: if(pp->lencode >= LENARC) {
! 1021: #ifdef DEBUG
! 1022: if(debug && (rbufp->recv_buffer[0] != '\r'))
! 1023: { printf("arc: rubbish in pp->a_lastcode[].\n"); }
! 1024: #endif
! 1025: pp->lencode = 0;
! 1026: return;
! 1027: }
! 1028:
! 1029: /* Append input to code buffer, avoiding overflow. */
! 1030: for(i = 0; i < rbufp->recv_length; i++) {
! 1031: if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
! 1032: c = rbufp->recv_buffer[i];
! 1033:
! 1034: /* Drop trailing '\r's and drop `h' command echo totally. */
! 1035: if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
! 1036:
! 1037: /*
! 1038: If we've just put an `o' in the lastcode[0], clear the
! 1039: timestamp in anticipation of a timecode arriving soon.
! 1040:
! 1041: We would expect to get to process this before any of the
! 1042: timecode arrives.
! 1043: */
! 1044: if((c == 'o') && (pp->lencode == 1)) {
! 1045: L_CLR(&(up->lastrec));
! 1046: #ifdef DEBUG
! 1047: if(debug > 1) { printf("arc: clearing timestamp.\n"); }
! 1048: #endif
! 1049: }
! 1050: }
! 1051: if (pp->lencode == 0) return;
! 1052:
! 1053: /* Handle a quality message. */
! 1054: if(pp->a_lastcode[0] == 'g') {
! 1055: int r, q;
! 1056:
! 1057: if(pp->lencode < 3) { return; } /* Need more data... */
! 1058: r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
! 1059: q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
! 1060: if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
! 1061: ((r & 0x70) != 0x30)) {
! 1062: /* Badly formatted response. */
! 1063: #ifdef DEBUG
! 1064: if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
! 1065: #endif
! 1066: return;
! 1067: }
! 1068: if(r == '3') { /* Only use quality value whilst sync in progress. */
! 1069: if (up->quality_stamp < current_time) {
! 1070: struct calendar cal;
! 1071: l_fp new_stamp;
! 1072:
! 1073: get_systime (&new_stamp);
! 1074: caljulian (new_stamp.l_ui, &cal);
! 1075: up->quality_stamp =
! 1076: current_time + 60 - cal.second + 5;
! 1077: quality_sum = 0;
! 1078: quality_polls = 0;
! 1079: }
! 1080: quality_sum += (q & 0xf);
! 1081: quality_polls++;
! 1082: quality_average = (quality_sum / quality_polls);
! 1083: #ifdef DEBUG
! 1084: if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
! 1085: #endif
! 1086: } else if( /* (r == '2') && */ up->resyncing) {
! 1087: up->quality = quality_average;
! 1088: #ifdef DEBUG
! 1089: if(debug)
! 1090: {
! 1091: printf("arc: sync finished, signal quality %d: %s\n",
! 1092: up->quality,
! 1093: quality_action(up->quality));
! 1094: }
! 1095: #endif
! 1096: msyslog(LOG_NOTICE,
! 1097: "ARCRON: sync finished, signal quality %d: %s",
! 1098: up->quality,
! 1099: quality_action(up->quality));
! 1100: up->resyncing = 0; /* Resync is over. */
! 1101: quality_average = 0;
! 1102: quality_sum = 0;
! 1103: quality_polls = 0;
! 1104:
! 1105: #ifdef ARCRON_KEEN
! 1106: /* Clock quality dubious; resync earlier than usual. */
! 1107: if((up->quality == QUALITY_UNKNOWN) ||
! 1108: (up->quality < MIN_CLOCK_QUALITY_OK))
! 1109: { up->next_resync = current_time + RETRY_RESYNC_TIME; }
! 1110: #endif
! 1111: }
! 1112: pp->lencode = 0;
! 1113: return;
! 1114: }
! 1115:
! 1116: /* Stop now if this is not a timecode message. */
! 1117: if(pp->a_lastcode[0] != 'o') {
! 1118: pp->lencode = 0;
! 1119: refclock_report(peer, CEVNT_BADREPLY);
! 1120: return;
! 1121: }
! 1122:
! 1123: /* If we don't have enough data, wait for more... */
! 1124: if(pp->lencode < LENARC) { return; }
! 1125:
! 1126:
! 1127: /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
! 1128: #ifdef DEBUG
! 1129: if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
! 1130: #endif
! 1131:
! 1132: /* But check that we actually captured a system timestamp on it. */
! 1133: if(L_ISZERO(&(up->lastrec))) {
! 1134: #ifdef DEBUG
! 1135: if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
! 1136: #endif
! 1137: pp->lencode = 0;
! 1138: refclock_report(peer, CEVNT_BADREPLY);
! 1139: return;
! 1140: }
! 1141: /*
! 1142: Append a mark of the clock's received signal quality for the
! 1143: benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
! 1144: quality value to `6' for his s/w) and terminate the string for
! 1145: sure. This should not go off the buffer end.
! 1146: */
! 1147: pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
! 1148: '6' : ('0' + up->quality));
! 1149: pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
! 1150:
! 1151: #ifdef PRE_NTP420
! 1152: /* We don't use the micro-/milli- second part... */
! 1153: pp->usec = 0;
! 1154: pp->msec = 0;
! 1155: #else
! 1156: /* We don't use the nano-second part... */
! 1157: pp->nsec = 0;
! 1158: #endif
! 1159: /* Validate format and numbers. */
! 1160: if (pp->a_lastcode[0] != 'o'
! 1161: || !get2(pp->a_lastcode + 1, &pp->hour)
! 1162: || !get2(pp->a_lastcode + 3, &pp->minute)
! 1163: || !get2(pp->a_lastcode + 5, &pp->second)
! 1164: || !get1(pp->a_lastcode + 7, &wday)
! 1165: || !get2(pp->a_lastcode + 8, &pp->day)
! 1166: || !get2(pp->a_lastcode + 10, &month)
! 1167: || !get2(pp->a_lastcode + 12, &pp->year)) {
! 1168: #ifdef DEBUG
! 1169: /* Would expect to have caught major problems already... */
! 1170: if(debug) { printf("arc: badly formatted data.\n"); }
! 1171: #endif
! 1172: pp->lencode = 0;
! 1173: refclock_report(peer, CEVNT_BADREPLY);
! 1174: return;
! 1175: }
! 1176: flags = pp->a_lastcode[14];
! 1177: status = pp->a_lastcode[15];
! 1178: #ifdef DEBUG
! 1179: if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
! 1180: #endif
! 1181: n = 9;
! 1182:
! 1183: /*
! 1184: Validate received values at least enough to prevent internal
! 1185: array-bounds problems, etc.
! 1186: */
! 1187: if((pp->hour < 0) || (pp->hour > 23) ||
! 1188: (pp->minute < 0) || (pp->minute > 59) ||
! 1189: (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
! 1190: (wday < 1) || (wday > 7) ||
! 1191: (pp->day < 1) || (pp->day > 31) ||
! 1192: (month < 1) || (month > 12) ||
! 1193: (pp->year < 0) || (pp->year > 99)) {
! 1194: /* Data out of range. */
! 1195: pp->lencode = 0;
! 1196: refclock_report(peer, CEVNT_BADREPLY);
! 1197: return;
! 1198: }
! 1199:
! 1200:
! 1201: if(peer->MODE == 0) { /* compatiblity to original version */
! 1202: int bst = flags;
! 1203: /* Check that BST/UTC bits are the complement of one another. */
! 1204: if(!(bst & 2) == !(bst & 4)) {
! 1205: pp->lencode = 0;
! 1206: refclock_report(peer, CEVNT_BADREPLY);
! 1207: return;
! 1208: }
! 1209: }
! 1210: if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
! 1211:
! 1212: /* Year-2000 alert! */
! 1213: /* Attempt to wrap 2-digit date into sensible window. */
! 1214: if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */
! 1215: pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */
! 1216: /*
! 1217: Attempt to do the right thing by screaming that the code will
! 1218: soon break when we get to the end of its useful life. What a
! 1219: hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
! 1220: */
! 1221: if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */
! 1222: /*This should get attention B^> */
! 1223: msyslog(LOG_NOTICE,
! 1224: "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
! 1225: }
! 1226: #ifdef DEBUG
! 1227: if(debug) {
! 1228: printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
! 1229: n,
! 1230: pp->hour, pp->minute, pp->second,
! 1231: pp->day, month, pp->year, flags, status);
! 1232: }
! 1233: #endif
! 1234:
! 1235: /*
! 1236: The status value tested for is not strictly supported by the
! 1237: clock spec since the value of bit 2 (0x4) is claimed to be
! 1238: undefined for MSF, yet does seem to indicate if the last resync
! 1239: was successful or not.
! 1240: */
! 1241: pp->leap = LEAP_NOWARNING;
! 1242: status &= 0x7;
! 1243: if(status == 0x3) {
! 1244: if(status != up->status)
! 1245: { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
! 1246: } else {
! 1247: if(status != up->status) {
! 1248: msyslog(LOG_NOTICE, "ARCRON: signal lost");
! 1249: pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
! 1250: up->status = status;
! 1251: pp->lencode = 0;
! 1252: refclock_report(peer, CEVNT_FAULT);
! 1253: return;
! 1254: }
! 1255: }
! 1256: up->status = status;
! 1257:
! 1258: if (peer->MODE == 0) { /* compatiblity to original version */
! 1259: int bst = flags;
! 1260:
! 1261: pp->day += moff[month - 1];
! 1262:
! 1263: if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
! 1264:
! 1265: /* Convert to UTC if required */
! 1266: if(bst & 2) {
! 1267: pp->hour--;
! 1268: if (pp->hour < 0) {
! 1269: pp->hour = 23;
! 1270: pp->day--;
! 1271: /* If we try to wrap round the year
! 1272: * (BST on 1st Jan), reject.*/
! 1273: if(pp->day < 0) {
! 1274: pp->lencode = 0;
! 1275: refclock_report(peer, CEVNT_BADTIME);
! 1276: return;
! 1277: }
! 1278: }
! 1279: }
! 1280: }
! 1281:
! 1282: if(peer->MODE > 0) {
! 1283: if(pp->sloppyclockflag & CLK_FLAG1) {
! 1284: struct tm local;
! 1285: struct tm *gmtp;
! 1286: time_t unixtime;
! 1287:
! 1288: /*
! 1289: * Convert to GMT for sites that distribute localtime.
! 1290: * This means we have to do Y2K conversion on the
! 1291: * 2-digit year; otherwise, we get the time wrong.
! 1292: */
! 1293:
! 1294: memset(&local, 0, sizeof(local));
! 1295:
! 1296: local.tm_year = pp->year-1900;
! 1297: local.tm_mon = month-1;
! 1298: local.tm_mday = pp->day;
! 1299: local.tm_hour = pp->hour;
! 1300: local.tm_min = pp->minute;
! 1301: local.tm_sec = pp->second;
! 1302: switch (peer->MODE) {
! 1303: case 1:
! 1304: local.tm_isdst = (flags & 2);
! 1305: break;
! 1306: case 2:
! 1307: local.tm_isdst = (flags & 2);
! 1308: break;
! 1309: case 3:
! 1310: switch (flags & 3) {
! 1311: case 0: /* It is unclear exactly when the
! 1312: Arcron changes from DST->ST and
! 1313: ST->DST. Testing has shown this
! 1314: to be irregular. For the time
! 1315: being, let the OS decide. */
! 1316: local.tm_isdst = 0;
! 1317: #ifdef DEBUG
! 1318: if (debug)
! 1319: printf ("arc: DST = 00 (0)\n");
! 1320: #endif
! 1321: break;
! 1322: case 1: /* dst->st time */
! 1323: local.tm_isdst = -1;
! 1324: #ifdef DEBUG
! 1325: if (debug)
! 1326: printf ("arc: DST = 01 (1)\n");
! 1327: #endif
! 1328: break;
! 1329: case 2: /* st->dst time */
! 1330: local.tm_isdst = -1;
! 1331: #ifdef DEBUG
! 1332: if (debug)
! 1333: printf ("arc: DST = 10 (2)\n");
! 1334: #endif
! 1335: break;
! 1336: case 3: /* dst time */
! 1337: local.tm_isdst = 1;
! 1338: #ifdef DEBUG
! 1339: if (debug)
! 1340: printf ("arc: DST = 11 (3)\n");
! 1341: #endif
! 1342: break;
! 1343: }
! 1344: break;
! 1345: default:
! 1346: msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
! 1347: peer->MODE);
! 1348: return;
! 1349: break;
! 1350: }
! 1351: unixtime = mktime (&local);
! 1352: if ((gmtp = gmtime (&unixtime)) == NULL)
! 1353: {
! 1354: pp->lencode = 0;
! 1355: refclock_report (peer, CEVNT_FAULT);
! 1356: return;
! 1357: }
! 1358: pp->year = gmtp->tm_year+1900;
! 1359: month = gmtp->tm_mon+1;
! 1360: pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
! 1361: /* pp->day = gmtp->tm_yday; */
! 1362: pp->hour = gmtp->tm_hour;
! 1363: pp->minute = gmtp->tm_min;
! 1364: pp->second = gmtp->tm_sec;
! 1365: #ifdef DEBUG
! 1366: if (debug)
! 1367: {
! 1368: printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
! 1369: pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
! 1370: pp->second);
! 1371: }
! 1372: #endif
! 1373: } else
! 1374: {
! 1375: /*
! 1376: * For more rational sites distributing UTC
! 1377: */
! 1378: pp->day = ymd2yd(pp->year,month,pp->day);
! 1379: }
! 1380: }
! 1381:
! 1382: if (peer->MODE == 0) { /* compatiblity to original version */
! 1383: /* If clock signal quality is
! 1384: * unknown, revert to default PRECISION...*/
! 1385: if(up->quality == QUALITY_UNKNOWN) {
! 1386: peer->precision = PRECISION;
! 1387: } else { /* ...else improve precision if flag3 is set... */
! 1388: peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
! 1389: HIGHPRECISION : PRECISION);
! 1390: }
! 1391: } else {
! 1392: if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
! 1393: peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
! 1394: HIGHPRECISION : PRECISION);
! 1395: } else if (up->quality == QUALITY_UNKNOWN) {
! 1396: peer->precision = PRECISION;
! 1397: } else {
! 1398: peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
! 1399: HIGHPRECISION : PRECISION);
! 1400: }
! 1401: }
! 1402:
! 1403: /* Notice and log any change (eg from initial defaults) for flags. */
! 1404: if(up->saved_flags != pp->sloppyclockflag) {
! 1405: #ifdef DEBUG
! 1406: msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
! 1407: ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
! 1408: ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
! 1409: ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
! 1410: ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
! 1411: /* Note effects of flags changing... */
! 1412: if(debug) {
! 1413: printf("arc: PRECISION = %d.\n", peer->precision);
! 1414: }
! 1415: #endif
! 1416: up->saved_flags = pp->sloppyclockflag;
! 1417: }
! 1418:
! 1419: /* Note time of last believable timestamp. */
! 1420: pp->lastrec = up->lastrec;
! 1421:
! 1422: #ifdef ARCRON_LEAPSECOND_KEEN
! 1423: /* Find out if a leap-second might just have happened...
! 1424: (ie is this the first hour of the first day of Jan or Jul?)
! 1425: */
! 1426: if((pp->hour == 0) &&
! 1427: (pp->day == 1) &&
! 1428: ((month == 1) || (month == 7))) {
! 1429: if(possible_leap >= 0) {
! 1430: /* A leap may have happened, and no resync has started yet...*/
! 1431: possible_leap = 1;
! 1432: }
! 1433: } else {
! 1434: /* Definitely not leap-second territory... */
! 1435: possible_leap = 0;
! 1436: }
! 1437: #endif
! 1438:
! 1439: if (!refclock_process(pp)) {
! 1440: pp->lencode = 0;
! 1441: refclock_report(peer, CEVNT_BADTIME);
! 1442: return;
! 1443: }
! 1444: record_clock_stats(&peer->srcadr, pp->a_lastcode);
! 1445: refclock_receive(peer);
! 1446: }
! 1447:
! 1448:
! 1449: /* request_time() sends a time request to the clock with given peer. */
! 1450: /* This automatically reports a fault if necessary. */
! 1451: /* No data should be sent after this until arc_poll() returns. */
! 1452: static void request_time (int, struct peer *);
! 1453: static void
! 1454: request_time(
! 1455: int unit,
! 1456: struct peer *peer
! 1457: )
! 1458: {
! 1459: struct refclockproc *pp = peer->procptr;
! 1460: register struct arcunit *up = pp->unitptr;
! 1461: #ifdef DEBUG
! 1462: if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
! 1463: #endif
! 1464: if (!send_slow(up, pp->io.fd, "o\r")) {
! 1465: #ifdef DEBUG
! 1466: if (debug) {
! 1467: printf("arc: unit %d: problem sending", unit);
! 1468: }
! 1469: #endif
! 1470: pp->lencode = 0;
! 1471: refclock_report(peer, CEVNT_FAULT);
! 1472: return;
! 1473: }
! 1474: pp->polls++;
! 1475: }
! 1476:
! 1477: /*
! 1478: * arc_poll - called by the transmit procedure
! 1479: */
! 1480: static void
! 1481: arc_poll(
! 1482: int unit,
! 1483: struct peer *peer
! 1484: )
! 1485: {
! 1486: register struct arcunit *up;
! 1487: struct refclockproc *pp;
! 1488: int resync_needed; /* Should we start a resync? */
! 1489:
! 1490: pp = peer->procptr;
! 1491: up = pp->unitptr;
! 1492: #if 0
! 1493: pp->lencode = 0;
! 1494: memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
! 1495: #endif
! 1496:
! 1497: #if 0
! 1498: /* Flush input. */
! 1499: tcflush(pp->io.fd, TCIFLUSH);
! 1500: #endif
! 1501:
! 1502: /* Resync if our next scheduled resync time is here or has passed. */
! 1503: resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
! 1504: (up->next_resync <= current_time) );
! 1505:
! 1506: #ifdef ARCRON_LEAPSECOND_KEEN
! 1507: /*
! 1508: Try to catch a potential leap-second insertion or deletion quickly.
! 1509:
! 1510: In addition to the normal NTP fun of clocks that don't report
! 1511: leap-seconds spooking their hosts, this clock does not even
! 1512: sample the radio sugnal the whole time, so may miss a
! 1513: leap-second insertion or deletion for up to a whole sample
! 1514: time.
! 1515:
! 1516: To try to minimise this effect, if in the first few minutes of
! 1517: the day immediately following a leap-second-insertion point
! 1518: (ie in the first hour of the first day of the first and sixth
! 1519: months), and if the last resync was in the previous day, and a
! 1520: resync is not already in progress, resync the clock
! 1521: immediately.
! 1522:
! 1523: */
! 1524: if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */
! 1525: (!up->resyncing)) { /* No resync in progress yet. */
! 1526: resync_needed = 1;
! 1527: possible_leap = -1; /* Prevent multiple resyncs. */
! 1528: msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
! 1529: }
! 1530: #endif
! 1531:
! 1532: /* Do a resync if required... */
! 1533: if(resync_needed) {
! 1534: /* First, reset quality value to `unknown' so we can detect */
! 1535: /* when a quality message has been responded to by this */
! 1536: /* being set to some other value. */
! 1537: up->quality = QUALITY_UNKNOWN;
! 1538:
! 1539: /* Note that we are resyncing... */
! 1540: up->resyncing = 1;
! 1541:
! 1542: /* Now actually send the resync command and an immediate poll. */
! 1543: #ifdef DEBUG
! 1544: if(debug) { printf("arc: sending resync command (h\\r).\n"); }
! 1545: #endif
! 1546: msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
! 1547: send_slow(up, pp->io.fd, "h\r");
! 1548:
! 1549: /* Schedule our next resync... */
! 1550: up->next_resync = current_time + DEFAULT_RESYNC_TIME;
! 1551:
! 1552: /* Drop through to request time if appropriate. */
! 1553: }
! 1554:
! 1555: /* If clock quality is too poor to trust, indicate a fault. */
! 1556: /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
! 1557: /* we'll cross our fingers and just hope that the thing */
! 1558: /* synced so quickly we did not catch it---we'll */
! 1559: /* double-check the clock is OK elsewhere. */
! 1560: if(
! 1561: #ifdef ARCRON_KEEN
! 1562: (up->quality != QUALITY_UNKNOWN) &&
! 1563: #else
! 1564: (up->quality == QUALITY_UNKNOWN) ||
! 1565: #endif
! 1566: (up->quality < MIN_CLOCK_QUALITY_OK)) {
! 1567: #ifdef DEBUG
! 1568: if(debug) {
! 1569: printf("arc: clock quality %d too poor.\n", up->quality);
! 1570: }
! 1571: #endif
! 1572: pp->lencode = 0;
! 1573: refclock_report(peer, CEVNT_FAULT);
! 1574: return;
! 1575: }
! 1576: /* This is the normal case: request a timestamp. */
! 1577: request_time(unit, peer);
! 1578: }
! 1579:
! 1580: #else
! 1581: int refclock_arc_bs;
! 1582: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>