refclock_oncore.c revision 56746
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * refclock_oncore.c
10 *
11 * Driver for some of the various the Motorola Oncore GPS receivers.
12 *
13 * Tested with:
14 *
15 *		(UT)				   (VP)
16 *   COPYRIGHT 1991-1997 MOTOROLA INC.	COPYRIGHT 1991-1996 MOTOROLA INC.
17 *   SFTW P/N #     98-P36848P		SFTW P/N # 98-P36830P
18 *   SOFTWARE VER # 2			SOFTWARE VER # 8
19 *   SOFTWARE REV # 2			SOFTWARE REV # 8
20 *   SOFTWARE DATE  APR 24 1998 	SOFTWARE DATE  06 Aug 1996
21 *   MODEL #	R1121N1114		MODEL #    B4121P1155
22 *   HWDR P/N # 1			HDWR P/N # _
23 *   SERIAL #	R0010A			SERIAL #   SSG0226478
24 *   MANUFACTUR DATE 6H07		MANUFACTUR DATE 7E02
25 *					OPTIONS LIST	IB
26 *
27 * --------------------------------------------------------------------------
28 * This code uses the two devices
29 *      /dev/oncore.serial.n
30 *      /dev/oncore.pps.n
31 * which may be linked to the same device.
32 * and can read initialization data from the file
33 *      /etc/ntp.oncoreN (where n and N are the unit number, viz 127.127.30.N)
34 *  or	/etc/ntp.oncore
35 * --------------------------------------------------------------------------
36 * Reg.Clemens <reg@dwf.com> Sep98.
37 *  Original code written for FreeBSD.
38 *  With these mods it works on SunOS, Solaris (untested) and Linux
39 *    (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + changes).
40 *
41 *  Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
42 *  state machine state) are printed to CLOCKSTATS if that file is enabled
43 *  in /etc/ntp.conf.
44 *
45 * --------------------------------------------------------------------------
46 */
47
48/*
49 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
50 * doing an average of 10000 valid 2D and 3D fixes is what the automatic
51 * site survey mode does.  Looking at the output from the receiver
52 * it seems like it is only using 3D fixes.
53 * When we do it ourselves, take 10000 3D fixes.
54 */
55
56#define POS_HOLD_AVERAGE	10000	/* nb, 10000s ~= 2h45m */
57
58/*
59 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
60 * "STATUS" line in the oncore config file, which contains the most recent
61 * copy of all types of messages we recognize.  This file can be mmap(2)'ed
62 * by monitoring and statistics programs.
63 */
64
65#ifdef HAVE_CONFIG_H
66#include <config.h>
67#endif
68
69#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
70
71#include <stdio.h>
72#include <ctype.h>
73#include <sys/types.h>
74#include <sys/time.h>
75#include <sys/stat.h>
76#ifdef ONCORE_SHMEM_STATUS
77# ifdef HAVE_SYS_MMAN_H
78#  include <sys/mman.h>
79#  ifndef MAP_FAILED
80#   define MAP_FAILED ((u_char *) -1)
81#  endif  /* not MAP_FAILED */
82# endif /* HAVE_SYS_MMAN_H */
83#endif /* ONCORE_SHMEM_STATUS */
84
85#ifdef HAVE_PPSAPI
86#  ifdef HAVE_TIMEPPS_H
87#    include <timepps.h>
88# else
89#  ifdef HAVE_SYS_TIMEPPS_H
90#    include <sys/timepps.h>
91#  endif
92# endif
93#endif
94
95#ifdef HAVE_SYS_SIO_H
96# include <sys/sio.h>
97#endif
98
99#include "ntpd.h"
100#include "ntp_io.h"
101#include "ntp_unixtime.h"
102#include "ntp_refclock.h"
103#include "ntp_stdlib.h"
104
105#ifdef HAVE_SYS_TERMIOS_H
106#include <sys/termios.h>
107#endif
108
109#ifdef HAVE_SYS_PPSCLOCK_H
110# include <sys/ppsclock.h>
111#endif
112
113#ifndef HAVE_STRUCT_PPSCLOCKEV
114struct ppsclockev {
115# ifdef HAVE_TIMESPEC
116	struct timespec tv;
117# else
118	struct timeval tv;
119# endif
120	u_int serial;
121};
122#endif /* not HAVE_STRUCT_PPSCLOCKEV */
123
124enum receive_state {
125	ONCORE_NO_IDEA,
126	ONCORE_RESET_SENT,
127	ONCORE_TEST_SENT,
128	ONCORE_ID_SENT,
129	ONCORE_ALMANAC,
130	ONCORE_RUN
131};
132
133enum site_survey_state {
134	ONCORE_SS_UNKNOWN,
135	ONCORE_SS_HW,
136	ONCORE_SS_SW,
137	ONCORE_SS_DONE
138};
139
140struct instance {
141	int	unit;		/* 127.127.30.unit */
142	int	ttyfd;		/* TTY file descriptor */
143	int	ppsfd;		/* PPS file descriptor */
144	int	statusfd;	/* Status shm descriptor */
145	u_char	*shmem;
146#ifdef HAVE_PPSAPI
147	pps_handle_t pps_h;
148	pps_params_t pps_p;
149#endif
150	enum receive_state o_state;		/* Receive state */
151
152	enum site_survey_state site_survey;	/* Site Survey state */
153
154	struct	refclockproc *pp;
155	struct	peer *peer;
156
157	int	Bj_day;
158
159	long	delay;		/* ns */
160	long	offset; 	/* ns */
161
162	double	ss_lat;
163	double	ss_long;
164	double	ss_ht;
165	int	ss_count;
166	u_char	ss_ht_type;
167	u_char  posn_set;
168
169	u_char  printed;
170	u_char  polled;
171	int	pollcnt;
172	u_int	ev_serial;
173	int	Rcvptr;
174	u_char	Rcvbuf[500];
175	u_char	Ea[77];
176	u_char	En[70];
177	u_char	Cj[300];
178	u_char	As;
179	u_char	Ay;
180	u_char	Az;
181	u_char	init_type;
182	s_char	saw_tooth;
183	u_char  timeout;        /* flag to retry Cj after Fa reset */
184	s_char  assert;
185};
186
187#define rcvbuf	instance->Rcvbuf
188#define rcvptr	instance->Rcvptr
189
190static	void	oncore_consume	  P((struct instance *));
191static	void	oncore_poll	  P((int, struct peer *));
192static	void	oncore_read_config	P((struct instance *));
193static	void	oncore_receive	  P((struct recvbuf *));
194static	void	oncore_sendmsg	  P((int fd, u_char *, u_int));
195static	void	oncore_shutdown   P((int, struct peer *));
196static	int	oncore_start	  P((int, struct peer *));
197
198static	void	oncore_msg_any	P((struct instance *, u_char *, u_int, int));
199static	void	oncore_msg_As	P((struct instance *, u_char *, u_int));
200static	void	oncore_msg_At	P((struct instance *, u_char *, u_int));
201static	void	oncore_msg_Ay	P((struct instance *, u_char *, u_int));
202static	void	oncore_msg_Az	P((struct instance *, u_char *, u_int));
203static	void	oncore_msg_Bj	P((struct instance *, u_char *, u_int));
204static	void	oncore_msg_Cb	P((struct instance *, u_char *, u_int));
205static	void	oncore_msg_Cf	P((struct instance *, u_char *, u_int));
206static	void	oncore_msg_Cj	P((struct instance *, u_char *, u_int));
207static	void	oncore_msg_Ea	P((struct instance *, u_char *, u_int));
208static	void	oncore_msg_En	P((struct instance *, u_char *, u_int));
209static	void	oncore_msg_Fa	P((struct instance *, u_char *, u_int));
210
211struct	refclock refclock_oncore = {
212	oncore_start,		/* start up driver */
213	oncore_shutdown,	/* shut down driver */
214	oncore_poll,		/* transmit poll message */
215	noentry,		/* not used */
216	noentry,		/* not used */
217	noentry,		/* not used */
218	NOFLAGS 		/* not used */
219};
220
221/*
222 * Understanding the next bit here is not easy unless you have a manual
223 * for the the UT or VP Oncore.
224 */
225
226static struct msg_desc {
227	const char	flag[3];
228	const int	len;
229	void		(*handler) P((struct instance *, u_char *, u_int));
230	const char	*fmt;
231	int		shmem;
232} oncore_messages[] = {
233	/* Ea and En first since they're most common */
234	{ "Ea",  76,    oncore_msg_Ea,  "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
235	{ "En",  69,    oncore_msg_En,  "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
236	{ "Ab",  10,    0,              "" },
237	{ "Ac",  11,    0,              "" },
238	{ "Ad",  11,    0,              "" },
239	{ "Ae",  11,    0,              "" },
240	{ "Af",  15,    0,              "" },
241	{ "As",  20,    oncore_msg_As,  "" },
242	{ "At",   8,    oncore_msg_At,  "" },
243	{ "Aw",   8,    0,              "" },
244	{ "Ay",  11,    oncore_msg_Ay,  "" },
245	{ "Az",  11,    oncore_msg_Az,  "" },
246	{ "AB",   8,    0,              "" },
247	{ "Bb",  92,    0,              "" },
248	{ "Bj",   8,    oncore_msg_Bj,  "" },
249	{ "Cb",  33,    oncore_msg_Cb,  "" },
250	{ "Cf",   7,    oncore_msg_Cf,  "" },
251	{ "Cg",   8,    0,              "" },
252	{ "Ch",   9,    0,              "" },
253	{ "Cj", 294,    oncore_msg_Cj,  "" },
254	{ "Ek",  71,    0,              "" },
255	{ "Fa",   9,    oncore_msg_Fa,  "" },
256	{ "Sz",   8,    0,              "" },
257	{ {0},	  7,	0, ""}
258};
259
260static unsigned int oncore_shmem_Cb;
261
262/*
263 * Position Set.
264 */
265u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 };
266u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 };
267u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 };
268
269/*
270 * Position-Hold Mode
271 *    Start automatic site survey
272 */
273static u_char oncore_cmd_At[] = { 'A', 't', 2 };
274
275/*
276 * Position-Hold Position
277 */
278u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
279u_char oncore_cmd_Asx[]= { 'A', 's', 0x7f, 0xff, 0xff, 0xff,
280				     0x7f, 0xff, 0xff, 0xff,
281				     0x7f, 0xff, 0xff, 0xff, 0xff };
282
283/*
284 * Set to UTC time (not GPS).
285 */
286u_char oncore_cmd_Aw[] = { 'A', 'w', 1 };
287
288/*
289 * Output Almanac when it changes
290 */
291u_char oncore_cmd_Be[] = { 'B', 'e', 1 };
292
293/*
294 * Read back PPS Offset for Output
295 */
296u_char oncore_cmd_Ay[]	= { 'A', 'y', 0, 0, 0, 0 };
297u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
298
299/*
300 * Read back Cable Delay for Output
301 */
302u_char oncore_cmd_Az[]	= { 'A', 'z', 0, 0, 0, 0 };
303u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };
304
305/*
306 * Application type = static.
307 */
308u_char oncore_cmd_AB[] = { 'A', 'B', 4 };
309
310/*
311 * Visible Satellite Status Msg.
312 */
313u_char oncore_cmd_Bb[] = { 'B', 'b', 1 };
314
315/*
316 * Leap Second Pending Message
317 *    Request message once
318 */
319u_char oncore_cmd_Bj[] = { 'B', 'j', 0 };
320
321/*
322 * Set to Defaults
323 */
324static u_char oncore_cmd_Cf[] = { 'C', 'f' };
325
326/*
327 * Set to Position Fix mode (only needed on VP).
328 */
329u_char oncore_cmd_Cg[] = { 'C', 'g', 1 };
330
331/*
332 * Receiver Id
333 */
334static u_char oncore_cmd_Cj[] = { 'C', 'j' };
335
336/*
337 * Position/Status/Data message
338 *    Send once per second
339 */
340static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 };
341
342/*
343 * Position/Status Extension Msg
344 */
345u_char oncore_cmd_Ek[] = { 'E', 'k', 0 };	/* just turn off */
346
347/*
348 * Time Raim Setup & Status Message
349 *    Send once per second
350 *    Time-RAIM on
351 *    Alarm limit 1us
352 *    PPS on when we have the first sat
353 */
354static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
355
356/*
357 * Self-test
358 */
359static u_char oncore_cmd_Fa[] = { 'F', 'a' };
360
361#define DEVICE1 	"/dev/oncore.serial.%d"   /* name of serial device */
362#define DEVICE2 	"/dev/oncore.pps.%d"   /* name of pps device */
363#define INIT_FILE	"/etc/ntp.oncore" /* optional init file */
364
365#define SPEED		B9600		/* Oncore Binary speed (9600 bps) */
366
367/*
368 * Assemble and disassemble 32bit signed quantities from a buffer.
369 *
370 */
371
372	/* to buffer, int w, u_char *buf */
373#define w32_buf(buf,w)	{ unsigned int i_tmp;		   \
374			  i_tmp = (w<0) ? (~(-w)+1) : (w); \
375			  (buf)[0] = (i_tmp >> 24) & 0xff; \
376			  (buf)[1] = (i_tmp >> 16) & 0xff; \
377			  (buf)[2] = (i_tmp >>	8) & 0xff; \
378			  (buf)[3] = (i_tmp	 ) & 0xff; \
379			}
380
381#define w32(buf)      (((buf)[0]&0xff) << 24 | \
382		       ((buf)[1]&0xff) << 16 | \
383		       ((buf)[2]&0xff) <<  8 | \
384		       ((buf)[3]&0xff) )
385
386	/* from buffer, char *buf, result to an int */
387#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
388
389extern int pps_assert;
390extern int pps_hardpps;
391
392
393/*
394 * oncore_start - initialize data for processing
395 */
396static int
397oncore_start(
398	int unit,
399	struct peer *peer
400	)
401{
402	register struct instance *instance;
403	struct refclockproc *pp;
404	int fd1, fd2, mode;
405	char device1[30], device2[30];
406	const char *cp;
407	struct stat stat1, stat2;
408
409	/* OPEN DEVICES */
410	/* opening different devices for fd1 and fd2 presents no problems */
411	/* opening the SAME device twice, seems to be OS dependent.
412		(a) on Linux (no streams) no problem
413		(b) on SunOS (and possibly Solaris, untested), (streams)
414			never see the line discipline.
415	   Since things ALWAYS work if we only open the device once, we check
416	     to see if the two devices are in fact the same, then proceed to
417	     do one open or two.
418	*/
419
420	(void)sprintf(device1, DEVICE1, unit);
421	(void)sprintf(device2, DEVICE2, unit);
422
423	if (stat(device1, &stat1)) {
424		perror("ONCORE: stat fd1");
425		exit(1);
426	}
427
428	if (stat(device2, &stat2)) {
429		perror("ONCORE: stat fd2");
430		exit(1);
431	}
432
433	if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
434		/* same device here */
435		if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
436#if !defined(HAVE_PPSAPI) && !defined(TIOCDCDTIMESTAMP)
437		      | LDISC_PPS
438#endif
439		   ))) {
440			perror("ONCORE: fd1");
441			exit(1);
442		}
443		fd2 = fd1;
444	} else { /* different devices here */
445		if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) {
446			perror("ONCORE: fd1");
447			exit(1);
448		}
449		if ((fd2=open(device2, O_RDWR)) < 0) {
450			perror("ONCORE: fd2");
451			exit(1);
452		}
453	}
454
455	/* Devices now open, initialize instance structure */
456
457	if (!(instance = (struct instance *)emalloc(sizeof *instance))) {
458		perror("malloc");
459		close(fd1);
460		return (0);
461	}
462	memset((char *) instance, 0, sizeof *instance);
463	pp = peer->procptr;
464	pp->unitptr = (caddr_t)instance;
465	instance->unit	= unit;
466	instance->ttyfd = fd1;
467	instance->ppsfd = fd2;
468
469	instance->Bj_day = -1;
470	instance->assert = pps_assert;
471
472	/* go read any input data in /etc/ntp.oncoreX */
473
474	oncore_read_config(instance);
475
476#ifdef HAVE_PPSAPI
477	if (time_pps_create(fd2, &instance->pps_h) < 0) {
478		perror("time_pps_create");
479		return(0);
480	}
481
482	if (time_pps_getcap(instance->pps_h, &mode) < 0) {
483		msyslog(LOG_ERR,
484		    "refclock_ioctl: time_pps_getcap failed: %m");
485		return (0);
486	}
487
488	if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
489		msyslog(LOG_ERR,
490		    "refclock_ioctl: time_pps_getparams failed: %m");
491		return (0);
492	}
493
494	/* nb. only turn things on, if someone else has turned something
495	 *      on before we get here, leave it alone!
496	 */
497
498	if (instance->assert) {         /* nb, default or ON */
499		instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
500		instance->pps_p.assert_offset.tv_sec = 0;
501		instance->pps_p.assert_offset.tv_nsec = 0;
502	} else {
503		instance->pps_p.mode = PPS_CAPTURECLEAR  | PPS_OFFSETCLEAR;
504		instance->pps_p.clear_offset.tv_sec = 0;
505		instance->pps_p.clear_offset.tv_nsec = 0;
506	}
507	instance->pps_p.mode |= PPS_TSFMT_TSPEC;
508	instance->pps_p.mode &= mode;           /* only do it if it is legal */
509
510	if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
511		perror("time_pps_setparams");
512		exit(1);
513	}
514
515	if (pps_device) {
516		if (stat(pps_device, &stat1)) {
517			perror("ONCORE: stat pps_device");
518			return(0);
519		}
520
521		/* must have hardpps ON, and fd2 must be the same device as on the pps line */
522
523		if (pps_hardpps && ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino))) {
524			int     i;
525
526			if (instance->assert)
527				i = PPS_CAPTUREASSERT;
528			else
529				i = PPS_CAPTURECLEAR;
530
531			if (i&mode) {
532				if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
533				    PPS_TSFMT_TSPEC) < 0) {
534					msyslog(LOG_ERR,
535					    "refclock_ioctl: time_pps_kcbind failed: %m");
536					return (0);
537				}
538			}
539		}
540	}
541#endif
542
543	instance->pp = pp;
544	instance->peer = peer;
545	instance->o_state = ONCORE_NO_IDEA;
546	cp = "state = ONCORE_NO_IDEA";
547	record_clock_stats(&(instance->peer->srcadr), cp);
548
549	/*
550	 * Initialize miscellaneous variables
551	 */
552
553	peer->precision = -26;
554	peer->minpoll = 4;
555	peer->maxpoll = 4;
556	pp->clockdesc = "Motorola UT/VP Oncore GPS Receiver";
557	memcpy((char *)&pp->refid, "GPS\0", 4);
558
559	pp->io.clock_recv = oncore_receive;
560	pp->io.srcclock = (caddr_t)peer;
561	pp->io.datalen = 0;
562	pp->io.fd = fd1;
563	if (!io_addclock(&pp->io)) {
564		perror("io_addclock");
565		(void) close(fd1);
566		free(instance);
567		return (0);
568	}
569	pp->unitptr = (caddr_t)instance;
570
571	/*
572	 * This will start the Oncore receiver.
573	 * We send info from config to Oncore later.
574	 */
575
576	instance->timeout = 1;
577	mode = instance->init_type;
578	if (mode == 3 || mode == 4) {
579		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof oncore_cmd_Cf);
580		instance->o_state = ONCORE_RESET_SENT;
581		cp = "state = ONCORE_RESET_SENT";
582		record_clock_stats(&(instance->peer->srcadr), cp);
583	} else {
584		oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
585		instance->o_state = ONCORE_TEST_SENT;
586		cp = "state = ONCORE_TEST_SENT";
587		record_clock_stats(&(instance->peer->srcadr), cp);
588	}
589
590	instance->pollcnt = 2;
591	return (1);
592}
593
594
595
596static void
597oncore_init_shmem(struct instance *instance, char *filename)
598{
599#ifdef ONCORE_SHMEM_STATUS
600	int i, l, n;
601	char *buf;
602	struct msg_desc *mp;
603	static unsigned int oncore_shmem_length;
604
605	if (oncore_messages[0].shmem == 0) {
606		n = 1;
607		for (mp = oncore_messages; mp->flag[0]; mp++) {
608			mp->shmem = n;
609			/* Allocate space for multiplexed almanac */
610			if (!strcmp(mp->flag, "Cb")) {
611				oncore_shmem_Cb = n;
612				n += (mp->len + 2) * 34;
613			}
614			n += mp->len + 2;
615		}
616		oncore_shmem_length = n + 2;
617		fprintf(stderr, "ONCORE: SHMEM length: %d bytes\n", oncore_shmem_length);
618	}
619	instance->statusfd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
620	if (instance->statusfd < 0) {
621		perror(filename);
622		exit(4);
623	}
624	buf = malloc(oncore_shmem_length);
625	if (buf == NULL) {
626		perror("malloc");
627		exit(4);
628	}
629	memset(buf, 0, sizeof(buf));
630	i = write(instance->statusfd, buf, oncore_shmem_length);
631	if (i != oncore_shmem_length) {
632		perror(filename);
633		exit(4);
634	}
635	free(buf);
636	instance->shmem = (u_char *) mmap(0, oncore_shmem_length,
637	    PROT_READ | PROT_WRITE,
638#ifdef MAP_HASSEMAPHORE
639			       MAP_HASSEMAPHORE |
640#endif
641			       MAP_SHARED,
642	    instance->statusfd, (off_t)0);
643	if (instance->shmem == MAP_FAILED) {
644		instance->shmem = 0;
645		close (instance->statusfd);
646		exit(4);
647	}
648	for (mp = oncore_messages; mp->flag[0]; mp++) {
649		l = mp->shmem;
650		instance->shmem[l + 0] = mp->len >> 8;
651		instance->shmem[l + 1] = mp->len & 0xff;
652		instance->shmem[l + 2] = '@';
653		instance->shmem[l + 3] = '@';
654		instance->shmem[l + 4] = mp->flag[0];
655		instance->shmem[l + 5] = mp->flag[1];
656		if (!strcmp(mp->flag, "Cb")) {
657			for (i = 1; i < 35; i++) {
658				instance->shmem[l + i * 35 + 0] = mp->len >> 8;
659				instance->shmem[l + i * 35 + 1] = mp->len & 0xff;
660				instance->shmem[l + i * 35 + 2] = '@';
661				instance->shmem[l + i * 35 + 3] = '@';
662				instance->shmem[l + i * 35 + 4] = mp->flag[0];
663				instance->shmem[l + i * 35 + 5] = mp->flag[1];
664			}
665		}
666	}
667#endif /* ONCORE_SHMEM_STATUS */
668}
669
670/*
671 * Read Input file if it exists.
672 */
673static void
674oncore_read_config(
675	struct instance *instance
676	)
677{
678/*
679 * First we try to open the configuration file /etc/ntp.oncoreN, where
680 * N is the unit number viz 127.127.30.N.
681 * If we don't find it, then we try the file /etc/ntp.oncore.
682 *
683 * If we find NEITHER then we don't have the cable delay or PPS offset
684 * and we choose MODE (4) below.
685 *
686 * Five Choices for MODE
687 *    (0) ONCORE is preinitialized, don't do anything to change it.
688 *	    nb, DON'T set 0D mode, DON'T set Delay, position...
689 *    (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
690 *    (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
691 *		    lock this in, go to 0D mode.
692 *    (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
693 *    (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
694 *		    lock this in, go to 0D mode.
695 *     NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
696 *	   then this position is set as the INITIAL position of the ONCORE.
697 *	   This can reduce the time to first fix.
698 * -------------------------------------------------------------------------------
699 * Note that an Oncore UT without a battery backup retains NO information if it is
700 *   power cycled, with a Battery Backup it remembers the almanac, etc.
701 * For an Oncore VP, there is an eeprom that will contain this data, along with the
702 *   option of Battery Backup.
703 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
704 *   power cycle, since there is nowhere to store the data.
705 * -------------------------------------------------------------------------------
706 *
707 * If we open one or the other of the files, we read it looking for
708 *   MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET
709 * then initialize using method MODE.  For Mode = (1,3) all of (LAT, LON, HT) must
710 *   be present or mode reverts to (2,4).
711 *
712 * Read input file.
713 *
714 *	# is comment to end of line
715 *	= allowed between 1st and 2nd fields.
716 *
717 *	Expect to see one line with 'MODE' as first field, followed by an integer
718 *	   in the range 0-4 (default = 4).
719 *
720 *	Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
721 *	All numbers are floating point.
722 *		DDD.ddd
723 *		DDD  MMM.mmm
724 *		DDD  MMM  SSS.sss
725 *
726 *	Expect to see one line with 'HT' (or 'HTMSL' or 'HTGPS') as first field.
727 *	   followed by 1-2 fields.  First is a number, the second is 'FT' or 'M'.
728 *	   for feet or meters.	HT is the same as HTGPS.
729 *	     HTMSL = HT above mean_sea_level,
730 *	     HTGPS = HT above GPS ellipse.
731 *
732 *	There are two optional lines, starting with DELAY and OFFSET, followed
733 *	   by 1 or two fields.	The first is a number (a time) the second is
734 *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
735 *	     DELAY  is cable delay, typically a few tens of ns.
736 *	     OFFSET is the offset of the PPS pulse from 0. (only fully implemented
737 *		with the PPSAPI, we need to be able to tell the Kernel about this
738 *		offset if the Kernel PLL is in use, but can only do this presently
739 *		when using the PPSAPI interface.  If not using the Kernel PLL,
740 *		then there is no problem.
741 *
742 *	There is another optional line, with either ASSERT or CLEAR on it, which
743 *	   determine which transition of the PPS signal is used for timing by the
744 *	   PPSAPI.  If neither is present, then ASSERT is assumed.
745 *
746 * So acceptable input would be
747 *	# these are my coordinates (RWC)
748 *	LON  -106 34.610
749 *	LAT    35 08.999
750 *	HT	1589	# could equally well say HT 5215 FT
751 *	DELAY  60 ns
752 */
753
754	FILE	*fd;
755	char	*cp, *cc, *ca, line[100], units[2], device[20];
756	int	i, sign, lat_flg, long_flg, ht_flg, mode;
757	double	f1, f2, f3;
758
759	sprintf(device, "%s%d", INIT_FILE, instance->unit);
760	if ((fd=fopen(device, "r")) == NULL)
761		if ((fd=fopen(INIT_FILE, "r")) == NULL) {
762			instance->init_type = 4;
763			return;
764		}
765
766	mode = 0;
767	lat_flg = long_flg = ht_flg = 0;
768	while (fgets(line, 100, fd)) {
769
770		/* Remove comments */
771		if ((cp = strchr(line, '#')))
772			*cp = '\0';
773
774		/* Remove trailing space */
775		for (i = strlen(line);
776		     i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
777			)
778			line[--i] = '\0';
779
780		/* Remove leading space */
781		for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
782			continue;
783
784		/* Stop if nothing left */
785		if (!*cc)
786			continue;
787
788		/* Lowercase the command and find the arg */
789		for (ca = cc; *ca; ca++) {
790			if (isascii((int)*ca) && islower((int)*ca)) {
791				*ca = toupper(*ca);
792			} else if (isascii((int)*ca) && isspace((int)*ca)) {
793				break;
794			} else if (*ca == '=') {
795				*ca = ' ';
796				break;
797			}
798		}
799
800		/* Remove space leading the arg */
801		for (; *ca && isascii((int)*ca) && isspace((int)*ca); ca++)
802			continue;
803
804		if (!strncmp(cc, "STATUS", 6)) {
805			oncore_init_shmem(instance, ca);
806			continue;
807		}
808
809		/* Uppercase argument as well */
810		for (cp = ca; *cp; cp++)
811			if (isascii((int)*cp) && islower((int)*cp))
812				*cp = toupper(*cp);
813
814		if (!strncmp(cc, "LAT", 3)) {
815			f1 = f2 = f3 = 0;
816			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
817			sign = 1;
818			if (f1 < 0) {
819				f1 = -f1;
820				sign = -1;
821			}
822			instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
823			lat_flg++;
824		} else if (!strncmp(cc, "LON", 3)) {
825			f1 = f2 = f3 = 0;
826			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
827			sign = 1;
828			if (f1 < 0) {
829				f1 = -f1;
830				sign = -1;
831			}
832			instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
833			long_flg++;
834		} else if (!strncmp(cc, "HT", 2)) {
835			if (!strncmp(cc, "HTGPS", 5))
836				instance->ss_ht_type = 0;
837			else if (!strncmp(cc, "HTMSL", 5))
838				instance->ss_ht_type = 1;
839			else
840				instance->ss_ht_type = 0;
841			f1 = 0;
842			units[0] = '\0';
843			sscanf(ca, "%lf %1s", &f1, units);
844			if (units[0] == 'F')
845				f1 = 0.3048 * f1;
846			instance->ss_ht = 100 * f1;    /* cm */
847			ht_flg++;
848		} else if (!strncmp(cc, "DELAY", 5)) {
849			f1 = 0;
850			units[0] = '\0';
851			sscanf(ca, "%lf %1s", &f1, units);
852			if (units[0] == 'N')
853				;
854			else if (units[0] == 'U')
855				f1 = 1000 * f1;
856			else if (units[0] == 'M')
857				f1 = 1000000 * f1;
858			else
859				f1 = 1000000000 * f1;
860			if (f1 < 0 || f1 > 1.e9)
861				f1 = 0;
862			instance->delay = f1;		/* delay in ns */
863		} else if (!strncmp(cc, "OFFSET", 6)) {
864			f1 = 0;
865			units[0] = '\0';
866			sscanf(ca, "%lf %1s", &f1, units);
867			if (units[0] == 'N')
868				;
869			else if (units[0] == 'U')
870				f1 = 1000 * f1;
871			else if (units[0] == 'M')
872				f1 = 1000000 * f1;
873			else
874				f1 = 1000000000 * f1;
875			if (f1 < 0 || f1 > 1.e9)
876				f1 = 0;
877			instance->offset = f1;		/* offset in ns */
878		} else if (!strncmp(cc, "MODE", 4)) {
879			sscanf(ca, "%d", &mode);
880			if (mode < 0 || mode > 4)
881				mode = 4;
882			instance->init_type = mode;
883		} else if (!strncmp(cc, "ASSERT", 6)) {
884			instance->assert = 1;
885		} else if (!strncmp(cc, "CLEAR", 5)) {
886			instance->assert = 0;
887		}
888	}
889	fclose(fd);
890
891	/*
892	 *    OK, have read all of data file, and extracted the good stuff.
893	 *    If lat/long/ht specified they ALL must be specified for mode = (1,3).
894	 */
895
896	instance->posn_set = 1;
897	if ((lat_flg || long_flg || ht_flg) && !(lat_flg * long_flg * ht_flg)) {
898		printf("ONCORE: incomplete data on %s\n", INIT_FILE);
899		instance->posn_set = 0;
900		if (mode == 1 || mode == 3)
901			instance->init_type++;
902	}
903}
904
905
906
907/*
908 * oncore_shutdown - shut down the clock
909 */
910static void
911oncore_shutdown(
912	int unit,
913	struct peer *peer
914	)
915{
916	register struct instance *instance;
917	struct refclockproc *pp;
918
919	pp = peer->procptr;
920	instance = (struct instance *) pp->unitptr;
921	free(instance);
922}
923
924
925
926/*
927 * oncore_poll - called by the transmit procedure
928 */
929static void
930oncore_poll(
931	int unit,
932	struct peer *peer
933	)
934{
935	struct instance *instance;
936
937	instance = (struct instance *) peer->procptr->unitptr;
938	if (instance->timeout) {
939		char    *cp;
940
941		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof oncore_cmd_Cj);
942		instance->o_state = ONCORE_ID_SENT;
943		cp = "state = ONCORE_ID_SENT";
944		record_clock_stats(&(instance->peer->srcadr), cp);
945		return;
946	}
947
948	if (!instance->pollcnt)
949		refclock_report(peer, CEVNT_TIMEOUT);
950	else
951		instance->pollcnt--;
952	peer->procptr->polls++;
953	instance->polled = 1;
954}
955
956
957
958/*
959 * move dta from NTP to buffer (toss in unlikely case it wont fit)
960 */
961static void
962oncore_receive(
963	struct recvbuf *rbufp
964	)
965{
966	u_int	i;
967	u_char *p;
968	struct peer *peer;
969	struct instance *instance;
970
971	peer = (struct peer *)rbufp->recv_srcclock;
972	instance = (struct instance *) peer->procptr->unitptr;
973	p = (u_char *) &rbufp->recv_space;
974
975#if 0
976	if (debug > 4) {
977		int i;
978		printf("ONCORE: >>>");
979		for(i=0; i<rbufp->recv_length; i++)
980			printf("%02x ", p[i]);
981		printf("\n");
982		printf("ONCORE: >>>");
983		for(i=0; i<rbufp->recv_length; i++)
984			printf("%03o ", p[i]);
985		printf("\n");
986	}
987#endif
988
989	i = rbufp->recv_length;
990	if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
991		i = sizeof(rcvbuf) - rcvptr;	/* and some char will be lost */
992	memcpy(rcvbuf+rcvptr, p, i);
993	rcvptr += i;
994	oncore_consume(instance);
995}
996
997
998
999/*
1000 * Deal with any complete messages
1001 */
1002static void
1003oncore_consume(
1004	struct instance *instance
1005	)
1006{
1007	int i, j, m;
1008	unsigned l;
1009
1010	while (rcvptr >= 7) {
1011		if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
1012			/* We're not in sync, lets try to get there */
1013			for (i=1; i < rcvptr-1; i++)
1014				if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
1015					break;
1016			if (debug > 4)
1017				printf("ONCORE: >>> skipping %d chars\n", i);
1018			if (i != rcvptr)
1019				memcpy(rcvbuf, rcvbuf+i, (unsigned)(rcvptr-i));
1020			rcvptr -= i;
1021		}
1022
1023		/* Ok, we have a header now */
1024		l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
1025		for(m=0; m<l; m++)
1026			if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), 2))
1027				break;
1028		l = oncore_messages[m].len;
1029#if 0
1030		if (debug > 3)
1031			printf("ONCORE: GOT: %c%c  %d of %d entry %d\n", rcvbuf[2], rcvbuf[3], rcvptr, l, m);
1032#endif
1033		/* Got the entire message ? */
1034
1035		if (rcvptr < l)
1036			return;
1037
1038		/* Check the checksum */
1039
1040		j = 0;
1041		for (i = 2; i < l-3; i++)
1042			j ^= rcvbuf[i];
1043		if (j == rcvbuf[l-3]) {
1044			if (instance->shmem != NULL)
1045				memcpy(instance->shmem + oncore_messages[m].shmem + 2,
1046				    rcvbuf, l);
1047			oncore_msg_any(instance, rcvbuf, (unsigned) (l-3), m);
1048			if (oncore_messages[m].handler)
1049				oncore_messages[m].handler(instance, rcvbuf, (unsigned) (l-3));
1050		} else if (debug) {
1051			printf("ONCORE: Checksum mismatch! calc %o is %o\n", j, rcvbuf[l-3]);
1052			printf("ONCORE: @@%c%c ", rcvbuf[2], rcvbuf[3]);
1053			for (i=4; i<l; i++)
1054				printf("%03o ", rcvbuf[i]);
1055			printf("\n");
1056		}
1057
1058		if (l != rcvptr)
1059			memcpy(rcvbuf, rcvbuf+l, (unsigned) (rcvptr-l));
1060		rcvptr -= l;
1061	}
1062}
1063
1064
1065
1066/*
1067 * write message to Oncore.
1068 */
1069static void
1070oncore_sendmsg(
1071	int	fd,
1072	u_char *ptr,
1073	u_int len
1074	)
1075{
1076	u_char cs = 0;
1077
1078	printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], len);
1079	write(fd, "@@", 2);
1080	write(fd, ptr, len);
1081	while (len--)
1082		cs ^= *ptr++;
1083	write(fd, &cs, 1);
1084	write(fd, "\r\n", 2);
1085}
1086
1087
1088
1089static void
1090oncore_msg_any(
1091	struct instance *instance,
1092	u_char *buf,
1093	u_int len,
1094	int idx
1095	)
1096{
1097	int i;
1098	const char *fmt = oncore_messages[idx].fmt;
1099	const char *p;
1100	struct timeval tv;
1101
1102	if (debug > 3) {
1103		GETTIMEOFDAY(&tv, 0);
1104		printf("ONCORE: %ld.%06ld\n", (long) tv.tv_sec, (long) tv.tv_usec);
1105
1106		if (!*fmt) {
1107			printf(">>@@%c%c ", buf[2], buf[3]);
1108			for(i=2; i < len && i < 2400 ; i++)
1109				printf("%02x", buf[i]);
1110			printf("\n");
1111			return;
1112		} else {
1113			printf("##");
1114			for (p = fmt; *p; p++) {
1115				putchar(*p);
1116				putchar('_');
1117			}
1118			printf("\n%c%c", buf[2], buf[3]);
1119			i = 4;
1120			for (p = fmt; *p; p++) {
1121				printf("%02x", buf[i++]);
1122			}
1123			printf("\n");
1124		}
1125	}
1126}
1127
1128
1129
1130/*
1131 * Demultiplex the almanac into shmem
1132 */
1133static void
1134oncore_msg_Cb(
1135	struct instance *instance,
1136	u_char *buf,
1137	u_int len
1138	)
1139{
1140	int i;
1141
1142	if (instance->shmem == NULL)
1143		return;
1144
1145	if (buf[4] == 5)
1146		i = buf[5];
1147	else if (buf[4] == 4 && buf[5] <= 5)
1148		i = buf[5] + 24;
1149	else if (buf[4] == 4 && buf[5] <= 10)
1150		i = buf[5] + 23;
1151	else
1152		i = 34;
1153	i *= 35;
1154	memcpy(instance->shmem + oncore_shmem_Cb + i + 2, buf, len + 3);
1155}
1156
1157/*
1158 * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
1159 *	not so for VP (eeprom) or UT with battery
1160 */
1161static void
1162oncore_msg_Cf(
1163	struct instance *instance,
1164	u_char *buf,
1165	u_int len
1166	)
1167{
1168	const char *cp;
1169
1170	if (instance->o_state == ONCORE_RESET_SENT) {
1171		oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
1172		instance->o_state = ONCORE_TEST_SENT;
1173		cp = "state = ONCORE_TEST_SENT";
1174		record_clock_stats(&(instance->peer->srcadr), cp);
1175	}
1176}
1177
1178
1179
1180/* there are good reasons NOT to do a @@Fa command with the ONCORE.
1181 * Doing it, it was found that under some circumstances the following
1182 * command would fail if issued immediately after the return from the
1183 * @@Fa, but a 2sec delay seemed to fix things.  Since simply calling
1184 * sleep(2) is wastefull, and may cause trouble for some OS's, repeating
1185 * itimer, we set a flag, and test it at the next POLL.  If it hasnt
1186 * been cleared, we reissue the @@Ca that is issued below.
1187 */
1188
1189static void
1190oncore_msg_Fa(
1191	struct instance *instance,
1192	u_char *buf,
1193	u_int len
1194	)
1195{
1196	const char *cp;
1197
1198	if (instance->o_state == ONCORE_TEST_SENT) {
1199		if (debug > 2)
1200			printf("ONCORE: >>@@Fa %x %x\n", buf[4], buf[5]);
1201		if (buf[4] || buf[5]) {
1202			printf("ONCORE: SELF TEST FAILED\n");
1203			exit(1);
1204		}
1205
1206		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof oncore_cmd_Cj);
1207		instance->o_state = ONCORE_ID_SENT;
1208		cp = "state = ONCORE_ID_SENT";
1209		record_clock_stats(&(instance->peer->srcadr), cp);
1210	}
1211}
1212
1213
1214
1215/*
1216 * preliminaries out of the way, this is the REAL start of initialization
1217 */
1218static void
1219oncore_msg_Cj(
1220	struct instance *instance,
1221	u_char *buf,
1222	u_int len
1223	)
1224{
1225	char *cp, *cp1;
1226	int	mode;
1227
1228	instance->timeout = 0;
1229	if (instance->o_state != ONCORE_ID_SENT)
1230		return;
1231
1232	memcpy(instance->Cj, buf, len);
1233
1234	/* Write Receiver ID to clockstats file */
1235
1236	instance->Cj[294] = '\0';
1237	for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
1238		cp1 = strchr(cp, '\r');
1239		if (!cp1)
1240			cp1 = (char *)&instance->Cj[294];
1241		*cp1 = '\0';
1242		record_clock_stats(&(instance->peer->srcadr), cp);
1243		*cp1 = '\r';
1244		cp = cp1+2;
1245	}
1246#ifdef HAVE_PPSAPI
1247	if (instance->assert)
1248		cp = "Timing on Assert.";
1249	else
1250		cp = "Timing on Clear.";
1251	record_clock_stats(&(instance->peer->srcadr), cp);
1252#endif
1253
1254	oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof oncore_cmd_Cg); /* Set Posn Fix mode (not Idle (VP)) */
1255	oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof oncore_cmd_Bb); /* turn off */
1256	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof oncore_cmd_Ek); /* turn off */
1257	oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof oncore_cmd_Aw); /* UTC time */
1258	oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof oncore_cmd_AB); /* Appl type static */
1259	oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof oncore_cmd_Be); /* Tell us the Almanac */
1260
1261	mode = instance->init_type;
1262	if (debug)
1263		printf("ONCORE: INIT mode = %d\n", mode);
1264
1265	/* If there is Position input in the Config file
1266	 * and mode = (1,3) set it as posn hold posn, goto 0D mode.
1267	 *  or mode = (2,4) set it as INITIAL position, and Site Survey.
1268	 */
1269
1270	switch (mode) {
1271	case 0: /* NO initialization, don't change anything */
1272		instance->site_survey = ONCORE_SS_DONE;
1273		break;
1274
1275	case 1:
1276	case 3:
1277		w32_buf(&oncore_cmd_As[2],  (int) instance->ss_lat);
1278		w32_buf(&oncore_cmd_As[6],  (int) instance->ss_long);
1279		w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
1280		oncore_cmd_As[14] = instance->ss_ht_type;
1281		oncore_sendmsg(instance->ttyfd, oncore_cmd_As,	sizeof oncore_cmd_As);
1282
1283		instance->site_survey = ONCORE_SS_DONE;
1284		oncore_cmd_At[2] = 1;
1285		oncore_sendmsg(instance->ttyfd, oncore_cmd_At,	sizeof oncore_cmd_At);
1286		record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
1287		break;
1288
1289	case 2:
1290	case 4:
1291		if (instance->posn_set) {
1292			w32_buf(&oncore_cmd_Ad[2], (int) instance->ss_lat);
1293			w32_buf(&oncore_cmd_Ae[2], (int) instance->ss_long);
1294			w32_buf(&oncore_cmd_Af[2], (int) instance->ss_ht);
1295			oncore_cmd_Af[6] = instance->ss_ht_type;
1296			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ad,	sizeof oncore_cmd_Ad);
1297			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ae,	sizeof oncore_cmd_Ae);
1298			oncore_sendmsg(instance->ttyfd, oncore_cmd_Af,	sizeof oncore_cmd_Af);
1299		}
1300		instance->site_survey = ONCORE_SS_UNKNOWN;
1301		oncore_cmd_At[2] = 2;
1302		oncore_sendmsg(instance->ttyfd, oncore_cmd_At,	sizeof oncore_cmd_At);
1303		break;
1304	}
1305
1306	if (mode != 0) {
1307			/* cable delay in ns */
1308		w32_buf(&oncore_cmd_Az[2], instance->delay);
1309		oncore_sendmsg(instance->ttyfd, oncore_cmd_Az,	sizeof oncore_cmd_Az);
1310
1311			/* PPS offset in ns */
1312		w32_buf(&oncore_cmd_Ay[2], instance->offset);
1313		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ay,	sizeof oncore_cmd_Ay);
1314	}
1315
1316	/* 8chan - Position/Status/Data Output Message, 1/s */
1317
1318	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea,	sizeof oncore_cmd_Ea);
1319
1320	instance->o_state = ONCORE_ALMANAC;
1321	cp = "state = ONCORE_ALMANAC";
1322	record_clock_stats(&(instance->peer->srcadr), cp);
1323}
1324
1325
1326
1327static void
1328oncore_msg_Ea(
1329	struct instance *instance,
1330	u_char *buf,
1331	u_int len
1332	)
1333{
1334	const char	*cp;
1335	char		Msg[160];
1336
1337	if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
1338		return;
1339
1340	memcpy(instance->Ea, buf, len);
1341
1342	/* When we have an almanac, start the En messages */
1343
1344	if (instance->o_state == ONCORE_ALMANAC) {
1345		if ((instance->Ea[72] & 1)) {
1346			if (debug)
1347				printf("ONCORE: waiting for almanac\n");
1348			return;
1349		} else {
1350			oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof oncore_cmd_En);
1351			instance->o_state = ONCORE_RUN;
1352			cp = "state = ONCORE_RUN";
1353			record_clock_stats(&(instance->peer->srcadr), cp);
1354		}
1355	}
1356
1357	/* must be ONCORE_RUN if we are here */
1358	/* First check if Hardware SiteSurvey has Finished */
1359
1360	if ((instance->site_survey == ONCORE_SS_HW) && !(instance->Ea[37] & 0x20)) {
1361		instance->site_survey = ONCORE_SS_DONE;
1362		record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
1363	}
1364
1365	if (!instance->printed && instance->site_survey == ONCORE_SS_DONE) {	/* will print to clockstat when all */
1366		instance->printed = 1;						/* three messages respond */
1367			/* Read back Position Hold Params */
1368		oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx,  sizeof oncore_cmd_Asx);
1369			/* Read back PPS Offset for Output */
1370			/* Nb. This will fail silently for early UT (no plus) model */
1371		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx,  sizeof oncore_cmd_Ayx);
1372			/* Read back Cable Delay for Output */
1373		oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx,  sizeof oncore_cmd_Azx);
1374	}
1375
1376	/* Check the leap second status once per day */
1377
1378	/*
1379	 * The following additional check, checking for June/December, is a
1380	 * workaround for incorrect ONCORE firmware.  The oncore starts
1381	 * reporting the leap second when the GPS satellite data message
1382	 * (page 18, subframe 4) is updated to a date in the future, which
1383	 * which can be several months before the leap second.	WWV and other
1384	 * services seem to wait until the month of the event to turn
1385	 * on their indicators (which are usually a single bit).
1386	 */
1387
1388	if ((buf[4] == 6) || (buf[4] == 12)) {
1389		if (instance->Bj_day != buf[5]) {     /* do this 1/day */
1390			instance->Bj_day = buf[5];
1391			oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof oncore_cmd_Bj);
1392		}
1393	}
1394	instance->pp->year = buf[6]*256+buf[7];
1395	instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
1396	instance->pp->hour = buf[8];
1397	instance->pp->minute = buf[9];
1398	instance->pp->second = buf[10];
1399
1400	if (instance->site_survey != ONCORE_SS_SW)
1401		return;
1402
1403	/*
1404	 * We have to average our own position for the Position Hold Mode
1405	 */
1406
1407	/* We only take PDOP/3D fixes */
1408
1409	if (instance->Ea[37] & 1)
1410		return;
1411
1412	/* Not if poor geometry or less than 3 sats */
1413
1414	if (instance->Ea[72] & 0x52)
1415		return;
1416
1417	/* Only 3D fix */
1418
1419	if (!(instance->Ea[72] & 0x20))
1420		return;
1421
1422	instance->ss_lat  += buf_w32(&instance->Ea[15]);
1423	instance->ss_long += buf_w32(&instance->Ea[19]);
1424	instance->ss_ht   += buf_w32(&instance->Ea[23]);  /* GPS ellipse */
1425	instance->ss_count++;
1426
1427	if (instance->ss_count != POS_HOLD_AVERAGE)
1428		return;
1429
1430	instance->ss_lat  /= POS_HOLD_AVERAGE;
1431	instance->ss_long /= POS_HOLD_AVERAGE;
1432	instance->ss_ht   /= POS_HOLD_AVERAGE;
1433
1434	sprintf(Msg, "Surveyed posn:  lat %.3f long %.3f ht %.3f",
1435			instance->ss_lat, instance->ss_long, instance->ss_ht);
1436	record_clock_stats(&(instance->peer->srcadr), Msg);
1437
1438	w32_buf(&oncore_cmd_As[2],  (int) instance->ss_lat);
1439	w32_buf(&oncore_cmd_As[6],  (int) instance->ss_long);
1440	w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
1441	oncore_cmd_As[14] = 0;
1442	oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As);
1443
1444	oncore_cmd_At[2] = 1;
1445	oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
1446	record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
1447	instance->site_survey = ONCORE_SS_DONE;
1448}
1449
1450
1451
1452static void
1453oncore_msg_En(
1454	struct instance *instance,
1455	u_char *buf,
1456	u_int len
1457	)
1458{
1459	int	j;
1460	l_fp ts, ts_tmp;
1461	double dmy;
1462#ifdef HAVE_TIMESPEC
1463	struct timespec *tsp = 0;
1464#else
1465	struct timeval	*tsp = 0;
1466#endif
1467#ifdef HAVE_PPSAPI
1468	struct timespec timeout;
1469	pps_info_t pps_i;
1470#else  /* ! HAVE_PPSAPI */
1471#ifdef HAVE_CIOGETEV
1472	struct ppsclockev ev;
1473	int r = CIOGETEV;
1474#endif
1475#ifdef HAVE_TIOCGPPSEV
1476	struct ppsclockev ev;
1477	int r = TIOCGPPSEV;
1478#endif
1479#if	TIOCDCDTIMESTAMP
1480	struct timeval	tv;
1481#endif
1482#endif	/* ! HAVE_PPS_API */
1483
1484	if (instance->o_state != ONCORE_RUN)
1485		return;
1486
1487	memcpy(instance->En, buf, len);
1488
1489	/* Don't do anything without an almanac to define the GPS->UTC delta */
1490
1491	if (instance->Ea[72] & 1)
1492		return;
1493
1494	/* If Time RAIM doesn't like it, don't trust it */
1495
1496	if (instance->En[21])
1497		return;
1498
1499#ifdef HAVE_PPSAPI
1500	j = instance->ev_serial;
1501	timeout.tv_sec = 0;
1502	timeout.tv_nsec = 0;
1503	if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
1504	    &timeout) < 0) {
1505		printf("ONCORE: time_pps_fetch failed\n");
1506		return;
1507	}
1508
1509	if (instance->assert) {
1510		tsp = &pps_i.assert_timestamp;
1511
1512		if (debug > 2)
1513			printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
1514			    pps_i.assert_sequence, j, tsp->tv_sec, tsp->tv_nsec);
1515
1516		if (pps_i.assert_sequence == j) {
1517			printf("ONCORE: oncore_msg_En, error serial pps\n");
1518			return;
1519		}
1520		instance->ev_serial = pps_i.assert_sequence;
1521	} else {
1522		tsp = &pps_i.clear_timestamp;
1523
1524		if (debug > 2)
1525			printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
1526			    pps_i.clear_sequence, j, tsp->tv_sec, tsp->tv_nsec);
1527
1528		if (pps_i.clear_sequence == j) {
1529			printf("ONCORE: oncore_msg_En, error serial pps\n");
1530			return;
1531		}
1532		instance->ev_serial = pps_i.clear_sequence;
1533	}
1534
1535	/* convert timespec -> ntp l_fp */
1536
1537	dmy = tsp->tv_nsec;
1538	dmy /= 1e9;
1539	ts.l_uf =  dmy * 4294967296.0;
1540	ts.l_ui = tsp->tv_sec;
1541#if 0
1542     alternate code for previous 4 lines is
1543	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1544	DTOLFP(dmy, &ts);
1545	dmy = tsp->tv_sec;		/* integer part */
1546	DTOLFP(dmy, &ts_tmp);
1547	L_ADD(&ts, &ts_tmp);
1548     or more simply
1549	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1550	DTOLFP(dmy, &ts);
1551	ts.l_ui = tsp->tv_sec;
1552#endif	/* 0 */
1553#else
1554# if defined(HAVE_TIOCGPPSEV) || defined(HAVE_CIOGETEV)
1555	j = instance->ev_serial;
1556	if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
1557		perror("ONCORE: IOCTL:");
1558		return;
1559	}
1560
1561	tsp = &ev.tv;
1562
1563	if (debug > 2)
1564		printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
1565			ev.serial, j, tsp->tv_sec, tsp->tv_usec);
1566
1567	if (ev.serial == j) {
1568		printf("ONCORE: oncore_msg_En, error serial pps\n");
1569		return;
1570	}
1571	instance->ev_serial = ev.serial;
1572
1573	/* convert timeval -> ntp l_fp */
1574
1575	TVTOTS(tsp, &ts);
1576# else
1577#  if defined(TIOCDCDTIMESTAMP)
1578	if(ioctl(instance->ppsfd, TIOCDCDTIMESTAMP, &tv) < 0) {
1579		perror("ONCORE: ioctl(TIOCDCDTIMESTAMP)");
1580		return;
1581	}
1582	tsp = &tv;
1583	TVTOTS(tsp, &ts);
1584#  else
1585#error "Cannot compile -- no PPS mechanism configured!"
1586#  endif
1587# endif
1588#endif
1589	/* now have timestamp in ts */
1590	/* add in saw_tooth and offset */
1591
1592	/* saw_tooth not really necessary if using TIMEVAL */
1593	/* since its only precise to us, but do it anyway. */
1594
1595	/* offset in ns, and is positive (late), we subtract */
1596	/* to put the PPS time transition back where it belongs */
1597
1598	j  = instance->saw_tooth + instance->offset;
1599	instance->saw_tooth = (s_char) buf[25]; /* update for next time */
1600#ifdef HAVE_PPSAPI
1601	/* must hand this offset off to the Kernel to do the addition */
1602	/* so that the Kernel PLL sees the offset too */
1603
1604	if (instance->assert) {
1605		instance->pps_p.assert_offset.tv_nsec =
1606			 -(instance->saw_tooth + instance->offset);
1607	} else {
1608		instance->pps_p.clear_offset.tv_nsec =
1609			 -(instance->saw_tooth + instance->offset);
1610	}
1611
1612	if (time_pps_setparams(instance->pps_h, &instance->pps_p))
1613		perror("time_pps_setparams");
1614#else
1615	/* if not PPSAPI, no way to inform kernel of OFFSET, just do it */
1616
1617	dmy = -1.0e-9*j;
1618	DTOLFP(dmy, &ts_tmp);
1619	L_ADD(&ts, &ts_tmp);
1620#endif
1621	/* have time from UNIX origin, convert to NTP origin. */
1622
1623	ts.l_ui += JAN_1970;
1624	instance->pp->lastrec = ts;
1625	instance->pp->msec = 0;
1626
1627	ts_tmp = ts;
1628	ts_tmp.l_ui = 0;        /* zero integer part */
1629	LFPTOD(&ts_tmp, dmy);   /* convert fractional part to a double */
1630	j = 1.0e9*dmy;          /* then to integer ns */
1631	sprintf(instance->pp->a_lastcode,
1632	    "%u.%09u %d %d %2d %2d %2d %2ld rstat %02x dop %d nsat %2d,%d raim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d",
1633	    ts.l_ui, j,
1634	    instance->pp->year, instance->pp->day,
1635	    instance->pp->hour, instance->pp->minute, instance->pp->second,
1636	    (long) tsp->tv_sec % 60,
1637
1638	    instance->Ea[72], instance->Ea[37], instance->Ea[38], instance->Ea[39], instance->En[21],
1639	    /*rstat           dop               nsat visible,     nsat tracked,     raim */
1640	    instance->En[23]*256+instance->En[24], (s_char) buf[25],
1641	    /* sigma				   neg-sawtooth */
1642  /*sat*/   instance->Ea[41], instance->Ea[45], instance->Ea[49], instance->Ea[53],
1643	    instance->Ea[57], instance->Ea[61], instance->Ea[65], instance->Ea[69]
1644	    );
1645
1646	if (debug > 2) {
1647		int i;
1648		i = strlen(instance->pp->a_lastcode);
1649		printf("ONCORE: len = %d %s\n", i, instance->pp->a_lastcode);
1650	}
1651
1652	if (!refclock_process(instance->pp)) {
1653		refclock_report(instance->peer, CEVNT_BADTIME);
1654		return;
1655	}
1656
1657	record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
1658	instance->pollcnt = 2;
1659
1660	if (instance->polled) {
1661		instance->polled = 0;
1662/*
1663		instance->pp->dispersion = instance->pp->skew = 0;
1664*/
1665		refclock_receive(instance->peer);
1666	}
1667}
1668
1669
1670
1671/*
1672 * Try to use Oncore UT+ Auto Survey Feature
1673 *	If its not there (VP), set flag to do it ourselves.
1674 */
1675static void
1676oncore_msg_At(
1677	struct instance *instance,
1678	u_char *buf,
1679	u_int len
1680	)
1681{
1682	if (instance->site_survey != ONCORE_SS_UNKNOWN)
1683		return;
1684
1685	if (buf[4] == 2) {
1686		record_clock_stats(&(instance->peer->srcadr),
1687				"Initiating hardware 3D site survey");
1688		instance->site_survey = ONCORE_SS_HW;
1689	} else {
1690		char Msg[160];
1691		/*
1692		 * Probably a VP or an older UT which can't do site-survey.
1693		 * We will have to do it ourselves
1694		 */
1695
1696		sprintf(Msg, "Initiating software 3D site survey (%d samples)",
1697				POS_HOLD_AVERAGE);
1698		record_clock_stats(&(instance->peer->srcadr), Msg);
1699		instance->site_survey = ONCORE_SS_SW;
1700
1701		oncore_cmd_At[2] = 0;
1702		instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
1703		oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
1704	}
1705}
1706
1707
1708
1709/* get leap-second warning message */
1710
1711/*
1712 * @@Bj does NOT behave as documented in current Oncore firmware.
1713 * It turns on the LEAP indicator when the data is set, and does not,
1714 * as documented, wait until the beginning of the month when the
1715 * leap second will occur.
1716 * Until this firmware bug is fixed, @@Bj is only called in June/December.
1717 */
1718
1719static void
1720oncore_msg_Bj(
1721	struct instance *instance,
1722	u_char *buf,
1723	u_int len
1724	)
1725{
1726	const char	*cp;
1727
1728	switch(buf[4]) {
1729	case 1:
1730		instance->peer->leap = LEAP_ADDSECOND;
1731		cp = "Set peer.leap to LEAP_ADDSECOND";
1732		break;
1733	case 2:
1734		instance->peer->leap = LEAP_DELSECOND;
1735		cp = "Set peer.leap to LEAP_DELSECOND";
1736		break;
1737	case 0:
1738	default:
1739		instance->peer->leap = LEAP_NOWARNING;
1740		cp = "Set peer.leap to LEAP_NOWARNING";
1741		break;
1742	}
1743	record_clock_stats(&(instance->peer->srcadr), cp);
1744}
1745
1746
1747
1748/*
1749 * get Position hold position
1750 */
1751static void
1752oncore_msg_As(
1753	struct instance *instance,
1754	u_char *buf,
1755	u_int len
1756	)
1757{
1758	char Msg[120], ew, ns;
1759	const char *Ht;
1760	double xd, xm, xs, yd, ym, ys, hm, hft;
1761	int idx, idy, is, imx, imy;
1762	long lat, lon, ht;
1763
1764	if (!instance->printed || instance->As)
1765		return;
1766
1767	instance->As = 1;
1768
1769	lat = buf_w32(&buf[4]);
1770	instance->ss_lat = lat;
1771
1772	lon = buf_w32(&buf[8]);
1773	instance->ss_long = lon;
1774
1775	ht = buf_w32(&buf[12]);
1776	instance->ss_ht = ht;
1777
1778	instance->ss_ht_type = buf[16];
1779
1780	/* Print out Position */
1781
1782	record_clock_stats(&(instance->peer->srcadr), "Posn:");
1783	ew = 'E';
1784	lon = instance->ss_long;
1785	if (lon < 0) {
1786		ew = 'W';
1787		lon = -lon;
1788	}
1789
1790	ns = 'N';
1791	lat = instance->ss_lat;
1792	if (lat < 0) {
1793		ns = 'S';
1794		lat = -lat;
1795	}
1796
1797	hm = instance->ss_ht/100.;
1798	hft= hm/0.3048;
1799	Ht = instance->ss_ht_type ? "MSL" : "GPS";
1800
1801	xd = lat/3600000.;	/* lat, lon in int msec arc, ht in cm. */
1802	yd = lon/3600000.;
1803	sprintf(Msg, "Lat = %c %11.7fdeg,    Long = %c %11.7fdeg,    Alt = %5.2fm (%5.2fft) %s", ns, xd, ew, yd, hm, hft, Ht);
1804	record_clock_stats(&(instance->peer->srcadr), Msg);
1805
1806	idx = xd;
1807	idy = yd;
1808	imx = lat%3600000;
1809	imy = lon%3600000;
1810	xm = imx/60000.;
1811	ym = imy/60000.;
1812	sprintf(Msg, "Lat = %c %3ddeg %7.4fm,   Long = %c %3ddeg %8.5fm,  Alt = %5.2fm (%5.2fft) %s", ns, idx, xm, ew, idy, ym, hm, hft, Ht);
1813	record_clock_stats(&(instance->peer->srcadr), Msg);
1814
1815	imx = xm;
1816	imy = ym;
1817	is  = lat%60000;
1818	xs  = is/1000.;
1819	is  = lon%60000;
1820	ys  = is/1000.;
1821	sprintf(Msg, "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %5.2fm (%5.2fft) %s", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft, Ht);
1822	record_clock_stats(&(instance->peer->srcadr), Msg);
1823}
1824
1825
1826
1827/*
1828 * get PPS Offset
1829 * Nb. @@Ay is not supported for early UT (no plus) model
1830 */
1831static void
1832oncore_msg_Ay(
1833	struct instance *instance,
1834	u_char *buf,
1835	u_int len
1836	)
1837{
1838	char Msg[120];
1839
1840	if (!instance->printed || instance->Ay)
1841		return;
1842
1843	instance->Ay = 1;
1844
1845	instance->offset = buf_w32(&buf[4]);
1846
1847	sprintf(Msg, "PPS Offset  is set to %ld ns", instance->offset);
1848	record_clock_stats(&(instance->peer->srcadr), Msg);
1849}
1850
1851
1852
1853/*
1854 * get Cable Delay
1855 */
1856static void
1857oncore_msg_Az(
1858	struct instance *instance,
1859	u_char *buf,
1860	u_int len
1861	)
1862{
1863	char Msg[120];
1864
1865	if (!instance->printed || instance->Az)
1866		return;
1867
1868	instance->Az = 1;
1869
1870	instance->delay = buf_w32(&buf[4]);
1871
1872	sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
1873	record_clock_stats(&(instance->peer->srcadr), Msg);
1874}
1875#else
1876int refclock_oncore_bs;
1877#endif /* REFCLOCK */
1878