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