⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ntp_refclock.c

📁 网络时间协议NTP 源码 版本v4.2.0b 该源码用于linux平台下
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * ntp_refclock - processing support for reference clocks */#ifdef HAVE_CONFIG_H# include <config.h>#endif#include "ntpd.h"#include "ntp_io.h"#include "ntp_unixtime.h"#include "ntp_tty.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#include <stdio.h>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif /* HAVE_SYS_IOCTL_H */#ifdef REFCLOCK#ifdef TTYCLK# ifdef HAVE_SYS_CLKDEFS_H#  include <sys/clkdefs.h>#  include <stropts.h># endif# ifdef HAVE_SYS_SIO_H#  include <sys/sio.h># endif#endif /* TTYCLK */#ifdef KERNEL_PLL#include "ntp_syscall.h"#endif /* KERNEL_PLL *//* * Reference clock support is provided here by maintaining the fiction * that the clock is actually a peer. As no packets are exchanged with a * reference clock, however, we replace the transmit, receive and packet * procedures with separate code to simulate them. Routines * refclock_transmit() and refclock_receive() maintain the peer * variables in a state analogous to an actual peer and pass reference * clock data on through the filters. Routines refclock_peer() and * refclock_unpeer() are called to initialize and terminate reference * clock associations. A set of utility routines is included to open * serial devices, process sample data, edit input lines to extract * embedded timestamps and to peform various debugging functions. * * The main interface used by these routines is the refclockproc * structure, which contains for most drivers the decimal equivalants of * the year, day, month, hour, second and millisecond/microsecond * decoded from the ASCII timecode. Additional information includes the * receive timestamp, exception report, statistics tallies, etc. In * addition, there may be a driver-specific unit structure used for * local control of the device. * * The support routines are passed a pointer to the peer structure, * which is used for all peer-specific processing and contains a pointer * to the refclockproc structure, which in turn containes a pointer to * the unit structure, if used. The peer structure is identified by an * interface address in the dotted quad form 127.127.t.u (for now only * IPv4 addresses are used, so we need to be sure the address is it), * where t is the clock type and u the unit. Some legacy drivers derive * the refclockproc structure pointer from the table * typeunit[type][unit]. This interface is strongly discouraged and may * be abandoned in future. */#define MAXUNIT 	4	/* max units */#define FUDGEFAC	.1	/* fudge correction factor */#define LF		0x0a	/* ASCII LF */#ifdef PPSint	fdpps;			/* ppsclock legacy */#endif /* PPS */int	cal_enable;		/* enable refclock calibrate *//* * Type/unit peer index. Used to find the peer structure for control and * debugging. When all clock drivers have been converted to new style, * this dissapears. */static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];/* * Forward declarations */#ifdef QSORT_USES_VOID_Pstatic int refclock_cmpl_fp P((const void *, const void *));#elsestatic int refclock_cmpl_fp P((const double *, const double *));#endif /* QSORT_USES_VOID_P */static int refclock_sample P((struct refclockproc *));/* * refclock_report - note the occurance of an event * * This routine presently just remembers the report and logs it, but * does nothing heroic for the trap handler. It tries to be a good * citizen and bothers the system log only if things change. */voidrefclock_report(	struct peer *peer,	int code	){	struct refclockproc *pp;	pp = peer->procptr;	if (pp == NULL)		return;	switch (code) {		case CEVNT_NOMINAL:			break;		case CEVNT_TIMEOUT:			pp->noreply++;			break;		case CEVNT_BADREPLY:			pp->badformat++;			break;		case CEVNT_FAULT:			break;		case CEVNT_PROP:			break;		case CEVNT_BADDATE:		case CEVNT_BADTIME:			pp->baddata++;			break;		default:			/* shouldn't happen */			break;	}	if (pp->currentstatus != code) {		pp->currentstatus = (u_char)code;		/* RFC1305: copy only iff not CEVNT_NOMINAL */		if (code != CEVNT_NOMINAL)			pp->lastevent = (u_char)code;		if (code == CEVNT_FAULT)			msyslog(LOG_ERR,			    "clock %s event '%s' (0x%02x)",			    refnumtoa(&peer->srcadr),			    ceventstr(code), code);		else {			NLOG(NLOG_CLOCKEVENT)			  msyslog(LOG_INFO,			    "clock %s event '%s' (0x%02x)",			    refnumtoa(&peer->srcadr),			    ceventstr(code), code);		}		/* RFC1305: post peer clock event */		report_event(EVNT_PEERCLOCK, peer);	}}/* * init_refclock - initialize the reference clock drivers * * This routine calls each of the drivers in turn to initialize internal * variables, if necessary. Most drivers have nothing to say at this * point. */voidinit_refclock(void){	int i, j;	for (i = 0; i < (int)num_refclock_conf; i++) {		if (refclock_conf[i]->clock_init != noentry)			(refclock_conf[i]->clock_init)();		for (j = 0; j < MAXUNIT; j++)			typeunit[i][j] = 0;	}}/* * refclock_newpeer - initialize and start a reference clock * * This routine allocates and initializes the interface structure which * supports a reference clock in the form of an ordinary NTP peer. A * driver-specific support routine completes the initialization, if * used. Default peer variables which identify the clock and establish * its reference ID and stratum are set here. It returns one if success * and zero if the clock address is invalid or already running, * insufficient resources are available or the driver declares a bum * rap. */intrefclock_newpeer(	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;	u_char clktype;	int unit;	/*	 * Check for valid clock address. If already running, shut it	 * down first.	 */	if (peer->srcadr.ss_family != AF_INET) {		msyslog(LOG_ERR,		       "refclock_newpeer: clock address %s invalid, address family not implemented for refclock",                        stoa(&peer->srcadr));                return (0);        }	if (!ISREFCLOCKADR(&peer->srcadr)) {		msyslog(LOG_ERR,			"refclock_newpeer: clock address %s invalid",			stoa(&peer->srcadr));		return (0);	}	clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);	unit = REFCLOCKUNIT(&peer->srcadr);	if (clktype >= num_refclock_conf || unit >= MAXUNIT ||		refclock_conf[clktype]->clock_start == noentry) {		msyslog(LOG_ERR,			"refclock_newpeer: clock type %d invalid\n",			clktype);		return (0);	}	/*	 * Allocate and initialize interface structure	 */	pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc));	if (pp == NULL)		return (0);	memset((char *)pp, 0, sizeof(struct refclockproc));	typeunit[clktype][unit] = peer;	peer->procptr = pp;	/*	 * Initialize structures	 */	peer->refclktype = clktype;	peer->refclkunit = (u_char)unit;	peer->flags |= FLAG_REFCLOCK | FLAG_FIXPOLL;	peer->leap = LEAP_NOTINSYNC;	peer->stratum = STRATUM_REFCLOCK;	peer->ppoll = peer->maxpoll;	pp->type = clktype;	pp->timestarted = current_time;	/*	 * 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;	}	/*	 * Do driver dependent initialization. The above defaults	 * can be wiggled, then finish up for consistency.	 */	if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {		refclock_unpeer(peer);		return (0);	}	peer->refid = pp->refid;	return (1);}/* * refclock_unpeer - shut down a clock */voidrefclock_unpeer(	struct peer *peer	/* peer structure pointer */	){	u_char clktype;	int unit;	/*	 * Wiggle the driver to release its resources, then give back	 * the interface structure.	 */	if (!peer->procptr)		return;	clktype = peer->refclktype;	unit = peer->refclkunit;	if (refclock_conf[clktype]->clock_shutdown != noentry)		(refclock_conf[clktype]->clock_shutdown)(unit, peer);	free(peer->procptr);	peer->procptr = 0;}/* * refclock_timer - called once per second for housekeeping. */voidrefclock_timer(	struct peer *peer	/* peer structure pointer */	){	u_char clktype;	int unit;	clktype = peer->refclktype;	unit = peer->refclkunit;	if (refclock_conf[clktype]->clock_timer != noentry)		(refclock_conf[clktype]->clock_timer)(unit, peer);}	/* * refclock_transmit - simulate the transmit procedure * * This routine implements the NTP transmit procedure for a reference * clock. This provides a mechanism to call the driver at the NTP poll * interval, as well as provides a reachability mechanism to detect a * broken radio or other madness. */voidrefclock_transmit(	struct peer *peer	/* peer structure pointer */	){	u_char clktype;	int unit;	clktype = peer->refclktype;	unit = peer->refclkunit;	peer->sent++;	get_systime(&peer->xmt);	/*	 * This is a ripoff of the peer transmit routine, but	 * specialized for reference clocks. We do a little less	 * protocol here and call the driver-specific transmit routine.	 */	if (peer->burst == 0) {		u_char oreach;#ifdef DEBUG		if (debug)			printf("refclock_transmit: at %ld %s\n",			    current_time, stoa(&(peer->srcadr)));#endif		/*		 * Update reachability and poll variables like the		 * network code.		 */		oreach = peer->reach;		peer->reach <<= 1;		peer->outdate = current_time;		if (!peer->reach) {			if (oreach) {				report_event(EVNT_UNREACH, peer);				peer->timereachable = current_time;			}		} else {			if (!(oreach & 0x07)) {				clock_filter(peer, 0., 0., MAXDISPERSE);				clock_select();			}			if (peer->flags & FLAG_BURST)				peer->burst = NSTAGE;		}	} else {		peer->burst--;	}	if (refclock_conf[clktype]->clock_poll != noentry)		(refclock_conf[clktype]->clock_poll)(unit, peer);	poll_update(peer, peer->hpoll);}/* * Compare two doubles - used with qsort() */#ifdef QSORT_USES_VOID_Pstatic intrefclock_cmpl_fp(	const void *p1,	const void *p2	){	const double *dp1 = (const double *)p1;	const double *dp2 = (const double *)p2;	if (*dp1 < *dp2)		return (-1);	if (*dp1 > *dp2)		return (1);	return (0);}#elsestatic intrefclock_cmpl_fp(	const double *dp1,	const double *dp2	){	if (*dp1 < *dp2)		return (-1);	if (*dp1 > *dp2)		return (1);	return (0);}#endif /* QSORT_USES_VOID_P *//* * refclock_process_offset - update median filter * * This routine uses the given offset and timestamps to construct a new * entry in the median filter circular buffer. Samples that overflow the * filter are quietly discarded. */voidrefclock_process_offset(	struct refclockproc *pp,	/* refclock structure pointer */	l_fp lasttim,			/* last timecode timestamp */	l_fp lastrec,			/* last receive timestamp */	double fudge	){	l_fp lftemp;	double doffset;	pp->lastrec = lastrec;	lftemp = lasttim;	L_SUB(&lftemp, &lastrec);	LFPTOD(&lftemp, doffset);	SAMPLE(doffset + fudge);}/* * refclock_process - process a sample from the clock * * This routine converts the timecode in the form days, hours, minutes, * seconds and milliseconds/microseconds to internal timestamp format, * then constructs a new entry in the median filter circular buffer. * Return success (1) if the data are correct and consistent with the * converntional calendar. * * Important for PPS users: Normally, the pp->lastrec is set to the * system time when the on-time character is received and the pp->year, * ..., pp->second decoded and the seconds fraction pp->nsec in * nanoseconds). When a PPS offset is available, pp->nsec is forced to * zero and the fraction for pp->lastrec is set to the PPS offset. */intrefclock_process(	struct refclockproc *pp		/* refclock structure pointer */	){	l_fp offset, ltemp;	/*	 * Compute the timecode timestamp from the days, hours, minutes,	 * seconds and milliseconds/microseconds of the timecode. Use	 * clocktime() for the aggregate seconds and the msec/usec for	 * the fraction, when present. Note that this code relies on the	 * filesystem time for the years and does not use the years of	 * the timecode.	 */	if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,		pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))		return (0);	offset.l_uf = 0;	DTOLFP(pp->nsec / 1e9, &ltemp);	L_ADD(&offset, &ltemp);	refclock_process_offset(pp, offset, pp->lastrec,	    pp->fudgetime1);	return (1);}/* * refclock_sample - process a pile of samples from the clock * * This routine implements a recursive median filter to suppress spikes * in the data, as well as determine a performance statistic. It * calculates the mean offset and RMS jitter. A time adjustment * fudgetime1 can be added to the final offset to compensate for various * systematic errors. The routine returns the number of samples * processed, which could be zero. */static intrefclock_sample(	struct refclockproc *pp		/* refclock structure pointer */	){	int	i, j, k, m, n;	double	off[MAXSTAGE];	double	offset;	/*	 * Copy the raw offsets and sort into ascending order. Don't do	 * anything if the buffer is empty.	 */	n = 0;	while (pp->codeproc != pp->coderecv) {		pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;		off[n] = pp->filter[pp->codeproc];		n++;	}	if (n == 0)		return (0);	if (n > 1)		qsort(#ifdef QSORT_USES_VOID_P		    (void *)#else		    (char *)#endif		    off, (size_t)n, sizeof(double), refclock_cmpl_fp);	/*	 * Reject the furthest from the median of the samples until	 * approximately 60 percent of the samples remain.	 */	i = 0; j = n;	m = n - (n * 4) / 10;	while ((j - i) > m) {		offset = off[(j + i) / 2];		if (off[j - 1] - offset < offset - off[i])			i++;	/* reject low end */		else			j--;	/* reject high end */	}	/*	 * Determine the offset and jitter.	 */	pp->offset = 0;	pp->jitter = 0;	for (k = i; k < j; k++) {		pp->offset += off[k];		if (k > i)			pp->jitter += SQUARE(off[k] - off[k - 1]);	}	pp->offset /= m;	pp->jitter = max(SQRT(pp->jitter / m), LOGTOD(sys_precision));#ifdef DEBUG	if (debug)		printf(		    "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",		    n, pp->offset, pp->disp, pp->jitter);#endif	return (n);}/* * 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(	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;#ifdef DEBUG	if (debug)		printf("refclock_receive: at %lu %s\n",		    current_time, stoa(&peer->srcadr));#endif	/*	 * Do a little sanity dance and update the peer structure. Groom	 * the median filter samples and give the data to the clock	 * filter.	 */	pp = peer->procptr;	peer->leap = pp->leap;	if (peer->leap == LEAP_NOTINSYNC)		return;	peer->received++;	peer->timereceived = current_time;	if (!peer->reach) {		report_event(EVNT_REACH, peer);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -