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