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