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