refclock_oncore.c revision 132451
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * refclock_oncore.c
10 *
11 * Driver for some of the various the Motorola Oncore GPS receivers.
12 *   should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
13 *	The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
14 *	than the others.
15 *	The receivers without position hold (GT, GT+) will be less accurate.
16 *
17 * Tested with:
18 *
19 *		(UT)				   (VP)
20 *   COPYRIGHT 1991-1997 MOTOROLA INC.	COPYRIGHT 1991-1996 MOTOROLA INC.
21 *   SFTW P/N #     98-P36848P		SFTW P/N # 98-P36830P
22 *   SOFTWARE VER # 2			SOFTWARE VER # 8
23 *   SOFTWARE REV # 2			SOFTWARE REV # 8
24 *   SOFTWARE DATE  APR 24 1998 	SOFTWARE DATE  06 Aug 1996
25 *   MODEL #	R1121N1114		MODEL #    B4121P1155
26 *   HWDR P/N # 1			HDWR P/N # _
27 *   SERIAL #	R0010A			SERIAL #   SSG0226478
28 *   MANUFACTUR DATE 6H07		MANUFACTUR DATE 7E02
29 *					OPTIONS LIST	IB
30 *
31 *	      (Basic)				   (M12)
32 *   COPYRIGHT 1991-1994 MOTOROLA INC.	COPYRIGHT 1991-2000 MOTOROLA INC.
33 *   SFTW P/N # 98-P39949M		SFTW P/N # 61-G10002A
34 *   SOFTWARE VER # 5			SOFTWARE VER # 1
35 *   SOFTWARE REV # 0			SOFTWARE REV # 3
36 *   SOFTWARE DATE  20 JAN 1994 	SOFTWARE DATE  Mar 13 2000
37 *   MODEL #	A11121P116		MODEL #    P143T12NR1
38 *   HDWR P/N # _			HWDR P/N # 1
39 *   SERIAL #	SSG0049809		SERIAL #   P003UD
40 *   MANUFACTUR DATE 417AMA199		MANUFACTUR DATE 0C27
41 *   OPTIONS LIST    AB
42 *
43 * --------------------------------------------------------------------------
44 * This code uses the two devices
45 *	/dev/oncore.serial.n
46 *	/dev/oncore.pps.n
47 * which may be linked to the same device.
48 * and can read initialization data from the file
49 *	/etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
50 *	n or N are the unit number, viz 127.127.30.N.
51 * --------------------------------------------------------------------------
52 * Reg.Clemens <reg@dwf.com> Sep98.
53 *  Original code written for FreeBSD.
54 *  With these mods it works on FreeBSD, SunOS, Solaris and Linux
55 *    (SunOS 4.1.3 + ppsclock)
56 *    (Solaris7 + MU4)
57 *    (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
58 *
59 *  Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
60 *  state machine state) are printed to CLOCKSTATS if that file is enabled
61 *  in /etc/ntp.conf.
62 *
63 * --------------------------------------------------------------------------
64 *
65 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
66 * doing an average of 10000 valid 2D and 3D fixes is what the automatic
67 * site survey mode does.  Looking at the output from the receiver
68 * it seems like it is only using 3D fixes.
69 * When we do it ourselves, take 10000 3D fixes.
70 */
71
72#define POS_HOLD_AVERAGE	10000	/* nb, 10000s ~= 2h45m */
73
74/*
75 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
76 * "STATUS" line in the oncore config file, which contains the most recent
77 * copy of all types of messages we recognize.	This file can be mmap(2)'ed
78 * by monitoring and statistics programs.
79 *
80 * See separate HTML documentation for this option.
81 */
82
83#ifdef HAVE_CONFIG_H
84#include <config.h>
85#endif
86
87#if defined(REFCLOCK) && defined(CLOCK_ONCORE) && defined(HAVE_PPSAPI)
88
89#include "ntpd.h"
90#include "ntp_io.h"
91#include "ntp_unixtime.h"
92#include "ntp_refclock.h"
93#include "ntp_stdlib.h"
94
95#include <stdio.h>
96#include <ctype.h>
97#include <sys/stat.h>
98#ifdef ONCORE_SHMEM_STATUS
99# ifdef HAVE_SYS_MMAN_H
100#  include <sys/mman.h>
101#  ifndef MAP_FAILED
102#   define MAP_FAILED ((u_char *) -1)
103#  endif  /* not MAP_FAILED */
104# endif /* HAVE_SYS_MMAN_H */
105#endif /* ONCORE_SHMEM_STATUS */
106
107#ifdef HAVE_PPSAPI
108# ifdef HAVE_TIMEPPS_H
109#  include <timepps.h>
110# else
111#  ifdef HAVE_SYS_TIMEPPS_H
112#   include <sys/timepps.h>
113#  endif
114# endif
115#endif
116
117#ifdef HAVE_SYS_SIO_H
118# include <sys/sio.h>
119#endif
120
121#ifdef HAVE_SYS_TERMIOS_H
122# include <sys/termios.h>
123#endif
124
125#ifdef HAVE_SYS_PPSCLOCK_H
126# include <sys/ppsclock.h>
127#endif
128
129#ifndef HAVE_STRUCT_PPSCLOCKEV
130struct ppsclockev {
131# ifdef HAVE_STRUCT_TIMESPEC
132	struct timespec tv;
133# else
134	struct timeval tv;
135# endif
136	u_int serial;
137};
138#endif /* not HAVE_STRUCT_PPSCLOCKEV */
139
140enum receive_state {
141	ONCORE_NO_IDEA,
142	ONCORE_CHECK_ID,
143	ONCORE_CHECK_CHAN,
144	ONCORE_HAVE_CHAN,
145	ONCORE_RESET_SENT,
146	ONCORE_TEST_SENT,
147	ONCORE_INIT,
148	ONCORE_ALMANAC,
149	ONCORE_RUN
150};
151
152enum site_survey_state {
153	ONCORE_SS_UNKNOWN,
154	ONCORE_SS_TESTING,
155	ONCORE_SS_HW,
156	ONCORE_SS_SW,
157	ONCORE_SS_DONE
158};
159
160enum antenna_state {
161      ONCORE_ANTENNA_UNKNOWN = -1,
162      ONCORE_ANTENNA_OK      =	0,
163      ONCORE_ANTENNA_OC      =	1,
164      ONCORE_ANTENNA_UC      =	2,
165      ONCORE_ANTENNA_NV      =	3
166};
167
168/* Model Name, derived from the @@Cj message.
169 * Used to initialize some variables.
170 */
171
172enum oncore_model {
173	ONCORE_BASIC,
174	ONCORE_PVT6,
175	ONCORE_VP,
176	ONCORE_UT,
177	ONCORE_UTPLUS,
178	ONCORE_GT,
179	ONCORE_GTPLUS,
180	ONCORE_SL,
181	ONCORE_M12,
182	ONCORE_UNKNOWN
183};
184
185/* the bits that describe these properties are in the same place
186 * on the VP/UT, but have moved on the M12.  As such we extract
187 * them, and use them from this struct.
188 *
189 */
190
191struct RSM {
192	u_char	posn0D;
193	u_char	posn2D;
194	u_char	posn3D;
195	u_char	bad_almanac;
196	u_char	bad_fix;
197};
198
199/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
200 * see what mode it is in.  The bits on the M12 are multiplexed with
201 * other messages, so we have to 'keep' the last known mode here.
202 */
203
204enum posn_mode {
205	MODE_UNKNOWN,
206	MODE_0D,
207	MODE_2D,
208	MODE_3D
209};
210
211struct instance {
212	int	unit;		/* 127.127.30.unit */
213	struct	refclockproc *pp;
214	struct	peer *peer;
215
216	int	ttyfd;		/* TTY file descriptor */
217	int	ppsfd;		/* PPS file descriptor */
218	int	shmemfd;	/* Status shm descriptor */
219#ifdef HAVE_PPSAPI
220	pps_handle_t pps_h;
221	pps_params_t pps_p;
222#endif
223	enum receive_state o_state;		/* Receive state */
224	enum posn_mode mode;			/* 0D, 2D, 3D */
225	enum site_survey_state site_survey;	/* Site Survey state */
226	enum antenna_state ant_state;		/* antenna state */
227
228	int	Bj_day;
229
230	u_long	delay;		/* ns */
231	long	offset; 	/* ns */
232
233	u_char	*shmem;
234	char	*shmem_fname;
235	u_int	shmem_Cb;
236	u_int	shmem_Ba;
237	u_int	shmem_Ea;
238	u_int	shmem_Ha;
239	u_char	shmem_reset;
240	u_char	shmem_Posn;
241	u_char	shmem_bad_Ea;
242	u_char	almanac_from_shmem;
243
244	double	ss_lat;
245	double	ss_long;
246	double	ss_ht;
247	double	dH;
248	int	ss_count;
249	u_char	posn_set;
250
251	enum oncore_model model;
252	u_int	version;
253	u_int	revision;
254
255	u_char	chan;		/* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
256	s_char	traim;		/* do we have traim? yes UT/VP, no BASIC, GT, M12+T, -1 unknown, 0 no, +1 yes */
257
258				/* the following 7 are all timing counters */
259	u_char	traim_delay;	/* seconds counter, waiting for reply */
260	u_char	count;		/* cycles thru Ea before starting */
261	u_char	count1; 	/* cycles thru Ea after SS_TESTING, waiting for SS_HW */
262	u_char	count2; 	/* cycles thru Ea after count, to check for @@Ea */
263	u_char	count3; 	/* cycles thru Ea checking for # channels */
264	u_char	count4; 	/* cycles thru leap after Gj to issue Bj */
265	u_char	pollcnt;
266	u_char	timeout;	/* count to retry Cj after Fa self-test */
267
268	struct	RSM rsm;	/* bits extracted from Receiver Status Msg in @@Ea */
269	u_char	printed;
270	u_char	polled;
271	u_long	ev_serial;
272	int	Rcvptr;
273	u_char	Rcvbuf[500];
274	u_char	BEHa[160];	/* Ba, Ea or Ha */
275	u_char	BEHn[80];	/* Bn , En , or Hn */
276	u_char	Cj[300];
277	u_char	Ag;		/* Satellite mask angle */
278	u_char	saw_At;
279	u_char	saw_Ay;
280	u_char	saw_Az;
281	s_char	saw_Gj;
282	u_char	have_dH;
283	u_char	init_type;
284	s_char	saw_tooth;
285	s_char	chan_in;	/* chan number from INPUT, will always use it */
286	u_char	chan_id;	/* chan number determined from part number */
287	u_char	chan_ck;	/* chan number determined by sending commands to hardware */
288	s_char	traim_in;	/* TRAIM from INPUT, will always use it */
289	s_char	traim_id;	/* TRAIM determined from part number */
290	u_char	traim_ck;	/* TRAIM determined by sending commands to hardware */
291	u_char	once;		/* one pass code at top of BaEaHa */
292	s_char	assert;
293	u_char	hardpps;
294};
295
296#define rcvbuf	instance->Rcvbuf
297#define rcvptr	instance->Rcvptr
298
299static	int	oncore_start	      P((int, struct peer *));
300static	void	oncore_control	      P((int, struct refclockstat *, struct refclockstat *, struct peer *));
301static	void	oncore_poll	      P((int, struct peer *));
302static	void	oncore_shutdown       P((int, struct peer *));
303static	void	oncore_consume	      P((struct instance *));
304static	void	oncore_read_config    P((struct instance *));
305static	void	oncore_receive	      P((struct recvbuf *));
306static	int	oncore_ppsapi	      P((struct instance *));
307static	void	oncore_get_timestamp  P((struct instance *, long, long));
308static	void	oncore_init_shmem     P((struct instance *));
309
310static	void	oncore_antenna_report P((struct instance *, enum antenna_state));
311static	void	oncore_chan_test      P((struct instance *));
312static	void	oncore_check_almanac  P((struct instance *));
313static	void	oncore_check_antenna  P((struct instance *));
314static	void	oncore_check_leap_sec P((struct instance *));
315static	int	oncore_checksum_ok    P((u_char *, int));
316static	void	oncore_compute_dH     P((struct instance *));
317static	void	oncore_load_almanac   P((struct instance *));
318static	void	oncore_print_Cb       P((struct instance *, u_char *));
319/* static  void    oncore_print_array	 P((u_char *, int));	*/
320static	void	oncore_print_posn     P((struct instance *));
321static	void	oncore_sendmsg	      P((int, u_char *, size_t));
322static	void	oncore_set_posn       P((struct instance *));
323static	void	oncore_set_traim      P((struct instance *));
324static	void	oncore_shmem_get_3D   P((struct instance *));
325static	void	oncore_ss	      P((struct instance *));
326static	int	oncore_wait_almanac   P((struct instance *));
327
328static	void	oncore_msg_any	   P((struct instance *, u_char *, size_t, int));
329static	void	oncore_msg_Adef    P((struct instance *, u_char *, size_t));
330static	void	oncore_msg_Ag	   P((struct instance *, u_char *, size_t));
331static	void	oncore_msg_As	   P((struct instance *, u_char *, size_t));
332static	void	oncore_msg_At	   P((struct instance *, u_char *, size_t));
333static	void	oncore_msg_Ay	   P((struct instance *, u_char *, size_t));
334static	void	oncore_msg_Az	   P((struct instance *, u_char *, size_t));
335static	void	oncore_msg_BaEaHa  P((struct instance *, u_char *, size_t));
336static	void	oncore_msg_Bd	   P((struct instance *, u_char *, size_t));
337static	void	oncore_msg_Bj	   P((struct instance *, u_char *, size_t));
338static	void	oncore_msg_BnEnHn  P((struct instance *, u_char *, size_t));
339static	void	oncore_msg_CaFaIa  P((struct instance *, u_char *, size_t));
340static	void	oncore_msg_Cb	   P((struct instance *, u_char *, size_t));
341static	void	oncore_msg_Cf	   P((struct instance *, u_char *, size_t));
342static	void	oncore_msg_Cj	   P((struct instance *, u_char *, size_t));
343static	void	oncore_msg_Cj_id   P((struct instance *, u_char *, size_t));
344static	void	oncore_msg_Cj_init P((struct instance *, u_char *, size_t));
345static	void	oncore_msg_Ga	   P((struct instance *, u_char *, size_t));
346static	void	oncore_msg_Gb	   P((struct instance *, u_char *, size_t));
347static	void	oncore_msg_Gd	   P((struct instance *, u_char *, size_t));
348static	void	oncore_msg_Gj	   P((struct instance *, u_char *, size_t));
349static	void	oncore_msg_Sz	   P((struct instance *, u_char *, size_t));
350
351struct	refclock refclock_oncore = {
352	oncore_start,		/* start up driver */
353	oncore_shutdown,	/* shut down driver */
354	oncore_poll,		/* transmit poll message */
355	oncore_control, 	/* fudge (flag) control messages */
356	noentry,		/* not used */
357	noentry,		/* not used */
358	NOFLAGS 		/* not used */
359};
360
361/*
362 * Understanding the next bit here is not easy unless you have a manual
363 * for the the various Oncore Models.
364 */
365
366static struct msg_desc {
367	const char	flag[3];
368	const int	len;
369	void		(*handler) P((struct instance *, u_char *, size_t));
370	const char	*fmt;
371	int		shmem;
372} oncore_messages[] = {
373			/* Ea and En first since they're most common */
374	{ "Ea",  76,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
375	{ "Ba",  68,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
376	{ "Ha", 154,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
377	{ "Bn",  59,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
378	{ "En",  69,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
379	{ "Hn",  78,    oncore_msg_BnEnHn, "" },
380	{ "Ab",  10,    0,                 "" },
381	{ "Ac",  11,    0,                 "" },
382	{ "Ad",  11,    oncore_msg_Adef,   "" },
383	{ "Ae",  11,    oncore_msg_Adef,   "" },
384	{ "Af",  15,    oncore_msg_Adef,   "" },
385	{ "Ag",   8,    oncore_msg_Ag,     "" }, /* Satellite mask angle */
386	{ "As",  20,    oncore_msg_As,     "" },
387	{ "At",   8,    oncore_msg_At,     "" },
388	{ "Au",  12,    0,                 "" },
389	{ "Av",   8,    0,                 "" },
390	{ "Aw",   8,    0,                 "" },
391	{ "Ay",  11,    oncore_msg_Ay,     "" },
392	{ "Az",  11,    oncore_msg_Az,     "" },
393	{ "AB",   8,    0,                 "" },
394	{ "Bb",  92,    0,                 "" },
395	{ "Bd",  23,    oncore_msg_Bd,     "" },
396	{ "Bj",   8,    oncore_msg_Bj,     "" },
397	{ "Ca",   9,    oncore_msg_CaFaIa, "" },
398	{ "Cb",  33,    oncore_msg_Cb,     "" },
399	{ "Cf",   7,    oncore_msg_Cf,     "" },
400	{ "Cg",   8,    0,                 "" },
401	{ "Ch",   9,    0,                 "" },
402	{ "Cj", 294,    oncore_msg_Cj,     "" },
403	{ "Ek",  71,    0,                 "" },
404	{ "Fa",   9,    oncore_msg_CaFaIa, "" },
405	{ "Ga",  20,    oncore_msg_Ga,     "" },
406	{ "Gb",  17,    oncore_msg_Gb,     "" },
407	{ "Gc",   8,    0,                 "" },
408	{ "Gd",   8,    oncore_msg_Gd,     "" },
409	{ "Ge",   8,    0,                 "" },
410	{ "Gj",  21,    oncore_msg_Gj,     "" },
411	{ "Ia",  10,    oncore_msg_CaFaIa, "" },
412	{ "Sz",   8,    oncore_msg_Sz,     "" },
413	{ {0},	  7,	0,		   "" }
414};
415
416
417static u_char oncore_cmd_Aa[]  = { 'A', 'a', 0, 0, 0 }; 			    /* 6/8	Time of Day				*/
418static u_char oncore_cmd_Ab[]  = { 'A', 'b', 0, 0, 0 }; 			    /* 6/8	GMT Correction				*/
419static u_char oncore_cmd_AB[]  = { 'A', 'B', 4 };				    /* VP	Application Type: Static		*/
420static u_char oncore_cmd_Ac[]  = { 'A', 'c', 0, 0, 0, 0 };			    /* 6/8	Date					*/
421static u_char oncore_cmd_Ad[]  = { 'A', 'd', 0,0,0,0 }; 			    /* 6/8	Latitude				*/
422static u_char oncore_cmd_Ae[]  = { 'A', 'e', 0,0,0,0 }; 			    /* 6/8	Longitude				*/
423static u_char oncore_cmd_Af[]  = { 'A', 'f', 0,0,0,0, 0 };			    /* 6/8	Height					*/
424static u_char oncore_cmd_Ag[]  = { 'A', 'g', 0 };				    /* 6/8/12	Satellite Mask Angle			*/
425static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff };				    /* 6/8/12	Satellite Mask Angle: read		*/
426static 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			*/
427static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff,		    /* 6/8/12	Posn Hold Readback			*/
428					     0x7f,0xff,0xff,0xff,		    /*		 on UT+ this doesnt work with 0xff	*/
429					     0x7f,0xff,0xff,0xff, 0xff };	    /*		 but does work with 0x7f (sigh).	*/
430static u_char oncore_cmd_At0[] = { 'A', 't', 0 };				    /* 6/8	Posn Hold: off				*/
431static u_char oncore_cmd_At1[] = { 'A', 't', 1 };				    /* 6/8	Posn Hold: on				*/
432static u_char oncore_cmd_At2[] = { 'A', 't', 2 };				    /* 6/8	Posn Hold: Start Site Survey		*/
433static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff };				    /* 6/8	Posn Hold: Read Back			*/
434static u_char oncore_cmd_Au[]  = { 'A', 'u', 0,0,0,0, 0 };			    /* GT/M12	Altitude Hold Ht.			*/
435static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 };				    /* VP/GT	Altitude Hold: off			*/
436static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 };				    /* VP/GT	Altitude Hold: on			*/
437static u_char oncore_cmd_Aw[]  = { 'A', 'w', 1 };				    /* 6/8/12	UTC/GPS time selection			*/
438static u_char oncore_cmd_Ay[]  = { 'A', 'y', 0, 0, 0, 0 };			    /* Timing	1PPS time offset: set			*/
439static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };		    /* Timing	1PPS time offset: Read			*/
440static u_char oncore_cmd_Az[]  = { 'A', 'z', 0, 0, 0, 0 };			    /* 6/8UT/12 1PPS Cable Delay: set			*/
441static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };		    /* 6/8UT/12 1PPS Cable Delay: Read			*/
442static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 };				    /* 6	Position/Data/Status: off		*/
443static u_char oncore_cmd_Ba[]  = { 'B', 'a', 1 };				    /* 6	Position/Data/Status: on		*/
444static u_char oncore_cmd_Bb[]  = { 'B', 'b', 1 };				    /* 6/8/12	Visible Satellites			*/
445static u_char oncore_cmd_Bd[]  = { 'B', 'd', 1 };				    /* 6/8/12?	Almanac Status Msg.			*/
446static u_char oncore_cmd_Be[]  = { 'B', 'e', 1 };				    /* 6/8/12	Request Almanac Data			*/
447static u_char oncore_cmd_Bj[]  = { 'B', 'j', 0 };				    /* 6/8	Leap Second Pending			*/
448static 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	*/
449static 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	*/
450static u_char oncore_cmd_Bnx[] = { 'B', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg on traim off	*/
451static u_char oncore_cmd_Ca[]  = { 'C', 'a' };					    /* 6	Self Test				*/
452static u_char oncore_cmd_Cf[]  = { 'C', 'f' };					    /* 6/8/12	Set to Defaults 			*/
453static u_char oncore_cmd_Cg[]  = { 'C', 'g', 1 };				    /* VP	Posn Fix/Idle Mode			*/
454static u_char oncore_cmd_Cj[]  = { 'C', 'j' };					    /* 6/8/12	Receiver ID				*/
455static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 };				    /* 8	Position/Data/Status: off		*/
456static u_char oncore_cmd_Ea[]  = { 'E', 'a', 1 };				    /* 8	Position/Data/Status: on		*/
457static u_char oncore_cmd_Ek[]  = { 'E', 'k', 0 }; /* just turn off */		    /* 8	Posn/Status/Data - extension		*/
458static 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	*/
459static 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	*/
460static u_char oncore_cmd_Enx[] = { 'E', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg on traim off	*/
461static u_char oncore_cmd_Fa[]  = { 'F', 'a' };					    /* 8	Self Test				*/
462static u_char oncore_cmd_Ga[]  = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };	    /* 12	Position Set				*/
463static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff,		    /* 12	Position Set: Read			*/
464					     0xff, 0xff, 0xff, 0xff,		    /*							*/
465					     0xff, 0xff, 0xff, 0xff, 0xff };	    /*							*/
466static u_char oncore_cmd_Gb[]  = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	    /* 12	set Date/Time				*/
467static u_char oncore_cmd_Gc[]  = { 'G', 'c', 1 };				    /* 12	PPS Control: On Cont			*/
468static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 };				    /* 12	Position Control: 3D (no hold)		*/
469static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 };				    /* 12	Position Control: 0D (3D hold)		*/
470static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 };				    /* 12	Position Control: 2D (Alt Hold) 	*/
471static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 };				    /* 12	Position Coltrol: Start Site Survey	*/
472static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 };				    /* M12+T	TRAIM: off				*/
473static u_char oncore_cmd_Ge[]  = { 'G', 'e', 1 };				    /* M12+T	TRAIM: on				*/
474static u_char oncore_cmd_Gj[]  = { 'G', 'j' };					    /* 8?/12	Leap Second Pending			*/
475static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 };				    /* 12	Position/Data/Status: off		*/
476static u_char oncore_cmd_Ha[]  = { 'H', 'a', 1 };				    /* 12	Position/Data/Status: on		*/
477static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 };				    /* 12	TRAIM Status: off			*/
478static u_char oncore_cmd_Hn[]  = { 'H', 'n', 1 };				    /* 12	TRAIM Status: on			*/
479static u_char oncore_cmd_Ia[]  = { 'I', 'a' };					    /* 12	Self Test				*/
480
481/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
482 *				    the GT had Au,Av, but not As,At
483 * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
484 * Bj in UT at v1.3
485 * dont see Bd in UT/GT thru 1999
486 * Gj in UT as of 3.0, 1999 , Bj as of 1.3
487 */
488
489static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly",
490	"Aug", "Sep", "Oct", "Nov", "Dec" };
491
492#define DEVICE1 	"/dev/oncore.serial.%d"   /* name of serial device */
493#define DEVICE2 	"/dev/oncore.pps.%d"   /* name of pps device */
494#define INIT_FILE	"/etc/ntp.oncore" /* optional init file */
495
496#define SPEED		B9600		/* Oncore Binary speed (9600 bps) */
497
498/*
499 * Assemble and disassemble 32bit signed quantities from a buffer.
500 *
501 */
502
503	/* to buffer, int w, u_char *buf */
504#define w32_buf(buf,w)	{ u_int i_tmp;			   \
505			  i_tmp = (w<0) ? (~(-w)+1) : (w); \
506			  (buf)[0] = (i_tmp >> 24) & 0xff; \
507			  (buf)[1] = (i_tmp >> 16) & 0xff; \
508			  (buf)[2] = (i_tmp >>	8) & 0xff; \
509			  (buf)[3] = (i_tmp	 ) & 0xff; \
510			}
511
512#define w32(buf)      (((buf)[0]&0xff) << 24 | \
513		       ((buf)[1]&0xff) << 16 | \
514		       ((buf)[2]&0xff) <<  8 | \
515		       ((buf)[3]&0xff) )
516
517	/* from buffer, char *buf, result to an int */
518#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
519
520
521/*
522 * oncore_start - initialize data for processing
523 */
524
525static int
526oncore_start(
527	int unit,
528	struct peer *peer
529	)
530{
531	register struct instance *instance;
532	struct refclockproc *pp;
533	int fd1, fd2;
534	char device1[30], device2[30];
535	const char *cp;
536	struct stat stat1, stat2;
537
538	/* OPEN DEVICES */
539	/* opening different devices for fd1 and fd2 presents no problems */
540	/* opening the SAME device twice, seems to be OS dependent.
541		(a) on Linux (no streams) no problem
542		(b) on SunOS (and possibly Solaris, untested), (streams)
543			never see the line discipline.
544	   Since things ALWAYS work if we only open the device once, we check
545	     to see if the two devices are in fact the same, then proceed to
546	     do one open or two.
547	*/
548
549	(void)sprintf(device1, DEVICE1, unit);
550	(void)sprintf(device2, DEVICE2, unit);
551
552	if (stat(device1, &stat1)) {
553		perror("ONCORE: stat fd1");
554		exit(1);
555	}
556
557	if (stat(device2, &stat2)) {
558		perror("ONCORE: stat fd2");
559		exit(1);
560	}
561
562	/* create instance structure for this unit */
563
564	if (!(instance = (struct instance *) malloc(sizeof *instance))) {
565		perror("malloc");
566		return (0);
567	}
568	memset((char *) instance, 0, sizeof *instance);
569
570	if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
571		/* same device here */
572		if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
573#if !defined(HAVE_PPSAPI) && !defined(TIOCDCDTIMESTAMP)
574		      | LDISC_PPS
575#endif
576		   ))) {
577			perror("ONCORE: fd1");
578			exit(1);
579		}
580		fd2 = fd1;
581	} else {			/* different devices here */
582		if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) {
583			perror("ONCORE: fd1");
584			exit(1);
585		}
586		if ((fd2=open(device2, O_RDWR)) < 0) {
587			perror("ONCORE: fd2");
588			exit(1);
589		}
590	}
591
592	/* initialize miscellaneous variables */
593
594	pp = peer->procptr;
595	pp->unitptr    = (caddr_t) instance;
596	instance->pp   = pp;
597	instance->unit = unit;
598	instance->peer = peer;
599	instance->assert = 1;
600	instance->once = 1;
601
602	cp = "ONCORE DRIVER -- CONFIGURING";
603	record_clock_stats(&(instance->peer->srcadr), cp);
604
605	instance->o_state = ONCORE_NO_IDEA;
606	cp = "state = ONCORE_NO_IDEA";
607	record_clock_stats(&(instance->peer->srcadr), cp);
608
609	instance->ttyfd = fd1;
610	instance->ppsfd = fd2;
611
612	instance->Bj_day = -1;
613	instance->traim = -1;
614	instance->traim_in = -1;
615	instance->chan_in = -1;
616	instance->model = ONCORE_UNKNOWN;
617	instance->mode = MODE_UNKNOWN;
618	instance->site_survey = ONCORE_SS_UNKNOWN;
619	instance->Ag = 0xff;		/* Satellite mask angle, unset by user */
620	instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
621
622	peer->precision = -26;
623	peer->minpoll = 4;
624	peer->maxpoll = 4;
625	pp->clockdesc = "Motorola Oncore GPS Receiver";
626	memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
627
628	/* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
629
630	oncore_read_config(instance);
631
632#ifdef HAVE_PPSAPI
633	if (time_pps_create(fd2, &instance->pps_h) < 0) {
634		perror("time_pps_create");
635		return(0);
636	}
637
638	if (instance->assert)
639		cp = "Initializing timing to Assert.";
640	else
641		cp = "Initializing timing to Clear.";
642	record_clock_stats(&(instance->peer->srcadr), cp);
643
644	if (instance->hardpps) {
645		cp = "HARDPPS Set.";
646		record_clock_stats(&(instance->peer->srcadr), cp);
647	}
648
649	if (!oncore_ppsapi(instance))
650		return(0);
651#endif
652
653	pp->io.clock_recv = oncore_receive;
654	pp->io.srcclock = (caddr_t)peer;
655	pp->io.datalen = 0;
656	pp->io.fd = fd1;
657	if (!io_addclock(&pp->io)) {
658		perror("io_addclock");
659		(void) close(fd1);
660		free(instance);
661		return (0);
662	}
663
664#ifdef ONCORE_SHMEM_STATUS
665	/*
666	 * Before starting ONCORE, lets setup SHMEM
667	 * This will include merging an old SHMEM into the new one if
668	 * an old one is found.
669	 */
670
671	oncore_init_shmem(instance);
672#endif
673
674	/*
675	 * This will return the Model of the Oncore receiver.
676	 * and start the Initialization loop in oncore_msg_Cj.
677	 */
678
679	instance->o_state = ONCORE_CHECK_ID;
680	cp = "state = ONCORE_CHECK_ID";
681	record_clock_stats(&(instance->peer->srcadr), cp);
682
683	instance->timeout = 4;
684	oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
685	oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
686
687	instance->pollcnt = 2;
688	return (1);
689}
690
691
692/*
693 * Fudge control (get Flag2 and Flag3, not available at oncore_start time.
694 */
695
696static void
697oncore_control(
698	int unit,		  /* unit (not used) */
699	struct refclockstat *in,  /* input parameters  (not used) */
700	struct refclockstat *out, /* output parameters (not used) */
701	struct peer *peer	  /* peer structure pointer */
702	)
703{
704	char *cp;
705	struct refclockproc *pp;
706	struct instance *instance;
707
708	pp = peer->procptr;
709	instance = (struct instance *) pp->unitptr;
710
711	instance->assert  = !(pp->sloppyclockflag & CLK_FLAG2);
712	instance->hardpps =   pp->sloppyclockflag & CLK_FLAG3;
713
714	if (instance->assert)
715		cp = "Resetting timing to Assert.";
716	else
717		cp = "Resetting timing to Clear.";
718	record_clock_stats(&(instance->peer->srcadr), cp);
719
720	if (instance->hardpps) {
721		cp = "HARDPPS Set.";
722		record_clock_stats(&(instance->peer->srcadr), cp);
723	}
724
725	(void) oncore_ppsapi(instance);
726}
727
728
729
730/*
731 * oncore_shutdown - shut down the clock
732 */
733
734static void
735oncore_shutdown(
736	int unit,
737	struct peer *peer
738	)
739{
740	register struct instance *instance;
741	struct refclockproc *pp;
742
743	pp = peer->procptr;
744	instance = (struct instance *) pp->unitptr;
745
746	io_closeclock(&pp->io);
747
748	close(instance->ttyfd);
749	close(instance->ppsfd);
750	if (instance->shmemfd)
751		close(instance->shmemfd);
752	free(instance);
753}
754
755
756
757/*
758 * oncore_poll - called by the transmit procedure
759 */
760
761static void
762oncore_poll(
763	int unit,
764	struct peer *peer
765	)
766{
767	struct instance *instance;
768
769	instance = (struct instance *) peer->procptr->unitptr;
770	if (instance->timeout) {
771		char	*cp;
772
773		instance->timeout--;
774		if (instance->timeout == 0) {
775			cp = "Oncore: No response from @@Cj, shutting down driver";
776			record_clock_stats(&(instance->peer->srcadr), cp);
777			oncore_shutdown(unit, peer);
778		} else {
779			oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
780			cp = "Oncore: Resend @@Cj";
781			record_clock_stats(&(instance->peer->srcadr), cp);
782		}
783		return;
784	}
785
786	if (!instance->pollcnt)
787		refclock_report(peer, CEVNT_TIMEOUT);
788	else
789		instance->pollcnt--;
790	peer->procptr->polls++;
791	instance->polled = 1;
792}
793
794
795
796/*
797 * Initialize PPSAPI
798 */
799
800#ifdef HAVE_PPSAPI
801static int
802oncore_ppsapi(
803	struct instance *instance
804	)
805{
806	int mode;
807
808	if (time_pps_getcap(instance->pps_h, &mode) < 0) {
809		msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m");
810		return (0);
811	}
812
813	if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
814		msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m");
815		return (0);
816	}
817
818	/* nb. only turn things on, if someone else has turned something
819	 *	on before we get here, leave it alone!
820	 */
821
822	if (instance->assert) { 	/* nb, default or ON */
823		instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
824		instance->pps_p.assert_offset.tv_sec = 0;
825		instance->pps_p.assert_offset.tv_nsec = 0;
826	} else {
827		instance->pps_p.mode = PPS_CAPTURECLEAR  | PPS_OFFSETCLEAR;
828		instance->pps_p.clear_offset.tv_sec = 0;
829		instance->pps_p.clear_offset.tv_nsec = 0;
830	}
831	instance->pps_p.mode |= PPS_TSFMT_TSPEC;
832	instance->pps_p.mode &= mode;		/* only set what is legal */
833
834	if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
835		perror("time_pps_setparams");
836		exit(1);
837	}
838
839	/* If HARDPPS is on, we tell kernel */
840
841	if (instance->hardpps) {
842		int	i;
843
844		if (instance->assert)
845			i = PPS_CAPTUREASSERT;
846		else
847			i = PPS_CAPTURECLEAR;
848
849		if (i&mode) {
850			if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
851			    PPS_TSFMT_TSPEC) < 0) {
852				msyslog(LOG_ERR, "refclock_ioctl: time_pps_kcbind failed: %m");
853				return (0);
854			}
855			pps_enable = 1;
856		}
857	}
858	return(1);
859}
860#endif
861
862
863
864#ifdef ONCORE_SHMEM_STATUS
865static void
866oncore_init_shmem(
867	struct instance *instance
868	)
869{
870	int i, l, n, fd, shmem_old_size, n1;
871	char *buf, Msg[160];
872	u_char *cp, *cp1, *shmem_old;
873	struct msg_desc *mp;
874	struct stat sbuf;
875	size_t shmem_length;
876
877       /*
878	* The first thing we do is see if there is an instance->shmem_fname file (still)
879	* out there from a previous run.  If so, we copy it in and use it to initialize
880	* shmem (so we won't lose our almanac if we need it).
881	*/
882
883	shmem_old = 0;
884	if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
885		perror("LOAD:SHMEM");
886	else {
887		fstat(fd, &sbuf);
888		shmem_old_size = sbuf.st_size;
889		shmem_old = (u_char *) malloc((unsigned) sbuf.st_size);
890		if (shmem_old == NULL) {
891			perror("malloc");
892			close(fd);
893			return;
894		}
895
896		read(fd, shmem_old, shmem_old_size);
897		close(fd);
898	}
899
900	/* OK, we now create the NEW SHMEM. */
901
902	if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
903		perror(instance->shmem_fname);
904		return;
905	}
906
907	/* see how big it needs to be */
908
909	n = 1;
910	for (mp=oncore_messages; mp->flag[0]; mp++) {
911		mp->shmem = n;
912		/* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
913		if (!strcmp(mp->flag, "Cb")) {
914			instance->shmem_Cb = n;
915			n += (mp->len + 3) * 34;
916		}
917		if (!strcmp(mp->flag, "Ba")) {
918			instance->shmem_Ba = n;
919			n += (mp->len + 3) * 3;
920		}
921		if (!strcmp(mp->flag, "Ea")) {
922			instance->shmem_Ea = n;
923			n += (mp->len + 3) * 3;
924		}
925		if (!strcmp(mp->flag, "Ha")) {
926			instance->shmem_Ha = n;
927			n += (mp->len + 3) * 3;
928		}
929		n += (mp->len + 3);
930	}
931	shmem_length = n + 2;
932	fprintf(stderr, "ONCORE: SHMEM length: %d bytes\n", (int) shmem_length);
933
934	buf = malloc(shmem_length);
935	if (buf == NULL) {
936		perror("malloc");
937		close(instance->shmemfd);
938		return;
939	}
940
941	memset(buf, 0, shmem_length);
942
943	/* next build the new SHMEM buffer in memory */
944
945	for (mp=oncore_messages; mp->flag[0]; mp++) {
946		l = mp->shmem;
947		buf[l + 0] = mp->len >> 8;
948		buf[l + 1] = mp->len & 0xff;
949		buf[l + 2] = 0;
950		buf[l + 3] = '@';
951		buf[l + 4] = '@';
952		buf[l + 5] = mp->flag[0];
953		buf[l + 6] = mp->flag[1];
954		if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
955			if (!strcmp(mp->flag, "Cb"))
956				n = 35;
957			else
958				n = 4;
959			for (i=1; i<n; i++) {
960				buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
961				buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
962				buf[l + i * (mp->len+3) + 2] = 0;
963				buf[l + i * (mp->len+3) + 3] = '@';
964				buf[l + i * (mp->len+3) + 4] = '@';
965				buf[l + i * (mp->len+3) + 5] = mp->flag[0];
966				buf[l + i * (mp->len+3) + 6] = mp->flag[1];
967			}
968		}
969	}
970
971	/* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
972	 * copying the data in shmem_old to buf.  When we are done we write it out
973	 * and free both buffers.
974	 * If the structures change (an addition or deletion) I will stop copying.
975	 * The two will be the same unless we add/subtract from the oncore_messages list
976	 * so this should work most of the time, and takes a lot less code than doing it right.
977	 */
978
979	if (shmem_old) {
980		for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2));	cp+=(n+3), cp1+=(n+3)) {
981			n1 = 256*(*(cp1-3)) + *(cp1-2);
982			if (n1 != n || strncmp(cp, cp1, 4))
983				break;
984
985			memcpy(cp, cp1, (size_t) n);
986		}
987		free(shmem_old);
988	}
989
990	i = write(instance->shmemfd, buf, shmem_length);
991	free(buf);
992
993	if (i != shmem_length) {
994		perror(instance->shmem_fname);
995		close(instance->shmemfd);
996		return;
997	}
998
999	instance->shmem = (u_char *) mmap(0, shmem_length,
1000		PROT_READ | PROT_WRITE,
1001#ifdef MAP_HASSEMAPHORE
1002		MAP_HASSEMAPHORE |
1003#endif
1004		MAP_SHARED, instance->shmemfd, (off_t)0);
1005
1006	if (instance->shmem == (u_char *)MAP_FAILED) {
1007		instance->shmem = 0;
1008		close(instance->shmemfd);
1009		return;
1010	}
1011
1012	sprintf(Msg, "SHMEM (size = %d) is CONFIGURED and available as %s", shmem_length, instance->shmem_fname);
1013	record_clock_stats(&(instance->peer->srcadr), Msg);
1014}
1015#endif /* ONCORE_SHMEM_STATUS */
1016
1017
1018
1019/*
1020 * Read Input file if it exists.
1021 */
1022
1023static void
1024oncore_read_config(
1025	struct instance *instance
1026	)
1027{
1028/*
1029 * First we try to open the configuration file
1030 *    /etc/oncoreN
1031 * where N is the unit number viz 127.127.30.N.
1032 * If we don't find it we try
1033 *    /etc/ntp.oncore.N
1034 * and then
1035 *    /etc/ntp.oncore
1036 *
1037 * If we don't find any then we don't have the cable delay or PPS offset
1038 * and we choose MODE (4) below.
1039 *
1040 * Five Choices for MODE
1041 *    (0) ONCORE is preinitialized, don't do anything to change it.
1042 *	    nb, DON'T set 0D mode, DON'T set Delay, position...
1043 *    (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
1044 *    (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
1045 *		    lock this in, go to 0D mode.
1046 *    (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
1047 *    (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
1048 *		    lock this in, go to 0D mode.
1049 *     NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
1050 *	   then this position is set as the INITIAL position of the ONCORE.
1051 *	   This can reduce the time to first fix.
1052 * -------------------------------------------------------------------------------
1053 * Note that an Oncore UT without a battery backup retains NO information if it is
1054 *   power cycled, with a Battery Backup it remembers the almanac, etc.
1055 * For an Oncore VP, there is an eeprom that will contain this data, along with the
1056 *   option of Battery Backup.
1057 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
1058 *   power cycle, since there is nowhere to store the data.
1059 * -------------------------------------------------------------------------------
1060 *
1061 * If we open one or the other of the files, we read it looking for
1062 *   MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
1063 *   STATUS, POSN3D, POSN2D, CHAN, TRAIM
1064 * then initialize using method MODE.  For Mode = (1,3) all of (LAT, LON, HT) must
1065 *   be present or mode reverts to (2,4).
1066 *
1067 * Read input file.
1068 *
1069 *	# is comment to end of line
1070 *	= allowed between 1st and 2nd fields.
1071 *
1072 *	Expect to see one line with 'MODE' as first field, followed by an integer
1073 *	   in the range 0-4 (default = 4).
1074 *
1075 *	Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
1076 *	All numbers are floating point.
1077 *		DDD.ddd
1078 *		DDD  MMM.mmm
1079 *		DDD  MMM  SSS.sss
1080 *
1081 *	Expect to see one line with 'HT' as first field,
1082 *	   followed by 1-2 fields.  First is a number, the second is 'FT' or 'M'
1083 *	   for feet or meters.	HT is the height above the GPS ellipsoid.
1084 *	   If the receiver reports height in both GPS and MSL, then we will report
1085 *	   the difference GPS-MSL on the clockstats file.
1086 *
1087 *	There is an optional line, starting with DELAY, followed
1088 *	   by 1 or two fields.	The first is a number (a time) the second is
1089 *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
1090 *	    DELAY  is cable delay, typically a few tens of ns.
1091 *
1092 *	There is an optional line, starting with OFFSET, followed
1093 *	   by 1 or two fields.	The first is a number (a time) the second is
1094 *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
1095 *	   OFFSET is the offset of the PPS pulse from 0. (only fully implemented
1096 *		with the PPSAPI, we need to be able to tell the Kernel about this
1097 *		offset if the Kernel PLL is in use, but can only do this presently
1098 *		when using the PPSAPI interface.  If not using the Kernel PLL,
1099 *		then there is no problem.
1100 *
1101 *	There is an optional line, with either ASSERT or CLEAR on it, which
1102 *	   determine which transition of the PPS signal is used for timing by the
1103 *	   PPSAPI.  If neither is present, then ASSERT is assumed.
1104 *	   ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
1105 *	   For Flag2, ASSERT=0, and hence is default.
1106 *
1107 *	There is an optional line, with HARDPPS on it.	Including this line causes
1108 *	   the PPS signal to control the kernel PLL.
1109 *	   HARDPPS can also be set with FLAG3 of the ntp.conf input.
1110 *	   For Flag3, 0 is disabled, and the default.
1111 *
1112 *	There are three options that have to do with using the shared memory option.
1113 *	   First, to enable the option there must be a SHMEM line with a file name.
1114 *	   The file name is the file associated with the shared memory.
1115 *
1116 *	In shared memory, there is one 'record' for each returned variable.
1117 *	For the @@Ea data there are three 'records' containing position data.
1118 *	   There will always be data in the record corresponding to the '0D' @@Ea record,
1119 *	   and the user has a choice of filling the '3D' record by specifying POSN3D,
1120 *	   or the '2D' record by specifying POSN2D.  In either case the '2D' or '3D'
1121 *	   record is filled once every 15s.
1122 *
1123 *	Two additional variables that can be set are CHAN and TRAIM.  These should be
1124 *	   set correctly by the code examining the @@Cj record, but we bring them out here
1125 *	   to allow the user to override either the # of channels, or the existence of TRAIM.
1126 *	   CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
1127 *	   followed by YES or NO.
1128 *
1129 *	There is an optional line with MASK on it followed by one integer field in the
1130 *	   range 0 to 89. This sets the satellite mask angle and will determine the minimum
1131 *	   elevation angle for satellites to be tracked by the receiver. The default value
1132 *	   is 10 deg for the VP and 0 deg for all other receivers.
1133 *
1134 * So acceptable input would be
1135 *	# these are my coordinates (RWC)
1136 *	LON  -106 34.610
1137 *	LAT    35 08.999
1138 *	HT	1589	# could equally well say HT 5215 FT
1139 *	DELAY  60 ns
1140 */
1141
1142	FILE	*fd;
1143	char	*cp, *cc, *ca, line[100], units[2], device[20], Msg[160];
1144	int	i, sign, lat_flg, long_flg, ht_flg, mode, mask;
1145	double	f1, f2, f3;
1146
1147	sprintf(device, "%s%d", INIT_FILE, instance->unit);             /* try "ntp.oncore0" first */
1148	if ((fd=fopen(device, "r")) == NULL) {                          /*   it was in the original documentation */
1149		sprintf(device, "%s.%d", INIT_FILE, instance->unit);    /* then try "ntp.oncore.0 */
1150		if ((fd=fopen(device, "r")) == NULL) {
1151			if ((fd=fopen(INIT_FILE, "r")) == NULL) {       /* and finally "ntp.oncore" */
1152				instance->init_type = 4;
1153				return;
1154			}
1155		}
1156	}
1157
1158	mode = mask = 0;
1159	lat_flg = long_flg = ht_flg = 0;
1160	while (fgets(line, 100, fd)) {
1161
1162		/* Remove comments */
1163		if ((cp = strchr(line, '#')))
1164			*cp = '\0';
1165
1166		/* Remove trailing space */
1167		for (i = strlen(line);
1168		     i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
1169			)
1170			line[--i] = '\0';
1171
1172		/* Remove leading space */
1173		for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
1174			continue;
1175
1176		/* Stop if nothing left */
1177		if (!*cc)
1178			continue;
1179
1180		/* Uppercase the command and find the arg */
1181		for (ca = cc; *ca; ca++) {
1182			if (isascii((int)*ca)) {
1183				if (islower((int)*ca)) {
1184					*ca = toupper(*ca);
1185				} else if (isspace((int)*ca) || (*ca == '='))
1186					break;
1187			}
1188		}
1189
1190		/* Remove space (and possible =) leading the arg */
1191		for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
1192			continue;
1193
1194		if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
1195			i = strlen(ca);
1196			instance->shmem_fname = (char *) malloc((unsigned) (i+1));
1197			strcpy(instance->shmem_fname, ca);
1198			continue;
1199		}
1200
1201		/* Uppercase argument as well */
1202		for (cp = ca; *cp; cp++)
1203			if (isascii((int)*cp) && islower((int)*cp))
1204				*cp = toupper(*cp);
1205
1206		if (!strncmp(cc, "LAT", (size_t) 3)) {
1207			f1 = f2 = f3 = 0;
1208			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
1209			sign = 1;
1210			if (f1 < 0) {
1211				f1 = -f1;
1212				sign = -1;
1213			}
1214			instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
1215			lat_flg++;
1216		} else if (!strncmp(cc, "LON", (size_t) 3)) {
1217			f1 = f2 = f3 = 0;
1218			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
1219			sign = 1;
1220			if (f1 < 0) {
1221				f1 = -f1;
1222				sign = -1;
1223			}
1224			instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
1225			long_flg++;
1226		} else if (!strncmp(cc, "HT", (size_t) 2)) {
1227			f1 = 0;
1228			units[0] = '\0';
1229			sscanf(ca, "%lf %1s", &f1, units);
1230			if (units[0] == 'F')
1231				f1 = 0.3048 * f1;
1232			instance->ss_ht = 100 * f1;    /* cm */
1233			ht_flg++;
1234		} else if (!strncmp(cc, "DELAY", (size_t) 5)) {
1235			f1 = 0;
1236			units[0] = '\0';
1237			sscanf(ca, "%lf %1s", &f1, units);
1238			if (units[0] == 'N')
1239				;
1240			else if (units[0] == 'U')
1241				f1 = 1000 * f1;
1242			else if (units[0] == 'M')
1243				f1 = 1000000 * f1;
1244			else
1245				f1 = 1000000000 * f1;
1246			if (f1 < 0 || f1 > 1.e9)
1247				f1 = 0;
1248			if (f1 < 0 || f1 > 999999) {
1249				sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1);
1250				record_clock_stats(&(instance->peer->srcadr), Msg);
1251			} else
1252				instance->delay = f1;		/* delay in ns */
1253		} else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
1254			f1 = 0;
1255			units[0] = '\0';
1256			sscanf(ca, "%lf %1s", &f1, units);
1257			if (units[0] == 'N')
1258				;
1259			else if (units[0] == 'U')
1260				f1 = 1000 * f1;
1261			else if (units[0] == 'M')
1262				f1 = 1000000 * f1;
1263			else
1264				f1 = 1000000000 * f1;
1265			if (f1 < 0 || f1 > 1.e9)
1266				f1 = 0;
1267			if (f1 < 0 || f1 > 999999999.) {
1268				sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1);
1269				record_clock_stats(&(instance->peer->srcadr), Msg);
1270			} else
1271				instance->offset = f1;		/* offset in ns */
1272		} else if (!strncmp(cc, "MODE", (size_t) 4)) {
1273			sscanf(ca, "%d", &mode);
1274			if (mode < 0 || mode > 4)
1275				mode = 4;
1276		} else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
1277			instance->assert = 1;
1278		} else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
1279			instance->assert = 0;
1280		} else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
1281			instance->hardpps = 1;
1282		} else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
1283			instance->shmem_Posn = 2;
1284		} else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
1285			instance->shmem_Posn = 3;
1286		} else if (!strncmp(cc, "CHAN", (size_t) 4)) {
1287			sscanf(ca, "%d", &i);
1288			if ((i == 6) || (i == 8) || (i == 12))
1289				instance->chan_in = i;
1290		} else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
1291			instance->traim_in = 1; 	/* so TRAIM alone is YES */
1292			if (!strcmp(ca, "NO") || !strcmp(ca, "OFF"))    /* Yes/No, On/Off */
1293				instance->traim_in = 0;
1294		} else if (!strncmp(cc, "MASK", (size_t) 4)) {
1295			sscanf(ca, "%d", &mask);
1296			if (mask > -1 && mask < 90)
1297				instance->Ag = mask;			/* Satellite mask angle */
1298		}
1299	}
1300	fclose(fd);
1301
1302	/*
1303	 *    OK, have read all of data file, and extracted the good stuff.
1304	 *    If lat/long/ht specified they ALL must be specified for mode = (1,3).
1305	 */
1306
1307	instance->posn_set = 1;
1308	if (!( lat_flg && long_flg && ht_flg )) {
1309		printf("ONCORE: incomplete data on %s\n", INIT_FILE);
1310		instance->posn_set = 0;
1311		if (mode == 1 || mode == 3) {
1312			sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1);
1313			record_clock_stats(&(instance->peer->srcadr), Msg);
1314			mode++;
1315		}
1316	}
1317	instance->init_type = mode;
1318
1319	sprintf(Msg, "Input mode = %d", mode);
1320	record_clock_stats(&(instance->peer->srcadr), Msg);
1321}
1322
1323
1324
1325/*
1326 * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
1327 */
1328
1329static void
1330oncore_receive(
1331	struct recvbuf *rbufp
1332	)
1333{
1334	size_t i;
1335	u_char *p;
1336	struct peer *peer;
1337	struct instance *instance;
1338
1339	peer = (struct peer *)rbufp->recv_srcclock;
1340	instance = (struct instance *) peer->procptr->unitptr;
1341	p = (u_char *) &rbufp->recv_space;
1342
1343#if 0
1344	if (debug > 4) {
1345		int i;
1346		printf("ONCORE: >>>");
1347		for(i=0; i<rbufp->recv_length; i++)
1348			printf("%02x ", p[i]);
1349		printf("\n");
1350		printf("ONCORE: >>>");
1351		for(i=0; i<rbufp->recv_length; i++)
1352			printf("%03o ", p[i]);
1353		printf("\n");
1354	}
1355#endif
1356
1357	i = rbufp->recv_length;
1358	if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
1359		i = sizeof(rcvbuf) - rcvptr;	/* and some char will be lost */
1360	memcpy(rcvbuf+rcvptr, p, i);
1361	rcvptr += i;
1362	oncore_consume(instance);
1363}
1364
1365
1366
1367/*
1368 * Deal with any complete messages
1369 */
1370
1371static void
1372oncore_consume(
1373	struct instance *instance
1374	)
1375{
1376	int i, m;
1377	unsigned l;
1378
1379	while (rcvptr >= 7) {
1380		if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
1381			/* We're not in sync, lets try to get there */
1382			for (i=1; i < rcvptr-1; i++)
1383				if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
1384					break;
1385			if (debug > 4)
1386				printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i);
1387			if (i != rcvptr)
1388				memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
1389			rcvptr -= i;
1390			continue;
1391		}
1392
1393		/* Ok, we have a header now */
1394		l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
1395		for(m=0; m<l; m++)
1396			if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
1397				break;
1398		if (m == l) {
1399			if (debug > 4)
1400				printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]);
1401			memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
1402			rcvptr -= 4;
1403			continue;
1404		}
1405
1406		l = oncore_messages[m].len;
1407#if 0
1408		if (debug > 3)
1409			printf("ONCORE[%d]: GOT: %c%c  %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m);
1410#endif
1411		/* Got the entire message ? */
1412
1413		if (rcvptr < l)
1414			return;
1415
1416		/* are we at the end of message? should be <Cksum><CR><LF> */
1417
1418		if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
1419			if (debug)
1420				printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit);
1421		} else {	/* check the CheckSum */
1422			if (oncore_checksum_ok(rcvbuf, l)) {
1423				if (instance->shmem != NULL) {
1424					instance->shmem[oncore_messages[m].shmem + 2]++;
1425					memcpy(instance->shmem + oncore_messages[m].shmem + 3,
1426					    rcvbuf, (size_t) l);
1427				}
1428				oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
1429				if (oncore_messages[m].handler)
1430					oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
1431			} else if (debug) {
1432				printf("ONCORE[%d]: Checksum mismatch!\n", instance->unit);
1433				printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]);
1434				for (i=4; i<l; i++)
1435					printf("%03o ", rcvbuf[i]);
1436				printf("\n");
1437			}
1438		}
1439
1440		if (l != rcvptr)
1441			memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
1442		rcvptr -= l;
1443	}
1444}
1445
1446
1447
1448static void
1449oncore_get_timestamp(
1450	struct instance *instance,
1451	long dt1,	/* tick offset THIS time step */
1452	long dt2	/* tick offset NEXT time step */
1453	)
1454{
1455	int	Rsm;
1456	u_long	i, j;
1457	l_fp ts, ts_tmp;
1458	double dmy;
1459#ifdef HAVE_STRUCT_TIMESPEC
1460	struct timespec *tsp = 0;
1461#else
1462	struct timeval	*tsp = 0;
1463#endif
1464#ifdef HAVE_PPSAPI
1465	int	current_mode;
1466	pps_params_t current_params;
1467	struct timespec timeout;
1468	pps_info_t pps_i;
1469#else  /* ! HAVE_PPSAPI */
1470#ifdef HAVE_CIOGETEV
1471	struct ppsclockev ev;
1472	int r = CIOGETEV;
1473#endif
1474#ifdef HAVE_TIOCGPPSEV
1475	struct ppsclockev ev;
1476	int r = TIOCGPPSEV;
1477#endif
1478#if	TIOCDCDTIMESTAMP
1479	struct timeval	tv;
1480#endif
1481#endif	/* ! HAVE_PPS_API */
1482
1483#if 1
1484	/* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
1485	 * If we have Finished the SiteSurvey, then we fall thru for the 14/15
1486	 *  times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
1487	 * This gives good time, which gets better when the SS is done.
1488	 */
1489
1490	if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D))
1491#else
1492	/* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
1493
1494	if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D))
1495#endif
1496		return;
1497
1498	/* Don't do anything without an almanac to define the GPS->UTC delta */
1499
1500	if (instance->rsm.bad_almanac)
1501		return;
1502
1503#ifdef HAVE_PPSAPI
1504	j = instance->ev_serial;
1505	timeout.tv_sec = 0;
1506	timeout.tv_nsec = 0;
1507	if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
1508	    &timeout) < 0) {
1509		printf("ONCORE: time_pps_fetch failed\n");
1510		return;
1511	}
1512
1513	if (instance->assert) {
1514		tsp = &pps_i.assert_timestamp;
1515
1516		if (debug > 2) {
1517			i = (u_long) pps_i.assert_sequence;
1518#ifdef HAVE_STRUCT_TIMESPEC
1519			printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
1520			    instance->unit, i, j,
1521			    (long)tsp->tv_sec, (long)tsp->tv_nsec);
1522#else
1523			printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
1524			    instance->unit, i, j,
1525			    (long)tsp->tv_sec, (long)tsp->tv_usec);
1526#endif
1527		}
1528
1529		if (pps_i.assert_sequence == j) {
1530			printf("ONCORE: oncore_get_timestamp, error serial pps\n");
1531			return;
1532		}
1533		instance->ev_serial = pps_i.assert_sequence;
1534	} else {
1535		tsp = &pps_i.clear_timestamp;
1536
1537		if (debug > 2) {
1538			i = (u_long) pps_i.clear_sequence;
1539#ifdef HAVE_STRUCT_TIMESPEC
1540			printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
1541			    instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_nsec);
1542#else
1543			printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
1544			    instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_usec);
1545#endif
1546		}
1547
1548		if (pps_i.clear_sequence == j) {
1549			printf("ONCORE: oncore_get_timestamp, error serial pps\n");
1550			return;
1551		}
1552		instance->ev_serial = pps_i.clear_sequence;
1553	}
1554
1555	/* convert timespec -> ntp l_fp */
1556
1557	dmy = tsp->tv_nsec;
1558	dmy /= 1e9;
1559	ts.l_uf =  dmy * 4294967296.0;
1560	ts.l_ui = tsp->tv_sec;
1561#if 0
1562     alternate code for previous 4 lines is
1563	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1564	DTOLFP(dmy, &ts);
1565	dmy = tsp->tv_sec;		/* integer part */
1566	DTOLFP(dmy, &ts_tmp);
1567	L_ADD(&ts, &ts_tmp);
1568     or more simply
1569	dmy = 1.0e-9*tsp->tv_nsec;	/* fractional part */
1570	DTOLFP(dmy, &ts);
1571	ts.l_ui = tsp->tv_sec;
1572#endif	/* 0 */
1573#else
1574# if defined(HAVE_TIOCGPPSEV) || defined(HAVE_CIOGETEV)
1575	j = instance->ev_serial;
1576	if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
1577		perror("ONCORE: IOCTL:");
1578		return;
1579	}
1580
1581	tsp = &ev.tv;
1582
1583	if (debug > 2)
1584#ifdef HAVE_STRUCT_TIMESPEC
1585		printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
1586			ev.serial, j, tsp->tv_sec, tsp->tv_nsec);
1587#else
1588		printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
1589			ev.serial, j, tsp->tv_sec, tsp->tv_usec);
1590#endif
1591
1592	if (ev.serial == j) {
1593		printf("ONCORE: oncore_get_timestamp, error serial pps\n");
1594		return;
1595	}
1596	instance->ev_serial = ev.serial;
1597
1598	/* convert timeval -> ntp l_fp */
1599
1600	TVTOTS(tsp, &ts);
1601# else
1602#  if defined(TIOCDCDTIMESTAMP)
1603	if(ioctl(instance->ppsfd, TIOCDCDTIMESTAMP, &tv) < 0) {
1604		perror("ONCORE: ioctl(TIOCDCDTIMESTAMP)");
1605		return;
1606	}
1607	tsp = &tv;
1608	TVTOTS(tsp, &ts);
1609#  else
1610#error "Cannot compile -- no PPS mechanism configured!"
1611#  endif
1612# endif
1613#endif
1614	/* now have timestamp in ts */
1615	/* add in saw_tooth and offset, these will be ZERO if no TRAIM */
1616
1617	/* saw_tooth not really necessary if using TIMEVAL */
1618	/* since its only precise to us, but do it anyway. */
1619
1620	/* offset in ns, and is positive (late), we subtract */
1621	/* to put the PPS time transition back where it belongs */
1622
1623#ifdef HAVE_PPSAPI
1624	/* must hand the offset for the NEXT sec off to the Kernel to do */
1625	/* the addition, so that the Kernel PLL sees the offset too */
1626
1627	if (instance->assert)
1628		instance->pps_p.assert_offset.tv_nsec = -dt2;
1629	else
1630		instance->pps_p.clear_offset.tv_nsec  = -dt2;
1631
1632	/* The following code is necessary, and not just a time_pps_setparams,
1633	 * using the saved instance->pps_p, since some other process on the
1634	 * machine may have diddled with the mode bits (say adding something
1635	 * that it needs).  We take what is there and ADD what we need.
1636	 * [[ The results from the time_pps_getcap is unlikely to change so
1637	 *    we could probably just save it, but I choose to do the call ]]
1638	 * Unfortunately, there is only ONE set of mode bits in the kernel per
1639	 * interface, and not one set for each open handle.
1640	 *
1641	 * There is still a race condition here where we might mess up someone
1642	 * elses mode, but if he is being careful too, he should survive.
1643	 */
1644
1645	if (time_pps_getcap(instance->pps_h, &current_mode) < 0) {
1646		msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m");
1647		return;
1648	}
1649
1650	if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
1651		msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m");
1652		return;
1653	}
1654
1655		/* or current and mine */
1656	current_params.mode |= instance->pps_p.mode;
1657		/* but only set whats legal */
1658	current_params.mode &= current_mode;
1659
1660	current_params.assert_offset.tv_sec = 0;
1661	current_params.assert_offset.tv_nsec = -dt2;
1662	current_params.clear_offset.tv_sec = 0;
1663	current_params.clear_offset.tv_nsec = -dt2;
1664
1665	if (time_pps_setparams(instance->pps_h, &current_params))
1666		perror("time_pps_setparams");
1667#else
1668	/* if not PPSAPI, no way to inform kernel of OFFSET, just add the */
1669	/* offset for THIS second */
1670
1671	dmy = -1.0e-9*dt1;
1672	DTOLFP(dmy, &ts_tmp);
1673	L_ADD(&ts, &ts_tmp);
1674#endif
1675	/* have time from UNIX origin, convert to NTP origin. */
1676
1677	ts.l_ui += JAN_1970;
1678	instance->pp->lastrec = ts;
1679
1680	/* print out information about this timestamp (long line) */
1681
1682	ts_tmp = ts;
1683	ts_tmp.l_ui = 0;	/* zero integer part */
1684	LFPTOD(&ts_tmp, dmy);	/* convert fractional part to a double */
1685	j = 1.0e9*dmy;		/* then to integer ns */
1686
1687	Rsm = 0;
1688	if (instance->chan == 6)
1689		Rsm = instance->BEHa[64];
1690	else if (instance->chan == 8)
1691		Rsm = instance->BEHa[72];
1692	else if (instance->chan == 12)
1693		Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]);
1694
1695	if (instance->chan == 6 || instance->chan == 8) {
1696		sprintf(instance->pp->a_lastcode,	/* MAX length 128, currently at 117 */
1697	"%u.%09lu %d %d %2d %2d %2d %2ld rstat   %02x dop %4.1f nsat %2d,%d traim %d sigma %2d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d",
1698		    ts.l_ui, j,
1699		    instance->pp->year, instance->pp->day,
1700		    instance->pp->hour, instance->pp->minute, instance->pp->second,
1701		    (long) tsp->tv_sec % 60,
1702		    Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]),
1703		    /*rsat	dop */
1704		    instance->BEHa[38], instance->BEHa[39], instance->BEHn[21],
1705		    /*	nsat visible,	  nsat tracked,     traim */
1706		    instance->BEHn[23]*256+instance->BEHn[24], (s_char) instance->BEHn[25],
1707		    /* sigma				   neg-sawtooth */
1708	  /*sat*/   instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53],
1709		    instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69]
1710		    );					/* will be 0 for 6 chan */
1711	} else if (instance->chan == 12) {
1712		sprintf(instance->pp->a_lastcode,
1713 "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d%d%d%d%d",
1714		    ts.l_ui, j,
1715		    instance->pp->year, instance->pp->day,
1716		    instance->pp->hour, instance->pp->minute, instance->pp->second,
1717		    (long) tsp->tv_sec % 60,
1718		    Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]),
1719		    /*rsat	dop */
1720		    instance->BEHa[55], instance->BEHa[56], instance->BEHn[6],
1721		    /*	nsat visible,	  nsat tracked	 traim */
1722		    instance->BEHn[12]*256+instance->BEHn[13], (s_char) instance->BEHn[14],
1723		    /* sigma				   neg-sawtooth */
1724	  /*sat*/   instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76],
1725		    instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100],
1726		    instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124]
1727		    );
1728	}
1729
1730	if (debug > 2) {
1731		int n;
1732		n = strlen(instance->pp->a_lastcode);
1733		printf("ONCORE[%d]: len = %d %s\n", instance->unit, n, instance->pp->a_lastcode);
1734	}
1735
1736	/* and some things I dont understnd (magic ntp things) */
1737
1738	if (!refclock_process(instance->pp)) {
1739		refclock_report(instance->peer, CEVNT_BADTIME);
1740		return;
1741	}
1742
1743	record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
1744	instance->pollcnt = 2;
1745
1746	if (instance->polled) {
1747		instance->polled = 0;
1748/*
1749		instance->pp->dispersion = instance->pp->skew = 0;
1750*/
1751		instance->pp->lastref = instance->pp->lastrec;
1752		refclock_receive(instance->peer);
1753	}
1754}
1755
1756
1757/*************** oncore_msg_XX routines start here *******************/
1758
1759
1760/*
1761 * print Oncore response message.
1762 */
1763
1764static void
1765oncore_msg_any(
1766	struct instance *instance,
1767	u_char *buf,
1768	size_t len,
1769	int idx
1770	)
1771{
1772	int i;
1773	const char *fmt = oncore_messages[idx].fmt;
1774	const char *p;
1775#ifdef HAVE_GETCLOCK
1776	struct timespec ts;
1777#endif
1778	struct timeval tv;
1779
1780	if (debug > 3) {
1781#ifdef HAVE_GETCLOCK
1782		(void) getclock(TIMEOFDAY, &ts);
1783		tv.tv_sec = ts.tv_sec;
1784		tv.tv_usec = ts.tv_nsec / 1000;
1785#else
1786		GETTIMEOFDAY(&tv, 0);
1787#endif
1788		printf("ONCORE[%d]: %ld.%06ld\n", instance->unit, (long) tv.tv_sec, (long) tv.tv_usec);
1789
1790		if (!*fmt) {
1791			printf(">>@@%c%c ", buf[2], buf[3]);
1792			for(i=2; i < len && i < 2400 ; i++)
1793				printf("%02x", buf[i]);
1794			printf("\n");
1795			return;
1796		} else {
1797			printf("##");
1798			for (p = fmt; *p; p++) {
1799				putchar(*p);
1800				putchar('_');
1801			}
1802			printf("\n%c%c", buf[2], buf[3]);
1803			i = 4;
1804			for (p = fmt; *p; p++) {
1805				printf("%02x", buf[i++]);
1806			}
1807			printf("\n");
1808		}
1809	}
1810}
1811
1812
1813
1814/* Latitude, Longitude, Height */
1815
1816static void
1817oncore_msg_Adef(
1818	struct instance *instance,
1819	u_char *buf,
1820	size_t len
1821	)
1822{
1823}
1824
1825
1826
1827/* Mask Angle */
1828
1829static void
1830oncore_msg_Ag(
1831	struct instance *instance,
1832	u_char *buf,
1833	size_t len
1834	)
1835{		char  Msg[160], *cp;
1836
1837		cp = "set to";
1838		if (instance->o_state == ONCORE_RUN)
1839			cp = "is";
1840
1841		instance->Ag = buf[4];
1842		sprintf(Msg, "Satellite mask angle %s %d degrees", cp, (int) instance->Ag);
1843		record_clock_stats(&(instance->peer->srcadr), Msg);
1844}
1845
1846
1847
1848/*
1849 * get Position hold position
1850 */
1851
1852static void
1853oncore_msg_As(
1854	struct instance *instance,
1855	u_char *buf,
1856	size_t len
1857	)
1858{
1859	instance->ss_lat  = buf_w32(&buf[4]);
1860	instance->ss_long = buf_w32(&buf[8]);
1861	instance->ss_ht   = buf_w32(&buf[12]);
1862
1863	/* Print out Position */
1864	oncore_print_posn(instance);
1865}
1866
1867
1868
1869/*
1870 * Try to use Oncore UT+ Auto Survey Feature
1871 *	If its not there (VP), set flag to do it ourselves.
1872 */
1873
1874static void
1875oncore_msg_At(
1876	struct instance *instance,
1877	u_char *buf,
1878	size_t len
1879	)
1880{
1881	char	*cp;
1882
1883	instance->saw_At = 1;
1884	if (instance->site_survey == ONCORE_SS_TESTING) {
1885		if (buf[4] == 2) {
1886			record_clock_stats(&(instance->peer->srcadr),
1887					"Initiating hardware 3D site survey");
1888
1889			cp = "SSstate = ONCORE_SS_HW";
1890			record_clock_stats(&(instance->peer->srcadr), cp);
1891			instance->site_survey = ONCORE_SS_HW;
1892		}
1893	}
1894}
1895
1896
1897
1898/*
1899 * get PPS Offset
1900 * Nb. @@Ay is not supported for early UT (no plus) model
1901 */
1902
1903static void
1904oncore_msg_Ay(
1905	struct instance *instance,
1906	u_char *buf,
1907	size_t len
1908	)
1909{
1910	char Msg[120];
1911
1912	if (instance->saw_Ay)
1913		return;
1914
1915	instance->saw_Ay = 1;
1916
1917	instance->offset = buf_w32(&buf[4]);
1918
1919	sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
1920	record_clock_stats(&(instance->peer->srcadr), Msg);
1921}
1922
1923
1924
1925/*
1926 * get Cable Delay
1927 */
1928
1929static void
1930oncore_msg_Az(
1931	struct instance *instance,
1932	u_char *buf,
1933	size_t len
1934	)
1935{
1936	char Msg[120];
1937
1938	if (instance->saw_Az)
1939		return;
1940
1941	instance->saw_Az = 1;
1942
1943	instance->delay = buf_w32(&buf[4]);
1944
1945	sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
1946	record_clock_stats(&(instance->peer->srcadr), Msg);
1947}
1948
1949
1950
1951/* Ba, Ea and Ha come here, these contain Position */
1952
1953static void
1954oncore_msg_BaEaHa(
1955	struct instance *instance,
1956	u_char *buf,
1957	size_t len
1958	)
1959{
1960	const char	*cp;
1961	char		Msg[160];
1962	int		mode;
1963
1964	/* OK, we are close to the RUN state now.
1965	 * But we have a few more items to initialize first.
1966	 *
1967	 * At the beginning of this routine there are several 'timers'.
1968	 * We enter this routine 1/sec, and since the upper levels of NTP have usurped
1969	 * the use of timers, we use the 1/sec entry to do things that
1970	 * we would normally do with timers...
1971	 */
1972
1973	if (instance->o_state == ONCORE_CHECK_CHAN) {	/* here while checking for the # chan */
1974		if (buf[2] == 'B') {		/* 6chan */
1975			if (instance->chan_ck < 6) instance->chan_ck = 6;
1976		} else if (buf[2] == 'E') {	/* 8chan */
1977			if (instance->chan_ck < 8) instance->chan_ck = 8;
1978		} else if (buf[2] == 'H') {	/* 12chan */
1979			if (instance->chan_ck < 12) instance->chan_ck = 12;
1980		}
1981
1982		if (instance->count3++ < 5)
1983			return;
1984
1985		instance->count3 = 0;
1986
1987		if (instance->chan_in != -1)	/* set in Input */
1988			instance->chan = instance->chan_in;
1989		else				/* set from test */
1990			instance->chan = instance->chan_ck;
1991
1992		sprintf(Msg, "Input   says chan = %d", instance->chan_in);
1993		record_clock_stats(&(instance->peer->srcadr), Msg);
1994		sprintf(Msg, "Model # says chan = %d", instance->chan_id);
1995		record_clock_stats(&(instance->peer->srcadr), Msg);
1996		sprintf(Msg, "Testing says chan = %d", instance->chan_ck);
1997		record_clock_stats(&(instance->peer->srcadr), Msg);
1998		sprintf(Msg, "Using        chan = %d", instance->chan);
1999		record_clock_stats(&(instance->peer->srcadr), Msg);
2000
2001		instance->o_state = ONCORE_HAVE_CHAN;
2002		cp = "state = ONCORE_HAVE_CHAN";
2003		record_clock_stats(&(instance->peer->srcadr), cp);
2004
2005		instance->timeout = 4;
2006		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
2007		return;
2008	}
2009
2010	if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
2011		return;
2012
2013	/* PAUSE 5sec */
2014
2015	if (instance->count) {
2016		if (instance->count++ < 5)	/* make sure results are stable, using position */
2017			return;
2018		instance->count = 0;
2019	}
2020
2021	memcpy(instance->BEHa, buf, (size_t) (len+3));	/* Ba, Ea or Ha */
2022
2023	/* check the antenna and almanac for changes (did it get unplugged, is it ready?) */
2024
2025	oncore_check_almanac(instance);
2026	oncore_check_antenna(instance);
2027
2028	/* Almanac mode, waiting for Almanac, we can't do anything till we have it */
2029	/* When we have an almanac, we will start the Bn/En/@@Hn messages */
2030
2031	if (instance->o_state == ONCORE_ALMANAC)
2032		if (oncore_wait_almanac(instance))
2033			return;
2034
2035	/* do some things once when we get this far in BaEaHa */
2036
2037	if (instance->once) {
2038		instance->once = 0;
2039		instance->count2 = 1;
2040
2041		/* Have we seen an @@At (position hold) command response */
2042		/* if not, message out */
2043
2044		if (instance->chan != 12 && !instance->saw_At) {
2045			cp = "Not Good, no @@At command (no Position Hold), must be a GT/GT+";
2046			record_clock_stats(&(instance->peer->srcadr), cp);
2047			oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
2048		}
2049
2050		/* have an Almanac, can start the SiteSurvey
2051		 * (actually only need to get past the almanac_load where we diddle with At
2052		 *  command,- we can't change it after we start the HW_SS below
2053		 */
2054
2055		mode = instance->init_type;
2056		switch (mode) {
2057		case 0: /* NO initialization, don't change anything */
2058		case 1: /* Use given Position */
2059		case 3:
2060			instance->site_survey = ONCORE_SS_DONE;
2061			cp = "SSstate = ONCORE_SS_DONE";
2062			record_clock_stats(&(instance->peer->srcadr), cp);
2063			break;
2064
2065		case 2:
2066		case 4: /* Site Survey */
2067			cp = "SSstate = ONCORE_SS_TESTING";
2068			record_clock_stats(&(instance->peer->srcadr), cp);
2069			instance->site_survey = ONCORE_SS_TESTING;
2070			instance->count1 = 1;
2071			if (instance->chan == 12)
2072				oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd3,  sizeof(oncore_cmd_Gd3));  /* M12+T */
2073			else
2074				oncore_sendmsg(instance->ttyfd, oncore_cmd_At2,  sizeof(oncore_cmd_At2));  /* not GT, arg not VP */
2075			break;
2076		}
2077
2078		/* Read back PPS Offset for Output */
2079		/* Nb. This will fail silently for early UT (no plus) and M12 models */
2080
2081		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx,  sizeof(oncore_cmd_Ayx));
2082
2083		/* Read back Cable Delay for Output */
2084
2085		oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx,  sizeof(oncore_cmd_Azx));
2086
2087		/* Read back Satellite Mask Angle for Output */
2088
2089		oncore_sendmsg(instance->ttyfd, oncore_cmd_Agx,  sizeof(oncore_cmd_Agx));
2090	}
2091
2092	if (instance->count1) {
2093		if (instance->count1++ > 5 || instance->site_survey == ONCORE_SS_HW) {
2094			instance->count1 = 0;
2095			if (instance->site_survey == ONCORE_SS_TESTING) {
2096				/*
2097				 * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec
2098				 * wait after the @@At2/@@Gd3 command we have not changed the state to
2099				 * ONCORE_SS_HW.  If the Hardware is capable of doing a Site Survey, then
2100				 * the variable would have been changed by now.
2101				 * There are three possibilities:
2102				 * 6/8chan
2103				 *   (a) We did not get a response to the @@At0 or @@At2 commands,
2104				 *	   and it must be a GT/GT+/SL with no position hold mode.
2105				 *	   We will have to do it ourselves.
2106				 *   (b) We saw the @@At0, @@At2 commands, but @@At2 failed,
2107				 *	   must be a VP or older UT which doesn't have Site Survey mode.
2108				 *	   We will have to do it ourselves.
2109				 * 12chan
2110				 *   (c) We saw the @@Gd command, but @@Gd3 failed,
2111				 *	   We will have to do it ourselves.
2112				 */
2113
2114				sprintf(Msg, "Initiating software 3D site survey (%d samples)",
2115					POS_HOLD_AVERAGE);
2116				record_clock_stats(&(instance->peer->srcadr), Msg);
2117
2118				record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_SW");
2119				instance->site_survey = ONCORE_SS_SW;
2120
2121				instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
2122				if (instance->chan == 12)
2123					oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
2124				else {
2125					oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
2126					oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */
2127				}
2128			}
2129		}
2130	}
2131
2132	/* check the mode we are in 0/2/3D */
2133
2134	if (instance->chan == 6) {
2135		if (instance->BEHa[64]&0x8)
2136			instance->mode = MODE_0D;
2137		else if (instance->BEHa[64]&0x10)
2138			instance->mode = MODE_2D;
2139		else if (instance->BEHa[64]&0x20)
2140			instance->mode = MODE_3D;
2141	} else if (instance->chan == 8) {
2142		if (instance->BEHa[72]&0x8)
2143			instance->mode = MODE_0D;
2144		else if (instance->BEHa[72]&0x10)
2145			instance->mode = MODE_2D;
2146		else if (instance->BEHa[72]&0x20)
2147			instance->mode = MODE_3D;
2148	} else if (instance->chan == 12) {
2149		int bits;
2150
2151		bits = (instance->BEHa[129]>>5) & 0x7;	/* actually Ha */
2152		if (bits == 0x4)
2153			instance->mode = MODE_0D;
2154		else if (bits == 0x6)
2155			instance->mode = MODE_2D;
2156		else if (bits == 0x7)
2157			instance->mode = MODE_3D;
2158	}
2159
2160	/* copy the record to the (extra) location in SHMEM */
2161
2162	if (instance->shmem) {
2163		int	i;
2164		u_char	*smp;	 /* pointer to start of shared mem for Ba/Ea/Ha */
2165
2166		switch(instance->chan) {
2167		case 6:   smp = &instance->shmem[instance->shmem_Ba]; break;
2168		case 8:   smp = &instance->shmem[instance->shmem_Ea]; break;
2169		case 12:  smp = &instance->shmem[instance->shmem_Ha]; break;
2170		default:  smp = (u_char) 0;			      break;
2171		}
2172
2173		switch (instance->mode) {
2174		case MODE_0D:	i = 1; break;	/* 0D, Position Hold */
2175		case MODE_2D:	i = 2; break;	/* 2D, Altitude Hold */
2176		case MODE_3D:	i = 3; break;	/* 3D fix */
2177		default:	i = 0; break;
2178		}
2179
2180		if (i) {
2181			i *= (len+6);
2182			smp[i + 2]++;
2183			memcpy(&smp[i+3], buf, (size_t) (len+3));
2184		}
2185	}
2186
2187	/*
2188	 * check if timer active
2189	 * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond
2190	 */
2191
2192	if (instance->traim_delay) {
2193		if (instance->traim_delay++ > 5) {
2194			instance->traim = 0;
2195			instance->traim_delay = 0;
2196			cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
2197			record_clock_stats(&(instance->peer->srcadr), cp);
2198
2199			oncore_set_traim(instance);
2200		} else
2201			return;
2202
2203	}
2204
2205	/* by now should have a @@Ba/@@Ea/@@Ha with good data in it */
2206
2207	if (!instance->have_dH && !instance->traim_delay)
2208		oncore_compute_dH(instance);
2209
2210	/*
2211	 * must be ONCORE_RUN if we are here.
2212	 * Have # chan and TRAIM by now.
2213	 */
2214
2215	instance->pp->year   = buf[6]*256+buf[7];
2216	instance->pp->day    = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
2217	instance->pp->hour   = buf[8];
2218	instance->pp->minute = buf[9];
2219	instance->pp->second = buf[10];
2220
2221	/*
2222	 * Are we doing a Hardware or Software Site Survey?
2223	 */
2224
2225	if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW)
2226		oncore_ss(instance);
2227
2228	/* see if we ever saw a response from the @@Ayx above */
2229
2230	if (instance->count2) {
2231		if (instance->count2++ > 5) {	/* this delay to check on @@Ay command */
2232			instance->count2 = 0;
2233
2234			/* Have we seen an Ay (1PPS time offset) command response */
2235			/* if not, and non-zero offset, zero the offset, and send message */
2236
2237			if (!instance->saw_Ay && instance->offset) {
2238				cp = "No @@Ay command, PPS OFFSET ignored";
2239				record_clock_stats(&(instance->peer->srcadr), cp);
2240				instance->offset = 0;
2241			}
2242		}
2243	}
2244
2245	/*
2246	 * Check the leap second status once per day.
2247	 */
2248
2249	oncore_check_leap_sec(instance);
2250
2251	/*
2252	 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
2253	 */
2254
2255	if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE))
2256		oncore_shmem_get_3D(instance);
2257
2258	if (!instance->traim)	/* NO traim, no BnEnHn, go get tick */
2259		oncore_get_timestamp(instance, instance->offset, instance->offset);
2260}
2261
2262
2263
2264/* Almanac Status */
2265
2266static void
2267oncore_msg_Bd(
2268	struct instance *instance,
2269	u_char *buf,
2270	size_t len
2271	)
2272{
2273	char Msg[160];
2274
2275	sprintf(Msg, "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
2276		((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], buf[7], w32(&buf[8]) );
2277	record_clock_stats(&(instance->peer->srcadr), Msg);
2278}
2279
2280
2281
2282/* get leap-second warning message */
2283
2284/*
2285 * @@Bj does NOT behave as documented in current Oncore firmware.
2286 * It turns on the LEAP indicator when the data is set, and does not,
2287 * as documented, wait until the beginning of the month when the
2288 * leap second will occur.
2289 * Since this firmware bug will never be fixed in all the outstanding Oncore receivers
2290 * @@Bj is only called in June/December.
2291 */
2292
2293static void
2294oncore_msg_Bj(
2295	struct instance *instance,
2296	u_char *buf,
2297	size_t len
2298	)
2299{
2300	const char	*cp;
2301
2302	switch(buf[4]) {
2303	case 1:
2304		instance->peer->leap = LEAP_ADDSECOND;
2305		cp = "Set peer.leap to LEAP_ADDSECOND";
2306		break;
2307	case 2:
2308		instance->peer->leap = LEAP_DELSECOND;
2309		cp = "Set peer.leap to LEAP_DELSECOND";
2310		break;
2311	case 0:
2312	default:
2313		instance->peer->leap = LEAP_NOWARNING;
2314		cp = "Set peer.leap to LEAP_NOWARNING";
2315		break;
2316	}
2317	record_clock_stats(&(instance->peer->srcadr), cp);
2318}
2319
2320
2321
2322static void
2323oncore_msg_BnEnHn(
2324	struct instance *instance,
2325	u_char *buf,
2326	size_t	len
2327	)
2328{
2329	long	dt1, dt2;
2330	char	*cp;
2331
2332	if (instance->o_state != ONCORE_RUN)
2333		return;
2334
2335	if (instance->traim_delay) {	 /* flag that @@Bn/@@En/Hn returned */
2336			instance->traim_ck = 1;
2337			instance->traim_delay = 0;
2338			cp = "ONCORE: Detected TRAIM, TRAIM = ON";
2339			record_clock_stats(&(instance->peer->srcadr), cp);
2340
2341			oncore_set_traim(instance);
2342	}
2343
2344	memcpy(instance->BEHn, buf, (size_t) len);	/* Bn or En or Hn */
2345
2346	/* If Time RAIM doesn't like it, don't trust it */
2347
2348	if (buf[2] == 'H') {
2349		if (instance->BEHn[6])	/* bad TRAIM */
2350			return;
2351
2352		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2353		instance->saw_tooth = (s_char) instance->BEHn[10]; /* update for next time Hn[10] */
2354		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2355	} else {
2356		if (instance->BEHn[21]) /* bad TRAIM */
2357			return;
2358
2359		dt1 = instance->saw_tooth + instance->offset;	 /* dt this time step */
2360		instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time */
2361		dt2 = instance->saw_tooth + instance->offset;	 /* dt next time step */
2362	}
2363
2364	oncore_get_timestamp(instance, dt1, dt2);
2365}
2366
2367
2368
2369/* Here for @@Ca, @@Fa and @@Ia messages */
2370
2371/* These are Self test Commands for 6, 8, and 12 chan receivers.
2372 * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
2373 * It was found that under some circumstances the following
2374 * command would fail if issued immediately after the return from the
2375 * @@Fa, but a 2sec delay seemed to fix things.  Since simply calling
2376 * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
2377 * itimer, we set a flag, and test it at the next POLL.  If it hasn't
2378 * been cleared, we reissue the @@Cj that is issued below.
2379 * Note that we do a @@Cj at the beginning, and again here.
2380 * The first is to get the info, the 2nd is just used as a safe command
2381 * after the @@Fa for all Oncores (and it was in this posn in the
2382 * original code).
2383 */
2384
2385static void
2386oncore_msg_CaFaIa(
2387	struct instance *instance,
2388	u_char *buf,
2389	size_t len
2390	)
2391{
2392	char *cp;
2393	int	i;
2394
2395	if (instance->o_state == ONCORE_TEST_SENT) {
2396		enum antenna_state antenna;
2397
2398		instance->timeout = 0;
2399
2400		if (debug > 2) {
2401			if (buf[2] == 'I')
2402				printf("ONCORE[%d]: >>@@%ca %x %x %x\n", instance->unit, buf[2], buf[4], buf[5], buf[6]);
2403			else
2404				printf("ONCORE[%d]: >>@@%ca %x %x\n", instance->unit, buf[2], buf[4], buf[5]);
2405		}
2406
2407		antenna = (buf[4] & 0xc0) >> 6;
2408		buf[4] &= ~0xc0;
2409
2410		i = buf[4] || buf[5];
2411		if (buf[2] == 'I') i = i || buf[6];
2412		if (i) {
2413			if (buf[2] == 'I') {
2414				msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x %02x",
2415					instance->unit, buf[4], buf[5], buf[6]);
2416			} else {
2417				msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x",
2418					instance->unit, buf[4], buf[5]);
2419			}
2420			cp = "ONCORE: self test failed, shutting down driver";
2421			record_clock_stats(&instance->peer->srcadr, cp);
2422
2423			refclock_report(instance->peer, CEVNT_FAULT);
2424			oncore_shutdown(instance->unit, instance->peer);
2425			return;
2426		}
2427
2428		/* report the current antenna state */
2429
2430		oncore_antenna_report(instance, antenna);
2431
2432		instance->o_state = ONCORE_INIT;
2433		cp = "state = ONCORE_INIT";
2434		record_clock_stats(&(instance->peer->srcadr), cp);
2435
2436		instance->timeout = 4;
2437		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
2438	}
2439}
2440
2441
2442
2443/*
2444 * Demultiplex the almanac into shmem
2445 */
2446
2447static void
2448oncore_msg_Cb(
2449	struct instance *instance,
2450	u_char *buf,
2451	size_t len
2452	)
2453{
2454	int i;
2455
2456	if (instance->shmem == NULL)
2457		return;
2458
2459	if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
2460		i = buf[5];
2461	else if (buf[4] == 4 && buf[5] <= 5)
2462		i = buf[5] + 24;
2463	else if (buf[4] == 4 && buf[5] <= 10)
2464		i = buf[5] + 23;
2465	else if (buf[4] == 4 && buf[5] == 25)
2466		i = 34;
2467	else {
2468		char *cp;
2469
2470		cp = "Cb: Response is NO ALMANAC";
2471		record_clock_stats(&(instance->peer->srcadr), cp);
2472		return;
2473	}
2474
2475	i *= 36;
2476	instance->shmem[instance->shmem_Cb + i + 2]++;
2477	memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
2478
2479#if 1
2480	{
2481	char Msg[160];
2482	sprintf(Msg, "See Cb [%d,%d]", buf[4], buf[5]);
2483	record_clock_stats(&(instance->peer->srcadr), Msg);
2484	}
2485#endif
2486}
2487
2488
2489
2490/*
2491 * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
2492 *	not so for VP (eeprom) or any unit with a battery
2493 */
2494
2495static void
2496oncore_msg_Cf(
2497	struct instance *instance,
2498	u_char *buf,
2499	size_t len
2500	)
2501{
2502	const char *cp;
2503
2504	if (instance->o_state == ONCORE_RESET_SENT) {
2505		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
2506										       /* Reset set VP to IDLE */
2507		instance->o_state = ONCORE_TEST_SENT;
2508		cp = "state = ONCORE_TEST_SENT";
2509		record_clock_stats(&(instance->peer->srcadr), cp);
2510
2511		oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
2512	}
2513}
2514
2515
2516
2517/*
2518 * This is the Grand Central Station for the Preliminary Initialization.
2519 * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
2520 *
2521 * We do an @@Cj whenever we need a safe command for all Oncores.
2522 * The @@Cj gets us back here where we can switch to the next phase of setup.
2523 *
2524 * o Once at the very beginning (in start) to get the Model number.
2525 *   This info is printed, but no longer used.
2526 * o Again after we have determined the number of Channels in the receiver.
2527 * o And once later after we have done a reset and test, (which may hang),
2528 *   as we are about to initialize the Oncore and start it running.
2529 * o We have one routine below for each case.
2530 */
2531
2532static void
2533oncore_msg_Cj(
2534	struct instance *instance,
2535	u_char *buf,
2536	size_t len
2537	)
2538{
2539	int	mode;
2540	char	*cp;
2541
2542	memcpy(instance->Cj, buf, len);
2543
2544	instance->timeout = 0;
2545	if (instance->o_state == ONCORE_CHECK_ID) {
2546		oncore_msg_Cj_id(instance, buf, len);
2547		oncore_chan_test(instance);
2548	} else if (instance->o_state == ONCORE_HAVE_CHAN) {
2549		mode = instance->init_type;
2550		if (mode == 3 || mode == 4) {	/* Cf will return here to check for TEST */
2551			instance->o_state = ONCORE_RESET_SENT;
2552			cp = "state = ONCORE_RESET_SENT";
2553			record_clock_stats(&(instance->peer->srcadr), cp);
2554			oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
2555		} else {
2556			instance->o_state = ONCORE_TEST_SENT;
2557			cp = "state = ONCORE_TEST_SENT";
2558			record_clock_stats(&(instance->peer->srcadr), cp);
2559		}
2560	}
2561
2562	if (instance->o_state == ONCORE_TEST_SENT) {
2563		if (instance->chan == 6)
2564			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
2565		else if (instance->chan == 8)
2566			oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
2567		else if (instance->chan == 12)
2568			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
2569	} else if (instance->o_state == ONCORE_INIT)
2570		oncore_msg_Cj_init(instance, buf, len);
2571}
2572
2573
2574
2575/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
2576 *	the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
2577 *	and from Motorola.  Until recently Rick was the only source of
2578 *	this information as Motorola didn't give the information out.
2579 *
2580 * Determine the Type from the Model #, this determines #chan and if TRAIM is
2581 *   available.
2582 *
2583 * The Information from this routine is NO LONGER USED.
2584 * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
2585 */
2586
2587static void
2588oncore_msg_Cj_id(
2589	struct instance *instance,
2590	u_char *buf,
2591	size_t len
2592	)
2593{
2594	char *cp, *cp1, *cp2, Model[21], Msg[160];
2595
2596	/* Write Receiver ID message to clockstats file */
2597
2598	instance->Cj[294] = '\0';
2599	for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
2600		cp1 = strchr(cp, '\r');
2601		if (!cp1)
2602			cp1 = (char *)&instance->Cj[294];
2603		*cp1 = '\0';
2604		record_clock_stats(&(instance->peer->srcadr), cp);
2605		*cp1 = '\r';
2606		cp = cp1+2;
2607	}
2608
2609	/* next, the Firmware Version and Revision numbers */
2610
2611	instance->version  = atoi(&instance->Cj[83]);
2612	instance->revision = atoi(&instance->Cj[111]);
2613
2614	/* from model number decide which Oncore this is,
2615		and then the number of channels */
2616
2617	for (cp=&instance->Cj[160]; *cp == ' '; cp++)	/* start right after 'Model #' */
2618		;
2619	cp1 = cp;
2620	cp2 = Model;
2621	for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++)
2622		*cp2 = *cp;
2623	*cp2 = '\0';
2624
2625	cp = 0;
2626	if (!strncmp(Model, "PVT6", (size_t) 4)) {
2627		cp = "PVT6";
2628		instance->model = ONCORE_PVT6;
2629	} else if (Model[0] == 'A') {
2630		cp = "Basic";
2631		instance->model = ONCORE_BASIC;
2632	} else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
2633		cp = "VP";
2634		instance->model = ONCORE_VP;
2635	} else if (Model[0] == 'P') {
2636		cp = "M12";
2637		instance->model = ONCORE_M12;
2638	} else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
2639		if (Model[5] == 'N') {
2640			cp = "GT";
2641			instance->model = ONCORE_GT;
2642		} else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
2643			cp = "GT+";
2644			instance->model = ONCORE_GTPLUS;
2645		} else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
2646				cp = "UT";
2647				instance->model = ONCORE_UT;
2648		} else if (Model[1] == '5' && Model[5] == 'G') {
2649			cp = "UT+";
2650			instance->model = ONCORE_UTPLUS;
2651		} else if (Model[1] == '6' && Model[5] == 'G') {
2652			cp = "SL";
2653			instance->model = ONCORE_SL;
2654		} else {
2655			cp = "Unknown";
2656			instance->model = ONCORE_UNKNOWN;
2657		}
2658	} else	{
2659		cp = "Unknown";
2660		instance->model = ONCORE_UNKNOWN;
2661	}
2662
2663	/* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
2664
2665	sprintf(Msg, "This looks like an Oncore %s with version %d.%d firmware.", cp, instance->version, instance->revision);
2666	record_clock_stats(&(instance->peer->srcadr), Msg);
2667
2668	instance->chan_id = 8;	   /* default */
2669	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2670		instance->chan_id = 6;
2671	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2672		instance->chan_id = 8;
2673	else if (instance->model == ONCORE_M12)
2674		instance->chan_id = 12;
2675
2676	instance->traim_id = 0;    /* default */
2677	if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
2678		instance->traim_id = 0;
2679	else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
2680		instance->traim_id = 1;
2681	else if (instance->model == ONCORE_M12)
2682		instance->traim_id = -1;
2683
2684	sprintf(Msg, "Channels = %d, TRAIM = %s", instance->chan_id,
2685		((instance->traim_id < 0) ? "UNKNOWN" : ((instance->traim_id > 0) ? "ON" : "OFF")));
2686	record_clock_stats(&(instance->peer->srcadr), Msg);
2687}
2688
2689
2690
2691/* OK, know type of Oncore, have possibly reset it, and have tested it.
2692 * We know the number of channels.
2693 * We will determine whether we have TRAIM before we actually start.
2694 * Now initialize.
2695 */
2696
2697static void
2698oncore_msg_Cj_init(
2699	struct instance *instance,
2700	u_char *buf,
2701	size_t len
2702	)
2703{
2704	char *cp, Cmd[20], Msg[160];
2705	int	mode;
2706
2707
2708	/* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
2709	 * start again if we go from 0D -> 3D, then loses them again when we
2710	 * go from 3D -> 0D.  We do this to get a @@Ea message for SHMEM.
2711	 * For NOW we will turn this aspect of filling SHMEM off for the M12
2712	 */
2713
2714	if (instance->chan == 12) {
2715		instance->shmem_bad_Ea = 1;
2716		sprintf(Msg, "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***", instance->version, instance->revision);
2717		record_clock_stats(&(instance->peer->srcadr), Msg);
2718	}
2719
2720	oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to  Posn Fix mode */
2721	oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
2722	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
2723	oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
2724	oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
2725	oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
2726	oncore_sendmsg(instance->ttyfd, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
2727
2728	mode = instance->init_type;
2729
2730	/* If there is Position input in the Config file
2731	 * and mode = (1,3) set it as posn hold posn, goto 0D mode.
2732	 *  or mode = (2,4) set it as INITIAL position, and do Site Survey.
2733	 */
2734
2735	if (instance->posn_set) {
2736		record_clock_stats(&(instance->peer->srcadr), "Setting Posn from input data");
2737		oncore_set_posn(instance);	/* this should print posn indirectly thru the As cmd */
2738	} else	/* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
2739		if (instance->chan != 12)
2740			oncore_sendmsg(instance->ttyfd, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
2741
2742	if (mode != 0) {
2743			/* cable delay in ns */
2744		memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
2745		w32_buf(&Cmd[-2+4], instance->delay);
2746		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Az));	/* 6,8,12 */
2747
2748			/* PPS offset in ns */
2749		if (instance->offset) {
2750			memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay));	/* some have it, some don't */
2751			w32_buf(&Cmd[-2+4], instance->offset);			/* will check for hw response */
2752			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ay));
2753		}
2754
2755		/* Satellite mask angle */
2756
2757		if (instance->Ag != 0xff) {	/* will have 0xff in it if not set by user */
2758			memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
2759			Cmd[-2+4] = instance->Ag;
2760			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ag));
2761		}
2762	}
2763
2764	/* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
2765	 * now we're really running
2766	 * these were ALL started in the chan test,
2767	 * However, if we had mode=3,4 then commands got turned off, so we turn
2768	 * them on again here just in case
2769	 */
2770
2771	if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
2772		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
2773		oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
2774		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
2775		oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
2776		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba,	sizeof(oncore_cmd_Ba ));
2777	} else if (instance->chan == 8) {  /* start 8chan, kill 6,12chan commands */
2778		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
2779		oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
2780		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
2781		oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
2782		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea,	sizeof(oncore_cmd_Ea ));
2783	} else if (instance->chan == 12){  /* start 12chan, kill 6,12chan commands */
2784		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
2785		oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
2786		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
2787		oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
2788		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha,	sizeof(oncore_cmd_Ha ));
2789	}
2790
2791	instance->count = 1;
2792	instance->o_state = ONCORE_ALMANAC;
2793	cp = "state = ONCORE_ALMANAC";
2794	record_clock_stats(&(instance->peer->srcadr), cp);
2795}
2796
2797
2798
2799/* 12chan position */
2800
2801static void
2802oncore_msg_Ga(
2803	struct instance *instance,
2804	u_char *buf,
2805	size_t len
2806	)
2807{
2808	char Msg[160];
2809	long lat, lon, ht;
2810	double Lat, Lon, Ht;
2811
2812
2813	lat = buf_w32(&buf[4]);
2814	lon = buf_w32(&buf[8]);
2815	ht  = buf_w32(&buf[12]);  /* GPS ellipsoid */
2816
2817	Lat = lat;
2818	Lon = lon;
2819	Ht  = ht;
2820
2821	Lat /= 3600000;
2822	Lon /= 3600000;
2823	Ht  /= 100;
2824
2825
2826	sprintf(Msg, "Ga Posn Lat = %.7f, Lon = %.7f, Ht  = %.2f", Lat, Lon, Ht);
2827	record_clock_stats(&(instance->peer->srcadr), Msg);
2828
2829	instance->ss_lat  = lat;
2830	instance->ss_long = lon;
2831	instance->ss_ht   = ht;
2832
2833	oncore_print_posn(instance);
2834}
2835
2836
2837
2838/* 12 chan time/date */
2839
2840static void
2841oncore_msg_Gb(
2842	struct instance *instance,
2843	u_char *buf,
2844	size_t len
2845	)
2846{
2847	char	Msg[160], *gmts;
2848	int	mo, d, y, h, m, s, gmth, gmtm;
2849
2850	mo = buf[4];
2851	d  = buf[5];
2852	y  = 256*buf[6]+buf[7];
2853
2854	h  = buf[8];
2855	m  = buf[9];
2856	s  = buf[10];
2857
2858	gmts = ((buf[11] == 0) ? "+" : "-");
2859	gmth = buf[12];
2860	gmtm = buf[13];
2861
2862	sprintf(Msg, "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
2863		d, Month[mo+1], y, h, m, s, gmts, gmth, gmtm);
2864	record_clock_stats(&(instance->peer->srcadr), Msg);
2865}
2866
2867
2868
2869/*
2870 * Try to use Oncore M12+Timing Auto Survey Feature
2871 *	If its not there (M12), set flag to do it ourselves.
2872 */
2873
2874static void
2875oncore_msg_Gd(
2876	struct instance *instance,
2877	u_char *buf,
2878	size_t len
2879	)
2880{
2881	char	*cp;
2882
2883	if (instance->site_survey == ONCORE_SS_TESTING) {
2884		if (buf[4] == 3) {
2885			record_clock_stats(&(instance->peer->srcadr),
2886					"Initiating hardware 3D site survey");
2887
2888			cp = "SSstate = ONCORE_SS_HW";
2889			record_clock_stats(&(instance->peer->srcadr), cp);
2890			instance->site_survey = ONCORE_SS_HW;
2891			}
2892	}
2893}
2894
2895
2896
2897/* Leap Second for M12, gives all info from satellite message */
2898/* also in UT v3.0 */
2899
2900static void
2901oncore_msg_Gj(
2902	struct instance *instance,
2903	u_char *buf,
2904	size_t len
2905	)
2906{
2907	int dt;
2908	char Msg[160], *cp;
2909
2910	instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
2911
2912	/* print the message to verify whats there */
2913
2914	dt = buf[5] - buf[4];
2915
2916#if 1
2917	sprintf(Msg, "ONCORE[%d]: Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
2918			instance->unit,
2919			buf[4], buf[5], 256*buf[6]+buf[7], buf[8], buf[9], buf[10],
2920			(buf[14]+256*(buf[13]+256*(buf[12]+256*buf[11]))),
2921			buf[15], buf[16], buf[17]);
2922	record_clock_stats(&(instance->peer->srcadr), Msg);
2923#endif
2924	if (dt) {
2925		sprintf(Msg, "ONCORE[%d]: Leap second (%d) scheduled for %d%s%d at %d:%d:%d",
2926			instance->unit,
2927			dt, buf[9], Month[buf[8]], 256*buf[6]+buf[7],
2928			buf[15], buf[16], buf[17]);
2929		record_clock_stats(&(instance->peer->srcadr), Msg);
2930	}
2931
2932	/* Only raise warning within a month of the leap second */
2933
2934	instance->peer->leap = LEAP_NOWARNING;
2935	cp = "Set peer.leap to LEAP_NOWARNING";
2936
2937	if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
2938	    buf[8] == instance->BEHa[4]) {	/* month */
2939		if (dt) {
2940			if (dt < 0) {
2941				instance->peer->leap = LEAP_DELSECOND;
2942				cp = "Set peer.leap to LEAP_DELSECOND";
2943			} else {
2944				instance->peer->leap = LEAP_ADDSECOND;
2945				cp = "Set peer.leap to LEAP_ADDSECOND";
2946			}
2947		}
2948	}
2949	record_clock_stats(&(instance->peer->srcadr), cp);
2950}
2951
2952
2953
2954/* Power on failure */
2955
2956static void
2957oncore_msg_Sz(
2958	struct instance *instance,
2959	u_char *buf,
2960	size_t len
2961	)
2962{
2963	const char *cp;
2964
2965	cp = "Oncore: System Failure at Power On";
2966	if (instance && instance->peer) {
2967		record_clock_stats(&(instance->peer->srcadr), cp);
2968		oncore_shutdown(instance->unit, instance->peer);
2969	}
2970}
2971
2972/************** Small Subroutines ***************/
2973
2974
2975static void
2976oncore_antenna_report(
2977	struct instance *instance,
2978	enum antenna_state new_state)
2979{
2980	char *cp;
2981
2982	if (instance->ant_state == new_state)
2983		return;
2984
2985	switch (new_state) {
2986	case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK";                   break;
2987	case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)";  break;
2988	case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
2989	case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)";   break;
2990	default:		cp = "GPS antenna: ?";                    break;
2991	}
2992
2993	instance->ant_state = new_state;
2994	record_clock_stats(&instance->peer->srcadr, cp);
2995}
2996
2997
2998
2999static void
3000oncore_chan_test(
3001	struct instance *instance
3002	)
3003{
3004	char	*cp;
3005
3006	/* subroutine oncore_Cj_id has determined the number of channels from the
3007	 * model number of the attached oncore.  This is not always correct since
3008	 * the oncore could have non-standard firmware.  Here we check (independently) by
3009	 * trying a 6, 8, and 12 chan command, and see which responds.
3010	 * Caution: more than one CAN respond.
3011	 *
3012	 * This #chan is used by the code rather than that calculated from the model number.
3013	 */
3014
3015	instance->o_state = ONCORE_CHECK_CHAN;
3016	cp = "state = ONCORE_CHECK_CHAN";
3017	record_clock_stats(&(instance->peer->srcadr), cp);
3018
3019	instance->count3 = 1;
3020	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
3021	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
3022	oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
3023}
3024
3025
3026
3027/* check for a GOOD Almanac, have we got one yet? */
3028
3029static void
3030oncore_check_almanac(
3031	struct instance *instance
3032	)
3033{
3034	if (instance->chan == 6) {
3035		instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
3036		instance->rsm.bad_fix	  = instance->BEHa[64]&0x52;
3037	} else if (instance->chan == 8) {
3038		instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
3039		instance->rsm.bad_fix	  = instance->BEHa[72]&0x52;
3040	} else if (instance->chan == 12) {
3041		int bits1, bits2;
3042
3043		bits1 = (instance->BEHa[129]>>5) & 0x7; 	/* actually Ha */
3044		bits2 = instance->BEHa[130];
3045		instance->rsm.bad_almanac = (bits2 & 0x80);
3046		instance->rsm.bad_fix	  = (bits2 & 0x8) || (bits1 == 0x2);
3047					  /* too few sat     Bad Geom	  */
3048#if 0
3049		fprintf(stderr, "ONCORE[%d]: DEBUG BITS: (%x %x), (%x %x),  %x %x %x %x %x\n",
3050		instance->unit,
3051		instance->BEHa[129], instance->BEHa[130], bits1, bits2, instance->mode == MODE_0D,
3052		instance->mode == MODE_2D, instance->mode == MODE_3D,
3053		instance->rsm.bad_almanac, instance->rsm.bad_fix);
3054#endif
3055	}
3056}
3057
3058
3059
3060/* check the antenna for changes (did it get unplugged?) */
3061
3062static void
3063oncore_check_antenna(
3064	struct instance *instance
3065	)
3066{
3067	enum antenna_state antenna;		/* antenna state */
3068
3069	antenna = instance->ant_state;
3070	if (instance->chan == 12)
3071		antenna = (instance->BEHa[130] & 0x6 ) >> 1;
3072	else
3073		antenna = (instance->BEHa[37] & 0xc0) >> 6;  /* prob unset 6, set GT, UT unset VP */
3074
3075	oncore_antenna_report (instance, antenna);
3076}
3077
3078
3079
3080/*
3081 * Check the leap second status once per day.
3082 *
3083 * Note that the ONCORE firmware for the Bj command is wrong at
3084 * least in the VP.
3085 * It starts advertising a LEAP SECOND as soon as the GPS satellite
3086 * data message (page 18, subframe 4) is updated to a date in the
3087 * future, and does not wait for the month that it will occur.
3088 * The event will usually be advertised several months in advance.
3089 * Since there is a one bit flag, there is no way to tell if it is
3090 * this month, or when...
3091 *
3092 * As such, we have the workaround below, of only checking for leap
3093 * seconds with the Bj command in June/December.
3094 *
3095 * The Gj command gives more information, and we can tell in which
3096 * month to apply the correction.
3097 *
3098 * Note that with the VP we COULD read the raw data message, and
3099 * interpret it ourselves, but since this is specific to this receiver
3100 * only, and the above workaround is adequate, we don't bother.
3101 */
3102
3103static void
3104oncore_check_leap_sec(
3105	struct instance *instance
3106	)
3107{
3108	if (instance->Bj_day != instance->BEHa[5]) {	 /* do this 1/day */
3109		instance->Bj_day = instance->BEHa[5];
3110
3111		if (instance->saw_Gj < 0) {	/* -1 DONT have Gj use Bj */
3112			if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
3113				oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3114			return;
3115		}
3116
3117		if (instance->saw_Gj == 0)	/* 0 is dont know if we have Gj */
3118			instance->count4 = 1;
3119
3120		oncore_sendmsg(instance->ttyfd, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
3121		return;
3122	}
3123
3124	/* Gj works for some 6/8 chan UT and the M12	  */
3125	/* if no response from Gj in 5 sec, we try Bj	  */
3126	/* which isnt implemented in all the GT/UT either */
3127
3128	if (instance->count4) { 	/* delay, waiting for Gj response */
3129		if (instance->saw_Gj == 1)
3130			instance->count4 = 0;
3131		else if (instance->count4++ > 5) {	/* delay, waiting for Gj response */
3132			instance->saw_Gj = -1;		/* didnt see it, will use Bj */
3133			instance->count4 = 0;
3134			if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
3135				oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
3136		}
3137	}
3138}
3139
3140
3141
3142/* check the message checksum,
3143 *  buf points to START of message ( @@ )
3144 *  len is length WITH CR/LF.
3145 */
3146
3147static int
3148oncore_checksum_ok(
3149	u_char *buf,
3150	int	len
3151	)
3152{
3153	int	i, j;
3154
3155	j = 0;
3156	for (i = 2; i < len-3; i++)
3157		j ^= buf[i];
3158
3159	return(j == buf[len-3]);
3160}
3161
3162
3163
3164static void
3165oncore_compute_dH(
3166	struct instance *instance
3167	)
3168{
3169	int GPS, MSL;
3170	char	Msg[160];
3171
3172	/* Here calculate dH = GPS - MSL for output message */
3173	/* also set Altitude Hold mode if GT */
3174
3175	instance->have_dH = 1;
3176	if (instance->chan == 12) {
3177		GPS = buf_w32(&instance->BEHa[39]);
3178		MSL = buf_w32(&instance->BEHa[43]);
3179	} else {
3180		GPS = buf_w32(&instance->BEHa[23]);
3181		MSL = buf_w32(&instance->BEHa[27]);
3182	}
3183	instance->dH = GPS - MSL;
3184	instance->dH /= 100.;
3185
3186	/* if MSL is not set, the calculation is meaningless */
3187
3188	if (MSL) {	/* not set ! */
3189		sprintf(Msg, "dH = (GPS - MSL) = %.2fm", instance->dH);
3190		record_clock_stats(&(instance->peer->srcadr), Msg);
3191	}
3192}
3193
3194
3195
3196/*
3197 * try loading Almanac from shmem (where it was copied from shmem_old
3198 */
3199
3200static void
3201oncore_load_almanac(
3202	struct instance *instance
3203	)
3204{
3205	u_char	*cp, Cmd[20];
3206	int	n;
3207	struct timeval tv;
3208	struct tm *tm;
3209
3210	if (!instance->shmem)
3211		return;
3212
3213#if 1
3214	for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
3215		if (!strncmp(cp, "@@Cb", 4) &&
3216		    oncore_checksum_ok(cp, 33) &&
3217		    (*(cp+4) == 4 || *(cp+4) == 5)) {
3218			write(instance->ttyfd, cp, n);
3219#if 1
3220			oncore_print_Cb(instance, cp);
3221#endif
3222		}
3223	}
3224#else
3225/************DEBUG************/
3226	for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
3227		char Msg[160];
3228
3229		sprintf(Msg, "See %c%c%c%c %d", *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4));
3230		record_clock_stats(&(instance->peer->srcadr), Msg);
3231
3232		if (!strncmp(cp, "@@Cb", 4)) {
3233			oncore_print_Cb(instance, cp);
3234			if (oncore_checksum_ok(cp, 33)) {
3235				if (*(cp+4) == 4 || *(cp+4) == 5) {
3236					record_clock_stats(&(instance->peer->srcadr), "GOOD SF");
3237					write(instance->ttyfd, cp, n);
3238				} else
3239					record_clock_stats(&(instance->peer->srcadr), "BAD SF");
3240			} else
3241				record_clock_stats(&(instance->peer->srcadr), "BAD CHECKSUM");
3242		}
3243	}
3244/************DEBUG************/
3245#endif
3246
3247	/* Must load position and time or the Almanac doesn't do us any good */
3248
3249	if (!instance->posn_set) {	/* if we input a posn use it, else from SHMEM */
3250		record_clock_stats(&(instance->peer->srcadr), "Loading Posn from SHMEM");
3251		for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2));  cp+=(n+3)) {
3252			if ((instance->chan == 6  && (!strncmp(cp, "@@Ba", 4) && oncore_checksum_ok(cp,  68))) ||
3253			    (instance->chan == 8  && (!strncmp(cp, "@@Ea", 4) && oncore_checksum_ok(cp,  76))) ||
3254			    (instance->chan == 12 && (!strncmp(cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) {
3255				int ii, jj, kk;
3256
3257				instance->posn_set = 1;
3258				ii = buf_w32(cp + 15);
3259				jj = buf_w32(cp + 19);
3260				kk = buf_w32(cp + 23);
3261{
3262char Msg[160];
3263sprintf(Msg, "SHMEM posn = %d (%d, %d, %d)", cp-instance->shmem, ii, jj, kk);
3264record_clock_stats(&(instance->peer->srcadr), Msg);
3265}
3266				if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */
3267					instance->ss_lat  = ii;
3268					instance->ss_long = jj;
3269					instance->ss_ht   = kk;
3270				}
3271			}
3272		}
3273	}
3274	oncore_set_posn(instance);
3275
3276	/* and set time to time from Computer clock */
3277
3278	gettimeofday(&tv, 0);
3279	tm = gmtime((const time_t *) &tv.tv_sec);
3280#if 1
3281	{
3282	char Msg[160];
3283	sprintf(Msg, "DATE %d %d %d, %d %d %d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday,
3284		tm->tm_hour, tm->tm_min, tm->tm_sec);
3285	record_clock_stats(&(instance->peer->srcadr), Msg);
3286	}
3287#endif
3288	if (instance->chan == 12) {
3289		memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb));
3290		Cmd[-2+4]  = tm->tm_mon;
3291		Cmd[-2+5]  = tm->tm_mday;
3292		Cmd[-2+6]  = (1900+tm->tm_year)/256;
3293		Cmd[-2+7]  = (1900+tm->tm_year)%256;
3294		Cmd[-2+8]  = tm->tm_hour;
3295		Cmd[-2+9]  = tm->tm_min;
3296		Cmd[-2+10] = tm->tm_sec;
3297		Cmd[-2+11] = 0;
3298		Cmd[-2+12] = 0;
3299		Cmd[-2+13] = 0;
3300		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Gb));
3301	} else {
3302		/* First set GMT offset to zero */
3303
3304		oncore_sendmsg(instance->ttyfd, oncore_cmd_Ab,	sizeof(oncore_cmd_Ab));
3305
3306		memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac));
3307		Cmd[-2+4] = tm->tm_mon;
3308		Cmd[-2+5] = tm->tm_mday;
3309		Cmd[-2+6] = (1900+tm->tm_year)/256;
3310		Cmd[-2+7] = (1900+tm->tm_year)%256;
3311		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ac));
3312
3313		memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa));
3314		Cmd[-2+4] = tm->tm_hour;
3315		Cmd[-2+5] = tm->tm_min;
3316		Cmd[-2+6] = tm->tm_sec;
3317		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Aa));
3318	}
3319
3320	record_clock_stats(&(instance->peer->srcadr), "Setting Posn and Time after Loading Almanac");
3321}
3322
3323
3324
3325/* Almanac data input */
3326
3327static void
3328oncore_print_Cb(
3329	struct instance *instance,
3330	u_char *cp
3331	)
3332{
3333#if 0
3334	int	ii;
3335	char	Msg[160];
3336
3337	printf("DEBUG: See: %c%c%c%c\n", *(cp), *(cp+1), *(cp+2), *(cp+3));
3338	printf("DEBUG: Cb: [%d,%d]", *(cp+4), *(cp+5));
3339	for(ii=0; ii<33; ii++)
3340		printf(" %d", *(cp+ii));
3341	printf("\n");
3342
3343	sprintf(Msg, "Debug: Cb: [%d,%d]", *(cp+4), *(cp+5));
3344	record_clock_stats(&(instance->peer->srcadr), Msg);
3345#endif
3346}
3347
3348
3349#if 0
3350static void
3351oncore_print_array(
3352	u_char *cp,
3353	int	n
3354	)
3355{
3356	int	jj, i, j, nn;
3357
3358	nn = 0;
3359	printf("\nTOP\n");
3360	jj = n/16;
3361	for (j=0; j<jj; j++) {
3362		printf("%4d: ", nn);
3363		nn += 16;
3364		for (i=0; i<16; i++)
3365			printf(" %o", *cp++);
3366		printf("\n");
3367	}
3368}
3369#endif
3370
3371
3372static void
3373oncore_print_posn(
3374	struct instance *instance
3375	)
3376{
3377	char Msg[120], ew, ns;
3378	double xd, xm, xs, yd, ym, ys, hm, hft;
3379	int idx, idy, is, imx, imy;
3380	long lat, lon;
3381
3382	record_clock_stats(&(instance->peer->srcadr), "Posn:");
3383	ew = 'E';
3384	lon = instance->ss_long;
3385	if (lon < 0) {
3386		ew = 'W';
3387		lon = -lon;
3388	}
3389
3390	ns = 'N';
3391	lat = instance->ss_lat;
3392	if (lat < 0) {
3393		ns = 'S';
3394		lat = -lat;
3395	}
3396
3397	hm = instance->ss_ht/100.;
3398	hft= hm/0.3048;
3399
3400	xd = lat/3600000.;	/* lat, lon in int msec arc, ht in cm. */
3401	yd = lon/3600000.;
3402	sprintf(Msg, "Lat = %c %11.7fdeg,    Long = %c %11.7fdeg,    Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft);
3403	record_clock_stats(&(instance->peer->srcadr), Msg);
3404
3405	idx = xd;
3406	idy = yd;
3407	imx = lat%3600000;
3408	imy = lon%3600000;
3409	xm = imx/60000.;
3410	ym = imy/60000.;
3411	sprintf(Msg,
3412	    "Lat = %c %3ddeg %7.4fm,   Long = %c %3ddeg %8.5fm,  Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft);
3413	record_clock_stats(&(instance->peer->srcadr), Msg);
3414
3415	imx = xm;
3416	imy = ym;
3417	is  = lat%60000;
3418	xs  = is/1000.;
3419	is  = lon%60000;
3420	ys  = is/1000.;
3421	sprintf(Msg,
3422	    "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
3423	record_clock_stats(&(instance->peer->srcadr), Msg);
3424}
3425
3426
3427
3428/*
3429 * write message to Oncore.
3430 */
3431
3432static void
3433oncore_sendmsg(
3434	int	fd,
3435	u_char *ptr,
3436	size_t len
3437	)
3438{
3439	u_char cs = 0;
3440
3441	if (debug > 4)
3442		printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], (int) len);
3443	write(fd, "@@", (size_t) 2);
3444	write(fd, ptr, len);
3445	while (len--)
3446		cs ^= *ptr++;
3447	write(fd, &cs, (size_t) 1);
3448	write(fd, "\r\n", (size_t) 2);
3449}
3450
3451
3452
3453static void
3454oncore_set_posn(
3455	struct instance *instance
3456	)
3457{
3458	int	mode;
3459	char	Cmd[20];
3460
3461	/* Turn OFF position hold, it needs to be off to set position (for some units),
3462	   will get set ON in @@Ea later */
3463
3464	if (instance->chan == 12)
3465		oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
3466	else {
3467		oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
3468		oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */
3469	}
3470
3471	mode = instance->init_type;
3472
3473	if (mode != 0) {	/* first set posn hold position */
3474		memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As));	/* don't modify static variables */
3475		w32_buf(&Cmd[-2+4],  (int) instance->ss_lat);
3476		w32_buf(&Cmd[-2+8],  (int) instance->ss_long);
3477		w32_buf(&Cmd[-2+12], (int) instance->ss_ht);
3478		Cmd[-2+16] = 0;
3479		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_As));	/* posn hold 3D posn (6/8/12) */
3480
3481		memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au));
3482		w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
3483		Cmd[-2+8] = 0;
3484		oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Au));	/* altitude hold (6/8/12 not UT, M12T) */
3485
3486		/* next set current position */
3487
3488		if (instance->chan == 12) {
3489			memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga));
3490			w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
3491			w32_buf(&Cmd[-2+8], (int) instance->ss_long);
3492			w32_buf(&Cmd[-2+12],(int) instance->ss_ht);
3493			Cmd[-2+16] = 0;
3494			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ga));		  /* 3d posn (12) */
3495		} else {
3496			memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad));
3497			w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
3498			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ad));	/* lat (6/8) */
3499
3500			memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae));
3501			w32_buf(&Cmd[-2+4], (int) instance->ss_long);
3502			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Ae));	/* long (6/8) */
3503
3504			memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af));
3505			w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
3506			Cmd[-2+8] = 0;
3507			oncore_sendmsg(instance->ttyfd, Cmd,  sizeof(oncore_cmd_Af));	/* ht (6/8) */
3508		}
3509
3510		/* Finally, turn on position hold */
3511
3512		if (instance->chan == 12)
3513			oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1,  sizeof(oncore_cmd_Gd1));
3514		else
3515			oncore_sendmsg(instance->ttyfd, oncore_cmd_At1,  sizeof(oncore_cmd_At1));
3516	}
3517}
3518
3519
3520
3521static void
3522oncore_set_traim(
3523	struct instance *instance
3524	)
3525{
3526	char	Msg[160];
3527
3528	if (instance->traim_in != -1)	/* set in Input */
3529		instance->traim = instance->traim_in;
3530	else
3531		instance->traim = instance->traim_ck;
3532
3533	sprintf(Msg, "Input   says TRAIM = %d", instance->traim_in);
3534	record_clock_stats(&(instance->peer->srcadr), Msg);
3535	sprintf(Msg, "Model # says TRAIM = %d", instance->traim_id);
3536	record_clock_stats(&(instance->peer->srcadr), Msg);
3537	sprintf(Msg, "Testing says TRAIM = %d", instance->traim_ck);
3538	record_clock_stats(&(instance->peer->srcadr), Msg);
3539	sprintf(Msg, "Using        TRAIM = %d", instance->traim);
3540	record_clock_stats(&(instance->peer->srcadr), Msg);
3541
3542	if (instance->traim_ck == 1 && instance->traim == 0) {
3543		/* if it should be off, and I turned it on during testing,
3544		   then turn it off again */
3545		if (instance->chan == 6)
3546			oncore_sendmsg(instance->ttyfd, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
3547		else if (instance->chan == 8)
3548			oncore_sendmsg(instance->ttyfd, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
3549		else	/* chan == 12 */
3550			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
3551	}
3552}
3553
3554
3555
3556/*
3557 * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
3558 */
3559
3560static void
3561oncore_shmem_get_3D(
3562	struct instance *instance
3563	)
3564{
3565	if (instance->pp->second%15 == 3) {	/* start the sequence */			/* by changing mode */
3566		instance->shmem_reset = 1;
3567		if (instance->chan == 12) {
3568			if (instance->shmem_Posn == 2)
3569				oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd2,  sizeof(oncore_cmd_Gd2));  /* 2D */
3570			else
3571				oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0,  sizeof(oncore_cmd_Gd0));  /* 3D */
3572		} else {
3573			if (instance->saw_At) { 		/* out of 0D -> 3D mode */
3574				oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0));
3575				if (instance->shmem_Posn == 2)	/* 3D -> 2D mode */
3576					oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
3577			} else
3578				oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
3579		}
3580	} else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
3581		instance->shmem_reset = 0;
3582		if (instance->chan == 12)
3583			oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1,  sizeof(oncore_cmd_Gd1));	/* 0D */
3584		else {
3585			if (instance->saw_At) {
3586				if (instance->mode == MODE_2D)	/* 2D -> 3D or 0D mode */
3587					oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
3588				oncore_sendmsg(instance->ttyfd, oncore_cmd_At1,  sizeof(oncore_cmd_At1)); /* to 0D mode */
3589			} else
3590				oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1,  sizeof(oncore_cmd_Av1));
3591		}
3592	}
3593}
3594
3595
3596
3597/*
3598 * Here we do the Software SiteSurvey.
3599 * We have to average our own position for the Position Hold Mode
3600 *   We use Heights from the GPS ellipsoid.
3601 * We check for the END of either HW or SW SiteSurvey.
3602 */
3603
3604static void
3605oncore_ss(
3606	struct instance *instance
3607	)
3608{
3609	char	*cp, Msg[160];
3610	double	lat, lon, ht;
3611
3612
3613	if (instance->site_survey == ONCORE_SS_HW) {
3614
3615		/*
3616		 * Check to see if Hardware SiteSurvey has Finished.
3617		 */
3618
3619		if ((instance->chan == 8  && !(instance->BEHa[37]  & 0x20)) ||
3620		    (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) {
3621			record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
3622
3623			if (instance->chan == 12)
3624				oncore_sendmsg(instance->ttyfd, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
3625			else
3626				oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
3627
3628			cp = "SSstate = ONCORE_SS_DONE";
3629			record_clock_stats(&(instance->peer->srcadr), cp);
3630			instance->site_survey = ONCORE_SS_DONE;
3631		}
3632	} else {
3633		/*
3634		 * Must be a Software Site Survey.
3635		 */
3636
3637		if (instance->rsm.bad_fix)	/* Not if poor geometry or less than 3 sats */
3638			return;
3639
3640		if (instance->mode != MODE_3D)	/* Use only 3D Fixes */
3641			return;
3642
3643		instance->ss_lat  += buf_w32(&instance->BEHa[15]);
3644		instance->ss_long += buf_w32(&instance->BEHa[19]);
3645		instance->ss_ht   += buf_w32(&instance->BEHa[23]);  /* GPS ellipsoid */
3646		instance->ss_count++;
3647
3648		if (instance->ss_count != POS_HOLD_AVERAGE)
3649			return;
3650
3651		instance->ss_lat  /= POS_HOLD_AVERAGE;
3652		instance->ss_long /= POS_HOLD_AVERAGE;
3653		instance->ss_ht   /= POS_HOLD_AVERAGE;
3654
3655		sprintf(Msg, "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
3656			instance->ss_lat, instance->ss_long, instance->ss_ht);
3657		record_clock_stats(&(instance->peer->srcadr), Msg);
3658		lat = instance->ss_lat/3600000.;
3659		lon = instance->ss_long/3600000.;
3660		ht  = instance->ss_ht/100;
3661		sprintf(Msg, "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
3662			lat, lon, ht);
3663		record_clock_stats(&(instance->peer->srcadr), Msg);
3664
3665		oncore_set_posn(instance);
3666
3667		record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
3668
3669		cp = "SSstate = ONCORE_SS_DONE";
3670		record_clock_stats(&(instance->peer->srcadr), cp);
3671		instance->site_survey = ONCORE_SS_DONE;
3672	}
3673}
3674
3675
3676
3677static int
3678oncore_wait_almanac(
3679	struct instance *instance
3680	)
3681{
3682	if (instance->rsm.bad_almanac) {
3683		if (debug)
3684			printf("ONCORE[%d]: waiting for almanac\n", instance->unit);
3685
3686		/*
3687		 * If we get here (first time) then we don't have an almanac in memory.
3688		 * Check if we have a SHMEM, and if so try to load whatever is there.
3689		 */
3690
3691		if (!instance->almanac_from_shmem) {
3692			instance->almanac_from_shmem = 1;
3693			oncore_load_almanac(instance);
3694		}
3695		return(1);
3696	} else {  /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn
3697		     commands, and can finally check for TRAIM.  Again, we set a delay
3698		     (5sec) and wait for things to settle down */
3699
3700		if (instance->chan == 6)
3701			oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
3702		else if (instance->chan == 8)
3703			oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof(oncore_cmd_En));
3704		else if (instance->chan == 12) {
3705			oncore_sendmsg(instance->ttyfd, oncore_cmd_Gc, sizeof(oncore_cmd_Gc));	/* 1PPS on, continuous */
3706			oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge, sizeof(oncore_cmd_Ge));	/* TRAIM on */
3707			oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn, sizeof(oncore_cmd_Hn));	/* TRAIM status 1/s */
3708		}
3709		instance->traim_delay = 1;
3710
3711		record_clock_stats(&(instance->peer->srcadr), "Have now loaded an ALMANAC");
3712
3713		instance->o_state = ONCORE_RUN;
3714		record_clock_stats(&(instance->peer->srcadr), "state = ONCORE_RUN");
3715	}
3716	return(0);
3717}
3718
3719
3720
3721#else
3722int refclock_oncore_bs;
3723#endif /* REFCLOCK */
3724