48
41
* Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
49
42
* available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
50
43
* present only the Alpha implementation provides the full generality of
51
* the API with multiple PPS drivers and multiple handles per driver.
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.
53
51
* In many configurations a single port is used for the radio timecode
54
52
* and PPS signal. In order to provide for this configuration and others
61
59
* configuration file.
63
61
* This driver normally uses the PLL/FLL clock discipline implemented in
64
* the ntpd code. If kernel support is available, the kernel PLL/FLL
65
* clock discipline is used instead. The default configuration is not to
66
* use the kernel PPS discipline, if present. The kernel PPS discipline
67
* can be enabled using the pps command.
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
71
* There are no special fudge factors other than the generic. The fudge
72
* time1 parameter can be used to compensate for miscellaneous device
73
* driver and OS delays.
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
76
78
* Interface definitions
107
108
static int atom_start P((int, struct peer *));
108
109
static void atom_poll P((int, struct peer *));
110
110
static void atom_shutdown P((int, struct peer *));
111
112
static void atom_control P((int, struct refclockstat *, struct
112
113
refclockstat *, struct peer *));
113
static int atom_pps P((struct peer *));
114
static int atom_ppsapi P((struct peer *, int, int));
114
static void atom_timer P((int, struct peer *));
115
static int atom_ppsapi P((struct peer *, int));
115
116
#endif /* HAVE_PPSAPI */
118
119
* Transfer vector
120
122
struct refclock refclock_atom = {
121
123
atom_start, /* start up driver */
123
124
atom_shutdown, /* shut down driver */
125
noentry, /* shut down driver */
126
#endif /* HAVE_PPSAPI */
127
125
atom_poll, /* transmit poll message */
129
126
atom_control, /* fudge control */
131
noentry, /* fudge control */
132
#endif /* HAVE_PPSAPI */
133
noentry, /* initialize driver */
134
noentry, /* not used (old atom_buginfo) */
127
noentry, /* initialize driver (not used) */
128
noentry, /* buginfo (not used) */
129
atom_timer, /* called once per second */
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) */
135
139
NOFLAGS /* not used */
141
#endif /* HAVE_PPPSAPI */
160
166
pp->clockdesc = DESCRIPTION;
161
167
pp->stratum = STRATUM_UNSPEC;
162
168
memcpy((char *)&pp->refid, REFID, 4);
163
peer->burst = ASTAGE;
164
169
#ifdef HAVE_PPSAPI
165
170
up = emalloc(sizeof(struct ppsunit));
166
171
memset(up, 0, sizeof(struct ppsunit));
167
172
pp->unitptr = (caddr_t)up;
170
* Open PPS device. If this fails and some driver has already
171
* opened the associated radio device, fdpps has the file
175
* Open PPS device. This can be any serial or parallel port and
176
* not necessarily the port used for the associated radio.
174
178
sprintf(device, DEVICE, unit);
175
179
up->fddev = open(device, O_RDWR, 0777);
176
if (up->fddev <= 0 && fdpps > 0) {
177
strcpy(device, pps_device);
180
180
if (up->fddev <= 0) {
182
182
"refclock_atom: %s: %m", device);
187
* Light off the PPSAPI interface. If this PPS device is shared
188
* with the radio device, take the default options from the pps
189
* command. This is for legacy purposes.
187
* Light off the PPSAPI interface.
191
189
if (time_pps_create(up->fddev, &up->handle) < 0) {
193
191
"refclock_atom: time_pps_create failed: %m");
196
return (atom_ppsapi(peer, 0, 0));
196
* If the mode is nonzero, use that for the time_pps_setparams()
197
* mode; otherwise, PPS_CAPTUREASSERT. Enable kernel PPS if
202
mode = PPS_CAPTUREASSERT;
203
return (atom_ppsapi(peer, mode));
197
204
#else /* HAVE_PPSAPI */
199
206
#endif /* HAVE_PPSAPI */
211
* atom_shutdown - shut down the clock
215
int unit, /* unit number (not used) */
216
struct peer *peer /* peer structure pointer */
219
struct refclockproc *pp;
220
register struct ppsunit *up;
223
up = (struct ppsunit *)pp->unitptr;
228
time_pps_destroy(up->handle);
229
#endif /* HAVE_PPSAPI */
230
if (pps_peer == peer)
203
236
#ifdef HAVE_PPSAPI
205
238
* atom_control - fudge control
215
248
struct refclockproc *pp;
217
251
pp = peer->procptr;
218
atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
219
pp->sloppyclockflag & CLK_FLAG3);
252
if (peer->ttl != 0) /* all legal modes must be nonzero */
255
if (pp->sloppyclockflag & CLK_FLAG2)
256
mode = PPS_CAPTURECLEAR;
258
mode = PPS_CAPTUREASSERT;
259
atom_ppsapi(peer, mode);
237
276
pp = peer->procptr;
238
277
up = (struct ppsunit *)pp->unitptr;
239
281
if (time_pps_getcap(up->handle, &capability) < 0) {
241
283
"refclock_atom: time_pps_getcap failed: %m");
244
286
memset(&up->pps_params, 0, sizeof(pps_params_t));
246
up->pps_params.mode = capability & PPS_CAPTURECLEAR;
248
up->pps_params.mode = capability & PPS_CAPTUREASSERT;
249
if (!up->pps_params.mode) {
251
"refclock_atom: invalid capture edge %d",
255
up->pps_params.mode |= PPS_TSFMT_TSPEC;
287
up->pps_params.api_version = PPS_API_VERS_1;
288
up->pps_params.mode = mode | PPS_TSFMT_TSPEC;
256
289
if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
258
291
"refclock_atom: time_pps_setparams failed: %m");
294
if (pp->sloppyclockflag & CLK_FLAG3) {
262
295
if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
263
296
up->pps_params.mode & ~PPS_TSFMT_TSPEC,
264
297
PPS_TSFMT_TSPEC) < 0) {
273
306
time_pps_getparams(up->handle, &up->pps_params);
275
"refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
308
"refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x\n",
276
309
up->fddev, capability, up->pps_params.api_version,
277
up->pps_params.mode, enb_hardpps);
310
up->pps_params.mode);
285
* atom_shutdown - shut down the clock
289
int unit, /* unit number (not used) */
290
struct peer *peer /* peer structure pointer */
293
struct refclockproc *pp;
294
register struct ppsunit *up;
297
up = (struct ppsunit *)pp->unitptr;
301
time_pps_destroy(up->handle);
302
if (pps_peer == peer)
309
* atom_pps - receive data from the PPSAPI interface
318
* atom_timer - called once per second
311
320
* This routine is called once per second when the PPSAPI interface is
312
321
* present. It snatches the PPS timestamp from the kernel and saves the
313
322
* sign-extended fraction in a circular buffer for processing at the
314
323
* next poll event.
327
int unit, /* unit number (not used) */
318
328
struct peer *peer /* peer structure pointer */
334
346
pp = peer->procptr;
335
347
up = (struct ppsunit *)pp->unitptr;
336
348
if (up->handle == 0)
338
351
timeout.tv_sec = 0;
339
352
timeout.tv_nsec = 0;
340
353
memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
341
354
if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
356
refclock_report(peer, CEVNT_FAULT);
344
359
if (up->pps_params.mode & PPS_CAPTUREASSERT) {
345
if (pps_info.assert_sequence ==
346
up->pps_info.assert_sequence)
348
360
ts = up->pps_info.assert_timestamp;
349
361
} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
350
if (pps_info.clear_sequence ==
351
up->pps_info.clear_sequence)
353
362
ts = up->pps_info.clear_timestamp;
357
if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
358
up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
359
(ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
360
up->ts.tv_nsec < RANGEGATE))) {
364
refclock_report(peer, CEVNT_FAULT);
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.
382
* If flag4 is lit, record each second offset to clockstats.
383
* That's so we can make awesome Allan deviation plots.
385
sec = ts.tv_sec - up->ts.tv_sec;
386
nsec = ts.tv_nsec - up->ts.tv_nsec;
391
} else if (nsec >= NANOSECOND) {
395
if (sec * NANOSECOND + nsec > NANOSECOND + RANGEGATE)
398
else if (sec * NANOSECOND + nsec < NANOSECOND - RANGEGATE)
365
401
pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
366
402
dtemp = ts.tv_nsec * FRAC / 1e9;
367
403
if (dtemp >= FRAC)
371
407
ts.tv_nsec -= NANOSECOND;
372
408
dtemp = -(double)ts.tv_nsec / NANOSECOND;
373
409
SAMPLE(dtemp + pp->fudgetime1);
410
if (pp->sloppyclockflag & CLK_FLAG4){
411
sprintf(tbuf, "%.9f", dtemp);
412
record_clock_stats(&peer->srcadr, tbuf);
376
printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
416
printf("atom_timer: %lu %f %f\n", current_time,
417
dtemp, pp->fudgetime1);
380
421
#endif /* HAVE_PPSAPI */
387
428
* processes PPS information. It processes the PPS timestamp and saves
388
429
* the sign-extended fraction in a circular buffer for processing at the
389
430
* next poll event. This works only for a single PPS device.
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.
433
475
struct refclockproc *pp;
436
#endif /* HAVE_PPSAPI */
439
* Accumulate samples in the median filter. If a noise sample,
440
* return with no prejudice; if a protocol error, get mean;
441
* otherwise, cool. At the end of each poll interval, do a
442
* little bookeeping and process the surviving samples.
444
476
pp = peer->procptr;
447
err = atom_pps(peer);
449
refclock_report(peer, CEVNT_FAULT);
452
#endif /* HAVE_PPSAPI */
455
480
* Valid time is returned only if the prefer peer has survived
456
* the intersection algorithm and within clock_max of local time
481
* the intersection algorithm and within 0.4 s of local time
457
482
* and not too long ago. This ensures the PPS time is within
458
* +-0.5 s of the local time and the seconds numbering is
459
* unambiguous. Note that the leap bits are set no-warning on
460
* the first valid update and the stratum is set at the prefer
461
* peer, unless overriden by a fudge command.
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
465
peer->leap = LEAP_NOTINSYNC;
466
488
if (pp->codeproc == pp->coderecv) {
467
489
refclock_report(peer, CEVNT_TIMEOUT);
468
peer->burst = ASTAGE;
471
492
} else if (sys_prefer == NULL) {
472
493
pp->codeproc = pp->coderecv;
473
peer->burst = ASTAGE;
476
} else if (fabs(sys_prefer->offset) > clock_max) {
496
} else if (fabs(sys_prefer->offset) >= 0.4) {
477
497
pp->codeproc = pp->coderecv;
478
peer->burst = ASTAGE;
481
pp->leap = LEAP_NOWARNING;
500
pp->leap = sys_prefer->leap;
482
501
if (pp->stratum >= STRATUM_UNSPEC)
483
502
peer->stratum = sys_prefer->stratum;
485
504
peer->stratum = pp->stratum;
486
if (peer->stratum == STRATUM_REFCLOCK || peer->stratum ==
488
peer->refid = pp->refid;
490
peer->refid = addr2refid(&sys_prefer->srcadr);
491
505
pp->lastref = pp->lastrec;
492
506
refclock_receive(peer);
493
peer->burst = ASTAGE;
496
509
int refclock_atom_bs;