diff options
author | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
commit | ef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch) | |
tree | fc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd/refclock_msfees.c | |
download | FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz |
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd/refclock_msfees.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_msfees.c | 1444 |
1 files changed, 1444 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_msfees.c b/contrib/ntp/ntpd/refclock_msfees.c new file mode 100644 index 0000000..01fe27f --- /dev/null +++ b/contrib/ntp/ntpd/refclock_msfees.c @@ -0,0 +1,1444 @@ +/* refclock_ees - clock driver for the EES M201 receiver */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS) + +/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes + * were removed as the code was overly hairy, they weren't in use + * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk + */ + +#include <ctype.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" +#if defined(HAVE_BSD_TTYS) +#include <sgtty.h> +#endif /* HAVE_BSD_TTYS */ +#if defined(HAVE_SYSV_TTYS) +#include <termio.h> +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) +#include <termios.h> +#endif +#if defined(STREAM) +#include <stropts.h> +#endif + +#ifdef HAVE_SYS_TERMIOS_H +# include <sys/termios.h> +#endif +#ifdef HAVE_SYS_PPSCLOCK_H +# include <sys/ppsclock.h> +#endif + +#include "ntp_stdlib.h" + +/* + fudgefactor = fudgetime1; + os_delay = fudgetime2; + offset_fudge = os_delay + fudgefactor + inherent_delay; + stratumtouse = fudgeval1 & 0xf + debug = fudgeval2; + sloppyclockflag = flags & CLK_FLAG1; + 1 log smoothing summary when processing sample + 4 dump the buffer from the clock + 8 EIOGETKD the last n uS time stamps + if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0; + ees->dump_vals = flags & CLK_FLAG3; + ees->usealldata = flags & CLK_FLAG4; + + + bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0; + bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0; + bug->values[2] = (u_long)ees->status; + bug->values[3] = (u_long)ees->lastevent; + bug->values[4] = (u_long)ees->reason; + bug->values[5] = (u_long)ees->nsamples; + bug->values[6] = (u_long)ees->codestate; + bug->values[7] = (u_long)ees->day; + bug->values[8] = (u_long)ees->hour; + bug->values[9] = (u_long)ees->minute; + bug->values[10] = (u_long)ees->second; + bug->values[11] = (u_long)ees->tz; + bug->values[12] = ees->yearstart; + bug->values[13] = (ees->leaphold > current_time) ? + ees->leaphold - current_time : 0; + bug->values[14] = inherent_delay[unit].l_uf; + bug->values[15] = offset_fudge[unit].l_uf; + + bug->times[0] = ees->reftime; + bug->times[1] = ees->arrvtime; + bug->times[2] = ees->lastsampletime; + bug->times[3] = ees->offset; + bug->times[4] = ees->lowoffset; + bug->times[5] = ees->highoffset; + bug->times[6] = inherent_delay[unit]; + bug->times[8] = os_delay[unit]; + bug->times[7] = fudgefactor[unit]; + bug->times[9] = offset_fudge[unit]; + bug->times[10]= ees->yearstart, 0; + */ + +/* This should support the use of an EES M201 receiver with RS232 + * output (modified to transmit time once per second). + * + * For the format of the message sent by the clock, see the EESM_ + * definitions below. + * + * It appears to run free for an integral number of minutes, until the error + * reaches 4mS, at which point it steps at second = 01. + * It appears that sometimes it steps 4mS (say at 7 min interval), + * then the next minute it decides that it was an error, so steps back. + * On the next minute it steps forward again :-( + * This is typically 16.5uS/S then 3975uS at the 4min re-sync, + * or 9.5uS/S then 3990.5uS at a 7min re-sync, + * at which point it may loose the "00" second time stamp. + * I assume that the most accurate time is just AFTER the re-sync. + * Hence remember the last cycle interval, + * + * Can run in any one of: + * + * PPSCD PPS signal sets CD which interupts, and grabs the current TOD + * (sun) *in the interupt code*, so as to avoid problems with + * the STREAMS scheduling. + * + * It appears that it goes 16.5 uS slow each second, then every 4 mins it + * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7) + */ + +/* Definitions */ +#ifndef MAXUNITS +#define MAXUNITS 4 /* maximum number of EES units permitted */ +#endif + +#ifndef EES232 +#define EES232 "/dev/ees%d" /* Device to open to read the data */ +#endif + +/* Other constant stuff */ +#ifndef EESPRECISION +#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */ +#endif +#ifndef EESREFID +#define EESREFID "MSF\0" /* String to identify the clock */ +#endif +#ifndef EESHSREFID +#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */ +#endif + +/* Description of clock */ +#define EESDESCRIPTION "EES M201 MSF Receiver" + +/* Speed we run the clock port at. If this is changed the UARTDELAY + * value should be recomputed to suit. + */ +#ifndef SPEED232 +#define SPEED232 B9600 /* 9600 baud */ +#endif + +/* What is the inherent delay for this mode of working, i.e. when is the + * data time stamped. + */ +#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */ +#define BITS_TO_L_FP(bits, baud) \ +(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT) +#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600) +#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600) + +#ifndef STREAM_PP1 +#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->" +#endif +#ifndef STREAM_PP2 +#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->" +#endif + + /* Offsets of the bytes of the serial line code. The clock gives + * local time with a GMT/BST indication. The EESM_ definitions + * give offsets into ees->lastcode. + */ +#define EESM_CSEC 0 /* centiseconds - always zero in our clock */ +#define EESM_SEC 1 /* seconds in BCD */ +#define EESM_MIN 2 /* minutes in BCD */ +#define EESM_HOUR 3 /* hours in BCD */ +#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */ +#define EESM_DAY 5 /* day of month in BCD */ +#define EESM_MON 6 /* month in BCD */ +#define EESM_YEAR 7 /* year MOD 100 in BCD */ +#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */ +#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */ +#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */ + /* followed by a frame alignment byte (0xff) / + / which is not put into the lastcode buffer*/ + +/* Length of the serial time code, in characters. The first length + * is less the frame alignment byte. + */ +#define LENEESPRT (EESM_MSFOK+1) +#define LENEESCODE (LENEESPRT+1) + + /* Code state. */ +#define EESCS_WAIT 0 /* waiting for start of timecode */ +#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */ + + /* Default fudge factor and character to receive */ +#define DEFFUDGETIME 0 /* Default user supplied fudge factor */ +#ifndef DEFOSTIME +#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */ +#endif +#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/ + + /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median + * elimination. If we're running with an accurate clock, chose the BESTSAMPLE + * as the estimated offset, otherwise average the remainder. + */ +#define FULLSHIFT 6 /* NCODES root 2 */ +#define NCODES (1<< FULLSHIFT) /* 64 */ +#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */ + + /* Towards the high ( Why ?) end of half */ +#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */ + + /* Leap hold time. After a leap second the clock will no longer be + * reliable until it resynchronizes. Hope 40 minutes is enough. */ +#define EESLEAPHOLD (40 * 60) + +#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */ +#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/ +#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */ +#define EES_STEP_NOTES 50 /* Only do a limited number */ +#define MAX_STEP 16 /* Max number of steps to remember */ + + /* debug is a bit mask of debugging that is wanted */ +#define DB_SYSLOG_SMPLI 0x0001 +#define DB_SYSLOG_SMPLE 0x0002 +#define DB_SYSLOG_SMTHI 0x0004 +#define DB_SYSLOG_NSMTHE 0x0008 +#define DB_SYSLOG_NSMTHI 0x0010 +#define DB_SYSLOG_SMTHE 0x0020 +#define DB_PRINT_EV 0x0040 +#define DB_PRINT_CDT 0x0080 +#define DB_PRINT_CDTC 0x0100 +#define DB_SYSLOG_KEEPD 0x0800 +#define DB_SYSLOG_KEEPE 0x1000 +#define DB_LOG_DELTAS 0x2000 +#define DB_PRINT_DELTAS 0x4000 +#define DB_LOG_AWAITMORE 0x8000 +#define DB_LOG_SAMPLES 0x10000 +#define DB_NO_PPS 0x20000 +#define DB_INC_PPS 0x40000 +#define DB_DUMP_DELTAS 0x80000 + + struct eesunit { /* EES unit control structure. */ + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp reftime; /* reference time */ + l_fp lastsampletime; /* time as in txt from last EES msg */ + l_fp arrvtime; /* Time at which pkt arrived */ + l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */ + l_fp offset; /* chosen offset (for clkbug) */ + l_fp lowoffset; /* lowest sample offset (for clkbug) */ + l_fp highoffset; /* highest " " (for clkbug) */ + char lastcode[LENEESCODE+6]; /* last time code we received */ + u_long lasttime; /* last time clock heard from */ + u_long clocklastgood; /* last time good radio seen */ + u_char lencode; /* length of code in buffer */ + u_char nsamples; /* number of samples we've collected */ + u_char codestate; /* state of 232 code reception */ + u_char unit; /* unit number for this guy */ + u_char status; /* clock status */ + u_char lastevent; /* last clock event */ + u_char reason; /* reason for last abort */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + char tz; /* timezone from clock */ + u_char ttytype; /* method used */ + u_char dump_vals; /* Should clock values be dumped */ + u_char usealldata; /* Use ALL samples */ + u_short day; /* day of year from last code */ + u_long yearstart; /* start of current year */ + u_long leaphold; /* time of leap hold expiry */ + u_long badformat; /* number of bad format codes */ + u_long baddata; /* number of invalid time codes */ + u_long timestarted; /* time we started this */ + long last_pps_no; /* The serial # of the last PPS */ + char fix_pending; /* Is a "sync to time" pending ? */ + /* Fine tuning - compensate for 4 mS ramping .... */ + l_fp last_l; /* last time stamp */ + u_char last_steps[MAX_STEP]; /* Most recent n steps */ + int best_av_step; /* Best guess at average step */ + char best_av_step_count; /* # of steps over used above */ + char this_step; /* Current pos in buffer */ + int last_step_late; /* How late the last step was (0-59) */ + long jump_fsecs; /* # of fractions of a sec last jump */ + u_long last_step; /* time of last step */ + int last_step_secs; /* Number of seconds in last step */ + int using_ramp; /* 1 -> noemal, -1 -> over stepped */ + }; +#define last_sec last_l.l_ui +#define last_sfsec last_l.l_f +#define this_uisec ((ees->arrvtime).l_ui) +#define this_sfsec ((ees->arrvtime).l_f) +#define msec(x) ((x) / (1<<22)) +#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0]) +#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5))) + +/* Bitmask for what methods to try to use -- currently only PPS enabled */ +#define T_CBREAK 1 +#define T_PPS 8 +/* macros to test above */ +#define is_cbreak(x) ((x)->ttytype & T_CBREAK) +#define is_pps(x) ((x)->ttytype & T_PPS) +#define is_any(x) ((x)->ttytype) + +#define CODEREASON 20 /* reason codes */ + +/* Data space for the unit structures. Note that we allocate these on + * the fly, but never give them back. */ +static struct eesunit *eesunits[MAXUNITS]; +static u_char unitinuse[MAXUNITS]; + +/* Keep the fudge factors separately so they can be set even + * when no clock is configured. */ +static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */ +static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */ +static l_fp os_delay[MAXUNITS]; /* fudgetime2 */ +static l_fp offset_fudge[MAXUNITS]; /* Sum of above */ +static u_char stratumtouse[MAXUNITS]; +static u_char sloppyclockflag[MAXUNITS]; + +static int deltas[60]; + +static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */ +static l_fp onesec; /* = { 1, 0 }; */ + +#ifdef DEBUG +static int debug; +#endif + +#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */ +#define DUMP_BUF_SIZE 10112 +#endif + +/* ees_reset - reset the count back to zero */ +#define ees_reset(ees) (ees)->nsamples = 0; \ +(ees)->codestate = EESCS_WAIT + +/* ees_event - record and report an event */ +#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \ +ees_report_event((ees), (evcode)) + + /* Find the precision of the system clock by reading it */ +#define USECS 1000000 +#define MINSTEP 5 /* some systems increment uS on each call */ +#define MAXLOOPS (USECS/9) + +/* + * Function prototypes + */ + +static int msfees_start P((int unit, struct peer *peer)); +static void msfees_shutdown P((int unit, struct peer *peer)); +static void msfees_poll P((int unit, struct peer *peer)); +static void msfees_init P((void)); +static void dump_buf P((l_fp *coffs, int from, int to, char *text)); +static void ees_report_event P((struct eesunit *ees, int code)); +static void ees_receive P((struct recvbuf *rbufp)); +static int offcompare P((l_fp *a, l_fp *b)); +static void ees_process P((struct eesunit *ees)); + +/* + * Transfer vector + */ +struct refclock refclock_msfees = { + msfees_start, /* start up driver */ + msfees_shutdown, /* shut down driver */ + msfees_poll, /* transmit poll message */ + noentry, /* not used */ + msfees_init, /* initialize driver */ + noentry, /* not used */ + NOFLAGS /* not used */ +}; + + +static void +dump_buf( + l_fp *coffs, + int from, + int to, + char *text + ) +{ + char buff[DUMP_BUF_SIZE + 80]; + int i; + register char *ptr = buff; + + sprintf(ptr, text); + for (i=from; i<to; i++) + { while (*ptr) ptr++; + if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff); + sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295); + } + msyslog(LOG_DEBUG, "D: %s", buff); +} + +/* msfees_init - initialize internal ees driver data */ +static void +msfees_init(void) +{ + register int i; + /* Just zero the data arrays */ + memset((char *)eesunits, 0, sizeof eesunits); + memset((char *)unitinuse, 0, sizeof unitinuse); + + acceptable_slop.l_ui = 0; + acceptable_slop.l_uf = 1 << (FRACTION_PREC -2); + + onesec.l_ui = 1; + onesec.l_uf = 0; + + /* Initialize fudge factors to default. */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor[i].l_ui = 0; + fudgefactor[i].l_uf = DEFFUDGETIME; + os_delay[i].l_ui = 0; + os_delay[i].l_uf = DEFOSTIME; + inherent_delay[i].l_ui = 0; + inherent_delay[i].l_uf = DEFINHTIME; + offset_fudge[i] = os_delay[i]; + L_ADD(&offset_fudge[i], &fudgefactor[i]); + L_ADD(&offset_fudge[i], &inherent_delay[i]); + stratumtouse[i] = 0; + sloppyclockflag[i] = 0; + } +} + + +/* msfees_start - open the EES devices and initialize data for processing */ +static int +msfees_start( + int unit, + struct peer *peer + ) +{ + register struct eesunit *ees; + register int i; + int fd232 = -1; + char eesdev[20]; + struct termios ttyb, *ttyp; + struct refclockproc *pp; + pp = peer->procptr; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", + unit, MAXUNITS-1); + return 0; + } + if (unitinuse[unit]) { + msyslog(LOG_ERR, "ees clock: unit number %d in use", unit); + return 0; + } + + /* Unit okay, attempt to open the devices. We do them both at + * once to make sure we can */ + (void) sprintf(eesdev, EES232, unit); + + fd232 = open(eesdev, O_RDWR, 0777); + if (fd232 == -1) { + msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev); + return 0; + } + +#ifdef TIOCEXCL + /* Set for exclusive use */ + if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) { + msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev); + goto screwed; + } +#endif + + /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */ + + /* Set port characteristics. If we don't have a STREAMS module or + * a clock line discipline, cooked mode is just usable, even though it + * strips the top bit. The only EES byte which uses the top + * bit is the year, and we don't use that anyway. If we do + * have the line discipline, we choose raw mode, and the + * line discipline code will block up the messages. + */ + + /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */ + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev); + goto screwed; + } + + ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; + ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; + ttyp->c_oflag = 0; + ttyp->c_lflag = ICANON; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; + if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { + msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); + goto screwed; + } + + if (tcflush(fd232, TCIOFLUSH) < 0) { + msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev); + goto screwed; + } + + inherent_delay[unit].l_uf = INH_DELAY_PPS; + + /* offset fudge (how *late* the timestamp is) = fudge + os delays */ + offset_fudge[unit] = os_delay[unit]; + L_ADD(&offset_fudge[unit], &fudgefactor[unit]); + L_ADD(&offset_fudge[unit], &inherent_delay[unit]); + + /* Looks like this might succeed. Find memory for the structure. + * Look to see if there are any unused ones, if not we malloc() one. + */ + if (eesunits[unit] != 0) /* The one we want is okay */ + ees = eesunits[unit]; + else { + /* Look for an unused, but allocated struct */ + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && eesunits[i] != 0) + break; + } + + if (i < MAXUNITS) { /* Reclaim this one */ + ees = eesunits[i]; + eesunits[i] = 0; + } /* no spare -- make a new one */ + else ees = (struct eesunit *) emalloc(sizeof(struct eesunit)); + } + memset((char *)ees, 0, sizeof(struct eesunit)); + eesunits[unit] = ees; + + /* Set up the structures */ + ees->peer = peer; + ees->unit = (u_char)unit; + ees->timestarted= current_time; + ees->ttytype = 0; + ees->io.clock_recv= ees_receive; + ees->io.srcclock= (caddr_t)ees; + ees->io.datalen = 0; + ees->io.fd = fd232; + + /* Okay. Push one of the two (linked into the kernel, or dynamically + * loaded) STREAMS module, and give it to the I/O code to start + * receiving stuff. + */ + +#ifdef STREAM + { + int rc1; + /* Pop any existing onews first ... */ + while (ioctl(fd232, I_POP, 0 ) >= 0) ; + + /* Now try pushing either of the possible modules */ + if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 && + ioctl(fd232, I_PUSH, STREAM_PP2) < 0) { + msyslog(LOG_ERR, + "ees clock: Push of `%s' and `%s' to %s failed %m", + STREAM_PP1, STREAM_PP2, eesdev); + goto screwed; + } + else { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s", + (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev); + ees->ttytype |= T_PPS; + } + } +#endif /* STREAM */ + + /* Add the clock */ + if (!io_addclock(&ees->io)) { + /* Oh shit. Just close and return. */ + msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev); + goto screwed; + } + + + /* All done. Initialize a few random peer variables, then + * return success. */ + peer->precision = sys_precision; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) { + memcpy((char *)&pp->refid, EESREFID, 4); + if (unit > 0 && unit < 10) + ((char *)&pp->refid)[3] = '0' + unit; + } else { + peer->refid = htonl(EESHSREFID); + } + unitinuse[unit] = 1; + pp->unitptr = (caddr_t) &eesunits[unit]; + pp->clockdesc = EESDESCRIPTION; + msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit); + return (1); + + screwed: + if (fd232 != -1) + (void) close(fd232); + return (0); +} + + +/* msfees_shutdown - shut down a EES clock */ +static void +msfees_shutdown( + int unit, + struct peer *peer + ) +{ + register struct eesunit *ees; + + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, + "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)", + unit, MAXUNITS); + return; + } + if (!unitinuse[unit]) { + msyslog(LOG_ERR, + "ees clock: INTERNAL ERROR, unit number %d not in use", unit); + return; + } + + /* Tell the I/O module to turn us off. We're history. */ + ees = eesunits[unit]; + io_closeclock(&ees->io); + unitinuse[unit] = 0; +} + + +/* ees_report_event - note the occurance of an event */ +static void +ees_report_event( + struct eesunit *ees, + int code + ) +{ + if (ees->status != (u_char)code) { + ees->status = (u_char)code; + if (code != CEVNT_NOMINAL) + ees->lastevent = (u_char)code; + /* Should report event to trap handler in here. + * Soon... + */ + } +} + + +/* ees_receive - receive data from the serial interface on an EES clock */ +static void +ees_receive( + struct recvbuf *rbufp + ) +{ + register int n_sample; + register int day; + register struct eesunit *ees; + register u_char *dpt; /* Data PoinTeR: move along ... */ + register u_char *dpend; /* Points just *after* last data char */ + register char *cp; + l_fp tmp; + int call_pps_sample = 0; + l_fp pps_arrvstamp; + int sincelast; + int pps_step = 0; + int suspect_4ms_step = 0; + struct ppsclockev ppsclockev; + long *ptr = (long *) &ppsclockev; + int rc; + int request; +#ifdef HAVE_CIOGETEV + request = CIOGETEV; +#endif +#ifdef HAVE_TIOCGPPSEV + request = TIOCGPPSEV; +#endif + + /* Get the clock this applies to and a pointer to the data */ + ees = (struct eesunit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + dpend = dpt + rbufp->recv_length; + if ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE)) + printf("[%d] ", rbufp->recv_length); + + /* Check out our state and process appropriately */ + switch (ees->codestate) { + case EESCS_WAIT: + /* Set an initial guess at the timestamp as the recv time. + * If just running in CBREAK mode, we can't improve this. + * If we have the CLOCK Line Discipline, PPSCD, or sime such, + * then we will do better later .... + */ + ees->arrvtime = rbufp->recv_time; + ees->codestate = EESCS_GOTSOME; + ees->lencode = 0; + /*FALLSTHROUGH*/ + + case EESCS_GOTSOME: + cp = &(ees->lastcode[ees->lencode]); + + /* Gobble the bytes until the final (possibly stripped) 0xff */ + while (dpt < dpend && (*dpt & 0x7f) != 0x7f) { + *cp++ = (char)*dpt++; + ees->lencode++; + /* Oh dear -- too many bytes .. */ + if (ees->lencode > LENEESPRT) { + NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, + "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]", + ees->lencode, dpend - dpt, LENEESPRT, +#define D(x) (ees->lastcode[x]) + D(0), D(1), D(2), D(3), D(4), D(5), D(6), + D(7), D(8), D(9), D(10), D(11), D(12)); +#undef D + ees->badformat++; + ees->reason = CODEREASON + 1; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + } + /* Gave up because it was end of the buffer, rather than ff */ + if (dpt == dpend) { + /* Incomplete. Wait for more. */ + if (debug & DB_LOG_AWAITMORE) + msyslog(LOG_INFO, + "I: ees clock %d: %x == %x: await more", + ees->unit, dpt, dpend); + return; + } + + /* This shouldn't happen ... ! */ + if ((*dpt & 0x7f) != 0x7f) { + msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt); + ees->badformat++; + ees->reason = CODEREASON + 2; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + /* Skip the 0xff */ + dpt++; + + /* Finally, got a complete buffer. Mainline code will + * continue on. */ + cp = ees->lastcode; + break; + + default: + msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d", + ees->unit, ees->codestate); + ees->reason = CODEREASON + 5; + ees_event(ees, CEVNT_FAULT); + ees_reset(ees); + return; + } + + /* Boy! After all that crap, the lastcode buffer now contains + * something we hope will be a valid time code. Do length + * checks and sanity checks on constant data. + */ + ees->codestate = EESCS_WAIT; + ees->lasttime = current_time; + if (ees->lencode != LENEESPRT) { + ees->badformat++; + ees->reason = CODEREASON + 6; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + cp = ees->lastcode; + + /* Check that centisecond is zero */ + if (cp[EESM_CSEC] != 0) { + ees->baddata++; + ees->reason = CODEREASON + 7; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + /* Check flag formats */ + if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) { + ees->badformat++; + ees->reason = CODEREASON + 8; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) { + ees->badformat++; + ees->reason = CODEREASON + 9; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) { + ees->badformat++; + ees->reason = CODEREASON + 10; + ees_event(ees, CEVNT_BADREPLY); + ees_reset(ees); + return; + } + + /* So far, so good. Compute day, hours, minutes, seconds, + * time zone. Do range checks on these. + */ + +#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) ) +#define istrue(x) ((x)?1:0) + + ees->second = bcdunpack(cp[EESM_SEC]); /* second */ + ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */ + ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */ + + day = bcdunpack(cp[EESM_DAY]); /* day of month */ + + switch (bcdunpack(cp[EESM_MON])) { /* month */ + + /* Add in lengths of all previous months. Add one more + if it is a leap year and after February. + */ + case 12: day += NOV; /*FALLSTHROUGH*/ + case 11: day += OCT; /*FALLSTHROUGH*/ + case 10: day += SEP; /*FALLSTHROUGH*/ + case 9: day += AUG; /*FALLSTHROUGH*/ + case 8: day += JUL; /*FALLSTHROUGH*/ + case 7: day += JUN; /*FALLSTHROUGH*/ + case 6: day += MAY; /*FALLSTHROUGH*/ + case 5: day += APR; /*FALLSTHROUGH*/ + case 4: day += MAR; /*FALLSTHROUGH*/ + case 3: day += FEB; + if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/ + case 2: day += JAN; /*FALLSTHROUGH*/ + case 1: break; + default: ees->baddata++; + ees->reason = CODEREASON + 11; + ees_event(ees, CEVNT_BADDATE); + ees_reset(ees); + return; + } + + ees->day = day; + + /* Get timezone. The clocktime routine wants the number + * of hours to add to the delivered time to get UT. + * Currently -1 if BST flag set, 0 otherwise. This + * is the place to tweak things if double summer time + * ever happens. + */ + ees->tz = istrue(cp[EESM_BST]) ? -1 : 0; + + if (ees->day > 366 || ees->day < 1 || + ees->hour > 23 || ees->minute > 59 || ees->second > 59) { + ees->baddata++; + ees->reason = CODEREASON + 12; + ees_event(ees, CEVNT_BADDATE); + ees_reset(ees); + return; + } + + n_sample = ees->nsamples; + + /* Now, compute the reference time value: text -> tmp.l_ui */ + if (!clocktime(ees->day, ees->hour, ees->minute, ees->second, + ees->tz, rbufp->recv_time.l_ui, &ees->yearstart, + &tmp.l_ui)) { + ees->baddata++; + ees->reason = CODEREASON + 13; + ees_event(ees, CEVNT_BADDATE); + ees_reset(ees); + return; + } + tmp.l_uf = 0; + + /* DON'T use ees->arrvtime -- it may be < reftime */ + ees->lastsampletime = tmp; + + /* If we are synchronised to the radio, update the reference time. + * Also keep a note of when clock was last good. + */ + if (istrue(cp[EESM_MSFOK])) { + ees->reftime = tmp; + ees->clocklastgood = current_time; + } + + + /* Compute the offset. For the fractional part of the + * offset we use the expected delay for the message. + */ + ees->codeoffsets[n_sample].l_ui = tmp.l_ui; + ees->codeoffsets[n_sample].l_uf = 0; + + /* Number of seconds since the last step */ + sincelast = this_uisec - ees->last_step; + + memset((char *) &ppsclockev, 0, sizeof ppsclockev); + + rc = ioctl(ees->io.fd, request, (char *) &ppsclockev); + if (debug & DB_PRINT_EV) fprintf(stderr, + "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n", + DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees), + rc, errno, ptr[0], ptr[1], ptr[2]); + + /* If we managed to get the time of arrival, process the info */ + if (rc >= 0) { + int conv = -1; + pps_step = ppsclockev.serial - ees->last_pps_no; + + /* Possible that PPS triggered, but text message didn't */ + if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second); + if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1; + if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4; + + /* allow for single loss of PPS only */ + if (pps_step != 1 && pps_step != 2) + fprintf(stderr, "PPS step: %d too far off %ld (%d)\n", + ppsclockev.serial, ees->last_pps_no, pps_step); + else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp)) + fprintf(stderr, "buftvtots failed\n"); + else { /* if ((ABS(time difference) - 0.25) < 0) + * then believe it ... + */ + l_fp diff; + diff = pps_arrvstamp; + conv = 0; + L_SUB(&diff, &ees->arrvtime); + if (debug & DB_PRINT_CDT) + printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s", + DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf, + (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf, + (long)diff.l_ui, (long)diff.l_uf, + ctime(&(ppsclockev.tv.tv_sec))); + if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); + L_SUB(&diff, &acceptable_slop); + if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ + ees->arrvtime = pps_arrvstamp; + conv++; + call_pps_sample++; + } + /* Some loss of some signals around sec = 1 */ + else if (ees->second == 1) { + diff = pps_arrvstamp; + L_ADD(&diff, &onesec); + L_SUB(&diff, &ees->arrvtime); + if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); + L_SUB(&diff, &acceptable_slop); + msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", + pps_arrvstamp.l_ui - ees->arrvtime.l_ui, + pps_arrvstamp.l_uf, + ees->arrvtime.l_uf, + diff.l_ui, diff.l_uf, + (int)ppsclockev.tv.tv_usec, + ctime(&(ppsclockev.tv.tv_sec))); + if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ + suspect_4ms_step |= 2; + ees->arrvtime = pps_arrvstamp; + L_ADD(&ees->arrvtime, &onesec); + conv++; + call_pps_sample++; + } + } + } + ees->last_pps_no = ppsclockev.serial; + if (debug & DB_PRINT_CDTC) + printf( + "[%x] %08lx %08lx %d u%d (%d %d)\n", + DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui, + (long)pps_arrvstamp.l_uf, conv, ees->unit, + call_pps_sample, pps_step); + } + + /* See if there has been a 4ms jump at a minute boundry */ + { l_fp delta; +#define delta_isec delta.l_ui +#define delta_ssec delta.l_i +#define delta_sfsec delta.l_f + long delta_f_abs; + + delta.l_i = ees->arrvtime.l_i; + delta.l_f = ees->arrvtime.l_f; + + L_SUB(&delta, &ees->last_l); + delta_f_abs = delta_sfsec; + if (delta_f_abs < 0) delta_f_abs = -delta_f_abs; + + /* Dump the deltas each minute */ + if (debug & DB_DUMP_DELTAS) + { if (/*0 <= ees->second && */ + ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec; + /* Dump on second 1, as second 0 sometimes missed */ + if (ees->second == 1) { + char text[16 * ((sizeof deltas) / (sizeof deltas[0]))]; + char *cptr=text; + int i; + for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) { + sprintf(cptr, " %d.%04d", + msec(deltas[i]), subms(deltas[i])); + while (*cptr) cptr++; + } + msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s", + msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE), + msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE), + text+1); + for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0; + } + } + + /* Lets see if we have a 4 mS step at a minute boundaary */ + if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) && + (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) && + (ees->second == 0 || ees->second == 1 || ees->second == 2) && + (sincelast < 0 || sincelast > 122) + ) { /* 4ms jump at min boundry */ + int old_sincelast; + int count=0; + int sum = 0; + /* Yes -- so compute the ramp time */ + if (ees->last_step == 0) sincelast = 0; + old_sincelast = sincelast; + + /* First time in, just set "ees->last_step" */ + if(ees->last_step) { + int other_step = 0; + int third_step = 0; + int this_step = (sincelast + (60 /2)) / 60; + int p_step = ees->this_step; + int p; + ees->last_steps[p_step] = this_step; + p= p_step; + p_step++; + if (p_step >= LAST_STEPS) p_step = 0; + ees->this_step = p_step; + /* Find the "average" interval */ + while (p != p_step) { + int this = ees->last_steps[p]; + if (this == 0) break; + if (this != this_step) { + if (other_step == 0 && ( + this== (this_step +2) || + this== (this_step -2) || + this== (this_step +1) || + this== (this_step -1))) + other_step = this; + if (other_step != this) { + int idelta = (this_step - other_step); + if (idelta < 0) idelta = - idelta; + if (third_step == 0 && ( + (idelta == 1) ? ( + this == (other_step +1) || + this == (other_step -1) || + this == (this_step +1) || + this == (this_step -1)) + : + ( + this == (this_step + other_step)/2 + ) + )) third_step = this; + if (third_step != this) break; + } + } + sum += this; + p--; + if (p < 0) p += LAST_STEPS; + count++; + } + 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); + if (count != 0) sum = ((sum * 60) + (count /2)) / count; +#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS]) + msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), + SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); + printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), + SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); +#undef SV + ees->jump_fsecs = delta_sfsec; + ees->using_ramp = 1; + if (sincelast > 170) + ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs); + else ees->last_step_late = 30; + if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30; + if (ees->last_step_late < 0) ees->last_step_late = 0; + if (ees->last_step_late >= 60) ees->last_step_late = 59; + sincelast = 0; + } + else { /* First time in -- just save info */ + ees->last_step_late = 30; + ees->jump_fsecs = delta_sfsec; + ees->using_ramp = 1; + sum = 4 * 60; + } + ees->last_step = this_uisec; + printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n", + ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), + ees->second, old_sincelast, ees->last_step_late, count, sum, + ees->last_step_secs); + msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d", + ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, + old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); + if (sum) ees->last_step_secs = sum; + } + /* OK, so not a 4ms step at a minute boundry */ + else { + if (suspect_4ms_step) msyslog(LOG_ERR, + "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]", + ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec), + msec(EES_STEP_F - EES_STEP_F_GRACE), + subms(EES_STEP_F - EES_STEP_F_GRACE), + (int)msec(delta_f_abs), + (int)subms(delta_f_abs), + msec(EES_STEP_F + EES_STEP_F_GRACE), + subms(EES_STEP_F + EES_STEP_F_GRACE), + ees->second, + sincelast); + if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) { + static int ees_step_notes = EES_STEP_NOTES; + if (ees_step_notes > 0) { + ees_step_notes--; + printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n", + ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), + ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); + msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s", + ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !"); + } + } + } + } + ees->last_l = ees->arrvtime; + + /* IF we have found that it's ramping + * && it's within twice the expected ramp period + * && there is a non zero step size (avoid /0 !) + * THEN we twiddle things + */ + if (ees->using_ramp && + sincelast < (ees->last_step_secs)*2 && + ees->last_step_secs) + { long sec_of_ramp = sincelast + ees->last_step_late; + long fsecs; + l_fp inc; + + /* Ramp time may vary, so may ramp for longer than last time */ + if (sec_of_ramp > (ees->last_step_secs + 120)) + sec_of_ramp = ees->last_step_secs; + + /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */ + fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs); + + if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR, + "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)", + DB_LOG_DELTAS, + ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, + pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); + if (debug & DB_PRINT_DELTAS) printf( + "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n", + ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, + (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); + + /* Must sign extend the result */ + inc.l_i = (fsecs < 0) ? -1 : 0; + inc.l_f = fsecs; + if (debug & DB_INC_PPS) + { L_SUB(&pps_arrvstamp, &inc); + L_SUB(&ees->arrvtime, &inc); + } + else + { L_ADD(&pps_arrvstamp, &inc); + L_ADD(&ees->arrvtime, &inc); + } + } + else { + if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR, + "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x", + DB_LOG_DELTAS, + ees->unit, ees->using_ramp, + sincelast, + (ees->last_step_secs)*2, + ees->last_step_secs); + if (debug & DB_PRINT_DELTAS) printf( + "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n", + DB_LOG_DELTAS, + ees->unit, ees->using_ramp, + sincelast, + (ees->last_step_secs)*2, + ees->last_step_secs); + } + + L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]); + L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]); + + if (call_pps_sample && !(debug & DB_NO_PPS)) { + /* Sigh -- it expects its args negated */ + L_NEG(&pps_arrvstamp); + /* + * I had to disable this here, since it appears there is no pointer to the + * peer structure. + * + (void) pps_sample(peer, &pps_arrvstamp); + */ + } + + /* Subtract off the local clock time stamp */ + L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime); + if (debug & DB_LOG_SAMPLES) msyslog(LOG_ERR, + "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s", + ees->unit, DB_LOG_DELTAS, n_sample, + ees->codeoffsets[n_sample].l_f, + ees->codeoffsets[n_sample].l_f / 4295, + pps_arrvstamp.l_f, + pps_arrvstamp.l_f /4295, + (debug & DB_NO_PPS) ? " [no PPS]" : ""); + + if (ees->nsamples++ == NCODES-1) ees_process(ees); + + /* Done! */ +} + + +/* offcompare - auxiliary comparison routine for offset sort */ + +static int +offcompare( + l_fp *a, + l_fp *b + ) +{ + return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1); +} + + +/* ees_process - process a pile of samples from the clock */ +static void +ees_process( + struct eesunit *ees + ) +{ + static int last_samples = -1; + register int i, j; + register int noff; + register l_fp *coffs = ees->codeoffsets; + l_fp offset, tmp; + double dispersion; /* ++++ */ + int lostsync, isinsync; + int samples = ees->nsamples; + int samplelog = 0; /* keep "gcc -Wall" happy ! */ + int samplereduce = (samples + 1) / 2; + double doffset; + + /* Reset things to zero so we don't have to worry later */ + ees_reset(ees); + + if (sloppyclockflag[ees->unit]) { + samplelog = (samples < 2) ? 0 : + (samples < 5) ? 1 : + (samples < 9) ? 2 : + (samples < 17) ? 3 : + (samples < 33) ? 4 : 5; + samplereduce = (1 << samplelog); + } + + if (samples != last_samples && + ((samples != (last_samples-1)) || samples < 3)) { + msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....", + samples, last_samples, samplereduce); + last_samples = samples; + } + if (samples < 1) return; + + /* If requested, dump the raw data we have in the buffer */ + if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:"); + + /* Sort the offsets, trim off the extremes, then choose one. */ + qsort((char *) coffs, (u_int)samples, sizeof(l_fp), offcompare); + + noff = samples; + i = 0; + while ((noff - i) > samplereduce) { + /* Trim off the sample which is further away + * from the median. We work this out by doubling + * the median, subtracting off the end samples, and + * looking at the sign of the answer, using the + * identity (c-b)-(b-a) == 2*b-a-c + */ + tmp = coffs[(noff + i)/2]; + L_ADD(&tmp, &tmp); + L_SUB(&tmp, &coffs[i]); + L_SUB(&tmp, &coffs[noff-1]); + if (L_ISNEG(&tmp)) noff--; else i++; + } + + /* If requested, dump the reduce data we have in the buffer */ + if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:"); + + /* What we do next depends on the setting of the sloppy clock flag. + * If it is on, average the remainder to derive our estimate. + * Otherwise, just pick a representative value from the remaining stuff + */ + if (sloppyclockflag[ees->unit]) { + offset.l_ui = offset.l_uf = 0; + for (j = i; j < noff; j++) + L_ADD(&offset, &coffs[j]); + for (j = samplelog; j > 0; j--) + L_RSHIFTU(&offset); + } + else offset = coffs[i+BESTSAMPLE]; + + /* Compute the dispersion as the difference between the + * lowest and highest offsets that remain in the + * consideration list. + * + * It looks like MOST clocks have MOD (max error), so halve it ! + */ + tmp = coffs[noff-1]; + L_SUB(&tmp, &coffs[i]); +#define FRACT_SEC(n) ((1 << 30) / (n/2)) + dispersion = LFPTOFP(&tmp) / 2; /* ++++ */ + if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog( + (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, + "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d", + debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE), + offset.l_f / 4295, offset.l_f, + (dispersion * 1526) / 100, + (sloppyclockflag[ees->unit]) ? " by averaging" : "", + FRACT_SEC(10) / 4295, + (coffs[0].l_f) / 4295, + i, + (coffs[i].l_f) / 4295, + (coffs[samples/2].l_f) / 4295, + (coffs[i+BESTSAMPLE].l_f) / 4295, + noff-1, + (coffs[noff-1].l_f) / 4295, + (coffs[samples-1].l_f) / 4295); + + /* Are we playing silly wotsits ? + * If we are using all data, see if there is a "small" delta, + * and if so, blurr this with 3/4 of the delta from the last value + */ + if (ees->usealldata && ees->offset.l_uf) { + long diff = (long) (ees->offset.l_uf - offset.l_uf); + + /* is the delta small enough ? */ + if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) { + int samd = (64 * 4) / samples; + long new; + if (samd < 2) samd = 2; + new = offset.l_uf + ((diff * (samd -1)) / samd); + + /* Sign change -> need to fix up int part */ + if ((new & (1 << 31)) != + (((long) offset.l_uf) & ( 1 << 31))) + { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d", + new & (1 << 31), + ((long) offset.l_uf) & ( 1 << 31), + new, (long) offset.l_uf, + (new < 0) ? -1 : 1); + offset.l_ui += (new < 0) ? -1 : 1; + } + dispersion /= 4; + if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog( + (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] Smooth data: %ld -> %ld, dispersion now %f", + debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE), + ((long) offset.l_uf) / 4295, new / 4295, + (dispersion * 1526) / 100); + offset.l_uf = (unsigned long) new; + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "[%x] No smooth as delta not %d < %ld < %d", + debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), + - FRACT_SEC(100), diff, FRACT_SEC(100)); + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)", + debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), + ees->usealldata, ees->offset.l_f, ees->offset.l_uf, + offset.l_f, ees->offset.l_f - offset.l_f); + + /* Collect offset info for debugging info */ + ees->offset = offset; + ees->lowoffset = coffs[i]; + ees->highoffset = coffs[noff-1]; + + /* Determine synchronization status. Can be unsync'd either + * by a report from the clock or by a leap hold. + * + * Loss of the radio signal for a short time does not cause + * us to go unsynchronised, since the receiver keeps quite + * good time on its own. The spec says 20ms in 4 hours; the + * observed drift in our clock (Cambridge) is about a second + * a day, but even that keeps us within the inherent tolerance + * of the clock for about 15 minutes. Observation shows that + * the typical "short" outage is 3 minutes, so to allow us + * to ride out those, we will give it 5 minutes. + */ + lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0; + isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1; + + /* Done. Use time of last good, synchronised code as the + * reference time, and lastsampletime as the receive time. + */ + if (ees->fix_pending) { + msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n", + ees->fix_pending, ees->unit, offset.l_i, offset.l_f); + ees->fix_pending = 0; + } + LFPTOD(&offset, doffset); + refclock_receive(ees->peer); + ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL); +} + +/* msfees_poll - called by the transmit procedure */ +static void +msfees_poll( + int unit, + struct peer *peer + ) +{ + if (unit >= MAXUNITS) { + msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid", + unit); + return; + } + if (!unitinuse[unit]) { + msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused", + unit); + return; + } + + ees_process(eesunits[unit]); + + if ((current_time - eesunits[unit]->lasttime) > 150) + ees_event(eesunits[unit], CEVNT_FAULT); +} + + +#else +int refclock_msfees_bs; +#endif /* REFCLOCK */ |