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