📄 ntp_refclock.c
字号:
* assembles the samples in a shift register. It implements a recursive * median filter to suppress spikes in the data, as well as determine a * rough dispersion estimate. A configuration constant time adjustment * fudgetime1 can be added to the final offset to compensate for various * systematic errors. The routine returns one if success and zero if * failure due to invalid timecode data or very noisy offsets. * * This interface is needed to allow for clocks (e. g. parse) that can * provide the correct offset including year information (though NTP * usually gives up on offsets greater than 1000 seconds). */intrefclock_sample(sample_offset, pp, nstart, nskeep) l_fp *sample_offset; /* input offset (offset! - not a time stamp) for filter machine */ struct refclockproc *pp; /* peer structure pointer */ int nstart; /* stages of median filter */ int nskeep; /* stages after outlyer trim */{ int i, n; l_fp offset, median, lftmp; l_fp off[MAXSTAGE]; u_fp disp; /* * Subtract the receive timestamp from the timecode timestamp * to form the raw offset. Insert in the median filter shift * register. */ pp->nstages = nstart; offset = *sample_offset; i = ((int)(pp->coderecv)) % pp->nstages; pp->filter[i] = offset; if (pp->coderecv == 0) for (i = 1; (u_int) i < pp->nstages; i++) pp->filter[i] = pp->filter[0]; pp->coderecv++; /* * Copy the raw offsets and sort into ascending order */ for (i = 0; (u_int) i < pp->nstages; i++) off[i] = pp->filter[i]; qsort((char *)off, pp->nstages, sizeof(l_fp), refclock_cmpl_fp); /* * Reject the furthest from the median of nstages samples until * nskeep samples remain. */ i = 0; n = pp->nstages; while ((n - i) > nskeep) { lftmp = off[n - 1]; median = off[(n + i) / 2]; L_SUB(&lftmp, &median); L_SUB(&median, &off[i]); if (L_ISHIS(&median, &lftmp)) { /* reject low end */ i++; } else { /* reject high end */ n--; } } /* * Compute the dispersion based on the difference between the * extremes of the remaining offsets. Add to this the time since * the last clock update, which represents the dispersion * increase with time. We know that NTP_MAXSKEW is 16. If the * sum is greater than the allowed sample dispersion, bail out. * If the loop is unlocked, return the most recent offset; * otherwise, return the median offset. */ lftmp = off[n - 1]; L_SUB(&lftmp, &off[i]); disp = LFPTOFP(&lftmp) + current_time - pp->lasttime; if (disp > REFCLOCKMAXDISPERSE) return (0); pp->offset = offset; pp->dispersion = disp; return (1);}/* * refclock_receive - simulate the receive and packet procedures * * This routine simulates the NTP receive and packet procedures for a * reference clock. This provides a mechanism in which the ordinary NTP * filter, selection and combining algorithms can be used to suppress * misbehaving radios and to mitigate between them when more than one is * available for backup. */voidrefclock_receive(peer, offset, delay, dispersion, reftime, rectime, leap) struct peer *peer; /* peer structure pointer */ l_fp *offset; /* computed offset (s) */ s_fp delay; /* computed delay to peer */ u_fp dispersion; /* computed dispersion to peer */ l_fp *reftime; /* time at last clock update */ l_fp *rectime; /* time at last peer update */ int leap; /* synchronization/leap code */{ int restrict; int trustable; int refclock_own_states; u_fp precision; peer->received++;#ifdef DEBUG if (debug) printf("refclock_receive: %s %s %s %s)\n", ntoa(&peer->srcadr), lfptoa(offset, 6), fptoa(delay, 5), ufptoa(dispersion, 5));#endif /* * some refclock implementations do a complete state and * event handling. reporting events must be disabled for * these critters (namely parse) */ refclock_own_states = leap & REFCLOCK_OWN_STATES; leap &= ~REFCLOCK_OWN_STATES; /* * The authentication and access-control machinery works, but * its utility may be questionable. */ restrict = restrictions(&peer->srcadr); if (restrict & (RES_IGNORE|RES_DONTSERVE)) return; peer->processed++; peer->timereceived = current_time; if (restrict & RES_DONTTRUST) trustable = 0; else trustable = 1; if (peer->flags & FLAG_AUTHENABLE) { if (trustable) peer->flags |= FLAG_AUTHENTIC; else peer->flags &= ~FLAG_AUTHENTIC; } peer->leap = leap; /* * Set the timestamps. rec and org are in local time, while ref * is in timecode time. */ peer->rec = peer->org = *rectime; peer->reftime = *reftime; /* * If the interface has been set to any_interface, set it to the * loopback address if we have one. This is so that peers which * are unreachable are easy to see in the peer display. */ if (peer->dstadr == any_interface && loopback_interface != 0) peer->dstadr = loopback_interface; /* * Set peer.pmode based on the hmode. For appearances only. */ switch (peer->hmode) { case MODE_ACTIVE: peer->pmode = MODE_PASSIVE; break; default: peer->pmode = MODE_SERVER; break; } /* * Abandon ship if the radio came bum. We only got this far * in order to make pretty billboards, even if bum. */ if (leap == LEAP_NOTINSYNC) return; /* * If this guy was previously unreachable, report him * reachable. */ if (peer->reach == 0) report_event(EVNT_REACH, peer); peer->reach |= 1; /* * Give the data to the clock filter and update the clock. Note * the clock reading precision initialized by the driver is * added at this point. */ precision = FP_SECOND >> -(int)peer->precision; if (precision == 0) precision = 1; if (!refclock_own_states) refclock_report(peer, CEVNT_NOMINAL); clock_filter(peer, offset, delay, dispersion + precision); clock_update(peer);}/* * refclock_gtlin - groom next input line and extract timestamp * * This routine processes the timecode received from the clock and * removes the parity bit and control characters. If a timestamp is * present in the timecode, as produced by the tty_clk line * discipline/streams module, it returns that as the timestamp; * otherwise, it returns the buffer timestamp. The routine return code * is the number of characters in the line. */intrefclock_gtlin(rbufp, lineptr, bmax, tsptr) struct recvbuf *rbufp; /* receive buffer pointer */ char *lineptr; /* current line pointer */ int bmax; /* remaining characters in line */ l_fp *tsptr; /* pointer to timestamp returned */{ char *dpt, *dpend, *dp; int i; l_fp trtmp, tstmp; char c; /* * Check for the presence of a timestamp left by the tty_clock * line discipline/streams module and, if present, use that * instead of the buffer timestamp captured by the I/O routines. * We recognize a timestamp by noting its value is earlier than * the buffer timestamp, but not more than one second earlier. */ dpt = (char *)&rbufp->recv_space; dpend = dpt + rbufp->recv_length; trtmp = rbufp->recv_time; if (dpend >= dpt + 8) { if (buftvtots(dpend - 8, &tstmp)) { L_SUB(&trtmp, &tstmp); if (trtmp.l_ui == 0) {#ifdef DEBUG if (debug) { printf( "refclock_gtlin: fd %d ldisc %s", rbufp->fd, lfptoa(&trtmp, 6)); get_systime(&trtmp); L_SUB(&trtmp, &tstmp); printf(" sigio %s\n", lfptoa(&trtmp, 6)); }#endif dpend -= 8; trtmp = tstmp; } else trtmp = rbufp->recv_time; } } /* * Edit timecode to remove control chars. Don't monkey with the * line buffer if the input buffer contains no ASCII printing * characters. */ if (dpend - dpt > bmax - 1) dpend = dpt + bmax - 1; for (dp = lineptr; dpt < dpend; dpt++) { c = *dpt & 0x7f; if (c >= ' ') *dp++ = c; } i = dp - lineptr; if (i > 0) *dp = '\0';#ifdef DEBUG if (debug) printf("refclock_gtlin: fd %d time %s timecode %d %s\n", rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);#endif *tsptr = trtmp; return (i);}/* * The following code does not apply to WINNT & VMS ... */#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)/* * refclock_open - open serial port for reference clock * * This routine opens a serial port for I/O and sets default options. It * returns the file descriptor if success and zero if failure. */intrefclock_open(dev, speed, flags) char *dev; /* device name pointer */ int speed; /* serial port speed (code) */ int flags; /* line discipline flags */{ int fd;#ifdef HAVE_TERMIOS struct termios ttyb, *ttyp;#endif /* HAVE_TERMIOS */#ifdef HAVE_SYSV_TTYS struct termio ttyb, *ttyp;#endif /* HAVE_SYSV_TTYS */#ifdef HAVE_BSD_TTYS struct sgttyb ttyb, *ttyp;#endif /* HAVE_BSD_TTYS */#ifdef TIOCMGET u_long ltemp;#endif /* TIOCMGET */ /* * Open serial port and set default options */ fd = open(dev, O_RDWR#ifdef O_NONBLOCK | O_NONBLOCK#endif , 0777); if (fd == -1) { msyslog(LOG_ERR, "refclock_open: %s: %m", dev); return (0); } /* * The following sections initialize the serial line port in * canonical (line-oriented) mode and set the specified line * speed, 8 bits and no parity. The modem control, break, erase * and kill functions are normally disabled. There is a * different section for each terminal interface, as selected at * compile time. */ ttyp = &ttyb;#ifdef HAVE_TERMIOS /* * POSIX serial line parameters (termios interface) */ if (tcgetattr(fd, ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d tcgetattr %m", fd); return (0); } /* * Set canonical mode and local connection; set specified speed, * 8 bits and no parity; map CR to NL; ignore break. */ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; ttyp->c_oflag = 0; ttyp->c_cflag = CS8 | CLOCAL | CREAD; (void)cfsetispeed(&ttyb, speed); (void)cfsetospeed(&ttyb, speed); ttyp->c_lflag = ICANON; ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';#ifdef TIOCMGET /* * If we have modem control, check to see if modem leads are * active; if so, set remote connection. This is necessary for * the kernel pps mods to work. */ ltemp = 0; if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) msyslog(LOG_ERR, "refclock_open: fd %d TIOCMGET failed: %m", fd);#if DEBUG if (debug) printf("refclock_open: fd %d modem status %lx\n", fd, ltemp);#endif if (ltemp & TIOCM_DSR) ttyp->c_cflag &= ~CLOCAL;#endif /* TIOCMGET */ if (tcsetattr(fd, TCSANOW, ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d TCSANOW failed: %m", fd); return (0); } if (tcflush(fd, TCIOFLUSH) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d TCIOFLUSH failed: %m", fd); return (0); }#endif /* HAVE_TERMIOS */#ifdef HAVE_SYSV_TTYS /* * System V serial line parameters (termio interface) * */ if (ioctl(fd, TCGETA, ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d TCGETA failed: %m", fd); return (0); } /* * Set canonical mode and local connection; set specified speed, * 8 bits and no parity; map CR to NL; ignore break. */ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; ttyp->c_oflag = 0; ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD; ttyp->c_lflag = ICANON; ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';#ifdef TIOCMGET /* * If we have modem control, check to see if modem leads are * active; if so, set remote connection. This is necessary for * the kernel pps mods to work. */ ltemp = 0; if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) msyslog(LOG_ERR, "refclock_open: fd %d TIOCMGET failed: %m", fd);#if DEBUG if (debug) printf("refclock_open: fd %d modem status %lx\n", fd, ltemp);#endif if (ltemp & TIOCM_DSR) ttyp->c_cflag &= ~CLOCAL;#endif /* TIOCMGET */ if (ioctl(fd, TCSETA, ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d TCSETA failed: %m", fd); return (0); }#endif /* HAVE_SYSV_TTYS */#ifdef HAVE_BSD_TTYS /* * 4.3bsd serial line parameters (sgttyb interface) */ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: fd %d TIOCGETP %m", fd); return (0); } ttyp->sg_ispeed = ttyp->sg_ospeed = speed; ttyp->sg_flags = EVENP | ODDP | CRMOD; if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) { msyslog(LOG_ERR, "refclock_open: TIOCSETP failed: %m"); return (0); }#endif /* HAVE_BSD_TTYS */ if (!refclock_ioctl(fd, flags)) { (void)close(fd); msyslog(LOG_ERR, "refclock_open: fd %d ioctl fails", fd); return (0); } return (fd);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -