Home | History | Annotate | Line # | Download | only in ntpd
      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