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