refclock_arc.c revision 54359
154359Sroberto/* 254359Sroberto * refclock_arc - clock driver for ARCRON MSF receivers 354359Sroberto */ 454359Sroberto 554359Sroberto#ifdef HAVE_CONFIG_H 654359Sroberto#include <config.h> 754359Sroberto#endif 854359Sroberto 954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF) 1054359Srobertostatic const char arc_version[] = { "V1.1 1997/06/23" }; 1154359Sroberto 1254359Sroberto#undef ARCRON_DEBUG /* Define only while in development... */ 1354359Sroberto 1454359Sroberto#ifndef ARCRON_NOT_KEEN 1554359Sroberto#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */ 1654359Sroberto#endif 1754359Sroberto 1854359Sroberto#ifndef ARCRON_NOT_MULTIPLE_SAMPLES 1954359Sroberto#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */ 2054359Sroberto#endif 2154359Sroberto 2254359Sroberto#ifndef ARCRON_NOT_LEAPSECOND_KEEN 2354359Sroberto#ifndef ARCRON_LEAPSECOND_KEEN 2454359Sroberto#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */ 2554359Sroberto#endif 2654359Sroberto#endif 2754359Sroberto 2854359Sroberto/* 2954359SrobertoCode by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997. 3054359SrobertoModifications by Damon Hart-Davis, <d@hd.org>, 1997. 3154359Sroberto 3254359SrobertoTHIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT 3354359SrobertoYOUR OWN RISK. 3454359Sroberto 3554359SrobertoOrginally developed and used with ntp3-5.85 by Derek Mulcahy. 3654359Sroberto 3754359SrobertoBuilt against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2. 3854359Sroberto 3954359SrobertoThis code may be freely copied and used and incorporated in other 4054359Srobertosystems providing the disclaimer and notice of authorship are 4154359Srobertoreproduced. 4254359Sroberto 4354359Sroberto------------------------------------------------------------------------------- 4454359Sroberto 4554359SrobertoAuthor's original note: 4654359Sroberto 4754359SrobertoI enclose my ntp driver for the Galleon Systems Arc MSF receiver. 4854359Sroberto 4954359SrobertoIt works (after a fashion) on both Solaris-1 and Solaris-2. 5054359Sroberto 5154359SrobertoI am currently using ntp3-5.85. I have been running the code for 5254359Srobertoabout 7 months without any problems. Even coped with the change to BST! 5354359Sroberto 5454359SrobertoI had to do some funky things to read from the clock because it uses the 5554359Srobertopower from the receive lines to drive the transmit lines. This makes the 5654359Srobertocode look a bit stupid but it works. I also had to put in some delays to 5754359Srobertoallow for the turnaround time from receive to transmit. These delays 5854359Srobertoare between characters when requesting a time stamp so that shouldn't affect 5954359Srobertothe results too drastically. 6054359Sroberto 6154359Sroberto... 6254359Sroberto 6354359SrobertoThe bottom line is that it works but could easily be improved. You are 6454359Srobertofree to do what you will with the code. I haven't been able to determine 6554359Srobertohow good the clock is. I think that this requires a known good clock 6654359Srobertoto compare it against. 6754359Sroberto 6854359Sroberto------------------------------------------------------------------------------- 6954359Sroberto 7054359SrobertoDamon's notes for adjustments: 7154359Sroberto 7254359SrobertoMAJOR CHANGES SINCE V1.0 7354359Sroberto======================== 7454359Sroberto 1) Removal of pollcnt variable that made the clock go permanently 7554359Sroberto off-line once two time polls failed to gain responses. 7654359Sroberto 7754359Sroberto 2) Avoiding (at least on Solaris-2) terminal becoming the controlling 7854359Sroberto terminal of the process when we do a low-level open(). 7954359Sroberto 8054359Sroberto 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being 8154359Sroberto defined) to try to resync quickly after a potential leap-second 8254359Sroberto insertion or deletion. 8354359Sroberto 8454359Sroberto 4) Code significantly slimmer at run-time than V1.0. 8554359Sroberto 8654359Sroberto 8754359SrobertoGENERAL 8854359Sroberto======= 8954359Sroberto 9054359Sroberto 1) The C preprocessor symbol to have the clock built has been changed 9154359Sroberto from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the 9254359Sroberto possiblity of clashes with other symbols in the future. 9354359Sroberto 9454359Sroberto 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons: 9554359Sroberto 9654359Sroberto a) The ARC documentation claims the internal clock is (only) 9754359Sroberto accurate to about 20ms relative to Rugby (plus there must be 9854359Sroberto noticable drift and delay in the ms range due to transmission 9954359Sroberto delays and changing atmospheric effects). This clock is not 10054359Sroberto designed for ms accuracy as NTP has spoilt us all to expect. 10154359Sroberto 10254359Sroberto b) The clock oscillator looks like a simple uncompensated quartz 10354359Sroberto crystal of the sort used in digital watches (ie 32768Hz) which 10454359Sroberto can have large temperature coefficients and drifts; it is not 10554359Sroberto clear if this oscillator is properly disciplined to the MSF 10654359Sroberto transmission, but as the default is to resync only once per 10754359Sroberto *day*, we can imagine that it is not, and is free-running. We 10854359Sroberto can minimise drift by resyncing more often (at the cost of 10954359Sroberto reduced battery life), but drift/wander may still be 11054359Sroberto significant. 11154359Sroberto 11254359Sroberto c) Note that the bit time of 3.3ms adds to the potential error in 11354359Sroberto the the clock timestamp, since the bit clock of the serial link 11454359Sroberto may effectively be free-running with respect to the host clock 11554359Sroberto and the MSF clock. Actually, the error is probably 1/16th of 11654359Sroberto the above, since the input data is probably sampled at at least 11754359Sroberto 16x the bit rate. 11854359Sroberto 11954359Sroberto By keeping the clock marked as not very precise, it will have a 12054359Sroberto fairly large dispersion, and thus will tend to be used as a 12154359Sroberto `backup' time source and sanity checker, which this clock is 12254359Sroberto probably ideal for. For an isolated network without other time 12354359Sroberto sources, this clock can probably be expected to provide *much* 12454359Sroberto better than 1s accuracy, which will be fine. 12554359Sroberto 12654359Sroberto By default, PRECISION is set to -4, but experience, especially at a 12754359Sroberto particular geographic location with a particular clock, may allow 12854359Sroberto this to be altered to -5. (Note that skews of +/- 10ms are to be 12954359Sroberto expected from the clock from time-to-time.) This improvement of 13054359Sroberto reported precision can be instigated by setting flag3 to 1, though 13154359Sroberto the PRECISION will revert to the normal value while the clock 13254359Sroberto signal quality is unknown whatever the flag3 setting. 13354359Sroberto 13454359Sroberto IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE 13554359Sroberto ANY RESIDUAL SKEW, eg: 13654359Sroberto 13754359Sroberto server 127.127.27.0 # ARCRON MSF radio clock unit 0. 13854359Sroberto # Fudge timestamps by about 20ms. 13954359Sroberto fudge 127.127.27.0 time1 0.020 14054359Sroberto 14154359Sroberto You will need to observe your system's behaviour, assuming you have 14254359Sroberto some other NTP source to compare it with, to work out what the 14354359Sroberto fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with 14454359Sroberto my MSF clock with my distance from the MSF transmitter, +20ms 14554359Sroberto seemed about right, after some observation. 14654359Sroberto 14754359Sroberto 3) REFID has been made "MSFa" to reflect the MSF time source and the 14854359Sroberto ARCRON receiver. 14954359Sroberto 15054359Sroberto 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before 15154359Sroberto forcing a resync since the last attempt. This is picked to give a 15254359Sroberto little less than an hour between resyncs and to try to avoid 15354359Sroberto clashing with any regular event at a regular time-past-the-hour 15454359Sroberto which might cause systematic errors. 15554359Sroberto 15654359Sroberto The INITIAL_RESYNC_DELAY is to avoid bothering the clock and 15754359Sroberto running down its batteries unnecesarily if ntpd is going to crash 15854359Sroberto or be killed or reconfigured quickly. If ARCRON_KEEN is defined 15954359Sroberto then this period is long enough for (with normal polling rates) 16054359Sroberto enough time samples to have been taken to allow ntpd to sync to 16154359Sroberto the clock before the interruption for the clock to resync to MSF. 16254359Sroberto This avoids ntpd syncing to another peer first and then 16354359Sroberto almost immediately hopping to the MSF clock. 16454359Sroberto 16554359Sroberto The RETRY_RESYNC_TIME is used before rescheduling a resync after a 16654359Sroberto resync failed to reveal a statisfatory signal quality (too low or 16754359Sroberto unknown). 16854359Sroberto 16954359Sroberto 5) The clock seems quite jittery, so I have increased the 17054359Sroberto median-filter size from the typical (previous) value of 3. I 17154359Sroberto discard up to half the results in the filter. It looks like maybe 17254359Sroberto 1 sample in 10 or so (maybe less) is a spike, so allow the median 17354359Sroberto filter to discard at least 10% of its entries or 1 entry, whichever 17454359Sroberto is greater. 17554359Sroberto 17654359Sroberto 6) Sleeping *before* each character sent to the unit to allow required 17754359Sroberto inter-character time but without introducting jitter and delay in 17854359Sroberto handling the response if possible. 17954359Sroberto 18054359Sroberto 7) If the flag ARCRON_KEEN is defined, take time samples whenever 18154359Sroberto possible, even while resyncing, etc. We rely, in this case, on the 18254359Sroberto clock always giving us a reasonable time or else telling us in the 18354359Sroberto status byte at the end of the timestamp that it failed to sync to 18454359Sroberto MSF---thus we should never end up syncing to completely the wrong 18554359Sroberto time. 18654359Sroberto 18754359Sroberto 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of 18854359Sroberto refclock median-filter routines to get round small bug in 3-5.90 18954359Sroberto code which does not return the median offset. XXX Removed this 19054359Sroberto bit due NTP Version 4 upgrade - dlm. 19154359Sroberto 19254359Sroberto 9) We would appear to have a year-2000 problem with this clock since 19354359Sroberto it returns only the two least-significant digits of the year. But 19454359Sroberto ntpd ignores the year and uses the local-system year instead, so 19554359Sroberto this is in fact not a problem. Nevertheless, we attempt to do a 19654359Sroberto sensible thing with the dates, wrapping them into a 100-year 19754359Sroberto window. 19854359Sroberto 19954359Sroberto 10)Logs stats information that can be used by Derek's Tcl/Tk utility 20054359Sroberto to show the status of the clock. 20154359Sroberto 20254359Sroberto 11)The clock documentation insists that the number of bits per 20354359Sroberto character to be sent to the clock, and sent by it, is 11, including 20454359Sroberto one start bit and two stop bits. The data format is either 7+even 20554359Sroberto or 8+none. 20654359Sroberto 20754359Sroberto 20854359SrobertoTO-DO LIST 20954359Sroberto========== 21054359Sroberto 21154359Sroberto * Eliminate use of scanf(), and maybe sprintf(). 21254359Sroberto 21354359Sroberto * Allow user setting of resync interval to trade battery life for 21454359Sroberto accuracy; maybe could be done via fudge factor or unit number. 21554359Sroberto 21654359Sroberto * Possibly note the time since the last resync of the MSF clock to 21754359Sroberto MSF as the age of the last reference timestamp, ie trust the 21854359Sroberto clock's oscillator not very much... 21954359Sroberto 22054359Sroberto * Add very slow auto-adjustment up to a value of +/- time2 to correct 22154359Sroberto for long-term errors in the clock value (time2 defaults to 0 so the 22254359Sroberto correction would be disabled by default). 22354359Sroberto 22454359Sroberto * Consider trying to use the tty_clk/ppsclock support. 22554359Sroberto 22654359Sroberto * Possibly use average or maximum signal quality reported during 22754359Sroberto resync, rather than just the last one, which may be atypical. 22854359Sroberto 22954359Sroberto*/ 23054359Sroberto 23154359Sroberto 23254359Sroberto/* Notes for HKW Elektronik GmBH Radio clock driver */ 23354359Sroberto/* Author Lyndon David, Sentinet Ltd, Feb 1997 */ 23454359Sroberto/* These notes seem also to apply usefully to the ARCRON clock. */ 23554359Sroberto 23654359Sroberto/* The HKW clock module is a radio receiver tuned into the Rugby */ 23754359Sroberto/* MSF time signal tranmitted on 60 kHz. The clock module connects */ 23854359Sroberto/* to the computer via a serial line and transmits the time encoded */ 23954359Sroberto/* in 15 bytes at 300 baud 7 bits two stop bits even parity */ 24054359Sroberto 24154359Sroberto/* Clock communications, from the datasheet */ 24254359Sroberto/* All characters sent to the clock are echoed back to the controlling */ 24354359Sroberto/* device. */ 24454359Sroberto/* Transmit time/date information */ 24554359Sroberto/* syntax ASCII o<cr> */ 24654359Sroberto/* Character o may be replaced if neccesary by a character whose code */ 24754359Sroberto/* contains the lowest four bits f(hex) eg */ 24854359Sroberto/* syntax binary: xxxx1111 00001101 */ 24954359Sroberto 25054359Sroberto/* DHD note: 25154359SrobertoYou have to wait for character echo + 10ms before sending next character. 25254359Sroberto*/ 25354359Sroberto 25454359Sroberto/* The clock replies to this command with a sequence of 15 characters */ 25554359Sroberto/* which contain the complete time and a final <cr> making 16 characters */ 25654359Sroberto/* in total. */ 25754359Sroberto/* The RC computer clock will not reply immediately to this command because */ 25854359Sroberto/* the start bit edge of the first reply character marks the beginning of */ 25954359Sroberto/* the second. So the RC Computer Clock will reply to this command at the */ 26054359Sroberto/* start of the next second */ 26154359Sroberto/* The characters have the following meaning */ 26254359Sroberto/* 1. hours tens */ 26354359Sroberto/* 2. hours units */ 26454359Sroberto/* 3. minutes tens */ 26554359Sroberto/* 4. minutes units */ 26654359Sroberto/* 5. seconds tens */ 26754359Sroberto/* 6. seconds units */ 26854359Sroberto/* 7. day of week 1-monday 7-sunday */ 26954359Sroberto/* 8. day of month tens */ 27054359Sroberto/* 9. day of month units */ 27154359Sroberto/* 10. month tens */ 27254359Sroberto/* 11. month units */ 27354359Sroberto/* 12. year tens */ 27454359Sroberto/* 13. year units */ 27554359Sroberto/* 14. BST/UTC status */ 27654359Sroberto/* bit 7 parity */ 27754359Sroberto/* bit 6 always 0 */ 27854359Sroberto/* bit 5 always 1 */ 27954359Sroberto/* bit 4 always 1 */ 28054359Sroberto/* bit 3 always 0 */ 28154359Sroberto/* bit 2 =1 if UTC is in effect, complementary to the BST bit */ 28254359Sroberto/* bit 1 =1 if BST is in effect, according to the BST bit */ 28354359Sroberto/* bit 0 BST/UTC change impending bit=1 in case of change impending */ 28454359Sroberto/* 15. status */ 28554359Sroberto/* bit 7 parity */ 28654359Sroberto/* bit 6 always 0 */ 28754359Sroberto/* bit 5 always 1 */ 28854359Sroberto/* bit 4 always 1 */ 28954359Sroberto/* bit 3 =1 if low battery is detected */ 29054359Sroberto/* bit 2 =1 if the very last reception attempt failed and a valid */ 29154359Sroberto/* time information already exists (bit0=1) */ 29254359Sroberto/* =0 if the last reception attempt was successful */ 29354359Sroberto/* bit 1 =1 if at least one reception since 2:30 am was successful */ 29454359Sroberto/* =0 if no reception attempt since 2:30 am was successful */ 29554359Sroberto/* bit 0 =1 if the RC Computer Clock contains valid time information */ 29654359Sroberto/* This bit is zero after reset and one after the first */ 29754359Sroberto/* successful reception attempt */ 29854359Sroberto 29954359Sroberto/* DHD note: 30054359SrobertoAlso note g<cr> command which confirms that a resync is in progress, and 30154359Srobertoif so what signal quality (0--5) is available. 30254359SrobertoAlso note h<cr> command which starts a resync to MSF signal. 30354359Sroberto*/ 30454359Sroberto 30554359Sroberto 30654359Sroberto 30754359Sroberto#include <stdio.h> 30854359Sroberto#include <ctype.h> 30954359Sroberto#include <sys/time.h> 31054359Sroberto 31154359Sroberto#if defined(HAVE_BSD_TTYS) 31254359Sroberto#include <sgtty.h> 31354359Sroberto#endif /* HAVE_BSD_TTYS */ 31454359Sroberto 31554359Sroberto#if defined(HAVE_SYSV_TTYS) 31654359Sroberto#include <termio.h> 31754359Sroberto#endif /* HAVE_SYSV_TTYS */ 31854359Sroberto 31954359Sroberto#if defined(HAVE_TERMIOS) 32054359Sroberto#include <termios.h> 32154359Sroberto#endif 32254359Sroberto 32354359Sroberto#include "ntpd.h" 32454359Sroberto#include "ntp_io.h" 32554359Sroberto#include "ntp_refclock.h" 32654359Sroberto#include "ntp_stdlib.h" 32754359Sroberto 32854359Sroberto/* 32954359Sroberto * This driver supports the ARCRON MSF Radio Controlled Clock 33054359Sroberto */ 33154359Sroberto 33254359Sroberto/* 33354359Sroberto * Interface definitions 33454359Sroberto */ 33554359Sroberto#define DEVICE "/dev/arc%d" /* Device name and unit. */ 33654359Sroberto#define SPEED B300 /* UART speed (300 baud) */ 33754359Sroberto#define PRECISION (-4) /* Precision (~63 ms). */ 33854359Sroberto#define HIGHPRECISION (-5) /* If things are going well... */ 33954359Sroberto#define REFID "MSFa" /* Reference ID. */ 34054359Sroberto#define DESCRIPTION "ARCRON MSF Receiver" 34154359Sroberto 34254359Sroberto#define NSAMPLESLONG 8 /* Stages of long filter. */ 34354359Sroberto 34454359Sroberto#define LENARC 16 /* Format `o' timecode length. */ 34554359Sroberto 34654359Sroberto#define BITSPERCHAR 11 /* Bits per character. */ 34754359Sroberto#define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */ 34854359Sroberto#define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */ 34954359Sroberto#define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */ 35054359Sroberto#define CHARTIME /* Time for char at 300bps. */ \ 35154359Sroberto( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \ 35254359Sroberto (BITSPERCHAR * BITTIME) ) ) 35354359Sroberto 35454359Sroberto /* Allow for UART to accept char half-way through final stop bit. */ 35554359Sroberto#define INITIALOFFSET (u_int32)(-BITTIME/2) 35654359Sroberto 35754359Sroberto /* 35854359Sroberto charoffsets[x] is the time after the start of the second that byte 35954359Sroberto x (with the first byte being byte 1) is received by the UART, 36054359Sroberto assuming that the initial edge of the start bit of the first byte 36154359Sroberto is on-time. The values are represented as the fractional part of 36254359Sroberto an l_fp. 36354359Sroberto 36454359Sroberto We store enough values to have the offset of each byte including 36554359Sroberto the trailing \r, on the assumption that the bytes follow one 36654359Sroberto another without gaps. 36754359Sroberto */ 36854359Sroberto static const u_int32 charoffsets[LENARC+1] = { 36954359Sroberto#if BITSPERCHAR == 11 /* Usual case. */ 37054359Sroberto /* Offsets computed as accurately as possible... */ 37154359Sroberto 0, 37254359Sroberto INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */ 37354359Sroberto INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */ 37454359Sroberto INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */ 37554359Sroberto INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */ 37654359Sroberto INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */ 37754359Sroberto INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */ 37854359Sroberto INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */ 37954359Sroberto INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */ 38054359Sroberto INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */ 38154359Sroberto INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */ 38254359Sroberto INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */ 38354359Sroberto INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */ 38454359Sroberto INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */ 38554359Sroberto INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */ 38654359Sroberto INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */ 38754359Sroberto INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */ 38854359Sroberto#else 38954359Sroberto /* Offsets computed with a small rounding error... */ 39054359Sroberto 0, 39154359Sroberto INITIALOFFSET + 1 * CHARTIME, 39254359Sroberto INITIALOFFSET + 2 * CHARTIME, 39354359Sroberto INITIALOFFSET + 3 * CHARTIME, 39454359Sroberto INITIALOFFSET + 4 * CHARTIME, 39554359Sroberto INITIALOFFSET + 5 * CHARTIME, 39654359Sroberto INITIALOFFSET + 6 * CHARTIME, 39754359Sroberto INITIALOFFSET + 7 * CHARTIME, 39854359Sroberto INITIALOFFSET + 8 * CHARTIME, 39954359Sroberto INITIALOFFSET + 9 * CHARTIME, 40054359Sroberto INITIALOFFSET + 10 * CHARTIME, 40154359Sroberto INITIALOFFSET + 11 * CHARTIME, 40254359Sroberto INITIALOFFSET + 12 * CHARTIME, 40354359Sroberto INITIALOFFSET + 13 * CHARTIME, 40454359Sroberto INITIALOFFSET + 14 * CHARTIME, 40554359Sroberto INITIALOFFSET + 15 * CHARTIME, 40654359Sroberto INITIALOFFSET + 16 * CHARTIME 40754359Sroberto#endif 40854359Sroberto }; 40954359Sroberto 41054359Sroberto/* Chose filter length dependent on fudge flag 4. */ 41154359Sroberto#define CHOSENSAMPLES(pp) \ 41254359Sroberto(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES) 41354359Sroberto /* 41454359SrobertoChose how many filter samples to keep. Several factors are in play. 41554359Sroberto 41654359Sroberto 1) Discard at least one sample to allow a spike value to be 41754359Sroberto discarded. 41854359Sroberto 41954359Sroberto 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes. 42054359Sroberto 42154359Sroberto 3) Keep an odd number of samples to avoid median value being biased 42254359Sroberto high or low. 42354359Sroberto*/ 42454359Sroberto#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1) 42554359Sroberto 42654359Sroberto#define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */ 42754359Sroberto#define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */ 42854359Sroberto#ifdef ARCRON_KEEN 42954359Sroberto#define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */ 43054359Sroberto#else 43154359Sroberto#define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */ 43254359Sroberto#endif 43354359Sroberto 43454359Sroberto static const int moff[12] = 43554359Sroberto{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 43654359Sroberto/* Flags for a raw open() of the clock serial device. */ 43754359Sroberto#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */ 43854359Sroberto#define OPEN_FLAGS (O_RDWR | O_NOCTTY) 43954359Sroberto#else /* Oh well, it may not matter... */ 44054359Sroberto#define OPEN_FLAGS (O_RDWR) 44154359Sroberto#endif 44254359Sroberto 44354359Sroberto 44454359Sroberto/* Length of queue of command bytes to be sent. */ 44554359Sroberto#define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */ 44654359Sroberto/* Queue tick time; interval in seconds between chars taken off queue. */ 44754359Sroberto/* Must be >= 2 to allow o\r response to come back uninterrupted. */ 44854359Sroberto#define QUEUETICK 2 /* Allow o\r reply to finish. */ 44954359Sroberto 45054359Sroberto/* 45154359Sroberto * ARC unit control structure 45254359Sroberto */ 45354359Srobertostruct arcunit { 45454359Sroberto l_fp lastrec; /* Time tag for the receive time (system). */ 45554359Sroberto int status; /* Clock status. */ 45654359Sroberto 45754359Sroberto int quality; /* Quality of reception 0--5 for unit. */ 45854359Sroberto /* We may also use the values -1 or 6 internally. */ 45954359Sroberto 46054359Sroberto u_long next_resync; /* Next resync time (s) compared to current_time. */ 46154359Sroberto int resyncing; /* Resync in progress if true. */ 46254359Sroberto 46354359Sroberto /* In the outgoing queue, cmdqueue[0] is next to be sent. */ 46454359Sroberto char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */ 46554359Sroberto 46654359Sroberto u_long saved_flags; /* Saved fudge flags. */ 46754359Sroberto}; 46854359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 46954359Sroberto/* The flag `possible_leap' is set non-zero when any MSF unit 47054359Sroberto thinks a leap-second may have happened. 47154359Sroberto 47254359Sroberto Set whenever we receive a valid time sample in the first hour of 47354359Sroberto the first day of the first/seventh months. 47454359Sroberto 47554359Sroberto Outside the special hour this value is unconditionally set 47654359Sroberto to zero by the receive routine. 47754359Sroberto 47854359Sroberto On finding itself in this timeslot, as long as the value is 47954359Sroberto non-negative, the receive routine sets it to a positive value to 48054359Sroberto indicate a resync to MSF should be performed. 48154359Sroberto 48254359Sroberto In the poll routine, if this value is positive and we are not 48354359Sroberto already resyncing (eg from a sync that started just before 48454359Sroberto midnight), start resyncing and set this value negative to 48554359Sroberto indicate that a leap-triggered resync has been started. Having 48654359Sroberto set this negative prevents the receive routine setting it 48754359Sroberto positive and thus prevents multiple resyncs during the witching 48854359Sroberto hour. 48954359Sroberto */ 49054359Srobertostatic int possible_leap = 0; /* No resync required by default. */ 49154359Sroberto#endif 49254359Sroberto 49354359Sroberto#if 0 49454359Srobertostatic void dummy_event_handler P((struct peer *)); 49554359Srobertostatic void arc_event_handler P((struct peer *)); 49654359Sroberto#endif /* 0 */ 49754359Sroberto 49854359Sroberto#define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */ 49954359Sroberto#define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */ 50054359Sroberto#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */ 50154359Sroberto#define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */ 50254359Sroberto 50354359Sroberto/* 50454359Sroberto * Function prototypes 50554359Sroberto */ 50654359Srobertostatic int arc_start P((int, struct peer *)); 50754359Srobertostatic void arc_shutdown P((int, struct peer *)); 50854359Srobertostatic void arc_receive P((struct recvbuf *)); 50954359Srobertostatic void arc_poll P((int, struct peer *)); 51054359Sroberto 51154359Sroberto/* 51254359Sroberto * Transfer vector 51354359Sroberto */ 51454359Srobertostruct refclock refclock_arc = { 51554359Sroberto arc_start, /* start up driver */ 51654359Sroberto arc_shutdown, /* shut down driver */ 51754359Sroberto arc_poll, /* transmit poll message */ 51854359Sroberto noentry, /* not used (old arc_control) */ 51954359Sroberto noentry, /* initialize driver (not used) */ 52054359Sroberto noentry, /* not used (old arc_buginfo) */ 52154359Sroberto NOFLAGS /* not used */ 52254359Sroberto}; 52354359Sroberto 52454359Sroberto/* Queue us up for the next tick. */ 52554359Sroberto#define ENQUEUE(up) \ 52654359Sroberto do { \ 52754359Sroberto if((up)->ev.next != 0) { break; } /* WHOOPS! */ \ 52854359Sroberto peer->nextdate = current_time + QUEUETICK; \ 52954359Sroberto } while(0) 53054359Sroberto 53154359Sroberto#if 0 53254359Sroberto/* Placeholder event handler---does nothing safely---soaks up lose tick. */ 53354359Srobertostatic void 53454359Srobertodummy_event_handler( 53554359Sroberto struct peer *peer 53654359Sroberto ) 53754359Sroberto{ 53854359Sroberto#ifdef ARCRON_DEBUG 53954359Sroberto if(debug) { printf("arc: dummy_event_handler() called.\n"); } 54054359Sroberto#endif 54154359Sroberto} 54254359Sroberto 54354359Sroberto/* 54454359SrobertoNormal event handler. 54554359Sroberto 54654359SrobertoTake first character off queue and send to clock if not a null. 54754359Sroberto 54854359SrobertoShift characters down and put a null on the end. 54954359Sroberto 55054359SrobertoWe assume that there is no parallelism so no race condition, but even 55154359Srobertoif there is nothing bad will happen except that we might send some bad 55254359Srobertodata to the clock once in a while. 55354359Sroberto*/ 55454359Srobertostatic void 55554359Srobertoarc_event_handler( 55654359Sroberto struct peer *peer 55754359Sroberto ) 55854359Sroberto{ 55954359Sroberto struct refclockproc *pp = peer->procptr; 56054359Sroberto register struct arcunit *up = (struct arcunit *)pp->unitptr; 56154359Sroberto int i; 56254359Sroberto char c; 56354359Sroberto#ifdef ARCRON_DEBUG 56454359Sroberto if(debug > 2) { printf("arc: arc_event_handler() called.\n"); } 56554359Sroberto#endif 56654359Sroberto 56754359Sroberto c = up->cmdqueue[0]; /* Next char to be sent. */ 56854359Sroberto /* Shift down characters, shifting trailing \0 in at end. */ 56954359Sroberto for(i = 0; i < CMDQUEUELEN; ++i) 57054359Sroberto { up->cmdqueue[i] = up->cmdqueue[i+1]; } 57154359Sroberto 57254359Sroberto /* Don't send '\0' characters. */ 57354359Sroberto if(c != '\0') { 57454359Sroberto if(write(pp->io.fd, &c, 1) != 1) { 57554359Sroberto msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd); 57654359Sroberto } 57754359Sroberto#ifdef ARCRON_DEBUG 57854359Sroberto else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } 57954359Sroberto#endif 58054359Sroberto } 58154359Sroberto} 58254359Sroberto#endif /* 0 */ 58354359Sroberto 58454359Sroberto/* 58554359Sroberto * arc_start - open the devices and initialize data for processing 58654359Sroberto */ 58754359Srobertostatic int 58854359Srobertoarc_start( 58954359Sroberto int unit, 59054359Sroberto struct peer *peer 59154359Sroberto ) 59254359Sroberto{ 59354359Sroberto register struct arcunit *up; 59454359Sroberto struct refclockproc *pp; 59554359Sroberto int fd; 59654359Sroberto char device[20]; 59754359Sroberto#ifdef HAVE_TERMIOS 59854359Sroberto struct termios arg; 59954359Sroberto#endif 60054359Sroberto 60154359Sroberto msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit); 60254359Sroberto#ifdef ARCRON_DEBUG 60354359Sroberto if(debug) { 60454359Sroberto printf("arc: %s: attempt to open unit %d.\n", arc_version, unit); 60554359Sroberto } 60654359Sroberto#endif 60754359Sroberto 60854359Sroberto /* Prevent a ridiculous device number causing overflow of device[]. */ 60954359Sroberto if((unit < 0) || (unit > 255)) { return(0); } 61054359Sroberto 61154359Sroberto /* 61254359Sroberto * Open serial port. Use CLK line discipline, if available. 61354359Sroberto */ 61454359Sroberto (void)sprintf(device, DEVICE, unit); 61554359Sroberto if (!(fd = refclock_open(device, SPEED, LDISC_CLK))) 61654359Sroberto return(0); 61754359Sroberto#ifdef ARCRON_DEBUG 61854359Sroberto if(debug) { printf("arc: unit %d using open().\n", unit); } 61954359Sroberto#endif 62054359Sroberto fd = open(device, OPEN_FLAGS); 62154359Sroberto if(fd < 0) { 62254359Sroberto#ifdef DEBUG 62354359Sroberto if(debug) { printf("arc: failed [open()] to open %s.\n", device); } 62454359Sroberto#endif 62554359Sroberto return(0); 62654359Sroberto } 62754359Sroberto 62854359Sroberto fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */ 62954359Sroberto#ifdef ARCRON_DEBUG 63054359Sroberto if(debug) 63154359Sroberto { printf("Opened RS232 port with file descriptor %d.\n", fd); } 63254359Sroberto#endif 63354359Sroberto 63454359Sroberto#ifdef HAVE_TERMIOS 63554359Sroberto 63654359Sroberto arg.c_iflag = IGNBRK | ISTRIP; 63754359Sroberto arg.c_oflag = 0; 63854359Sroberto arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB; 63954359Sroberto arg.c_lflag = 0; 64054359Sroberto arg.c_cc[VMIN] = 1; 64154359Sroberto arg.c_cc[VTIME] = 0; 64254359Sroberto 64354359Sroberto tcsetattr(fd, TCSANOW, &arg); 64454359Sroberto 64554359Sroberto#else 64654359Sroberto 64754359Sroberto msyslog(LOG_ERR, "ARCRON: termios not supported in this driver"); 64854359Sroberto (void)close(fd); 64954359Sroberto 65054359Sroberto return 0; 65154359Sroberto 65254359Sroberto#endif 65354359Sroberto 65454359Sroberto up = (struct arcunit *) emalloc(sizeof(struct arcunit)); 65554359Sroberto if(!up) { (void) close(fd); return(0); } 65654359Sroberto /* Set structure to all zeros... */ 65754359Sroberto memset((char *)up, 0, sizeof(struct arcunit)); 65854359Sroberto pp = peer->procptr; 65954359Sroberto pp->io.clock_recv = arc_receive; 66054359Sroberto pp->io.srcclock = (caddr_t)peer; 66154359Sroberto pp->io.datalen = 0; 66254359Sroberto pp->io.fd = fd; 66354359Sroberto if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); } 66454359Sroberto pp->unitptr = (caddr_t)up; 66554359Sroberto 66654359Sroberto /* 66754359Sroberto * Initialize miscellaneous variables 66854359Sroberto */ 66954359Sroberto peer->precision = PRECISION; 67054359Sroberto peer->stratum = 2; /* Default to stratum 2 not 0. */ 67154359Sroberto pp->clockdesc = DESCRIPTION; 67254359Sroberto memcpy((char *)&pp->refid, REFID, 4); 67354359Sroberto /* Spread out resyncs so that they should remain separated. */ 67454359Sroberto up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009; 67554359Sroberto 67654359Sroberto#if 0 /* Not needed because of zeroing of arcunit structure... */ 67754359Sroberto up->resyncing = 0; /* Not resyncing yet. */ 67854359Sroberto up->saved_flags = 0; /* Default is all flags off. */ 67954359Sroberto /* Clear send buffer out... */ 68054359Sroberto { 68154359Sroberto int i; 68254359Sroberto for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; } 68354359Sroberto } 68454359Sroberto#endif 68554359Sroberto 68654359Sroberto#ifdef ARCRON_KEEN 68754359Sroberto up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */ 68854359Sroberto#else 68954359Sroberto up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */ 69054359Sroberto#endif 69154359Sroberto return(1); 69254359Sroberto} 69354359Sroberto 69454359Sroberto 69554359Sroberto/* 69654359Sroberto * arc_shutdown - shut down the clock 69754359Sroberto */ 69854359Srobertostatic void 69954359Srobertoarc_shutdown( 70054359Sroberto int unit, 70154359Sroberto struct peer *peer 70254359Sroberto ) 70354359Sroberto{ 70454359Sroberto register struct arcunit *up; 70554359Sroberto struct refclockproc *pp; 70654359Sroberto 70754359Sroberto pp = peer->procptr; 70854359Sroberto up = (struct arcunit *)pp->unitptr; 70954359Sroberto io_closeclock(&pp->io); 71054359Sroberto free(up); 71154359Sroberto} 71254359Sroberto 71354359Sroberto/* 71454359SrobertoCompute space left in output buffer. 71554359Sroberto*/ 71654359Srobertostatic int 71754359Srobertospace_left( 71854359Sroberto register struct arcunit *up 71954359Sroberto ) 72054359Sroberto{ 72154359Sroberto int spaceleft; 72254359Sroberto 72354359Sroberto /* Compute space left in buffer after any pending output. */ 72454359Sroberto for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft) 72554359Sroberto { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } } 72654359Sroberto return(spaceleft); 72754359Sroberto} 72854359Sroberto 72954359Sroberto/* 73054359SrobertoSend command by copying into command buffer as far forward as possible, 73154359Srobertoafter any pending output. 73254359Sroberto 73354359SrobertoIndicate an error by returning 0 if there is not space for the command. 73454359Sroberto*/ 73554359Srobertostatic int 73654359Srobertosend_slow( 73754359Sroberto register struct arcunit *up, 73854359Sroberto int fd, 73954359Sroberto const char *s 74054359Sroberto ) 74154359Sroberto{ 74254359Sroberto int sl = strlen(s); 74354359Sroberto int spaceleft = space_left(up); 74454359Sroberto 74554359Sroberto#ifdef ARCRON_DEBUG 74654359Sroberto if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } 74754359Sroberto#endif 74854359Sroberto if(spaceleft < sl) { /* Should not normally happen... */ 74954359Sroberto#ifdef ARCRON_DEBUG 75054359Sroberto msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)", 75154359Sroberto sl, spaceleft); 75254359Sroberto#endif 75354359Sroberto return(0); /* FAILED! */ 75454359Sroberto } 75554359Sroberto 75654359Sroberto /* Copy in the command to be sent. */ 75754359Sroberto while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; } 75854359Sroberto 75954359Sroberto return(1); 76054359Sroberto} 76154359Sroberto 76254359Sroberto 76354359Sroberto/* Macro indicating action we will take for different quality values. */ 76454359Sroberto#define quality_action(q) \ 76554359Sroberto(((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \ 76654359Sroberto (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \ 76754359Sroberto "OK, will use clock")) 76854359Sroberto 76954359Sroberto /* 77054359Sroberto * arc_receive - receive data from the serial interface 77154359Sroberto */ 77254359Sroberto static void 77354359Srobertoarc_receive( 77454359Sroberto struct recvbuf *rbufp 77554359Sroberto ) 77654359Sroberto{ 77754359Sroberto register struct arcunit *up; 77854359Sroberto struct refclockproc *pp; 77954359Sroberto struct peer *peer; 78054359Sroberto char c; 78154359Sroberto int i, n, wday, month, bst, status; 78254359Sroberto int arc_last_offset; 78354359Sroberto 78454359Sroberto /* 78554359Sroberto * Initialize pointers and read the timecode and timestamp 78654359Sroberto */ 78754359Sroberto peer = (struct peer *)rbufp->recv_srcclock; 78854359Sroberto pp = peer->procptr; 78954359Sroberto up = (struct arcunit *)pp->unitptr; 79054359Sroberto 79154359Sroberto 79254359Sroberto /* 79354359Sroberto If the command buffer is empty, and we are resyncing, insert a 79454359Sroberto g\r quality request into it to poll for signal quality again. 79554359Sroberto */ 79654359Sroberto if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) { 79754359Sroberto#ifdef DEBUG 79854359Sroberto if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); } 79954359Sroberto#endif 80054359Sroberto send_slow(up, pp->io.fd, "g\r"); 80154359Sroberto } 80254359Sroberto 80354359Sroberto /* 80454359Sroberto The `arc_last_offset' is the offset in lastcode[] of the last byte 80554359Sroberto received, and which we assume actually received the input 80654359Sroberto timestamp. 80754359Sroberto 80854359Sroberto (When we get round to using tty_clk and it is available, we 80954359Sroberto assume that we will receive the whole timecode with the 81054359Sroberto trailing \r, and that that \r will be timestamped. But this 81154359Sroberto assumption also works if receive the characters one-by-one.) 81254359Sroberto */ 81354359Sroberto arc_last_offset = pp->lencode+rbufp->recv_length - 1; 81454359Sroberto 81554359Sroberto /* 81654359Sroberto We catch a timestamp iff: 81754359Sroberto 81854359Sroberto * The command code is `o' for a timestamp. 81954359Sroberto 82054359Sroberto * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have 82154359Sroberto exactly char in the buffer (the command code) so that we 82254359Sroberto only sample the first character of the timecode as our 82354359Sroberto `on-time' character. 82454359Sroberto 82554359Sroberto * The first character in the buffer is not the echoed `\r' 82654359Sroberto from the `o` command (so if we are to timestamp an `\r' it 82754359Sroberto must not be first in the receive buffer with lencode==1. 82854359Sroberto (Even if we had other characters following it, we probably 82954359Sroberto would have a premature timestamp on the '\r'.) 83054359Sroberto 83154359Sroberto * We have received at least one character (I cannot imagine 83254359Sroberto how it could be otherwise, but anyway...). 83354359Sroberto */ 83454359Sroberto c = rbufp->recv_buffer[0]; 83554359Sroberto if((pp->a_lastcode[0] == 'o') && 83654359Sroberto#ifndef ARCRON_MULTIPLE_SAMPLES 83754359Sroberto (pp->lencode == 1) && 83854359Sroberto#endif 83954359Sroberto ((pp->lencode != 1) || (c != '\r')) && 84054359Sroberto (arc_last_offset >= 1)) { 84154359Sroberto /* Note that the timestamp should be corrected if >1 char rcvd. */ 84254359Sroberto l_fp timestamp; 84354359Sroberto timestamp = rbufp->recv_time; 84454359Sroberto#ifdef DEBUG 84554359Sroberto if(debug) { /* Show \r as `R', other non-printing char as `?'. */ 84654359Sroberto printf("arc: stamp -->%c<-- (%d chars rcvd)\n", 84754359Sroberto ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')), 84854359Sroberto rbufp->recv_length); 84954359Sroberto } 85054359Sroberto#endif 85154359Sroberto 85254359Sroberto /* 85354359Sroberto Now correct timestamp by offset of last byte received---we 85454359Sroberto subtract from the receive time the delay implied by the 85554359Sroberto extra characters received. 85654359Sroberto 85754359Sroberto Reject the input if the resulting code is too long, but 85854359Sroberto allow for the trailing \r, normally not used but a good 85954359Sroberto handle for tty_clk or somesuch kernel timestamper. 86054359Sroberto */ 86154359Sroberto if(arc_last_offset > LENARC) { 86254359Sroberto#ifdef ARCRON_DEBUG 86354359Sroberto if(debug) { 86454359Sroberto printf("arc: input code too long (%d cf %d); rejected.\n", 86554359Sroberto arc_last_offset, LENARC); 86654359Sroberto } 86754359Sroberto#endif 86854359Sroberto pp->lencode = 0; 86954359Sroberto refclock_report(peer, CEVNT_BADREPLY); 87054359Sroberto return; 87154359Sroberto } 87254359Sroberto 87354359Sroberto L_SUBUF(×tamp, charoffsets[arc_last_offset]); 87454359Sroberto#ifdef ARCRON_DEBUG 87554359Sroberto if(debug > 1) { 87654359Sroberto printf( 87754359Sroberto "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n", 87854359Sroberto ((rbufp->recv_length > 1) ? "*** " : ""), 87954359Sroberto rbufp->recv_length, 88054359Sroberto arc_last_offset, 88154359Sroberto mfptoms((unsigned long)0, 88254359Sroberto charoffsets[arc_last_offset], 88354359Sroberto 1)); 88454359Sroberto } 88554359Sroberto#endif 88654359Sroberto 88754359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES 88854359Sroberto /* 88954359Sroberto If taking multiple samples, capture the current adjusted 89054359Sroberto sample iff: 89154359Sroberto 89254359Sroberto * No timestamp has yet been captured (it is zero), OR 89354359Sroberto 89454359Sroberto * This adjusted timestamp is earlier than the one already 89554359Sroberto captured, on the grounds that this one suffered less 89654359Sroberto delay in being delivered to us and is more accurate. 89754359Sroberto 89854359Sroberto */ 89954359Sroberto if(L_ISZERO(&(up->lastrec)) || 90054359Sroberto L_ISGEQ(&(up->lastrec), ×tamp)) 90154359Sroberto#endif 90254359Sroberto { 90354359Sroberto#ifdef ARCRON_DEBUG 90454359Sroberto if(debug > 1) { 90554359Sroberto printf("arc: system timestamp captured.\n"); 90654359Sroberto#ifdef ARCRON_MULTIPLE_SAMPLES 90754359Sroberto if(!L_ISZERO(&(up->lastrec))) { 90854359Sroberto l_fp diff; 90954359Sroberto diff = up->lastrec; 91054359Sroberto L_SUB(&diff, ×tamp); 91154359Sroberto printf("arc: adjusted timestamp by -%sms.\n", 91254359Sroberto mfptoms(diff.l_i, diff.l_f, 3)); 91354359Sroberto } 91454359Sroberto#endif 91554359Sroberto } 91654359Sroberto#endif 91754359Sroberto up->lastrec = timestamp; 91854359Sroberto } 91954359Sroberto 92054359Sroberto } 92154359Sroberto 92254359Sroberto /* Just in case we still have lots of rubbish in the buffer... */ 92354359Sroberto /* ...and to avoid the same timestamp being reused by mistake, */ 92454359Sroberto /* eg on receipt of the \r coming in on its own after the */ 92554359Sroberto /* timecode. */ 92654359Sroberto if(pp->lencode >= LENARC) { 92754359Sroberto#ifdef ARCRON_DEBUG 92854359Sroberto if(debug && (rbufp->recv_buffer[0] != '\r')) 92954359Sroberto { printf("arc: rubbish in pp->a_lastcode[].\n"); } 93054359Sroberto#endif 93154359Sroberto pp->lencode = 0; 93254359Sroberto return; 93354359Sroberto } 93454359Sroberto 93554359Sroberto /* Append input to code buffer, avoiding overflow. */ 93654359Sroberto for(i = 0; i < rbufp->recv_length; i++) { 93754359Sroberto if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */ 93854359Sroberto c = rbufp->recv_buffer[i]; 93954359Sroberto 94054359Sroberto /* Drop trailing '\r's and drop `h' command echo totally. */ 94154359Sroberto if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; } 94254359Sroberto 94354359Sroberto /* 94454359Sroberto If we've just put an `o' in the lastcode[0], clear the 94554359Sroberto timestamp in anticipation of a timecode arriving soon. 94654359Sroberto 94754359Sroberto We would expect to get to process this before any of the 94854359Sroberto timecode arrives. 94954359Sroberto */ 95054359Sroberto if((c == 'o') && (pp->lencode == 1)) { 95154359Sroberto L_CLR(&(up->lastrec)); 95254359Sroberto#ifdef ARCRON_DEBUG 95354359Sroberto if(debug > 1) { printf("arc: clearing timestamp.\n"); } 95454359Sroberto#endif 95554359Sroberto } 95654359Sroberto } 95754359Sroberto 95854359Sroberto /* Handle a quality message. */ 95954359Sroberto if(pp->a_lastcode[0] == 'g') { 96054359Sroberto int r, q; 96154359Sroberto 96254359Sroberto if(pp->lencode < 3) { return; } /* Need more data... */ 96354359Sroberto r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */ 96454359Sroberto q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */ 96554359Sroberto if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) || 96654359Sroberto ((r & 0x70) != 0x30)) { 96754359Sroberto /* Badly formatted response. */ 96854359Sroberto#ifdef ARCRON_DEBUG 96954359Sroberto if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); } 97054359Sroberto#endif 97154359Sroberto return; 97254359Sroberto } 97354359Sroberto if(r == '3') { /* Only use quality value whilst sync in progress. */ 97454359Sroberto up->quality = (q & 0xf); 97554359Sroberto#ifdef DEBUG 97654359Sroberto if(debug) { printf("arc: signal quality %d.\n", up->quality); } 97754359Sroberto#endif 97854359Sroberto } else if( /* (r == '2') && */ up->resyncing) { 97954359Sroberto#ifdef DEBUG 98054359Sroberto if(debug) 98154359Sroberto { 98254359Sroberto printf("arc: sync finished, signal quality %d: %s\n", 98354359Sroberto up->quality, 98454359Sroberto quality_action(up->quality)); 98554359Sroberto } 98654359Sroberto#endif 98754359Sroberto msyslog(LOG_NOTICE, 98854359Sroberto "ARCRON: sync finished, signal quality %d: %s", 98954359Sroberto up->quality, 99054359Sroberto quality_action(up->quality)); 99154359Sroberto up->resyncing = 0; /* Resync is over. */ 99254359Sroberto 99354359Sroberto#ifdef ARCRON_KEEN 99454359Sroberto /* Clock quality dubious; resync earlier than usual. */ 99554359Sroberto if((up->quality == QUALITY_UNKNOWN) || 99654359Sroberto (up->quality < MIN_CLOCK_QUALITY_OK)) 99754359Sroberto { up->next_resync = current_time + RETRY_RESYNC_TIME; } 99854359Sroberto#endif 99954359Sroberto } 100054359Sroberto pp->lencode = 0; 100154359Sroberto return; 100254359Sroberto } 100354359Sroberto 100454359Sroberto /* Stop now if this is not a timecode message. */ 100554359Sroberto if(pp->a_lastcode[0] != 'o') { 100654359Sroberto pp->lencode = 0; 100754359Sroberto refclock_report(peer, CEVNT_BADREPLY); 100854359Sroberto return; 100954359Sroberto } 101054359Sroberto 101154359Sroberto /* If we don't have enough data, wait for more... */ 101254359Sroberto if(pp->lencode < LENARC) { return; } 101354359Sroberto 101454359Sroberto 101554359Sroberto /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */ 101654359Sroberto#ifdef ARCRON_DEBUG 101754359Sroberto if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); } 101854359Sroberto#endif 101954359Sroberto 102054359Sroberto /* But check that we actually captured a system timestamp on it. */ 102154359Sroberto if(L_ISZERO(&(up->lastrec))) { 102254359Sroberto#ifdef ARCRON_DEBUG 102354359Sroberto if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); } 102454359Sroberto#endif 102554359Sroberto pp->lencode = 0; 102654359Sroberto refclock_report(peer, CEVNT_BADREPLY); 102754359Sroberto return; 102854359Sroberto } 102954359Sroberto /* 103054359Sroberto Append a mark of the clock's received signal quality for the 103154359Sroberto benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown' 103254359Sroberto quality value to `6' for his s/w) and terminate the string for 103354359Sroberto sure. This should not go off the buffer end. 103454359Sroberto */ 103554359Sroberto pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ? 103654359Sroberto '6' : ('0' + up->quality)); 103754359Sroberto pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */ 103854359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 103954359Sroberto 104054359Sroberto /* We don't use the micro-/milli- second part... */ 104154359Sroberto pp->usec = 0; 104254359Sroberto pp->msec = 0; 104354359Sroberto 104454359Sroberto n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d", 104554359Sroberto &pp->hour, &pp->minute, &pp->second, 104654359Sroberto &wday, &pp->day, &month, &pp->year, &bst, &status); 104754359Sroberto 104854359Sroberto /* Validate format and numbers. */ 104954359Sroberto if(n != 9) { 105054359Sroberto#ifdef ARCRON_DEBUG 105154359Sroberto /* Would expect to have caught major problems already... */ 105254359Sroberto if(debug) { printf("arc: badly formatted data.\n"); } 105354359Sroberto#endif 105454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 105554359Sroberto return; 105654359Sroberto } 105754359Sroberto /* 105854359Sroberto Validate received values at least enough to prevent internal 105954359Sroberto array-bounds problems, etc. 106054359Sroberto */ 106154359Sroberto if((pp->hour < 0) || (pp->hour > 23) || 106254359Sroberto (pp->minute < 0) || (pp->minute > 59) || 106354359Sroberto (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 106454359Sroberto (wday < 1) || (wday > 7) || 106554359Sroberto (pp->day < 1) || (pp->day > 31) || 106654359Sroberto (month < 1) || (month > 12) || 106754359Sroberto (pp->year < 0) || (pp->year > 99)) { 106854359Sroberto /* Data out of range. */ 106954359Sroberto refclock_report(peer, CEVNT_BADREPLY); 107054359Sroberto return; 107154359Sroberto } 107254359Sroberto /* Check that BST/UTC bits are the complement of one another. */ 107354359Sroberto if(!(bst & 2) == !(bst & 4)) { 107454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 107554359Sroberto return; 107654359Sroberto } 107754359Sroberto 107854359Sroberto if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); } 107954359Sroberto 108054359Sroberto /* Year-2000 alert! */ 108154359Sroberto /* Attempt to wrap 2-digit date into sensible window. */ 108254359Sroberto if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */ 108354359Sroberto pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */ 108454359Sroberto /* 108554359Sroberto Attempt to do the right thing by screaming that the code will 108654359Sroberto soon break when we get to the end of its useful life. What a 108754359Sroberto hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X! 108854359Sroberto */ 108954359Sroberto if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */ 109054359Sroberto /*This should get attention B^> */ 109154359Sroberto msyslog(LOG_NOTICE, 109254359Sroberto "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!"); 109354359Sroberto } 109454359Sroberto#ifdef DEBUG 109554359Sroberto if(debug) { 109654359Sroberto printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n", 109754359Sroberto n, 109854359Sroberto pp->hour, pp->minute, pp->second, 109954359Sroberto pp->day, month, pp->year, bst, status); 110054359Sroberto } 110154359Sroberto#endif 110254359Sroberto 110354359Sroberto /* 110454359Sroberto The status value tested for is not strictly supported by the 110554359Sroberto clock spec since the value of bit 2 (0x4) is claimed to be 110654359Sroberto undefined for MSF, yet does seem to indicate if the last resync 110754359Sroberto was successful or not. 110854359Sroberto */ 110954359Sroberto pp->leap = LEAP_NOWARNING; 111054359Sroberto status &= 0x7; 111154359Sroberto if(status == 0x3) { 111254359Sroberto if(status != up->status) 111354359Sroberto { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); } 111454359Sroberto } else { 111554359Sroberto if(status != up->status) { 111654359Sroberto msyslog(LOG_NOTICE, "ARCRON: signal lost"); 111754359Sroberto pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */ 111854359Sroberto up->status = status; 111954359Sroberto refclock_report(peer, CEVNT_FAULT); 112054359Sroberto return; 112154359Sroberto } 112254359Sroberto } 112354359Sroberto up->status = status; 112454359Sroberto 112554359Sroberto pp->day += moff[month - 1]; 112654359Sroberto 112754359Sroberto if(isleap_4(pp->year) && month > 2) { pp->day++; } /* Y2KFixes */ 112854359Sroberto 112954359Sroberto /* Convert to UTC if required */ 113054359Sroberto if(bst & 2) { 113154359Sroberto pp->hour--; 113254359Sroberto if (pp->hour < 0) { 113354359Sroberto pp->hour = 23; 113454359Sroberto pp->day--; 113554359Sroberto /* If we try to wrap round the year (BST on 1st Jan), reject.*/ 113654359Sroberto if(pp->day < 0) { 113754359Sroberto refclock_report(peer, CEVNT_BADTIME); 113854359Sroberto return; 113954359Sroberto } 114054359Sroberto } 114154359Sroberto } 114254359Sroberto 114354359Sroberto /* If clock signal quality is unknown, revert to default PRECISION...*/ 114454359Sroberto if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; } 114554359Sroberto /* ...else improve precision if flag3 is set... */ 114654359Sroberto else { 114754359Sroberto peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? 114854359Sroberto HIGHPRECISION : PRECISION); 114954359Sroberto } 115054359Sroberto 115154359Sroberto /* Notice and log any change (eg from initial defaults) for flags. */ 115254359Sroberto if(up->saved_flags != pp->sloppyclockflag) { 115354359Sroberto#ifdef ARCRON_DEBUG 115454359Sroberto msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s", 115554359Sroberto ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."), 115654359Sroberto ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."), 115754359Sroberto ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."), 115854359Sroberto ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : ".")); 115954359Sroberto /* Note effects of flags changing... */ 116054359Sroberto if(debug) { 116154359Sroberto printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp)); 116254359Sroberto printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp)); 116354359Sroberto printf("arc: PRECISION = %d.\n", peer->precision); 116454359Sroberto } 116554359Sroberto#endif 116654359Sroberto up->saved_flags = pp->sloppyclockflag; 116754359Sroberto } 116854359Sroberto 116954359Sroberto /* Note time of last believable timestamp. */ 117054359Sroberto pp->lastrec = up->lastrec; 117154359Sroberto 117254359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 117354359Sroberto /* Find out if a leap-second might just have happened... 117454359Sroberto (ie is this the first hour of the first day of Jan or Jul?) 117554359Sroberto */ 117654359Sroberto if((pp->hour == 0) && 117754359Sroberto (pp->day == 1) && 117854359Sroberto ((month == 1) || (month == 7))) { 117954359Sroberto if(possible_leap >= 0) { 118054359Sroberto /* A leap may have happened, and no resync has started yet...*/ 118154359Sroberto possible_leap = 1; 118254359Sroberto } 118354359Sroberto } else { 118454359Sroberto /* Definitely not leap-second territory... */ 118554359Sroberto possible_leap = 0; 118654359Sroberto } 118754359Sroberto#endif 118854359Sroberto 118954359Sroberto if (!refclock_process(pp)) { 119054359Sroberto refclock_report(peer, CEVNT_BADTIME); 119154359Sroberto return; 119254359Sroberto } 119354359Sroberto refclock_receive(peer); 119454359Sroberto} 119554359Sroberto 119654359Sroberto 119754359Sroberto/* request_time() sends a time request to the clock with given peer. */ 119854359Sroberto/* This automatically reports a fault if necessary. */ 119954359Sroberto/* No data should be sent after this until arc_poll() returns. */ 120054359Srobertostatic void request_time P((int, struct peer *)); 120154359Srobertostatic void 120254359Srobertorequest_time( 120354359Sroberto int unit, 120454359Sroberto struct peer *peer 120554359Sroberto ) 120654359Sroberto{ 120754359Sroberto struct refclockproc *pp = peer->procptr; 120854359Sroberto register struct arcunit *up = (struct arcunit *)pp->unitptr; 120954359Sroberto#ifdef DEBUG 121054359Sroberto if(debug) { printf("arc: unit %d: requesting time.\n", unit); } 121154359Sroberto#endif 121254359Sroberto if (!send_slow(up, pp->io.fd, "o\r")) { 121354359Sroberto#ifdef ARCRON_DEBUG 121454359Sroberto msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit); 121554359Sroberto#endif 121654359Sroberto refclock_report(peer, CEVNT_FAULT); 121754359Sroberto return; 121854359Sroberto } 121954359Sroberto pp->polls++; 122054359Sroberto} 122154359Sroberto 122254359Sroberto/* 122354359Sroberto * arc_poll - called by the transmit procedure 122454359Sroberto */ 122554359Srobertostatic void 122654359Srobertoarc_poll( 122754359Sroberto int unit, 122854359Sroberto struct peer *peer 122954359Sroberto ) 123054359Sroberto{ 123154359Sroberto register struct arcunit *up; 123254359Sroberto struct refclockproc *pp; 123354359Sroberto int resync_needed; /* Should we start a resync? */ 123454359Sroberto 123554359Sroberto pp = peer->procptr; 123654359Sroberto up = (struct arcunit *)pp->unitptr; 123754359Sroberto pp->lencode = 0; 123854359Sroberto memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); 123954359Sroberto 124054359Sroberto#if 0 124154359Sroberto /* Flush input. */ 124254359Sroberto tcflush(pp->io.fd, TCIFLUSH); 124354359Sroberto#endif 124454359Sroberto 124554359Sroberto /* Resync if our next scheduled resync time is here or has passed. */ 124654359Sroberto resync_needed = (up->next_resync <= current_time); 124754359Sroberto 124854359Sroberto#ifdef ARCRON_LEAPSECOND_KEEN 124954359Sroberto /* 125054359Sroberto Try to catch a potential leap-second insertion or deletion quickly. 125154359Sroberto 125254359Sroberto In addition to the normal NTP fun of clocks that don't report 125354359Sroberto leap-seconds spooking their hosts, this clock does not even 125454359Sroberto sample the radio sugnal the whole time, so may miss a 125554359Sroberto leap-second insertion or deletion for up to a whole sample 125654359Sroberto time. 125754359Sroberto 125854359Sroberto To try to minimise this effect, if in the first few minutes of 125954359Sroberto the day immediately following a leap-second-insertion point 126054359Sroberto (ie in the first hour of the first day of the first and sixth 126154359Sroberto months), and if the last resync was in the previous day, and a 126254359Sroberto resync is not already in progress, resync the clock 126354359Sroberto immediately. 126454359Sroberto 126554359Sroberto */ 126654359Sroberto if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */ 126754359Sroberto (!up->resyncing)) { /* No resync in progress yet. */ 126854359Sroberto resync_needed = 1; 126954359Sroberto possible_leap = -1; /* Prevent multiple resyncs. */ 127054359Sroberto msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit); 127154359Sroberto } 127254359Sroberto#endif 127354359Sroberto 127454359Sroberto /* Do a resync if required... */ 127554359Sroberto if(resync_needed) { 127654359Sroberto /* First, reset quality value to `unknown' so we can detect */ 127754359Sroberto /* when a quality message has been responded to by this */ 127854359Sroberto /* being set to some other value. */ 127954359Sroberto up->quality = QUALITY_UNKNOWN; 128054359Sroberto 128154359Sroberto /* Note that we are resyncing... */ 128254359Sroberto up->resyncing = 1; 128354359Sroberto 128454359Sroberto /* Now actually send the resync command and an immediate poll. */ 128554359Sroberto#ifdef DEBUG 128654359Sroberto if(debug) { printf("arc: sending resync command (h\\r).\n"); } 128754359Sroberto#endif 128854359Sroberto msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit); 128954359Sroberto send_slow(up, pp->io.fd, "h\r"); 129054359Sroberto 129154359Sroberto /* Schedule our next resync... */ 129254359Sroberto up->next_resync = current_time + DEFAULT_RESYNC_TIME; 129354359Sroberto 129454359Sroberto /* Drop through to request time if appropriate. */ 129554359Sroberto } 129654359Sroberto 129754359Sroberto /* If clock quality is too poor to trust, indicate a fault. */ 129854359Sroberto /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/ 129954359Sroberto /* we'll cross our fingers and just hope that the thing */ 130054359Sroberto /* synced so quickly we did not catch it---we'll */ 130154359Sroberto /* double-check the clock is OK elsewhere. */ 130254359Sroberto if( 130354359Sroberto#ifdef ARCRON_KEEN 130454359Sroberto (up->quality != QUALITY_UNKNOWN) && 130554359Sroberto#else 130654359Sroberto (up->quality == QUALITY_UNKNOWN) || 130754359Sroberto#endif 130854359Sroberto (up->quality < MIN_CLOCK_QUALITY_OK)) { 130954359Sroberto#ifdef DEBUG 131054359Sroberto if(debug) { 131154359Sroberto printf("arc: clock quality %d too poor.\n", up->quality); 131254359Sroberto } 131354359Sroberto#endif 131454359Sroberto refclock_report(peer, CEVNT_FAULT); 131554359Sroberto return; 131654359Sroberto } 131754359Sroberto /* This is the normal case: request a timestamp. */ 131854359Sroberto request_time(unit, peer); 131954359Sroberto} 132054359Sroberto 132154359Sroberto#else 132254359Srobertoint refclock_arc_bs; 132354359Sroberto#endif 1324