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