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