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 306224ba2bdSOllivier Robert #include "ntpd.h" 307224ba2bdSOllivier Robert #include "ntp_io.h" 308224ba2bdSOllivier Robert #include "ntp_refclock.h" 309224ba2bdSOllivier Robert #include "ntp_stdlib.h" 310c0b746e5SOllivier Robert 311c0b746e5SOllivier Robert #include <stdio.h> 312c0b746e5SOllivier Robert #include <ctype.h> 313c0b746e5SOllivier Robert 314c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS) 315c0b746e5SOllivier Robert #include <sgtty.h> 316c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */ 317c0b746e5SOllivier Robert 318c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS) 319c0b746e5SOllivier Robert #include <termio.h> 320c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */ 321c0b746e5SOllivier Robert 322c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS) 323c0b746e5SOllivier Robert #include <termios.h> 324c0b746e5SOllivier Robert #endif 325c0b746e5SOllivier Robert 326c0b746e5SOllivier Robert /* 327c0b746e5SOllivier Robert * This driver supports the ARCRON MSF Radio Controlled Clock 328c0b746e5SOllivier Robert */ 329c0b746e5SOllivier Robert 330c0b746e5SOllivier Robert /* 331c0b746e5SOllivier Robert * Interface definitions 332c0b746e5SOllivier Robert */ 333c0b746e5SOllivier Robert #define DEVICE "/dev/arc%d" /* Device name and unit. */ 334c0b746e5SOllivier Robert #define SPEED B300 /* UART speed (300 baud) */ 335c0b746e5SOllivier Robert #define PRECISION (-4) /* Precision (~63 ms). */ 336c0b746e5SOllivier Robert #define HIGHPRECISION (-5) /* If things are going well... */ 337c0b746e5SOllivier Robert #define REFID "MSFa" /* Reference ID. */ 338c0b746e5SOllivier Robert #define DESCRIPTION "ARCRON MSF Receiver" 339c0b746e5SOllivier Robert 340c0b746e5SOllivier Robert #define NSAMPLESLONG 8 /* Stages of long filter. */ 341c0b746e5SOllivier Robert 342c0b746e5SOllivier Robert #define LENARC 16 /* Format `o' timecode length. */ 343c0b746e5SOllivier Robert 344c0b746e5SOllivier Robert #define BITSPERCHAR 11 /* Bits per character. */ 345c0b746e5SOllivier Robert #define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */ 346c0b746e5SOllivier Robert #define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */ 347c0b746e5SOllivier Robert #define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */ 348c0b746e5SOllivier Robert #define CHARTIME /* Time for char at 300bps. */ \ 349c0b746e5SOllivier Robert ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \ 350c0b746e5SOllivier Robert (BITSPERCHAR * BITTIME) ) ) 351c0b746e5SOllivier Robert 352c0b746e5SOllivier Robert /* Allow for UART to accept char half-way through final stop bit. */ 353c0b746e5SOllivier Robert #define INITIALOFFSET (u_int32)(-BITTIME/2) 354c0b746e5SOllivier Robert 355c0b746e5SOllivier Robert /* 356c0b746e5SOllivier Robert charoffsets[x] is the time after the start of the second that byte 357c0b746e5SOllivier Robert x (with the first byte being byte 1) is received by the UART, 358c0b746e5SOllivier Robert assuming that the initial edge of the start bit of the first byte 359c0b746e5SOllivier Robert is on-time. The values are represented as the fractional part of 360c0b746e5SOllivier Robert an l_fp. 361c0b746e5SOllivier Robert 362c0b746e5SOllivier Robert We store enough values to have the offset of each byte including 363c0b746e5SOllivier Robert the trailing \r, on the assumption that the bytes follow one 364c0b746e5SOllivier Robert another without gaps. 365c0b746e5SOllivier Robert */ 366c0b746e5SOllivier Robert static const u_int32 charoffsets[LENARC+1] = { 367c0b746e5SOllivier Robert #if BITSPERCHAR == 11 /* Usual case. */ 368c0b746e5SOllivier Robert /* Offsets computed as accurately as possible... */ 369c0b746e5SOllivier Robert 0, 370c0b746e5SOllivier Robert INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */ 371c0b746e5SOllivier Robert INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */ 372c0b746e5SOllivier Robert INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */ 373c0b746e5SOllivier Robert INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */ 374c0b746e5SOllivier Robert INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */ 375c0b746e5SOllivier Robert INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */ 376c0b746e5SOllivier Robert INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */ 377c0b746e5SOllivier Robert INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */ 378c0b746e5SOllivier Robert INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */ 379c0b746e5SOllivier Robert INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */ 380c0b746e5SOllivier Robert INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */ 381c0b746e5SOllivier Robert INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */ 382c0b746e5SOllivier Robert INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */ 383c0b746e5SOllivier Robert INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */ 384c0b746e5SOllivier Robert INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */ 385c0b746e5SOllivier Robert INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */ 386c0b746e5SOllivier Robert #else 387c0b746e5SOllivier Robert /* Offsets computed with a small rounding error... */ 388c0b746e5SOllivier Robert 0, 389c0b746e5SOllivier Robert INITIALOFFSET + 1 * CHARTIME, 390c0b746e5SOllivier Robert INITIALOFFSET + 2 * CHARTIME, 391c0b746e5SOllivier Robert INITIALOFFSET + 3 * CHARTIME, 392c0b746e5SOllivier Robert INITIALOFFSET + 4 * CHARTIME, 393c0b746e5SOllivier Robert INITIALOFFSET + 5 * CHARTIME, 394c0b746e5SOllivier Robert INITIALOFFSET + 6 * CHARTIME, 395c0b746e5SOllivier Robert INITIALOFFSET + 7 * CHARTIME, 396c0b746e5SOllivier Robert INITIALOFFSET + 8 * CHARTIME, 397c0b746e5SOllivier Robert INITIALOFFSET + 9 * CHARTIME, 398c0b746e5SOllivier Robert INITIALOFFSET + 10 * CHARTIME, 399c0b746e5SOllivier Robert INITIALOFFSET + 11 * CHARTIME, 400c0b746e5SOllivier Robert INITIALOFFSET + 12 * CHARTIME, 401c0b746e5SOllivier Robert INITIALOFFSET + 13 * CHARTIME, 402c0b746e5SOllivier Robert INITIALOFFSET + 14 * CHARTIME, 403c0b746e5SOllivier Robert INITIALOFFSET + 15 * CHARTIME, 404c0b746e5SOllivier Robert INITIALOFFSET + 16 * CHARTIME 405c0b746e5SOllivier Robert #endif 406c0b746e5SOllivier Robert }; 407c0b746e5SOllivier Robert 408c0b746e5SOllivier Robert /* Chose filter length dependent on fudge flag 4. */ 409c0b746e5SOllivier Robert #define CHOSENSAMPLES(pp) \ 410c0b746e5SOllivier Robert (((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES) 411c0b746e5SOllivier Robert /* 412c0b746e5SOllivier Robert Chose how many filter samples to keep. Several factors are in play. 413c0b746e5SOllivier Robert 414c0b746e5SOllivier Robert 1) Discard at least one sample to allow a spike value to be 415c0b746e5SOllivier Robert discarded. 416c0b746e5SOllivier Robert 417c0b746e5SOllivier Robert 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes. 418c0b746e5SOllivier Robert 419c0b746e5SOllivier Robert 3) Keep an odd number of samples to avoid median value being biased 420c0b746e5SOllivier Robert high or low. 421c0b746e5SOllivier Robert */ 422c0b746e5SOllivier Robert #define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1) 423c0b746e5SOllivier Robert 424c0b746e5SOllivier Robert #define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */ 425c0b746e5SOllivier Robert #define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */ 426c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 427c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */ 428c0b746e5SOllivier Robert #else 429c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */ 430c0b746e5SOllivier Robert #endif 431c0b746e5SOllivier Robert 432c0b746e5SOllivier Robert static const int moff[12] = 433c0b746e5SOllivier Robert { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 434c0b746e5SOllivier Robert /* Flags for a raw open() of the clock serial device. */ 435c0b746e5SOllivier Robert #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */ 436c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR | O_NOCTTY) 437c0b746e5SOllivier Robert #else /* Oh well, it may not matter... */ 438c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR) 439c0b746e5SOllivier Robert #endif 440c0b746e5SOllivier Robert 441c0b746e5SOllivier Robert 442c0b746e5SOllivier Robert /* Length of queue of command bytes to be sent. */ 443c0b746e5SOllivier Robert #define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */ 444c0b746e5SOllivier Robert /* Queue tick time; interval in seconds between chars taken off queue. */ 445c0b746e5SOllivier Robert /* Must be >= 2 to allow o\r response to come back uninterrupted. */ 446c0b746e5SOllivier Robert #define QUEUETICK 2 /* Allow o\r reply to finish. */ 447c0b746e5SOllivier Robert 448c0b746e5SOllivier Robert /* 449c0b746e5SOllivier Robert * ARC unit control structure 450c0b746e5SOllivier Robert */ 451c0b746e5SOllivier Robert struct arcunit { 452c0b746e5SOllivier Robert l_fp lastrec; /* Time tag for the receive time (system). */ 453c0b746e5SOllivier Robert int status; /* Clock status. */ 454c0b746e5SOllivier Robert 455c0b746e5SOllivier Robert int quality; /* Quality of reception 0--5 for unit. */ 456c0b746e5SOllivier Robert /* We may also use the values -1 or 6 internally. */ 457c0b746e5SOllivier Robert 458c0b746e5SOllivier Robert u_long next_resync; /* Next resync time (s) compared to current_time. */ 459c0b746e5SOllivier Robert int resyncing; /* Resync in progress if true. */ 460c0b746e5SOllivier Robert 461c0b746e5SOllivier Robert /* In the outgoing queue, cmdqueue[0] is next to be sent. */ 462c0b746e5SOllivier Robert char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */ 463c0b746e5SOllivier Robert 464c0b746e5SOllivier Robert u_long saved_flags; /* Saved fudge flags. */ 465c0b746e5SOllivier Robert }; 466c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 467c0b746e5SOllivier Robert /* The flag `possible_leap' is set non-zero when any MSF unit 468c0b746e5SOllivier Robert thinks a leap-second may have happened. 469c0b746e5SOllivier Robert 470c0b746e5SOllivier Robert Set whenever we receive a valid time sample in the first hour of 471c0b746e5SOllivier Robert the first day of the first/seventh months. 472c0b746e5SOllivier Robert 473c0b746e5SOllivier Robert Outside the special hour this value is unconditionally set 474c0b746e5SOllivier Robert to zero by the receive routine. 475c0b746e5SOllivier Robert 476c0b746e5SOllivier Robert On finding itself in this timeslot, as long as the value is 477c0b746e5SOllivier Robert non-negative, the receive routine sets it to a positive value to 478c0b746e5SOllivier Robert indicate a resync to MSF should be performed. 479c0b746e5SOllivier Robert 480c0b746e5SOllivier Robert In the poll routine, if this value is positive and we are not 481c0b746e5SOllivier Robert already resyncing (eg from a sync that started just before 482c0b746e5SOllivier Robert midnight), start resyncing and set this value negative to 483c0b746e5SOllivier Robert indicate that a leap-triggered resync has been started. Having 484c0b746e5SOllivier Robert set this negative prevents the receive routine setting it 485c0b746e5SOllivier Robert positive and thus prevents multiple resyncs during the witching 486c0b746e5SOllivier Robert hour. 487c0b746e5SOllivier Robert */ 488c0b746e5SOllivier Robert static int possible_leap = 0; /* No resync required by default. */ 489c0b746e5SOllivier Robert #endif 490c0b746e5SOllivier Robert 491c0b746e5SOllivier Robert #if 0 492c0b746e5SOllivier Robert static void dummy_event_handler P((struct peer *)); 493c0b746e5SOllivier Robert static void arc_event_handler P((struct peer *)); 494c0b746e5SOllivier Robert #endif /* 0 */ 495c0b746e5SOllivier Robert 496c0b746e5SOllivier Robert #define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */ 497c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */ 498c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */ 499c0b746e5SOllivier Robert #define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */ 500c0b746e5SOllivier Robert 501c0b746e5SOllivier Robert /* 502c0b746e5SOllivier Robert * Function prototypes 503c0b746e5SOllivier Robert */ 504c0b746e5SOllivier Robert static int arc_start P((int, struct peer *)); 505c0b746e5SOllivier Robert static void arc_shutdown P((int, struct peer *)); 506c0b746e5SOllivier Robert static void arc_receive P((struct recvbuf *)); 507c0b746e5SOllivier Robert static void arc_poll P((int, struct peer *)); 508c0b746e5SOllivier Robert 509c0b746e5SOllivier Robert /* 510c0b746e5SOllivier Robert * Transfer vector 511c0b746e5SOllivier Robert */ 512c0b746e5SOllivier Robert struct refclock refclock_arc = { 513c0b746e5SOllivier Robert arc_start, /* start up driver */ 514c0b746e5SOllivier Robert arc_shutdown, /* shut down driver */ 515c0b746e5SOllivier Robert arc_poll, /* transmit poll message */ 516c0b746e5SOllivier Robert noentry, /* not used (old arc_control) */ 517c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 518c0b746e5SOllivier Robert noentry, /* not used (old arc_buginfo) */ 519c0b746e5SOllivier Robert NOFLAGS /* not used */ 520c0b746e5SOllivier Robert }; 521c0b746e5SOllivier Robert 522c0b746e5SOllivier Robert /* Queue us up for the next tick. */ 523c0b746e5SOllivier Robert #define ENQUEUE(up) \ 524c0b746e5SOllivier Robert do { \ 525c0b746e5SOllivier Robert if((up)->ev.next != 0) { break; } /* WHOOPS! */ \ 526c0b746e5SOllivier Robert peer->nextdate = current_time + QUEUETICK; \ 527c0b746e5SOllivier Robert } while(0) 528c0b746e5SOllivier Robert 529c0b746e5SOllivier Robert #if 0 530c0b746e5SOllivier Robert /* Placeholder event handler---does nothing safely---soaks up lose tick. */ 531c0b746e5SOllivier Robert static void 532c0b746e5SOllivier Robert dummy_event_handler( 533c0b746e5SOllivier Robert struct peer *peer 534c0b746e5SOllivier Robert ) 535c0b746e5SOllivier Robert { 536c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 537c0b746e5SOllivier Robert if(debug) { printf("arc: dummy_event_handler() called.\n"); } 538c0b746e5SOllivier Robert #endif 539c0b746e5SOllivier Robert } 540c0b746e5SOllivier Robert 541c0b746e5SOllivier Robert /* 542c0b746e5SOllivier Robert Normal event handler. 543c0b746e5SOllivier Robert 544c0b746e5SOllivier Robert Take first character off queue and send to clock if not a null. 545c0b746e5SOllivier Robert 546c0b746e5SOllivier Robert Shift characters down and put a null on the end. 547c0b746e5SOllivier Robert 548c0b746e5SOllivier Robert We assume that there is no parallelism so no race condition, but even 549c0b746e5SOllivier Robert if there is nothing bad will happen except that we might send some bad 550c0b746e5SOllivier Robert data to the clock once in a while. 551c0b746e5SOllivier Robert */ 552c0b746e5SOllivier Robert static void 553c0b746e5SOllivier Robert arc_event_handler( 554c0b746e5SOllivier Robert struct peer *peer 555c0b746e5SOllivier Robert ) 556c0b746e5SOllivier Robert { 557c0b746e5SOllivier Robert struct refclockproc *pp = peer->procptr; 558c0b746e5SOllivier Robert register struct arcunit *up = (struct arcunit *)pp->unitptr; 559c0b746e5SOllivier Robert int i; 560c0b746e5SOllivier Robert char c; 561c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 562c0b746e5SOllivier Robert if(debug > 2) { printf("arc: arc_event_handler() called.\n"); } 563c0b746e5SOllivier Robert #endif 564c0b746e5SOllivier Robert 565c0b746e5SOllivier Robert c = up->cmdqueue[0]; /* Next char to be sent. */ 566c0b746e5SOllivier Robert /* Shift down characters, shifting trailing \0 in at end. */ 567c0b746e5SOllivier Robert for(i = 0; i < CMDQUEUELEN; ++i) 568c0b746e5SOllivier Robert { up->cmdqueue[i] = up->cmdqueue[i+1]; } 569c0b746e5SOllivier Robert 570c0b746e5SOllivier Robert /* Don't send '\0' characters. */ 571c0b746e5SOllivier Robert if(c != '\0') { 572c0b746e5SOllivier Robert if(write(pp->io.fd, &c, 1) != 1) { 573c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd); 574c0b746e5SOllivier Robert } 575c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 576c0b746e5SOllivier Robert else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } 577c0b746e5SOllivier Robert #endif 578c0b746e5SOllivier Robert } 579c0b746e5SOllivier Robert } 580c0b746e5SOllivier Robert #endif /* 0 */ 581c0b746e5SOllivier Robert 582c0b746e5SOllivier Robert /* 583c0b746e5SOllivier Robert * arc_start - open the devices and initialize data for processing 584c0b746e5SOllivier Robert */ 585c0b746e5SOllivier Robert static int 586c0b746e5SOllivier Robert arc_start( 587c0b746e5SOllivier Robert int unit, 588c0b746e5SOllivier Robert struct peer *peer 589c0b746e5SOllivier Robert ) 590c0b746e5SOllivier Robert { 591c0b746e5SOllivier Robert register struct arcunit *up; 592c0b746e5SOllivier Robert struct refclockproc *pp; 593c0b746e5SOllivier Robert int fd; 594c0b746e5SOllivier Robert char device[20]; 595c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS 596c0b746e5SOllivier Robert struct termios arg; 597c0b746e5SOllivier Robert #endif 598c0b746e5SOllivier Robert 599c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit); 600c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 601c0b746e5SOllivier Robert if(debug) { 602c0b746e5SOllivier Robert printf("arc: %s: attempt to open unit %d.\n", arc_version, unit); 603c0b746e5SOllivier Robert } 604c0b746e5SOllivier Robert #endif 605c0b746e5SOllivier Robert 606c0b746e5SOllivier Robert /* Prevent a ridiculous device number causing overflow of device[]. */ 607c0b746e5SOllivier Robert if((unit < 0) || (unit > 255)) { return(0); } 608c0b746e5SOllivier Robert 609c0b746e5SOllivier Robert /* 610c0b746e5SOllivier Robert * Open serial port. Use CLK line discipline, if available. 611c0b746e5SOllivier Robert */ 612c0b746e5SOllivier Robert (void)sprintf(device, DEVICE, unit); 613c0b746e5SOllivier Robert if (!(fd = refclock_open(device, SPEED, LDISC_CLK))) 614c0b746e5SOllivier Robert return(0); 615c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 616c0b746e5SOllivier Robert if(debug) { printf("arc: unit %d using open().\n", unit); } 617c0b746e5SOllivier Robert #endif 618c0b746e5SOllivier Robert fd = open(device, OPEN_FLAGS); 619c0b746e5SOllivier Robert if(fd < 0) { 620c0b746e5SOllivier Robert #ifdef DEBUG 621c0b746e5SOllivier Robert if(debug) { printf("arc: failed [open()] to open %s.\n", device); } 622c0b746e5SOllivier Robert #endif 623c0b746e5SOllivier Robert return(0); 624c0b746e5SOllivier Robert } 625c0b746e5SOllivier Robert 626c0b746e5SOllivier Robert fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */ 627c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 628c0b746e5SOllivier Robert if(debug) 629c0b746e5SOllivier Robert { printf("Opened RS232 port with file descriptor %d.\n", fd); } 630c0b746e5SOllivier Robert #endif 631c0b746e5SOllivier Robert 632c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS 633c0b746e5SOllivier Robert 634c0b746e5SOllivier Robert arg.c_iflag = IGNBRK | ISTRIP; 635c0b746e5SOllivier Robert arg.c_oflag = 0; 636c0b746e5SOllivier Robert arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB; 637c0b746e5SOllivier Robert arg.c_lflag = 0; 638c0b746e5SOllivier Robert arg.c_cc[VMIN] = 1; 639c0b746e5SOllivier Robert arg.c_cc[VTIME] = 0; 640c0b746e5SOllivier Robert 641c0b746e5SOllivier Robert tcsetattr(fd, TCSANOW, &arg); 642c0b746e5SOllivier Robert 643c0b746e5SOllivier Robert #else 644c0b746e5SOllivier Robert 645c0b746e5SOllivier Robert msyslog(LOG_ERR, "ARCRON: termios not supported in this driver"); 646c0b746e5SOllivier Robert (void)close(fd); 647c0b746e5SOllivier Robert 648c0b746e5SOllivier Robert return 0; 649c0b746e5SOllivier Robert 650c0b746e5SOllivier Robert #endif 651c0b746e5SOllivier Robert 652c0b746e5SOllivier Robert up = (struct arcunit *) emalloc(sizeof(struct arcunit)); 653c0b746e5SOllivier Robert if(!up) { (void) close(fd); return(0); } 654c0b746e5SOllivier Robert /* Set structure to all zeros... */ 655c0b746e5SOllivier Robert memset((char *)up, 0, sizeof(struct arcunit)); 656c0b746e5SOllivier Robert pp = peer->procptr; 657c0b746e5SOllivier Robert pp->io.clock_recv = arc_receive; 658c0b746e5SOllivier Robert pp->io.srcclock = (caddr_t)peer; 659c0b746e5SOllivier Robert pp->io.datalen = 0; 660c0b746e5SOllivier Robert pp->io.fd = fd; 661c0b746e5SOllivier Robert if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); } 662c0b746e5SOllivier Robert pp->unitptr = (caddr_t)up; 663c0b746e5SOllivier Robert 664c0b746e5SOllivier Robert /* 665c0b746e5SOllivier Robert * Initialize miscellaneous variables 666c0b746e5SOllivier Robert */ 667c0b746e5SOllivier Robert peer->precision = PRECISION; 668c0b746e5SOllivier Robert peer->stratum = 2; /* Default to stratum 2 not 0. */ 669c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 670c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 671c0b746e5SOllivier Robert /* Spread out resyncs so that they should remain separated. */ 672c0b746e5SOllivier Robert up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009; 673c0b746e5SOllivier Robert 674c0b746e5SOllivier Robert #if 0 /* Not needed because of zeroing of arcunit structure... */ 675c0b746e5SOllivier Robert up->resyncing = 0; /* Not resyncing yet. */ 676c0b746e5SOllivier Robert up->saved_flags = 0; /* Default is all flags off. */ 677c0b746e5SOllivier Robert /* Clear send buffer out... */ 678c0b746e5SOllivier Robert { 679c0b746e5SOllivier Robert int i; 680c0b746e5SOllivier Robert for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; } 681c0b746e5SOllivier Robert } 682c0b746e5SOllivier Robert #endif 683c0b746e5SOllivier Robert 684c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 685c0b746e5SOllivier Robert up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */ 686c0b746e5SOllivier Robert #else 687c0b746e5SOllivier Robert up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */ 688c0b746e5SOllivier Robert #endif 689c0b746e5SOllivier Robert return(1); 690c0b746e5SOllivier Robert } 691c0b746e5SOllivier Robert 692c0b746e5SOllivier Robert 693c0b746e5SOllivier Robert /* 694c0b746e5SOllivier Robert * arc_shutdown - shut down the clock 695c0b746e5SOllivier Robert */ 696c0b746e5SOllivier Robert static void 697c0b746e5SOllivier Robert arc_shutdown( 698c0b746e5SOllivier Robert int unit, 699c0b746e5SOllivier Robert struct peer *peer 700c0b746e5SOllivier Robert ) 701c0b746e5SOllivier Robert { 702c0b746e5SOllivier Robert register struct arcunit *up; 703c0b746e5SOllivier Robert struct refclockproc *pp; 704c0b746e5SOllivier Robert 705c0b746e5SOllivier Robert pp = peer->procptr; 706c0b746e5SOllivier Robert up = (struct arcunit *)pp->unitptr; 707c0b746e5SOllivier Robert io_closeclock(&pp->io); 708c0b746e5SOllivier Robert free(up); 709c0b746e5SOllivier Robert } 710c0b746e5SOllivier Robert 711c0b746e5SOllivier Robert /* 712c0b746e5SOllivier Robert Compute space left in output buffer. 713c0b746e5SOllivier Robert */ 714c0b746e5SOllivier Robert static int 715c0b746e5SOllivier Robert space_left( 716c0b746e5SOllivier Robert register struct arcunit *up 717c0b746e5SOllivier Robert ) 718c0b746e5SOllivier Robert { 719c0b746e5SOllivier Robert int spaceleft; 720c0b746e5SOllivier Robert 721c0b746e5SOllivier Robert /* Compute space left in buffer after any pending output. */ 722c0b746e5SOllivier Robert for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft) 723c0b746e5SOllivier Robert { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } } 724c0b746e5SOllivier Robert return(spaceleft); 725c0b746e5SOllivier Robert } 726c0b746e5SOllivier Robert 727c0b746e5SOllivier Robert /* 728c0b746e5SOllivier Robert Send command by copying into command buffer as far forward as possible, 729c0b746e5SOllivier Robert after any pending output. 730c0b746e5SOllivier Robert 731c0b746e5SOllivier Robert Indicate an error by returning 0 if there is not space for the command. 732c0b746e5SOllivier Robert */ 733c0b746e5SOllivier Robert static int 734c0b746e5SOllivier Robert send_slow( 735c0b746e5SOllivier Robert register struct arcunit *up, 736c0b746e5SOllivier Robert int fd, 737c0b746e5SOllivier Robert const char *s 738c0b746e5SOllivier Robert ) 739c0b746e5SOllivier Robert { 740c0b746e5SOllivier Robert int sl = strlen(s); 741c0b746e5SOllivier Robert int spaceleft = space_left(up); 742c0b746e5SOllivier Robert 743c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 744c0b746e5SOllivier Robert if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } 745c0b746e5SOllivier Robert #endif 746c0b746e5SOllivier Robert if(spaceleft < sl) { /* Should not normally happen... */ 747c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 748c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)", 749c0b746e5SOllivier Robert sl, spaceleft); 750c0b746e5SOllivier Robert #endif 751c0b746e5SOllivier Robert return(0); /* FAILED! */ 752c0b746e5SOllivier Robert } 753c0b746e5SOllivier Robert 754c0b746e5SOllivier Robert /* Copy in the command to be sent. */ 755c0b746e5SOllivier Robert while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; } 756c0b746e5SOllivier Robert 757c0b746e5SOllivier Robert return(1); 758c0b746e5SOllivier Robert } 759c0b746e5SOllivier Robert 760c0b746e5SOllivier Robert 761c0b746e5SOllivier Robert /* Macro indicating action we will take for different quality values. */ 762c0b746e5SOllivier Robert #define quality_action(q) \ 763c0b746e5SOllivier Robert (((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \ 764c0b746e5SOllivier Robert (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \ 765c0b746e5SOllivier Robert "OK, will use clock")) 766c0b746e5SOllivier Robert 767c0b746e5SOllivier Robert /* 768c0b746e5SOllivier Robert * arc_receive - receive data from the serial interface 769c0b746e5SOllivier Robert */ 770c0b746e5SOllivier Robert static void 771c0b746e5SOllivier Robert arc_receive( 772c0b746e5SOllivier Robert struct recvbuf *rbufp 773c0b746e5SOllivier Robert ) 774c0b746e5SOllivier Robert { 775c0b746e5SOllivier Robert register struct arcunit *up; 776c0b746e5SOllivier Robert struct refclockproc *pp; 777c0b746e5SOllivier Robert struct peer *peer; 778c0b746e5SOllivier Robert char c; 779c0b746e5SOllivier Robert int i, n, wday, month, bst, status; 780c0b746e5SOllivier Robert int arc_last_offset; 781c0b746e5SOllivier Robert 782c0b746e5SOllivier Robert /* 783c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp 784c0b746e5SOllivier Robert */ 785c0b746e5SOllivier Robert peer = (struct peer *)rbufp->recv_srcclock; 786c0b746e5SOllivier Robert pp = peer->procptr; 787c0b746e5SOllivier Robert up = (struct arcunit *)pp->unitptr; 788c0b746e5SOllivier Robert 789c0b746e5SOllivier Robert 790c0b746e5SOllivier Robert /* 791c0b746e5SOllivier Robert If the command buffer is empty, and we are resyncing, insert a 792c0b746e5SOllivier Robert g\r quality request into it to poll for signal quality again. 793c0b746e5SOllivier Robert */ 794c0b746e5SOllivier Robert if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) { 795c0b746e5SOllivier Robert #ifdef DEBUG 796c0b746e5SOllivier Robert if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); } 797c0b746e5SOllivier Robert #endif 798c0b746e5SOllivier Robert send_slow(up, pp->io.fd, "g\r"); 799c0b746e5SOllivier Robert } 800c0b746e5SOllivier Robert 801c0b746e5SOllivier Robert /* 802c0b746e5SOllivier Robert The `arc_last_offset' is the offset in lastcode[] of the last byte 803c0b746e5SOllivier Robert received, and which we assume actually received the input 804c0b746e5SOllivier Robert timestamp. 805c0b746e5SOllivier Robert 806c0b746e5SOllivier Robert (When we get round to using tty_clk and it is available, we 807c0b746e5SOllivier Robert assume that we will receive the whole timecode with the 808c0b746e5SOllivier Robert trailing \r, and that that \r will be timestamped. But this 809c0b746e5SOllivier Robert assumption also works if receive the characters one-by-one.) 810c0b746e5SOllivier Robert */ 811c0b746e5SOllivier Robert arc_last_offset = pp->lencode+rbufp->recv_length - 1; 812c0b746e5SOllivier Robert 813c0b746e5SOllivier Robert /* 814c0b746e5SOllivier Robert We catch a timestamp iff: 815c0b746e5SOllivier Robert 816c0b746e5SOllivier Robert * The command code is `o' for a timestamp. 817c0b746e5SOllivier Robert 818c0b746e5SOllivier Robert * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have 819c0b746e5SOllivier Robert exactly char in the buffer (the command code) so that we 820c0b746e5SOllivier Robert only sample the first character of the timecode as our 821c0b746e5SOllivier Robert `on-time' character. 822c0b746e5SOllivier Robert 823c0b746e5SOllivier Robert * The first character in the buffer is not the echoed `\r' 824c0b746e5SOllivier Robert from the `o` command (so if we are to timestamp an `\r' it 825c0b746e5SOllivier Robert must not be first in the receive buffer with lencode==1. 826c0b746e5SOllivier Robert (Even if we had other characters following it, we probably 827c0b746e5SOllivier Robert would have a premature timestamp on the '\r'.) 828c0b746e5SOllivier Robert 829c0b746e5SOllivier Robert * We have received at least one character (I cannot imagine 830c0b746e5SOllivier Robert how it could be otherwise, but anyway...). 831c0b746e5SOllivier Robert */ 832c0b746e5SOllivier Robert c = rbufp->recv_buffer[0]; 833c0b746e5SOllivier Robert if((pp->a_lastcode[0] == 'o') && 834c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES 835c0b746e5SOllivier Robert (pp->lencode == 1) && 836c0b746e5SOllivier Robert #endif 837c0b746e5SOllivier Robert ((pp->lencode != 1) || (c != '\r')) && 838c0b746e5SOllivier Robert (arc_last_offset >= 1)) { 839c0b746e5SOllivier Robert /* Note that the timestamp should be corrected if >1 char rcvd. */ 840c0b746e5SOllivier Robert l_fp timestamp; 841c0b746e5SOllivier Robert timestamp = rbufp->recv_time; 842c0b746e5SOllivier Robert #ifdef DEBUG 843c0b746e5SOllivier Robert if(debug) { /* Show \r as `R', other non-printing char as `?'. */ 844c0b746e5SOllivier Robert printf("arc: stamp -->%c<-- (%d chars rcvd)\n", 845c0b746e5SOllivier Robert ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')), 846c0b746e5SOllivier Robert rbufp->recv_length); 847c0b746e5SOllivier Robert } 848c0b746e5SOllivier Robert #endif 849c0b746e5SOllivier Robert 850c0b746e5SOllivier Robert /* 851c0b746e5SOllivier Robert Now correct timestamp by offset of last byte received---we 852c0b746e5SOllivier Robert subtract from the receive time the delay implied by the 853c0b746e5SOllivier Robert extra characters received. 854c0b746e5SOllivier Robert 855c0b746e5SOllivier Robert Reject the input if the resulting code is too long, but 856c0b746e5SOllivier Robert allow for the trailing \r, normally not used but a good 857c0b746e5SOllivier Robert handle for tty_clk or somesuch kernel timestamper. 858c0b746e5SOllivier Robert */ 859c0b746e5SOllivier Robert if(arc_last_offset > LENARC) { 860c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 861c0b746e5SOllivier Robert if(debug) { 862c0b746e5SOllivier Robert printf("arc: input code too long (%d cf %d); rejected.\n", 863c0b746e5SOllivier Robert arc_last_offset, LENARC); 864c0b746e5SOllivier Robert } 865c0b746e5SOllivier Robert #endif 866c0b746e5SOllivier Robert pp->lencode = 0; 867c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 868c0b746e5SOllivier Robert return; 869c0b746e5SOllivier Robert } 870c0b746e5SOllivier Robert 871c0b746e5SOllivier Robert L_SUBUF(×tamp, charoffsets[arc_last_offset]); 872c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 873c0b746e5SOllivier Robert if(debug > 1) { 874c0b746e5SOllivier Robert printf( 875c0b746e5SOllivier Robert "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n", 876c0b746e5SOllivier Robert ((rbufp->recv_length > 1) ? "*** " : ""), 877c0b746e5SOllivier Robert rbufp->recv_length, 878c0b746e5SOllivier Robert arc_last_offset, 879c0b746e5SOllivier Robert mfptoms((unsigned long)0, 880c0b746e5SOllivier Robert charoffsets[arc_last_offset], 881c0b746e5SOllivier Robert 1)); 882c0b746e5SOllivier Robert } 883c0b746e5SOllivier Robert #endif 884c0b746e5SOllivier Robert 885c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES 886c0b746e5SOllivier Robert /* 887c0b746e5SOllivier Robert If taking multiple samples, capture the current adjusted 888c0b746e5SOllivier Robert sample iff: 889c0b746e5SOllivier Robert 890c0b746e5SOllivier Robert * No timestamp has yet been captured (it is zero), OR 891c0b746e5SOllivier Robert 892c0b746e5SOllivier Robert * This adjusted timestamp is earlier than the one already 893c0b746e5SOllivier Robert captured, on the grounds that this one suffered less 894c0b746e5SOllivier Robert delay in being delivered to us and is more accurate. 895c0b746e5SOllivier Robert 896c0b746e5SOllivier Robert */ 897c0b746e5SOllivier Robert if(L_ISZERO(&(up->lastrec)) || 898c0b746e5SOllivier Robert L_ISGEQ(&(up->lastrec), ×tamp)) 899c0b746e5SOllivier Robert #endif 900c0b746e5SOllivier Robert { 901c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 902c0b746e5SOllivier Robert if(debug > 1) { 903c0b746e5SOllivier Robert printf("arc: system timestamp captured.\n"); 904c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES 905c0b746e5SOllivier Robert if(!L_ISZERO(&(up->lastrec))) { 906c0b746e5SOllivier Robert l_fp diff; 907c0b746e5SOllivier Robert diff = up->lastrec; 908c0b746e5SOllivier Robert L_SUB(&diff, ×tamp); 909c0b746e5SOllivier Robert printf("arc: adjusted timestamp by -%sms.\n", 910c0b746e5SOllivier Robert mfptoms(diff.l_i, diff.l_f, 3)); 911c0b746e5SOllivier Robert } 912c0b746e5SOllivier Robert #endif 913c0b746e5SOllivier Robert } 914c0b746e5SOllivier Robert #endif 915c0b746e5SOllivier Robert up->lastrec = timestamp; 916c0b746e5SOllivier Robert } 917c0b746e5SOllivier Robert 918c0b746e5SOllivier Robert } 919c0b746e5SOllivier Robert 920c0b746e5SOllivier Robert /* Just in case we still have lots of rubbish in the buffer... */ 921c0b746e5SOllivier Robert /* ...and to avoid the same timestamp being reused by mistake, */ 922c0b746e5SOllivier Robert /* eg on receipt of the \r coming in on its own after the */ 923c0b746e5SOllivier Robert /* timecode. */ 924c0b746e5SOllivier Robert if(pp->lencode >= LENARC) { 925c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 926c0b746e5SOllivier Robert if(debug && (rbufp->recv_buffer[0] != '\r')) 927c0b746e5SOllivier Robert { printf("arc: rubbish in pp->a_lastcode[].\n"); } 928c0b746e5SOllivier Robert #endif 929c0b746e5SOllivier Robert pp->lencode = 0; 930c0b746e5SOllivier Robert return; 931c0b746e5SOllivier Robert } 932c0b746e5SOllivier Robert 933c0b746e5SOllivier Robert /* Append input to code buffer, avoiding overflow. */ 934c0b746e5SOllivier Robert for(i = 0; i < rbufp->recv_length; i++) { 935c0b746e5SOllivier Robert if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */ 936c0b746e5SOllivier Robert c = rbufp->recv_buffer[i]; 937c0b746e5SOllivier Robert 938c0b746e5SOllivier Robert /* Drop trailing '\r's and drop `h' command echo totally. */ 939c0b746e5SOllivier Robert if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; } 940c0b746e5SOllivier Robert 941c0b746e5SOllivier Robert /* 942c0b746e5SOllivier Robert If we've just put an `o' in the lastcode[0], clear the 943c0b746e5SOllivier Robert timestamp in anticipation of a timecode arriving soon. 944c0b746e5SOllivier Robert 945c0b746e5SOllivier Robert We would expect to get to process this before any of the 946c0b746e5SOllivier Robert timecode arrives. 947c0b746e5SOllivier Robert */ 948c0b746e5SOllivier Robert if((c == 'o') && (pp->lencode == 1)) { 949c0b746e5SOllivier Robert L_CLR(&(up->lastrec)); 950c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 951c0b746e5SOllivier Robert if(debug > 1) { printf("arc: clearing timestamp.\n"); } 952c0b746e5SOllivier Robert #endif 953c0b746e5SOllivier Robert } 954c0b746e5SOllivier Robert } 955c0b746e5SOllivier Robert 956c0b746e5SOllivier Robert /* Handle a quality message. */ 957c0b746e5SOllivier Robert if(pp->a_lastcode[0] == 'g') { 958c0b746e5SOllivier Robert int r, q; 959c0b746e5SOllivier Robert 960c0b746e5SOllivier Robert if(pp->lencode < 3) { return; } /* Need more data... */ 961c0b746e5SOllivier Robert r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */ 962c0b746e5SOllivier Robert q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */ 963c0b746e5SOllivier Robert if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) || 964c0b746e5SOllivier Robert ((r & 0x70) != 0x30)) { 965c0b746e5SOllivier Robert /* Badly formatted response. */ 966c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 967c0b746e5SOllivier Robert if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); } 968c0b746e5SOllivier Robert #endif 969c0b746e5SOllivier Robert return; 970c0b746e5SOllivier Robert } 971c0b746e5SOllivier Robert if(r == '3') { /* Only use quality value whilst sync in progress. */ 972c0b746e5SOllivier Robert up->quality = (q & 0xf); 973c0b746e5SOllivier Robert #ifdef DEBUG 974c0b746e5SOllivier Robert if(debug) { printf("arc: signal quality %d.\n", up->quality); } 975c0b746e5SOllivier Robert #endif 976c0b746e5SOllivier Robert } else if( /* (r == '2') && */ up->resyncing) { 977c0b746e5SOllivier Robert #ifdef DEBUG 978c0b746e5SOllivier Robert if(debug) 979c0b746e5SOllivier Robert { 980c0b746e5SOllivier Robert printf("arc: sync finished, signal quality %d: %s\n", 981c0b746e5SOllivier Robert up->quality, 982c0b746e5SOllivier Robert quality_action(up->quality)); 983c0b746e5SOllivier Robert } 984c0b746e5SOllivier Robert #endif 985c0b746e5SOllivier Robert msyslog(LOG_NOTICE, 986c0b746e5SOllivier Robert "ARCRON: sync finished, signal quality %d: %s", 987c0b746e5SOllivier Robert up->quality, 988c0b746e5SOllivier Robert quality_action(up->quality)); 989c0b746e5SOllivier Robert up->resyncing = 0; /* Resync is over. */ 990c0b746e5SOllivier Robert 991c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 992c0b746e5SOllivier Robert /* Clock quality dubious; resync earlier than usual. */ 993c0b746e5SOllivier Robert if((up->quality == QUALITY_UNKNOWN) || 994c0b746e5SOllivier Robert (up->quality < MIN_CLOCK_QUALITY_OK)) 995c0b746e5SOllivier Robert { up->next_resync = current_time + RETRY_RESYNC_TIME; } 996c0b746e5SOllivier Robert #endif 997c0b746e5SOllivier Robert } 998c0b746e5SOllivier Robert pp->lencode = 0; 999c0b746e5SOllivier Robert return; 1000c0b746e5SOllivier Robert } 1001c0b746e5SOllivier Robert 1002c0b746e5SOllivier Robert /* Stop now if this is not a timecode message. */ 1003c0b746e5SOllivier Robert if(pp->a_lastcode[0] != 'o') { 1004c0b746e5SOllivier Robert pp->lencode = 0; 1005c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1006c0b746e5SOllivier Robert return; 1007c0b746e5SOllivier Robert } 1008c0b746e5SOllivier Robert 1009c0b746e5SOllivier Robert /* If we don't have enough data, wait for more... */ 1010c0b746e5SOllivier Robert if(pp->lencode < LENARC) { return; } 1011c0b746e5SOllivier Robert 1012c0b746e5SOllivier Robert 1013c0b746e5SOllivier Robert /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */ 1014c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 1015c0b746e5SOllivier Robert if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); } 1016c0b746e5SOllivier Robert #endif 1017c0b746e5SOllivier Robert 1018c0b746e5SOllivier Robert /* But check that we actually captured a system timestamp on it. */ 1019c0b746e5SOllivier Robert if(L_ISZERO(&(up->lastrec))) { 1020c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 1021c0b746e5SOllivier Robert if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); } 1022c0b746e5SOllivier Robert #endif 1023c0b746e5SOllivier Robert pp->lencode = 0; 1024c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1025c0b746e5SOllivier Robert return; 1026c0b746e5SOllivier Robert } 1027c0b746e5SOllivier Robert /* 1028c0b746e5SOllivier Robert Append a mark of the clock's received signal quality for the 1029c0b746e5SOllivier Robert benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown' 1030c0b746e5SOllivier Robert quality value to `6' for his s/w) and terminate the string for 1031c0b746e5SOllivier Robert sure. This should not go off the buffer end. 1032c0b746e5SOllivier Robert */ 1033c0b746e5SOllivier Robert pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ? 1034c0b746e5SOllivier Robert '6' : ('0' + up->quality)); 1035c0b746e5SOllivier Robert pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */ 1036c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 1037c0b746e5SOllivier Robert 1038c0b746e5SOllivier Robert /* We don't use the micro-/milli- second part... */ 1039c0b746e5SOllivier Robert pp->usec = 0; 1040c0b746e5SOllivier Robert pp->msec = 0; 1041c0b746e5SOllivier Robert 1042c0b746e5SOllivier Robert n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d", 1043c0b746e5SOllivier Robert &pp->hour, &pp->minute, &pp->second, 1044c0b746e5SOllivier Robert &wday, &pp->day, &month, &pp->year, &bst, &status); 1045c0b746e5SOllivier Robert 1046c0b746e5SOllivier Robert /* Validate format and numbers. */ 1047c0b746e5SOllivier Robert if(n != 9) { 1048c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 1049c0b746e5SOllivier Robert /* Would expect to have caught major problems already... */ 1050c0b746e5SOllivier Robert if(debug) { printf("arc: badly formatted data.\n"); } 1051c0b746e5SOllivier Robert #endif 1052c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1053c0b746e5SOllivier Robert return; 1054c0b746e5SOllivier Robert } 1055c0b746e5SOllivier Robert /* 1056c0b746e5SOllivier Robert Validate received values at least enough to prevent internal 1057c0b746e5SOllivier Robert array-bounds problems, etc. 1058c0b746e5SOllivier Robert */ 1059c0b746e5SOllivier Robert if((pp->hour < 0) || (pp->hour > 23) || 1060c0b746e5SOllivier Robert (pp->minute < 0) || (pp->minute > 59) || 1061c0b746e5SOllivier Robert (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 1062c0b746e5SOllivier Robert (wday < 1) || (wday > 7) || 1063c0b746e5SOllivier Robert (pp->day < 1) || (pp->day > 31) || 1064c0b746e5SOllivier Robert (month < 1) || (month > 12) || 1065c0b746e5SOllivier Robert (pp->year < 0) || (pp->year > 99)) { 1066c0b746e5SOllivier Robert /* Data out of range. */ 1067c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1068c0b746e5SOllivier Robert return; 1069c0b746e5SOllivier Robert } 1070c0b746e5SOllivier Robert /* Check that BST/UTC bits are the complement of one another. */ 1071c0b746e5SOllivier Robert if(!(bst & 2) == !(bst & 4)) { 1072c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1073c0b746e5SOllivier Robert return; 1074c0b746e5SOllivier Robert } 1075c0b746e5SOllivier Robert 1076c0b746e5SOllivier Robert if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); } 1077c0b746e5SOllivier Robert 1078c0b746e5SOllivier Robert /* Year-2000 alert! */ 1079c0b746e5SOllivier Robert /* Attempt to wrap 2-digit date into sensible window. */ 1080c0b746e5SOllivier Robert if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */ 1081c0b746e5SOllivier Robert pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */ 1082c0b746e5SOllivier Robert /* 1083c0b746e5SOllivier Robert Attempt to do the right thing by screaming that the code will 1084c0b746e5SOllivier Robert soon break when we get to the end of its useful life. What a 1085c0b746e5SOllivier Robert hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X! 1086c0b746e5SOllivier Robert */ 1087c0b746e5SOllivier Robert if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */ 1088c0b746e5SOllivier Robert /*This should get attention B^> */ 1089c0b746e5SOllivier Robert msyslog(LOG_NOTICE, 1090c0b746e5SOllivier Robert "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!"); 1091c0b746e5SOllivier Robert } 1092c0b746e5SOllivier Robert #ifdef DEBUG 1093c0b746e5SOllivier Robert if(debug) { 1094c0b746e5SOllivier Robert printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n", 1095c0b746e5SOllivier Robert n, 1096c0b746e5SOllivier Robert pp->hour, pp->minute, pp->second, 1097c0b746e5SOllivier Robert pp->day, month, pp->year, bst, status); 1098c0b746e5SOllivier Robert } 1099c0b746e5SOllivier Robert #endif 1100c0b746e5SOllivier Robert 1101c0b746e5SOllivier Robert /* 1102c0b746e5SOllivier Robert The status value tested for is not strictly supported by the 1103c0b746e5SOllivier Robert clock spec since the value of bit 2 (0x4) is claimed to be 1104c0b746e5SOllivier Robert undefined for MSF, yet does seem to indicate if the last resync 1105c0b746e5SOllivier Robert was successful or not. 1106c0b746e5SOllivier Robert */ 1107c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 1108c0b746e5SOllivier Robert status &= 0x7; 1109c0b746e5SOllivier Robert if(status == 0x3) { 1110c0b746e5SOllivier Robert if(status != up->status) 1111c0b746e5SOllivier Robert { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); } 1112c0b746e5SOllivier Robert } else { 1113c0b746e5SOllivier Robert if(status != up->status) { 1114c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: signal lost"); 1115c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */ 1116c0b746e5SOllivier Robert up->status = status; 1117c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1118c0b746e5SOllivier Robert return; 1119c0b746e5SOllivier Robert } 1120c0b746e5SOllivier Robert } 1121c0b746e5SOllivier Robert up->status = status; 1122c0b746e5SOllivier Robert 1123c0b746e5SOllivier Robert pp->day += moff[month - 1]; 1124c0b746e5SOllivier Robert 1125c0b746e5SOllivier Robert if(isleap_4(pp->year) && month > 2) { pp->day++; } /* Y2KFixes */ 1126c0b746e5SOllivier Robert 1127c0b746e5SOllivier Robert /* Convert to UTC if required */ 1128c0b746e5SOllivier Robert if(bst & 2) { 1129c0b746e5SOllivier Robert pp->hour--; 1130c0b746e5SOllivier Robert if (pp->hour < 0) { 1131c0b746e5SOllivier Robert pp->hour = 23; 1132c0b746e5SOllivier Robert pp->day--; 1133c0b746e5SOllivier Robert /* If we try to wrap round the year (BST on 1st Jan), reject.*/ 1134c0b746e5SOllivier Robert if(pp->day < 0) { 1135c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1136c0b746e5SOllivier Robert return; 1137c0b746e5SOllivier Robert } 1138c0b746e5SOllivier Robert } 1139c0b746e5SOllivier Robert } 1140c0b746e5SOllivier Robert 1141c0b746e5SOllivier Robert /* If clock signal quality is unknown, revert to default PRECISION...*/ 1142c0b746e5SOllivier Robert if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; } 1143c0b746e5SOllivier Robert /* ...else improve precision if flag3 is set... */ 1144c0b746e5SOllivier Robert else { 1145c0b746e5SOllivier Robert peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 1146c0b746e5SOllivier Robert HIGHPRECISION : PRECISION); 1147c0b746e5SOllivier Robert } 1148c0b746e5SOllivier Robert 1149c0b746e5SOllivier Robert /* Notice and log any change (eg from initial defaults) for flags. */ 1150c0b746e5SOllivier Robert if(up->saved_flags != pp->sloppyclockflag) { 1151c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 1152c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s", 1153c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."), 1154c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."), 1155c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."), 1156c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : ".")); 1157c0b746e5SOllivier Robert /* Note effects of flags changing... */ 1158c0b746e5SOllivier Robert if(debug) { 1159c0b746e5SOllivier Robert printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp)); 1160c0b746e5SOllivier Robert printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp)); 1161c0b746e5SOllivier Robert printf("arc: PRECISION = %d.\n", peer->precision); 1162c0b746e5SOllivier Robert } 1163c0b746e5SOllivier Robert #endif 1164c0b746e5SOllivier Robert up->saved_flags = pp->sloppyclockflag; 1165c0b746e5SOllivier Robert } 1166c0b746e5SOllivier Robert 1167c0b746e5SOllivier Robert /* Note time of last believable timestamp. */ 1168c0b746e5SOllivier Robert pp->lastrec = up->lastrec; 1169c0b746e5SOllivier Robert 1170c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 1171c0b746e5SOllivier Robert /* Find out if a leap-second might just have happened... 1172c0b746e5SOllivier Robert (ie is this the first hour of the first day of Jan or Jul?) 1173c0b746e5SOllivier Robert */ 1174c0b746e5SOllivier Robert if((pp->hour == 0) && 1175c0b746e5SOllivier Robert (pp->day == 1) && 1176c0b746e5SOllivier Robert ((month == 1) || (month == 7))) { 1177c0b746e5SOllivier Robert if(possible_leap >= 0) { 1178c0b746e5SOllivier Robert /* A leap may have happened, and no resync has started yet...*/ 1179c0b746e5SOllivier Robert possible_leap = 1; 1180c0b746e5SOllivier Robert } 1181c0b746e5SOllivier Robert } else { 1182c0b746e5SOllivier Robert /* Definitely not leap-second territory... */ 1183c0b746e5SOllivier Robert possible_leap = 0; 1184c0b746e5SOllivier Robert } 1185c0b746e5SOllivier Robert #endif 1186c0b746e5SOllivier Robert 1187c0b746e5SOllivier Robert if (!refclock_process(pp)) { 1188c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1189c0b746e5SOllivier Robert return; 1190c0b746e5SOllivier Robert } 1191c0b746e5SOllivier Robert refclock_receive(peer); 1192c0b746e5SOllivier Robert } 1193c0b746e5SOllivier Robert 1194c0b746e5SOllivier Robert 1195c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */ 1196c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */ 1197c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */ 1198c0b746e5SOllivier Robert static void request_time P((int, struct peer *)); 1199c0b746e5SOllivier Robert static void 1200c0b746e5SOllivier Robert request_time( 1201c0b746e5SOllivier Robert int unit, 1202c0b746e5SOllivier Robert struct peer *peer 1203c0b746e5SOllivier Robert ) 1204c0b746e5SOllivier Robert { 1205c0b746e5SOllivier Robert struct refclockproc *pp = peer->procptr; 1206c0b746e5SOllivier Robert register struct arcunit *up = (struct arcunit *)pp->unitptr; 1207c0b746e5SOllivier Robert #ifdef DEBUG 1208c0b746e5SOllivier Robert if(debug) { printf("arc: unit %d: requesting time.\n", unit); } 1209c0b746e5SOllivier Robert #endif 1210c0b746e5SOllivier Robert if (!send_slow(up, pp->io.fd, "o\r")) { 1211c0b746e5SOllivier Robert #ifdef ARCRON_DEBUG 1212c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit); 1213c0b746e5SOllivier Robert #endif 1214c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1215c0b746e5SOllivier Robert return; 1216c0b746e5SOllivier Robert } 1217c0b746e5SOllivier Robert pp->polls++; 1218c0b746e5SOllivier Robert } 1219c0b746e5SOllivier Robert 1220c0b746e5SOllivier Robert /* 1221c0b746e5SOllivier Robert * arc_poll - called by the transmit procedure 1222c0b746e5SOllivier Robert */ 1223c0b746e5SOllivier Robert static void 1224c0b746e5SOllivier Robert arc_poll( 1225c0b746e5SOllivier Robert int unit, 1226c0b746e5SOllivier Robert struct peer *peer 1227c0b746e5SOllivier Robert ) 1228c0b746e5SOllivier Robert { 1229c0b746e5SOllivier Robert register struct arcunit *up; 1230c0b746e5SOllivier Robert struct refclockproc *pp; 1231c0b746e5SOllivier Robert int resync_needed; /* Should we start a resync? */ 1232c0b746e5SOllivier Robert 1233c0b746e5SOllivier Robert pp = peer->procptr; 1234c0b746e5SOllivier Robert up = (struct arcunit *)pp->unitptr; 1235c0b746e5SOllivier Robert pp->lencode = 0; 1236c0b746e5SOllivier Robert memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); 1237c0b746e5SOllivier Robert 1238c0b746e5SOllivier Robert #if 0 1239c0b746e5SOllivier Robert /* Flush input. */ 1240c0b746e5SOllivier Robert tcflush(pp->io.fd, TCIFLUSH); 1241c0b746e5SOllivier Robert #endif 1242c0b746e5SOllivier Robert 1243c0b746e5SOllivier Robert /* Resync if our next scheduled resync time is here or has passed. */ 1244c0b746e5SOllivier Robert resync_needed = (up->next_resync <= current_time); 1245c0b746e5SOllivier Robert 1246c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 1247c0b746e5SOllivier Robert /* 1248c0b746e5SOllivier Robert Try to catch a potential leap-second insertion or deletion quickly. 1249c0b746e5SOllivier Robert 1250c0b746e5SOllivier Robert In addition to the normal NTP fun of clocks that don't report 1251c0b746e5SOllivier Robert leap-seconds spooking their hosts, this clock does not even 1252c0b746e5SOllivier Robert sample the radio sugnal the whole time, so may miss a 1253c0b746e5SOllivier Robert leap-second insertion or deletion for up to a whole sample 1254c0b746e5SOllivier Robert time. 1255c0b746e5SOllivier Robert 1256c0b746e5SOllivier Robert To try to minimise this effect, if in the first few minutes of 1257c0b746e5SOllivier Robert the day immediately following a leap-second-insertion point 1258c0b746e5SOllivier Robert (ie in the first hour of the first day of the first and sixth 1259c0b746e5SOllivier Robert months), and if the last resync was in the previous day, and a 1260c0b746e5SOllivier Robert resync is not already in progress, resync the clock 1261c0b746e5SOllivier Robert immediately. 1262c0b746e5SOllivier Robert 1263c0b746e5SOllivier Robert */ 1264c0b746e5SOllivier Robert if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */ 1265c0b746e5SOllivier Robert (!up->resyncing)) { /* No resync in progress yet. */ 1266c0b746e5SOllivier Robert resync_needed = 1; 1267c0b746e5SOllivier Robert possible_leap = -1; /* Prevent multiple resyncs. */ 1268c0b746e5SOllivier Robert msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit); 1269c0b746e5SOllivier Robert } 1270c0b746e5SOllivier Robert #endif 1271c0b746e5SOllivier Robert 1272c0b746e5SOllivier Robert /* Do a resync if required... */ 1273c0b746e5SOllivier Robert if(resync_needed) { 1274c0b746e5SOllivier Robert /* First, reset quality value to `unknown' so we can detect */ 1275c0b746e5SOllivier Robert /* when a quality message has been responded to by this */ 1276c0b746e5SOllivier Robert /* being set to some other value. */ 1277c0b746e5SOllivier Robert up->quality = QUALITY_UNKNOWN; 1278c0b746e5SOllivier Robert 1279c0b746e5SOllivier Robert /* Note that we are resyncing... */ 1280c0b746e5SOllivier Robert up->resyncing = 1; 1281c0b746e5SOllivier Robert 1282c0b746e5SOllivier Robert /* Now actually send the resync command and an immediate poll. */ 1283c0b746e5SOllivier Robert #ifdef DEBUG 1284c0b746e5SOllivier Robert if(debug) { printf("arc: sending resync command (h\\r).\n"); } 1285c0b746e5SOllivier Robert #endif 1286c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit); 1287c0b746e5SOllivier Robert send_slow(up, pp->io.fd, "h\r"); 1288c0b746e5SOllivier Robert 1289c0b746e5SOllivier Robert /* Schedule our next resync... */ 1290c0b746e5SOllivier Robert up->next_resync = current_time + DEFAULT_RESYNC_TIME; 1291c0b746e5SOllivier Robert 1292c0b746e5SOllivier Robert /* Drop through to request time if appropriate. */ 1293c0b746e5SOllivier Robert } 1294c0b746e5SOllivier Robert 1295c0b746e5SOllivier Robert /* If clock quality is too poor to trust, indicate a fault. */ 1296c0b746e5SOllivier Robert /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/ 1297c0b746e5SOllivier Robert /* we'll cross our fingers and just hope that the thing */ 1298c0b746e5SOllivier Robert /* synced so quickly we did not catch it---we'll */ 1299c0b746e5SOllivier Robert /* double-check the clock is OK elsewhere. */ 1300c0b746e5SOllivier Robert if( 1301c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 1302c0b746e5SOllivier Robert (up->quality != QUALITY_UNKNOWN) && 1303c0b746e5SOllivier Robert #else 1304c0b746e5SOllivier Robert (up->quality == QUALITY_UNKNOWN) || 1305c0b746e5SOllivier Robert #endif 1306c0b746e5SOllivier Robert (up->quality < MIN_CLOCK_QUALITY_OK)) { 1307c0b746e5SOllivier Robert #ifdef DEBUG 1308c0b746e5SOllivier Robert if(debug) { 1309c0b746e5SOllivier Robert printf("arc: clock quality %d too poor.\n", up->quality); 1310c0b746e5SOllivier Robert } 1311c0b746e5SOllivier Robert #endif 1312c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1313c0b746e5SOllivier Robert return; 1314c0b746e5SOllivier Robert } 1315c0b746e5SOllivier Robert /* This is the normal case: request a timestamp. */ 1316c0b746e5SOllivier Robert request_time(unit, peer); 1317c0b746e5SOllivier Robert } 1318c0b746e5SOllivier Robert 1319c0b746e5SOllivier Robert #else 1320c0b746e5SOllivier Robert int refclock_arc_bs; 1321c0b746e5SOllivier Robert #endif 1322