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

📄 refclock_atom.c

📁 网络时间协议NTP 源码 版本v4.2.0b 该源码用于linux平台下
💻 C
字号:
/* * refclock_atom - clock driver for 1-pps signals */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <ctype.h>#include "ntpd.h"#include "ntp_io.h"#include "ntp_unixtime.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#if defined(REFCLOCK) && defined(CLOCK_ATOM)#ifdef HAVE_PPSAPI# include "ppsapi_timepps.h"#endif /* HAVE_PPSAPI *//* * This driver furnishes an interface for pulse-per-second (PPS) signals * produced by a cesium clock, timing receiver or related equipment. It * can be used to remove accumulated jitter and retime a secondary * server when synchronized to a primary server over a congested, wide- * area network and before redistributing the time to local clients. * * Before this driver becomes active, the local clock must be set to * within +-500 ms by another means, such as a radio clock or NTP * itself. There are two ways to connect the PPS signal, normally at TTL * levels, to the computer. One is to shift to EIA levels and connect to * pin 8 (DCD) of a serial port. This requires a level converter and * may require a one-shot flipflop to lengthen the pulse. The other is * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell * port. These methods are architecture dependent. * * Both methods require a modified device driver and kernel interface * compatible with the Pulse-per-Second API for Unix-like Operating * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at * present only the Alpha implementation provides the full generality of * the API with multiple PPS drivers and multiple handles per driver. If * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h * header file and kernel support specific to each operating system. * However, this driver can operate without this interface if means are * proviced to call the pps_sample() routine from another driver. Please * note; if the PPSAPI interface is present, it must be used. * * In many configurations a single port is used for the radio timecode * and PPS signal. In order to provide for this configuration and others * involving dedicated multiple serial/parallel ports, the driver first * attempts to open the device /dev/pps%d, where %d is the unit number. * If this fails, the driver attempts to open the device specified by * the pps configuration command. If a port is to be shared, the pps * command must be placed before the radio device(s) and the radio * device(s) must be placed before the PPS driver(s) in the * configuration file. * * This driver normally uses the PLL/FLL clock discipline implemented in * the ntpd code. Ordinarily, this is the most accurate means, as the * median filter in the driver interface is much larger than in the * kernel. However, if the systemic clock frequency error is large (tens * to hundreds of PPM), it's better to used the kernel support, if * available. * * Fudge Factors * * If flag2 is dim (default), the on-time epoch is the assert edge of * the PPS signal; if lit, the on-time epoch is the clear edge. If flag2 * is lit, the assert edge is used; if flag3 is dim (default), the * kernel PPS support is disabled; if lit it is enabled. The time1 * parameter can be used to compensate for miscellaneous device driver * and OS delays. *//* * Interface definitions */#ifdef HAVE_PPSAPI#define DEVICE		"/dev/pps%d" /* device name and unit */#endif /* HAVE_PPSAPI */#define	PRECISION	(-20)	/* precision assumed (about 1 us) */#define	REFID		"PPS\0"	/* reference ID */#define	DESCRIPTION	"PPS Clock Discipline" /* WRU */#define NANOSECOND	1000000000 /* one second (ns) */#define RANGEGATE	500000	/* range gate (ns) */static struct peer *pps_peer;	/* atom driver for PPS sources */#ifdef HAVE_PPSAPI/* * PPS unit control structure */struct ppsunit {	struct timespec ts;	/* last timestamp */	int fddev;		/* pps device descriptor */	pps_params_t pps_params; /* pps parameters */	pps_info_t pps_info;	/* last pps data */	pps_handle_t handle;	/* pps handlebars */};#endif /* HAVE_PPSAPI *//* * Function prototypes */static	int	atom_start	P((int, struct peer *));static	void	atom_poll	P((int, struct peer *));static	void	atom_shutdown	P((int, struct peer *));#ifdef HAVE_PPSAPIstatic	void	atom_control	P((int, struct refclockstat *, struct				    refclockstat *, struct peer *));static	void	atom_timer	P((int, struct peer *));static	int	atom_ppsapi	P((struct peer *, int));#endif /* HAVE_PPSAPI *//* * Transfer vector */#ifdef HAVE_PPSAPIstruct	refclock refclock_atom = {	atom_start,		/* start up driver */	atom_shutdown,		/* shut down driver */	atom_poll,		/* transmit poll message */	atom_control,		/* fudge control */	noentry,		/* initialize driver (not used) */	noentry,		/* buginfo (not used) */	atom_timer,		/* called once per second */};#else /* HAVE_PPSAPI */struct	refclock refclock_atom = {	atom_start,		/* start up driver */	atom_shutdown,		/* shut down driver */	atom_poll,		/* transmit poll message */	noentry,		/* fudge control (not used) */	noentry,		/* initialize driver (not used) */	noentry,		/* buginfo (not used) */	NOFLAGS			/* not used */};#endif /* HAVE_PPPSAPI *//* * atom_start - initialize data for processing */static intatom_start(	int unit,		/* unit number (not used) */	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;#ifdef HAVE_PPSAPI	register struct ppsunit *up;	char	device[80];	int	mode;#endif /* HAVE_PPSAPI */	/*	 * Allocate and initialize unit structure	 */	pps_peer = peer;	pp = peer->procptr;	peer->precision = PRECISION;	pp->clockdesc = DESCRIPTION;	pp->stratum = STRATUM_UNSPEC;	memcpy((char *)&pp->refid, REFID, 4);#ifdef HAVE_PPSAPI	up = emalloc(sizeof(struct ppsunit));	memset(up, 0, sizeof(struct ppsunit));	pp->unitptr = (caddr_t)up;	/*	 * Open PPS device. This can be any serial or parallel port and	 * not necessarily the port used for the associated radio.	 */	sprintf(device, DEVICE, unit);	up->fddev = open(device, O_RDWR, 0777);	if (up->fddev <= 0) {		msyslog(LOG_ERR,		    "refclock_atom: %s: %m", device);		return (0);	}	/*	 * Light off the PPSAPI interface.	 */	if (time_pps_create(up->fddev, &up->handle) < 0) {		msyslog(LOG_ERR,		    "refclock_atom: time_pps_create failed: %m");		return (0);	}	/*	 * If the mode is nonzero, use that for the time_pps_setparams()	 * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if	 * flag3 is lit.	 */	mode = peer->ttl;	if (mode == 0)		mode = PPS_CAPTUREASSERT;	return (atom_ppsapi(peer, mode));#else /* HAVE_PPSAPI */	return (1);#endif /* HAVE_PPSAPI */}/* * atom_shutdown - shut down the clock */static voidatom_shutdown(	int unit,		/* unit number (not used) */	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;	register struct ppsunit *up;	pp = peer->procptr;	up = (struct ppsunit *)pp->unitptr;#ifdef HAVE_PPSAPI	if (up->fddev > 0)		close(up->fddev);	if (up->handle != 0)		time_pps_destroy(up->handle);#endif /* HAVE_PPSAPI */	if (pps_peer == peer)		pps_peer = NULL;	free(up);}#ifdef HAVE_PPSAPI/* * atom_control - fudge control */static voidatom_control(	int unit,		/* unit (not used */	struct refclockstat *in, /* input parameters (not uded) */	struct refclockstat *out, /* output parameters (not used) */	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;	int	mode;	pp = peer->procptr;	if (peer->ttl != 0)	/* all legal modes must be nonzero */		return;	if (pp->sloppyclockflag & CLK_FLAG2)		mode = PPS_CAPTURECLEAR;	else		mode = PPS_CAPTUREASSERT;	atom_ppsapi(peer, mode);}/* * Initialize PPSAPI */intatom_ppsapi(	struct peer *peer,	/* peer structure pointer */	int mode		/* mode */	){	struct refclockproc *pp;	register struct ppsunit *up;	int capability;	pp = peer->procptr;	up = (struct ppsunit *)pp->unitptr;	if (up->handle == 0)		return (0);	if (time_pps_getcap(up->handle, &capability) < 0) {		msyslog(LOG_ERR,		    "refclock_atom: time_pps_getcap failed: %m");		return (0);	}	memset(&up->pps_params, 0, sizeof(pps_params_t));	up->pps_params.api_version = PPS_API_VERS_1;	up->pps_params.mode = mode | PPS_TSFMT_TSPEC;	if (time_pps_setparams(up->handle, &up->pps_params) < 0) {		msyslog(LOG_ERR,		    "refclock_atom: time_pps_setparams failed: %m");		return (0);	}	if (pp->sloppyclockflag & CLK_FLAG3) {		if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,		    up->pps_params.mode & ~PPS_TSFMT_TSPEC,		    PPS_TSFMT_TSPEC) < 0) {			msyslog(LOG_ERR,			    "refclock_atom: time_pps_kcbind failed: %m");			return (0);		}		pps_enable = 1;	}#if DEBUG	if (debug) {		time_pps_getparams(up->handle, &up->pps_params);		printf(		    "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",		    up->fddev, capability, up->pps_params.api_version,		    up->pps_params.mode);	}#endif	return (1);}/* * atom_timer - called once per second * * This routine is called once per second when the PPSAPI interface is * present. It snatches the PPS timestamp from the kernel and saves the * sign-extended fraction in a circular buffer for processing at the * next poll event. */static voidatom_timer(	int	unit,		/* unit number (not used) */	struct peer *peer	/* peer structure pointer */	){	register struct ppsunit *up;	struct refclockproc *pp;	pps_info_t pps_info;	struct timespec timeout, ts;	long	sec, nsec;	double	dtemp;	char	tbuf[80];	/* monitor buffer */	/*	 * Convert the timespec nanoseconds field to signed double and	 * save in the median filter. for billboards. No harm is done if	 * previous data are overwritten. If the discipline comes bum or	 * the data grow stale, just forget it. A range gate rejects new	 * samples if less than a jiggle time from the next second.	 */ 	pp = peer->procptr;	up = (struct ppsunit *)pp->unitptr;	if (up->handle == 0)		return;	timeout.tv_sec = 0;	timeout.tv_nsec = 0;	memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));	if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,	    &timeout) < 0) {		refclock_report(peer, CEVNT_FAULT);		return;	}	if (up->pps_params.mode & PPS_CAPTUREASSERT) {		ts = up->pps_info.assert_timestamp;	} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {		ts = up->pps_info.clear_timestamp;	} else {		refclock_report(peer, CEVNT_FAULT);		return;	}	/*	 * There can be zero, one or two PPS seconds between polls. If	 * zero, either the poll clock is slightly faster than the PPS	 * clock or the PPS clock has died. If the PPS clock advanced	 * once between polls, we make sure the fraction time difference	 * since the last sample is within the range gate of 5 ms (500	 * PPM). If the PPS clock advanced twice since the last poll,	 * the poll bracketed more than one second and the first second	 * was lost to a slip. Since the interval since the last sample	 * found is now two seconds, just widen the range gate. If the	 * PPS clock advanced three or more times, either the signal has	 * failed for a number of seconds or we have runts, in which	 * case just ignore them.	 *	 * If flag4 is lit, record each second offset to clockstats.	 * That's so we can make awesome Allan deviation plots.	 */	sec = ts.tv_sec - up->ts.tv_sec;	nsec = ts.tv_nsec - up->ts.tv_nsec;	up->ts = ts;	if (nsec < 0) {		sec --;		nsec += NANOSECOND;	} else if (nsec >= NANOSECOND) {		sec++;		nsec -= NANOSECOND;	}	if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)		return;	else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)		return;	pp->lastrec.l_ui = ts.tv_sec + JAN_1970;	dtemp = ts.tv_nsec * FRAC / 1e9;	if (dtemp >= FRAC)		pp->lastrec.l_ui++;	pp->lastrec.l_uf = (u_int32)dtemp;	if (ts.tv_nsec > NANOSECOND / 2)		ts.tv_nsec -= NANOSECOND;	dtemp = -(double)ts.tv_nsec / NANOSECOND;	SAMPLE(dtemp + pp->fudgetime1);	if (pp->sloppyclockflag & CLK_FLAG4){		sprintf(tbuf, "%.9f", dtemp);		record_clock_stats(&peer->srcadr, tbuf);	}#ifdef DEBUG	if (debug > 1)		printf("atom_timer: %lu %f %f\n", current_time,		    dtemp, pp->fudgetime1);#endif	return;}#endif /* HAVE_PPSAPI *//* * pps_sample - receive PPS data from some other clock driver * * This routine is called once per second when the external clock driver * processes PPS information. It processes the PPS timestamp and saves * the sign-extended fraction in a circular buffer for processing at the * next poll event. This works only for a single PPS device. * * The routine should be used by another configured driver ONLY when * this driver is configured as well and the PPSAPI is NOT in use. */intpps_sample(	   l_fp *offset		/* PPS offset */	   ){	register struct peer *peer;	struct refclockproc *pp;	l_fp lftmp;	double doffset;	peer = pps_peer;	if (peer == NULL)		return (1);	pp = peer->procptr;	/*	 * Convert the timeval to l_fp and save for billboards. Sign-	 * extend the fraction and stash in the buffer. No harm is done	 * if previous data are overwritten. If the discipline comes bum	 * or the data grow stale, just forget it.	 */ 	pp->lastrec = *offset;	L_CLR(&lftmp);	L_ADDF(&lftmp, pp->lastrec.l_f);	LFPTOD(&lftmp, doffset);	SAMPLE(-doffset + pp->fudgetime1);	return (0);}/* * atom_poll - called by the transmit procedure */static voidatom_poll(	int unit,		/* unit number (not used) */	struct peer *peer	/* peer structure pointer */	){	struct refclockproc *pp;	pp = peer->procptr;	pp->polls++;	/*	 * Valid time is returned only if the prefer peer has survived	 * the intersection algorithm and within 0.4 s of local time	 * and not too long ago. This ensures the PPS time is within	 * 0.5 s of the local time and the seconds numbering is	 * unambiguous. Note that the leap bits, stratum and refid are	 * set from the prefer peer, unless overriden by a fudge	 * command.	 */	if (pp->codeproc == pp->coderecv) {		refclock_report(peer, CEVNT_TIMEOUT);		return;	} else if (sys_prefer == NULL) {		pp->codeproc = pp->coderecv;		return;	} else if (fabs(sys_prefer->offset) >= 0.4) {		pp->codeproc = pp->coderecv;		return;	}	pp->leap = sys_prefer->leap;	if (pp->stratum >= STRATUM_UNSPEC)		peer->stratum = sys_prefer->stratum;	else		peer->stratum = pp->stratum;	pp->lastref = pp->lastrec;	refclock_receive(peer);}#elseint refclock_atom_bs;intpps_sample(	   l_fp *offset		/* PPS offset */	   ){	return (1);}#endif /* REFCLOCK */

⌨️ 快捷键说明

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