refclock_datum.c revision 182007
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 1454359Sroberto#if defined(REFCLOCK) && defined(CLOCK_DATUM) 1554359Sroberto 1654359Sroberto/* 1754359Sroberto** Include Files 1854359Sroberto*/ 1954359Sroberto 2054359Sroberto#include "ntpd.h" 2154359Sroberto#include "ntp_io.h" 2254359Sroberto#include "ntp_refclock.h" 2354359Sroberto#include "ntp_unixtime.h" 2454359Sroberto#include "ntp_stdlib.h" 2554359Sroberto 2682498Sroberto#include <stdio.h> 2782498Sroberto#include <ctype.h> 2882498Sroberto 2954359Sroberto#if defined(HAVE_BSD_TTYS) 3054359Sroberto#include <sgtty.h> 3154359Sroberto#endif /* HAVE_BSD_TTYS */ 3254359Sroberto 3354359Sroberto#if defined(HAVE_SYSV_TTYS) 3454359Sroberto#include <termio.h> 3554359Sroberto#endif /* HAVE_SYSV_TTYS */ 3654359Sroberto 3754359Sroberto#if defined(HAVE_TERMIOS) 3854359Sroberto#include <termios.h> 3954359Sroberto#endif 4054359Sroberto#if defined(STREAM) 4154359Sroberto#include <stropts.h> 4254359Sroberto#if defined(WWVBCLK) 4354359Sroberto#include <sys/clkdefs.h> 4454359Sroberto#endif /* WWVBCLK */ 4554359Sroberto#endif /* STREAM */ 4654359Sroberto 4754359Sroberto#include "ntp_stdlib.h" 4854359Sroberto 4954359Sroberto/* 5054359Sroberto** This driver supports the Datum Programmable Time System (PTS) clock. 5154359Sroberto** The clock works in very straight forward manner. When it receives a 5254359Sroberto** time code request (e.g., the ascii string "//k/mn"), it responds with 5354359Sroberto** a seven byte BCD time code. This clock only responds with a 5454359Sroberto** time code after it first receives the "//k/mn" message. It does not 5554359Sroberto** periodically send time codes back at some rate once it is started. 5654359Sroberto** the returned time code can be broken down into the following fields. 5754359Sroberto** 5854359Sroberto** _______________________________ 5954359Sroberto** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 6054359Sroberto** =============================== 6154359Sroberto** byte 0: | - - - - | H D | 6254359Sroberto** =============================== 6354359Sroberto** byte 1: | T D | U D | 6454359Sroberto** =============================== 6554359Sroberto** byte 2: | - - | T H | U H | 6654359Sroberto** =============================== 6754359Sroberto** byte 3: | - | T M | U M | 6854359Sroberto** =============================== 6954359Sroberto** byte 4: | - | T S | U S | 7054359Sroberto** =============================== 7154359Sroberto** byte 5: | t S | h S | 7254359Sroberto** =============================== 7354359Sroberto** byte 6: | m S | - - - - | 7454359Sroberto** =============================== 7554359Sroberto** 7654359Sroberto** In the table above: 7754359Sroberto** 7854359Sroberto** "-" means don't care 7954359Sroberto** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days 8054359Sroberto** "T H", and "UH" means Tens and Units of Hours 8154359Sroberto** "T M", and "U M" means Tens and Units of Minutes 8254359Sroberto** "T S", and "U S" means Tens and Units of Seconds 8354359Sroberto** "t S", "h S", and "m S" means tenths, hundredths, and thousandths 8454359Sroberto** of seconds 8554359Sroberto** 8654359Sroberto** The Datum PTS communicates throught the RS232 port on your machine. 8754359Sroberto** Right now, it assumes that you have termios. This driver has been tested 8854359Sroberto** on SUN and HP workstations. The Datum PTS supports various IRIG and 8954359Sroberto** NASA input codes. This driver assumes that the name of the device is 9054359Sroberto** /dev/datum. You will need to make a soft link to your RS232 device or 9154359Sroberto** create a new driver to use this refclock. 9254359Sroberto*/ 9354359Sroberto 9454359Sroberto/* 9554359Sroberto** Datum PTS defines 9654359Sroberto*/ 9754359Sroberto 9854359Sroberto/* 9954359Sroberto** Note that if GMT is defined, then the Datum PTS must use Greenwich 10054359Sroberto** time. Otherwise, this driver allows the Datum PTS to use the current 10154359Sroberto** wall clock for its time. It determines the time zone offset by minimizing 10254359Sroberto** the error after trying several time zone offsets. If the Datum PTS 10354359Sroberto** time is Greenwich time and GMT is not defined, everything should still 10454359Sroberto** work since the time zone will be found to be 0. What this really means 10554359Sroberto** is that your system time (at least to start with) must be within the 10654359Sroberto** correct time by less than +- 30 minutes. The default is for GMT to not 10754359Sroberto** defined. If you really want to force GMT without the funny +- 30 minute 10854359Sroberto** stuff then you must define (uncomment) GMT below. 10954359Sroberto*/ 11054359Sroberto 11154359Sroberto/* 11254359Sroberto#define GMT 11354359Sroberto#define DEBUG_DATUM_PTC 11454359Sroberto#define LOG_TIME_ERRORS 11554359Sroberto*/ 11654359Sroberto 11754359Sroberto 11882498Sroberto#define PRECISION (-10) /* precision assumed 1/1024 ms */ 11982498Sroberto#define REFID "DATM" /* reference id */ 12054359Sroberto#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */ 12154359Sroberto#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */ 122182007Sroberto#define DATUM_DEV "/dev/datum" /* device name */ 12354359Sroberto 12454359Sroberto#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR) 12554359Sroberto 12654359Sroberto/* 12754359Sroberto** The Datum PTS structure 12854359Sroberto*/ 12954359Sroberto 13054359Sroberto/* 13154359Sroberto** I don't use a fixed array of MAXUNITS like everyone else just because 13254359Sroberto** I don't like to program that way. Sorry if this bothers anyone. I assume 13354359Sroberto** that you can use any id for your unit and I will search for it in a 13454359Sroberto** dynamic array of units until I find it. I was worried that users might 13554359Sroberto** enter a bad id in their configuration file (larger than MAXUNITS) and 13654359Sroberto** besides, it is just cleaner not to have to assume that you have a fixed 13754359Sroberto** number of anything in a program. 13854359Sroberto*/ 13954359Sroberto 14054359Srobertostruct datum_pts_unit { 14154359Sroberto struct peer *peer; /* peer used by ntp */ 14254359Sroberto struct refclockio io; /* io structure used by ntp */ 14354359Sroberto int PTS_fd; /* file descriptor for PTS */ 14454359Sroberto u_int unit; /* id for unit */ 14554359Sroberto u_long timestarted; /* time started */ 14654359Sroberto l_fp lastrec; /* time tag for the receive time (system) */ 14754359Sroberto l_fp lastref; /* reference time (Datum time) */ 14854359Sroberto u_long yearstart; /* the year that this clock started */ 14954359Sroberto int coderecv; /* number of time codes received */ 15054359Sroberto int day; /* day */ 15154359Sroberto int hour; /* hour */ 15254359Sroberto int minute; /* minutes */ 15354359Sroberto int second; /* seconds */ 15454359Sroberto int msec; /* miliseconds */ 15554359Sroberto int usec; /* miliseconds */ 15654359Sroberto u_char leap; /* funny leap character code */ 15754359Sroberto char retbuf[8]; /* returned time from the datum pts */ 15854359Sroberto char nbytes; /* number of bytes received from datum pts */ 15954359Sroberto double sigma2; /* average squared error (roughly) */ 16054359Sroberto int tzoff; /* time zone offest from GMT */ 16154359Sroberto}; 16254359Sroberto 16354359Sroberto/* 16454359Sroberto** PTS static constant variables for internal use 16554359Sroberto*/ 16654359Sroberto 16754359Srobertostatic char TIME_REQUEST[6]; /* request message sent to datum for time */ 16854359Srobertostatic int nunits; /* number of active units */ 16954359Srobertostatic struct datum_pts_unit 17054359Sroberto**datum_pts_unit; /* dynamic array of datum PTS structures */ 17154359Sroberto 17254359Sroberto/* 17354359Sroberto** Callback function prototypes that ntpd needs to know about. 17454359Sroberto*/ 17554359Sroberto 17654359Srobertostatic int datum_pts_start P((int, struct peer *)); 17754359Srobertostatic void datum_pts_shutdown P((int, struct peer *)); 17854359Srobertostatic void datum_pts_poll P((int, struct peer *)); 17954359Srobertostatic void datum_pts_control P((int, struct refclockstat *, 18054359Sroberto struct refclockstat *, struct peer *)); 18154359Srobertostatic void datum_pts_init P((void)); 18254359Srobertostatic void datum_pts_buginfo P((int, struct refclockbug *, struct peer *)); 18354359Sroberto 18454359Sroberto/* 18554359Sroberto** This is the call back function structure that ntpd actually uses for 18654359Sroberto** this refclock. 18754359Sroberto*/ 18854359Sroberto 18954359Srobertostruct refclock refclock_datum = { 19054359Sroberto datum_pts_start, /* start up a new Datum refclock */ 19154359Sroberto datum_pts_shutdown, /* shutdown a Datum refclock */ 19254359Sroberto datum_pts_poll, /* sends out the time request */ 19354359Sroberto datum_pts_control, /* not used */ 19454359Sroberto datum_pts_init, /* initialization (called first) */ 19554359Sroberto datum_pts_buginfo, /* not used */ 19654359Sroberto NOFLAGS /* we are not setting any special flags */ 19754359Sroberto}; 19854359Sroberto 19954359Sroberto/* 20054359Sroberto** The datum_pts_receive callback function is handled differently from the 20154359Sroberto** rest. It is passed to the ntpd io data structure. Basically, every 20254359Sroberto** 64 seconds, the datum_pts_poll() routine is called. It sends out the time 20354359Sroberto** request message to the Datum Programmable Time System. Then, ntpd 20454359Sroberto** waits on a select() call to receive data back. The datum_pts_receive() 20554359Sroberto** function is called as data comes back. We expect a seven byte time 20654359Sroberto** code to be returned but the datum_pts_receive() function may only get 20754359Sroberto** a few bytes passed to it at a time. In other words, this routine may 20854359Sroberto** get called by the io stuff in ntpd a few times before we get all seven 20954359Sroberto** bytes. Once the last byte is received, we process it and then pass the 21054359Sroberto** new time measurement to ntpd for updating the system time. For now, 21154359Sroberto** there is no 3 state filtering done on the time measurements. The 21254359Sroberto** jitter may be a little high but at least for its current use, it is not 21354359Sroberto** a problem. We have tried to keep things as simple as possible. This 21454359Sroberto** clock should not jitter more than 1 or 2 mseconds at the most once 21554359Sroberto** things settle down. It is important to get the right drift calibrated 21654359Sroberto** in the ntpd.drift file as well as getting the right tick set up right 21754359Sroberto** using tickadj for SUNs. Tickadj is not used for the HP but you need to 21854359Sroberto** remember to bring up the adjtime daemon because HP does not support 21954359Sroberto** the adjtime() call. 22054359Sroberto*/ 22154359Sroberto 22254359Srobertostatic void datum_pts_receive P((struct recvbuf *)); 22354359Sroberto 22454359Sroberto/*......................................................................*/ 22554359Sroberto/* datum_pts_start - start up the datum PTS. This means open the */ 22654359Sroberto/* RS232 device and set up the data structure for my unit. */ 22754359Sroberto/*......................................................................*/ 22854359Sroberto 22954359Srobertostatic int 23054359Srobertodatum_pts_start( 23154359Sroberto int unit, 23254359Sroberto struct peer *peer 23354359Sroberto ) 23454359Sroberto{ 23554359Sroberto struct datum_pts_unit **temp_datum_pts_unit; 23654359Sroberto struct datum_pts_unit *datum_pts; 237182007Sroberto int fd; 23854359Sroberto#ifdef HAVE_TERMIOS 23954359Sroberto struct termios arg; 24054359Sroberto#endif 24154359Sroberto 24254359Sroberto#ifdef DEBUG_DATUM_PTC 24354359Sroberto if (debug) 24454359Sroberto printf("Starting Datum PTS unit %d\n", unit); 24554359Sroberto#endif 24654359Sroberto 24754359Sroberto /* 248182007Sroberto ** Open the Datum PTS device 249182007Sroberto */ 250182007Sroberto fd = open(DATUM_DEV, O_RDWR); 251182007Sroberto 252182007Sroberto if (fd < 0) { 253182007Sroberto msyslog(LOG_ERR, "Datum_PTS: open(\"%s\", O_RDWR) failed: %m", DATUM_DEV); 254182007Sroberto return 0; 255182007Sroberto } 256182007Sroberto 257182007Sroberto /* 25854359Sroberto ** Create the memory for the new unit 25954359Sroberto */ 26054359Sroberto 26154359Sroberto temp_datum_pts_unit = (struct datum_pts_unit **) 26254359Sroberto malloc((nunits+1)*sizeof(struct datum_pts_unit *)); 26354359Sroberto if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit, 26454359Sroberto nunits*sizeof(struct datum_pts_unit *)); 26554359Sroberto free(datum_pts_unit); 26654359Sroberto datum_pts_unit = temp_datum_pts_unit; 26754359Sroberto datum_pts_unit[nunits] = (struct datum_pts_unit *) 26854359Sroberto malloc(sizeof(struct datum_pts_unit)); 26954359Sroberto datum_pts = datum_pts_unit[nunits]; 27054359Sroberto 27154359Sroberto datum_pts->unit = unit; /* set my unit id */ 27254359Sroberto datum_pts->yearstart = 0; /* initialize the yearstart to 0 */ 27354359Sroberto datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */ 27454359Sroberto 275182007Sroberto datum_pts->PTS_fd = fd; 27654359Sroberto 27754359Sroberto fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */ 27854359Sroberto 27954359Sroberto#ifdef DEBUG_DATUM_PTC 28054359Sroberto if (debug) 28154359Sroberto printf("Opening RS232 port with file descriptor %d\n", 28254359Sroberto datum_pts->PTS_fd); 28354359Sroberto#endif 28454359Sroberto 28554359Sroberto /* 28654359Sroberto ** Set up the RS232 terminal device information. Note that we assume that 28754359Sroberto ** we have termios. This code has only been tested on SUNs and HPs. If your 28854359Sroberto ** machine does not have termios this driver cannot be initialized. You can change this 28954359Sroberto ** if you want by editing this source. Please give the changes back to the 29054359Sroberto ** ntp folks so that it can become part of their regular distribution. 29154359Sroberto */ 29254359Sroberto 29354359Sroberto#ifdef HAVE_TERMIOS 29454359Sroberto 29554359Sroberto arg.c_iflag = IGNBRK; 29654359Sroberto arg.c_oflag = 0; 29754359Sroberto arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL; 29854359Sroberto arg.c_lflag = 0; 29954359Sroberto arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */ 30054359Sroberto arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */ 30154359Sroberto 30254359Sroberto tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg); 30354359Sroberto 30454359Sroberto#else 30554359Sroberto 30654359Sroberto msyslog(LOG_ERR, "Datum_PTS: Termios not supported in this driver"); 30754359Sroberto (void)close(datum_pts->PTS_fd); 30854359Sroberto 30982498Sroberto peer->precision = PRECISION; 31082498Sroberto pp->clockdesc = DESCRIPTION; 31182498Sroberto memcpy((char *)&pp->refid, REFID, 4); 31282498Sroberto 31354359Sroberto return 0; 31454359Sroberto 31554359Sroberto#endif 31654359Sroberto 31754359Sroberto /* 31854359Sroberto ** Initialize the ntpd IO structure 31954359Sroberto */ 32054359Sroberto 32154359Sroberto datum_pts->peer = peer; 32254359Sroberto datum_pts->io.clock_recv = datum_pts_receive; 32354359Sroberto datum_pts->io.srcclock = (caddr_t)datum_pts; 32454359Sroberto datum_pts->io.datalen = 0; 32554359Sroberto datum_pts->io.fd = datum_pts->PTS_fd; 32654359Sroberto 32754359Sroberto if (!io_addclock(&(datum_pts->io))) { 32854359Sroberto 32954359Sroberto#ifdef DEBUG_DATUM_PTC 33054359Sroberto if (debug) 33154359Sroberto printf("Problem adding clock\n"); 33254359Sroberto#endif 33354359Sroberto 33454359Sroberto msyslog(LOG_ERR, "Datum_PTS: Problem adding clock"); 33554359Sroberto (void)close(datum_pts->PTS_fd); 33654359Sroberto 33754359Sroberto return 0; 33854359Sroberto } 33954359Sroberto 34054359Sroberto /* 34154359Sroberto ** Now add one to the number of units and return a successful code 34254359Sroberto */ 34354359Sroberto 34454359Sroberto nunits++; 34554359Sroberto return 1; 34654359Sroberto 34754359Sroberto} 34854359Sroberto 34954359Sroberto 35054359Sroberto/*......................................................................*/ 35154359Sroberto/* datum_pts_shutdown - this routine shuts doen the device and */ 35254359Sroberto/* removes the memory for the unit. */ 35354359Sroberto/*......................................................................*/ 35454359Sroberto 35554359Srobertostatic void 35654359Srobertodatum_pts_shutdown( 35754359Sroberto int unit, 35854359Sroberto struct peer *peer 35954359Sroberto ) 36054359Sroberto{ 36154359Sroberto int i,j; 36254359Sroberto struct datum_pts_unit **temp_datum_pts_unit; 36354359Sroberto 36454359Sroberto#ifdef DEBUG_DATUM_PTC 36554359Sroberto if (debug) 36654359Sroberto printf("Shutdown Datum PTS\n"); 36754359Sroberto#endif 36854359Sroberto 36954359Sroberto msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS"); 37054359Sroberto 37154359Sroberto /* 37254359Sroberto ** First we have to find the right unit (i.e., the one with the same id). 37354359Sroberto ** We do this by looping through the dynamic array of units intil we find 37454359Sroberto ** it. Note, that I don't simply use an array with a maximimum number of 37554359Sroberto ** Datum PTS units. Everything is completely dynamic. 37654359Sroberto */ 37754359Sroberto 37854359Sroberto for (i=0; i<nunits; i++) { 37954359Sroberto if (datum_pts_unit[i]->unit == unit) { 38054359Sroberto 38154359Sroberto /* 38254359Sroberto ** We found the unit so close the file descriptor and free up the memory used 38354359Sroberto ** by the structure. 38454359Sroberto */ 38554359Sroberto 38654359Sroberto io_closeclock(&datum_pts_unit[i]->io); 38754359Sroberto close(datum_pts_unit[i]->PTS_fd); 38854359Sroberto free(datum_pts_unit[i]); 38954359Sroberto 39054359Sroberto /* 39154359Sroberto ** Now clean up the datum_pts_unit dynamic array so that there are no holes. 39254359Sroberto ** This may mean moving pointers around, etc., to keep things compact. 39354359Sroberto */ 39454359Sroberto 39554359Sroberto if (nunits > 1) { 39654359Sroberto 39754359Sroberto temp_datum_pts_unit = (struct datum_pts_unit **) 39854359Sroberto malloc((nunits-1)*sizeof(struct datum_pts_unit *)); 39954359Sroberto if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit, 40054359Sroberto i*sizeof(struct datum_pts_unit *)); 40154359Sroberto 40254359Sroberto for (j=i+1; j<nunits; j++) { 40354359Sroberto temp_datum_pts_unit[j-1] = datum_pts_unit[j]; 40454359Sroberto } 40554359Sroberto 40654359Sroberto free(datum_pts_unit); 40754359Sroberto datum_pts_unit = temp_datum_pts_unit; 40854359Sroberto 40954359Sroberto }else{ 41054359Sroberto 41154359Sroberto free(datum_pts_unit); 41254359Sroberto datum_pts_unit = NULL; 41354359Sroberto 41454359Sroberto } 41554359Sroberto 41654359Sroberto return; 41754359Sroberto 41854359Sroberto } 41954359Sroberto } 42054359Sroberto 42154359Sroberto#ifdef DEBUG_DATUM_PTC 42254359Sroberto if (debug) 42354359Sroberto printf("Error, could not shut down unit %d\n",unit); 42454359Sroberto#endif 42554359Sroberto 42654359Sroberto msyslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit); 42754359Sroberto 42854359Sroberto} 42954359Sroberto 43054359Sroberto/*......................................................................*/ 43154359Sroberto/* datum_pts_poll - this routine sends out the time request to the */ 43254359Sroberto/* Datum PTS device. The time will be passed back in the */ 43354359Sroberto/* datum_pts_receive() routine. */ 43454359Sroberto/*......................................................................*/ 43554359Sroberto 43654359Srobertostatic void 43754359Srobertodatum_pts_poll( 43854359Sroberto int unit, 43954359Sroberto struct peer *peer 44054359Sroberto ) 44154359Sroberto{ 44254359Sroberto int i; 44382498Sroberto int unit_index; 44454359Sroberto int error_code; 44554359Sroberto struct datum_pts_unit *datum_pts; 44654359Sroberto 44754359Sroberto#ifdef DEBUG_DATUM_PTC 44854359Sroberto if (debug) 44954359Sroberto printf("Poll Datum PTS\n"); 45054359Sroberto#endif 45154359Sroberto 45254359Sroberto /* 45354359Sroberto ** Find the right unit and send out a time request once it is found. 45454359Sroberto */ 45554359Sroberto 45682498Sroberto unit_index = -1; 45754359Sroberto for (i=0; i<nunits; i++) { 45854359Sroberto if (datum_pts_unit[i]->unit == unit) { 45982498Sroberto unit_index = i; 46054359Sroberto datum_pts = datum_pts_unit[i]; 46154359Sroberto error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6); 46254359Sroberto if (error_code != 6) perror("TIME_REQUEST"); 46354359Sroberto datum_pts->nbytes = 0; 46454359Sroberto break; 46554359Sroberto } 46654359Sroberto } 46754359Sroberto 46854359Sroberto /* 46954359Sroberto ** Print out an error message if we could not find the right unit. 47054359Sroberto */ 47154359Sroberto 47282498Sroberto if (unit_index == -1) { 47354359Sroberto 47454359Sroberto#ifdef DEBUG_DATUM_PTC 47554359Sroberto if (debug) 47654359Sroberto printf("Error, could not poll unit %d\n",unit); 47754359Sroberto#endif 47854359Sroberto 47954359Sroberto msyslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit); 48054359Sroberto return; 48154359Sroberto 48254359Sroberto } 48354359Sroberto 48454359Sroberto} 48554359Sroberto 48654359Sroberto 48754359Sroberto/*......................................................................*/ 48854359Sroberto/* datum_pts_control - not used */ 48954359Sroberto/*......................................................................*/ 49054359Sroberto 49154359Srobertostatic void 49254359Srobertodatum_pts_control( 49354359Sroberto int unit, 49454359Sroberto struct refclockstat *in, 49554359Sroberto struct refclockstat *out, 49654359Sroberto struct peer *peer 49754359Sroberto ) 49854359Sroberto{ 49954359Sroberto 50054359Sroberto#ifdef DEBUG_DATUM_PTC 50154359Sroberto if (debug) 50254359Sroberto printf("Control Datum PTS\n"); 50354359Sroberto#endif 50454359Sroberto 50554359Sroberto} 50654359Sroberto 50754359Sroberto 50854359Sroberto/*......................................................................*/ 50954359Sroberto/* datum_pts_init - initializes things for all possible Datum */ 51054359Sroberto/* time code generators that might be used. In practice, this is */ 51154359Sroberto/* only called once at the beginning before anything else is */ 51254359Sroberto/* called. */ 51354359Sroberto/*......................................................................*/ 51454359Sroberto 51554359Srobertostatic void 51654359Srobertodatum_pts_init(void) 51754359Sroberto{ 51854359Sroberto 51954359Sroberto /* */ 52054359Sroberto /*...... open up the log file if we are debugging ......................*/ 52154359Sroberto /* */ 52254359Sroberto 52354359Sroberto /* 52454359Sroberto ** Open up the log file if we are debugging. For now, send data out to the 52554359Sroberto ** screen (stdout). 52654359Sroberto */ 52754359Sroberto 52854359Sroberto#ifdef DEBUG_DATUM_PTC 52954359Sroberto if (debug) 53054359Sroberto printf("Init Datum PTS\n"); 53154359Sroberto#endif 53254359Sroberto 53354359Sroberto /* 53454359Sroberto ** Initialize the time request command string. This is the only message 53554359Sroberto ** that we ever have to send to the Datum PTS (although others are defined). 53654359Sroberto */ 53754359Sroberto 53854359Sroberto memcpy(TIME_REQUEST, "//k/mn",6); 53954359Sroberto 54054359Sroberto /* 54154359Sroberto ** Initialize the number of units to 0 and set the dynamic array of units to 54254359Sroberto ** NULL since there are no units defined yet. 54354359Sroberto */ 54454359Sroberto 54554359Sroberto datum_pts_unit = NULL; 54654359Sroberto nunits = 0; 54754359Sroberto 54854359Sroberto} 54954359Sroberto 55054359Sroberto 55154359Sroberto/*......................................................................*/ 55254359Sroberto/* datum_pts_buginfo - not used */ 55354359Sroberto/*......................................................................*/ 55454359Sroberto 55554359Srobertostatic void 55654359Srobertodatum_pts_buginfo( 55754359Sroberto int unit, 55854359Sroberto register struct refclockbug *bug, 55954359Sroberto register struct peer *peer 56054359Sroberto ) 56154359Sroberto{ 56254359Sroberto 56354359Sroberto#ifdef DEBUG_DATUM_PTC 56454359Sroberto if (debug) 56554359Sroberto printf("Buginfo Datum PTS\n"); 56654359Sroberto#endif 56754359Sroberto 56854359Sroberto} 56954359Sroberto 57054359Sroberto 57154359Sroberto/*......................................................................*/ 57254359Sroberto/* datum_pts_receive - receive the time buffer that was read in */ 57354359Sroberto/* by the ntpd io handling routines. When 7 bytes have been */ 57454359Sroberto/* received (it may take several tries before all 7 bytes are */ 57554359Sroberto/* received), then the time code must be unpacked and sent to */ 57654359Sroberto/* the ntpd clock_receive() routine which causes the systems */ 57754359Sroberto/* clock to be updated (several layers down). */ 57854359Sroberto/*......................................................................*/ 57954359Sroberto 58054359Srobertostatic void 58154359Srobertodatum_pts_receive( 58254359Sroberto struct recvbuf *rbufp 58354359Sroberto ) 58454359Sroberto{ 58554359Sroberto int i; 58654359Sroberto l_fp tstmp; 58754359Sroberto struct datum_pts_unit *datum_pts; 58854359Sroberto char *dpt; 58954359Sroberto int dpend; 59054359Sroberto int tzoff; 59154359Sroberto int timerr; 59254359Sroberto double ftimerr, abserr; 59354359Sroberto#ifdef DEBUG_DATUM_PTC 59454359Sroberto double dispersion; 59554359Sroberto#endif 59654359Sroberto int goodtime; 59754359Sroberto /*double doffset;*/ 59854359Sroberto 59954359Sroberto /* 60054359Sroberto ** Get the time code (maybe partial) message out of the rbufp buffer. 60154359Sroberto */ 60254359Sroberto 60354359Sroberto datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock; 60454359Sroberto dpt = (char *)&rbufp->recv_space; 60554359Sroberto dpend = rbufp->recv_length; 60654359Sroberto 60754359Sroberto#ifdef DEBUG_DATUM_PTC 60854359Sroberto if (debug) 60954359Sroberto printf("Receive Datum PTS: %d bytes\n", dpend); 61054359Sroberto#endif 61154359Sroberto 61254359Sroberto /* */ 61354359Sroberto /*...... save the ntp system time when the first byte is received ......*/ 61454359Sroberto /* */ 61554359Sroberto 61654359Sroberto /* 61754359Sroberto ** Save the ntp system time when the first byte is received. Note that 61854359Sroberto ** because it may take several calls to this routine before all seven 61954359Sroberto ** bytes of our return message are finally received by the io handlers in 62054359Sroberto ** ntpd, we really do want to use the time tag when the first byte is 62154359Sroberto ** received to reduce the jitter. 62254359Sroberto */ 62354359Sroberto 62454359Sroberto if (datum_pts->nbytes == 0) { 62554359Sroberto datum_pts->lastrec = rbufp->recv_time; 62654359Sroberto } 62754359Sroberto 62854359Sroberto /* 62954359Sroberto ** Increment our count to the number of bytes received so far. Return if we 63054359Sroberto ** haven't gotten all seven bytes yet. 63154359Sroberto */ 63254359Sroberto 63354359Sroberto for (i=0; i<dpend; i++) { 63454359Sroberto datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i]; 63554359Sroberto } 63654359Sroberto 63754359Sroberto datum_pts->nbytes += dpend; 63854359Sroberto 63954359Sroberto if (datum_pts->nbytes != 7) { 64054359Sroberto return; 64154359Sroberto } 64254359Sroberto 64354359Sroberto /* 64454359Sroberto ** Convert the seven bytes received in our time buffer to day, hour, minute, 64554359Sroberto ** second, and msecond values. The usec value is not used for anything 64654359Sroberto ** currently. It is just the fractional part of the time stored in units 64754359Sroberto ** of microseconds. 64854359Sroberto */ 64954359Sroberto 65054359Sroberto datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) + 65154359Sroberto 10*((datum_pts->retbuf[1] & 0xf0)>>4) + 65254359Sroberto (datum_pts->retbuf[1] & 0x0f); 65354359Sroberto 65454359Sroberto datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) + 65554359Sroberto (datum_pts->retbuf[2] & 0x0f); 65654359Sroberto 65754359Sroberto datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) + 65854359Sroberto (datum_pts->retbuf[3] & 0x0f); 65954359Sroberto 66054359Sroberto datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) + 66154359Sroberto (datum_pts->retbuf[4] & 0x0f); 66254359Sroberto 66354359Sroberto datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) + 66454359Sroberto 10*(datum_pts->retbuf[5] & 0x0f) + 66554359Sroberto ((datum_pts->retbuf[6] & 0xf0)>>4); 66654359Sroberto 66754359Sroberto datum_pts->usec = 1000*datum_pts->msec; 66854359Sroberto 66954359Sroberto#ifdef DEBUG_DATUM_PTC 67054359Sroberto if (debug) 67154359Sroberto printf("day %d, hour %d, minute %d, second %d, msec %d\n", 67254359Sroberto datum_pts->day, 67354359Sroberto datum_pts->hour, 67454359Sroberto datum_pts->minute, 67554359Sroberto datum_pts->second, 67654359Sroberto datum_pts->msec); 67754359Sroberto#endif 67854359Sroberto 67954359Sroberto /* 68054359Sroberto ** Get the GMT time zone offset. Note that GMT should be zero if the Datum 68154359Sroberto ** reference time is using GMT as its time base. Otherwise we have to 68254359Sroberto ** determine the offset if the Datum PTS is using time of day as its time 68354359Sroberto ** base. 68454359Sroberto */ 68554359Sroberto 68654359Sroberto goodtime = 0; /* We are not sure about the time and offset yet */ 68754359Sroberto 68854359Sroberto#ifdef GMT 68954359Sroberto 69054359Sroberto /* 69154359Sroberto ** This is the case where the Datum PTS is using GMT so there is no time 69254359Sroberto ** zone offset. 69354359Sroberto */ 69454359Sroberto 69554359Sroberto tzoff = 0; /* set time zone offset to 0 */ 69654359Sroberto 69754359Sroberto#else 69854359Sroberto 69954359Sroberto /* 70054359Sroberto ** This is the case where the Datum PTS is using regular time of day for its 70154359Sroberto ** time so we must compute the time zone offset. The way we do it is kind of 70254359Sroberto ** funny but it works. We loop through different time zones (0 to 24) and 70354359Sroberto ** pick the one that gives the smallest error (+- one half hour). The time 70454359Sroberto ** zone offset is stored in the datum_pts structure for future use. Normally, 70554359Sroberto ** the clocktime() routine is only called once (unless the time zone offset 70654359Sroberto ** changes due to daylight savings) since the goodtime flag is set when a 70754359Sroberto ** good time is found (with a good offset). Note that even if the Datum 70854359Sroberto ** PTS is using GMT, this mechanism will still work since it should come up 70954359Sroberto ** with a value for tzoff = 0 (assuming that your system clock is within 71054359Sroberto ** a half hour of the Datum time (even with time zone differences). 71154359Sroberto */ 71254359Sroberto 71354359Sroberto for (tzoff=0; tzoff<24; tzoff++) { 71454359Sroberto if (clocktime( datum_pts->day, 71554359Sroberto datum_pts->hour, 71654359Sroberto datum_pts->minute, 71754359Sroberto datum_pts->second, 71854359Sroberto (tzoff + datum_pts->tzoff) % 24, 71954359Sroberto datum_pts->lastrec.l_ui, 72054359Sroberto &datum_pts->yearstart, 72154359Sroberto &datum_pts->lastref.l_ui) ) { 72254359Sroberto 723132451Sroberto datum_pts->lastref.l_uf = 0; 72454359Sroberto error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui; 72554359Sroberto 72654359Sroberto#ifdef DEBUG_DATUM_PTC 72754359Sroberto printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error); 72854359Sroberto#endif 72954359Sroberto 73054359Sroberto if ((error < 1799) && (error > -1799)) { 73154359Sroberto tzoff = (tzoff + datum_pts->tzoff) % 24; 73254359Sroberto datum_pts->tzoff = tzoff; 73354359Sroberto goodtime = 1; 73454359Sroberto 73554359Sroberto#ifdef DEBUG_DATUM_PTC 73654359Sroberto printf("Time Zone found (clocktime method) = %d\n",tzoff); 73754359Sroberto#endif 73854359Sroberto 73954359Sroberto break; 74054359Sroberto } 74154359Sroberto 74254359Sroberto } 74354359Sroberto } 74454359Sroberto 74554359Sroberto#endif 74654359Sroberto 74754359Sroberto /* 74854359Sroberto ** Make sure that we have a good time from the Datum PTS. Clocktime() also 74954359Sroberto ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e., 75054359Sroberto ** the fraction of a second) stuff later. 75154359Sroberto */ 75254359Sroberto 75354359Sroberto if (!goodtime) { 75454359Sroberto 75554359Sroberto if (!clocktime( datum_pts->day, 75654359Sroberto datum_pts->hour, 75754359Sroberto datum_pts->minute, 75854359Sroberto datum_pts->second, 75954359Sroberto tzoff, 76054359Sroberto datum_pts->lastrec.l_ui, 76154359Sroberto &datum_pts->yearstart, 76254359Sroberto &datum_pts->lastref.l_ui) ) { 76354359Sroberto 76454359Sroberto#ifdef DEBUG_DATUM_PTC 76554359Sroberto if (debug) 76654359Sroberto { 76754359Sroberto printf("Error: bad clocktime\n"); 76854359Sroberto printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n", 76954359Sroberto tzoff, 77054359Sroberto datum_pts->lastrec.l_ui, 77154359Sroberto datum_pts->yearstart, 77254359Sroberto datum_pts->lastref.l_ui); 77354359Sroberto } 77454359Sroberto#endif 77554359Sroberto 77654359Sroberto msyslog(LOG_ERR, "Datum_PTS: Bad clocktime"); 77754359Sroberto 77854359Sroberto return; 77954359Sroberto 78054359Sroberto }else{ 78154359Sroberto 78254359Sroberto#ifdef DEBUG_DATUM_PTC 78354359Sroberto if (debug) 78454359Sroberto printf("Good clocktime\n"); 78554359Sroberto#endif 78654359Sroberto 78754359Sroberto } 78854359Sroberto 78954359Sroberto } 79054359Sroberto 79154359Sroberto /* 79254359Sroberto ** We have datum_pts->lastref.l_ui set (which is the integer part of the 79354359Sroberto ** time. Now set the microseconds field. 79454359Sroberto */ 79554359Sroberto 79654359Sroberto TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf); 79754359Sroberto 79854359Sroberto /* 79954359Sroberto ** Compute the time correction as the difference between the reference 80054359Sroberto ** time (i.e., the Datum time) minus the receive time (system time). 80154359Sroberto */ 80254359Sroberto 80354359Sroberto tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */ 80454359Sroberto L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */ 80554359Sroberto datum_pts->coderecv++; /* increment a counter */ 80654359Sroberto 80754359Sroberto#ifdef DEBUG_DATUM_PTC 80854359Sroberto dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */ 80954359Sroberto ftimerr = dispersion; 81054359Sroberto ftimerr /= (1024.0 * 64.0); 81154359Sroberto if (debug) 81254359Sroberto printf("dispersion = %d, %f\n", dispersion, ftimerr); 81354359Sroberto#endif 81454359Sroberto 81554359Sroberto /* 81654359Sroberto ** Pass the new time to ntpd through the refclock_receive function. Note 81754359Sroberto ** that we are not trying to make any corrections due to the time it takes 81854359Sroberto ** for the Datum PTS to send the message back. I am (erroneously) assuming 81954359Sroberto ** that the time for the Datum PTS to send the time back to us is negligable. 82054359Sroberto ** I suspect that this time delay may be as much as 15 ms or so (but probably 82154359Sroberto ** less). For our needs at JPL, this kind of error is ok so it is not 82254359Sroberto ** necessary to use fudge factors in the ntp.conf file. Maybe later we will. 82354359Sroberto */ 82454359Sroberto /*LFPTOD(&tstmp, doffset);*/ 825132451Sroberto datum_pts->lastref = datum_pts->lastrec; 82654359Sroberto refclock_receive(datum_pts->peer); 82754359Sroberto 82854359Sroberto /* 82954359Sroberto ** Compute sigma squared (not used currently). Maybe later, this could be 83054359Sroberto ** used for the dispersion estimate. The problem is that ntpd does not link 83154359Sroberto ** in the math library so sqrt() is not available. Anyway, this is useful 83254359Sroberto ** for debugging. Maybe later I will just use absolute values for the time 83354359Sroberto ** error to come up with my dispersion estimate. Anyway, for now my dispersion 83454359Sroberto ** is set to 0. 83554359Sroberto */ 83654359Sroberto 83754359Sroberto timerr = tstmp.l_ui<<20; 83854359Sroberto timerr |= (tstmp.l_uf>>12) & 0x000fffff; 83954359Sroberto ftimerr = timerr; 84054359Sroberto ftimerr /= 1024*1024; 84154359Sroberto abserr = ftimerr; 84254359Sroberto if (ftimerr < 0.0) abserr = -ftimerr; 84354359Sroberto 84454359Sroberto if (datum_pts->sigma2 == 0.0) { 84554359Sroberto if (abserr < DATUM_MAX_ERROR) { 84654359Sroberto datum_pts->sigma2 = abserr*abserr; 84754359Sroberto }else{ 84854359Sroberto datum_pts->sigma2 = DATUM_MAX_ERROR2; 84954359Sroberto } 85054359Sroberto }else{ 85154359Sroberto if (abserr < DATUM_MAX_ERROR) { 85254359Sroberto datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr; 85354359Sroberto }else{ 85454359Sroberto datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2; 85554359Sroberto } 85654359Sroberto } 85754359Sroberto 85854359Sroberto#ifdef DEBUG_DATUM_PTC 85954359Sroberto if (debug) 86054359Sroberto printf("Time error = %f seconds\n", ftimerr); 86154359Sroberto#endif 86254359Sroberto 86354359Sroberto#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) 86454359Sroberto if (debug) 86554359Sroberto printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n", 86654359Sroberto datum_pts->day, 86754359Sroberto datum_pts->hour, 86854359Sroberto datum_pts->minute, 86954359Sroberto datum_pts->second, 87054359Sroberto datum_pts->msec, 87154359Sroberto ftimerr); 87254359Sroberto#endif 87354359Sroberto 87454359Sroberto} 87554359Sroberto#else 87654359Srobertoint refclock_datum_bs; 87754359Sroberto#endif /* REFCLOCK */ 878