/contrib/ntp/ntpd/refclock_atom.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 517 lines · 316 code · 44 blank · 157 comment · 47 complexity · 8491e98e2304168d2d09f768b7692264 MD5 · raw file

  1. /*
  2. * refclock_atom - clock driver for 1-pps signals
  3. */
  4. #ifdef HAVE_CONFIG_H
  5. #include <config.h>
  6. #endif
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include "ntpd.h"
  10. #include "ntp_io.h"
  11. #include "ntp_unixtime.h"
  12. #include "ntp_refclock.h"
  13. #include "ntp_stdlib.h"
  14. #if defined(REFCLOCK) && defined(CLOCK_ATOM)
  15. #ifdef HAVE_PPSAPI
  16. # include "ppsapi_timepps.h"
  17. #endif /* HAVE_PPSAPI */
  18. /*
  19. * This driver furnishes an interface for pulse-per-second (PPS) signals
  20. * produced by a cesium clock, timing receiver or related equipment. It
  21. * can be used to remove accumulated jitter and retime a secondary
  22. * server when synchronized to a primary server over a congested, wide-
  23. * area network and before redistributing the time to local clients.
  24. *
  25. * Before this driver becomes active, the local clock must be set to
  26. * within +-500 ms by another means, such as a radio clock or NTP
  27. * itself. There are two ways to connect the PPS signal, normally at TTL
  28. * levels, to the computer. One is to shift to EIA levels and connect to
  29. * pin 8 (DCD) of a serial port. This requires a level converter and
  30. * may require a one-shot flipflop to lengthen the pulse. The other is
  31. * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
  32. * port. These methods are architecture dependent.
  33. *
  34. * Both methods require a modified device driver and kernel interface
  35. * compatible with the Pulse-per-Second API for Unix-like Operating
  36. * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
  37. * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
  38. * present only the Alpha implementation provides the full generality of
  39. * the API with multiple PPS drivers and multiple handles per driver. If
  40. * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h
  41. * header file and kernel support specific to each operating system.
  42. * However, this driver can operate without this interface if means are
  43. * proviced to call the pps_sample() routine from another driver. Please
  44. * note; if the PPSAPI interface is present, it must be used.
  45. *
  46. * In many configurations a single port is used for the radio timecode
  47. * and PPS signal. In order to provide for this configuration and others
  48. * involving dedicated multiple serial/parallel ports, the driver first
  49. * attempts to open the device /dev/pps%d, where %d is the unit number.
  50. * If this fails, the driver attempts to open the device specified by
  51. * the pps configuration command. If a port is to be shared, the pps
  52. * command must be placed before the radio device(s) and the radio
  53. * device(s) must be placed before the PPS driver(s) in the
  54. * configuration file.
  55. *
  56. * This driver normally uses the PLL/FLL clock discipline implemented in
  57. * the ntpd code. Ordinarily, this is the most accurate means, as the
  58. * median filter in the driver interface is much larger than in the
  59. * kernel. However, if the systemic clock frequency error is large (tens
  60. * to hundreds of PPM), it's better to used the kernel support, if
  61. * available.
  62. *
  63. * Fudge Factors
  64. *
  65. * If flag2 is dim (default), the on-time epoch is the assert edge of
  66. * the PPS signal; if lit, the on-time epoch is the clear edge. If flag2
  67. * is lit, the assert edge is used; if flag3 is dim (default), the
  68. * kernel PPS support is disabled; if lit it is enabled. The time1
  69. * parameter can be used to compensate for miscellaneous device driver
  70. * and OS delays.
  71. */
  72. /*
  73. * Interface definitions
  74. */
  75. #ifdef HAVE_PPSAPI
  76. #define DEVICE "/dev/pps%d" /* device name and unit */
  77. #endif /* HAVE_PPSAPI */
  78. #define PRECISION (-20) /* precision assumed (about 1 us) */
  79. #define REFID "PPS\0" /* reference ID */
  80. #define DESCRIPTION "PPS Clock Discipline" /* WRU */
  81. #define NANOSECOND 1000000000 /* one second (ns) */
  82. #define RANGEGATE 500000 /* range gate (ns) */
  83. static struct peer *pps_peer; /* atom driver for PPS sources */
  84. #ifdef HAVE_PPSAPI
  85. /*
  86. * PPS unit control structure
  87. */
  88. struct ppsunit {
  89. struct timespec ts; /* last timestamp */
  90. int fddev; /* pps device descriptor */
  91. pps_params_t pps_params; /* pps parameters */
  92. pps_info_t pps_info; /* last pps data */
  93. pps_handle_t handle; /* pps handlebars */
  94. };
  95. #endif /* HAVE_PPSAPI */
  96. /*
  97. * Function prototypes
  98. */
  99. static int atom_start P((int, struct peer *));
  100. static void atom_poll P((int, struct peer *));
  101. static void atom_shutdown P((int, struct peer *));
  102. #ifdef HAVE_PPSAPI
  103. static void atom_control P((int, struct refclockstat *, struct
  104. refclockstat *, struct peer *));
  105. static void atom_timer P((int, struct peer *));
  106. static int atom_ppsapi P((struct peer *, int));
  107. #endif /* HAVE_PPSAPI */
  108. /*
  109. * Transfer vector
  110. */
  111. #ifdef HAVE_PPSAPI
  112. struct refclock refclock_atom = {
  113. atom_start, /* start up driver */
  114. atom_shutdown, /* shut down driver */
  115. atom_poll, /* transmit poll message */
  116. atom_control, /* fudge control */
  117. noentry, /* initialize driver (not used) */
  118. noentry, /* buginfo (not used) */
  119. atom_timer, /* called once per second */
  120. };
  121. #else /* HAVE_PPSAPI */
  122. struct refclock refclock_atom = {
  123. atom_start, /* start up driver */
  124. atom_shutdown, /* shut down driver */
  125. atom_poll, /* transmit poll message */
  126. noentry, /* fudge control (not used) */
  127. noentry, /* initialize driver (not used) */
  128. noentry, /* buginfo (not used) */
  129. NOFLAGS /* not used */
  130. };
  131. #endif /* HAVE_PPPSAPI */
  132. /*
  133. * atom_start - initialize data for processing
  134. */
  135. static int
  136. atom_start(
  137. int unit, /* unit number (not used) */
  138. struct peer *peer /* peer structure pointer */
  139. )
  140. {
  141. struct refclockproc *pp;
  142. #ifdef HAVE_PPSAPI
  143. register struct ppsunit *up;
  144. char device[80];
  145. int mode;
  146. #endif /* HAVE_PPSAPI */
  147. /*
  148. * Allocate and initialize unit structure
  149. */
  150. pps_peer = peer;
  151. pp = peer->procptr;
  152. peer->precision = PRECISION;
  153. pp->clockdesc = DESCRIPTION;
  154. pp->stratum = STRATUM_UNSPEC;
  155. memcpy((char *)&pp->refid, REFID, 4);
  156. #ifdef HAVE_PPSAPI
  157. up = emalloc(sizeof(struct ppsunit));
  158. memset(up, 0, sizeof(struct ppsunit));
  159. pp->unitptr = (caddr_t)up;
  160. /*
  161. * Open PPS device. This can be any serial or parallel port and
  162. * not necessarily the port used for the associated radio.
  163. */
  164. sprintf(device, DEVICE, unit);
  165. up->fddev = open(device, O_RDWR, 0777);
  166. if (up->fddev <= 0) {
  167. msyslog(LOG_ERR,
  168. "refclock_atom: %s: %m", device);
  169. return (0);
  170. }
  171. /*
  172. * Light off the PPSAPI interface.
  173. */
  174. if (time_pps_create(up->fddev, &up->handle) < 0) {
  175. msyslog(LOG_ERR,
  176. "refclock_atom: time_pps_create failed: %m");
  177. return (0);
  178. }
  179. /*
  180. * If the mode is nonzero, use that for the time_pps_setparams()
  181. * mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
  182. * flag3 is lit.
  183. */
  184. mode = peer->ttl;
  185. if (mode == 0)
  186. mode = PPS_CAPTUREASSERT;
  187. return (atom_ppsapi(peer, mode));
  188. #else /* HAVE_PPSAPI */
  189. return (1);
  190. #endif /* HAVE_PPSAPI */
  191. }
  192. /*
  193. * atom_shutdown - shut down the clock
  194. */
  195. static void
  196. atom_shutdown(
  197. int unit, /* unit number (not used) */
  198. struct peer *peer /* peer structure pointer */
  199. )
  200. {
  201. struct refclockproc *pp;
  202. register struct ppsunit *up;
  203. pp = peer->procptr;
  204. up = (struct ppsunit *)pp->unitptr;
  205. #ifdef HAVE_PPSAPI
  206. if (up->fddev > 0)
  207. close(up->fddev);
  208. if (up->handle != 0)
  209. time_pps_destroy(up->handle);
  210. #endif /* HAVE_PPSAPI */
  211. if (pps_peer == peer)
  212. pps_peer = NULL;
  213. free(up);
  214. }
  215. #ifdef HAVE_PPSAPI
  216. /*
  217. * atom_control - fudge control
  218. */
  219. static void
  220. atom_control(
  221. int unit, /* unit (not used */
  222. struct refclockstat *in, /* input parameters (not uded) */
  223. struct refclockstat *out, /* output parameters (not used) */
  224. struct peer *peer /* peer structure pointer */
  225. )
  226. {
  227. struct refclockproc *pp;
  228. int mode;
  229. pp = peer->procptr;
  230. if (peer->ttl != 0) /* all legal modes must be nonzero */
  231. return;
  232. if (pp->sloppyclockflag & CLK_FLAG2)
  233. mode = PPS_CAPTURECLEAR;
  234. else
  235. mode = PPS_CAPTUREASSERT;
  236. atom_ppsapi(peer, mode);
  237. }
  238. /*
  239. * Initialize PPSAPI
  240. */
  241. int
  242. atom_ppsapi(
  243. struct peer *peer, /* peer structure pointer */
  244. int mode /* mode */
  245. )
  246. {
  247. struct refclockproc *pp;
  248. register struct ppsunit *up;
  249. int capability;
  250. pp = peer->procptr;
  251. up = (struct ppsunit *)pp->unitptr;
  252. if (up->handle == 0)
  253. return (0);
  254. if (time_pps_getcap(up->handle, &capability) < 0) {
  255. msyslog(LOG_ERR,
  256. "refclock_atom: time_pps_getcap failed: %m");
  257. return (0);
  258. }
  259. memset(&up->pps_params, 0, sizeof(pps_params_t));
  260. up->pps_params.api_version = PPS_API_VERS_1;
  261. up->pps_params.mode = mode | PPS_TSFMT_TSPEC;
  262. if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
  263. msyslog(LOG_ERR,
  264. "refclock_atom: time_pps_setparams failed: %m");
  265. return (0);
  266. }
  267. if (pp->sloppyclockflag & CLK_FLAG3) {
  268. if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
  269. up->pps_params.mode & ~PPS_TSFMT_TSPEC,
  270. PPS_TSFMT_TSPEC) < 0) {
  271. msyslog(LOG_ERR,
  272. "refclock_atom: time_pps_kcbind failed: %m");
  273. return (0);
  274. }
  275. pps_enable = 1;
  276. }
  277. #if DEBUG
  278. if (debug) {
  279. time_pps_getparams(up->handle, &up->pps_params);
  280. printf(
  281. "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",
  282. up->fddev, capability, up->pps_params.api_version,
  283. up->pps_params.mode);
  284. }
  285. #endif
  286. return (1);
  287. }
  288. /*
  289. * atom_timer - called once per second
  290. *
  291. * This routine is called once per second when the PPSAPI interface is
  292. * present. It snatches the PPS timestamp from the kernel and saves the
  293. * sign-extended fraction in a circular buffer for processing at the
  294. * next poll event.
  295. */
  296. static void
  297. atom_timer(
  298. int unit, /* unit number (not used) */
  299. struct peer *peer /* peer structure pointer */
  300. )
  301. {
  302. register struct ppsunit *up;
  303. struct refclockproc *pp;
  304. pps_info_t pps_info;
  305. struct timespec timeout, ts;
  306. long sec, nsec;
  307. double dtemp;
  308. char tbuf[80]; /* monitor buffer */
  309. /*
  310. * Convert the timespec nanoseconds field to signed double and
  311. * save in the median filter. for billboards. No harm is done if
  312. * previous data are overwritten. If the discipline comes bum or
  313. * the data grow stale, just forget it. A range gate rejects new
  314. * samples if less than a jiggle time from the next second.
  315. */
  316. pp = peer->procptr;
  317. up = (struct ppsunit *)pp->unitptr;
  318. if (up->handle == 0)
  319. return;
  320. timeout.tv_sec = 0;
  321. timeout.tv_nsec = 0;
  322. memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
  323. if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
  324. &timeout) < 0) {
  325. refclock_report(peer, CEVNT_FAULT);
  326. return;
  327. }
  328. if (up->pps_params.mode & PPS_CAPTUREASSERT) {
  329. ts = up->pps_info.assert_timestamp;
  330. } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
  331. ts = up->pps_info.clear_timestamp;
  332. } else {
  333. refclock_report(peer, CEVNT_FAULT);
  334. return;
  335. }
  336. /*
  337. * There can be zero, one or two PPS seconds between polls. If
  338. * zero, either the poll clock is slightly faster than the PPS
  339. * clock or the PPS clock has died. If the PPS clock advanced
  340. * once between polls, we make sure the fraction time difference
  341. * since the last sample is within the range gate of 5 ms (500
  342. * PPM). If the PPS clock advanced twice since the last poll,
  343. * the poll bracketed more than one second and the first second
  344. * was lost to a slip. Since the interval since the last sample
  345. * found is now two seconds, just widen the range gate. If the
  346. * PPS clock advanced three or more times, either the signal has
  347. * failed for a number of seconds or we have runts, in which
  348. * case just ignore them.
  349. *
  350. * If flag4 is lit, record each second offset to clockstats.
  351. * That's so we can make awesome Allan deviation plots.
  352. */
  353. sec = ts.tv_sec - up->ts.tv_sec;
  354. nsec = ts.tv_nsec - up->ts.tv_nsec;
  355. up->ts = ts;
  356. if (nsec < 0) {
  357. sec --;
  358. nsec += NANOSECOND;
  359. } else if (nsec >= NANOSECOND) {
  360. sec++;
  361. nsec -= NANOSECOND;
  362. }
  363. if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)
  364. return;
  365. else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)
  366. return;
  367. pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
  368. dtemp = ts.tv_nsec * FRAC / 1e9;
  369. if (dtemp >= FRAC)
  370. pp->lastrec.l_ui++;
  371. pp->lastrec.l_uf = (u_int32)dtemp;
  372. if (ts.tv_nsec > NANOSECOND / 2)
  373. ts.tv_nsec -= NANOSECOND;
  374. dtemp = -(double)ts.tv_nsec / NANOSECOND;
  375. SAMPLE(dtemp + pp->fudgetime1);
  376. if (pp->sloppyclockflag & CLK_FLAG4){
  377. sprintf(tbuf, "%.9f", dtemp);
  378. record_clock_stats(&peer->srcadr, tbuf);
  379. }
  380. #ifdef DEBUG
  381. if (debug > 1)
  382. printf("atom_timer: %lu %f %f\n", current_time,
  383. dtemp, pp->fudgetime1);
  384. #endif
  385. return;
  386. }
  387. #endif /* HAVE_PPSAPI */
  388. /*
  389. * pps_sample - receive PPS data from some other clock driver
  390. *
  391. * This routine is called once per second when the external clock driver
  392. * processes PPS information. It processes the PPS timestamp and saves
  393. * the sign-extended fraction in a circular buffer for processing at the
  394. * next poll event. This works only for a single PPS device.
  395. *
  396. * The routine should be used by another configured driver ONLY when
  397. * this driver is configured as well and the PPSAPI is NOT in use.
  398. */
  399. int
  400. pps_sample(
  401. l_fp *offset /* PPS offset */
  402. )
  403. {
  404. register struct peer *peer;
  405. struct refclockproc *pp;
  406. l_fp lftmp;
  407. double doffset;
  408. peer = pps_peer;
  409. if (peer == NULL)
  410. return (1);
  411. pp = peer->procptr;
  412. /*
  413. * Convert the timeval to l_fp and save for billboards. Sign-
  414. * extend the fraction and stash in the buffer. No harm is done
  415. * if previous data are overwritten. If the discipline comes bum
  416. * or the data grow stale, just forget it.
  417. */
  418. pp->lastrec = *offset;
  419. L_CLR(&lftmp);
  420. L_ADDF(&lftmp, pp->lastrec.l_f);
  421. LFPTOD(&lftmp, doffset);
  422. SAMPLE(-doffset + pp->fudgetime1);
  423. return (0);
  424. }
  425. /*
  426. * atom_poll - called by the transmit procedure
  427. */
  428. static void
  429. atom_poll(
  430. int unit, /* unit number (not used) */
  431. struct peer *peer /* peer structure pointer */
  432. )
  433. {
  434. struct refclockproc *pp;
  435. pp = peer->procptr;
  436. pp->polls++;
  437. /*
  438. * Valid time is returned only if the prefer peer has survived
  439. * the intersection algorithm and within 0.4 s of local time
  440. * and not too long ago. This ensures the PPS time is within
  441. * 0.5 s of the local time and the seconds numbering is
  442. * unambiguous. Note that the leap bits, stratum and refid are
  443. * set from the prefer peer, unless overriden by a fudge
  444. * command.
  445. */
  446. if (pp->codeproc == pp->coderecv) {
  447. refclock_report(peer, CEVNT_TIMEOUT);
  448. return;
  449. } else if (sys_prefer == NULL) {
  450. pp->codeproc = pp->coderecv;
  451. return;
  452. } else if (fabs(sys_prefer->offset) >= 0.4) {
  453. pp->codeproc = pp->coderecv;
  454. return;
  455. }
  456. pp->leap = sys_prefer->leap;
  457. if (pp->stratum >= STRATUM_UNSPEC)
  458. peer->stratum = sys_prefer->stratum;
  459. else
  460. peer->stratum = pp->stratum;
  461. pp->lastref = pp->lastrec;
  462. refclock_receive(peer);
  463. }
  464. #else
  465. int refclock_atom_bs;
  466. int
  467. pps_sample(
  468. l_fp *offset /* PPS offset */
  469. )
  470. {
  471. return (1);
  472. }
  473. #endif /* REFCLOCK */