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