154359Sroberto/* refclock_ees - clock driver for the EES M201 receiver */ 254359Sroberto 354359Sroberto#ifdef HAVE_CONFIG_H 454359Sroberto#include <config.h> 554359Sroberto#endif 654359Sroberto 7280849Scy#include "ntp_types.h" 8280849Scy 954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS) 1054359Sroberto 1154359Sroberto/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes 1254359Sroberto * were removed as the code was overly hairy, they weren't in use 1354359Sroberto * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk 1454359Sroberto */ 1554359Sroberto 1654359Sroberto#include "ntpd.h" 1754359Sroberto#include "ntp_io.h" 1854359Sroberto#include "ntp_refclock.h" 19280849Scy#include "timevalops.h" 2082498Sroberto 2182498Sroberto#include <ctype.h> 2254359Sroberto#if defined(HAVE_BSD_TTYS) 2354359Sroberto#include <sgtty.h> 2454359Sroberto#endif /* HAVE_BSD_TTYS */ 2554359Sroberto#if defined(HAVE_SYSV_TTYS) 2654359Sroberto#include <termio.h> 2754359Sroberto#endif /* HAVE_SYSV_TTYS */ 2854359Sroberto#if defined(HAVE_TERMIOS) 2954359Sroberto#include <termios.h> 3054359Sroberto#endif 3154359Sroberto#if defined(STREAM) 3254359Sroberto#include <stropts.h> 3354359Sroberto#endif 3454359Sroberto 3554359Sroberto#ifdef HAVE_SYS_TERMIOS_H 3654359Sroberto# include <sys/termios.h> 3754359Sroberto#endif 3854359Sroberto#ifdef HAVE_SYS_PPSCLOCK_H 3954359Sroberto# include <sys/ppsclock.h> 4054359Sroberto#endif 4154359Sroberto 4254359Sroberto#include "ntp_stdlib.h" 4354359Sroberto 44182007Srobertoint dbg = 0; 4554359Sroberto/* 4654359Sroberto fudgefactor = fudgetime1; 4754359Sroberto os_delay = fudgetime2; 4854359Sroberto offset_fudge = os_delay + fudgefactor + inherent_delay; 4954359Sroberto stratumtouse = fudgeval1 & 0xf 50182007Sroberto dbg = fudgeval2; 5154359Sroberto sloppyclockflag = flags & CLK_FLAG1; 5254359Sroberto 1 log smoothing summary when processing sample 5354359Sroberto 4 dump the buffer from the clock 5454359Sroberto 8 EIOGETKD the last n uS time stamps 5554359Sroberto if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0; 5654359Sroberto ees->dump_vals = flags & CLK_FLAG3; 5754359Sroberto ees->usealldata = flags & CLK_FLAG4; 5854359Sroberto 5954359Sroberto 6054359Sroberto bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0; 6154359Sroberto bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0; 6254359Sroberto bug->values[2] = (u_long)ees->status; 6354359Sroberto bug->values[3] = (u_long)ees->lastevent; 6454359Sroberto bug->values[4] = (u_long)ees->reason; 6554359Sroberto bug->values[5] = (u_long)ees->nsamples; 6654359Sroberto bug->values[6] = (u_long)ees->codestate; 6754359Sroberto bug->values[7] = (u_long)ees->day; 6854359Sroberto bug->values[8] = (u_long)ees->hour; 6954359Sroberto bug->values[9] = (u_long)ees->minute; 7054359Sroberto bug->values[10] = (u_long)ees->second; 7154359Sroberto bug->values[11] = (u_long)ees->tz; 7254359Sroberto bug->values[12] = ees->yearstart; 7354359Sroberto bug->values[13] = (ees->leaphold > current_time) ? 7454359Sroberto ees->leaphold - current_time : 0; 7554359Sroberto bug->values[14] = inherent_delay[unit].l_uf; 7654359Sroberto bug->values[15] = offset_fudge[unit].l_uf; 7754359Sroberto 7854359Sroberto bug->times[0] = ees->reftime; 7954359Sroberto bug->times[1] = ees->arrvtime; 8054359Sroberto bug->times[2] = ees->lastsampletime; 8154359Sroberto bug->times[3] = ees->offset; 8254359Sroberto bug->times[4] = ees->lowoffset; 8354359Sroberto bug->times[5] = ees->highoffset; 8454359Sroberto bug->times[6] = inherent_delay[unit]; 8554359Sroberto bug->times[8] = os_delay[unit]; 8654359Sroberto bug->times[7] = fudgefactor[unit]; 8754359Sroberto bug->times[9] = offset_fudge[unit]; 8854359Sroberto bug->times[10]= ees->yearstart, 0; 8954359Sroberto */ 9054359Sroberto 9154359Sroberto/* This should support the use of an EES M201 receiver with RS232 9254359Sroberto * output (modified to transmit time once per second). 9354359Sroberto * 9454359Sroberto * For the format of the message sent by the clock, see the EESM_ 9554359Sroberto * definitions below. 9654359Sroberto * 9754359Sroberto * It appears to run free for an integral number of minutes, until the error 9854359Sroberto * reaches 4mS, at which point it steps at second = 01. 9954359Sroberto * It appears that sometimes it steps 4mS (say at 7 min interval), 10054359Sroberto * then the next minute it decides that it was an error, so steps back. 10154359Sroberto * On the next minute it steps forward again :-( 10254359Sroberto * This is typically 16.5uS/S then 3975uS at the 4min re-sync, 10354359Sroberto * or 9.5uS/S then 3990.5uS at a 7min re-sync, 104132451Sroberto * at which point it may lose the "00" second time stamp. 10554359Sroberto * I assume that the most accurate time is just AFTER the re-sync. 10654359Sroberto * Hence remember the last cycle interval, 10754359Sroberto * 10854359Sroberto * Can run in any one of: 10954359Sroberto * 11054359Sroberto * PPSCD PPS signal sets CD which interupts, and grabs the current TOD 11154359Sroberto * (sun) *in the interupt code*, so as to avoid problems with 11254359Sroberto * the STREAMS scheduling. 11354359Sroberto * 11454359Sroberto * It appears that it goes 16.5 uS slow each second, then every 4 mins it 11554359Sroberto * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7) 11654359Sroberto */ 11754359Sroberto 11854359Sroberto/* Definitions */ 11954359Sroberto#ifndef MAXUNITS 12054359Sroberto#define MAXUNITS 4 /* maximum number of EES units permitted */ 12154359Sroberto#endif 12254359Sroberto 12354359Sroberto#ifndef EES232 12454359Sroberto#define EES232 "/dev/ees%d" /* Device to open to read the data */ 12554359Sroberto#endif 12654359Sroberto 12754359Sroberto/* Other constant stuff */ 12854359Sroberto#ifndef EESPRECISION 12954359Sroberto#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */ 13054359Sroberto#endif 13154359Sroberto#ifndef EESREFID 13254359Sroberto#define EESREFID "MSF\0" /* String to identify the clock */ 13354359Sroberto#endif 13454359Sroberto#ifndef EESHSREFID 13554359Sroberto#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */ 13654359Sroberto#endif 13754359Sroberto 13854359Sroberto/* Description of clock */ 13954359Sroberto#define EESDESCRIPTION "EES M201 MSF Receiver" 14054359Sroberto 14154359Sroberto/* Speed we run the clock port at. If this is changed the UARTDELAY 14254359Sroberto * value should be recomputed to suit. 14354359Sroberto */ 14454359Sroberto#ifndef SPEED232 14554359Sroberto#define SPEED232 B9600 /* 9600 baud */ 14654359Sroberto#endif 14754359Sroberto 14854359Sroberto/* What is the inherent delay for this mode of working, i.e. when is the 14954359Sroberto * data time stamped. 15054359Sroberto */ 15154359Sroberto#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */ 15254359Sroberto#define BITS_TO_L_FP(bits, baud) \ 15354359Sroberto(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT) 15454359Sroberto#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600) 15554359Sroberto#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600) 15654359Sroberto 15754359Sroberto#ifndef STREAM_PP1 15854359Sroberto#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->" 15954359Sroberto#endif 16054359Sroberto#ifndef STREAM_PP2 16154359Sroberto#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->" 16254359Sroberto#endif 16354359Sroberto 16454359Sroberto /* Offsets of the bytes of the serial line code. The clock gives 16554359Sroberto * local time with a GMT/BST indication. The EESM_ definitions 16654359Sroberto * give offsets into ees->lastcode. 16754359Sroberto */ 16854359Sroberto#define EESM_CSEC 0 /* centiseconds - always zero in our clock */ 16954359Sroberto#define EESM_SEC 1 /* seconds in BCD */ 17054359Sroberto#define EESM_MIN 2 /* minutes in BCD */ 17154359Sroberto#define EESM_HOUR 3 /* hours in BCD */ 17254359Sroberto#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */ 17354359Sroberto#define EESM_DAY 5 /* day of month in BCD */ 17454359Sroberto#define EESM_MON 6 /* month in BCD */ 17554359Sroberto#define EESM_YEAR 7 /* year MOD 100 in BCD */ 17654359Sroberto#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */ 17754359Sroberto#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */ 17854359Sroberto#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */ 17954359Sroberto /* followed by a frame alignment byte (0xff) / 18054359Sroberto / which is not put into the lastcode buffer*/ 18154359Sroberto 18254359Sroberto/* Length of the serial time code, in characters. The first length 18354359Sroberto * is less the frame alignment byte. 18454359Sroberto */ 18554359Sroberto#define LENEESPRT (EESM_MSFOK+1) 18654359Sroberto#define LENEESCODE (LENEESPRT+1) 18754359Sroberto 18854359Sroberto /* Code state. */ 18954359Sroberto#define EESCS_WAIT 0 /* waiting for start of timecode */ 19054359Sroberto#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */ 19154359Sroberto 19254359Sroberto /* Default fudge factor and character to receive */ 19354359Sroberto#define DEFFUDGETIME 0 /* Default user supplied fudge factor */ 19454359Sroberto#ifndef DEFOSTIME 19554359Sroberto#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */ 19654359Sroberto#endif 19754359Sroberto#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/ 19854359Sroberto 19954359Sroberto /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median 20054359Sroberto * elimination. If we're running with an accurate clock, chose the BESTSAMPLE 20154359Sroberto * as the estimated offset, otherwise average the remainder. 20254359Sroberto */ 20354359Sroberto#define FULLSHIFT 6 /* NCODES root 2 */ 20454359Sroberto#define NCODES (1<< FULLSHIFT) /* 64 */ 20554359Sroberto#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */ 20654359Sroberto 20754359Sroberto /* Towards the high ( Why ?) end of half */ 20854359Sroberto#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */ 20954359Sroberto 21054359Sroberto /* Leap hold time. After a leap second the clock will no longer be 21154359Sroberto * reliable until it resynchronizes. Hope 40 minutes is enough. */ 21254359Sroberto#define EESLEAPHOLD (40 * 60) 21354359Sroberto 21454359Sroberto#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */ 21554359Sroberto#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/ 21654359Sroberto#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */ 21754359Sroberto#define EES_STEP_NOTES 50 /* Only do a limited number */ 21854359Sroberto#define MAX_STEP 16 /* Max number of steps to remember */ 21954359Sroberto 22054359Sroberto /* debug is a bit mask of debugging that is wanted */ 22154359Sroberto#define DB_SYSLOG_SMPLI 0x0001 22254359Sroberto#define DB_SYSLOG_SMPLE 0x0002 22354359Sroberto#define DB_SYSLOG_SMTHI 0x0004 22454359Sroberto#define DB_SYSLOG_NSMTHE 0x0008 22554359Sroberto#define DB_SYSLOG_NSMTHI 0x0010 22654359Sroberto#define DB_SYSLOG_SMTHE 0x0020 22754359Sroberto#define DB_PRINT_EV 0x0040 22854359Sroberto#define DB_PRINT_CDT 0x0080 22954359Sroberto#define DB_PRINT_CDTC 0x0100 23054359Sroberto#define DB_SYSLOG_KEEPD 0x0800 23154359Sroberto#define DB_SYSLOG_KEEPE 0x1000 23254359Sroberto#define DB_LOG_DELTAS 0x2000 23354359Sroberto#define DB_PRINT_DELTAS 0x4000 23454359Sroberto#define DB_LOG_AWAITMORE 0x8000 23554359Sroberto#define DB_LOG_SAMPLES 0x10000 23654359Sroberto#define DB_NO_PPS 0x20000 23754359Sroberto#define DB_INC_PPS 0x40000 23854359Sroberto#define DB_DUMP_DELTAS 0x80000 23954359Sroberto 24054359Sroberto struct eesunit { /* EES unit control structure. */ 24154359Sroberto struct peer *peer; /* associated peer structure */ 24254359Sroberto struct refclockio io; /* given to the I/O handler */ 24354359Sroberto l_fp reftime; /* reference time */ 24454359Sroberto l_fp lastsampletime; /* time as in txt from last EES msg */ 24554359Sroberto l_fp arrvtime; /* Time at which pkt arrived */ 24654359Sroberto l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */ 24754359Sroberto l_fp offset; /* chosen offset (for clkbug) */ 24854359Sroberto l_fp lowoffset; /* lowest sample offset (for clkbug) */ 24954359Sroberto l_fp highoffset; /* highest " " (for clkbug) */ 25054359Sroberto char lastcode[LENEESCODE+6]; /* last time code we received */ 25154359Sroberto u_long lasttime; /* last time clock heard from */ 25254359Sroberto u_long clocklastgood; /* last time good radio seen */ 25354359Sroberto u_char lencode; /* length of code in buffer */ 25454359Sroberto u_char nsamples; /* number of samples we've collected */ 25554359Sroberto u_char codestate; /* state of 232 code reception */ 25654359Sroberto u_char unit; /* unit number for this guy */ 25754359Sroberto u_char status; /* clock status */ 25854359Sroberto u_char lastevent; /* last clock event */ 25954359Sroberto u_char reason; /* reason for last abort */ 26054359Sroberto u_char hour; /* hour of day */ 26154359Sroberto u_char minute; /* minute of hour */ 26254359Sroberto u_char second; /* seconds of minute */ 26354359Sroberto char tz; /* timezone from clock */ 26454359Sroberto u_char ttytype; /* method used */ 26554359Sroberto u_char dump_vals; /* Should clock values be dumped */ 26654359Sroberto u_char usealldata; /* Use ALL samples */ 26754359Sroberto u_short day; /* day of year from last code */ 26854359Sroberto u_long yearstart; /* start of current year */ 26954359Sroberto u_long leaphold; /* time of leap hold expiry */ 27054359Sroberto u_long badformat; /* number of bad format codes */ 27154359Sroberto u_long baddata; /* number of invalid time codes */ 27254359Sroberto u_long timestarted; /* time we started this */ 27354359Sroberto long last_pps_no; /* The serial # of the last PPS */ 27454359Sroberto char fix_pending; /* Is a "sync to time" pending ? */ 27554359Sroberto /* Fine tuning - compensate for 4 mS ramping .... */ 27654359Sroberto l_fp last_l; /* last time stamp */ 27754359Sroberto u_char last_steps[MAX_STEP]; /* Most recent n steps */ 27854359Sroberto int best_av_step; /* Best guess at average step */ 27954359Sroberto char best_av_step_count; /* # of steps over used above */ 28054359Sroberto char this_step; /* Current pos in buffer */ 28154359Sroberto int last_step_late; /* How late the last step was (0-59) */ 28254359Sroberto long jump_fsecs; /* # of fractions of a sec last jump */ 28354359Sroberto u_long last_step; /* time of last step */ 28454359Sroberto int last_step_secs; /* Number of seconds in last step */ 28554359Sroberto int using_ramp; /* 1 -> noemal, -1 -> over stepped */ 28654359Sroberto }; 28754359Sroberto#define last_sec last_l.l_ui 28854359Sroberto#define last_sfsec last_l.l_f 28954359Sroberto#define this_uisec ((ees->arrvtime).l_ui) 29054359Sroberto#define this_sfsec ((ees->arrvtime).l_f) 29154359Sroberto#define msec(x) ((x) / (1<<22)) 29254359Sroberto#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0]) 29354359Sroberto#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5))) 29454359Sroberto 29554359Sroberto/* Bitmask for what methods to try to use -- currently only PPS enabled */ 29654359Sroberto#define T_CBREAK 1 29754359Sroberto#define T_PPS 8 29854359Sroberto/* macros to test above */ 29954359Sroberto#define is_cbreak(x) ((x)->ttytype & T_CBREAK) 30054359Sroberto#define is_pps(x) ((x)->ttytype & T_PPS) 30154359Sroberto#define is_any(x) ((x)->ttytype) 30254359Sroberto 30354359Sroberto#define CODEREASON 20 /* reason codes */ 30454359Sroberto 30554359Sroberto/* Data space for the unit structures. Note that we allocate these on 30654359Sroberto * the fly, but never give them back. */ 30754359Srobertostatic struct eesunit *eesunits[MAXUNITS]; 30854359Srobertostatic u_char unitinuse[MAXUNITS]; 30954359Sroberto 31054359Sroberto/* Keep the fudge factors separately so they can be set even 31154359Sroberto * when no clock is configured. */ 31254359Srobertostatic l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */ 31354359Srobertostatic l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */ 31454359Srobertostatic l_fp os_delay[MAXUNITS]; /* fudgetime2 */ 31554359Srobertostatic l_fp offset_fudge[MAXUNITS]; /* Sum of above */ 31654359Srobertostatic u_char stratumtouse[MAXUNITS]; 31754359Srobertostatic u_char sloppyclockflag[MAXUNITS]; 31854359Sroberto 31954359Srobertostatic int deltas[60]; 32054359Sroberto 32154359Srobertostatic l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */ 32254359Srobertostatic l_fp onesec; /* = { 1, 0 }; */ 32354359Sroberto 32454359Sroberto#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */ 32554359Sroberto#define DUMP_BUF_SIZE 10112 32654359Sroberto#endif 32754359Sroberto 32854359Sroberto/* ees_reset - reset the count back to zero */ 32954359Sroberto#define ees_reset(ees) (ees)->nsamples = 0; \ 33054359Sroberto(ees)->codestate = EESCS_WAIT 33154359Sroberto 33254359Sroberto/* ees_event - record and report an event */ 33354359Sroberto#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \ 33454359Srobertoees_report_event((ees), (evcode)) 33554359Sroberto 33654359Sroberto /* Find the precision of the system clock by reading it */ 33754359Sroberto#define USECS 1000000 33854359Sroberto#define MINSTEP 5 /* some systems increment uS on each call */ 33954359Sroberto#define MAXLOOPS (USECS/9) 34054359Sroberto 34154359Sroberto/* 34254359Sroberto * Function prototypes 34354359Sroberto */ 34454359Sroberto 34554359Srobertostatic int msfees_start P((int unit, struct peer *peer)); 34654359Srobertostatic void msfees_shutdown P((int unit, struct peer *peer)); 34754359Srobertostatic void msfees_poll P((int unit, struct peer *peer)); 34854359Srobertostatic void msfees_init P((void)); 34954359Srobertostatic void dump_buf P((l_fp *coffs, int from, int to, char *text)); 35054359Srobertostatic void ees_report_event P((struct eesunit *ees, int code)); 35154359Srobertostatic void ees_receive P((struct recvbuf *rbufp)); 35254359Srobertostatic void ees_process P((struct eesunit *ees)); 35356746Srobertostatic int offcompare P((const void *va, const void *vb)); 35454359Sroberto 35556746Sroberto 35654359Sroberto/* 35754359Sroberto * Transfer vector 35854359Sroberto */ 35954359Srobertostruct refclock refclock_msfees = { 36054359Sroberto msfees_start, /* start up driver */ 36154359Sroberto msfees_shutdown, /* shut down driver */ 36254359Sroberto msfees_poll, /* transmit poll message */ 36354359Sroberto noentry, /* not used */ 36454359Sroberto msfees_init, /* initialize driver */ 36554359Sroberto noentry, /* not used */ 36654359Sroberto NOFLAGS /* not used */ 36754359Sroberto}; 36854359Sroberto 36954359Sroberto 37054359Srobertostatic void 37154359Srobertodump_buf( 37254359Sroberto l_fp *coffs, 37354359Sroberto int from, 37454359Sroberto int to, 37554359Sroberto char *text 37654359Sroberto ) 37754359Sroberto{ 37854359Sroberto char buff[DUMP_BUF_SIZE + 80]; 37954359Sroberto int i; 38054359Sroberto register char *ptr = buff; 38154359Sroberto 382280849Scy snprintf(buff, sizeof(buff), text); 383280849Scy for (i = from; i < to; i++) { 384280849Scy ptr += strlen(ptr); 385280849Scy if ((ptr - buff) > DUMP_BUF_SIZE) { 386280849Scy msyslog(LOG_DEBUG, "D: %s", buff); 387280849Scy ptr = buff; 388280849Scy } 389280849Scy snprintf(ptr, sizeof(buff) - (ptr - buff), 390280849Scy " %06d", ((int)coffs[i].l_f) / 4295); 39154359Sroberto } 39254359Sroberto msyslog(LOG_DEBUG, "D: %s", buff); 39354359Sroberto} 39454359Sroberto 39554359Sroberto/* msfees_init - initialize internal ees driver data */ 39654359Srobertostatic void 39754359Srobertomsfees_init(void) 39854359Sroberto{ 39954359Sroberto register int i; 40054359Sroberto /* Just zero the data arrays */ 40154359Sroberto memset((char *)eesunits, 0, sizeof eesunits); 40254359Sroberto memset((char *)unitinuse, 0, sizeof unitinuse); 40354359Sroberto 40454359Sroberto acceptable_slop.l_ui = 0; 40554359Sroberto acceptable_slop.l_uf = 1 << (FRACTION_PREC -2); 40654359Sroberto 40754359Sroberto onesec.l_ui = 1; 40854359Sroberto onesec.l_uf = 0; 40954359Sroberto 41054359Sroberto /* Initialize fudge factors to default. */ 41154359Sroberto for (i = 0; i < MAXUNITS; i++) { 41254359Sroberto fudgefactor[i].l_ui = 0; 41354359Sroberto fudgefactor[i].l_uf = DEFFUDGETIME; 41454359Sroberto os_delay[i].l_ui = 0; 41554359Sroberto os_delay[i].l_uf = DEFOSTIME; 41654359Sroberto inherent_delay[i].l_ui = 0; 41754359Sroberto inherent_delay[i].l_uf = DEFINHTIME; 41854359Sroberto offset_fudge[i] = os_delay[i]; 41954359Sroberto L_ADD(&offset_fudge[i], &fudgefactor[i]); 42054359Sroberto L_ADD(&offset_fudge[i], &inherent_delay[i]); 42154359Sroberto stratumtouse[i] = 0; 42254359Sroberto sloppyclockflag[i] = 0; 42354359Sroberto } 42454359Sroberto} 42554359Sroberto 42654359Sroberto 42754359Sroberto/* msfees_start - open the EES devices and initialize data for processing */ 42854359Srobertostatic int 42954359Srobertomsfees_start( 43054359Sroberto int unit, 43154359Sroberto struct peer *peer 43254359Sroberto ) 43354359Sroberto{ 43454359Sroberto register struct eesunit *ees; 43554359Sroberto register int i; 43654359Sroberto int fd232 = -1; 43754359Sroberto char eesdev[20]; 43854359Sroberto struct termios ttyb, *ttyp; 43954359Sroberto struct refclockproc *pp; 44054359Sroberto pp = peer->procptr; 44154359Sroberto 44254359Sroberto if (unit >= MAXUNITS) { 44354359Sroberto msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", 44454359Sroberto unit, MAXUNITS-1); 44554359Sroberto return 0; 44654359Sroberto } 44754359Sroberto if (unitinuse[unit]) { 44854359Sroberto msyslog(LOG_ERR, "ees clock: unit number %d in use", unit); 44954359Sroberto return 0; 45054359Sroberto } 45154359Sroberto 45254359Sroberto /* Unit okay, attempt to open the devices. We do them both at 45354359Sroberto * once to make sure we can */ 454280849Scy snprintf(eesdev, sizeof(eesdev), EES232, unit); 45554359Sroberto 45654359Sroberto fd232 = open(eesdev, O_RDWR, 0777); 45754359Sroberto if (fd232 == -1) { 45854359Sroberto msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev); 45954359Sroberto return 0; 46054359Sroberto } 46154359Sroberto 46254359Sroberto#ifdef TIOCEXCL 46354359Sroberto /* Set for exclusive use */ 46454359Sroberto if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) { 46554359Sroberto msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev); 46654359Sroberto goto screwed; 46754359Sroberto } 46854359Sroberto#endif 46954359Sroberto 47054359Sroberto /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */ 47154359Sroberto 47254359Sroberto /* Set port characteristics. If we don't have a STREAMS module or 47354359Sroberto * a clock line discipline, cooked mode is just usable, even though it 47454359Sroberto * strips the top bit. The only EES byte which uses the top 47554359Sroberto * bit is the year, and we don't use that anyway. If we do 47654359Sroberto * have the line discipline, we choose raw mode, and the 47754359Sroberto * line discipline code will block up the messages. 47854359Sroberto */ 47954359Sroberto 48054359Sroberto /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */ 48154359Sroberto 48254359Sroberto ttyp = &ttyb; 48354359Sroberto if (tcgetattr(fd232, ttyp) < 0) { 48454359Sroberto msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev); 48554359Sroberto goto screwed; 48654359Sroberto } 48754359Sroberto 48854359Sroberto ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; 48954359Sroberto ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; 49054359Sroberto ttyp->c_oflag = 0; 49154359Sroberto ttyp->c_lflag = ICANON; 49254359Sroberto ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; 49354359Sroberto if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { 49454359Sroberto msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); 49554359Sroberto goto screwed; 49654359Sroberto } 49754359Sroberto 49854359Sroberto if (tcflush(fd232, TCIOFLUSH) < 0) { 49954359Sroberto msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev); 50054359Sroberto goto screwed; 50154359Sroberto } 50254359Sroberto 50354359Sroberto inherent_delay[unit].l_uf = INH_DELAY_PPS; 50454359Sroberto 50554359Sroberto /* offset fudge (how *late* the timestamp is) = fudge + os delays */ 50654359Sroberto offset_fudge[unit] = os_delay[unit]; 50754359Sroberto L_ADD(&offset_fudge[unit], &fudgefactor[unit]); 50854359Sroberto L_ADD(&offset_fudge[unit], &inherent_delay[unit]); 50954359Sroberto 51054359Sroberto /* Looks like this might succeed. Find memory for the structure. 51154359Sroberto * Look to see if there are any unused ones, if not we malloc() one. 51254359Sroberto */ 51354359Sroberto if (eesunits[unit] != 0) /* The one we want is okay */ 51454359Sroberto ees = eesunits[unit]; 51554359Sroberto else { 51654359Sroberto /* Look for an unused, but allocated struct */ 51754359Sroberto for (i = 0; i < MAXUNITS; i++) { 51854359Sroberto if (!unitinuse[i] && eesunits[i] != 0) 51954359Sroberto break; 52054359Sroberto } 52154359Sroberto 52254359Sroberto if (i < MAXUNITS) { /* Reclaim this one */ 52354359Sroberto ees = eesunits[i]; 52454359Sroberto eesunits[i] = 0; 52554359Sroberto } /* no spare -- make a new one */ 52654359Sroberto else ees = (struct eesunit *) emalloc(sizeof(struct eesunit)); 52754359Sroberto } 52854359Sroberto memset((char *)ees, 0, sizeof(struct eesunit)); 52954359Sroberto eesunits[unit] = ees; 53054359Sroberto 53154359Sroberto /* Set up the structures */ 53254359Sroberto ees->peer = peer; 53354359Sroberto ees->unit = (u_char)unit; 53454359Sroberto ees->timestarted= current_time; 53554359Sroberto ees->ttytype = 0; 53654359Sroberto ees->io.clock_recv= ees_receive; 537280849Scy ees->io.srcclock= peer; 53854359Sroberto ees->io.datalen = 0; 53954359Sroberto ees->io.fd = fd232; 54054359Sroberto 54154359Sroberto /* Okay. Push one of the two (linked into the kernel, or dynamically 54254359Sroberto * loaded) STREAMS module, and give it to the I/O code to start 54354359Sroberto * receiving stuff. 54454359Sroberto */ 54554359Sroberto 54654359Sroberto#ifdef STREAM 54754359Sroberto { 54854359Sroberto int rc1; 54954359Sroberto /* Pop any existing onews first ... */ 55054359Sroberto while (ioctl(fd232, I_POP, 0 ) >= 0) ; 55154359Sroberto 55254359Sroberto /* Now try pushing either of the possible modules */ 55354359Sroberto if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 && 55454359Sroberto ioctl(fd232, I_PUSH, STREAM_PP2) < 0) { 55554359Sroberto msyslog(LOG_ERR, 55654359Sroberto "ees clock: Push of `%s' and `%s' to %s failed %m", 55754359Sroberto STREAM_PP1, STREAM_PP2, eesdev); 55854359Sroberto goto screwed; 55954359Sroberto } 56054359Sroberto else { 56154359Sroberto NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 56254359Sroberto msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s", 56354359Sroberto (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev); 56454359Sroberto ees->ttytype |= T_PPS; 56554359Sroberto } 56654359Sroberto } 56754359Sroberto#endif /* STREAM */ 56854359Sroberto 56954359Sroberto /* Add the clock */ 57054359Sroberto if (!io_addclock(&ees->io)) { 57154359Sroberto /* Oh shit. Just close and return. */ 57254359Sroberto msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev); 57354359Sroberto goto screwed; 57454359Sroberto } 57554359Sroberto 57654359Sroberto 57754359Sroberto /* All done. Initialize a few random peer variables, then 57854359Sroberto * return success. */ 57954359Sroberto peer->precision = sys_precision; 58054359Sroberto peer->stratum = stratumtouse[unit]; 58154359Sroberto if (stratumtouse[unit] <= 1) { 58254359Sroberto memcpy((char *)&pp->refid, EESREFID, 4); 58354359Sroberto if (unit > 0 && unit < 10) 58454359Sroberto ((char *)&pp->refid)[3] = '0' + unit; 58554359Sroberto } else { 58654359Sroberto peer->refid = htonl(EESHSREFID); 58754359Sroberto } 58854359Sroberto unitinuse[unit] = 1; 589280849Scy pp->unitptr = &eesunits[unit]; 59054359Sroberto pp->clockdesc = EESDESCRIPTION; 59154359Sroberto msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit); 59254359Sroberto return (1); 59354359Sroberto 59454359Sroberto screwed: 59554359Sroberto if (fd232 != -1) 59654359Sroberto (void) close(fd232); 59754359Sroberto return (0); 59854359Sroberto} 59954359Sroberto 60054359Sroberto 60154359Sroberto/* msfees_shutdown - shut down a EES clock */ 60254359Srobertostatic void 60354359Srobertomsfees_shutdown( 60454359Sroberto int unit, 60554359Sroberto struct peer *peer 60654359Sroberto ) 60754359Sroberto{ 60854359Sroberto register struct eesunit *ees; 60954359Sroberto 61054359Sroberto if (unit >= MAXUNITS) { 61154359Sroberto msyslog(LOG_ERR, 61254359Sroberto "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)", 61354359Sroberto unit, MAXUNITS); 61454359Sroberto return; 61554359Sroberto } 61654359Sroberto if (!unitinuse[unit]) { 61754359Sroberto msyslog(LOG_ERR, 61854359Sroberto "ees clock: INTERNAL ERROR, unit number %d not in use", unit); 61954359Sroberto return; 62054359Sroberto } 62154359Sroberto 62254359Sroberto /* Tell the I/O module to turn us off. We're history. */ 62354359Sroberto ees = eesunits[unit]; 62454359Sroberto io_closeclock(&ees->io); 62554359Sroberto unitinuse[unit] = 0; 62654359Sroberto} 62754359Sroberto 62854359Sroberto 62954359Sroberto/* ees_report_event - note the occurance of an event */ 63054359Srobertostatic void 63154359Srobertoees_report_event( 63254359Sroberto struct eesunit *ees, 63354359Sroberto int code 63454359Sroberto ) 63554359Sroberto{ 63654359Sroberto if (ees->status != (u_char)code) { 63754359Sroberto ees->status = (u_char)code; 63854359Sroberto if (code != CEVNT_NOMINAL) 63954359Sroberto ees->lastevent = (u_char)code; 64054359Sroberto /* Should report event to trap handler in here. 64154359Sroberto * Soon... 64254359Sroberto */ 64354359Sroberto } 64454359Sroberto} 64554359Sroberto 64654359Sroberto 64754359Sroberto/* ees_receive - receive data from the serial interface on an EES clock */ 64854359Srobertostatic void 64954359Srobertoees_receive( 65054359Sroberto struct recvbuf *rbufp 65154359Sroberto ) 65254359Sroberto{ 65354359Sroberto register int n_sample; 65454359Sroberto register int day; 65554359Sroberto register struct eesunit *ees; 65654359Sroberto register u_char *dpt; /* Data PoinTeR: move along ... */ 65754359Sroberto register u_char *dpend; /* Points just *after* last data char */ 65854359Sroberto register char *cp; 65954359Sroberto l_fp tmp; 66054359Sroberto int call_pps_sample = 0; 66154359Sroberto l_fp pps_arrvstamp; 66254359Sroberto int sincelast; 66354359Sroberto int pps_step = 0; 66454359Sroberto int suspect_4ms_step = 0; 66554359Sroberto struct ppsclockev ppsclockev; 66654359Sroberto long *ptr = (long *) &ppsclockev; 66754359Sroberto int rc; 66854359Sroberto int request; 66954359Sroberto#ifdef HAVE_CIOGETEV 67054359Sroberto request = CIOGETEV; 67154359Sroberto#endif 67254359Sroberto#ifdef HAVE_TIOCGPPSEV 67354359Sroberto request = TIOCGPPSEV; 67454359Sroberto#endif 67554359Sroberto 67654359Sroberto /* Get the clock this applies to and a pointer to the data */ 677280849Scy ees = (struct eesunit *)rbufp->recv_peer->procptr->unitptr; 67854359Sroberto dpt = (u_char *)&rbufp->recv_space; 67954359Sroberto dpend = dpt + rbufp->recv_length; 680182007Sroberto if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE)) 68154359Sroberto printf("[%d] ", rbufp->recv_length); 68254359Sroberto 68354359Sroberto /* Check out our state and process appropriately */ 68454359Sroberto switch (ees->codestate) { 68554359Sroberto case EESCS_WAIT: 68654359Sroberto /* Set an initial guess at the timestamp as the recv time. 68754359Sroberto * If just running in CBREAK mode, we can't improve this. 68854359Sroberto * If we have the CLOCK Line Discipline, PPSCD, or sime such, 68954359Sroberto * then we will do better later .... 69054359Sroberto */ 69154359Sroberto ees->arrvtime = rbufp->recv_time; 69254359Sroberto ees->codestate = EESCS_GOTSOME; 69354359Sroberto ees->lencode = 0; 69454359Sroberto /*FALLSTHROUGH*/ 69554359Sroberto 69654359Sroberto case EESCS_GOTSOME: 69754359Sroberto cp = &(ees->lastcode[ees->lencode]); 69854359Sroberto 69954359Sroberto /* Gobble the bytes until the final (possibly stripped) 0xff */ 70054359Sroberto while (dpt < dpend && (*dpt & 0x7f) != 0x7f) { 70154359Sroberto *cp++ = (char)*dpt++; 70254359Sroberto ees->lencode++; 70354359Sroberto /* Oh dear -- too many bytes .. */ 70454359Sroberto if (ees->lencode > LENEESPRT) { 70554359Sroberto NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 70654359Sroberto msyslog(LOG_INFO, 70754359Sroberto "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]", 70854359Sroberto ees->lencode, dpend - dpt, LENEESPRT, 70954359Sroberto#define D(x) (ees->lastcode[x]) 71054359Sroberto D(0), D(1), D(2), D(3), D(4), D(5), D(6), 71154359Sroberto D(7), D(8), D(9), D(10), D(11), D(12)); 71254359Sroberto#undef D 71354359Sroberto ees->badformat++; 71454359Sroberto ees->reason = CODEREASON + 1; 71554359Sroberto ees_event(ees, CEVNT_BADREPLY); 71654359Sroberto ees_reset(ees); 71754359Sroberto return; 71854359Sroberto } 71954359Sroberto } 72054359Sroberto /* Gave up because it was end of the buffer, rather than ff */ 72154359Sroberto if (dpt == dpend) { 72254359Sroberto /* Incomplete. Wait for more. */ 723182007Sroberto if (dbg & DB_LOG_AWAITMORE) 72454359Sroberto msyslog(LOG_INFO, 725132451Sroberto "I: ees clock %d: %p == %p: await more", 72654359Sroberto ees->unit, dpt, dpend); 72754359Sroberto return; 72854359Sroberto } 72954359Sroberto 73054359Sroberto /* This shouldn't happen ... ! */ 73154359Sroberto if ((*dpt & 0x7f) != 0x7f) { 73254359Sroberto msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt); 73354359Sroberto ees->badformat++; 73454359Sroberto ees->reason = CODEREASON + 2; 73554359Sroberto ees_event(ees, CEVNT_BADREPLY); 73654359Sroberto ees_reset(ees); 73754359Sroberto return; 73854359Sroberto } 73954359Sroberto 74054359Sroberto /* Skip the 0xff */ 74154359Sroberto dpt++; 74254359Sroberto 74354359Sroberto /* Finally, got a complete buffer. Mainline code will 74454359Sroberto * continue on. */ 74554359Sroberto cp = ees->lastcode; 74654359Sroberto break; 74754359Sroberto 74854359Sroberto default: 74954359Sroberto msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d", 75054359Sroberto ees->unit, ees->codestate); 75154359Sroberto ees->reason = CODEREASON + 5; 75254359Sroberto ees_event(ees, CEVNT_FAULT); 75354359Sroberto ees_reset(ees); 75454359Sroberto return; 75554359Sroberto } 75654359Sroberto 75754359Sroberto /* Boy! After all that crap, the lastcode buffer now contains 75854359Sroberto * something we hope will be a valid time code. Do length 75954359Sroberto * checks and sanity checks on constant data. 76054359Sroberto */ 76154359Sroberto ees->codestate = EESCS_WAIT; 76254359Sroberto ees->lasttime = current_time; 76354359Sroberto if (ees->lencode != LENEESPRT) { 76454359Sroberto ees->badformat++; 76554359Sroberto ees->reason = CODEREASON + 6; 76654359Sroberto ees_event(ees, CEVNT_BADREPLY); 76754359Sroberto ees_reset(ees); 76854359Sroberto return; 76954359Sroberto } 77054359Sroberto 77154359Sroberto cp = ees->lastcode; 77254359Sroberto 77354359Sroberto /* Check that centisecond is zero */ 77454359Sroberto if (cp[EESM_CSEC] != 0) { 77554359Sroberto ees->baddata++; 77654359Sroberto ees->reason = CODEREASON + 7; 77754359Sroberto ees_event(ees, CEVNT_BADREPLY); 77854359Sroberto ees_reset(ees); 77954359Sroberto return; 78054359Sroberto } 78154359Sroberto 78254359Sroberto /* Check flag formats */ 78354359Sroberto if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) { 78454359Sroberto ees->badformat++; 78554359Sroberto ees->reason = CODEREASON + 8; 78654359Sroberto ees_event(ees, CEVNT_BADREPLY); 78754359Sroberto ees_reset(ees); 78854359Sroberto return; 78954359Sroberto } 79054359Sroberto 79154359Sroberto if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) { 79254359Sroberto ees->badformat++; 79354359Sroberto ees->reason = CODEREASON + 9; 79454359Sroberto ees_event(ees, CEVNT_BADREPLY); 79554359Sroberto ees_reset(ees); 79654359Sroberto return; 79754359Sroberto } 79854359Sroberto 79954359Sroberto if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) { 80054359Sroberto ees->badformat++; 80154359Sroberto ees->reason = CODEREASON + 10; 80254359Sroberto ees_event(ees, CEVNT_BADREPLY); 80354359Sroberto ees_reset(ees); 80454359Sroberto return; 80554359Sroberto } 80654359Sroberto 80754359Sroberto /* So far, so good. Compute day, hours, minutes, seconds, 80854359Sroberto * time zone. Do range checks on these. 80954359Sroberto */ 81054359Sroberto 81154359Sroberto#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) ) 81254359Sroberto#define istrue(x) ((x)?1:0) 81354359Sroberto 81454359Sroberto ees->second = bcdunpack(cp[EESM_SEC]); /* second */ 81554359Sroberto ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */ 81654359Sroberto ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */ 81754359Sroberto 81854359Sroberto day = bcdunpack(cp[EESM_DAY]); /* day of month */ 81954359Sroberto 82054359Sroberto switch (bcdunpack(cp[EESM_MON])) { /* month */ 82154359Sroberto 82254359Sroberto /* Add in lengths of all previous months. Add one more 82354359Sroberto if it is a leap year and after February. 82454359Sroberto */ 82554359Sroberto case 12: day += NOV; /*FALLSTHROUGH*/ 82654359Sroberto case 11: day += OCT; /*FALLSTHROUGH*/ 82754359Sroberto case 10: day += SEP; /*FALLSTHROUGH*/ 82854359Sroberto case 9: day += AUG; /*FALLSTHROUGH*/ 82954359Sroberto case 8: day += JUL; /*FALLSTHROUGH*/ 83054359Sroberto case 7: day += JUN; /*FALLSTHROUGH*/ 83154359Sroberto case 6: day += MAY; /*FALLSTHROUGH*/ 83254359Sroberto case 5: day += APR; /*FALLSTHROUGH*/ 83354359Sroberto case 4: day += MAR; /*FALLSTHROUGH*/ 83454359Sroberto case 3: day += FEB; 83554359Sroberto if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/ 83654359Sroberto case 2: day += JAN; /*FALLSTHROUGH*/ 83754359Sroberto case 1: break; 83854359Sroberto default: ees->baddata++; 83954359Sroberto ees->reason = CODEREASON + 11; 84054359Sroberto ees_event(ees, CEVNT_BADDATE); 84154359Sroberto ees_reset(ees); 84254359Sroberto return; 84354359Sroberto } 84454359Sroberto 84554359Sroberto ees->day = day; 84654359Sroberto 84754359Sroberto /* Get timezone. The clocktime routine wants the number 84854359Sroberto * of hours to add to the delivered time to get UT. 84954359Sroberto * Currently -1 if BST flag set, 0 otherwise. This 85054359Sroberto * is the place to tweak things if double summer time 85154359Sroberto * ever happens. 85254359Sroberto */ 85354359Sroberto ees->tz = istrue(cp[EESM_BST]) ? -1 : 0; 85454359Sroberto 85554359Sroberto if (ees->day > 366 || ees->day < 1 || 85654359Sroberto ees->hour > 23 || ees->minute > 59 || ees->second > 59) { 85754359Sroberto ees->baddata++; 85854359Sroberto ees->reason = CODEREASON + 12; 85954359Sroberto ees_event(ees, CEVNT_BADDATE); 86054359Sroberto ees_reset(ees); 86154359Sroberto return; 86254359Sroberto } 86354359Sroberto 86454359Sroberto n_sample = ees->nsamples; 86554359Sroberto 86654359Sroberto /* Now, compute the reference time value: text -> tmp.l_ui */ 86754359Sroberto if (!clocktime(ees->day, ees->hour, ees->minute, ees->second, 86854359Sroberto ees->tz, rbufp->recv_time.l_ui, &ees->yearstart, 86954359Sroberto &tmp.l_ui)) { 87054359Sroberto ees->baddata++; 87154359Sroberto ees->reason = CODEREASON + 13; 87254359Sroberto ees_event(ees, CEVNT_BADDATE); 87354359Sroberto ees_reset(ees); 87454359Sroberto return; 87554359Sroberto } 87654359Sroberto tmp.l_uf = 0; 87754359Sroberto 87854359Sroberto /* DON'T use ees->arrvtime -- it may be < reftime */ 87954359Sroberto ees->lastsampletime = tmp; 88054359Sroberto 88154359Sroberto /* If we are synchronised to the radio, update the reference time. 88254359Sroberto * Also keep a note of when clock was last good. 88354359Sroberto */ 88454359Sroberto if (istrue(cp[EESM_MSFOK])) { 88554359Sroberto ees->reftime = tmp; 88654359Sroberto ees->clocklastgood = current_time; 88754359Sroberto } 88854359Sroberto 88954359Sroberto 89054359Sroberto /* Compute the offset. For the fractional part of the 89154359Sroberto * offset we use the expected delay for the message. 89254359Sroberto */ 89354359Sroberto ees->codeoffsets[n_sample].l_ui = tmp.l_ui; 89454359Sroberto ees->codeoffsets[n_sample].l_uf = 0; 89554359Sroberto 89654359Sroberto /* Number of seconds since the last step */ 89754359Sroberto sincelast = this_uisec - ees->last_step; 89854359Sroberto 89954359Sroberto memset((char *) &ppsclockev, 0, sizeof ppsclockev); 90054359Sroberto 90154359Sroberto rc = ioctl(ees->io.fd, request, (char *) &ppsclockev); 902182007Sroberto if (dbg & DB_PRINT_EV) fprintf(stderr, 90354359Sroberto "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n", 90454359Sroberto DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees), 90554359Sroberto rc, errno, ptr[0], ptr[1], ptr[2]); 90654359Sroberto 90754359Sroberto /* If we managed to get the time of arrival, process the info */ 90854359Sroberto if (rc >= 0) { 90954359Sroberto int conv = -1; 91054359Sroberto pps_step = ppsclockev.serial - ees->last_pps_no; 91154359Sroberto 91254359Sroberto /* Possible that PPS triggered, but text message didn't */ 91354359Sroberto if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second); 91454359Sroberto if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1; 91554359Sroberto if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4; 91654359Sroberto 91754359Sroberto /* allow for single loss of PPS only */ 91854359Sroberto if (pps_step != 1 && pps_step != 2) 91954359Sroberto fprintf(stderr, "PPS step: %d too far off %ld (%d)\n", 92054359Sroberto ppsclockev.serial, ees->last_pps_no, pps_step); 921280849Scy else { 922280849Scy pps_arrvstamp = tval_stamp_to_lfp(ppsclockev.tv); 923280849Scy /* if ((ABS(time difference) - 0.25) < 0) 92454359Sroberto * then believe it ... 92554359Sroberto */ 92654359Sroberto l_fp diff; 92754359Sroberto diff = pps_arrvstamp; 92854359Sroberto conv = 0; 92954359Sroberto L_SUB(&diff, &ees->arrvtime); 930182007Sroberto if (dbg & DB_PRINT_CDT) 93154359Sroberto printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s", 93254359Sroberto DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf, 93354359Sroberto (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf, 93454359Sroberto (long)diff.l_ui, (long)diff.l_uf, 93554359Sroberto ctime(&(ppsclockev.tv.tv_sec))); 93654359Sroberto if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); 93754359Sroberto L_SUB(&diff, &acceptable_slop); 93854359Sroberto if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ 93954359Sroberto ees->arrvtime = pps_arrvstamp; 94054359Sroberto conv++; 94154359Sroberto call_pps_sample++; 94254359Sroberto } 94354359Sroberto /* Some loss of some signals around sec = 1 */ 94454359Sroberto else if (ees->second == 1) { 94554359Sroberto diff = pps_arrvstamp; 94654359Sroberto L_ADD(&diff, &onesec); 94754359Sroberto L_SUB(&diff, &ees->arrvtime); 94854359Sroberto if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); 94954359Sroberto L_SUB(&diff, &acceptable_slop); 95054359Sroberto msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", 95154359Sroberto pps_arrvstamp.l_ui - ees->arrvtime.l_ui, 95254359Sroberto pps_arrvstamp.l_uf, 95354359Sroberto ees->arrvtime.l_uf, 95454359Sroberto diff.l_ui, diff.l_uf, 95554359Sroberto (int)ppsclockev.tv.tv_usec, 95654359Sroberto ctime(&(ppsclockev.tv.tv_sec))); 95754359Sroberto if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ 95854359Sroberto suspect_4ms_step |= 2; 95954359Sroberto ees->arrvtime = pps_arrvstamp; 96054359Sroberto L_ADD(&ees->arrvtime, &onesec); 96154359Sroberto conv++; 96254359Sroberto call_pps_sample++; 96354359Sroberto } 96454359Sroberto } 96554359Sroberto } 96654359Sroberto ees->last_pps_no = ppsclockev.serial; 967182007Sroberto if (dbg & DB_PRINT_CDTC) 96854359Sroberto printf( 96954359Sroberto "[%x] %08lx %08lx %d u%d (%d %d)\n", 97054359Sroberto DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui, 97154359Sroberto (long)pps_arrvstamp.l_uf, conv, ees->unit, 97254359Sroberto call_pps_sample, pps_step); 97354359Sroberto } 97454359Sroberto 97554359Sroberto /* See if there has been a 4ms jump at a minute boundry */ 97654359Sroberto { l_fp delta; 97754359Sroberto#define delta_isec delta.l_ui 97854359Sroberto#define delta_ssec delta.l_i 97954359Sroberto#define delta_sfsec delta.l_f 98054359Sroberto long delta_f_abs; 98154359Sroberto 98254359Sroberto delta.l_i = ees->arrvtime.l_i; 98354359Sroberto delta.l_f = ees->arrvtime.l_f; 98454359Sroberto 98554359Sroberto L_SUB(&delta, &ees->last_l); 98654359Sroberto delta_f_abs = delta_sfsec; 98754359Sroberto if (delta_f_abs < 0) delta_f_abs = -delta_f_abs; 98854359Sroberto 98954359Sroberto /* Dump the deltas each minute */ 990182007Sroberto if (dbg & DB_DUMP_DELTAS) 991280849Scy { 992280849Scy if (/*0 <= ees->second && */ 993280849Scy ees->second < COUNTOF(deltas)) 994280849Scy deltas[ees->second] = delta_sfsec; 99554359Sroberto /* Dump on second 1, as second 0 sometimes missed */ 99654359Sroberto if (ees->second == 1) { 997280849Scy char text[16 * COUNTOF(deltas)]; 99854359Sroberto char *cptr=text; 99954359Sroberto int i; 1000280849Scy for (i = 0; i < COUNTOF(deltas); i++) { 1001280849Scy snprintf(cptr, sizeof(text) / COUNTOF(deltas), 1002280849Scy " %d.%04d", msec(deltas[i]), 1003280849Scy subms(deltas[i])); 1004280849Scy cptr += strlen(cptr); 100554359Sroberto } 100654359Sroberto msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s", 100754359Sroberto msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE), 100854359Sroberto msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE), 100954359Sroberto text+1); 101054359Sroberto for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0; 101154359Sroberto } 101254359Sroberto } 101354359Sroberto 101454359Sroberto /* Lets see if we have a 4 mS step at a minute boundaary */ 101554359Sroberto if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) && 101654359Sroberto (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) && 101754359Sroberto (ees->second == 0 || ees->second == 1 || ees->second == 2) && 101854359Sroberto (sincelast < 0 || sincelast > 122) 101954359Sroberto ) { /* 4ms jump at min boundry */ 102054359Sroberto int old_sincelast; 102154359Sroberto int count=0; 102254359Sroberto int sum = 0; 102354359Sroberto /* Yes -- so compute the ramp time */ 102454359Sroberto if (ees->last_step == 0) sincelast = 0; 102554359Sroberto old_sincelast = sincelast; 102654359Sroberto 102754359Sroberto /* First time in, just set "ees->last_step" */ 102854359Sroberto if(ees->last_step) { 102954359Sroberto int other_step = 0; 103054359Sroberto int third_step = 0; 103154359Sroberto int this_step = (sincelast + (60 /2)) / 60; 103254359Sroberto int p_step = ees->this_step; 103354359Sroberto int p; 103454359Sroberto ees->last_steps[p_step] = this_step; 103554359Sroberto p= p_step; 103654359Sroberto p_step++; 103754359Sroberto if (p_step >= LAST_STEPS) p_step = 0; 103854359Sroberto ees->this_step = p_step; 103954359Sroberto /* Find the "average" interval */ 104054359Sroberto while (p != p_step) { 104154359Sroberto int this = ees->last_steps[p]; 104254359Sroberto if (this == 0) break; 104354359Sroberto if (this != this_step) { 104454359Sroberto if (other_step == 0 && ( 104554359Sroberto this== (this_step +2) || 104654359Sroberto this== (this_step -2) || 104754359Sroberto this== (this_step +1) || 104854359Sroberto this== (this_step -1))) 104954359Sroberto other_step = this; 105054359Sroberto if (other_step != this) { 105154359Sroberto int idelta = (this_step - other_step); 105254359Sroberto if (idelta < 0) idelta = - idelta; 105354359Sroberto if (third_step == 0 && ( 105454359Sroberto (idelta == 1) ? ( 105554359Sroberto this == (other_step +1) || 105654359Sroberto this == (other_step -1) || 105754359Sroberto this == (this_step +1) || 105854359Sroberto this == (this_step -1)) 105954359Sroberto : 106054359Sroberto ( 106154359Sroberto this == (this_step + other_step)/2 106254359Sroberto ) 106354359Sroberto )) third_step = this; 106454359Sroberto if (third_step != this) break; 106554359Sroberto } 106654359Sroberto } 106754359Sroberto sum += this; 106854359Sroberto p--; 106954359Sroberto if (p < 0) p += LAST_STEPS; 107054359Sroberto count++; 107154359Sroberto } 107254359Sroberto msyslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step); 107354359Sroberto if (count != 0) sum = ((sum * 60) + (count /2)) / count; 107454359Sroberto#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS]) 107554359Sroberto msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 107654359Sroberto ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), 107754359Sroberto SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); 107854359Sroberto printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", 107954359Sroberto ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), 108054359Sroberto SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); 108154359Sroberto#undef SV 108254359Sroberto ees->jump_fsecs = delta_sfsec; 108354359Sroberto ees->using_ramp = 1; 108454359Sroberto if (sincelast > 170) 108554359Sroberto ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs); 108654359Sroberto else ees->last_step_late = 30; 108754359Sroberto if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30; 108854359Sroberto if (ees->last_step_late < 0) ees->last_step_late = 0; 108954359Sroberto if (ees->last_step_late >= 60) ees->last_step_late = 59; 109054359Sroberto sincelast = 0; 109154359Sroberto } 109254359Sroberto else { /* First time in -- just save info */ 109354359Sroberto ees->last_step_late = 30; 109454359Sroberto ees->jump_fsecs = delta_sfsec; 109554359Sroberto ees->using_ramp = 1; 109654359Sroberto sum = 4 * 60; 109754359Sroberto } 109854359Sroberto ees->last_step = this_uisec; 109954359Sroberto printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n", 110054359Sroberto ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), 110154359Sroberto ees->second, old_sincelast, ees->last_step_late, count, sum, 110254359Sroberto ees->last_step_secs); 110354359Sroberto msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d", 110454359Sroberto ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, 110554359Sroberto old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); 110654359Sroberto if (sum) ees->last_step_secs = sum; 110754359Sroberto } 110854359Sroberto /* OK, so not a 4ms step at a minute boundry */ 110954359Sroberto else { 111054359Sroberto if (suspect_4ms_step) msyslog(LOG_ERR, 111154359Sroberto "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]", 111254359Sroberto ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec), 111354359Sroberto msec(EES_STEP_F - EES_STEP_F_GRACE), 111454359Sroberto subms(EES_STEP_F - EES_STEP_F_GRACE), 111554359Sroberto (int)msec(delta_f_abs), 111654359Sroberto (int)subms(delta_f_abs), 111754359Sroberto msec(EES_STEP_F + EES_STEP_F_GRACE), 111854359Sroberto subms(EES_STEP_F + EES_STEP_F_GRACE), 111954359Sroberto ees->second, 112054359Sroberto sincelast); 112154359Sroberto if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) { 112254359Sroberto static int ees_step_notes = EES_STEP_NOTES; 112354359Sroberto if (ees_step_notes > 0) { 112454359Sroberto ees_step_notes--; 112554359Sroberto printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n", 112654359Sroberto ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), 112754359Sroberto ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); 112854359Sroberto msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s", 112954359Sroberto ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !"); 113054359Sroberto } 113154359Sroberto } 113254359Sroberto } 113354359Sroberto } 113454359Sroberto ees->last_l = ees->arrvtime; 113554359Sroberto 113654359Sroberto /* IF we have found that it's ramping 113754359Sroberto * && it's within twice the expected ramp period 113854359Sroberto * && there is a non zero step size (avoid /0 !) 113954359Sroberto * THEN we twiddle things 114054359Sroberto */ 114154359Sroberto if (ees->using_ramp && 114254359Sroberto sincelast < (ees->last_step_secs)*2 && 114354359Sroberto ees->last_step_secs) 114454359Sroberto { long sec_of_ramp = sincelast + ees->last_step_late; 114554359Sroberto long fsecs; 114654359Sroberto l_fp inc; 114754359Sroberto 114854359Sroberto /* Ramp time may vary, so may ramp for longer than last time */ 114954359Sroberto if (sec_of_ramp > (ees->last_step_secs + 120)) 115054359Sroberto sec_of_ramp = ees->last_step_secs; 115154359Sroberto 115254359Sroberto /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */ 115354359Sroberto fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs); 115454359Sroberto 1155182007Sroberto if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR, 115654359Sroberto "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)", 115754359Sroberto DB_LOG_DELTAS, 115854359Sroberto ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, 115954359Sroberto pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); 1160182007Sroberto if (dbg & DB_PRINT_DELTAS) printf( 116154359Sroberto "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n", 116254359Sroberto ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, 116354359Sroberto (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); 116454359Sroberto 116554359Sroberto /* Must sign extend the result */ 116654359Sroberto inc.l_i = (fsecs < 0) ? -1 : 0; 116754359Sroberto inc.l_f = fsecs; 1168182007Sroberto if (dbg & DB_INC_PPS) 116954359Sroberto { L_SUB(&pps_arrvstamp, &inc); 117054359Sroberto L_SUB(&ees->arrvtime, &inc); 117154359Sroberto } 117254359Sroberto else 117354359Sroberto { L_ADD(&pps_arrvstamp, &inc); 117454359Sroberto L_ADD(&ees->arrvtime, &inc); 117554359Sroberto } 117654359Sroberto } 117754359Sroberto else { 1178182007Sroberto if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR, 117954359Sroberto "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x", 118054359Sroberto DB_LOG_DELTAS, 118154359Sroberto ees->unit, ees->using_ramp, 118254359Sroberto sincelast, 118354359Sroberto (ees->last_step_secs)*2, 118454359Sroberto ees->last_step_secs); 1185182007Sroberto if (dbg & DB_PRINT_DELTAS) printf( 118654359Sroberto "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n", 118754359Sroberto DB_LOG_DELTAS, 118854359Sroberto ees->unit, ees->using_ramp, 118954359Sroberto sincelast, 119054359Sroberto (ees->last_step_secs)*2, 119154359Sroberto ees->last_step_secs); 119254359Sroberto } 119354359Sroberto 119454359Sroberto L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]); 119554359Sroberto L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]); 119654359Sroberto 1197182007Sroberto if (call_pps_sample && !(dbg & DB_NO_PPS)) { 119854359Sroberto /* Sigh -- it expects its args negated */ 119954359Sroberto L_NEG(&pps_arrvstamp); 120054359Sroberto /* 120154359Sroberto * I had to disable this here, since it appears there is no pointer to the 120254359Sroberto * peer structure. 120354359Sroberto * 120454359Sroberto (void) pps_sample(peer, &pps_arrvstamp); 120554359Sroberto */ 120654359Sroberto } 120754359Sroberto 120854359Sroberto /* Subtract off the local clock time stamp */ 120954359Sroberto L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime); 1210182007Sroberto if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR, 121154359Sroberto "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s", 121254359Sroberto ees->unit, DB_LOG_DELTAS, n_sample, 121354359Sroberto ees->codeoffsets[n_sample].l_f, 121454359Sroberto ees->codeoffsets[n_sample].l_f / 4295, 121554359Sroberto pps_arrvstamp.l_f, 121654359Sroberto pps_arrvstamp.l_f /4295, 1217182007Sroberto (dbg & DB_NO_PPS) ? " [no PPS]" : ""); 121854359Sroberto 121954359Sroberto if (ees->nsamples++ == NCODES-1) ees_process(ees); 122054359Sroberto 122154359Sroberto /* Done! */ 122254359Sroberto} 122354359Sroberto 122454359Sroberto 122554359Sroberto/* offcompare - auxiliary comparison routine for offset sort */ 122654359Sroberto 122754359Srobertostatic int 122854359Srobertooffcompare( 122956746Sroberto const void *va, 123056746Sroberto const void *vb 123154359Sroberto ) 123254359Sroberto{ 123356746Sroberto const l_fp *a = (const l_fp *)va; 123456746Sroberto const l_fp *b = (const l_fp *)vb; 123554359Sroberto return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1); 123654359Sroberto} 123754359Sroberto 123854359Sroberto 123954359Sroberto/* ees_process - process a pile of samples from the clock */ 124054359Srobertostatic void 124154359Srobertoees_process( 124254359Sroberto struct eesunit *ees 124354359Sroberto ) 124454359Sroberto{ 124554359Sroberto static int last_samples = -1; 124654359Sroberto register int i, j; 124754359Sroberto register int noff; 124854359Sroberto register l_fp *coffs = ees->codeoffsets; 124954359Sroberto l_fp offset, tmp; 125054359Sroberto double dispersion; /* ++++ */ 125154359Sroberto int lostsync, isinsync; 125254359Sroberto int samples = ees->nsamples; 125354359Sroberto int samplelog = 0; /* keep "gcc -Wall" happy ! */ 125454359Sroberto int samplereduce = (samples + 1) / 2; 125554359Sroberto double doffset; 125654359Sroberto 125754359Sroberto /* Reset things to zero so we don't have to worry later */ 125854359Sroberto ees_reset(ees); 125954359Sroberto 126054359Sroberto if (sloppyclockflag[ees->unit]) { 126154359Sroberto samplelog = (samples < 2) ? 0 : 126254359Sroberto (samples < 5) ? 1 : 126354359Sroberto (samples < 9) ? 2 : 126454359Sroberto (samples < 17) ? 3 : 126554359Sroberto (samples < 33) ? 4 : 5; 126654359Sroberto samplereduce = (1 << samplelog); 126754359Sroberto } 126854359Sroberto 126954359Sroberto if (samples != last_samples && 127054359Sroberto ((samples != (last_samples-1)) || samples < 3)) { 127154359Sroberto msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....", 127254359Sroberto samples, last_samples, samplereduce); 127354359Sroberto last_samples = samples; 127454359Sroberto } 127554359Sroberto if (samples < 1) return; 127654359Sroberto 127754359Sroberto /* If requested, dump the raw data we have in the buffer */ 1278280849Scy if (ees->dump_vals) 1279280849Scy dump_buf(coffs, 0, samples, "Raw data is:"); 128054359Sroberto 128154359Sroberto /* Sort the offsets, trim off the extremes, then choose one. */ 1282280849Scy qsort(coffs, (size_t)samples, sizeof(coffs[0]), offcompare); 128354359Sroberto 128454359Sroberto noff = samples; 128554359Sroberto i = 0; 128654359Sroberto while ((noff - i) > samplereduce) { 128754359Sroberto /* Trim off the sample which is further away 128854359Sroberto * from the median. We work this out by doubling 128954359Sroberto * the median, subtracting off the end samples, and 129054359Sroberto * looking at the sign of the answer, using the 129154359Sroberto * identity (c-b)-(b-a) == 2*b-a-c 129254359Sroberto */ 129354359Sroberto tmp = coffs[(noff + i)/2]; 129454359Sroberto L_ADD(&tmp, &tmp); 129554359Sroberto L_SUB(&tmp, &coffs[i]); 129654359Sroberto L_SUB(&tmp, &coffs[noff-1]); 129754359Sroberto if (L_ISNEG(&tmp)) noff--; else i++; 129854359Sroberto } 129954359Sroberto 130054359Sroberto /* If requested, dump the reduce data we have in the buffer */ 130154359Sroberto if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:"); 130254359Sroberto 130354359Sroberto /* What we do next depends on the setting of the sloppy clock flag. 130454359Sroberto * If it is on, average the remainder to derive our estimate. 130554359Sroberto * Otherwise, just pick a representative value from the remaining stuff 130654359Sroberto */ 130754359Sroberto if (sloppyclockflag[ees->unit]) { 130854359Sroberto offset.l_ui = offset.l_uf = 0; 130954359Sroberto for (j = i; j < noff; j++) 131054359Sroberto L_ADD(&offset, &coffs[j]); 131154359Sroberto for (j = samplelog; j > 0; j--) 131254359Sroberto L_RSHIFTU(&offset); 131354359Sroberto } 131454359Sroberto else offset = coffs[i+BESTSAMPLE]; 131554359Sroberto 131654359Sroberto /* Compute the dispersion as the difference between the 131754359Sroberto * lowest and highest offsets that remain in the 131854359Sroberto * consideration list. 131954359Sroberto * 132054359Sroberto * It looks like MOST clocks have MOD (max error), so halve it ! 132154359Sroberto */ 132254359Sroberto tmp = coffs[noff-1]; 132354359Sroberto L_SUB(&tmp, &coffs[i]); 132454359Sroberto#define FRACT_SEC(n) ((1 << 30) / (n/2)) 132554359Sroberto dispersion = LFPTOFP(&tmp) / 2; /* ++++ */ 1326182007Sroberto if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog( 1327182007Sroberto (dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, 132854359Sroberto "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d", 1329182007Sroberto dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE), 133054359Sroberto offset.l_f / 4295, offset.l_f, 133154359Sroberto (dispersion * 1526) / 100, 133254359Sroberto (sloppyclockflag[ees->unit]) ? " by averaging" : "", 133354359Sroberto FRACT_SEC(10) / 4295, 133454359Sroberto (coffs[0].l_f) / 4295, 133554359Sroberto i, 133654359Sroberto (coffs[i].l_f) / 4295, 133754359Sroberto (coffs[samples/2].l_f) / 4295, 133854359Sroberto (coffs[i+BESTSAMPLE].l_f) / 4295, 133954359Sroberto noff-1, 134054359Sroberto (coffs[noff-1].l_f) / 4295, 134154359Sroberto (coffs[samples-1].l_f) / 4295); 134254359Sroberto 134354359Sroberto /* Are we playing silly wotsits ? 134454359Sroberto * If we are using all data, see if there is a "small" delta, 134554359Sroberto * and if so, blurr this with 3/4 of the delta from the last value 134654359Sroberto */ 134754359Sroberto if (ees->usealldata && ees->offset.l_uf) { 134854359Sroberto long diff = (long) (ees->offset.l_uf - offset.l_uf); 134954359Sroberto 135054359Sroberto /* is the delta small enough ? */ 135154359Sroberto if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) { 135254359Sroberto int samd = (64 * 4) / samples; 135354359Sroberto long new; 135454359Sroberto if (samd < 2) samd = 2; 135554359Sroberto new = offset.l_uf + ((diff * (samd -1)) / samd); 135654359Sroberto 135754359Sroberto /* Sign change -> need to fix up int part */ 135856746Sroberto if ((new & 0x80000000) != 135956746Sroberto (((long) offset.l_uf) & 0x80000000)) 136054359Sroberto { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 136154359Sroberto msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d", 136256746Sroberto new & 0x80000000, 136356746Sroberto ((long) offset.l_uf) & 0x80000000, 136454359Sroberto new, (long) offset.l_uf, 136554359Sroberto (new < 0) ? -1 : 1); 136654359Sroberto offset.l_ui += (new < 0) ? -1 : 1; 136754359Sroberto } 136854359Sroberto dispersion /= 4; 1369182007Sroberto if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog( 1370182007Sroberto (dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, 137154359Sroberto "I: [%x] Smooth data: %ld -> %ld, dispersion now %f", 1372182007Sroberto dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE), 137354359Sroberto ((long) offset.l_uf) / 4295, new / 4295, 137454359Sroberto (dispersion * 1526) / 100); 137554359Sroberto offset.l_uf = (unsigned long) new; 137654359Sroberto } 1377182007Sroberto else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( 1378182007Sroberto (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, 137954359Sroberto "[%x] No smooth as delta not %d < %ld < %d", 1380182007Sroberto dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), 138154359Sroberto - FRACT_SEC(100), diff, FRACT_SEC(100)); 138254359Sroberto } 1383182007Sroberto else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( 1384182007Sroberto (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, 138554359Sroberto "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)", 1386182007Sroberto dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), 138754359Sroberto ees->usealldata, ees->offset.l_f, ees->offset.l_uf, 138854359Sroberto offset.l_f, ees->offset.l_f - offset.l_f); 138954359Sroberto 139054359Sroberto /* Collect offset info for debugging info */ 139154359Sroberto ees->offset = offset; 139254359Sroberto ees->lowoffset = coffs[i]; 139354359Sroberto ees->highoffset = coffs[noff-1]; 139454359Sroberto 139554359Sroberto /* Determine synchronization status. Can be unsync'd either 139654359Sroberto * by a report from the clock or by a leap hold. 139754359Sroberto * 139854359Sroberto * Loss of the radio signal for a short time does not cause 139954359Sroberto * us to go unsynchronised, since the receiver keeps quite 140054359Sroberto * good time on its own. The spec says 20ms in 4 hours; the 140154359Sroberto * observed drift in our clock (Cambridge) is about a second 140254359Sroberto * a day, but even that keeps us within the inherent tolerance 140354359Sroberto * of the clock for about 15 minutes. Observation shows that 140454359Sroberto * the typical "short" outage is 3 minutes, so to allow us 140554359Sroberto * to ride out those, we will give it 5 minutes. 140654359Sroberto */ 140754359Sroberto lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0; 140854359Sroberto isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1; 140954359Sroberto 141054359Sroberto /* Done. Use time of last good, synchronised code as the 141154359Sroberto * reference time, and lastsampletime as the receive time. 141254359Sroberto */ 141354359Sroberto if (ees->fix_pending) { 1414280849Scy msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x", 141554359Sroberto ees->fix_pending, ees->unit, offset.l_i, offset.l_f); 141654359Sroberto ees->fix_pending = 0; 141754359Sroberto } 141854359Sroberto LFPTOD(&offset, doffset); 141954359Sroberto refclock_receive(ees->peer); 142054359Sroberto ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL); 142154359Sroberto} 142254359Sroberto 142354359Sroberto/* msfees_poll - called by the transmit procedure */ 142454359Srobertostatic void 142554359Srobertomsfees_poll( 142654359Sroberto int unit, 142754359Sroberto struct peer *peer 142854359Sroberto ) 142954359Sroberto{ 143054359Sroberto if (unit >= MAXUNITS) { 143154359Sroberto msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid", 143254359Sroberto unit); 143354359Sroberto return; 143454359Sroberto } 143554359Sroberto if (!unitinuse[unit]) { 143654359Sroberto msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused", 143754359Sroberto unit); 143854359Sroberto return; 143954359Sroberto } 144054359Sroberto 144154359Sroberto ees_process(eesunits[unit]); 144254359Sroberto 144354359Sroberto if ((current_time - eesunits[unit]->lasttime) > 150) 144454359Sroberto ees_event(eesunits[unit], CEVNT_FAULT); 144554359Sroberto} 144654359Sroberto 144754359Sroberto 144854359Sroberto#else 1449280849ScyNONEMPTY_TRANSLATION_UNIT 145054359Sroberto#endif /* REFCLOCK */ 1451