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 # ifdef HAVE_TIMEPPS_H 21 # include <timepps.h> 22 # else 23 # ifdef HAVE_SYS_TIMEPPS_H 24 # include <sys/timepps.h> 25 # endif 26 # endif 27 #endif /* HAVE_PPSAPI */ 28 29 /* 30 * This driver furnishes an interface for pulse-per-second (PPS) signals 31 * produced by a cesium clock, timing receiver or related equipment. It 32 * can be used to remove accumulated jitter and retime a secondary 33 * server when synchronized to a primary server over a congested, wide- 34 * area network and before redistributing the time to local clients. 35 * 36 * Before this driver becomes active, the local clock must be set to 37 * within +-500 ms by another means, such as a radio clock or NTP 38 * itself. There are two ways to connect the PPS signal, normally at TTL 39 * levels, to the computer. One is to shift to EIA levels and connect to 40 * pin 8 (DCD) of a serial port. This requires a level converter and 41 * may require a one-shot flipflop to lengthen the pulse. The other is 42 * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell 43 * port. These methods are architecture dependent. 44 * 45 * Both methods require a modified device driver and kernel interface 46 * compatible with the Pulse-per-Second API for Unix-like Operating 47 * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are 48 * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at 49 * present only the Alpha implementation provides the full generality of 50 * the API with multiple PPS drivers and multiple handles per driver. 51 * 52 * In many configurations a single port is used for the radio timecode 53 * and PPS signal. In order to provide for this configuration and others 54 * involving dedicated multiple serial/parallel ports, the driver first 55 * attempts to open the device /dev/pps%d, where %d is the unit number. 56 * If this fails, the driver attempts to open the device specified by 57 * the pps configuration command. If a port is to be shared, the pps 58 * command must be placed before the radio device(s) and the radio 59 * device(s) must be placed before the PPS driver(s) in the 60 * configuration file. 61 * 62 * This driver normally uses the PLL/FLL clock discipline implemented in 63 * the ntpd code. If kernel support is available, the kernel PLL/FLL 64 * clock discipline is used instead. The default configuration is not to 65 * use the kernel PPS discipline, if present. The kernel PPS discipline 66 * can be enabled using the pps command. 67 * 68 * Fudge Factors 69 * 70 * There are no special fudge factors other than the generic. The fudge 71 * time1 parameter can be used to compensate for miscellaneous device 72 * driver and OS delays. 73 */ 74 /* 75 * Interface definitions 76 */ 77 #ifdef HAVE_PPSAPI 78 extern int pps_assert; /* selects rising or falling edge */ 79 extern int pps_hardpps; /* enables the kernel PPS interface */ 80 #define DEVICE "/dev/pps%d" /* device name and unit */ 81 #endif /* HAVE_PPSAPI */ 82 83 #define PRECISION (-20) /* precision assumed (about 1 us) */ 84 #define REFID "PPS\0" /* reference ID */ 85 #define DESCRIPTION "PPS Clock Discipline" /* WRU */ 86 #define NANOSECOND 1000000000 /* one second (ns) */ 87 #define RANGEGATE 500000 /* range gate (ns) */ 88 #define ASTAGE 8 /* filter stages */ 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 #ifdef HAVE_PPSAPI 111 static void atom_shutdown P((int, struct peer *)); 112 static void atom_control P((int, struct refclockstat *, struct 113 refclockstat *, struct peer *)); 114 static int atom_pps P((struct peer *)); 115 static int atom_ppsapi P((struct peer *, int, int)); 116 #endif /* HAVE_PPSAPI */ 117 118 /* 119 * Transfer vector 120 */ 121 struct refclock refclock_atom = { 122 atom_start, /* start up driver */ 123 #ifdef HAVE_PPSAPI 124 atom_shutdown, /* shut down driver */ 125 #else 126 noentry, /* shut down driver */ 127 #endif /* HAVE_PPSAPI */ 128 atom_poll, /* transmit poll message */ 129 #ifdef HAVE_PPSAPI 130 atom_control, /* fudge control */ 131 #else 132 noentry, /* fudge control */ 133 #endif /* HAVE_PPSAPI */ 134 noentry, /* initialize driver */ 135 noentry, /* not used (old atom_buginfo) */ 136 NOFLAGS /* not used */ 137 }; 138 139 140 /* 141 * atom_start - initialize data for processing 142 */ 143 static int 144 atom_start( 145 int unit, /* unit number (not used) */ 146 struct peer *peer /* peer structure pointer */ 147 ) 148 { 149 struct refclockproc *pp; 150 #ifdef HAVE_PPSAPI 151 register struct ppsunit *up; 152 char device[80]; 153 #endif /* HAVE_PPSAPI */ 154 155 /* 156 * Allocate and initialize unit structure 157 */ 158 pps_peer = peer; 159 pp = peer->procptr; 160 peer->precision = PRECISION; 161 pp->clockdesc = DESCRIPTION; 162 memcpy((char *)&pp->refid, REFID, 4); 163 peer->burst = ASTAGE; 164 peer->stratum = STRATUM_UNSPEC; 165 #ifdef HAVE_PPSAPI 166 up = emalloc(sizeof(struct ppsunit)); 167 memset(up, 0, sizeof(struct ppsunit)); 168 pp->unitptr = (caddr_t)up; 169 170 /* 171 * Open PPS device. If this fails and some driver has already 172 * opened the associated radio device, fdpps has the file 173 * descriptor for it. 174 */ 175 sprintf(device, DEVICE, unit); 176 up->fddev = open(device, O_RDWR, 0777); 177 if (up->fddev <= 0 && fdpps > 0) { 178 strcpy(device, pps_device); 179 up->fddev = fdpps; 180 } 181 if (up->fddev <= 0) { 182 msyslog(LOG_ERR, 183 "refclock_atom: %s: %m", device); 184 return (0); 185 } 186 187 /* 188 * Light off the PPSAPI interface. If this PPS device is shared 189 * with the radio device, take the default options from the pps 190 * command. This is for legacy purposes. 191 */ 192 if (time_pps_create(up->fddev, &up->handle) < 0) { 193 msyslog(LOG_ERR, 194 "refclock_atom: time_pps_create failed: %m"); 195 return (0); 196 } 197 return (atom_ppsapi(peer, pps_assert, pps_hardpps)); 198 #else /* HAVE_PPSAPI */ 199 return (1); 200 #endif /* HAVE_PPSAPI */ 201 } 202 203 204 #ifdef HAVE_PPSAPI 205 /* 206 * atom_control - fudge control 207 */ 208 static void 209 atom_control( 210 int unit, /* unit (not used */ 211 struct refclockstat *in, /* input parameters (not uded) */ 212 struct refclockstat *out, /* output parameters (not used) */ 213 struct peer *peer /* peer structure pointer */ 214 ) 215 { 216 struct refclockproc *pp; 217 218 pp = peer->procptr; 219 atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2, 220 pp->sloppyclockflag & CLK_FLAG3); 221 } 222 223 224 /* 225 * Initialize PPSAPI 226 */ 227 int 228 atom_ppsapi( 229 struct peer *peer, /* peer structure pointer */ 230 int enb_clear, /* clear enable */ 231 int enb_hardpps /* hardpps enable */ 232 ) 233 { 234 struct refclockproc *pp; 235 register struct ppsunit *up; 236 int capability; 237 238 pp = peer->procptr; 239 up = (struct ppsunit *)pp->unitptr; 240 if (time_pps_getcap(up->handle, &capability) < 0) { 241 msyslog(LOG_ERR, 242 "refclock_atom: time_pps_getcap failed: %m"); 243 return (0); 244 } 245 memset(&up->pps_params, 0, sizeof(pps_params_t)); 246 if (enb_clear) 247 up->pps_params.mode = capability & PPS_CAPTURECLEAR; 248 else 249 up->pps_params.mode = capability & PPS_CAPTUREASSERT; 250 if (!up->pps_params.mode) { 251 msyslog(LOG_ERR, 252 "refclock_atom: invalid capture edge %d", 253 pps_assert); 254 return (0); 255 } 256 up->pps_params.mode |= PPS_TSFMT_TSPEC; 257 if (time_pps_setparams(up->handle, &up->pps_params) < 0) { 258 msyslog(LOG_ERR, 259 "refclock_atom: time_pps_setparams failed: %m"); 260 return (0); 261 } 262 if (enb_hardpps) { 263 if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS, 264 up->pps_params.mode & ~PPS_TSFMT_TSPEC, 265 PPS_TSFMT_TSPEC) < 0) { 266 msyslog(LOG_ERR, 267 "refclock_atom: time_pps_kcbind failed: %m"); 268 return (0); 269 } 270 pps_enable = 1; 271 } 272 #if DEBUG 273 if (debug) { 274 time_pps_getparams(up->handle, &up->pps_params); 275 printf( 276 "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n", 277 up->fddev, capability, up->pps_params.api_version, 278 up->pps_params.mode, enb_hardpps); 279 } 280 #endif 281 return (1); 282 } 283 284 285 /* 286 * atom_shutdown - shut down the clock 287 */ 288 static void 289 atom_shutdown( 290 int unit, /* unit number (not used) */ 291 struct peer *peer /* peer structure pointer */ 292 ) 293 { 294 struct refclockproc *pp; 295 register struct ppsunit *up; 296 297 pp = peer->procptr; 298 up = (struct ppsunit *)pp->unitptr; 299 if (up->fddev > 0) 300 close(up->fddev); 301 if (up->handle != 0) 302 time_pps_destroy(up->handle); 303 if (pps_peer == peer) 304 pps_peer = 0; 305 free(up); 306 } 307 308 309 /* 310 * atom_pps - receive data from the PPSAPI interface 311 * 312 * This routine is called once per second when the PPSAPI interface is 313 * present. It snatches the PPS timestamp from the kernel and saves the 314 * sign-extended fraction in a circular buffer for processing at the 315 * next poll event. 316 */ 317 static int 318 atom_pps( 319 struct peer *peer /* peer structure pointer */ 320 ) 321 { 322 register struct ppsunit *up; 323 struct refclockproc *pp; 324 pps_info_t pps_info; 325 struct timespec timeout, ts; 326 double dtemp; 327 328 /* 329 * Convert the timespec nanoseconds field to signed double and 330 * save in the median filter. for billboards. No harm is done if 331 * previous data are overwritten. If the discipline comes bum or 332 * the data grow stale, just forget it. A range gate rejects new 333 * samples if less than a jiggle time from the next second. 334 */ 335 pp = peer->procptr; 336 up = (struct ppsunit *)pp->unitptr; 337 if (up->handle == 0) 338 return (-1); 339 timeout.tv_sec = 0; 340 timeout.tv_nsec = 0; 341 memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t)); 342 if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info, 343 &timeout) < 0) 344 return (-1); 345 if (up->pps_params.mode & PPS_CAPTUREASSERT) { 346 if (pps_info.assert_sequence == 347 up->pps_info.assert_sequence) 348 return (1); 349 ts = up->pps_info.assert_timestamp; 350 } else if (up->pps_params.mode & PPS_CAPTURECLEAR) { 351 if (pps_info.clear_sequence == 352 up->pps_info.clear_sequence) 353 return (1); 354 ts = up->pps_info.clear_timestamp; 355 } else { 356 return (-1); 357 } 358 if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec - 359 up->ts.tv_nsec > NANOSECOND - RANGEGATE) || 360 (ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec - 361 up->ts.tv_nsec < RANGEGATE))) { 362 up->ts = ts; 363 return (1); 364 } 365 up->ts = ts; 366 pp->lastrec.l_ui = ts.tv_sec + JAN_1970; 367 dtemp = ts.tv_nsec * FRAC / 1e9; 368 if (dtemp >= FRAC) 369 pp->lastrec.l_ui++; 370 pp->lastrec.l_uf = (u_int32)dtemp; 371 if (ts.tv_nsec > NANOSECOND / 2) 372 ts.tv_nsec -= NANOSECOND; 373 dtemp = -(double)ts.tv_nsec / NANOSECOND; 374 SAMPLE(dtemp + pp->fudgetime1); 375 #ifdef DEBUG 376 if (debug > 1) 377 printf("atom_pps %f %f\n", dtemp, pp->fudgetime1); 378 #endif 379 return (0); 380 } 381 #endif /* HAVE_PPSAPI */ 382 383 384 /* 385 * pps_sample - receive PPS data from some other clock driver 386 * 387 * This routine is called once per second when the external clock driver 388 * processes PPS information. It processes the PPS timestamp and saves 389 * the sign-extended fraction in a circular buffer for processing at the 390 * next poll event. This works only for a single PPS device. 391 */ 392 int 393 pps_sample( 394 l_fp *offset /* PPS offset */ 395 ) 396 { 397 register struct peer *peer; 398 struct refclockproc *pp; 399 l_fp lftmp; 400 double doffset; 401 402 peer = pps_peer; 403 if (peer == 0) /* nobody home */ 404 return (1); 405 pp = peer->procptr; 406 407 /* 408 * Convert the timeval to l_fp and save for billboards. Sign- 409 * extend the fraction and stash in the buffer. No harm is done 410 * if previous data are overwritten. If the discipline comes bum 411 * or the data grow stale, just forget it. 412 */ 413 pp->lastrec = *offset; 414 L_CLR(&lftmp); 415 L_ADDF(&lftmp, pp->lastrec.l_f); 416 LFPTOD(&lftmp, doffset); 417 SAMPLE(-doffset + pp->fudgetime1); 418 return (0); 419 } 420 421 /* 422 * atom_poll - called by the transmit procedure 423 * 424 * This routine is called once per second when in burst mode to save PPS 425 * sample offsets in the median filter. At the end of the burst period 426 * the samples are processed as a heap and the clock filter updated. 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 #ifdef HAVE_PPSAPI 436 int err; 437 #endif /* HAVE_PPSAPI */ 438 439 /* 440 * Accumulate samples in the median filter. If a noise sample, 441 * return with no prejudice; if a protocol error, get mean; 442 * otherwise, cool. At the end of each poll interval, do a 443 * little bookeeping and process the surviving samples. 444 */ 445 pp = peer->procptr; 446 pp->polls++; 447 #ifdef HAVE_PPSAPI 448 err = atom_pps(peer); 449 if (err < 0) { 450 refclock_report(peer, CEVNT_FAULT); 451 return; 452 } 453 #endif /* HAVE_PPSAPI */ 454 455 /* 456 * Valid time is returned only if the prefer peer has survived 457 * the intersection algorithm and within clock_max of local time 458 * and not too long ago. This ensures the PPS time is within 459 * +-0.5 s of the local time and the seconds numbering is 460 * unambiguous. Note that the leap bits are set no-warning on 461 * the first valid update and the stratum is set at the prefer 462 * peer. 463 */ 464 if (peer->burst > 0) 465 return; 466 peer->stratum = STRATUM_UNSPEC; 467 if (pp->codeproc == pp->coderecv) { 468 refclock_report(peer, CEVNT_TIMEOUT); 469 peer->burst = ASTAGE; 470 return; 471 472 } else if (!sys_prefer) { 473 pp->codeproc = pp->coderecv; 474 peer->burst = ASTAGE; 475 return; 476 477 } else if (fabs(sys_prefer->offset) > clock_max) { 478 pp->codeproc = pp->coderecv; 479 peer->burst = ASTAGE; 480 return; 481 } 482 peer->stratum = sys_prefer->stratum; 483 if (peer->stratum <= 1) 484 peer->refid = pp->refid; 485 else 486 peer->refid = peer->srcadr.sin_addr.s_addr; 487 pp->leap = LEAP_NOWARNING; 488 refclock_receive(peer); 489 peer->burst = ASTAGE; 490 } 491 #else 492 int refclock_atom_bs; 493 int 494 pps_sample( 495 l_fp *offset /* PPS offset */ 496 ) 497 { 498 return 1; 499 } 500 #endif /* REFCLOCK */ 501