1/*
2 * $Id: refclock_ripencc.c,v 1.13 2002/06/18 14:20:55 marks Exp marks $
3 *
4 * Copyright (c) 2002  RIPE NCC
5 *
6 * All Rights Reserved
7 *
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and that
11 * both that copyright notice and this permission notice appear in
12 * supporting documentation, and that the name of the author not be
13 * used in advertising or publicity pertaining to distribution of the
14 * software without specific, written prior permission.
15 *
16 * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
18 * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
19 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
20 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 *
24 *
25 * This driver was developed for use with the RIPE NCC TTM project.
26 *
27 *
28 * The initial driver was developed by Daniel Karrenberg <dfk@ripe.net>
29 * using the code made available by Trimble. This was for xntpd-3.x.x
30 *
31 * Rewrite of the driver for ntpd-4.x.x by Mark Santcroos <marks@ripe.net>
32 *
33 */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif /* HAVE_CONFIG_H */
38
39#if defined(REFCLOCK) && defined(CLOCK_RIPENCC)
40
41#include "ntp_stdlib.h"
42#include "ntpd.h"
43#include "ntp_refclock.h"
44#include "ntp_unixtime.h"
45#include "ntp_io.h"
46
47#ifdef HAVE_PPSAPI
48# include "ppsapi_timepps.h"
49#endif
50
51/*
52 * Definitions
53 */
54
55/* we are on little endian */
56#define BYTESWAP
57
58/*
59 * DEBUG statements: uncomment if necessary
60 */
61/* #define DEBUG_NCC */ /* general debug statements */
62/* #define DEBUG_PPS */ /* debug pps */
63/* #define DEBUG_RAW */ /* print raw packets */
64
65#define TRIMBLE_OUTPUT_FUNC
66#define TSIP_VERNUM "7.12a"
67
68#ifndef FALSE
69#define FALSE 	(0)
70#define TRUE 	(!FALSE)
71#endif /* FALSE */
72
73#define GPS_PI 	(3.1415926535898)
74#define GPS_C 		(299792458.)
75#define	D2R		(GPS_PI/180.0)
76#define	R2D		(180.0/GPS_PI)
77#define WEEK 	(604800.)
78#define MAXCHAN  (8)
79
80/* control characters for TSIP packets */
81#define DLE 	(0x10)
82#define ETX 	(0x03)
83
84#define MAX_RPTBUF (256)
85
86/* values of TSIPPKT.status */
87#define TSIP_PARSED_EMPTY 	0
88#define TSIP_PARSED_FULL 	1
89#define TSIP_PARSED_DLE_1 	2
90#define TSIP_PARSED_DATA 	3
91#define TSIP_PARSED_DLE_2 	4
92
93#define UTCF_UTC_AVAIL  (unsigned char) (1)             /* UTC available */
94#define UTCF_LEAP_SCHD  (unsigned char) (1<<4)  /* Leap scheduled */
95#define UTCF_LEAP_PNDG  (unsigned char) (1<<5)  /* Leap pending, will occur at end of day */
96
97#define DEVICE  "/dev/gps%d"	/* name of radio device */
98#define PRECISION       (-9)    /* precision assumed (about 2 ms) */
99#define PPS_PRECISION   (-20)	/* precision assumed (about 1 us) */
100#define REFID           "GPS\0" /* reference id */
101#define REFID_LEN	4
102#define DESCRIPTION     "RIPE NCC GPS (Palisade)"	/* Description */
103#define SPEED232        B9600   /* 9600 baud */
104
105#define NSAMPLES        3       /* stages of median filter */
106
107/* Structures */
108
109/* TSIP packets have the following structure, whether report or command. */
110typedef struct {
111	short
112		counter, 	/* counter */
113		len;		/* size of buf; < MAX_RPTBUF unsigned chars */
114	unsigned char
115		status,		/* TSIP packet format/parse status */
116		code,		/* TSIP code */
117		buf[MAX_RPTBUF];/* report or command string */
118} TSIPPKT;
119
120/* TSIP binary data structures */
121typedef struct {
122	unsigned char
123		t_oa_raw, SV_health;
124	float
125		e, t_oa, i_0, OMEGADOT, sqrt_A,
126		OMEGA_0, omega, M_0, a_f0, a_f1,
127		Axis, n, OMEGA_n, ODOT_n, t_zc;
128	short
129		weeknum, wn_oa;
130} ALM_INFO;
131
132typedef struct {     /*  Almanac health page (25) parameters  */
133	unsigned char
134		WN_a, SV_health[32], t_oa;
135} ALH_PARMS;
136
137typedef struct {     /*  Universal Coordinated Time (UTC) parms */
138	double
139		A_0;
140	float
141		A_1;
142	short
143		delta_t_LS;
144	float
145		t_ot;
146	short
147		WN_t, WN_LSF, DN, delta_t_LSF;
148} UTC_INFO;
149
150typedef struct {      /*  Ionospheric info (float)  */
151	float
152		alpha_0, alpha_1, alpha_2, alpha_3,
153		beta_0, beta_1, beta_2, beta_3;
154} ION_INFO;
155
156typedef struct {      /*  Subframe 1 info (float)  */
157	short
158		weeknum;
159	unsigned char
160		codeL2, L2Pdata, SVacc_raw, SV_health;
161	short
162		IODC;
163	float
164		T_GD, t_oc, a_f2, a_f1, a_f0, SVacc;
165} EPHEM_CLOCK;
166
167typedef	struct {     /*  Ephemeris info (float)  */
168	unsigned char
169		IODE, fit_interval;
170	float
171		C_rs, delta_n;
172	double
173		M_0;
174	float
175		C_uc;
176	double
177		e;
178	float
179		C_us;
180	double
181		sqrt_A;
182	float
183		t_oe, C_ic;
184	double
185		OMEGA_0;
186	float
187		C_is;
188	double
189		i_0;
190	float
191		C_rc;
192	double
193		omega;
194	float
195		OMEGADOT, IDOT;
196	double
197		Axis, n, r1me2, OMEGA_n, ODOT_n;
198} EPHEM_ORBIT;
199
200typedef struct {     /* Navigation data structure */
201	short
202		sv_number;     /* SV number (0 = no entry) */
203	float
204		t_ephem;       /* time of ephemeris collection */
205	EPHEM_CLOCK
206		ephclk;        /* subframe 1 data */
207	EPHEM_ORBIT
208		ephorb;        /* ephemeris data */
209} NAV_INFO;
210
211typedef struct {
212	unsigned char
213		bSubcode,
214		operating_mode,
215		dgps_mode,
216		dyn_code,
217		trackmode;
218	float
219		elev_mask,
220		cno_mask,
221		dop_mask,
222		dop_switch;
223	unsigned char
224		dgps_age_limit;
225} TSIP_RCVR_CFG;
226
227
228#ifdef TRIMBLE_OUTPUT_FUNC
229static char
230	*dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
231	old_baudnum[] = {0, 1, 4, 5, 6, 8, 9, 11, 28, 12},
232        *st_baud_text_app [] = {"", "", "  300", "  600", " 1200", " 2400",
233		" 4800", " 9600", "19200", "38400"},
234	*old_parity_text[] = {"EVEN", "ODD", "", "", "NONE"},
235	*parity_text [] = {"NONE", "ODD", "EVEN"},
236	*old_input_ch[] = { "TSIP", "RTCM (6 of 8 bits)"},
237	*old_output_ch[] = { "TSIP", "No output", "", "", "", "NMEA 0183"},
238	*protocols_in_text[] = { "", "TSIP", "", ""},
239	*protocols_out_text[] =	{ "", "TSIP", "NMEA"},
240	*rcvr_port_text [] = { "Port A      ", "Port B      ", "Current Port"},
241	*dyn_text [] = {"Unchanged", "Land", "Sea", "Air", "Static"},
242	*NavModeText0xBB[] = {"automatic", "time only (0-D)", "", "2-D",
243		"3-D", "", "", "OverDetermined Time"},
244	*PPSTimeBaseText[] = {"GPS", "UTC", "USER"},
245	*PPSPolarityText[] = {"Positive", "Negative"},
246  	*MaskText[] = { "Almanac  ", "Ephemeris", "UTC      ", "Iono     ",
247		"GPS Msg  ", "Alm Hlth ", "Time Fix ", "SV Select",
248		"Ext Event", "Pos Fix  ", "Raw Meas "};
249
250#endif /* TRIMBLE_OUTPUT_FUNC */
251
252/*
253 * Unit control structure
254 */
255struct ripencc_unit {
256        int unit;                       /* unit number */
257        int     pollcnt;                /* poll message counter */
258        int     polled;                 /* Hand in a sample? */
259        char leapdelta;                 /* delta of next leap event */
260        unsigned char utcflags;         /* delta of next leap event */
261        l_fp    tstamp;                 /* timestamp of last poll */
262
263        struct timespec ts;             /* last timestamp */
264        pps_params_t pps_params;        /* pps parameters */
265        pps_info_t pps_info;            /* last pps data */
266        pps_handle_t handle;            /* pps handlebars */
267
268};
269
270
271/*******************        PROTOYPES            *****************/
272
273/*  prototypes for report parsing primitives */
274short rpt_0x3D (TSIPPKT *rpt, unsigned char *tx_baud_index,
275	unsigned char *rx_baud_index, unsigned char *char_format_index,
276	unsigned char *stop_bits, unsigned char *tx_mode_index,
277	unsigned char *rx_mode_index);
278short rpt_0x40 (TSIPPKT *rpt, unsigned char *sv_prn, short *week_num,
279	float *t_zc, float *eccentricity, float *t_oa, float *i_0,
280	float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega,
281	float *M_0);
282short rpt_0x41 (TSIPPKT *rpt, float *time_of_week, float *UTC_offset,
283	short *week_num);
284short rpt_0x42 (TSIPPKT *rpt, float ECEF_pos[3], float *time_of_fix);
285short rpt_0x43 (TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset,
286	float *time_of_fix);
287short rpt_0x45 (TSIPPKT *rpt, unsigned char *major_nav_version,
288	unsigned char *minor_nav_version, unsigned char *nav_day,
289	unsigned char *nav_month, unsigned char *nav_year,
290	unsigned char *major_dsp_version, unsigned char *minor_dsp_version,
291	unsigned char *dsp_day, unsigned char *dsp_month,
292	unsigned char *dsp_year);
293short rpt_0x46 (TSIPPKT *rpt, unsigned char *status1, unsigned char *status2);
294short rpt_0x47 (TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn,
295	float *snr);
296short rpt_0x48 (TSIPPKT *rpt, unsigned char *message);
297short rpt_0x49 (TSIPPKT *rpt, unsigned char *sv_health);
298short rpt_0x4A (TSIPPKT *rpt, float *lat, float *lon, float *alt,
299	float *clock_bias, float *time_of_fix);
300short rpt_0x4A_2 (TSIPPKT *rpt, float *alt, float *dummy,
301	unsigned char *alt_flag);
302short rpt_0x4B (TSIPPKT *rpt, unsigned char *machine_id,
303	unsigned char *status3, unsigned char *status4);
304short rpt_0x4C (TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask,
305	float *snr_mask, float *dop_mask, float *dop_switch);
306short rpt_0x4D (TSIPPKT *rpt, float *osc_offset);
307short rpt_0x4E (TSIPPKT *rpt, unsigned char *response);
308short rpt_0x4F (TSIPPKT *rpt, double *a0, float *a1, float *time_of_data,
309	short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf);
310short rpt_0x54 (TSIPPKT *rpt, float *clock_bias, float *freq_offset,
311	float *time_of_fix);
312short rpt_0x55 (TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code,
313	unsigned char *time_code, unsigned char *aux_code);
314short rpt_0x56 (TSIPPKT *rpt, float vel_ENU[3], float *freq_offset,
315	float *time_of_fix);
316short rpt_0x57 (TSIPPKT *rpt, unsigned char *source_code,
317	unsigned char *diag_code, short *week_num, float *time_of_fix);
318short rpt_0x58 (TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type,
319	unsigned char *sv_prn, unsigned char *data_length,
320	unsigned char *data_packet);
321short rpt_0x59 (TSIPPKT *rpt, unsigned char *code_type,
322	unsigned char status_code[32]);
323short rpt_0x5A (TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length,
324	float *signal_level, float *code_phase, float *Doppler,
325	double *time_of_fix);
326short rpt_0x5B (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health,
327	unsigned char *sv_iode, unsigned char *fit_interval_flag,
328	float *time_of_collection, float *time_of_eph, float *sv_accy);
329short rpt_0x5C (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot,
330	unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag,
331	float *signal_level, float *time_of_last_msmt, float *elev,
332	float *azim, unsigned char *old_msmt_flag,
333	unsigned char *integer_msec_flag, unsigned char *bad_data_flag,
334	unsigned char *data_collect_flag);
335short rpt_0x6D (TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs,
336	unsigned char *ndim, unsigned char sv_prn[], float *pdop,
337	float *hdop, float *vdop, float *tdop);
338short rpt_0x82 (TSIPPKT *rpt, unsigned char *diff_mode);
339short rpt_0x83 (TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias,
340	float *time_of_fix);
341short rpt_0x84 (TSIPPKT *rpt, double *lat, double *lon, double *alt,
342	double *clock_bias, float *time_of_fix);
343short rpt_Paly0xBB(TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB);
344short rpt_0xBC   (TSIPPKT *rpt, unsigned char *port_num,
345	unsigned char *in_baud, unsigned char *out_baud,
346	unsigned char *data_bits, unsigned char *parity,
347	unsigned char *stop_bits, unsigned char *flow_control,
348	unsigned char *protocols_in, unsigned char *protocols_out,
349	unsigned char *reserved);
350
351/* prototypes for superpacket parsers */
352
353short rpt_0x8F0B (TSIPPKT *rpt, unsigned short *event, double *tow,
354   unsigned char *date, unsigned char *month, short *year,
355   unsigned char *dim_mode, short *utc_offset, double *bias, double *drift,
356   float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt,
357   char sv_id[8]);
358short rpt_0x8F14 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
359short rpt_0x8F15 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
360short rpt_0x8F20 (TSIPPKT *rpt, unsigned char *info, double *lat,
361	double *lon, double *alt, double vel_enu[], double *time_of_fix,
362	short *week_num, unsigned char *nsvs, unsigned char sv_prn[],
363	short sv_IODC[], short *datum_index);
364short rpt_0x8F41 (TSIPPKT *rpt, unsigned char *bSearchRange,
365	unsigned char *bBoardOptions, unsigned long *iiSerialNumber,
366	unsigned char *bBuildYear, unsigned char *bBuildMonth,
367	unsigned char *bBuildDay, unsigned char *bBuildHour,
368	float *fOscOffset, unsigned short *iTestCodeId);
369short rpt_0x8F42 (TSIPPKT *rpt, unsigned char *bProdOptionsPre,
370	unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre,
371	unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber,
372	unsigned short *iPremiumOptions, unsigned short *iMachineID,
373	unsigned short *iKey);
374short rpt_0x8F45 (TSIPPKT *rpt, unsigned char *bSegMask);
375short rpt_0x8F4A_16 (TSIPPKT *rpt, unsigned char *pps_enabled,
376	unsigned char *pps_timebase, unsigned char *pos_polarity,
377	double *pps_offset, float *bias_unc_threshold);
378short rpt_0x8F4B (TSIPPKT *rpt, unsigned long *decorr_max);
379short rpt_0x8F4D (TSIPPKT *rpt, unsigned long *event_mask);
380short rpt_0x8FA5 (TSIPPKT *rpt, unsigned char *spktmask);
381short rpt_0x8FAD (TSIPPKT *rpt, unsigned short *COUNT, double *FracSec,
382    unsigned char *Hour, unsigned char *Minute, unsigned char *Second,
383    unsigned char *Day, unsigned char *Month, unsigned short *Year,
384    unsigned char *Status, unsigned char *Flags);
385
386/**/
387/* prototypes for command-encode primitives with suffix convention:  */
388/* c = clear, s = set, q = query, e = enable, d = disable            */
389void cmd_0x1F  (TSIPPKT *cmd);
390void cmd_0x26  (TSIPPKT *cmd);
391void cmd_0x2F  (TSIPPKT *cmd);
392void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
393	unsigned char time_code, unsigned char opts_code);
394void cmd_0x3C  (TSIPPKT *cmd, unsigned char sv_prn);
395void cmd_0x3Ds (TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp,
396	unsigned char char_code, unsigned char stopbitcode,
397	unsigned char output_mode, unsigned char input_mode);
398void cmd_0xBBq (TSIPPKT *cmd, unsigned char subcode) ;
399
400/* prototypes 8E commands */
401void cmd_0x8E0Bq (TSIPPKT *cmd);
402void cmd_0x8E41q (TSIPPKT *cmd);
403void cmd_0x8E42q (TSIPPKT *cmd);
404void cmd_0x8E4Aq (TSIPPKT *cmd);
405void cmd_0x8E4As (TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase,
406	unsigned char Polarity, double PPSOffset, float Uncertainty);
407void cmd_0x8E4Bq (TSIPPKT *cmd);
408void cmd_0x8E4Ds (TSIPPKT *cmd, unsigned long AutoOutputMask);
409void cmd_0x8EADq (TSIPPKT *cmd);
410
411/* header/source border XXXXXXXXXXXXXXXXXXXXXXXXXX */
412
413/* Trimble parse functions */
414static 	int	parse0x8FAD	P((TSIPPKT *, struct peer *));
415static 	int	parse0x8F0B	P((TSIPPKT *, struct peer *));
416#ifdef TRIMBLE_OUTPUT_FUNC
417static 	int	parseany	P((TSIPPKT *, struct peer *));
418static 	void	TranslateTSIPReportToText	P((TSIPPKT *, char *));
419#endif /* TRIMBLE_OUTPUT_FUNC */
420static 	int	parse0x5C	P((TSIPPKT *, struct peer *));
421static 	int	parse0x4F	P((TSIPPKT *, struct peer *));
422static	void	tsip_input_proc	P((TSIPPKT *, int));
423
424/* Trimble helper functions */
425static	void	bPutFloat 	P((float *, unsigned char *));
426static	void	bPutDouble 	P((double *, unsigned char *));
427static	void	bPutULong 	P((unsigned long *, unsigned char *));
428static	int	print_msg_table_header	P((int rptcode, char *HdrStr, int force));
429static	char *	show_time	P((float time_of_week));
430
431/* RIPE NCC functions */
432static	void	ripencc_control	P((int, struct refclockstat *, struct
433				refclockstat *, struct peer *));
434static	int	ripencc_ppsapi	P((struct peer *, int, int));
435static	int	ripencc_get_pps_ts	P((struct ripencc_unit *, l_fp *));
436static	int	ripencc_start	P((int, struct peer *));
437static 	void	ripencc_shutdown	P((int, struct peer *));
438static 	void	ripencc_poll	P((int, struct peer *));
439static 	void	ripencc_send	P((struct peer *, TSIPPKT spt));
440static 	void	ripencc_receive	P((struct recvbuf *));
441
442/* fill in reflock structure for our clock */
443struct refclock refclock_ripencc = {
444	ripencc_start,		/* start up driver */
445	ripencc_shutdown,	/* shut down driver */
446	ripencc_poll,		/* transmit poll message */
447	ripencc_control,	/* control function */
448	noentry,		/* initialize driver */
449	noentry,		/* debug info */
450	NOFLAGS			/* clock flags */
451};
452
453/*
454 *  Tables to compute the ddd of year form icky dd/mm timecode. Viva la
455 *  leap.
456 */
457static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
458static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
459
460
461/*
462 * ripencc_start - open the GPS devices and initialize data for processing
463 */
464static int
465ripencc_start(int unit, struct peer *peer)
466{
467	register struct ripencc_unit *up;
468	struct refclockproc *pp;
469	char device[40];
470	int fd;
471	struct termios tio;
472	TSIPPKT spt;
473
474	/*
475	 * Open serial port
476	 */
477	(void)snprintf(device, sizeof(device), DEVICE, unit);
478	if (!(fd = refclock_open(device, SPEED232, LDISC_RAW)))
479		return (0);
480
481	/* from refclock_palisade.c */
482	if (tcgetattr(fd, &tio) < 0) {
483		msyslog(LOG_ERR, "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
484		return (0);
485	}
486
487	/*
488	 * set flags
489	 */
490	tio.c_cflag |= (PARENB|PARODD);
491	tio.c_iflag &= ~ICRNL;
492	if (tcsetattr(fd, TCSANOW, &tio) == -1) {
493		msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
494		return (0);
495	}
496
497	/*
498	 * Allocate and initialize unit structure
499	 */
500	if (!(up = (struct ripencc_unit *)
501				emalloc(sizeof(struct ripencc_unit)))) {
502		(void) close(fd);
503		return (0);
504	}
505	memset((char *)up, 0, sizeof(struct ripencc_unit));
506	pp = peer->procptr;
507	pp->io.clock_recv = ripencc_receive;
508	pp->io.srcclock = (caddr_t)peer;
509	pp->io.datalen = 0;
510	pp->io.fd = fd;
511	if (!io_addclock(&pp->io)) {
512		(void) close(fd);
513		free(up);
514		return (0);
515	}
516	pp->unitptr = (caddr_t)up;
517
518	/*
519	 * Initialize miscellaneous variables
520	 */
521	peer->precision = PRECISION;
522	pp->clockdesc = DESCRIPTION;
523	memcpy((char *)&pp->refid, REFID, REFID_LEN);
524	up->pollcnt = 2;
525	up->unit = unit;
526	up->leapdelta = 0;
527	up->utcflags = 0;
528
529	/*
530	 * Initialize the Clock
531	 */
532
533	/* query software versions */
534	cmd_0x1F(&spt);
535	ripencc_send(peer, spt);
536
537	/* query receiver health */
538	cmd_0x26(&spt);
539	ripencc_send(peer, spt);
540
541	/* query serial numbers */
542	cmd_0x8E42q(&spt);
543	ripencc_send(peer, spt);
544
545	/* query manuf params */
546	cmd_0x8E41q(&spt);
547	ripencc_send(peer, spt);
548
549	/* i/o opts */ /* trimble manual page A30 */
550	cmd_0x35s(&spt,
551		0x1C, 	/* position */
552		0x00, 	/* velocity */
553		0x05, 	/* timing */
554		0x0a); 	/* auxilary */
555	ripencc_send(peer, spt);
556
557	/* turn off port A */
558	cmd_0x3Ds (&spt,
559		0x0B, /* baud_out */
560		0x0B, /* baud_inp */
561		0x07, /* char_code */
562		0x07, /* stopbitcode */
563		0x01, /* output_mode */
564		0x00); /* input_mode */
565	ripencc_send(peer, spt);
566
567	/* set i/o options */
568	cmd_0x8E4As (&spt,
569		0x01, 		/* PPS on */
570		0x01, 		/* Timebase UTC */
571		0x00, 		/* polarity positive */
572		0., 		/* 100 ft. cable XXX make flag */
573		1e-6 * GPS_C); 	/* turn of biasuncert. > (1us) */
574	ripencc_send(peer,spt);
575
576	/* all outomatic packet output off */
577	cmd_0x8E4Ds(&spt,
578		0x00000000); /* AutoOutputMask */
579	ripencc_send(peer, spt);
580
581	cmd_0xBBq (&spt,
582		0x00); /* query primary configuration */
583	ripencc_send(peer,spt);
584
585
586	/* query PPS parameters */
587	cmd_0x8E4Aq (&spt); /* query PPS params */
588	ripencc_send(peer,spt);
589
590	/* query survey limit */
591	cmd_0x8E4Bq (&spt); /* query survey limit */
592	ripencc_send(peer,spt);
593
594#ifdef DEBUG_NCC
595	if (debug)
596		printf("ripencc_start: success\n");
597#endif /* DEBUG_NCC */
598
599	/*
600	 * Start the PPSAPI interface if it is there. Default to use
601	 * the assert edge and do not enable the kernel hardpps.
602	 */
603	if (time_pps_create(fd, &up->handle) < 0) {
604		up->handle = 0;
605		msyslog(LOG_ERR, "refclock_ripencc: time_pps_create failed: %m");
606		return (1);
607	}
608
609	return(ripencc_ppsapi(peer, 0, 0));
610}
611
612/*
613 * ripencc_control - fudge control
614 */
615static void
616ripencc_control(
617	int unit,		/* unit (not used) */
618	struct refclockstat *in, /* input parameters (not used) */
619	struct refclockstat *out, /* output parameters (not used) */
620	struct peer *peer	/* peer structure pointer */
621	)
622{
623	struct refclockproc *pp;
624
625#ifdef DEBUG_NCC
626	msyslog(LOG_INFO,"%s()",__FUNCTION__);
627#endif /* DEBUG_NCC */
628
629	pp = peer->procptr;
630	ripencc_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
631	    pp->sloppyclockflag & CLK_FLAG3);
632}
633
634
635/*
636 * Initialize PPSAPI
637 */
638int
639ripencc_ppsapi(
640	struct peer *peer,	/* peer structure pointer */
641	int enb_clear,		/* clear enable */
642	int enb_hardpps		/* hardpps enable */
643	)
644{
645	struct refclockproc *pp;
646	struct ripencc_unit *up;
647	int capability;
648
649	pp = peer->procptr;
650	up = (struct ripencc_unit *)pp->unitptr;
651	if (time_pps_getcap(up->handle, &capability) < 0) {
652		msyslog(LOG_ERR,
653		    "refclock_ripencc: time_pps_getcap failed: %m");
654		return (0);
655	}
656	memset(&up->pps_params, 0, sizeof(pps_params_t));
657	if (enb_clear)
658		up->pps_params.mode = capability & PPS_CAPTURECLEAR;
659	else
660		up->pps_params.mode = capability & PPS_CAPTUREASSERT;
661	if (!up->pps_params.mode) {
662		msyslog(LOG_ERR,
663		    "refclock_ripencc: invalid capture edge %d",
664		    !enb_clear);
665		return (0);
666	}
667	up->pps_params.mode |= PPS_TSFMT_TSPEC;
668	if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
669		msyslog(LOG_ERR,
670		    "refclock_ripencc: time_pps_setparams failed: %m");
671		return (0);
672	}
673	if (enb_hardpps) {
674		if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
675				    up->pps_params.mode & ~PPS_TSFMT_TSPEC,
676				    PPS_TSFMT_TSPEC) < 0) {
677			msyslog(LOG_ERR,
678			    "refclock_ripencc: time_pps_kcbind failed: %m");
679			return (0);
680		}
681		pps_enable = 1;
682	}
683	peer->precision = PPS_PRECISION;
684
685#if DEBUG_NCC
686	if (debug) {
687		time_pps_getparams(up->handle, &up->pps_params);
688		printf(
689		    "refclock_ripencc: capability 0x%x version %d mode 0x%x kern %d\n",
690		    capability, up->pps_params.api_version,
691		    up->pps_params.mode, enb_hardpps);
692	}
693#endif /* DEBUG_NCC */
694
695	return (1);
696}
697
698/*
699 * This function is called every 64 seconds from ripencc_receive
700 * It will fetch the pps time
701 *
702 * Return 0 on failure and 1 on success.
703 */
704static int
705ripencc_get_pps_ts(
706	struct ripencc_unit *up,
707	l_fp *tsptr
708	)
709{
710	pps_info_t pps_info;
711	struct timespec timeout, ts;
712	double dtemp;
713	l_fp tstmp;
714
715#ifdef DEBUG_PPS
716	msyslog(LOG_INFO,"ripencc_get_pps_ts\n");
717#endif /* DEBUG_PPS */
718
719
720	/*
721	 * Convert the timespec nanoseconds field to ntp l_fp units.
722	 */
723	if (up->handle == 0)
724		return (0);
725	timeout.tv_sec = 0;
726	timeout.tv_nsec = 0;
727	memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
728	if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
729	    &timeout) < 0)
730		return (0);
731	if (up->pps_params.mode & PPS_CAPTUREASSERT) {
732		if (pps_info.assert_sequence ==
733		    up->pps_info.assert_sequence)
734			return (0);
735		ts = up->pps_info.assert_timestamp;
736	} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
737		if (pps_info.clear_sequence ==
738		    up->pps_info.clear_sequence)
739			return (0);
740		ts = up->pps_info.clear_timestamp;
741	} else {
742		return (0);
743	}
744	if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
745		return (0);
746	up->ts = ts;
747
748	tstmp.l_ui = ts.tv_sec + JAN_1970;
749	dtemp = ts.tv_nsec * FRAC / 1e9;
750	tstmp.l_uf = (u_int32)dtemp;
751
752#ifdef DEBUG_PPS
753	msyslog(LOG_INFO,"ts.tv_sec: %d\n",(int)ts.tv_sec);
754	msyslog(LOG_INFO,"ts.tv_nsec: %ld\n",ts.tv_nsec);
755#endif /* DEBUG_PPS */
756
757	*tsptr = tstmp;
758	return (1);
759}
760
761/*
762 * ripencc_shutdown - shut down a GPS clock
763 */
764static void
765ripencc_shutdown(int unit, struct peer *peer)
766{
767	register struct ripencc_unit *up;
768	struct refclockproc *pp;
769
770	pp = peer->procptr;
771	up = (struct ripencc_unit *)pp->unitptr;
772
773	if (up->handle != 0)
774		time_pps_destroy(up->handle);
775
776	io_closeclock(&pp->io);
777
778	free(up);
779}
780
781/*
782 * ripencc_poll - called by the transmit procedure
783 */
784static void
785ripencc_poll(int unit, struct peer *peer)
786{
787	register struct ripencc_unit *up;
788	struct refclockproc *pp;
789	TSIPPKT spt;
790
791#ifdef DEBUG_NCC
792	if (debug)
793		fprintf(stderr, "ripencc_poll(%d)\n", unit);
794#endif /* DEBUG_NCC */
795	pp = peer->procptr;
796	up = (struct ripencc_unit *)pp->unitptr;
797	if (up->pollcnt == 0)
798		refclock_report(peer, CEVNT_TIMEOUT);
799	else
800		up->pollcnt--;
801
802	pp->polls++;
803	up->polled = 1;
804
805	/* poll for UTC superpacket */
806	cmd_0x8EADq (&spt);
807	ripencc_send(peer,spt);
808}
809
810/*
811 * ripencc_send - send message to clock
812 * use the structures being created by the trimble functions!
813 * makes the code more readable/clean
814 */
815static void
816ripencc_send(struct peer *peer, TSIPPKT spt)
817{
818	unsigned char *ip, *op;
819	unsigned char obuf[512];
820
821#ifdef DEBUG_RAW
822	{
823		register struct ripencc_unit *up;
824		register struct refclockproc *pp;
825
826		pp = peer->procptr;
827		up = (struct ripencc_unit *)pp->unitptr;
828		if (debug)
829			printf("ripencc_send(%d, %02X)\n", up->unit, cmd);
830	}
831#endif /* DEBUG_RAW */
832
833	ip = spt.buf;
834	op = obuf;
835
836	*op++ = 0x10;
837	*op++ = spt.code;
838
839	while (spt.len--) {
840		if (op-obuf > sizeof(obuf)-5) {
841			msyslog(LOG_ERR, "ripencc_send obuf overflow!");
842			refclock_report(peer, CEVNT_FAULT);
843			return;
844		}
845
846		if (*ip == 0x10)  /* byte stuffing */
847			*op++ = 0x10;
848		*op++ = *ip++;
849	}
850
851	*op++ = 0x10;
852	*op++ = 0x03;
853
854#ifdef DEBUG_RAW
855	if (debug) { /* print raw packet */
856		unsigned char *cp;
857		int i;
858
859		printf("ripencc_send: len %d\n", op-obuf);
860		for (i=1, cp=obuf; cp<op; i++, cp++) {
861			printf(" %02X", *cp);
862			if (i%10 == 0)
863				printf("\n");
864		}
865		printf("\n");
866	}
867#endif /* DEBUG_RAW */
868
869	if (write(peer->procptr->io.fd, obuf, op-obuf) == -1) {
870			refclock_report(peer, CEVNT_FAULT);
871	}
872}
873
874/*
875 * ripencc_receive()
876 *
877 * called when a packet is received on the serial port
878 * takes care of further processing
879 *
880 */
881static void
882ripencc_receive(struct recvbuf *rbufp)
883{
884	register struct ripencc_unit *up;
885	register struct refclockproc *pp;
886	struct peer *peer;
887	static TSIPPKT rpt; /* structure for current incoming TSIP report  */
888	TSIPPKT spt; /* send packet */
889	int ns_since_pps;
890	int i;
891	char *cp;
892	/* Use these variables to hold data until we decide its worth keeping */
893	char    rd_lastcode[BMAX];
894	l_fp    rd_tmp;
895	u_short rd_lencode;
896
897	/* msyslog(LOG_INFO, "%s",__FUNCTION__); */
898
899	/*
900	 * Initialize pointers and read the timecode and timestamp
901	 */
902	peer = (struct peer *)rbufp->recv_srcclock;
903	pp = peer->procptr;
904	up = (struct ripencc_unit *)pp->unitptr;
905	rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
906
907#ifdef DEBUG_RAW
908	if (debug)
909		fprintf(stderr, "ripencc_receive(%d)\n", up->unit);
910#endif /* DEBUG_RAW */
911
912#ifdef DEBUG_RAW
913	if (debug) { /* print raw packet */
914		int i;
915		unsigned char *cp;
916
917		printf("ripencc_receive: len %d\n", rbufp->recv_length);
918		for (i=1, cp=(char*)&rbufp->recv_space; i <= rbufp->recv_length; i++, cp++) {
919			printf(" %02X", *cp);
920			if (i%10 == 0)
921				printf("\n");
922		}
923		printf("\n");
924	}
925#endif /* DEBUG_RAW */
926
927	cp = (char*) &rbufp->recv_space;
928	i=rbufp->recv_length;
929
930	while (i--) { /* loop over received chars */
931
932		tsip_input_proc(&rpt, (unsigned char) *cp++);
933
934		if (rpt.status != TSIP_PARSED_FULL)
935			continue;
936
937		switch (rpt.code) {
938
939		case 0x8F:	/* superpacket */
940
941			switch (rpt.buf[0]) {
942
943			case 0xAD:	/* UTC Time */
944				/*
945				 * When polling on port B the timecode
946				 * is the time of the previous PPS.
947				 * If we completed receiving the packet
948				 * less than 150ms after the turn of the second,
949				 * it may have the code of the previous second.
950				 * We do not trust that and simply poll again
951				 * without even parsing it.
952				 *
953				 * More elegant would be to re-schedule the poll,
954				 * but I do not know (yet) how to do that cleanly.
955				 *
956				 */
957				/* BLA ns_since_pps = ncc_tstmp(rbufp, &trtmp); */
958/*   if (up->polled && ns_since_pps > -1 && ns_since_pps < 150) { */
959
960				ns_since_pps=200;
961				if (up->polled && ns_since_pps < 150) {
962					msyslog(LOG_INFO, "%s(): up->polled",__FUNCTION__);
963					ripencc_poll(up->unit, peer);
964					break;
965				}
966
967			        /*
968 				 * Parse primary utc time packet
969				 * and fill refclock structure
970				 * from results.
971				 */
972				if (parse0x8FAD(&rpt, peer) < 0) {
973						msyslog(LOG_INFO, "%s(): parse0x8FAD < 0",__FUNCTION__);
974						refclock_report(peer, CEVNT_BADREPLY);
975						break;
976				}
977				/*
978				 * If the PPSAPI is working, rather use its
979				 * timestamps.
980				 * assume that the PPS occurs on the second
981				 * so blow any msec
982				 */
983				if (ripencc_get_pps_ts(up, &rd_tmp) == 1) {
984					pp->lastrec = up->tstamp = rd_tmp;
985					pp->nsec = 0;
986				}
987				else
988					msyslog(LOG_INFO, "%s(): ripencc_get_pps_ts returns failure\n",__FUNCTION__);
989
990
991				if (!up->polled) {
992					msyslog(LOG_INFO, "%s(): unrequested packet\n",__FUNCTION__);
993					/* unrequested packet */
994					break;
995				}
996
997				/* we have been polled ! */
998				up->polled = 0;
999				up->pollcnt = 2;
1000
1001				/* poll for next packet */
1002				cmd_0x8E0Bq(&spt);
1003				ripencc_send(peer,spt);
1004
1005				if (ns_since_pps < 0) { /* no PPS */
1006					msyslog(LOG_INFO, "%s(): ns_since_pps < 0",__FUNCTION__);
1007					refclock_report(peer, CEVNT_BADTIME);
1008					break;
1009				}
1010
1011				/*
1012				 * Process the new sample in the median filter and determine the
1013				 * reference clock offset and dispersion.
1014 				 */
1015				if (!refclock_process(pp)) {
1016					msyslog(LOG_INFO, "%s(): !refclock_process",__FUNCTION__);
1017					refclock_report(peer, CEVNT_BADTIME);
1018					break;
1019				}
1020
1021				refclock_receive(peer);
1022				break;
1023
1024			case 0x0B: /* comprehensive time packet */
1025				parse0x8F0B(&rpt, peer);
1026				break;
1027
1028			default: /* other superpackets */
1029#ifdef DEBUG_NCC
1030				msyslog(LOG_INFO, "%s(): calling parseany",__FUNCTION__);
1031#endif /* DEBUG_NCC */
1032#ifdef TRIMBLE_OUTPUT_FUNC
1033				parseany(&rpt, peer);
1034#endif /* TRIMBLE_OUTPUT_FUNC */
1035				break;
1036			}
1037			break;
1038
1039		case 0x4F:	/* UTC parameters, for leap info */
1040			parse0x4F(&rpt, peer);
1041			break;
1042
1043		case 0x5C:	/* sat tracking data */
1044			parse0x5C(&rpt, peer);
1045			break;
1046
1047		default: /* other packets */
1048#ifdef TRIMBLE_OUTPUT_FUNC
1049			parseany(&rpt, peer);
1050#endif /* TRIMBLE_OUTPUT_FUNC */
1051			break;
1052		}
1053   		rpt.status = TSIP_PARSED_EMPTY;
1054	}
1055}
1056
1057/*
1058 * All trimble functions that are directly referenced from driver code
1059 * (so not from parseany)
1060 */
1061
1062void cmd_0x1F (TSIPPKT *cmd)
1063/* request software versions */
1064{
1065	cmd->len = 0;
1066	cmd->code = 0x1F;
1067}
1068
1069void cmd_0x26 (TSIPPKT *cmd)
1070/* request receiver health */
1071{
1072	cmd->len = 0;
1073	cmd->code = 0x26;
1074}
1075
1076
1077
1078
1079void cmd_0x2F (TSIPPKT *cmd)
1080/* request UTC params */
1081{
1082	cmd->len = 0;
1083	cmd->code = 0x2F;
1084}
1085
1086void cmd_0x35s  (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
1087	unsigned char time_code, unsigned char opts_code)
1088/* set serial I/O options */
1089{
1090	cmd->buf[0] = pos_code;
1091	cmd->buf[1] = vel_code;
1092	cmd->buf[2] = time_code;
1093	cmd->buf[3] = opts_code;
1094	cmd->len = 4;
1095	cmd->code = 0x35;
1096}
1097void cmd_0x3C  (TSIPPKT *cmd, unsigned char sv_prn)
1098/* request tracking status */
1099{
1100	cmd->buf[0] = sv_prn;
1101	cmd->len = 1;
1102	cmd->code = 0x3C;
1103}
1104
1105
1106void cmd_0x3Ds (TSIPPKT *cmd,
1107	unsigned char baud_out, unsigned char baud_inp,
1108   unsigned char char_code, unsigned char stopbitcode,
1109   unsigned char output_mode, unsigned char input_mode)
1110/* set Channel A configuration for dual-port operation */
1111{
1112	cmd->buf[0] = baud_out;		/* XMT baud rate */
1113	cmd->buf[1] = baud_inp;		/* RCV baud rate */
1114	cmd->buf[2] = char_code;	   /* parity and #bits per byte */
1115	cmd->buf[3] = stopbitcode;	/* number of stop bits code */
1116	cmd->buf[4] = output_mode;	/* Ch. A transmission mode */
1117	cmd->buf[5] = input_mode;	/* Ch. A reception mode */
1118	cmd->len = 6;
1119	cmd->code = 0x3D;
1120}
1121
1122
1123/* query primary configuration */
1124void cmd_0xBBq (TSIPPKT *cmd,
1125	unsigned char subcode)
1126{
1127
1128	cmd->len = 1;
1129	cmd->code = 0xBB;
1130	cmd->buf[0] = subcode;
1131}
1132
1133
1134/**** Superpackets ****/
1135void cmd_0x8E0Bq (TSIPPKT *cmd)
1136/* 8E-0B to query 8F-0B controls */
1137{
1138
1139	cmd->len = 1;
1140	cmd->code = 0x8E;
1141	cmd->buf[0] = 0x0B;
1142}
1143
1144
1145void cmd_0x8E41q (TSIPPKT *cmd)
1146/* 8F-41 to query board serial number */
1147{
1148
1149	cmd->len = 1;
1150	cmd->code = 0x8E;
1151	cmd->buf[0] = 0x41;
1152}
1153
1154
1155void cmd_0x8E42q (TSIPPKT *cmd)
1156/* 8F-42 to query product serial number */
1157{
1158
1159	cmd->len = 1;
1160	cmd->code = 0x8E;
1161	cmd->buf[0] = 0x42;
1162}
1163void cmd_0x8E4Aq (TSIPPKT *cmd)
1164/* 8F-4A to query PPS parameters */
1165{
1166	cmd->len = 1;
1167	cmd->code = 0x8E;
1168	cmd->buf[0] = 0x4A;
1169}
1170
1171
1172/* set i/o options */
1173void cmd_0x8E4As (TSIPPKT *cmd,
1174	unsigned char PPSOnOff,
1175	unsigned char TimeBase,
1176	unsigned char Polarity,
1177   double PPSOffset,
1178   float Uncertainty)
1179{
1180	cmd->len = 16;
1181	cmd->code = 0x8E;
1182	cmd->buf[0] = 0x4A;
1183	cmd->buf[1] = PPSOnOff;
1184	cmd->buf[2] = TimeBase;
1185	cmd->buf[3] = Polarity;
1186	bPutDouble (&PPSOffset, &cmd->buf[4]);
1187	bPutFloat (&Uncertainty, &cmd->buf[12]);
1188}
1189void cmd_0x8E4Bq (TSIPPKT *cmd)
1190/* 8F-4B query survey limit */
1191{
1192	cmd->len = 1;
1193	cmd->code = 0x8E;
1194	cmd->buf[0] = 0x4B;
1195}
1196
1197
1198/* poll for UTC superpacket */
1199void cmd_0x8EADq (TSIPPKT *cmd)
1200/* 8E-AD to query 8F-AD controls */
1201{
1202	cmd->len = 1;
1203	cmd->code = 0x8E;
1204	cmd->buf[0] = 0xAD;
1205}
1206
1207/* all outomatic packet output off */
1208void cmd_0x8E4Ds (TSIPPKT *cmd,
1209	unsigned long AutoOutputMask)
1210{
1211	cmd->len = 5;
1212	cmd->code = 0x8E;
1213	cmd->buf[0] = 0x4D;
1214	bPutULong (&AutoOutputMask, &cmd->buf[1]);
1215}
1216
1217
1218
1219
1220/* for DOS machines, reverse order of bytes as they come through the
1221 * serial port. */
1222#ifdef BYTESWAP
1223static short bGetShort (unsigned char *bp)
1224{
1225	short outval;
1226   unsigned char *optr;
1227
1228   optr = (unsigned char*)&outval + 1;
1229   *optr-- = *bp++;
1230   *optr = *bp;
1231	return outval;
1232}
1233
1234#ifdef TRIMBLE_OUTPUT_FUNC
1235static unsigned short bGetUShort (unsigned char *bp)
1236{
1237	unsigned short outval;
1238   unsigned char *optr;
1239
1240   optr = (unsigned char*)&outval + 1;
1241   *optr-- = *bp++;
1242   *optr = *bp;
1243	return outval;
1244}
1245
1246static long bGetLong (unsigned char *bp)
1247{
1248	long outval;
1249   unsigned char *optr;
1250
1251   optr = (unsigned char*)&outval + 3;
1252   *optr-- = *bp++;
1253   *optr-- = *bp++;
1254   *optr-- = *bp++;
1255   *optr = *bp;
1256	return outval;
1257}
1258
1259static unsigned long bGetULong (unsigned char *bp)
1260{
1261	unsigned long outval;
1262   unsigned char *optr;
1263
1264   optr = (unsigned char*)&outval + 3;
1265   *optr-- = *bp++;
1266   *optr-- = *bp++;
1267   *optr-- = *bp++;
1268   *optr = *bp;
1269	return outval;
1270}
1271#endif /* TRIMBLE_OUTPUT_FUNC */
1272
1273static float bGetSingle (unsigned char *bp)
1274{
1275	float outval;
1276   unsigned char *optr;
1277
1278   optr = (unsigned char*)&outval + 3;
1279   *optr-- = *bp++;
1280   *optr-- = *bp++;
1281   *optr-- = *bp++;
1282   *optr = *bp;
1283	return outval;
1284}
1285
1286static double bGetDouble (unsigned char *bp)
1287{
1288	double outval;
1289   unsigned char *optr;
1290
1291   optr = (unsigned char*)&outval + 7;
1292   *optr-- = *bp++;
1293   *optr-- = *bp++;
1294   *optr-- = *bp++;
1295   *optr-- = *bp++;
1296   *optr-- = *bp++;
1297   *optr-- = *bp++;
1298   *optr-- = *bp++;
1299   *optr = *bp;
1300	return outval;
1301}
1302
1303#else /* not BYTESWAP */
1304
1305#define bGetShort(bp) 	(*(short*)(bp))
1306#define bGetLong(bp) 	(*(long*)(bp))
1307#define bGetULong(bp) 	(*(unsigned long*)(bp))
1308#define bGetSingle(bp) 	(*(float*)(bp))
1309#define bGetDouble(bp)	(*(double*)(bp))
1310
1311#endif /* BYTESWAP */
1312/*
1313 * Byte-reversal is necessary for little-endian (Intel-based) machines.
1314 * TSIP streams are Big-endian (Motorola-based).
1315 */
1316#ifdef BYTESWAP
1317
1318void
1319bPutFloat (float *in, unsigned char *out)
1320{
1321	unsigned char *inptr;
1322
1323   inptr = (unsigned char*)in + 3;
1324   *out++ = *inptr--;
1325   *out++ = *inptr--;
1326   *out++ = *inptr--;
1327   *out = *inptr;
1328}
1329
1330static void
1331bPutULong (unsigned long *in, unsigned char *out)
1332{
1333	unsigned char *inptr;
1334
1335   inptr = (unsigned char*)in + 3;
1336   *out++ = *inptr--;
1337   *out++ = *inptr--;
1338   *out++ = *inptr--;
1339   *out = *inptr;
1340}
1341
1342static void
1343bPutDouble (double *in, unsigned char *out)
1344{
1345	unsigned char *inptr;
1346
1347   inptr = (unsigned char*)in + 7;
1348   *out++ = *inptr--;
1349   *out++ = *inptr--;
1350   *out++ = *inptr--;
1351   *out++ = *inptr--;
1352   *out++ = *inptr--;
1353   *out++ = *inptr--;
1354   *out++ = *inptr--;
1355   *out = *inptr;
1356}
1357
1358#else	/* not BYTESWAP */
1359
1360void bPutShort (short a, unsigned char *cmdbuf) {*(short*) cmdbuf = a;}
1361void bPutULong (long a, unsigned char *cmdbuf) 	{*(long*) cmdbuf = a;}
1362void bPutFloat (float a, unsigned char *cmdbuf) {*(float*) cmdbuf = a;}
1363void bPutDouble (double a, unsigned char *cmdbuf){*(double*) cmdbuf = a;}
1364
1365#endif /* BYTESWAP */
1366
1367/*
1368 * Parse primary utc time packet
1369 * and fill refclock structure
1370 * from results.
1371 *
1372 * 0 = success
1373 * -1 = errors
1374 */
1375
1376static int
1377parse0x8FAD(rpt, peer)
1378	TSIPPKT *rpt;
1379	struct peer *peer;
1380{
1381	register struct refclockproc *pp;
1382	register struct ripencc_unit *up;
1383
1384	unsigned day, month, year;	/* data derived from received timecode */
1385	unsigned hour, minute, second;
1386	unsigned char trackstat, utcflags;
1387
1388   	static char logbuf[1024];	/* logging string buffer */
1389	int i;
1390	unsigned char *buf;
1391
1392	buf = rpt->buf;
1393	pp = peer->procptr;
1394
1395	if (rpt->len != 22)
1396		return (-1);
1397
1398	if (bGetShort(&buf[1]) != 0) {
1399#ifdef DEBUG_NCC
1400		if (debug)
1401			printf("parse0x8FAD: event count != 0\n");
1402#endif /* DEBUG_NCC */
1403		return(-1);
1404	}
1405
1406
1407	if (bGetDouble(&buf[3]) != 0.0) {
1408#ifdef DEBUG_NCC
1409		if (debug)
1410			printf("parse0x8FAD: fracsecs != 0\n");
1411#endif /* DEBUG_NCC */
1412		return(-1);
1413	}
1414
1415	hour = (unsigned int) buf[11];
1416	minute = (unsigned int) buf[12];
1417	second = (unsigned int) buf[13];
1418	day =		(unsigned int) buf[14];
1419	month =		(unsigned int) buf[15];
1420	year =		bGetShort(&buf[16]);
1421	trackstat = buf[18];
1422	utcflags = buf[19];
1423
1424
1425	sprintf(logbuf, "U1 %d.%d.%d %02d:%02d:%02d %d %02x",
1426		day, month, year, hour, minute, second, trackstat, utcflags);
1427
1428#ifdef DEBUG_NCC
1429	if (debug)
1430   		puts(logbuf);
1431#endif /* DEBUG_NCC */
1432
1433	record_clock_stats(&peer->srcadr, logbuf);
1434
1435	if (!utcflags & UTCF_UTC_AVAIL)
1436		return(-1);
1437
1438	/* poll for UTC parameters once and then if UTC flag changed */
1439	up = (struct ripencc_unit *) pp->unitptr;
1440	if (utcflags != up->utcflags) {
1441		TSIPPKT spt; /* local structure for send packet */
1442		cmd_0x2F (&spt); /* request UTC params */
1443		ripencc_send(peer,spt);
1444		up->utcflags = utcflags;
1445	}
1446
1447	/*
1448	 * If we hit the leap second, we choose to skip this sample
1449	 * rather than rely on other code to be perfectly correct.
1450	 * No offense, just defense ;-).
1451	 */
1452	if (second == 60)
1453		return(-1);
1454
1455	/* now check and convert the time we received */
1456
1457	pp->year = year;
1458	if (month < 1 || month > 12 || day < 1 || day > 31)
1459		return(-1);
1460
1461	if (pp->year % 4) {
1462		if (day > day1tab[month - 1])
1463			return(-1);
1464		for (i = 0; i < month - 1; i++)
1465			day += day1tab[i];
1466	} else {
1467		if (day > day2tab[month - 1])
1468			return(-1);
1469		for (i = 0; i < month - 1; i++)
1470			day += day2tab[i];
1471	}
1472	pp->day = day;
1473	pp->hour = hour;
1474	pp->minute = minute;
1475	pp-> second = second;
1476	pp->nsec = 0;
1477
1478	if ((utcflags&UTCF_LEAP_PNDG) && up->leapdelta != 0)
1479		pp-> leap = (up->leapdelta > 0 ? LEAP_ADDSECOND : LEAP_DELSECOND);
1480	else
1481		pp-> leap = LEAP_NOWARNING;
1482
1483	return (0);
1484}
1485
1486/*
1487 * Parse comprehensive time packet
1488 *
1489 * 0 = success
1490 * -1 = errors
1491 */
1492
1493int parse0x8F0B(rpt, peer)
1494	TSIPPKT *rpt;
1495	struct peer *peer;
1496{
1497	register struct refclockproc *pp;
1498
1499	unsigned day, month, year;	/* data derived from received timecode */
1500	unsigned hour, minute, second;
1501	unsigned utcoff;
1502	unsigned char mode;
1503	double  bias, rate;
1504	float biasunc, rateunc;
1505	double lat, lon, alt;
1506	short lat_deg, lon_deg;
1507	float lat_min, lon_min;
1508	unsigned char north_south, east_west;
1509	char sv[9];
1510
1511   	static char logbuf[1024];	/* logging string buffer */
1512	unsigned char b;
1513	int i;
1514	unsigned char *buf;
1515	double tow;
1516
1517	buf = rpt->buf;
1518	pp = peer->procptr;
1519
1520	if (rpt->len != 74)
1521		return (-1);
1522
1523	if (bGetShort(&buf[1]) != 0)
1524		return(-1);;
1525
1526	tow =  bGetDouble(&buf[3]);
1527
1528	if (tow == -1.0) {
1529		return(-1);
1530	}
1531	else if ((tow >= 604800.0) || (tow < 0.0)) {
1532		return(-1);
1533	}
1534	else
1535	{
1536		if (tow < 604799.9) tow = tow + .00000001;
1537		second = (unsigned int) fmod(tow, 60.);
1538		minute =  (unsigned int) fmod(tow/60., 60.);
1539		hour = (unsigned int )fmod(tow / 3600., 24.);
1540	}
1541
1542
1543	day =		(unsigned int) buf[11];
1544	month =		(unsigned int) buf[12];
1545	year =		bGetShort(&buf[13]);
1546	mode =		buf[15];
1547	utcoff =	bGetShort(&buf[16]);
1548	bias = 		bGetDouble(&buf[18]) / GPS_C * 1e9;	/* ns */
1549	rate = 		bGetDouble(&buf[26]) / GPS_C * 1e9;	/* ppb */
1550	biasunc = 	bGetSingle(&buf[34]) / GPS_C * 1e9;	/* ns */
1551	rateunc = 	bGetSingle(&buf[38]) / GPS_C * 1e9;	/* ppb */
1552	lat = 		bGetDouble(&buf[42]) * R2D;
1553	lon = 		bGetDouble(&buf[50]) * R2D;
1554	alt = 		bGetDouble(&buf[58]);
1555
1556	if (lat < 0.0) {
1557		north_south = 'S';
1558		lat = -lat;
1559	}
1560	else {
1561		north_south = 'N';
1562	}
1563	lat_deg = (short)lat;
1564	lat_min = (lat - lat_deg) * 60.0;
1565
1566	if (lon < 0.0) {
1567		east_west = 'W';
1568		lon = -lon;
1569	}
1570	else {
1571		east_west = 'E';
1572	}
1573
1574	lon_deg = (short)lon;
1575	lon_min = (lon - lon_deg) * 60.0;
1576
1577	for (i=0; i<8; i++) {
1578		sv[i] = buf[i + 66];
1579		if (sv[i]) {
1580			TSIPPKT spt; /* local structure for sendpacket */
1581			b = (unsigned char) (sv[i]<0 ? -sv[i] : sv[i]);
1582			/* request tracking status */
1583			cmd_0x3C  (&spt, b);
1584			ripencc_send(peer,spt);
1585		}
1586	}
1587
1588
1589	sprintf(logbuf, "C1 %02d%02d%04d %02d%02d%02d %d %7.0f %.1f %.0f %.1f %d %02d%09.6f %c %02d%09.6f %c %.0f  %d %d %d %d %d %d %d %d",
1590		day, month, year, hour, minute, second, mode, bias, biasunc, rate, rateunc, utcoff,
1591		lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt,
1592		sv[0], sv[1], sv[2], sv[3], sv[4], sv[5], sv[6], sv[7]);
1593
1594#ifdef DEBUG_NCC
1595	if (debug)
1596   		puts(logbuf);
1597#endif /* DEBUG_NCC */
1598
1599	record_clock_stats(&peer->srcadr, logbuf);
1600
1601	return (0);
1602}
1603
1604#ifdef TRIMBLE_OUTPUT_FUNC
1605/*
1606 * Parse any packet using Trimble machinery
1607 */
1608int parseany(rpt, peer)
1609	TSIPPKT *rpt;
1610	struct peer *peer;
1611{
1612   	static char logbuf[1024];	/* logging string buffer */
1613
1614   	TranslateTSIPReportToText (rpt, logbuf);	/* anything else */
1615#ifdef DEBUG_NCC
1616	if (debug)
1617   		puts(&logbuf[1]);
1618#endif /* DEBUG_NCC */
1619	record_clock_stats(&peer->srcadr, &logbuf[1]);
1620	return(0);
1621}
1622#endif /* TRIMBLE_OUTPUT_FUNC */
1623
1624
1625/*
1626 * Parse UTC Parameter Packet
1627 *
1628 * See the IDE for documentation!
1629 *
1630 * 0 = success
1631 * -1 = errors
1632 */
1633
1634int parse0x4F(rpt, peer)
1635	TSIPPKT *rpt;
1636	struct peer *peer;
1637{
1638	register struct ripencc_unit *up;
1639
1640	double a0;
1641	float a1, tot;
1642	int dt_ls, wn_t, wn_lsf, dn, dt_lsf;
1643
1644   	static char logbuf[1024];	/* logging string buffer */
1645	unsigned char *buf;
1646
1647	buf = rpt->buf;
1648
1649	if (rpt->len != 26)
1650		return (-1);
1651	a0 = bGetDouble (buf);
1652	a1 = bGetSingle (&buf[8]);
1653	dt_ls = bGetShort (&buf[12]);
1654	tot = bGetSingle (&buf[14]);
1655	wn_t = bGetShort (&buf[18]);
1656	wn_lsf = bGetShort (&buf[20]);
1657	dn = bGetShort (&buf[22]);
1658	dt_lsf = bGetShort (&buf[24]);
1659
1660	sprintf(logbuf, "L1 %d %d %d %g %g %g %d %d %d",
1661		dt_lsf - dt_ls, dt_ls, dt_lsf, a0, a1, tot, wn_t, wn_lsf, dn);
1662
1663#ifdef DEBUG_NCC
1664	if (debug)
1665   		puts(logbuf);
1666#endif /* DEBUG_NCC */
1667
1668	record_clock_stats(&peer->srcadr, logbuf);
1669
1670	up = (struct ripencc_unit *) peer->procptr->unitptr;
1671	up->leapdelta = dt_lsf - dt_ls;
1672
1673	return (0);
1674}
1675
1676/*
1677 * Parse Tracking Status packet
1678 *
1679 * 0 = success
1680 * -1 = errors
1681 */
1682
1683int parse0x5C(rpt, peer)
1684	TSIPPKT *rpt;
1685	struct peer *peer;
1686{
1687	unsigned char prn, channel, aqflag, ephstat;
1688	float snr, azinuth, elevation;
1689
1690   	static char logbuf[1024];	/* logging string buffer */
1691	unsigned char *buf;
1692
1693	buf = rpt->buf;
1694
1695	if (rpt->len != 24)
1696		return(-1);
1697
1698	prn = buf[0];
1699	channel = (unsigned char)(buf[1] >> 3);
1700	if (channel == 0x10)
1701		channel = 2;
1702	else
1703		channel++;
1704	aqflag = buf[2];
1705	ephstat = buf[3];
1706	snr = bGetSingle(&buf[4]);
1707	elevation = bGetSingle(&buf[12]) * R2D;
1708	azinuth = bGetSingle(&buf[16]) * R2D;
1709
1710	sprintf(logbuf, "S1 %02d %d %d %02x %4.1f %5.1f %4.1f",
1711		prn, channel, aqflag, ephstat, snr, azinuth, elevation);
1712
1713#ifdef DEBUG_NCC
1714	if (debug)
1715   		puts(logbuf);
1716#endif /* DEBUG_NCC */
1717
1718	record_clock_stats(&peer->srcadr, logbuf);
1719
1720	return (0);
1721}
1722
1723/******* Code below is from Trimble Tsipchat *************/
1724
1725/*
1726 * *************************************************************************
1727 *
1728 * Trimble Navigation, Ltd.
1729 * OEM Products Development Group
1730 * P.O. Box 3642
1731 * 645 North Mary Avenue
1732 * Sunnyvale, California 94088-3642
1733 *
1734 * Corporate Headquarter:
1735 *    Telephone:  (408) 481-8000
1736 *    Fax:        (408) 481-6005
1737 *
1738 * Technical Support Center:
1739 *    Telephone:  (800) 767-4822	(U.S. and Canada)
1740 *                (408) 481-6940    (outside U.S. and Canada)
1741 *    Fax:        (408) 481-6020
1742 *    BBS:        (408) 481-7800
1743 *    e-mail:     trimble_support@trimble.com
1744 *		ftp://ftp.trimble.com/pub/sct/embedded/bin
1745 *
1746 * *************************************************************************
1747 *
1748 * -------  BYTE-SWAPPING  -------
1749 * TSIP is big-endian (Motorola) protocol.  To use on little-endian (Intel)
1750 * systems, the bytes of all multi-byte types (shorts, floats, doubles, etc.)
1751 * must be reversed.  This is controlled by the MACRO BYTESWAP; if defined, it
1752 * assumes little-endian protocol.
1753 * --------------------------------
1754 *
1755 * T_PARSER.C and T_PARSER.H contains primitive functions that interpret
1756 * reports received from the receiver.  A second source file pair,
1757 * T_FORMAT.C and T_FORMAT.H, contin the matching TSIP command formatters.
1758 *
1759 * The module is in very portable, basic C language.  It can be used as is, or
1760 * with minimal changes if a TSIP communications application is needed separate
1761 * from TSIPCHAT. The construction of most argument lists avoid the use of
1762 * structures, but the developer is encouraged to reconstruct them using such
1763 * definitions to meet project requirements.  Declarations of T_PARSER.C
1764 * functions are included in T_PARSER.H to provide prototyping definitions.
1765 *
1766 * There are two types of functions: a serial input processing routine,
1767 *                            tsip_input_proc()
1768 * which assembles incoming bytes into a TSIPPKT structure, and the
1769 * report parsers, rpt_0x??().
1770 *
1771 * 1) The function tsip_input_proc() accumulates bytes from the receiver,
1772 * strips control bytes (DLE), and checks if the report end sequence (DLE ETX)
1773 * has been received.  rpt.status is defined as TSIP_PARSED_FULL (== 1)
1774 * if a complete packet is available.
1775 *
1776 * 2) The functions rpt_0x??() are report string interpreters patterned after
1777 * the document called "Trimble Standard Interface Protocol".  It should be
1778 * noted that if the report buffer is sent into the receiver with the wrong
1779 * length (byte count), the rpt_0x??() returns the Boolean equivalence for
1780 * TRUE.
1781 *
1782 * *************************************************************************
1783 *
1784 */
1785
1786
1787/**/
1788static void tsip_input_proc (
1789	TSIPPKT *rpt,
1790	int inbyte)
1791/* reads bytes until serial buffer is empty or a complete report
1792 * has been received; end of report is signified by DLE ETX.
1793 */
1794{
1795	unsigned char newbyte;
1796
1797	if (inbyte < 0 || inbyte > 0xFF) return;
1798
1799	newbyte = (unsigned char)(inbyte);
1800	switch (rpt->status)
1801	{
1802	case TSIP_PARSED_DLE_1:
1803		switch (newbyte)
1804		{
1805		case 0:
1806		case ETX:
1807      	/* illegal TSIP IDs */
1808         rpt->len = 0;
1809			rpt->status = TSIP_PARSED_EMPTY;
1810			break;
1811		case DLE:
1812      	/* try normal message start again */
1813			rpt->len = 0;
1814			rpt->status = TSIP_PARSED_DLE_1;
1815			break;
1816		default:
1817      	/* legal TSIP ID; start message */
1818			rpt->code = newbyte;
1819         rpt->len = 0;
1820			rpt->status = TSIP_PARSED_DATA;
1821			break;
1822		}
1823		break;
1824	case TSIP_PARSED_DATA:
1825		switch (newbyte) {
1826		case DLE:
1827      	/* expect DLE or ETX next */
1828			rpt->status = TSIP_PARSED_DLE_2;
1829			break;
1830		default:
1831      	/* normal data byte  */
1832			rpt->buf[rpt->len] = newbyte;
1833			rpt->len++;
1834         /* no change in rpt->status */
1835			break;
1836		}
1837		break;
1838	case TSIP_PARSED_DLE_2:
1839		switch (newbyte) {
1840		case DLE:
1841      	/* normal data byte */
1842			rpt->buf[rpt->len] = newbyte;
1843			rpt->len++;
1844			rpt->status = TSIP_PARSED_DATA;
1845			break;
1846		case ETX:
1847			/* end of message; return TRUE here. */
1848			rpt->status = TSIP_PARSED_FULL;
1849			break;
1850		default:
1851			/* error: treat as TSIP_PARSED_DLE_1; start new report packet */
1852			rpt->code = newbyte;
1853         rpt->len = 0;
1854			rpt->status = TSIP_PARSED_DATA;
1855		}
1856		break;
1857	case TSIP_PARSED_FULL:
1858	case TSIP_PARSED_EMPTY:
1859	default:
1860		switch (newbyte) {
1861		case DLE:
1862      	/* normal message start */
1863			rpt->len = 0;
1864			rpt->status = TSIP_PARSED_DLE_1;
1865			break;
1866		default:
1867			/* error: ignore newbyte */
1868			rpt->len = 0;
1869			rpt->status = TSIP_PARSED_EMPTY;
1870		}
1871		break;
1872	}
1873	if (rpt->len > MAX_RPTBUF) {
1874		/* error: start new report packet */
1875		rpt->status = TSIP_PARSED_EMPTY;
1876		rpt->len = 0;
1877	}
1878}
1879
1880#ifdef TRIMBLE_OUTPUT_FUNC
1881
1882/**/
1883short rpt_0x3D (TSIPPKT *rpt,
1884	unsigned char *tx_baud_index,
1885	unsigned char *rx_baud_index,
1886	unsigned char *char_format_index,
1887	unsigned char *stop_bits,
1888	unsigned char *tx_mode_index,
1889	unsigned char *rx_mode_index)
1890/* Channel A configuration for dual port operation */
1891{
1892	unsigned char *buf;
1893	buf = rpt->buf;
1894
1895	if (rpt->len != 6) return TRUE;
1896	*tx_baud_index = buf[0];
1897	*rx_baud_index = buf[1];
1898	*char_format_index = buf[2];
1899	*stop_bits = (unsigned char)((buf[3] == 0x07) ? 1 : 2);
1900	*tx_mode_index = buf[4];
1901	*rx_mode_index = buf[5];
1902	return FALSE;
1903}
1904
1905/**/
1906short rpt_0x40 (TSIPPKT *rpt,
1907	unsigned char *sv_prn,
1908	short *week_num,
1909	float *t_zc,
1910	float *eccentricity,
1911	float *t_oa,
1912	float *i_0,
1913	float *OMEGA_dot,
1914	float *sqrt_A,
1915	float *OMEGA_0,
1916	float *omega,
1917	float *M_0)
1918/* almanac data for specified satellite */
1919{
1920	unsigned char *buf;
1921	buf = rpt->buf;
1922
1923	if (rpt->len != 39) return TRUE;
1924	*sv_prn = buf[0];
1925	*t_zc = bGetSingle (&buf[1]);
1926	*week_num = bGetShort (&buf[5]);
1927	*eccentricity = bGetSingle (&buf[7]);
1928	*t_oa = bGetSingle (&buf[11]);
1929	*i_0 = bGetSingle (&buf[15]);
1930	*OMEGA_dot = bGetSingle (&buf[19]);
1931	*sqrt_A = bGetSingle (&buf[23]);
1932	*OMEGA_0 = bGetSingle (&buf[27]);
1933	*omega = bGetSingle (&buf[31]);
1934	*M_0 = bGetSingle (&buf[35]);
1935	return FALSE;
1936}
1937
1938short rpt_0x41 (TSIPPKT *rpt,
1939	float *time_of_week,
1940	float *UTC_offset,
1941	short *week_num)
1942/* GPS time */
1943{
1944	unsigned char *buf;
1945	buf = rpt->buf;
1946
1947	if (rpt->len != 10) return TRUE;
1948	*time_of_week = bGetSingle (buf);
1949	*week_num = bGetShort (&buf[4]);
1950	*UTC_offset = bGetSingle (&buf[6]);
1951	return FALSE;
1952}
1953
1954short rpt_0x42 (TSIPPKT *rpt,
1955	float pos_ECEF[3],
1956	float *time_of_fix)
1957/* position in ECEF, single precision */
1958{
1959	unsigned char *buf;
1960	buf = rpt->buf;
1961
1962	if (rpt->len != 16) return TRUE;
1963	pos_ECEF[0] = bGetSingle (buf);
1964	pos_ECEF[1]= bGetSingle (&buf[4]);
1965	pos_ECEF[2]= bGetSingle (&buf[8]);
1966	*time_of_fix = bGetSingle (&buf[12]);
1967	return FALSE;
1968}
1969
1970short rpt_0x43 (TSIPPKT *rpt,
1971	float ECEF_vel[3],
1972	float *freq_offset,
1973	float *time_of_fix)
1974/* velocity in ECEF, single precision */
1975{
1976	unsigned char *buf;
1977	buf = rpt->buf;
1978
1979	if (rpt->len != 20) return TRUE;
1980	ECEF_vel[0] = bGetSingle (buf);
1981	ECEF_vel[1] = bGetSingle (&buf[4]);
1982	ECEF_vel[2] = bGetSingle (&buf[8]);
1983	*freq_offset = bGetSingle (&buf[12]);
1984	*time_of_fix = bGetSingle (&buf[16]);
1985	return FALSE;
1986}
1987
1988short rpt_0x45 (TSIPPKT *rpt,
1989	unsigned char *major_nav_version,
1990	unsigned char *minor_nav_version,
1991	unsigned char *nav_day,
1992	unsigned char *nav_month,
1993	unsigned char *nav_year,
1994	unsigned char *major_dsp_version,
1995	unsigned char *minor_dsp_version,
1996	unsigned char *dsp_day,
1997	unsigned char *dsp_month,
1998	unsigned char *dsp_year)
1999/* software versions */
2000{
2001	unsigned char *buf;
2002	buf = rpt->buf;
2003
2004	if (rpt->len != 10) return TRUE;
2005	*major_nav_version = buf[0];
2006	*minor_nav_version = buf[1];
2007	*nav_day = buf[2];
2008	*nav_month = buf[3];
2009	*nav_year = buf[4];
2010	*major_dsp_version = buf[5];
2011	*minor_dsp_version = buf[6];
2012	*dsp_day = buf[7];
2013	*dsp_month = buf[8];
2014	*dsp_year = buf[9];
2015	return FALSE;
2016}
2017
2018short rpt_0x46 (TSIPPKT *rpt,
2019	unsigned char *status1,
2020	unsigned char *status2)
2021/* receiver health and status */
2022{
2023	unsigned char *buf;
2024	buf = rpt->buf;
2025
2026	if (rpt->len != 2) return TRUE;
2027	*status1 = buf[0];
2028	*status2 = buf[1];
2029	return FALSE;
2030}
2031
2032short rpt_0x47 (TSIPPKT *rpt,
2033	unsigned char *nsvs, unsigned char *sv_prn,
2034	float *snr)
2035/* signal levels for all satellites tracked */
2036{
2037	short isv;
2038	unsigned char *buf;
2039	buf = rpt->buf;
2040
2041	if (rpt->len != 1 + 5*buf[0]) return TRUE;
2042	*nsvs = buf[0];
2043	for (isv = 0; isv < (*nsvs); isv++) {
2044		sv_prn[isv] = buf[5*isv + 1];
2045		snr[isv] = bGetSingle (&buf[5*isv + 2]);
2046	}
2047	return FALSE;
2048}
2049
2050short rpt_0x48 (TSIPPKT *rpt,
2051	unsigned char *message)
2052/* GPS system message */
2053{
2054	unsigned char *buf;
2055	buf = rpt->buf;
2056
2057	if (rpt->len != 22) return TRUE;
2058	memcpy (message, buf, 22);
2059	message[22] = 0;
2060	return FALSE;
2061}
2062
2063short rpt_0x49 (TSIPPKT *rpt,
2064	unsigned char *sv_health)
2065/* health for all satellites from almanac health page */
2066{
2067	short i;
2068	unsigned char *buf;
2069	buf = rpt->buf;
2070
2071	if (rpt->len != 32) return TRUE;
2072	for (i = 0; i < 32; i++) sv_health [i]= buf[i];
2073	return FALSE;
2074}
2075
2076short rpt_0x4A (TSIPPKT *rpt,
2077	float *lat,
2078	float *lon,
2079	float *alt,
2080	float *clock_bias,
2081	float *time_of_fix)
2082/* position in lat-lon-alt, single precision */
2083{
2084	unsigned char *buf;
2085	buf = rpt->buf;
2086
2087	if (rpt->len != 20) return TRUE;
2088	*lat = bGetSingle (buf);
2089	*lon = bGetSingle (&buf[4]);
2090	*alt = bGetSingle (&buf[8]);
2091	*clock_bias = bGetSingle (&buf[12]);
2092	*time_of_fix = bGetSingle (&buf[16]);
2093	return FALSE;
2094}
2095
2096short rpt_0x4A_2 (TSIPPKT *rpt,
2097	float *alt, float *dummy , unsigned char *alt_flag)
2098/* reference altitude parameters */
2099{
2100	unsigned char *buf;
2101
2102	buf = rpt->buf;
2103
2104	if (rpt->len != 9) return TRUE;
2105	*alt = bGetSingle (buf);
2106	*dummy = bGetSingle (&buf[4]);
2107	*alt_flag = buf[8];
2108	return FALSE;
2109}
2110
2111short rpt_0x4B (TSIPPKT *rpt,
2112	unsigned char *machine_id,
2113	unsigned char *status3,
2114	unsigned char *status4)
2115/* machine ID code, status */
2116{
2117	unsigned char *buf;
2118	buf = rpt->buf;
2119
2120	if (rpt->len != 3) return TRUE;
2121	*machine_id = buf[0];
2122	*status3 = buf[1];
2123	*status4 = buf[2];
2124	return FALSE;
2125}
2126
2127short rpt_0x4C (TSIPPKT *rpt,
2128	unsigned char *dyn_code,
2129	float *el_mask,
2130	float *snr_mask,
2131	float *dop_mask,
2132	float *dop_switch)
2133/* operating parameters and masks */
2134{
2135	unsigned char *buf;
2136	buf = rpt->buf;
2137
2138	if (rpt->len != 17) return TRUE;
2139	*dyn_code = buf[0];
2140	*el_mask = bGetSingle (&buf[1]);
2141	*snr_mask = bGetSingle (&buf[5]);
2142	*dop_mask = bGetSingle (&buf[9]);
2143	*dop_switch = bGetSingle (&buf[13]);
2144	return FALSE;
2145}
2146
2147short rpt_0x4D (TSIPPKT *rpt,
2148	float *osc_offset)
2149/* oscillator offset */
2150{
2151	unsigned char *buf;
2152	buf = rpt->buf;
2153
2154	if (rpt->len != 4) return TRUE;
2155	*osc_offset = bGetSingle (buf);
2156	return FALSE;
2157}
2158
2159short rpt_0x4E (TSIPPKT *rpt,
2160	unsigned char *response)
2161/* yes/no response to command to set GPS time */
2162{
2163	unsigned char *buf;
2164	buf = rpt->buf;
2165
2166	if (rpt->len != 1) return TRUE;
2167	*response = buf[0];
2168	return FALSE;
2169}
2170
2171short rpt_0x4F (TSIPPKT *rpt,
2172	double *a0,
2173	float *a1,
2174	float *time_of_data,
2175	short *dt_ls,
2176	short *wn_t,
2177	short *wn_lsf,
2178	short *dn,
2179	short *dt_lsf)
2180/* UTC data */
2181{
2182	unsigned char *buf;
2183	buf = rpt->buf;
2184
2185	if (rpt->len != 26) return TRUE;
2186	*a0 = bGetDouble (buf);
2187	*a1 = bGetSingle (&buf[8]);
2188	*dt_ls = bGetShort (&buf[12]);
2189	*time_of_data = bGetSingle (&buf[14]);
2190	*wn_t = bGetShort (&buf[18]);
2191	*wn_lsf = bGetShort (&buf[20]);
2192	*dn = bGetShort (&buf[22]);
2193	*dt_lsf = bGetShort (&buf[24]);
2194	return FALSE;
2195}
2196
2197/**/
2198short rpt_0x54 (TSIPPKT *rpt,
2199	float *clock_bias,
2200   float *freq_offset,
2201   float *time_of_fix)
2202/* clock offset and frequency offset in 1-SV (0-D) mode */
2203{
2204	unsigned char *buf;
2205	buf = rpt->buf;
2206
2207	if (rpt->len != 12) return TRUE;
2208	*clock_bias = bGetSingle (buf);
2209	*freq_offset = bGetSingle (&buf[4]);
2210	*time_of_fix = bGetSingle (&buf[8]);
2211	return FALSE;
2212}
2213
2214short rpt_0x55 (TSIPPKT *rpt,
2215	unsigned char *pos_code,
2216	unsigned char *vel_code,
2217	unsigned char *time_code,
2218	unsigned char *aux_code)
2219/* I/O serial options */
2220{
2221	unsigned char *buf;
2222	buf = rpt->buf;
2223
2224	if (rpt->len != 4) return TRUE;
2225	*pos_code = buf[0];
2226	*vel_code = buf[1];
2227	*time_code = buf[2];
2228	*aux_code = buf[3];
2229	return FALSE;
2230}
2231
2232short rpt_0x56 (TSIPPKT *rpt,
2233	float vel_ENU[3], float *freq_offset, float *time_of_fix)
2234/* velocity in east-north-up coordinates */
2235{
2236	unsigned char *buf;
2237	buf = rpt->buf;
2238
2239	if (rpt->len != 20) return TRUE;
2240	/* east */
2241	vel_ENU[0] = bGetSingle (buf);
2242	/* north */
2243	vel_ENU[1] = bGetSingle (&buf[4]);
2244	/* up */
2245	vel_ENU[2] = bGetSingle (&buf[8]);
2246	*freq_offset = bGetSingle (&buf[12]);
2247	*time_of_fix = bGetSingle (&buf[16]);
2248	return FALSE;
2249}
2250
2251short rpt_0x57 (TSIPPKT *rpt,
2252	unsigned char *source_code, unsigned char *diag_code,
2253	short *week_num,
2254	float *time_of_fix)
2255/* info about last computed fix */
2256{
2257	unsigned char *buf;
2258	buf = rpt->buf;
2259
2260	if (rpt->len != 8) return TRUE;
2261	*source_code = buf[0];
2262	*diag_code = buf[1];
2263	*time_of_fix = bGetSingle (&buf[2]);
2264	*week_num = bGetShort (&buf[6]);
2265	return FALSE;
2266}
2267
2268short rpt_0x58 (TSIPPKT *rpt,
2269	unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn,
2270	unsigned char *data_length, unsigned char *data_packet)
2271/* GPS system data or acknowledgment of GPS system data load */
2272{
2273	unsigned char *buf, *buf4;
2274	short dl;
2275	ALM_INFO* alminfo;
2276	ION_INFO* ioninfo;
2277	UTC_INFO* utcinfo;
2278	NAV_INFO* navinfo;
2279
2280	buf = rpt->buf;
2281
2282	if (buf[0] == 2) {
2283		if (rpt->len < 4) return TRUE;
2284		if (rpt->len != 4+buf[3]) return TRUE;
2285	}
2286	else if (rpt->len != 3) {
2287		return TRUE;
2288	}
2289	*op_code = buf[0];
2290	*data_type = buf[1];
2291	*sv_prn = buf[2];
2292	if (*op_code == 2) {
2293		dl = buf[3];
2294		*data_length = (unsigned char)dl;
2295		buf4 = &buf[4];
2296		switch (*data_type) {
2297		case 2:
2298			/* Almanac */
2299			if (*data_length != sizeof (ALM_INFO)) return TRUE;
2300			alminfo = (ALM_INFO*)data_packet;
2301			alminfo->t_oa_raw  = buf4[0];
2302			alminfo->SV_health = buf4[1];
2303			alminfo->e         = bGetSingle(&buf4[2]);
2304			alminfo->t_oa      = bGetSingle(&buf4[6]);
2305			alminfo->i_0       = bGetSingle(&buf4[10]);
2306			alminfo->OMEGADOT  = bGetSingle(&buf4[14]);
2307			alminfo->sqrt_A    = bGetSingle(&buf4[18]);
2308			alminfo->OMEGA_0   = bGetSingle(&buf4[22]);
2309			alminfo->omega     = bGetSingle(&buf4[26]);
2310			alminfo->M_0       = bGetSingle(&buf4[30]);
2311			alminfo->a_f0      = bGetSingle(&buf4[34]);
2312			alminfo->a_f1      = bGetSingle(&buf4[38]);
2313			alminfo->Axis      = bGetSingle(&buf4[42]);
2314			alminfo->n         = bGetSingle(&buf4[46]);
2315			alminfo->OMEGA_n   = bGetSingle(&buf4[50]);
2316			alminfo->ODOT_n    = bGetSingle(&buf4[54]);
2317			alminfo->t_zc      = bGetSingle(&buf4[58]);
2318			alminfo->weeknum   = bGetShort(&buf4[62]);
2319			alminfo->wn_oa     = bGetShort(&buf4[64]);
2320			break;
2321
2322		case 3:
2323			/* Almanac health page */
2324			if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE;
2325
2326			/* this record is returned raw */
2327			memcpy (data_packet, buf4, dl);
2328			break;
2329
2330		case 4:
2331			/* Ionosphere */
2332			if (*data_length != sizeof (ION_INFO) + 8) return TRUE;
2333			ioninfo = (ION_INFO*)data_packet;
2334			ioninfo->alpha_0   = bGetSingle (&buf4[8]);
2335			ioninfo->alpha_1   = bGetSingle (&buf4[12]);
2336			ioninfo->alpha_2   = bGetSingle (&buf4[16]);
2337			ioninfo->alpha_3   = bGetSingle (&buf4[20]);
2338			ioninfo->beta_0    = bGetSingle (&buf4[24]);
2339			ioninfo->beta_1    = bGetSingle (&buf4[28]);
2340			ioninfo->beta_2    = bGetSingle (&buf4[32]);
2341			ioninfo->beta_3    = bGetSingle (&buf4[36]);
2342			break;
2343
2344		case 5:
2345			/* UTC */
2346			if (*data_length != sizeof (UTC_INFO) + 13) return TRUE;
2347			utcinfo = (UTC_INFO*)data_packet;
2348			utcinfo->A_0       = bGetDouble (&buf4[13]);
2349			utcinfo->A_1       = bGetSingle (&buf4[21]);
2350			utcinfo->delta_t_LS = bGetShort (&buf4[25]);
2351			utcinfo->t_ot      = bGetSingle(&buf4[27]);
2352			utcinfo->WN_t      = bGetShort (&buf4[31]);
2353			utcinfo->WN_LSF    = bGetShort (&buf4[33]);
2354			utcinfo->DN        = bGetShort (&buf4[35]);
2355			utcinfo->delta_t_LSF = bGetShort (&buf4[37]);
2356			break;
2357
2358		case 6:
2359			/* Ephemeris */
2360			if (*data_length != sizeof (NAV_INFO) - 1) return TRUE;
2361
2362			navinfo = (NAV_INFO*)data_packet;
2363
2364			navinfo->sv_number = buf4[0];
2365			navinfo->t_ephem = bGetSingle (&buf4[1]);
2366			navinfo->ephclk.weeknum = bGetShort (&buf4[5]);
2367
2368			navinfo->ephclk.codeL2 = buf4[7];
2369			navinfo->ephclk.L2Pdata = buf4[8];
2370			navinfo->ephclk.SVacc_raw = buf4[9];
2371			navinfo->ephclk.SV_health = buf4[10];
2372			navinfo->ephclk.IODC = bGetShort (&buf4[11]);
2373			navinfo->ephclk.T_GD = bGetSingle (&buf4[13]);
2374			navinfo->ephclk.t_oc = bGetSingle (&buf4[17]);
2375			navinfo->ephclk.a_f2 = bGetSingle (&buf4[21]);
2376			navinfo->ephclk.a_f1 = bGetSingle (&buf4[25]);
2377			navinfo->ephclk.a_f0 = bGetSingle (&buf4[29]);
2378			navinfo->ephclk.SVacc = bGetSingle (&buf4[33]);
2379
2380			navinfo->ephorb.IODE = buf4[37];
2381			navinfo->ephorb.fit_interval = buf4[38];
2382			navinfo->ephorb.C_rs = bGetSingle (&buf4[39]);
2383			navinfo->ephorb.delta_n = bGetSingle (&buf4[43]);
2384			navinfo->ephorb.M_0 = bGetDouble (&buf4[47]);
2385			navinfo->ephorb.C_uc = bGetSingle (&buf4[55]);
2386			navinfo->ephorb.e = bGetDouble (&buf4[59]);
2387			navinfo->ephorb.C_us = bGetSingle (&buf4[67]);
2388			navinfo->ephorb.sqrt_A = bGetDouble (&buf4[71]);
2389			navinfo->ephorb.t_oe = bGetSingle (&buf4[79]);
2390			navinfo->ephorb.C_ic = bGetSingle (&buf4[83]);
2391			navinfo->ephorb.OMEGA_0 = bGetDouble (&buf4[87]);
2392			navinfo->ephorb.C_is = bGetSingle (&buf4[95]);
2393			navinfo->ephorb.i_0 = bGetDouble (&buf4[99]);
2394			navinfo->ephorb.C_rc = bGetSingle (&buf4[107]);
2395			navinfo->ephorb.omega = bGetDouble (&buf4[111]);
2396			navinfo->ephorb.OMEGADOT=bGetSingle (&buf4[119]);
2397			navinfo->ephorb.IDOT = bGetSingle (&buf4[123]);
2398			navinfo->ephorb.Axis = bGetDouble (&buf4[127]);
2399			navinfo->ephorb.n = bGetDouble (&buf4[135]);
2400			navinfo->ephorb.r1me2 = bGetDouble (&buf4[143]);
2401			navinfo->ephorb.OMEGA_n=bGetDouble (&buf4[151]);
2402			navinfo->ephorb.ODOT_n = bGetDouble (&buf4[159]);
2403			break;
2404		}
2405	}
2406	return FALSE;
2407}
2408
2409short rpt_0x59 (TSIPPKT *rpt,
2410	unsigned char *code_type,
2411	unsigned char status_code[32])
2412/* satellite enable/disable or health heed/ignore list */
2413{
2414	short iprn;
2415	unsigned char *buf;
2416	buf = rpt->buf;
2417
2418	if (rpt->len != 33) return TRUE;
2419	*code_type = buf[0];
2420	for (iprn = 0; iprn < 32; iprn++)
2421		status_code[iprn] = buf[iprn + 1];
2422	return FALSE;
2423}
2424
2425short rpt_0x5A (TSIPPKT *rpt,
2426	unsigned char *sv_prn,
2427	float *sample_length,
2428	float *signal_level,
2429	float *code_phase,
2430	float *Doppler,
2431	double *time_of_fix)
2432/* raw measurement data - code phase/Doppler */
2433{
2434	unsigned char *buf;
2435	buf = rpt->buf;
2436
2437	if (rpt->len != 25) return TRUE;
2438	*sv_prn = buf[0];
2439	*sample_length = bGetSingle (&buf[1]);
2440	*signal_level = bGetSingle (&buf[5]);
2441	*code_phase = bGetSingle (&buf[9]);
2442	*Doppler = bGetSingle (&buf[13]);
2443	*time_of_fix = bGetDouble (&buf[17]);
2444	return FALSE;
2445}
2446
2447short rpt_0x5B (TSIPPKT *rpt,
2448	unsigned char *sv_prn,
2449	unsigned char *sv_health,
2450	unsigned char *sv_iode,
2451	unsigned char *fit_interval_flag,
2452	float *time_of_collection,
2453	float *time_of_eph,
2454	float *sv_accy)
2455/* satellite ephorb status */
2456{
2457	unsigned char *buf;
2458	buf = rpt->buf;
2459
2460	if (rpt->len != 16) return TRUE;
2461	*sv_prn = buf[0];
2462	*time_of_collection = bGetSingle (&buf[1]);
2463	*sv_health = buf[5];
2464	*sv_iode = buf[6];
2465	*time_of_eph = bGetSingle (&buf[7]);
2466	*fit_interval_flag = buf[11];
2467	*sv_accy = bGetSingle (&buf[12]);
2468	return FALSE;
2469}
2470
2471short rpt_0x5C (TSIPPKT *rpt,
2472	unsigned char *sv_prn,
2473	unsigned char *slot,
2474	unsigned char *chan,
2475	unsigned char *acq_flag,
2476	unsigned char *eph_flag,
2477	float *signal_level,
2478	float *time_of_last_msmt,
2479	float *elev,
2480	float *azim,
2481	unsigned char *old_msmt_flag,
2482	unsigned char *integer_msec_flag,
2483	unsigned char *bad_data_flag,
2484	unsigned char *data_collect_flag)
2485/* satellite tracking status */
2486{
2487	unsigned char *buf;
2488	buf = rpt->buf;
2489
2490	if (rpt->len != 24) return TRUE;
2491	*sv_prn = buf[0];
2492	*slot = (unsigned char)((buf[1] & 0x07) + 1);
2493	*chan = (unsigned char)(buf[1] >> 3);
2494	if (*chan == 0x10) *chan = 2;
2495	else (*chan)++;
2496	*acq_flag = buf[2];
2497	*eph_flag = buf[3];
2498	*signal_level = bGetSingle (&buf[4]);
2499	*time_of_last_msmt = bGetSingle (&buf[8]);
2500	*elev = bGetSingle (&buf[12]);
2501	*azim = bGetSingle (&buf[16]);
2502	*old_msmt_flag = buf[20];
2503	*integer_msec_flag = buf[21];
2504	*bad_data_flag = buf[22];
2505	*data_collect_flag = buf[23];
2506	return FALSE;
2507}
2508
2509/**/
2510short rpt_0x6D (TSIPPKT *rpt,
2511	unsigned char *manual_mode,
2512	unsigned char *nsvs,
2513	unsigned char *ndim,
2514	unsigned char sv_prn[],
2515	float *pdop,
2516	float *hdop,
2517	float *vdop,
2518	float *tdop)
2519/* over-determined satellite selection for position fixes, PDOP, fix mode */
2520{
2521	short islot;
2522	unsigned char *buf;
2523	buf = rpt->buf;
2524
2525	*nsvs = (unsigned char)((buf[0] & 0xF0) >> 4);
2526	if ((*nsvs)>8) return TRUE;
2527	if (rpt->len != 17 + (*nsvs) ) return TRUE;
2528
2529	*manual_mode = (unsigned char)(buf[0] & 0x08);
2530	*ndim  = (unsigned char)((buf[0] & 0x07));
2531	*pdop = bGetSingle (&buf[1]);
2532	*hdop = bGetSingle (&buf[5]);
2533	*vdop = bGetSingle (&buf[9]);
2534	*tdop = bGetSingle (&buf[13]);
2535	for (islot = 0; islot < (*nsvs); islot++)
2536		sv_prn[islot] = buf[islot + 17];
2537	return FALSE;
2538}
2539
2540/**/
2541short rpt_0x82 (TSIPPKT *rpt,
2542	unsigned char *diff_mode)
2543/* differential fix mode */
2544{
2545	unsigned char *buf;
2546	buf = rpt->buf;
2547
2548	if (rpt->len != 1) return TRUE;
2549	*diff_mode = buf[0];
2550	return FALSE;
2551}
2552
2553short rpt_0x83 (TSIPPKT *rpt,
2554	double ECEF_pos[3],
2555	double *clock_bias,
2556	float *time_of_fix)
2557/* position, ECEF double precision */
2558{
2559	unsigned char *buf;
2560	buf = rpt->buf;
2561
2562	if (rpt->len != 36) return TRUE;
2563	ECEF_pos[0] = bGetDouble (buf);
2564	ECEF_pos[1] = bGetDouble (&buf[8]);
2565	ECEF_pos[2] = bGetDouble (&buf[16]);
2566	*clock_bias  = bGetDouble (&buf[24]);
2567	*time_of_fix = bGetSingle (&buf[32]);
2568	return FALSE;
2569}
2570
2571short rpt_0x84 (TSIPPKT *rpt,
2572	double *lat,
2573	double *lon,
2574	double *alt,
2575	double *clock_bias,
2576	float *time_of_fix)
2577/* position, lat-lon-alt double precision */
2578{
2579	unsigned char *buf;
2580	buf = rpt->buf;
2581
2582	if (rpt->len != 36) return TRUE;
2583	*lat = bGetDouble (buf);
2584	*lon = bGetDouble (&buf[8]);
2585	*alt = bGetDouble (&buf[16]);
2586	*clock_bias = bGetDouble (&buf[24]);
2587	*time_of_fix = bGetSingle (&buf[32]);
2588	return FALSE;
2589}
2590
2591short rpt_Paly0xBB(TSIPPKT *rpt,
2592	TSIP_RCVR_CFG *TsipxBB)
2593{
2594
2595	unsigned char *buf;
2596	buf = rpt->buf;
2597
2598	/* Palisade is inconsistent with other TSIP, which has a kength of 40 */
2599	/* if (rpt->len != 40) return TRUE; */
2600	if (rpt->len != 43) return TRUE;
2601
2602	TsipxBB->bSubcode		=	buf[0];
2603	TsipxBB->operating_mode	=	buf[1]	;
2604	TsipxBB->dyn_code			=	buf[3]	;
2605	TsipxBB->elev_mask		=  bGetSingle (&buf[5]);
2606	TsipxBB->cno_mask			=	bGetSingle (&buf[9]);
2607	TsipxBB->dop_mask 		=  bGetSingle (&buf[13]);
2608	TsipxBB->dop_switch 	=  bGetSingle (&buf[17]);
2609	return FALSE;
2610}
2611
2612short rpt_0xBC (TSIPPKT *rpt,
2613	unsigned char *port_num,
2614   unsigned char *in_baud,
2615	unsigned char *out_baud,
2616   unsigned char *data_bits,
2617	unsigned char *parity,
2618   unsigned char *stop_bits,
2619   unsigned char *flow_control,
2620	unsigned char *protocols_in,
2621   unsigned char *protocols_out,
2622   unsigned char *reserved)
2623/* Receiver serial port configuration */
2624{
2625	unsigned char *buf;
2626	buf = rpt->buf;
2627
2628	if (rpt->len != 10) return TRUE;
2629	*port_num = buf[0];
2630	*in_baud = buf[1];
2631	*out_baud = buf[2];
2632	*data_bits = buf[3];
2633	*parity = buf[4];
2634	*stop_bits = buf[5];
2635	*flow_control = buf[6];
2636	*protocols_in = buf[7];
2637	*protocols_out = buf[8];
2638	*reserved = buf[9];
2639
2640	return FALSE;
2641}
2642
2643/**** Superpackets ****/
2644
2645short rpt_0x8F0B(TSIPPKT *rpt,
2646                 unsigned short *event,
2647                 double *tow,
2648                 unsigned char *date,
2649                 unsigned char *month,
2650                 short *year,
2651                 unsigned char *dim_mode,
2652                 short *utc_offset,
2653                 double *bias,
2654                 double *drift,
2655                 float *bias_unc,
2656                 float *dr_unc,
2657                 double *lat,
2658                 double *lon,
2659                 double *alt,
2660                 char sv_id[8])
2661{
2662       short local_index;
2663       unsigned char *buf;
2664
2665	buf = rpt->buf;
2666       if (rpt->len != 74) return TRUE;
2667       *event = bGetShort(&buf[1]);
2668       *tow = bGetDouble(&buf[3]);
2669       *date = buf[11];
2670       *month = buf[12];
2671       *year = bGetShort(&buf[13]);
2672       *dim_mode = buf[15];
2673       *utc_offset = bGetShort(&buf[16]);
2674       *bias = bGetDouble(&buf[18]);
2675       *drift = bGetDouble(&buf[26]);
2676       *bias_unc = bGetSingle(&buf[34]);
2677       *dr_unc = bGetSingle(&buf[38]);
2678       *lat = bGetDouble(&buf[42]);
2679       *lon = bGetDouble(&buf[50]);
2680       *alt = bGetDouble(&buf[58]);
2681
2682       for (local_index=0; local_index<8; local_index++) sv_id[local_index] = buf[local_index + 66];
2683       return FALSE;
2684}
2685
2686short rpt_0x8F14 (TSIPPKT *rpt,
2687	short *datum_idx,
2688	double datum_coeffs[5])
2689/*  datum index and coefficients  */
2690{
2691	unsigned char *buf;
2692	buf = rpt->buf;
2693
2694	if (rpt->len != 43) return TRUE;
2695	*datum_idx = bGetShort(&buf[1]);
2696	datum_coeffs[0] = bGetDouble (&buf[3]);
2697	datum_coeffs[1] = bGetDouble (&buf[11]);
2698	datum_coeffs[2] = bGetDouble (&buf[19]);
2699	datum_coeffs[3] = bGetDouble (&buf[27]);
2700	datum_coeffs[4] = bGetDouble (&buf[35]);
2701	return FALSE;
2702}
2703
2704
2705short rpt_0x8F15 (TSIPPKT *rpt,
2706	short *datum_idx,
2707	double datum_coeffs[5])
2708/*  datum index and coefficients  */
2709{
2710	unsigned char *buf;
2711	buf = rpt->buf;
2712
2713	if (rpt->len != 43) return TRUE;
2714	*datum_idx = bGetShort(&buf[1]);
2715	datum_coeffs[0] = bGetDouble (&buf[3]);
2716	datum_coeffs[1] = bGetDouble (&buf[11]);
2717	datum_coeffs[2] = bGetDouble (&buf[19]);
2718	datum_coeffs[3] = bGetDouble (&buf[27]);
2719	datum_coeffs[4] = bGetDouble (&buf[35]);
2720	return FALSE;
2721}
2722
2723
2724#define MAX_LONG  (2147483648.)   /* 2**31 */
2725
2726short rpt_0x8F20 (TSIPPKT *rpt,
2727	unsigned char *info,
2728	double *lat,
2729	double *lon,
2730	double *alt,
2731	double vel_enu[],
2732	double *time_of_fix,
2733	short *week_num,
2734	unsigned char *nsvs,
2735	unsigned char sv_prn[],
2736	short sv_IODC[],
2737	short *datum_index)
2738{
2739	short
2740		isv;
2741	unsigned char
2742		*buf, prnx, iode;
2743	unsigned long
2744		ulongtemp;
2745	long
2746		longtemp;
2747	double
2748		vel_scale;
2749
2750	buf = rpt->buf;
2751
2752	if (rpt->len != 56) return TRUE;
2753
2754	vel_scale = (buf[24]&1)? 0.020 : 0.005;
2755	vel_enu[0] = bGetShort (buf+2)*vel_scale;
2756	vel_enu[1] = bGetShort (buf+4)*vel_scale;
2757	vel_enu[2] = bGetShort (buf+6)*vel_scale;
2758
2759	*time_of_fix = bGetULong (buf+8)*.001;
2760
2761	longtemp = bGetLong (buf+12);
2762	*lat = longtemp*(GPS_PI/MAX_LONG);
2763
2764	ulongtemp = bGetULong (buf+16);
2765	*lon = ulongtemp*(GPS_PI/MAX_LONG);
2766	if (*lon > GPS_PI) *lon -= 2.0*GPS_PI;
2767
2768	*alt = bGetLong (buf+20)*.001;
2769	/* 25 blank; 29 = UTC */
2770	(*datum_index) = (short)((short)buf[26]-1);
2771	*info = buf[27];
2772	*nsvs = buf[28];
2773	*week_num = bGetShort (&buf[30]);
2774	for (isv = 0; isv < 8; isv++) {
2775		prnx = buf[32+2*isv];
2776		sv_prn[isv] = (unsigned char)(prnx&0x3F);
2777      iode = buf[33+2*isv];
2778		sv_IODC[isv] = (short)(iode | ((prnx>>6)<<8));
2779	}
2780	return FALSE;
2781}
2782
2783short rpt_0x8F41 (TSIPPKT *rpt,
2784	unsigned char *bSearchRange,
2785	unsigned char *bBoardOptions,
2786	unsigned long *iiSerialNumber,
2787	unsigned char *bBuildYear,
2788	unsigned char *bBuildMonth,
2789	unsigned char *bBuildDay,
2790	unsigned char *bBuildHour,
2791	float *fOscOffset,
2792	unsigned short *iTestCodeId)
2793{
2794	if(rpt->len != 17) return FALSE;
2795	*bSearchRange = rpt->buf[1];
2796	*bBoardOptions = rpt->buf[2];
2797	*iiSerialNumber = bGetLong(&rpt->buf[3]);
2798	*bBuildYear = rpt->buf[7];
2799	*bBuildMonth = rpt->buf[8];
2800	*bBuildDay = rpt->buf[9];
2801	*bBuildHour =	rpt->buf[10];
2802	*fOscOffset = bGetSingle(&rpt->buf[11]);
2803	*iTestCodeId = bGetShort(&rpt->buf[15]);
2804/*	Tsipx8E41Data = *Tsipx8E41; */
2805	return TRUE;
2806}
2807
2808short rpt_0x8F42 (TSIPPKT *rpt,
2809	unsigned char *bProdOptionsPre,
2810	unsigned char *bProdNumberExt,
2811	unsigned short *iCaseSerialNumberPre,
2812	unsigned long *iiCaseSerialNumber,
2813	unsigned long *iiProdNumber,
2814	unsigned short *iPremiumOptions,
2815	unsigned short *iMachineID,
2816	unsigned short *iKey)
2817{
2818	if(rpt->len != 19) return FALSE;
2819	*bProdOptionsPre = rpt->buf[1];
2820	*bProdNumberExt = rpt->buf[2];
2821	*iCaseSerialNumberPre = bGetShort(&rpt->buf[3]);
2822	*iiCaseSerialNumber = bGetLong(&rpt->buf[5]);
2823	*iiProdNumber = bGetLong(&rpt->buf[9]);
2824	*iPremiumOptions = bGetShort(&rpt->buf[13]);
2825	*iMachineID = bGetShort(&rpt->buf[15]);
2826	*iKey = bGetShort(&rpt->buf[17]);
2827	return TRUE;
2828}
2829
2830short rpt_0x8F45(TSIPPKT *rpt,
2831   unsigned char *bSegMask)
2832{
2833	if(rpt->len != 2) return FALSE;
2834	*bSegMask = rpt->buf[1];
2835	return TRUE;
2836}
2837
2838short rpt_0x8F4A_16(TSIPPKT *rpt,
2839	unsigned char *pps_enabled,
2840   unsigned char *pps_timebase,
2841   unsigned char *pos_polarity,
2842   double *pps_offset,
2843   float *bias_unc_threshold)
2844/* Stinger PPS definition */
2845{
2846	unsigned char
2847   	*buf;
2848
2849   buf = rpt->buf;
2850   if (rpt->len != 16) return TRUE;
2851   *pps_enabled = buf[1];
2852   *pps_timebase = buf[2];
2853   *pos_polarity = buf[3];
2854   *pps_offset = bGetDouble(&buf[4]);
2855   *bias_unc_threshold = bGetSingle(&buf[12]);
2856	return FALSE;
2857}
2858
2859short rpt_0x8F4B(TSIPPKT *rpt,
2860                 unsigned long *decorr_max)
2861{
2862	unsigned char
2863   	*buf;
2864
2865   buf = rpt->buf;
2866   if (rpt->len != 5) return TRUE;
2867   *decorr_max = bGetLong(&buf[1]);
2868   return FALSE;
2869}
2870
2871short rpt_0x8F4D(TSIPPKT *rpt,
2872	unsigned long *event_mask)
2873{
2874	unsigned char
2875   	*buf;
2876
2877   buf = rpt->buf;
2878   if (rpt->len != 5) return TRUE;
2879   *event_mask = bGetULong (&buf[1]);
2880   return FALSE;
2881}
2882
2883short rpt_0x8FA5(TSIPPKT *rpt,
2884	unsigned char *spktmask)
2885{
2886	unsigned char
2887   	*buf;
2888
2889   buf = rpt->buf;
2890   if (rpt->len != 5) return TRUE;
2891   spktmask[0] = buf[1];
2892   spktmask[1] = buf[2];
2893   spktmask[2] = buf[3];
2894   spktmask[3] = buf[4];
2895   return FALSE;
2896}
2897
2898short rpt_0x8FAD (TSIPPKT *rpt,
2899    unsigned short *COUNT,
2900    double *FracSec,
2901    unsigned char *Hour,
2902    unsigned char *Minute,
2903    unsigned char *Second,
2904    unsigned char *Day,
2905    unsigned char *Month,
2906    unsigned short *Year,
2907    unsigned char *Status,
2908    unsigned char *Flags)
2909{
2910
2911	if (rpt->len != 22) return TRUE;
2912
2913    *COUNT = bGetUShort(&rpt->buf[1]);
2914    *FracSec = bGetDouble(&rpt->buf[3]);
2915    *Hour = rpt->buf[11];
2916    *Minute = rpt->buf[12];
2917    *Second = rpt->buf[13];
2918    *Day = rpt->buf[14];
2919    *Month = rpt->buf[15];
2920    *Year = bGetUShort(&rpt->buf[16]);
2921    *Status = rpt->buf[18];
2922    *Flags = rpt->buf[19];
2923    return FALSE;
2924}
2925
2926
2927/*
2928 * *************************************************************************
2929 *
2930 * Trimble Navigation, Ltd.
2931 * OEM Products Development Group
2932 * P.O. Box 3642
2933 * 645 North Mary Avenue
2934 * Sunnyvale, California 94088-3642
2935 *
2936 * Corporate Headquarter:
2937 *    Telephone:  (408) 481-8000
2938 *    Fax:        (408) 481-6005
2939 *
2940 * Technical Support Center:
2941 *    Telephone:  (800) 767-4822	(U.S. and Canada)
2942 *                (408) 481-6940    (outside U.S. and Canada)
2943 *    Fax:        (408) 481-6020
2944 *    BBS:        (408) 481-7800
2945 *    e-mail:     trimble_support@trimble.com
2946 *		ftp://ftp.trimble.com/pub/sct/embedded/bin
2947 *
2948 * *************************************************************************
2949 *
2950 * T_REPORT.C consists of a primary function TranslateTSIPReportToText()
2951 * called by main().
2952 *
2953 * This function takes a character buffer that has been received as a report
2954 * from a TSIP device and interprets it.  The character buffer has been
2955 * assembled using tsip_input_proc() in T_PARSER.C.
2956 *
2957 * A large case statement directs processing to one of many mid-level
2958 * functions.  The mid-level functions specific to the current report
2959 * code passes the report buffer to the appropriate report decoder
2960 * rpt_0x?? () in T_PARSER.C, which converts the byte stream in rpt.buf
2961 * to data values approporaite for use.
2962 *
2963 * *************************************************************************
2964 *
2965 */
2966
2967
2968#define GOOD_PARSE 0
2969#define BADID_PARSE 1
2970#define BADLEN_PARSE 2
2971#define BADDATA_PARSE 3
2972
2973#define B_TSIP	0x02
2974#define B_NMEA	0x04
2975
2976
2977/* pbuf is the pointer to the current location of the text output */
2978static char
2979	*pbuf;
2980
2981/* keep track of whether the message has been successfully parsed */
2982static short
2983	parsed;
2984
2985
2986/* convert time of week into day-hour-minute-second and print */
2987char* show_time (float time_of_week)
2988{
2989	short	days, hours, minutes;
2990	float seconds;
2991	double tow = 0;
2992   static char timestring [80];
2993
2994	if (time_of_week == -1.0)
2995   {
2996		sprintf(timestring, "   <No time yet>   ");
2997	}
2998	else if ((time_of_week >= 604800.0) || (time_of_week < 0.0))
2999   {
3000		sprintf(timestring, "     <Bad time>     ");
3001	}
3002   else
3003   {
3004		if (time_of_week < 604799.9)
3005			tow = time_of_week + .00000001;
3006		seconds = (float)fmod(tow, 60.);
3007		minutes =  (short) fmod(tow/60., 60.);
3008		hours = (short)fmod(tow / 3600., 24.);
3009		days = (short)(tow / 86400.0);
3010		sprintf(timestring, " %s %02d:%02d:%05.2f   ",
3011  	   	dayname[days], hours, minutes, seconds);
3012   }
3013   return timestring;
3014}
3015
3016/**/
3017/* 0x3D */
3018static void rpt_chan_A_config (TSIPPKT *rpt)
3019{
3020	unsigned char
3021		tx_baud_index, rx_baud_index,
3022		char_format_index, stop_bits,
3023      tx_mode_index, rx_mode_index,
3024      databits, parity;
3025	int
3026		i, nbaud;
3027
3028	/* unload rptbuf */
3029	if (rpt_0x3D (rpt,
3030		&tx_baud_index, &rx_baud_index, &char_format_index,
3031		&stop_bits, &tx_mode_index, &rx_mode_index)) {
3032		parsed = BADLEN_PARSE;
3033		return;
3034	}
3035
3036	pbuf += sprintf(pbuf, "\nChannel A Configuration");
3037
3038   nbaud = sizeof(old_baudnum);
3039
3040	for (i = 0; i < nbaud; ++i) if (tx_baud_index == old_baudnum[i]) break;
3041	pbuf += sprintf(pbuf, "\n   Transmit speed: %s at %s",
3042		old_output_ch[tx_mode_index], st_baud_text_app[i]);
3043
3044	for (i = 0; i < nbaud; ++i) if (rx_baud_index == old_baudnum[i]) break;
3045	pbuf += sprintf(pbuf, "\n   Receive speed: %s at %s",
3046		old_input_ch[rx_mode_index], st_baud_text_app[i]);
3047
3048	databits = (unsigned char)((char_format_index & 0x03) + 5);
3049
3050	parity = (unsigned char)(char_format_index >> 2);
3051	if (parity > 4) parity = 2;
3052
3053	pbuf += sprintf(pbuf, "\n   Character format (bits/char, parity, stop bits): %d-%s-%d",
3054		databits, old_parity_text[parity], stop_bits);
3055}
3056
3057/**/
3058/* 0x40 */
3059static void rpt_almanac_data_page (TSIPPKT *rpt)
3060{
3061	unsigned char
3062		sv_prn;
3063	short
3064		week_num;
3065	float
3066		t_zc,
3067		eccentricity,
3068		t_oa,
3069		i_0,
3070		OMEGA_dot,
3071		sqrt_A,
3072		OMEGA_0,
3073		omega,
3074		M_0;
3075
3076	/* unload rptbuf */
3077	if (rpt_0x40 (rpt,
3078		&sv_prn, &week_num, &t_zc, &eccentricity, &t_oa,
3079		&i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) {
3080		parsed = BADLEN_PARSE;
3081		return;
3082	}
3083
3084	pbuf += sprintf(pbuf, "\nAlmanac for SV %02d", sv_prn);
3085	pbuf += sprintf(pbuf, "\n       Captured:%15.0f %s",
3086   	t_zc, show_time (t_zc));
3087	pbuf += sprintf(pbuf, "\n           week:%15d", week_num);
3088	pbuf += sprintf(pbuf, "\n   Eccentricity:%15g", eccentricity);
3089	pbuf += sprintf(pbuf, "\n           T_oa:%15.0f %s",
3090   	t_oa, show_time (t_oa));
3091	pbuf += sprintf(pbuf, "\n            i 0:%15g", i_0);
3092	pbuf += sprintf(pbuf, "\n      OMEGA dot:%15g", OMEGA_dot);
3093	pbuf += sprintf(pbuf, "\n         sqrt A:%15g", sqrt_A);
3094	pbuf += sprintf(pbuf, "\n        OMEGA 0:%15g", OMEGA_0);
3095	pbuf += sprintf(pbuf, "\n          omega:%15g", omega);
3096	pbuf += sprintf(pbuf, "\n            M 0:%15g", M_0);
3097}
3098
3099/* 0x41 */
3100static void rpt_GPS_time (TSIPPKT *rpt)
3101{
3102	float
3103		time_of_week, UTC_offset;
3104	short
3105		week_num;
3106
3107	/* unload rptbuf */
3108	if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) {
3109		parsed = BADLEN_PARSE;
3110		return;
3111	}
3112
3113	pbuf += sprintf(pbuf, "\nGPS time:%s GPS week: %d   UTC offset %.1f",
3114   	show_time(time_of_week), week_num, UTC_offset);
3115
3116}
3117
3118/* 0x42 */
3119static void rpt_single_ECEF_position (TSIPPKT *rpt)
3120{
3121	float
3122		ECEF_pos[3], time_of_fix;
3123
3124	/* unload rptbuf */
3125	if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) {
3126		parsed = BADLEN_PARSE;
3127		return;
3128	}
3129
3130	pbuf += sprintf(pbuf, "\nSXYZ:  %15.0f  %15.0f  %15.0f    %s",
3131		ECEF_pos[0], ECEF_pos[1], ECEF_pos[2],
3132      show_time(time_of_fix));
3133}
3134
3135/* 0x43 */
3136static void rpt_single_ECEF_velocity (TSIPPKT *rpt)
3137{
3138
3139	float
3140		ECEF_vel[3], freq_offset, time_of_fix;
3141
3142	/* unload rptbuf */
3143	if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) {
3144		parsed = BADLEN_PARSE;
3145		return;
3146	}
3147
3148	pbuf += sprintf(pbuf, "\nVelECEF: %11.3f  %11.3f  %11.3f  %12.3f%s",
3149		ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset,
3150      show_time(time_of_fix));
3151}
3152
3153/*  0x45  */
3154static void rpt_SW_version (TSIPPKT *rpt) {
3155	unsigned char
3156		major_nav_version, minor_nav_version,
3157		nav_day, nav_month, nav_year,
3158		major_dsp_version, minor_dsp_version,
3159		dsp_day, dsp_month, dsp_year;
3160
3161	/* unload rptbuf */
3162	if (rpt_0x45 (rpt,
3163		&major_nav_version, &minor_nav_version,
3164		&nav_day, &nav_month, &nav_year,
3165		&major_dsp_version, &minor_dsp_version,
3166		&dsp_day, &dsp_month, &dsp_year)) {
3167		parsed = BADLEN_PARSE;
3168		return;
3169	}
3170
3171	pbuf += sprintf(pbuf,
3172"\nFW Versions:  Nav Proc %2d.%02d  %2d/%2d/%2d  Sig Proc %2d.%02d  %2d/%2d/%2d",
3173		major_nav_version, minor_nav_version, nav_day, nav_month, nav_year,
3174		major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year);
3175}
3176
3177/* 0x46 */
3178static void rpt_rcvr_health (TSIPPKT *rpt)
3179{
3180	unsigned char
3181		status1, status2;
3182	static char
3183		*sc_text[] = {
3184			"Doing position fixes",
3185			"Don't have GPS time yet",
3186			"Waiting for almanac collection",
3187			"DOP too high          ",
3188			"No satellites available",
3189			"Only 1 satellite available",
3190			"Only 2 satellites available",
3191			"Only 3 satellites available",
3192			"No satellites usable   ",
3193			"Only 1 satellite usable",
3194			"Only 2 satellites usable",
3195			"Only 3 satellites usable",
3196			"Chosen satellite unusable"};
3197
3198
3199	/* unload rptbuf */
3200	if (rpt_0x46 (rpt, &status1, &status2))
3201	{
3202		parsed = BADLEN_PARSE;
3203		return;
3204	}
3205
3206	pbuf += sprintf(pbuf, "\nRcvr status1: %s (%02Xh); ",
3207     	sc_text[rpt->buf[0]], status1);
3208
3209	pbuf += sprintf(pbuf, "status2: %s, %s (%02Xh)",
3210		(status2 & 0x01)?"No BBRAM":"BBRAM OK",
3211		(status2 & 0x10)?"No Ant":"Ant OK",
3212      status2);
3213}
3214
3215/* 0x47 */
3216static void rpt_SNR_all_SVs (TSIPPKT *rpt)
3217{
3218	unsigned char
3219		nsvs, sv_prn[12];
3220	short
3221		isv;
3222	float
3223		snr[12];
3224
3225	/* unload rptbuf */
3226	if (rpt_0x47 (rpt, &nsvs, sv_prn, snr))
3227   {
3228		parsed = BADLEN_PARSE;
3229		return;
3230	}
3231
3232	pbuf += sprintf(pbuf, "\nSNR for satellites: %d", nsvs);
3233	for (isv = 0; isv < nsvs; isv++)
3234   {
3235		pbuf += sprintf(pbuf, "\n    SV %02d   %6.2f",
3236      	sv_prn[isv], snr[isv]);
3237	}
3238}
3239
3240/* 0x48 */
3241static void rpt_GPS_system_message (TSIPPKT *rpt)
3242{
3243	unsigned char
3244		message[23];
3245
3246	/* unload rptbuf */
3247	if (rpt_0x48 (rpt, message))
3248   {
3249		parsed = BADLEN_PARSE;
3250		return;
3251	}
3252
3253	pbuf += sprintf(pbuf, "\nGPS message: %s", message);
3254}
3255
3256/* 0x49 */
3257static void rpt_almanac_health_page (TSIPPKT *rpt)
3258{
3259	short
3260		iprn;
3261	unsigned char
3262		sv_health [32];
3263
3264	/* unload rptbuf */
3265	if (rpt_0x49 (rpt, sv_health))
3266   {
3267		parsed = BADLEN_PARSE;
3268		return;
3269	}
3270
3271	pbuf += sprintf(pbuf, "\nAlmanac health page:");
3272	for (iprn = 0; iprn < 32; iprn++)
3273   {
3274		if (!(iprn%5)) *pbuf++ = '\n';
3275		pbuf += sprintf(pbuf, "    SV%02d  %2X",
3276      	(iprn+1) , sv_health[iprn]);
3277	}
3278}
3279
3280/* 0x4A */
3281static void rpt_single_lla_position (TSIPPKT *rpt) {
3282	short
3283		lat_deg, lon_deg;
3284	float
3285		lat, lon,
3286		alt, clock_bias, time_of_fix;
3287	double lat_min, lon_min;
3288	unsigned char
3289		north_south, east_west;
3290
3291	if (rpt_0x4A (rpt,
3292		&lat, &lon, &alt, &clock_bias, &time_of_fix))
3293   {
3294		parsed = BADLEN_PARSE;
3295		return;
3296	}
3297
3298	/* convert from radians to degrees */
3299	lat *= (float)R2D;
3300	north_south = 'N';
3301	if (lat < 0.0)
3302   {
3303		north_south = 'S';
3304		lat = -lat;
3305	}
3306	lat_deg = (short)lat;
3307	lat_min = (lat - lat_deg) * 60.0;
3308
3309	lon *= (float)R2D;
3310	east_west = 'E';
3311	if (lon < 0.0)
3312   {
3313		east_west = 'W';
3314		lon = -lon;
3315	}
3316	lon_deg = (short)lon;
3317	lon_min = (lon - lon_deg) * 60.0;
3318
3319	pbuf += sprintf(pbuf, "\nSLLA: %4d: %06.3f  %c%5d:%06.3f  %c%10.2f  %12.2f%s",
3320		lat_deg, lat_min, north_south,
3321		lon_deg, lon_min, east_west,
3322		alt, clock_bias,
3323		show_time(time_of_fix));
3324}
3325
3326/* 0x4A */
3327static void rpt_ref_alt (TSIPPKT *rpt) {
3328
3329	float
3330		alt, dummy;
3331	unsigned char
3332		alt_flag;
3333
3334	if (rpt_0x4A_2 (rpt,
3335		&alt, &dummy, &alt_flag))
3336   {
3337		parsed = BADLEN_PARSE;
3338		return;
3339	}
3340
3341	pbuf += sprintf(pbuf, "\nReference Alt:   %.1f m;    %s",
3342   	alt, alt_flag?"ON":"OFF");
3343}
3344
3345/* 0x4B */
3346static void rpt_rcvr_id_and_status (TSIPPKT *rpt)
3347{
3348
3349	unsigned char
3350		machine_id, status3, status4;
3351
3352	/* unload rptbuf */
3353	if (rpt_0x4B (rpt, &machine_id, &status3, &status4))
3354   {
3355		parsed = BADLEN_PARSE;
3356		return;
3357	}
3358
3359	pbuf += sprintf(pbuf, "\nRcvr Machine ID: %d; Status3 = %s, %s (%02Xh)",
3360   	machine_id,
3361		(status3 & 0x02)?"No RTC":"RTC OK",
3362		(status3 & 0x08)?"No Alm":"Alm OK",
3363		status3);
3364}
3365
3366/* 0x4C */
3367static void rpt_operating_parameters (TSIPPKT *rpt)
3368{
3369	unsigned char
3370		dyn_code;
3371	float
3372		el_mask, snr_mask, dop_mask, dop_switch;
3373
3374	/* unload rptbuf */
3375	if (rpt_0x4C (rpt, &dyn_code, &el_mask,
3376		&snr_mask, &dop_mask, &dop_switch))
3377   {
3378		parsed = BADLEN_PARSE;
3379		return;
3380	}
3381
3382	pbuf += sprintf(pbuf, "\nOperating Parameters:");
3383	pbuf += sprintf(pbuf, "\n     Dynamics code = %d %s",
3384   	dyn_code, dyn_text[dyn_code]);
3385	pbuf += sprintf(pbuf, "\n     Elevation mask = %.2f�", el_mask * R2D);
3386	pbuf += sprintf(pbuf, "\n     SNR mask = %.2f", snr_mask);
3387	pbuf += sprintf(pbuf, "\n     DOP mask = %.2f", dop_mask);
3388	pbuf += sprintf(pbuf, "\n     DOP switch = %.2f", dop_switch);
3389}
3390
3391/* 0x4D */
3392static void rpt_oscillator_offset (TSIPPKT *rpt)
3393{
3394	float
3395		osc_offset;
3396
3397	/* unload rptbuf */
3398	if (rpt_0x4D (rpt, &osc_offset))
3399   {
3400		parsed = BADLEN_PARSE;
3401		return;
3402	}
3403
3404	pbuf += sprintf(pbuf, "\nOscillator offset: %.2f Hz = %.3f PPM",
3405   	osc_offset, osc_offset/1575.42);
3406}
3407
3408/* 0x4E */
3409static void rpt_GPS_time_set_response (TSIPPKT *rpt)
3410{
3411
3412	unsigned char
3413		response;
3414
3415	/* unload rptbuf */
3416	if (rpt_0x4E (rpt, &response))
3417   {
3418		parsed = BADLEN_PARSE;
3419		return;
3420	}
3421
3422	switch (response)
3423   {
3424	case 'Y':
3425		pbuf += sprintf(pbuf, "\nTime set accepted");
3426		break;
3427
3428	case 'N':
3429		pbuf += sprintf(pbuf, "\nTime set rejected or not required");
3430		break;
3431
3432	default:
3433		parsed = BADDATA_PARSE;
3434	}
3435}
3436
3437/* 0x4F */
3438static void rpt_UTC_offset (TSIPPKT *rpt)
3439{
3440	double
3441		a0;
3442	float
3443		a1, time_of_data;
3444	short
3445		dt_ls, wn_t, wn_lsf, dn, dt_lsf;
3446
3447	/* unload rptbuf */
3448	if (rpt_0x4F (rpt, &a0, &a1, &time_of_data,
3449		&dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) {
3450		parsed = BADLEN_PARSE;
3451		return;
3452	}
3453
3454	pbuf += sprintf(pbuf, "\nUTC Correction Data");
3455	pbuf += sprintf(pbuf, "\n   A_0         = %g  ", a0);
3456	pbuf += sprintf(pbuf, "\n   A_1         = %g  ", a1);
3457	pbuf += sprintf(pbuf, "\n   delta_t_LS  = %d  ", dt_ls);
3458	pbuf += sprintf(pbuf, "\n   t_ot        = %.0f  ", time_of_data);
3459	pbuf += sprintf(pbuf, "\n   WN_t        = %d  ", wn_t );
3460	pbuf += sprintf(pbuf, "\n   WN_LSF      = %d  ", wn_lsf );
3461	pbuf += sprintf(pbuf, "\n   DN          = %d  ", dn );
3462	pbuf += sprintf(pbuf, "\n   delta_t_LSF = %d  ", dt_lsf );
3463}
3464
3465/**/
3466/* 0x54 */
3467static void rpt_1SV_bias (TSIPPKT *rpt)
3468{
3469	float
3470		clock_bias, freq_offset, time_of_fix;
3471
3472	/* unload rptbuf */
3473	if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) {
3474		parsed = BADLEN_PARSE;
3475		return;
3476	}
3477
3478	pbuf += sprintf (pbuf, "\nTime Fix   Clock Bias: %6.2f m  Freq Bias: %6.2f m/s%s",
3479		clock_bias, freq_offset, show_time (time_of_fix));
3480}
3481
3482/* 0x55 */
3483static void rpt_io_opt (TSIPPKT *rpt)
3484{
3485	unsigned char
3486		pos_code, vel_code, time_code, aux_code;
3487
3488	/* unload rptbuf */
3489	if (rpt_0x55 (rpt,
3490		&pos_code, &vel_code, &time_code, &aux_code)) {
3491		parsed = BADLEN_PARSE;
3492		return;
3493	}
3494	/* rptbuf unloaded */
3495
3496	pbuf += sprintf(pbuf, "\nI/O Options: %2X %2X %2X %2X",
3497		pos_code, vel_code, time_code, aux_code);
3498
3499	if (pos_code & 0x01) {
3500		pbuf += sprintf(pbuf, "\n    ECEF XYZ position output");
3501	}
3502
3503	if (pos_code & 0x02) {
3504		pbuf += sprintf(pbuf, "\n    LLA position output");
3505	}
3506
3507	pbuf += sprintf(pbuf, (pos_code & 0x04)?
3508		"\n    MSL altitude output (Geoid height) ":
3509		"\n    WGS-84 altitude output");
3510
3511	pbuf += sprintf(pbuf, (pos_code & 0x08)?
3512		"\n    MSL altitude input":
3513      "\n    WGS-84 altitude input");
3514
3515	pbuf += sprintf(pbuf, (pos_code & 0x10)?
3516		"\n    Double precision":
3517      "\n    Single precision");
3518
3519	if (pos_code & 0x20) {
3520		pbuf += sprintf(pbuf, "\n    All Enabled Superpackets");
3521	}
3522
3523	if (vel_code & 0x01) {
3524		pbuf += sprintf(pbuf, "\n    ECEF XYZ velocity output");
3525	}
3526
3527	if (vel_code & 0x02) {
3528		pbuf += sprintf(pbuf, "\n    ENU velocity output");
3529	}
3530
3531	pbuf += sprintf(pbuf, (time_code & 0x01)?
3532		  "\n    Time tags in UTC":
3533        "\n    Time tags in GPS time");
3534
3535	if (time_code & 0x02) {
3536		pbuf += sprintf(pbuf, "\n    Fixes delayed to integer seconds");
3537	}
3538
3539	if (time_code & 0x04) {
3540		pbuf += sprintf(pbuf, "\n    Fixes sent only on request");
3541	}
3542
3543	if (time_code & 0x08) {
3544		pbuf += sprintf(pbuf, "\n    Synchronized measurements");
3545	}
3546
3547	if (time_code & 0x10) {
3548		pbuf += sprintf(pbuf, "\n    Minimize measurement propagation");
3549	}
3550
3551   pbuf += sprintf(pbuf, (time_code & 0x20) ?
3552		"\n    PPS output at all times" :
3553   	"\n    PPS output during fixes");
3554
3555	if (aux_code & 0x01) {
3556		pbuf += sprintf(pbuf, "\n    Raw measurement output");
3557	}
3558
3559	if (aux_code & 0x02) {
3560		pbuf += sprintf(pbuf, "\n    Code-phase smoothed before output");
3561	}
3562
3563	if (aux_code & 0x04) {
3564		pbuf += sprintf(pbuf, "\n    Additional fix status");
3565	}
3566
3567	pbuf += sprintf(pbuf, (aux_code & 0x08)?
3568   	"\n    Signal Strength Output as dBHz" :
3569   	"\n    Signal Strength Output as AMU");
3570}
3571
3572/* 0x56 */
3573static void rpt_ENU_velocity (TSIPPKT *rpt)
3574{
3575	float
3576		vel_ENU[3], freq_offset, time_of_fix;
3577
3578	/* unload rptbuf */
3579	if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) {
3580		parsed = BADLEN_PARSE;
3581		return;
3582	}
3583
3584	pbuf += sprintf(pbuf, "\nVel ENU: %11.3f  %11.3f  %11.3f  %12.3f%s",
3585		vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset,
3586      show_time (time_of_fix));
3587}
3588
3589/* 0x57 */
3590static void rpt_last_fix_info (TSIPPKT *rpt)
3591{
3592	unsigned char
3593		source_code, diag_code;
3594	short
3595		week_num;
3596	float
3597		time_of_fix;
3598
3599	/* unload rptbuf */
3600	if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) {
3601		parsed = BADLEN_PARSE;
3602		return;
3603	}
3604
3605	pbuf += sprintf(pbuf, "\n source code %d;   diag code: %2Xh",
3606   	source_code, diag_code);
3607	pbuf += sprintf(pbuf, "\n    Time of last fix:%s", show_time(time_of_fix));
3608	pbuf += sprintf(pbuf, "\n    Week of last fix: %d", week_num);
3609}
3610
3611/* 0x58 */
3612static void rpt_GPS_system_data (TSIPPKT *rpt)
3613{
3614	unsigned char
3615   	iprn,
3616		op_code, data_type, sv_prn,
3617		data_length, data_packet[250];
3618	ALM_INFO
3619		*almanac;
3620	ALH_PARMS
3621		*almh;
3622	UTC_INFO
3623		*utc;
3624	ION_INFO
3625		*ionosphere;
3626	EPHEM_CLOCK
3627		*cdata;
3628	EPHEM_ORBIT
3629		*edata;
3630	NAV_INFO
3631		*nav_data;
3632	unsigned char
3633		curr_t_oa;
3634	unsigned short
3635		curr_wn_oa;
3636	static char
3637		*datname[] =
3638		{"", "", "Almanac Orbit",
3639		"Health Page & Ref Time", "Ionosphere", "UTC ",
3640		"Ephemeris"};
3641
3642	/* unload rptbuf */
3643	if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn,
3644		&data_length, data_packet))
3645   {
3646		parsed = BADLEN_PARSE;
3647		return;
3648	}
3649
3650	pbuf += sprintf(pbuf, "\nSystem data [%d]:  %s  SV%02d",
3651		data_type, datname[data_type], sv_prn);
3652	switch (op_code)
3653	{
3654	case 1:
3655		pbuf += sprintf(pbuf, "  Acknowledgment");
3656		break;
3657	case 2:
3658		pbuf += sprintf(pbuf, "  length = %d bytes", data_length);
3659		switch (data_type) {
3660		case 2:
3661			/* Almanac */
3662			if (sv_prn == 0 || sv_prn > 32) {
3663				pbuf += sprintf(pbuf, "  Binary PRN invalid");
3664				return;
3665			}
3666			almanac = (ALM_INFO*)data_packet;
3667			pbuf += sprintf(pbuf, "\n   t_oa_raw = % -12d    SV_hlth  = % -12d  ",
3668         	almanac->t_oa_raw , almanac->SV_health );
3669			pbuf += sprintf(pbuf, "\n   e        = % -12g    t_oa     = % -12g  ",
3670         	almanac->e        , almanac->t_oa     );
3671			pbuf += sprintf(pbuf, "\n   i_0      = % -12g    OMEGADOT = % -12g  ",
3672         	almanac->i_0      , almanac->OMEGADOT );
3673			pbuf += sprintf(pbuf, "\n   sqrt_A   = % -12g    OMEGA_0  = % -12g  ",
3674         	almanac->sqrt_A   , almanac->OMEGA_0  );
3675			pbuf += sprintf(pbuf, "\n   omega    = % -12g    M_0      = % -12g  ",
3676         	almanac->omega    , almanac->M_0      );
3677			pbuf += sprintf(pbuf, "\n   a_f0     = % -12g    a_f1     = % -12g  ",
3678         	almanac->a_f0     , almanac->a_f1     );
3679			pbuf += sprintf(pbuf, "\n   Axis     = % -12g    n        = % -12g  ",
3680         	almanac->Axis     , almanac->n        );
3681			pbuf += sprintf(pbuf, "\n   OMEGA_n  = % -12g    ODOT_n   = % -12g  ",
3682         	almanac->OMEGA_n  , almanac->ODOT_n   );
3683			pbuf += sprintf(pbuf, "\n   t_zc     = % -12g    weeknum  = % -12d  ",
3684         	almanac->t_zc     , almanac->weeknum  );
3685			pbuf += sprintf(pbuf, "\n   wn_oa    = % -12d", almanac->wn_oa    );
3686			break;
3687
3688		case 3:
3689			/* Almanac health page */
3690			almh = (ALH_PARMS*)data_packet;
3691			pbuf += sprintf(pbuf, "\n   t_oa = %d, wn_oa&0xFF = %d  ",
3692         	almh->t_oa, almh->WN_a);
3693			pbuf += sprintf(pbuf, "\nAlmanac health page:");
3694			for (iprn = 0; iprn < 32; iprn++) {
3695				if (!(iprn%5)) *pbuf++ = '\n';
3696				pbuf += sprintf(pbuf, "    SV%02d  %2X",
3697            	(iprn+1) , almh->SV_health[iprn]);
3698			}
3699			curr_t_oa = data_packet[34];
3700			curr_wn_oa = (unsigned short)((data_packet[35]<<8) + data_packet[36]);
3701			pbuf += sprintf(pbuf, "\n   current t_oa = %d, wn_oa = %d  ",
3702         	curr_t_oa, curr_wn_oa);
3703			break;
3704
3705		case 4:
3706			/* Ionosphere */
3707			ionosphere = (ION_INFO*)data_packet;
3708			pbuf += sprintf(pbuf, "\n   alpha_0 = % -12g  alpha_1 = % -12g ",
3709	         ionosphere->alpha_0, ionosphere->alpha_1);
3710			pbuf += sprintf(pbuf, "\n   alpha_2 = % -12g  alpha_3 = % -12g ",
3711	         ionosphere->alpha_2, ionosphere->alpha_3);
3712			pbuf += sprintf(pbuf, "\n   beta_0  = % -12g  beta_1  = % -12g  ",
3713	         ionosphere->beta_0, ionosphere->beta_1);
3714			pbuf += sprintf(pbuf, "\n   beta_2  = % -12g  beta_3  = % -12g  ",
3715	         ionosphere->beta_2, ionosphere->beta_3);
3716			break;
3717
3718		case 5:
3719			/* UTC */
3720			utc = (UTC_INFO*)data_packet;
3721			pbuf += sprintf(pbuf, "\n   A_0         = %g  ", utc->A_0);
3722			pbuf += sprintf(pbuf, "\n   A_1         = %g  ", utc->A_1);
3723			pbuf += sprintf(pbuf, "\n   delta_t_LS  = %d  ", utc->delta_t_LS);
3724			pbuf += sprintf(pbuf, "\n   t_ot        = %.0f  ", utc->t_ot );
3725			pbuf += sprintf(pbuf, "\n   WN_t        = %d  ", utc->WN_t );
3726			pbuf += sprintf(pbuf, "\n   WN_LSF      = %d  ", utc->WN_LSF );
3727			pbuf += sprintf(pbuf, "\n   DN          = %d  ", utc->DN );
3728			pbuf += sprintf(pbuf, "\n   delta_t_LSF = %d  ", utc->delta_t_LSF );
3729			break;
3730
3731		case 6: /* Ephemeris */
3732			if (sv_prn == 0 || sv_prn > 32) {
3733				pbuf += sprintf(pbuf, "  Binary PRN invalid");
3734				return;
3735			}
3736			nav_data = (NAV_INFO*)data_packet;
3737
3738			pbuf += sprintf(pbuf, "\n     SV_PRN = % -12d .  t_ephem = % -12g . ",
3739         	nav_data->sv_number , nav_data->t_ephem );
3740			cdata = &(nav_data->ephclk);
3741			pbuf += sprintf(pbuf,
3742         	"\n    weeknum = % -12d .   codeL2 = % -12d .  L2Pdata = % -12d",
3743         	cdata->weeknum , cdata->codeL2 , cdata->L2Pdata );
3744			pbuf += sprintf(pbuf,
3745         	"\n  SVacc_raw = % -12d .SV_health = % -12d .     IODC = % -12d",
3746         	cdata->SVacc_raw, cdata->SV_health, cdata->IODC );
3747			pbuf += sprintf(pbuf,
3748         	"\n       T_GD = % -12g .     t_oc = % -12g .     a_f2 = % -12g",
3749         	cdata->T_GD, cdata->t_oc, cdata->a_f2 );
3750			pbuf += sprintf(pbuf,
3751         	"\n       a_f1 = % -12g .     a_f0 = % -12g .    SVacc = % -12g",
3752         	cdata->a_f1, cdata->a_f0, cdata->SVacc );
3753			edata = &(nav_data->ephorb);
3754			pbuf += sprintf(pbuf,
3755	         "\n       IODE = % -12d .fit_intvl = % -12d .     C_rs = % -12g",
3756	         edata->IODE, edata->fit_interval, edata->C_rs );
3757			pbuf += sprintf(pbuf,
3758         	"\n    delta_n = % -12g .      M_0 = % -12g .     C_uc = % -12g",
3759         	edata->delta_n, edata->M_0, edata->C_uc );
3760			pbuf += sprintf(pbuf,
3761         	"\n        ecc = % -12g .     C_us = % -12g .   sqrt_A = % -12g",
3762	         edata->e, edata->C_us, edata->sqrt_A );
3763			pbuf += sprintf(pbuf,
3764         	"\n       t_oe = % -12g .     C_ic = % -12g .  OMEGA_0 = % -12g",
3765            edata->t_oe, edata->C_ic, edata->OMEGA_0 );
3766			pbuf += sprintf(pbuf,
3767	         "\n       C_is = % -12g .      i_0 = % -12g .     C_rc = % -12g",
3768	         edata->C_is, edata->i_0, edata->C_rc );
3769			pbuf += sprintf(pbuf,
3770	         "\n      omega = % -12g . OMEGADOT = % -12g .     IDOT = % -12g",
3771         	edata->omega, edata->OMEGADOT, edata->IDOT );
3772			pbuf += sprintf(pbuf,
3773   	      "\n       Axis = % -12g .        n = % -12g .    r1me2 = % -12g",
3774	         edata->Axis, edata->n, edata->r1me2 );
3775			pbuf += sprintf(pbuf,
3776      	   "\n    OMEGA_n = % -12g .   ODOT_n = % -12g",
3777	         edata->OMEGA_n, edata->ODOT_n );
3778			break;
3779		}
3780	}
3781}
3782
3783
3784/* 0x59: */
3785static void rpt_SVs_enabled (TSIPPKT *rpt)
3786{
3787	unsigned char
3788   	numsvs,
3789		code_type,
3790      status_code[32];
3791	short
3792		iprn;
3793
3794	/* unload rptbuf */
3795	if (rpt_0x59 (rpt, &code_type, status_code))
3796   {
3797		parsed = BADLEN_PARSE;
3798		return;
3799	}
3800   switch (code_type)
3801   {
3802   case 3: pbuf += sprintf(pbuf, "\nSVs Disabled:\n"); break;
3803   case 6: pbuf += sprintf(pbuf, "\nSVs with Health Ignored:\n"); break;
3804   default: return;
3805   }
3806   numsvs = 0;
3807	for (iprn=0; iprn<32; iprn++)
3808   {
3809     	if (status_code[iprn])
3810      {
3811	   	pbuf += sprintf(pbuf, " %02d", iprn+1);
3812   	   numsvs++;
3813      }
3814   }
3815   if (numsvs == 0) pbuf += sprintf(pbuf, "None");
3816}
3817
3818
3819/* 0x5A */
3820static void rpt_raw_msmt (TSIPPKT *rpt)
3821{
3822	unsigned char
3823		sv_prn;
3824	float
3825		sample_length, signal_level, code_phase, Doppler;
3826	double
3827		time_of_fix;
3828
3829	/* unload rptbuf */
3830	if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level,
3831		&code_phase, &Doppler, &time_of_fix))
3832   {
3833		parsed = BADLEN_PARSE;
3834		return;
3835	}
3836
3837	pbuf += sprintf(pbuf, "\n   %02d %5.0f %7.1f %10.2f %10.2f %12.3f %s",
3838		sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix,
3839		show_time ((float)time_of_fix));
3840}
3841
3842/* 0x5B */
3843static void rpt_SV_ephemeris_status (TSIPPKT *rpt)
3844{
3845	unsigned char
3846		sv_prn, sv_health, sv_iode, fit_interval_flag;
3847	float
3848		time_of_collection, time_of_eph, sv_accy;
3849
3850	/* unload rptbuf */
3851	if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag,
3852		&time_of_collection, &time_of_eph, &sv_accy))
3853   {
3854		parsed = BADLEN_PARSE;
3855		return;
3856	}
3857
3858	pbuf += sprintf(pbuf, "\n  SV%02d  %s   %2Xh     %2Xh ",
3859   	sv_prn, show_time (time_of_collection), sv_health, sv_iode);
3860	/* note: cannot use show_time twice in same call */
3861	pbuf += sprintf(pbuf, "%s   %1d   %4.1f",
3862      show_time (time_of_eph), fit_interval_flag, sv_accy);
3863}
3864
3865/* 0x5C */
3866static void rpt_SV_tracking_status (TSIPPKT *rpt)
3867{
3868	unsigned char
3869		sv_prn, chan, slot, acq_flag, eph_flag,
3870		old_msmt_flag, integer_msec_flag, bad_data_flag,
3871		data_collect_flag;
3872	float
3873		signal_level, time_of_last_msmt,
3874		elev, azim;
3875
3876	/* unload rptbuf */
3877	if (rpt_0x5C (rpt,
3878		&sv_prn, &slot, &chan, &acq_flag, &eph_flag,
3879		&signal_level, &time_of_last_msmt, &elev, &azim,
3880		&old_msmt_flag, &integer_msec_flag, &bad_data_flag,
3881		&data_collect_flag))
3882   {
3883		parsed = BADLEN_PARSE;
3884		return;
3885	}
3886
3887	pbuf += sprintf(pbuf,
3888"\n SV%2d  %1d   %1d   %1d   %4.1f  %s  %5.1f  %5.1f",
3889		sv_prn, chan,
3890      acq_flag, eph_flag, signal_level,
3891      show_time(time_of_last_msmt),
3892		elev*R2D, azim*R2D);
3893}
3894
3895/**/
3896/* 0x6D */
3897static void rpt_allSV_selection (TSIPPKT *rpt)
3898{
3899	unsigned char
3900		manual_mode, nsvs, sv_prn[8], ndim;
3901	short
3902		islot;
3903	float
3904		pdop, hdop, vdop, tdop;
3905
3906	/* unload rptbuf */
3907	if (rpt_0x6D (rpt,
3908		&manual_mode, &nsvs, &ndim, sv_prn,
3909		&pdop, &hdop, &vdop, &tdop))
3910   {
3911		parsed = BADLEN_PARSE;
3912		return;
3913	}
3914
3915	switch (ndim)
3916   {
3917   case 0:
3918		pbuf += sprintf(pbuf, "\nMode: Searching, %d-SV:", nsvs);
3919      break;
3920   case 1:
3921		pbuf += sprintf(pbuf, "\nMode: One-SV Timing:");
3922      break;
3923   case 3: case 4:
3924		pbuf += sprintf(pbuf, "\nMode: %c-%dD, %d-SV:",
3925   			manual_mode ? 'M' : 'A', ndim - 1,  nsvs);
3926      break;
3927	case 5:
3928		pbuf += sprintf(pbuf, "\nMode: Timing, %d-SV:", nsvs);
3929      break;
3930   default:
3931		pbuf += sprintf(pbuf, "\nMode: Unknown = %d:", ndim);
3932      break;
3933   }
3934
3935	for (islot = 0; islot < nsvs; islot++)
3936   {
3937		if (sv_prn[islot]) pbuf += sprintf(pbuf, " %02d", sv_prn[islot]);
3938	}
3939   if (ndim == 3 || ndim == 4)
3940   {
3941		pbuf += sprintf(pbuf, ";  DOPs: P %.1f H %.1f V %.1f T %.1f",
3942			pdop, hdop, vdop, tdop);
3943   }
3944}
3945
3946/**/
3947/* 0x82 */
3948static void rpt_DGPS_position_mode (TSIPPKT *rpt)
3949{
3950	unsigned char
3951		diff_mode;
3952
3953	/* unload rptbuf */
3954	if (rpt_0x82 (rpt, &diff_mode)) {
3955		parsed = BADLEN_PARSE;
3956		return;
3957	}
3958
3959	pbuf += sprintf(pbuf, "\nFix is%s DGPS-corrected (%s mode)  (%d)",
3960   	(diff_mode&1) ? "" : " not",
3961   	(diff_mode&2) ? "auto" : "manual",
3962      diff_mode);
3963}
3964
3965/* 0x83 */
3966static void rpt_double_ECEF_position (TSIPPKT *rpt)
3967{
3968
3969	double
3970		ECEF_pos[3], clock_bias;
3971	float
3972		time_of_fix;
3973
3974	/* unload rptbuf */
3975	if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix))
3976   {
3977		parsed = BADLEN_PARSE;
3978		return;
3979	}
3980
3981	pbuf += sprintf(pbuf, "\nDXYZ:%12.2f  %13.2f  %13.2f %12.2f%s",
3982		ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias,
3983		show_time(time_of_fix));
3984}
3985
3986/* 0x84 */
3987static void rpt_double_lla_position (TSIPPKT *rpt)
3988{
3989	short
3990		lat_deg, lon_deg;
3991	double
3992		lat, lon, lat_min, lon_min,
3993		alt, clock_bias;
3994	float
3995		time_of_fix;
3996	unsigned char
3997		north_south, east_west;
3998
3999	/* unload rptbuf */
4000	if (rpt_0x84 (rpt,
4001		&lat, &lon, &alt, &clock_bias, &time_of_fix))
4002   {
4003		parsed = BADLEN_PARSE;
4004		return;
4005	}
4006
4007	lat *= R2D;
4008	lon *= R2D;
4009	if (lat < 0.0) {
4010		north_south = 'S';
4011		lat = -lat;
4012	} else {
4013		north_south = 'N';
4014	}
4015	lat_deg = (short)lat;
4016	lat_min = (lat - lat_deg) * 60.0;
4017
4018	if (lon < 0.0) {
4019		east_west = 'W';
4020		lon = -lon;
4021	} else {
4022		east_west = 'E';
4023	}
4024	lon_deg = (short)lon;
4025	lon_min = (lon - lon_deg) * 60.0;
4026	pbuf += sprintf(pbuf, "\nDLLA: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f%s",
4027		lat_deg, lat_min, north_south,
4028		lon_deg, lon_min, east_west,
4029		alt, clock_bias,
4030		show_time(time_of_fix));
4031}
4032
4033/* 0xBB */
4034static void rpt_complete_rcvr_config (TSIPPKT *rpt)
4035{
4036	TSIP_RCVR_CFG TsipxBB ;
4037	/* unload rptbuf */
4038	if (rpt_Paly0xBB (rpt, &TsipxBB))
4039	{
4040		parsed = BADLEN_PARSE;
4041		return;
4042	}
4043
4044	pbuf += sprintf(pbuf, "\n   operating mode:      %s",
4045		NavModeText0xBB[TsipxBB.operating_mode]);
4046	pbuf += sprintf(pbuf, "\n   dynamics:            %s",
4047		dyn_text[TsipxBB.dyn_code]);
4048	pbuf += sprintf(pbuf, "\n   elev angle mask:     %g deg",
4049		TsipxBB.elev_mask * R2D);
4050	pbuf += sprintf(pbuf, "\n   SNR mask:            %g AMU",
4051		TsipxBB.cno_mask);
4052	pbuf += sprintf(pbuf, "\n   DOP mask:            %g",
4053		TsipxBB.dop_mask);
4054	pbuf += sprintf(pbuf, "\n   DOP switch:          %g",
4055		TsipxBB.dop_switch);
4056	return ;
4057}
4058
4059/* 0xBC */
4060static void rpt_rcvr_serial_port_config (TSIPPKT *rpt)
4061{
4062	unsigned char
4063		port_num, in_baud, out_baud, data_bits, parity, stop_bits, flow_control,
4064		protocols_in, protocols_out, reserved;
4065	unsigned char known;
4066
4067	/* unload rptbuf */
4068	if (rpt_0xBC (rpt, &port_num, &in_baud, &out_baud, &data_bits, &parity,
4069			&stop_bits, &flow_control, &protocols_in, &protocols_out, &reserved)) {
4070		parsed = BADLEN_PARSE;
4071		return;
4072	}
4073	/* rptbuf unloaded */
4074
4075	pbuf += sprintf(pbuf, "\n   RECEIVER serial port %s config:",
4076		rcvr_port_text[port_num]);
4077
4078	pbuf += sprintf(pbuf, "\n             I/O Baud %s/%s, %d - %s - %d",
4079		st_baud_text_app[in_baud],
4080		st_baud_text_app[out_baud],
4081		data_bits+5,
4082		parity_text[parity],
4083		stop_bits=1);
4084	pbuf += sprintf(pbuf, "\n             Input protocols: ");
4085	known = FALSE;
4086	if (protocols_in&B_TSIP)
4087   {
4088		pbuf += sprintf(pbuf, "%s ", protocols_in_text[1]);
4089		known = TRUE;
4090	}
4091	if (known == FALSE) pbuf += sprintf(pbuf, "No known");
4092
4093	pbuf += sprintf(pbuf, "\n             Output protocols: ");
4094	known = FALSE;
4095	if (protocols_out&B_TSIP)
4096   {
4097		pbuf += sprintf(pbuf, "%s ", protocols_out_text[1]);
4098		known = TRUE;
4099	}
4100	if (protocols_out&B_NMEA)
4101   {
4102		pbuf += sprintf(pbuf, "%s ", protocols_out_text[2]);
4103		known = TRUE;
4104	}
4105	if (known == FALSE) pbuf += sprintf(pbuf, "No known");
4106	reserved = reserved;
4107
4108 }
4109
4110/* 0x8F */
4111/* 8F0B */
4112static void rpt_8F0B(TSIPPKT *rpt)
4113{
4114	const char
4115   	*oprtng_dim[7] = {
4116      	"horizontal (2-D)",
4117         "full position (3-D)",
4118         "single satellite (0-D)",
4119         "automatic",
4120         "N/A",
4121         "N/A",
4122         "overdetermined clock"};
4123   char
4124   	sv_id[8];
4125   unsigned char
4126   	month,
4127      date,
4128      dim_mode,
4129      north_south,
4130      east_west;
4131   unsigned short
4132   	event;
4133   short
4134   	utc_offset,
4135      year,
4136      local_index;
4137	short
4138   	lat_deg,
4139      lon_deg;
4140   float
4141   	bias_unc,
4142      dr_unc;
4143   double
4144   	tow,
4145      bias,
4146      drift,
4147      lat,
4148      lon,
4149      alt,
4150      lat_min,
4151      lon_min;
4152   int
4153   	numfix,
4154      numnotfix;
4155
4156	if (rpt_0x8F0B(rpt,
4157   	&event,
4158      &tow,
4159      &date,
4160      &month,
4161      &year,
4162      &dim_mode,
4163      &utc_offset,
4164      &bias,
4165      &drift,
4166      &bias_unc,
4167      &dr_unc,
4168      &lat,
4169      &lon,
4170      &alt,
4171      sv_id))
4172   {
4173		parsed = BADLEN_PARSE;
4174		return;
4175	}
4176
4177	if (event == 0)
4178   {
4179   	pbuf += sprintf(pbuf, "\nNew partial+full meas");
4180	}
4181   else
4182   {
4183		pbuf += sprintf(pbuf, "\nEvent count: %5d", event);
4184   }
4185
4186	pbuf += sprintf(pbuf, "\nGPS time  : %s %2d/%2d/%2d (DMY)",
4187   	show_time(tow), date, month, year);
4188	pbuf += sprintf(pbuf, "\nMode      : %s", oprtng_dim[dim_mode]);
4189	pbuf += sprintf(pbuf, "\nUTC offset: %2d", utc_offset);
4190	pbuf += sprintf(pbuf, "\nClock Bias: %6.2f m", bias);
4191	pbuf += sprintf(pbuf, "\nFreq bias : %6.2f m/s", drift);
4192	pbuf += sprintf(pbuf, "\nBias unc  : %6.2f m", bias_unc);
4193	pbuf += sprintf(pbuf, "\nFreq unc  : %6.2f m/s", dr_unc);
4194
4195	lat *= R2D; /* convert from radians to degrees */
4196	lon *= R2D;
4197	if (lat < 0.0)
4198   {
4199		north_south = 'S';
4200		lat = -lat;
4201	}
4202   else
4203   {
4204		north_south = 'N';
4205	}
4206
4207	lat_deg = (short)lat;
4208	lat_min = (lat - lat_deg) * 60.0;
4209	if (lon < 0.0)
4210   {
4211		east_west = 'W';
4212		lon = -lon;
4213	}
4214   else
4215   {
4216		east_west = 'E';
4217	}
4218
4219	lon_deg = (short)lon;
4220	lon_min = (lon - lon_deg) * 60.0;
4221	pbuf += sprintf(pbuf, "\nPosition  :");
4222	pbuf += sprintf(pbuf, " %4d %6.3f %c", lat_deg, lat_min, north_south);
4223	pbuf += sprintf(pbuf, " %5d %6.3f %c", lon_deg, lon_min, east_west);
4224	pbuf += sprintf(pbuf, " %10.2f", alt);
4225
4226   numfix = numnotfix = 0;
4227	for (local_index=0; local_index<8; local_index++)
4228   {
4229		if (sv_id[local_index] < 0) numnotfix++;
4230		if (sv_id[local_index] > 0) numfix++;
4231   }
4232   if (numfix > 0)
4233   {
4234		pbuf += sprintf(pbuf, "\nSVs used in fix  : ");
4235		for (local_index=0; local_index<8; local_index++)
4236	   {
4237			if (sv_id[local_index] > 0)
4238      	{
4239      		pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
4240	      }
4241   	}
4242   }
4243   if (numnotfix > 0)
4244   {
4245		pbuf += sprintf(pbuf, "\nOther SVs tracked: ");
4246		for (local_index=0; local_index<8; local_index++)
4247	   {
4248			if (sv_id[local_index] < 0)
4249      	{
4250      		pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
4251	      }
4252   	}
4253   }
4254}
4255
4256/* 0x8F14 */
4257static void rpt_8F14 (TSIPPKT *rpt)
4258/* Datum parameters */
4259{
4260	double
4261		datum_coeffs[5];
4262	short
4263		datum_idx;
4264
4265	/* unload rptbuf */
4266	if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs))
4267   {
4268		parsed = BADLEN_PARSE;
4269		return;
4270	}
4271
4272	if (datum_idx == -1)
4273   {
4274   	pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
4275		pbuf += sprintf(pbuf, "\n   dx        = %6.1f", datum_coeffs[0]);
4276		pbuf += sprintf(pbuf, "\n   dy        = %6.1f", datum_coeffs[1]);
4277		pbuf += sprintf(pbuf, "\n   dz        = %6.1f", datum_coeffs[2]);
4278		pbuf += sprintf(pbuf, "\n   a-axis    = %10.3f", datum_coeffs[3]);
4279		pbuf += sprintf(pbuf, "\n   e-squared = %16.14f", datum_coeffs[4]);
4280   }
4281   else if (datum_idx == 0)
4282   {
4283   	pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
4284   }
4285   else
4286   {
4287   	pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
4288   }
4289}
4290
4291/* 0x8F15 */
4292static void rpt_8F15 (TSIPPKT *rpt)
4293/* Datum parameters */
4294{
4295	double
4296		datum_coeffs[5];
4297	short
4298		datum_idx;
4299
4300	/* unload rptbuf */
4301	if (rpt_0x8F15 (rpt, &datum_idx, datum_coeffs)) {
4302		parsed = BADLEN_PARSE;
4303		return;
4304	}
4305
4306	if (datum_idx == -1)
4307   {
4308   	pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
4309		pbuf += sprintf(pbuf, "\n   dx        = %6.1f", datum_coeffs[0]);
4310		pbuf += sprintf(pbuf, "\n   dy        = %6.1f", datum_coeffs[1]);
4311		pbuf += sprintf(pbuf, "\n   dz        = %6.1f", datum_coeffs[2]);
4312		pbuf += sprintf(pbuf, "\n   a-axis    = %10.3f", datum_coeffs[3]);
4313		pbuf += sprintf(pbuf, "\n   e-squared = %16.14f", datum_coeffs[4]);
4314   }
4315   else if (datum_idx == 0)
4316   {
4317   	pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
4318   }
4319   else
4320   {
4321   	pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
4322   }
4323}
4324
4325/* 0x8F20 */
4326#define INFO_DGPS       0x02
4327#define INFO_2D         0x04
4328#define INFO_ALTSET     0x08
4329#define INFO_FILTERED   0x10
4330static void rpt_8F20 (TSIPPKT *rpt)
4331{
4332	unsigned char
4333		info, nsvs, sv_prn[32];
4334	short
4335		week_num, datum_index, sv_IODC[32];
4336	double
4337		lat, lon, alt, time_of_fix;
4338	double
4339		londeg, latdeg, vel[3];
4340	short
4341		isv;
4342   char
4343   	datum_string[20];
4344
4345	/* unload rptbuf */
4346	if (rpt_0x8F20 (rpt,
4347		&info, &lat, &lon, &alt, vel,
4348		&time_of_fix,
4349		&week_num, &nsvs, sv_prn, sv_IODC, &datum_index))
4350	{
4351		parsed = BADLEN_PARSE;
4352		return;
4353	}
4354	pbuf += sprintf(pbuf,
4355   	"\nFix at: %04d:%3s:%02d:%02d:%06.3f GPS (=UTC+%2ds)  FixType: %s%s%s",
4356   	week_num,
4357		dayname[(short)(time_of_fix/86400.0)],
4358		(short)fmod(time_of_fix/3600., 24.),
4359		(short)fmod(time_of_fix/60., 60.),
4360		fmod(time_of_fix, 60.),
4361      (char)rpt->buf[29],		/* UTC offset */
4362		(info & INFO_DGPS)?"Diff":"",
4363		(info & INFO_2D)?"2D":"3D",
4364		(info & INFO_FILTERED)?"-Filtrd":"");
4365
4366   if (datum_index > 0)
4367   {
4368		sprintf(datum_string, "Datum%3d", datum_index);
4369   }
4370   else if (datum_index)
4371   {
4372		sprintf(datum_string, "Unknown ");
4373   }
4374   else
4375   {
4376		sprintf(datum_string, "WGS-84");
4377   }
4378
4379	/* convert from radians to degrees */
4380	latdeg = R2D * fabs(lat);
4381	londeg = R2D * fabs(lon);
4382	pbuf += sprintf(pbuf,
4383   	"\n   Pos: %4d:%09.6f %c %5d:%09.6f %c %10.2f m HAE (%s)",
4384		(short)latdeg, fmod (latdeg, 1.)*60.0,
4385		(lat<0.0)?'S':'N',
4386		(short)londeg, fmod (londeg, 1.)*60.0,
4387		(lon<0.0)?'W':'E',
4388		alt,
4389      datum_string);
4390	pbuf += sprintf(pbuf,
4391   	"\n   Vel:    %9.3f E       %9.3f N      %9.3f U   (m/sec)",
4392		vel[0], vel[1], vel[2]);
4393
4394	pbuf += sprintf(pbuf,
4395   	"\n   SVs: ");
4396	for (isv = 0; isv < nsvs; isv++) {
4397		pbuf += sprintf(pbuf, " %02d", sv_prn[isv]);
4398	}
4399	pbuf += sprintf(pbuf, "     (IODEs:");
4400	for (isv = 0; isv < nsvs; isv++) {
4401		pbuf += sprintf(pbuf, " %02X", sv_IODC[isv]&0xFF);
4402	}
4403	pbuf += sprintf(pbuf, ")");
4404}
4405
4406/* 0x8F41 */
4407static void rpt_8F41(TSIPPKT *rpt)
4408{
4409	unsigned char
4410   	bSearchRange,
4411		bBoardOptions,
4412		bBuildYear,
4413		bBuildMonth,
4414		bBuildDay,
4415		bBuildHour;
4416	float
4417   	fOscOffset;
4418	unsigned short
4419   	iTestCodeId;
4420	unsigned long
4421		iiSerialNumber;
4422
4423   if (!rpt_0x8F41(rpt,
4424		&bSearchRange,
4425		&bBoardOptions,
4426		&iiSerialNumber,
4427		&bBuildYear,
4428		&bBuildMonth,
4429		&bBuildDay,
4430		&bBuildHour,
4431		&fOscOffset,
4432		&iTestCodeId))
4433   {
4434		parsed = BADLEN_PARSE;
4435      return;
4436   }
4437
4438   pbuf += sprintf(pbuf, "\n  search range:          %d",
4439   	bSearchRange);
4440   pbuf += sprintf(pbuf, "\n  board options:         %d",
4441      bBoardOptions);
4442   pbuf += sprintf(pbuf, "\n  board serial #:        %ld",
4443      iiSerialNumber);
4444   pbuf += sprintf(pbuf, "\n  build date/hour:       %02d/%02d/%02d %02d:00",
4445   	bBuildDay, bBuildMonth, bBuildYear, bBuildHour);
4446   pbuf += sprintf(pbuf, "\n  osc offset:            %.3f PPM (%.0f Hz)",
4447   	fOscOffset/1575.42, fOscOffset);
4448   pbuf += sprintf(pbuf, "\n  test code:             %d",
4449   	iTestCodeId);
4450}
4451
4452/* 0x8F42 */
4453static void rpt_8F42(TSIPPKT *rpt)
4454{
4455	unsigned char
4456   	bProdOptionsPre,
4457      bProdNumberExt;
4458	unsigned short
4459   	iCaseSerialNumberPre,
4460      iPremiumOptions,
4461      iMachineID,
4462      iKey;
4463	unsigned long
4464   	iiCaseSerialNumber,
4465		iiProdNumber;
4466
4467   if (!rpt_0x8F42(rpt,
4468		&bProdOptionsPre,
4469		&bProdNumberExt,
4470		&iCaseSerialNumberPre,
4471		&iiCaseSerialNumber,
4472		&iiProdNumber,
4473		&iPremiumOptions,
4474		&iMachineID,
4475		&iKey))
4476   {
4477		parsed = BADLEN_PARSE;
4478      return;
4479   }
4480
4481	pbuf += sprintf(pbuf, "\nProduct ID 8F42");
4482   pbuf += sprintf(pbuf, "\n   extension:            %d", bProdNumberExt);
4483   pbuf += sprintf(pbuf, "\n   case serial # prefix: %d", iCaseSerialNumberPre);
4484   pbuf += sprintf(pbuf, "\n   case serial #:        %ld", iiCaseSerialNumber);
4485   pbuf += sprintf(pbuf, "\n   prod. #:              %ld", iiProdNumber);
4486	pbuf += sprintf(pbuf, "\n   premium options:      %Xh", iPremiumOptions);
4487   pbuf += sprintf(pbuf, "\n   machine ID:           %d", iMachineID);
4488   pbuf += sprintf(pbuf, "\n   key:                  %Xh", iKey);
4489}
4490
4491/* 0x8F45 */
4492static void rpt_8F45(TSIPPKT *rpt)
4493{
4494   unsigned char bSegMask;
4495
4496   if (!rpt_0x8F45(rpt,
4497   	&bSegMask))
4498   {
4499		parsed = BADLEN_PARSE;
4500		return;
4501	}
4502	pbuf += sprintf(pbuf, "\nCleared Segment Mask: %Xh", bSegMask);
4503}
4504
4505static void rpt_8F4A(TSIPPKT *rpt)
4506/* Stinger PPS def */
4507{
4508	unsigned char
4509   	pps_enabled,
4510      pps_timebase,
4511      pps_polarity;
4512   float
4513   	bias_unc_threshold;
4514   double
4515   	pps_offset;
4516
4517  	if (rpt_0x8F4A_16 (rpt,
4518   	&pps_enabled,
4519      &pps_timebase,
4520      &pps_polarity,
4521      &pps_offset,
4522      &bias_unc_threshold))
4523   {
4524   	parsed = BADLEN_PARSE;
4525	   return;
4526   }
4527
4528	pbuf += sprintf(pbuf, "\nPPS is         %s",	pps_enabled?"enabled":"disabled");
4529   pbuf += sprintf(pbuf, "\n   timebase:   %s", PPSTimeBaseText[pps_timebase]);
4530   pbuf += sprintf(pbuf, "\n   polarity:   %s", PPSPolarityText[pps_polarity]);
4531   pbuf += sprintf(pbuf, "\n   offset:     %.1f ns, ", pps_offset*1.e9);
4532   pbuf += sprintf(pbuf, "\n   biasunc:    %.1f ns", bias_unc_threshold/GPS_C*1.e9);
4533}
4534
4535static void rpt_8F4B(TSIPPKT *rpt)
4536/* fast-SA decorrolation time for self-survey */
4537{
4538	unsigned long
4539   	decorr_max;
4540
4541   if (rpt_0x8F4B(rpt, &decorr_max))
4542   {
4543		parsed = BADLEN_PARSE;
4544      return;
4545   }
4546
4547   pbuf += sprintf(pbuf,
4548   	"\nMax # of position fixes for self-survey : %ld",
4549      decorr_max);
4550}
4551
4552static void rpt_8F4D(TSIPPKT *rpt)
4553{
4554	static char
4555   	*linestart;
4556	unsigned long
4557   	OutputMask;
4558   static unsigned long
4559   	MaskBit[] = {
4560      	0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
4561      	0x00000100L, 0x00000800L, 0x00001000L,
4562         0x40000000L, 0x80000000L};
4563   int
4564   	ichoice,
4565   	numchoices;
4566
4567   if (rpt_0x8F4D(rpt, &OutputMask))
4568   {
4569		parsed = BADLEN_PARSE;
4570      return;
4571   }
4572
4573   pbuf += sprintf(pbuf, "\nAuto-Report Mask: %02X %02X %02X %02X",
4574   	(unsigned char)(OutputMask>>24),
4575   	(unsigned char)(OutputMask>>16),
4576   	(unsigned char)(OutputMask>>8),
4577   	(unsigned char)OutputMask);
4578
4579   numchoices = sizeof(MaskText)/sizeof(char*);
4580   pbuf += sprintf(pbuf, "\nAuto-Reports scheduled for Output:");
4581   linestart = pbuf;
4582   for (ichoice=0; ichoice<numchoices; ichoice++)
4583   {
4584   	if (OutputMask&MaskBit[ichoice])
4585      {
4586	     	pbuf += sprintf(pbuf, "%s %s",
4587   	   	(pbuf==linestart)?"\n     ":",",
4588      	   MaskText[ichoice]);
4589			if (pbuf-linestart > 60) linestart = pbuf;
4590      }
4591   }
4592
4593   pbuf += sprintf(pbuf, "\nAuto-Reports NOT scheduled for Output:");
4594   linestart = pbuf;
4595   for (ichoice=0; ichoice<numchoices; ichoice++)
4596   {
4597   	if (OutputMask&MaskBit[ichoice]) continue;
4598	     	pbuf += sprintf(pbuf, "%s %s",
4599   	   	(pbuf==linestart)?"\n     ":",",
4600         MaskText[ichoice]);
4601		if (pbuf-linestart > 60) linestart = pbuf;
4602   }
4603}
4604
4605static void rpt_8FA5(TSIPPKT *rpt)
4606{
4607	unsigned char
4608   	spktmask[4];
4609
4610   if (rpt_0x8FA5(rpt, spktmask))
4611   {
4612		parsed = BADLEN_PARSE;
4613      return;
4614   }
4615
4616   pbuf += sprintf(pbuf, "\nSuperpacket auto-output mask: %02X %02X %02X %02X",
4617   	spktmask[0], spktmask[1], spktmask[2], spktmask[3]);
4618
4619   if (spktmask[0]&0x01) pbuf+= sprintf (pbuf, "\n    PPS   8F-0B");
4620   if (spktmask[0]&0x02) pbuf+= sprintf (pbuf, "\n    Event 8F-0B");
4621   if (spktmask[0]&0x10) pbuf+= sprintf (pbuf, "\n    PPS   8F-AD");
4622   if (spktmask[0]&0x20) pbuf+= sprintf (pbuf, "\n    Event 8F-AD");
4623   if (spktmask[2]&0x01) pbuf+= sprintf (pbuf, "\n    ppos Fix 8F-20");
4624}
4625
4626static void rpt_8FAD (TSIPPKT *rpt)
4627{
4628   unsigned short
4629    	Count,
4630    	Year;
4631   double
4632    	FracSec;
4633   unsigned char
4634    	Hour,
4635    	Minute,
4636    	Second,
4637    	Day,
4638    	Month,
4639    	Status,
4640    	Flags;
4641	static char* Status8FADText[] = {
4642      "CODE_DOING_FIXES",
4643      "CODE_GOOD_1_SV",
4644      "CODE_APPX_1SV",
4645      "CODE_NEED_TIME",
4646      "CODE_NEED_INITIALIZATION",
4647      "CODE_PDOP_HIGH",
4648      "CODE_BAD_1SV",
4649      "CODE_0SVS",
4650      "CODE_1SV",
4651      "CODE_2SVS",
4652      "CODE_3SVS",
4653      "CODE_NO_INTEGRITY",
4654      "CODE_DCORR_GEN",
4655      "CODE_OVERDET_CLK",
4656      "Invalid Status"},
4657    	*LeapStatusText[] = {
4658    	" UTC Avail", " ", " ", " ",
4659      " Scheduled", " Pending", " Warning", " In Progress"};
4660    int i;
4661
4662	if (rpt_0x8FAD (rpt,
4663    	&Count,
4664    	&FracSec,
4665    	&Hour,
4666    	&Minute,
4667    	&Second,
4668    	&Day,
4669    	&Month,
4670    	&Year,
4671    	&Status,
4672    	&Flags))
4673   {
4674		parsed = BADLEN_PARSE;
4675		return;
4676   }
4677
4678	pbuf += sprintf(pbuf,    "\n8FAD   Count: %d   Status: %s",
4679   	Count, Status8FADText[Status]);
4680
4681  	pbuf += sprintf(pbuf, "\n   Leap Flags:");
4682   if (Flags)
4683   {
4684   	for (i=0; i<8; i++)
4685      {
4686      	if (Flags&(1<<i)) pbuf += sprintf(pbuf, LeapStatusText[i]);
4687      }
4688   }
4689   else
4690   {
4691   	pbuf += sprintf(pbuf, "  UTC info not available");
4692   }
4693
4694	pbuf += sprintf(pbuf,     "\n      %02d/%02d/%04d (DMY)  %02d:%02d:%02d.%09ld UTC",
4695   		Day, Month, Year, Hour, Minute, Second, (long)(FracSec*1.e9));
4696}
4697
4698
4699int print_msg_table_header (int rptcode, char *HdrStr, int force)
4700{
4701	/* force header is to help auto-output function */
4702	/* last_rptcode is to determine whether to print a header */
4703	/* for the first occurrence of a series of reports */
4704	static int
4705		last_rptcode = 0;
4706   int
4707   	numchars;
4708
4709   numchars = 0;
4710	if (force || rptcode!=last_rptcode)
4711   {
4712		/* supply a header in console output */
4713   	switch (rptcode)
4714		{
4715		case 0x5A:
4716			numchars = sprintf(HdrStr, "\nRaw Measurement Data");
4717			numchars += sprintf(HdrStr+numchars,
4718      		"\n   SV  Sample   SNR  Code Phase   Doppler    Seconds     Time of Meas");
4719			break;
4720
4721		case 0x5B:
4722			numchars = sprintf(HdrStr, "\nEphemeris Status");
4723			numchars += sprintf(HdrStr+numchars,
4724				"\n    SV     Time collected     Health  IODE        t oe         Fit   URA");
4725			break;
4726
4727		case 0x5C:
4728			numchars = sprintf(HdrStr, "\nTracking Info");
4729			numchars += sprintf(HdrStr+numchars,
4730   	   	"\n   SV  C Acq Eph   SNR     Time of Meas       Elev  Azim   ");
4731			break;
4732
4733      }
4734	}
4735	last_rptcode = rptcode;
4736   return (short)numchars;
4737}
4738
4739static void unknown_rpt (TSIPPKT *rpt)
4740{
4741	int i;
4742
4743	/* app-specific rpt packets */
4744	if (parsed == BADLEN_PARSE)
4745   {
4746		pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: Bad length",
4747      	rpt->code, rpt->len);
4748   }
4749	if (parsed == BADID_PARSE)
4750   {
4751		pbuf += sprintf(pbuf,
4752      	"\nTSIP report packet ID %2Xh, length %d: translation not supported",
4753   		rpt->code, rpt->len);
4754   }
4755
4756	if (parsed == BADDATA_PARSE)
4757   {
4758		pbuf += sprintf(pbuf,
4759      	"\nTSIP report packet ID %2Xh, length %d: data content incorrect",
4760   		rpt->code, rpt->len);
4761   }
4762
4763	for (i = 0; i < rpt->len; i++) {
4764		if ((i % 20) == 0) *pbuf++ = '\n';
4765		pbuf += sprintf(pbuf, " %02X", rpt->buf[i]);
4766	}
4767}
4768/**/
4769/*
4770** main subroutine, called from ProcessInputBytesWhileWaitingForKBHit()
4771*/
4772void TranslateTSIPReportToText (TSIPPKT *rpt, char *TextOutputBuffer)
4773{
4774
4775	/* pbuf is the pointer to the current location of the text output */
4776	pbuf = TextOutputBuffer;
4777
4778   /* keep track of whether the message has been successfully parsed */
4779	parsed = GOOD_PARSE;
4780
4781	/* print a header if this is the first of a series of messages */
4782	pbuf += print_msg_table_header (rpt->code, pbuf, FALSE);
4783
4784   /* process incoming TSIP report according to code */
4785	switch (rpt->code)
4786   {
4787	case 0x3D: rpt_chan_A_config (rpt); break;
4788	case 0x40: rpt_almanac_data_page (rpt); break;
4789	case 0x41: rpt_GPS_time (rpt); break;
4790	case 0x42: rpt_single_ECEF_position (rpt); break;
4791	case 0x43: rpt_single_ECEF_velocity (rpt); break;
4792	case 0x45: rpt_SW_version (rpt); break;
4793	case 0x46: rpt_rcvr_health (rpt); break;
4794	case 0x47: rpt_SNR_all_SVs (rpt); break;
4795	case 0x48: rpt_GPS_system_message (rpt); break;
4796	case 0x49: rpt_almanac_health_page (rpt); break;
4797	case 0x4A: switch (rpt->len) {
4798   	/*
4799      ** special case (=slip-up) in the TSIP protocol;
4800      ** parsing method depends on length
4801      */
4802   	case 20: rpt_single_lla_position (rpt); break;
4803      case  9: rpt_ref_alt (rpt); break;
4804		} break;
4805	case 0x4B: rpt_rcvr_id_and_status (rpt);break;
4806	case 0x4C: rpt_operating_parameters (rpt); break;
4807	case 0x4D: rpt_oscillator_offset (rpt); break;
4808	case 0x4E: rpt_GPS_time_set_response (rpt); break;
4809	case 0x4F: rpt_UTC_offset (rpt); break;
4810   case 0x54: rpt_1SV_bias (rpt); break;
4811	case 0x55: rpt_io_opt (rpt); break;
4812	case 0x56: rpt_ENU_velocity (rpt); break;
4813	case 0x57: rpt_last_fix_info (rpt); break;
4814	case 0x58: rpt_GPS_system_data (rpt); break;
4815	case 0x59: rpt_SVs_enabled (rpt); break;
4816	case 0x5A: rpt_raw_msmt (rpt); break;
4817	case 0x5B: rpt_SV_ephemeris_status (rpt); break;
4818	case 0x5C: rpt_SV_tracking_status (rpt); break;
4819	case 0x6D: rpt_allSV_selection (rpt); break;
4820	case 0x82: rpt_DGPS_position_mode (rpt); break;
4821	case 0x83: rpt_double_ECEF_position (rpt); break;
4822	case 0x84: rpt_double_lla_position (rpt); break;
4823	case 0xBB: rpt_complete_rcvr_config (rpt); break;
4824	case 0xBC: rpt_rcvr_serial_port_config (rpt); break;
4825
4826	case 0x8F: switch (rpt->buf[0])
4827   	{
4828      /* superpackets; parsed according to subcodes */
4829      case 0x0B: rpt_8F0B(rpt); break;
4830      case 0x14: rpt_8F14(rpt); break;
4831      case 0x15: rpt_8F15(rpt); break;
4832		case 0x20: rpt_8F20(rpt); break;
4833      case 0x41: rpt_8F41(rpt); break;
4834      case 0x42: rpt_8F42(rpt); break;
4835      case 0x45: rpt_8F45(rpt); break;
4836      case 0x4A: rpt_8F4A(rpt); break;
4837      case 0x4B: rpt_8F4B(rpt); break;
4838      case 0x4D: rpt_8F4D(rpt); break;
4839      case 0xA5: rpt_8FA5(rpt); break;
4840 	   case 0xAD: rpt_8FAD(rpt); break;
4841		default: parsed = BADID_PARSE; break;
4842		}
4843		break;
4844
4845	default: parsed = BADID_PARSE; break;
4846	}
4847
4848	if (parsed != GOOD_PARSE)
4849	{
4850	   /*
4851   	**The message has TSIP structure (DLEs, etc.)
4852	   ** but could not be parsed by above routines
4853   	*/
4854		unknown_rpt (rpt);
4855	}
4856
4857   /* close TextOutputBuffer */
4858   pbuf = '\0';
4859}
4860
4861#endif /* TRIMBLE_OUTPUT_FUNC */
4862
4863#else  /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
4864int refclock_ripencc_bs;
4865#endif /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
4866
4867