154359Sroberto/* 254359Sroberto** refclock_datum - clock driver for the Datum Programmable Time Server 354359Sroberto** 454359Sroberto** Important note: This driver assumes that you have termios. If you have 554359Sroberto** a system that does not have termios, you will have to modify this driver. 654359Sroberto** 754359Sroberto** Sorry, I have only tested this driver on SUN and HP platforms. 854359Sroberto*/ 954359Sroberto 1054359Sroberto#ifdef HAVE_CONFIG_H 1154359Sroberto# include <config.h> 1254359Sroberto#endif 1354359Sroberto 14290001Sglebius#include "ntp_types.h" 15290001Sglebius 1654359Sroberto#if defined(REFCLOCK) && defined(CLOCK_DATUM) 1754359Sroberto 1854359Sroberto/* 1954359Sroberto** Include Files 2054359Sroberto*/ 2154359Sroberto 2254359Sroberto#include "ntpd.h" 2354359Sroberto#include "ntp_io.h" 24290001Sglebius#include "ntp_tty.h" 2554359Sroberto#include "ntp_refclock.h" 26290001Sglebius#include "timevalops.h" 2754359Sroberto#include "ntp_stdlib.h" 2854359Sroberto 2982498Sroberto#include <stdio.h> 3082498Sroberto#include <ctype.h> 3182498Sroberto 3254359Sroberto#if defined(STREAM) 3354359Sroberto#include <stropts.h> 3454359Sroberto#endif /* STREAM */ 3554359Sroberto 3654359Sroberto#include "ntp_stdlib.h" 3754359Sroberto 3854359Sroberto/* 3954359Sroberto** This driver supports the Datum Programmable Time System (PTS) clock. 4054359Sroberto** The clock works in very straight forward manner. When it receives a 4154359Sroberto** time code request (e.g., the ascii string "//k/mn"), it responds with 4254359Sroberto** a seven byte BCD time code. This clock only responds with a 4354359Sroberto** time code after it first receives the "//k/mn" message. It does not 4454359Sroberto** periodically send time codes back at some rate once it is started. 4554359Sroberto** the returned time code can be broken down into the following fields. 4654359Sroberto** 4754359Sroberto** _______________________________ 4854359Sroberto** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 4954359Sroberto** =============================== 5054359Sroberto** byte 0: | - - - - | H D | 5154359Sroberto** =============================== 5254359Sroberto** byte 1: | T D | U D | 5354359Sroberto** =============================== 5454359Sroberto** byte 2: | - - | T H | U H | 5554359Sroberto** =============================== 5654359Sroberto** byte 3: | - | T M | U M | 5754359Sroberto** =============================== 5854359Sroberto** byte 4: | - | T S | U S | 5954359Sroberto** =============================== 6054359Sroberto** byte 5: | t S | h S | 6154359Sroberto** =============================== 6254359Sroberto** byte 6: | m S | - - - - | 6354359Sroberto** =============================== 6454359Sroberto** 6554359Sroberto** In the table above: 6654359Sroberto** 6754359Sroberto** "-" means don't care 6854359Sroberto** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days 6954359Sroberto** "T H", and "UH" means Tens and Units of Hours 7054359Sroberto** "T M", and "U M" means Tens and Units of Minutes 7154359Sroberto** "T S", and "U S" means Tens and Units of Seconds 7254359Sroberto** "t S", "h S", and "m S" means tenths, hundredths, and thousandths 7354359Sroberto** of seconds 7454359Sroberto** 7554359Sroberto** The Datum PTS communicates throught the RS232 port on your machine. 7654359Sroberto** Right now, it assumes that you have termios. This driver has been tested 7754359Sroberto** on SUN and HP workstations. The Datum PTS supports various IRIG and 7854359Sroberto** NASA input codes. This driver assumes that the name of the device is 7954359Sroberto** /dev/datum. You will need to make a soft link to your RS232 device or 8054359Sroberto** create a new driver to use this refclock. 8154359Sroberto*/ 8254359Sroberto 8354359Sroberto/* 8454359Sroberto** Datum PTS defines 8554359Sroberto*/ 8654359Sroberto 8754359Sroberto/* 8854359Sroberto** Note that if GMT is defined, then the Datum PTS must use Greenwich 8954359Sroberto** time. Otherwise, this driver allows the Datum PTS to use the current 9054359Sroberto** wall clock for its time. It determines the time zone offset by minimizing 9154359Sroberto** the error after trying several time zone offsets. If the Datum PTS 9254359Sroberto** time is Greenwich time and GMT is not defined, everything should still 9354359Sroberto** work since the time zone will be found to be 0. What this really means 9454359Sroberto** is that your system time (at least to start with) must be within the 9554359Sroberto** correct time by less than +- 30 minutes. The default is for GMT to not 9654359Sroberto** defined. If you really want to force GMT without the funny +- 30 minute 9754359Sroberto** stuff then you must define (uncomment) GMT below. 9854359Sroberto*/ 9954359Sroberto 10054359Sroberto/* 10154359Sroberto#define GMT 10254359Sroberto#define DEBUG_DATUM_PTC 10354359Sroberto#define LOG_TIME_ERRORS 10454359Sroberto*/ 10554359Sroberto 10654359Sroberto 10782498Sroberto#define PRECISION (-10) /* precision assumed 1/1024 ms */ 10882498Sroberto#define REFID "DATM" /* reference id */ 10954359Sroberto#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */ 11054359Sroberto#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */ 111182007Sroberto#define DATUM_DEV "/dev/datum" /* device name */ 11254359Sroberto 11354359Sroberto#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR) 11454359Sroberto 11554359Sroberto/* 11654359Sroberto** The Datum PTS structure 11754359Sroberto*/ 11854359Sroberto 11954359Sroberto/* 12054359Sroberto** I don't use a fixed array of MAXUNITS like everyone else just because 12154359Sroberto** I don't like to program that way. Sorry if this bothers anyone. I assume 12254359Sroberto** that you can use any id for your unit and I will search for it in a 12354359Sroberto** dynamic array of units until I find it. I was worried that users might 12454359Sroberto** enter a bad id in their configuration file (larger than MAXUNITS) and 12554359Sroberto** besides, it is just cleaner not to have to assume that you have a fixed 12654359Sroberto** number of anything in a program. 12754359Sroberto*/ 12854359Sroberto 12954359Srobertostruct datum_pts_unit { 13054359Sroberto struct peer *peer; /* peer used by ntp */ 13154359Sroberto int PTS_fd; /* file descriptor for PTS */ 13254359Sroberto u_int unit; /* id for unit */ 13354359Sroberto u_long timestarted; /* time started */ 13454359Sroberto l_fp lastrec; /* time tag for the receive time (system) */ 13554359Sroberto l_fp lastref; /* reference time (Datum time) */ 13654359Sroberto u_long yearstart; /* the year that this clock started */ 13754359Sroberto int coderecv; /* number of time codes received */ 13854359Sroberto int day; /* day */ 13954359Sroberto int hour; /* hour */ 14054359Sroberto int minute; /* minutes */ 14154359Sroberto int second; /* seconds */ 14254359Sroberto int msec; /* miliseconds */ 14354359Sroberto int usec; /* miliseconds */ 14454359Sroberto u_char leap; /* funny leap character code */ 14554359Sroberto char retbuf[8]; /* returned time from the datum pts */ 14654359Sroberto char nbytes; /* number of bytes received from datum pts */ 14754359Sroberto double sigma2; /* average squared error (roughly) */ 14854359Sroberto int tzoff; /* time zone offest from GMT */ 14954359Sroberto}; 15054359Sroberto 15154359Sroberto/* 15254359Sroberto** PTS static constant variables for internal use 15354359Sroberto*/ 15454359Sroberto 15554359Srobertostatic char TIME_REQUEST[6]; /* request message sent to datum for time */ 15654359Srobertostatic int nunits; /* number of active units */ 15754359Sroberto 15854359Sroberto/* 15954359Sroberto** Callback function prototypes that ntpd needs to know about. 16054359Sroberto*/ 16154359Sroberto 162290001Sglebiusstatic int datum_pts_start (int, struct peer *); 163290001Sglebiusstatic void datum_pts_shutdown (int, struct peer *); 164290001Sglebiusstatic void datum_pts_poll (int, struct peer *); 165290001Sglebiusstatic void datum_pts_control (int, const struct refclockstat *, 166290001Sglebius struct refclockstat *, struct peer *); 167290001Sglebiusstatic void datum_pts_init (void); 168290001Sglebiusstatic void datum_pts_buginfo (int, struct refclockbug *, struct peer *); 16954359Sroberto 17054359Sroberto/* 17154359Sroberto** This is the call back function structure that ntpd actually uses for 17254359Sroberto** this refclock. 17354359Sroberto*/ 17454359Sroberto 17554359Srobertostruct refclock refclock_datum = { 17654359Sroberto datum_pts_start, /* start up a new Datum refclock */ 17754359Sroberto datum_pts_shutdown, /* shutdown a Datum refclock */ 17854359Sroberto datum_pts_poll, /* sends out the time request */ 17954359Sroberto datum_pts_control, /* not used */ 18054359Sroberto datum_pts_init, /* initialization (called first) */ 18154359Sroberto datum_pts_buginfo, /* not used */ 18254359Sroberto NOFLAGS /* we are not setting any special flags */ 18354359Sroberto}; 18454359Sroberto 18554359Sroberto/* 18654359Sroberto** The datum_pts_receive callback function is handled differently from the 18754359Sroberto** rest. It is passed to the ntpd io data structure. Basically, every 18854359Sroberto** 64 seconds, the datum_pts_poll() routine is called. It sends out the time 18954359Sroberto** request message to the Datum Programmable Time System. Then, ntpd 19054359Sroberto** waits on a select() call to receive data back. The datum_pts_receive() 19154359Sroberto** function is called as data comes back. We expect a seven byte time 19254359Sroberto** code to be returned but the datum_pts_receive() function may only get 19354359Sroberto** a few bytes passed to it at a time. In other words, this routine may 19454359Sroberto** get called by the io stuff in ntpd a few times before we get all seven 19554359Sroberto** bytes. Once the last byte is received, we process it and then pass the 19654359Sroberto** new time measurement to ntpd for updating the system time. For now, 19754359Sroberto** there is no 3 state filtering done on the time measurements. The 19854359Sroberto** jitter may be a little high but at least for its current use, it is not 19954359Sroberto** a problem. We have tried to keep things as simple as possible. This 20054359Sroberto** clock should not jitter more than 1 or 2 mseconds at the most once 20154359Sroberto** things settle down. It is important to get the right drift calibrated 20254359Sroberto** in the ntpd.drift file as well as getting the right tick set up right 20354359Sroberto** using tickadj for SUNs. Tickadj is not used for the HP but you need to 20454359Sroberto** remember to bring up the adjtime daemon because HP does not support 20554359Sroberto** the adjtime() call. 20654359Sroberto*/ 20754359Sroberto 208290001Sglebiusstatic void datum_pts_receive (struct recvbuf *); 20954359Sroberto 21054359Sroberto/*......................................................................*/ 21154359Sroberto/* datum_pts_start - start up the datum PTS. This means open the */ 21254359Sroberto/* RS232 device and set up the data structure for my unit. */ 21354359Sroberto/*......................................................................*/ 21454359Sroberto 21554359Srobertostatic int 21654359Srobertodatum_pts_start( 21754359Sroberto int unit, 21854359Sroberto struct peer *peer 21954359Sroberto ) 22054359Sroberto{ 221290001Sglebius struct refclockproc *pp; 22254359Sroberto struct datum_pts_unit *datum_pts; 223182007Sroberto int fd; 22454359Sroberto#ifdef HAVE_TERMIOS 225290001Sglebius int rc; 22654359Sroberto struct termios arg; 22754359Sroberto#endif 22854359Sroberto 22954359Sroberto#ifdef DEBUG_DATUM_PTC 23054359Sroberto if (debug) 23154359Sroberto printf("Starting Datum PTS unit %d\n", unit); 23254359Sroberto#endif 23354359Sroberto 23454359Sroberto /* 235182007Sroberto ** Open the Datum PTS device 236182007Sroberto */ 237182007Sroberto fd = open(DATUM_DEV, O_RDWR); 238182007Sroberto 239182007Sroberto if (fd < 0) { 240182007Sroberto msyslog(LOG_ERR, "Datum_PTS: open(\"%s\", O_RDWR) failed: %m", DATUM_DEV); 241182007Sroberto return 0; 242182007Sroberto } 243182007Sroberto 244182007Sroberto /* 24554359Sroberto ** Create the memory for the new unit 24654359Sroberto */ 247290001Sglebius datum_pts = emalloc_zero(sizeof(*datum_pts)); 24854359Sroberto datum_pts->unit = unit; /* set my unit id */ 24954359Sroberto datum_pts->yearstart = 0; /* initialize the yearstart to 0 */ 25054359Sroberto datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */ 25154359Sroberto 252182007Sroberto datum_pts->PTS_fd = fd; 25354359Sroberto 254290001Sglebius if (-1 == fcntl(datum_pts->PTS_fd, F_SETFL, 0)) /* clear the descriptor flags */ 255290001Sglebius msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.", 256290001Sglebius unit); 25754359Sroberto 25854359Sroberto#ifdef DEBUG_DATUM_PTC 25954359Sroberto if (debug) 26054359Sroberto printf("Opening RS232 port with file descriptor %d\n", 26154359Sroberto datum_pts->PTS_fd); 26254359Sroberto#endif 26354359Sroberto 26454359Sroberto /* 26554359Sroberto ** Set up the RS232 terminal device information. Note that we assume that 26654359Sroberto ** we have termios. This code has only been tested on SUNs and HPs. If your 26754359Sroberto ** machine does not have termios this driver cannot be initialized. You can change this 26854359Sroberto ** if you want by editing this source. Please give the changes back to the 26954359Sroberto ** ntp folks so that it can become part of their regular distribution. 27054359Sroberto */ 27154359Sroberto 272290001Sglebius memset(&arg, 0, sizeof(arg)); 27354359Sroberto 27454359Sroberto arg.c_iflag = IGNBRK; 27554359Sroberto arg.c_oflag = 0; 27654359Sroberto arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL; 27754359Sroberto arg.c_lflag = 0; 27854359Sroberto arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */ 27954359Sroberto arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */ 28054359Sroberto 281290001Sglebius rc = tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg); 282290001Sglebius if (rc < 0) { 283290001Sglebius msyslog(LOG_ERR, "Datum_PTS: tcsetattr(\"%s\") failed: %m", DATUM_DEV); 284290001Sglebius close(datum_pts->PTS_fd); 285290001Sglebius free(datum_pts); 286290001Sglebius return 0; 287290001Sglebius } 28854359Sroberto 28954359Sroberto /* 29054359Sroberto ** Initialize the ntpd IO structure 29154359Sroberto */ 29254359Sroberto 29354359Sroberto datum_pts->peer = peer; 294290001Sglebius pp = peer->procptr; 295290001Sglebius pp->io.clock_recv = datum_pts_receive; 296290001Sglebius pp->io.srcclock = peer; 297290001Sglebius pp->io.datalen = 0; 298290001Sglebius pp->io.fd = datum_pts->PTS_fd; 29954359Sroberto 300290001Sglebius if (!io_addclock(&pp->io)) { 301290001Sglebius pp->io.fd = -1; 30254359Sroberto#ifdef DEBUG_DATUM_PTC 30354359Sroberto if (debug) 30454359Sroberto printf("Problem adding clock\n"); 30554359Sroberto#endif 30654359Sroberto 30754359Sroberto msyslog(LOG_ERR, "Datum_PTS: Problem adding clock"); 308290001Sglebius close(datum_pts->PTS_fd); 309290001Sglebius free(datum_pts); 31054359Sroberto 31154359Sroberto return 0; 31254359Sroberto } 313290001Sglebius peer->procptr->unitptr = datum_pts; 31454359Sroberto 31554359Sroberto /* 31654359Sroberto ** Now add one to the number of units and return a successful code 31754359Sroberto */ 31854359Sroberto 31954359Sroberto nunits++; 32054359Sroberto return 1; 32154359Sroberto 32254359Sroberto} 32354359Sroberto 32454359Sroberto 32554359Sroberto/*......................................................................*/ 32654359Sroberto/* datum_pts_shutdown - this routine shuts doen the device and */ 32754359Sroberto/* removes the memory for the unit. */ 32854359Sroberto/*......................................................................*/ 32954359Sroberto 33054359Srobertostatic void 33154359Srobertodatum_pts_shutdown( 33254359Sroberto int unit, 33354359Sroberto struct peer *peer 33454359Sroberto ) 33554359Sroberto{ 336290001Sglebius struct refclockproc *pp; 337290001Sglebius struct datum_pts_unit *datum_pts; 33854359Sroberto 33954359Sroberto#ifdef DEBUG_DATUM_PTC 34054359Sroberto if (debug) 34154359Sroberto printf("Shutdown Datum PTS\n"); 34254359Sroberto#endif 34354359Sroberto 34454359Sroberto msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS"); 34554359Sroberto 34654359Sroberto /* 347290001Sglebius ** We found the unit so close the file descriptor and free up the memory used 348290001Sglebius ** by the structure. 34954359Sroberto */ 350290001Sglebius pp = peer->procptr; 351290001Sglebius datum_pts = pp->unitptr; 352290001Sglebius if (NULL != datum_pts) { 353290001Sglebius io_closeclock(&pp->io); 354290001Sglebius free(datum_pts); 35554359Sroberto } 356290001Sglebius} 35754359Sroberto 35854359Sroberto 35954359Sroberto/*......................................................................*/ 36054359Sroberto/* datum_pts_poll - this routine sends out the time request to the */ 36154359Sroberto/* Datum PTS device. The time will be passed back in the */ 36254359Sroberto/* datum_pts_receive() routine. */ 36354359Sroberto/*......................................................................*/ 36454359Sroberto 36554359Srobertostatic void 36654359Srobertodatum_pts_poll( 36754359Sroberto int unit, 36854359Sroberto struct peer *peer 36954359Sroberto ) 37054359Sroberto{ 37154359Sroberto int error_code; 37254359Sroberto struct datum_pts_unit *datum_pts; 37354359Sroberto 374290001Sglebius datum_pts = peer->procptr->unitptr; 375290001Sglebius 37654359Sroberto#ifdef DEBUG_DATUM_PTC 37754359Sroberto if (debug) 37854359Sroberto printf("Poll Datum PTS\n"); 37954359Sroberto#endif 38054359Sroberto 38154359Sroberto /* 38254359Sroberto ** Find the right unit and send out a time request once it is found. 38354359Sroberto */ 384290001Sglebius error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6); 385290001Sglebius if (error_code != 6) 386290001Sglebius perror("TIME_REQUEST"); 387290001Sglebius datum_pts->nbytes = 0; 38854359Sroberto} 38954359Sroberto 39054359Sroberto 39154359Sroberto/*......................................................................*/ 39254359Sroberto/* datum_pts_control - not used */ 39354359Sroberto/*......................................................................*/ 39454359Sroberto 39554359Srobertostatic void 39654359Srobertodatum_pts_control( 39754359Sroberto int unit, 398290001Sglebius const struct refclockstat *in, 39954359Sroberto struct refclockstat *out, 40054359Sroberto struct peer *peer 40154359Sroberto ) 40254359Sroberto{ 40354359Sroberto 40454359Sroberto#ifdef DEBUG_DATUM_PTC 40554359Sroberto if (debug) 40654359Sroberto printf("Control Datum PTS\n"); 40754359Sroberto#endif 40854359Sroberto 40954359Sroberto} 41054359Sroberto 41154359Sroberto 41254359Sroberto/*......................................................................*/ 41354359Sroberto/* datum_pts_init - initializes things for all possible Datum */ 41454359Sroberto/* time code generators that might be used. In practice, this is */ 41554359Sroberto/* only called once at the beginning before anything else is */ 41654359Sroberto/* called. */ 41754359Sroberto/*......................................................................*/ 41854359Sroberto 41954359Srobertostatic void 42054359Srobertodatum_pts_init(void) 42154359Sroberto{ 42254359Sroberto 42354359Sroberto /* */ 42454359Sroberto /*...... open up the log file if we are debugging ......................*/ 42554359Sroberto /* */ 42654359Sroberto 42754359Sroberto /* 42854359Sroberto ** Open up the log file if we are debugging. For now, send data out to the 42954359Sroberto ** screen (stdout). 43054359Sroberto */ 43154359Sroberto 43254359Sroberto#ifdef DEBUG_DATUM_PTC 43354359Sroberto if (debug) 43454359Sroberto printf("Init Datum PTS\n"); 43554359Sroberto#endif 43654359Sroberto 43754359Sroberto /* 43854359Sroberto ** Initialize the time request command string. This is the only message 43954359Sroberto ** that we ever have to send to the Datum PTS (although others are defined). 44054359Sroberto */ 44154359Sroberto 44254359Sroberto memcpy(TIME_REQUEST, "//k/mn",6); 44354359Sroberto 44454359Sroberto /* 44554359Sroberto ** Initialize the number of units to 0 and set the dynamic array of units to 44654359Sroberto ** NULL since there are no units defined yet. 44754359Sroberto */ 44854359Sroberto 44954359Sroberto nunits = 0; 45054359Sroberto 45154359Sroberto} 45254359Sroberto 45354359Sroberto 45454359Sroberto/*......................................................................*/ 45554359Sroberto/* datum_pts_buginfo - not used */ 45654359Sroberto/*......................................................................*/ 45754359Sroberto 45854359Srobertostatic void 45954359Srobertodatum_pts_buginfo( 46054359Sroberto int unit, 46154359Sroberto register struct refclockbug *bug, 46254359Sroberto register struct peer *peer 46354359Sroberto ) 46454359Sroberto{ 46554359Sroberto 46654359Sroberto#ifdef DEBUG_DATUM_PTC 46754359Sroberto if (debug) 46854359Sroberto printf("Buginfo Datum PTS\n"); 46954359Sroberto#endif 47054359Sroberto 47154359Sroberto} 47254359Sroberto 47354359Sroberto 47454359Sroberto/*......................................................................*/ 47554359Sroberto/* datum_pts_receive - receive the time buffer that was read in */ 47654359Sroberto/* by the ntpd io handling routines. When 7 bytes have been */ 47754359Sroberto/* received (it may take several tries before all 7 bytes are */ 47854359Sroberto/* received), then the time code must be unpacked and sent to */ 47954359Sroberto/* the ntpd clock_receive() routine which causes the systems */ 48054359Sroberto/* clock to be updated (several layers down). */ 48154359Sroberto/*......................................................................*/ 48254359Sroberto 48354359Srobertostatic void 48454359Srobertodatum_pts_receive( 48554359Sroberto struct recvbuf *rbufp 48654359Sroberto ) 48754359Sroberto{ 48854359Sroberto int i; 48954359Sroberto l_fp tstmp; 490290001Sglebius struct peer *p; 49154359Sroberto struct datum_pts_unit *datum_pts; 49254359Sroberto char *dpt; 49354359Sroberto int dpend; 49454359Sroberto int tzoff; 49554359Sroberto int timerr; 49654359Sroberto double ftimerr, abserr; 49754359Sroberto#ifdef DEBUG_DATUM_PTC 49854359Sroberto double dispersion; 49954359Sroberto#endif 50054359Sroberto int goodtime; 50154359Sroberto /*double doffset;*/ 50254359Sroberto 50354359Sroberto /* 50454359Sroberto ** Get the time code (maybe partial) message out of the rbufp buffer. 50554359Sroberto */ 50654359Sroberto 507290001Sglebius p = rbufp->recv_peer; 508290001Sglebius datum_pts = p->procptr->unitptr; 50954359Sroberto dpt = (char *)&rbufp->recv_space; 51054359Sroberto dpend = rbufp->recv_length; 51154359Sroberto 51254359Sroberto#ifdef DEBUG_DATUM_PTC 51354359Sroberto if (debug) 514290001Sglebius printf("Receive Datum PTS: %d bytes\n", dpend); 51554359Sroberto#endif 51654359Sroberto 51754359Sroberto /* */ 51854359Sroberto /*...... save the ntp system time when the first byte is received ......*/ 51954359Sroberto /* */ 52054359Sroberto 52154359Sroberto /* 52254359Sroberto ** Save the ntp system time when the first byte is received. Note that 52354359Sroberto ** because it may take several calls to this routine before all seven 52454359Sroberto ** bytes of our return message are finally received by the io handlers in 52554359Sroberto ** ntpd, we really do want to use the time tag when the first byte is 52654359Sroberto ** received to reduce the jitter. 52754359Sroberto */ 52854359Sroberto 52954359Sroberto if (datum_pts->nbytes == 0) { 53054359Sroberto datum_pts->lastrec = rbufp->recv_time; 53154359Sroberto } 53254359Sroberto 53354359Sroberto /* 53454359Sroberto ** Increment our count to the number of bytes received so far. Return if we 53554359Sroberto ** haven't gotten all seven bytes yet. 53654359Sroberto */ 53754359Sroberto 53854359Sroberto for (i=0; i<dpend; i++) { 53954359Sroberto datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i]; 54054359Sroberto } 54154359Sroberto 54254359Sroberto datum_pts->nbytes += dpend; 54354359Sroberto 54454359Sroberto if (datum_pts->nbytes != 7) { 54554359Sroberto return; 54654359Sroberto } 54754359Sroberto 54854359Sroberto /* 54954359Sroberto ** Convert the seven bytes received in our time buffer to day, hour, minute, 55054359Sroberto ** second, and msecond values. The usec value is not used for anything 55154359Sroberto ** currently. It is just the fractional part of the time stored in units 55254359Sroberto ** of microseconds. 55354359Sroberto */ 55454359Sroberto 55554359Sroberto datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) + 55654359Sroberto 10*((datum_pts->retbuf[1] & 0xf0)>>4) + 55754359Sroberto (datum_pts->retbuf[1] & 0x0f); 55854359Sroberto 55954359Sroberto datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) + 56054359Sroberto (datum_pts->retbuf[2] & 0x0f); 56154359Sroberto 56254359Sroberto datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) + 56354359Sroberto (datum_pts->retbuf[3] & 0x0f); 56454359Sroberto 56554359Sroberto datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) + 56654359Sroberto (datum_pts->retbuf[4] & 0x0f); 56754359Sroberto 56854359Sroberto datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) + 56954359Sroberto 10*(datum_pts->retbuf[5] & 0x0f) + 57054359Sroberto ((datum_pts->retbuf[6] & 0xf0)>>4); 57154359Sroberto 57254359Sroberto datum_pts->usec = 1000*datum_pts->msec; 57354359Sroberto 57454359Sroberto#ifdef DEBUG_DATUM_PTC 57554359Sroberto if (debug) 57654359Sroberto printf("day %d, hour %d, minute %d, second %d, msec %d\n", 57754359Sroberto datum_pts->day, 57854359Sroberto datum_pts->hour, 57954359Sroberto datum_pts->minute, 58054359Sroberto datum_pts->second, 58154359Sroberto datum_pts->msec); 58254359Sroberto#endif 58354359Sroberto 58454359Sroberto /* 58554359Sroberto ** Get the GMT time zone offset. Note that GMT should be zero if the Datum 58654359Sroberto ** reference time is using GMT as its time base. Otherwise we have to 58754359Sroberto ** determine the offset if the Datum PTS is using time of day as its time 58854359Sroberto ** base. 58954359Sroberto */ 59054359Sroberto 59154359Sroberto goodtime = 0; /* We are not sure about the time and offset yet */ 59254359Sroberto 59354359Sroberto#ifdef GMT 59454359Sroberto 59554359Sroberto /* 59654359Sroberto ** This is the case where the Datum PTS is using GMT so there is no time 59754359Sroberto ** zone offset. 59854359Sroberto */ 59954359Sroberto 60054359Sroberto tzoff = 0; /* set time zone offset to 0 */ 60154359Sroberto 60254359Sroberto#else 60354359Sroberto 60454359Sroberto /* 60554359Sroberto ** This is the case where the Datum PTS is using regular time of day for its 60654359Sroberto ** time so we must compute the time zone offset. The way we do it is kind of 60754359Sroberto ** funny but it works. We loop through different time zones (0 to 24) and 60854359Sroberto ** pick the one that gives the smallest error (+- one half hour). The time 60954359Sroberto ** zone offset is stored in the datum_pts structure for future use. Normally, 61054359Sroberto ** the clocktime() routine is only called once (unless the time zone offset 61154359Sroberto ** changes due to daylight savings) since the goodtime flag is set when a 61254359Sroberto ** good time is found (with a good offset). Note that even if the Datum 61354359Sroberto ** PTS is using GMT, this mechanism will still work since it should come up 61454359Sroberto ** with a value for tzoff = 0 (assuming that your system clock is within 61554359Sroberto ** a half hour of the Datum time (even with time zone differences). 61654359Sroberto */ 61754359Sroberto 61854359Sroberto for (tzoff=0; tzoff<24; tzoff++) { 61954359Sroberto if (clocktime( datum_pts->day, 62054359Sroberto datum_pts->hour, 62154359Sroberto datum_pts->minute, 62254359Sroberto datum_pts->second, 62354359Sroberto (tzoff + datum_pts->tzoff) % 24, 62454359Sroberto datum_pts->lastrec.l_ui, 62554359Sroberto &datum_pts->yearstart, 62654359Sroberto &datum_pts->lastref.l_ui) ) { 62754359Sroberto 628132451Sroberto datum_pts->lastref.l_uf = 0; 62954359Sroberto error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui; 63054359Sroberto 63154359Sroberto#ifdef DEBUG_DATUM_PTC 63254359Sroberto printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error); 63354359Sroberto#endif 63454359Sroberto 63554359Sroberto if ((error < 1799) && (error > -1799)) { 63654359Sroberto tzoff = (tzoff + datum_pts->tzoff) % 24; 63754359Sroberto datum_pts->tzoff = tzoff; 63854359Sroberto goodtime = 1; 63954359Sroberto 64054359Sroberto#ifdef DEBUG_DATUM_PTC 64154359Sroberto printf("Time Zone found (clocktime method) = %d\n",tzoff); 64254359Sroberto#endif 64354359Sroberto 64454359Sroberto break; 64554359Sroberto } 64654359Sroberto 64754359Sroberto } 64854359Sroberto } 64954359Sroberto 65054359Sroberto#endif 65154359Sroberto 65254359Sroberto /* 65354359Sroberto ** Make sure that we have a good time from the Datum PTS. Clocktime() also 65454359Sroberto ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e., 65554359Sroberto ** the fraction of a second) stuff later. 65654359Sroberto */ 65754359Sroberto 65854359Sroberto if (!goodtime) { 65954359Sroberto 66054359Sroberto if (!clocktime( datum_pts->day, 66154359Sroberto datum_pts->hour, 66254359Sroberto datum_pts->minute, 66354359Sroberto datum_pts->second, 66454359Sroberto tzoff, 66554359Sroberto datum_pts->lastrec.l_ui, 66654359Sroberto &datum_pts->yearstart, 66754359Sroberto &datum_pts->lastref.l_ui) ) { 66854359Sroberto 66954359Sroberto#ifdef DEBUG_DATUM_PTC 67054359Sroberto if (debug) 67154359Sroberto { 67254359Sroberto printf("Error: bad clocktime\n"); 67354359Sroberto printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n", 67454359Sroberto tzoff, 67554359Sroberto datum_pts->lastrec.l_ui, 67654359Sroberto datum_pts->yearstart, 67754359Sroberto datum_pts->lastref.l_ui); 67854359Sroberto } 67954359Sroberto#endif 68054359Sroberto 68154359Sroberto msyslog(LOG_ERR, "Datum_PTS: Bad clocktime"); 68254359Sroberto 68354359Sroberto return; 68454359Sroberto 68554359Sroberto }else{ 68654359Sroberto 68754359Sroberto#ifdef DEBUG_DATUM_PTC 68854359Sroberto if (debug) 68954359Sroberto printf("Good clocktime\n"); 69054359Sroberto#endif 69154359Sroberto 69254359Sroberto } 69354359Sroberto 69454359Sroberto } 69554359Sroberto 69654359Sroberto /* 69754359Sroberto ** We have datum_pts->lastref.l_ui set (which is the integer part of the 69854359Sroberto ** time. Now set the microseconds field. 69954359Sroberto */ 70054359Sroberto 70154359Sroberto TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf); 70254359Sroberto 70354359Sroberto /* 70454359Sroberto ** Compute the time correction as the difference between the reference 70554359Sroberto ** time (i.e., the Datum time) minus the receive time (system time). 70654359Sroberto */ 70754359Sroberto 70854359Sroberto tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */ 70954359Sroberto L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */ 71054359Sroberto datum_pts->coderecv++; /* increment a counter */ 71154359Sroberto 71254359Sroberto#ifdef DEBUG_DATUM_PTC 71354359Sroberto dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */ 71454359Sroberto ftimerr = dispersion; 71554359Sroberto ftimerr /= (1024.0 * 64.0); 71654359Sroberto if (debug) 71754359Sroberto printf("dispersion = %d, %f\n", dispersion, ftimerr); 71854359Sroberto#endif 71954359Sroberto 72054359Sroberto /* 72154359Sroberto ** Pass the new time to ntpd through the refclock_receive function. Note 72254359Sroberto ** that we are not trying to make any corrections due to the time it takes 72354359Sroberto ** for the Datum PTS to send the message back. I am (erroneously) assuming 72454359Sroberto ** that the time for the Datum PTS to send the time back to us is negligable. 72554359Sroberto ** I suspect that this time delay may be as much as 15 ms or so (but probably 72654359Sroberto ** less). For our needs at JPL, this kind of error is ok so it is not 72754359Sroberto ** necessary to use fudge factors in the ntp.conf file. Maybe later we will. 72854359Sroberto */ 72954359Sroberto /*LFPTOD(&tstmp, doffset);*/ 730132451Sroberto datum_pts->lastref = datum_pts->lastrec; 73154359Sroberto refclock_receive(datum_pts->peer); 73254359Sroberto 73354359Sroberto /* 73454359Sroberto ** Compute sigma squared (not used currently). Maybe later, this could be 73554359Sroberto ** used for the dispersion estimate. The problem is that ntpd does not link 73654359Sroberto ** in the math library so sqrt() is not available. Anyway, this is useful 73754359Sroberto ** for debugging. Maybe later I will just use absolute values for the time 73854359Sroberto ** error to come up with my dispersion estimate. Anyway, for now my dispersion 73954359Sroberto ** is set to 0. 74054359Sroberto */ 74154359Sroberto 74254359Sroberto timerr = tstmp.l_ui<<20; 74354359Sroberto timerr |= (tstmp.l_uf>>12) & 0x000fffff; 74454359Sroberto ftimerr = timerr; 74554359Sroberto ftimerr /= 1024*1024; 74654359Sroberto abserr = ftimerr; 74754359Sroberto if (ftimerr < 0.0) abserr = -ftimerr; 74854359Sroberto 74954359Sroberto if (datum_pts->sigma2 == 0.0) { 75054359Sroberto if (abserr < DATUM_MAX_ERROR) { 75154359Sroberto datum_pts->sigma2 = abserr*abserr; 75254359Sroberto }else{ 75354359Sroberto datum_pts->sigma2 = DATUM_MAX_ERROR2; 75454359Sroberto } 75554359Sroberto }else{ 75654359Sroberto if (abserr < DATUM_MAX_ERROR) { 75754359Sroberto datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr; 75854359Sroberto }else{ 75954359Sroberto datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2; 76054359Sroberto } 76154359Sroberto } 76254359Sroberto 76354359Sroberto#ifdef DEBUG_DATUM_PTC 76454359Sroberto if (debug) 76554359Sroberto printf("Time error = %f seconds\n", ftimerr); 76654359Sroberto#endif 76754359Sroberto 76854359Sroberto#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) 76954359Sroberto if (debug) 77054359Sroberto printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n", 77154359Sroberto datum_pts->day, 77254359Sroberto datum_pts->hour, 77354359Sroberto datum_pts->minute, 77454359Sroberto datum_pts->second, 77554359Sroberto datum_pts->msec, 77654359Sroberto ftimerr); 77754359Sroberto#endif 77854359Sroberto 77954359Sroberto} 78054359Sroberto#else 781290001SglebiusNONEMPTY_TRANSLATION_UNIT 78254359Sroberto#endif /* REFCLOCK */ 783