154359Sroberto/*
254359Sroberto * ----------------------------------------------------------------------------
354359Sroberto * "THE BEER-WARE LICENSE" (Revision 42):
454359Sroberto * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
554359Sroberto * can do whatever you want with this stuff. If we meet some day, and you think
654359Sroberto * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
754359Sroberto * ----------------------------------------------------------------------------
854359Sroberto *
954359Sroberto * refclock_oncore.c
1054359Sroberto *
1154359Sroberto * Driver for some of the various the Motorola Oncore GPS receivers.
12132451Sroberto *   should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
13132451Sroberto *	The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
14285612Sdelphij *	   than the others.
1582498Sroberto *	The receivers without position hold (GT, GT+) will be less accurate.
1654359Sroberto *
1754359Sroberto * Tested with:
1854359Sroberto *
1954359Sroberto *		(UT)				   (VP)
2054359Sroberto *   COPYRIGHT 1991-1997 MOTOROLA INC.	COPYRIGHT 1991-1996 MOTOROLA INC.
2154359Sroberto *   SFTW P/N #     98-P36848P		SFTW P/N # 98-P36830P
2254359Sroberto *   SOFTWARE VER # 2			SOFTWARE VER # 8
2354359Sroberto *   SOFTWARE REV # 2			SOFTWARE REV # 8
2454359Sroberto *   SOFTWARE DATE  APR 24 1998 	SOFTWARE DATE  06 Aug 1996
2554359Sroberto *   MODEL #	R1121N1114		MODEL #    B4121P1155
2654359Sroberto *   HWDR P/N # 1			HDWR P/N # _
2754359Sroberto *   SERIAL #	R0010A			SERIAL #   SSG0226478
2854359Sroberto *   MANUFACTUR DATE 6H07		MANUFACTUR DATE 7E02
2954359Sroberto *					OPTIONS LIST	IB
3054359Sroberto *
3182498Sroberto *	      (Basic)				   (M12)
32132451Sroberto *   COPYRIGHT 1991-1994 MOTOROLA INC.	COPYRIGHT 1991-2000 MOTOROLA INC.
33132451Sroberto *   SFTW P/N # 98-P39949M		SFTW P/N # 61-G10002A
34132451Sroberto *   SOFTWARE VER # 5			SOFTWARE VER # 1
35132451Sroberto *   SOFTWARE REV # 0			SOFTWARE REV # 3
36132451Sroberto *   SOFTWARE DATE  20 JAN 1994 	SOFTWARE DATE  Mar 13 2000
37132451Sroberto *   MODEL #	A11121P116		MODEL #    P143T12NR1
3882498Sroberto *   HDWR P/N # _			HWDR P/N # 1
39132451Sroberto *   SERIAL #	SSG0049809		SERIAL #   P003UD
40132451Sroberto *   MANUFACTUR DATE 417AMA199		MANUFACTUR DATE 0C27
41132451Sroberto *   OPTIONS LIST    AB
4282498Sroberto *
43182007Sroberto *	      (M12+T)				  (M12+T later version)
44182007Sroberto *   COPYRIGHT 1991-2002 MOTOROLA INC.	COPYRIGHT 1991-2003 MOTOROLA INC.
45182007Sroberto *   SFTW P/N #     61-G10268A		SFTW P/N #     61-G10268A
46182007Sroberto *   SOFTWARE VER # 2			SOFTWARE VER # 2
47182007Sroberto *   SOFTWARE REV # 0			SOFTWARE REV # 1
48182007Sroberto *   SOFTWARE DATE  AUG 14 2002 	SOFTWARE DATE  APR 16 2003
49182007Sroberto *   MODEL #	P283T12T11		MODEL #    P273T12T12
50182007Sroberto *   HWDR P/N # 2			HWDR P/N # 2
51182007Sroberto *   SERIAL #	P04DC2			SERIAL #   P05Z7Z
52182007Sroberto *   MANUFACTUR DATE 2J17		MANUFACTUR DATE 3G15
53182007Sroberto *
5454359Sroberto * --------------------------------------------------------------------------
55285612Sdelphij * Reg Clemens (June 2009)
56285612Sdelphij * BUG[1220] OK, big patch, but mostly done mechanically.  Change direct calls to write
57285612Sdelphij * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog.
58285612Sdelphij * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New
59285612Sdelphij * routine oncore_log.
60285612Sdelphij * --------------------------------------------------------------------------
61285612Sdelphij * Reg Clemens (June 2009)
62285612Sdelphij * BUG[1218] The comment on where the oncore driver gets its input file does not
63285612Sdelphij * agree with the code.  Change the comment.
64285612Sdelphij * --------------------------------------------------------------------------
65285612Sdelphij * Reg Clemens (June 2009)
66285612Sdelphij * change exit statements to return(0) in main program.  I had assumed that if the
67285612Sdelphij * PPS driver did not start for some reason, we shuould stop NTPD itelf.  Others
68285612Sdelphij * disagree.  We now give an ERR log message and stop this driver.
69285612Sdelphij * --------------------------------------------------------------------------
70285612Sdelphij * Reg Clemens (June 2009)
71285612Sdelphij * A bytes available message for the input subsystem (Debug message).
72285612Sdelphij * --------------------------------------------------------------------------
73285612Sdelphij * Reg Clemens (Nov 2008)
74285612Sdelphij * This code adds a message for TRAIM messages.  Users often worry about the
75285612Sdelphij * driver not starting up, and it is often because of signal strength being low.
76285612Sdelphij * Low signal strength will give TRAIM messages.
77285612Sdelphij * --------------------------------------------------------------------------
78285612Sdelphij * Reg Clemens (Nov 2008)
79285612Sdelphij * Add waiting on Almanac Message.
80285612Sdelphij * --------------------------------------------------------------------------
81285612Sdelphij * Reg Clemens (Nov 2008)
82285612Sdelphij * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs
83285612Sdelphij * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command
84285612Sdelphij * that says 'Leap Pending'.  As documented it only becomes true in the month
85285612Sdelphij * before the leap second is to be applied, but in practice at least some of
86285612Sdelphij * the receivers turn this indicator on as soon as the message is posted, which
87285612Sdelphij * can be 6months early.  As such, we use the Bj command to turn on the
88285612Sdelphij * instance->pp->leap indicator but only run this test in December and June for
89285612Sdelphij * updates on 1Jan and 1July.
90285612Sdelphij *
91285612Sdelphij * The @@Gj command exists in later ONCOREs, and it gives the exact date
92285612Sdelphij * and size of the Leap Update.  It can be emulated in the VP using the @@Bl
93285612Sdelphij * command which reads the raw Satellite Broadcast Messages.
94285612Sdelphij * We use these two commands to print informative messages in the clockstats
95285612Sdelphij * file once per day as soon as the message appears on the satellites.
96285612Sdelphij * --------------------------------------------------------------------------
97182007Sroberto * Reg Clemens (Feb 2006)
98182007Sroberto * Fix some gcc4 compiler complaints
99182007Sroberto * Fix possible segfault in oncore_init_shmem
100182007Sroberto * change all (possible) fprintf(stderr, to record_clock_stats
101182007Sroberto * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct
102182007Sroberto *   immediately after new Almanac Read.
103182007Sroberto * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it>
104182007Sroberto *   now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or
105182007Sroberto *   the new one.  Compiles depending on timepps.h seen.
106182007Sroberto * --------------------------------------------------------------------------
107182007Sroberto * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds
108182007Sroberto * (the oncore driver was setting the wrong ntpd variable)
109182007Sroberto * --------------------------------------------------------------------------
110182007Sroberto * Reg.Clemens (Mar 2004)
111182007Sroberto * Support for interfaces other than PPSAPI removed, for Solaris, SunOS,
112182007Sroberto * SCO, you now need to use one of the timepps.h files in the root dir.
113182007Sroberto * this driver will 'grab' it for you if you dont have one in /usr/include
114182007Sroberto * --------------------------------------------------------------------------
11554359Sroberto * This code uses the two devices
11682498Sroberto *	/dev/oncore.serial.n
11782498Sroberto *	/dev/oncore.pps.n
11854359Sroberto * which may be linked to the same device.
11954359Sroberto * and can read initialization data from the file
12082498Sroberto *	/etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
12182498Sroberto *	n or N are the unit number, viz 127.127.30.N.
12254359Sroberto * --------------------------------------------------------------------------
12354359Sroberto * Reg.Clemens <reg@dwf.com> Sep98.
12454359Sroberto *  Original code written for FreeBSD.
12582498Sroberto *  With these mods it works on FreeBSD, SunOS, Solaris and Linux
12682498Sroberto *    (SunOS 4.1.3 + ppsclock)
12782498Sroberto *    (Solaris7 + MU4)
12882498Sroberto *    (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
12954359Sroberto *
13054359Sroberto *  Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
13154359Sroberto *  state machine state) are printed to CLOCKSTATS if that file is enabled
13254359Sroberto *  in /etc/ntp.conf.
13354359Sroberto *
13454359Sroberto * --------------------------------------------------------------------------
13582498Sroberto *
13654359Sroberto * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
13754359Sroberto * doing an average of 10000 valid 2D and 3D fixes is what the automatic
13854359Sroberto * site survey mode does.  Looking at the output from the receiver
13954359Sroberto * it seems like it is only using 3D fixes.
14054359Sroberto * When we do it ourselves, take 10000 3D fixes.
14154359Sroberto */
14254359Sroberto
14354359Sroberto#define POS_HOLD_AVERAGE	10000	/* nb, 10000s ~= 2h45m */
14454359Sroberto
14556746Sroberto/*
14656746Sroberto * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
14756746Sroberto * "STATUS" line in the oncore config file, which contains the most recent
14882498Sroberto * copy of all types of messages we recognize.	This file can be mmap(2)'ed
14956746Sroberto * by monitoring and statistics programs.
15082498Sroberto *
15182498Sroberto * See separate HTML documentation for this option.
15256746Sroberto */
15356746Sroberto
15454359Sroberto#ifdef HAVE_CONFIG_H
15554359Sroberto#include <config.h>
15654359Sroberto#endif
15754359Sroberto
158182007Sroberto#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
15954359Sroberto
16082498Sroberto#include "ntpd.h"
16182498Sroberto#include "ntp_io.h"
16282498Sroberto#include "ntp_unixtime.h"
16382498Sroberto#include "ntp_refclock.h"
164285612Sdelphij#include "ntp_calendar.h"
16582498Sroberto#include "ntp_stdlib.h"
16682498Sroberto
16754359Sroberto#include <stdio.h>
168285612Sdelphij#include <stdarg.h>
16954359Sroberto#include <ctype.h>
17054359Sroberto#include <sys/stat.h>
17156746Sroberto#ifdef ONCORE_SHMEM_STATUS
17256746Sroberto# ifdef HAVE_SYS_MMAN_H
17356746Sroberto#  include <sys/mman.h>
17456746Sroberto#  ifndef MAP_FAILED
17556746Sroberto#   define MAP_FAILED ((u_char *) -1)
176182007Sroberto#  endif  /* MAP_FAILED */
17756746Sroberto# endif /* HAVE_SYS_MMAN_H */
17856746Sroberto#endif /* ONCORE_SHMEM_STATUS */
17954359Sroberto
18054359Sroberto#ifdef HAVE_PPSAPI
181182007Sroberto# include "ppsapi_timepps.h"
18254359Sroberto#endif
18354359Sroberto
184285612Sdelphijstruct Bl {
185285612Sdelphij	int	dt_ls;
186285612Sdelphij	int	dt_lsf;
187285612Sdelphij	int	WN;
188285612Sdelphij	int	DN;
189285612Sdelphij	int	WN_lsf;
190285612Sdelphij	int	DN_lsf;
191285612Sdelphij	int	wn_flg;
192285612Sdelphij	int	lsf_flg;
193285612Sdelphij	int	Bl_day;
194285612Sdelphij} Bl;
19556746Sroberto
19654359Srobertoenum receive_state {
19754359Sroberto	ONCORE_NO_IDEA,
198132451Sroberto	ONCORE_CHECK_ID,
199132451Sroberto	ONCORE_CHECK_CHAN,
200132451Sroberto	ONCORE_HAVE_CHAN,
20154359Sroberto	ONCORE_RESET_SENT,
20254359Sroberto	ONCORE_TEST_SENT,
20382498Sroberto	ONCORE_INIT,
20454359Sroberto	ONCORE_ALMANAC,
20554359Sroberto	ONCORE_RUN
20654359Sroberto};
20754359Sroberto
20854359Srobertoenum site_survey_state {
20954359Sroberto	ONCORE_SS_UNKNOWN,
21082498Sroberto	ONCORE_SS_TESTING,
21154359Sroberto	ONCORE_SS_HW,
21254359Sroberto	ONCORE_SS_SW,
21354359Sroberto	ONCORE_SS_DONE
21454359Sroberto};
21554359Sroberto
216132451Srobertoenum antenna_state {
217132451Sroberto      ONCORE_ANTENNA_UNKNOWN = -1,
218132451Sroberto      ONCORE_ANTENNA_OK      =	0,
219132451Sroberto      ONCORE_ANTENNA_OC      =	1,
220132451Sroberto      ONCORE_ANTENNA_UC      =	2,
221132451Sroberto      ONCORE_ANTENNA_NV      =	3
222132451Sroberto};
223132451Sroberto
22482498Sroberto/* Model Name, derived from the @@Cj message.
22582498Sroberto * Used to initialize some variables.
22682498Sroberto */
22782498Sroberto
22882498Srobertoenum oncore_model {
22982498Sroberto	ONCORE_BASIC,
23082498Sroberto	ONCORE_PVT6,
23182498Sroberto	ONCORE_VP,
23282498Sroberto	ONCORE_UT,
23382498Sroberto	ONCORE_UTPLUS,
23482498Sroberto	ONCORE_GT,
23582498Sroberto	ONCORE_GTPLUS,
23682498Sroberto	ONCORE_SL,
23782498Sroberto	ONCORE_M12,
23882498Sroberto	ONCORE_UNKNOWN
23982498Sroberto};
24082498Sroberto
24182498Sroberto/* the bits that describe these properties are in the same place
24282498Sroberto * on the VP/UT, but have moved on the M12.  As such we extract
24382498Sroberto * them, and use them from this struct.
24482498Sroberto *
24582498Sroberto */
24682498Sroberto
24782498Srobertostruct RSM {
24882498Sroberto	u_char	posn0D;
24982498Sroberto	u_char	posn2D;
25082498Sroberto	u_char	posn3D;
25182498Sroberto	u_char	bad_almanac;
25282498Sroberto	u_char	bad_fix;
25382498Sroberto};
25482498Sroberto
25582498Sroberto/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
25682498Sroberto * see what mode it is in.  The bits on the M12 are multiplexed with
25782498Sroberto * other messages, so we have to 'keep' the last known mode here.
25882498Sroberto */
25982498Sroberto
26082498Srobertoenum posn_mode {
26182498Sroberto	MODE_UNKNOWN,
26282498Sroberto	MODE_0D,
26382498Sroberto	MODE_2D,
26482498Sroberto	MODE_3D
26582498Sroberto};
26682498Sroberto
26754359Srobertostruct instance {
26854359Sroberto	int	unit;		/* 127.127.30.unit */
26982498Sroberto	struct	refclockproc *pp;
27082498Sroberto	struct	peer *peer;
27182498Sroberto
27254359Sroberto	int	ttyfd;		/* TTY file descriptor */
27354359Sroberto	int	ppsfd;		/* PPS file descriptor */
274132451Sroberto	int	shmemfd;	/* Status shm descriptor */
27554359Sroberto	pps_handle_t pps_h;
27654359Sroberto	pps_params_t pps_p;
27754359Sroberto	enum receive_state o_state;		/* Receive state */
27882498Sroberto	enum posn_mode mode;			/* 0D, 2D, 3D */
27954359Sroberto	enum site_survey_state site_survey;	/* Site Survey state */
280132451Sroberto	enum antenna_state ant_state;		/* antenna state */
28154359Sroberto
28254359Sroberto	int	Bj_day;
28354359Sroberto
28482498Sroberto	u_long	delay;		/* ns */
28554359Sroberto	long	offset; 	/* ns */
28654359Sroberto
28782498Sroberto	u_char	*shmem;
28882498Sroberto	char	*shmem_fname;
28982498Sroberto	u_int	shmem_Cb;
29082498Sroberto	u_int	shmem_Ba;
29182498Sroberto	u_int	shmem_Ea;
29282498Sroberto	u_int	shmem_Ha;
29382498Sroberto	u_char	shmem_reset;
29482498Sroberto	u_char	shmem_Posn;
295132451Sroberto	u_char	shmem_bad_Ea;
296132451Sroberto	u_char	almanac_from_shmem;
29782498Sroberto
29854359Sroberto	double	ss_lat;
29954359Sroberto	double	ss_long;
30054359Sroberto	double	ss_ht;
30182498Sroberto	double	dH;
30254359Sroberto	int	ss_count;
30382498Sroberto	u_char	posn_set;
30454359Sroberto
30582498Sroberto	enum oncore_model model;
30682498Sroberto	u_int	version;
30782498Sroberto	u_int	revision;
30882498Sroberto
30982498Sroberto	u_char	chan;		/* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
310182007Sroberto	s_char	traim;		/* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */
311132451Sroberto				/* the following 7 are all timing counters */
31282498Sroberto	u_char	traim_delay;	/* seconds counter, waiting for reply */
313132451Sroberto	u_char	count;		/* cycles thru Ea before starting */
314132451Sroberto	u_char	count1; 	/* cycles thru Ea after SS_TESTING, waiting for SS_HW */
315132451Sroberto	u_char	count2; 	/* cycles thru Ea after count, to check for @@Ea */
316132451Sroberto	u_char	count3; 	/* cycles thru Ea checking for # channels */
317132451Sroberto	u_char	count4; 	/* cycles thru leap after Gj to issue Bj */
318182007Sroberto	u_char	count5; 	/* cycles thru get_timestamp waiting for valid UTC correction */
319182007Sroberto	u_char	count5_set;	/* only set count5 once */
320285612Sdelphij	u_char	counta; 	/* count for waiting on almanac message */
321132451Sroberto	u_char	pollcnt;
322132451Sroberto	u_char	timeout;	/* count to retry Cj after Fa self-test */
323285612Sdelphij	u_char	max_len;	/* max length message seen by oncore_log, for debugging */
324285612Sdelphij	u_char	max_count;	/* count for message statistics */
32582498Sroberto
32682498Sroberto	struct	RSM rsm;	/* bits extracted from Receiver Status Msg in @@Ea */
327285612Sdelphij	struct	Bl Bl;		/* Satellite Broadcast Data Message */
32882498Sroberto	u_char	printed;
32982498Sroberto	u_char	polled;
330132451Sroberto	u_long	ev_serial;
331285612Sdelphij	unsigned	Rcvptr;
33254359Sroberto	u_char	Rcvbuf[500];
333132451Sroberto	u_char	BEHa[160];	/* Ba, Ea or Ha */
334132451Sroberto	u_char	BEHn[80];	/* Bn , En , or Hn */
33554359Sroberto	u_char	Cj[300];
336132451Sroberto	u_char	Ag;		/* Satellite mask angle */
337132451Sroberto	u_char	saw_At;
338132451Sroberto	u_char	saw_Ay;
339132451Sroberto	u_char	saw_Az;
340285612Sdelphij	s_char	saw_Bj;
341132451Sroberto	s_char	saw_Gj;
34282498Sroberto	u_char	have_dH;
34354359Sroberto	u_char	init_type;
34454359Sroberto	s_char	saw_tooth;
345132451Sroberto	s_char	chan_in;	/* chan number from INPUT, will always use it */
346132451Sroberto	u_char	chan_id;	/* chan number determined from part number */
347132451Sroberto	u_char	chan_ck;	/* chan number determined by sending commands to hardware */
348182007Sroberto	s_char	traim_in;	/* TRAIM from INPUT, will always use ON/OFF specified */
349132451Sroberto	s_char	traim_id;	/* TRAIM determined from part number */
350132451Sroberto	u_char	traim_ck;	/* TRAIM determined by sending commands to hardware */
351132451Sroberto	u_char	once;		/* one pass code at top of BaEaHa */
35282498Sroberto	s_char	assert;
353132451Sroberto	u_char	hardpps;
354285612Sdelphij	s_char	pps_control;	/* PPS control, M12 only */
355285612Sdelphij	s_char	pps_control_msg_seen;
35654359Sroberto};
35754359Sroberto
35854359Sroberto#define rcvbuf	instance->Rcvbuf
35954359Sroberto#define rcvptr	instance->Rcvptr
36054359Sroberto
361285612Sdelphijstatic	int	oncore_start	      (int, struct peer *);
362285612Sdelphijstatic	void	oncore_poll	      (int, struct peer *);
363285612Sdelphijstatic	void	oncore_shutdown       (int, struct peer *);
364285612Sdelphijstatic	void	oncore_consume	      (struct instance *);
365285612Sdelphijstatic	void	oncore_read_config    (struct instance *);
366285612Sdelphijstatic	void	oncore_receive	      (struct recvbuf *);
367285612Sdelphijstatic	int	oncore_ppsapi	      (struct instance *);
368285612Sdelphijstatic	void	oncore_get_timestamp  (struct instance *, long, long);
369285612Sdelphijstatic	void	oncore_init_shmem     (struct instance *);
37054359Sroberto
371285612Sdelphijstatic	void	oncore_antenna_report (struct instance *, enum antenna_state);
372285612Sdelphijstatic	void	oncore_chan_test      (struct instance *);
373285612Sdelphijstatic	void	oncore_check_almanac  (struct instance *);
374285612Sdelphijstatic	void	oncore_check_antenna  (struct instance *);
375285612Sdelphijstatic	void	oncore_check_leap_sec (struct instance *);
376285612Sdelphijstatic	int	oncore_checksum_ok    (u_char *, int);
377285612Sdelphijstatic	void	oncore_compute_dH     (struct instance *);
378285612Sdelphijstatic	void	oncore_load_almanac   (struct instance *);
379285612Sdelphijstatic	void	oncore_log	      (struct instance *, int, const char *);
380285612Sdelphijstatic	int	oncore_log_f	      (struct instance *, int, const char *, ...)
381285612Sdelphij		NTP_PRINTF(3, 4);
382285612Sdelphijstatic	void	oncore_print_Cb       (struct instance *, u_char *);
383285612Sdelphij/* static  void    oncore_print_array	 (u_char *, int);	*/
384285612Sdelphijstatic	void	oncore_print_posn     (struct instance *);
385285612Sdelphijstatic	void	oncore_sendmsg	      (struct instance *, u_char *, size_t);
386285612Sdelphijstatic	void	oncore_set_posn       (struct instance *);
387285612Sdelphijstatic	void	oncore_set_traim      (struct instance *);
388285612Sdelphijstatic	void	oncore_shmem_get_3D   (struct instance *);
389285612Sdelphijstatic	void	oncore_ss	      (struct instance *);
390285612Sdelphijstatic	int	oncore_wait_almanac   (struct instance *);
391132451Sroberto
392285612Sdelphijstatic	void	oncore_msg_any	   (struct instance *, u_char *, size_t, int);
393285612Sdelphijstatic	void	oncore_msg_Adef    (struct instance *, u_char *, size_t);
394285612Sdelphijstatic	void	oncore_msg_Ag	   (struct instance *, u_char *, size_t);
395285612Sdelphijstatic	void	oncore_msg_As	   (struct instance *, u_char *, size_t);
396285612Sdelphijstatic	void	oncore_msg_At	   (struct instance *, u_char *, size_t);
397285612Sdelphijstatic	void	oncore_msg_Ay	   (struct instance *, u_char *, size_t);
398285612Sdelphijstatic	void	oncore_msg_Az	   (struct instance *, u_char *, size_t);
399285612Sdelphijstatic	void	oncore_msg_BaEaHa  (struct instance *, u_char *, size_t);
400285612Sdelphijstatic	void	oncore_msg_Bd	   (struct instance *, u_char *, size_t);
401285612Sdelphijstatic	void	oncore_msg_Bj	   (struct instance *, u_char *, size_t);
402285612Sdelphijstatic	void	oncore_msg_Bl	   (struct instance *, u_char *, size_t);
403285612Sdelphijstatic	void	oncore_msg_BnEnHn  (struct instance *, u_char *, size_t);
404285612Sdelphijstatic	void	oncore_msg_CaFaIa  (struct instance *, u_char *, size_t);
405285612Sdelphijstatic	void	oncore_msg_Cb	   (struct instance *, u_char *, size_t);
406285612Sdelphijstatic	void	oncore_msg_Cf	   (struct instance *, u_char *, size_t);
407285612Sdelphijstatic	void	oncore_msg_Cj	   (struct instance *, u_char *, size_t);
408285612Sdelphijstatic	void	oncore_msg_Cj_id   (struct instance *, u_char *, size_t);
409285612Sdelphijstatic	void	oncore_msg_Cj_init (struct instance *, u_char *, size_t);
410285612Sdelphijstatic	void	oncore_msg_Ga	   (struct instance *, u_char *, size_t);
411285612Sdelphijstatic	void	oncore_msg_Gb	   (struct instance *, u_char *, size_t);
412285612Sdelphijstatic	void	oncore_msg_Gc	   (struct instance *, u_char *, size_t);
413285612Sdelphijstatic	void	oncore_msg_Gj	   (struct instance *, u_char *, size_t);
414285612Sdelphijstatic	void	oncore_msg_Sz	   (struct instance *, u_char *, size_t);
41554359Sroberto
41654359Srobertostruct	refclock refclock_oncore = {
41754359Sroberto	oncore_start,		/* start up driver */
41854359Sroberto	oncore_shutdown,	/* shut down driver */
41954359Sroberto	oncore_poll,		/* transmit poll message */
42054359Sroberto	noentry,		/* not used */
42154359Sroberto	noentry,		/* not used */
422182007Sroberto	noentry,		/* not used */
42354359Sroberto	NOFLAGS 		/* not used */
42454359Sroberto};
42554359Sroberto
42654359Sroberto/*
42754359Sroberto * Understanding the next bit here is not easy unless you have a manual
42882498Sroberto * for the the various Oncore Models.
42954359Sroberto */
43054359Sroberto
43156746Srobertostatic struct msg_desc {
43254359Sroberto	const char	flag[3];
43354359Sroberto	const int	len;
434285612Sdelphij	void		(*handler) (struct instance *, u_char *, size_t);
43554359Sroberto	const char	*fmt;
43656746Sroberto	int		shmem;
43754359Sroberto} oncore_messages[] = {
43882498Sroberto			/* Ea and En first since they're most common */
439285612Sdelphij	{ "Ea",  76,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC", 0 },
440285612Sdelphij	{ "Ba",  68,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC", 0 },
441285612Sdelphij	{ "Ha", 154,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC", 0 },
442285612Sdelphij	{ "Bn",  59,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC", 0 },
443285612Sdelphij	{ "En",  69,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC", 0 },
444285612Sdelphij	{ "Hn",  78,    oncore_msg_BnEnHn, "", 0 },
445285612Sdelphij	{ "Ab",  10,    0,                 "", 0 },
446285612Sdelphij	{ "Ac",  11,    0,                 "", 0 },
447285612Sdelphij	{ "Ad",  11,    oncore_msg_Adef,   "", 0 },
448285612Sdelphij	{ "Ae",  11,    oncore_msg_Adef,   "", 0 },
449285612Sdelphij	{ "Af",  15,    oncore_msg_Adef,   "", 0 },
450285612Sdelphij	{ "Ag",   8,    oncore_msg_Ag,     "", 0 }, /* Satellite mask angle */
451285612Sdelphij	{ "As",  20,    oncore_msg_As,     "", 0 },
452285612Sdelphij	{ "At",   8,    oncore_msg_At,     "", 0 },
453285612Sdelphij	{ "Au",  12,    0,                 "", 0 },
454285612Sdelphij	{ "Av",   8,    0,                 "", 0 },
455285612Sdelphij	{ "Aw",   8,    0,                 "", 0 },
456285612Sdelphij	{ "Ay",  11,    oncore_msg_Ay,     "", 0 },
457285612Sdelphij	{ "Az",  11,    oncore_msg_Az,     "", 0 },
458285612Sdelphij	{ "AB",   8,    0,                 "", 0 },
459285612Sdelphij	{ "Bb",  92,    0,                 "", 0 },
460285612Sdelphij	{ "Bd",  23,    oncore_msg_Bd,     "", 0 },
461285612Sdelphij	{ "Bj",   8,    oncore_msg_Bj,     "", 0 },
462285612Sdelphij	{ "Bl",  41,    oncore_msg_Bl,     "", 0 },
463285612Sdelphij	{ "Ca",   9,    oncore_msg_CaFaIa, "", 0 },
464285612Sdelphij	{ "Cb",  33,    oncore_msg_Cb,     "", 0 },
465285612Sdelphij	{ "Cf",   7,    oncore_msg_Cf,     "", 0 },
466285612Sdelphij	{ "Cg",   8,    0,                 "", 0 },
467285612Sdelphij	{ "Ch",   9,    0,                 "", 0 },
468285612Sdelphij	{ "Cj", 294,    oncore_msg_Cj,     "", 0 },
469285612Sdelphij	{ "Ek",  71,    0,                 "", 0 },
470285612Sdelphij	{ "Fa",   9,    oncore_msg_CaFaIa, "", 0 },
471285612Sdelphij	{ "Ga",  20,    oncore_msg_Ga,     "", 0 },
472285612Sdelphij	{ "Gb",  17,    oncore_msg_Gb,     "", 0 },
473285612Sdelphij	{ "Gc",   8,    oncore_msg_Gc,     "", 0 },
474285612Sdelphij	{ "Gd",   8,    0,                 "", 0 },
475285612Sdelphij	{ "Ge",   8,    0,                 "", 0 },
476285612Sdelphij	{ "Gj",  21,    oncore_msg_Gj,     "", 0 },
477285612Sdelphij	{ "Ia",  10,    oncore_msg_CaFaIa, "", 0 },
478285612Sdelphij	{ "Sz",   8,    oncore_msg_Sz,     "", 0 },
479285612Sdelphij	{ {0},	  7,	0,		   "", 0 }
48054359Sroberto};
48154359Sroberto
48254359Sroberto
483132451Srobertostatic u_char oncore_cmd_Aa[]  = { 'A', 'a', 0, 0, 0 }; 			    /* 6/8	Time of Day				*/
484132451Srobertostatic u_char oncore_cmd_Ab[]  = { 'A', 'b', 0, 0, 0 }; 			    /* 6/8	GMT Correction				*/
485132451Srobertostatic u_char oncore_cmd_AB[]  = { 'A', 'B', 4 };				    /* VP	Application Type: Static		*/
486132451Srobertostatic u_char oncore_cmd_Ac[]  = { 'A', 'c', 0, 0, 0, 0 };			    /* 6/8	Date					*/
487132451Srobertostatic u_char oncore_cmd_Ad[]  = { 'A', 'd', 0,0,0,0 }; 			    /* 6/8	Latitude				*/
488132451Srobertostatic u_char oncore_cmd_Ae[]  = { 'A', 'e', 0,0,0,0 }; 			    /* 6/8	Longitude				*/
489132451Srobertostatic u_char oncore_cmd_Af[]  = { 'A', 'f', 0,0,0,0, 0 };			    /* 6/8	Height					*/
490132451Srobertostatic u_char oncore_cmd_Ag[]  = { 'A', 'g', 0 };				    /* 6/8/12	Satellite Mask Angle			*/
491132451Srobertostatic u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff };				    /* 6/8/12	Satellite Mask Angle: read		*/
492132451Srobertostatic u_char oncore_cmd_As[]  = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };	    /* 6/8/12	Posn Hold Parameters			*/
493132451Srobertostatic u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff,		    /* 6/8/12	Posn Hold Readback			*/
494132451Sroberto					     0x7f,0xff,0xff,0xff,		    /*		 on UT+ this doesnt work with 0xff	*/
495132451Sroberto					     0x7f,0xff,0xff,0xff, 0xff };	    /*		 but does work with 0x7f (sigh).	*/
496132451Srobertostatic u_char oncore_cmd_At0[] = { 'A', 't', 0 };				    /* 6/8	Posn Hold: off				*/
497132451Srobertostatic u_char oncore_cmd_At1[] = { 'A', 't', 1 };				    /* 6/8	Posn Hold: on				*/
498132451Srobertostatic u_char oncore_cmd_At2[] = { 'A', 't', 2 };				    /* 6/8	Posn Hold: Start Site Survey		*/
499132451Srobertostatic u_char oncore_cmd_Atx[] = { 'A', 't', 0xff };				    /* 6/8	Posn Hold: Read Back			*/
500132451Srobertostatic u_char oncore_cmd_Au[]  = { 'A', 'u', 0,0,0,0, 0 };			    /* GT/M12	Altitude Hold Ht.			*/
501132451Srobertostatic u_char oncore_cmd_Av0[] = { 'A', 'v', 0 };				    /* VP/GT	Altitude Hold: off			*/
502132451Srobertostatic u_char oncore_cmd_Av1[] = { 'A', 'v', 1 };				    /* VP/GT	Altitude Hold: on			*/
503132451Srobertostatic u_char oncore_cmd_Aw[]  = { 'A', 'w', 1 };				    /* 6/8/12	UTC/GPS time selection			*/
504132451Srobertostatic u_char oncore_cmd_Ay[]  = { 'A', 'y', 0, 0, 0, 0 };			    /* Timing	1PPS time offset: set			*/
505132451Srobertostatic u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };		    /* Timing	1PPS time offset: Read			*/
506132451Srobertostatic u_char oncore_cmd_Az[]  = { 'A', 'z', 0, 0, 0, 0 };			    /* 6/8UT/12 1PPS Cable Delay: set			*/
507132451Srobertostatic u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };		    /* 6/8UT/12 1PPS Cable Delay: Read			*/
508132451Srobertostatic u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 };				    /* 6	Position/Data/Status: off		*/
509132451Srobertostatic u_char oncore_cmd_Ba[]  = { 'B', 'a', 1 };				    /* 6	Position/Data/Status: on		*/
510132451Srobertostatic u_char oncore_cmd_Bb[]  = { 'B', 'b', 1 };				    /* 6/8/12	Visible Satellites			*/
511132451Srobertostatic u_char oncore_cmd_Bd[]  = { 'B', 'd', 1 };				    /* 6/8/12?	Almanac Status Msg.			*/
512132451Srobertostatic u_char oncore_cmd_Be[]  = { 'B', 'e', 1 };				    /* 6/8/12	Request Almanac Data			*/
513132451Srobertostatic u_char oncore_cmd_Bj[]  = { 'B', 'j', 0 };				    /* 6/8	Leap Second Pending			*/
514285612Sdelphijstatic u_char oncore_cmd_Bl[]  = { 'B', 'l', 1 };				    /* VP	Satellite Broadcast Data Msg		*/
515132451Srobertostatic u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg off, traim on	*/
516182007Srobertostatic u_char oncore_cmd_Bn[]  = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg on,  traim on	*/
517182007Srobertostatic u_char oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg off, traim off	*/
518132451Srobertostatic u_char oncore_cmd_Ca[]  = { 'C', 'a' };					    /* 6	Self Test				*/
519132451Srobertostatic u_char oncore_cmd_Cf[]  = { 'C', 'f' };					    /* 6/8/12	Set to Defaults 			*/
520132451Srobertostatic u_char oncore_cmd_Cg[]  = { 'C', 'g', 1 };				    /* VP	Posn Fix/Idle Mode			*/
521132451Srobertostatic u_char oncore_cmd_Cj[]  = { 'C', 'j' };					    /* 6/8/12	Receiver ID				*/
522132451Srobertostatic u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 };				    /* 8	Position/Data/Status: off		*/
523132451Srobertostatic u_char oncore_cmd_Ea[]  = { 'E', 'a', 1 };				    /* 8	Position/Data/Status: on		*/
524132451Srobertostatic u_char oncore_cmd_Ek[]  = { 'E', 'k', 0 }; /* just turn off */		    /* 8	Posn/Status/Data - extension		*/
525132451Srobertostatic u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg off, traim on	*/
526182007Srobertostatic u_char oncore_cmd_En[]  = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg on,  traim on	*/
527182007Srobertostatic u_char oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg off, traim off	*/
528132451Srobertostatic u_char oncore_cmd_Fa[]  = { 'F', 'a' };					    /* 8	Self Test				*/
529132451Srobertostatic u_char oncore_cmd_Ga[]  = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };	    /* 12	Position Set				*/
530132451Srobertostatic u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff,		    /* 12	Position Set: Read			*/
531132451Sroberto					     0xff, 0xff, 0xff, 0xff,		    /*							*/
532132451Sroberto					     0xff, 0xff, 0xff, 0xff, 0xff };	    /*							*/
533132451Srobertostatic u_char oncore_cmd_Gb[]  = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	    /* 12	set Date/Time				*/
534285612Sdelphijstatic u_char oncore_cmd_Gc[]  = { 'G', 'c', 0 };				    /* 12	PPS Control: Off, On, 1+satellite,TRAIM */
535132451Srobertostatic u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 };				    /* 12	Position Control: 3D (no hold)		*/
536132451Srobertostatic u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 };				    /* 12	Position Control: 0D (3D hold)		*/
537132451Srobertostatic u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 };				    /* 12	Position Control: 2D (Alt Hold) 	*/
538132451Srobertostatic u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 };				    /* 12	Position Coltrol: Start Site Survey	*/
539132451Srobertostatic u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 };				    /* M12+T	TRAIM: off				*/
540132451Srobertostatic u_char oncore_cmd_Ge[]  = { 'G', 'e', 1 };				    /* M12+T	TRAIM: on				*/
541132451Srobertostatic u_char oncore_cmd_Gj[]  = { 'G', 'j' };					    /* 8?/12	Leap Second Pending			*/
542132451Srobertostatic u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 };				    /* 12	Position/Data/Status: off		*/
543132451Srobertostatic u_char oncore_cmd_Ha[]  = { 'H', 'a', 1 };				    /* 12	Position/Data/Status: on		*/
544132451Srobertostatic u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 };				    /* 12	TRAIM Status: off			*/
545132451Srobertostatic u_char oncore_cmd_Hn[]  = { 'H', 'n', 1 };				    /* 12	TRAIM Status: on			*/
546132451Srobertostatic u_char oncore_cmd_Ia[]  = { 'I', 'a' };					    /* 12	Self Test				*/
54754359Sroberto
548132451Sroberto/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
549132451Sroberto *				    the GT had Au,Av, but not As,At
550132451Sroberto * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
551132451Sroberto * Bj in UT at v1.3
552132451Sroberto * dont see Bd in UT/GT thru 1999
553132451Sroberto * Gj in UT as of 3.0, 1999 , Bj as of 1.3
55454359Sroberto */
55554359Sroberto
556182007Sroberto#define DEVICE1 	"/dev/oncore.serial.%d" /* name of serial device */
557182007Sroberto#define DEVICE2 	"/dev/oncore.pps.%d"    /* name of pps device */
55854359Sroberto
55954359Sroberto#define SPEED		B9600		/* Oncore Binary speed (9600 bps) */
56054359Sroberto
56154359Sroberto/*
56254359Sroberto * Assemble and disassemble 32bit signed quantities from a buffer.
56354359Sroberto *
56454359Sroberto */
56554359Sroberto
56654359Sroberto	/* to buffer, int w, u_char *buf */
56782498Sroberto#define w32_buf(buf,w)	{ u_int i_tmp;			   \
56854359Sroberto			  i_tmp = (w<0) ? (~(-w)+1) : (w); \
56954359Sroberto			  (buf)[0] = (i_tmp >> 24) & 0xff; \
57054359Sroberto			  (buf)[1] = (i_tmp >> 16) & 0xff; \
57154359Sroberto			  (buf)[2] = (i_tmp >>	8) & 0xff; \
57254359Sroberto			  (buf)[3] = (i_tmp	 ) & 0xff; \
57354359Sroberto			}
57454359Sroberto
57554359Sroberto#define w32(buf)      (((buf)[0]&0xff) << 24 | \
57654359Sroberto		       ((buf)[1]&0xff) << 16 | \
57754359Sroberto		       ((buf)[2]&0xff) <<  8 | \
57854359Sroberto		       ((buf)[3]&0xff) )
57954359Sroberto
58054359Sroberto	/* from buffer, char *buf, result to an int */
58154359Sroberto#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
58254359Sroberto
58356746Sroberto
58454359Sroberto/*
58554359Sroberto * oncore_start - initialize data for processing
58654359Sroberto */
58782498Sroberto
58854359Srobertostatic int
58954359Srobertooncore_start(
59054359Sroberto	int unit,
59154359Sroberto	struct peer *peer
59254359Sroberto	)
59354359Sroberto{
594182007Sroberto#define STRING_LEN	32
59554359Sroberto	register struct instance *instance;
59654359Sroberto	struct refclockproc *pp;
597285612Sdelphij	int fd1, fd2;
598285612Sdelphij	char device1[STRING_LEN], device2[STRING_LEN];
599285612Sdelphij#ifndef SYS_WINNT
60054359Sroberto	struct stat stat1, stat2;
601285612Sdelphij#endif
60254359Sroberto
603132451Sroberto	/* create instance structure for this unit */
604132451Sroberto
605285612Sdelphij	instance = emalloc(sizeof(*instance));
606285612Sdelphij	memset(instance, 0, sizeof(*instance));
607132451Sroberto
608132451Sroberto	/* initialize miscellaneous variables */
60954359Sroberto
61054359Sroberto	pp = peer->procptr;
61182498Sroberto	instance->pp   = pp;
61282498Sroberto	instance->unit = unit;
61382498Sroberto	instance->peer = peer;
614132451Sroberto	instance->assert = 1;
615132451Sroberto	instance->once = 1;
61682498Sroberto
61754359Sroberto	instance->Bj_day = -1;
61882498Sroberto	instance->traim = -1;
619132451Sroberto	instance->traim_in = -1;
620132451Sroberto	instance->chan_in = -1;
621285612Sdelphij	instance->pps_control = -1;	/* PPS control, M12 only */
622285612Sdelphij	instance->pps_control_msg_seen = -1;	/* Have seen response to Gc msg */
62382498Sroberto	instance->model = ONCORE_UNKNOWN;
62482498Sroberto	instance->mode = MODE_UNKNOWN;
62582498Sroberto	instance->site_survey = ONCORE_SS_UNKNOWN;
626132451Sroberto	instance->Ag = 0xff;		/* Satellite mask angle, unset by user */
627132451Sroberto	instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
62854359Sroberto
629285612Sdelphij	peer->flags &= ~FLAG_PPS;	/* PPS not active yet */
63082498Sroberto	peer->precision = -26;
63182498Sroberto	peer->minpoll = 4;
63282498Sroberto	peer->maxpoll = 4;
63382498Sroberto	pp->clockdesc = "Motorola Oncore GPS Receiver";
63482498Sroberto	memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
63582498Sroberto
636285612Sdelphij	oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING");
637182007Sroberto	instance->o_state = ONCORE_NO_IDEA;
638285612Sdelphij	oncore_log(instance, LOG_NOTICE, "state = ONCORE_NO_IDEA");
63954359Sroberto
640182007Sroberto	/* Now open files.
641182007Sroberto	 * This is a bit complicated, a we dont want to open the same file twice
642182007Sroberto	 * (its a problem on some OS), and device2 may not exist for the new PPS
643182007Sroberto	 */
644182007Sroberto
645285612Sdelphij	(void)snprintf(device1, sizeof(device1), DEVICE1, unit);
646285612Sdelphij	(void)snprintf(device2, sizeof(device2), DEVICE2, unit);
647182007Sroberto
648182007Sroberto	/* OPEN DEVICES */
649182007Sroberto	/* opening different devices for fd1 and fd2 presents no problems */
650182007Sroberto	/* opening the SAME device twice, seems to be OS dependent.
651182007Sroberto		(a) on Linux (no streams) no problem
652182007Sroberto		(b) on SunOS (and possibly Solaris, untested), (streams)
653182007Sroberto			never see the line discipline.
654182007Sroberto	   Since things ALWAYS work if we only open the device once, we check
655182007Sroberto	     to see if the two devices are in fact the same, then proceed to
656182007Sroberto	     do one open or two.
657285612Sdelphij
658285612Sdelphij	   For use with linuxPPS we assume that the N_TTY file has been opened
659285612Sdelphij	     and that the line discipline has been changed to N_PPS by another
660285612Sdelphij	     program (say ppsldisc) so that the two files expected by the oncore
661285612Sdelphij	     driver can be opened.
662285612Sdelphij
663285612Sdelphij	   Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do
664285612Sdelphij	     the stat below without error even though the file has already had its
665285612Sdelphij	     line discipline changed by another process.
666285612Sdelphij
667285612Sdelphij	   The Windows port of ntpd arranges to return duplicate handles for
668285612Sdelphij	     multiple opens of the same serial device, and doesn't have inodes
669285612Sdelphij	     for serial handles, so we just open both on Windows.
670182007Sroberto	*/
671285612Sdelphij#ifndef SYS_WINNT
672182007Sroberto	if (stat(device1, &stat1)) {
673285612Sdelphij		oncore_log_f(instance, LOG_ERR, "Can't stat fd1 (%s)",
674285612Sdelphij			     device1);
675285612Sdelphij		return(0);			/* exit, no file, can't start driver */
67654359Sroberto	}
67754359Sroberto
678182007Sroberto	if (stat(device2, &stat2)) {
679285612Sdelphij		stat2.st_dev = stat2.st_ino = -2;
680285612Sdelphij		oncore_log_f(instance, LOG_ERR, "Can't stat fd2 (%s) %d %m",
681285612Sdelphij			     device2, errno);
682182007Sroberto	}
683285612Sdelphij#endif	/* !SYS_WINNT */
684132451Sroberto
685285612Sdelphij	fd1 = refclock_open(device1, SPEED, LDISC_RAW);
686285612Sdelphij	if (fd1 <= 0) {
687285612Sdelphij		oncore_log_f(instance, LOG_ERR, "Can't open fd1 (%s)",
688285612Sdelphij			     device1);
689285612Sdelphij		return(0);			/* exit, can't open file, can't start driver */
690132451Sroberto	}
691132451Sroberto
692285612Sdelphij	/* for LINUX the PPS device is the result of a line discipline.
693285612Sdelphij	   It seems simplest to let an external program create the appropriate
694285612Sdelphij	   /dev/pps<n> file, and only check (carefully) for its existance here
695285612Sdelphij	 */
696285612Sdelphij
697285612Sdelphij#ifndef SYS_WINNT
698182007Sroberto	if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino))	/* same device here */
699182007Sroberto		fd2 = fd1;
700285612Sdelphij	else
701285612Sdelphij#endif	/* !SYS_WINNT */
702285612Sdelphij	{	/* different devices here */
703285612Sdelphij		if ((fd2=tty_open(device2, O_RDWR, 0777)) < 0) {
704285612Sdelphij			oncore_log_f(instance, LOG_ERR,
705285612Sdelphij				     "Can't open fd2 (%s)", device2);
706285612Sdelphij			return(0);		/* exit, can't open PPS file, can't start driver */
707182007Sroberto		}
708182007Sroberto	}
709182007Sroberto
710285612Sdelphij	/* open ppsapi source */
711182007Sroberto
712285612Sdelphij	if (time_pps_create(fd2, &instance->pps_h) < 0) {
713285612Sdelphij		oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel");
714285612Sdelphij		return(0);			/* exit, don't find PPSAPI in kernel */
715182007Sroberto	}
716182007Sroberto
717182007Sroberto	/* continue initialization */
718182007Sroberto
719182007Sroberto	instance->ttyfd = fd1;
720182007Sroberto	instance->ppsfd = fd2;
721182007Sroberto
722182007Sroberto	/* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
723182007Sroberto
724182007Sroberto	oncore_read_config(instance);
725182007Sroberto
726132451Sroberto	if (!oncore_ppsapi(instance))
727132451Sroberto		return(0);
728132451Sroberto
729132451Sroberto	pp->io.clock_recv = oncore_receive;
730285612Sdelphij	pp->io.srcclock = peer;
731132451Sroberto	pp->io.datalen = 0;
732132451Sroberto	pp->io.fd = fd1;
733132451Sroberto	if (!io_addclock(&pp->io)) {
734285612Sdelphij		oncore_log(instance, LOG_ERR, "can't do io_addclock");
735285612Sdelphij		close(fd1);
736285612Sdelphij		pp->io.fd = -1;
737132451Sroberto		free(instance);
738132451Sroberto		return (0);
739132451Sroberto	}
740285612Sdelphij	pp->unitptr = instance;
741132451Sroberto
742132451Sroberto#ifdef ONCORE_SHMEM_STATUS
743132451Sroberto	/*
744132451Sroberto	 * Before starting ONCORE, lets setup SHMEM
745132451Sroberto	 * This will include merging an old SHMEM into the new one if
746132451Sroberto	 * an old one is found.
747132451Sroberto	 */
748132451Sroberto
749132451Sroberto	oncore_init_shmem(instance);
750132451Sroberto#endif
751132451Sroberto
752132451Sroberto	/*
753132451Sroberto	 * This will return the Model of the Oncore receiver.
754132451Sroberto	 * and start the Initialization loop in oncore_msg_Cj.
755132451Sroberto	 */
756132451Sroberto
757132451Sroberto	instance->o_state = ONCORE_CHECK_ID;
758285612Sdelphij	oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID");
759132451Sroberto
760132451Sroberto	instance->timeout = 4;
761285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
762285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
763132451Sroberto
764132451Sroberto	instance->pollcnt = 2;
765132451Sroberto	return (1);
766132451Sroberto}
767132451Sroberto
768132451Sroberto
769132451Sroberto/*
770132451Sroberto * oncore_shutdown - shut down the clock
771132451Sroberto */
772132451Sroberto
773132451Srobertostatic void
774132451Srobertooncore_shutdown(
775132451Sroberto	int unit,
776132451Sroberto	struct peer *peer
777132451Sroberto	)
778132451Sroberto{
779132451Sroberto	register struct instance *instance;
780132451Sroberto	struct refclockproc *pp;
781132451Sroberto
782132451Sroberto	pp = peer->procptr;
783285612Sdelphij	instance = pp->unitptr;
784132451Sroberto
785285612Sdelphij	if (pp->io.fd != -1)
786285612Sdelphij		io_closeclock(&pp->io);
787132451Sroberto
788285612Sdelphij	if (instance != NULL) {
789285612Sdelphij		time_pps_destroy (instance->pps_h);
790182007Sroberto
791285612Sdelphij		close(instance->ttyfd);
792182007Sroberto
793285612Sdelphij		if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
794285612Sdelphij			close(instance->ppsfd);
795182007Sroberto
796285612Sdelphij		if (instance->shmemfd)
797285612Sdelphij			close(instance->shmemfd);
798182007Sroberto
799285612Sdelphij		free(instance);
800285612Sdelphij	}
801132451Sroberto}
802132451Sroberto
803132451Sroberto
804132451Sroberto
805132451Sroberto/*
806132451Sroberto * oncore_poll - called by the transmit procedure
807132451Sroberto */
808132451Sroberto
809132451Srobertostatic void
810132451Srobertooncore_poll(
811132451Sroberto	int unit,
812132451Sroberto	struct peer *peer
813132451Sroberto	)
814132451Sroberto{
815132451Sroberto	struct instance *instance;
816132451Sroberto
817285612Sdelphij	instance = peer->procptr->unitptr;
818132451Sroberto	if (instance->timeout) {
819132451Sroberto		instance->timeout--;
820132451Sroberto		if (instance->timeout == 0) {
821285612Sdelphij			oncore_log(instance, LOG_ERR,
822285612Sdelphij			    "Oncore: No response from @@Cj, shutting down driver");
823132451Sroberto			oncore_shutdown(unit, peer);
824132451Sroberto		} else {
825285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
826285612Sdelphij			oncore_log(instance, LOG_WARNING, "Oncore: Resend @@Cj");
827132451Sroberto		}
828132451Sroberto		return;
829132451Sroberto	}
830132451Sroberto
831132451Sroberto	if (!instance->pollcnt)
832132451Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
833132451Sroberto	else
834132451Sroberto		instance->pollcnt--;
835132451Sroberto	peer->procptr->polls++;
836132451Sroberto	instance->polled = 1;
837132451Sroberto}
838132451Sroberto
839132451Sroberto
840132451Sroberto
841132451Sroberto/*
842132451Sroberto * Initialize PPSAPI
843132451Sroberto */
844132451Sroberto
845132451Srobertostatic int
846132451Srobertooncore_ppsapi(
847132451Sroberto	struct instance *instance
848132451Sroberto	)
849132451Sroberto{
850182007Sroberto	int cap, mode, mode1;
851285612Sdelphij	const char *cp;
852132451Sroberto
853182007Sroberto	if (time_pps_getcap(instance->pps_h, &cap) < 0) {
854285612Sdelphij		oncore_log_f(instance, LOG_ERR, "time_pps_getcap failed: %m");
85556746Sroberto		return (0);
85656746Sroberto	}
85756746Sroberto
85856746Sroberto	if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
859285612Sdelphij		oncore_log_f(instance, LOG_ERR, "time_pps_getparams failed: %m");
86056746Sroberto		return (0);
86156746Sroberto	}
86256746Sroberto
86356746Sroberto	/* nb. only turn things on, if someone else has turned something
86482498Sroberto	 *	on before we get here, leave it alone!
86556746Sroberto	 */
86656746Sroberto
867182007Sroberto	if (instance->assert) {
868285612Sdelphij		cp = "Assert";
869182007Sroberto		mode = PPS_CAPTUREASSERT;
870182007Sroberto		mode1 = PPS_OFFSETASSERT;
87154359Sroberto	} else {
872285612Sdelphij		cp = "Clear";
873182007Sroberto		mode = PPS_CAPTURECLEAR;
874182007Sroberto		mode1 = PPS_OFFSETCLEAR;
87554359Sroberto	}
876285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Initializing timing to %s.",
877285612Sdelphij		     cp);
87856746Sroberto
879182007Sroberto	if (!(mode & cap)) {
880285612Sdelphij		oncore_log_f(instance, LOG_ERR,
881285612Sdelphij			     "Can't set timing to %s, exiting...", cp);
882182007Sroberto		return(0);
883182007Sroberto	}
884182007Sroberto
885182007Sroberto	if (!(mode1 & cap)) {
886285612Sdelphij		oncore_log_f(instance, LOG_NOTICE,
887285612Sdelphij			     "Can't set %s, this will increase jitter.",
888285612Sdelphij			     cp);
889182007Sroberto		mode1 = 0;
890182007Sroberto	}
891182007Sroberto
892182007Sroberto	/* only set what is legal */
893182007Sroberto
894182007Sroberto	instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
895182007Sroberto
89654359Sroberto	if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
897285612Sdelphij		oncore_log_f(instance, LOG_ERR, "ONCORE: time_pps_setparams fails %m");
898285612Sdelphij		return(0);		/* exit, can't do time_pps_setparans on PPS file */
89954359Sroberto	}
90056746Sroberto
901132451Sroberto	/* If HARDPPS is on, we tell kernel */
90282498Sroberto
903132451Sroberto	if (instance->hardpps) {
904132451Sroberto		int	i;
90582498Sroberto
906285612Sdelphij		oncore_log(instance, LOG_INFO, "HARDPPS Set.");
907182007Sroberto
908132451Sroberto		if (instance->assert)
909132451Sroberto			i = PPS_CAPTUREASSERT;
910132451Sroberto		else
911132451Sroberto			i = PPS_CAPTURECLEAR;
91282498Sroberto
913182007Sroberto		/* we know that 'i' is legal from above */
914182007Sroberto
915182007Sroberto		if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
916182007Sroberto		    PPS_TSFMT_TSPEC) < 0) {
917285612Sdelphij			oncore_log_f(instance, LOG_ERR, "time_pps_kcbind failed: %m");
918285612Sdelphij			oncore_log(instance, LOG_ERR, "HARDPPS failed, abort...");
919182007Sroberto			return (0);
92056746Sroberto		}
921285612Sdelphij
922285612Sdelphij		hardpps_enable = 1;
92354359Sroberto	}
924132451Sroberto	return(1);
925132451Sroberto}
92656746Sroberto
927132451Sroberto
928132451Sroberto
929132451Sroberto#ifdef ONCORE_SHMEM_STATUS
930132451Srobertostatic void
931132451Srobertooncore_init_shmem(
932132451Sroberto	struct instance *instance
933132451Sroberto	)
934132451Sroberto{
935285612Sdelphij	int l, fd;
936182007Sroberto	u_char *cp, *cp1, *buf, *shmem_old;
937132451Sroberto	struct msg_desc *mp;
938132451Sroberto	struct stat sbuf;
939285612Sdelphij	size_t i, n, n1, shmem_length, shmem_old_size;
940132451Sroberto
941285612Sdelphij	/*
942132451Sroberto	* The first thing we do is see if there is an instance->shmem_fname file (still)
943132451Sroberto	* out there from a previous run.  If so, we copy it in and use it to initialize
944132451Sroberto	* shmem (so we won't lose our almanac if we need it).
945132451Sroberto	*/
946132451Sroberto
947132451Sroberto	shmem_old = 0;
948182007Sroberto	shmem_old_size = 0;
949132451Sroberto	if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
950285612Sdelphij		oncore_log(instance, LOG_WARNING, "ONCORE: Can't open SHMEM file");
951132451Sroberto	else {
952132451Sroberto		fstat(fd, &sbuf);
953132451Sroberto		shmem_old_size = sbuf.st_size;
954182007Sroberto		if (shmem_old_size != 0) {
955285612Sdelphij			shmem_old = emalloc((unsigned) sbuf.st_size);
956285612Sdelphij			read(fd, shmem_old, shmem_old_size);
957132451Sroberto		}
958132451Sroberto		close(fd);
95954359Sroberto	}
96054359Sroberto
961132451Sroberto	/* OK, we now create the NEW SHMEM. */
962132451Sroberto
963132451Sroberto	if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
964285612Sdelphij		oncore_log(instance, LOG_WARNING, "ONCORE: Can't open shmem");
965182007Sroberto		if (shmem_old)
966182007Sroberto			free(shmem_old);
967182007Sroberto
968132451Sroberto		return;
969132451Sroberto	}
970132451Sroberto
971132451Sroberto	/* see how big it needs to be */
972132451Sroberto
973132451Sroberto	n = 1;
974132451Sroberto	for (mp=oncore_messages; mp->flag[0]; mp++) {
975132451Sroberto		mp->shmem = n;
976132451Sroberto		/* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
977132451Sroberto		if (!strcmp(mp->flag, "Cb")) {
978132451Sroberto			instance->shmem_Cb = n;
979132451Sroberto			n += (mp->len + 3) * 34;
980132451Sroberto		}
981132451Sroberto		if (!strcmp(mp->flag, "Ba")) {
982132451Sroberto			instance->shmem_Ba = n;
983132451Sroberto			n += (mp->len + 3) * 3;
984132451Sroberto		}
985132451Sroberto		if (!strcmp(mp->flag, "Ea")) {
986132451Sroberto			instance->shmem_Ea = n;
987132451Sroberto			n += (mp->len + 3) * 3;
988132451Sroberto		}
989132451Sroberto		if (!strcmp(mp->flag, "Ha")) {
990132451Sroberto			instance->shmem_Ha = n;
991132451Sroberto			n += (mp->len + 3) * 3;
992132451Sroberto		}
993132451Sroberto		n += (mp->len + 3);
994132451Sroberto	}
995132451Sroberto	shmem_length = n + 2;
996132451Sroberto
997285612Sdelphij	buf = emalloc(shmem_length);
998132451Sroberto	memset(buf, 0, shmem_length);
999132451Sroberto
1000132451Sroberto	/* next build the new SHMEM buffer in memory */
1001132451Sroberto
1002132451Sroberto	for (mp=oncore_messages; mp->flag[0]; mp++) {
1003132451Sroberto		l = mp->shmem;
1004132451Sroberto		buf[l + 0] = mp->len >> 8;
1005132451Sroberto		buf[l + 1] = mp->len & 0xff;
1006132451Sroberto		buf[l + 2] = 0;
1007132451Sroberto		buf[l + 3] = '@';
1008132451Sroberto		buf[l + 4] = '@';
1009132451Sroberto		buf[l + 5] = mp->flag[0];
1010132451Sroberto		buf[l + 6] = mp->flag[1];
1011132451Sroberto		if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
1012132451Sroberto			if (!strcmp(mp->flag, "Cb"))
1013132451Sroberto				n = 35;
1014132451Sroberto			else
1015132451Sroberto				n = 4;
1016132451Sroberto			for (i=1; i<n; i++) {
1017132451Sroberto				buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
1018132451Sroberto				buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
1019132451Sroberto				buf[l + i * (mp->len+3) + 2] = 0;
1020132451Sroberto				buf[l + i * (mp->len+3) + 3] = '@';
1021132451Sroberto				buf[l + i * (mp->len+3) + 4] = '@';
1022132451Sroberto				buf[l + i * (mp->len+3) + 5] = mp->flag[0];
1023132451Sroberto				buf[l + i * (mp->len+3) + 6] = mp->flag[1];
1024132451Sroberto			}
1025132451Sroberto		}
1026132451Sroberto	}
1027132451Sroberto
1028132451Sroberto	/* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
1029182007Sroberto	 * copying the data in shmem_old to buf.
1030182007Sroberto	 * When we are done we write it out and free both buffers.
1031182007Sroberto	 * If the structure sizes dont agree, I will not copy.
1032182007Sroberto	 * This could be due to an addition/deletion or a problem with the disk file.
103354359Sroberto	 */
103454359Sroberto
1035132451Sroberto	if (shmem_old) {
1036182007Sroberto		if (shmem_old_size == shmem_length) {
1037182007Sroberto			for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2));	cp+=(n+3), cp1+=(n+3)) {
1038182007Sroberto				n1 = 256*(*(cp1-3)) + *(cp1-2);
1039182007Sroberto				if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4))
1040182007Sroberto					break;
104154359Sroberto
1042182007Sroberto				memcpy(cp, cp1, (size_t) n);
1043182007Sroberto			}
1044132451Sroberto		}
1045132451Sroberto		free(shmem_old);
1046132451Sroberto	}
1047132451Sroberto
1048132451Sroberto	i = write(instance->shmemfd, buf, shmem_length);
1049132451Sroberto	free(buf);
1050132451Sroberto
1051132451Sroberto	if (i != shmem_length) {
1052285612Sdelphij		oncore_log(instance, LOG_ERR, "ONCORE: error writing shmem");
1053132451Sroberto		close(instance->shmemfd);
1054132451Sroberto		return;
1055132451Sroberto	}
1056132451Sroberto
1057132451Sroberto	instance->shmem = (u_char *) mmap(0, shmem_length,
1058132451Sroberto		PROT_READ | PROT_WRITE,
1059132451Sroberto#ifdef MAP_HASSEMAPHORE
1060132451Sroberto		MAP_HASSEMAPHORE |
1061132451Sroberto#endif
1062132451Sroberto		MAP_SHARED, instance->shmemfd, (off_t)0);
1063132451Sroberto
1064132451Sroberto	if (instance->shmem == (u_char *)MAP_FAILED) {
1065132451Sroberto		instance->shmem = 0;
1066132451Sroberto		close(instance->shmemfd);
1067132451Sroberto		return;
1068132451Sroberto	}
1069132451Sroberto
1070285612Sdelphij	oncore_log_f(instance, LOG_NOTICE,
1071285612Sdelphij		     "SHMEM (size = %ld) is CONFIGURED and available as %s",
1072285612Sdelphij		     (u_long) shmem_length, instance->shmem_fname);
107354359Sroberto}
1074132451Sroberto#endif /* ONCORE_SHMEM_STATUS */
107554359Sroberto
107656746Sroberto
107756746Sroberto
107854359Sroberto/*
107954359Sroberto * Read Input file if it exists.
108054359Sroberto */
108182498Sroberto
108254359Srobertostatic void
108354359Srobertooncore_read_config(
108454359Sroberto	struct instance *instance
108554359Sroberto	)
108654359Sroberto{
108754359Sroberto/*
108882498Sroberto * First we try to open the configuration file
1089285612Sdelphij *    /etc/ntp.oncore.N
109082498Sroberto * where N is the unit number viz 127.127.30.N.
109182498Sroberto * If we don't find it we try
1092285612Sdelphij *    /etc/ntp.oncoreN
109382498Sroberto * and then
109482498Sroberto *    /etc/ntp.oncore
109554359Sroberto *
109682498Sroberto * If we don't find any then we don't have the cable delay or PPS offset
109754359Sroberto * and we choose MODE (4) below.
109854359Sroberto *
109954359Sroberto * Five Choices for MODE
110054359Sroberto *    (0) ONCORE is preinitialized, don't do anything to change it.
110154359Sroberto *	    nb, DON'T set 0D mode, DON'T set Delay, position...
110254359Sroberto *    (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
110354359Sroberto *    (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
110454359Sroberto *		    lock this in, go to 0D mode.
110554359Sroberto *    (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
110654359Sroberto *    (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
110754359Sroberto *		    lock this in, go to 0D mode.
110854359Sroberto *     NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
110954359Sroberto *	   then this position is set as the INITIAL position of the ONCORE.
111054359Sroberto *	   This can reduce the time to first fix.
111154359Sroberto * -------------------------------------------------------------------------------
111254359Sroberto * Note that an Oncore UT without a battery backup retains NO information if it is
111354359Sroberto *   power cycled, with a Battery Backup it remembers the almanac, etc.
111454359Sroberto * For an Oncore VP, there is an eeprom that will contain this data, along with the
111554359Sroberto *   option of Battery Backup.
111654359Sroberto * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
111754359Sroberto *   power cycle, since there is nowhere to store the data.
111854359Sroberto * -------------------------------------------------------------------------------
111954359Sroberto *
112054359Sroberto * If we open one or the other of the files, we read it looking for
1121132451Sroberto *   MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
1122132451Sroberto *   STATUS, POSN3D, POSN2D, CHAN, TRAIM
112354359Sroberto * then initialize using method MODE.  For Mode = (1,3) all of (LAT, LON, HT) must
112454359Sroberto *   be present or mode reverts to (2,4).
112554359Sroberto *
112654359Sroberto * Read input file.
112754359Sroberto *
112854359Sroberto *	# is comment to end of line
112954359Sroberto *	= allowed between 1st and 2nd fields.
113054359Sroberto *
113154359Sroberto *	Expect to see one line with 'MODE' as first field, followed by an integer
113254359Sroberto *	   in the range 0-4 (default = 4).
113354359Sroberto *
113454359Sroberto *	Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
113554359Sroberto *	All numbers are floating point.
113654359Sroberto *		DDD.ddd
113754359Sroberto *		DDD  MMM.mmm
113854359Sroberto *		DDD  MMM  SSS.sss
113954359Sroberto *
114082498Sroberto *	Expect to see one line with 'HT' as first field,
114182498Sroberto *	   followed by 1-2 fields.  First is a number, the second is 'FT' or 'M'
114282498Sroberto *	   for feet or meters.	HT is the height above the GPS ellipsoid.
1143132451Sroberto *	   If the receiver reports height in both GPS and MSL, then we will report
114482498Sroberto *	   the difference GPS-MSL on the clockstats file.
114554359Sroberto *
114682498Sroberto *	There is an optional line, starting with DELAY, followed
114754359Sroberto *	   by 1 or two fields.	The first is a number (a time) the second is
114854359Sroberto *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
114982498Sroberto *	    DELAY  is cable delay, typically a few tens of ns.
115082498Sroberto *
115182498Sroberto *	There is an optional line, starting with OFFSET, followed
115282498Sroberto *	   by 1 or two fields.	The first is a number (a time) the second is
115382498Sroberto *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
115482498Sroberto *	   OFFSET is the offset of the PPS pulse from 0. (only fully implemented
115554359Sroberto *		with the PPSAPI, we need to be able to tell the Kernel about this
115654359Sroberto *		offset if the Kernel PLL is in use, but can only do this presently
115754359Sroberto *		when using the PPSAPI interface.  If not using the Kernel PLL,
115854359Sroberto *		then there is no problem.
115954359Sroberto *
116082498Sroberto *	There is an optional line, with either ASSERT or CLEAR on it, which
116154359Sroberto *	   determine which transition of the PPS signal is used for timing by the
116254359Sroberto *	   PPSAPI.  If neither is present, then ASSERT is assumed.
1163132451Sroberto *	   ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
1164132451Sroberto *	   For Flag2, ASSERT=0, and hence is default.
116554359Sroberto *
1166132451Sroberto *	There is an optional line, with HARDPPS on it.	Including this line causes
1167285612Sdelphij *	     the PPS signal to control the kernel PLL.
1168132451Sroberto *	   HARDPPS can also be set with FLAG3 of the ntp.conf input.
1169132451Sroberto *	   For Flag3, 0 is disabled, and the default.
1170132451Sroberto *
1171132451Sroberto *	There are three options that have to do with using the shared memory option.
1172132451Sroberto *	   First, to enable the option there must be a SHMEM line with a file name.
117382498Sroberto *	   The file name is the file associated with the shared memory.
117482498Sroberto *
1175132451Sroberto *	In shared memory, there is one 'record' for each returned variable.
1176132451Sroberto *	For the @@Ea data there are three 'records' containing position data.
1177132451Sroberto *	   There will always be data in the record corresponding to the '0D' @@Ea record,
1178132451Sroberto *	   and the user has a choice of filling the '3D' record by specifying POSN3D,
1179132451Sroberto *	   or the '2D' record by specifying POSN2D.  In either case the '2D' or '3D'
1180132451Sroberto *	   record is filled once every 15s.
118182498Sroberto *
118282498Sroberto *	Two additional variables that can be set are CHAN and TRAIM.  These should be
118382498Sroberto *	   set correctly by the code examining the @@Cj record, but we bring them out here
1184132451Sroberto *	   to allow the user to override either the # of channels, or the existence of TRAIM.
118582498Sroberto *	   CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
118682498Sroberto *	   followed by YES or NO.
118782498Sroberto *
1188132451Sroberto *	There is an optional line with MASK on it followed by one integer field in the
1189132451Sroberto *	   range 0 to 89. This sets the satellite mask angle and will determine the minimum
1190132451Sroberto *	   elevation angle for satellites to be tracked by the receiver. The default value
1191132451Sroberto *	   is 10 deg for the VP and 0 deg for all other receivers.
1192132451Sroberto *
1193285612Sdelphij *	There is an optional line with PPSCONTROL on it (only valid for M12 or M12+T
1194285612Sdelphij *	   receivers, the option is read, but ignored for all others)
1195285612Sdelphij *	   and it is followed by:
1196285612Sdelphij *		ON	   Turn PPS on.  This is the default and the default for other
1197285612Sdelphij *			       oncore receivers.  The PPS is on even if not tracking
1198285612Sdelphij *			       any satellites.
1199285612Sdelphij *		SATELLITE  Turns PPS on if tracking at least 1 satellite, else off.
1200285612Sdelphij *		TRAIM	   Turns PPS on or off controlled by TRAIM.
1201285612Sdelphij *	  The OFF option is NOT implemented, since the Oncore driver will not work
1202285612Sdelphij *	     without the PPS signal.
1203285612Sdelphij *
120454359Sroberto * So acceptable input would be
120554359Sroberto *	# these are my coordinates (RWC)
120654359Sroberto *	LON  -106 34.610
120754359Sroberto *	LAT    35 08.999
120854359Sroberto *	HT	1589	# could equally well say HT 5215 FT
120954359Sroberto *	DELAY  60 ns
121054359Sroberto */
121154359Sroberto
121254359Sroberto	FILE	*fd;
1213285612Sdelphij	char	*cc, *ca, line[100], units[2], device[64];
1214285612Sdelphij	const char	*dirs[] = { "/etc/ntp", "/etc", 0 };
1215285612Sdelphij	const char *cp, **cpp;
1216132451Sroberto	int	i, sign, lat_flg, long_flg, ht_flg, mode, mask;
121754359Sroberto	double	f1, f2, f3;
121854359Sroberto
1219182007Sroberto	fd = NULL;	/* just to shutup gcc complaint */
1220182007Sroberto	for (cpp=dirs; *cpp; cpp++) {
1221182007Sroberto		cp = *cpp;
1222285612Sdelphij		snprintf(device, sizeof(device), "%s/ntp.oncore.%d",
1223285612Sdelphij			 cp, instance->unit);  /* try "ntp.oncore.0 */
1224182007Sroberto		if ((fd=fopen(device, "r")))
1225182007Sroberto			break;
1226285612Sdelphij		snprintf(device, sizeof(device), "%s/ntp.oncore%d",
1227285612Sdelphij			 cp, instance->unit);  /* try "ntp.oncore0" */
1228182007Sroberto		if ((fd=fopen(device, "r")))
1229182007Sroberto			break;
1230285612Sdelphij		snprintf(device, sizeof(device), "%s/ntp.oncore", cp);
1231285612Sdelphij		if ((fd=fopen(device, "r")))   /* last try "ntp.oncore" */
1232182007Sroberto			break;
123382498Sroberto	}
123454359Sroberto
1235182007Sroberto	if (!fd) {	/* no inputfile, default to the works ... */
1236182007Sroberto		instance->init_type = 4;
1237182007Sroberto		return;
1238182007Sroberto	}
1239182007Sroberto
1240132451Sroberto	mode = mask = 0;
124154359Sroberto	lat_flg = long_flg = ht_flg = 0;
124254359Sroberto	while (fgets(line, 100, fd)) {
1243285612Sdelphij		char *cpw;
124456746Sroberto
124556746Sroberto		/* Remove comments */
1246285612Sdelphij		if ((cpw = strchr(line, '#')))
1247285612Sdelphij			*cpw = '\0';
124882498Sroberto
124956746Sroberto		/* Remove trailing space */
125056746Sroberto		for (i = strlen(line);
1251285612Sdelphij		     i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]);
125256746Sroberto			)
125356746Sroberto			line[--i] = '\0';
125456746Sroberto
125556746Sroberto		/* Remove leading space */
1256285612Sdelphij		for (cc = line; *cc && isascii((unsigned char)*cc) && isspace((unsigned char)*cc); cc++)
125756746Sroberto			continue;
125856746Sroberto
125956746Sroberto		/* Stop if nothing left */
126056746Sroberto		if (!*cc)
126156746Sroberto			continue;
126256746Sroberto
126382498Sroberto		/* Uppercase the command and find the arg */
126456746Sroberto		for (ca = cc; *ca; ca++) {
1265285612Sdelphij			if (isascii((unsigned char)*ca)) {
1266285612Sdelphij				if (islower((unsigned char)*ca)) {
1267285612Sdelphij					*ca = toupper((unsigned char)*ca);
1268285612Sdelphij				} else if (isspace((unsigned char)*ca) || (*ca == '='))
126982498Sroberto					break;
127056746Sroberto			}
127156746Sroberto		}
127282498Sroberto
127382498Sroberto		/* Remove space (and possible =) leading the arg */
1274285612Sdelphij		for (; *ca && isascii((unsigned char)*ca) && (isspace((unsigned char)*ca) || (*ca == '=')); ca++)
127556746Sroberto			continue;
127656746Sroberto
127782498Sroberto		if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
1278285612Sdelphij			instance->shmem_fname = estrdup(ca);
127956746Sroberto			continue;
128056746Sroberto		}
128156746Sroberto
128256746Sroberto		/* Uppercase argument as well */
1283285612Sdelphij		for (cpw = ca; *cpw; cpw++)
1284285612Sdelphij			if (isascii((unsigned char)*cpw) && islower((unsigned char)*cpw))
1285285612Sdelphij				*cpw = toupper((unsigned char)*cpw);
128656746Sroberto
128782498Sroberto		if (!strncmp(cc, "LAT", (size_t) 3)) {
128854359Sroberto			f1 = f2 = f3 = 0;
128956746Sroberto			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
129054359Sroberto			sign = 1;
129154359Sroberto			if (f1 < 0) {
129254359Sroberto				f1 = -f1;
129354359Sroberto				sign = -1;
129454359Sroberto			}
129554359Sroberto			instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
129654359Sroberto			lat_flg++;
129782498Sroberto		} else if (!strncmp(cc, "LON", (size_t) 3)) {
129854359Sroberto			f1 = f2 = f3 = 0;
129956746Sroberto			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
130054359Sroberto			sign = 1;
130154359Sroberto			if (f1 < 0) {
130254359Sroberto				f1 = -f1;
130354359Sroberto				sign = -1;
130454359Sroberto			}
130554359Sroberto			instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
130654359Sroberto			long_flg++;
130782498Sroberto		} else if (!strncmp(cc, "HT", (size_t) 2)) {
130854359Sroberto			f1 = 0;
130954359Sroberto			units[0] = '\0';
131056746Sroberto			sscanf(ca, "%lf %1s", &f1, units);
131154359Sroberto			if (units[0] == 'F')
131254359Sroberto				f1 = 0.3048 * f1;
131354359Sroberto			instance->ss_ht = 100 * f1;    /* cm */
131454359Sroberto			ht_flg++;
131582498Sroberto		} else if (!strncmp(cc, "DELAY", (size_t) 5)) {
131654359Sroberto			f1 = 0;
131754359Sroberto			units[0] = '\0';
131856746Sroberto			sscanf(ca, "%lf %1s", &f1, units);
131954359Sroberto			if (units[0] == 'N')
132054359Sroberto				;
132154359Sroberto			else if (units[0] == 'U')
132254359Sroberto				f1 = 1000 * f1;
132354359Sroberto			else if (units[0] == 'M')
132454359Sroberto				f1 = 1000000 * f1;
132554359Sroberto			else
132654359Sroberto				f1 = 1000000000 * f1;
132754359Sroberto			if (f1 < 0 || f1 > 1.e9)
132854359Sroberto				f1 = 0;
1329285612Sdelphij			if (f1 < 0 || f1 > 999999)
1330285612Sdelphij				oncore_log_f(instance, LOG_WARNING,
1331285612Sdelphij					     "PPS Cable delay of %fns out of Range, ignored",
1332285612Sdelphij					     f1);
1333285612Sdelphij			else
133482498Sroberto				instance->delay = f1;		/* delay in ns */
133582498Sroberto		} else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
133654359Sroberto			f1 = 0;
133754359Sroberto			units[0] = '\0';
133856746Sroberto			sscanf(ca, "%lf %1s", &f1, units);
133954359Sroberto			if (units[0] == 'N')
134054359Sroberto				;
134154359Sroberto			else if (units[0] == 'U')
134254359Sroberto				f1 = 1000 * f1;
134354359Sroberto			else if (units[0] == 'M')
134454359Sroberto				f1 = 1000000 * f1;
134554359Sroberto			else
134654359Sroberto				f1 = 1000000000 * f1;
134754359Sroberto			if (f1 < 0 || f1 > 1.e9)
134854359Sroberto				f1 = 0;
1349285612Sdelphij			if (f1 < 0 || f1 > 999999999.)
1350285612Sdelphij				oncore_log_f(instance, LOG_WARNING,
1351285612Sdelphij					     "PPS Offset of %fns out of Range, ignored",
1352285612Sdelphij					     f1);
1353285612Sdelphij			else
135482498Sroberto				instance->offset = f1;		/* offset in ns */
135582498Sroberto		} else if (!strncmp(cc, "MODE", (size_t) 4)) {
135656746Sroberto			sscanf(ca, "%d", &mode);
135754359Sroberto			if (mode < 0 || mode > 4)
135854359Sroberto				mode = 4;
135982498Sroberto		} else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
136054359Sroberto			instance->assert = 1;
136182498Sroberto		} else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
136254359Sroberto			instance->assert = 0;
1363132451Sroberto		} else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
1364132451Sroberto			instance->hardpps = 1;
136582498Sroberto		} else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
136682498Sroberto			instance->shmem_Posn = 2;
136782498Sroberto		} else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
136882498Sroberto			instance->shmem_Posn = 3;
136982498Sroberto		} else if (!strncmp(cc, "CHAN", (size_t) 4)) {
137082498Sroberto			sscanf(ca, "%d", &i);
137182498Sroberto			if ((i == 6) || (i == 8) || (i == 12))
1372132451Sroberto				instance->chan_in = i;
137382498Sroberto		} else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
1374132451Sroberto			instance->traim_in = 1; 	/* so TRAIM alone is YES */
137582498Sroberto			if (!strcmp(ca, "NO") || !strcmp(ca, "OFF"))    /* Yes/No, On/Off */
1376132451Sroberto				instance->traim_in = 0;
1377132451Sroberto		} else if (!strncmp(cc, "MASK", (size_t) 4)) {
1378132451Sroberto			sscanf(ca, "%d", &mask);
1379132451Sroberto			if (mask > -1 && mask < 90)
1380132451Sroberto				instance->Ag = mask;			/* Satellite mask angle */
1381285612Sdelphij		} else if (!strncmp(cc,"PPSCONTROL",10)) {              /* pps control M12 only */
1382285612Sdelphij			if (!strcmp(ca,"ON") || !strcmp(ca, "CONTINUOUS")) {
1383285612Sdelphij				instance->pps_control = 1;		/* PPS always on */
1384285612Sdelphij			} else if (!strcmp(ca,"SATELLITE")) {
1385285612Sdelphij				instance->pps_control = 2;		/* PPS on when satellite is available */
1386285612Sdelphij			} else if (!strcmp(ca,"TRAIM")) {
1387285612Sdelphij				instance->pps_control = 3;		/* PPS on when TRAIM status is OK */
1388285612Sdelphij			} else {
1389285612Sdelphij				oncore_log_f(instance, LOG_WARNING,
1390285612Sdelphij				             "Unknown value \"%s\" for PPSCONTROL, ignored",
1391285612Sdelphij					     cc);
1392285612Sdelphij			}
139354359Sroberto		}
139454359Sroberto	}
139554359Sroberto	fclose(fd);
139654359Sroberto
139754359Sroberto	/*
139854359Sroberto	 *    OK, have read all of data file, and extracted the good stuff.
139954359Sroberto	 *    If lat/long/ht specified they ALL must be specified for mode = (1,3).
140054359Sroberto	 */
140154359Sroberto
140254359Sroberto	instance->posn_set = 1;
1403132451Sroberto	if (!( lat_flg && long_flg && ht_flg )) {
1404285612Sdelphij		oncore_log_f(instance, LOG_WARNING,
1405285612Sdelphij			     "ONCORE: incomplete data on %s", device);
140654359Sroberto		instance->posn_set = 0;
140782498Sroberto		if (mode == 1 || mode == 3) {
1408285612Sdelphij			oncore_log_f(instance, LOG_WARNING,
1409285612Sdelphij				     "Input Mode = %d, but no/incomplete position, mode set to %d",
1410285612Sdelphij				     mode, mode+1);
141182498Sroberto			mode++;
141282498Sroberto		}
141354359Sroberto	}
141482498Sroberto	instance->init_type = mode;
141582498Sroberto
1416285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Input mode = %d", mode);
141754359Sroberto}
141854359Sroberto
141954359Sroberto
142054359Sroberto
142154359Sroberto/*
1422132451Sroberto * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
142354359Sroberto */
142482498Sroberto
142554359Srobertostatic void
142654359Srobertooncore_receive(
142754359Sroberto	struct recvbuf *rbufp
142854359Sroberto	)
142954359Sroberto{
143082498Sroberto	size_t i;
143154359Sroberto	u_char *p;
143254359Sroberto	struct peer *peer;
143354359Sroberto	struct instance *instance;
143454359Sroberto
1435285612Sdelphij	peer = rbufp->recv_peer;
1436285612Sdelphij	instance = peer->procptr->unitptr;
143754359Sroberto	p = (u_char *) &rbufp->recv_space;
143854359Sroberto
1439285612Sdelphij#ifdef ONCORE_VERBOSE_RECEIVE
144054359Sroberto	if (debug > 4) {
144154359Sroberto		int i;
1442285612Sdelphij		char	Msg[120], Msg2[10];
1443285612Sdelphij
1444285612Sdelphij		oncore_log_f(instance, LOG_DEBUG,
1445285612Sdelphij			     ">>> %d bytes available",
1446285612Sdelphij			     rbufp->recv_length);
1447285612Sdelphij		strlcpy(Msg, ">>>", sizeof(Msg));
1448285612Sdelphij		for (i = 0; i < rbufp->recv_length; i++) {
1449285612Sdelphij			snprintf(Msg2, sizeof(Msg2), "%02x ", p[i]);
1450285612Sdelphij			strlcat(Msg, Msg2, sizeof(Msg));
1451285612Sdelphij		}
1452285612Sdelphij		oncore_log(instance, LOG_DEBUG, Msg);
1453285612Sdelphij
1454285612Sdelphij		strlcpy(Msg, ">>>", sizeof(Msg));
1455285612Sdelphij		for (i = 0; i < rbufp->recv_length; i++) {
1456285612Sdelphij			snprintf(Msg2, sizeof(Msg2), "%03o ", p[i]);
1457285612Sdelphij			strlcat(Msg, Msg2, sizeof(Msg));
1458285612Sdelphij		}
1459285612Sdelphij		oncore_log(instance, LOG_DEBUG, Msg);
146054359Sroberto	}
146154359Sroberto#endif
146254359Sroberto
146354359Sroberto	i = rbufp->recv_length;
1464316722Sdelphij	if ((size_t)rcvptr + i >= sizeof(rcvbuf))
146554359Sroberto		i = sizeof(rcvbuf) - rcvptr;	/* and some char will be lost */
146654359Sroberto	memcpy(rcvbuf+rcvptr, p, i);
146754359Sroberto	rcvptr += i;
146854359Sroberto	oncore_consume(instance);
146954359Sroberto}
147054359Sroberto
147154359Sroberto
147254359Sroberto
147354359Sroberto/*
147454359Sroberto * Deal with any complete messages
147554359Sroberto */
147682498Sroberto
147754359Srobertostatic void
147854359Srobertooncore_consume(
147954359Sroberto	struct instance *instance
148054359Sroberto	)
148154359Sroberto{
1482285612Sdelphij	unsigned i, m, l;
148354359Sroberto
148454359Sroberto	while (rcvptr >= 7) {
148554359Sroberto		if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
148654359Sroberto			/* We're not in sync, lets try to get there */
148754359Sroberto			for (i=1; i < rcvptr-1; i++)
148854359Sroberto				if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
148954359Sroberto					break;
1490285612Sdelphij#ifdef ONCORE_VERBOSE_CONSUME
149154359Sroberto			if (debug > 4)
1492285612Sdelphij				oncore_log_f(instance, LOG_DEBUG,
1493285612Sdelphij					     ">>> skipping %d chars",
1494285612Sdelphij					     i);
1495182007Sroberto#endif
149654359Sroberto			if (i != rcvptr)
149782498Sroberto				memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
149854359Sroberto			rcvptr -= i;
149982498Sroberto			continue;
150054359Sroberto		}
150154359Sroberto
150254359Sroberto		/* Ok, we have a header now */
150354359Sroberto		l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
150454359Sroberto		for(m=0; m<l; m++)
150582498Sroberto			if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
150654359Sroberto				break;
150782498Sroberto		if (m == l) {
1508285612Sdelphij#ifdef ONCORE_VERBOSE_CONSUME
150982498Sroberto			if (debug > 4)
1510285612Sdelphij				oncore_log_f(instance, LOG_DEBUG,
1511285612Sdelphij					     ">>> Unknown MSG, skipping 4 (%c%c)",
1512285612Sdelphij					     rcvbuf[2], rcvbuf[3]);
1513182007Sroberto#endif
151482498Sroberto			memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
151582498Sroberto			rcvptr -= 4;
151682498Sroberto			continue;
151782498Sroberto		}
151882498Sroberto
151954359Sroberto		l = oncore_messages[m].len;
1520285612Sdelphij#ifdef ONCORE_VERBOSE_CONSUME
152154359Sroberto		if (debug > 3)
1522285612Sdelphij			oncore_log_f(instance, LOG_DEBUG,
1523285612Sdelphij				     "GOT: %c%c  %d of %d entry %d",
1524285612Sdelphij				     instance->unit, rcvbuf[2],
1525285612Sdelphij				     rcvbuf[3], rcvptr, l, m);
152654359Sroberto#endif
152754359Sroberto		/* Got the entire message ? */
152854359Sroberto
152954359Sroberto		if (rcvptr < l)
153054359Sroberto			return;
153154359Sroberto
153282498Sroberto		/* are we at the end of message? should be <Cksum><CR><LF> */
153354359Sroberto
153482498Sroberto		if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
1535285612Sdelphij#ifdef ONCORE_VERBOSE_CONSUME
153682498Sroberto			if (debug)
1537285612Sdelphij				oncore_log(instance, LOG_DEBUG, "NO <CR><LF> at end of message");
1538182007Sroberto#endif
153982498Sroberto		} else {	/* check the CheckSum */
1540132451Sroberto			if (oncore_checksum_ok(rcvbuf, l)) {
154182498Sroberto				if (instance->shmem != NULL) {
154282498Sroberto					instance->shmem[oncore_messages[m].shmem + 2]++;
154382498Sroberto					memcpy(instance->shmem + oncore_messages[m].shmem + 3,
154482498Sroberto					    rcvbuf, (size_t) l);
154582498Sroberto				}
154682498Sroberto				oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
154782498Sroberto				if (oncore_messages[m].handler)
154882498Sroberto					oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
1549182007Sroberto			}
1550285612Sdelphij#ifdef ONCORE_VERBOSE_CONSUME
1551182007Sroberto			else if (debug) {
1552285612Sdelphij				char	Msg[120], Msg2[10];
1553285612Sdelphij
1554285612Sdelphij				oncore_log(instance, LOG_ERR, "Checksum mismatch!");
1555285612Sdelphij				snprintf(Msg, sizeof(Msg), "@@%c%c ", rcvbuf[2], rcvbuf[3]);
1556285612Sdelphij				for (i = 4; i < l; i++) {
1557285612Sdelphij					snprintf(Msg2, sizeof(Msg2),
1558285612Sdelphij						 "%03o ", rcvbuf[i]);
1559285612Sdelphij					strlcat(Msg, Msg2, sizeof(Msg));
1560285612Sdelphij				}
1561285612Sdelphij				oncore_log(instance, LOG_DEBUG, Msg);
156282498Sroberto			}
1563182007Sroberto#endif
156454359Sroberto		}
156554359Sroberto
156654359Sroberto		if (l != rcvptr)
156782498Sroberto			memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
156854359Sroberto		rcvptr -= l;
156954359Sroberto	}
157054359Sroberto}
157154359Sroberto
157254359Sroberto
157354359Sroberto
157454359Srobertostatic void
1575132451Srobertooncore_get_timestamp(
1576132451Sroberto	struct instance *instance,
1577132451Sroberto	long dt1,	/* tick offset THIS time step */
1578132451Sroberto	long dt2	/* tick offset NEXT time step */
157954359Sroberto	)
158054359Sroberto{
1581132451Sroberto	int	Rsm;
1582182007Sroberto	u_long	j;
1583132451Sroberto	l_fp ts, ts_tmp;
1584132451Sroberto	double dmy;
1585132451Sroberto#ifdef HAVE_STRUCT_TIMESPEC
1586132451Sroberto	struct timespec *tsp = 0;
1587132451Sroberto#else
1588132451Sroberto	struct timeval	*tsp = 0;
1589132451Sroberto#endif
1590132451Sroberto	int	current_mode;
1591132451Sroberto	pps_params_t current_params;
1592132451Sroberto	struct timespec timeout;
1593285612Sdelphij	struct peer *peer;
1594132451Sroberto	pps_info_t pps_i;
1595285612Sdelphij	char Msg[160];
159654359Sroberto
1597285612Sdelphij	peer = instance->peer;
1598285612Sdelphij
1599132451Sroberto#if 1
1600132451Sroberto	/* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
1601132451Sroberto	 * If we have Finished the SiteSurvey, then we fall thru for the 14/15
1602132451Sroberto	 *  times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
1603132451Sroberto	 * This gives good time, which gets better when the SS is done.
1604132451Sroberto	 */
1605132451Sroberto
1606285612Sdelphij	if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D)) {
1607132451Sroberto#else
1608132451Sroberto	/* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
1609132451Sroberto
1610285612Sdelphij	if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) {
1611132451Sroberto#endif
1612285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1613132451Sroberto		return;
1614285612Sdelphij	}
1615132451Sroberto
1616132451Sroberto	/* Don't do anything without an almanac to define the GPS->UTC delta */
1617132451Sroberto
1618285612Sdelphij	if (instance->rsm.bad_almanac) {
1619285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1620132451Sroberto		return;
1621285612Sdelphij	}
1622132451Sroberto
1623182007Sroberto	/* Once the Almanac is valid, the M12+T does not produce valid UTC
1624182007Sroberto	 * immediately.
1625182007Sroberto	 * Wait for UTC offset decode valid, then wait one message more
1626182007Sroberto	 * so we are not off by 13 seconds after  reset.
1627182007Sroberto	 */
1628182007Sroberto
1629182007Sroberto	if (instance->count5) {
1630182007Sroberto		instance->count5--;
1631285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1632182007Sroberto		return;
1633182007Sroberto	}
1634182007Sroberto
1635132451Sroberto	j = instance->ev_serial;
1636132451Sroberto	timeout.tv_sec = 0;
1637132451Sroberto	timeout.tv_nsec = 0;
1638132451Sroberto	if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
1639132451Sroberto	    &timeout) < 0) {
1640285612Sdelphij		oncore_log_f(instance, LOG_ERR,
1641285612Sdelphij			     "time_pps_fetch failed %m");
1642285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1643132451Sroberto		return;
1644132451Sroberto	}
1645132451Sroberto
1646132451Sroberto	if (instance->assert) {
1647132451Sroberto		tsp = &pps_i.assert_timestamp;
1648132451Sroberto
1649285612Sdelphij#ifdef ONCORE_VERBOSE_GET_TIMESTAMP
1650132451Sroberto		if (debug > 2) {
1651285612Sdelphij			u_long i;
1652285612Sdelphij
1653132451Sroberto			i = (u_long) pps_i.assert_sequence;
1654182007Sroberto# ifdef HAVE_STRUCT_TIMESPEC
1655285612Sdelphij			oncore_log_f(instance, LOG_DEBUG,
1656285612Sdelphij				     "serial/j (%lu, %lu) %ld.%09ld", i,
1657285612Sdelphij				     j, (long)tsp->tv_sec,
1658285612Sdelphij				     (long)tsp->tv_nsec);
1659182007Sroberto# else
1660285612Sdelphij			oncore_log_f(instance, LOG_DEBUG,
1661285612Sdelphij				     "serial/j (%lu, %lu) %ld.%06ld", i,
1662285612Sdelphij				     j, (long)tsp->tv_sec,
1663285612Sdelphij				     (long)tsp->tv_usec);
1664182007Sroberto# endif
1665182007Sroberto		}
1666132451Sroberto#endif
1667132451Sroberto
1668132451Sroberto		if (pps_i.assert_sequence == j) {
1669285612Sdelphij			oncore_log(instance, LOG_NOTICE, "ONCORE: oncore_get_timestamp, error serial pps");
1670285612Sdelphij			peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1671132451Sroberto			return;
1672132451Sroberto		}
1673285612Sdelphij
1674132451Sroberto		instance->ev_serial = pps_i.assert_sequence;
1675132451Sroberto	} else {
1676132451Sroberto		tsp = &pps_i.clear_timestamp;
1677132451Sroberto
1678285612Sdelphij#if 0
1679132451Sroberto		if (debug > 2) {
1680285612Sdelphij			u_long i;
1681285612Sdelphij
1682132451Sroberto			i = (u_long) pps_i.clear_sequence;
1683182007Sroberto# ifdef HAVE_STRUCT_TIMESPEC
1684285612Sdelphij			oncore_log_f(instance, LOG_DEBUG,
1685285612Sdelphij				     "serial/j (%lu, %lu) %ld.%09ld", i,
1686285612Sdelphij				     j, (long)tsp->tv_sec,
1687285612Sdelphij				     (long)tsp->tv_nsec);
1688182007Sroberto# else
1689285612Sdelphij			oncore_log_f(instance, LOG_DEBUG,
1690285612Sdelphij				     "serial/j (%lu, %lu) %ld.%06ld", i,
1691285612Sdelphij				     j, (long)tsp->tv_sec,
1692285612Sdelphij				     (long)tsp->tv_usec);
1693182007Sroberto# endif
1694182007Sroberto		}
1695132451Sroberto#endif
1696132451Sroberto
1697132451Sroberto		if (pps_i.clear_sequence == j) {
1698285612Sdelphij			oncore_log(instance, LOG_ERR, "oncore_get_timestamp, error serial pps");
1699285612Sdelphij			peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1700132451Sroberto			return;
1701132451Sroberto		}
1702132451Sroberto		instance->ev_serial = pps_i.clear_sequence;
1703132451Sroberto	}
1704132451Sroberto
1705132451Sroberto	/* convert timespec -> ntp l_fp */
1706132451Sroberto
1707132451Sroberto	dmy = tsp->tv_nsec;
1708132451Sroberto	dmy /= 1e9;
1709182007Sroberto	ts.l_uf = dmy * 4294967296.0;
1710132451Sroberto	ts.l_ui = tsp->tv_sec;
1711182007Sroberto
1712132451Sroberto#if 0
1713132451Sroberto     alternate code for previous 4 lines is
1714132451Sroberto	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1715132451Sroberto	DTOLFP(dmy, &ts);
1716132451Sroberto	dmy = tsp->tv_sec;		/* integer part */
1717132451Sroberto	DTOLFP(dmy, &ts_tmp);
1718132451Sroberto	L_ADD(&ts, &ts_tmp);
1719132451Sroberto     or more simply
1720132451Sroberto	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1721132451Sroberto	DTOLFP(dmy, &ts);
1722132451Sroberto	ts.l_ui = tsp->tv_sec;
1723132451Sroberto#endif	/* 0 */
1724132451Sroberto
1725132451Sroberto	/* now have timestamp in ts */
1726132451Sroberto	/* add in saw_tooth and offset, these will be ZERO if no TRAIM */
1727182007Sroberto	/* they will be IGNORED if the PPSAPI cant do PPS_OFFSET/ASSERT/CLEAR */
1728182007Sroberto	/* we just try to add them in and dont test for that here */
1729132451Sroberto
1730132451Sroberto	/* saw_tooth not really necessary if using TIMEVAL */
1731132451Sroberto	/* since its only precise to us, but do it anyway. */
1732132451Sroberto
1733132451Sroberto	/* offset in ns, and is positive (late), we subtract */
1734132451Sroberto	/* to put the PPS time transition back where it belongs */
1735132451Sroberto
1736132451Sroberto	/* must hand the offset for the NEXT sec off to the Kernel to do */
1737132451Sroberto	/* the addition, so that the Kernel PLL sees the offset too */
1738132451Sroberto
1739132451Sroberto	if (instance->assert)
1740132451Sroberto		instance->pps_p.assert_offset.tv_nsec = -dt2;
1741132451Sroberto	else
1742132451Sroberto		instance->pps_p.clear_offset.tv_nsec  = -dt2;
1743132451Sroberto
1744132451Sroberto	/* The following code is necessary, and not just a time_pps_setparams,
1745132451Sroberto	 * using the saved instance->pps_p, since some other process on the
1746132451Sroberto	 * machine may have diddled with the mode bits (say adding something
1747132451Sroberto	 * that it needs).  We take what is there and ADD what we need.
1748132451Sroberto	 * [[ The results from the time_pps_getcap is unlikely to change so
1749132451Sroberto	 *    we could probably just save it, but I choose to do the call ]]
1750132451Sroberto	 * Unfortunately, there is only ONE set of mode bits in the kernel per
1751132451Sroberto	 * interface, and not one set for each open handle.
1752132451Sroberto	 *
1753132451Sroberto	 * There is still a race condition here where we might mess up someone
1754132451Sroberto	 * elses mode, but if he is being careful too, he should survive.
1755132451Sroberto	 */
1756132451Sroberto
1757132451Sroberto	if (time_pps_getcap(instance->pps_h, &current_mode) < 0) {
1758285612Sdelphij		oncore_log_f(instance, LOG_ERR,
1759285612Sdelphij			     "time_pps_getcap failed: %m");
1760285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1761132451Sroberto		return;
1762132451Sroberto	}
1763132451Sroberto
1764132451Sroberto	if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
1765285612Sdelphij		oncore_log_f(instance, LOG_ERR,
1766285612Sdelphij			     "time_pps_getparams failed: %m");
1767285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1768132451Sroberto		return;
1769132451Sroberto	}
1770132451Sroberto
1771132451Sroberto		/* or current and mine */
1772132451Sroberto	current_params.mode |= instance->pps_p.mode;
1773132451Sroberto		/* but only set whats legal */
1774132451Sroberto	current_params.mode &= current_mode;
1775132451Sroberto
1776132451Sroberto	current_params.assert_offset.tv_sec = 0;
1777132451Sroberto	current_params.assert_offset.tv_nsec = -dt2;
1778132451Sroberto	current_params.clear_offset.tv_sec = 0;
1779132451Sroberto	current_params.clear_offset.tv_nsec = -dt2;
1780132451Sroberto
1781132451Sroberto	if (time_pps_setparams(instance->pps_h, &current_params))
1782285612Sdelphij		oncore_log(instance, LOG_ERR, "ONCORE: Error doing time_pps_setparams");
1783132451Sroberto
1784132451Sroberto	/* have time from UNIX origin, convert to NTP origin. */
1785132451Sroberto
1786132451Sroberto	ts.l_ui += JAN_1970;
1787132451Sroberto	instance->pp->lastrec = ts;
1788132451Sroberto
1789132451Sroberto	/* print out information about this timestamp (long line) */
1790132451Sroberto
1791132451Sroberto	ts_tmp = ts;
1792132451Sroberto	ts_tmp.l_ui = 0;	/* zero integer part */
1793132451Sroberto	LFPTOD(&ts_tmp, dmy);	/* convert fractional part to a double */
1794132451Sroberto	j = 1.0e9*dmy;		/* then to integer ns */
1795132451Sroberto
1796132451Sroberto	Rsm = 0;
1797132451Sroberto	if (instance->chan == 6)
1798132451Sroberto		Rsm = instance->BEHa[64];
1799132451Sroberto	else if (instance->chan == 8)
1800132451Sroberto		Rsm = instance->BEHa[72];
1801132451Sroberto	else if (instance->chan == 12)
1802132451Sroberto		Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]);
1803132451Sroberto
1804132451Sroberto	if (instance->chan == 6 || instance->chan == 8) {
1805182007Sroberto		char	f1[5], f2[5], f3[5], f4[5];
1806182007Sroberto		if (instance->traim) {
1807285612Sdelphij			snprintf(f1, sizeof(f1), "%d",
1808285612Sdelphij				 instance->BEHn[21]);
1809285612Sdelphij			snprintf(f2, sizeof(f2), "%d",
1810285612Sdelphij				 instance->BEHn[22]);
1811285612Sdelphij			snprintf(f3, sizeof(f3), "%2d",
1812285612Sdelphij				 instance->BEHn[23] * 256 +
1813285612Sdelphij				     instance->BEHn[24]);
1814285612Sdelphij			snprintf(f4, sizeof(f4), "%3d",
1815285612Sdelphij				 (s_char)instance->BEHn[25]);
1816182007Sroberto		} else {
1817285612Sdelphij			strlcpy(f1, "x", sizeof(f1));
1818285612Sdelphij			strlcpy(f2, "x", sizeof(f2));
1819285612Sdelphij			strlcpy(f3, "xx", sizeof(f3));
1820285612Sdelphij			strlcpy(f4, "xxx", sizeof(f4));
1821182007Sroberto		}
1822285612Sdelphij		snprintf(Msg, sizeof(Msg),	/* MAX length 128, currently at 127 */
1823182007Sroberto "%u.%09lu %d %d %2d %2d %2d %2ld rstat   %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d",
1824132451Sroberto		    ts.l_ui, j,
1825132451Sroberto		    instance->pp->year, instance->pp->day,
1826132451Sroberto		    instance->pp->hour, instance->pp->minute, instance->pp->second,
1827132451Sroberto		    (long) tsp->tv_sec % 60,
1828132451Sroberto		    Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]),
1829132451Sroberto		    /*rsat	dop */
1830182007Sroberto		    instance->BEHa[38], instance->BEHa[39], instance->traim, f1, f2,
1831182007Sroberto		    /*	nsat visible,	  nsat tracked,     traim,traim,traim */
1832182007Sroberto		    f3, f4,
1833182007Sroberto		    /* sigma neg-sawtooth */
1834132451Sroberto	  /*sat*/   instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53],
1835132451Sroberto		    instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69]
1836132451Sroberto		    );					/* will be 0 for 6 chan */
1837132451Sroberto	} else if (instance->chan == 12) {
1838182007Sroberto		char	f1[5], f2[5], f3[5], f4[5];
1839182007Sroberto		if (instance->traim) {
1840285612Sdelphij			snprintf(f1, sizeof(f1), "%d",
1841285612Sdelphij				 instance->BEHn[6]);
1842285612Sdelphij			snprintf(f2, sizeof(f2), "%d",
1843285612Sdelphij				 instance->BEHn[7]);
1844285612Sdelphij			snprintf(f3, sizeof(f3), "%d",
1845285612Sdelphij				 instance->BEHn[12] * 256 +
1846285612Sdelphij				     instance->BEHn[13]);
1847285612Sdelphij			snprintf(f4, sizeof(f4), "%3d",
1848285612Sdelphij				 (s_char)instance->BEHn[14]);
1849182007Sroberto		} else {
1850285612Sdelphij			strlcpy(f1, "x", sizeof(f1));
1851285612Sdelphij			strlcpy(f2, "x", sizeof(f2));
1852285612Sdelphij			strlcpy(f3, "xx", sizeof(f3));
1853285612Sdelphij			strlcpy(f4, "xxx", sizeof(f4));
1854182007Sroberto		}
1855285612Sdelphij		snprintf(Msg, sizeof(Msg),
1856182007Sroberto "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d%d%d%d%d",
1857132451Sroberto		    ts.l_ui, j,
1858132451Sroberto		    instance->pp->year, instance->pp->day,
1859132451Sroberto		    instance->pp->hour, instance->pp->minute, instance->pp->second,
1860132451Sroberto		    (long) tsp->tv_sec % 60,
1861132451Sroberto		    Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]),
1862132451Sroberto		    /*rsat	dop */
1863182007Sroberto		    instance->BEHa[55], instance->BEHa[56], instance->traim, f1, f2,
1864182007Sroberto		    /*	nsat visible,	  nsat tracked	 traim,traim,traim */
1865182007Sroberto		    f3, f4,
1866182007Sroberto		    /* sigma neg-sawtooth */
1867132451Sroberto	  /*sat*/   instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76],
1868132451Sroberto		    instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100],
1869132451Sroberto		    instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124]
1870132451Sroberto		    );
1871132451Sroberto	}
1872132451Sroberto
1873182007Sroberto	/* and some things I dont understand (magic ntp things) */
1874132451Sroberto
1875132451Sroberto	if (!refclock_process(instance->pp)) {
1876132451Sroberto		refclock_report(instance->peer, CEVNT_BADTIME);
1877285612Sdelphij		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1878132451Sroberto		return;
1879132451Sroberto	}
1880132451Sroberto
1881285612Sdelphij	oncore_log(instance, LOG_INFO, Msg);	 /* this is long message above */
1882132451Sroberto	instance->pollcnt = 2;
1883132451Sroberto
1884132451Sroberto	if (instance->polled) {
1885132451Sroberto		instance->polled = 0;
1886182007Sroberto	     /* instance->pp->dispersion = instance->pp->skew = 0;	*/
1887132451Sroberto		instance->pp->lastref = instance->pp->lastrec;
1888132451Sroberto		refclock_receive(instance->peer);
1889132451Sroberto	}
1890285612Sdelphij	peer->flags |= FLAG_PPS;
189154359Sroberto}
189254359Sroberto
189354359Sroberto
1894132451Sroberto/*************** oncore_msg_XX routines start here *******************/
189554359Sroberto
1896132451Sroberto
189782498Sroberto/*
189882498Sroberto * print Oncore response message.
189982498Sroberto */
190082498Sroberto
190154359Srobertostatic void
190254359Srobertooncore_msg_any(
190354359Sroberto	struct instance *instance,
190454359Sroberto	u_char *buf,
190582498Sroberto	size_t len,
190654359Sroberto	int idx
190754359Sroberto	)
190854359Sroberto{
1909285612Sdelphij#ifdef ONCORE_VERBOSE_MSG_ANY
191054359Sroberto	int i;
191154359Sroberto	const char *fmt = oncore_messages[idx].fmt;
191254359Sroberto	const char *p;
1913285612Sdelphij	char *q;
1914285612Sdelphij	char *qlim;
191582498Sroberto#ifdef HAVE_GETCLOCK
191682498Sroberto	struct timespec ts;
191782498Sroberto#endif
191854359Sroberto	struct timeval tv;
1919285612Sdelphij	char	Msg[120], Msg2[10];
192054359Sroberto
192154359Sroberto	if (debug > 3) {
1922182007Sroberto# ifdef HAVE_GETCLOCK
1923132451Sroberto		(void) getclock(TIMEOFDAY, &ts);
1924132451Sroberto		tv.tv_sec = ts.tv_sec;
1925132451Sroberto		tv.tv_usec = ts.tv_nsec / 1000;
1926182007Sroberto# else
192754359Sroberto		GETTIMEOFDAY(&tv, 0);
1928182007Sroberto# endif
1929285612Sdelphij		oncore_log(instance, LOG_DEBUG, "%ld.%06ld",
1930285612Sdelphij			   (long)tv.tv_sec, (long)tv.tv_usec);
193154359Sroberto
193254359Sroberto		if (!*fmt) {
1933285612Sdelphij			snprintf(Msg, sizeof(Msg), ">>@@%c%c ", buf[2],
1934285612Sdelphij				 buf[3]);
1935285612Sdelphij			for(i = 2; i < len && i < 2400 ; i++) {
1936285612Sdelphij				snprintf(Msg2, sizeof(Msg2), "%02x",
1937285612Sdelphij					 buf[i]);
1938285612Sdelphij				strlcat(Msg, Msg2, sizeof(Msg));
1939285612Sdelphij			}
1940285612Sdelphij			oncore_log(instance, LOG_DEBUG, Msg);
194154359Sroberto			return;
194254359Sroberto		} else {
1943285612Sdelphij			strlcpy(Msg, "##", sizeof(Msg));
1944285612Sdelphij			qlim = Msg + sizeof(Msg) - 3;
1945285612Sdelphij			for (p = fmt, q = Msg + 2; q < qlim && *p; ) {
1946285612Sdelphij				*q++ = *p++;
1947285612Sdelphij				*q++ = '_';
194854359Sroberto			}
1949285612Sdelphij			*q = '\0';
1950285612Sdelphij			oncore_log(instance, LOG_DEBUG, Msg);
1951285612Sdelphij			snprintf(Msg, sizeof(Msg), "%c%c", buf[2],
1952285612Sdelphij				 buf[3]);
195354359Sroberto			i = 4;
195454359Sroberto			for (p = fmt; *p; p++) {
1955285612Sdelphij				snprintf(Msg2, "%02x", buf[i++]);
1956285612Sdelphij				strlcat(Msg, Msg2, sizeof(Msg));
195754359Sroberto			}
1958285612Sdelphij			oncore_log(instance, LOG_DEBUG, Msg);
195954359Sroberto		}
196054359Sroberto	}
1961182007Sroberto#endif
196254359Sroberto}
196354359Sroberto
196454359Sroberto
196554359Sroberto
1966132451Sroberto/* Latitude, Longitude, Height */
196782498Sroberto
196856746Srobertostatic void
1969132451Srobertooncore_msg_Adef(
197056746Sroberto	struct instance *instance,
197156746Sroberto	u_char *buf,
197282498Sroberto	size_t len
197356746Sroberto	)
197456746Sroberto{
1975132451Sroberto}
197656746Sroberto
197756746Sroberto
197856746Sroberto
1979132451Sroberto/* Mask Angle */
198082498Sroberto
1981132451Srobertostatic void
1982132451Srobertooncore_msg_Ag(
1983132451Sroberto	struct instance *instance,
1984132451Sroberto	u_char *buf,
1985132451Sroberto	size_t len
1986132451Sroberto	)
1987285612Sdelphij{
1988285612Sdelphij	const char *cp;
198982498Sroberto
1990285612Sdelphij	cp = "set to";
1991285612Sdelphij	if (instance->o_state == ONCORE_RUN)
1992285612Sdelphij		cp = "is";
199354359Sroberto
1994285612Sdelphij	instance->Ag = buf[4];
1995285612Sdelphij	oncore_log_f(instance, LOG_INFO,
1996285612Sdelphij		     "Satellite mask angle %s %d degrees", cp,
1997285612Sdelphij		     (int)instance->Ag);
1998132451Sroberto}
199954359Sroberto
2000132451Sroberto
2001132451Sroberto
200282498Sroberto/*
2003132451Sroberto * get Position hold position
200456746Sroberto */
200556746Sroberto
200654359Srobertostatic void
2007132451Srobertooncore_msg_As(
200854359Sroberto	struct instance *instance,
200954359Sroberto	u_char *buf,
201082498Sroberto	size_t len
201154359Sroberto	)
201254359Sroberto{
2013132451Sroberto	instance->ss_lat  = buf_w32(&buf[4]);
2014132451Sroberto	instance->ss_long = buf_w32(&buf[8]);
2015132451Sroberto	instance->ss_ht   = buf_w32(&buf[12]);
201654359Sroberto
2017132451Sroberto	/* Print out Position */
2018132451Sroberto	oncore_print_posn(instance);
201954359Sroberto}
202054359Sroberto
202154359Sroberto
202254359Sroberto
2023132451Sroberto/*
2024132451Sroberto * Try to use Oncore UT+ Auto Survey Feature
2025132451Sroberto *	If its not there (VP), set flag to do it ourselves.
202654359Sroberto */
202782498Sroberto
202854359Srobertostatic void
2029132451Srobertooncore_msg_At(
203054359Sroberto	struct instance *instance,
203154359Sroberto	u_char *buf,
203282498Sroberto	size_t len
203354359Sroberto	)
203454359Sroberto{
2035132451Sroberto	instance->saw_At = 1;
2036132451Sroberto	if (instance->site_survey == ONCORE_SS_TESTING) {
2037132451Sroberto		if (buf[4] == 2) {
2038285612Sdelphij			oncore_log(instance, LOG_NOTICE,
2039132451Sroberto					"Initiating hardware 3D site survey");
204054359Sroberto
2041285612Sdelphij			oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW");
2042132451Sroberto			instance->site_survey = ONCORE_SS_HW;
2043132451Sroberto		}
204456746Sroberto	}
2045132451Sroberto}
204682498Sroberto
204782498Sroberto
204882498Sroberto
2049132451Sroberto/*
2050132451Sroberto * get PPS Offset
2051132451Sroberto * Nb. @@Ay is not supported for early UT (no plus) model
2052132451Sroberto */
205382498Sroberto
2054132451Srobertostatic void
2055132451Srobertooncore_msg_Ay(
2056132451Sroberto	struct instance *instance,
2057132451Sroberto	u_char *buf,
2058132451Sroberto	size_t len
2059132451Sroberto	)
2060132451Sroberto{
2061132451Sroberto	if (instance->saw_Ay)
2062132451Sroberto		return;
206382498Sroberto
2064132451Sroberto	instance->saw_Ay = 1;
206582498Sroberto
2066132451Sroberto	instance->offset = buf_w32(&buf[4]);
206782498Sroberto
2068285612Sdelphij	oncore_log_f(instance, LOG_INFO, "PPS Offset is set to %ld ns",
2069285612Sdelphij		     instance->offset);
2070132451Sroberto}
207182498Sroberto
207282498Sroberto
207382498Sroberto
2074132451Sroberto/*
2075132451Sroberto * get Cable Delay
2076132451Sroberto */
207782498Sroberto
2078132451Srobertostatic void
2079132451Srobertooncore_msg_Az(
2080132451Sroberto	struct instance *instance,
2081132451Sroberto	u_char *buf,
2082132451Sroberto	size_t len
2083132451Sroberto	)
2084132451Sroberto{
2085132451Sroberto	if (instance->saw_Az)
2086132451Sroberto		return;
208782498Sroberto
2088132451Sroberto	instance->saw_Az = 1;
208956746Sroberto
2090132451Sroberto	instance->delay = buf_w32(&buf[4]);
209154359Sroberto
2092285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Cable delay is set to %ld ns",
2093285612Sdelphij		     instance->delay);
209482498Sroberto}
209582498Sroberto
209682498Sroberto
209782498Sroberto
2098132451Sroberto/* Ba, Ea and Ha come here, these contain Position */
2099132451Sroberto
210082498Srobertostatic void
2101132451Srobertooncore_msg_BaEaHa(
210282498Sroberto	struct instance *instance,
210382498Sroberto	u_char *buf,
210482498Sroberto	size_t len
210582498Sroberto	)
210682498Sroberto{
2107132451Sroberto	const char	*cp;
2108132451Sroberto	int		mode;
210982498Sroberto
2110132451Sroberto	/* OK, we are close to the RUN state now.
2111132451Sroberto	 * But we have a few more items to initialize first.
2112132451Sroberto	 *
2113132451Sroberto	 * At the beginning of this routine there are several 'timers'.
2114132451Sroberto	 * We enter this routine 1/sec, and since the upper levels of NTP have usurped
2115132451Sroberto	 * the use of timers, we use the 1/sec entry to do things that
2116132451Sroberto	 * we would normally do with timers...
211782498Sroberto	 */
211882498Sroberto
2119132451Sroberto	if (instance->o_state == ONCORE_CHECK_CHAN) {	/* here while checking for the # chan */
2120132451Sroberto		if (buf[2] == 'B') {		/* 6chan */
2121132451Sroberto			if (instance->chan_ck < 6) instance->chan_ck = 6;
2122132451Sroberto		} else if (buf[2] == 'E') {	/* 8chan */
2123132451Sroberto			if (instance->chan_ck < 8) instance->chan_ck = 8;
2124132451Sroberto		} else if (buf[2] == 'H') {	/* 12chan */
2125132451Sroberto			if (instance->chan_ck < 12) instance->chan_ck = 12;
2126132451Sroberto		}
212782498Sroberto
2128132451Sroberto		if (instance->count3++ < 5)
2129132451Sroberto			return;
213082498Sroberto
2131132451Sroberto		instance->count3 = 0;
2132132451Sroberto
2133132451Sroberto		if (instance->chan_in != -1)	/* set in Input */
2134132451Sroberto			instance->chan = instance->chan_in;
2135132451Sroberto		else				/* set from test */
2136132451Sroberto			instance->chan = instance->chan_ck;
2137132451Sroberto
2138285612Sdelphij		oncore_log_f(instance, LOG_INFO, "Input   says chan = %d",
2139285612Sdelphij			    instance->chan_in);
2140285612Sdelphij		oncore_log_f(instance, LOG_INFO, "Model # says chan = %d",
2141285612Sdelphij			     instance->chan_id);
2142285612Sdelphij		oncore_log_f(instance, LOG_INFO, "Testing says chan = %d",
2143285612Sdelphij			     instance->chan_ck);
2144285612Sdelphij		oncore_log_f(instance, LOG_INFO, "Using        chan = %d",
2145285612Sdelphij			     instance->chan);
2146132451Sroberto
2147132451Sroberto		instance->o_state = ONCORE_HAVE_CHAN;
2148285612Sdelphij		oncore_log(instance, LOG_NOTICE, "state = ONCORE_HAVE_CHAN");
2149132451Sroberto
2150132451Sroberto		instance->timeout = 4;
2151285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
2152132451Sroberto		return;
215382498Sroberto	}
215482498Sroberto
2155132451Sroberto	if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
2156132451Sroberto		return;
2157132451Sroberto
2158182007Sroberto	/* PAUSE 5sec - make sure results are stable, before using position */
2159132451Sroberto
2160132451Sroberto	if (instance->count) {
2161182007Sroberto		if (instance->count++ < 5)
2162132451Sroberto			return;
2163132451Sroberto		instance->count = 0;
216482498Sroberto	}
216554359Sroberto
2166132451Sroberto	memcpy(instance->BEHa, buf, (size_t) (len+3));	/* Ba, Ea or Ha */
216754359Sroberto
2168285612Sdelphij	/* check if we saw a response to Gc (M12 or M12+T */
2169285612Sdelphij
2170285612Sdelphij	if (instance->pps_control_msg_seen != -2) {
2171285612Sdelphij		if ((instance->pps_control_msg_seen == -1) && (instance->pps_control != -1)) {
2172285612Sdelphij			oncore_log(instance, LOG_INFO, "PPSCONTROL set, but not implemented (not M12)");
2173285612Sdelphij		}
2174285612Sdelphij		instance->pps_control_msg_seen = -2;
2175285612Sdelphij	}
2176285612Sdelphij
2177182007Sroberto	/* check the antenna (did it get unplugged) and almanac (is it ready) for changes. */
217882498Sroberto
2179132451Sroberto	oncore_check_almanac(instance);
2180132451Sroberto	oncore_check_antenna(instance);
2181132451Sroberto
2182182007Sroberto	/* If we are in Almanac mode, waiting for Almanac, we can't do anything till we have it */
2183132451Sroberto	/* When we have an almanac, we will start the Bn/En/@@Hn messages */
2184132451Sroberto
2185132451Sroberto	if (instance->o_state == ONCORE_ALMANAC)
2186132451Sroberto		if (oncore_wait_almanac(instance))
2187132451Sroberto			return;
2188132451Sroberto
2189132451Sroberto	/* do some things once when we get this far in BaEaHa */
2190132451Sroberto
2191132451Sroberto	if (instance->once) {
2192132451Sroberto		instance->once = 0;
2193132451Sroberto		instance->count2 = 1;
2194132451Sroberto
2195132451Sroberto		/* Have we seen an @@At (position hold) command response */
2196132451Sroberto		/* if not, message out */
2197132451Sroberto
2198132451Sroberto		if (instance->chan != 12 && !instance->saw_At) {
2199285612Sdelphij			oncore_log(instance, LOG_NOTICE,
2200285612Sdelphij				"Not Good, no @@At command (no Position Hold), must be a GT/GT+");
2201285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
2202132451Sroberto		}
2203132451Sroberto
2204132451Sroberto		/* have an Almanac, can start the SiteSurvey
2205132451Sroberto		 * (actually only need to get past the almanac_load where we diddle with At
2206132451Sroberto		 *  command,- we can't change it after we start the HW_SS below
2207132451Sroberto		 */
2208132451Sroberto
2209132451Sroberto		mode = instance->init_type;
2210132451Sroberto		switch (mode) {
2211132451Sroberto		case 0: /* NO initialization, don't change anything */
2212132451Sroberto		case 1: /* Use given Position */
2213132451Sroberto		case 3:
2214132451Sroberto			instance->site_survey = ONCORE_SS_DONE;
2215285612Sdelphij			oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
221682498Sroberto			break;
2217132451Sroberto
221882498Sroberto		case 2:
2219132451Sroberto		case 4: /* Site Survey */
2220285612Sdelphij			oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_TESTING");
2221132451Sroberto			instance->site_survey = ONCORE_SS_TESTING;
2222132451Sroberto			instance->count1 = 1;
2223132451Sroberto			if (instance->chan == 12)
2224285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Gd3,  sizeof(oncore_cmd_Gd3));  /* M12+T */
2225132451Sroberto			else
2226285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_At2,  sizeof(oncore_cmd_At2));  /* not GT, arg not VP */
2227132451Sroberto			break;
2228132451Sroberto		}
222982498Sroberto
2230132451Sroberto		/* Read back PPS Offset for Output */
2231132451Sroberto		/* Nb. This will fail silently for early UT (no plus) and M12 models */
223282498Sroberto
2233285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ayx,  sizeof(oncore_cmd_Ayx));
223482498Sroberto
2235132451Sroberto		/* Read back Cable Delay for Output */
223682498Sroberto
2237285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Azx,  sizeof(oncore_cmd_Azx));
2238132451Sroberto
2239132451Sroberto		/* Read back Satellite Mask Angle for Output */
2240132451Sroberto
2241285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Agx,  sizeof(oncore_cmd_Agx));
2242132451Sroberto	}
2243132451Sroberto
2244132451Sroberto
2245182007Sroberto	/* Unfortunately, the Gd3 command returns '3' for the M12 v1.3 firmware where it is
2246182007Sroberto	 * out-of-range and it should return 0-2. (v1.3 can't do a HW Site Survey)
2247182007Sroberto	 * We must do the Gd3, and then wait a cycle or two for things to settle,
2248182007Sroberto	 * then check Ha[130]&0x10 to see if a SS is in progress.
2249182007Sroberto	 * We will set SW if HW has not been set after an appropriate delay.
2250182007Sroberto	 */
2251132451Sroberto
2252182007Sroberto	if (instance->site_survey == ONCORE_SS_TESTING) {
2253182007Sroberto		if (instance->chan == 12) {
2254182007Sroberto			if (instance->count1) {
2255182007Sroberto				if (instance->count1++ > 5 || instance->BEHa[130]&0x10) {
2256182007Sroberto					instance->count1 = 0;
2257182007Sroberto					if (instance->BEHa[130]&0x10) {
2258285612Sdelphij						oncore_log(instance, LOG_NOTICE,
2259182007Sroberto								"Initiating hardware 3D site survey");
2260132451Sroberto
2261285612Sdelphij						oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW");
2262182007Sroberto						instance->site_survey = ONCORE_SS_HW;
2263182007Sroberto					} else {
2264285612Sdelphij						oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW");
2265182007Sroberto						instance->site_survey = ONCORE_SS_SW;
2266182007Sroberto					}
2267132451Sroberto				}
226882498Sroberto			}
2269182007Sroberto		} else {
2270182007Sroberto			if (instance->count1) {
2271182007Sroberto				if (instance->count1++ > 5) {
2272182007Sroberto					instance->count1 = 0;
2273182007Sroberto					/*
2274182007Sroberto					 * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec
2275182007Sroberto					 * wait after the @@At2/@@Gd3 command we have not changed the state to
2276182007Sroberto					 * ONCORE_SS_HW.  If the Hardware is capable of doing a Site Survey, then
2277182007Sroberto					 * the variable would have been changed by now.
2278182007Sroberto					 * There are three possibilities:
2279182007Sroberto					 * 6/8chan
2280182007Sroberto					 *   (a) We did not get a response to the @@At0 or @@At2 commands,
2281182007Sroberto					 *	   and it must be a GT/GT+/SL with no position hold mode.
2282182007Sroberto					 *	   We will have to do it ourselves.
2283182007Sroberto					 *   (b) We saw the @@At0, @@At2 commands, but @@At2 failed,
2284182007Sroberto					 *	   must be a VP or older UT which doesn't have Site Survey mode.
2285182007Sroberto					 *	   We will have to do it ourselves.
2286182007Sroberto					 * 12chan
2287182007Sroberto					 *   (c) We saw the @@Gd command, and saw H[13]*0x10
2288182007Sroberto					 *	   We will have to do it ourselves (done above)
2289182007Sroberto					 */
2290182007Sroberto
2291285612Sdelphij					oncore_log_f(instance, LOG_INFO,
2292285612Sdelphij						     "Initiating software 3D site survey (%d samples)",
2293285612Sdelphij						     POS_HOLD_AVERAGE);
2294182007Sroberto
2295285612Sdelphij					oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW");
2296182007Sroberto					instance->site_survey = ONCORE_SS_SW;
2297182007Sroberto
2298182007Sroberto					instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
2299182007Sroberto					if (instance->chan == 12)
2300285612Sdelphij						oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
2301182007Sroberto					else {
2302285612Sdelphij						oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
2303285612Sdelphij						oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */
2304182007Sroberto					}
2305182007Sroberto				}
2306182007Sroberto			}
230782498Sroberto		}
230882498Sroberto	}
230982498Sroberto
2310132451Sroberto	/* check the mode we are in 0/2/3D */
231182498Sroberto
2312132451Sroberto	if (instance->chan == 6) {
2313132451Sroberto		if (instance->BEHa[64]&0x8)
2314132451Sroberto			instance->mode = MODE_0D;
2315132451Sroberto		else if (instance->BEHa[64]&0x10)
2316132451Sroberto			instance->mode = MODE_2D;
2317132451Sroberto		else if (instance->BEHa[64]&0x20)
2318132451Sroberto			instance->mode = MODE_3D;
2319132451Sroberto	} else if (instance->chan == 8) {
2320132451Sroberto		if (instance->BEHa[72]&0x8)
2321132451Sroberto			instance->mode = MODE_0D;
2322132451Sroberto		else if (instance->BEHa[72]&0x10)
2323132451Sroberto			instance->mode = MODE_2D;
2324132451Sroberto		else if (instance->BEHa[72]&0x20)
2325132451Sroberto			instance->mode = MODE_3D;
2326132451Sroberto	} else if (instance->chan == 12) {
2327132451Sroberto		int bits;
232854359Sroberto
2329132451Sroberto		bits = (instance->BEHa[129]>>5) & 0x7;	/* actually Ha */
2330132451Sroberto		if (bits == 0x4)
2331132451Sroberto			instance->mode = MODE_0D;
2332132451Sroberto		else if (bits == 0x6)
2333132451Sroberto			instance->mode = MODE_2D;
2334132451Sroberto		else if (bits == 0x7)
2335132451Sroberto			instance->mode = MODE_3D;
2336132451Sroberto	}
233754359Sroberto
2338132451Sroberto	/* copy the record to the (extra) location in SHMEM */
2339132451Sroberto
2340132451Sroberto	if (instance->shmem) {
2341132451Sroberto		int	i;
2342132451Sroberto		u_char	*smp;	 /* pointer to start of shared mem for Ba/Ea/Ha */
2343132451Sroberto
2344132451Sroberto		switch(instance->chan) {
2345132451Sroberto		case 6:   smp = &instance->shmem[instance->shmem_Ba]; break;
2346132451Sroberto		case 8:   smp = &instance->shmem[instance->shmem_Ea]; break;
2347132451Sroberto		case 12:  smp = &instance->shmem[instance->shmem_Ha]; break;
2348182007Sroberto		default:  smp = (u_char *) NULL;		      break;
234954359Sroberto		}
2350132451Sroberto
2351132451Sroberto		switch (instance->mode) {
2352132451Sroberto		case MODE_0D:	i = 1; break;	/* 0D, Position Hold */
2353132451Sroberto		case MODE_2D:	i = 2; break;	/* 2D, Altitude Hold */
2354132451Sroberto		case MODE_3D:	i = 3; break;	/* 3D fix */
2355132451Sroberto		default:	i = 0; break;
2356132451Sroberto		}
2357132451Sroberto
2358182007Sroberto		if (i && smp != NULL) {
2359132451Sroberto			i *= (len+6);
2360132451Sroberto			smp[i + 2]++;
2361132451Sroberto			memcpy(&smp[i+3], buf, (size_t) (len+3));
2362132451Sroberto		}
236354359Sroberto	}
236454359Sroberto
2365132451Sroberto	/*
2366182007Sroberto	 * check if traim timer active
2367132451Sroberto	 * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond
2368132451Sroberto	 */
236954359Sroberto
2370132451Sroberto	if (instance->traim_delay) {
2371132451Sroberto		if (instance->traim_delay++ > 5) {
2372132451Sroberto			instance->traim = 0;
2373132451Sroberto			instance->traim_delay = 0;
2374132451Sroberto			cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
2375285612Sdelphij			oncore_log(instance, LOG_INFO, cp);
2376132451Sroberto
2377132451Sroberto			oncore_set_traim(instance);
2378132451Sroberto		} else
2379132451Sroberto			return;
2380132451Sroberto
2381132451Sroberto	}
2382132451Sroberto
2383132451Sroberto	/* by now should have a @@Ba/@@Ea/@@Ha with good data in it */
2384132451Sroberto
2385132451Sroberto	if (!instance->have_dH && !instance->traim_delay)
2386132451Sroberto		oncore_compute_dH(instance);
2387132451Sroberto
2388132451Sroberto	/*
2389132451Sroberto	 * must be ONCORE_RUN if we are here.
2390132451Sroberto	 * Have # chan and TRAIM by now.
2391132451Sroberto	 */
2392132451Sroberto
2393132451Sroberto	instance->pp->year   = buf[6]*256+buf[7];
2394132451Sroberto	instance->pp->day    = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
2395132451Sroberto	instance->pp->hour   = buf[8];
2396132451Sroberto	instance->pp->minute = buf[9];
2397132451Sroberto	instance->pp->second = buf[10];
2398132451Sroberto
2399132451Sroberto	/*
2400132451Sroberto	 * Are we doing a Hardware or Software Site Survey?
2401132451Sroberto	 */
2402132451Sroberto
2403132451Sroberto	if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW)
2404132451Sroberto		oncore_ss(instance);
2405132451Sroberto
2406132451Sroberto	/* see if we ever saw a response from the @@Ayx above */
2407132451Sroberto
2408132451Sroberto	if (instance->count2) {
2409132451Sroberto		if (instance->count2++ > 5) {	/* this delay to check on @@Ay command */
2410132451Sroberto			instance->count2 = 0;
2411132451Sroberto
2412132451Sroberto			/* Have we seen an Ay (1PPS time offset) command response */
2413132451Sroberto			/* if not, and non-zero offset, zero the offset, and send message */
2414132451Sroberto
2415132451Sroberto			if (!instance->saw_Ay && instance->offset) {
2416285612Sdelphij				oncore_log(instance, LOG_INFO, "No @@Ay command, PPS OFFSET ignored");
241782498Sroberto				instance->offset = 0;
241882498Sroberto			}
241982498Sroberto		}
242054359Sroberto	}
242154359Sroberto
2422132451Sroberto	/*
2423132451Sroberto	 * Check the leap second status once per day.
2424132451Sroberto	 */
242554359Sroberto
2426132451Sroberto	oncore_check_leap_sec(instance);
242754359Sroberto
2428132451Sroberto	/*
2429132451Sroberto	 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
2430132451Sroberto	 */
2431132451Sroberto
2432132451Sroberto	if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE))
2433132451Sroberto		oncore_shmem_get_3D(instance);
2434132451Sroberto
2435132451Sroberto	if (!instance->traim)	/* NO traim, no BnEnHn, go get tick */
2436132451Sroberto		oncore_get_timestamp(instance, instance->offset, instance->offset);
243754359Sroberto}
243854359Sroberto
243954359Sroberto
244054359Sroberto
2441132451Sroberto/* Almanac Status */
2442132451Sroberto
2443132451Srobertostatic void
2444132451Srobertooncore_msg_Bd(
2445132451Sroberto	struct instance *instance,
2446132451Sroberto	u_char *buf,
2447132451Sroberto	size_t len
2448132451Sroberto	)
2449132451Sroberto{
2450285612Sdelphij	oncore_log_f(instance, LOG_NOTICE,
2451285612Sdelphij		     "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
2452285612Sdelphij		     ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6],
2453285612Sdelphij		     buf[7], w32(&buf[8]));
2454132451Sroberto}
2455132451Sroberto
2456132451Sroberto
2457132451Sroberto
2458132451Sroberto/* get leap-second warning message */
2459132451Sroberto
246082498Sroberto/*
2461132451Sroberto * @@Bj does NOT behave as documented in current Oncore firmware.
2462132451Sroberto * It turns on the LEAP indicator when the data is set, and does not,
2463132451Sroberto * as documented, wait until the beginning of the month when the
2464132451Sroberto * leap second will occur.
2465132451Sroberto * Since this firmware bug will never be fixed in all the outstanding Oncore receivers
2466132451Sroberto * @@Bj is only called in June/December.
246782498Sroberto */
246882498Sroberto
246954359Srobertostatic void
2470132451Srobertooncore_msg_Bj(
247154359Sroberto	struct instance *instance,
247254359Sroberto	u_char *buf,
247382498Sroberto	size_t len
247454359Sroberto	)
247554359Sroberto{
2476132451Sroberto	const char	*cp;
247782498Sroberto
2478285612Sdelphij	instance->saw_Bj = 1;
2479285612Sdelphij
2480132451Sroberto	switch(buf[4]) {
2481132451Sroberto	case 1:
2482182007Sroberto		instance->pp->leap = LEAP_ADDSECOND;
2483182007Sroberto		cp = "Set pp.leap to LEAP_ADDSECOND";
2484132451Sroberto		break;
2485132451Sroberto	case 2:
2486182007Sroberto		instance->pp->leap = LEAP_DELSECOND;
2487182007Sroberto		cp = "Set pp.leap to LEAP_DELSECOND";
2488132451Sroberto		break;
2489132451Sroberto	case 0:
2490132451Sroberto	default:
2491182007Sroberto		instance->pp->leap = LEAP_NOWARNING;
2492182007Sroberto		cp = "Set pp.leap to LEAP_NOWARNING";
2493132451Sroberto		break;
2494132451Sroberto	}
2495285612Sdelphij	oncore_log(instance, LOG_NOTICE, cp);
2496132451Sroberto}
249782498Sroberto
2498132451Sroberto
2499132451Sroberto
2500132451Srobertostatic void
2501285612Sdelphijoncore_msg_Bl(
2502285612Sdelphij	struct instance *instance,
2503285612Sdelphij	u_char *buf,
2504285612Sdelphij	size_t	len
2505285612Sdelphij	)
2506285612Sdelphij{
2507285612Sdelphij	int	subframe, valid, page, i, j, tow;
2508285612Sdelphij	int	day_now, day_lsf;
2509285612Sdelphij	const char	*cp;
2510285612Sdelphij	enum {
2511285612Sdelphij		WARN_NOT_YET,
2512285612Sdelphij		WARN_0,
2513285612Sdelphij		WARN_PLUS,
2514285612Sdelphij		WARN_MINUS
2515285612Sdelphij	} warn;
2516285612Sdelphij
2517285612Sdelphij
2518285612Sdelphij	subframe = buf[6] & 017;
2519285612Sdelphij	valid = (buf[6] >> 4) & 017;
2520285612Sdelphij	page = buf[7];
2521285612Sdelphij
2522285612Sdelphij	if ((!instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 4 && page == 18 && valid == 10)) {
2523285612Sdelphij		instance->Bl.dt_ls  = buf[32];
2524285612Sdelphij		instance->Bl.WN_lsf = buf[33];
2525285612Sdelphij		instance->Bl.DN_lsf = buf[34];
2526285612Sdelphij		instance->Bl.dt_lsf = buf[35];
2527285612Sdelphij		instance->Bl.lsf_flg++;
2528285612Sdelphij	}
2529285612Sdelphij	if ((instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 1 && valid == 10)) {
2530285612Sdelphij		i = (buf[7+7]<<8) + buf[7+8];
2531285612Sdelphij		instance->Bl.WN = i >> 6;
2532285612Sdelphij		tow = (buf[7+4]<<16) + (buf[7+5]<<8) + buf[7+6];
2533285612Sdelphij		tow >>= 7;
2534285612Sdelphij		tow = tow & 0377777;
2535285612Sdelphij		tow <<= 2;
2536285612Sdelphij		instance->Bl.DN = tow/57600L + 1;
2537285612Sdelphij		instance->Bl.wn_flg++;
2538285612Sdelphij	}
2539285612Sdelphij	if (instance->Bl.wn_flg && instance->Bl.lsf_flg)  {
2540285612Sdelphij		instance->Bl.wn_flg = instance->Bl.lsf_flg = 0;
2541285612Sdelphij		oncore_cmd_Bl[2] = 0;
2542285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Bl, sizeof oncore_cmd_Bl);
2543285612Sdelphij		oncore_cmd_Bl[2] = 1;
2544285612Sdelphij
2545285612Sdelphij		i = instance->Bl.WN&01400;
2546285612Sdelphij		instance->Bl.WN_lsf |= i;
2547285612Sdelphij
2548285612Sdelphij		/* have everything I need, doit */
2549285612Sdelphij
2550285612Sdelphij		i = (instance->Bl.WN_lsf - instance->Bl.WN);
2551285612Sdelphij		if (i < 0)
2552285612Sdelphij			i += 1024;
2553285612Sdelphij		day_now = instance->Bl.DN;
2554285612Sdelphij		day_lsf = 7*i + instance->Bl.DN_lsf;
2555285612Sdelphij
2556285612Sdelphij		/* ignore if in past or more than a month in future */
2557285612Sdelphij
2558285612Sdelphij		warn = WARN_NOT_YET;
2559285612Sdelphij		if (day_lsf >= day_now && day_lsf - day_now < 32) {
2560285612Sdelphij			/* if < 28d, doit, if 28-31, ck day-of-month < 20 (not at end of prev month) */
2561285612Sdelphij			if (day_lsf - day_now < 28 ||  instance->BEHa[5] < 20) {
2562285612Sdelphij				i = instance->Bl.dt_lsf - instance->Bl.dt_ls;
2563285612Sdelphij				switch (i) {
2564285612Sdelphij				case -1:
2565285612Sdelphij					warn = WARN_MINUS;
2566285612Sdelphij					break;
2567285612Sdelphij				case  0:
2568285612Sdelphij					warn = WARN_0;
2569285612Sdelphij					break;
2570285612Sdelphij				case  1:
2571285612Sdelphij					warn = WARN_PLUS;
2572285612Sdelphij					break;
2573285612Sdelphij				}
2574285612Sdelphij			}
2575285612Sdelphij		}
2576285612Sdelphij
2577285612Sdelphij		switch (warn) {
2578285612Sdelphij		case WARN_0:
2579285612Sdelphij		case WARN_NOT_YET:
2580285612Sdelphij			instance->peer->leap = LEAP_NOWARNING;
2581285612Sdelphij			cp = "Set peer.leap to LEAP_NOWARNING";
2582285612Sdelphij			break;
2583285612Sdelphij		case WARN_MINUS:
2584285612Sdelphij			instance->peer->leap = LEAP_DELSECOND;
2585285612Sdelphij			cp = "Set peer.leap to LEAP_DELSECOND";
2586285612Sdelphij			break;
2587285612Sdelphij		case WARN_PLUS:
2588285612Sdelphij			instance->peer->leap = LEAP_ADDSECOND;
2589285612Sdelphij			cp = "Set peer.leap to LEAP_ADDSECOND";
2590285612Sdelphij			break;
2591316722Sdelphij		default:
2592316722Sdelphij			cp = NULL;
2593316722Sdelphij			break;
2594285612Sdelphij		}
2595285612Sdelphij		oncore_log(instance, LOG_NOTICE, cp);
2596285612Sdelphij
2597285612Sdelphij		i = instance->Bl.dt_lsf-instance->Bl.dt_ls;
2598285612Sdelphij		if (i) {
2599285612Sdelphij			j = (i >= 0) ? i : -i;		/* abs(i) */
2600285612Sdelphij			oncore_log_f(instance, LOG_NOTICE,
2601285612Sdelphij				     "see Leap_Second (%c%d) in %d days",
2602285612Sdelphij				     ((i >= 0) ? '+' : '-'), j,
2603285612Sdelphij				     day_lsf-day_now);
2604285612Sdelphij		}
2605285612Sdelphij	}
2606285612Sdelphij
2607285612Sdelphij/*
2608285612Sdelphij * Reg only wants the following output for "deeper" driver debugging.
2609285612Sdelphij * See Bug 2142 and Bug 1866
2610285612Sdelphij */
2611285612Sdelphij#if 0
2612285612Sdelphij	oncore_log_f(instance, LOG_DEBUG,
2613285612Sdelphij		     "dt_ls = %d  dt_lsf = %d  WN = %d  DN = %d  WN_lsf = %d  DNlsf = %d  wn_flg = %d  lsf_flg = %d  Bl_day = %d",
2614285612Sdelphij		     instance->Bl.dt_ls, instance->Bl.dt_lsf,
2615285612Sdelphij		     instance->Bl.WN, instance->Bl.DN,
2616285612Sdelphij		     instance->Bl.WN_lsf, instance->Bl.DN_lsf,
2617285612Sdelphij		     instance->Bl.wn_flg, instance->Bl.lsf_flg,
2618285612Sdelphij		     instance->Bl.Bl_day);
2619285612Sdelphij#endif
2620285612Sdelphij}
2621285612Sdelphij
2622285612Sdelphij
2623285612Sdelphijstatic void
2624132451Srobertooncore_msg_BnEnHn(
2625132451Sroberto	struct instance *instance,
2626132451Sroberto	u_char *buf,
2627132451Sroberto	size_t	len
2628132451Sroberto	)
2629132451Sroberto{
2630132451Sroberto	long	dt1, dt2;
2631132451Sroberto
2632132451Sroberto	if (instance->o_state != ONCORE_RUN)
2633132451Sroberto		return;
2634132451Sroberto
2635132451Sroberto	if (instance->traim_delay) {	 /* flag that @@Bn/@@En/Hn returned */
2636132451Sroberto			instance->traim_ck = 1;
2637132451Sroberto			instance->traim_delay = 0;
2638285612Sdelphij			oncore_log(instance, LOG_NOTICE, "ONCORE: Detected TRAIM, TRAIM = ON");
2639132451Sroberto
2640132451Sroberto			oncore_set_traim(instance);
264182498Sroberto	}
2642132451Sroberto
2643132451Sroberto	memcpy(instance->BEHn, buf, (size_t) len);	/* Bn or En or Hn */
2644132451Sroberto
2645182007Sroberto	if (!instance->traim)	/* BnEnHn will be turned off in any case */
2646182007Sroberto		return;
2647182007Sroberto
2648132451Sroberto	/* If Time RAIM doesn't like it, don't trust it */
2649132451Sroberto
2650132451Sroberto	if (buf[2] == 'H') {
2651285612Sdelphij		if (instance->BEHn[6]) {    /* bad TRAIM */
2652285612Sdelphij			oncore_log(instance, LOG_WARNING, "BAD TRAIM");
2653132451Sroberto			return;
2654285612Sdelphij		}
2655132451Sroberto
2656132451Sroberto		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2657182007Sroberto		instance->saw_tooth = (s_char) instance->BEHn[14]; /* update for next time Hn[14] */
2658132451Sroberto		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2659132451Sroberto	} else {
2660132451Sroberto		if (instance->BEHn[21]) /* bad TRAIM */
2661132451Sroberto			return;
2662132451Sroberto
2663132451Sroberto		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2664182007Sroberto		instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time Bn[25], En[25] */
2665132451Sroberto		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2666132451Sroberto	}
2667132451Sroberto
2668132451Sroberto	oncore_get_timestamp(instance, dt1, dt2);
266982498Sroberto}
267082498Sroberto
267182498Sroberto
267282498Sroberto
267382498Sroberto/* Here for @@Ca, @@Fa and @@Ia messages */
267482498Sroberto
2675132451Sroberto/* These are Self test Commands for 6, 8, and 12 chan receivers.
2676132451Sroberto * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
2677132451Sroberto * It was found that under some circumstances the following
267882498Sroberto * command would fail if issued immediately after the return from the
267982498Sroberto * @@Fa, but a 2sec delay seemed to fix things.  Since simply calling
2680132451Sroberto * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
2681132451Sroberto * itimer, we set a flag, and test it at the next POLL.  If it hasn't
268282498Sroberto * been cleared, we reissue the @@Cj that is issued below.
268382498Sroberto * Note that we do a @@Cj at the beginning, and again here.
268482498Sroberto * The first is to get the info, the 2nd is just used as a safe command
268582498Sroberto * after the @@Fa for all Oncores (and it was in this posn in the
268682498Sroberto * original code).
268782498Sroberto */
268882498Sroberto
268982498Srobertostatic void
269082498Srobertooncore_msg_CaFaIa(
269182498Sroberto	struct instance *instance,
269282498Sroberto	u_char *buf,
269382498Sroberto	size_t len
269482498Sroberto	)
269582498Sroberto{
2696132451Sroberto	int	i;
269782498Sroberto
269882498Sroberto	if (instance->o_state == ONCORE_TEST_SENT) {
2699132451Sroberto		enum antenna_state antenna;
270082498Sroberto
270182498Sroberto		instance->timeout = 0;
270282498Sroberto
2703285612Sdelphij#if ONCORE_VERBOSE_SELF_TEST
270482498Sroberto		if (debug > 2) {
270582498Sroberto			if (buf[2] == 'I')
2706285612Sdelphij				oncore_log_f(instance, LOG_DEBUG,
2707285612Sdelphij					     ">>@@%ca %x %x %x", buf[2],
2708285612Sdelphij					     buf[4], buf[5], buf[6]);
270982498Sroberto			else
2710285612Sdelphij				oncore_log_f(instance, LOG_DEBUG,
2711285612Sdelphij					     ">>@@%ca %x %x", buf[2],
2712285612Sdelphij					     buf[4], buf[5]);
271382498Sroberto		}
2714182007Sroberto#endif
271582498Sroberto
2716132451Sroberto		antenna = (buf[4] & 0xc0) >> 6;
271782498Sroberto		buf[4] &= ~0xc0;
271882498Sroberto
2719132451Sroberto		i = buf[4] || buf[5];
2720132451Sroberto		if (buf[2] == 'I') i = i || buf[6];
2721132451Sroberto		if (i) {
2722285612Sdelphij			if (buf[2] == 'I')
2723285612Sdelphij				oncore_log_f(instance, LOG_ERR,
2724285612Sdelphij					     "self test failed: result %02x %02x %02x",
2725285612Sdelphij					     buf[4], buf[5], buf[6]);
2726285612Sdelphij			else
2727285612Sdelphij				oncore_log_f(instance, LOG_ERR,
2728285612Sdelphij					     "self test failed: result %02x %02x",
2729285612Sdelphij					     buf[4], buf[5]);
2730132451Sroberto
2731285612Sdelphij			oncore_log(instance, LOG_ERR,
2732285612Sdelphij				   "ONCORE: self test failed, shutting down driver");
2733285612Sdelphij
2734132451Sroberto			refclock_report(instance->peer, CEVNT_FAULT);
273582498Sroberto			oncore_shutdown(instance->unit, instance->peer);
273682498Sroberto			return;
273782498Sroberto		}
273882498Sroberto
2739132451Sroberto		/* report the current antenna state */
274082498Sroberto
2741132451Sroberto		oncore_antenna_report(instance, antenna);
274282498Sroberto
274382498Sroberto		instance->o_state = ONCORE_INIT;
2744285612Sdelphij		oncore_log(instance, LOG_NOTICE, "state = ONCORE_INIT");
2745132451Sroberto
2746132451Sroberto		instance->timeout = 4;
2747285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
274882498Sroberto	}
274982498Sroberto}
275082498Sroberto
275182498Sroberto
275282498Sroberto
2753132451Sroberto/*
2754132451Sroberto * Demultiplex the almanac into shmem
2755132451Sroberto */
275682498Sroberto
275782498Srobertostatic void
2758132451Srobertooncore_msg_Cb(
275982498Sroberto	struct instance *instance,
276082498Sroberto	u_char *buf,
276182498Sroberto	size_t len
276282498Sroberto	)
276382498Sroberto{
2764132451Sroberto	int i;
276554359Sroberto
2766132451Sroberto	if (instance->shmem == NULL)
2767132451Sroberto		return;
276882498Sroberto
2769132451Sroberto	if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
2770132451Sroberto		i = buf[5];
2771132451Sroberto	else if (buf[4] == 4 && buf[5] <= 5)
2772132451Sroberto		i = buf[5] + 24;
2773132451Sroberto	else if (buf[4] == 4 && buf[5] <= 10)
2774132451Sroberto		i = buf[5] + 23;
2775132451Sroberto	else if (buf[4] == 4 && buf[5] == 25)
2776132451Sroberto		i = 34;
2777132451Sroberto	else {
2778285612Sdelphij		oncore_log(instance, LOG_NOTICE, "Cb: Response is NO ALMANAC");
277954359Sroberto		return;
2780132451Sroberto	}
278154359Sroberto
2782132451Sroberto	i *= 36;
2783132451Sroberto	instance->shmem[instance->shmem_Cb + i + 2]++;
2784132451Sroberto	memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
278554359Sroberto
2786285612Sdelphij#ifdef ONCORE_VERBOSE_MSG_CB
2787285612Sdelphij	oncore_log_f(instance, LOG_DEBUG, "See Cb [%d,%d]", buf[4],
2788285612Sdelphij		     buf[5]);
2789132451Sroberto#endif
2790132451Sroberto}
279182498Sroberto
279282498Sroberto
2793132451Sroberto
2794132451Sroberto/*
2795132451Sroberto * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
2796132451Sroberto *	not so for VP (eeprom) or any unit with a battery
2797132451Sroberto */
2798132451Sroberto
2799132451Srobertostatic void
2800132451Srobertooncore_msg_Cf(
2801132451Sroberto	struct instance *instance,
2802132451Sroberto	u_char *buf,
2803132451Sroberto	size_t len
2804132451Sroberto	)
2805132451Sroberto{
2806132451Sroberto	if (instance->o_state == ONCORE_RESET_SENT) {
2807285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
2808132451Sroberto										       /* Reset set VP to IDLE */
2809132451Sroberto		instance->o_state = ONCORE_TEST_SENT;
2810285612Sdelphij		oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
2811132451Sroberto
2812285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
281382498Sroberto	}
2814132451Sroberto}
281582498Sroberto
281682498Sroberto
281782498Sroberto
2818132451Sroberto/*
2819132451Sroberto * This is the Grand Central Station for the Preliminary Initialization.
2820132451Sroberto * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
2821132451Sroberto *
2822132451Sroberto * We do an @@Cj whenever we need a safe command for all Oncores.
2823132451Sroberto * The @@Cj gets us back here where we can switch to the next phase of setup.
2824132451Sroberto *
2825132451Sroberto * o Once at the very beginning (in start) to get the Model number.
2826132451Sroberto *   This info is printed, but no longer used.
2827132451Sroberto * o Again after we have determined the number of Channels in the receiver.
2828132451Sroberto * o And once later after we have done a reset and test, (which may hang),
2829132451Sroberto *   as we are about to initialize the Oncore and start it running.
2830132451Sroberto * o We have one routine below for each case.
2831132451Sroberto */
283282498Sroberto
2833132451Srobertostatic void
2834132451Srobertooncore_msg_Cj(
2835132451Sroberto	struct instance *instance,
2836132451Sroberto	u_char *buf,
2837132451Sroberto	size_t len
2838132451Sroberto	)
2839132451Sroberto{
2840132451Sroberto	int	mode;
284182498Sroberto
2842132451Sroberto	memcpy(instance->Cj, buf, len);
284382498Sroberto
2844132451Sroberto	instance->timeout = 0;
2845132451Sroberto	if (instance->o_state == ONCORE_CHECK_ID) {
2846132451Sroberto		oncore_msg_Cj_id(instance, buf, len);
2847132451Sroberto		oncore_chan_test(instance);
2848132451Sroberto	} else if (instance->o_state == ONCORE_HAVE_CHAN) {
2849132451Sroberto		mode = instance->init_type;
2850132451Sroberto		if (mode == 3 || mode == 4) {	/* Cf will return here to check for TEST */
2851132451Sroberto			instance->o_state = ONCORE_RESET_SENT;
2852285612Sdelphij			oncore_log(instance, LOG_NOTICE, "state = ONCORE_RESET_SENT");
2853285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
2854132451Sroberto		} else {
2855132451Sroberto			instance->o_state = ONCORE_TEST_SENT;
2856285612Sdelphij			oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
285782498Sroberto		}
285882498Sroberto	}
285982498Sroberto
2860132451Sroberto	if (instance->o_state == ONCORE_TEST_SENT) {
2861132451Sroberto		if (instance->chan == 6)
2862285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
2863132451Sroberto		else if (instance->chan == 8)
2864285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
2865132451Sroberto		else if (instance->chan == 12)
2866285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
2867132451Sroberto	} else if (instance->o_state == ONCORE_INIT)
2868132451Sroberto		oncore_msg_Cj_init(instance, buf, len);
2869132451Sroberto}
287082498Sroberto
287182498Sroberto
287282498Sroberto
2873132451Sroberto/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
2874132451Sroberto *	the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
2875132451Sroberto *	and from Motorola.  Until recently Rick was the only source of
2876132451Sroberto *	this information as Motorola didn't give the information out.
2877132451Sroberto *
2878132451Sroberto * Determine the Type from the Model #, this determines #chan and if TRAIM is
2879132451Sroberto *   available.
2880132451Sroberto *
2881132451Sroberto * The Information from this routine is NO LONGER USED.
2882132451Sroberto * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
2883132451Sroberto */
288482498Sroberto
2885132451Srobertostatic void
2886132451Srobertooncore_msg_Cj_id(
2887132451Sroberto	struct instance *instance,
2888132451Sroberto	u_char *buf,
2889132451Sroberto	size_t len
2890132451Sroberto	)
2891132451Sroberto{
2892285612Sdelphij	char *cp2, Model[21];
2893285612Sdelphij	const char *cp, *cp1;
2894132451Sroberto
2895132451Sroberto	/* Write Receiver ID message to clockstats file */
2896132451Sroberto
2897132451Sroberto	instance->Cj[294] = '\0';
2898285612Sdelphij	for (cp= (char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
2899285612Sdelphij		char *cpw = strchr(cp, '\r');
2900285612Sdelphij		if (!cpw)
2901285612Sdelphij			cpw = (char *)&instance->Cj[294];
2902285612Sdelphij		*cpw = '\0';
2903285612Sdelphij		oncore_log(instance, LOG_NOTICE, cp);
2904285612Sdelphij		*cpw = '\r';
2905285612Sdelphij		cp = cpw+2;
290682498Sroberto	}
290782498Sroberto
2908132451Sroberto	/* next, the Firmware Version and Revision numbers */
290982498Sroberto
2910182007Sroberto	instance->version  = atoi((char *) &instance->Cj[83]);
2911182007Sroberto	instance->revision = atoi((char *) &instance->Cj[111]);
291282498Sroberto
2913132451Sroberto	/* from model number decide which Oncore this is,
2914132451Sroberto		and then the number of channels */
291582498Sroberto
2916182007Sroberto	for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++)   /* start right after 'Model #' */
2917132451Sroberto		;
2918132451Sroberto	cp1 = cp;
2919132451Sroberto	cp2 = Model;
2920285612Sdelphij	for (; !isspace((unsigned char)*cp) && cp-cp1 < 20; cp++, cp2++)
2921132451Sroberto		*cp2 = *cp;
2922132451Sroberto	*cp2 = '\0';
2923132451Sroberto
2924132451Sroberto	cp = 0;
2925132451Sroberto	if (!strncmp(Model, "PVT6", (size_t) 4)) {
2926132451Sroberto		cp = "PVT6";
2927132451Sroberto		instance->model = ONCORE_PVT6;
2928132451Sroberto	} else if (Model[0] == 'A') {
2929132451Sroberto		cp = "Basic";
2930132451Sroberto		instance->model = ONCORE_BASIC;
2931132451Sroberto	} else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
2932132451Sroberto		cp = "VP";
2933132451Sroberto		instance->model = ONCORE_VP;
2934132451Sroberto	} else if (Model[0] == 'P') {
2935132451Sroberto		cp = "M12";
2936132451Sroberto		instance->model = ONCORE_M12;
2937132451Sroberto	} else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
2938132451Sroberto		if (Model[5] == 'N') {
2939132451Sroberto			cp = "GT";
2940132451Sroberto			instance->model = ONCORE_GT;
2941132451Sroberto		} else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
2942132451Sroberto			cp = "GT+";
2943132451Sroberto			instance->model = ONCORE_GTPLUS;
2944132451Sroberto		} else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
2945132451Sroberto				cp = "UT";
2946132451Sroberto				instance->model = ONCORE_UT;
2947132451Sroberto		} else if (Model[1] == '5' && Model[5] == 'G') {
2948132451Sroberto			cp = "UT+";
2949132451Sroberto			instance->model = ONCORE_UTPLUS;
2950132451Sroberto		} else if (Model[1] == '6' && Model[5] == 'G') {
2951132451Sroberto			cp = "SL";
2952132451Sroberto			instance->model = ONCORE_SL;
2953132451Sroberto		} else {
2954132451Sroberto			cp = "Unknown";
2955132451Sroberto			instance->model = ONCORE_UNKNOWN;
295654359Sroberto		}
2957132451Sroberto	} else	{
2958132451Sroberto		cp = "Unknown";
2959132451Sroberto		instance->model = ONCORE_UNKNOWN;
296054359Sroberto	}
296154359Sroberto
2962132451Sroberto	/* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
296354359Sroberto
2964285612Sdelphij	oncore_log_f(instance, LOG_INFO,
2965285612Sdelphij		     "This looks like an Oncore %s with version %d.%d firmware.",
2966285612Sdelphij		     cp, instance->version, instance->revision);
296782498Sroberto
2968132451Sroberto	instance->chan_id = 8;	   /* default */
2969132451Sroberto	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2970132451Sroberto		instance->chan_id = 6;
2971132451Sroberto	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2972132451Sroberto		instance->chan_id = 8;
2973132451Sroberto	else if (instance->model == ONCORE_M12)
2974132451Sroberto		instance->chan_id = 12;
297582498Sroberto
2976132451Sroberto	instance->traim_id = 0;    /* default */
2977132451Sroberto	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2978132451Sroberto		instance->traim_id = 0;
2979132451Sroberto	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2980132451Sroberto		instance->traim_id = 1;
2981132451Sroberto	else if (instance->model == ONCORE_M12)
2982132451Sroberto		instance->traim_id = -1;
298382498Sroberto
2984285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Channels = %d, TRAIM = %s",
2985285612Sdelphij		     instance->chan_id,
2986285612Sdelphij		     ((instance->traim_id < 0)
2987285612Sdelphij			  ? "UNKNOWN"
2988285612Sdelphij			  : (instance->traim_id > 0)
2989285612Sdelphij				? "ON"
2990285612Sdelphij				: "OFF"));
2991132451Sroberto}
2992132451Sroberto
2993132451Sroberto
2994132451Sroberto
2995132451Sroberto/* OK, know type of Oncore, have possibly reset it, and have tested it.
2996132451Sroberto * We know the number of channels.
2997132451Sroberto * We will determine whether we have TRAIM before we actually start.
2998132451Sroberto * Now initialize.
2999132451Sroberto */
3000132451Sroberto
3001132451Srobertostatic void
3002132451Srobertooncore_msg_Cj_init(
3003132451Sroberto	struct instance *instance,
3004132451Sroberto	u_char *buf,
3005132451Sroberto	size_t len
3006132451Sroberto	)
3007132451Sroberto{
3008285612Sdelphij	u_char	Cmd[20];
3009132451Sroberto	int	mode;
3010132451Sroberto
3011132451Sroberto
3012132451Sroberto	/* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
3013132451Sroberto	 * start again if we go from 0D -> 3D, then loses them again when we
3014132451Sroberto	 * go from 3D -> 0D.  We do this to get a @@Ea message for SHMEM.
3015132451Sroberto	 * For NOW we will turn this aspect of filling SHMEM off for the M12
301682498Sroberto	 */
301782498Sroberto
3018132451Sroberto	if (instance->chan == 12) {
3019132451Sroberto		instance->shmem_bad_Ea = 1;
3020285612Sdelphij		oncore_log_f(instance, LOG_NOTICE,
3021285612Sdelphij			     "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***",
3022285612Sdelphij			     instance->version, instance->revision);
302354359Sroberto	}
302454359Sroberto
3025285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
3026285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
3027285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
3028285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
3029285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
3030285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
3031285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
303282498Sroberto
3033132451Sroberto	mode = instance->init_type;
303482498Sroberto
3035132451Sroberto	/* If there is Position input in the Config file
3036132451Sroberto	 * and mode = (1,3) set it as posn hold posn, goto 0D mode.
3037132451Sroberto	 *  or mode = (2,4) set it as INITIAL position, and do Site Survey.
303882498Sroberto	 */
303954359Sroberto
3040132451Sroberto	if (instance->posn_set) {
3041285612Sdelphij		oncore_log(instance, LOG_INFO, "Setting Posn from input data");
3042132451Sroberto		oncore_set_posn(instance);	/* this should print posn indirectly thru the As cmd */
3043132451Sroberto	} else	/* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
3044132451Sroberto		if (instance->chan != 12)
3045285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
304682498Sroberto
3047132451Sroberto	if (mode != 0) {
3048132451Sroberto			/* cable delay in ns */
3049132451Sroberto		memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
3050285612Sdelphij		w32_buf(&Cmd[-2+4], (int)instance->delay);
3051285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Az));	/* 6,8,12 */
305282498Sroberto
3053132451Sroberto			/* PPS offset in ns */
3054285612Sdelphij		memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay));	/* some have it, some don't */
3055285612Sdelphij		w32_buf(&Cmd[-2+4], instance->offset);			/* will check for hw response */
3056285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ay));
3057132451Sroberto
3058132451Sroberto		/* Satellite mask angle */
3059132451Sroberto
3060132451Sroberto		if (instance->Ag != 0xff) {	/* will have 0xff in it if not set by user */
3061132451Sroberto			memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
3062132451Sroberto			Cmd[-2+4] = instance->Ag;
3063285612Sdelphij			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ag));
3064132451Sroberto		}
306582498Sroberto	}
306682498Sroberto
3067132451Sroberto	/* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
3068132451Sroberto	 * now we're really running
3069132451Sroberto	 * these were ALL started in the chan test,
3070132451Sroberto	 * However, if we had mode=3,4 then commands got turned off, so we turn
3071132451Sroberto	 * them on again here just in case
307254359Sroberto	 */
307354359Sroberto
3074132451Sroberto	if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
3075285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
3076285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
3077285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
3078285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
3079285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba ));
3080132451Sroberto	} else if (instance->chan == 8) {  /* start 8chan, kill 6,12chan commands */
3081285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
3082285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
3083285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
3084285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
3085285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea ));
3086132451Sroberto	} else if (instance->chan == 12){  /* start 12chan, kill 6,12chan commands */
3087285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
3088285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
3089285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
3090285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
3091285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha ));
3092285612Sdelphij		oncore_cmd_Gc[2] = (instance->pps_control < 0) ? 1 : instance->pps_control;
3093285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* PPS off/continuous/Tracking 1+sat/TRAIM */
309454359Sroberto	}
309554359Sroberto
3096132451Sroberto	instance->count = 1;
3097132451Sroberto	instance->o_state = ONCORE_ALMANAC;
3098285612Sdelphij	oncore_log(instance, LOG_NOTICE, "state = ONCORE_ALMANAC");
3099132451Sroberto}
310082498Sroberto
310154359Sroberto
310254359Sroberto
3103132451Sroberto/* 12chan position */
310454359Sroberto
3105132451Srobertostatic void
3106132451Srobertooncore_msg_Ga(
3107132451Sroberto	struct instance *instance,
3108132451Sroberto	u_char *buf,
3109132451Sroberto	size_t len
3110132451Sroberto	)
3111132451Sroberto{
3112132451Sroberto	long lat, lon, ht;
3113132451Sroberto	double Lat, Lon, Ht;
311454359Sroberto
311554359Sroberto
3116132451Sroberto	lat = buf_w32(&buf[4]);
3117132451Sroberto	lon = buf_w32(&buf[8]);
3118132451Sroberto	ht  = buf_w32(&buf[12]);  /* GPS ellipsoid */
311954359Sroberto
3120132451Sroberto	Lat = lat;
3121132451Sroberto	Lon = lon;
3122132451Sroberto	Ht  = ht;
312354359Sroberto
3124132451Sroberto	Lat /= 3600000;
3125132451Sroberto	Lon /= 3600000;
3126132451Sroberto	Ht  /= 100;
3127132451Sroberto
3128285612Sdelphij	oncore_log_f(instance, LOG_NOTICE,
3129285612Sdelphij		     "Ga Posn Lat = %.7f, Lon = %.7f, Ht  = %.2f", Lat,
3130285612Sdelphij		     Lon, Ht);
3131132451Sroberto
3132132451Sroberto	instance->ss_lat  = lat;
3133132451Sroberto	instance->ss_long = lon;
3134132451Sroberto	instance->ss_ht   = ht;
313554359Sroberto
3136132451Sroberto	oncore_print_posn(instance);
3137132451Sroberto}
313882498Sroberto
313982498Sroberto
314082498Sroberto
3141132451Sroberto/* 12 chan time/date */
314282498Sroberto
3143132451Srobertostatic void
3144132451Srobertooncore_msg_Gb(
3145132451Sroberto	struct instance *instance,
3146132451Sroberto	u_char *buf,
3147132451Sroberto	size_t len
3148132451Sroberto	)
3149132451Sroberto{
3150285612Sdelphij	const char *	gmts;
3151132451Sroberto	int	mo, d, y, h, m, s, gmth, gmtm;
315282498Sroberto
3153132451Sroberto	mo = buf[4];
3154132451Sroberto	d  = buf[5];
3155132451Sroberto	y  = 256*buf[6]+buf[7];
3156132451Sroberto
3157132451Sroberto	h  = buf[8];
3158132451Sroberto	m  = buf[9];
3159132451Sroberto	s  = buf[10];
3160132451Sroberto
3161132451Sroberto	gmts = ((buf[11] == 0) ? "+" : "-");
3162132451Sroberto	gmth = buf[12];
3163132451Sroberto	gmtm = buf[13];
3164132451Sroberto
3165285612Sdelphij	oncore_log_f(instance, LOG_NOTICE,
3166285612Sdelphij		     "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
3167285612Sdelphij		     d, months[mo-1], y, h, m, s, gmts, gmth, gmtm);
316854359Sroberto}
316954359Sroberto
317054359Sroberto
317154359Sroberto
3172285612Sdelphij/* Response to PPS Control message (M12 and M12+T only ) */
3173285612Sdelphij
3174285612Sdelphijstatic void
3175285612Sdelphijoncore_msg_Gc(
3176285612Sdelphij	struct instance *instance,
3177285612Sdelphij	u_char *buf,
3178285612Sdelphij	size_t len
3179285612Sdelphij	)
3180285612Sdelphij{
3181285612Sdelphij	const char *tbl[] = {"OFF", "ON", "SATELLITE", "TRAIM" };
3182285612Sdelphij
3183285612Sdelphij	instance->pps_control_msg_seen = 1;
3184285612Sdelphij	oncore_log_f(instance, LOG_INFO, "PPS Control set to %s",
3185285612Sdelphij		     tbl[buf[4]]);
3186285612Sdelphij}
3187285612Sdelphij
3188285612Sdelphij
3189285612Sdelphij
3190132451Sroberto/* Leap Second for M12, gives all info from satellite message */
3191132451Sroberto/* also in UT v3.0 */
319282498Sroberto
319382498Srobertostatic void
3194132451Srobertooncore_msg_Gj(
319582498Sroberto	struct instance *instance,
3196132451Sroberto	u_char *buf,
3197132451Sroberto	size_t len
319882498Sroberto	)
319982498Sroberto{
3200285612Sdelphij	static const char * insrem[2] = {
3201285612Sdelphij		"removed",
3202285612Sdelphij		"inserted"
3203285612Sdelphij	};
3204285612Sdelphij
3205132451Sroberto	int dt;
3206285612Sdelphij	const char *cp;
320754359Sroberto
3208132451Sroberto	instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
320954359Sroberto
3210132451Sroberto	/* print the message to verify whats there */
321154359Sroberto
3212132451Sroberto	dt = buf[5] - buf[4];
321354359Sroberto
3214285612Sdelphij	oncore_log_f(instance, LOG_INFO,
3215285612Sdelphij		     "Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
3216285612Sdelphij		     buf[4], buf[5], 256 * buf[6] + buf[7], buf[8],
3217285612Sdelphij		     buf[9], buf[10],
3218285612Sdelphij		     (buf[14] + 256 *
3219285612Sdelphij		         (buf[13] + 256 * (buf[12] + 256 * buf[11]))),
3220285612Sdelphij		     buf[15], buf[16], buf[17]);
322154359Sroberto
3222285612Sdelphij	/* There seems to be eternal confusion about when a leap second
3223285612Sdelphij	 * takes place. It's the second *before* the new TAI offset
3224285612Sdelphij	 * becomes effective. But since the ONCORE receiver tells us
3225285612Sdelphij	 * just that, we would have to do some time/date calculations to
3226285612Sdelphij	 * get the actual leap second -- that is, the one that is
3227285612Sdelphij	 * deleted or inserted.
3228285612Sdelphij	 *
3229285612Sdelphij	 * Going through all this for a simple log is probably overkill,
3230285612Sdelphij	 * so for fixing bug#1050 the message output is changed to
3231285612Sdelphij	 * reflect the fact that it tells the second after the leap
3232285612Sdelphij	 * second.
3233285612Sdelphij	 */
3234285612Sdelphij	if (dt)
3235285612Sdelphij		oncore_log_f(instance, LOG_NOTICE,
3236285612Sdelphij			     "Leap second %s (%d) before %04u-%02u-%02u/%02u:%02u:%02u",
3237285612Sdelphij			     insrem[(dt > 0)], dt,
3238285612Sdelphij			     256u * buf[6] + buf[7], buf[8], buf[9],
3239285612Sdelphij			     buf[15], buf[16], buf[17]);
3240285612Sdelphij
3241132451Sroberto	/* Only raise warning within a month of the leap second */
324254359Sroberto
3243182007Sroberto	instance->pp->leap = LEAP_NOWARNING;
3244182007Sroberto	cp = "Set pp.leap to LEAP_NOWARNING";
324554359Sroberto
3246132451Sroberto	if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
3247132451Sroberto	    buf[8] == instance->BEHa[4]) {	/* month */
3248132451Sroberto		if (dt) {
3249132451Sroberto			if (dt < 0) {
3250182007Sroberto				instance->pp->leap = LEAP_DELSECOND;
3251182007Sroberto				cp = "Set pp.leap to LEAP_DELSECOND";
3252132451Sroberto			} else {
3253182007Sroberto				instance->pp->leap = LEAP_ADDSECOND;
3254182007Sroberto				cp = "Set pp.leap to LEAP_ADDSECOND";
3255132451Sroberto			}
325654359Sroberto		}
3257132451Sroberto	}
3258285612Sdelphij	oncore_log(instance, LOG_INFO, cp);
3259132451Sroberto}
326054359Sroberto
326154359Sroberto
326254359Sroberto
3263132451Sroberto/* Power on failure */
326454359Sroberto
3265132451Srobertostatic void
3266132451Srobertooncore_msg_Sz(
3267132451Sroberto	struct instance *instance,
3268132451Sroberto	u_char *buf,
3269132451Sroberto	size_t len
3270132451Sroberto	)
3271132451Sroberto{
3272132451Sroberto	if (instance && instance->peer) {
3273285612Sdelphij		oncore_log(instance, LOG_ERR, "Oncore: System Failure at Power On");
3274132451Sroberto		oncore_shutdown(instance->unit, instance->peer);
327554359Sroberto	}
3276132451Sroberto}
327754359Sroberto
3278132451Sroberto/************** Small Subroutines ***************/
327954359Sroberto
328054359Sroberto
3281132451Srobertostatic void
3282132451Srobertooncore_antenna_report(
3283132451Sroberto	struct instance *instance,
3284132451Sroberto	enum antenna_state new_state)
3285132451Sroberto{
3286285612Sdelphij	const char *cp;
3287132451Sroberto
3288132451Sroberto	if (instance->ant_state == new_state)
328954359Sroberto		return;
329054359Sroberto
3291132451Sroberto	switch (new_state) {
3292132451Sroberto	case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK";                   break;
3293132451Sroberto	case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)";  break;
3294132451Sroberto	case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
3295132451Sroberto	case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)";   break;
3296132451Sroberto	default:		cp = "GPS antenna: ?";                    break;
329756746Sroberto	}
329854359Sroberto
3299132451Sroberto	instance->ant_state = new_state;
3300285612Sdelphij	oncore_log(instance, LOG_NOTICE, cp);
3301132451Sroberto}
330254359Sroberto
330354359Sroberto
330454359Sroberto
3305132451Srobertostatic void
3306132451Srobertooncore_chan_test(
3307132451Sroberto	struct instance *instance
3308132451Sroberto	)
3309132451Sroberto{
3310132451Sroberto	/* subroutine oncore_Cj_id has determined the number of channels from the
3311132451Sroberto	 * model number of the attached oncore.  This is not always correct since
3312132451Sroberto	 * the oncore could have non-standard firmware.  Here we check (independently) by
3313132451Sroberto	 * trying a 6, 8, and 12 chan command, and see which responds.
3314132451Sroberto	 * Caution: more than one CAN respond.
331582498Sroberto	 *
3316132451Sroberto	 * This #chan is used by the code rather than that calculated from the model number.
331782498Sroberto	 */
331882498Sroberto
3319132451Sroberto	instance->o_state = ONCORE_CHECK_CHAN;
3320285612Sdelphij	oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_CHAN");
332154359Sroberto
3322132451Sroberto	instance->count3 = 1;
3323285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
3324285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
3325285612Sdelphij	oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
3326132451Sroberto}
332782498Sroberto
332882498Sroberto
332982498Sroberto
3330132451Sroberto/* check for a GOOD Almanac, have we got one yet? */
333154359Sroberto
3332132451Srobertostatic void
3333132451Srobertooncore_check_almanac(
3334132451Sroberto	struct instance *instance
3335132451Sroberto	)
3336132451Sroberto{
3337132451Sroberto	if (instance->chan == 6) {
3338132451Sroberto		instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
3339132451Sroberto		instance->rsm.bad_fix	  = instance->BEHa[64]&0x52;
3340132451Sroberto	} else if (instance->chan == 8) {
3341132451Sroberto		instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
3342132451Sroberto		instance->rsm.bad_fix	  = instance->BEHa[72]&0x52;
3343132451Sroberto	} else if (instance->chan == 12) {
3344182007Sroberto		int bits1, bits2, bits3;
3345132451Sroberto
3346132451Sroberto		bits1 = (instance->BEHa[129]>>5) & 0x7; 	/* actually Ha */
3347132451Sroberto		bits2 = instance->BEHa[130];
3348132451Sroberto		instance->rsm.bad_almanac = (bits2 & 0x80);
3349132451Sroberto		instance->rsm.bad_fix	  = (bits2 & 0x8) || (bits1 == 0x2);
3350132451Sroberto					  /* too few sat     Bad Geom	  */
3351182007Sroberto
3352182007Sroberto		bits3 = instance->BEHa[141];	/* UTC parameters */
3353182007Sroberto		if (!instance->count5_set && (bits3 & 0xC0)) {
3354285612Sdelphij			instance->count5 = 4;	/* was 2 [Bug 1766] */
3355182007Sroberto			instance->count5_set = 1;
3356182007Sroberto		}
3357285612Sdelphij#ifdef ONCORE_VERBOSE_CHECK_ALMANAC
3358285612Sdelphij		oncore_log_f(instance, LOG_DEBUG,
3359285612Sdelphij			     "DEBUG BITS: (%x %x), (%x %x %x),  %x %x %x %x %x",
3360285612Sdelphij			     instance->BEHa[129], instance->BEHa[130],
3361285612Sdelphij			     bits1, bits2, bits3,
3362285612Sdelphij			     instance->mode == MODE_0D,
3363285612Sdelphij			     instance->mode == MODE_2D,
3364285612Sdelphij			     instance->mode == MODE_3D,
3365285612Sdelphij			     instance->rsm.bad_almanac,
3366285612Sdelphij			     instance->rsm.bad_fix);
3367285612Sdelphij		}
336854359Sroberto#endif
3369132451Sroberto	}
3370132451Sroberto}
337154359Sroberto
337254359Sroberto
337354359Sroberto
3374132451Sroberto/* check the antenna for changes (did it get unplugged?) */
337554359Sroberto
3376132451Srobertostatic void
3377132451Srobertooncore_check_antenna(
3378132451Sroberto	struct instance *instance
3379132451Sroberto	)
3380132451Sroberto{
3381132451Sroberto	enum antenna_state antenna;		/* antenna state */
338282498Sroberto
3383132451Sroberto	if (instance->chan == 12)
3384132451Sroberto		antenna = (instance->BEHa[130] & 0x6 ) >> 1;
3385132451Sroberto	else
3386132451Sroberto		antenna = (instance->BEHa[37] & 0xc0) >> 6;  /* prob unset 6, set GT, UT unset VP */
338754359Sroberto
3388132451Sroberto	oncore_antenna_report (instance, antenna);
3389132451Sroberto}
3390132451Sroberto
3391132451Sroberto
3392132451Sroberto
3393132451Sroberto/*
3394132451Sroberto * Check the leap second status once per day.
3395132451Sroberto *
3396132451Sroberto * Note that the ONCORE firmware for the Bj command is wrong at
3397132451Sroberto * least in the VP.
3398132451Sroberto * It starts advertising a LEAP SECOND as soon as the GPS satellite
3399132451Sroberto * data message (page 18, subframe 4) is updated to a date in the
3400132451Sroberto * future, and does not wait for the month that it will occur.
3401132451Sroberto * The event will usually be advertised several months in advance.
3402132451Sroberto * Since there is a one bit flag, there is no way to tell if it is
3403132451Sroberto * this month, or when...
3404132451Sroberto *
3405132451Sroberto * As such, we have the workaround below, of only checking for leap
3406132451Sroberto * seconds with the Bj command in June/December.
3407132451Sroberto *
3408132451Sroberto * The Gj command gives more information, and we can tell in which
3409132451Sroberto * month to apply the correction.
3410132451Sroberto *
3411132451Sroberto * Note that with the VP we COULD read the raw data message, and
3412132451Sroberto * interpret it ourselves, but since this is specific to this receiver
3413132451Sroberto * only, and the above workaround is adequate, we don't bother.
3414132451Sroberto */
3415132451Sroberto
3416132451Srobertostatic void
3417132451Srobertooncore_check_leap_sec(
3418132451Sroberto	struct instance *instance
3419132451Sroberto	)
3420132451Sroberto{
3421285612Sdelphij	oncore_cmd_Bl[2] = 1;				/* just to be sure */
3422285612Sdelphij	if (instance->Bj_day != instance->BEHa[5]) {	/* do this 1/day */
3423132451Sroberto		instance->Bj_day = instance->BEHa[5];
3424132451Sroberto
3425132451Sroberto		if (instance->saw_Gj < 0) {	/* -1 DONT have Gj use Bj */
3426132451Sroberto			if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
3427285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3428285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl));
3429132451Sroberto			return;
3430132451Sroberto		}
3431132451Sroberto
3432132451Sroberto		if (instance->saw_Gj == 0)	/* 0 is dont know if we have Gj */
3433132451Sroberto			instance->count4 = 1;
3434132451Sroberto
3435285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
343654359Sroberto		return;
343754359Sroberto	}
343854359Sroberto
3439132451Sroberto	/* Gj works for some 6/8 chan UT and the M12	  */
3440132451Sroberto	/* if no response from Gj in 5 sec, we try Bj	  */
3441132451Sroberto	/* which isnt implemented in all the GT/UT either */
344254359Sroberto
3443132451Sroberto	if (instance->count4) { 	/* delay, waiting for Gj response */
3444132451Sroberto		if (instance->saw_Gj == 1)
3445132451Sroberto			instance->count4 = 0;
3446132451Sroberto		else if (instance->count4++ > 5) {	/* delay, waiting for Gj response */
3447132451Sroberto			instance->saw_Gj = -1;		/* didnt see it, will use Bj */
3448132451Sroberto			instance->count4 = 0;
3449285612Sdelphij			if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) {
3450285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3451285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl));
3452285612Sdelphij			}
3453132451Sroberto		}
345454359Sroberto	}
345554359Sroberto}
345654359Sroberto
345754359Sroberto
345854359Sroberto
3459132451Sroberto/* check the message checksum,
3460132451Sroberto *  buf points to START of message ( @@ )
3461132451Sroberto *  len is length WITH CR/LF.
346254359Sroberto */
346382498Sroberto
3464132451Srobertostatic int
3465132451Srobertooncore_checksum_ok(
346654359Sroberto	u_char *buf,
3467132451Sroberto	int	len
346854359Sroberto	)
346954359Sroberto{
3470132451Sroberto	int	i, j;
347154359Sroberto
3472132451Sroberto	j = 0;
3473132451Sroberto	for (i = 2; i < len-3; i++)
3474132451Sroberto		j ^= buf[i];
347554359Sroberto
3476132451Sroberto	return(j == buf[len-3]);
3477132451Sroberto}
347854359Sroberto
347954359Sroberto
348054359Sroberto
348154359Srobertostatic void
3482132451Srobertooncore_compute_dH(
3483132451Sroberto	struct instance *instance
348454359Sroberto	)
348554359Sroberto{
3486132451Sroberto	int GPS, MSL;
348754359Sroberto
3488132451Sroberto	/* Here calculate dH = GPS - MSL for output message */
3489132451Sroberto	/* also set Altitude Hold mode if GT */
3490132451Sroberto
3491132451Sroberto	instance->have_dH = 1;
3492132451Sroberto	if (instance->chan == 12) {
3493132451Sroberto		GPS = buf_w32(&instance->BEHa[39]);
3494132451Sroberto		MSL = buf_w32(&instance->BEHa[43]);
3495132451Sroberto	} else {
3496132451Sroberto		GPS = buf_w32(&instance->BEHa[23]);
3497132451Sroberto		MSL = buf_w32(&instance->BEHa[27]);
349854359Sroberto	}
3499132451Sroberto	instance->dH = GPS - MSL;
3500132451Sroberto	instance->dH /= 100.;
3501132451Sroberto
3502132451Sroberto	/* if MSL is not set, the calculation is meaningless */
3503132451Sroberto
3504285612Sdelphij	if (MSL)	/* not set ! */
3505285612Sdelphij		oncore_log_f(instance, LOG_INFO,
3506285612Sdelphij		             "dH = (GPS - MSL) = %.2fm", instance->dH);
350754359Sroberto}
350854359Sroberto
350954359Sroberto
3510132451Sroberto
3511132451Sroberto/*
3512132451Sroberto * try loading Almanac from shmem (where it was copied from shmem_old
3513132451Sroberto */
3514132451Sroberto
351582498Srobertostatic void
3516132451Srobertooncore_load_almanac(
3517132451Sroberto	struct instance *instance
351882498Sroberto	)
351982498Sroberto{
3520132451Sroberto	u_char	*cp, Cmd[20];
3521132451Sroberto	int	n;
3522132451Sroberto	struct timeval tv;
3523132451Sroberto	struct tm *tm;
352454359Sroberto
3525132451Sroberto	if (!instance->shmem)
3526132451Sroberto		return;
352782498Sroberto
3528285612Sdelphij#ifndef ONCORE_VERBOSE_LOAD_ALMANAC
3529285612Sdelphij	for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
3530285612Sdelphij	     cp += (n + 3)) {
3531182007Sroberto		if (!strncmp((char *) cp, "@@Cb", 4) &&
3532132451Sroberto		    oncore_checksum_ok(cp, 33) &&
3533132451Sroberto		    (*(cp+4) == 4 || *(cp+4) == 5)) {
3534132451Sroberto			write(instance->ttyfd, cp, n);
3535132451Sroberto			oncore_print_Cb(instance, cp);
3536132451Sroberto		}
3537132451Sroberto	}
3538285612Sdelphij#else	/* ONCORE_VERBOSE_LOAD_ALMANAC follows */
3539285612Sdelphij	for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
3540285612Sdelphij	     cp += (n+3)) {
3541285612Sdelphij		oncore_log_f(instance, LOG_DEBUG, "See %c%c%c%c %d",
3542285612Sdelphij			   *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4));
3543132451Sroberto
3544132451Sroberto		if (!strncmp(cp, "@@Cb", 4)) {
3545132451Sroberto			oncore_print_Cb(instance, cp);
3546132451Sroberto			if (oncore_checksum_ok(cp, 33)) {
3547132451Sroberto				if (*(cp+4) == 4 || *(cp+4) == 5) {
3548285612Sdelphij					oncore_log(instance, LOG_DEBUG, "GOOD SF");
3549132451Sroberto					write(instance->ttyfd, cp, n);
3550132451Sroberto				} else
3551285612Sdelphij					oncore_log(instance, LOG_DEBUG, "BAD SF");
3552132451Sroberto			} else
3553285612Sdelphij				oncore_log(instance, LOG_DEBUG, "BAD CHECKSUM");
3554132451Sroberto		}
355582498Sroberto	}
3556132451Sroberto#endif
355782498Sroberto
3558132451Sroberto	/* Must load position and time or the Almanac doesn't do us any good */
355982498Sroberto
3560132451Sroberto	if (!instance->posn_set) {	/* if we input a posn use it, else from SHMEM */
3561285612Sdelphij		oncore_log(instance, LOG_NOTICE, "Loading Posn from SHMEM");
3562132451Sroberto		for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2));  cp+=(n+3)) {
3563182007Sroberto			if ((instance->chan == 6  && (!strncmp((char *) cp, "@@Ba", 4) && oncore_checksum_ok(cp,  68))) ||
3564182007Sroberto			    (instance->chan == 8  && (!strncmp((char *) cp, "@@Ea", 4) && oncore_checksum_ok(cp,  76))) ||
3565182007Sroberto			    (instance->chan == 12 && (!strncmp((char *) cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) {
3566132451Sroberto				int ii, jj, kk;
356782498Sroberto
3568132451Sroberto				instance->posn_set = 1;
3569132451Sroberto				ii = buf_w32(cp + 15);
3570132451Sroberto				jj = buf_w32(cp + 19);
3571132451Sroberto				kk = buf_w32(cp + 23);
3572285612Sdelphij#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
3573285612Sdelphij				oncore_log_f(instance, LOG_DEBUG,
3574285612Sdelphij					     "SHMEM posn = %ld (%d, %d, %d)",
3575285612Sdelphij					     (long)(cp-instance->shmem),
3576285612Sdelphij					     ii, jj, kk);
3577182007Sroberto#endif
3578132451Sroberto				if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */
3579132451Sroberto					instance->ss_lat  = ii;
3580132451Sroberto					instance->ss_long = jj;
3581132451Sroberto					instance->ss_ht   = kk;
3582132451Sroberto				}
358382498Sroberto			}
358482498Sroberto		}
358582498Sroberto	}
3586132451Sroberto	oncore_set_posn(instance);
3587132451Sroberto
3588132451Sroberto	/* and set time to time from Computer clock */
3589132451Sroberto
3590285612Sdelphij	GETTIMEOFDAY(&tv, 0);
3591132451Sroberto	tm = gmtime((const time_t *) &tv.tv_sec);
3592285612Sdelphij
3593285612Sdelphij#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
3594285612Sdelphij	oncore_log_f(instance, LOG_DEBUG, "DATE %d %d %d, %d %d %d",
3595285612Sdelphij		     1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
3596285612Sdelphij		     tm->tm_hour, tm->tm_min, tm->tm_sec);
3597132451Sroberto#endif
3598132451Sroberto	if (instance->chan == 12) {
3599132451Sroberto		memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb));
3600182007Sroberto		Cmd[-2+4]  = tm->tm_mon + 1;
3601132451Sroberto		Cmd[-2+5]  = tm->tm_mday;
3602132451Sroberto		Cmd[-2+6]  = (1900+tm->tm_year)/256;
3603132451Sroberto		Cmd[-2+7]  = (1900+tm->tm_year)%256;
3604132451Sroberto		Cmd[-2+8]  = tm->tm_hour;
3605132451Sroberto		Cmd[-2+9]  = tm->tm_min;
3606132451Sroberto		Cmd[-2+10] = tm->tm_sec;
3607132451Sroberto		Cmd[-2+11] = 0;
3608132451Sroberto		Cmd[-2+12] = 0;
3609132451Sroberto		Cmd[-2+13] = 0;
3610285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Gb));
3611132451Sroberto	} else {
3612132451Sroberto		/* First set GMT offset to zero */
3613132451Sroberto
3614285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Ab, sizeof(oncore_cmd_Ab));
3615132451Sroberto
3616132451Sroberto		memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac));
3617182007Sroberto		Cmd[-2+4] = tm->tm_mon + 1;
3618132451Sroberto		Cmd[-2+5] = tm->tm_mday;
3619132451Sroberto		Cmd[-2+6] = (1900+tm->tm_year)/256;
3620132451Sroberto		Cmd[-2+7] = (1900+tm->tm_year)%256;
3621285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ac));
3622132451Sroberto
3623132451Sroberto		memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa));
3624132451Sroberto		Cmd[-2+4] = tm->tm_hour;
3625132451Sroberto		Cmd[-2+5] = tm->tm_min;
3626132451Sroberto		Cmd[-2+6] = tm->tm_sec;
3627285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Aa));
3628132451Sroberto	}
3629132451Sroberto
3630285612Sdelphij	oncore_log(instance, LOG_INFO, "Setting Posn and Time after Loading Almanac");
363182498Sroberto}
363282498Sroberto
363382498Sroberto
363482498Sroberto
3635132451Sroberto/* Almanac data input */
363682498Sroberto
363754359Srobertostatic void
3638132451Srobertooncore_print_Cb(
363954359Sroberto	struct instance *instance,
3640132451Sroberto	u_char *cp
364154359Sroberto	)
364254359Sroberto{
3643285612Sdelphij#ifdef ONCORE_VERBOSE_CB
3644132451Sroberto	int	ii;
3645285612Sdelphij	char	Msg[160], Msg2[10];
364654359Sroberto
3647285612Sdelphij	oncore_log_f(instance, LOG_DEBUG, "DEBUG: See: %c%c%c%c", *(cp),
3648285612Sdelphij		     *(cp+1), *(cp+2), *(cp+3));
364954359Sroberto
3650285612Sdelphij	snprintf(Msg, sizeof(Msg), "DEBUG: Cb: [%d,%d]", *(cp+4),
3651285612Sdelphij		*(cp+5));
3652285612Sdelphij	for (ii = 0; ii < 33; ii++) {
3653285612Sdelphij		snprintf(Msg2, sizeof(Msg2), " %d", *(cp+ii));
3654285612Sdelphij		strlcat(Msg, Msg2, sizeof(Msg));
3655285612Sdelphij	}
3656285612Sdelphij	oncore_log(instance, LOG_DEBUG, Msg);
3657285612Sdelphij
3658285612Sdelphij	oncore_log_f(instance, LOG_DEBUG, "Debug: Cb: [%d,%d]", *(cp+4),
3659285612Sdelphij		     *(cp+5));
3660132451Sroberto#endif
3661132451Sroberto}
366254359Sroberto
3663132451Sroberto
3664132451Sroberto#if 0
3665132451Srobertostatic void
3666132451Srobertooncore_print_array(
3667132451Sroberto	u_char *cp,
3668132451Sroberto	int	n
3669132451Sroberto	)
3670132451Sroberto{
3671132451Sroberto	int	jj, i, j, nn;
3672132451Sroberto
3673132451Sroberto	nn = 0;
3674132451Sroberto	printf("\nTOP\n");
3675132451Sroberto	jj = n/16;
3676132451Sroberto	for (j=0; j<jj; j++) {
3677132451Sroberto		printf("%4d: ", nn);
3678132451Sroberto		nn += 16;
3679132451Sroberto		for (i=0; i<16; i++)
3680132451Sroberto			printf(" %o", *cp++);
3681132451Sroberto		printf("\n");
3682132451Sroberto	}
368382498Sroberto}
3684132451Sroberto#endif
368554359Sroberto
368654359Sroberto
368782498Srobertostatic void
3688132451Srobertooncore_print_posn(
368982498Sroberto	struct instance *instance
369082498Sroberto	)
369182498Sroberto{
3692285612Sdelphij	char ew, ns;
369382498Sroberto	double xd, xm, xs, yd, ym, ys, hm, hft;
369482498Sroberto	int idx, idy, is, imx, imy;
369582498Sroberto	long lat, lon;
369654359Sroberto
3697285612Sdelphij	oncore_log(instance, LOG_INFO, "Posn:");
369854359Sroberto	ew = 'E';
369954359Sroberto	lon = instance->ss_long;
370054359Sroberto	if (lon < 0) {
370154359Sroberto		ew = 'W';
370254359Sroberto		lon = -lon;
370354359Sroberto	}
370454359Sroberto
370554359Sroberto	ns = 'N';
370654359Sroberto	lat = instance->ss_lat;
370754359Sroberto	if (lat < 0) {
370854359Sroberto		ns = 'S';
370954359Sroberto		lat = -lat;
371054359Sroberto	}
371154359Sroberto
371254359Sroberto	hm = instance->ss_ht/100.;
371354359Sroberto	hft= hm/0.3048;
371454359Sroberto
371554359Sroberto	xd = lat/3600000.;	/* lat, lon in int msec arc, ht in cm. */
371654359Sroberto	yd = lon/3600000.;
3717285612Sdelphij	oncore_log_f(instance, LOG_INFO,
3718285612Sdelphij		     "Lat = %c %11.7fdeg,    Long = %c %11.7fdeg,    Alt = %5.2fm (%5.2fft) GPS",
3719285612Sdelphij		     ns, xd, ew, yd, hm, hft);
372054359Sroberto
372154359Sroberto	idx = xd;
372254359Sroberto	idy = yd;
372354359Sroberto	imx = lat%3600000;
372454359Sroberto	imy = lon%3600000;
372554359Sroberto	xm = imx/60000.;
372654359Sroberto	ym = imy/60000.;
3727285612Sdelphij	oncore_log_f(instance, LOG_INFO,
3728285612Sdelphij		     "Lat = %c %3ddeg %7.4fm,   Long = %c %3ddeg %8.5fm,  Alt = %7.2fm (%7.2fft) GPS",
3729285612Sdelphij		     ns, idx, xm, ew, idy, ym, hm, hft);
373054359Sroberto
373154359Sroberto	imx = xm;
373254359Sroberto	imy = ym;
373354359Sroberto	is  = lat%60000;
373454359Sroberto	xs  = is/1000.;
373554359Sroberto	is  = lon%60000;
373654359Sroberto	ys  = is/1000.;
3737285612Sdelphij	oncore_log_f(instance, LOG_INFO,
3738285612Sdelphij		     "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS",
3739285612Sdelphij		     ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
374056746Sroberto}
374154359Sroberto
374254359Sroberto
374354359Sroberto
374456746Sroberto/*
3745132451Sroberto * write message to Oncore.
374656746Sroberto */
374782498Sroberto
374856746Srobertostatic void
3749132451Srobertooncore_sendmsg(
3750285612Sdelphij	struct	instance *instance,
3751132451Sroberto	u_char *ptr,
375282498Sroberto	size_t len
375356746Sroberto	)
375456746Sroberto{
3755285612Sdelphij	int	fd;
3756132451Sroberto	u_char cs = 0;
375756746Sroberto
3758285612Sdelphij	fd = instance->ttyfd;
3759285612Sdelphij#ifdef ONCORE_VERBOSE_SENDMSG
3760285612Sdelphij	if (debug > 4) {
3761285612Sdelphij		oncore_log_f(instance, LOG_DEBUG, "ONCORE: Send @@%c%c %d",
3762285612Sdelphij			     ptr[0], ptr[1], (int)len);
3763285612Sdelphij	}
3764182007Sroberto#endif
3765132451Sroberto	write(fd, "@@", (size_t) 2);
3766132451Sroberto	write(fd, ptr, len);
3767132451Sroberto	while (len--)
3768132451Sroberto		cs ^= *ptr++;
3769132451Sroberto	write(fd, &cs, (size_t) 1);
3770132451Sroberto	write(fd, "\r\n", (size_t) 2);
3771132451Sroberto}
377256746Sroberto
377356746Sroberto
377456746Sroberto
3775132451Srobertostatic void
3776132451Srobertooncore_set_posn(
3777132451Sroberto	struct instance *instance
3778132451Sroberto	)
3779132451Sroberto{
3780132451Sroberto	int	mode;
3781182007Sroberto	u_char	  Cmd[20];
3782132451Sroberto
3783132451Sroberto	/* Turn OFF position hold, it needs to be off to set position (for some units),
3784132451Sroberto	   will get set ON in @@Ea later */
3785132451Sroberto
3786132451Sroberto	if (instance->chan == 12)
3787285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
3788132451Sroberto	else {
3789285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
3790285612Sdelphij		oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */
3791132451Sroberto	}
3792132451Sroberto
3793132451Sroberto	mode = instance->init_type;
3794132451Sroberto
3795132451Sroberto	if (mode != 0) {	/* first set posn hold position */
3796132451Sroberto		memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As));	/* don't modify static variables */
3797132451Sroberto		w32_buf(&Cmd[-2+4],  (int) instance->ss_lat);
3798132451Sroberto		w32_buf(&Cmd[-2+8],  (int) instance->ss_long);
3799132451Sroberto		w32_buf(&Cmd[-2+12], (int) instance->ss_ht);
3800132451Sroberto		Cmd[-2+16] = 0;
3801285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_As));	/* posn hold 3D posn (6/8/12) */
3802132451Sroberto
3803132451Sroberto		memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au));
3804132451Sroberto		w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
3805132451Sroberto		Cmd[-2+8] = 0;
3806285612Sdelphij		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Au));	/* altitude hold (6/8/12 not UT, M12T) */
3807132451Sroberto
3808132451Sroberto		/* next set current position */
3809132451Sroberto
3810132451Sroberto		if (instance->chan == 12) {
3811132451Sroberto			memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga));
3812132451Sroberto			w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
3813132451Sroberto			w32_buf(&Cmd[-2+8], (int) instance->ss_long);
3814132451Sroberto			w32_buf(&Cmd[-2+12],(int) instance->ss_ht);
3815132451Sroberto			Cmd[-2+16] = 0;
3816285612Sdelphij			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ga));		  /* 3d posn (12) */
3817132451Sroberto		} else {
3818132451Sroberto			memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad));
3819132451Sroberto			w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
3820285612Sdelphij			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ad));	/* lat (6/8) */
3821132451Sroberto
3822132451Sroberto			memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae));
3823132451Sroberto			w32_buf(&Cmd[-2+4], (int) instance->ss_long);
3824285612Sdelphij			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ae));	/* long (6/8) */
3825132451Sroberto
3826132451Sroberto			memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af));
3827132451Sroberto			w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
3828132451Sroberto			Cmd[-2+8] = 0;
3829285612Sdelphij			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Af));	/* ht (6/8) */
3830132451Sroberto		}
3831132451Sroberto
3832132451Sroberto		/* Finally, turn on position hold */
3833132451Sroberto
3834132451Sroberto		if (instance->chan == 12)
3835285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Gd1,  sizeof(oncore_cmd_Gd1));
3836132451Sroberto		else
3837285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_At1,  sizeof(oncore_cmd_At1));
3838132451Sroberto	}
3839132451Sroberto}
3840132451Sroberto
3841132451Sroberto
3842132451Sroberto
3843132451Srobertostatic void
3844132451Srobertooncore_set_traim(
3845132451Sroberto	struct instance *instance
3846132451Sroberto	)
3847132451Sroberto{
3848132451Sroberto	if (instance->traim_in != -1)	/* set in Input */
3849132451Sroberto		instance->traim = instance->traim_in;
3850132451Sroberto	else
3851132451Sroberto		instance->traim = instance->traim_ck;
3852132451Sroberto
3853285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Input   says TRAIM = %d",
3854285612Sdelphij		     instance->traim_in);
3855285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Model # says TRAIM = %d",
3856285612Sdelphij		     instance->traim_id);
3857285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Testing says TRAIM = %d",
3858285612Sdelphij		     instance->traim_ck);
3859285612Sdelphij	oncore_log_f(instance, LOG_INFO, "Using        TRAIM = %d",
3860285612Sdelphij		     instance->traim);
3861132451Sroberto
3862132451Sroberto	if (instance->traim_ck == 1 && instance->traim == 0) {
3863132451Sroberto		/* if it should be off, and I turned it on during testing,
3864132451Sroberto		   then turn it off again */
3865132451Sroberto		if (instance->chan == 6)
3866285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
3867132451Sroberto		else if (instance->chan == 8)
3868285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
3869132451Sroberto		else	/* chan == 12 */
3870285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
3871285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
3872132451Sroberto	}
387356746Sroberto}
387454359Sroberto
387556746Sroberto
387656746Sroberto
387756746Sroberto/*
3878132451Sroberto * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
387956746Sroberto */
388082498Sroberto
388156746Srobertostatic void
3882132451Srobertooncore_shmem_get_3D(
3883132451Sroberto	struct instance *instance
388456746Sroberto	)
388556746Sroberto{
3886132451Sroberto	if (instance->pp->second%15 == 3) {	/* start the sequence */			/* by changing mode */
3887132451Sroberto		instance->shmem_reset = 1;
3888132451Sroberto		if (instance->chan == 12) {
3889132451Sroberto			if (instance->shmem_Posn == 2)
3890285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Gd2,  sizeof(oncore_cmd_Gd2));  /* 2D */
3891132451Sroberto			else
3892285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Gd0,  sizeof(oncore_cmd_Gd0));  /* 3D */
3893132451Sroberto		} else {
3894132451Sroberto			if (instance->saw_At) { 		/* out of 0D -> 3D mode */
3895285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0));
3896132451Sroberto				if (instance->shmem_Posn == 2)	/* 3D -> 2D mode */
3897285612Sdelphij					oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
3898132451Sroberto			} else
3899285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
3900132451Sroberto		}
3901132451Sroberto	} else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
3902132451Sroberto		instance->shmem_reset = 0;
3903132451Sroberto		if (instance->chan == 12)
3904285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Gd1,  sizeof(oncore_cmd_Gd1));	/* 0D */
3905132451Sroberto		else {
3906132451Sroberto			if (instance->saw_At) {
3907132451Sroberto				if (instance->mode == MODE_2D)	/* 2D -> 3D or 0D mode */
3908285612Sdelphij					oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
3909285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_At1,  sizeof(oncore_cmd_At1)); /* to 0D mode */
3910132451Sroberto			} else
3911285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Av1,  sizeof(oncore_cmd_Av1));
3912132451Sroberto		}
3913132451Sroberto	}
3914132451Sroberto}
391556746Sroberto
391656746Sroberto
391756746Sroberto
3918132451Sroberto/*
3919132451Sroberto * Here we do the Software SiteSurvey.
3920132451Sroberto * We have to average our own position for the Position Hold Mode
3921132451Sroberto *   We use Heights from the GPS ellipsoid.
3922132451Sroberto * We check for the END of either HW or SW SiteSurvey.
3923132451Sroberto */
392456746Sroberto
392582498Srobertostatic void
3926132451Srobertooncore_ss(
3927132451Sroberto	struct instance *instance
392882498Sroberto	)
392982498Sroberto{
3930132451Sroberto	double	lat, lon, ht;
393182498Sroberto
3932132451Sroberto
3933132451Sroberto	if (instance->site_survey == ONCORE_SS_HW) {
3934132451Sroberto		/*
3935132451Sroberto		 * Check to see if Hardware SiteSurvey has Finished.
3936132451Sroberto		 */
3937132451Sroberto
3938132451Sroberto		if ((instance->chan == 8  && !(instance->BEHa[37]  & 0x20)) ||
3939132451Sroberto		    (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) {
3940285612Sdelphij			oncore_log(instance, LOG_INFO, "Now in 0D mode");
3941132451Sroberto
3942132451Sroberto			if (instance->chan == 12)
3943285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
3944132451Sroberto			else
3945285612Sdelphij				oncore_sendmsg(instance, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
3946132451Sroberto
3947285612Sdelphij			oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
3948132451Sroberto			instance->site_survey = ONCORE_SS_DONE;
3949132451Sroberto		}
3950132451Sroberto	} else {
3951132451Sroberto		/*
3952132451Sroberto		 * Must be a Software Site Survey.
3953132451Sroberto		 */
3954132451Sroberto
3955132451Sroberto		if (instance->rsm.bad_fix)	/* Not if poor geometry or less than 3 sats */
3956132451Sroberto			return;
3957132451Sroberto
3958132451Sroberto		if (instance->mode != MODE_3D)	/* Use only 3D Fixes */
3959132451Sroberto			return;
3960132451Sroberto
3961132451Sroberto		instance->ss_lat  += buf_w32(&instance->BEHa[15]);
3962132451Sroberto		instance->ss_long += buf_w32(&instance->BEHa[19]);
3963132451Sroberto		instance->ss_ht   += buf_w32(&instance->BEHa[23]);  /* GPS ellipsoid */
3964132451Sroberto		instance->ss_count++;
3965132451Sroberto
3966132451Sroberto		if (instance->ss_count != POS_HOLD_AVERAGE)
3967132451Sroberto			return;
3968132451Sroberto
3969132451Sroberto		instance->ss_lat  /= POS_HOLD_AVERAGE;
3970132451Sroberto		instance->ss_long /= POS_HOLD_AVERAGE;
3971132451Sroberto		instance->ss_ht   /= POS_HOLD_AVERAGE;
3972132451Sroberto
3973285612Sdelphij		oncore_log_f(instance, LOG_NOTICE,
3974285612Sdelphij			     "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
3975285612Sdelphij			     instance->ss_lat, instance->ss_long,
3976285612Sdelphij			     instance->ss_ht);
3977132451Sroberto		lat = instance->ss_lat/3600000.;
3978132451Sroberto		lon = instance->ss_long/3600000.;
3979132451Sroberto		ht  = instance->ss_ht/100;
3980285612Sdelphij		oncore_log_f(instance, LOG_NOTICE,
3981285612Sdelphij			     "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
3982285612Sdelphij			     lat, lon, ht);
3983132451Sroberto
3984132451Sroberto		oncore_set_posn(instance);
3985132451Sroberto
3986285612Sdelphij		oncore_log(instance, LOG_INFO, "Now in 0D mode");
3987132451Sroberto
3988285612Sdelphij		oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
3989132451Sroberto		instance->site_survey = ONCORE_SS_DONE;
399082498Sroberto	}
399182498Sroberto}
399282498Sroberto
3993132451Sroberto
3994132451Sroberto
3995132451Srobertostatic int
3996132451Srobertooncore_wait_almanac(
3997132451Sroberto	struct instance *instance
3998132451Sroberto	)
3999132451Sroberto{
4000132451Sroberto	if (instance->rsm.bad_almanac) {
4001285612Sdelphij		instance->counta++;
4002285612Sdelphij		if (instance->counta%5 == 0)
4003285612Sdelphij			oncore_log(instance, LOG_INFO, "Waiting for Almanac");
4004132451Sroberto
4005132451Sroberto		/*
4006132451Sroberto		 * If we get here (first time) then we don't have an almanac in memory.
4007132451Sroberto		 * Check if we have a SHMEM, and if so try to load whatever is there.
4008132451Sroberto		 */
4009132451Sroberto
4010132451Sroberto		if (!instance->almanac_from_shmem) {
4011132451Sroberto			instance->almanac_from_shmem = 1;
4012132451Sroberto			oncore_load_almanac(instance);
4013132451Sroberto		}
4014132451Sroberto		return(1);
4015132451Sroberto	} else {  /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn
4016132451Sroberto		     commands, and can finally check for TRAIM.  Again, we set a delay
4017132451Sroberto		     (5sec) and wait for things to settle down */
4018132451Sroberto
4019132451Sroberto		if (instance->chan == 6)
4020285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
4021132451Sroberto		else if (instance->chan == 8)
4022285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_En, sizeof(oncore_cmd_En));
4023132451Sroberto		else if (instance->chan == 12) {
4024285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */
4025285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */
4026285612Sdelphij			oncore_sendmsg(instance, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */
4027132451Sroberto		}
4028132451Sroberto		instance->traim_delay = 1;
4029132451Sroberto
4030285612Sdelphij		oncore_log(instance, LOG_NOTICE, "Have now loaded an ALMANAC");
4031132451Sroberto
4032132451Sroberto		instance->o_state = ONCORE_RUN;
4033285612Sdelphij		oncore_log(instance, LOG_NOTICE, "state = ONCORE_RUN");
4034132451Sroberto	}
4035132451Sroberto	return(0);
4036132451Sroberto}
4037132451Sroberto
4038132451Sroberto
4039132451Sroberto
4040285612Sdelphijstatic void
4041285612Sdelphijoncore_log (
4042285612Sdelphij	struct instance *instance,
4043285612Sdelphij	int log_level,
4044285612Sdelphij	const char *msg
4045285612Sdelphij	)
4046285612Sdelphij{
4047285612Sdelphij	msyslog(log_level, "ONCORE[%d]: %s", instance->unit, msg);
4048285612Sdelphij	mprintf_clock_stats(&instance->peer->srcadr, "ONCORE[%d]: %s",
4049285612Sdelphij			    instance->unit, msg);
4050285612Sdelphij}
4051285612Sdelphij
4052285612Sdelphij
4053285612Sdelphijstatic int
4054285612Sdelphijoncore_log_f(
4055285612Sdelphij	struct instance *	instance,
4056285612Sdelphij	int			log_level,
4057285612Sdelphij	const char *		fmt,
4058285612Sdelphij	...
4059285612Sdelphij	)
4060285612Sdelphij{
4061285612Sdelphij	va_list	ap;
4062285612Sdelphij	int	rc;
4063285612Sdelphij	char	msg[512];
4064285612Sdelphij
4065285612Sdelphij	va_start(ap, fmt);
4066285612Sdelphij	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
4067285612Sdelphij	va_end(ap);
4068285612Sdelphij	oncore_log(instance, log_level, msg);
4069285612Sdelphij
4070285612Sdelphij#ifdef ONCORE_VERBOSE_ONCORE_LOG
4071285612Sdelphij	instance->max_len = max(strlen(msg), instance->max_len);
4072285612Sdelphij	instance->max_count++;
4073285612Sdelphij	if (instance->max_count % 100 == 0)
4074285612Sdelphij		oncore_log_f(instance, LOG_INFO,
4075285612Sdelphij			    "Max Message Length so far is %d",
4076285612Sdelphij			    instance->max_len);
4077285612Sdelphij#endif
4078285612Sdelphij	return rc;
4079285612Sdelphij}
4080285612Sdelphij
408154359Sroberto#else
408254359Srobertoint refclock_oncore_bs;
4083285612Sdelphij#endif	/* REFCLOCK && CLOCK_ONCORE */
4084