Annotation of embedaddon/ntp/ntpd/refclock_msfees.c, revision 1.1.1.1
1.1 misho 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: snprintf(buff, sizeof(buff), text);
386: for (i = from; i < to; i++) {
387: ptr += strlen(ptr);
388: if ((ptr - buff) > DUMP_BUF_SIZE) {
389: msyslog(LOG_DEBUG, "D: %s", buff);
390: ptr = buff;
391: }
392: snprintf(ptr, sizeof(buff) - (ptr - buff),
393: " %06d", ((int)coffs[i].l_f) / 4295);
394: }
395: msyslog(LOG_DEBUG, "D: %s", buff);
396: }
397:
398: /* msfees_init - initialize internal ees driver data */
399: static void
400: msfees_init(void)
401: {
402: register int i;
403: /* Just zero the data arrays */
404: memset((char *)eesunits, 0, sizeof eesunits);
405: memset((char *)unitinuse, 0, sizeof unitinuse);
406:
407: acceptable_slop.l_ui = 0;
408: acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
409:
410: onesec.l_ui = 1;
411: onesec.l_uf = 0;
412:
413: /* Initialize fudge factors to default. */
414: for (i = 0; i < MAXUNITS; i++) {
415: fudgefactor[i].l_ui = 0;
416: fudgefactor[i].l_uf = DEFFUDGETIME;
417: os_delay[i].l_ui = 0;
418: os_delay[i].l_uf = DEFOSTIME;
419: inherent_delay[i].l_ui = 0;
420: inherent_delay[i].l_uf = DEFINHTIME;
421: offset_fudge[i] = os_delay[i];
422: L_ADD(&offset_fudge[i], &fudgefactor[i]);
423: L_ADD(&offset_fudge[i], &inherent_delay[i]);
424: stratumtouse[i] = 0;
425: sloppyclockflag[i] = 0;
426: }
427: }
428:
429:
430: /* msfees_start - open the EES devices and initialize data for processing */
431: static int
432: msfees_start(
433: int unit,
434: struct peer *peer
435: )
436: {
437: register struct eesunit *ees;
438: register int i;
439: int fd232 = -1;
440: char eesdev[20];
441: struct termios ttyb, *ttyp;
442: struct refclockproc *pp;
443: pp = peer->procptr;
444:
445: if (unit >= MAXUNITS) {
446: msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
447: unit, MAXUNITS-1);
448: return 0;
449: }
450: if (unitinuse[unit]) {
451: msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
452: return 0;
453: }
454:
455: /* Unit okay, attempt to open the devices. We do them both at
456: * once to make sure we can */
457: snprintf(eesdev, sizeof(eesdev), EES232, unit);
458:
459: fd232 = open(eesdev, O_RDWR, 0777);
460: if (fd232 == -1) {
461: msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
462: return 0;
463: }
464:
465: #ifdef TIOCEXCL
466: /* Set for exclusive use */
467: if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
468: msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
469: goto screwed;
470: }
471: #endif
472:
473: /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
474:
475: /* Set port characteristics. If we don't have a STREAMS module or
476: * a clock line discipline, cooked mode is just usable, even though it
477: * strips the top bit. The only EES byte which uses the top
478: * bit is the year, and we don't use that anyway. If we do
479: * have the line discipline, we choose raw mode, and the
480: * line discipline code will block up the messages.
481: */
482:
483: /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
484:
485: ttyp = &ttyb;
486: if (tcgetattr(fd232, ttyp) < 0) {
487: msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
488: goto screwed;
489: }
490:
491: ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
492: ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
493: ttyp->c_oflag = 0;
494: ttyp->c_lflag = ICANON;
495: ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
496: if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
497: msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
498: goto screwed;
499: }
500:
501: if (tcflush(fd232, TCIOFLUSH) < 0) {
502: msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
503: goto screwed;
504: }
505:
506: inherent_delay[unit].l_uf = INH_DELAY_PPS;
507:
508: /* offset fudge (how *late* the timestamp is) = fudge + os delays */
509: offset_fudge[unit] = os_delay[unit];
510: L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
511: L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
512:
513: /* Looks like this might succeed. Find memory for the structure.
514: * Look to see if there are any unused ones, if not we malloc() one.
515: */
516: if (eesunits[unit] != 0) /* The one we want is okay */
517: ees = eesunits[unit];
518: else {
519: /* Look for an unused, but allocated struct */
520: for (i = 0; i < MAXUNITS; i++) {
521: if (!unitinuse[i] && eesunits[i] != 0)
522: break;
523: }
524:
525: if (i < MAXUNITS) { /* Reclaim this one */
526: ees = eesunits[i];
527: eesunits[i] = 0;
528: } /* no spare -- make a new one */
529: else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
530: }
531: memset((char *)ees, 0, sizeof(struct eesunit));
532: eesunits[unit] = ees;
533:
534: /* Set up the structures */
535: ees->peer = peer;
536: ees->unit = (u_char)unit;
537: ees->timestarted= current_time;
538: ees->ttytype = 0;
539: ees->io.clock_recv= ees_receive;
540: ees->io.srcclock= (caddr_t)ees;
541: ees->io.datalen = 0;
542: ees->io.fd = fd232;
543:
544: /* Okay. Push one of the two (linked into the kernel, or dynamically
545: * loaded) STREAMS module, and give it to the I/O code to start
546: * receiving stuff.
547: */
548:
549: #ifdef STREAM
550: {
551: int rc1;
552: /* Pop any existing onews first ... */
553: while (ioctl(fd232, I_POP, 0 ) >= 0) ;
554:
555: /* Now try pushing either of the possible modules */
556: if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
557: ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
558: msyslog(LOG_ERR,
559: "ees clock: Push of `%s' and `%s' to %s failed %m",
560: STREAM_PP1, STREAM_PP2, eesdev);
561: goto screwed;
562: }
563: else {
564: NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
565: msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
566: (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
567: ees->ttytype |= T_PPS;
568: }
569: }
570: #endif /* STREAM */
571:
572: /* Add the clock */
573: if (!io_addclock(&ees->io)) {
574: /* Oh shit. Just close and return. */
575: msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
576: goto screwed;
577: }
578:
579:
580: /* All done. Initialize a few random peer variables, then
581: * return success. */
582: peer->precision = sys_precision;
583: peer->stratum = stratumtouse[unit];
584: if (stratumtouse[unit] <= 1) {
585: memcpy((char *)&pp->refid, EESREFID, 4);
586: if (unit > 0 && unit < 10)
587: ((char *)&pp->refid)[3] = '0' + unit;
588: } else {
589: peer->refid = htonl(EESHSREFID);
590: }
591: unitinuse[unit] = 1;
592: pp->unitptr = (caddr_t) &eesunits[unit];
593: pp->clockdesc = EESDESCRIPTION;
594: msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
595: return (1);
596:
597: screwed:
598: if (fd232 != -1)
599: (void) close(fd232);
600: return (0);
601: }
602:
603:
604: /* msfees_shutdown - shut down a EES clock */
605: static void
606: msfees_shutdown(
607: int unit,
608: struct peer *peer
609: )
610: {
611: register struct eesunit *ees;
612:
613: if (unit >= MAXUNITS) {
614: msyslog(LOG_ERR,
615: "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
616: unit, MAXUNITS);
617: return;
618: }
619: if (!unitinuse[unit]) {
620: msyslog(LOG_ERR,
621: "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
622: return;
623: }
624:
625: /* Tell the I/O module to turn us off. We're history. */
626: ees = eesunits[unit];
627: io_closeclock(&ees->io);
628: unitinuse[unit] = 0;
629: }
630:
631:
632: /* ees_report_event - note the occurance of an event */
633: static void
634: ees_report_event(
635: struct eesunit *ees,
636: int code
637: )
638: {
639: if (ees->status != (u_char)code) {
640: ees->status = (u_char)code;
641: if (code != CEVNT_NOMINAL)
642: ees->lastevent = (u_char)code;
643: /* Should report event to trap handler in here.
644: * Soon...
645: */
646: }
647: }
648:
649:
650: /* ees_receive - receive data from the serial interface on an EES clock */
651: static void
652: ees_receive(
653: struct recvbuf *rbufp
654: )
655: {
656: register int n_sample;
657: register int day;
658: register struct eesunit *ees;
659: register u_char *dpt; /* Data PoinTeR: move along ... */
660: register u_char *dpend; /* Points just *after* last data char */
661: register char *cp;
662: l_fp tmp;
663: int call_pps_sample = 0;
664: l_fp pps_arrvstamp;
665: int sincelast;
666: int pps_step = 0;
667: int suspect_4ms_step = 0;
668: struct ppsclockev ppsclockev;
669: long *ptr = (long *) &ppsclockev;
670: int rc;
671: int request;
672: #ifdef HAVE_CIOGETEV
673: request = CIOGETEV;
674: #endif
675: #ifdef HAVE_TIOCGPPSEV
676: request = TIOCGPPSEV;
677: #endif
678:
679: /* Get the clock this applies to and a pointer to the data */
680: ees = (struct eesunit *)rbufp->recv_srcclock;
681: dpt = (u_char *)&rbufp->recv_space;
682: dpend = dpt + rbufp->recv_length;
683: if ((dbg & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
684: printf("[%d] ", rbufp->recv_length);
685:
686: /* Check out our state and process appropriately */
687: switch (ees->codestate) {
688: case EESCS_WAIT:
689: /* Set an initial guess at the timestamp as the recv time.
690: * If just running in CBREAK mode, we can't improve this.
691: * If we have the CLOCK Line Discipline, PPSCD, or sime such,
692: * then we will do better later ....
693: */
694: ees->arrvtime = rbufp->recv_time;
695: ees->codestate = EESCS_GOTSOME;
696: ees->lencode = 0;
697: /*FALLSTHROUGH*/
698:
699: case EESCS_GOTSOME:
700: cp = &(ees->lastcode[ees->lencode]);
701:
702: /* Gobble the bytes until the final (possibly stripped) 0xff */
703: while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
704: *cp++ = (char)*dpt++;
705: ees->lencode++;
706: /* Oh dear -- too many bytes .. */
707: if (ees->lencode > LENEESPRT) {
708: NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
709: msyslog(LOG_INFO,
710: "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
711: ees->lencode, dpend - dpt, LENEESPRT,
712: #define D(x) (ees->lastcode[x])
713: D(0), D(1), D(2), D(3), D(4), D(5), D(6),
714: D(7), D(8), D(9), D(10), D(11), D(12));
715: #undef D
716: ees->badformat++;
717: ees->reason = CODEREASON + 1;
718: ees_event(ees, CEVNT_BADREPLY);
719: ees_reset(ees);
720: return;
721: }
722: }
723: /* Gave up because it was end of the buffer, rather than ff */
724: if (dpt == dpend) {
725: /* Incomplete. Wait for more. */
726: if (dbg & DB_LOG_AWAITMORE)
727: msyslog(LOG_INFO,
728: "I: ees clock %d: %p == %p: await more",
729: ees->unit, dpt, dpend);
730: return;
731: }
732:
733: /* This shouldn't happen ... ! */
734: if ((*dpt & 0x7f) != 0x7f) {
735: msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
736: ees->badformat++;
737: ees->reason = CODEREASON + 2;
738: ees_event(ees, CEVNT_BADREPLY);
739: ees_reset(ees);
740: return;
741: }
742:
743: /* Skip the 0xff */
744: dpt++;
745:
746: /* Finally, got a complete buffer. Mainline code will
747: * continue on. */
748: cp = ees->lastcode;
749: break;
750:
751: default:
752: msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
753: ees->unit, ees->codestate);
754: ees->reason = CODEREASON + 5;
755: ees_event(ees, CEVNT_FAULT);
756: ees_reset(ees);
757: return;
758: }
759:
760: /* Boy! After all that crap, the lastcode buffer now contains
761: * something we hope will be a valid time code. Do length
762: * checks and sanity checks on constant data.
763: */
764: ees->codestate = EESCS_WAIT;
765: ees->lasttime = current_time;
766: if (ees->lencode != LENEESPRT) {
767: ees->badformat++;
768: ees->reason = CODEREASON + 6;
769: ees_event(ees, CEVNT_BADREPLY);
770: ees_reset(ees);
771: return;
772: }
773:
774: cp = ees->lastcode;
775:
776: /* Check that centisecond is zero */
777: if (cp[EESM_CSEC] != 0) {
778: ees->baddata++;
779: ees->reason = CODEREASON + 7;
780: ees_event(ees, CEVNT_BADREPLY);
781: ees_reset(ees);
782: return;
783: }
784:
785: /* Check flag formats */
786: if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
787: ees->badformat++;
788: ees->reason = CODEREASON + 8;
789: ees_event(ees, CEVNT_BADREPLY);
790: ees_reset(ees);
791: return;
792: }
793:
794: if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
795: ees->badformat++;
796: ees->reason = CODEREASON + 9;
797: ees_event(ees, CEVNT_BADREPLY);
798: ees_reset(ees);
799: return;
800: }
801:
802: if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
803: ees->badformat++;
804: ees->reason = CODEREASON + 10;
805: ees_event(ees, CEVNT_BADREPLY);
806: ees_reset(ees);
807: return;
808: }
809:
810: /* So far, so good. Compute day, hours, minutes, seconds,
811: * time zone. Do range checks on these.
812: */
813:
814: #define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
815: #define istrue(x) ((x)?1:0)
816:
817: ees->second = bcdunpack(cp[EESM_SEC]); /* second */
818: ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
819: ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
820:
821: day = bcdunpack(cp[EESM_DAY]); /* day of month */
822:
823: switch (bcdunpack(cp[EESM_MON])) { /* month */
824:
825: /* Add in lengths of all previous months. Add one more
826: if it is a leap year and after February.
827: */
828: case 12: day += NOV; /*FALLSTHROUGH*/
829: case 11: day += OCT; /*FALLSTHROUGH*/
830: case 10: day += SEP; /*FALLSTHROUGH*/
831: case 9: day += AUG; /*FALLSTHROUGH*/
832: case 8: day += JUL; /*FALLSTHROUGH*/
833: case 7: day += JUN; /*FALLSTHROUGH*/
834: case 6: day += MAY; /*FALLSTHROUGH*/
835: case 5: day += APR; /*FALLSTHROUGH*/
836: case 4: day += MAR; /*FALLSTHROUGH*/
837: case 3: day += FEB;
838: if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
839: case 2: day += JAN; /*FALLSTHROUGH*/
840: case 1: break;
841: default: ees->baddata++;
842: ees->reason = CODEREASON + 11;
843: ees_event(ees, CEVNT_BADDATE);
844: ees_reset(ees);
845: return;
846: }
847:
848: ees->day = day;
849:
850: /* Get timezone. The clocktime routine wants the number
851: * of hours to add to the delivered time to get UT.
852: * Currently -1 if BST flag set, 0 otherwise. This
853: * is the place to tweak things if double summer time
854: * ever happens.
855: */
856: ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
857:
858: if (ees->day > 366 || ees->day < 1 ||
859: ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
860: ees->baddata++;
861: ees->reason = CODEREASON + 12;
862: ees_event(ees, CEVNT_BADDATE);
863: ees_reset(ees);
864: return;
865: }
866:
867: n_sample = ees->nsamples;
868:
869: /* Now, compute the reference time value: text -> tmp.l_ui */
870: if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
871: ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
872: &tmp.l_ui)) {
873: ees->baddata++;
874: ees->reason = CODEREASON + 13;
875: ees_event(ees, CEVNT_BADDATE);
876: ees_reset(ees);
877: return;
878: }
879: tmp.l_uf = 0;
880:
881: /* DON'T use ees->arrvtime -- it may be < reftime */
882: ees->lastsampletime = tmp;
883:
884: /* If we are synchronised to the radio, update the reference time.
885: * Also keep a note of when clock was last good.
886: */
887: if (istrue(cp[EESM_MSFOK])) {
888: ees->reftime = tmp;
889: ees->clocklastgood = current_time;
890: }
891:
892:
893: /* Compute the offset. For the fractional part of the
894: * offset we use the expected delay for the message.
895: */
896: ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
897: ees->codeoffsets[n_sample].l_uf = 0;
898:
899: /* Number of seconds since the last step */
900: sincelast = this_uisec - ees->last_step;
901:
902: memset((char *) &ppsclockev, 0, sizeof ppsclockev);
903:
904: rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
905: if (dbg & DB_PRINT_EV) fprintf(stderr,
906: "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
907: DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
908: rc, errno, ptr[0], ptr[1], ptr[2]);
909:
910: /* If we managed to get the time of arrival, process the info */
911: if (rc >= 0) {
912: int conv = -1;
913: pps_step = ppsclockev.serial - ees->last_pps_no;
914:
915: /* Possible that PPS triggered, but text message didn't */
916: if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
917: if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
918: if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
919:
920: /* allow for single loss of PPS only */
921: if (pps_step != 1 && pps_step != 2)
922: fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
923: ppsclockev.serial, ees->last_pps_no, pps_step);
924: else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
925: fprintf(stderr, "buftvtots failed\n");
926: else { /* if ((ABS(time difference) - 0.25) < 0)
927: * then believe it ...
928: */
929: l_fp diff;
930: diff = pps_arrvstamp;
931: conv = 0;
932: L_SUB(&diff, &ees->arrvtime);
933: if (dbg & DB_PRINT_CDT)
934: printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
935: DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
936: (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
937: (long)diff.l_ui, (long)diff.l_uf,
938: ctime(&(ppsclockev.tv.tv_sec)));
939: if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
940: L_SUB(&diff, &acceptable_slop);
941: if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
942: ees->arrvtime = pps_arrvstamp;
943: conv++;
944: call_pps_sample++;
945: }
946: /* Some loss of some signals around sec = 1 */
947: else if (ees->second == 1) {
948: diff = pps_arrvstamp;
949: L_ADD(&diff, &onesec);
950: L_SUB(&diff, &ees->arrvtime);
951: if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
952: L_SUB(&diff, &acceptable_slop);
953: msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
954: pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
955: pps_arrvstamp.l_uf,
956: ees->arrvtime.l_uf,
957: diff.l_ui, diff.l_uf,
958: (int)ppsclockev.tv.tv_usec,
959: ctime(&(ppsclockev.tv.tv_sec)));
960: if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
961: suspect_4ms_step |= 2;
962: ees->arrvtime = pps_arrvstamp;
963: L_ADD(&ees->arrvtime, &onesec);
964: conv++;
965: call_pps_sample++;
966: }
967: }
968: }
969: ees->last_pps_no = ppsclockev.serial;
970: if (dbg & DB_PRINT_CDTC)
971: printf(
972: "[%x] %08lx %08lx %d u%d (%d %d)\n",
973: DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
974: (long)pps_arrvstamp.l_uf, conv, ees->unit,
975: call_pps_sample, pps_step);
976: }
977:
978: /* See if there has been a 4ms jump at a minute boundry */
979: { l_fp delta;
980: #define delta_isec delta.l_ui
981: #define delta_ssec delta.l_i
982: #define delta_sfsec delta.l_f
983: long delta_f_abs;
984:
985: delta.l_i = ees->arrvtime.l_i;
986: delta.l_f = ees->arrvtime.l_f;
987:
988: L_SUB(&delta, &ees->last_l);
989: delta_f_abs = delta_sfsec;
990: if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
991:
992: /* Dump the deltas each minute */
993: if (dbg & DB_DUMP_DELTAS)
994: {
995: if (/*0 <= ees->second && */
996: ees->second < COUNTOF(deltas))
997: deltas[ees->second] = delta_sfsec;
998: /* Dump on second 1, as second 0 sometimes missed */
999: if (ees->second == 1) {
1000: char text[16 * COUNTOF(deltas)];
1001: char *cptr=text;
1002: int i;
1003: for (i = 0; i < COUNTOF(deltas); i++) {
1004: snprintf(cptr, sizeof(text) / COUNTOF(deltas),
1005: " %d.%04d", msec(deltas[i]),
1006: subms(deltas[i]));
1007: cptr += strlen(cptr);
1008: }
1009: msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
1010: msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
1011: msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
1012: text+1);
1013: for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
1014: }
1015: }
1016:
1017: /* Lets see if we have a 4 mS step at a minute boundaary */
1018: if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
1019: (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
1020: (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
1021: (sincelast < 0 || sincelast > 122)
1022: ) { /* 4ms jump at min boundry */
1023: int old_sincelast;
1024: int count=0;
1025: int sum = 0;
1026: /* Yes -- so compute the ramp time */
1027: if (ees->last_step == 0) sincelast = 0;
1028: old_sincelast = sincelast;
1029:
1030: /* First time in, just set "ees->last_step" */
1031: if(ees->last_step) {
1032: int other_step = 0;
1033: int third_step = 0;
1034: int this_step = (sincelast + (60 /2)) / 60;
1035: int p_step = ees->this_step;
1036: int p;
1037: ees->last_steps[p_step] = this_step;
1038: p= p_step;
1039: p_step++;
1040: if (p_step >= LAST_STEPS) p_step = 0;
1041: ees->this_step = p_step;
1042: /* Find the "average" interval */
1043: while (p != p_step) {
1044: int this = ees->last_steps[p];
1045: if (this == 0) break;
1046: if (this != this_step) {
1047: if (other_step == 0 && (
1048: this== (this_step +2) ||
1049: this== (this_step -2) ||
1050: this== (this_step +1) ||
1051: this== (this_step -1)))
1052: other_step = this;
1053: if (other_step != this) {
1054: int idelta = (this_step - other_step);
1055: if (idelta < 0) idelta = - idelta;
1056: if (third_step == 0 && (
1057: (idelta == 1) ? (
1058: this == (other_step +1) ||
1059: this == (other_step -1) ||
1060: this == (this_step +1) ||
1061: this == (this_step -1))
1062: :
1063: (
1064: this == (this_step + other_step)/2
1065: )
1066: )) third_step = this;
1067: if (third_step != this) break;
1068: }
1069: }
1070: sum += this;
1071: p--;
1072: if (p < 0) p += LAST_STEPS;
1073: count++;
1074: }
1075: 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);
1076: if (count != 0) sum = ((sum * 60) + (count /2)) / count;
1077: #define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
1078: msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
1079: ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1080: SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1081: printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
1082: ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
1083: SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
1084: #undef SV
1085: ees->jump_fsecs = delta_sfsec;
1086: ees->using_ramp = 1;
1087: if (sincelast > 170)
1088: ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
1089: else ees->last_step_late = 30;
1090: if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
1091: if (ees->last_step_late < 0) ees->last_step_late = 0;
1092: if (ees->last_step_late >= 60) ees->last_step_late = 59;
1093: sincelast = 0;
1094: }
1095: else { /* First time in -- just save info */
1096: ees->last_step_late = 30;
1097: ees->jump_fsecs = delta_sfsec;
1098: ees->using_ramp = 1;
1099: sum = 4 * 60;
1100: }
1101: ees->last_step = this_uisec;
1102: printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
1103: ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
1104: ees->second, old_sincelast, ees->last_step_late, count, sum,
1105: ees->last_step_secs);
1106: msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
1107: ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
1108: old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
1109: if (sum) ees->last_step_secs = sum;
1110: }
1111: /* OK, so not a 4ms step at a minute boundry */
1112: else {
1113: if (suspect_4ms_step) msyslog(LOG_ERR,
1114: "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
1115: ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
1116: msec(EES_STEP_F - EES_STEP_F_GRACE),
1117: subms(EES_STEP_F - EES_STEP_F_GRACE),
1118: (int)msec(delta_f_abs),
1119: (int)subms(delta_f_abs),
1120: msec(EES_STEP_F + EES_STEP_F_GRACE),
1121: subms(EES_STEP_F + EES_STEP_F_GRACE),
1122: ees->second,
1123: sincelast);
1124: if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
1125: static int ees_step_notes = EES_STEP_NOTES;
1126: if (ees_step_notes > 0) {
1127: ees_step_notes--;
1128: printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
1129: ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
1130: ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
1131: msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
1132: ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
1133: }
1134: }
1135: }
1136: }
1137: ees->last_l = ees->arrvtime;
1138:
1139: /* IF we have found that it's ramping
1140: * && it's within twice the expected ramp period
1141: * && there is a non zero step size (avoid /0 !)
1142: * THEN we twiddle things
1143: */
1144: if (ees->using_ramp &&
1145: sincelast < (ees->last_step_secs)*2 &&
1146: ees->last_step_secs)
1147: { long sec_of_ramp = sincelast + ees->last_step_late;
1148: long fsecs;
1149: l_fp inc;
1150:
1151: /* Ramp time may vary, so may ramp for longer than last time */
1152: if (sec_of_ramp > (ees->last_step_secs + 120))
1153: sec_of_ramp = ees->last_step_secs;
1154:
1155: /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
1156: fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
1157:
1158: if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
1159: "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
1160: DB_LOG_DELTAS,
1161: ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
1162: pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1163: if (dbg & DB_PRINT_DELTAS) printf(
1164: "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
1165: ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
1166: (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
1167:
1168: /* Must sign extend the result */
1169: inc.l_i = (fsecs < 0) ? -1 : 0;
1170: inc.l_f = fsecs;
1171: if (dbg & DB_INC_PPS)
1172: { L_SUB(&pps_arrvstamp, &inc);
1173: L_SUB(&ees->arrvtime, &inc);
1174: }
1175: else
1176: { L_ADD(&pps_arrvstamp, &inc);
1177: L_ADD(&ees->arrvtime, &inc);
1178: }
1179: }
1180: else {
1181: if (dbg & DB_LOG_DELTAS) msyslog(LOG_ERR,
1182: "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
1183: DB_LOG_DELTAS,
1184: ees->unit, ees->using_ramp,
1185: sincelast,
1186: (ees->last_step_secs)*2,
1187: ees->last_step_secs);
1188: if (dbg & DB_PRINT_DELTAS) printf(
1189: "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
1190: DB_LOG_DELTAS,
1191: ees->unit, ees->using_ramp,
1192: sincelast,
1193: (ees->last_step_secs)*2,
1194: ees->last_step_secs);
1195: }
1196:
1197: L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
1198: L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
1199:
1200: if (call_pps_sample && !(dbg & DB_NO_PPS)) {
1201: /* Sigh -- it expects its args negated */
1202: L_NEG(&pps_arrvstamp);
1203: /*
1204: * I had to disable this here, since it appears there is no pointer to the
1205: * peer structure.
1206: *
1207: (void) pps_sample(peer, &pps_arrvstamp);
1208: */
1209: }
1210:
1211: /* Subtract off the local clock time stamp */
1212: L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
1213: if (dbg & DB_LOG_SAMPLES) msyslog(LOG_ERR,
1214: "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
1215: ees->unit, DB_LOG_DELTAS, n_sample,
1216: ees->codeoffsets[n_sample].l_f,
1217: ees->codeoffsets[n_sample].l_f / 4295,
1218: pps_arrvstamp.l_f,
1219: pps_arrvstamp.l_f /4295,
1220: (dbg & DB_NO_PPS) ? " [no PPS]" : "");
1221:
1222: if (ees->nsamples++ == NCODES-1) ees_process(ees);
1223:
1224: /* Done! */
1225: }
1226:
1227:
1228: /* offcompare - auxiliary comparison routine for offset sort */
1229:
1230: #ifdef QSORT_USES_VOID_P
1231: static int
1232: offcompare(
1233: const void *va,
1234: const void *vb
1235: )
1236: {
1237: const l_fp *a = (const l_fp *)va;
1238: const l_fp *b = (const l_fp *)vb;
1239: return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
1240: }
1241: #else
1242: static int
1243: offcompare(
1244: const l_fp *a,
1245: const l_fp *b
1246: )
1247: {
1248: return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
1249: }
1250: #endif /* QSORT_USES_VOID_P */
1251:
1252:
1253: /* ees_process - process a pile of samples from the clock */
1254: static void
1255: ees_process(
1256: struct eesunit *ees
1257: )
1258: {
1259: static int last_samples = -1;
1260: register int i, j;
1261: register int noff;
1262: register l_fp *coffs = ees->codeoffsets;
1263: l_fp offset, tmp;
1264: double dispersion; /* ++++ */
1265: int lostsync, isinsync;
1266: int samples = ees->nsamples;
1267: int samplelog = 0; /* keep "gcc -Wall" happy ! */
1268: int samplereduce = (samples + 1) / 2;
1269: double doffset;
1270:
1271: /* Reset things to zero so we don't have to worry later */
1272: ees_reset(ees);
1273:
1274: if (sloppyclockflag[ees->unit]) {
1275: samplelog = (samples < 2) ? 0 :
1276: (samples < 5) ? 1 :
1277: (samples < 9) ? 2 :
1278: (samples < 17) ? 3 :
1279: (samples < 33) ? 4 : 5;
1280: samplereduce = (1 << samplelog);
1281: }
1282:
1283: if (samples != last_samples &&
1284: ((samples != (last_samples-1)) || samples < 3)) {
1285: msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
1286: samples, last_samples, samplereduce);
1287: last_samples = samples;
1288: }
1289: if (samples < 1) return;
1290:
1291: /* If requested, dump the raw data we have in the buffer */
1292: if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:");
1293:
1294: /* Sort the offsets, trim off the extremes, then choose one. */
1295: qsort(
1296: #ifdef QSORT_USES_VOID_P
1297: (void *)
1298: #else
1299: (char *)
1300: #endif
1301: coffs, (size_t)samples, sizeof(l_fp), offcompare);
1302:
1303: noff = samples;
1304: i = 0;
1305: while ((noff - i) > samplereduce) {
1306: /* Trim off the sample which is further away
1307: * from the median. We work this out by doubling
1308: * the median, subtracting off the end samples, and
1309: * looking at the sign of the answer, using the
1310: * identity (c-b)-(b-a) == 2*b-a-c
1311: */
1312: tmp = coffs[(noff + i)/2];
1313: L_ADD(&tmp, &tmp);
1314: L_SUB(&tmp, &coffs[i]);
1315: L_SUB(&tmp, &coffs[noff-1]);
1316: if (L_ISNEG(&tmp)) noff--; else i++;
1317: }
1318:
1319: /* If requested, dump the reduce data we have in the buffer */
1320: if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
1321:
1322: /* What we do next depends on the setting of the sloppy clock flag.
1323: * If it is on, average the remainder to derive our estimate.
1324: * Otherwise, just pick a representative value from the remaining stuff
1325: */
1326: if (sloppyclockflag[ees->unit]) {
1327: offset.l_ui = offset.l_uf = 0;
1328: for (j = i; j < noff; j++)
1329: L_ADD(&offset, &coffs[j]);
1330: for (j = samplelog; j > 0; j--)
1331: L_RSHIFTU(&offset);
1332: }
1333: else offset = coffs[i+BESTSAMPLE];
1334:
1335: /* Compute the dispersion as the difference between the
1336: * lowest and highest offsets that remain in the
1337: * consideration list.
1338: *
1339: * It looks like MOST clocks have MOD (max error), so halve it !
1340: */
1341: tmp = coffs[noff-1];
1342: L_SUB(&tmp, &coffs[i]);
1343: #define FRACT_SEC(n) ((1 << 30) / (n/2))
1344: dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
1345: if (dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
1346: (dbg & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
1347: "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
1348: dbg & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
1349: offset.l_f / 4295, offset.l_f,
1350: (dispersion * 1526) / 100,
1351: (sloppyclockflag[ees->unit]) ? " by averaging" : "",
1352: FRACT_SEC(10) / 4295,
1353: (coffs[0].l_f) / 4295,
1354: i,
1355: (coffs[i].l_f) / 4295,
1356: (coffs[samples/2].l_f) / 4295,
1357: (coffs[i+BESTSAMPLE].l_f) / 4295,
1358: noff-1,
1359: (coffs[noff-1].l_f) / 4295,
1360: (coffs[samples-1].l_f) / 4295);
1361:
1362: /* Are we playing silly wotsits ?
1363: * If we are using all data, see if there is a "small" delta,
1364: * and if so, blurr this with 3/4 of the delta from the last value
1365: */
1366: if (ees->usealldata && ees->offset.l_uf) {
1367: long diff = (long) (ees->offset.l_uf - offset.l_uf);
1368:
1369: /* is the delta small enough ? */
1370: if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
1371: int samd = (64 * 4) / samples;
1372: long new;
1373: if (samd < 2) samd = 2;
1374: new = offset.l_uf + ((diff * (samd -1)) / samd);
1375:
1376: /* Sign change -> need to fix up int part */
1377: if ((new & 0x80000000) !=
1378: (((long) offset.l_uf) & 0x80000000))
1379: { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
1380: msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
1381: new & 0x80000000,
1382: ((long) offset.l_uf) & 0x80000000,
1383: new, (long) offset.l_uf,
1384: (new < 0) ? -1 : 1);
1385: offset.l_ui += (new < 0) ? -1 : 1;
1386: }
1387: dispersion /= 4;
1388: if (dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
1389: (dbg & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
1390: "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
1391: dbg & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
1392: ((long) offset.l_uf) / 4295, new / 4295,
1393: (dispersion * 1526) / 100);
1394: offset.l_uf = (unsigned long) new;
1395: }
1396: else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1397: (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1398: "[%x] No smooth as delta not %d < %ld < %d",
1399: dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1400: - FRACT_SEC(100), diff, FRACT_SEC(100));
1401: }
1402: else if (dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
1403: (dbg & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
1404: "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
1405: dbg & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
1406: ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
1407: offset.l_f, ees->offset.l_f - offset.l_f);
1408:
1409: /* Collect offset info for debugging info */
1410: ees->offset = offset;
1411: ees->lowoffset = coffs[i];
1412: ees->highoffset = coffs[noff-1];
1413:
1414: /* Determine synchronization status. Can be unsync'd either
1415: * by a report from the clock or by a leap hold.
1416: *
1417: * Loss of the radio signal for a short time does not cause
1418: * us to go unsynchronised, since the receiver keeps quite
1419: * good time on its own. The spec says 20ms in 4 hours; the
1420: * observed drift in our clock (Cambridge) is about a second
1421: * a day, but even that keeps us within the inherent tolerance
1422: * of the clock for about 15 minutes. Observation shows that
1423: * the typical "short" outage is 3 minutes, so to allow us
1424: * to ride out those, we will give it 5 minutes.
1425: */
1426: lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
1427: isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
1428:
1429: /* Done. Use time of last good, synchronised code as the
1430: * reference time, and lastsampletime as the receive time.
1431: */
1432: if (ees->fix_pending) {
1433: msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
1434: ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
1435: ees->fix_pending = 0;
1436: }
1437: LFPTOD(&offset, doffset);
1438: refclock_receive(ees->peer);
1439: ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
1440: }
1441:
1442: /* msfees_poll - called by the transmit procedure */
1443: static void
1444: msfees_poll(
1445: int unit,
1446: struct peer *peer
1447: )
1448: {
1449: if (unit >= MAXUNITS) {
1450: msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
1451: unit);
1452: return;
1453: }
1454: if (!unitinuse[unit]) {
1455: msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
1456: unit);
1457: return;
1458: }
1459:
1460: ees_process(eesunits[unit]);
1461:
1462: if ((current_time - eesunits[unit]->lasttime) > 150)
1463: ees_event(eesunits[unit], CEVNT_FAULT);
1464: }
1465:
1466:
1467: #else
1468: int refclock_msfees_bs;
1469: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>