1 /* 2 ** refclock_datum - clock driver for the Datum Programmable Time Server 3 ** 4 ** Important note: This driver assumes that you have termios. If you have 5 ** a system that does not have termios, you will have to modify this driver. 6 ** 7 ** Sorry, I have only tested this driver on SUN and HP platforms. 8 */ 9 10 #ifdef HAVE_CONFIG_H 11 # include <config.h> 12 #endif 13 14 #if defined(REFCLOCK) && defined(CLOCK_DATUM) 15 16 /* 17 ** Include Files 18 */ 19 20 #include "ntpd.h" 21 #include "ntp_io.h" 22 #include "ntp_refclock.h" 23 #include "ntp_unixtime.h" 24 #include "ntp_stdlib.h" 25 26 #include <stdio.h> 27 #include <ctype.h> 28 29 #if defined(HAVE_BSD_TTYS) 30 #include <sgtty.h> 31 #endif /* HAVE_BSD_TTYS */ 32 33 #if defined(HAVE_SYSV_TTYS) 34 #include <termio.h> 35 #endif /* HAVE_SYSV_TTYS */ 36 37 #if defined(HAVE_TERMIOS) 38 #include <termios.h> 39 #endif 40 #if defined(STREAM) 41 #include <stropts.h> 42 #if defined(WWVBCLK) 43 #include <sys/clkdefs.h> 44 #endif /* WWVBCLK */ 45 #endif /* STREAM */ 46 47 #include "ntp_stdlib.h" 48 49 /* 50 ** This driver supports the Datum Programmable Time System (PTS) clock. 51 ** The clock works in very straight forward manner. When it receives a 52 ** time code request (e.g., the ascii string "//k/mn"), it responds with 53 ** a seven byte BCD time code. This clock only responds with a 54 ** time code after it first receives the "//k/mn" message. It does not 55 ** periodically send time codes back at some rate once it is started. 56 ** the returned time code can be broken down into the following fields. 57 ** 58 ** _______________________________ 59 ** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 60 ** =============================== 61 ** byte 0: | - - - - | H D | 62 ** =============================== 63 ** byte 1: | T D | U D | 64 ** =============================== 65 ** byte 2: | - - | T H | U H | 66 ** =============================== 67 ** byte 3: | - | T M | U M | 68 ** =============================== 69 ** byte 4: | - | T S | U S | 70 ** =============================== 71 ** byte 5: | t S | h S | 72 ** =============================== 73 ** byte 6: | m S | - - - - | 74 ** =============================== 75 ** 76 ** In the table above: 77 ** 78 ** "-" means don't care 79 ** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days 80 ** "T H", and "UH" means Tens and Units of Hours 81 ** "T M", and "U M" means Tens and Units of Minutes 82 ** "T S", and "U S" means Tens and Units of Seconds 83 ** "t S", "h S", and "m S" means tenths, hundredths, and thousandths 84 ** of seconds 85 ** 86 ** The Datum PTS communicates throught the RS232 port on your machine. 87 ** Right now, it assumes that you have termios. This driver has been tested 88 ** on SUN and HP workstations. The Datum PTS supports various IRIG and 89 ** NASA input codes. This driver assumes that the name of the device is 90 ** /dev/datum. You will need to make a soft link to your RS232 device or 91 ** create a new driver to use this refclock. 92 */ 93 94 /* 95 ** Datum PTS defines 96 */ 97 98 /* 99 ** Note that if GMT is defined, then the Datum PTS must use Greenwich 100 ** time. Otherwise, this driver allows the Datum PTS to use the current 101 ** wall clock for its time. It determines the time zone offset by minimizing 102 ** the error after trying several time zone offsets. If the Datum PTS 103 ** time is Greenwich time and GMT is not defined, everything should still 104 ** work since the time zone will be found to be 0. What this really means 105 ** is that your system time (at least to start with) must be within the 106 ** correct time by less than +- 30 minutes. The default is for GMT to not 107 ** defined. If you really want to force GMT without the funny +- 30 minute 108 ** stuff then you must define (uncomment) GMT below. 109 */ 110 111 /* 112 #define GMT 113 #define DEBUG_DATUM_PTC 114 #define LOG_TIME_ERRORS 115 */ 116 117 118 #define PRECISION (-10) /* precision assumed 1/1024 ms */ 119 #define REFID "DATM" /* reference id */ 120 #define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */ 121 #define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */ 122 123 #define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR) 124 125 /* 126 ** The Datum PTS structure 127 */ 128 129 /* 130 ** I don't use a fixed array of MAXUNITS like everyone else just because 131 ** I don't like to program that way. Sorry if this bothers anyone. I assume 132 ** that you can use any id for your unit and I will search for it in a 133 ** dynamic array of units until I find it. I was worried that users might 134 ** enter a bad id in their configuration file (larger than MAXUNITS) and 135 ** besides, it is just cleaner not to have to assume that you have a fixed 136 ** number of anything in a program. 137 */ 138 139 struct datum_pts_unit { 140 struct peer *peer; /* peer used by ntp */ 141 struct refclockio io; /* io structure used by ntp */ 142 int PTS_fd; /* file descriptor for PTS */ 143 u_int unit; /* id for unit */ 144 u_long timestarted; /* time started */ 145 l_fp lastrec; /* time tag for the receive time (system) */ 146 l_fp lastref; /* reference time (Datum time) */ 147 u_long yearstart; /* the year that this clock started */ 148 int coderecv; /* number of time codes received */ 149 int day; /* day */ 150 int hour; /* hour */ 151 int minute; /* minutes */ 152 int second; /* seconds */ 153 int msec; /* miliseconds */ 154 int usec; /* miliseconds */ 155 u_char leap; /* funny leap character code */ 156 char retbuf[8]; /* returned time from the datum pts */ 157 char nbytes; /* number of bytes received from datum pts */ 158 double sigma2; /* average squared error (roughly) */ 159 int tzoff; /* time zone offest from GMT */ 160 }; 161 162 /* 163 ** PTS static constant variables for internal use 164 */ 165 166 static char TIME_REQUEST[6]; /* request message sent to datum for time */ 167 static int nunits; /* number of active units */ 168 static struct datum_pts_unit 169 **datum_pts_unit; /* dynamic array of datum PTS structures */ 170 171 /* 172 ** Callback function prototypes that ntpd needs to know about. 173 */ 174 175 static int datum_pts_start P((int, struct peer *)); 176 static void datum_pts_shutdown P((int, struct peer *)); 177 static void datum_pts_poll P((int, struct peer *)); 178 static void datum_pts_control P((int, struct refclockstat *, 179 struct refclockstat *, struct peer *)); 180 static void datum_pts_init P((void)); 181 static void datum_pts_buginfo P((int, struct refclockbug *, struct peer *)); 182 183 /* 184 ** This is the call back function structure that ntpd actually uses for 185 ** this refclock. 186 */ 187 188 struct refclock refclock_datum = { 189 datum_pts_start, /* start up a new Datum refclock */ 190 datum_pts_shutdown, /* shutdown a Datum refclock */ 191 datum_pts_poll, /* sends out the time request */ 192 datum_pts_control, /* not used */ 193 datum_pts_init, /* initialization (called first) */ 194 datum_pts_buginfo, /* not used */ 195 NOFLAGS /* we are not setting any special flags */ 196 }; 197 198 /* 199 ** The datum_pts_receive callback function is handled differently from the 200 ** rest. It is passed to the ntpd io data structure. Basically, every 201 ** 64 seconds, the datum_pts_poll() routine is called. It sends out the time 202 ** request message to the Datum Programmable Time System. Then, ntpd 203 ** waits on a select() call to receive data back. The datum_pts_receive() 204 ** function is called as data comes back. We expect a seven byte time 205 ** code to be returned but the datum_pts_receive() function may only get 206 ** a few bytes passed to it at a time. In other words, this routine may 207 ** get called by the io stuff in ntpd a few times before we get all seven 208 ** bytes. Once the last byte is received, we process it and then pass the 209 ** new time measurement to ntpd for updating the system time. For now, 210 ** there is no 3 state filtering done on the time measurements. The 211 ** jitter may be a little high but at least for its current use, it is not 212 ** a problem. We have tried to keep things as simple as possible. This 213 ** clock should not jitter more than 1 or 2 mseconds at the most once 214 ** things settle down. It is important to get the right drift calibrated 215 ** in the ntpd.drift file as well as getting the right tick set up right 216 ** using tickadj for SUNs. Tickadj is not used for the HP but you need to 217 ** remember to bring up the adjtime daemon because HP does not support 218 ** the adjtime() call. 219 */ 220 221 static void datum_pts_receive P((struct recvbuf *)); 222 223 /*......................................................................*/ 224 /* datum_pts_start - start up the datum PTS. This means open the */ 225 /* RS232 device and set up the data structure for my unit. */ 226 /*......................................................................*/ 227 228 static int 229 datum_pts_start( 230 int unit, 231 struct peer *peer 232 ) 233 { 234 struct datum_pts_unit **temp_datum_pts_unit; 235 struct datum_pts_unit *datum_pts; 236 #ifdef HAVE_TERMIOS 237 struct termios arg; 238 #endif 239 240 #ifdef DEBUG_DATUM_PTC 241 if (debug) 242 printf("Starting Datum PTS unit %d\n", unit); 243 #endif 244 245 /* 246 ** Create the memory for the new unit 247 */ 248 249 temp_datum_pts_unit = (struct datum_pts_unit **) 250 malloc((nunits+1)*sizeof(struct datum_pts_unit *)); 251 if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit, 252 nunits*sizeof(struct datum_pts_unit *)); 253 free(datum_pts_unit); 254 datum_pts_unit = temp_datum_pts_unit; 255 datum_pts_unit[nunits] = (struct datum_pts_unit *) 256 malloc(sizeof(struct datum_pts_unit)); 257 datum_pts = datum_pts_unit[nunits]; 258 259 datum_pts->unit = unit; /* set my unit id */ 260 datum_pts->yearstart = 0; /* initialize the yearstart to 0 */ 261 datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */ 262 263 /* 264 ** Open the Datum PTS device 265 */ 266 267 datum_pts->PTS_fd = open("/dev/datum",O_RDWR); 268 269 fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */ 270 271 #ifdef DEBUG_DATUM_PTC 272 if (debug) 273 printf("Opening RS232 port with file descriptor %d\n", 274 datum_pts->PTS_fd); 275 #endif 276 277 /* 278 ** Set up the RS232 terminal device information. Note that we assume that 279 ** we have termios. This code has only been tested on SUNs and HPs. If your 280 ** machine does not have termios this driver cannot be initialized. You can change this 281 ** if you want by editing this source. Please give the changes back to the 282 ** ntp folks so that it can become part of their regular distribution. 283 */ 284 285 #ifdef HAVE_TERMIOS 286 287 arg.c_iflag = IGNBRK; 288 arg.c_oflag = 0; 289 arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL; 290 arg.c_lflag = 0; 291 arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */ 292 arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */ 293 294 tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg); 295 296 #else 297 298 msyslog(LOG_ERR, "Datum_PTS: Termios not supported in this driver"); 299 (void)close(datum_pts->PTS_fd); 300 301 peer->precision = PRECISION; 302 pp->clockdesc = DESCRIPTION; 303 memcpy((char *)&pp->refid, REFID, 4); 304 305 return 0; 306 307 #endif 308 309 /* 310 ** Initialize the ntpd IO structure 311 */ 312 313 datum_pts->peer = peer; 314 datum_pts->io.clock_recv = datum_pts_receive; 315 datum_pts->io.srcclock = (caddr_t)datum_pts; 316 datum_pts->io.datalen = 0; 317 datum_pts->io.fd = datum_pts->PTS_fd; 318 319 if (!io_addclock(&(datum_pts->io))) { 320 321 #ifdef DEBUG_DATUM_PTC 322 if (debug) 323 printf("Problem adding clock\n"); 324 #endif 325 326 msyslog(LOG_ERR, "Datum_PTS: Problem adding clock"); 327 (void)close(datum_pts->PTS_fd); 328 329 return 0; 330 } 331 332 /* 333 ** Now add one to the number of units and return a successful code 334 */ 335 336 nunits++; 337 return 1; 338 339 } 340 341 342 /*......................................................................*/ 343 /* datum_pts_shutdown - this routine shuts doen the device and */ 344 /* removes the memory for the unit. */ 345 /*......................................................................*/ 346 347 static void 348 datum_pts_shutdown( 349 int unit, 350 struct peer *peer 351 ) 352 { 353 int i,j; 354 struct datum_pts_unit **temp_datum_pts_unit; 355 356 #ifdef DEBUG_DATUM_PTC 357 if (debug) 358 printf("Shutdown Datum PTS\n"); 359 #endif 360 361 msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS"); 362 363 /* 364 ** First we have to find the right unit (i.e., the one with the same id). 365 ** We do this by looping through the dynamic array of units intil we find 366 ** it. Note, that I don't simply use an array with a maximimum number of 367 ** Datum PTS units. Everything is completely dynamic. 368 */ 369 370 for (i=0; i<nunits; i++) { 371 if (datum_pts_unit[i]->unit == unit) { 372 373 /* 374 ** We found the unit so close the file descriptor and free up the memory used 375 ** by the structure. 376 */ 377 378 io_closeclock(&datum_pts_unit[i]->io); 379 close(datum_pts_unit[i]->PTS_fd); 380 free(datum_pts_unit[i]); 381 382 /* 383 ** Now clean up the datum_pts_unit dynamic array so that there are no holes. 384 ** This may mean moving pointers around, etc., to keep things compact. 385 */ 386 387 if (nunits > 1) { 388 389 temp_datum_pts_unit = (struct datum_pts_unit **) 390 malloc((nunits-1)*sizeof(struct datum_pts_unit *)); 391 if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit, 392 i*sizeof(struct datum_pts_unit *)); 393 394 for (j=i+1; j<nunits; j++) { 395 temp_datum_pts_unit[j-1] = datum_pts_unit[j]; 396 } 397 398 free(datum_pts_unit); 399 datum_pts_unit = temp_datum_pts_unit; 400 401 }else{ 402 403 free(datum_pts_unit); 404 datum_pts_unit = NULL; 405 406 } 407 408 return; 409 410 } 411 } 412 413 #ifdef DEBUG_DATUM_PTC 414 if (debug) 415 printf("Error, could not shut down unit %d\n",unit); 416 #endif 417 418 msyslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit); 419 420 } 421 422 /*......................................................................*/ 423 /* datum_pts_poll - this routine sends out the time request to the */ 424 /* Datum PTS device. The time will be passed back in the */ 425 /* datum_pts_receive() routine. */ 426 /*......................................................................*/ 427 428 static void 429 datum_pts_poll( 430 int unit, 431 struct peer *peer 432 ) 433 { 434 int i; 435 int unit_index; 436 int error_code; 437 struct datum_pts_unit *datum_pts; 438 439 #ifdef DEBUG_DATUM_PTC 440 if (debug) 441 printf("Poll Datum PTS\n"); 442 #endif 443 444 /* 445 ** Find the right unit and send out a time request once it is found. 446 */ 447 448 unit_index = -1; 449 for (i=0; i<nunits; i++) { 450 if (datum_pts_unit[i]->unit == unit) { 451 unit_index = i; 452 datum_pts = datum_pts_unit[i]; 453 error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6); 454 if (error_code != 6) perror("TIME_REQUEST"); 455 datum_pts->nbytes = 0; 456 break; 457 } 458 } 459 460 /* 461 ** Print out an error message if we could not find the right unit. 462 */ 463 464 if (unit_index == -1) { 465 466 #ifdef DEBUG_DATUM_PTC 467 if (debug) 468 printf("Error, could not poll unit %d\n",unit); 469 #endif 470 471 msyslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit); 472 return; 473 474 } 475 476 } 477 478 479 /*......................................................................*/ 480 /* datum_pts_control - not used */ 481 /*......................................................................*/ 482 483 static void 484 datum_pts_control( 485 int unit, 486 struct refclockstat *in, 487 struct refclockstat *out, 488 struct peer *peer 489 ) 490 { 491 492 #ifdef DEBUG_DATUM_PTC 493 if (debug) 494 printf("Control Datum PTS\n"); 495 #endif 496 497 } 498 499 500 /*......................................................................*/ 501 /* datum_pts_init - initializes things for all possible Datum */ 502 /* time code generators that might be used. In practice, this is */ 503 /* only called once at the beginning before anything else is */ 504 /* called. */ 505 /*......................................................................*/ 506 507 static void 508 datum_pts_init(void) 509 { 510 511 /* */ 512 /*...... open up the log file if we are debugging ......................*/ 513 /* */ 514 515 /* 516 ** Open up the log file if we are debugging. For now, send data out to the 517 ** screen (stdout). 518 */ 519 520 #ifdef DEBUG_DATUM_PTC 521 if (debug) 522 printf("Init Datum PTS\n"); 523 #endif 524 525 /* 526 ** Initialize the time request command string. This is the only message 527 ** that we ever have to send to the Datum PTS (although others are defined). 528 */ 529 530 memcpy(TIME_REQUEST, "//k/mn",6); 531 532 /* 533 ** Initialize the number of units to 0 and set the dynamic array of units to 534 ** NULL since there are no units defined yet. 535 */ 536 537 datum_pts_unit = NULL; 538 nunits = 0; 539 540 } 541 542 543 /*......................................................................*/ 544 /* datum_pts_buginfo - not used */ 545 /*......................................................................*/ 546 547 static void 548 datum_pts_buginfo( 549 int unit, 550 register struct refclockbug *bug, 551 register struct peer *peer 552 ) 553 { 554 555 #ifdef DEBUG_DATUM_PTC 556 if (debug) 557 printf("Buginfo Datum PTS\n"); 558 #endif 559 560 } 561 562 563 /*......................................................................*/ 564 /* datum_pts_receive - receive the time buffer that was read in */ 565 /* by the ntpd io handling routines. When 7 bytes have been */ 566 /* received (it may take several tries before all 7 bytes are */ 567 /* received), then the time code must be unpacked and sent to */ 568 /* the ntpd clock_receive() routine which causes the systems */ 569 /* clock to be updated (several layers down). */ 570 /*......................................................................*/ 571 572 static void 573 datum_pts_receive( 574 struct recvbuf *rbufp 575 ) 576 { 577 int i; 578 l_fp tstmp; 579 struct datum_pts_unit *datum_pts; 580 char *dpt; 581 int dpend; 582 int tzoff; 583 int timerr; 584 double ftimerr, abserr; 585 #ifdef DEBUG_DATUM_PTC 586 double dispersion; 587 #endif 588 int goodtime; 589 /*double doffset;*/ 590 591 /* 592 ** Get the time code (maybe partial) message out of the rbufp buffer. 593 */ 594 595 datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock; 596 dpt = (char *)&rbufp->recv_space; 597 dpend = rbufp->recv_length; 598 599 #ifdef DEBUG_DATUM_PTC 600 if (debug) 601 printf("Receive Datum PTS: %d bytes\n", dpend); 602 #endif 603 604 /* */ 605 /*...... save the ntp system time when the first byte is received ......*/ 606 /* */ 607 608 /* 609 ** Save the ntp system time when the first byte is received. Note that 610 ** because it may take several calls to this routine before all seven 611 ** bytes of our return message are finally received by the io handlers in 612 ** ntpd, we really do want to use the time tag when the first byte is 613 ** received to reduce the jitter. 614 */ 615 616 if (datum_pts->nbytes == 0) { 617 datum_pts->lastrec = rbufp->recv_time; 618 } 619 620 /* 621 ** Increment our count to the number of bytes received so far. Return if we 622 ** haven't gotten all seven bytes yet. 623 */ 624 625 for (i=0; i<dpend; i++) { 626 datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i]; 627 } 628 629 datum_pts->nbytes += dpend; 630 631 if (datum_pts->nbytes != 7) { 632 return; 633 } 634 635 /* 636 ** Convert the seven bytes received in our time buffer to day, hour, minute, 637 ** second, and msecond values. The usec value is not used for anything 638 ** currently. It is just the fractional part of the time stored in units 639 ** of microseconds. 640 */ 641 642 datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) + 643 10*((datum_pts->retbuf[1] & 0xf0)>>4) + 644 (datum_pts->retbuf[1] & 0x0f); 645 646 datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) + 647 (datum_pts->retbuf[2] & 0x0f); 648 649 datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) + 650 (datum_pts->retbuf[3] & 0x0f); 651 652 datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) + 653 (datum_pts->retbuf[4] & 0x0f); 654 655 datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) + 656 10*(datum_pts->retbuf[5] & 0x0f) + 657 ((datum_pts->retbuf[6] & 0xf0)>>4); 658 659 datum_pts->usec = 1000*datum_pts->msec; 660 661 #ifdef DEBUG_DATUM_PTC 662 if (debug) 663 printf("day %d, hour %d, minute %d, second %d, msec %d\n", 664 datum_pts->day, 665 datum_pts->hour, 666 datum_pts->minute, 667 datum_pts->second, 668 datum_pts->msec); 669 #endif 670 671 /* 672 ** Get the GMT time zone offset. Note that GMT should be zero if the Datum 673 ** reference time is using GMT as its time base. Otherwise we have to 674 ** determine the offset if the Datum PTS is using time of day as its time 675 ** base. 676 */ 677 678 goodtime = 0; /* We are not sure about the time and offset yet */ 679 680 #ifdef GMT 681 682 /* 683 ** This is the case where the Datum PTS is using GMT so there is no time 684 ** zone offset. 685 */ 686 687 tzoff = 0; /* set time zone offset to 0 */ 688 689 #else 690 691 /* 692 ** This is the case where the Datum PTS is using regular time of day for its 693 ** time so we must compute the time zone offset. The way we do it is kind of 694 ** funny but it works. We loop through different time zones (0 to 24) and 695 ** pick the one that gives the smallest error (+- one half hour). The time 696 ** zone offset is stored in the datum_pts structure for future use. Normally, 697 ** the clocktime() routine is only called once (unless the time zone offset 698 ** changes due to daylight savings) since the goodtime flag is set when a 699 ** good time is found (with a good offset). Note that even if the Datum 700 ** PTS is using GMT, this mechanism will still work since it should come up 701 ** with a value for tzoff = 0 (assuming that your system clock is within 702 ** a half hour of the Datum time (even with time zone differences). 703 */ 704 705 for (tzoff=0; tzoff<24; tzoff++) { 706 if (clocktime( datum_pts->day, 707 datum_pts->hour, 708 datum_pts->minute, 709 datum_pts->second, 710 (tzoff + datum_pts->tzoff) % 24, 711 datum_pts->lastrec.l_ui, 712 &datum_pts->yearstart, 713 &datum_pts->lastref.l_ui) ) { 714 715 datum_pts->lastref.l_uf = 0; 716 error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui; 717 718 #ifdef DEBUG_DATUM_PTC 719 printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error); 720 #endif 721 722 if ((error < 1799) && (error > -1799)) { 723 tzoff = (tzoff + datum_pts->tzoff) % 24; 724 datum_pts->tzoff = tzoff; 725 goodtime = 1; 726 727 #ifdef DEBUG_DATUM_PTC 728 printf("Time Zone found (clocktime method) = %d\n",tzoff); 729 #endif 730 731 break; 732 } 733 734 } 735 } 736 737 #endif 738 739 /* 740 ** Make sure that we have a good time from the Datum PTS. Clocktime() also 741 ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e., 742 ** the fraction of a second) stuff later. 743 */ 744 745 if (!goodtime) { 746 747 if (!clocktime( datum_pts->day, 748 datum_pts->hour, 749 datum_pts->minute, 750 datum_pts->second, 751 tzoff, 752 datum_pts->lastrec.l_ui, 753 &datum_pts->yearstart, 754 &datum_pts->lastref.l_ui) ) { 755 756 #ifdef DEBUG_DATUM_PTC 757 if (debug) 758 { 759 printf("Error: bad clocktime\n"); 760 printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n", 761 tzoff, 762 datum_pts->lastrec.l_ui, 763 datum_pts->yearstart, 764 datum_pts->lastref.l_ui); 765 } 766 #endif 767 768 msyslog(LOG_ERR, "Datum_PTS: Bad clocktime"); 769 770 return; 771 772 }else{ 773 774 #ifdef DEBUG_DATUM_PTC 775 if (debug) 776 printf("Good clocktime\n"); 777 #endif 778 779 } 780 781 } 782 783 /* 784 ** We have datum_pts->lastref.l_ui set (which is the integer part of the 785 ** time. Now set the microseconds field. 786 */ 787 788 TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf); 789 790 /* 791 ** Compute the time correction as the difference between the reference 792 ** time (i.e., the Datum time) minus the receive time (system time). 793 */ 794 795 tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */ 796 L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */ 797 datum_pts->coderecv++; /* increment a counter */ 798 799 #ifdef DEBUG_DATUM_PTC 800 dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */ 801 ftimerr = dispersion; 802 ftimerr /= (1024.0 * 64.0); 803 if (debug) 804 printf("dispersion = %d, %f\n", dispersion, ftimerr); 805 #endif 806 807 /* 808 ** Pass the new time to ntpd through the refclock_receive function. Note 809 ** that we are not trying to make any corrections due to the time it takes 810 ** for the Datum PTS to send the message back. I am (erroneously) assuming 811 ** that the time for the Datum PTS to send the time back to us is negligable. 812 ** I suspect that this time delay may be as much as 15 ms or so (but probably 813 ** less). For our needs at JPL, this kind of error is ok so it is not 814 ** necessary to use fudge factors in the ntp.conf file. Maybe later we will. 815 */ 816 /*LFPTOD(&tstmp, doffset);*/ 817 datum_pts->lastref = datum_pts->lastrec; 818 refclock_receive(datum_pts->peer); 819 820 /* 821 ** Compute sigma squared (not used currently). Maybe later, this could be 822 ** used for the dispersion estimate. The problem is that ntpd does not link 823 ** in the math library so sqrt() is not available. Anyway, this is useful 824 ** for debugging. Maybe later I will just use absolute values for the time 825 ** error to come up with my dispersion estimate. Anyway, for now my dispersion 826 ** is set to 0. 827 */ 828 829 timerr = tstmp.l_ui<<20; 830 timerr |= (tstmp.l_uf>>12) & 0x000fffff; 831 ftimerr = timerr; 832 ftimerr /= 1024*1024; 833 abserr = ftimerr; 834 if (ftimerr < 0.0) abserr = -ftimerr; 835 836 if (datum_pts->sigma2 == 0.0) { 837 if (abserr < DATUM_MAX_ERROR) { 838 datum_pts->sigma2 = abserr*abserr; 839 }else{ 840 datum_pts->sigma2 = DATUM_MAX_ERROR2; 841 } 842 }else{ 843 if (abserr < DATUM_MAX_ERROR) { 844 datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr; 845 }else{ 846 datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2; 847 } 848 } 849 850 #ifdef DEBUG_DATUM_PTC 851 if (debug) 852 printf("Time error = %f seconds\n", ftimerr); 853 #endif 854 855 #if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) 856 if (debug) 857 printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n", 858 datum_pts->day, 859 datum_pts->hour, 860 datum_pts->minute, 861 datum_pts->second, 862 datum_pts->msec, 863 ftimerr); 864 #endif 865 866 } 867 #else 868 int refclock_datum_bs; 869 #endif /* REFCLOCK */ 870