154359Sroberto/* 2132451Sroberto * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers 354359Sroberto */ 454359Sroberto 554359Sroberto#ifdef HAVE_CONFIG_H 654359Sroberto#include <config.h> 754359Sroberto#endif 854359Sroberto 9290001Sglebius#include "ntp_types.h" 10290001Sglebius 1154359Sroberto#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF) 12182007Sroberto 13132451Srobertostatic const char arc_version[] = { "V1.3 2003/02/21" }; 1454359Sroberto 15132451Sroberto/* define PRE_NTP420 for compatibility to previous versions of NTP (at least 16132451Sroberto to 4.1.0 */ 17132451Sroberto#undef PRE_NTP420 1854359Sroberto 1954359Sroberto#ifndef ARCRON_NOT_KEEN 2054359Sroberto#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */ 2154359Sroberto#endif 2254359Sroberto 2354359Sroberto#ifndef ARCRON_NOT_MULTIPLE_SAMPLES 2454359Sroberto#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */ 2554359Sroberto#endif 2654359Sroberto 2754359Sroberto#ifndef ARCRON_NOT_LEAPSECOND_KEEN 2854359Sroberto#ifndef ARCRON_LEAPSECOND_KEEN 2954359Sroberto#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */ 3054359Sroberto#endif 3154359Sroberto#endif 3254359Sroberto 3354359Sroberto/* 3454359SrobertoCode by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997. 3554359SrobertoModifications by Damon Hart-Davis, <d@hd.org>, 1997. 36132451SrobertoModifications by Paul Alfille, <palfille@partners.org>, 2003. 37132451SrobertoModifications by Christopher Price, <cprice@cs-home.com>, 2003. 38182007SrobertoModifications by Nigel Roles <nigel@9fs.org>, 2003. 3954359Sroberto 40132451Sroberto 4154359SrobertoTHIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT 4254359SrobertoYOUR OWN RISK. 4354359Sroberto 4454359SrobertoOrginally developed and used with ntp3-5.85 by Derek Mulcahy. 4554359Sroberto 4654359SrobertoBuilt against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2. 4754359Sroberto 4854359SrobertoThis code may be freely copied and used and incorporated in other 4954359Srobertosystems providing the disclaimer and notice of authorship are 5054359Srobertoreproduced. 5154359Sroberto 5254359Sroberto------------------------------------------------------------------------------- 5354359Sroberto 54182007SrobertoNigel's notes: 55182007Sroberto 56182007Sroberto1) Called tcgetattr() before modifying, so that fields correctly initialised 57182007Sroberto for all operating systems 58182007Sroberto 59182007Sroberto2) Altered parsing of timestamp line so that it copes with fields which are 60182007Sroberto not always ASCII digits (e.g. status field when battery low) 61182007Sroberto 62182007Sroberto------------------------------------------------------------------------------- 63182007Sroberto 64132451SrobertoChristopher's notes: 65132451Sroberto 66132451SrobertoMAJOR CHANGES SINCE V1.2 67132451Sroberto======================== 68132451Sroberto 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk> 69132451Sroberto 2001-02-17 comp.protocols.time.ntp 70132451Sroberto 71132451Sroberto 2) Added WWVB support via clock mode command, localtime/UTC time configured 72132451Sroberto via flag1=(0=UTC, 1=localtime) 73132451Sroberto 74132451Sroberto 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync) 75132451Sroberto 76132451Sroberto 4) Added simplified conversion from localtime to UTC with dst/bst translation 77132451Sroberto 78132451Sroberto 5) Added average signal quality poll 79132451Sroberto 80132451Sroberto 6) Fixed a badformat error when no code is available due to stripping 81132451Sroberto \n & \r's 82132451Sroberto 83132451Sroberto 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll 84132451Sroberto routine 85132451Sroberto 86132451Sroberto 8) Lots of code cleanup, including standardized DEBUG macros and removal 87132451Sroberto of unused code 88132451Sroberto 89132451Sroberto------------------------------------------------------------------------------- 90132451Sroberto 9154359SrobertoAuthor's original note: 9254359Sroberto 9354359SrobertoI enclose my ntp driver for the Galleon Systems Arc MSF receiver. 9454359Sroberto 9554359SrobertoIt works (after a fashion) on both Solaris-1 and Solaris-2. 9654359Sroberto 9754359SrobertoI am currently using ntp3-5.85. I have been running the code for 9854359Srobertoabout 7 months without any problems. Even coped with the change to BST! 9954359Sroberto 10054359SrobertoI had to do some funky things to read from the clock because it uses the 10154359Srobertopower from the receive lines to drive the transmit lines. This makes the 10254359Srobertocode look a bit stupid but it works. I also had to put in some delays to 10354359Srobertoallow for the turnaround time from receive to transmit. These delays 10454359Srobertoare between characters when requesting a time stamp so that shouldn't affect 10554359Srobertothe results too drastically. 10654359Sroberto 10754359Sroberto... 10854359Sroberto 10954359SrobertoThe bottom line is that it works but could easily be improved. You are 11054359Srobertofree to do what you will with the code. I haven't been able to determine 11154359Srobertohow good the clock is. I think that this requires a known good clock 11254359Srobertoto compare it against. 11354359Sroberto 11454359Sroberto------------------------------------------------------------------------------- 11554359Sroberto 11654359SrobertoDamon's notes for adjustments: 11754359Sroberto 11854359SrobertoMAJOR CHANGES SINCE V1.0 11954359Sroberto======================== 12054359Sroberto 1) Removal of pollcnt variable that made the clock go permanently 12154359Sroberto off-line once two time polls failed to gain responses. 12254359Sroberto 12354359Sroberto 2) Avoiding (at least on Solaris-2) terminal becoming the controlling 12454359Sroberto terminal of the process when we do a low-level open(). 12554359Sroberto 12654359Sroberto 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being 12754359Sroberto defined) to try to resync quickly after a potential leap-second 12854359Sroberto insertion or deletion. 12954359Sroberto 13054359Sroberto 4) Code significantly slimmer at run-time than V1.0. 13154359Sroberto 13254359Sroberto 13354359SrobertoGENERAL 13454359Sroberto======= 13554359Sroberto 13654359Sroberto 1) The C preprocessor symbol to have the clock built has been changed 13754359Sroberto from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the 13854359Sroberto possiblity of clashes with other symbols in the future. 13954359Sroberto 14054359Sroberto 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons: 14154359Sroberto 14254359Sroberto a) The ARC documentation claims the internal clock is (only) 143290001Sglebius accurate to about 20ms relative to Rugby (plus there must be 144290001Sglebius noticable drift and delay in the ms range due to transmission 145290001Sglebius delays and changing atmospheric effects). This clock is not 146290001Sglebius designed for ms accuracy as NTP has spoilt us all to expect. 14754359Sroberto 14854359Sroberto b) The clock oscillator looks like a simple uncompensated quartz 149290001Sglebius crystal of the sort used in digital watches (ie 32768Hz) which 150290001Sglebius can have large temperature coefficients and drifts; it is not 151290001Sglebius clear if this oscillator is properly disciplined to the MSF 152290001Sglebius transmission, but as the default is to resync only once per 153290001Sglebius *day*, we can imagine that it is not, and is free-running. We 154290001Sglebius can minimise drift by resyncing more often (at the cost of 155290001Sglebius reduced battery life), but drift/wander may still be 156290001Sglebius significant. 15754359Sroberto 15854359Sroberto c) Note that the bit time of 3.3ms adds to the potential error in 159290001Sglebius the the clock timestamp, since the bit clock of the serial link 160290001Sglebius may effectively be free-running with respect to the host clock 161290001Sglebius and the MSF clock. Actually, the error is probably 1/16th of 162290001Sglebius the above, since the input data is probably sampled at at least 163290001Sglebius 16x the bit rate. 16454359Sroberto 16554359Sroberto By keeping the clock marked as not very precise, it will have a 16654359Sroberto fairly large dispersion, and thus will tend to be used as a 16754359Sroberto `backup' time source and sanity checker, which this clock is 16854359Sroberto probably ideal for. For an isolated network without other time 16954359Sroberto sources, this clock can probably be expected to provide *much* 17054359Sroberto better than 1s accuracy, which will be fine. 17154359Sroberto 17254359Sroberto By default, PRECISION is set to -4, but experience, especially at a 17354359Sroberto particular geographic location with a particular clock, may allow 17454359Sroberto this to be altered to -5. (Note that skews of +/- 10ms are to be 17554359Sroberto expected from the clock from time-to-time.) This improvement of 17654359Sroberto reported precision can be instigated by setting flag3 to 1, though 17754359Sroberto the PRECISION will revert to the normal value while the clock 17854359Sroberto signal quality is unknown whatever the flag3 setting. 17954359Sroberto 18054359Sroberto IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE 18154359Sroberto ANY RESIDUAL SKEW, eg: 18254359Sroberto 183290001Sglebius server 127.127.27.0 # ARCRON MSF radio clock unit 0. 184290001Sglebius # Fudge timestamps by about 20ms. 185290001Sglebius fudge 127.127.27.0 time1 0.020 18654359Sroberto 18754359Sroberto You will need to observe your system's behaviour, assuming you have 18854359Sroberto some other NTP source to compare it with, to work out what the 18954359Sroberto fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with 19054359Sroberto my MSF clock with my distance from the MSF transmitter, +20ms 19154359Sroberto seemed about right, after some observation. 19254359Sroberto 19354359Sroberto 3) REFID has been made "MSFa" to reflect the MSF time source and the 19454359Sroberto ARCRON receiver. 19554359Sroberto 19654359Sroberto 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before 19754359Sroberto forcing a resync since the last attempt. This is picked to give a 19854359Sroberto little less than an hour between resyncs and to try to avoid 19954359Sroberto clashing with any regular event at a regular time-past-the-hour 20054359Sroberto which might cause systematic errors. 20154359Sroberto 20254359Sroberto The INITIAL_RESYNC_DELAY is to avoid bothering the clock and 20354359Sroberto running down its batteries unnecesarily if ntpd is going to crash 20454359Sroberto or be killed or reconfigured quickly. If ARCRON_KEEN is defined 20554359Sroberto then this period is long enough for (with normal polling rates) 20654359Sroberto enough time samples to have been taken to allow ntpd to sync to 20754359Sroberto the clock before the interruption for the clock to resync to MSF. 20854359Sroberto This avoids ntpd syncing to another peer first and then 20954359Sroberto almost immediately hopping to the MSF clock. 21054359Sroberto 21154359Sroberto The RETRY_RESYNC_TIME is used before rescheduling a resync after a 21254359Sroberto resync failed to reveal a statisfatory signal quality (too low or 21354359Sroberto unknown). 21454359Sroberto 21554359Sroberto 5) The clock seems quite jittery, so I have increased the 21654359Sroberto median-filter size from the typical (previous) value of 3. I 21754359Sroberto discard up to half the results in the filter. It looks like maybe 21854359Sroberto 1 sample in 10 or so (maybe less) is a spike, so allow the median 21954359Sroberto filter to discard at least 10% of its entries or 1 entry, whichever 22054359Sroberto is greater. 22154359Sroberto 22254359Sroberto 6) Sleeping *before* each character sent to the unit to allow required 22354359Sroberto inter-character time but without introducting jitter and delay in 22454359Sroberto handling the response if possible. 22554359Sroberto 22654359Sroberto 7) If the flag ARCRON_KEEN is defined, take time samples whenever 22754359Sroberto possible, even while resyncing, etc. We rely, in this case, on the 22854359Sroberto clock always giving us a reasonable time or else telling us in the 22954359Sroberto status byte at the end of the timestamp that it failed to sync to 23054359Sroberto MSF---thus we should never end up syncing to completely the wrong 23154359Sroberto time. 23254359Sroberto 23354359Sroberto 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of 23454359Sroberto refclock median-filter routines to get round small bug in 3-5.90 23554359Sroberto code which does not return the median offset. XXX Removed this 23654359Sroberto bit due NTP Version 4 upgrade - dlm. 23754359Sroberto 23854359Sroberto 9) We would appear to have a year-2000 problem with this clock since 23954359Sroberto it returns only the two least-significant digits of the year. But 24054359Sroberto ntpd ignores the year and uses the local-system year instead, so 24154359Sroberto this is in fact not a problem. Nevertheless, we attempt to do a 24254359Sroberto sensible thing with the dates, wrapping them into a 100-year 24354359Sroberto window. 24454359Sroberto 24554359Sroberto 10)Logs stats information that can be used by Derek's Tcl/Tk utility 24654359Sroberto to show the status of the clock. 24754359Sroberto 24854359Sroberto 11)The clock documentation insists that the number of bits per 24954359Sroberto character to be sent to the clock, and sent by it, is 11, including 25054359Sroberto one start bit and two stop bits. The data format is either 7+even 25154359Sroberto or 8+none. 25254359Sroberto 25354359Sroberto 25454359SrobertoTO-DO LIST 25554359Sroberto========== 25654359Sroberto 25754359Sroberto * Eliminate use of scanf(), and maybe sprintf(). 25854359Sroberto 25954359Sroberto * Allow user setting of resync interval to trade battery life for 26054359Sroberto accuracy; maybe could be done via fudge factor or unit number. 26154359Sroberto 26254359Sroberto * Possibly note the time since the last resync of the MSF clock to 26354359Sroberto MSF as the age of the last reference timestamp, ie trust the 26454359Sroberto clock's oscillator not very much... 26554359Sroberto 26654359Sroberto * Add very slow auto-adjustment up to a value of +/- time2 to correct 26754359Sroberto for long-term errors in the clock value (time2 defaults to 0 so the 26854359Sroberto correction would be disabled by default). 26954359Sroberto 27054359Sroberto * Consider trying to use the tty_clk/ppsclock support. 27154359Sroberto 27254359Sroberto * Possibly use average or maximum signal quality reported during 27354359Sroberto resync, rather than just the last one, which may be atypical. 27454359Sroberto 27554359Sroberto*/ 27654359Sroberto 27754359Sroberto 27854359Sroberto/* Notes for HKW Elektronik GmBH Radio clock driver */ 27954359Sroberto/* Author Lyndon David, Sentinet Ltd, Feb 1997 */ 28054359Sroberto/* These notes seem also to apply usefully to the ARCRON clock. */ 28154359Sroberto 28254359Sroberto/* The HKW clock module is a radio receiver tuned into the Rugby */ 28354359Sroberto/* MSF time signal tranmitted on 60 kHz. The clock module connects */ 28454359Sroberto/* to the computer via a serial line and transmits the time encoded */ 28554359Sroberto/* in 15 bytes at 300 baud 7 bits two stop bits even parity */ 28654359Sroberto 28754359Sroberto/* Clock communications, from the datasheet */ 28854359Sroberto/* All characters sent to the clock are echoed back to the controlling */ 28954359Sroberto/* device. */ 29054359Sroberto/* Transmit time/date information */ 29154359Sroberto/* syntax ASCII o<cr> */ 29254359Sroberto/* Character o may be replaced if neccesary by a character whose code */ 29354359Sroberto/* contains the lowest four bits f(hex) eg */ 29454359Sroberto/* syntax binary: xxxx1111 00001101 */ 29554359Sroberto 29654359Sroberto/* DHD note: 29754359SrobertoYou have to wait for character echo + 10ms before sending next character. 29854359Sroberto*/ 29954359Sroberto 30054359Sroberto/* The clock replies to this command with a sequence of 15 characters */ 30154359Sroberto/* which contain the complete time and a final <cr> making 16 characters */ 30254359Sroberto/* in total. */ 30354359Sroberto/* The RC computer clock will not reply immediately to this command because */ 30454359Sroberto/* the start bit edge of the first reply character marks the beginning of */ 30554359Sroberto/* the second. So the RC Computer Clock will reply to this command at the */ 30654359Sroberto/* start of the next second */ 30754359Sroberto/* The characters have the following meaning */ 30854359Sroberto/* 1. hours tens */ 30954359Sroberto/* 2. hours units */ 31054359Sroberto/* 3. minutes tens */ 31154359Sroberto/* 4. minutes units */ 31254359Sroberto/* 5. seconds tens */ 31354359Sroberto/* 6. seconds units */ 31454359Sroberto/* 7. day of week 1-monday 7-sunday */ 31554359Sroberto/* 8. day of month tens */ 31654359Sroberto/* 9. day of month units */ 31754359Sroberto/* 10. month tens */ 31854359Sroberto/* 11. month units */ 31954359Sroberto/* 12. year tens */ 32054359Sroberto/* 13. year units */ 32154359Sroberto/* 14. BST/UTC status */ 322290001Sglebius/* bit 7 parity */ 323290001Sglebius/* bit 6 always 0 */ 324290001Sglebius/* bit 5 always 1 */ 325290001Sglebius/* bit 4 always 1 */ 326290001Sglebius/* bit 3 always 0 */ 327290001Sglebius/* bit 2 =1 if UTC is in effect, complementary to the BST bit */ 328290001Sglebius/* bit 1 =1 if BST is in effect, according to the BST bit */ 329290001Sglebius/* bit 0 BST/UTC change impending bit=1 in case of change impending */ 33054359Sroberto/* 15. status */ 331290001Sglebius/* bit 7 parity */ 332290001Sglebius/* bit 6 always 0 */ 333290001Sglebius/* bit 5 always 1 */ 334290001Sglebius/* bit 4 always 1 */ 335290001Sglebius/* bit 3 =1 if low battery is detected */ 336290001Sglebius/* bit 2 =1 if the very last reception attempt failed and a valid */ 337290001Sglebius/* time information already exists (bit0=1) */ 338290001Sglebius/* =0 if the last reception attempt was successful */ 339290001Sglebius/* bit 1 =1 if at least one reception since 2:30 am was successful */ 340290001Sglebius/* =0 if no reception attempt since 2:30 am was successful */ 341290001Sglebius/* bit 0 =1 if the RC Computer Clock contains valid time information */ 342290001Sglebius/* This bit is zero after reset and one after the first */ 343290001Sglebius/* successful reception attempt */ 34454359Sroberto 34554359Sroberto/* DHD note: 34654359SrobertoAlso note g<cr> command which confirms that a resync is in progress, and 34754359Srobertoif so what signal quality (0--5) is available. 34854359SrobertoAlso note h<cr> command which starts a resync to MSF signal. 34954359Sroberto*/ 35054359Sroberto 35154359Sroberto 35282498Sroberto#include "ntpd.h" 35382498Sroberto#include "ntp_io.h" 35482498Sroberto#include "ntp_refclock.h" 355132451Sroberto#include "ntp_calendar.h" 35682498Sroberto#include "ntp_stdlib.h" 35754359Sroberto 35854359Sroberto#include <stdio.h> 35954359Sroberto#include <ctype.h> 36054359Sroberto 36154359Sroberto#if defined(HAVE_BSD_TTYS) 36254359Sroberto#include <sgtty.h> 36354359Sroberto#endif /* HAVE_BSD_TTYS */ 36454359Sroberto 36554359Sroberto#if defined(HAVE_SYSV_TTYS) 36654359Sroberto#include <termio.h> 36754359Sroberto#endif /* HAVE_SYSV_TTYS */ 36854359Sroberto 36954359Sroberto#if defined(HAVE_TERMIOS) 37054359Sroberto#include <termios.h> 37154359Sroberto#endif 37254359Sroberto 37354359Sroberto/* 374132451Sroberto * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock 37554359Sroberto */ 37654359Sroberto 37754359Sroberto/* 37854359Sroberto * Interface definitions 37954359Sroberto */ 380290001Sglebius#define DEVICE "/dev/arc%d" /* Device name and unit. */ 381290001Sglebius#define SPEED B300 /* UART speed (300 baud) */ 382290001Sglebius#define PRECISION (-4) /* Precision (~63 ms). */ 383290001Sglebius#define HIGHPRECISION (-5) /* If things are going well... */ 384290001Sglebius#define REFID "MSFa" /* Reference ID. */ 385290001Sglebius#define REFID_MSF "MSF" /* Reference ID. */ 386290001Sglebius#define REFID_DCF77 "DCF" /* Reference ID. */ 387290001Sglebius#define REFID_WWVB "WWVB" /* Reference ID. */ 388290001Sglebius#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver" 38954359Sroberto 390132451Sroberto#ifdef PRE_NTP420 391132451Sroberto#define MODE ttlmax 392132451Sroberto#else 393132451Sroberto#define MODE ttl 394132451Sroberto#endif 39554359Sroberto 396290001Sglebius#define LENARC 16 /* Format `o' timecode length. */ 39754359Sroberto 398290001Sglebius#define BITSPERCHAR 11 /* Bits per character. */ 399290001Sglebius#define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */ 400290001Sglebius#define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */ 401290001Sglebius#define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */ 402290001Sglebius#define CHARTIME /* Time for char at 300bps. */ \ 40354359Sroberto( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \ 40454359Sroberto (BITSPERCHAR * BITTIME) ) ) 40554359Sroberto 40654359Sroberto /* Allow for UART to accept char half-way through final stop bit. */ 407290001Sglebius#define INITIALOFFSET ((u_int32)(-BITTIME/2)) 40854359Sroberto 40954359Sroberto /* 41054359Sroberto charoffsets[x] is the time after the start of the second that byte 41154359Sroberto x (with the first byte being byte 1) is received by the UART, 41254359Sroberto assuming that the initial edge of the start bit of the first byte 41354359Sroberto is on-time. The values are represented as the fractional part of 41454359Sroberto an l_fp. 41554359Sroberto 41654359Sroberto We store enough values to have the offset of each byte including 41754359Sroberto the trailing \r, on the assumption that the bytes follow one 41854359Sroberto another without gaps. 41954359Sroberto */ 42054359Sroberto static const u_int32 charoffsets[LENARC+1] = { 42154359Sroberto#if BITSPERCHAR == 11 /* Usual case. */ 42254359Sroberto /* Offsets computed as accurately as possible... */ 42354359Sroberto 0, 42454359Sroberto INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */ 42554359Sroberto INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */ 42654359Sroberto INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */ 42754359Sroberto INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */ 42854359Sroberto INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */ 42954359Sroberto INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */ 43054359Sroberto INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */ 43154359Sroberto INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */ 43254359Sroberto INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */ 43354359Sroberto INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */ 43454359Sroberto INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */ 43554359Sroberto INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */ 43654359Sroberto INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */ 43754359Sroberto INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */ 43854359Sroberto INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */ 43954359Sroberto INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */ 44054359Sroberto#else 44154359Sroberto /* Offsets computed with a small rounding error... */ 44254359Sroberto 0, 44354359Sroberto INITIALOFFSET + 1 * CHARTIME, 44454359Sroberto INITIALOFFSET + 2 * CHARTIME, 44554359Sroberto INITIALOFFSET + 3 * CHARTIME, 44654359Sroberto INITIALOFFSET + 4 * CHARTIME, 44754359Sroberto INITIALOFFSET + 5 * CHARTIME, 44854359Sroberto INITIALOFFSET + 6 * CHARTIME, 44954359Sroberto INITIALOFFSET + 7 * CHARTIME, 45054359Sroberto INITIALOFFSET + 8 * CHARTIME, 45154359Sroberto INITIALOFFSET + 9 * CHARTIME, 45254359Sroberto INITIALOFFSET + 10 * CHARTIME, 45354359Sroberto INITIALOFFSET + 11 * CHARTIME, 45454359Sroberto INITIALOFFSET + 12 * CHARTIME, 45554359Sroberto INITIALOFFSET + 13 * CHARTIME, 45654359Sroberto INITIALOFFSET + 14 * CHARTIME, 45754359Sroberto INITIALOFFSET + 15 * CHARTIME, 45854359Sroberto INITIALOFFSET + 16 * CHARTIME 45954359Sroberto#endif 46054359Sroberto }; 46154359Sroberto 462290001Sglebius#define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */ 463290001Sglebius#define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */ 46454359Sroberto#ifdef ARCRON_KEEN 465290001Sglebius#define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */ 46654359Sroberto#else 467290001Sglebius#define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */ 46854359Sroberto#endif 46954359Sroberto 47054359Sroberto static const int moff[12] = 47154359Sroberto{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 47254359Sroberto/* Flags for a raw open() of the clock serial device. */ 47354359Sroberto#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */ 47454359Sroberto#define OPEN_FLAGS (O_RDWR | O_NOCTTY) 475290001Sglebius#else /* Oh well, it may not matter... */ 47654359Sroberto#define OPEN_FLAGS (O_RDWR) 47754359Sroberto#endif 47854359Sroberto 47954359Sroberto 48054359Sroberto/* Length of queue of command bytes to be sent. */ 481290001Sglebius#define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */ 48254359Sroberto/* Queue tick time; interval in seconds between chars taken off queue. */ 48354359Sroberto/* Must be >= 2 to allow o\r response to come back uninterrupted. */ 484290001Sglebius#define QUEUETICK 2 /* Allow o\r reply to finish. */ 48554359Sroberto 48654359Sroberto/* 48754359Sroberto * ARC unit control structure 48854359Sroberto */ 48954359Srobertostruct arcunit { 490290001Sglebius l_fp lastrec; /* Time tag for the receive time (system). */ 491290001Sglebius int status; /* Clock status. */ 49254359Sroberto 493290001Sglebius int quality; /* Quality of reception 0--5 for unit. */ 49454359Sroberto /* We may also use the values -1 or 6 internally. */ 495132451Sroberto u_long quality_stamp; /* Next time to reset quality average. */ 49654359Sroberto 49754359Sroberto u_long next_resync; /* Next resync time (s) compared to current_time. */ 498290001Sglebius int resyncing; /* Resync in progress if true. */ 49954359Sroberto 50054359Sroberto /* In the outgoing queue, cmdqueue[0] is next to be sent. */ 50154359Sroberto char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */ 50254359Sroberto 50354359Sroberto u_long saved_flags; /* Saved fudge flags. */ 50454359Sroberto}; 505132451Sroberto 50654359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 50754359Sroberto/* The flag `possible_leap' is set non-zero when any MSF unit 50854359Sroberto thinks a leap-second may have happened. 50954359Sroberto 51054359Sroberto Set whenever we receive a valid time sample in the first hour of 51154359Sroberto the first day of the first/seventh months. 51254359Sroberto 51354359Sroberto Outside the special hour this value is unconditionally set 51454359Sroberto to zero by the receive routine. 51554359Sroberto 51654359Sroberto On finding itself in this timeslot, as long as the value is 51754359Sroberto non-negative, the receive routine sets it to a positive value to 51854359Sroberto indicate a resync to MSF should be performed. 51954359Sroberto 52054359Sroberto In the poll routine, if this value is positive and we are not 52154359Sroberto already resyncing (eg from a sync that started just before 52254359Sroberto midnight), start resyncing and set this value negative to 52354359Sroberto indicate that a leap-triggered resync has been started. Having 52454359Sroberto set this negative prevents the receive routine setting it 52554359Sroberto positive and thus prevents multiple resyncs during the witching 52654359Sroberto hour. 52754359Sroberto */ 52854359Srobertostatic int possible_leap = 0; /* No resync required by default. */ 52954359Sroberto#endif 53054359Sroberto 53154359Sroberto#if 0 532290001Sglebiusstatic void dummy_event_handler (struct peer *); 533290001Sglebiusstatic void arc_event_handler (struct peer *); 53454359Sroberto#endif /* 0 */ 53554359Sroberto 536290001Sglebius#define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */ 53754359Sroberto#define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */ 53854359Sroberto#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */ 53954359Sroberto#define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */ 54054359Sroberto 54154359Sroberto/* 54254359Sroberto * Function prototypes 54354359Sroberto */ 544290001Sglebiusstatic int arc_start (int, struct peer *); 545290001Sglebiusstatic void arc_shutdown (int, struct peer *); 546290001Sglebiusstatic void arc_receive (struct recvbuf *); 547290001Sglebiusstatic void arc_poll (int, struct peer *); 54854359Sroberto 54954359Sroberto/* 55054359Sroberto * Transfer vector 55154359Sroberto */ 55254359Srobertostruct refclock refclock_arc = { 553290001Sglebius arc_start, /* start up driver */ 554290001Sglebius arc_shutdown, /* shut down driver */ 555290001Sglebius arc_poll, /* transmit poll message */ 556290001Sglebius noentry, /* not used (old arc_control) */ 557290001Sglebius noentry, /* initialize driver (not used) */ 558290001Sglebius noentry, /* not used (old arc_buginfo) */ 559290001Sglebius NOFLAGS /* not used */ 56054359Sroberto}; 56154359Sroberto 56254359Sroberto/* Queue us up for the next tick. */ 56354359Sroberto#define ENQUEUE(up) \ 56454359Sroberto do { \ 565290001Sglebius peer->procptr->nextaction = current_time + QUEUETICK; \ 56654359Sroberto } while(0) 56754359Sroberto 568132451Sroberto/* Placeholder event handler---does nothing safely---soaks up loose tick. */ 56954359Srobertostatic void 57054359Srobertodummy_event_handler( 57154359Sroberto struct peer *peer 57254359Sroberto ) 57354359Sroberto{ 574132451Sroberto#ifdef DEBUG 57554359Sroberto if(debug) { printf("arc: dummy_event_handler() called.\n"); } 57654359Sroberto#endif 57754359Sroberto} 57854359Sroberto 57954359Sroberto/* 58054359SrobertoNormal event handler. 58154359Sroberto 58254359SrobertoTake first character off queue and send to clock if not a null. 58354359Sroberto 58454359SrobertoShift characters down and put a null on the end. 58554359Sroberto 58654359SrobertoWe assume that there is no parallelism so no race condition, but even 58754359Srobertoif there is nothing bad will happen except that we might send some bad 58854359Srobertodata to the clock once in a while. 58954359Sroberto*/ 59054359Srobertostatic void 59154359Srobertoarc_event_handler( 59254359Sroberto struct peer *peer 59354359Sroberto ) 59454359Sroberto{ 59554359Sroberto struct refclockproc *pp = peer->procptr; 596290001Sglebius register struct arcunit *up = pp->unitptr; 59754359Sroberto int i; 59854359Sroberto char c; 599132451Sroberto#ifdef DEBUG 60054359Sroberto if(debug > 2) { printf("arc: arc_event_handler() called.\n"); } 60154359Sroberto#endif 60254359Sroberto 60354359Sroberto c = up->cmdqueue[0]; /* Next char to be sent. */ 60454359Sroberto /* Shift down characters, shifting trailing \0 in at end. */ 60554359Sroberto for(i = 0; i < CMDQUEUELEN; ++i) 60654359Sroberto { up->cmdqueue[i] = up->cmdqueue[i+1]; } 60754359Sroberto 60854359Sroberto /* Don't send '\0' characters. */ 60954359Sroberto if(c != '\0') { 61054359Sroberto if(write(pp->io.fd, &c, 1) != 1) { 61154359Sroberto msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd); 61254359Sroberto } 613132451Sroberto#ifdef DEBUG 61454359Sroberto else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } 61554359Sroberto#endif 61654359Sroberto } 617132451Sroberto 618132451Sroberto ENQUEUE(up); 61954359Sroberto} 62054359Sroberto 62154359Sroberto/* 62254359Sroberto * arc_start - open the devices and initialize data for processing 62354359Sroberto */ 62454359Srobertostatic int 62554359Srobertoarc_start( 62654359Sroberto int unit, 62754359Sroberto struct peer *peer 62854359Sroberto ) 62954359Sroberto{ 63054359Sroberto register struct arcunit *up; 63154359Sroberto struct refclockproc *pp; 632290001Sglebius int temp_fd; 63354359Sroberto int fd; 63454359Sroberto char device[20]; 63554359Sroberto#ifdef HAVE_TERMIOS 63654359Sroberto struct termios arg; 63754359Sroberto#endif 63854359Sroberto 639290001Sglebius msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d", 640290001Sglebius arc_version, unit); 641290001Sglebius DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version, 642290001Sglebius unit)); 64354359Sroberto 64454359Sroberto /* 64554359Sroberto * Open serial port. Use CLK line discipline, if available. 64654359Sroberto */ 647290001Sglebius snprintf(device, sizeof(device), DEVICE, unit); 648290001Sglebius temp_fd = refclock_open(device, SPEED, LDISC_CLK); 649290001Sglebius if (temp_fd <= 0) 650290001Sglebius return 0; 651290001Sglebius DPRINTF(1, ("arc: unit %d using tty_open().\n", unit)); 652290001Sglebius fd = tty_open(device, OPEN_FLAGS, 0777); 653290001Sglebius if (fd < 0) { 654290001Sglebius msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.", 655290001Sglebius unit, device); 656290001Sglebius close(temp_fd); 657290001Sglebius return 0; 65854359Sroberto } 659290001Sglebius close(temp_fd); 660290001Sglebius temp_fd = -1; /* not used after this, at *this* time. */ 66154359Sroberto 662290001Sglebius#ifndef SYS_WINNT 663290001Sglebius if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */ 664290001Sglebius msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.", 665290001Sglebius unit); 666290001Sglebius 66754359Sroberto#endif 668290001Sglebius DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd)); 66954359Sroberto 67054359Sroberto#ifdef HAVE_TERMIOS 67154359Sroberto 672290001Sglebius if (tcgetattr(fd, &arg) < 0) { 673290001Sglebius msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.", 674290001Sglebius unit, device); 675290001Sglebius close(fd); 676290001Sglebius return 0; 677290001Sglebius } 678182007Sroberto 67954359Sroberto arg.c_iflag = IGNBRK | ISTRIP; 68054359Sroberto arg.c_oflag = 0; 68154359Sroberto arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB; 68254359Sroberto arg.c_lflag = 0; 68354359Sroberto arg.c_cc[VMIN] = 1; 68454359Sroberto arg.c_cc[VTIME] = 0; 68554359Sroberto 686290001Sglebius if (tcsetattr(fd, TCSANOW, &arg) < 0) { 687290001Sglebius msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.", 688290001Sglebius unit, device); 689290001Sglebius close(fd); 690290001Sglebius return 0; 691290001Sglebius } 69254359Sroberto 69354359Sroberto#else 69454359Sroberto 695290001Sglebius msyslog(LOG_ERR, "ARCRON: termios required by this driver"); 69654359Sroberto (void)close(fd); 69754359Sroberto 69854359Sroberto return 0; 69954359Sroberto 70054359Sroberto#endif 70154359Sroberto 70254359Sroberto /* Set structure to all zeros... */ 703290001Sglebius up = emalloc_zero(sizeof(*up)); 70454359Sroberto pp = peer->procptr; 70554359Sroberto pp->io.clock_recv = arc_receive; 706290001Sglebius pp->io.srcclock = peer; 70754359Sroberto pp->io.datalen = 0; 70854359Sroberto pp->io.fd = fd; 709290001Sglebius if (!io_addclock(&pp->io)) { 710290001Sglebius close(fd); 711290001Sglebius pp->io.fd = -1; 712290001Sglebius free(up); 713290001Sglebius return(0); 714290001Sglebius } 715290001Sglebius pp->unitptr = up; 71654359Sroberto 71754359Sroberto /* 71854359Sroberto * Initialize miscellaneous variables 71954359Sroberto */ 72054359Sroberto peer->precision = PRECISION; 72154359Sroberto peer->stratum = 2; /* Default to stratum 2 not 0. */ 72254359Sroberto pp->clockdesc = DESCRIPTION; 723132451Sroberto if (peer->MODE > 3) { 724132451Sroberto msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE); 725132451Sroberto return 0; 726132451Sroberto } 727132451Sroberto#ifdef DEBUG 728132451Sroberto if(debug) { printf("arc: mode = %d.\n", peer->MODE); } 729132451Sroberto#endif 730132451Sroberto switch (peer->MODE) { 731132451Sroberto case 1: 732132451Sroberto memcpy((char *)&pp->refid, REFID_MSF, 4); 733132451Sroberto break; 734132451Sroberto case 2: 735132451Sroberto memcpy((char *)&pp->refid, REFID_DCF77, 4); 736132451Sroberto break; 737132451Sroberto case 3: 738132451Sroberto memcpy((char *)&pp->refid, REFID_WWVB, 4); 739132451Sroberto break; 740132451Sroberto default: 741132451Sroberto memcpy((char *)&pp->refid, REFID, 4); 742132451Sroberto break; 743132451Sroberto } 74454359Sroberto /* Spread out resyncs so that they should remain separated. */ 74554359Sroberto up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009; 74654359Sroberto 74754359Sroberto#if 0 /* Not needed because of zeroing of arcunit structure... */ 74854359Sroberto up->resyncing = 0; /* Not resyncing yet. */ 74954359Sroberto up->saved_flags = 0; /* Default is all flags off. */ 75054359Sroberto /* Clear send buffer out... */ 75154359Sroberto { 75254359Sroberto int i; 75354359Sroberto for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; } 75454359Sroberto } 75554359Sroberto#endif 75654359Sroberto 75754359Sroberto#ifdef ARCRON_KEEN 75854359Sroberto up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */ 75954359Sroberto#else 76054359Sroberto up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */ 76154359Sroberto#endif 762132451Sroberto 763290001Sglebius peer->procptr->action = arc_event_handler; 764132451Sroberto 765132451Sroberto ENQUEUE(up); 766132451Sroberto 76754359Sroberto return(1); 76854359Sroberto} 76954359Sroberto 77054359Sroberto 77154359Sroberto/* 77254359Sroberto * arc_shutdown - shut down the clock 77354359Sroberto */ 77454359Srobertostatic void 77554359Srobertoarc_shutdown( 77654359Sroberto int unit, 77754359Sroberto struct peer *peer 77854359Sroberto ) 77954359Sroberto{ 78054359Sroberto register struct arcunit *up; 78154359Sroberto struct refclockproc *pp; 78254359Sroberto 783290001Sglebius peer->procptr->action = dummy_event_handler; 784132451Sroberto 78554359Sroberto pp = peer->procptr; 786290001Sglebius up = pp->unitptr; 787290001Sglebius if (-1 != pp->io.fd) 788290001Sglebius io_closeclock(&pp->io); 789290001Sglebius if (NULL != up) 790290001Sglebius free(up); 79154359Sroberto} 79254359Sroberto 79354359Sroberto/* 79454359SrobertoCompute space left in output buffer. 79554359Sroberto*/ 79654359Srobertostatic int 79754359Srobertospace_left( 79854359Sroberto register struct arcunit *up 79954359Sroberto ) 80054359Sroberto{ 80154359Sroberto int spaceleft; 80254359Sroberto 80354359Sroberto /* Compute space left in buffer after any pending output. */ 80454359Sroberto for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft) 80554359Sroberto { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } } 80654359Sroberto return(spaceleft); 80754359Sroberto} 80854359Sroberto 80954359Sroberto/* 81054359SrobertoSend command by copying into command buffer as far forward as possible, 81154359Srobertoafter any pending output. 81254359Sroberto 81354359SrobertoIndicate an error by returning 0 if there is not space for the command. 81454359Sroberto*/ 81554359Srobertostatic int 81654359Srobertosend_slow( 81754359Sroberto register struct arcunit *up, 81854359Sroberto int fd, 81954359Sroberto const char *s 82054359Sroberto ) 82154359Sroberto{ 82254359Sroberto int sl = strlen(s); 82354359Sroberto int spaceleft = space_left(up); 82454359Sroberto 825132451Sroberto#ifdef DEBUG 82654359Sroberto if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } 82754359Sroberto#endif 82854359Sroberto if(spaceleft < sl) { /* Should not normally happen... */ 829132451Sroberto#ifdef DEBUG 83054359Sroberto msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)", 831290001Sglebius sl, spaceleft); 83254359Sroberto#endif 833290001Sglebius return(0); /* FAILED! */ 83454359Sroberto } 83554359Sroberto 83654359Sroberto /* Copy in the command to be sent. */ 837182007Sroberto while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; } 83854359Sroberto 83954359Sroberto return(1); 84054359Sroberto} 84154359Sroberto 84254359Sroberto 843182007Srobertostatic int 844182007Srobertoget2(char *p, int *val) 845182007Sroberto{ 846290001Sglebius if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0; 847182007Sroberto *val = (p[0] - '0') * 10 + p[1] - '0'; 848182007Sroberto return 1; 849182007Sroberto} 850182007Sroberto 851182007Srobertostatic int 852182007Srobertoget1(char *p, int *val) 853182007Sroberto{ 854290001Sglebius if (!isdigit((unsigned char)p[0])) return 0; 855182007Sroberto *val = p[0] - '0'; 856182007Sroberto return 1; 857182007Sroberto} 858182007Sroberto 85954359Sroberto/* Macro indicating action we will take for different quality values. */ 86054359Sroberto#define quality_action(q) \ 86154359Sroberto(((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \ 86254359Sroberto (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \ 86354359Sroberto "OK, will use clock")) 86454359Sroberto 865290001Sglebius/* 86654359Sroberto * arc_receive - receive data from the serial interface 86754359Sroberto */ 868290001Sglebiusstatic void 86954359Srobertoarc_receive( 87054359Sroberto struct recvbuf *rbufp 87154359Sroberto ) 87254359Sroberto{ 87354359Sroberto register struct arcunit *up; 87454359Sroberto struct refclockproc *pp; 87554359Sroberto struct peer *peer; 87654359Sroberto char c; 877132451Sroberto int i, n, wday, month, flags, status; 87854359Sroberto int arc_last_offset; 879132451Sroberto static int quality_average = 0; 880132451Sroberto static int quality_sum = 0; 881132451Sroberto static int quality_polls = 0; 88254359Sroberto 88354359Sroberto /* 88454359Sroberto * Initialize pointers and read the timecode and timestamp 88554359Sroberto */ 886290001Sglebius peer = rbufp->recv_peer; 88754359Sroberto pp = peer->procptr; 888290001Sglebius up = pp->unitptr; 88954359Sroberto 89054359Sroberto 89154359Sroberto /* 89254359Sroberto If the command buffer is empty, and we are resyncing, insert a 89354359Sroberto g\r quality request into it to poll for signal quality again. 89454359Sroberto */ 89554359Sroberto if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) { 89654359Sroberto#ifdef DEBUG 89754359Sroberto if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); } 89854359Sroberto#endif 89954359Sroberto send_slow(up, pp->io.fd, "g\r"); 90054359Sroberto } 90154359Sroberto 90254359Sroberto /* 90354359Sroberto The `arc_last_offset' is the offset in lastcode[] of the last byte 90454359Sroberto received, and which we assume actually received the input 90554359Sroberto timestamp. 90654359Sroberto 90754359Sroberto (When we get round to using tty_clk and it is available, we 90854359Sroberto assume that we will receive the whole timecode with the 90954359Sroberto trailing \r, and that that \r will be timestamped. But this 91054359Sroberto assumption also works if receive the characters one-by-one.) 91154359Sroberto */ 91254359Sroberto arc_last_offset = pp->lencode+rbufp->recv_length - 1; 91354359Sroberto 91454359Sroberto /* 91554359Sroberto We catch a timestamp iff: 91654359Sroberto 91754359Sroberto * The command code is `o' for a timestamp. 91854359Sroberto 91954359Sroberto * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have 92054359Sroberto exactly char in the buffer (the command code) so that we 92154359Sroberto only sample the first character of the timecode as our 92254359Sroberto `on-time' character. 92354359Sroberto 92454359Sroberto * The first character in the buffer is not the echoed `\r' 92554359Sroberto from the `o` command (so if we are to timestamp an `\r' it 92654359Sroberto must not be first in the receive buffer with lencode==1. 92754359Sroberto (Even if we had other characters following it, we probably 92854359Sroberto would have a premature timestamp on the '\r'.) 92954359Sroberto 93054359Sroberto * We have received at least one character (I cannot imagine 93154359Sroberto how it could be otherwise, but anyway...). 93254359Sroberto */ 93354359Sroberto c = rbufp->recv_buffer[0]; 93454359Sroberto if((pp->a_lastcode[0] == 'o') && 93554359Sroberto#ifndef ARCRON_MULTIPLE_SAMPLES 93654359Sroberto (pp->lencode == 1) && 93754359Sroberto#endif 93854359Sroberto ((pp->lencode != 1) || (c != '\r')) && 93954359Sroberto (arc_last_offset >= 1)) { 94054359Sroberto /* Note that the timestamp should be corrected if >1 char rcvd. */ 94154359Sroberto l_fp timestamp; 94254359Sroberto timestamp = rbufp->recv_time; 94354359Sroberto#ifdef DEBUG 94454359Sroberto if(debug) { /* Show \r as `R', other non-printing char as `?'. */ 94554359Sroberto printf("arc: stamp -->%c<-- (%d chars rcvd)\n", 946290001Sglebius ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')), 94754359Sroberto rbufp->recv_length); 94854359Sroberto } 94954359Sroberto#endif 95054359Sroberto 95154359Sroberto /* 95254359Sroberto Now correct timestamp by offset of last byte received---we 95354359Sroberto subtract from the receive time the delay implied by the 95454359Sroberto extra characters received. 95554359Sroberto 95654359Sroberto Reject the input if the resulting code is too long, but 95754359Sroberto allow for the trailing \r, normally not used but a good 95854359Sroberto handle for tty_clk or somesuch kernel timestamper. 95954359Sroberto */ 96054359Sroberto if(arc_last_offset > LENARC) { 961132451Sroberto#ifdef DEBUG 96254359Sroberto if(debug) { 96354359Sroberto printf("arc: input code too long (%d cf %d); rejected.\n", 96454359Sroberto arc_last_offset, LENARC); 96554359Sroberto } 96654359Sroberto#endif 96754359Sroberto pp->lencode = 0; 96854359Sroberto refclock_report(peer, CEVNT_BADREPLY); 96954359Sroberto return; 97054359Sroberto } 97154359Sroberto 97254359Sroberto L_SUBUF(×tamp, charoffsets[arc_last_offset]); 973132451Sroberto#ifdef DEBUG 97454359Sroberto if(debug > 1) { 97554359Sroberto printf( 97654359Sroberto "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n", 97754359Sroberto ((rbufp->recv_length > 1) ? "*** " : ""), 97854359Sroberto rbufp->recv_length, 97954359Sroberto arc_last_offset, 98054359Sroberto mfptoms((unsigned long)0, 98154359Sroberto charoffsets[arc_last_offset], 98254359Sroberto 1)); 98354359Sroberto } 98454359Sroberto#endif 98554359Sroberto 98654359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES 98754359Sroberto /* 98854359Sroberto If taking multiple samples, capture the current adjusted 98954359Sroberto sample iff: 99054359Sroberto 99154359Sroberto * No timestamp has yet been captured (it is zero), OR 99254359Sroberto 99354359Sroberto * This adjusted timestamp is earlier than the one already 99454359Sroberto captured, on the grounds that this one suffered less 99554359Sroberto delay in being delivered to us and is more accurate. 99654359Sroberto 99754359Sroberto */ 99854359Sroberto if(L_ISZERO(&(up->lastrec)) || 99954359Sroberto L_ISGEQ(&(up->lastrec), ×tamp)) 100054359Sroberto#endif 100154359Sroberto { 1002132451Sroberto#ifdef DEBUG 100354359Sroberto if(debug > 1) { 100454359Sroberto printf("arc: system timestamp captured.\n"); 100554359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES 100654359Sroberto if(!L_ISZERO(&(up->lastrec))) { 100754359Sroberto l_fp diff; 100854359Sroberto diff = up->lastrec; 100954359Sroberto L_SUB(&diff, ×tamp); 101054359Sroberto printf("arc: adjusted timestamp by -%sms.\n", 1011290001Sglebius mfptoms(diff.l_ui, diff.l_uf, 3)); 101254359Sroberto } 101354359Sroberto#endif 101454359Sroberto } 101554359Sroberto#endif 101654359Sroberto up->lastrec = timestamp; 101754359Sroberto } 101854359Sroberto 101954359Sroberto } 102054359Sroberto 102154359Sroberto /* Just in case we still have lots of rubbish in the buffer... */ 102254359Sroberto /* ...and to avoid the same timestamp being reused by mistake, */ 102354359Sroberto /* eg on receipt of the \r coming in on its own after the */ 1024290001Sglebius /* timecode. */ 102554359Sroberto if(pp->lencode >= LENARC) { 1026132451Sroberto#ifdef DEBUG 102754359Sroberto if(debug && (rbufp->recv_buffer[0] != '\r')) 102854359Sroberto { printf("arc: rubbish in pp->a_lastcode[].\n"); } 102954359Sroberto#endif 103054359Sroberto pp->lencode = 0; 103154359Sroberto return; 103254359Sroberto } 103354359Sroberto 103454359Sroberto /* Append input to code buffer, avoiding overflow. */ 103554359Sroberto for(i = 0; i < rbufp->recv_length; i++) { 103654359Sroberto if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */ 103754359Sroberto c = rbufp->recv_buffer[i]; 103854359Sroberto 103954359Sroberto /* Drop trailing '\r's and drop `h' command echo totally. */ 104054359Sroberto if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; } 104154359Sroberto 104254359Sroberto /* 104354359Sroberto If we've just put an `o' in the lastcode[0], clear the 104454359Sroberto timestamp in anticipation of a timecode arriving soon. 104554359Sroberto 104654359Sroberto We would expect to get to process this before any of the 104754359Sroberto timecode arrives. 104854359Sroberto */ 104954359Sroberto if((c == 'o') && (pp->lencode == 1)) { 105054359Sroberto L_CLR(&(up->lastrec)); 1051132451Sroberto#ifdef DEBUG 105254359Sroberto if(debug > 1) { printf("arc: clearing timestamp.\n"); } 105354359Sroberto#endif 105454359Sroberto } 105554359Sroberto } 1056132451Sroberto if (pp->lencode == 0) return; 105754359Sroberto 105854359Sroberto /* Handle a quality message. */ 105954359Sroberto if(pp->a_lastcode[0] == 'g') { 106054359Sroberto int r, q; 106154359Sroberto 106254359Sroberto if(pp->lencode < 3) { return; } /* Need more data... */ 106354359Sroberto r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */ 106454359Sroberto q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */ 106554359Sroberto if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) || 106654359Sroberto ((r & 0x70) != 0x30)) { 106754359Sroberto /* Badly formatted response. */ 1068132451Sroberto#ifdef DEBUG 106954359Sroberto if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); } 107054359Sroberto#endif 107154359Sroberto return; 107254359Sroberto } 107354359Sroberto if(r == '3') { /* Only use quality value whilst sync in progress. */ 1074132451Sroberto if (up->quality_stamp < current_time) { 1075132451Sroberto struct calendar cal; 1076132451Sroberto l_fp new_stamp; 1077132451Sroberto 1078132451Sroberto get_systime (&new_stamp); 1079132451Sroberto caljulian (new_stamp.l_ui, &cal); 1080132451Sroberto up->quality_stamp = 1081132451Sroberto current_time + 60 - cal.second + 5; 1082132451Sroberto quality_sum = 0; 1083132451Sroberto quality_polls = 0; 1084132451Sroberto } 1085132451Sroberto quality_sum += (q & 0xf); 1086132451Sroberto quality_polls++; 1087132451Sroberto quality_average = (quality_sum / quality_polls); 108854359Sroberto#ifdef DEBUG 1089132451Sroberto if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); } 109054359Sroberto#endif 109154359Sroberto } else if( /* (r == '2') && */ up->resyncing) { 1092132451Sroberto up->quality = quality_average; 109354359Sroberto#ifdef DEBUG 109454359Sroberto if(debug) 109554359Sroberto { 109654359Sroberto printf("arc: sync finished, signal quality %d: %s\n", 109754359Sroberto up->quality, 109854359Sroberto quality_action(up->quality)); 109954359Sroberto } 110054359Sroberto#endif 110154359Sroberto msyslog(LOG_NOTICE, 1102290001Sglebius "ARCRON: sync finished, signal quality %d: %s", 1103290001Sglebius up->quality, 1104290001Sglebius quality_action(up->quality)); 110554359Sroberto up->resyncing = 0; /* Resync is over. */ 1106132451Sroberto quality_average = 0; 1107132451Sroberto quality_sum = 0; 1108132451Sroberto quality_polls = 0; 110954359Sroberto 111054359Sroberto#ifdef ARCRON_KEEN 111154359Sroberto /* Clock quality dubious; resync earlier than usual. */ 111254359Sroberto if((up->quality == QUALITY_UNKNOWN) || 111354359Sroberto (up->quality < MIN_CLOCK_QUALITY_OK)) 111454359Sroberto { up->next_resync = current_time + RETRY_RESYNC_TIME; } 111554359Sroberto#endif 111654359Sroberto } 111754359Sroberto pp->lencode = 0; 111854359Sroberto return; 111954359Sroberto } 112054359Sroberto 112154359Sroberto /* Stop now if this is not a timecode message. */ 112254359Sroberto if(pp->a_lastcode[0] != 'o') { 112354359Sroberto pp->lencode = 0; 112454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 112554359Sroberto return; 112654359Sroberto } 112754359Sroberto 112854359Sroberto /* If we don't have enough data, wait for more... */ 112954359Sroberto if(pp->lencode < LENARC) { return; } 113054359Sroberto 113154359Sroberto 113254359Sroberto /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */ 1133132451Sroberto#ifdef DEBUG 113454359Sroberto if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); } 113554359Sroberto#endif 113654359Sroberto 113754359Sroberto /* But check that we actually captured a system timestamp on it. */ 113854359Sroberto if(L_ISZERO(&(up->lastrec))) { 1139132451Sroberto#ifdef DEBUG 114054359Sroberto if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); } 114154359Sroberto#endif 114254359Sroberto pp->lencode = 0; 114354359Sroberto refclock_report(peer, CEVNT_BADREPLY); 114454359Sroberto return; 114554359Sroberto } 114654359Sroberto /* 114754359Sroberto Append a mark of the clock's received signal quality for the 114854359Sroberto benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown' 114954359Sroberto quality value to `6' for his s/w) and terminate the string for 115054359Sroberto sure. This should not go off the buffer end. 115154359Sroberto */ 115254359Sroberto pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ? 115354359Sroberto '6' : ('0' + up->quality)); 115454359Sroberto pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */ 115554359Sroberto 1156132451Sroberto#ifdef PRE_NTP420 115754359Sroberto /* We don't use the micro-/milli- second part... */ 115854359Sroberto pp->usec = 0; 115954359Sroberto pp->msec = 0; 1160132451Sroberto#else 1161132451Sroberto /* We don't use the nano-second part... */ 1162132451Sroberto pp->nsec = 0; 1163132451Sroberto#endif 116454359Sroberto /* Validate format and numbers. */ 1165182007Sroberto if (pp->a_lastcode[0] != 'o' 1166182007Sroberto || !get2(pp->a_lastcode + 1, &pp->hour) 1167182007Sroberto || !get2(pp->a_lastcode + 3, &pp->minute) 1168182007Sroberto || !get2(pp->a_lastcode + 5, &pp->second) 1169182007Sroberto || !get1(pp->a_lastcode + 7, &wday) 1170182007Sroberto || !get2(pp->a_lastcode + 8, &pp->day) 1171182007Sroberto || !get2(pp->a_lastcode + 10, &month) 1172182007Sroberto || !get2(pp->a_lastcode + 12, &pp->year)) { 1173132451Sroberto#ifdef DEBUG 117454359Sroberto /* Would expect to have caught major problems already... */ 117554359Sroberto if(debug) { printf("arc: badly formatted data.\n"); } 117654359Sroberto#endif 1177132451Sroberto pp->lencode = 0; 117854359Sroberto refclock_report(peer, CEVNT_BADREPLY); 117954359Sroberto return; 118054359Sroberto } 1181182007Sroberto flags = pp->a_lastcode[14]; 1182182007Sroberto status = pp->a_lastcode[15]; 1183182007Sroberto#ifdef DEBUG 1184182007Sroberto if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); } 1185182007Sroberto#endif 1186182007Sroberto n = 9; 1187182007Sroberto 118854359Sroberto /* 118954359Sroberto Validate received values at least enough to prevent internal 119054359Sroberto array-bounds problems, etc. 119154359Sroberto */ 119254359Sroberto if((pp->hour < 0) || (pp->hour > 23) || 119354359Sroberto (pp->minute < 0) || (pp->minute > 59) || 119454359Sroberto (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 119554359Sroberto (wday < 1) || (wday > 7) || 119654359Sroberto (pp->day < 1) || (pp->day > 31) || 119754359Sroberto (month < 1) || (month > 12) || 119854359Sroberto (pp->year < 0) || (pp->year > 99)) { 119954359Sroberto /* Data out of range. */ 1200132451Sroberto pp->lencode = 0; 120154359Sroberto refclock_report(peer, CEVNT_BADREPLY); 120254359Sroberto return; 120354359Sroberto } 1204132451Sroberto 1205132451Sroberto 1206132451Sroberto if(peer->MODE == 0) { /* compatiblity to original version */ 1207132451Sroberto int bst = flags; 1208132451Sroberto /* Check that BST/UTC bits are the complement of one another. */ 1209132451Sroberto if(!(bst & 2) == !(bst & 4)) { 1210132451Sroberto pp->lencode = 0; 1211132451Sroberto refclock_report(peer, CEVNT_BADREPLY); 1212132451Sroberto return; 1213132451Sroberto } 121454359Sroberto } 121554359Sroberto if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); } 121654359Sroberto 121754359Sroberto /* Year-2000 alert! */ 121854359Sroberto /* Attempt to wrap 2-digit date into sensible window. */ 121954359Sroberto if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */ 122054359Sroberto pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */ 122154359Sroberto /* 122254359Sroberto Attempt to do the right thing by screaming that the code will 122354359Sroberto soon break when we get to the end of its useful life. What a 122454359Sroberto hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X! 122554359Sroberto */ 122654359Sroberto if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */ 122754359Sroberto /*This should get attention B^> */ 122854359Sroberto msyslog(LOG_NOTICE, 1229290001Sglebius "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!"); 123054359Sroberto } 123154359Sroberto#ifdef DEBUG 123254359Sroberto if(debug) { 123354359Sroberto printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n", 123454359Sroberto n, 123554359Sroberto pp->hour, pp->minute, pp->second, 1236132451Sroberto pp->day, month, pp->year, flags, status); 123754359Sroberto } 123854359Sroberto#endif 123954359Sroberto 124054359Sroberto /* 124154359Sroberto The status value tested for is not strictly supported by the 124254359Sroberto clock spec since the value of bit 2 (0x4) is claimed to be 124354359Sroberto undefined for MSF, yet does seem to indicate if the last resync 124454359Sroberto was successful or not. 124554359Sroberto */ 124654359Sroberto pp->leap = LEAP_NOWARNING; 124754359Sroberto status &= 0x7; 124854359Sroberto if(status == 0x3) { 124954359Sroberto if(status != up->status) 125054359Sroberto { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); } 125154359Sroberto } else { 125254359Sroberto if(status != up->status) { 125354359Sroberto msyslog(LOG_NOTICE, "ARCRON: signal lost"); 125454359Sroberto pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */ 125554359Sroberto up->status = status; 1256132451Sroberto pp->lencode = 0; 125754359Sroberto refclock_report(peer, CEVNT_FAULT); 125854359Sroberto return; 125954359Sroberto } 126054359Sroberto } 126154359Sroberto up->status = status; 126254359Sroberto 1263132451Sroberto if (peer->MODE == 0) { /* compatiblity to original version */ 1264132451Sroberto int bst = flags; 126554359Sroberto 1266132451Sroberto pp->day += moff[month - 1]; 126754359Sroberto 1268132451Sroberto if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */ 1269132451Sroberto 1270132451Sroberto /* Convert to UTC if required */ 1271132451Sroberto if(bst & 2) { 1272132451Sroberto pp->hour--; 1273132451Sroberto if (pp->hour < 0) { 1274132451Sroberto pp->hour = 23; 1275132451Sroberto pp->day--; 1276132451Sroberto /* If we try to wrap round the year 1277132451Sroberto * (BST on 1st Jan), reject.*/ 1278132451Sroberto if(pp->day < 0) { 1279132451Sroberto pp->lencode = 0; 1280132451Sroberto refclock_report(peer, CEVNT_BADTIME); 1281132451Sroberto return; 1282132451Sroberto } 1283132451Sroberto } 1284132451Sroberto } 1285132451Sroberto } 1286132451Sroberto 1287132451Sroberto if(peer->MODE > 0) { 1288132451Sroberto if(pp->sloppyclockflag & CLK_FLAG1) { 1289132451Sroberto struct tm local; 1290290001Sglebius struct tm *gmtp; 1291290001Sglebius time_t unixtime; 1292132451Sroberto 1293290001Sglebius /* 1294290001Sglebius * Convert to GMT for sites that distribute localtime. 1295132451Sroberto * This means we have to do Y2K conversion on the 1296132451Sroberto * 2-digit year; otherwise, we get the time wrong. 1297290001Sglebius */ 1298290001Sglebius 1299290001Sglebius memset(&local, 0, sizeof(local)); 1300290001Sglebius 1301132451Sroberto local.tm_year = pp->year-1900; 1302290001Sglebius local.tm_mon = month-1; 1303290001Sglebius local.tm_mday = pp->day; 1304290001Sglebius local.tm_hour = pp->hour; 1305290001Sglebius local.tm_min = pp->minute; 1306290001Sglebius local.tm_sec = pp->second; 1307290001Sglebius switch (peer->MODE) { 1308132451Sroberto case 1: 1309132451Sroberto local.tm_isdst = (flags & 2); 1310132451Sroberto break; 1311132451Sroberto case 2: 1312290001Sglebius local.tm_isdst = (flags & 2); 1313132451Sroberto break; 1314132451Sroberto case 3: 1315132451Sroberto switch (flags & 3) { 1316132451Sroberto case 0: /* It is unclear exactly when the 1317290001Sglebius Arcron changes from DST->ST and 1318132451Sroberto ST->DST. Testing has shown this 1319132451Sroberto to be irregular. For the time 1320132451Sroberto being, let the OS decide. */ 1321290001Sglebius local.tm_isdst = 0; 1322132451Sroberto#ifdef DEBUG 1323132451Sroberto if (debug) 1324132451Sroberto printf ("arc: DST = 00 (0)\n"); 1325132451Sroberto#endif 1326132451Sroberto break; 1327132451Sroberto case 1: /* dst->st time */ 1328290001Sglebius local.tm_isdst = -1; 1329132451Sroberto#ifdef DEBUG 1330132451Sroberto if (debug) 1331132451Sroberto printf ("arc: DST = 01 (1)\n"); 1332132451Sroberto#endif 1333132451Sroberto break; 1334132451Sroberto case 2: /* st->dst time */ 1335290001Sglebius local.tm_isdst = -1; 1336132451Sroberto#ifdef DEBUG 1337132451Sroberto if (debug) 1338132451Sroberto printf ("arc: DST = 10 (2)\n"); 1339132451Sroberto#endif 1340132451Sroberto break; 1341132451Sroberto case 3: /* dst time */ 1342132451Sroberto local.tm_isdst = 1; 1343132451Sroberto#ifdef DEBUG 1344132451Sroberto if (debug) 1345132451Sroberto printf ("arc: DST = 11 (3)\n"); 1346132451Sroberto#endif 1347132451Sroberto break; 1348132451Sroberto } 1349132451Sroberto break; 1350132451Sroberto default: 1351132451Sroberto msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", 1352290001Sglebius peer->MODE); 135354359Sroberto return; 1354132451Sroberto break; 135554359Sroberto } 1356290001Sglebius unixtime = mktime (&local); 1357290001Sglebius if ((gmtp = gmtime (&unixtime)) == NULL) 1358290001Sglebius { 1359132451Sroberto pp->lencode = 0; 1360290001Sglebius refclock_report (peer, CEVNT_FAULT); 1361290001Sglebius return; 1362290001Sglebius } 1363132451Sroberto pp->year = gmtp->tm_year+1900; 1364290001Sglebius month = gmtp->tm_mon+1; 1365290001Sglebius pp->day = ymd2yd(pp->year,month,gmtp->tm_mday); 1366290001Sglebius /* pp->day = gmtp->tm_yday; */ 1367290001Sglebius pp->hour = gmtp->tm_hour; 1368290001Sglebius pp->minute = gmtp->tm_min; 1369290001Sglebius pp->second = gmtp->tm_sec; 1370132451Sroberto#ifdef DEBUG 1371290001Sglebius if (debug) 1372132451Sroberto { 1373132451Sroberto printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 1374132451Sroberto pp->year,month,gmtp->tm_mday,pp->hour,pp->minute, 1375132451Sroberto pp->second); 1376132451Sroberto } 1377132451Sroberto#endif 1378132451Sroberto } else 1379132451Sroberto { 1380290001Sglebius /* 1381290001Sglebius * For more rational sites distributing UTC 1382290001Sglebius */ 1383290001Sglebius pp->day = ymd2yd(pp->year,month,pp->day); 138454359Sroberto } 138554359Sroberto } 138654359Sroberto 1387132451Sroberto if (peer->MODE == 0) { /* compatiblity to original version */ 1388132451Sroberto /* If clock signal quality is 1389132451Sroberto * unknown, revert to default PRECISION...*/ 1390132451Sroberto if(up->quality == QUALITY_UNKNOWN) { 1391132451Sroberto peer->precision = PRECISION; 1392132451Sroberto } else { /* ...else improve precision if flag3 is set... */ 1393132451Sroberto peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 1394132451Sroberto HIGHPRECISION : PRECISION); 1395132451Sroberto } 1396132451Sroberto } else { 1397132451Sroberto if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) { 1398132451Sroberto peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 1399132451Sroberto HIGHPRECISION : PRECISION); 1400132451Sroberto } else if (up->quality == QUALITY_UNKNOWN) { 1401132451Sroberto peer->precision = PRECISION; 1402132451Sroberto } else { 1403132451Sroberto peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 1404132451Sroberto HIGHPRECISION : PRECISION); 1405132451Sroberto } 140654359Sroberto } 140754359Sroberto 140854359Sroberto /* Notice and log any change (eg from initial defaults) for flags. */ 140954359Sroberto if(up->saved_flags != pp->sloppyclockflag) { 1410132451Sroberto#ifdef DEBUG 141154359Sroberto msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s", 1412290001Sglebius ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."), 1413290001Sglebius ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."), 1414290001Sglebius ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."), 1415290001Sglebius ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : ".")); 141654359Sroberto /* Note effects of flags changing... */ 141754359Sroberto if(debug) { 141854359Sroberto printf("arc: PRECISION = %d.\n", peer->precision); 141954359Sroberto } 142054359Sroberto#endif 142154359Sroberto up->saved_flags = pp->sloppyclockflag; 142254359Sroberto } 142354359Sroberto 142454359Sroberto /* Note time of last believable timestamp. */ 142554359Sroberto pp->lastrec = up->lastrec; 142654359Sroberto 142754359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 142854359Sroberto /* Find out if a leap-second might just have happened... 142954359Sroberto (ie is this the first hour of the first day of Jan or Jul?) 143054359Sroberto */ 143154359Sroberto if((pp->hour == 0) && 143254359Sroberto (pp->day == 1) && 143354359Sroberto ((month == 1) || (month == 7))) { 143454359Sroberto if(possible_leap >= 0) { 143554359Sroberto /* A leap may have happened, and no resync has started yet...*/ 143654359Sroberto possible_leap = 1; 143754359Sroberto } 143854359Sroberto } else { 143954359Sroberto /* Definitely not leap-second territory... */ 144054359Sroberto possible_leap = 0; 144154359Sroberto } 144254359Sroberto#endif 144354359Sroberto 144454359Sroberto if (!refclock_process(pp)) { 1445132451Sroberto pp->lencode = 0; 144654359Sroberto refclock_report(peer, CEVNT_BADTIME); 144754359Sroberto return; 144854359Sroberto } 1449132451Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 145054359Sroberto refclock_receive(peer); 145154359Sroberto} 145254359Sroberto 145354359Sroberto 145454359Sroberto/* request_time() sends a time request to the clock with given peer. */ 145554359Sroberto/* This automatically reports a fault if necessary. */ 145654359Sroberto/* No data should be sent after this until arc_poll() returns. */ 1457290001Sglebiusstatic void request_time (int, struct peer *); 145854359Srobertostatic void 145954359Srobertorequest_time( 146054359Sroberto int unit, 146154359Sroberto struct peer *peer 146254359Sroberto ) 146354359Sroberto{ 146454359Sroberto struct refclockproc *pp = peer->procptr; 1465290001Sglebius register struct arcunit *up = pp->unitptr; 146654359Sroberto#ifdef DEBUG 146754359Sroberto if(debug) { printf("arc: unit %d: requesting time.\n", unit); } 146854359Sroberto#endif 146954359Sroberto if (!send_slow(up, pp->io.fd, "o\r")) { 1470132451Sroberto#ifdef DEBUG 1471132451Sroberto if (debug) { 1472132451Sroberto printf("arc: unit %d: problem sending", unit); 1473132451Sroberto } 147454359Sroberto#endif 1475132451Sroberto pp->lencode = 0; 147654359Sroberto refclock_report(peer, CEVNT_FAULT); 147754359Sroberto return; 147854359Sroberto } 147954359Sroberto pp->polls++; 148054359Sroberto} 148154359Sroberto 148254359Sroberto/* 148354359Sroberto * arc_poll - called by the transmit procedure 148454359Sroberto */ 148554359Srobertostatic void 148654359Srobertoarc_poll( 148754359Sroberto int unit, 148854359Sroberto struct peer *peer 148954359Sroberto ) 149054359Sroberto{ 149154359Sroberto register struct arcunit *up; 149254359Sroberto struct refclockproc *pp; 149354359Sroberto int resync_needed; /* Should we start a resync? */ 149454359Sroberto 149554359Sroberto pp = peer->procptr; 1496290001Sglebius up = pp->unitptr; 1497132451Sroberto#if 0 149854359Sroberto pp->lencode = 0; 149954359Sroberto memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); 1500132451Sroberto#endif 150154359Sroberto 150254359Sroberto#if 0 150354359Sroberto /* Flush input. */ 150454359Sroberto tcflush(pp->io.fd, TCIFLUSH); 150554359Sroberto#endif 150654359Sroberto 150754359Sroberto /* Resync if our next scheduled resync time is here or has passed. */ 1508132451Sroberto resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) && 1509132451Sroberto (up->next_resync <= current_time) ); 151054359Sroberto 151154359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 151254359Sroberto /* 151354359Sroberto Try to catch a potential leap-second insertion or deletion quickly. 151454359Sroberto 151554359Sroberto In addition to the normal NTP fun of clocks that don't report 151654359Sroberto leap-seconds spooking their hosts, this clock does not even 151754359Sroberto sample the radio sugnal the whole time, so may miss a 151854359Sroberto leap-second insertion or deletion for up to a whole sample 151954359Sroberto time. 152054359Sroberto 152154359Sroberto To try to minimise this effect, if in the first few minutes of 152254359Sroberto the day immediately following a leap-second-insertion point 152354359Sroberto (ie in the first hour of the first day of the first and sixth 152454359Sroberto months), and if the last resync was in the previous day, and a 152554359Sroberto resync is not already in progress, resync the clock 152654359Sroberto immediately. 152754359Sroberto 152854359Sroberto */ 152954359Sroberto if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */ 153054359Sroberto (!up->resyncing)) { /* No resync in progress yet. */ 153154359Sroberto resync_needed = 1; 153254359Sroberto possible_leap = -1; /* Prevent multiple resyncs. */ 153354359Sroberto msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit); 153454359Sroberto } 153554359Sroberto#endif 153654359Sroberto 153754359Sroberto /* Do a resync if required... */ 153854359Sroberto if(resync_needed) { 153954359Sroberto /* First, reset quality value to `unknown' so we can detect */ 154054359Sroberto /* when a quality message has been responded to by this */ 154154359Sroberto /* being set to some other value. */ 154254359Sroberto up->quality = QUALITY_UNKNOWN; 154354359Sroberto 154454359Sroberto /* Note that we are resyncing... */ 154554359Sroberto up->resyncing = 1; 154654359Sroberto 154754359Sroberto /* Now actually send the resync command and an immediate poll. */ 154854359Sroberto#ifdef DEBUG 154954359Sroberto if(debug) { printf("arc: sending resync command (h\\r).\n"); } 155054359Sroberto#endif 155154359Sroberto msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit); 155254359Sroberto send_slow(up, pp->io.fd, "h\r"); 155354359Sroberto 155454359Sroberto /* Schedule our next resync... */ 155554359Sroberto up->next_resync = current_time + DEFAULT_RESYNC_TIME; 155654359Sroberto 155754359Sroberto /* Drop through to request time if appropriate. */ 155854359Sroberto } 155954359Sroberto 156054359Sroberto /* If clock quality is too poor to trust, indicate a fault. */ 156154359Sroberto /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/ 156254359Sroberto /* we'll cross our fingers and just hope that the thing */ 156354359Sroberto /* synced so quickly we did not catch it---we'll */ 156454359Sroberto /* double-check the clock is OK elsewhere. */ 156554359Sroberto if( 156654359Sroberto#ifdef ARCRON_KEEN 156754359Sroberto (up->quality != QUALITY_UNKNOWN) && 156854359Sroberto#else 156954359Sroberto (up->quality == QUALITY_UNKNOWN) || 157054359Sroberto#endif 157154359Sroberto (up->quality < MIN_CLOCK_QUALITY_OK)) { 157254359Sroberto#ifdef DEBUG 157354359Sroberto if(debug) { 157454359Sroberto printf("arc: clock quality %d too poor.\n", up->quality); 157554359Sroberto } 157654359Sroberto#endif 1577132451Sroberto pp->lencode = 0; 157854359Sroberto refclock_report(peer, CEVNT_FAULT); 157954359Sroberto return; 158054359Sroberto } 158154359Sroberto /* This is the normal case: request a timestamp. */ 158254359Sroberto request_time(unit, peer); 158354359Sroberto} 158454359Sroberto 158554359Sroberto#else 1586290001SglebiusNONEMPTY_TRANSLATION_UNIT 158754359Sroberto#endif 1588