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
14290001Sglebius *	   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 * --------------------------------------------------------------------------
55290001Sglebius * Reg Clemens (June 2009)
56290001Sglebius * BUG[1220] OK, big patch, but mostly done mechanically.  Change direct calls to write
57290001Sglebius * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog.
58290001Sglebius * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New
59290001Sglebius * routine oncore_log.
60290001Sglebius * --------------------------------------------------------------------------
61290001Sglebius * Reg Clemens (June 2009)
62290001Sglebius * BUG[1218] The comment on where the oncore driver gets its input file does not
63290001Sglebius * agree with the code.  Change the comment.
64290001Sglebius * --------------------------------------------------------------------------
65290001Sglebius * Reg Clemens (June 2009)
66290001Sglebius * change exit statements to return(0) in main program.  I had assumed that if the
67290001Sglebius * PPS driver did not start for some reason, we shuould stop NTPD itelf.  Others
68290001Sglebius * disagree.  We now give an ERR log message and stop this driver.
69290001Sglebius * --------------------------------------------------------------------------
70290001Sglebius * Reg Clemens (June 2009)
71290001Sglebius * A bytes available message for the input subsystem (Debug message).
72290001Sglebius * --------------------------------------------------------------------------
73290001Sglebius * Reg Clemens (Nov 2008)
74290001Sglebius * This code adds a message for TRAIM messages.  Users often worry about the
75290001Sglebius * driver not starting up, and it is often because of signal strength being low.
76290001Sglebius * Low signal strength will give TRAIM messages.
77290001Sglebius * --------------------------------------------------------------------------
78290001Sglebius * Reg Clemens (Nov 2008)
79290001Sglebius * Add waiting on Almanac Message.
80290001Sglebius * --------------------------------------------------------------------------
81290001Sglebius * Reg Clemens (Nov 2008)
82290001Sglebius * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs
83290001Sglebius * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command
84290001Sglebius * that says 'Leap Pending'.  As documented it only becomes true in the month
85290001Sglebius * before the leap second is to be applied, but in practice at least some of
86290001Sglebius * the receivers turn this indicator on as soon as the message is posted, which
87290001Sglebius * can be 6months early.  As such, we use the Bj command to turn on the
88290001Sglebius * instance->pp->leap indicator but only run this test in December and June for
89290001Sglebius * updates on 1Jan and 1July.
90290001Sglebius *
91290001Sglebius * The @@Gj command exists in later ONCOREs, and it gives the exact date
92290001Sglebius * and size of the Leap Update.  It can be emulated in the VP using the @@Bl
93290001Sglebius * command which reads the raw Satellite Broadcast Messages.
94290001Sglebius * We use these two commands to print informative messages in the clockstats
95290001Sglebius * file once per day as soon as the message appears on the satellites.
96290001Sglebius * --------------------------------------------------------------------------
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"
164290001Sglebius#include "ntp_calendar.h"
16582498Sroberto#include "ntp_stdlib.h"
16682498Sroberto
16754359Sroberto#include <stdio.h>
168290001Sglebius#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
184290001Sglebiusstruct Bl {
185290001Sglebius	int	dt_ls;
186290001Sglebius	int	dt_lsf;
187290001Sglebius	int	WN;
188290001Sglebius	int	DN;
189290001Sglebius	int	WN_lsf;
190290001Sglebius	int	DN_lsf;
191290001Sglebius	int	wn_flg;
192290001Sglebius	int	lsf_flg;
193290001Sglebius	int	Bl_day;
194290001Sglebius} 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 */
320290001Sglebius	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 */
323290001Sglebius	u_char	max_len;	/* max length message seen by oncore_log, for debugging */
324290001Sglebius	u_char	max_count;	/* count for message statistics */
32582498Sroberto
32682498Sroberto	struct	RSM rsm;	/* bits extracted from Receiver Status Msg in @@Ea */
327290001Sglebius	struct	Bl Bl;		/* Satellite Broadcast Data Message */
32882498Sroberto	u_char	printed;
32982498Sroberto	u_char	polled;
330132451Sroberto	u_long	ev_serial;
331290001Sglebius	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;
340290001Sglebius	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;
354290001Sglebius	s_char	pps_control;	/* PPS control, M12 only */
355290001Sglebius	s_char	pps_control_msg_seen;
35654359Sroberto};
35754359Sroberto
35854359Sroberto#define rcvbuf	instance->Rcvbuf
35954359Sroberto#define rcvptr	instance->Rcvptr
36054359Sroberto
361290001Sglebiusstatic	int	oncore_start	      (int, struct peer *);
362290001Sglebiusstatic	void	oncore_poll	      (int, struct peer *);
363290001Sglebiusstatic	void	oncore_shutdown       (int, struct peer *);
364290001Sglebiusstatic	void	oncore_consume	      (struct instance *);
365290001Sglebiusstatic	void	oncore_read_config    (struct instance *);
366290001Sglebiusstatic	void	oncore_receive	      (struct recvbuf *);
367290001Sglebiusstatic	int	oncore_ppsapi	      (struct instance *);
368290001Sglebiusstatic	void	oncore_get_timestamp  (struct instance *, long, long);
369290001Sglebiusstatic	void	oncore_init_shmem     (struct instance *);
37054359Sroberto
371290001Sglebiusstatic	void	oncore_antenna_report (struct instance *, enum antenna_state);
372290001Sglebiusstatic	void	oncore_chan_test      (struct instance *);
373290001Sglebiusstatic	void	oncore_check_almanac  (struct instance *);
374290001Sglebiusstatic	void	oncore_check_antenna  (struct instance *);
375290001Sglebiusstatic	void	oncore_check_leap_sec (struct instance *);
376290001Sglebiusstatic	int	oncore_checksum_ok    (u_char *, int);
377290001Sglebiusstatic	void	oncore_compute_dH     (struct instance *);
378290001Sglebiusstatic	void	oncore_load_almanac   (struct instance *);
379290001Sglebiusstatic	void	oncore_log	      (struct instance *, int, const char *);
380290001Sglebiusstatic	int	oncore_log_f	      (struct instance *, int, const char *, ...)
381290001Sglebius		NTP_PRINTF(3, 4);
382290001Sglebiusstatic	void	oncore_print_Cb       (struct instance *, u_char *);
383290001Sglebius/* static  void    oncore_print_array	 (u_char *, int);	*/
384290001Sglebiusstatic	void	oncore_print_posn     (struct instance *);
385290001Sglebiusstatic	void	oncore_sendmsg	      (struct instance *, u_char *, size_t);
386290001Sglebiusstatic	void	oncore_set_posn       (struct instance *);
387290001Sglebiusstatic	void	oncore_set_traim      (struct instance *);
388290001Sglebiusstatic	void	oncore_shmem_get_3D   (struct instance *);
389290001Sglebiusstatic	void	oncore_ss	      (struct instance *);
390290001Sglebiusstatic	int	oncore_wait_almanac   (struct instance *);
391132451Sroberto
392290001Sglebiusstatic	void	oncore_msg_any	   (struct instance *, u_char *, size_t, int);
393290001Sglebiusstatic	void	oncore_msg_Adef    (struct instance *, u_char *, size_t);
394290001Sglebiusstatic	void	oncore_msg_Ag	   (struct instance *, u_char *, size_t);
395290001Sglebiusstatic	void	oncore_msg_As	   (struct instance *, u_char *, size_t);
396290001Sglebiusstatic	void	oncore_msg_At	   (struct instance *, u_char *, size_t);
397290001Sglebiusstatic	void	oncore_msg_Ay	   (struct instance *, u_char *, size_t);
398290001Sglebiusstatic	void	oncore_msg_Az	   (struct instance *, u_char *, size_t);
399290001Sglebiusstatic	void	oncore_msg_BaEaHa  (struct instance *, u_char *, size_t);
400290001Sglebiusstatic	void	oncore_msg_Bd	   (struct instance *, u_char *, size_t);
401290001Sglebiusstatic	void	oncore_msg_Bj	   (struct instance *, u_char *, size_t);
402290001Sglebiusstatic	void	oncore_msg_Bl	   (struct instance *, u_char *, size_t);
403290001Sglebiusstatic	void	oncore_msg_BnEnHn  (struct instance *, u_char *, size_t);
404290001Sglebiusstatic	void	oncore_msg_CaFaIa  (struct instance *, u_char *, size_t);
405290001Sglebiusstatic	void	oncore_msg_Cb	   (struct instance *, u_char *, size_t);
406290001Sglebiusstatic	void	oncore_msg_Cf	   (struct instance *, u_char *, size_t);
407290001Sglebiusstatic	void	oncore_msg_Cj	   (struct instance *, u_char *, size_t);
408290001Sglebiusstatic	void	oncore_msg_Cj_id   (struct instance *, u_char *, size_t);
409290001Sglebiusstatic	void	oncore_msg_Cj_init (struct instance *, u_char *, size_t);
410290001Sglebiusstatic	void	oncore_msg_Ga	   (struct instance *, u_char *, size_t);
411290001Sglebiusstatic	void	oncore_msg_Gb	   (struct instance *, u_char *, size_t);
412290001Sglebiusstatic	void	oncore_msg_Gc	   (struct instance *, u_char *, size_t);
413290001Sglebiusstatic	void	oncore_msg_Gj	   (struct instance *, u_char *, size_t);
414290001Sglebiusstatic	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;
434290001Sglebius	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 */
439290001Sglebius	{ "Ea",  76,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC", 0 },
440290001Sglebius	{ "Ba",  68,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC", 0 },
441290001Sglebius	{ "Ha", 154,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC", 0 },
442290001Sglebius	{ "Bn",  59,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC", 0 },
443290001Sglebius	{ "En",  69,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC", 0 },
444290001Sglebius	{ "Hn",  78,    oncore_msg_BnEnHn, "", 0 },
445290001Sglebius	{ "Ab",  10,    0,                 "", 0 },
446290001Sglebius	{ "Ac",  11,    0,                 "", 0 },
447290001Sglebius	{ "Ad",  11,    oncore_msg_Adef,   "", 0 },
448290001Sglebius	{ "Ae",  11,    oncore_msg_Adef,   "", 0 },
449290001Sglebius	{ "Af",  15,    oncore_msg_Adef,   "", 0 },
450290001Sglebius	{ "Ag",   8,    oncore_msg_Ag,     "", 0 }, /* Satellite mask angle */
451290001Sglebius	{ "As",  20,    oncore_msg_As,     "", 0 },
452290001Sglebius	{ "At",   8,    oncore_msg_At,     "", 0 },
453290001Sglebius	{ "Au",  12,    0,                 "", 0 },
454290001Sglebius	{ "Av",   8,    0,                 "", 0 },
455290001Sglebius	{ "Aw",   8,    0,                 "", 0 },
456290001Sglebius	{ "Ay",  11,    oncore_msg_Ay,     "", 0 },
457290001Sglebius	{ "Az",  11,    oncore_msg_Az,     "", 0 },
458290001Sglebius	{ "AB",   8,    0,                 "", 0 },
459290001Sglebius	{ "Bb",  92,    0,                 "", 0 },
460290001Sglebius	{ "Bd",  23,    oncore_msg_Bd,     "", 0 },
461290001Sglebius	{ "Bj",   8,    oncore_msg_Bj,     "", 0 },
462290001Sglebius	{ "Bl",  41,    oncore_msg_Bl,     "", 0 },
463290001Sglebius	{ "Ca",   9,    oncore_msg_CaFaIa, "", 0 },
464290001Sglebius	{ "Cb",  33,    oncore_msg_Cb,     "", 0 },
465290001Sglebius	{ "Cf",   7,    oncore_msg_Cf,     "", 0 },
466290001Sglebius	{ "Cg",   8,    0,                 "", 0 },
467290001Sglebius	{ "Ch",   9,    0,                 "", 0 },
468290001Sglebius	{ "Cj", 294,    oncore_msg_Cj,     "", 0 },
469290001Sglebius	{ "Ek",  71,    0,                 "", 0 },
470290001Sglebius	{ "Fa",   9,    oncore_msg_CaFaIa, "", 0 },
471290001Sglebius	{ "Ga",  20,    oncore_msg_Ga,     "", 0 },
472290001Sglebius	{ "Gb",  17,    oncore_msg_Gb,     "", 0 },
473290001Sglebius	{ "Gc",   8,    oncore_msg_Gc,     "", 0 },
474290001Sglebius	{ "Gd",   8,    0,                 "", 0 },
475290001Sglebius	{ "Ge",   8,    0,                 "", 0 },
476290001Sglebius	{ "Gj",  21,    oncore_msg_Gj,     "", 0 },
477290001Sglebius	{ "Ia",  10,    oncore_msg_CaFaIa, "", 0 },
478290001Sglebius	{ "Sz",   8,    oncore_msg_Sz,     "", 0 },
479290001Sglebius	{ {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			*/
514290001Sglebiusstatic 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				*/
534290001Sglebiusstatic 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;
597290001Sglebius	int fd1, fd2;
598290001Sglebius	char device1[STRING_LEN], device2[STRING_LEN];
599290001Sglebius#ifndef SYS_WINNT
60054359Sroberto	struct stat stat1, stat2;
601290001Sglebius#endif
60254359Sroberto
603132451Sroberto	/* create instance structure for this unit */
604132451Sroberto
605290001Sglebius	instance = emalloc(sizeof(*instance));
606290001Sglebius	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;
621290001Sglebius	instance->pps_control = -1;	/* PPS control, M12 only */
622290001Sglebius	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
629290001Sglebius	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
636290001Sglebius	oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING");
637182007Sroberto	instance->o_state = ONCORE_NO_IDEA;
638290001Sglebius	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
645290001Sglebius	(void)snprintf(device1, sizeof(device1), DEVICE1, unit);
646290001Sglebius	(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.
657290001Sglebius
658290001Sglebius	   For use with linuxPPS we assume that the N_TTY file has been opened
659290001Sglebius	     and that the line discipline has been changed to N_PPS by another
660290001Sglebius	     program (say ppsldisc) so that the two files expected by the oncore
661290001Sglebius	     driver can be opened.
662290001Sglebius
663290001Sglebius	   Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do
664290001Sglebius	     the stat below without error even though the file has already had its
665290001Sglebius	     line discipline changed by another process.
666290001Sglebius
667290001Sglebius	   The Windows port of ntpd arranges to return duplicate handles for
668290001Sglebius	     multiple opens of the same serial device, and doesn't have inodes
669290001Sglebius	     for serial handles, so we just open both on Windows.
670182007Sroberto	*/
671290001Sglebius#ifndef SYS_WINNT
672182007Sroberto	if (stat(device1, &stat1)) {
673290001Sglebius		oncore_log_f(instance, LOG_ERR, "Can't stat fd1 (%s)",
674290001Sglebius			     device1);
675290001Sglebius		return(0);			/* exit, no file, can't start driver */
67654359Sroberto	}
67754359Sroberto
678182007Sroberto	if (stat(device2, &stat2)) {
679290001Sglebius		stat2.st_dev = stat2.st_ino = -2;
680290001Sglebius		oncore_log_f(instance, LOG_ERR, "Can't stat fd2 (%s) %d %m",
681290001Sglebius			     device2, errno);
682182007Sroberto	}
683290001Sglebius#endif	/* !SYS_WINNT */
684132451Sroberto
685290001Sglebius	fd1 = refclock_open(device1, SPEED, LDISC_RAW);
686290001Sglebius	if (fd1 <= 0) {
687290001Sglebius		oncore_log_f(instance, LOG_ERR, "Can't open fd1 (%s)",
688290001Sglebius			     device1);
689290001Sglebius		return(0);			/* exit, can't open file, can't start driver */
690132451Sroberto	}
691132451Sroberto
692290001Sglebius	/* for LINUX the PPS device is the result of a line discipline.
693290001Sglebius	   It seems simplest to let an external program create the appropriate
694290001Sglebius	   /dev/pps<n> file, and only check (carefully) for its existance here
695290001Sglebius	 */
696290001Sglebius
697290001Sglebius#ifndef SYS_WINNT
698182007Sroberto	if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino))	/* same device here */
699182007Sroberto		fd2 = fd1;
700290001Sglebius	else
701290001Sglebius#endif	/* !SYS_WINNT */
702290001Sglebius	{	/* different devices here */
703290001Sglebius		if ((fd2=tty_open(device2, O_RDWR, 0777)) < 0) {
704290001Sglebius			oncore_log_f(instance, LOG_ERR,
705290001Sglebius				     "Can't open fd2 (%s)", device2);
706290001Sglebius			return(0);		/* exit, can't open PPS file, can't start driver */
707182007Sroberto		}
708182007Sroberto	}
709182007Sroberto
710290001Sglebius	/* open ppsapi source */
711182007Sroberto
712290001Sglebius	if (time_pps_create(fd2, &instance->pps_h) < 0) {
713290001Sglebius		oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel");
714290001Sglebius		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;
730290001Sglebius	pp->io.srcclock = peer;
731132451Sroberto	pp->io.datalen = 0;
732132451Sroberto	pp->io.fd = fd1;
733132451Sroberto	if (!io_addclock(&pp->io)) {
734290001Sglebius		oncore_log(instance, LOG_ERR, "can't do io_addclock");
735290001Sglebius		close(fd1);
736290001Sglebius		pp->io.fd = -1;
737132451Sroberto		free(instance);
738132451Sroberto		return (0);
739132451Sroberto	}
740290001Sglebius	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;
758290001Sglebius	oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID");
759132451Sroberto
760132451Sroberto	instance->timeout = 4;
761290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
762290001Sglebius	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;
783290001Sglebius	instance = pp->unitptr;
784132451Sroberto
785290001Sglebius	if (pp->io.fd != -1)
786290001Sglebius		io_closeclock(&pp->io);
787132451Sroberto
788290001Sglebius	if (instance != NULL) {
789290001Sglebius		time_pps_destroy (instance->pps_h);
790182007Sroberto
791290001Sglebius		close(instance->ttyfd);
792182007Sroberto
793290001Sglebius		if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
794290001Sglebius			close(instance->ppsfd);
795182007Sroberto
796290001Sglebius		if (instance->shmemfd)
797290001Sglebius			close(instance->shmemfd);
798182007Sroberto
799290001Sglebius		free(instance);
800290001Sglebius	}
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
817290001Sglebius	instance = peer->procptr->unitptr;
818132451Sroberto	if (instance->timeout) {
819132451Sroberto		instance->timeout--;
820132451Sroberto		if (instance->timeout == 0) {
821290001Sglebius			oncore_log(instance, LOG_ERR,
822290001Sglebius			    "Oncore: No response from @@Cj, shutting down driver");
823132451Sroberto			oncore_shutdown(unit, peer);
824132451Sroberto		} else {
825290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
826290001Sglebius			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;
851290001Sglebius	const char *cp;
852132451Sroberto
853182007Sroberto	if (time_pps_getcap(instance->pps_h, &cap) < 0) {
854290001Sglebius		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) {
859290001Sglebius		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) {
868290001Sglebius		cp = "Assert";
869182007Sroberto		mode = PPS_CAPTUREASSERT;
870182007Sroberto		mode1 = PPS_OFFSETASSERT;
87154359Sroberto	} else {
872290001Sglebius		cp = "Clear";
873182007Sroberto		mode = PPS_CAPTURECLEAR;
874182007Sroberto		mode1 = PPS_OFFSETCLEAR;
87554359Sroberto	}
876290001Sglebius	oncore_log_f(instance, LOG_INFO, "Initializing timing to %s.",
877290001Sglebius		     cp);
87856746Sroberto
879182007Sroberto	if (!(mode & cap)) {
880290001Sglebius		oncore_log_f(instance, LOG_ERR,
881290001Sglebius			     "Can't set timing to %s, exiting...", cp);
882182007Sroberto		return(0);
883182007Sroberto	}
884182007Sroberto
885182007Sroberto	if (!(mode1 & cap)) {
886290001Sglebius		oncore_log_f(instance, LOG_NOTICE,
887290001Sglebius			     "Can't set %s, this will increase jitter.",
888290001Sglebius			     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) {
897290001Sglebius		oncore_log_f(instance, LOG_ERR, "ONCORE: time_pps_setparams fails %m");
898290001Sglebius		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
906290001Sglebius		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) {
917290001Sglebius			oncore_log_f(instance, LOG_ERR, "time_pps_kcbind failed: %m");
918290001Sglebius			oncore_log(instance, LOG_ERR, "HARDPPS failed, abort...");
919182007Sroberto			return (0);
92056746Sroberto		}
921290001Sglebius
922290001Sglebius		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{
935290001Sglebius	int l, fd;
936182007Sroberto	u_char *cp, *cp1, *buf, *shmem_old;
937132451Sroberto	struct msg_desc *mp;
938132451Sroberto	struct stat sbuf;
939290001Sglebius	size_t i, n, n1, shmem_length, shmem_old_size;
940132451Sroberto
941290001Sglebius	/*
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)
950290001Sglebius		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) {
955290001Sglebius			shmem_old = emalloc((unsigned) sbuf.st_size);
956290001Sglebius			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) {
964290001Sglebius		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
997290001Sglebius	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) {
1052290001Sglebius		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
1070290001Sglebius	oncore_log_f(instance, LOG_NOTICE,
1071290001Sglebius		     "SHMEM (size = %ld) is CONFIGURED and available as %s",
1072290001Sglebius		     (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
1089290001Sglebius *    /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
1092290001Sglebius *    /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
1167290001Sglebius *	     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 *
1193290001Sglebius *	There is an optional line with PPSCONTROL on it (only valid for M12 or M12+T
1194290001Sglebius *	   receivers, the option is read, but ignored for all others)
1195290001Sglebius *	   and it is followed by:
1196290001Sglebius *		ON	   Turn PPS on.  This is the default and the default for other
1197290001Sglebius *			       oncore receivers.  The PPS is on even if not tracking
1198290001Sglebius *			       any satellites.
1199290001Sglebius *		SATELLITE  Turns PPS on if tracking at least 1 satellite, else off.
1200290001Sglebius *		TRAIM	   Turns PPS on or off controlled by TRAIM.
1201290001Sglebius *	  The OFF option is NOT implemented, since the Oncore driver will not work
1202290001Sglebius *	     without the PPS signal.
1203290001Sglebius *
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;
1213290001Sglebius	char	*cc, *ca, line[100], units[2], device[64];
1214290001Sglebius	const char	*dirs[] = { "/etc/ntp", "/etc", 0 };
1215290001Sglebius	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;
1222290001Sglebius		snprintf(device, sizeof(device), "%s/ntp.oncore.%d",
1223290001Sglebius			 cp, instance->unit);  /* try "ntp.oncore.0 */
1224182007Sroberto		if ((fd=fopen(device, "r")))
1225182007Sroberto			break;
1226290001Sglebius		snprintf(device, sizeof(device), "%s/ntp.oncore%d",
1227290001Sglebius			 cp, instance->unit);  /* try "ntp.oncore0" */
1228182007Sroberto		if ((fd=fopen(device, "r")))
1229182007Sroberto			break;
1230290001Sglebius		snprintf(device, sizeof(device), "%s/ntp.oncore", cp);
1231290001Sglebius		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)) {
1243290001Sglebius		char *cpw;
124456746Sroberto
124556746Sroberto		/* Remove comments */
1246290001Sglebius		if ((cpw = strchr(line, '#')))
1247290001Sglebius			*cpw = '\0';
124882498Sroberto
124956746Sroberto		/* Remove trailing space */
125056746Sroberto		for (i = strlen(line);
1251290001Sglebius		     i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]);
125256746Sroberto			)
125356746Sroberto			line[--i] = '\0';
125456746Sroberto
125556746Sroberto		/* Remove leading space */
1256290001Sglebius		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++) {
1265290001Sglebius			if (isascii((unsigned char)*ca)) {
1266290001Sglebius				if (islower((unsigned char)*ca)) {
1267290001Sglebius					*ca = toupper((unsigned char)*ca);
1268290001Sglebius				} else if (isspace((unsigned char)*ca) || (*ca == '='))
126982498Sroberto					break;
127056746Sroberto			}
127156746Sroberto		}
127282498Sroberto
127382498Sroberto		/* Remove space (and possible =) leading the arg */
1274290001Sglebius		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)) {
1278290001Sglebius			instance->shmem_fname = estrdup(ca);
127956746Sroberto			continue;
128056746Sroberto		}
128156746Sroberto
128256746Sroberto		/* Uppercase argument as well */
1283290001Sglebius		for (cpw = ca; *cpw; cpw++)
1284290001Sglebius			if (isascii((unsigned char)*cpw) && islower((unsigned char)*cpw))
1285290001Sglebius				*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;
1329290001Sglebius			if (f1 < 0 || f1 > 999999)
1330290001Sglebius				oncore_log_f(instance, LOG_WARNING,
1331290001Sglebius					     "PPS Cable delay of %fns out of Range, ignored",
1332290001Sglebius					     f1);
1333290001Sglebius			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;
1349290001Sglebius			if (f1 < 0 || f1 > 999999999.)
1350290001Sglebius				oncore_log_f(instance, LOG_WARNING,
1351290001Sglebius					     "PPS Offset of %fns out of Range, ignored",
1352290001Sglebius					     f1);
1353290001Sglebius			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 */
1381290001Sglebius		} else if (!strncmp(cc,"PPSCONTROL",10)) {              /* pps control M12 only */
1382290001Sglebius			if (!strcmp(ca,"ON") || !strcmp(ca, "CONTINUOUS")) {
1383290001Sglebius				instance->pps_control = 1;		/* PPS always on */
1384290001Sglebius			} else if (!strcmp(ca,"SATELLITE")) {
1385290001Sglebius				instance->pps_control = 2;		/* PPS on when satellite is available */
1386290001Sglebius			} else if (!strcmp(ca,"TRAIM")) {
1387290001Sglebius				instance->pps_control = 3;		/* PPS on when TRAIM status is OK */
1388290001Sglebius			} else {
1389290001Sglebius				oncore_log_f(instance, LOG_WARNING,
1390290001Sglebius				             "Unknown value \"%s\" for PPSCONTROL, ignored",
1391290001Sglebius					     cc);
1392290001Sglebius			}
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 )) {
1404290001Sglebius		oncore_log_f(instance, LOG_WARNING,
1405290001Sglebius			     "ONCORE: incomplete data on %s", device);
140654359Sroberto		instance->posn_set = 0;
140782498Sroberto		if (mode == 1 || mode == 3) {
1408290001Sglebius			oncore_log_f(instance, LOG_WARNING,
1409290001Sglebius				     "Input Mode = %d, but no/incomplete position, mode set to %d",
1410290001Sglebius				     mode, mode+1);
141182498Sroberto			mode++;
141282498Sroberto		}
141354359Sroberto	}
141482498Sroberto	instance->init_type = mode;
141582498Sroberto
1416290001Sglebius	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
1435290001Sglebius	peer = rbufp->recv_peer;
1436290001Sglebius	instance = peer->procptr->unitptr;
143754359Sroberto	p = (u_char *) &rbufp->recv_space;
143854359Sroberto
1439290001Sglebius#ifdef ONCORE_VERBOSE_RECEIVE
144054359Sroberto	if (debug > 4) {
144154359Sroberto		int i;
1442290001Sglebius		char	Msg[120], Msg2[10];
1443290001Sglebius
1444290001Sglebius		oncore_log_f(instance, LOG_DEBUG,
1445290001Sglebius			     ">>> %d bytes available",
1446290001Sglebius			     rbufp->recv_length);
1447290001Sglebius		strlcpy(Msg, ">>>", sizeof(Msg));
1448290001Sglebius		for (i = 0; i < rbufp->recv_length; i++) {
1449290001Sglebius			snprintf(Msg2, sizeof(Msg2), "%02x ", p[i]);
1450290001Sglebius			strlcat(Msg, Msg2, sizeof(Msg));
1451290001Sglebius		}
1452290001Sglebius		oncore_log(instance, LOG_DEBUG, Msg);
1453290001Sglebius
1454290001Sglebius		strlcpy(Msg, ">>>", sizeof(Msg));
1455290001Sglebius		for (i = 0; i < rbufp->recv_length; i++) {
1456290001Sglebius			snprintf(Msg2, sizeof(Msg2), "%03o ", p[i]);
1457290001Sglebius			strlcat(Msg, Msg2, sizeof(Msg));
1458290001Sglebius		}
1459290001Sglebius		oncore_log(instance, LOG_DEBUG, Msg);
146054359Sroberto	}
146154359Sroberto#endif
146254359Sroberto
146354359Sroberto	i = rbufp->recv_length;
146454359Sroberto	if (rcvbuf+rcvptr+i > &rcvbuf[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{
1482290001Sglebius	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;
1490290001Sglebius#ifdef ONCORE_VERBOSE_CONSUME
149154359Sroberto			if (debug > 4)
1492290001Sglebius				oncore_log_f(instance, LOG_DEBUG,
1493290001Sglebius					     ">>> skipping %d chars",
1494290001Sglebius					     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) {
1508290001Sglebius#ifdef ONCORE_VERBOSE_CONSUME
150982498Sroberto			if (debug > 4)
1510290001Sglebius				oncore_log_f(instance, LOG_DEBUG,
1511290001Sglebius					     ">>> Unknown MSG, skipping 4 (%c%c)",
1512290001Sglebius					     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;
1520290001Sglebius#ifdef ONCORE_VERBOSE_CONSUME
152154359Sroberto		if (debug > 3)
1522290001Sglebius			oncore_log_f(instance, LOG_DEBUG,
1523290001Sglebius				     "GOT: %c%c  %d of %d entry %d",
1524290001Sglebius				     instance->unit, rcvbuf[2],
1525290001Sglebius				     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') {
1535290001Sglebius#ifdef ONCORE_VERBOSE_CONSUME
153682498Sroberto			if (debug)
1537290001Sglebius				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			}
1550290001Sglebius#ifdef ONCORE_VERBOSE_CONSUME
1551182007Sroberto			else if (debug) {
1552290001Sglebius				char	Msg[120], Msg2[10];
1553290001Sglebius
1554290001Sglebius				oncore_log(instance, LOG_ERR, "Checksum mismatch!");
1555290001Sglebius				snprintf(Msg, sizeof(Msg), "@@%c%c ", rcvbuf[2], rcvbuf[3]);
1556290001Sglebius				for (i = 4; i < l; i++) {
1557290001Sglebius					snprintf(Msg2, sizeof(Msg2),
1558290001Sglebius						 "%03o ", rcvbuf[i]);
1559290001Sglebius					strlcat(Msg, Msg2, sizeof(Msg));
1560290001Sglebius				}
1561290001Sglebius				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;
1593290001Sglebius	struct peer *peer;
1594132451Sroberto	pps_info_t pps_i;
1595290001Sglebius	char Msg[160];
159654359Sroberto
1597290001Sglebius	peer = instance->peer;
1598290001Sglebius
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
1606290001Sglebius	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
1610290001Sglebius	if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) {
1611132451Sroberto#endif
1612290001Sglebius		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1613132451Sroberto		return;
1614290001Sglebius	}
1615132451Sroberto
1616132451Sroberto	/* Don't do anything without an almanac to define the GPS->UTC delta */
1617132451Sroberto
1618290001Sglebius	if (instance->rsm.bad_almanac) {
1619290001Sglebius		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1620132451Sroberto		return;
1621290001Sglebius	}
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--;
1631290001Sglebius		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) {
1640290001Sglebius		oncore_log_f(instance, LOG_ERR,
1641290001Sglebius			     "time_pps_fetch failed %m");
1642290001Sglebius		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1643132451Sroberto		return;
1644132451Sroberto	}
1645132451Sroberto
1646132451Sroberto	if (instance->assert) {
1647132451Sroberto		tsp = &pps_i.assert_timestamp;
1648132451Sroberto
1649290001Sglebius#ifdef ONCORE_VERBOSE_GET_TIMESTAMP
1650132451Sroberto		if (debug > 2) {
1651290001Sglebius			u_long i;
1652290001Sglebius
1653132451Sroberto			i = (u_long) pps_i.assert_sequence;
1654182007Sroberto# ifdef HAVE_STRUCT_TIMESPEC
1655290001Sglebius			oncore_log_f(instance, LOG_DEBUG,
1656290001Sglebius				     "serial/j (%lu, %lu) %ld.%09ld", i,
1657290001Sglebius				     j, (long)tsp->tv_sec,
1658290001Sglebius				     (long)tsp->tv_nsec);
1659182007Sroberto# else
1660290001Sglebius			oncore_log_f(instance, LOG_DEBUG,
1661290001Sglebius				     "serial/j (%lu, %lu) %ld.%06ld", i,
1662290001Sglebius				     j, (long)tsp->tv_sec,
1663290001Sglebius				     (long)tsp->tv_usec);
1664182007Sroberto# endif
1665182007Sroberto		}
1666132451Sroberto#endif
1667132451Sroberto
1668132451Sroberto		if (pps_i.assert_sequence == j) {
1669290001Sglebius			oncore_log(instance, LOG_NOTICE, "ONCORE: oncore_get_timestamp, error serial pps");
1670290001Sglebius			peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1671132451Sroberto			return;
1672132451Sroberto		}
1673290001Sglebius
1674132451Sroberto		instance->ev_serial = pps_i.assert_sequence;
1675132451Sroberto	} else {
1676132451Sroberto		tsp = &pps_i.clear_timestamp;
1677132451Sroberto
1678290001Sglebius#if 0
1679132451Sroberto		if (debug > 2) {
1680290001Sglebius			u_long i;
1681290001Sglebius
1682132451Sroberto			i = (u_long) pps_i.clear_sequence;
1683182007Sroberto# ifdef HAVE_STRUCT_TIMESPEC
1684290001Sglebius			oncore_log_f(instance, LOG_DEBUG,
1685290001Sglebius				     "serial/j (%lu, %lu) %ld.%09ld", i,
1686290001Sglebius				     j, (long)tsp->tv_sec,
1687290001Sglebius				     (long)tsp->tv_nsec);
1688182007Sroberto# else
1689290001Sglebius			oncore_log_f(instance, LOG_DEBUG,
1690290001Sglebius				     "serial/j (%lu, %lu) %ld.%06ld", i,
1691290001Sglebius				     j, (long)tsp->tv_sec,
1692290001Sglebius				     (long)tsp->tv_usec);
1693182007Sroberto# endif
1694182007Sroberto		}
1695132451Sroberto#endif
1696132451Sroberto
1697132451Sroberto		if (pps_i.clear_sequence == j) {
1698290001Sglebius			oncore_log(instance, LOG_ERR, "oncore_get_timestamp, error serial pps");
1699290001Sglebius			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) {
1758290001Sglebius		oncore_log_f(instance, LOG_ERR,
1759290001Sglebius			     "time_pps_getcap failed: %m");
1760290001Sglebius		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1761132451Sroberto		return;
1762132451Sroberto	}
1763132451Sroberto
1764132451Sroberto	if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
1765290001Sglebius		oncore_log_f(instance, LOG_ERR,
1766290001Sglebius			     "time_pps_getparams failed: %m");
1767290001Sglebius		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))
1782290001Sglebius		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) {
1807290001Sglebius			snprintf(f1, sizeof(f1), "%d",
1808290001Sglebius				 instance->BEHn[21]);
1809290001Sglebius			snprintf(f2, sizeof(f2), "%d",
1810290001Sglebius				 instance->BEHn[22]);
1811290001Sglebius			snprintf(f3, sizeof(f3), "%2d",
1812290001Sglebius				 instance->BEHn[23] * 256 +
1813290001Sglebius				     instance->BEHn[24]);
1814290001Sglebius			snprintf(f4, sizeof(f4), "%3d",
1815290001Sglebius				 (s_char)instance->BEHn[25]);
1816182007Sroberto		} else {
1817290001Sglebius			strlcpy(f1, "x", sizeof(f1));
1818290001Sglebius			strlcpy(f2, "x", sizeof(f2));
1819290001Sglebius			strlcpy(f3, "xx", sizeof(f3));
1820290001Sglebius			strlcpy(f4, "xxx", sizeof(f4));
1821182007Sroberto		}
1822290001Sglebius		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) {
1840290001Sglebius			snprintf(f1, sizeof(f1), "%d",
1841290001Sglebius				 instance->BEHn[6]);
1842290001Sglebius			snprintf(f2, sizeof(f2), "%d",
1843290001Sglebius				 instance->BEHn[7]);
1844290001Sglebius			snprintf(f3, sizeof(f3), "%d",
1845290001Sglebius				 instance->BEHn[12] * 256 +
1846290001Sglebius				     instance->BEHn[13]);
1847290001Sglebius			snprintf(f4, sizeof(f4), "%3d",
1848290001Sglebius				 (s_char)instance->BEHn[14]);
1849182007Sroberto		} else {
1850290001Sglebius			strlcpy(f1, "x", sizeof(f1));
1851290001Sglebius			strlcpy(f2, "x", sizeof(f2));
1852290001Sglebius			strlcpy(f3, "xx", sizeof(f3));
1853290001Sglebius			strlcpy(f4, "xxx", sizeof(f4));
1854182007Sroberto		}
1855290001Sglebius		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);
1877290001Sglebius		peer->flags &= ~FLAG_PPS;	/* problem - clear PPS FLAG */
1878132451Sroberto		return;
1879132451Sroberto	}
1880132451Sroberto
1881290001Sglebius	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	}
1890290001Sglebius	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{
1909290001Sglebius#ifdef ONCORE_VERBOSE_MSG_ANY
191054359Sroberto	int i;
191154359Sroberto	const char *fmt = oncore_messages[idx].fmt;
191254359Sroberto	const char *p;
1913290001Sglebius	char *q;
1914290001Sglebius	char *qlim;
191582498Sroberto#ifdef HAVE_GETCLOCK
191682498Sroberto	struct timespec ts;
191782498Sroberto#endif
191854359Sroberto	struct timeval tv;
1919290001Sglebius	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
1929290001Sglebius		oncore_log(instance, LOG_DEBUG, "%ld.%06ld",
1930290001Sglebius			   (long)tv.tv_sec, (long)tv.tv_usec);
193154359Sroberto
193254359Sroberto		if (!*fmt) {
1933290001Sglebius			snprintf(Msg, sizeof(Msg), ">>@@%c%c ", buf[2],
1934290001Sglebius				 buf[3]);
1935290001Sglebius			for(i = 2; i < len && i < 2400 ; i++) {
1936290001Sglebius				snprintf(Msg2, sizeof(Msg2), "%02x",
1937290001Sglebius					 buf[i]);
1938290001Sglebius				strlcat(Msg, Msg2, sizeof(Msg));
1939290001Sglebius			}
1940290001Sglebius			oncore_log(instance, LOG_DEBUG, Msg);
194154359Sroberto			return;
194254359Sroberto		} else {
1943290001Sglebius			strlcpy(Msg, "##", sizeof(Msg));
1944290001Sglebius			qlim = Msg + sizeof(Msg) - 3;
1945290001Sglebius			for (p = fmt, q = Msg + 2; q < qlim && *p; ) {
1946290001Sglebius				*q++ = *p++;
1947290001Sglebius				*q++ = '_';
194854359Sroberto			}
1949290001Sglebius			*q = '\0';
1950290001Sglebius			oncore_log(instance, LOG_DEBUG, Msg);
1951290001Sglebius			snprintf(Msg, sizeof(Msg), "%c%c", buf[2],
1952290001Sglebius				 buf[3]);
195354359Sroberto			i = 4;
195454359Sroberto			for (p = fmt; *p; p++) {
1955290001Sglebius				snprintf(Msg2, "%02x", buf[i++]);
1956290001Sglebius				strlcat(Msg, Msg2, sizeof(Msg));
195754359Sroberto			}
1958290001Sglebius			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	)
1987290001Sglebius{
1988290001Sglebius	const char *cp;
198982498Sroberto
1990290001Sglebius	cp = "set to";
1991290001Sglebius	if (instance->o_state == ONCORE_RUN)
1992290001Sglebius		cp = "is";
199354359Sroberto
1994290001Sglebius	instance->Ag = buf[4];
1995290001Sglebius	oncore_log_f(instance, LOG_INFO,
1996290001Sglebius		     "Satellite mask angle %s %d degrees", cp,
1997290001Sglebius		     (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) {
2038290001Sglebius			oncore_log(instance, LOG_NOTICE,
2039132451Sroberto					"Initiating hardware 3D site survey");
204054359Sroberto
2041290001Sglebius			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
2068290001Sglebius	oncore_log_f(instance, LOG_INFO, "PPS Offset is set to %ld ns",
2069290001Sglebius		     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
2092290001Sglebius	oncore_log_f(instance, LOG_INFO, "Cable delay is set to %ld ns",
2093290001Sglebius		     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
2138290001Sglebius		oncore_log_f(instance, LOG_INFO, "Input   says chan = %d",
2139290001Sglebius			    instance->chan_in);
2140290001Sglebius		oncore_log_f(instance, LOG_INFO, "Model # says chan = %d",
2141290001Sglebius			     instance->chan_id);
2142290001Sglebius		oncore_log_f(instance, LOG_INFO, "Testing says chan = %d",
2143290001Sglebius			     instance->chan_ck);
2144290001Sglebius		oncore_log_f(instance, LOG_INFO, "Using        chan = %d",
2145290001Sglebius			     instance->chan);
2146132451Sroberto
2147132451Sroberto		instance->o_state = ONCORE_HAVE_CHAN;
2148290001Sglebius		oncore_log(instance, LOG_NOTICE, "state = ONCORE_HAVE_CHAN");
2149132451Sroberto
2150132451Sroberto		instance->timeout = 4;
2151290001Sglebius		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
2168290001Sglebius	/* check if we saw a response to Gc (M12 or M12+T */
2169290001Sglebius
2170290001Sglebius	if (instance->pps_control_msg_seen != -2) {
2171290001Sglebius		if ((instance->pps_control_msg_seen == -1) && (instance->pps_control != -1)) {
2172290001Sglebius			oncore_log(instance, LOG_INFO, "PPSCONTROL set, but not implemented (not M12)");
2173290001Sglebius		}
2174290001Sglebius		instance->pps_control_msg_seen = -2;
2175290001Sglebius	}
2176290001Sglebius
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) {
2199290001Sglebius			oncore_log(instance, LOG_NOTICE,
2200290001Sglebius				"Not Good, no @@At command (no Position Hold), must be a GT/GT+");
2201290001Sglebius			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;
2215290001Sglebius			oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE");
221682498Sroberto			break;
2217132451Sroberto
221882498Sroberto		case 2:
2219132451Sroberto		case 4: /* Site Survey */
2220290001Sglebius			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)
2224290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Gd3,  sizeof(oncore_cmd_Gd3));  /* M12+T */
2225132451Sroberto			else
2226290001Sglebius				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
2233290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ayx,  sizeof(oncore_cmd_Ayx));
223482498Sroberto
2235132451Sroberto		/* Read back Cable Delay for Output */
223682498Sroberto
2237290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Azx,  sizeof(oncore_cmd_Azx));
2238132451Sroberto
2239132451Sroberto		/* Read back Satellite Mask Angle for Output */
2240132451Sroberto
2241290001Sglebius		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) {
2258290001Sglebius						oncore_log(instance, LOG_NOTICE,
2259182007Sroberto								"Initiating hardware 3D site survey");
2260132451Sroberto
2261290001Sglebius						oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW");
2262182007Sroberto						instance->site_survey = ONCORE_SS_HW;
2263182007Sroberto					} else {
2264290001Sglebius						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
2291290001Sglebius					oncore_log_f(instance, LOG_INFO,
2292290001Sglebius						     "Initiating software 3D site survey (%d samples)",
2293290001Sglebius						     POS_HOLD_AVERAGE);
2294182007Sroberto
2295290001Sglebius					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)
2300290001Sglebius						oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
2301182007Sroberto					else {
2302290001Sglebius						oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
2303290001Sglebius						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";
2375290001Sglebius			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) {
2416290001Sglebius				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{
2450290001Sglebius	oncore_log_f(instance, LOG_NOTICE,
2451290001Sglebius		     "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
2452290001Sglebius		     ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6],
2453290001Sglebius		     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
2478290001Sglebius	instance->saw_Bj = 1;
2479290001Sglebius
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	}
2495290001Sglebius	oncore_log(instance, LOG_NOTICE, cp);
2496132451Sroberto}
249782498Sroberto
2498132451Sroberto
2499132451Sroberto
2500132451Srobertostatic void
2501290001Sglebiusoncore_msg_Bl(
2502290001Sglebius	struct instance *instance,
2503290001Sglebius	u_char *buf,
2504290001Sglebius	size_t	len
2505290001Sglebius	)
2506290001Sglebius{
2507290001Sglebius	int	subframe, valid, page, i, j, tow;
2508290001Sglebius	int	day_now, day_lsf;
2509290001Sglebius	const char	*cp;
2510290001Sglebius	enum {
2511290001Sglebius		WARN_NOT_YET,
2512290001Sglebius		WARN_0,
2513290001Sglebius		WARN_PLUS,
2514290001Sglebius		WARN_MINUS
2515290001Sglebius	} warn;
2516290001Sglebius
2517290001Sglebius	day_now = day_lsf = 0;
2518290001Sglebius	cp = NULL;	/* keep gcc happy */
2519290001Sglebius
2520290001Sglebius	subframe = buf[6] & 017;
2521290001Sglebius	valid = (buf[6] >> 4) & 017;
2522290001Sglebius	page = buf[7];
2523290001Sglebius
2524290001Sglebius	if ((!instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 4 && page == 18 && valid == 10)) {
2525290001Sglebius		instance->Bl.dt_ls  = buf[32];
2526290001Sglebius		instance->Bl.WN_lsf = buf[33];
2527290001Sglebius		instance->Bl.DN_lsf = buf[34];
2528290001Sglebius		instance->Bl.dt_lsf = buf[35];
2529290001Sglebius		instance->Bl.lsf_flg++;
2530290001Sglebius	}
2531290001Sglebius	if ((instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 1 && valid == 10)) {
2532290001Sglebius		i = (buf[7+7]<<8) + buf[7+8];
2533290001Sglebius		instance->Bl.WN = i >> 6;
2534290001Sglebius		tow = (buf[7+4]<<16) + (buf[7+5]<<8) + buf[7+6];
2535290001Sglebius		tow >>= 7;
2536290001Sglebius		tow = tow & 0377777;
2537290001Sglebius		tow <<= 2;
2538290001Sglebius		instance->Bl.DN = tow/57600L + 1;
2539290001Sglebius		instance->Bl.wn_flg++;
2540290001Sglebius	}
2541290001Sglebius	if (instance->Bl.wn_flg && instance->Bl.lsf_flg)  {
2542290001Sglebius		instance->Bl.wn_flg = instance->Bl.lsf_flg = 0;
2543290001Sglebius		oncore_cmd_Bl[2] = 0;
2544290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Bl, sizeof oncore_cmd_Bl);
2545290001Sglebius		oncore_cmd_Bl[2] = 1;
2546290001Sglebius
2547290001Sglebius		i = instance->Bl.WN&01400;
2548290001Sglebius		instance->Bl.WN_lsf |= i;
2549290001Sglebius
2550290001Sglebius		/* have everything I need, doit */
2551290001Sglebius
2552290001Sglebius		i = (instance->Bl.WN_lsf - instance->Bl.WN);
2553290001Sglebius		if (i < 0)
2554290001Sglebius			i += 1024;
2555290001Sglebius		day_now = instance->Bl.DN;
2556290001Sglebius		day_lsf = 7*i + instance->Bl.DN_lsf;
2557290001Sglebius
2558290001Sglebius		/* ignore if in past or more than a month in future */
2559290001Sglebius
2560290001Sglebius		warn = WARN_NOT_YET;
2561290001Sglebius		if (day_lsf >= day_now && day_lsf - day_now < 32) {
2562290001Sglebius			/* if < 28d, doit, if 28-31, ck day-of-month < 20 (not at end of prev month) */
2563290001Sglebius			if (day_lsf - day_now < 28 ||  instance->BEHa[5] < 20) {
2564290001Sglebius				i = instance->Bl.dt_lsf - instance->Bl.dt_ls;
2565290001Sglebius				switch (i) {
2566290001Sglebius				case -1:
2567290001Sglebius					warn = WARN_MINUS;
2568290001Sglebius					break;
2569290001Sglebius				case  0:
2570290001Sglebius					warn = WARN_0;
2571290001Sglebius					break;
2572290001Sglebius				case  1:
2573290001Sglebius					warn = WARN_PLUS;
2574290001Sglebius					break;
2575290001Sglebius				}
2576290001Sglebius			}
2577290001Sglebius		}
2578290001Sglebius
2579290001Sglebius		switch (warn) {
2580290001Sglebius		case WARN_0:
2581290001Sglebius		case WARN_NOT_YET:
2582290001Sglebius			instance->peer->leap = LEAP_NOWARNING;
2583290001Sglebius			cp = "Set peer.leap to LEAP_NOWARNING";
2584290001Sglebius			break;
2585290001Sglebius		case WARN_MINUS:
2586290001Sglebius			instance->peer->leap = LEAP_DELSECOND;
2587290001Sglebius			cp = "Set peer.leap to LEAP_DELSECOND";
2588290001Sglebius			break;
2589290001Sglebius		case WARN_PLUS:
2590290001Sglebius			instance->peer->leap = LEAP_ADDSECOND;
2591290001Sglebius			cp = "Set peer.leap to LEAP_ADDSECOND";
2592290001Sglebius			break;
2593290001Sglebius		}
2594290001Sglebius		oncore_log(instance, LOG_NOTICE, cp);
2595290001Sglebius
2596290001Sglebius		i = instance->Bl.dt_lsf-instance->Bl.dt_ls;
2597290001Sglebius		if (i) {
2598290001Sglebius			j = (i >= 0) ? i : -i;		/* abs(i) */
2599290001Sglebius			oncore_log_f(instance, LOG_NOTICE,
2600290001Sglebius				     "see Leap_Second (%c%d) in %d days",
2601290001Sglebius				     ((i >= 0) ? '+' : '-'), j,
2602290001Sglebius				     day_lsf-day_now);
2603290001Sglebius		}
2604290001Sglebius	}
2605290001Sglebius
2606290001Sglebius/*
2607290001Sglebius * Reg only wants the following output for "deeper" driver debugging.
2608290001Sglebius * See Bug 2142 and Bug 1866
2609290001Sglebius */
2610290001Sglebius#if 0
2611290001Sglebius	oncore_log_f(instance, LOG_DEBUG,
2612290001Sglebius		     "dt_ls = %d  dt_lsf = %d  WN = %d  DN = %d  WN_lsf = %d  DNlsf = %d  wn_flg = %d  lsf_flg = %d  Bl_day = %d",
2613290001Sglebius		     instance->Bl.dt_ls, instance->Bl.dt_lsf,
2614290001Sglebius		     instance->Bl.WN, instance->Bl.DN,
2615290001Sglebius		     instance->Bl.WN_lsf, instance->Bl.DN_lsf,
2616290001Sglebius		     instance->Bl.wn_flg, instance->Bl.lsf_flg,
2617290001Sglebius		     instance->Bl.Bl_day);
2618290001Sglebius#endif
2619290001Sglebius}
2620290001Sglebius
2621290001Sglebius
2622290001Sglebiusstatic void
2623132451Srobertooncore_msg_BnEnHn(
2624132451Sroberto	struct instance *instance,
2625132451Sroberto	u_char *buf,
2626132451Sroberto	size_t	len
2627132451Sroberto	)
2628132451Sroberto{
2629132451Sroberto	long	dt1, dt2;
2630132451Sroberto
2631132451Sroberto	if (instance->o_state != ONCORE_RUN)
2632132451Sroberto		return;
2633132451Sroberto
2634132451Sroberto	if (instance->traim_delay) {	 /* flag that @@Bn/@@En/Hn returned */
2635132451Sroberto			instance->traim_ck = 1;
2636132451Sroberto			instance->traim_delay = 0;
2637290001Sglebius			oncore_log(instance, LOG_NOTICE, "ONCORE: Detected TRAIM, TRAIM = ON");
2638132451Sroberto
2639132451Sroberto			oncore_set_traim(instance);
264082498Sroberto	}
2641132451Sroberto
2642132451Sroberto	memcpy(instance->BEHn, buf, (size_t) len);	/* Bn or En or Hn */
2643132451Sroberto
2644182007Sroberto	if (!instance->traim)	/* BnEnHn will be turned off in any case */
2645182007Sroberto		return;
2646182007Sroberto
2647132451Sroberto	/* If Time RAIM doesn't like it, don't trust it */
2648132451Sroberto
2649132451Sroberto	if (buf[2] == 'H') {
2650290001Sglebius		if (instance->BEHn[6]) {    /* bad TRAIM */
2651290001Sglebius			oncore_log(instance, LOG_WARNING, "BAD TRAIM");
2652132451Sroberto			return;
2653290001Sglebius		}
2654132451Sroberto
2655132451Sroberto		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2656182007Sroberto		instance->saw_tooth = (s_char) instance->BEHn[14]; /* update for next time Hn[14] */
2657132451Sroberto		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2658132451Sroberto	} else {
2659132451Sroberto		if (instance->BEHn[21]) /* bad TRAIM */
2660132451Sroberto			return;
2661132451Sroberto
2662132451Sroberto		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2663182007Sroberto		instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time Bn[25], En[25] */
2664132451Sroberto		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2665132451Sroberto	}
2666132451Sroberto
2667132451Sroberto	oncore_get_timestamp(instance, dt1, dt2);
266882498Sroberto}
266982498Sroberto
267082498Sroberto
267182498Sroberto
267282498Sroberto/* Here for @@Ca, @@Fa and @@Ia messages */
267382498Sroberto
2674132451Sroberto/* These are Self test Commands for 6, 8, and 12 chan receivers.
2675132451Sroberto * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
2676132451Sroberto * It was found that under some circumstances the following
267782498Sroberto * command would fail if issued immediately after the return from the
267882498Sroberto * @@Fa, but a 2sec delay seemed to fix things.  Since simply calling
2679132451Sroberto * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
2680132451Sroberto * itimer, we set a flag, and test it at the next POLL.  If it hasn't
268182498Sroberto * been cleared, we reissue the @@Cj that is issued below.
268282498Sroberto * Note that we do a @@Cj at the beginning, and again here.
268382498Sroberto * The first is to get the info, the 2nd is just used as a safe command
268482498Sroberto * after the @@Fa for all Oncores (and it was in this posn in the
268582498Sroberto * original code).
268682498Sroberto */
268782498Sroberto
268882498Srobertostatic void
268982498Srobertooncore_msg_CaFaIa(
269082498Sroberto	struct instance *instance,
269182498Sroberto	u_char *buf,
269282498Sroberto	size_t len
269382498Sroberto	)
269482498Sroberto{
2695132451Sroberto	int	i;
269682498Sroberto
269782498Sroberto	if (instance->o_state == ONCORE_TEST_SENT) {
2698132451Sroberto		enum antenna_state antenna;
269982498Sroberto
270082498Sroberto		instance->timeout = 0;
270182498Sroberto
2702290001Sglebius#if ONCORE_VERBOSE_SELF_TEST
270382498Sroberto		if (debug > 2) {
270482498Sroberto			if (buf[2] == 'I')
2705290001Sglebius				oncore_log_f(instance, LOG_DEBUG,
2706290001Sglebius					     ">>@@%ca %x %x %x", buf[2],
2707290001Sglebius					     buf[4], buf[5], buf[6]);
270882498Sroberto			else
2709290001Sglebius				oncore_log_f(instance, LOG_DEBUG,
2710290001Sglebius					     ">>@@%ca %x %x", buf[2],
2711290001Sglebius					     buf[4], buf[5]);
271282498Sroberto		}
2713182007Sroberto#endif
271482498Sroberto
2715132451Sroberto		antenna = (buf[4] & 0xc0) >> 6;
271682498Sroberto		buf[4] &= ~0xc0;
271782498Sroberto
2718132451Sroberto		i = buf[4] || buf[5];
2719132451Sroberto		if (buf[2] == 'I') i = i || buf[6];
2720132451Sroberto		if (i) {
2721290001Sglebius			if (buf[2] == 'I')
2722290001Sglebius				oncore_log_f(instance, LOG_ERR,
2723290001Sglebius					     "self test failed: result %02x %02x %02x",
2724290001Sglebius					     buf[4], buf[5], buf[6]);
2725290001Sglebius			else
2726290001Sglebius				oncore_log_f(instance, LOG_ERR,
2727290001Sglebius					     "self test failed: result %02x %02x",
2728290001Sglebius					     buf[4], buf[5]);
2729132451Sroberto
2730290001Sglebius			oncore_log(instance, LOG_ERR,
2731290001Sglebius				   "ONCORE: self test failed, shutting down driver");
2732290001Sglebius
2733132451Sroberto			refclock_report(instance->peer, CEVNT_FAULT);
273482498Sroberto			oncore_shutdown(instance->unit, instance->peer);
273582498Sroberto			return;
273682498Sroberto		}
273782498Sroberto
2738132451Sroberto		/* report the current antenna state */
273982498Sroberto
2740132451Sroberto		oncore_antenna_report(instance, antenna);
274182498Sroberto
274282498Sroberto		instance->o_state = ONCORE_INIT;
2743290001Sglebius		oncore_log(instance, LOG_NOTICE, "state = ONCORE_INIT");
2744132451Sroberto
2745132451Sroberto		instance->timeout = 4;
2746290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
274782498Sroberto	}
274882498Sroberto}
274982498Sroberto
275082498Sroberto
275182498Sroberto
2752132451Sroberto/*
2753132451Sroberto * Demultiplex the almanac into shmem
2754132451Sroberto */
275582498Sroberto
275682498Srobertostatic void
2757132451Srobertooncore_msg_Cb(
275882498Sroberto	struct instance *instance,
275982498Sroberto	u_char *buf,
276082498Sroberto	size_t len
276182498Sroberto	)
276282498Sroberto{
2763132451Sroberto	int i;
276454359Sroberto
2765132451Sroberto	if (instance->shmem == NULL)
2766132451Sroberto		return;
276782498Sroberto
2768132451Sroberto	if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
2769132451Sroberto		i = buf[5];
2770132451Sroberto	else if (buf[4] == 4 && buf[5] <= 5)
2771132451Sroberto		i = buf[5] + 24;
2772132451Sroberto	else if (buf[4] == 4 && buf[5] <= 10)
2773132451Sroberto		i = buf[5] + 23;
2774132451Sroberto	else if (buf[4] == 4 && buf[5] == 25)
2775132451Sroberto		i = 34;
2776132451Sroberto	else {
2777290001Sglebius		oncore_log(instance, LOG_NOTICE, "Cb: Response is NO ALMANAC");
277854359Sroberto		return;
2779132451Sroberto	}
278054359Sroberto
2781132451Sroberto	i *= 36;
2782132451Sroberto	instance->shmem[instance->shmem_Cb + i + 2]++;
2783132451Sroberto	memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
278454359Sroberto
2785290001Sglebius#ifdef ONCORE_VERBOSE_MSG_CB
2786290001Sglebius	oncore_log_f(instance, LOG_DEBUG, "See Cb [%d,%d]", buf[4],
2787290001Sglebius		     buf[5]);
2788132451Sroberto#endif
2789132451Sroberto}
279082498Sroberto
279182498Sroberto
2792132451Sroberto
2793132451Sroberto/*
2794132451Sroberto * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
2795132451Sroberto *	not so for VP (eeprom) or any unit with a battery
2796132451Sroberto */
2797132451Sroberto
2798132451Srobertostatic void
2799132451Srobertooncore_msg_Cf(
2800132451Sroberto	struct instance *instance,
2801132451Sroberto	u_char *buf,
2802132451Sroberto	size_t len
2803132451Sroberto	)
2804132451Sroberto{
2805132451Sroberto	if (instance->o_state == ONCORE_RESET_SENT) {
2806290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
2807132451Sroberto										       /* Reset set VP to IDLE */
2808132451Sroberto		instance->o_state = ONCORE_TEST_SENT;
2809290001Sglebius		oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
2810132451Sroberto
2811290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
281282498Sroberto	}
2813132451Sroberto}
281482498Sroberto
281582498Sroberto
281682498Sroberto
2817132451Sroberto/*
2818132451Sroberto * This is the Grand Central Station for the Preliminary Initialization.
2819132451Sroberto * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
2820132451Sroberto *
2821132451Sroberto * We do an @@Cj whenever we need a safe command for all Oncores.
2822132451Sroberto * The @@Cj gets us back here where we can switch to the next phase of setup.
2823132451Sroberto *
2824132451Sroberto * o Once at the very beginning (in start) to get the Model number.
2825132451Sroberto *   This info is printed, but no longer used.
2826132451Sroberto * o Again after we have determined the number of Channels in the receiver.
2827132451Sroberto * o And once later after we have done a reset and test, (which may hang),
2828132451Sroberto *   as we are about to initialize the Oncore and start it running.
2829132451Sroberto * o We have one routine below for each case.
2830132451Sroberto */
283182498Sroberto
2832132451Srobertostatic void
2833132451Srobertooncore_msg_Cj(
2834132451Sroberto	struct instance *instance,
2835132451Sroberto	u_char *buf,
2836132451Sroberto	size_t len
2837132451Sroberto	)
2838132451Sroberto{
2839132451Sroberto	int	mode;
284082498Sroberto
2841132451Sroberto	memcpy(instance->Cj, buf, len);
284282498Sroberto
2843132451Sroberto	instance->timeout = 0;
2844132451Sroberto	if (instance->o_state == ONCORE_CHECK_ID) {
2845132451Sroberto		oncore_msg_Cj_id(instance, buf, len);
2846132451Sroberto		oncore_chan_test(instance);
2847132451Sroberto	} else if (instance->o_state == ONCORE_HAVE_CHAN) {
2848132451Sroberto		mode = instance->init_type;
2849132451Sroberto		if (mode == 3 || mode == 4) {	/* Cf will return here to check for TEST */
2850132451Sroberto			instance->o_state = ONCORE_RESET_SENT;
2851290001Sglebius			oncore_log(instance, LOG_NOTICE, "state = ONCORE_RESET_SENT");
2852290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
2853132451Sroberto		} else {
2854132451Sroberto			instance->o_state = ONCORE_TEST_SENT;
2855290001Sglebius			oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT");
285682498Sroberto		}
285782498Sroberto	}
285882498Sroberto
2859132451Sroberto	if (instance->o_state == ONCORE_TEST_SENT) {
2860132451Sroberto		if (instance->chan == 6)
2861290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
2862132451Sroberto		else if (instance->chan == 8)
2863290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
2864132451Sroberto		else if (instance->chan == 12)
2865290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
2866132451Sroberto	} else if (instance->o_state == ONCORE_INIT)
2867132451Sroberto		oncore_msg_Cj_init(instance, buf, len);
2868132451Sroberto}
286982498Sroberto
287082498Sroberto
287182498Sroberto
2872132451Sroberto/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
2873132451Sroberto *	the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
2874132451Sroberto *	and from Motorola.  Until recently Rick was the only source of
2875132451Sroberto *	this information as Motorola didn't give the information out.
2876132451Sroberto *
2877132451Sroberto * Determine the Type from the Model #, this determines #chan and if TRAIM is
2878132451Sroberto *   available.
2879132451Sroberto *
2880132451Sroberto * The Information from this routine is NO LONGER USED.
2881132451Sroberto * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
2882132451Sroberto */
288382498Sroberto
2884132451Srobertostatic void
2885132451Srobertooncore_msg_Cj_id(
2886132451Sroberto	struct instance *instance,
2887132451Sroberto	u_char *buf,
2888132451Sroberto	size_t len
2889132451Sroberto	)
2890132451Sroberto{
2891290001Sglebius	char *cp2, Model[21];
2892290001Sglebius	const char *cp, *cp1;
2893132451Sroberto
2894132451Sroberto	/* Write Receiver ID message to clockstats file */
2895132451Sroberto
2896132451Sroberto	instance->Cj[294] = '\0';
2897290001Sglebius	for (cp= (char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
2898290001Sglebius		char *cpw = strchr(cp, '\r');
2899290001Sglebius		if (!cpw)
2900290001Sglebius			cpw = (char *)&instance->Cj[294];
2901290001Sglebius		*cpw = '\0';
2902290001Sglebius		oncore_log(instance, LOG_NOTICE, cp);
2903290001Sglebius		*cpw = '\r';
2904290001Sglebius		cp = cpw+2;
290582498Sroberto	}
290682498Sroberto
2907132451Sroberto	/* next, the Firmware Version and Revision numbers */
290882498Sroberto
2909182007Sroberto	instance->version  = atoi((char *) &instance->Cj[83]);
2910182007Sroberto	instance->revision = atoi((char *) &instance->Cj[111]);
291182498Sroberto
2912132451Sroberto	/* from model number decide which Oncore this is,
2913132451Sroberto		and then the number of channels */
291482498Sroberto
2915182007Sroberto	for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++)   /* start right after 'Model #' */
2916132451Sroberto		;
2917132451Sroberto	cp1 = cp;
2918132451Sroberto	cp2 = Model;
2919290001Sglebius	for (; !isspace((unsigned char)*cp) && cp-cp1 < 20; cp++, cp2++)
2920132451Sroberto		*cp2 = *cp;
2921132451Sroberto	*cp2 = '\0';
2922132451Sroberto
2923132451Sroberto	cp = 0;
2924132451Sroberto	if (!strncmp(Model, "PVT6", (size_t) 4)) {
2925132451Sroberto		cp = "PVT6";
2926132451Sroberto		instance->model = ONCORE_PVT6;
2927132451Sroberto	} else if (Model[0] == 'A') {
2928132451Sroberto		cp = "Basic";
2929132451Sroberto		instance->model = ONCORE_BASIC;
2930132451Sroberto	} else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
2931132451Sroberto		cp = "VP";
2932132451Sroberto		instance->model = ONCORE_VP;
2933132451Sroberto	} else if (Model[0] == 'P') {
2934132451Sroberto		cp = "M12";
2935132451Sroberto		instance->model = ONCORE_M12;
2936132451Sroberto	} else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
2937132451Sroberto		if (Model[5] == 'N') {
2938132451Sroberto			cp = "GT";
2939132451Sroberto			instance->model = ONCORE_GT;
2940132451Sroberto		} else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
2941132451Sroberto			cp = "GT+";
2942132451Sroberto			instance->model = ONCORE_GTPLUS;
2943132451Sroberto		} else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
2944132451Sroberto				cp = "UT";
2945132451Sroberto				instance->model = ONCORE_UT;
2946132451Sroberto		} else if (Model[1] == '5' && Model[5] == 'G') {
2947132451Sroberto			cp = "UT+";
2948132451Sroberto			instance->model = ONCORE_UTPLUS;
2949132451Sroberto		} else if (Model[1] == '6' && Model[5] == 'G') {
2950132451Sroberto			cp = "SL";
2951132451Sroberto			instance->model = ONCORE_SL;
2952132451Sroberto		} else {
2953132451Sroberto			cp = "Unknown";
2954132451Sroberto			instance->model = ONCORE_UNKNOWN;
295554359Sroberto		}
2956132451Sroberto	} else	{
2957132451Sroberto		cp = "Unknown";
2958132451Sroberto		instance->model = ONCORE_UNKNOWN;
295954359Sroberto	}
296054359Sroberto
2961132451Sroberto	/* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
296254359Sroberto
2963290001Sglebius	oncore_log_f(instance, LOG_INFO,
2964290001Sglebius		     "This looks like an Oncore %s with version %d.%d firmware.",
2965290001Sglebius		     cp, instance->version, instance->revision);
296682498Sroberto
2967132451Sroberto	instance->chan_id = 8;	   /* default */
2968132451Sroberto	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2969132451Sroberto		instance->chan_id = 6;
2970132451Sroberto	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2971132451Sroberto		instance->chan_id = 8;
2972132451Sroberto	else if (instance->model == ONCORE_M12)
2973132451Sroberto		instance->chan_id = 12;
297482498Sroberto
2975132451Sroberto	instance->traim_id = 0;    /* default */
2976132451Sroberto	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2977132451Sroberto		instance->traim_id = 0;
2978132451Sroberto	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2979132451Sroberto		instance->traim_id = 1;
2980132451Sroberto	else if (instance->model == ONCORE_M12)
2981132451Sroberto		instance->traim_id = -1;
298282498Sroberto
2983290001Sglebius	oncore_log_f(instance, LOG_INFO, "Channels = %d, TRAIM = %s",
2984290001Sglebius		     instance->chan_id,
2985290001Sglebius		     ((instance->traim_id < 0)
2986290001Sglebius			  ? "UNKNOWN"
2987290001Sglebius			  : (instance->traim_id > 0)
2988290001Sglebius				? "ON"
2989290001Sglebius				: "OFF"));
2990132451Sroberto}
2991132451Sroberto
2992132451Sroberto
2993132451Sroberto
2994132451Sroberto/* OK, know type of Oncore, have possibly reset it, and have tested it.
2995132451Sroberto * We know the number of channels.
2996132451Sroberto * We will determine whether we have TRAIM before we actually start.
2997132451Sroberto * Now initialize.
2998132451Sroberto */
2999132451Sroberto
3000132451Srobertostatic void
3001132451Srobertooncore_msg_Cj_init(
3002132451Sroberto	struct instance *instance,
3003132451Sroberto	u_char *buf,
3004132451Sroberto	size_t len
3005132451Sroberto	)
3006132451Sroberto{
3007290001Sglebius	u_char	Cmd[20];
3008132451Sroberto	int	mode;
3009132451Sroberto
3010132451Sroberto
3011132451Sroberto	/* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
3012132451Sroberto	 * start again if we go from 0D -> 3D, then loses them again when we
3013132451Sroberto	 * go from 3D -> 0D.  We do this to get a @@Ea message for SHMEM.
3014132451Sroberto	 * For NOW we will turn this aspect of filling SHMEM off for the M12
301582498Sroberto	 */
301682498Sroberto
3017132451Sroberto	if (instance->chan == 12) {
3018132451Sroberto		instance->shmem_bad_Ea = 1;
3019290001Sglebius		oncore_log_f(instance, LOG_NOTICE,
3020290001Sglebius			     "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***",
3021290001Sglebius			     instance->version, instance->revision);
302254359Sroberto	}
302354359Sroberto
3024290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
3025290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
3026290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
3027290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
3028290001Sglebius	oncore_sendmsg(instance, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
3029290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
3030290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
303182498Sroberto
3032132451Sroberto	mode = instance->init_type;
303382498Sroberto
3034132451Sroberto	/* If there is Position input in the Config file
3035132451Sroberto	 * and mode = (1,3) set it as posn hold posn, goto 0D mode.
3036132451Sroberto	 *  or mode = (2,4) set it as INITIAL position, and do Site Survey.
303782498Sroberto	 */
303854359Sroberto
3039132451Sroberto	if (instance->posn_set) {
3040290001Sglebius		oncore_log(instance, LOG_INFO, "Setting Posn from input data");
3041132451Sroberto		oncore_set_posn(instance);	/* this should print posn indirectly thru the As cmd */
3042132451Sroberto	} else	/* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
3043132451Sroberto		if (instance->chan != 12)
3044290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
304582498Sroberto
3046132451Sroberto	if (mode != 0) {
3047132451Sroberto			/* cable delay in ns */
3048132451Sroberto		memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
3049290001Sglebius		w32_buf(&Cmd[-2+4], (int)instance->delay);
3050290001Sglebius		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Az));	/* 6,8,12 */
305182498Sroberto
3052132451Sroberto			/* PPS offset in ns */
3053290001Sglebius		memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay));	/* some have it, some don't */
3054290001Sglebius		w32_buf(&Cmd[-2+4], instance->offset);			/* will check for hw response */
3055290001Sglebius		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ay));
3056132451Sroberto
3057132451Sroberto		/* Satellite mask angle */
3058132451Sroberto
3059132451Sroberto		if (instance->Ag != 0xff) {	/* will have 0xff in it if not set by user */
3060132451Sroberto			memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
3061132451Sroberto			Cmd[-2+4] = instance->Ag;
3062290001Sglebius			oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Ag));
3063132451Sroberto		}
306482498Sroberto	}
306582498Sroberto
3066132451Sroberto	/* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
3067132451Sroberto	 * now we're really running
3068132451Sroberto	 * these were ALL started in the chan test,
3069132451Sroberto	 * However, if we had mode=3,4 then commands got turned off, so we turn
3070132451Sroberto	 * them on again here just in case
307154359Sroberto	 */
307254359Sroberto
3073132451Sroberto	if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
3074290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
3075290001Sglebius		oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
3076290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
3077290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
3078290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba ));
3079132451Sroberto	} else if (instance->chan == 8) {  /* start 8chan, kill 6,12chan commands */
3080290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
3081290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
3082290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
3083290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
3084290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea ));
3085132451Sroberto	} else if (instance->chan == 12){  /* start 12chan, kill 6,12chan commands */
3086290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
3087290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
3088290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
3089290001Sglebius		oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0));
3090290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha ));
3091290001Sglebius		oncore_cmd_Gc[2] = (instance->pps_control < 0) ? 1 : instance->pps_control;
3092290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* PPS off/continuous/Tracking 1+sat/TRAIM */
309354359Sroberto	}
309454359Sroberto
3095132451Sroberto	instance->count = 1;
3096132451Sroberto	instance->o_state = ONCORE_ALMANAC;
3097290001Sglebius	oncore_log(instance, LOG_NOTICE, "state = ONCORE_ALMANAC");
3098132451Sroberto}
309982498Sroberto
310054359Sroberto
310154359Sroberto
3102132451Sroberto/* 12chan position */
310354359Sroberto
3104132451Srobertostatic void
3105132451Srobertooncore_msg_Ga(
3106132451Sroberto	struct instance *instance,
3107132451Sroberto	u_char *buf,
3108132451Sroberto	size_t len
3109132451Sroberto	)
3110132451Sroberto{
3111132451Sroberto	long lat, lon, ht;
3112132451Sroberto	double Lat, Lon, Ht;
311354359Sroberto
311454359Sroberto
3115132451Sroberto	lat = buf_w32(&buf[4]);
3116132451Sroberto	lon = buf_w32(&buf[8]);
3117132451Sroberto	ht  = buf_w32(&buf[12]);  /* GPS ellipsoid */
311854359Sroberto
3119132451Sroberto	Lat = lat;
3120132451Sroberto	Lon = lon;
3121132451Sroberto	Ht  = ht;
312254359Sroberto
3123132451Sroberto	Lat /= 3600000;
3124132451Sroberto	Lon /= 3600000;
3125132451Sroberto	Ht  /= 100;
3126132451Sroberto
3127290001Sglebius	oncore_log_f(instance, LOG_NOTICE,
3128290001Sglebius		     "Ga Posn Lat = %.7f, Lon = %.7f, Ht  = %.2f", Lat,
3129290001Sglebius		     Lon, Ht);
3130132451Sroberto
3131132451Sroberto	instance->ss_lat  = lat;
3132132451Sroberto	instance->ss_long = lon;
3133132451Sroberto	instance->ss_ht   = ht;
313454359Sroberto
3135132451Sroberto	oncore_print_posn(instance);
3136132451Sroberto}
313782498Sroberto
313882498Sroberto
313982498Sroberto
3140132451Sroberto/* 12 chan time/date */
314182498Sroberto
3142132451Srobertostatic void
3143132451Srobertooncore_msg_Gb(
3144132451Sroberto	struct instance *instance,
3145132451Sroberto	u_char *buf,
3146132451Sroberto	size_t len
3147132451Sroberto	)
3148132451Sroberto{
3149290001Sglebius	const char *	gmts;
3150132451Sroberto	int	mo, d, y, h, m, s, gmth, gmtm;
315182498Sroberto
3152132451Sroberto	mo = buf[4];
3153132451Sroberto	d  = buf[5];
3154132451Sroberto	y  = 256*buf[6]+buf[7];
3155132451Sroberto
3156132451Sroberto	h  = buf[8];
3157132451Sroberto	m  = buf[9];
3158132451Sroberto	s  = buf[10];
3159132451Sroberto
3160132451Sroberto	gmts = ((buf[11] == 0) ? "+" : "-");
3161132451Sroberto	gmth = buf[12];
3162132451Sroberto	gmtm = buf[13];
3163132451Sroberto
3164290001Sglebius	oncore_log_f(instance, LOG_NOTICE,
3165290001Sglebius		     "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
3166290001Sglebius		     d, months[mo-1], y, h, m, s, gmts, gmth, gmtm);
316754359Sroberto}
316854359Sroberto
316954359Sroberto
317054359Sroberto
3171290001Sglebius/* Response to PPS Control message (M12 and M12+T only ) */
3172290001Sglebius
3173290001Sglebiusstatic void
3174290001Sglebiusoncore_msg_Gc(
3175290001Sglebius	struct instance *instance,
3176290001Sglebius	u_char *buf,
3177290001Sglebius	size_t len
3178290001Sglebius	)
3179290001Sglebius{
3180290001Sglebius	const char *tbl[] = {"OFF", "ON", "SATELLITE", "TRAIM" };
3181290001Sglebius
3182290001Sglebius	instance->pps_control_msg_seen = 1;
3183290001Sglebius	oncore_log_f(instance, LOG_INFO, "PPS Control set to %s",
3184290001Sglebius		     tbl[buf[4]]);
3185290001Sglebius}
3186290001Sglebius
3187290001Sglebius
3188290001Sglebius
3189132451Sroberto/* Leap Second for M12, gives all info from satellite message */
3190132451Sroberto/* also in UT v3.0 */
319182498Sroberto
319282498Srobertostatic void
3193132451Srobertooncore_msg_Gj(
319482498Sroberto	struct instance *instance,
3195132451Sroberto	u_char *buf,
3196132451Sroberto	size_t len
319782498Sroberto	)
319882498Sroberto{
3199290001Sglebius	static const char * insrem[2] = {
3200290001Sglebius		"removed",
3201290001Sglebius		"inserted"
3202290001Sglebius	};
3203290001Sglebius
3204132451Sroberto	int dt;
3205290001Sglebius	const char *cp;
320654359Sroberto
3207132451Sroberto	instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
320854359Sroberto
3209132451Sroberto	/* print the message to verify whats there */
321054359Sroberto
3211132451Sroberto	dt = buf[5] - buf[4];
321254359Sroberto
3213290001Sglebius	oncore_log_f(instance, LOG_INFO,
3214290001Sglebius		     "Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
3215290001Sglebius		     buf[4], buf[5], 256 * buf[6] + buf[7], buf[8],
3216290001Sglebius		     buf[9], buf[10],
3217290001Sglebius		     (buf[14] + 256 *
3218290001Sglebius		         (buf[13] + 256 * (buf[12] + 256 * buf[11]))),
3219290001Sglebius		     buf[15], buf[16], buf[17]);
322054359Sroberto
3221290001Sglebius	/* There seems to be eternal confusion about when a leap second
3222290001Sglebius	 * takes place. It's the second *before* the new TAI offset
3223290001Sglebius	 * becomes effective. But since the ONCORE receiver tells us
3224290001Sglebius	 * just that, we would have to do some time/date calculations to
3225290001Sglebius	 * get the actual leap second -- that is, the one that is
3226290001Sglebius	 * deleted or inserted.
3227290001Sglebius	 *
3228290001Sglebius	 * Going through all this for a simple log is probably overkill,
3229290001Sglebius	 * so for fixing bug#1050 the message output is changed to
3230290001Sglebius	 * reflect the fact that it tells the second after the leap
3231290001Sglebius	 * second.
3232290001Sglebius	 */
3233290001Sglebius	if (dt)
3234290001Sglebius		oncore_log_f(instance, LOG_NOTICE,
3235290001Sglebius			     "Leap second %s (%d) before %04u-%02u-%02u/%02u:%02u:%02u",
3236290001Sglebius			     insrem[(dt > 0)], dt,
3237290001Sglebius			     256u * buf[6] + buf[7], buf[8], buf[9],
3238290001Sglebius			     buf[15], buf[16], buf[17]);
3239290001Sglebius
3240132451Sroberto	/* Only raise warning within a month of the leap second */
324154359Sroberto
3242182007Sroberto	instance->pp->leap = LEAP_NOWARNING;
3243182007Sroberto	cp = "Set pp.leap to LEAP_NOWARNING";
324454359Sroberto
3245132451Sroberto	if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
3246132451Sroberto	    buf[8] == instance->BEHa[4]) {	/* month */
3247132451Sroberto		if (dt) {
3248132451Sroberto			if (dt < 0) {
3249182007Sroberto				instance->pp->leap = LEAP_DELSECOND;
3250182007Sroberto				cp = "Set pp.leap to LEAP_DELSECOND";
3251132451Sroberto			} else {
3252182007Sroberto				instance->pp->leap = LEAP_ADDSECOND;
3253182007Sroberto				cp = "Set pp.leap to LEAP_ADDSECOND";
3254132451Sroberto			}
325554359Sroberto		}
3256132451Sroberto	}
3257290001Sglebius	oncore_log(instance, LOG_INFO, cp);
3258132451Sroberto}
325954359Sroberto
326054359Sroberto
326154359Sroberto
3262132451Sroberto/* Power on failure */
326354359Sroberto
3264132451Srobertostatic void
3265132451Srobertooncore_msg_Sz(
3266132451Sroberto	struct instance *instance,
3267132451Sroberto	u_char *buf,
3268132451Sroberto	size_t len
3269132451Sroberto	)
3270132451Sroberto{
3271132451Sroberto	if (instance && instance->peer) {
3272290001Sglebius		oncore_log(instance, LOG_ERR, "Oncore: System Failure at Power On");
3273132451Sroberto		oncore_shutdown(instance->unit, instance->peer);
327454359Sroberto	}
3275132451Sroberto}
327654359Sroberto
3277132451Sroberto/************** Small Subroutines ***************/
327854359Sroberto
327954359Sroberto
3280132451Srobertostatic void
3281132451Srobertooncore_antenna_report(
3282132451Sroberto	struct instance *instance,
3283132451Sroberto	enum antenna_state new_state)
3284132451Sroberto{
3285290001Sglebius	const char *cp;
3286132451Sroberto
3287132451Sroberto	if (instance->ant_state == new_state)
328854359Sroberto		return;
328954359Sroberto
3290132451Sroberto	switch (new_state) {
3291132451Sroberto	case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK";                   break;
3292132451Sroberto	case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)";  break;
3293132451Sroberto	case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
3294132451Sroberto	case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)";   break;
3295132451Sroberto	default:		cp = "GPS antenna: ?";                    break;
329656746Sroberto	}
329754359Sroberto
3298132451Sroberto	instance->ant_state = new_state;
3299290001Sglebius	oncore_log(instance, LOG_NOTICE, cp);
3300132451Sroberto}
330154359Sroberto
330254359Sroberto
330354359Sroberto
3304132451Srobertostatic void
3305132451Srobertooncore_chan_test(
3306132451Sroberto	struct instance *instance
3307132451Sroberto	)
3308132451Sroberto{
3309132451Sroberto	/* subroutine oncore_Cj_id has determined the number of channels from the
3310132451Sroberto	 * model number of the attached oncore.  This is not always correct since
3311132451Sroberto	 * the oncore could have non-standard firmware.  Here we check (independently) by
3312132451Sroberto	 * trying a 6, 8, and 12 chan command, and see which responds.
3313132451Sroberto	 * Caution: more than one CAN respond.
331482498Sroberto	 *
3315132451Sroberto	 * This #chan is used by the code rather than that calculated from the model number.
331682498Sroberto	 */
331782498Sroberto
3318132451Sroberto	instance->o_state = ONCORE_CHECK_CHAN;
3319290001Sglebius	oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_CHAN");
332054359Sroberto
3321132451Sroberto	instance->count3 = 1;
3322290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
3323290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
3324290001Sglebius	oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
3325132451Sroberto}
332682498Sroberto
332782498Sroberto
332882498Sroberto
3329132451Sroberto/* check for a GOOD Almanac, have we got one yet? */
333054359Sroberto
3331132451Srobertostatic void
3332132451Srobertooncore_check_almanac(
3333132451Sroberto	struct instance *instance
3334132451Sroberto	)
3335132451Sroberto{
3336132451Sroberto	if (instance->chan == 6) {
3337132451Sroberto		instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
3338132451Sroberto		instance->rsm.bad_fix	  = instance->BEHa[64]&0x52;
3339132451Sroberto	} else if (instance->chan == 8) {
3340132451Sroberto		instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
3341132451Sroberto		instance->rsm.bad_fix	  = instance->BEHa[72]&0x52;
3342132451Sroberto	} else if (instance->chan == 12) {
3343182007Sroberto		int bits1, bits2, bits3;
3344132451Sroberto
3345132451Sroberto		bits1 = (instance->BEHa[129]>>5) & 0x7; 	/* actually Ha */
3346132451Sroberto		bits2 = instance->BEHa[130];
3347132451Sroberto		instance->rsm.bad_almanac = (bits2 & 0x80);
3348132451Sroberto		instance->rsm.bad_fix	  = (bits2 & 0x8) || (bits1 == 0x2);
3349132451Sroberto					  /* too few sat     Bad Geom	  */
3350182007Sroberto
3351182007Sroberto		bits3 = instance->BEHa[141];	/* UTC parameters */
3352182007Sroberto		if (!instance->count5_set && (bits3 & 0xC0)) {
3353290001Sglebius			instance->count5 = 4;	/* was 2 [Bug 1766] */
3354182007Sroberto			instance->count5_set = 1;
3355182007Sroberto		}
3356290001Sglebius#ifdef ONCORE_VERBOSE_CHECK_ALMANAC
3357290001Sglebius		oncore_log_f(instance, LOG_DEBUG,
3358290001Sglebius			     "DEBUG BITS: (%x %x), (%x %x %x),  %x %x %x %x %x",
3359290001Sglebius			     instance->BEHa[129], instance->BEHa[130],
3360290001Sglebius			     bits1, bits2, bits3,
3361290001Sglebius			     instance->mode == MODE_0D,
3362290001Sglebius			     instance->mode == MODE_2D,
3363290001Sglebius			     instance->mode == MODE_3D,
3364290001Sglebius			     instance->rsm.bad_almanac,
3365290001Sglebius			     instance->rsm.bad_fix);
3366290001Sglebius		}
336754359Sroberto#endif
3368132451Sroberto	}
3369132451Sroberto}
337054359Sroberto
337154359Sroberto
337254359Sroberto
3373132451Sroberto/* check the antenna for changes (did it get unplugged?) */
337454359Sroberto
3375132451Srobertostatic void
3376132451Srobertooncore_check_antenna(
3377132451Sroberto	struct instance *instance
3378132451Sroberto	)
3379132451Sroberto{
3380132451Sroberto	enum antenna_state antenna;		/* antenna state */
338182498Sroberto
3382132451Sroberto	antenna = instance->ant_state;
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{
3421290001Sglebius	oncore_cmd_Bl[2] = 1;				/* just to be sure */
3422290001Sglebius	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))
3427290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3428290001Sglebius				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
3435290001Sglebius		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;
3449290001Sglebius			if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) {
3450290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3451290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl));
3452290001Sglebius			}
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
3504290001Sglebius	if (MSL)	/* not set ! */
3505290001Sglebius		oncore_log_f(instance, LOG_INFO,
3506290001Sglebius		             "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
3528290001Sglebius#ifndef ONCORE_VERBOSE_LOAD_ALMANAC
3529290001Sglebius	for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
3530290001Sglebius	     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	}
3538290001Sglebius#else	/* ONCORE_VERBOSE_LOAD_ALMANAC follows */
3539290001Sglebius	for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2));
3540290001Sglebius	     cp += (n+3)) {
3541290001Sglebius		oncore_log_f(instance, LOG_DEBUG, "See %c%c%c%c %d",
3542290001Sglebius			   *(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) {
3548290001Sglebius					oncore_log(instance, LOG_DEBUG, "GOOD SF");
3549132451Sroberto					write(instance->ttyfd, cp, n);
3550132451Sroberto				} else
3551290001Sglebius					oncore_log(instance, LOG_DEBUG, "BAD SF");
3552132451Sroberto			} else
3553290001Sglebius				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 */
3561290001Sglebius		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);
3572290001Sglebius#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
3573290001Sglebius				oncore_log_f(instance, LOG_DEBUG,
3574290001Sglebius					     "SHMEM posn = %ld (%d, %d, %d)",
3575290001Sglebius					     (long)(cp-instance->shmem),
3576290001Sglebius					     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
3590290001Sglebius	GETTIMEOFDAY(&tv, 0);
3591132451Sroberto	tm = gmtime((const time_t *) &tv.tv_sec);
3592290001Sglebius
3593290001Sglebius#ifdef ONCORE_VERBOSE_LOAD_ALMANAC
3594290001Sglebius	oncore_log_f(instance, LOG_DEBUG, "DATE %d %d %d, %d %d %d",
3595290001Sglebius		     1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
3596290001Sglebius		     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;
3610290001Sglebius		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Gb));
3611132451Sroberto	} else {
3612132451Sroberto		/* First set GMT offset to zero */
3613132451Sroberto
3614290001Sglebius		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;
3621290001Sglebius		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;
3627290001Sglebius		oncore_sendmsg(instance, Cmd,  sizeof(oncore_cmd_Aa));
3628132451Sroberto	}
3629132451Sroberto
3630290001Sglebius	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{
3643290001Sglebius#ifdef ONCORE_VERBOSE_CB
3644132451Sroberto	int	ii;
3645290001Sglebius	char	Msg[160], Msg2[10];
364654359Sroberto
3647290001Sglebius	oncore_log_f(instance, LOG_DEBUG, "DEBUG: See: %c%c%c%c", *(cp),
3648290001Sglebius		     *(cp+1), *(cp+2), *(cp+3));
364954359Sroberto
3650290001Sglebius	snprintf(Msg, sizeof(Msg), "DEBUG: Cb: [%d,%d]", *(cp+4),
3651290001Sglebius		*(cp+5));
3652290001Sglebius	for (ii = 0; ii < 33; ii++) {
3653290001Sglebius		snprintf(Msg2, sizeof(Msg2), " %d", *(cp+ii));
3654290001Sglebius		strlcat(Msg, Msg2, sizeof(Msg));
3655290001Sglebius	}
3656290001Sglebius	oncore_log(instance, LOG_DEBUG, Msg);
3657290001Sglebius
3658290001Sglebius	oncore_log_f(instance, LOG_DEBUG, "Debug: Cb: [%d,%d]", *(cp+4),
3659290001Sglebius		     *(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{
3692290001Sglebius	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
3697290001Sglebius	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.;
3717290001Sglebius	oncore_log_f(instance, LOG_INFO,
3718290001Sglebius		     "Lat = %c %11.7fdeg,    Long = %c %11.7fdeg,    Alt = %5.2fm (%5.2fft) GPS",
3719290001Sglebius		     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.;
3727290001Sglebius	oncore_log_f(instance, LOG_INFO,
3728290001Sglebius		     "Lat = %c %3ddeg %7.4fm,   Long = %c %3ddeg %8.5fm,  Alt = %7.2fm (%7.2fft) GPS",
3729290001Sglebius		     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.;
3737290001Sglebius	oncore_log_f(instance, LOG_INFO,
3738290001Sglebius		     "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS",
3739290001Sglebius		     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(
3750290001Sglebius	struct	instance *instance,
3751132451Sroberto	u_char *ptr,
375282498Sroberto	size_t len
375356746Sroberto	)
375456746Sroberto{
3755290001Sglebius	int	fd;
3756132451Sroberto	u_char cs = 0;
375756746Sroberto
3758290001Sglebius	fd = instance->ttyfd;
3759290001Sglebius#ifdef ONCORE_VERBOSE_SENDMSG
3760290001Sglebius	if (debug > 4) {
3761290001Sglebius		oncore_log_f(instance, LOG_DEBUG, "ONCORE: Send @@%c%c %d",
3762290001Sglebius			     ptr[0], ptr[1], (int)len);
3763290001Sglebius	}
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)
3787290001Sglebius		oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
3788132451Sroberto	else {
3789290001Sglebius		oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
3790290001Sglebius		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;
3801290001Sglebius		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;
3806290001Sglebius		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;
3816290001Sglebius			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);
3820290001Sglebius			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);
3824290001Sglebius			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;
3829290001Sglebius			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)
3835290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Gd1,  sizeof(oncore_cmd_Gd1));
3836132451Sroberto		else
3837290001Sglebius			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
3853290001Sglebius	oncore_log_f(instance, LOG_INFO, "Input   says TRAIM = %d",
3854290001Sglebius		     instance->traim_in);
3855290001Sglebius	oncore_log_f(instance, LOG_INFO, "Model # says TRAIM = %d",
3856290001Sglebius		     instance->traim_id);
3857290001Sglebius	oncore_log_f(instance, LOG_INFO, "Testing says TRAIM = %d",
3858290001Sglebius		     instance->traim_ck);
3859290001Sglebius	oncore_log_f(instance, LOG_INFO, "Using        TRAIM = %d",
3860290001Sglebius		     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)
3866290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
3867132451Sroberto		else if (instance->chan == 8)
3868290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
3869132451Sroberto		else	/* chan == 12 */
3870290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
3871290001Sglebius			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)
3890290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Gd2,  sizeof(oncore_cmd_Gd2));  /* 2D */
3891132451Sroberto			else
3892290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Gd0,  sizeof(oncore_cmd_Gd0));  /* 3D */
3893132451Sroberto		} else {
3894132451Sroberto			if (instance->saw_At) { 		/* out of 0D -> 3D mode */
3895290001Sglebius				oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0));
3896132451Sroberto				if (instance->shmem_Posn == 2)	/* 3D -> 2D mode */
3897290001Sglebius					oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
3898132451Sroberto			} else
3899290001Sglebius				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)
3904290001Sglebius			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 */
3908290001Sglebius					oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
3909290001Sglebius				oncore_sendmsg(instance, oncore_cmd_At1,  sizeof(oncore_cmd_At1)); /* to 0D mode */
3910132451Sroberto			} else
3911290001Sglebius				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))) {
3940290001Sglebius			oncore_log(instance, LOG_INFO, "Now in 0D mode");
3941132451Sroberto
3942132451Sroberto			if (instance->chan == 12)
3943290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
3944132451Sroberto			else
3945290001Sglebius				oncore_sendmsg(instance, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
3946132451Sroberto
3947290001Sglebius			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
3973290001Sglebius		oncore_log_f(instance, LOG_NOTICE,
3974290001Sglebius			     "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
3975290001Sglebius			     instance->ss_lat, instance->ss_long,
3976290001Sglebius			     instance->ss_ht);
3977132451Sroberto		lat = instance->ss_lat/3600000.;
3978132451Sroberto		lon = instance->ss_long/3600000.;
3979132451Sroberto		ht  = instance->ss_ht/100;
3980290001Sglebius		oncore_log_f(instance, LOG_NOTICE,
3981290001Sglebius			     "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
3982290001Sglebius			     lat, lon, ht);
3983132451Sroberto
3984132451Sroberto		oncore_set_posn(instance);
3985132451Sroberto
3986290001Sglebius		oncore_log(instance, LOG_INFO, "Now in 0D mode");
3987132451Sroberto
3988290001Sglebius		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) {
4001290001Sglebius		instance->counta++;
4002290001Sglebius		if (instance->counta%5 == 0)
4003290001Sglebius			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)
4020290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
4021132451Sroberto		else if (instance->chan == 8)
4022290001Sglebius			oncore_sendmsg(instance, oncore_cmd_En, sizeof(oncore_cmd_En));
4023132451Sroberto		else if (instance->chan == 12) {
4024290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */
4025290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */
4026290001Sglebius			oncore_sendmsg(instance, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */
4027132451Sroberto		}
4028132451Sroberto		instance->traim_delay = 1;
4029132451Sroberto
4030290001Sglebius		oncore_log(instance, LOG_NOTICE, "Have now loaded an ALMANAC");
4031132451Sroberto
4032132451Sroberto		instance->o_state = ONCORE_RUN;
4033290001Sglebius		oncore_log(instance, LOG_NOTICE, "state = ONCORE_RUN");
4034132451Sroberto	}
4035132451Sroberto	return(0);
4036132451Sroberto}
4037132451Sroberto
4038132451Sroberto
4039132451Sroberto
4040290001Sglebiusstatic void
4041290001Sglebiusoncore_log (
4042290001Sglebius	struct instance *instance,
4043290001Sglebius	int log_level,
4044290001Sglebius	const char *msg
4045290001Sglebius	)
4046290001Sglebius{
4047290001Sglebius	msyslog(log_level, "ONCORE[%d]: %s", instance->unit, msg);
4048290001Sglebius	mprintf_clock_stats(&instance->peer->srcadr, "ONCORE[%d]: %s",
4049290001Sglebius			    instance->unit, msg);
4050290001Sglebius}
4051290001Sglebius
4052290001Sglebius
4053290001Sglebiusstatic int
4054290001Sglebiusoncore_log_f(
4055290001Sglebius	struct instance *	instance,
4056290001Sglebius	int			log_level,
4057290001Sglebius	const char *		fmt,
4058290001Sglebius	...
4059290001Sglebius	)
4060290001Sglebius{
4061290001Sglebius	va_list	ap;
4062290001Sglebius	int	rc;
4063290001Sglebius	char	msg[512];
4064290001Sglebius
4065290001Sglebius	va_start(ap, fmt);
4066290001Sglebius	rc = mvsnprintf(msg, sizeof(msg), fmt, ap);
4067290001Sglebius	va_end(ap);
4068290001Sglebius	oncore_log(instance, log_level, msg);
4069290001Sglebius
4070290001Sglebius#ifdef ONCORE_VERBOSE_ONCORE_LOG
4071290001Sglebius	instance->max_len = max(strlen(msg), instance->max_len);
4072290001Sglebius	instance->max_count++;
4073290001Sglebius	if (instance->max_count % 100 == 0)
4074290001Sglebius		oncore_log_f(instance, LOG_INFO,
4075290001Sglebius			    "Max Message Length so far is %d",
4076290001Sglebius			    instance->max_len);
4077290001Sglebius#endif
4078290001Sglebius	return rc;
4079290001Sglebius}
4080290001Sglebius
408154359Sroberto#else
408254359Srobertoint refclock_oncore_bs;
4083290001Sglebius#endif	/* REFCLOCK && CLOCK_ONCORE */
4084