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