1 /* refclock_ees - clock driver for the EES M201 receiver */ 2 3 #ifdef HAVE_CONFIG_H 4 #include <config.h> 5 #endif 6 7 #if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS) 8 9 /* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes 10 * were removed as the code was overly hairy, they weren't in use 11 * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk 12 */ 13 14 #include "ntpd.h" 15 #include "ntp_io.h" 16 #include "ntp_refclock.h" 17 #include "ntp_unixtime.h" 18 #include "ntp_calendar.h" 19 20 #include <ctype.h> 21 #if defined(HAVE_BSD_TTYS) 22 #include <sgtty.h> 23 #endif /* HAVE_BSD_TTYS */ 24 #if defined(HAVE_SYSV_TTYS) 25 #include <termio.h> 26 #endif /* HAVE_SYSV_TTYS */ 27 #if defined(HAVE_TERMIOS) 28 #include <termios.h> 29 #endif 30 #if defined(STREAM) 31 #include <stropts.h> 32 #endif 33 34 #ifdef HAVE_SYS_TERMIOS_H 35 # include <sys/termios.h> 36 #endif 37 #ifdef HAVE_SYS_PPSCLOCK_H 38 # include <sys/ppsclock.h> 39 #endif 40 41 #include "ntp_stdlib.h" 42 43 int dbg = 0; 44 /* 45 fudgefactor = fudgetime1; 46 os_delay = fudgetime2; 47 offset_fudge = os_delay + fudgefactor + inherent_delay; 48 stratumtouse = fudgeval1 & 0xf 49 dbg = fudgeval2; 50 sloppyclockflag = flags & CLK_FLAG1; 51 1 log smoothing summary when processing sample 52 4 dump the buffer from the clock 53 8 EIOGETKD the last n uS time stamps 54 if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0; 55 ees->dump_vals = flags & CLK_FLAG3; 56 ees->usealldata = flags & CLK_FLAG4; 57 58 59 bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0; 60 bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0; 61 bug->values[2] = (u_long)ees->status; 62 bug->values[3] = (u_long)ees->lastevent; 63 bug->values[4] = (u_long)ees->reason; 64 bug->values[5] = (u_long)ees->nsamples; 65 bug->values[6] = (u_long)ees->codestate; 66 bug->values[7] = (u_long)ees->day; 67 bug->values[8] = (u_long)ees->hour; 68 bug->values[9] = (u_long)ees->minute; 69 bug->values[10] = (u_long)ees->second; 70 bug->values[11] = (u_long)ees->tz; 71 bug->values[12] = ees->yearstart; 72 bug->values[13] = (ees->leaphold > current_time) ? 73 ees->leaphold - current_time : 0; 74 bug->values[14] = inherent_delay[unit].l_uf; 75 bug->values[15] = offset_fudge[unit].l_uf; 76 77 bug->times[0] = ees->reftime; 78 bug->times[1] = ees->arrvtime; 79 bug->times[2] = ees->lastsampletime; 80 bug->times[3] = ees->offset; 81 bug->times[4] = ees->lowoffset; 82 bug->times[5] = ees->highoffset; 83 bug->times[6] = inherent_delay[unit]; 84 bug->times[8] = os_delay[unit]; 85 bug->times[7] = fudgefactor[unit]; 86 bug->times[9] = offset_fudge[unit]; 87 bug->times[10]= ees->yearstart, 0; 88 */ 89 90 /* This should support the use of an EES M201 receiver with RS232 91 * output (modified to transmit time once per second). 92 * 93 * For the format of the message sent by the clock, see the EESM_ 94 * definitions below. 95 * 96 * It appears to run free for an integral number of minutes, until the error 97 * reaches 4mS, at which point it steps at second = 01. 98 * It appears that sometimes it steps 4mS (say at 7 min interval), 99 * then the next minute it decides that it was an error, so steps back. 100 * On the next minute it steps forward again :-( 101 * This is typically 16.5uS/S then 3975uS at the 4min re-sync, 102 * or 9.5uS/S then 3990.5uS at a 7min re-sync, 103 * at which point it may lose the "00" second time stamp. 104 * I assume that the most accurate time is just AFTER the re-sync. 105 * Hence remember the last cycle interval, 106 * 107 * Can run in any one of: 108 * 109 * PPSCD PPS signal sets CD which interupts, and grabs the current TOD 110 * (sun) *in the interupt code*, so as to avoid problems with 111 * the STREAMS scheduling. 112 * 113 * It appears that it goes 16.5 uS slow each second, then every 4 mins it 114 * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7) 115 */ 116 117 /* Definitions */ 118 #ifndef MAXUNITS 119 #define MAXUNITS 4 /* maximum number of EES units permitted */ 120 #endif 121 122 #ifndef EES232 123 #define EES232 "/dev/ees%d" /* Device to open to read the data */ 124 #endif 125 126 /* Other constant stuff */ 127 #ifndef EESPRECISION 128 #define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */ 129 #endif 130 #ifndef EESREFID 131 #define EESREFID "MSF\0" /* String to identify the clock */ 132 #endif 133 #ifndef EESHSREFID 134 #define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */ 135 #endif 136 137 /* Description of clock */ 138 #define EESDESCRIPTION "EES M201 MSF Receiver" 139 140 /* Speed we run the clock port at. If this is changed the UARTDELAY 141 * value should be recomputed to suit. 142 */ 143 #ifndef SPEED232 144 #define SPEED232 B9600 /* 9600 baud */ 145 #endif 146 147 /* What is the inherent delay for this mode of working, i.e. when is the 148 * data time stamped. 149 */ 150 #define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */ 151 #define BITS_TO_L_FP(bits, baud) \ 152 (((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT) 153 #define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600) 154 #define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600) 155 156 #ifndef STREAM_PP1 157 #define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->" 158 #endif 159 #ifndef STREAM_PP2 160 #define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->" 161 #endif 162 163 /* Offsets of the bytes of the serial line code. The clock gives 164 * local time with a GMT/BST indication. The EESM_ definitions 165 * give offsets into ees->lastcode. 166 */ 167 #define EESM_CSEC 0 /* centiseconds - always zero in our clock */ 168 #define EESM_SEC 1 /* seconds in BCD */ 169 #define EESM_MIN 2 /* minutes in BCD */ 170 #define EESM_HOUR 3 /* hours in BCD */ 171 #define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */ 172 #define EESM_DAY 5 /* day of month in BCD */ 173 #define EESM_MON 6 /* month in BCD */ 174 #define EESM_YEAR 7 /* year MOD 100 in BCD */ 175 #define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */ 176 #define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */ 177 #define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */ 178 /* followed by a frame alignment byte (0xff) / 179 / which is not put into the lastcode buffer*/ 180 181 /* Length of the serial time code, in characters. The first length 182 * is less the frame alignment byte. 183 */ 184 #define LENEESPRT (EESM_MSFOK+1) 185 #define LENEESCODE (LENEESPRT+1) 186 187 /* Code state. */ 188 #define EESCS_WAIT 0 /* waiting for start of timecode */ 189 #define EESCS_GOTSOME 1 /* have an incomplete time code buffered */ 190 191 /* Default fudge factor and character to receive */ 192 #define DEFFUDGETIME 0 /* Default user supplied fudge factor */ 193 #ifndef DEFOSTIME 194 #define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */ 195 #endif 196 #define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/ 197 198 /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median 199 * elimination. If we're running with an accurate clock, chose the BESTSAMPLE 200 * as the estimated offset, otherwise average the remainder. 201 */ 202 #define FULLSHIFT 6 /* NCODES root 2 */ 203 #define NCODES (1<< FULLSHIFT) /* 64 */ 204 #define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */ 205 206 /* Towards the high ( Why ?) end of half */ 207 #define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */ 208 209 /* Leap hold time. After a leap second the clock will no longer be 210 * reliable until it resynchronizes. Hope 40 minutes is enough. */ 211 #define EESLEAPHOLD (40 * 60) 212 213 #define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */ 214 #define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/ 215 #define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */ 216 #define EES_STEP_NOTES 50 /* Only do a limited number */ 217 #define MAX_STEP 16 /* Max number of steps to remember */ 218 219 /* debug is a bit mask of debugging that is wanted */ 220 #define DB_SYSLOG_SMPLI 0x0001 221 #define DB_SYSLOG_SMPLE 0x0002 222 #define DB_SYSLOG_SMTHI 0x0004 223 #define DB_SYSLOG_NSMTHE 0x0008 224 #define DB_SYSLOG_NSMTHI 0x0010 225 #define DB_SYSLOG_SMTHE 0x0020 226 #define DB_PRINT_EV 0x0040 227 #define DB_PRINT_CDT 0x0080 228 #define DB_PRINT_CDTC 0x0100 229 #define DB_SYSLOG_KEEPD 0x0800 230 #define DB_SYSLOG_KEEPE 0x1000 231 #define DB_LOG_DELTAS 0x2000 232 #define DB_PRINT_DELTAS 0x4000 233 #define DB_LOG_AWAITMORE 0x8000 234 #define DB_LOG_SAMPLES 0x10000 235 #define DB_NO_PPS 0x20000 236 #define DB_INC_PPS 0x40000 237 #define DB_DUMP_DELTAS 0x80000 238 239 struct eesunit { /* EES unit control structure. */ 240 struct peer *peer; /* associated peer structure */ 241 struct refclockio io; /* given to the I/O handler */ 242 l_fp reftime; /* reference time */ 243 l_fp lastsampletime; /* time as in txt from last EES msg */ 244 l_fp arrvtime; /* Time at which pkt arrived */ 245 l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */ 246 l_fp offset; /* chosen offset (for clkbug) */ 247 l_fp lowoffset; /* lowest sample offset (for clkbug) */ 248 l_fp highoffset; /* highest " " (for clkbug) */ 249 char lastcode[LENEESCODE+6]; /* last time code we received */ 250 u_long lasttime; /* last time clock heard from */ 251 u_long clocklastgood; /* last time good radio seen */ 252 u_char lencode; /* length of code in buffer */ 253 u_char nsamples; /* number of samples we've collected */ 254 u_char codestate; /* state of 232 code reception */ 255 u_char unit; /* unit number for this guy */ 256 u_char status; /* clock status */ 257 u_char lastevent; /* last clock event */ 258 u_char reason; /* reason for last abort */ 259 u_char hour; /* hour of day */ 260 u_char minute; /* minute of hour */ 261 u_char second; /* seconds of minute */ 262 char tz; /* timezone from clock */ 263 u_char ttytype; /* method used */ 264 u_char dump_vals; /* Should clock values be dumped */ 265 u_char usealldata; /* Use ALL samples */ 266 u_short day; /* day of year from last code */ 267 u_long yearstart; /* start of current year */ 268 u_long leaphold; /* time of leap hold expiry */ 269 u_long badformat; /* number of bad format codes */ 270 u_long baddata; /* number of invalid time codes */ 271 u_long timestarted; /* time we started this */ 272 long last_pps_no; /* The serial # of the last PPS */ 273 char fix_pending; /* Is a "sync to time" pending ? */ 274 /* Fine tuning - compensate for 4 mS ramping .... */ 275 l_fp last_l; /* last time stamp */ 276 u_char last_steps[MAX_STEP]; /* Most recent n steps */ 277 int best_av_step; /* Best guess at average step */ 278 char best_av_step_count; /* # of steps over used above */ 279 char this_step; /* Current pos in buffer */ 280 int last_step_late; /* How late the last step was (0-59) */ 281 long jump_fsecs; /* # of fractions of a sec last jump */ 282 u_long last_step; /* time of last step */ 283 int last_step_secs; /* Number of seconds in last step */ 284 int using_ramp; /* 1 -> noemal, -1 -> over stepped */ 285 }; 286 #define last_sec last_l.l_ui 287 #define last_sfsec last_l.l_f 288 #define this_uisec ((ees->arrvtime).l_ui) 289 #define this_sfsec ((ees->arrvtime).l_f) 290 #define msec(x) ((x) / (1<<22)) 291 #define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0]) 292 #define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5))) 293 294 /* Bitmask for what methods to try to use -- currently only PPS enabled */ 295 #define T_CBREAK 1 296 #define T_PPS 8 297 /* macros to test above */ 298 #define is_cbreak(x) ((x)->ttytype & T_CBREAK) 299 #define is_pps(x) ((x)->ttytype & T_PPS) 300 #define is_any(x) ((x)->ttytype) 301 302 #define CODEREASON 20 /* reason codes */ 303 304 /* Data space for the unit structures. Note that we allocate these on 305 * the fly, but never give them back. */ 306 static struct eesunit *eesunits[MAXUNITS]; 307 static u_char unitinuse[MAXUNITS]; 308 309 /* Keep the fudge factors separately so they can be set even 310 * when no clock is configured. */ 311 static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */ 312 static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */ 313 static l_fp os_delay[MAXUNITS]; /* fudgetime2 */ 314 static l_fp offset_fudge[MAXUNITS]; /* Sum of above */ 315 static u_char stratumtouse[MAXUNITS]; 316 static u_char sloppyclockflag[MAXUNITS]; 317 318 static int deltas[60]; 319 320 static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */ 321 static l_fp onesec; /* = { 1, 0 }; */ 322 323 #ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */ 324 #define DUMP_BUF_SIZE 10112 325 #endif 326 327 /* ees_reset - reset the count back to zero */ 328 #define ees_reset(ees) (ees)->nsamples = 0; \ 329 (ees)->codestate = EESCS_WAIT 330 331 /* ees_event - record and report an event */ 332 #define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \ 333 ees_report_event((ees), (evcode)) 334 335 /* Find the precision of the system clock by reading it */ 336 #define USECS 1000000 337 #define MINSTEP 5 /* some systems increment uS on each call */ 338 #define MAXLOOPS (USECS/9) 339 340 /* 341 * Function prototypes 342 */ 343 344 static int msfees_start P((int unit, struct peer *peer)); 345 static void msfees_shutdown P((int unit, struct peer *peer)); 346 static void msfees_poll P((int unit, struct peer *peer)); 347 static void msfees_init P((void)); 348 static void dump_buf P((l_fp *coffs, int from, int to, char *text)); 349 static void ees_report_event P((struct eesunit *ees, int code)); 350 static void ees_receive P((struct recvbuf *rbufp)); 351 static void ees_process P((struct eesunit *ees)); 352 #ifdef QSORT_USES_VOID_P 353 static int offcompare P((const void *va, const void *vb)); 354 #else 355 static int offcompare P((const l_fp *a, const l_fp *b)); 356 #endif /* QSORT_USES_VOID_P */ 357 358 359 /* 360 * Transfer vector 361 */ 362 struct refclock refclock_msfees = { 363 msfees_start, /* start up driver */ 364 msfees_shutdown, /* shut down driver */ 365 msfees_poll, /* transmit poll message */ 366 noentry, /* not used */ 367 msfees_init, /* initialize driver */ 368 noentry, /* not used */ 369 NOFLAGS /* not used */ 370 }; 371 372 373 static void 374 dump_buf( 375 l_fp *coffs, 376 int from, 377 int to, 378 char *text 379 ) 380 { 381 char buff[DUMP_BUF_SIZE + 80]; 382 int i; 383 register char *ptr = buff; 384 385 sprintf(ptr, text); 386 for (i=from; i<to; i++) 387 { while (*ptr) ptr++; 388 if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff); 389 sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295); 390 } 391 msyslog(LOG_DEBUG, "D: %s", buff); 392 } 393 394 /* msfees_init - initialize internal ees driver data */ 395 static void 396 msfees_init(void) 397 { 398 register int i; 399 /* Just zero the data arrays */ 400 memset((char *)eesunits, 0, sizeof eesunits); 401 memset((char *)unitinuse, 0, sizeof unitinuse); 402 403 acceptable_slop.l_ui = 0; 404 acceptable_slop.l_uf = 1 << (FRACTION_PREC -2); 405 406 onesec.l_ui = 1; 407 onesec.l_uf = 0; 408 409 /* Initialize fudge factors to default. */ 410 for (i = 0; i < MAXUNITS; i++) { 411 fudgefactor[i].l_ui = 0; 412 fudgefactor[i].l_uf = DEFFUDGETIME; 413 os_delay[i].l_ui = 0; 414 os_delay[i].l_uf = DEFOSTIME; 415 inherent_delay[i].l_ui = 0; 416 inherent_delay[i].l_uf = DEFINHTIME; 417 offset_fudge[i] = os_delay[i]; 418 L_ADD(&offset_fudge[i], &fudgefactor[i]); 419 L_ADD(&offset_fudge[i], &inherent_delay[i]); 420 stratumtouse[i] = 0; 421 sloppyclockflag[i] = 0; 422 } 423 } 424 425 426 /* msfees_start - open the EES devices and initialize data for processing */ 427 static int 428 msfees_start( 429 int unit, 430 struct peer *peer 431 ) 432 { 433 register struct eesunit *ees; 434 register int i; 435 int fd232 = -1; 436 char eesdev[20]; 437 struct termios ttyb, *ttyp; 438 struct refclockproc *pp; 439 pp = peer->procptr; 440 441 if (unit >= MAXUNITS) { 442 msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", 443 unit, MAXUNITS-1); 444 return 0; 445 } 446 if (unitinuse[unit]) { 447 msyslog(LOG_ERR, "ees clock: unit number %d in use", unit); 448 return 0; 449 } 450 451 /* Unit okay, attempt to open the devices. We do them both at 452 * once to make sure we can */ 453 (void) sprintf(eesdev, EES232, unit); 454 455 fd232 = open(eesdev, O_RDWR, 0777); 456 if (fd232 == -1) { 457 msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev); 458 return 0; 459 } 460 461 #ifdef TIOCEXCL 462 /* Set for exclusive use */ 463 if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) { 464 msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev); 465 goto screwed; 466 } 467 #endif 468 469 /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */ 470 471 /* Set port characteristics. If we don't have a STREAMS module or 472 * a clock line discipline, cooked mode is just usable, even though it 473 * strips the top bit. The only EES byte which uses the top 474 * bit is the year, and we don't use that anyway. If we do 475 * have the line discipline, we choose raw mode, and the 476 * line discipline code will block up the messages. 477 */ 478 479 /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */ 480 481 ttyp = &ttyb; 482 if (tcgetattr(fd232, ttyp) < 0) { 483 msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev); 484 goto screwed; 485 } 486 487 ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; 488 ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; 489 ttyp->c_oflag = 0; 490 ttyp->c_lflag = ICANON; 491 ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; 492 if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { 493 msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); 494 goto screwed; 495 } 496 497 if (tcflush(fd232, TCIOFLUSH) < 0) { 498 msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev); 499 goto screwed; 500 } 501 502 inherent_delay[unit].l_uf = INH_DELAY_PPS; 503 504 /* offset fudge (how *late* the timestamp is) = fudge + os delays */ 505 offset_fudge[unit] = os_delay[unit]; 506 L_ADD(&offset_fudge[unit], &fudgefactor[unit]); 507 L_ADD(&offset_fudge[unit], &inherent_delay[unit]); 508 509 /* Looks like this might succeed. Find memory for the structure. 510 * Look to see if there are any unused ones, if not we malloc() one. 511 */ 512 if (eesunits[unit] != 0) /* The one we want is okay */ 513 ees = eesunits[unit]; 514 else { 515 /* Look for an unused, but allocated struct */ 516 for (i = 0; i < MAXUNITS; i++) { 517 if (!unitinuse[i] && eesunits[i] != 0) 518 break; 519 } 520 521 if (i < MAXUNITS) { /* Reclaim this one */ 522 ees = eesunits[i]; 523 eesunits[i] = 0; 524 } /* no spare -- make a new one */ 525 else ees = (struct eesunit *) emalloc(sizeof(struct eesunit)); 526 } 527 memset((char *)ees, 0, sizeof(struct eesunit)); 528 eesunits[unit] = ees; 529 530 /* Set up the structures */ 531 ees->peer = peer; 532 ees->unit = (u_char)unit; 533 ees->timestarted= current_time; 534 ees->ttytype = 0; 535 ees->io.clock_recv= ees_receive; 536 ees->io.srcclock= (caddr_t)ees; 537 ees->io.datalen = 0; 538 ees->io.fd = fd232; 539 540 /* Okay. Push one of the two (linked into the kernel, or dynamically 541 * loaded) STREAMS module, and give it to the I/O code to start 542 * receiving stuff. 543 */ 544 545 #ifdef STREAM 546 { 547 int rc1; 548 /* Pop any existing onews first ... */ 549 while (ioctl(fd232, I_POP, 0 ) >= 0) ; 550 551 /* Now try pushing either of the possible modules */ 552 if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 && 553 ioctl(fd232, I_PUSH, STREAM_PP2) < 0) { 554 msyslog(LOG_ERR, 555 "ees clock: Push of `%s' and `%s' to %s failed %m", 556 STREAM_PP1, STREAM_PP2, eesdev); 557 goto screwed; 558 } 559 else { 560 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 561 msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s", 562 (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev); 563 ees->ttytype |= T_PPS; 564 } 565 } 566 #endif /* STREAM */ 567 568 /* Add the clock */ 569 if (!io_addclock(&ees->io)) { 570 /* Oh shit. Just close and return. */ 571 msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev); 572 goto screwed; 573 } 574 575 576 /* All done. Initialize a few random peer variables, then 577 * return success. */ 578 peer->precision = sys_precision; 579 peer->stratum = stratumtouse[unit]; 580 if (stratumtouse[unit] <= 1) { 581 memcpy((char *)&pp->refid, EESREFID, 4); 582 if (unit > 0 && unit < 10) 583 ((char *)&pp->refid)[3] = '0' + unit; 584 } else { 585 peer->refid = htonl(EESHSREFID); 586 } 587 unitinuse[unit] = 1; 588 pp->unitptr = (caddr_t) &eesunits[unit]; 589 pp->clockdesc = EESDESCRIPTION; 590 msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit); 591 return (1); 592 593 screwed: 594 if (fd232 != -1) 595 (void) close(fd232); 596 return (0); 597 } 598 599 600 /* msfees_shutdown - shut down a EES clock */ 601 static void 602 msfees_shutdown( 603 int unit, 604 struct peer *peer 605 ) 606 { 607 register struct eesunit *ees; 608 609 if (unit >= MAXUNITS) { 610 msyslog(LOG_ERR, 611 "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)", 612 unit, MAXUNITS); 613 return; 614 } 615 if (!unitinuse[unit]) { 616 msyslog(LOG_ERR, 617 "ees clock: INTERNAL ERROR, unit number %d not in use", unit); 618 return; 619 } 620 621 /* Tell the I/O module to turn us off. We're history. */ 622 ees = eesunits[unit]; 623 io_closeclock(&ees->io); 624 unitinuse[unit] = 0; 625 } 626 627 628 /* ees_report_event - note the occurance of an event */ 629 static void 630 ees_report_event( 631 struct eesunit *ees, 632 int code 633 ) 634 { 635 if (ees->status != (u_char)code) { 636 ees->status = (u_char)code; 637 if (code != CEVNT_NOMINAL) 638 ees->lastevent = (u_char)code; 639 /* Should report event to trap handler in here. 640 * Soon... 641 */ 642 } 643 } 644 645 646 /* ees_receive - receive data from the serial interface on an EES clock */ 647 static void 648 ees_receive( 649 struct recvbuf *rbufp 650 ) 651 { 652 register int n_sample; 653 register int day; 654 register struct eesunit *ees; 655 register u_char *dpt; /* Data PoinTeR: move along ... */ 656 register u_char *dpend; /* Points just *after* last data char */ 657 register char *cp; 658 l_fp tmp; 659 int call_pps_sample = 0; 660 l_fp pps_arrvstamp; 661 int sincelast; 662 int pps_step = 0; 663 int suspect_4ms_step = 0; 664 struct ppsclockev ppsclockev; 665 long *ptr = (long *) &ppsclockev; 666 int rc; 667 int request; 668 #ifdef HAVE_CIOGETEV 669 request = CIOGETEV; 670 #endif 671 #ifdef HAVE_TIOCGPPSEV 672 request = TIOCGPPSEV; 673 #endif 674 675 /* Get the clock this applies to and a pointer to the data */ 676 ees = (struct eesunit *)rbufp->recv_srcclock; 677 dpt = (u_char *)&rbufp->recv_space; 678 dpend = dpt + rbufp->recv_length; 679 if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE)) 680 printf("[%d] ", rbufp->recv_length); 681 682 /* Check out our state and process appropriately */ 683 switch (ees->codestate) { 684 case EESCS_WAIT: 685 /* Set an initial guess at the timestamp as the recv time. 686 * If just running in CBREAK mode, we can't improve this. 687 * If we have the CLOCK Line Discipline, PPSCD, or sime such, 688 * then we will do better later .... 689 */ 690 ees->arrvtime = rbufp->recv_time; 691 ees->codestate = EESCS_GOTSOME; 692 ees->lencode = 0; 693 /*FALLSTHROUGH*/ 694 695 case EESCS_GOTSOME: 696 cp = &(ees->lastcode[ees->lencode]); 697 698 /* Gobble the bytes until the final (possibly stripped) 0xff */ 699 while (dpt < dpend && (*dpt & 0x7f) != 0x7f) { 700 *cp++ = (char)*dpt++; 701 ees->lencode++; 702 /* Oh dear -- too many bytes .. */ 703 if (ees->lencode > LENEESPRT) { 704 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 705 msyslog(LOG_INFO, 706 "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]", 707 ees->lencode, dpend - dpt, LENEESPRT, 708 #define D(x) (ees->lastcode[x]) 709 D(0), D(1), D(2), D(3), D(4), D(5), D(6), 710 D(7), D(8), D(9), D(10), D(11), D(12)); 711 #undef D 712 ees->badformat++; 713 ees->reason = CODEREASON + 1; 714 ees_event(ees, CEVNT_BADREPLY); 715 ees_reset(ees); 716 return; 717 } 718 } 719 /* Gave up because it was end of the buffer, rather than ff */ 720 if (dpt == dpend) { 721 /* Incomplete. Wait for more. */ 722 if (dbg & DB_LOG_AWAITMORE) 723 msyslog(LOG_INFO, 724 "I: ees clock %d: %p == %p: await more", 725 ees->unit, dpt, dpend); 726 return; 727 } 728 729 /* This shouldn't happen ... ! */ 730 if ((*dpt & 0x7f) != 0x7f) { 731 msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt); 732 ees->badformat++; 733 ees->reason = CODEREASON + 2; 734 ees_event(ees, CEVNT_BADREPLY); 735 ees_reset(ees); 736 return; 737 } 738 739 /* Skip the 0xff */ 740 dpt++; 741 742 /* Finally, got a complete buffer. Mainline code will 743 * continue on. */ 744 cp = ees->lastcode; 745 break; 746 747 default: 748 msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d", 749 ees->unit, ees->codestate); 750 ees->reason = CODEREASON + 5; 751 ees_event(ees, CEVNT_FAULT); 752 ees_reset(ees); 753 return; 754 } 755 756 /* Boy! After all that crap, the lastcode buffer now contains 757 * something we hope will be a valid time code. Do length 758 * checks and sanity checks on constant data. 759 */ 760 ees->codestate = EESCS_WAIT; 761 ees->lasttime = current_time; 762 if (ees->lencode != LENEESPRT) { 763 ees->badformat++; 764 ees->reason = CODEREASON + 6; 765 ees_event(ees, CEVNT_BADREPLY); 766 ees_reset(ees); 767 return; 768 } 769 770 cp = ees->lastcode; 771 772 /* Check that centisecond is zero */ 773 if (cp[EESM_CSEC] != 0) { 774 ees->baddata++; 775 ees->reason = CODEREASON + 7; 776 ees_event(ees, CEVNT_BADREPLY); 777 ees_reset(ees); 778 return; 779 } 780 781 /* Check flag formats */ 782 if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) { 783 ees->badformat++; 784 ees->reason = CODEREASON + 8; 785 ees_event(ees, CEVNT_BADREPLY); 786 ees_reset(ees); 787 return; 788 } 789 790 if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) { 791 ees->badformat++; 792 ees->reason = CODEREASON + 9; 793 ees_event(ees, CEVNT_BADREPLY); 794 ees_reset(ees); 795 return; 796 } 797 798 if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) { 799 ees->badformat++; 800 ees->reason = CODEREASON + 10; 801 ees_event(ees, CEVNT_BADREPLY); 802 ees_reset(ees); 803 return; 804 } 805 806 /* So far, so good. Compute day, hours, minutes, seconds, 807 * time zone. Do range checks on these. 808 */ 809 810 #define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) ) 811 #define istrue(x) ((x)?1:0) 812 813 ees->second = bcdunpack(cp[EESM_SEC]); /* second */ 814 ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */ 815 ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */ 816 817 day = bcdunpack(cp[EESM_DAY]); /* day of month */ 818 819 switch (bcdunpack(cp[EESM_MON])) { /* month */ 820 821 /* Add in lengths of all previous months. Add one more 822 if it is a leap year and after February. 823 */ 824 case 12: day += NOV; /*FALLSTHROUGH*/ 825 case 11: day += OCT; /*FALLSTHROUGH*/ 826 case 10: day += SEP; /*FALLSTHROUGH*/ 827 case 9: day += AUG; /*FALLSTHROUGH*/ 828 case 8: day += JUL; /*FALLSTHROUGH*/ 829 case 7: day += JUN; /*FALLSTHROUGH*/ 830 case 6: day += MAY; /*FALLSTHROUGH*/ 831 case 5: day += APR; /*FALLSTHROUGH*/ 832 case 4: day += MAR; /*FALLSTHROUGH*/ 833 case 3: day += FEB; 834 if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/ 835 case 2: day += JAN; /*FALLSTHROUGH*/ 836 case 1: break; 837 default: ees->baddata++; 838 ees->reason = CODEREASON + 11; 839 ees_event(ees, CEVNT_BADDATE); 840 ees_reset(ees); 841 return; 842 } 843 844 ees->day = day; 845 846 /* Get timezone. The clocktime routine wants the number 847 * of hours to add to the delivered time to get UT. 848 * Currently -1 if BST flag set, 0 otherwise. This 849 * is the place to tweak things if double summer time 850 * ever happens. 851 */ 852 ees->tz = istrue(cp[EESM_BST]) ? -1 : 0; 853 854 if (ees->day > 366 || ees->day < 1 || 855 ees->hour > 23 || ees->minute > 59 || ees->second > 59) { 856 ees->baddata++; 857 ees->reason = CODEREASON + 12; 858 ees_event(ees, CEVNT_BADDATE); 859 ees_reset(ees); 860 return; 861 } 862 863 n_sample = ees->nsamples; 864 865 /* Now, compute the reference time value: text -> tmp.l_ui */ 866 if (!clocktime(ees->day, ees->hour, ees->minute, ees->second, 867 ees->tz, rbufp->recv_time.l_ui, &ees->yearstart, 868 &tmp.l_ui)) { 869 ees->baddata++; 870 ees->reason = CODEREASON + 13; 871 ees_event(ees, CEVNT_BADDATE); 872 ees_reset(ees); 873 return; 874 } 875 tmp.l_uf = 0; 876 877 /* DON'T use ees->arrvtime -- it may be < reftime */ 878 ees->lastsampletime = tmp; 879 880 /* If we are synchronised to the radio, update the reference time. 881 * Also keep a note of when clock was last good. 882 */ 883 if (istrue(cp[EESM_MSFOK])) { 884 ees->reftime = tmp; 885 ees->clocklastgood = current_time; 886 } 887 888 889 /* Compute the offset. For the fractional part of the 890 * offset we use the expected delay for the message. 891 */ 892 ees->codeoffsets[n_sample].l_ui = tmp.l_ui; 893 ees->codeoffsets[n_sample].l_uf = 0; 894 895 /* Number of seconds since the last step */ 896 sincelast = this_uisec - ees->last_step; 897 898 memset((char *) &ppsclockev, 0, sizeof ppsclockev); 899 900 rc = ioctl(ees->io.fd, request, (char *) &ppsclockev); 901 if (dbg & DB_PRINT_EV) fprintf(stderr, 902 "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n", 903 DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees), 904 rc, errno, ptr[0], ptr[1], ptr[2]); 905 906 /* If we managed to get the time of arrival, process the info */ 907 if (rc >= 0) { 908 int conv = -1; 909 pps_step = ppsclockev.serial - ees->last_pps_no; 910 911 /* Possible that PPS triggered, but text message didn't */ 912 if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second); 913 if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1; 914 if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4; 915 916 /* allow for single loss of PPS only */ 917 if (pps_step != 1 && pps_step != 2) 918 fprintf(stderr, "PPS step: %d too far off %ld (%d)\n", 919 ppsclockev.serial, ees->last_pps_no, pps_step); 920 else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp)) 921 fprintf(stderr, "buftvtots failed\n"); 922 else { /* if ((ABS(time difference) - 0.25) < 0) 923 * then believe it ... 924 */ 925 l_fp diff; 926 diff = pps_arrvstamp; 927 conv = 0; 928 L_SUB(&diff, &ees->arrvtime); 929 if (dbg & DB_PRINT_CDT) 930 printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s", 931 DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf, 932 (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf, 933 (long)diff.l_ui, (long)diff.l_uf, 934 ctime(&(ppsclockev.tv.tv_sec))); 935 if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); 936 L_SUB(&diff, &acceptable_slop); 937 if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ 938 ees->arrvtime = pps_arrvstamp; 939 conv++; 940 call_pps_sample++; 941 } 942 /* Some loss of some signals around sec = 1 */ 943 else if (ees->second == 1) { 944 diff = pps_arrvstamp; 945 L_ADD(&diff, &onesec); 946 L_SUB(&diff, &ees->arrvtime); 947 if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf); 948 L_SUB(&diff, &acceptable_slop); 949 msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", 950 pps_arrvstamp.l_ui - ees->arrvtime.l_ui, 951 pps_arrvstamp.l_uf, 952 ees->arrvtime.l_uf, 953 diff.l_ui, diff.l_uf, 954 (int)ppsclockev.tv.tv_usec, 955 ctime(&(ppsclockev.tv.tv_sec))); 956 if (L_ISNEG(&diff)) { /* AOK -- pps_sample */ 957 suspect_4ms_step |= 2; 958 ees->arrvtime = pps_arrvstamp; 959 L_ADD(&ees->arrvtime, &onesec); 960 conv++; 961 call_pps_sample++; 962 } 963 } 964 } 965 ees->last_pps_no = ppsclockev.serial; 966 if (dbg & DB_PRINT_CDTC) 967 printf( 968 "[%x] %08lx %08lx %d u%d (%d %d)\n", 969 DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui, 970 (long)pps_arrvstamp.l_uf, conv, ees->unit, 971 call_pps_sample, pps_step); 972 } 973 974 /* See if there has been a 4ms jump at a minute boundry */ 975 { l_fp delta; 976 #define delta_isec delta.l_ui 977 #define delta_ssec delta.l_i 978 #define delta_sfsec delta.l_f 979 long delta_f_abs; 980 981 delta.l_i = ees->arrvtime.l_i; 982 delta.l_f = ees->arrvtime.l_f; 983 984 L_SUB(&delta, &ees->last_l); 985 delta_f_abs = delta_sfsec; 986 if (delta_f_abs < 0) delta_f_abs = -delta_f_abs; 987 988 /* Dump the deltas each minute */ 989 if (dbg & DB_DUMP_DELTAS) 990 { if (/*0 <= ees->second && */ 991 ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec; 992 /* Dump on second 1, as second 0 sometimes missed */ 993 if (ees->second == 1) { 994 char text[16 * ((sizeof deltas) / (sizeof deltas[0]))]; 995 char *cptr=text; 996 int i; 997 for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) { 998 sprintf(cptr, " %d.%04d", 999 msec(deltas[i]), subms(deltas[i])); 1000 while (*cptr) cptr++; 1001 } 1002 msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s", 1003 msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE), 1004 msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE), 1005 text+1); 1006 for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0; 1007 } 1008 } 1009 1010 /* Lets see if we have a 4 mS step at a minute boundaary */ 1011 if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) && 1012 (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) && 1013 (ees->second == 0 || ees->second == 1 || ees->second == 2) && 1014 (sincelast < 0 || sincelast > 122) 1015 ) { /* 4ms jump at min boundry */ 1016 int old_sincelast; 1017 int count=0; 1018 int sum = 0; 1019 /* Yes -- so compute the ramp time */ 1020 if (ees->last_step == 0) sincelast = 0; 1021 old_sincelast = sincelast; 1022 1023 /* First time in, just set "ees->last_step" */ 1024 if(ees->last_step) { 1025 int other_step = 0; 1026 int third_step = 0; 1027 int this_step = (sincelast + (60 /2)) / 60; 1028 int p_step = ees->this_step; 1029 int p; 1030 ees->last_steps[p_step] = this_step; 1031 p= p_step; 1032 p_step++; 1033 if (p_step >= LAST_STEPS) p_step = 0; 1034 ees->this_step = p_step; 1035 /* Find the "average" interval */ 1036 while (p != p_step) { 1037 int this = ees->last_steps[p]; 1038 if (this == 0) break; 1039 if (this != this_step) { 1040 if (other_step == 0 && ( 1041 this== (this_step +2) || 1042 this== (this_step -2) || 1043 this== (this_step +1) || 1044 this== (this_step -1))) 1045 other_step = this; 1046 if (other_step != this) { 1047 int idelta = (this_step - other_step); 1048 if (idelta < 0) idelta = - idelta; 1049 if (third_step == 0 && ( 1050 (idelta == 1) ? ( 1051 this == (other_step +1) || 1052 this == (other_step -1) || 1053 this == (this_step +1) || 1054 this == (this_step -1)) 1055 : 1056 ( 1057 this == (this_step + other_step)/2 1058 ) 1059 )) third_step = this; 1060 if (third_step != this) break; 1061 } 1062 } 1063 sum += this; 1064 p--; 1065 if (p < 0) p += LAST_STEPS; 1066 count++; 1067 } 1068 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); 1069 if (count != 0) sum = ((sum * 60) + (count /2)) / count; 1070 #define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS]) 1071 msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", 1072 ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), 1073 SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); 1074 printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", 1075 ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6), 1076 SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15)); 1077 #undef SV 1078 ees->jump_fsecs = delta_sfsec; 1079 ees->using_ramp = 1; 1080 if (sincelast > 170) 1081 ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs); 1082 else ees->last_step_late = 30; 1083 if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30; 1084 if (ees->last_step_late < 0) ees->last_step_late = 0; 1085 if (ees->last_step_late >= 60) ees->last_step_late = 59; 1086 sincelast = 0; 1087 } 1088 else { /* First time in -- just save info */ 1089 ees->last_step_late = 30; 1090 ees->jump_fsecs = delta_sfsec; 1091 ees->using_ramp = 1; 1092 sum = 4 * 60; 1093 } 1094 ees->last_step = this_uisec; 1095 printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n", 1096 ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), 1097 ees->second, old_sincelast, ees->last_step_late, count, sum, 1098 ees->last_step_secs); 1099 msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d", 1100 ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, 1101 old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); 1102 if (sum) ees->last_step_secs = sum; 1103 } 1104 /* OK, so not a 4ms step at a minute boundry */ 1105 else { 1106 if (suspect_4ms_step) msyslog(LOG_ERR, 1107 "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]", 1108 ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec), 1109 msec(EES_STEP_F - EES_STEP_F_GRACE), 1110 subms(EES_STEP_F - EES_STEP_F_GRACE), 1111 (int)msec(delta_f_abs), 1112 (int)subms(delta_f_abs), 1113 msec(EES_STEP_F + EES_STEP_F_GRACE), 1114 subms(EES_STEP_F + EES_STEP_F_GRACE), 1115 ees->second, 1116 sincelast); 1117 if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) { 1118 static int ees_step_notes = EES_STEP_NOTES; 1119 if (ees_step_notes > 0) { 1120 ees_step_notes--; 1121 printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n", 1122 ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec), 1123 ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); 1124 msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s", 1125 ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !"); 1126 } 1127 } 1128 } 1129 } 1130 ees->last_l = ees->arrvtime; 1131 1132 /* IF we have found that it's ramping 1133 * && it's within twice the expected ramp period 1134 * && there is a non zero step size (avoid /0 !) 1135 * THEN we twiddle things 1136 */ 1137 if (ees->using_ramp && 1138 sincelast < (ees->last_step_secs)*2 && 1139 ees->last_step_secs) 1140 { long sec_of_ramp = sincelast + ees->last_step_late; 1141 long fsecs; 1142 l_fp inc; 1143 1144 /* Ramp time may vary, so may ramp for longer than last time */ 1145 if (sec_of_ramp > (ees->last_step_secs + 120)) 1146 sec_of_ramp = ees->last_step_secs; 1147 1148 /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */ 1149 fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs); 1150 1151 if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR, 1152 "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)", 1153 DB_LOG_DELTAS, 1154 ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, 1155 pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); 1156 if (dbg & DB_PRINT_DELTAS) printf( 1157 "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n", 1158 ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, 1159 (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); 1160 1161 /* Must sign extend the result */ 1162 inc.l_i = (fsecs < 0) ? -1 : 0; 1163 inc.l_f = fsecs; 1164 if (dbg & DB_INC_PPS) 1165 { L_SUB(&pps_arrvstamp, &inc); 1166 L_SUB(&ees->arrvtime, &inc); 1167 } 1168 else 1169 { L_ADD(&pps_arrvstamp, &inc); 1170 L_ADD(&ees->arrvtime, &inc); 1171 } 1172 } 1173 else { 1174 if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR, 1175 "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x", 1176 DB_LOG_DELTAS, 1177 ees->unit, ees->using_ramp, 1178 sincelast, 1179 (ees->last_step_secs)*2, 1180 ees->last_step_secs); 1181 if (dbg & DB_PRINT_DELTAS) printf( 1182 "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n", 1183 DB_LOG_DELTAS, 1184 ees->unit, ees->using_ramp, 1185 sincelast, 1186 (ees->last_step_secs)*2, 1187 ees->last_step_secs); 1188 } 1189 1190 L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]); 1191 L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]); 1192 1193 if (call_pps_sample && !(dbg & DB_NO_PPS)) { 1194 /* Sigh -- it expects its args negated */ 1195 L_NEG(&pps_arrvstamp); 1196 /* 1197 * I had to disable this here, since it appears there is no pointer to the 1198 * peer structure. 1199 * 1200 (void) pps_sample(peer, &pps_arrvstamp); 1201 */ 1202 } 1203 1204 /* Subtract off the local clock time stamp */ 1205 L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime); 1206 if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR, 1207 "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s", 1208 ees->unit, DB_LOG_DELTAS, n_sample, 1209 ees->codeoffsets[n_sample].l_f, 1210 ees->codeoffsets[n_sample].l_f / 4295, 1211 pps_arrvstamp.l_f, 1212 pps_arrvstamp.l_f /4295, 1213 (dbg & DB_NO_PPS) ? " [no PPS]" : ""); 1214 1215 if (ees->nsamples++ == NCODES-1) ees_process(ees); 1216 1217 /* Done! */ 1218 } 1219 1220 1221 /* offcompare - auxiliary comparison routine for offset sort */ 1222 1223 #ifdef QSORT_USES_VOID_P 1224 static int 1225 offcompare( 1226 const void *va, 1227 const void *vb 1228 ) 1229 { 1230 const l_fp *a = (const l_fp *)va; 1231 const l_fp *b = (const l_fp *)vb; 1232 return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1); 1233 } 1234 #else 1235 static int 1236 offcompare( 1237 const l_fp *a, 1238 const l_fp *b 1239 ) 1240 { 1241 return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1); 1242 } 1243 #endif /* QSORT_USES_VOID_P */ 1244 1245 1246 /* ees_process - process a pile of samples from the clock */ 1247 static void 1248 ees_process( 1249 struct eesunit *ees 1250 ) 1251 { 1252 static int last_samples = -1; 1253 register int i, j; 1254 register int noff; 1255 register l_fp *coffs = ees->codeoffsets; 1256 l_fp offset, tmp; 1257 double dispersion; /* ++++ */ 1258 int lostsync, isinsync; 1259 int samples = ees->nsamples; 1260 int samplelog = 0; /* keep "gcc -Wall" happy ! */ 1261 int samplereduce = (samples + 1) / 2; 1262 double doffset; 1263 1264 /* Reset things to zero so we don't have to worry later */ 1265 ees_reset(ees); 1266 1267 if (sloppyclockflag[ees->unit]) { 1268 samplelog = (samples < 2) ? 0 : 1269 (samples < 5) ? 1 : 1270 (samples < 9) ? 2 : 1271 (samples < 17) ? 3 : 1272 (samples < 33) ? 4 : 5; 1273 samplereduce = (1 << samplelog); 1274 } 1275 1276 if (samples != last_samples && 1277 ((samples != (last_samples-1)) || samples < 3)) { 1278 msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....", 1279 samples, last_samples, samplereduce); 1280 last_samples = samples; 1281 } 1282 if (samples < 1) return; 1283 1284 /* If requested, dump the raw data we have in the buffer */ 1285 if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:"); 1286 1287 /* Sort the offsets, trim off the extremes, then choose one. */ 1288 qsort( 1289 #ifdef QSORT_USES_VOID_P 1290 (void *) 1291 #else 1292 (char *) 1293 #endif 1294 coffs, (size_t)samples, sizeof(l_fp), offcompare); 1295 1296 noff = samples; 1297 i = 0; 1298 while ((noff - i) > samplereduce) { 1299 /* Trim off the sample which is further away 1300 * from the median. We work this out by doubling 1301 * the median, subtracting off the end samples, and 1302 * looking at the sign of the answer, using the 1303 * identity (c-b)-(b-a) == 2*b-a-c 1304 */ 1305 tmp = coffs[(noff + i)/2]; 1306 L_ADD(&tmp, &tmp); 1307 L_SUB(&tmp, &coffs[i]); 1308 L_SUB(&tmp, &coffs[noff-1]); 1309 if (L_ISNEG(&tmp)) noff--; else i++; 1310 } 1311 1312 /* If requested, dump the reduce data we have in the buffer */ 1313 if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:"); 1314 1315 /* What we do next depends on the setting of the sloppy clock flag. 1316 * If it is on, average the remainder to derive our estimate. 1317 * Otherwise, just pick a representative value from the remaining stuff 1318 */ 1319 if (sloppyclockflag[ees->unit]) { 1320 offset.l_ui = offset.l_uf = 0; 1321 for (j = i; j < noff; j++) 1322 L_ADD(&offset, &coffs[j]); 1323 for (j = samplelog; j > 0; j--) 1324 L_RSHIFTU(&offset); 1325 } 1326 else offset = coffs[i+BESTSAMPLE]; 1327 1328 /* Compute the dispersion as the difference between the 1329 * lowest and highest offsets that remain in the 1330 * consideration list. 1331 * 1332 * It looks like MOST clocks have MOD (max error), so halve it ! 1333 */ 1334 tmp = coffs[noff-1]; 1335 L_SUB(&tmp, &coffs[i]); 1336 #define FRACT_SEC(n) ((1 << 30) / (n/2)) 1337 dispersion = LFPTOFP(&tmp) / 2; /* ++++ */ 1338 if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog( 1339 (dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, 1340 "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d", 1341 dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE), 1342 offset.l_f / 4295, offset.l_f, 1343 (dispersion * 1526) / 100, 1344 (sloppyclockflag[ees->unit]) ? " by averaging" : "", 1345 FRACT_SEC(10) / 4295, 1346 (coffs[0].l_f) / 4295, 1347 i, 1348 (coffs[i].l_f) / 4295, 1349 (coffs[samples/2].l_f) / 4295, 1350 (coffs[i+BESTSAMPLE].l_f) / 4295, 1351 noff-1, 1352 (coffs[noff-1].l_f) / 4295, 1353 (coffs[samples-1].l_f) / 4295); 1354 1355 /* Are we playing silly wotsits ? 1356 * If we are using all data, see if there is a "small" delta, 1357 * and if so, blurr this with 3/4 of the delta from the last value 1358 */ 1359 if (ees->usealldata && ees->offset.l_uf) { 1360 long diff = (long) (ees->offset.l_uf - offset.l_uf); 1361 1362 /* is the delta small enough ? */ 1363 if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) { 1364 int samd = (64 * 4) / samples; 1365 long new; 1366 if (samd < 2) samd = 2; 1367 new = offset.l_uf + ((diff * (samd -1)) / samd); 1368 1369 /* Sign change -> need to fix up int part */ 1370 if ((new & 0x80000000) != 1371 (((long) offset.l_uf) & 0x80000000)) 1372 { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ 1373 msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d", 1374 new & 0x80000000, 1375 ((long) offset.l_uf) & 0x80000000, 1376 new, (long) offset.l_uf, 1377 (new < 0) ? -1 : 1); 1378 offset.l_ui += (new < 0) ? -1 : 1; 1379 } 1380 dispersion /= 4; 1381 if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog( 1382 (dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, 1383 "I: [%x] Smooth data: %ld -> %ld, dispersion now %f", 1384 dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE), 1385 ((long) offset.l_uf) / 4295, new / 4295, 1386 (dispersion * 1526) / 100); 1387 offset.l_uf = (unsigned long) new; 1388 } 1389 else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( 1390 (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, 1391 "[%x] No smooth as delta not %d < %ld < %d", 1392 dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), 1393 - FRACT_SEC(100), diff, FRACT_SEC(100)); 1394 } 1395 else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog( 1396 (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, 1397 "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)", 1398 dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), 1399 ees->usealldata, ees->offset.l_f, ees->offset.l_uf, 1400 offset.l_f, ees->offset.l_f - offset.l_f); 1401 1402 /* Collect offset info for debugging info */ 1403 ees->offset = offset; 1404 ees->lowoffset = coffs[i]; 1405 ees->highoffset = coffs[noff-1]; 1406 1407 /* Determine synchronization status. Can be unsync'd either 1408 * by a report from the clock or by a leap hold. 1409 * 1410 * Loss of the radio signal for a short time does not cause 1411 * us to go unsynchronised, since the receiver keeps quite 1412 * good time on its own. The spec says 20ms in 4 hours; the 1413 * observed drift in our clock (Cambridge) is about a second 1414 * a day, but even that keeps us within the inherent tolerance 1415 * of the clock for about 15 minutes. Observation shows that 1416 * the typical "short" outage is 3 minutes, so to allow us 1417 * to ride out those, we will give it 5 minutes. 1418 */ 1419 lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0; 1420 isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1; 1421 1422 /* Done. Use time of last good, synchronised code as the 1423 * reference time, and lastsampletime as the receive time. 1424 */ 1425 if (ees->fix_pending) { 1426 msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n", 1427 ees->fix_pending, ees->unit, offset.l_i, offset.l_f); 1428 ees->fix_pending = 0; 1429 } 1430 LFPTOD(&offset, doffset); 1431 refclock_receive(ees->peer); 1432 ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL); 1433 } 1434 1435 /* msfees_poll - called by the transmit procedure */ 1436 static void 1437 msfees_poll( 1438 int unit, 1439 struct peer *peer 1440 ) 1441 { 1442 if (unit >= MAXUNITS) { 1443 msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid", 1444 unit); 1445 return; 1446 } 1447 if (!unitinuse[unit]) { 1448 msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused", 1449 unit); 1450 return; 1451 } 1452 1453 ees_process(eesunits[unit]); 1454 1455 if ((current_time - eesunits[unit]->lasttime) > 150) 1456 ees_event(eesunits[unit], CEVNT_FAULT); 1457 } 1458 1459 1460 #else 1461 int refclock_msfees_bs; 1462 #endif /* REFCLOCK */ 1463