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