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