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