1c0b746e5SOllivier Robert /* 29c2daa00SOllivier Robert * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers 3c0b746e5SOllivier Robert */ 4c0b746e5SOllivier Robert 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert #include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 92b15cb3dSCy Schubert #include "ntp_types.h" 102b15cb3dSCy Schubert 11c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF) 12ea906c41SOllivier Robert 139c2daa00SOllivier Robert static const char arc_version[] = { "V1.3 2003/02/21" }; 14c0b746e5SOllivier Robert 159c2daa00SOllivier Robert /* define PRE_NTP420 for compatibility to previous versions of NTP (at least 169c2daa00SOllivier Robert to 4.1.0 */ 179c2daa00SOllivier Robert #undef PRE_NTP420 18c0b746e5SOllivier Robert 19c0b746e5SOllivier Robert #ifndef ARCRON_NOT_KEEN 20c0b746e5SOllivier Robert #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */ 21c0b746e5SOllivier Robert #endif 22c0b746e5SOllivier Robert 23c0b746e5SOllivier Robert #ifndef ARCRON_NOT_MULTIPLE_SAMPLES 24c0b746e5SOllivier Robert #define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */ 25c0b746e5SOllivier Robert #endif 26c0b746e5SOllivier Robert 27c0b746e5SOllivier Robert #ifndef ARCRON_NOT_LEAPSECOND_KEEN 28c0b746e5SOllivier Robert #ifndef ARCRON_LEAPSECOND_KEEN 29c0b746e5SOllivier Robert #undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */ 30c0b746e5SOllivier Robert #endif 31c0b746e5SOllivier Robert #endif 32c0b746e5SOllivier Robert 33c0b746e5SOllivier Robert /* 34c0b746e5SOllivier Robert Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997. 35c0b746e5SOllivier Robert Modifications by Damon Hart-Davis, <d@hd.org>, 1997. 369c2daa00SOllivier Robert Modifications by Paul Alfille, <palfille@partners.org>, 2003. 379c2daa00SOllivier Robert Modifications by Christopher Price, <cprice@cs-home.com>, 2003. 38ea906c41SOllivier Robert Modifications by Nigel Roles <nigel@9fs.org>, 2003. 399c2daa00SOllivier Robert 40c0b746e5SOllivier Robert 41c0b746e5SOllivier Robert THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT 42c0b746e5SOllivier Robert YOUR OWN RISK. 43c0b746e5SOllivier Robert 44c0b746e5SOllivier Robert Orginally developed and used with ntp3-5.85 by Derek Mulcahy. 45c0b746e5SOllivier Robert 46c0b746e5SOllivier Robert Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2. 47c0b746e5SOllivier Robert 48c0b746e5SOllivier Robert This code may be freely copied and used and incorporated in other 49c0b746e5SOllivier Robert systems providing the disclaimer and notice of authorship are 50c0b746e5SOllivier Robert reproduced. 51c0b746e5SOllivier Robert 52c0b746e5SOllivier Robert ------------------------------------------------------------------------------- 53c0b746e5SOllivier Robert 54ea906c41SOllivier Robert Nigel's notes: 55ea906c41SOllivier Robert 56ea906c41SOllivier Robert 1) Called tcgetattr() before modifying, so that fields correctly initialised 57ea906c41SOllivier Robert for all operating systems 58ea906c41SOllivier Robert 59ea906c41SOllivier Robert 2) Altered parsing of timestamp line so that it copes with fields which are 60ea906c41SOllivier Robert not always ASCII digits (e.g. status field when battery low) 61ea906c41SOllivier Robert 62ea906c41SOllivier Robert ------------------------------------------------------------------------------- 63ea906c41SOllivier Robert 649c2daa00SOllivier Robert Christopher's notes: 659c2daa00SOllivier Robert 669c2daa00SOllivier Robert MAJOR CHANGES SINCE V1.2 679c2daa00SOllivier Robert ======================== 689c2daa00SOllivier Robert 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk> 699c2daa00SOllivier Robert 2001-02-17 comp.protocols.time.ntp 709c2daa00SOllivier Robert 719c2daa00SOllivier Robert 2) Added WWVB support via clock mode command, localtime/UTC time configured 729c2daa00SOllivier Robert via flag1=(0=UTC, 1=localtime) 739c2daa00SOllivier Robert 749c2daa00SOllivier Robert 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync) 759c2daa00SOllivier Robert 769c2daa00SOllivier Robert 4) Added simplified conversion from localtime to UTC with dst/bst translation 779c2daa00SOllivier Robert 789c2daa00SOllivier Robert 5) Added average signal quality poll 799c2daa00SOllivier Robert 809c2daa00SOllivier Robert 6) Fixed a badformat error when no code is available due to stripping 819c2daa00SOllivier Robert \n & \r's 829c2daa00SOllivier Robert 839c2daa00SOllivier Robert 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll 849c2daa00SOllivier Robert routine 859c2daa00SOllivier Robert 869c2daa00SOllivier Robert 8) Lots of code cleanup, including standardized DEBUG macros and removal 879c2daa00SOllivier Robert of unused code 889c2daa00SOllivier Robert 899c2daa00SOllivier Robert ------------------------------------------------------------------------------- 909c2daa00SOllivier Robert 91c0b746e5SOllivier Robert Author's original note: 92c0b746e5SOllivier Robert 93c0b746e5SOllivier Robert I enclose my ntp driver for the Galleon Systems Arc MSF receiver. 94c0b746e5SOllivier Robert 95c0b746e5SOllivier Robert It works (after a fashion) on both Solaris-1 and Solaris-2. 96c0b746e5SOllivier Robert 97c0b746e5SOllivier Robert I am currently using ntp3-5.85. I have been running the code for 98c0b746e5SOllivier Robert about 7 months without any problems. Even coped with the change to BST! 99c0b746e5SOllivier Robert 100c0b746e5SOllivier Robert I had to do some funky things to read from the clock because it uses the 101c0b746e5SOllivier Robert power from the receive lines to drive the transmit lines. This makes the 102c0b746e5SOllivier Robert code look a bit stupid but it works. I also had to put in some delays to 103c0b746e5SOllivier Robert allow for the turnaround time from receive to transmit. These delays 104c0b746e5SOllivier Robert are between characters when requesting a time stamp so that shouldn't affect 105c0b746e5SOllivier Robert the results too drastically. 106c0b746e5SOllivier Robert 107c0b746e5SOllivier Robert ... 108c0b746e5SOllivier Robert 109c0b746e5SOllivier Robert The bottom line is that it works but could easily be improved. You are 110c0b746e5SOllivier Robert free to do what you will with the code. I haven't been able to determine 111c0b746e5SOllivier Robert how good the clock is. I think that this requires a known good clock 112c0b746e5SOllivier Robert to compare it against. 113c0b746e5SOllivier Robert 114c0b746e5SOllivier Robert ------------------------------------------------------------------------------- 115c0b746e5SOllivier Robert 116c0b746e5SOllivier Robert Damon's notes for adjustments: 117c0b746e5SOllivier Robert 118c0b746e5SOllivier Robert MAJOR CHANGES SINCE V1.0 119c0b746e5SOllivier Robert ======================== 120c0b746e5SOllivier Robert 1) Removal of pollcnt variable that made the clock go permanently 121c0b746e5SOllivier Robert off-line once two time polls failed to gain responses. 122c0b746e5SOllivier Robert 123c0b746e5SOllivier Robert 2) Avoiding (at least on Solaris-2) terminal becoming the controlling 124c0b746e5SOllivier Robert terminal of the process when we do a low-level open(). 125c0b746e5SOllivier Robert 126c0b746e5SOllivier Robert 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being 127c0b746e5SOllivier Robert defined) to try to resync quickly after a potential leap-second 128c0b746e5SOllivier Robert insertion or deletion. 129c0b746e5SOllivier Robert 130c0b746e5SOllivier Robert 4) Code significantly slimmer at run-time than V1.0. 131c0b746e5SOllivier Robert 132c0b746e5SOllivier Robert 133c0b746e5SOllivier Robert GENERAL 134c0b746e5SOllivier Robert ======= 135c0b746e5SOllivier Robert 136c0b746e5SOllivier Robert 1) The C preprocessor symbol to have the clock built has been changed 137c0b746e5SOllivier Robert from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the 138c0b746e5SOllivier Robert possiblity of clashes with other symbols in the future. 139c0b746e5SOllivier Robert 140c0b746e5SOllivier Robert 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons: 141c0b746e5SOllivier Robert 142c0b746e5SOllivier Robert a) The ARC documentation claims the internal clock is (only) 143c0b746e5SOllivier Robert accurate to about 20ms relative to Rugby (plus there must be 144c0b746e5SOllivier Robert noticable drift and delay in the ms range due to transmission 145c0b746e5SOllivier Robert delays and changing atmospheric effects). This clock is not 146c0b746e5SOllivier Robert designed for ms accuracy as NTP has spoilt us all to expect. 147c0b746e5SOllivier Robert 148c0b746e5SOllivier Robert b) The clock oscillator looks like a simple uncompensated quartz 149c0b746e5SOllivier Robert crystal of the sort used in digital watches (ie 32768Hz) which 150c0b746e5SOllivier Robert can have large temperature coefficients and drifts; it is not 151c0b746e5SOllivier Robert clear if this oscillator is properly disciplined to the MSF 152c0b746e5SOllivier Robert transmission, but as the default is to resync only once per 153c0b746e5SOllivier Robert *day*, we can imagine that it is not, and is free-running. We 154c0b746e5SOllivier Robert can minimise drift by resyncing more often (at the cost of 155c0b746e5SOllivier Robert reduced battery life), but drift/wander may still be 156c0b746e5SOllivier Robert significant. 157c0b746e5SOllivier Robert 158c0b746e5SOllivier Robert c) Note that the bit time of 3.3ms adds to the potential error in 159c0b746e5SOllivier Robert the the clock timestamp, since the bit clock of the serial link 160c0b746e5SOllivier Robert may effectively be free-running with respect to the host clock 161c0b746e5SOllivier Robert and the MSF clock. Actually, the error is probably 1/16th of 162c0b746e5SOllivier Robert the above, since the input data is probably sampled at at least 163c0b746e5SOllivier Robert 16x the bit rate. 164c0b746e5SOllivier Robert 165c0b746e5SOllivier Robert By keeping the clock marked as not very precise, it will have a 166c0b746e5SOllivier Robert fairly large dispersion, and thus will tend to be used as a 167c0b746e5SOllivier Robert `backup' time source and sanity checker, which this clock is 168c0b746e5SOllivier Robert probably ideal for. For an isolated network without other time 169c0b746e5SOllivier Robert sources, this clock can probably be expected to provide *much* 170c0b746e5SOllivier Robert better than 1s accuracy, which will be fine. 171c0b746e5SOllivier Robert 172c0b746e5SOllivier Robert By default, PRECISION is set to -4, but experience, especially at a 173c0b746e5SOllivier Robert particular geographic location with a particular clock, may allow 174c0b746e5SOllivier Robert this to be altered to -5. (Note that skews of +/- 10ms are to be 175c0b746e5SOllivier Robert expected from the clock from time-to-time.) This improvement of 176c0b746e5SOllivier Robert reported precision can be instigated by setting flag3 to 1, though 177c0b746e5SOllivier Robert the PRECISION will revert to the normal value while the clock 178c0b746e5SOllivier Robert signal quality is unknown whatever the flag3 setting. 179c0b746e5SOllivier Robert 180c0b746e5SOllivier Robert IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE 181c0b746e5SOllivier Robert ANY RESIDUAL SKEW, eg: 182c0b746e5SOllivier Robert 183c0b746e5SOllivier Robert server 127.127.27.0 # ARCRON MSF radio clock unit 0. 184c0b746e5SOllivier Robert # Fudge timestamps by about 20ms. 185c0b746e5SOllivier Robert fudge 127.127.27.0 time1 0.020 186c0b746e5SOllivier Robert 187c0b746e5SOllivier Robert You will need to observe your system's behaviour, assuming you have 188c0b746e5SOllivier Robert some other NTP source to compare it with, to work out what the 189c0b746e5SOllivier Robert fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with 190c0b746e5SOllivier Robert my MSF clock with my distance from the MSF transmitter, +20ms 191c0b746e5SOllivier Robert seemed about right, after some observation. 192c0b746e5SOllivier Robert 193c0b746e5SOllivier Robert 3) REFID has been made "MSFa" to reflect the MSF time source and the 194c0b746e5SOllivier Robert ARCRON receiver. 195c0b746e5SOllivier Robert 196c0b746e5SOllivier Robert 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before 197c0b746e5SOllivier Robert forcing a resync since the last attempt. This is picked to give a 198c0b746e5SOllivier Robert little less than an hour between resyncs and to try to avoid 199c0b746e5SOllivier Robert clashing with any regular event at a regular time-past-the-hour 200c0b746e5SOllivier Robert which might cause systematic errors. 201c0b746e5SOllivier Robert 202c0b746e5SOllivier Robert The INITIAL_RESYNC_DELAY is to avoid bothering the clock and 203c0b746e5SOllivier Robert running down its batteries unnecesarily if ntpd is going to crash 204c0b746e5SOllivier Robert or be killed or reconfigured quickly. If ARCRON_KEEN is defined 205c0b746e5SOllivier Robert then this period is long enough for (with normal polling rates) 206c0b746e5SOllivier Robert enough time samples to have been taken to allow ntpd to sync to 207c0b746e5SOllivier Robert the clock before the interruption for the clock to resync to MSF. 208c0b746e5SOllivier Robert This avoids ntpd syncing to another peer first and then 209c0b746e5SOllivier Robert almost immediately hopping to the MSF clock. 210c0b746e5SOllivier Robert 211c0b746e5SOllivier Robert The RETRY_RESYNC_TIME is used before rescheduling a resync after a 212c0b746e5SOllivier Robert resync failed to reveal a statisfatory signal quality (too low or 213c0b746e5SOllivier Robert unknown). 214c0b746e5SOllivier Robert 215c0b746e5SOllivier Robert 5) The clock seems quite jittery, so I have increased the 216c0b746e5SOllivier Robert median-filter size from the typical (previous) value of 3. I 217c0b746e5SOllivier Robert discard up to half the results in the filter. It looks like maybe 218c0b746e5SOllivier Robert 1 sample in 10 or so (maybe less) is a spike, so allow the median 219c0b746e5SOllivier Robert filter to discard at least 10% of its entries or 1 entry, whichever 220c0b746e5SOllivier Robert is greater. 221c0b746e5SOllivier Robert 222c0b746e5SOllivier Robert 6) Sleeping *before* each character sent to the unit to allow required 223c0b746e5SOllivier Robert inter-character time but without introducting jitter and delay in 224c0b746e5SOllivier Robert handling the response if possible. 225c0b746e5SOllivier Robert 226c0b746e5SOllivier Robert 7) If the flag ARCRON_KEEN is defined, take time samples whenever 227c0b746e5SOllivier Robert possible, even while resyncing, etc. We rely, in this case, on the 228c0b746e5SOllivier Robert clock always giving us a reasonable time or else telling us in the 229c0b746e5SOllivier Robert status byte at the end of the timestamp that it failed to sync to 230c0b746e5SOllivier Robert MSF---thus we should never end up syncing to completely the wrong 231c0b746e5SOllivier Robert time. 232c0b746e5SOllivier Robert 233c0b746e5SOllivier Robert 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of 234c0b746e5SOllivier Robert refclock median-filter routines to get round small bug in 3-5.90 235c0b746e5SOllivier Robert code which does not return the median offset. XXX Removed this 236c0b746e5SOllivier Robert bit due NTP Version 4 upgrade - dlm. 237c0b746e5SOllivier Robert 238c0b746e5SOllivier Robert 9) We would appear to have a year-2000 problem with this clock since 239c0b746e5SOllivier Robert it returns only the two least-significant digits of the year. But 240c0b746e5SOllivier Robert ntpd ignores the year and uses the local-system year instead, so 241c0b746e5SOllivier Robert this is in fact not a problem. Nevertheless, we attempt to do a 242c0b746e5SOllivier Robert sensible thing with the dates, wrapping them into a 100-year 243c0b746e5SOllivier Robert window. 244c0b746e5SOllivier Robert 245c0b746e5SOllivier Robert 10)Logs stats information that can be used by Derek's Tcl/Tk utility 246c0b746e5SOllivier Robert to show the status of the clock. 247c0b746e5SOllivier Robert 248c0b746e5SOllivier Robert 11)The clock documentation insists that the number of bits per 249c0b746e5SOllivier Robert character to be sent to the clock, and sent by it, is 11, including 250c0b746e5SOllivier Robert one start bit and two stop bits. The data format is either 7+even 251c0b746e5SOllivier Robert or 8+none. 252c0b746e5SOllivier Robert 253c0b746e5SOllivier Robert 254c0b746e5SOllivier Robert TO-DO LIST 255c0b746e5SOllivier Robert ========== 256c0b746e5SOllivier Robert 257c0b746e5SOllivier Robert * Eliminate use of scanf(), and maybe sprintf(). 258c0b746e5SOllivier Robert 259c0b746e5SOllivier Robert * Allow user setting of resync interval to trade battery life for 260c0b746e5SOllivier Robert accuracy; maybe could be done via fudge factor or unit number. 261c0b746e5SOllivier Robert 262c0b746e5SOllivier Robert * Possibly note the time since the last resync of the MSF clock to 263c0b746e5SOllivier Robert MSF as the age of the last reference timestamp, ie trust the 264c0b746e5SOllivier Robert clock's oscillator not very much... 265c0b746e5SOllivier Robert 266c0b746e5SOllivier Robert * Add very slow auto-adjustment up to a value of +/- time2 to correct 267c0b746e5SOllivier Robert for long-term errors in the clock value (time2 defaults to 0 so the 268c0b746e5SOllivier Robert correction would be disabled by default). 269c0b746e5SOllivier Robert 270c0b746e5SOllivier Robert * Consider trying to use the tty_clk/ppsclock support. 271c0b746e5SOllivier Robert 272c0b746e5SOllivier Robert * Possibly use average or maximum signal quality reported during 273c0b746e5SOllivier Robert resync, rather than just the last one, which may be atypical. 274c0b746e5SOllivier Robert 275c0b746e5SOllivier Robert */ 276c0b746e5SOllivier Robert 277c0b746e5SOllivier Robert 278c0b746e5SOllivier Robert /* Notes for HKW Elektronik GmBH Radio clock driver */ 279c0b746e5SOllivier Robert /* Author Lyndon David, Sentinet Ltd, Feb 1997 */ 280c0b746e5SOllivier Robert /* These notes seem also to apply usefully to the ARCRON clock. */ 281c0b746e5SOllivier Robert 282c0b746e5SOllivier Robert /* The HKW clock module is a radio receiver tuned into the Rugby */ 283c0b746e5SOllivier Robert /* MSF time signal tranmitted on 60 kHz. The clock module connects */ 284c0b746e5SOllivier Robert /* to the computer via a serial line and transmits the time encoded */ 285c0b746e5SOllivier Robert /* in 15 bytes at 300 baud 7 bits two stop bits even parity */ 286c0b746e5SOllivier Robert 287c0b746e5SOllivier Robert /* Clock communications, from the datasheet */ 288c0b746e5SOllivier Robert /* All characters sent to the clock are echoed back to the controlling */ 289c0b746e5SOllivier Robert /* device. */ 290c0b746e5SOllivier Robert /* Transmit time/date information */ 291c0b746e5SOllivier Robert /* syntax ASCII o<cr> */ 292c0b746e5SOllivier Robert /* Character o may be replaced if neccesary by a character whose code */ 293c0b746e5SOllivier Robert /* contains the lowest four bits f(hex) eg */ 294c0b746e5SOllivier Robert /* syntax binary: xxxx1111 00001101 */ 295c0b746e5SOllivier Robert 296c0b746e5SOllivier Robert /* DHD note: 297c0b746e5SOllivier Robert You have to wait for character echo + 10ms before sending next character. 298c0b746e5SOllivier Robert */ 299c0b746e5SOllivier Robert 300c0b746e5SOllivier Robert /* The clock replies to this command with a sequence of 15 characters */ 301c0b746e5SOllivier Robert /* which contain the complete time and a final <cr> making 16 characters */ 302c0b746e5SOllivier Robert /* in total. */ 303c0b746e5SOllivier Robert /* The RC computer clock will not reply immediately to this command because */ 304c0b746e5SOllivier Robert /* the start bit edge of the first reply character marks the beginning of */ 305c0b746e5SOllivier Robert /* the second. So the RC Computer Clock will reply to this command at the */ 306c0b746e5SOllivier Robert /* start of the next second */ 307c0b746e5SOllivier Robert /* The characters have the following meaning */ 308c0b746e5SOllivier Robert /* 1. hours tens */ 309c0b746e5SOllivier Robert /* 2. hours units */ 310c0b746e5SOllivier Robert /* 3. minutes tens */ 311c0b746e5SOllivier Robert /* 4. minutes units */ 312c0b746e5SOllivier Robert /* 5. seconds tens */ 313c0b746e5SOllivier Robert /* 6. seconds units */ 314c0b746e5SOllivier Robert /* 7. day of week 1-monday 7-sunday */ 315c0b746e5SOllivier Robert /* 8. day of month tens */ 316c0b746e5SOllivier Robert /* 9. day of month units */ 317c0b746e5SOllivier Robert /* 10. month tens */ 318c0b746e5SOllivier Robert /* 11. month units */ 319c0b746e5SOllivier Robert /* 12. year tens */ 320c0b746e5SOllivier Robert /* 13. year units */ 321c0b746e5SOllivier Robert /* 14. BST/UTC status */ 322c0b746e5SOllivier Robert /* bit 7 parity */ 323c0b746e5SOllivier Robert /* bit 6 always 0 */ 324c0b746e5SOllivier Robert /* bit 5 always 1 */ 325c0b746e5SOllivier Robert /* bit 4 always 1 */ 326c0b746e5SOllivier Robert /* bit 3 always 0 */ 327c0b746e5SOllivier Robert /* bit 2 =1 if UTC is in effect, complementary to the BST bit */ 328c0b746e5SOllivier Robert /* bit 1 =1 if BST is in effect, according to the BST bit */ 329c0b746e5SOllivier Robert /* bit 0 BST/UTC change impending bit=1 in case of change impending */ 330c0b746e5SOllivier Robert /* 15. status */ 331c0b746e5SOllivier Robert /* bit 7 parity */ 332c0b746e5SOllivier Robert /* bit 6 always 0 */ 333c0b746e5SOllivier Robert /* bit 5 always 1 */ 334c0b746e5SOllivier Robert /* bit 4 always 1 */ 335c0b746e5SOllivier Robert /* bit 3 =1 if low battery is detected */ 336c0b746e5SOllivier Robert /* bit 2 =1 if the very last reception attempt failed and a valid */ 337c0b746e5SOllivier Robert /* time information already exists (bit0=1) */ 338c0b746e5SOllivier Robert /* =0 if the last reception attempt was successful */ 339c0b746e5SOllivier Robert /* bit 1 =1 if at least one reception since 2:30 am was successful */ 340c0b746e5SOllivier Robert /* =0 if no reception attempt since 2:30 am was successful */ 341c0b746e5SOllivier Robert /* bit 0 =1 if the RC Computer Clock contains valid time information */ 342c0b746e5SOllivier Robert /* This bit is zero after reset and one after the first */ 343c0b746e5SOllivier Robert /* successful reception attempt */ 344c0b746e5SOllivier Robert 345c0b746e5SOllivier Robert /* DHD note: 346c0b746e5SOllivier Robert Also note g<cr> command which confirms that a resync is in progress, and 347c0b746e5SOllivier Robert if so what signal quality (0--5) is available. 348c0b746e5SOllivier Robert Also note h<cr> command which starts a resync to MSF signal. 349c0b746e5SOllivier Robert */ 350c0b746e5SOllivier Robert 351c0b746e5SOllivier Robert 352224ba2bdSOllivier Robert #include "ntpd.h" 353224ba2bdSOllivier Robert #include "ntp_io.h" 354224ba2bdSOllivier Robert #include "ntp_refclock.h" 3559c2daa00SOllivier Robert #include "ntp_calendar.h" 356224ba2bdSOllivier Robert #include "ntp_stdlib.h" 357c0b746e5SOllivier Robert 358c0b746e5SOllivier Robert #include <stdio.h> 359c0b746e5SOllivier Robert #include <ctype.h> 360c0b746e5SOllivier Robert 361c0b746e5SOllivier Robert #if defined(HAVE_BSD_TTYS) 362c0b746e5SOllivier Robert #include <sgtty.h> 363c0b746e5SOllivier Robert #endif /* HAVE_BSD_TTYS */ 364c0b746e5SOllivier Robert 365c0b746e5SOllivier Robert #if defined(HAVE_SYSV_TTYS) 366c0b746e5SOllivier Robert #include <termio.h> 367c0b746e5SOllivier Robert #endif /* HAVE_SYSV_TTYS */ 368c0b746e5SOllivier Robert 369c0b746e5SOllivier Robert #if defined(HAVE_TERMIOS) 370c0b746e5SOllivier Robert #include <termios.h> 371c0b746e5SOllivier Robert #endif 372c0b746e5SOllivier Robert 373c0b746e5SOllivier Robert /* 3749c2daa00SOllivier Robert * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock 375c0b746e5SOllivier Robert */ 376c0b746e5SOllivier Robert 377c0b746e5SOllivier Robert /* 378c0b746e5SOllivier Robert * Interface definitions 379c0b746e5SOllivier Robert */ 380c0b746e5SOllivier Robert #define DEVICE "/dev/arc%d" /* Device name and unit. */ 381c0b746e5SOllivier Robert #define SPEED B300 /* UART speed (300 baud) */ 382c0b746e5SOllivier Robert #define PRECISION (-4) /* Precision (~63 ms). */ 383c0b746e5SOllivier Robert #define HIGHPRECISION (-5) /* If things are going well... */ 384c0b746e5SOllivier Robert #define REFID "MSFa" /* Reference ID. */ 3859c2daa00SOllivier Robert #define REFID_MSF "MSF" /* Reference ID. */ 3869c2daa00SOllivier Robert #define REFID_DCF77 "DCF" /* Reference ID. */ 3879c2daa00SOllivier Robert #define REFID_WWVB "WWVB" /* Reference ID. */ 3889c2daa00SOllivier Robert #define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver" 389c0b746e5SOllivier Robert 3909c2daa00SOllivier Robert #ifdef PRE_NTP420 3919c2daa00SOllivier Robert #define MODE ttlmax 3929c2daa00SOllivier Robert #else 3939c2daa00SOllivier Robert #define MODE ttl 3949c2daa00SOllivier Robert #endif 395c0b746e5SOllivier Robert 396c0b746e5SOllivier Robert #define LENARC 16 /* Format `o' timecode length. */ 397c0b746e5SOllivier Robert 398c0b746e5SOllivier Robert #define BITSPERCHAR 11 /* Bits per character. */ 399c0b746e5SOllivier Robert #define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */ 400c0b746e5SOllivier Robert #define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */ 401c0b746e5SOllivier Robert #define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */ 402c0b746e5SOllivier Robert #define CHARTIME /* Time for char at 300bps. */ \ 403c0b746e5SOllivier Robert ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \ 404c0b746e5SOllivier Robert (BITSPERCHAR * BITTIME) ) ) 405c0b746e5SOllivier Robert 406c0b746e5SOllivier Robert /* Allow for UART to accept char half-way through final stop bit. */ 4072b15cb3dSCy Schubert #define INITIALOFFSET ((u_int32)(-BITTIME/2)) 408c0b746e5SOllivier Robert 409c0b746e5SOllivier Robert /* 410c0b746e5SOllivier Robert charoffsets[x] is the time after the start of the second that byte 411c0b746e5SOllivier Robert x (with the first byte being byte 1) is received by the UART, 412c0b746e5SOllivier Robert assuming that the initial edge of the start bit of the first byte 413c0b746e5SOllivier Robert is on-time. The values are represented as the fractional part of 414c0b746e5SOllivier Robert an l_fp. 415c0b746e5SOllivier Robert 416c0b746e5SOllivier Robert We store enough values to have the offset of each byte including 417c0b746e5SOllivier Robert the trailing \r, on the assumption that the bytes follow one 418c0b746e5SOllivier Robert another without gaps. 419c0b746e5SOllivier Robert */ 420c0b746e5SOllivier Robert static const u_int32 charoffsets[LENARC+1] = { 421c0b746e5SOllivier Robert #if BITSPERCHAR == 11 /* Usual case. */ 422c0b746e5SOllivier Robert /* Offsets computed as accurately as possible... */ 423c0b746e5SOllivier Robert 0, 424c0b746e5SOllivier Robert INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */ 425c0b746e5SOllivier Robert INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */ 426c0b746e5SOllivier Robert INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */ 427c0b746e5SOllivier Robert INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */ 428c0b746e5SOllivier Robert INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */ 429c0b746e5SOllivier Robert INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */ 430c0b746e5SOllivier Robert INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */ 431c0b746e5SOllivier Robert INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */ 432c0b746e5SOllivier Robert INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */ 433c0b746e5SOllivier Robert INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */ 434c0b746e5SOllivier Robert INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */ 435c0b746e5SOllivier Robert INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */ 436c0b746e5SOllivier Robert INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */ 437c0b746e5SOllivier Robert INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */ 438c0b746e5SOllivier Robert INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */ 439c0b746e5SOllivier Robert INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */ 440c0b746e5SOllivier Robert #else 441c0b746e5SOllivier Robert /* Offsets computed with a small rounding error... */ 442c0b746e5SOllivier Robert 0, 443c0b746e5SOllivier Robert INITIALOFFSET + 1 * CHARTIME, 444c0b746e5SOllivier Robert INITIALOFFSET + 2 * CHARTIME, 445c0b746e5SOllivier Robert INITIALOFFSET + 3 * CHARTIME, 446c0b746e5SOllivier Robert INITIALOFFSET + 4 * CHARTIME, 447c0b746e5SOllivier Robert INITIALOFFSET + 5 * CHARTIME, 448c0b746e5SOllivier Robert INITIALOFFSET + 6 * CHARTIME, 449c0b746e5SOllivier Robert INITIALOFFSET + 7 * CHARTIME, 450c0b746e5SOllivier Robert INITIALOFFSET + 8 * CHARTIME, 451c0b746e5SOllivier Robert INITIALOFFSET + 9 * CHARTIME, 452c0b746e5SOllivier Robert INITIALOFFSET + 10 * CHARTIME, 453c0b746e5SOllivier Robert INITIALOFFSET + 11 * CHARTIME, 454c0b746e5SOllivier Robert INITIALOFFSET + 12 * CHARTIME, 455c0b746e5SOllivier Robert INITIALOFFSET + 13 * CHARTIME, 456c0b746e5SOllivier Robert INITIALOFFSET + 14 * CHARTIME, 457c0b746e5SOllivier Robert INITIALOFFSET + 15 * CHARTIME, 458c0b746e5SOllivier Robert INITIALOFFSET + 16 * CHARTIME 459c0b746e5SOllivier Robert #endif 460c0b746e5SOllivier Robert }; 461c0b746e5SOllivier Robert 462c0b746e5SOllivier Robert #define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */ 463c0b746e5SOllivier Robert #define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */ 464c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 465c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */ 466c0b746e5SOllivier Robert #else 467c0b746e5SOllivier Robert #define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */ 468c0b746e5SOllivier Robert #endif 469c0b746e5SOllivier Robert 470c0b746e5SOllivier Robert static const int moff[12] = 471c0b746e5SOllivier Robert { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 472c0b746e5SOllivier Robert /* Flags for a raw open() of the clock serial device. */ 473c0b746e5SOllivier Robert #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */ 474c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR | O_NOCTTY) 475c0b746e5SOllivier Robert #else /* Oh well, it may not matter... */ 476c0b746e5SOllivier Robert #define OPEN_FLAGS (O_RDWR) 477c0b746e5SOllivier Robert #endif 478c0b746e5SOllivier Robert 479c0b746e5SOllivier Robert 480c0b746e5SOllivier Robert /* Length of queue of command bytes to be sent. */ 481c0b746e5SOllivier Robert #define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */ 482c0b746e5SOllivier Robert /* Queue tick time; interval in seconds between chars taken off queue. */ 483c0b746e5SOllivier Robert /* Must be >= 2 to allow o\r response to come back uninterrupted. */ 484c0b746e5SOllivier Robert #define QUEUETICK 2 /* Allow o\r reply to finish. */ 485c0b746e5SOllivier Robert 486c0b746e5SOllivier Robert /* 487c0b746e5SOllivier Robert * ARC unit control structure 488c0b746e5SOllivier Robert */ 489c0b746e5SOllivier Robert struct arcunit { 490c0b746e5SOllivier Robert l_fp lastrec; /* Time tag for the receive time (system). */ 491c0b746e5SOllivier Robert int status; /* Clock status. */ 492c0b746e5SOllivier Robert 493c0b746e5SOllivier Robert int quality; /* Quality of reception 0--5 for unit. */ 494c0b746e5SOllivier Robert /* We may also use the values -1 or 6 internally. */ 4959c2daa00SOllivier Robert u_long quality_stamp; /* Next time to reset quality average. */ 496c0b746e5SOllivier Robert 497c0b746e5SOllivier Robert u_long next_resync; /* Next resync time (s) compared to current_time. */ 498c0b746e5SOllivier Robert int resyncing; /* Resync in progress if true. */ 499c0b746e5SOllivier Robert 500c0b746e5SOllivier Robert /* In the outgoing queue, cmdqueue[0] is next to be sent. */ 501c0b746e5SOllivier Robert char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */ 502c0b746e5SOllivier Robert 503c0b746e5SOllivier Robert u_long saved_flags; /* Saved fudge flags. */ 504c0b746e5SOllivier Robert }; 5059c2daa00SOllivier Robert 506c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 507c0b746e5SOllivier Robert /* The flag `possible_leap' is set non-zero when any MSF unit 508c0b746e5SOllivier Robert thinks a leap-second may have happened. 509c0b746e5SOllivier Robert 510c0b746e5SOllivier Robert Set whenever we receive a valid time sample in the first hour of 511c0b746e5SOllivier Robert the first day of the first/seventh months. 512c0b746e5SOllivier Robert 513c0b746e5SOllivier Robert Outside the special hour this value is unconditionally set 514c0b746e5SOllivier Robert to zero by the receive routine. 515c0b746e5SOllivier Robert 516c0b746e5SOllivier Robert On finding itself in this timeslot, as long as the value is 517c0b746e5SOllivier Robert non-negative, the receive routine sets it to a positive value to 518c0b746e5SOllivier Robert indicate a resync to MSF should be performed. 519c0b746e5SOllivier Robert 520c0b746e5SOllivier Robert In the poll routine, if this value is positive and we are not 521c0b746e5SOllivier Robert already resyncing (eg from a sync that started just before 522c0b746e5SOllivier Robert midnight), start resyncing and set this value negative to 523c0b746e5SOllivier Robert indicate that a leap-triggered resync has been started. Having 524c0b746e5SOllivier Robert set this negative prevents the receive routine setting it 525c0b746e5SOllivier Robert positive and thus prevents multiple resyncs during the witching 526c0b746e5SOllivier Robert hour. 527c0b746e5SOllivier Robert */ 528c0b746e5SOllivier Robert static int possible_leap = 0; /* No resync required by default. */ 529c0b746e5SOllivier Robert #endif 530c0b746e5SOllivier Robert 531c0b746e5SOllivier Robert #if 0 5322b15cb3dSCy Schubert static void dummy_event_handler (struct peer *); 5332b15cb3dSCy Schubert static void arc_event_handler (struct peer *); 534c0b746e5SOllivier Robert #endif /* 0 */ 535c0b746e5SOllivier Robert 536c0b746e5SOllivier Robert #define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */ 537c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */ 538c0b746e5SOllivier Robert #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */ 539c0b746e5SOllivier Robert #define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */ 540c0b746e5SOllivier Robert 541c0b746e5SOllivier Robert /* 542c0b746e5SOllivier Robert * Function prototypes 543c0b746e5SOllivier Robert */ 5442b15cb3dSCy Schubert static int arc_start (int, struct peer *); 5452b15cb3dSCy Schubert static void arc_shutdown (int, struct peer *); 5462b15cb3dSCy Schubert static void arc_receive (struct recvbuf *); 5472b15cb3dSCy Schubert static void arc_poll (int, struct peer *); 548c0b746e5SOllivier Robert 549c0b746e5SOllivier Robert /* 550c0b746e5SOllivier Robert * Transfer vector 551c0b746e5SOllivier Robert */ 552c0b746e5SOllivier Robert struct refclock refclock_arc = { 553c0b746e5SOllivier Robert arc_start, /* start up driver */ 554c0b746e5SOllivier Robert arc_shutdown, /* shut down driver */ 555c0b746e5SOllivier Robert arc_poll, /* transmit poll message */ 556c0b746e5SOllivier Robert noentry, /* not used (old arc_control) */ 557c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 558c0b746e5SOllivier Robert noentry, /* not used (old arc_buginfo) */ 559c0b746e5SOllivier Robert NOFLAGS /* not used */ 560c0b746e5SOllivier Robert }; 561c0b746e5SOllivier Robert 562c0b746e5SOllivier Robert /* Queue us up for the next tick. */ 563c0b746e5SOllivier Robert #define ENQUEUE(up) \ 564c0b746e5SOllivier Robert do { \ 5652b15cb3dSCy Schubert peer->procptr->nextaction = current_time + QUEUETICK; \ 566c0b746e5SOllivier Robert } while(0) 567c0b746e5SOllivier Robert 5689c2daa00SOllivier Robert /* Placeholder event handler---does nothing safely---soaks up loose tick. */ 569c0b746e5SOllivier Robert static void 570c0b746e5SOllivier Robert dummy_event_handler( 571c0b746e5SOllivier Robert struct peer *peer 572c0b746e5SOllivier Robert ) 573c0b746e5SOllivier Robert { 5749c2daa00SOllivier Robert #ifdef DEBUG 575c0b746e5SOllivier Robert if(debug) { printf("arc: dummy_event_handler() called.\n"); } 576c0b746e5SOllivier Robert #endif 577c0b746e5SOllivier Robert } 578c0b746e5SOllivier Robert 579c0b746e5SOllivier Robert /* 580c0b746e5SOllivier Robert Normal event handler. 581c0b746e5SOllivier Robert 582c0b746e5SOllivier Robert Take first character off queue and send to clock if not a null. 583c0b746e5SOllivier Robert 584c0b746e5SOllivier Robert Shift characters down and put a null on the end. 585c0b746e5SOllivier Robert 586c0b746e5SOllivier Robert We assume that there is no parallelism so no race condition, but even 587c0b746e5SOllivier Robert if there is nothing bad will happen except that we might send some bad 588c0b746e5SOllivier Robert data to the clock once in a while. 589c0b746e5SOllivier Robert */ 590c0b746e5SOllivier Robert static void 591c0b746e5SOllivier Robert arc_event_handler( 592c0b746e5SOllivier Robert struct peer *peer 593c0b746e5SOllivier Robert ) 594c0b746e5SOllivier Robert { 595c0b746e5SOllivier Robert struct refclockproc *pp = peer->procptr; 5962b15cb3dSCy Schubert register struct arcunit *up = pp->unitptr; 597c0b746e5SOllivier Robert int i; 598c0b746e5SOllivier Robert char c; 5999c2daa00SOllivier Robert #ifdef DEBUG 600c0b746e5SOllivier Robert if(debug > 2) { printf("arc: arc_event_handler() called.\n"); } 601c0b746e5SOllivier Robert #endif 602c0b746e5SOllivier Robert 603c0b746e5SOllivier Robert c = up->cmdqueue[0]; /* Next char to be sent. */ 604c0b746e5SOllivier Robert /* Shift down characters, shifting trailing \0 in at end. */ 605c0b746e5SOllivier Robert for(i = 0; i < CMDQUEUELEN; ++i) 606c0b746e5SOllivier Robert { up->cmdqueue[i] = up->cmdqueue[i+1]; } 607c0b746e5SOllivier Robert 608c0b746e5SOllivier Robert /* Don't send '\0' characters. */ 609c0b746e5SOllivier Robert if(c != '\0') { 610c0b746e5SOllivier Robert if(write(pp->io.fd, &c, 1) != 1) { 611c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd); 612c0b746e5SOllivier Robert } 6139c2daa00SOllivier Robert #ifdef DEBUG 614c0b746e5SOllivier Robert else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } 615c0b746e5SOllivier Robert #endif 616c0b746e5SOllivier Robert } 6179c2daa00SOllivier Robert 6189c2daa00SOllivier Robert ENQUEUE(up); 619c0b746e5SOllivier Robert } 620c0b746e5SOllivier Robert 621c0b746e5SOllivier Robert /* 622c0b746e5SOllivier Robert * arc_start - open the devices and initialize data for processing 623c0b746e5SOllivier Robert */ 624c0b746e5SOllivier Robert static int 625c0b746e5SOllivier Robert arc_start( 626c0b746e5SOllivier Robert int unit, 627c0b746e5SOllivier Robert struct peer *peer 628c0b746e5SOllivier Robert ) 629c0b746e5SOllivier Robert { 630c0b746e5SOllivier Robert register struct arcunit *up; 631c0b746e5SOllivier Robert struct refclockproc *pp; 6322b15cb3dSCy Schubert int temp_fd; 633c0b746e5SOllivier Robert int fd; 634c0b746e5SOllivier Robert char device[20]; 635c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS 636c0b746e5SOllivier Robert struct termios arg; 637c0b746e5SOllivier Robert #endif 638c0b746e5SOllivier Robert 6392b15cb3dSCy Schubert msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d", 6402b15cb3dSCy Schubert arc_version, unit); 6412b15cb3dSCy Schubert DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version, 6422b15cb3dSCy Schubert unit)); 643c0b746e5SOllivier Robert 644c0b746e5SOllivier Robert /* 645c0b746e5SOllivier Robert * Open serial port. Use CLK line discipline, if available. 646c0b746e5SOllivier Robert */ 6472b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 648a466cc55SCy Schubert temp_fd = refclock_open(&peer->srcadr, device, SPEED, LDISC_CLK); 6492b15cb3dSCy Schubert if (temp_fd <= 0) 6502b15cb3dSCy Schubert return 0; 6512b15cb3dSCy Schubert DPRINTF(1, ("arc: unit %d using tty_open().\n", unit)); 6522b15cb3dSCy Schubert fd = tty_open(device, OPEN_FLAGS, 0777); 653c0b746e5SOllivier Robert if (fd < 0) { 6542b15cb3dSCy Schubert msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.", 6552b15cb3dSCy Schubert unit, device); 6562b15cb3dSCy Schubert close(temp_fd); 6572b15cb3dSCy Schubert return 0; 658c0b746e5SOllivier Robert } 6592b15cb3dSCy Schubert close(temp_fd); 6609034852cSGleb Smirnoff temp_fd = -1; /* not used after this, at *this* time. */ 661c0b746e5SOllivier Robert 6622b15cb3dSCy Schubert #ifndef SYS_WINNT 6632b15cb3dSCy Schubert if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */ 6642b15cb3dSCy Schubert msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.", 6652b15cb3dSCy Schubert unit); 6662b15cb3dSCy Schubert 667c0b746e5SOllivier Robert #endif 6682b15cb3dSCy Schubert DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd)); 669c0b746e5SOllivier Robert 670c0b746e5SOllivier Robert #ifdef HAVE_TERMIOS 671c0b746e5SOllivier Robert 6722b15cb3dSCy Schubert if (tcgetattr(fd, &arg) < 0) { 6732b15cb3dSCy Schubert msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.", 6742b15cb3dSCy Schubert unit, device); 6752b15cb3dSCy Schubert close(fd); 6762b15cb3dSCy Schubert return 0; 6772b15cb3dSCy Schubert } 678ea906c41SOllivier Robert 679c0b746e5SOllivier Robert arg.c_iflag = IGNBRK | ISTRIP; 680c0b746e5SOllivier Robert arg.c_oflag = 0; 681c0b746e5SOllivier Robert arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB; 682c0b746e5SOllivier Robert arg.c_lflag = 0; 683c0b746e5SOllivier Robert arg.c_cc[VMIN] = 1; 684c0b746e5SOllivier Robert arg.c_cc[VTIME] = 0; 685c0b746e5SOllivier Robert 6862b15cb3dSCy Schubert if (tcsetattr(fd, TCSANOW, &arg) < 0) { 6872b15cb3dSCy Schubert msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.", 6882b15cb3dSCy Schubert unit, device); 6892b15cb3dSCy Schubert close(fd); 6902b15cb3dSCy Schubert return 0; 6912b15cb3dSCy Schubert } 692c0b746e5SOllivier Robert 693c0b746e5SOllivier Robert #else 694c0b746e5SOllivier Robert 6952b15cb3dSCy Schubert msyslog(LOG_ERR, "ARCRON: termios required by this driver"); 696c0b746e5SOllivier Robert (void)close(fd); 697c0b746e5SOllivier Robert 698c0b746e5SOllivier Robert return 0; 699c0b746e5SOllivier Robert 700c0b746e5SOllivier Robert #endif 701c0b746e5SOllivier Robert 702c0b746e5SOllivier Robert /* Set structure to all zeros... */ 7032b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 704c0b746e5SOllivier Robert pp = peer->procptr; 705c0b746e5SOllivier Robert pp->io.clock_recv = arc_receive; 7062b15cb3dSCy Schubert pp->io.srcclock = peer; 707c0b746e5SOllivier Robert pp->io.datalen = 0; 708c0b746e5SOllivier Robert pp->io.fd = fd; 7092b15cb3dSCy Schubert if (!io_addclock(&pp->io)) { 7102b15cb3dSCy Schubert close(fd); 7112b15cb3dSCy Schubert pp->io.fd = -1; 7122b15cb3dSCy Schubert free(up); 7132b15cb3dSCy Schubert return(0); 7142b15cb3dSCy Schubert } 7152b15cb3dSCy Schubert pp->unitptr = up; 716c0b746e5SOllivier Robert 717c0b746e5SOllivier Robert /* 718c0b746e5SOllivier Robert * Initialize miscellaneous variables 719c0b746e5SOllivier Robert */ 720c0b746e5SOllivier Robert peer->precision = PRECISION; 721c0b746e5SOllivier Robert peer->stratum = 2; /* Default to stratum 2 not 0. */ 722c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 7239c2daa00SOllivier Robert if (peer->MODE > 3) { 7249c2daa00SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE); 7259c2daa00SOllivier Robert return 0; 7269c2daa00SOllivier Robert } 7279c2daa00SOllivier Robert #ifdef DEBUG 7289c2daa00SOllivier Robert if(debug) { printf("arc: mode = %d.\n", peer->MODE); } 7299c2daa00SOllivier Robert #endif 7309c2daa00SOllivier Robert switch (peer->MODE) { 7319c2daa00SOllivier Robert case 1: 7329c2daa00SOllivier Robert memcpy((char *)&pp->refid, REFID_MSF, 4); 7339c2daa00SOllivier Robert break; 7349c2daa00SOllivier Robert case 2: 7359c2daa00SOllivier Robert memcpy((char *)&pp->refid, REFID_DCF77, 4); 7369c2daa00SOllivier Robert break; 7379c2daa00SOllivier Robert case 3: 7389c2daa00SOllivier Robert memcpy((char *)&pp->refid, REFID_WWVB, 4); 7399c2daa00SOllivier Robert break; 7409c2daa00SOllivier Robert default: 741c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 7429c2daa00SOllivier Robert break; 7439c2daa00SOllivier Robert } 744c0b746e5SOllivier Robert /* Spread out resyncs so that they should remain separated. */ 745c0b746e5SOllivier Robert up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009; 746c0b746e5SOllivier Robert 747c0b746e5SOllivier Robert #if 0 /* Not needed because of zeroing of arcunit structure... */ 748c0b746e5SOllivier Robert up->resyncing = 0; /* Not resyncing yet. */ 749c0b746e5SOllivier Robert up->saved_flags = 0; /* Default is all flags off. */ 750c0b746e5SOllivier Robert /* Clear send buffer out... */ 751c0b746e5SOllivier Robert { 752c0b746e5SOllivier Robert int i; 753c0b746e5SOllivier Robert for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; } 754c0b746e5SOllivier Robert } 755c0b746e5SOllivier Robert #endif 756c0b746e5SOllivier Robert 757c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 758c0b746e5SOllivier Robert up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */ 759c0b746e5SOllivier Robert #else 760c0b746e5SOllivier Robert up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */ 761c0b746e5SOllivier Robert #endif 7629c2daa00SOllivier Robert 7632b15cb3dSCy Schubert peer->procptr->action = arc_event_handler; 7649c2daa00SOllivier Robert 7659c2daa00SOllivier Robert ENQUEUE(up); 7669c2daa00SOllivier Robert 767c0b746e5SOllivier Robert return(1); 768c0b746e5SOllivier Robert } 769c0b746e5SOllivier Robert 770c0b746e5SOllivier Robert 771c0b746e5SOllivier Robert /* 772c0b746e5SOllivier Robert * arc_shutdown - shut down the clock 773c0b746e5SOllivier Robert */ 774c0b746e5SOllivier Robert static void 775c0b746e5SOllivier Robert arc_shutdown( 776c0b746e5SOllivier Robert int unit, 777c0b746e5SOllivier Robert struct peer *peer 778c0b746e5SOllivier Robert ) 779c0b746e5SOllivier Robert { 780c0b746e5SOllivier Robert register struct arcunit *up; 781c0b746e5SOllivier Robert struct refclockproc *pp; 782c0b746e5SOllivier Robert 7832b15cb3dSCy Schubert peer->procptr->action = dummy_event_handler; 7849c2daa00SOllivier Robert 785c0b746e5SOllivier Robert pp = peer->procptr; 7862b15cb3dSCy Schubert up = pp->unitptr; 7872b15cb3dSCy Schubert if (-1 != pp->io.fd) 788c0b746e5SOllivier Robert io_closeclock(&pp->io); 7892b15cb3dSCy Schubert if (NULL != up) 790c0b746e5SOllivier Robert free(up); 791c0b746e5SOllivier Robert } 792c0b746e5SOllivier Robert 793c0b746e5SOllivier Robert /* 794c0b746e5SOllivier Robert Compute space left in output buffer. 795c0b746e5SOllivier Robert */ 796c0b746e5SOllivier Robert static int 797c0b746e5SOllivier Robert space_left( 798c0b746e5SOllivier Robert register struct arcunit *up 799c0b746e5SOllivier Robert ) 800c0b746e5SOllivier Robert { 801c0b746e5SOllivier Robert int spaceleft; 802c0b746e5SOllivier Robert 803c0b746e5SOllivier Robert /* Compute space left in buffer after any pending output. */ 804c0b746e5SOllivier Robert for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft) 805c0b746e5SOllivier Robert { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } } 806c0b746e5SOllivier Robert return(spaceleft); 807c0b746e5SOllivier Robert } 808c0b746e5SOllivier Robert 809c0b746e5SOllivier Robert /* 810c0b746e5SOllivier Robert Send command by copying into command buffer as far forward as possible, 811c0b746e5SOllivier Robert after any pending output. 812c0b746e5SOllivier Robert 813c0b746e5SOllivier Robert Indicate an error by returning 0 if there is not space for the command. 814c0b746e5SOllivier Robert */ 815c0b746e5SOllivier Robert static int 816c0b746e5SOllivier Robert send_slow( 817c0b746e5SOllivier Robert register struct arcunit *up, 818c0b746e5SOllivier Robert int fd, 819c0b746e5SOllivier Robert const char *s 820c0b746e5SOllivier Robert ) 821c0b746e5SOllivier Robert { 822c0b746e5SOllivier Robert int sl = strlen(s); 823c0b746e5SOllivier Robert int spaceleft = space_left(up); 824c0b746e5SOllivier Robert 8259c2daa00SOllivier Robert #ifdef DEBUG 826c0b746e5SOllivier Robert if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } 827c0b746e5SOllivier Robert #endif 828c0b746e5SOllivier Robert if(spaceleft < sl) { /* Should not normally happen... */ 8299c2daa00SOllivier Robert #ifdef DEBUG 830c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)", 831c0b746e5SOllivier Robert sl, spaceleft); 832c0b746e5SOllivier Robert #endif 833c0b746e5SOllivier Robert return(0); /* FAILED! */ 834c0b746e5SOllivier Robert } 835c0b746e5SOllivier Robert 836c0b746e5SOllivier Robert /* Copy in the command to be sent. */ 837ea906c41SOllivier Robert while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; } 838c0b746e5SOllivier Robert 839c0b746e5SOllivier Robert return(1); 840c0b746e5SOllivier Robert } 841c0b746e5SOllivier Robert 842c0b746e5SOllivier Robert 843ea906c41SOllivier Robert static int 844ea906c41SOllivier Robert get2(char *p, int *val) 845ea906c41SOllivier Robert { 8462b15cb3dSCy Schubert if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0; 847ea906c41SOllivier Robert *val = (p[0] - '0') * 10 + p[1] - '0'; 848ea906c41SOllivier Robert return 1; 849ea906c41SOllivier Robert } 850ea906c41SOllivier Robert 851ea906c41SOllivier Robert static int 852ea906c41SOllivier Robert get1(char *p, int *val) 853ea906c41SOllivier Robert { 8542b15cb3dSCy Schubert if (!isdigit((unsigned char)p[0])) return 0; 855ea906c41SOllivier Robert *val = p[0] - '0'; 856ea906c41SOllivier Robert return 1; 857ea906c41SOllivier Robert } 858ea906c41SOllivier Robert 859c0b746e5SOllivier Robert /* Macro indicating action we will take for different quality values. */ 860c0b746e5SOllivier Robert #define quality_action(q) \ 861c0b746e5SOllivier Robert (((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \ 862c0b746e5SOllivier Robert (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \ 863c0b746e5SOllivier Robert "OK, will use clock")) 864c0b746e5SOllivier Robert 865c0b746e5SOllivier Robert /* 866c0b746e5SOllivier Robert * arc_receive - receive data from the serial interface 867c0b746e5SOllivier Robert */ 868c0b746e5SOllivier Robert static void 869c0b746e5SOllivier Robert arc_receive( 870c0b746e5SOllivier Robert struct recvbuf *rbufp 871c0b746e5SOllivier Robert ) 872c0b746e5SOllivier Robert { 873*f5f40dd6SCy Schubert static int quality_average = 0; 874*f5f40dd6SCy Schubert static int quality_sum = 0; 875*f5f40dd6SCy Schubert static int quality_polls = 0; 876c0b746e5SOllivier Robert register struct arcunit *up; 877c0b746e5SOllivier Robert struct refclockproc *pp; 878c0b746e5SOllivier Robert struct peer *peer; 879c0b746e5SOllivier Robert char c; 880*f5f40dd6SCy Schubert int i, wday, month, flags, status; 881c0b746e5SOllivier Robert int arc_last_offset; 882*f5f40dd6SCy Schubert #ifdef DEBUG 883*f5f40dd6SCy Schubert int n; 884*f5f40dd6SCy Schubert #endif 885c0b746e5SOllivier Robert 886c0b746e5SOllivier Robert /* 887c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp 888c0b746e5SOllivier Robert */ 8892b15cb3dSCy Schubert peer = rbufp->recv_peer; 890c0b746e5SOllivier Robert pp = peer->procptr; 8912b15cb3dSCy Schubert up = pp->unitptr; 892c0b746e5SOllivier Robert 893c0b746e5SOllivier Robert 894c0b746e5SOllivier Robert /* 895c0b746e5SOllivier Robert If the command buffer is empty, and we are resyncing, insert a 896c0b746e5SOllivier Robert g\r quality request into it to poll for signal quality again. 897c0b746e5SOllivier Robert */ 898c0b746e5SOllivier Robert if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) { 899c0b746e5SOllivier Robert #ifdef DEBUG 900c0b746e5SOllivier Robert if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); } 901c0b746e5SOllivier Robert #endif 902c0b746e5SOllivier Robert send_slow(up, pp->io.fd, "g\r"); 903c0b746e5SOllivier Robert } 904c0b746e5SOllivier Robert 905c0b746e5SOllivier Robert /* 906c0b746e5SOllivier Robert The `arc_last_offset' is the offset in lastcode[] of the last byte 907c0b746e5SOllivier Robert received, and which we assume actually received the input 908c0b746e5SOllivier Robert timestamp. 909c0b746e5SOllivier Robert 910c0b746e5SOllivier Robert (When we get round to using tty_clk and it is available, we 911c0b746e5SOllivier Robert assume that we will receive the whole timecode with the 912c0b746e5SOllivier Robert trailing \r, and that that \r will be timestamped. But this 913c0b746e5SOllivier Robert assumption also works if receive the characters one-by-one.) 914c0b746e5SOllivier Robert */ 915c0b746e5SOllivier Robert arc_last_offset = pp->lencode+rbufp->recv_length - 1; 916c0b746e5SOllivier Robert 917c0b746e5SOllivier Robert /* 918c0b746e5SOllivier Robert We catch a timestamp iff: 919c0b746e5SOllivier Robert 920c0b746e5SOllivier Robert * The command code is `o' for a timestamp. 921c0b746e5SOllivier Robert 922c0b746e5SOllivier Robert * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have 923c0b746e5SOllivier Robert exactly char in the buffer (the command code) so that we 924c0b746e5SOllivier Robert only sample the first character of the timecode as our 925c0b746e5SOllivier Robert `on-time' character. 926c0b746e5SOllivier Robert 927c0b746e5SOllivier Robert * The first character in the buffer is not the echoed `\r' 928c0b746e5SOllivier Robert from the `o` command (so if we are to timestamp an `\r' it 929c0b746e5SOllivier Robert must not be first in the receive buffer with lencode==1. 930c0b746e5SOllivier Robert (Even if we had other characters following it, we probably 931c0b746e5SOllivier Robert would have a premature timestamp on the '\r'.) 932c0b746e5SOllivier Robert 933c0b746e5SOllivier Robert * We have received at least one character (I cannot imagine 934c0b746e5SOllivier Robert how it could be otherwise, but anyway...). 935c0b746e5SOllivier Robert */ 936c0b746e5SOllivier Robert c = rbufp->recv_buffer[0]; 937c0b746e5SOllivier Robert if((pp->a_lastcode[0] == 'o') && 938c0b746e5SOllivier Robert #ifndef ARCRON_MULTIPLE_SAMPLES 939c0b746e5SOllivier Robert (pp->lencode == 1) && 940c0b746e5SOllivier Robert #endif 941c0b746e5SOllivier Robert ((pp->lencode != 1) || (c != '\r')) && 942c0b746e5SOllivier Robert (arc_last_offset >= 1)) { 943c0b746e5SOllivier Robert /* Note that the timestamp should be corrected if >1 char rcvd. */ 944c0b746e5SOllivier Robert l_fp timestamp; 945c0b746e5SOllivier Robert timestamp = rbufp->recv_time; 946c0b746e5SOllivier Robert #ifdef DEBUG 947c0b746e5SOllivier Robert if(debug) { /* Show \r as `R', other non-printing char as `?'. */ 948c0b746e5SOllivier Robert printf("arc: stamp -->%c<-- (%d chars rcvd)\n", 9492b15cb3dSCy Schubert ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')), 950c0b746e5SOllivier Robert rbufp->recv_length); 951c0b746e5SOllivier Robert } 952c0b746e5SOllivier Robert #endif 953c0b746e5SOllivier Robert 954c0b746e5SOllivier Robert /* 955c0b746e5SOllivier Robert Now correct timestamp by offset of last byte received---we 956c0b746e5SOllivier Robert subtract from the receive time the delay implied by the 957c0b746e5SOllivier Robert extra characters received. 958c0b746e5SOllivier Robert 959c0b746e5SOllivier Robert Reject the input if the resulting code is too long, but 960c0b746e5SOllivier Robert allow for the trailing \r, normally not used but a good 961c0b746e5SOllivier Robert handle for tty_clk or somesuch kernel timestamper. 962c0b746e5SOllivier Robert */ 963c0b746e5SOllivier Robert if(arc_last_offset > LENARC) { 9649c2daa00SOllivier Robert #ifdef DEBUG 965c0b746e5SOllivier Robert if(debug) { 966c0b746e5SOllivier Robert printf("arc: input code too long (%d cf %d); rejected.\n", 967c0b746e5SOllivier Robert arc_last_offset, LENARC); 968c0b746e5SOllivier Robert } 969c0b746e5SOllivier Robert #endif 970c0b746e5SOllivier Robert pp->lencode = 0; 971c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 972c0b746e5SOllivier Robert return; 973c0b746e5SOllivier Robert } 974c0b746e5SOllivier Robert 975c0b746e5SOllivier Robert L_SUBUF(×tamp, charoffsets[arc_last_offset]); 9769c2daa00SOllivier Robert #ifdef DEBUG 977c0b746e5SOllivier Robert if(debug > 1) { 978c0b746e5SOllivier Robert printf( 979c0b746e5SOllivier Robert "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n", 980c0b746e5SOllivier Robert ((rbufp->recv_length > 1) ? "*** " : ""), 981c0b746e5SOllivier Robert rbufp->recv_length, 982c0b746e5SOllivier Robert arc_last_offset, 983c0b746e5SOllivier Robert mfptoms((unsigned long)0, 984c0b746e5SOllivier Robert charoffsets[arc_last_offset], 985c0b746e5SOllivier Robert 1)); 986c0b746e5SOllivier Robert } 987c0b746e5SOllivier Robert #endif 988c0b746e5SOllivier Robert 989c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES 990c0b746e5SOllivier Robert /* 991c0b746e5SOllivier Robert If taking multiple samples, capture the current adjusted 992c0b746e5SOllivier Robert sample iff: 993c0b746e5SOllivier Robert 994c0b746e5SOllivier Robert * No timestamp has yet been captured (it is zero), OR 995c0b746e5SOllivier Robert 996c0b746e5SOllivier Robert * This adjusted timestamp is earlier than the one already 997c0b746e5SOllivier Robert captured, on the grounds that this one suffered less 998c0b746e5SOllivier Robert delay in being delivered to us and is more accurate. 999c0b746e5SOllivier Robert 1000c0b746e5SOllivier Robert */ 1001c0b746e5SOllivier Robert if(L_ISZERO(&(up->lastrec)) || 1002c0b746e5SOllivier Robert L_ISGEQ(&(up->lastrec), ×tamp)) 1003c0b746e5SOllivier Robert #endif 1004c0b746e5SOllivier Robert { 10059c2daa00SOllivier Robert #ifdef DEBUG 1006c0b746e5SOllivier Robert if(debug > 1) { 1007c0b746e5SOllivier Robert printf("arc: system timestamp captured.\n"); 1008c0b746e5SOllivier Robert #ifdef ARCRON_MULTIPLE_SAMPLES 1009c0b746e5SOllivier Robert if(!L_ISZERO(&(up->lastrec))) { 1010c0b746e5SOllivier Robert l_fp diff; 1011c0b746e5SOllivier Robert diff = up->lastrec; 1012c0b746e5SOllivier Robert L_SUB(&diff, ×tamp); 1013c0b746e5SOllivier Robert printf("arc: adjusted timestamp by -%sms.\n", 10142b15cb3dSCy Schubert mfptoms(diff.l_ui, diff.l_uf, 3)); 1015c0b746e5SOllivier Robert } 1016c0b746e5SOllivier Robert #endif 1017c0b746e5SOllivier Robert } 1018c0b746e5SOllivier Robert #endif 1019c0b746e5SOllivier Robert up->lastrec = timestamp; 1020c0b746e5SOllivier Robert } 1021c0b746e5SOllivier Robert 1022c0b746e5SOllivier Robert } 1023c0b746e5SOllivier Robert 1024c0b746e5SOllivier Robert /* Just in case we still have lots of rubbish in the buffer... */ 1025c0b746e5SOllivier Robert /* ...and to avoid the same timestamp being reused by mistake, */ 1026c0b746e5SOllivier Robert /* eg on receipt of the \r coming in on its own after the */ 1027c0b746e5SOllivier Robert /* timecode. */ 1028c0b746e5SOllivier Robert if(pp->lencode >= LENARC) { 10299c2daa00SOllivier Robert #ifdef DEBUG 1030c0b746e5SOllivier Robert if(debug && (rbufp->recv_buffer[0] != '\r')) 1031c0b746e5SOllivier Robert { printf("arc: rubbish in pp->a_lastcode[].\n"); } 1032c0b746e5SOllivier Robert #endif 1033c0b746e5SOllivier Robert pp->lencode = 0; 1034c0b746e5SOllivier Robert return; 1035c0b746e5SOllivier Robert } 1036c0b746e5SOllivier Robert 1037c0b746e5SOllivier Robert /* Append input to code buffer, avoiding overflow. */ 1038c0b746e5SOllivier Robert for(i = 0; i < rbufp->recv_length; i++) { 1039c0b746e5SOllivier Robert if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */ 1040c0b746e5SOllivier Robert c = rbufp->recv_buffer[i]; 1041c0b746e5SOllivier Robert 1042c0b746e5SOllivier Robert /* Drop trailing '\r's and drop `h' command echo totally. */ 1043c0b746e5SOllivier Robert if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; } 1044c0b746e5SOllivier Robert 1045c0b746e5SOllivier Robert /* 1046c0b746e5SOllivier Robert If we've just put an `o' in the lastcode[0], clear the 1047c0b746e5SOllivier Robert timestamp in anticipation of a timecode arriving soon. 1048c0b746e5SOllivier Robert 1049c0b746e5SOllivier Robert We would expect to get to process this before any of the 1050c0b746e5SOllivier Robert timecode arrives. 1051c0b746e5SOllivier Robert */ 1052c0b746e5SOllivier Robert if((c == 'o') && (pp->lencode == 1)) { 1053c0b746e5SOllivier Robert L_CLR(&(up->lastrec)); 10549c2daa00SOllivier Robert #ifdef DEBUG 1055c0b746e5SOllivier Robert if(debug > 1) { printf("arc: clearing timestamp.\n"); } 1056c0b746e5SOllivier Robert #endif 1057c0b746e5SOllivier Robert } 1058c0b746e5SOllivier Robert } 10599c2daa00SOllivier Robert if (pp->lencode == 0) return; 1060c0b746e5SOllivier Robert 1061c0b746e5SOllivier Robert /* Handle a quality message. */ 1062c0b746e5SOllivier Robert if(pp->a_lastcode[0] == 'g') { 1063c0b746e5SOllivier Robert int r, q; 1064c0b746e5SOllivier Robert 1065c0b746e5SOllivier Robert if(pp->lencode < 3) { return; } /* Need more data... */ 1066c0b746e5SOllivier Robert r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */ 1067c0b746e5SOllivier Robert q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */ 1068c0b746e5SOllivier Robert if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) || 1069c0b746e5SOllivier Robert ((r & 0x70) != 0x30)) { 1070c0b746e5SOllivier Robert /* Badly formatted response. */ 10719c2daa00SOllivier Robert #ifdef DEBUG 1072c0b746e5SOllivier Robert if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); } 1073c0b746e5SOllivier Robert #endif 1074c0b746e5SOllivier Robert return; 1075c0b746e5SOllivier Robert } 1076c0b746e5SOllivier Robert if(r == '3') { /* Only use quality value whilst sync in progress. */ 10779c2daa00SOllivier Robert if (up->quality_stamp < current_time) { 10789c2daa00SOllivier Robert struct calendar cal; 10799c2daa00SOllivier Robert l_fp new_stamp; 10809c2daa00SOllivier Robert 10819c2daa00SOllivier Robert get_systime (&new_stamp); 10829c2daa00SOllivier Robert caljulian (new_stamp.l_ui, &cal); 10839c2daa00SOllivier Robert up->quality_stamp = 10849c2daa00SOllivier Robert current_time + 60 - cal.second + 5; 10859c2daa00SOllivier Robert quality_sum = 0; 10869c2daa00SOllivier Robert quality_polls = 0; 10879c2daa00SOllivier Robert } 10889c2daa00SOllivier Robert quality_sum += (q & 0xf); 10899c2daa00SOllivier Robert quality_polls++; 10909c2daa00SOllivier Robert quality_average = (quality_sum / quality_polls); 1091c0b746e5SOllivier Robert #ifdef DEBUG 10929c2daa00SOllivier Robert if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); } 1093c0b746e5SOllivier Robert #endif 1094c0b746e5SOllivier Robert } else if( /* (r == '2') && */ up->resyncing) { 10959c2daa00SOllivier Robert up->quality = quality_average; 1096c0b746e5SOllivier Robert #ifdef DEBUG 1097c0b746e5SOllivier Robert if(debug) 1098c0b746e5SOllivier Robert { 1099c0b746e5SOllivier Robert printf("arc: sync finished, signal quality %d: %s\n", 1100c0b746e5SOllivier Robert up->quality, 1101c0b746e5SOllivier Robert quality_action(up->quality)); 1102c0b746e5SOllivier Robert } 1103c0b746e5SOllivier Robert #endif 1104c0b746e5SOllivier Robert msyslog(LOG_NOTICE, 1105c0b746e5SOllivier Robert "ARCRON: sync finished, signal quality %d: %s", 1106c0b746e5SOllivier Robert up->quality, 1107c0b746e5SOllivier Robert quality_action(up->quality)); 1108c0b746e5SOllivier Robert up->resyncing = 0; /* Resync is over. */ 11099c2daa00SOllivier Robert quality_average = 0; 11109c2daa00SOllivier Robert quality_sum = 0; 11119c2daa00SOllivier Robert quality_polls = 0; 1112c0b746e5SOllivier Robert 1113c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 1114c0b746e5SOllivier Robert /* Clock quality dubious; resync earlier than usual. */ 1115c0b746e5SOllivier Robert if((up->quality == QUALITY_UNKNOWN) || 1116c0b746e5SOllivier Robert (up->quality < MIN_CLOCK_QUALITY_OK)) 1117c0b746e5SOllivier Robert { up->next_resync = current_time + RETRY_RESYNC_TIME; } 1118c0b746e5SOllivier Robert #endif 1119c0b746e5SOllivier Robert } 1120c0b746e5SOllivier Robert pp->lencode = 0; 1121c0b746e5SOllivier Robert return; 1122c0b746e5SOllivier Robert } 1123c0b746e5SOllivier Robert 1124c0b746e5SOllivier Robert /* Stop now if this is not a timecode message. */ 1125c0b746e5SOllivier Robert if(pp->a_lastcode[0] != 'o') { 1126c0b746e5SOllivier Robert pp->lencode = 0; 1127c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1128c0b746e5SOllivier Robert return; 1129c0b746e5SOllivier Robert } 1130c0b746e5SOllivier Robert 1131c0b746e5SOllivier Robert /* If we don't have enough data, wait for more... */ 1132c0b746e5SOllivier Robert if(pp->lencode < LENARC) { return; } 1133c0b746e5SOllivier Robert 1134c0b746e5SOllivier Robert 1135c0b746e5SOllivier Robert /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */ 11369c2daa00SOllivier Robert #ifdef DEBUG 1137c0b746e5SOllivier Robert if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); } 1138c0b746e5SOllivier Robert #endif 1139c0b746e5SOllivier Robert 1140c0b746e5SOllivier Robert /* But check that we actually captured a system timestamp on it. */ 1141c0b746e5SOllivier Robert if(L_ISZERO(&(up->lastrec))) { 11429c2daa00SOllivier Robert #ifdef DEBUG 1143c0b746e5SOllivier Robert if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); } 1144c0b746e5SOllivier Robert #endif 1145c0b746e5SOllivier Robert pp->lencode = 0; 1146c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1147c0b746e5SOllivier Robert return; 1148c0b746e5SOllivier Robert } 1149c0b746e5SOllivier Robert /* 1150c0b746e5SOllivier Robert Append a mark of the clock's received signal quality for the 1151c0b746e5SOllivier Robert benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown' 1152c0b746e5SOllivier Robert quality value to `6' for his s/w) and terminate the string for 1153c0b746e5SOllivier Robert sure. This should not go off the buffer end. 1154c0b746e5SOllivier Robert */ 1155c0b746e5SOllivier Robert pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ? 1156c0b746e5SOllivier Robert '6' : ('0' + up->quality)); 1157c0b746e5SOllivier Robert pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */ 1158c0b746e5SOllivier Robert 11599c2daa00SOllivier Robert #ifdef PRE_NTP420 1160c0b746e5SOllivier Robert /* We don't use the micro-/milli- second part... */ 1161c0b746e5SOllivier Robert pp->usec = 0; 1162c0b746e5SOllivier Robert pp->msec = 0; 11639c2daa00SOllivier Robert #else 11649c2daa00SOllivier Robert /* We don't use the nano-second part... */ 11659c2daa00SOllivier Robert pp->nsec = 0; 11669c2daa00SOllivier Robert #endif 1167c0b746e5SOllivier Robert /* Validate format and numbers. */ 1168ea906c41SOllivier Robert if (pp->a_lastcode[0] != 'o' 1169ea906c41SOllivier Robert || !get2(pp->a_lastcode + 1, &pp->hour) 1170ea906c41SOllivier Robert || !get2(pp->a_lastcode + 3, &pp->minute) 1171ea906c41SOllivier Robert || !get2(pp->a_lastcode + 5, &pp->second) 1172ea906c41SOllivier Robert || !get1(pp->a_lastcode + 7, &wday) 1173ea906c41SOllivier Robert || !get2(pp->a_lastcode + 8, &pp->day) 1174ea906c41SOllivier Robert || !get2(pp->a_lastcode + 10, &month) 1175ea906c41SOllivier Robert || !get2(pp->a_lastcode + 12, &pp->year)) { 11769c2daa00SOllivier Robert #ifdef DEBUG 1177c0b746e5SOllivier Robert /* Would expect to have caught major problems already... */ 1178c0b746e5SOllivier Robert if(debug) { printf("arc: badly formatted data.\n"); } 1179c0b746e5SOllivier Robert #endif 11809c2daa00SOllivier Robert pp->lencode = 0; 1181c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1182c0b746e5SOllivier Robert return; 1183c0b746e5SOllivier Robert } 1184ea906c41SOllivier Robert flags = pp->a_lastcode[14]; 1185ea906c41SOllivier Robert status = pp->a_lastcode[15]; 1186ea906c41SOllivier Robert #ifdef DEBUG 1187ea906c41SOllivier Robert if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); } 1188ea906c41SOllivier Robert n = 9; 1189*f5f40dd6SCy Schubert #endif 1190ea906c41SOllivier Robert 1191c0b746e5SOllivier Robert /* 1192c0b746e5SOllivier Robert Validate received values at least enough to prevent internal 1193c0b746e5SOllivier Robert array-bounds problems, etc. 1194c0b746e5SOllivier Robert */ 1195c0b746e5SOllivier Robert if((pp->hour < 0) || (pp->hour > 23) || 1196c0b746e5SOllivier Robert (pp->minute < 0) || (pp->minute > 59) || 1197c0b746e5SOllivier Robert (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 1198c0b746e5SOllivier Robert (wday < 1) || (wday > 7) || 1199c0b746e5SOllivier Robert (pp->day < 1) || (pp->day > 31) || 1200c0b746e5SOllivier Robert (month < 1) || (month > 12) || 1201c0b746e5SOllivier Robert (pp->year < 0) || (pp->year > 99)) { 1202c0b746e5SOllivier Robert /* Data out of range. */ 12039c2daa00SOllivier Robert pp->lencode = 0; 1204c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 1205c0b746e5SOllivier Robert return; 1206c0b746e5SOllivier Robert } 1207c0b746e5SOllivier Robert 12089c2daa00SOllivier Robert 12099c2daa00SOllivier Robert if(peer->MODE == 0) { /* compatiblity to original version */ 12109c2daa00SOllivier Robert int bst = flags; 12119c2daa00SOllivier Robert /* Check that BST/UTC bits are the complement of one another. */ 12129c2daa00SOllivier Robert if(!(bst & 2) == !(bst & 4)) { 12139c2daa00SOllivier Robert pp->lencode = 0; 12149c2daa00SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 12159c2daa00SOllivier Robert return; 12169c2daa00SOllivier Robert } 12179c2daa00SOllivier Robert } 1218c0b746e5SOllivier Robert if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); } 1219c0b746e5SOllivier Robert 1220c0b746e5SOllivier Robert /* Year-2000 alert! */ 1221c0b746e5SOllivier Robert /* Attempt to wrap 2-digit date into sensible window. */ 1222c0b746e5SOllivier Robert if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */ 1223c0b746e5SOllivier Robert pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */ 1224c0b746e5SOllivier Robert /* 1225c0b746e5SOllivier Robert Attempt to do the right thing by screaming that the code will 1226c0b746e5SOllivier Robert soon break when we get to the end of its useful life. What a 1227c0b746e5SOllivier Robert hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X! 1228c0b746e5SOllivier Robert */ 1229c0b746e5SOllivier Robert if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */ 1230c0b746e5SOllivier Robert /*This should get attention B^> */ 1231c0b746e5SOllivier Robert msyslog(LOG_NOTICE, 1232c0b746e5SOllivier Robert "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!"); 1233c0b746e5SOllivier Robert } 1234c0b746e5SOllivier Robert #ifdef DEBUG 1235c0b746e5SOllivier Robert if(debug) { 1236c0b746e5SOllivier Robert printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n", 1237c0b746e5SOllivier Robert n, 1238c0b746e5SOllivier Robert pp->hour, pp->minute, pp->second, 12399c2daa00SOllivier Robert pp->day, month, pp->year, flags, status); 1240c0b746e5SOllivier Robert } 1241c0b746e5SOllivier Robert #endif 1242c0b746e5SOllivier Robert 1243c0b746e5SOllivier Robert /* 1244c0b746e5SOllivier Robert The status value tested for is not strictly supported by the 1245c0b746e5SOllivier Robert clock spec since the value of bit 2 (0x4) is claimed to be 1246c0b746e5SOllivier Robert undefined for MSF, yet does seem to indicate if the last resync 1247c0b746e5SOllivier Robert was successful or not. 1248c0b746e5SOllivier Robert */ 1249c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 1250c0b746e5SOllivier Robert status &= 0x7; 1251c0b746e5SOllivier Robert if(status == 0x3) { 1252c0b746e5SOllivier Robert if(status != up->status) 1253c0b746e5SOllivier Robert { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); } 1254c0b746e5SOllivier Robert } else { 1255c0b746e5SOllivier Robert if(status != up->status) { 1256c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: signal lost"); 1257c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */ 1258c0b746e5SOllivier Robert up->status = status; 12599c2daa00SOllivier Robert pp->lencode = 0; 1260c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1261c0b746e5SOllivier Robert return; 1262c0b746e5SOllivier Robert } 1263c0b746e5SOllivier Robert } 1264c0b746e5SOllivier Robert up->status = status; 1265c0b746e5SOllivier Robert 12669c2daa00SOllivier Robert if (peer->MODE == 0) { /* compatiblity to original version */ 12679c2daa00SOllivier Robert int bst = flags; 12689c2daa00SOllivier Robert 1269c0b746e5SOllivier Robert pp->day += moff[month - 1]; 1270c0b746e5SOllivier Robert 1271c0b746e5SOllivier Robert if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */ 1272c0b746e5SOllivier Robert 1273c0b746e5SOllivier Robert /* Convert to UTC if required */ 1274c0b746e5SOllivier Robert if(bst & 2) { 1275c0b746e5SOllivier Robert pp->hour--; 1276c0b746e5SOllivier Robert if (pp->hour < 0) { 1277c0b746e5SOllivier Robert pp->hour = 23; 1278c0b746e5SOllivier Robert pp->day--; 12799c2daa00SOllivier Robert /* If we try to wrap round the year 12809c2daa00SOllivier Robert * (BST on 1st Jan), reject.*/ 1281c0b746e5SOllivier Robert if(pp->day < 0) { 12829c2daa00SOllivier Robert pp->lencode = 0; 1283c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1284c0b746e5SOllivier Robert return; 1285c0b746e5SOllivier Robert } 1286c0b746e5SOllivier Robert } 1287c0b746e5SOllivier Robert } 12889c2daa00SOllivier Robert } 1289c0b746e5SOllivier Robert 12909c2daa00SOllivier Robert if(peer->MODE > 0) { 12919c2daa00SOllivier Robert if(pp->sloppyclockflag & CLK_FLAG1) { 12929c2daa00SOllivier Robert struct tm local; 12939c2daa00SOllivier Robert struct tm *gmtp; 12949c2daa00SOllivier Robert time_t unixtime; 12959c2daa00SOllivier Robert 12969c2daa00SOllivier Robert /* 12979c2daa00SOllivier Robert * Convert to GMT for sites that distribute localtime. 12989c2daa00SOllivier Robert * This means we have to do Y2K conversion on the 12999c2daa00SOllivier Robert * 2-digit year; otherwise, we get the time wrong. 13009c2daa00SOllivier Robert */ 13019c2daa00SOllivier Robert 13022b15cb3dSCy Schubert memset(&local, 0, sizeof(local)); 13032b15cb3dSCy Schubert 13049c2daa00SOllivier Robert local.tm_year = pp->year-1900; 13059c2daa00SOllivier Robert local.tm_mon = month-1; 13069c2daa00SOllivier Robert local.tm_mday = pp->day; 13079c2daa00SOllivier Robert local.tm_hour = pp->hour; 13089c2daa00SOllivier Robert local.tm_min = pp->minute; 13099c2daa00SOllivier Robert local.tm_sec = pp->second; 13109c2daa00SOllivier Robert switch (peer->MODE) { 13119c2daa00SOllivier Robert case 1: 13129c2daa00SOllivier Robert local.tm_isdst = (flags & 2); 13139c2daa00SOllivier Robert break; 13149c2daa00SOllivier Robert case 2: 13159c2daa00SOllivier Robert local.tm_isdst = (flags & 2); 13169c2daa00SOllivier Robert break; 13179c2daa00SOllivier Robert case 3: 13189c2daa00SOllivier Robert switch (flags & 3) { 13199c2daa00SOllivier Robert case 0: /* It is unclear exactly when the 13209c2daa00SOllivier Robert Arcron changes from DST->ST and 13219c2daa00SOllivier Robert ST->DST. Testing has shown this 13229c2daa00SOllivier Robert to be irregular. For the time 13239c2daa00SOllivier Robert being, let the OS decide. */ 13249c2daa00SOllivier Robert local.tm_isdst = 0; 13259c2daa00SOllivier Robert #ifdef DEBUG 13269c2daa00SOllivier Robert if (debug) 13279c2daa00SOllivier Robert printf ("arc: DST = 00 (0)\n"); 13289c2daa00SOllivier Robert #endif 13299c2daa00SOllivier Robert break; 13309c2daa00SOllivier Robert case 1: /* dst->st time */ 13319c2daa00SOllivier Robert local.tm_isdst = -1; 13329c2daa00SOllivier Robert #ifdef DEBUG 13339c2daa00SOllivier Robert if (debug) 13349c2daa00SOllivier Robert printf ("arc: DST = 01 (1)\n"); 13359c2daa00SOllivier Robert #endif 13369c2daa00SOllivier Robert break; 13379c2daa00SOllivier Robert case 2: /* st->dst time */ 13389c2daa00SOllivier Robert local.tm_isdst = -1; 13399c2daa00SOllivier Robert #ifdef DEBUG 13409c2daa00SOllivier Robert if (debug) 13419c2daa00SOllivier Robert printf ("arc: DST = 10 (2)\n"); 13429c2daa00SOllivier Robert #endif 13439c2daa00SOllivier Robert break; 13449c2daa00SOllivier Robert case 3: /* dst time */ 13459c2daa00SOllivier Robert local.tm_isdst = 1; 13469c2daa00SOllivier Robert #ifdef DEBUG 13479c2daa00SOllivier Robert if (debug) 13489c2daa00SOllivier Robert printf ("arc: DST = 11 (3)\n"); 13499c2daa00SOllivier Robert #endif 13509c2daa00SOllivier Robert break; 13519c2daa00SOllivier Robert } 13529c2daa00SOllivier Robert break; 13539c2daa00SOllivier Robert default: 13549c2daa00SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", 13559c2daa00SOllivier Robert peer->MODE); 13569c2daa00SOllivier Robert return; 13579c2daa00SOllivier Robert break; 13589c2daa00SOllivier Robert } 13599c2daa00SOllivier Robert unixtime = mktime (&local); 13609c2daa00SOllivier Robert if ((gmtp = gmtime (&unixtime)) == NULL) 13619c2daa00SOllivier Robert { 13629c2daa00SOllivier Robert pp->lencode = 0; 13639c2daa00SOllivier Robert refclock_report (peer, CEVNT_FAULT); 13649c2daa00SOllivier Robert return; 13659c2daa00SOllivier Robert } 13669c2daa00SOllivier Robert pp->year = gmtp->tm_year+1900; 13679c2daa00SOllivier Robert month = gmtp->tm_mon+1; 13689c2daa00SOllivier Robert pp->day = ymd2yd(pp->year,month,gmtp->tm_mday); 13699c2daa00SOllivier Robert /* pp->day = gmtp->tm_yday; */ 13709c2daa00SOllivier Robert pp->hour = gmtp->tm_hour; 13719c2daa00SOllivier Robert pp->minute = gmtp->tm_min; 13729c2daa00SOllivier Robert pp->second = gmtp->tm_sec; 13739c2daa00SOllivier Robert #ifdef DEBUG 13749c2daa00SOllivier Robert if (debug) 13759c2daa00SOllivier Robert { 13769c2daa00SOllivier Robert printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 13779c2daa00SOllivier Robert pp->year,month,gmtp->tm_mday,pp->hour,pp->minute, 13789c2daa00SOllivier Robert pp->second); 13799c2daa00SOllivier Robert } 13809c2daa00SOllivier Robert #endif 13819c2daa00SOllivier Robert } else 13829c2daa00SOllivier Robert { 13839c2daa00SOllivier Robert /* 13849c2daa00SOllivier Robert * For more rational sites distributing UTC 13859c2daa00SOllivier Robert */ 13869c2daa00SOllivier Robert pp->day = ymd2yd(pp->year,month,pp->day); 13879c2daa00SOllivier Robert } 13889c2daa00SOllivier Robert } 13899c2daa00SOllivier Robert 13909c2daa00SOllivier Robert if (peer->MODE == 0) { /* compatiblity to original version */ 13919c2daa00SOllivier Robert /* If clock signal quality is 13929c2daa00SOllivier Robert * unknown, revert to default PRECISION...*/ 13939c2daa00SOllivier Robert if(up->quality == QUALITY_UNKNOWN) { 13949c2daa00SOllivier Robert peer->precision = PRECISION; 13959c2daa00SOllivier Robert } else { /* ...else improve precision if flag3 is set... */ 1396c0b746e5SOllivier Robert peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 1397c0b746e5SOllivier Robert HIGHPRECISION : PRECISION); 1398c0b746e5SOllivier Robert } 13999c2daa00SOllivier Robert } else { 14009c2daa00SOllivier Robert if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) { 14019c2daa00SOllivier Robert peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 14029c2daa00SOllivier Robert HIGHPRECISION : PRECISION); 14039c2daa00SOllivier Robert } else if (up->quality == QUALITY_UNKNOWN) { 14049c2daa00SOllivier Robert peer->precision = PRECISION; 14059c2daa00SOllivier Robert } else { 14069c2daa00SOllivier Robert peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 14079c2daa00SOllivier Robert HIGHPRECISION : PRECISION); 14089c2daa00SOllivier Robert } 14099c2daa00SOllivier Robert } 1410c0b746e5SOllivier Robert 1411c0b746e5SOllivier Robert /* Notice and log any change (eg from initial defaults) for flags. */ 1412c0b746e5SOllivier Robert if(up->saved_flags != pp->sloppyclockflag) { 14139c2daa00SOllivier Robert #ifdef DEBUG 1414c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s", 1415c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."), 1416c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."), 1417c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."), 1418c0b746e5SOllivier Robert ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : ".")); 1419c0b746e5SOllivier Robert /* Note effects of flags changing... */ 1420c0b746e5SOllivier Robert if(debug) { 1421c0b746e5SOllivier Robert printf("arc: PRECISION = %d.\n", peer->precision); 1422c0b746e5SOllivier Robert } 1423c0b746e5SOllivier Robert #endif 1424c0b746e5SOllivier Robert up->saved_flags = pp->sloppyclockflag; 1425c0b746e5SOllivier Robert } 1426c0b746e5SOllivier Robert 1427c0b746e5SOllivier Robert /* Note time of last believable timestamp. */ 1428c0b746e5SOllivier Robert pp->lastrec = up->lastrec; 1429c0b746e5SOllivier Robert 1430c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 1431c0b746e5SOllivier Robert /* Find out if a leap-second might just have happened... 1432c0b746e5SOllivier Robert (ie is this the first hour of the first day of Jan or Jul?) 1433c0b746e5SOllivier Robert */ 1434c0b746e5SOllivier Robert if((pp->hour == 0) && 1435c0b746e5SOllivier Robert (pp->day == 1) && 1436c0b746e5SOllivier Robert ((month == 1) || (month == 7))) { 1437c0b746e5SOllivier Robert if(possible_leap >= 0) { 1438c0b746e5SOllivier Robert /* A leap may have happened, and no resync has started yet...*/ 1439c0b746e5SOllivier Robert possible_leap = 1; 1440c0b746e5SOllivier Robert } 1441c0b746e5SOllivier Robert } else { 1442c0b746e5SOllivier Robert /* Definitely not leap-second territory... */ 1443c0b746e5SOllivier Robert possible_leap = 0; 1444c0b746e5SOllivier Robert } 1445c0b746e5SOllivier Robert #endif 1446c0b746e5SOllivier Robert 1447c0b746e5SOllivier Robert if (!refclock_process(pp)) { 14489c2daa00SOllivier Robert pp->lencode = 0; 1449c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 1450c0b746e5SOllivier Robert return; 1451c0b746e5SOllivier Robert } 14529c2daa00SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 1453c0b746e5SOllivier Robert refclock_receive(peer); 1454c0b746e5SOllivier Robert } 1455c0b746e5SOllivier Robert 1456c0b746e5SOllivier Robert 1457c0b746e5SOllivier Robert /* request_time() sends a time request to the clock with given peer. */ 1458c0b746e5SOllivier Robert /* This automatically reports a fault if necessary. */ 1459c0b746e5SOllivier Robert /* No data should be sent after this until arc_poll() returns. */ 14602b15cb3dSCy Schubert static void request_time (int, struct peer *); 1461c0b746e5SOllivier Robert static void 1462c0b746e5SOllivier Robert request_time( 1463c0b746e5SOllivier Robert int unit, 1464c0b746e5SOllivier Robert struct peer *peer 1465c0b746e5SOllivier Robert ) 1466c0b746e5SOllivier Robert { 1467c0b746e5SOllivier Robert struct refclockproc *pp = peer->procptr; 14682b15cb3dSCy Schubert register struct arcunit *up = pp->unitptr; 1469c0b746e5SOllivier Robert #ifdef DEBUG 1470c0b746e5SOllivier Robert if(debug) { printf("arc: unit %d: requesting time.\n", unit); } 1471c0b746e5SOllivier Robert #endif 1472c0b746e5SOllivier Robert if (!send_slow(up, pp->io.fd, "o\r")) { 14739c2daa00SOllivier Robert #ifdef DEBUG 14749c2daa00SOllivier Robert if (debug) { 14759c2daa00SOllivier Robert printf("arc: unit %d: problem sending", unit); 14769c2daa00SOllivier Robert } 1477c0b746e5SOllivier Robert #endif 14789c2daa00SOllivier Robert pp->lencode = 0; 1479c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1480c0b746e5SOllivier Robert return; 1481c0b746e5SOllivier Robert } 1482c0b746e5SOllivier Robert pp->polls++; 1483c0b746e5SOllivier Robert } 1484c0b746e5SOllivier Robert 1485c0b746e5SOllivier Robert /* 1486c0b746e5SOllivier Robert * arc_poll - called by the transmit procedure 1487c0b746e5SOllivier Robert */ 1488c0b746e5SOllivier Robert static void 1489c0b746e5SOllivier Robert arc_poll( 1490c0b746e5SOllivier Robert int unit, 1491c0b746e5SOllivier Robert struct peer *peer 1492c0b746e5SOllivier Robert ) 1493c0b746e5SOllivier Robert { 1494c0b746e5SOllivier Robert register struct arcunit *up; 1495c0b746e5SOllivier Robert struct refclockproc *pp; 1496c0b746e5SOllivier Robert int resync_needed; /* Should we start a resync? */ 1497c0b746e5SOllivier Robert 1498c0b746e5SOllivier Robert pp = peer->procptr; 14992b15cb3dSCy Schubert up = pp->unitptr; 15009c2daa00SOllivier Robert #if 0 1501c0b746e5SOllivier Robert pp->lencode = 0; 1502c0b746e5SOllivier Robert memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); 15039c2daa00SOllivier Robert #endif 1504c0b746e5SOllivier Robert 1505c0b746e5SOllivier Robert #if 0 1506c0b746e5SOllivier Robert /* Flush input. */ 1507c0b746e5SOllivier Robert tcflush(pp->io.fd, TCIFLUSH); 1508c0b746e5SOllivier Robert #endif 1509c0b746e5SOllivier Robert 1510c0b746e5SOllivier Robert /* Resync if our next scheduled resync time is here or has passed. */ 15119c2daa00SOllivier Robert resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) && 15129c2daa00SOllivier Robert (up->next_resync <= current_time) ); 1513c0b746e5SOllivier Robert 1514c0b746e5SOllivier Robert #ifdef ARCRON_LEAPSECOND_KEEN 1515c0b746e5SOllivier Robert /* 1516c0b746e5SOllivier Robert Try to catch a potential leap-second insertion or deletion quickly. 1517c0b746e5SOllivier Robert 1518c0b746e5SOllivier Robert In addition to the normal NTP fun of clocks that don't report 1519c0b746e5SOllivier Robert leap-seconds spooking their hosts, this clock does not even 1520c0b746e5SOllivier Robert sample the radio sugnal the whole time, so may miss a 1521c0b746e5SOllivier Robert leap-second insertion or deletion for up to a whole sample 1522c0b746e5SOllivier Robert time. 1523c0b746e5SOllivier Robert 1524c0b746e5SOllivier Robert To try to minimise this effect, if in the first few minutes of 1525c0b746e5SOllivier Robert the day immediately following a leap-second-insertion point 1526c0b746e5SOllivier Robert (ie in the first hour of the first day of the first and sixth 1527c0b746e5SOllivier Robert months), and if the last resync was in the previous day, and a 1528c0b746e5SOllivier Robert resync is not already in progress, resync the clock 1529c0b746e5SOllivier Robert immediately. 1530c0b746e5SOllivier Robert 1531c0b746e5SOllivier Robert */ 1532c0b746e5SOllivier Robert if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */ 1533c0b746e5SOllivier Robert (!up->resyncing)) { /* No resync in progress yet. */ 1534c0b746e5SOllivier Robert resync_needed = 1; 1535c0b746e5SOllivier Robert possible_leap = -1; /* Prevent multiple resyncs. */ 1536c0b746e5SOllivier Robert msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit); 1537c0b746e5SOllivier Robert } 1538c0b746e5SOllivier Robert #endif 1539c0b746e5SOllivier Robert 1540c0b746e5SOllivier Robert /* Do a resync if required... */ 1541c0b746e5SOllivier Robert if(resync_needed) { 1542c0b746e5SOllivier Robert /* First, reset quality value to `unknown' so we can detect */ 1543c0b746e5SOllivier Robert /* when a quality message has been responded to by this */ 1544c0b746e5SOllivier Robert /* being set to some other value. */ 1545c0b746e5SOllivier Robert up->quality = QUALITY_UNKNOWN; 1546c0b746e5SOllivier Robert 1547c0b746e5SOllivier Robert /* Note that we are resyncing... */ 1548c0b746e5SOllivier Robert up->resyncing = 1; 1549c0b746e5SOllivier Robert 1550c0b746e5SOllivier Robert /* Now actually send the resync command and an immediate poll. */ 1551c0b746e5SOllivier Robert #ifdef DEBUG 1552c0b746e5SOllivier Robert if(debug) { printf("arc: sending resync command (h\\r).\n"); } 1553c0b746e5SOllivier Robert #endif 1554c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit); 1555c0b746e5SOllivier Robert send_slow(up, pp->io.fd, "h\r"); 1556c0b746e5SOllivier Robert 1557c0b746e5SOllivier Robert /* Schedule our next resync... */ 1558c0b746e5SOllivier Robert up->next_resync = current_time + DEFAULT_RESYNC_TIME; 1559c0b746e5SOllivier Robert 1560c0b746e5SOllivier Robert /* Drop through to request time if appropriate. */ 1561c0b746e5SOllivier Robert } 1562c0b746e5SOllivier Robert 1563c0b746e5SOllivier Robert /* If clock quality is too poor to trust, indicate a fault. */ 1564c0b746e5SOllivier Robert /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/ 1565c0b746e5SOllivier Robert /* we'll cross our fingers and just hope that the thing */ 1566c0b746e5SOllivier Robert /* synced so quickly we did not catch it---we'll */ 1567c0b746e5SOllivier Robert /* double-check the clock is OK elsewhere. */ 1568c0b746e5SOllivier Robert if( 1569c0b746e5SOllivier Robert #ifdef ARCRON_KEEN 1570c0b746e5SOllivier Robert (up->quality != QUALITY_UNKNOWN) && 1571c0b746e5SOllivier Robert #else 1572c0b746e5SOllivier Robert (up->quality == QUALITY_UNKNOWN) || 1573c0b746e5SOllivier Robert #endif 1574c0b746e5SOllivier Robert (up->quality < MIN_CLOCK_QUALITY_OK)) { 1575c0b746e5SOllivier Robert #ifdef DEBUG 1576c0b746e5SOllivier Robert if(debug) { 1577c0b746e5SOllivier Robert printf("arc: clock quality %d too poor.\n", up->quality); 1578c0b746e5SOllivier Robert } 1579c0b746e5SOllivier Robert #endif 15809c2daa00SOllivier Robert pp->lencode = 0; 1581c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1582c0b746e5SOllivier Robert return; 1583c0b746e5SOllivier Robert } 1584c0b746e5SOllivier Robert /* This is the normal case: request a timestamp. */ 1585c0b746e5SOllivier Robert request_time(unit, peer); 1586c0b746e5SOllivier Robert } 1587c0b746e5SOllivier Robert 1588c0b746e5SOllivier Robert #else 15892b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT 1590c0b746e5SOllivier Robert #endif 1591