/*
 * *************************************************************************
 *
 * Trimble Navigation, Ltd.
 * OEM Products Development Group
 * P.O. Box 3642
 * 645 North Mary Avenue
 * Sunnyvale, California 94088-3642
 *
 * Corporate Headquarter:
 *    Telephone:  (408) 481-8000
 *    Fax:        (408) 481-6005
 *
 * Technical Support Center:
 *    Telephone:  (800) 767-4822	(U.S. and Canada)
 *                (408) 481-6940    (outside U.S. and Canada)
 *    Fax:        (408) 481-6020
 *    BBS:        (408) 481-7800
 *    e-mail:     trimble_support@trimble.com
 *
 * *************************************************************************
 *
 * Vers	Date		   Changes									    Author
 * ----	---------   ----------------------------------------	   ----------
 * 1.40              initial
 * 5a10  18 jul 97   matched 5.10 manual                             pvwl
 *
 * *************************************************************************
 *
 * TSIP_IFC.C carries several interface functions observed as primitive
 * functions that perform two specific tasks with the receiver:  prepare
 * commands to be transferred to the receiver and interpret reports received
 * from the receiver.  The module can be used as is, or with minimal changes
 * if there is a need to create a TSIP communications application separate
 * from TSIPCHAT. (The construction of most argument lists do not use
 * structures, but the developer is encouraged to reconstruct them using such
 * definitions to meet the project requirements.)  Declarations of TSIP_IFC.C
 * functions are included in TSIP_IFC.H to provide prototyping definitions.
 * Two serial port primitives defined in TSIPCHAT.C, getb() and sendb(), are
 * required for these functions.  (Note: getb() is no longer used in this file.
 * sendb() is still required.)
 *
 * 1) Command Functions:  Command functions come in the form cmd_0x??().
 * Command functions handle data/command packets to be transferred to the
 * receiver.  The function send_cmd() is called by all packet interpreters.
 * The function send_cmd() supplies the DLE stuffing to a command string and
 * then sends the command to the serial port using the primitive function
 * sendb().
 *
 * The functions cmd_0x??() are command string generators patterned after
 * the document called "Trimble Standard Interface Protocol" listed in
 * Appendix A of the "System Designer Reference Guide."  These functions
 * are called from a command input parser such as the function proc_kbd()
 * in TSIP_CMD.C.  Because a TSIP command code may have different argument
 * lists depending on optional operating modes, there are more functions
 * than command command codes.  Each cmd_0x??() function corresponds to a
 * different functional mode for the command.  The following suffix convention
 * allows each function to serve a unique purpose and cleans up the argument
 * somewhat.
 *
 *           cmd_0x??q   -  query for current value
 * 	         cmd_0x??c   -  clear value (0x1D, 0x73 only)
 * 	         cmd_0x??s   -  set new values
 * 	         cmd_0x??e   -  enable
 * 	         cmd_0x??d   -  disable
 *
 * 2) Report Functions:  Report functions come in the form rpt_0x??().
 * Report functions take care of the incoming packets from the receiver.
 * There is only one function that is used in the main functions in TCHAT.C,
 * TSIPCHAT.C, and TSIPPRNT.C:
 *                            tsip_input_proc().
 *
 * The function tsip_input_proc() accumulates bytes from the receiver,
 * strips control bytes (DLE), and checks if the report end sequence (DLE ETX)
 * has been received.  rpt.status is defined as TSIP_PARSED_FULL (== 1)
 * if a complete packet is available.
 *
 * The functions rpt_0x??() are report string interpreters patterned after
 * the document called "Trimble Standard Interface Protocol", and are called
 * by the report output functio rpt_packet() in TSIP_RPT.C.  It should be
 * noted that if the report buffer is sent into the receiver with the wrong
 * length (byte count), the rpt_0x??() returns the Boolean equivalence for
 * TRUE.
 *
 * *************************************************************************
 *
 */

#include <string.h>
#include <stdio.h>
#include "tsip_ifc.h"

/* for DOS machines, reverse order of bytes as they come through the
 * serial port. */
#define BYTESWAP

/* the following two routines give the itme of the most recent fix */
static double
	most_recent_fix_time = 0.0;

double get_tsip_time (void)
{
	return most_recent_fix_time;
}

static void set_tsip_time (double time_of_week, short week_num) {
	if (!week_num) {
		if (most_recent_fix_time == 0.0) return;
		week_num = (short)((most_recent_fix_time - time_of_week) / WEEK + 0.5);
	}
	most_recent_fix_time = time_of_week + week_num*WEEK;
}

/**/
void send_cmd (TSIPPKT *cmd)
/* formats a command for sending to TSIP receiver */
{
	short
		i;

	sendb (DLE);
	sendb (cmd->code);
	for (i = 0; i < cmd->cnt; i++) {
		if (cmd->buf[i] == DLE) sendb (DLE);
		sendb (cmd->buf[i]);
	}
	sendb (DLE);
	sendb (ETX);
}

void bputsingle (float a, unsigned char *cmdbuf)
/* puts a float in a command string */
{
#ifdef BYTESWAP
	union {unsigned char bd[4]; float fv;} uval;

	uval.fv = a;
	*cmdbuf++ = uval.bd[3];
	*cmdbuf++ = uval.bd[2];
	*cmdbuf++ = uval.bd[1];
	*cmdbuf++ = uval.bd[0];
#else
	*(float*) cmdbuf = a;
#endif
}
void bputshort (short a, unsigned char *cmdbuf)
/* puts a float in a command string */
{
#ifdef BYTESWAP
	union {unsigned char bd[2]; short sv;} uval;

	uval.sv = a;
	*cmdbuf++ = uval.bd[1];
	*cmdbuf++ = uval.bd[0];
#else
	*(short*) cmdbuf = a;
#endif
}
/**/
void cmd_0x1Dc (void)
/* clear oscillator offset */
{
	TSIPPKT cmd;
	cmd.buf[0] = 0x43;
	cmd.cnt = 1;
	cmd.code = 0x1D;
	send_cmd (&cmd);
}

void cmd_0x1Ds (
	float offset)
/* set oscillator offset */
{
	TSIPPKT cmd;
	bputsingle (offset, &cmd.buf[0]);
	cmd.cnt = 4;
	cmd.code = 0x1D;
	send_cmd (&cmd);
}

void cmd_0x1E  (unsigned char reset_type)
/* clear battery back-up, then reset */
{
	TSIPPKT cmd;
	cmd.buf[0] = reset_type;
	cmd.cnt = 1;
	cmd.code = 0x1E;
	send_cmd (&cmd);
}

void cmd_0x1F (void)
/* request software versions */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x1F;
	send_cmd (&cmd);
}

/**/
void cmd_0x20 (unsigned char sv_prn)
/* request almanac */	
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x20;
	send_cmd (&cmd);
}

void cmd_0x21 (void)
/* request current time */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x21;
	send_cmd (&cmd);
}

void cmd_0x22 (unsigned char nav_mode)
/* select position fix mode (2D, 3D, ...) */
{
	TSIPPKT cmd;
	cmd.buf[0] = nav_mode;
	cmd.cnt = 1;
	cmd.code = 0x22;
	send_cmd (&cmd);
}

void cmd_0x23 (float pos_ECEF[3])
/* initial position in ECEF coordinates */	
{
	TSIPPKT cmd;
	bputsingle (pos_ECEF[0], &cmd.buf[0]);
	bputsingle (pos_ECEF[1], &cmd.buf[4]);
	bputsingle (pos_ECEF[2], &cmd.buf[8]);
	cmd.cnt = 12;
	cmd.code = 0x23;
	send_cmd (&cmd);
}

void cmd_0x24 (void)
/* request position fix mode */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x24;
	send_cmd (&cmd);
}

void cmd_0x25 (void)
/* initiate soft reset & self-test (equivalent to power cycle) */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x25;
	send_cmd (&cmd);
}

void cmd_0x26 (void)
/* request receiver health */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x26;
	send_cmd (&cmd);
}

void cmd_0x27 (void)
/* request signal levels */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x27;
	send_cmd (&cmd);
}

void cmd_0x28 (void)
/* request GPS system message */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x28;
	send_cmd (&cmd);
}

void cmd_0x29 (void)
/* request almanac health page */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x29;
	send_cmd (&cmd);
}

void cmd_0x2A (float alt)
/* altitude for 2-D mode */	
{
	TSIPPKT cmd;
	bputsingle (alt, &cmd.buf[0]);
	cmd.cnt = 4;
	cmd.code = 0x2A;
	send_cmd (&cmd);
}

void cmd_0x2Ad (void)
/* disable altitude for 2-D mode */	
{
	TSIPPKT cmd;
	cmd.buf[0] = 0xFF;
	cmd.cnt = 1;
	cmd.code = 0x2A;
	send_cmd (&cmd);
}

void cmd_0x2B (float lat, float lon, float alt)
/* initial position (latitude-longitude-altitude) */	
{
	TSIPPKT cmd;
	bputsingle (lat, &cmd.buf[0]);
	bputsingle (lon, &cmd.buf[4]);
	bputsingle (alt, &cmd.buf[8]);
	cmd.cnt = 12;
	cmd.code = 0x2B;
	send_cmd (&cmd);
}

void cmd_0x2Cq (void)
/* request operating parameters and masks */
{
	TSIPPKT cmd;
	cmd.buf[0] = 0;
	bputsingle (-1.0F, &cmd.buf[1]);
	bputsingle (-1.0F, &cmd.buf[5]);
	bputsingle (-1.0F, &cmd.buf[9]);
	bputsingle (-1.0F, &cmd.buf[13]);
	cmd.cnt = 17;
	cmd.code = 0x2C;
	send_cmd (&cmd);
}

void cmd_0x2Cs (unsigned char dyn_code,
	float elev_mask, float snr, 
	float dop_mask, float dop_switch)
/* set operating parameters and masks */
{
	TSIPPKT cmd;
	cmd.buf[0] = dyn_code;
	bputsingle (elev_mask, &cmd.buf[1]);
	bputsingle (snr, &cmd.buf[5]);
	bputsingle (dop_mask, &cmd.buf[9]);
	bputsingle (dop_switch, &cmd.buf[13]);
	cmd.cnt = 17;
	cmd.code = 0x2C;
	send_cmd (&cmd);
}

void cmd_0x2D (void)
/* request oscillator offset */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x2D;
	send_cmd (&cmd);
}

void cmd_0x2E (float time_of_week, short week_num)
/* set GPS time */	
{
	TSIPPKT cmd;
	bputsingle (time_of_week, &cmd.buf[0]);
	bputshort (week_num, &cmd.buf[4]);
	cmd.cnt = 6;
	cmd.code = 0x2E;
	send_cmd (&cmd);
}

void cmd_0x2F (void)
/* request UTC parameters */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x2F;
	send_cmd (&cmd);
}

/**/
void cmd_0x31 (float ECEF_pos[3])
/* initial accurate position in ECEF coordinates */	
{
	TSIPPKT cmd;
	bputsingle (ECEF_pos[0], &cmd.buf[0]);
	bputsingle (ECEF_pos[1], &cmd.buf[4]);
	bputsingle (ECEF_pos[2], &cmd.buf[8]);
	cmd.cnt = 12;
	cmd.code = 0x31;
	send_cmd (&cmd);
}

void cmd_0x32 (float lat, float lon, float alt)
/* initial accurate position in latitude-longitude-altitude */	
{
	TSIPPKT cmd;
	bputsingle (lat, &cmd.buf[0]);
	bputsingle (lon, &cmd.buf[4]);
	bputsingle (alt, &cmd.buf[8]);
	cmd.cnt = 12;
	cmd.code = 0x32;
	send_cmd (&cmd);
}

void cmd_0x34 (unsigned char sv_prn)
/* choose satellite for 1-SV mode */
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x34;
	send_cmd (&cmd);
}

void cmd_0x35q (void)
/* request serial I/O options */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x35;
	send_cmd (&cmd);
}

void cmd_0x35s (unsigned char pos_code, unsigned char vel_code, 
	unsigned char time_code, unsigned char opts_code)
/* set serial I/O options */	
{
	TSIPPKT cmd;
	cmd.buf[0] = pos_code;
	cmd.buf[1] = vel_code;
	cmd.buf[2] = time_code;
	cmd.buf[3] = opts_code;
	cmd.cnt = 4;
	cmd.code = 0x35;
	send_cmd (&cmd);
}

void cmd_0x36 (unsigned char coord_frame, unsigned char enable, float vel[3])
/* set velocity for acquisition aiding */	
{
	TSIPPKT cmd;
	cmd.buf[0] = coord_frame;	/* 0=ECEF, 1=ENU */
	cmd.buf[1] = enable;
	bputsingle (vel[0], &cmd.buf[2]);
	bputsingle (vel[1], &cmd.buf[6]);
	bputsingle (vel[2], &cmd.buf[10]);
	cmd.cnt = 14;
	cmd.code = 0x36;
	send_cmd (&cmd);
}

void cmd_0x37 (void)
/* request last position, velocity, and status */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x37;
	send_cmd (&cmd);
}

void cmd_0x38q (unsigned char data_type, unsigned char sv_prn)
/* request GPS system data (binary) */
{
	TSIPPKT cmd;
	cmd.buf[0] = 1;
	cmd.buf[1] = data_type;
	cmd.buf[2] = sv_prn;
	cmd.cnt = 3;
	cmd.code = 0x38;
	send_cmd (&cmd);
}

void cmd_0x38s (unsigned char data_type, unsigned char sv_prn,
	unsigned char data_length, unsigned char *databuf)
/* load GPS system data (binary) */	
{
	TSIPPKT cmd;
	cmd.buf[0] = 2;
	cmd.buf[1] = data_type;
	cmd.buf[2] = sv_prn;
	cmd.buf[3] = data_length;
	memcpy (&cmd.buf[4], databuf, (short)data_length);
	cmd.cnt = (short)data_length + 4;
	cmd.code = 0x38;
	send_cmd (&cmd);
}

void cmd_0x39 (unsigned char op_code, unsigned char sv_prn)
/* set or request enable/health-heed status of satellites */	
{
	TSIPPKT cmd;
	cmd.buf[0] = op_code;
	cmd.buf[1] = sv_prn;
	cmd.cnt = 2;
	cmd.code = 0x39;
	send_cmd (&cmd);
}

void cmd_0x3A (unsigned char sv_prn)
/* request last code-phase/Doppler measurement */	
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x3A;
	send_cmd (&cmd);
}

void cmd_0x3B (unsigned char sv_prn)
/* request satellite epehmeris status */	
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x3B;
	send_cmd (&cmd);
}

void cmd_0x3C (unsigned char sv_prn)
/* request tracking status */	
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x3C;
	send_cmd (&cmd);
}

void cmd_0x3Dq (void)
/* request Channel A configuration for dual-port operation */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x3D;
	send_cmd (&cmd);
}

void cmd_0x3Ds (unsigned char baud_out,
	unsigned char baud_inp,
	unsigned char char_code,
	unsigned char stopbitcode,
	unsigned char output_mode,
	unsigned char input_mode)
/* set Channel A configuration for dual-port operation */
{
	TSIPPKT cmd;
	cmd.buf[0] = baud_out;		/* XMT baud rate */
	cmd.buf[1] = baud_inp;		/* RCV baud rate */
	cmd.buf[2] = char_code;	   /* parity and #bits per unsigned char */
	cmd.buf[3] = stopbitcode;	/* number of stop bits code */
	cmd.buf[4] = output_mode;	/* Ch. A transmission mode */
	cmd.buf[5] = input_mode;	/* Ch. A reception mode */
	cmd.cnt = 6;
	cmd.code = 0x3D;
	send_cmd (&cmd);
}

void cmd_0x3E (void)
/* request auxiliary fix parameters */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x3E;
	send_cmd (&cmd);
}

/**/
void cmd_0x62q (void)
/* query DGPS fix mode */
{
	TSIPPKT cmd;
	cmd.buf[0] = 4;
	cmd.cnt = 1;
	cmd.code = 0x62;
	send_cmd (&cmd);
}

void cmd_0x62s (unsigned char dgps_mode)
/* set DGPS fix mode */
{
	TSIPPKT cmd;
	cmd.buf[0] = dgps_mode;
	cmd.cnt = 1;
	cmd.code = 0x62;
	send_cmd (&cmd);
}

void cmd_0x65 (unsigned char sv_prn)
/* request satellite differential correction info */
{
	TSIPPKT cmd;
	cmd.buf[0] = sv_prn;
	cmd.cnt = 1;
	cmd.code = 0x65;
	send_cmd (&cmd);
}

/**/
void cmd_0x71q (void)
/* request position-velocity filter parameters */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x71;
	send_cmd (&cmd);
}

void cmd_0x71s (unsigned char pf,
	TSIP_POS_FILT_PARMS *params)
/* set position-velocity filter parameters */
{
	TSIPPKT cmd;
	cmd.buf[0] = pf;
	bputsingle (params->flt1, &cmd.buf[1]);
	cmd.buf[5] = params->chr1;
	cmd.buf[6] = params->chr2;
	cmd.buf[7] = params->chr3;
	cmd.buf[8] = params->chr4;
	bputsingle (params->flt2, &cmd.buf[9]);
	bputsingle (params->flt3, &cmd.buf[13]);
	bputsingle (params->flt4, &cmd.buf[17]);
	bputsingle (params->flt5, &cmd.buf[21]);
	cmd.buf[25] = params->chr5;
	cmd.cnt = 26;
	cmd.code = 0x71;
	send_cmd (&cmd);
}

void cmd_0x73q (void)
/* request altitude filter parameters */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x73;
	send_cmd (&cmd);
}

void cmd_0x73s (float time_constant)
/* set altitude filter parameters */
{
	TSIPPKT cmd;
	bputsingle (time_constant, &cmd.buf[0]);
	cmd.cnt = 4;
	cmd.code = 0x73;
	send_cmd (&cmd);
}

void cmd_0x73d (void)
/* disable altitude filter */
{
	TSIPPKT cmd;
	bputsingle (1.0F, &cmd.buf[0]);
	cmd.cnt = 4;
	cmd.code = 0x73;
	send_cmd (&cmd);
}

void cmd_0x73c (void)
/* set altitude filter parameters to default */
{
	TSIPPKT cmd;
	bputsingle (0.0F, &cmd.buf[0]);
	cmd.cnt = 4;
	cmd.code = 0x73;
	send_cmd (&cmd);
}

void cmd_0x75q (void)
/* request track and fix mode status */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x75;
	send_cmd (&cmd);
}

void cmd_0x75s (unsigned char trackmode)
/* set track and fix mode */
{
	TSIPPKT cmd;
	cmd.buf[0] = trackmode;
	cmd.cnt = 1;
	cmd.code = 0x75;
	send_cmd (&cmd);
}

void cmd_0x77q (void)
/* request DC max age */
{
	TSIPPKT cmd;
	cmd.cnt = 0;
	cmd.code = 0x77;
	send_cmd (&cmd);
}

void cmd_0x77s (short maxage)
/* request DC max age */
{
	TSIPPKT cmd;
	bputshort (maxage, &cmd.buf[0]);
	cmd.cnt = 2;
	cmd.code = 0x77;
	send_cmd (&cmd);
}

void cmd_0x8E03q (void)
/* set Channel B configuration for dual-port operation */
{
   TSIPPKT cmd;

	cmd.buf[0] = 3;
	cmd.cnt = 1;
	cmd.code = 0x8E;
	send_cmd (&cmd);
}

void cmd_0x8E03s (unsigned char baud_out, unsigned char baud_inp,
   unsigned char char_code, unsigned char stopbitcode,
   unsigned char output_mode, unsigned char input_mode)
/* set Channel B configuration for dual-port operation */
{
   TSIPPKT cmd;

	cmd.buf[0] = 3;
	cmd.buf[1] = baud_out;
	cmd.buf[2] = baud_inp;
	cmd.buf[3] = char_code;
	cmd.buf[4] = stopbitcode;
	cmd.buf[5] = output_mode;
	cmd.buf[6] = input_mode;
	cmd.cnt = 7;
	cmd.code = 0x8E;
	send_cmd (&cmd);
}

void cmd_0x8E14q (void)
/* datum query */
{
   TSIPPKT cmd;

	cmd.cnt = 1;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x14;
	send_cmd (&cmd);
}

void cmd_0x8E14s (unsigned char datum_index)
/* datum set */
{
   TSIPPKT cmd;

	cmd.cnt = 3;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x14;
	cmd.buf[1] = 0;	/* datum index is a short; this is always 0 */
	cmd.buf[2] = datum_index;
	send_cmd (&cmd);
}

void cmd_0x8E15q (void)
/* datum query */
{
	cmd_0x8E14q();
}

void cmd_0x8E15s (unsigned char datum_index)
/* datum set */
{
	cmd_0x8E14s (datum_index);
}

void cmd_0x8E17d (void)
/* UTM SP msg auto-output disable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x17;
	cmd.buf[1] = 0;
	send_cmd (&cmd);
}

void cmd_0x8E17e (void)
/* UTM SP msg auto-output enable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x17;
	cmd.buf[1] = 1;
	send_cmd (&cmd);
}

void cmd_0x8E18q (void)
/* UTM SP msg query */
{
   TSIPPKT cmd;

	cmd.cnt = 1;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x18;
	send_cmd (&cmd);
}

void cmd_0x8E18d (void)
/* UTM SP msg auto-output disable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x18;
	cmd.buf[1] = 0;
	send_cmd (&cmd);
}

void cmd_0x8E18e (void)
/* UTM SP msg auto-output enable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x18;
	cmd.buf[1] = 1;
	send_cmd (&cmd);
}

void cmd_0x8E19q (void)
/* UTM msg enable/disable/status */
{
   TSIPPKT cmd;

	cmd.cnt = 1;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x19;
	send_cmd (&cmd);
}

void cmd_0x8E19s (short cmd_code)
/* UTM msg enable/disable/status */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x19;
	cmd.buf[1] = (unsigned char)cmd_code;
	send_cmd (&cmd);
}

void cmd_0x8E20q (void)
/* UTM SP msg query */
{
   TSIPPKT cmd;

	cmd.cnt = 1;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x20;
	send_cmd (&cmd);
}

void cmd_0x8E20d (void)
/* UTM SP msg auto-output disable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x20;
	cmd.buf[1] = 0;
	send_cmd (&cmd);
}

void cmd_0x8E20e (void)
/* UTM SP msg auto-output enable */
{
   TSIPPKT cmd;

	cmd.cnt = 2;
	cmd.code = 0x8E;
	cmd.buf[0] = 0x20;
	cmd.buf[1] = 1;
	send_cmd (&cmd);
}

void cmd_0x8E (unsigned char hexbyte[], unsigned char hexnum)
/* turn on superpacket, etc. */
{
	TSIPPKT cmd;
	memcpy (cmd.buf, hexbyte, (short)hexnum);
	cmd.cnt = hexnum;
	cmd.code = 0x8E;
	send_cmd (&cmd);
}

void cmd_0xxx (unsigned char hexcode, unsigned char hexbyte[], unsigned char hexnum)
/* turn on superpacket, etc. */
{
	TSIPPKT cmd;
	memcpy (cmd.buf, hexbyte, (short)hexnum);
	cmd.cnt = hexnum;
	cmd.code = hexcode;
	send_cmd (&cmd);
}

/**/
void tsip_input_proc (TSIPPKT *rpt, short inbyte)
/* reads bytes until serial buffer is empty or a complete report
 * has been received; end of report is signified by DLE ETX.
 */
{                 
	unsigned char newbyte;

	if (inbyte & 0xFF00) return;

	newbyte = (unsigned char)(inbyte & 0x00FF);
	switch (rpt->status)
	{
	case TSIP_PARSED_DLE_1:
		switch (newbyte)
		{
		case 0:
		case DLE:
		case ETX:
			rpt->status = TSIP_PARSED_EMPTY;
			break;
		default:
			rpt->status = TSIP_PARSED_DATA;
			rpt->code = newbyte;
			break;
		}
		break;
	case TSIP_PARSED_DATA:
		switch (newbyte) {
		case DLE:
			rpt->status = TSIP_PARSED_DLE_2;
			break;
		default:
			rpt->buf[rpt->cnt++] = newbyte;
			break;
		}
		break;
	case TSIP_PARSED_DLE_2:
		switch (newbyte) {
		case DLE:
			rpt->status = TSIP_PARSED_DATA;
			rpt->buf[rpt->cnt++] = newbyte;
			break;
		case ETX:
			/* end of message; return TRUE here. */
			rpt->status = TSIP_PARSED_FULL;
			break;
		default:
			/* error: start new report packet */
			rpt->status = TSIP_PARSED_DLE_1;
			rpt->code = newbyte;
		}
		break;
	case TSIP_PARSED_FULL:
	case TSIP_PARSED_EMPTY:
	default:
		rpt->status = (newbyte==DLE) ? TSIP_PARSED_DLE_1 : TSIP_PARSED_EMPTY;
		break;
	}
	if (rpt->cnt > MAX_RPTBUF) {
		/* error: start new report packet */
		rpt->status = TSIP_PARSED_EMPTY;
	}
	if (rpt->status==TSIP_PARSED_EMPTY || rpt->status==TSIP_PARSED_DLE_1)
	{
		rpt->cnt = 0;
	}
}

short bgetint (unsigned char *bp)
/* gets an integer from the report string */
{
#ifdef BYTESWAP
	union {
		unsigned char    bd[2];
		short     iv;
	}  uval;

	uval.bd[1] = *(bp++);
	uval.bd[0] = *bp;
	return uval.iv;
#else
	return *(short*) bp;
#endif
}

long bgetlong (unsigned char *bp)
/* gets an integer from the report string */
{
#ifdef BYTESWAP
	union {
		unsigned char    bd[4];
		long    iv;
	}  uval;

	uval.bd[3] = *(bp++);
	uval.bd[2] = *(bp++);
	uval.bd[1] = *(bp++);
	uval.bd[0] = *bp;
	return uval.iv;
#else
	return *(long*) bp;
#endif
}

unsigned long bgetulong (unsigned char *bp)
/* gets an integer from the report string */
{
#ifdef BYTESWAP
	union {
		unsigned char    bd[4];
		unsigned long    iv;
	}  uval;

	uval.bd[3] = *(bp++);
	uval.bd[2] = *(bp++);
	uval.bd[1] = *(bp++);
	uval.bd[0] = *bp;
	return uval.iv;
#else
	return *(unsigned long*) bp;
#endif
}

float bgetsingle (unsigned char *bp)
/* gets a float from the report string */
{
#ifdef BYTESWAP
	union {
		unsigned char    bd[4];
		float    fv;
	}  uval;

	uval.bd[3] = *(bp++);
	uval.bd[2] = *(bp++);
	uval.bd[1] = *(bp++);
	uval.bd[0] = *bp;
	return uval.fv;
#else
	return *(float*) bp;
#endif
}

double bgetdouble (unsigned char *bp)
/* gets a double from the report string */
{
#ifdef BYTESWAP
	union {
		unsigned char  bd[8];
		double  dv;
	}  uval;

	uval.bd[7] = *(bp++);
	uval.bd[6] = *(bp++);
	uval.bd[5] = *(bp++);
	uval.bd[4] = *(bp++);
	uval.bd[3] = *(bp++);
	uval.bd[2] = *(bp++);
	uval.bd[1] = *(bp++);
	uval.bd[0] = *bp;
	return uval.dv;
#else
	return *(double*) bp;
#endif
}
/**/
short rpt_0x3D (TSIPPKT *rpt,
	unsigned char *tx_baud_index,
	unsigned char *rx_baud_index,
	unsigned char *char_format_index,
	unsigned char *stop_bits,
	unsigned char *tx_mode_index,
	unsigned char *rx_mode_index)
/* Channel A configuration for dual port operation */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 6) return TRUE;
	*tx_baud_index = buf[0];
	*rx_baud_index = buf[1];
	*char_format_index = buf[2];
	*stop_bits = (buf[3] == 0x07) ? 1 : 2;
	*tx_mode_index = buf[4];
	*rx_mode_index = buf[5];
	return FALSE;
}

/**/
short rpt_0x40 (TSIPPKT *rpt,
	unsigned char *sv_prn,
	short *week_num,
	float *t_zc,
	float *eccentricity,
	float *t_oa,
	float *i_0,
	float *OMEGA_dot,
	float *sqrt_A,
	float *OMEGA_0,
	float *omega,
	float *M_0)
/* almanac data for specified satellite */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 39) return TRUE;
	*sv_prn = buf[0];
	*t_zc = bgetsingle (&buf[1]);
	*week_num = bgetint (&buf[5]);
	*eccentricity = bgetsingle (&buf[7]);
	*t_oa = bgetsingle (&buf[11]);
	*i_0 = bgetsingle (&buf[15]);
	*OMEGA_dot = bgetsingle (&buf[19]);
	*sqrt_A = bgetsingle (&buf[23]);
	*OMEGA_0 = bgetsingle (&buf[27]);
	*omega = bgetsingle (&buf[31]);
	*M_0 = bgetsingle (&buf[35]);
	return FALSE;
}

short rpt_0x41 (TSIPPKT *rpt,
	float *time_of_week,
	float *UTC_offset,
	short *week_num)
/* GPS time */
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 10) return TRUE;
	*time_of_week = bgetsingle (buf);
	*week_num = bgetint (&buf[4]);
	*UTC_offset = bgetsingle (&buf[6]);
	set_tsip_time (*time_of_week, *week_num);
	return FALSE;
}

short rpt_0x42 (TSIPPKT *rpt,
	float pos_ECEF[3],
	float *time_of_fix)
/* position in ECEF, single precision */
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 16) return TRUE;
	pos_ECEF[0] = bgetsingle (buf);
	pos_ECEF[1]= bgetsingle (&buf[4]);
	pos_ECEF[2]= bgetsingle (&buf[8]);
	*time_of_fix = bgetsingle (&buf[12]);
	set_tsip_time (*time_of_fix, 0);
	return FALSE;
}

short rpt_0x43 (TSIPPKT *rpt,
	float ECEF_vel[3],
	float *freq_offset,
	float *time_of_fix)
/* velocity in ECEF, single precision */
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 20) return TRUE;
	ECEF_vel[0] = bgetsingle (buf);
	ECEF_vel[1] = bgetsingle (&buf[4]);
	ECEF_vel[2] = bgetsingle (&buf[8]);
	*freq_offset = bgetsingle (&buf[12]);
	*time_of_fix = bgetsingle (&buf[16]);
	return FALSE;
}

short rpt_0x44 (TSIPPKT *rpt,
	unsigned char *manual_mode,
	unsigned char *nsvs,
	unsigned char sv_prn[4],
	float *pdop,
	float *hdop,
	float *vdop,
	float *tdop)
/* best-4 satellite selection for position fixes, PDOP, fix mode */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 21) return TRUE;
	*manual_mode = buf[0] & 0x10;
	*nsvs = buf[0] & 0x07;
	sv_prn[0] = buf[1];
	sv_prn[1] = buf[2];
	sv_prn[2] = buf[3];
	sv_prn[3] = buf[4];
	*pdop = bgetsingle (&buf[5]);
	*hdop = bgetsingle (&buf[9]);
	*vdop = bgetsingle (&buf[13]);
	*tdop = bgetsingle (&buf[17]);
	return FALSE;
}

short rpt_0x45 (TSIPPKT *rpt,
	unsigned char *major_nav_version,
	unsigned char *minor_nav_version,
	unsigned char *nav_day,
	unsigned char *nav_month,
	unsigned char *nav_year,
	unsigned char *major_dsp_version,
	unsigned char *minor_dsp_version,
	unsigned char *dsp_day,
	unsigned char *dsp_month,
	unsigned char *dsp_year)
/* software versions */	
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 10) return TRUE;
	*major_nav_version = buf[0];
	*minor_nav_version = buf[1];
	*nav_day = buf[2];
	*nav_month = buf[3];
	*nav_year = buf[4];
	*major_dsp_version = buf[5];
	*minor_dsp_version = buf[6];
	*dsp_day = buf[7];
	*dsp_month = buf[8];
	*dsp_year = buf[9];
	return FALSE;
}
 
short rpt_0x46 (TSIPPKT *rpt,
	unsigned char *status_code,
	unsigned char *error_code)
/* receiver health and status */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 2) return TRUE;
	*status_code = buf[0];
	*error_code = buf[1];
	return FALSE;
}

short rpt_0x47 (TSIPPKT *rpt,
	unsigned char *nsvs, unsigned char *sv_prn,
	float *snr)
/* signal levels for all satellites tracked */
{
	short isv;
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 1 + 5*buf[0]) return TRUE;
	*nsvs = buf[0];
	for (isv = 0; isv < (*nsvs); isv++) {
		sv_prn[isv] = buf[5*isv + 1];
		snr[isv] = bgetsingle (&buf[5*isv + 2]);
	}
	return FALSE;
}

short rpt_0x48 (TSIPPKT *rpt,
	unsigned char *message)
/* GPS system message */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 22) return TRUE;
	memcpy (message, buf, 22);
	message[22] = 0;
	return FALSE;
}

short rpt_0x49 (TSIPPKT *rpt,
	unsigned char *sv_health)
/* health for all satellites from almanac health page */
{
	short i;
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 32) return TRUE;
	for (i = 0; i < 32; i++) sv_health [i]= buf[i];
	return FALSE;
}

short rpt_0x4A (TSIPPKT *rpt,
	float *lat,
	float *lon,
	float *alt,
	float *clock_bias,
	float *time_of_fix)
/* position in lat-lon-alt, single precision */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 20) return TRUE;
	*lat = bgetsingle (buf);
	*lon = bgetsingle (&buf[4]);
	*alt = bgetsingle (&buf[8]);
	*clock_bias = bgetsingle (&buf[12]);
	*time_of_fix = bgetsingle (&buf[16]);
	set_tsip_time (*time_of_fix, 0);
	return FALSE;
}

short rpt_0x4B (TSIPPKT *rpt,
	unsigned char *machine_id,
	unsigned char *status_1,
	unsigned char *status_2)
/* machine ID code, status */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 3) return TRUE;
	*machine_id = buf[0];
	*status_1 = buf[1];
	*status_2 = buf[2];
	return FALSE;
}

short rpt_0x4C (TSIPPKT *rpt,
	unsigned char *dyn_code,
	float *el_mask,
	float *snr_mask,
	float *dop_mask,
	float *dop_switch)
/* operating parameters and masks */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 17) return TRUE;
	*dyn_code = buf[0];
	*el_mask = bgetsingle (&buf[1]);
	*snr_mask = bgetsingle (&buf[5]);
	*dop_mask = bgetsingle (&buf[9]);
	*dop_switch = bgetsingle (&buf[13]);
	return FALSE;
}

short rpt_0x4D (TSIPPKT *rpt,
	float *osc_offset)
/* oscillator offset */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 4) return TRUE;
	*osc_offset = bgetsingle (buf);
	return FALSE;
}

short rpt_0x4E (TSIPPKT *rpt,
	unsigned char *response)
/* yes/no response to command to set GPS time */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 1) return TRUE;
	*response = buf[0];
	return FALSE;
}

short rpt_0x4F (TSIPPKT *rpt,
	double *a0,
	float *a1,
	float *time_of_data,
	short *dt_ls,
	short *wn_t,
	short *wn_lsf,
	short *dn,
	short *dt_lsf)
/* UTC data */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 26) return TRUE;
	*a0 = bgetdouble (buf);
	*a1 = bgetsingle (&buf[8]);
	*dt_ls = bgetint (&buf[12]);
	*time_of_data = bgetsingle (&buf[14]);
	*wn_t = bgetint (&buf[18]);
	*wn_lsf = bgetint (&buf[20]);
	*dn = bgetint (&buf[22]);
	*dt_lsf = bgetint (&buf[24]);
	return FALSE;
}
/**/
short rpt_0x54 (TSIPPKT *rpt,
	float *clock_bias,
	float *freq_offset,
	float *time_of_fix)
/* clock offset and frequency offset in 1-SV (0-D) mode */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 12) return TRUE;
	*clock_bias = bgetsingle (buf);
	*freq_offset = bgetsingle (&buf[4]);
	*time_of_fix = bgetsingle (&buf[8]);
	set_tsip_time (*time_of_fix, 0);
	return FALSE;
}

short rpt_0x55 (TSIPPKT *rpt,
	unsigned char *pos_code,
	unsigned char *vel_code,
	unsigned char *time_code,
	unsigned char *aux_code)
/* I/O serial options */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 4) return TRUE;
	*pos_code = buf[0];
	*vel_code = buf[1];
	*time_code = buf[2];
	*aux_code = buf[3];
	return FALSE;
}

short rpt_0x56 (TSIPPKT *rpt,
	float vel_ENU[3], float *freq_offset, float *time_of_fix)
/* velocity in east-north-up coordinates */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 20) return TRUE;
	/* east */
	vel_ENU[0] = bgetsingle (buf);
	/* north */
	vel_ENU[1] = bgetsingle (&buf[4]);
	/* up */
	vel_ENU[2] = bgetsingle (&buf[8]);
	*freq_offset = bgetsingle (&buf[12]);
	*time_of_fix = bgetsingle (&buf[16]);
	return FALSE;
}

short rpt_0x57 (TSIPPKT *rpt,
	unsigned char *source_code, unsigned char *diag_code,
	short *week_num,
	float *time_of_fix)
/* info about last computed fix */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 8) return TRUE;
	*source_code = buf[0];
	*diag_code = buf[1];
	*time_of_fix = bgetsingle (&buf[2]);
	*week_num = bgetint (&buf[6]);
	return FALSE;
}

short rpt_0x58 (TSIPPKT *rpt,
	unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn,
	unsigned char *data_length, unsigned char *data_packet)
/* GPS system data or acknowledgment of GPS system data load */
{
	unsigned char *buf, *buf4;
	short dl;
	ALM_INFO* alminfo;
	ION_INFO* ioninfo;
	UTC_INFO* utcinfo;
	NAV_INFO* navinfo;

	buf = rpt->buf;

	if (buf[0] == 2) {
		if (rpt->cnt < 4) return TRUE;
		if (rpt->cnt != 4+buf[3]) return TRUE;
	}
	else if (rpt->cnt != 3) {
		return TRUE;
	}
	*op_code = buf[0];
	*data_type = buf[1];
	*sv_prn = buf[2];
	if (*op_code == 2) {
		dl = buf[3];
		*data_length = (unsigned char)dl;
		buf4 = &buf[4];
		switch (*data_type) {
		case 2:
			/* Almanac */
			if (*data_length != sizeof (ALM_INFO)) return TRUE;
			alminfo = (ALM_INFO*)data_packet;
			alminfo->t_oa_raw  = buf4[0];
			alminfo->SV_health = buf4[1];
			alminfo->e         = bgetsingle(&buf4[2]);
			alminfo->t_oa      = bgetsingle(&buf4[6]);
			alminfo->i_0       = bgetsingle(&buf4[10]);
			alminfo->OMEGADOT  = bgetsingle(&buf4[14]);
			alminfo->sqrt_A    = bgetsingle(&buf4[18]);
			alminfo->OMEGA_0   = bgetsingle(&buf4[22]);
			alminfo->omega     = bgetsingle(&buf4[26]);
			alminfo->M_0       = bgetsingle(&buf4[30]);
			alminfo->a_f0      = bgetsingle(&buf4[34]);
			alminfo->a_f1      = bgetsingle(&buf4[38]);
			alminfo->Axis      = bgetsingle(&buf4[42]);
			alminfo->n         = bgetsingle(&buf4[46]);
			alminfo->OMEGA_n   = bgetsingle(&buf4[50]);
			alminfo->ODOT_n    = bgetsingle(&buf4[54]);
			alminfo->t_zc      = bgetsingle(&buf4[58]);
			alminfo->weeknum   = bgetint(&buf4[62]);
			alminfo->wn_oa     = bgetint(&buf4[64]);
			break;

		case 3:
			/* Almanac health page */
			if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE;

			/* this record is returned raw */
			memcpy (data_packet, buf4, dl);
			break;

		case 4:
			/* Ionosphere */
			if (*data_length != sizeof (ION_INFO) + 8) return TRUE;
			ioninfo = (ION_INFO*)data_packet;
			ioninfo->alpha_0   = bgetsingle (&buf4[8]);
			ioninfo->alpha_1   = bgetsingle (&buf4[12]);
			ioninfo->alpha_2   = bgetsingle (&buf4[16]);
			ioninfo->alpha_3   = bgetsingle (&buf4[20]);
			ioninfo->beta_0    = bgetsingle (&buf4[24]);
			ioninfo->beta_1    = bgetsingle (&buf4[28]);
			ioninfo->beta_2    = bgetsingle (&buf4[32]);
			ioninfo->beta_3    = bgetsingle (&buf4[36]);
			break;

		case 5:
			/* UTC */
			if (*data_length != sizeof (UTC_INFO) + 13) return TRUE;
			utcinfo = (UTC_INFO*)data_packet;
			utcinfo->A_0       = bgetdouble (&buf4[13]);
			utcinfo->A_1       = bgetsingle (&buf4[21]);
			utcinfo->delta_t_LS = bgetint (&buf4[25]);
			utcinfo->t_ot      = bgetsingle(&buf4[27]);
			utcinfo->WN_t      = bgetint (&buf4[31]);
			utcinfo->WN_LSF    = bgetint (&buf4[33]);
			utcinfo->DN        = bgetint (&buf4[35]);
			utcinfo->delta_t_LSF = bgetint (&buf4[37]);
			break;

		case 6:
			/* Ephemeris */
			if (*data_length != sizeof (NAV_INFO) - 1) return TRUE;

			navinfo = (NAV_INFO*)data_packet;

			navinfo->sv_number = buf4[0];
			navinfo->t_ephem = bgetsingle (&buf4[1]);
			navinfo->ephclk.weeknum = bgetint (&buf4[5]);

			navinfo->ephclk.codeL2 = buf4[7];
			navinfo->ephclk.L2Pdata = buf4[8];
			navinfo->ephclk.SVacc_raw = buf4[9];
			navinfo->ephclk.SV_health = buf4[10];
			navinfo->ephclk.IODC = bgetint (&buf4[11]);
			navinfo->ephclk.T_GD = bgetsingle (&buf4[13]);
			navinfo->ephclk.t_oc = bgetsingle (&buf4[17]);
			navinfo->ephclk.a_f2 = bgetsingle (&buf4[21]);
			navinfo->ephclk.a_f1 = bgetsingle (&buf4[25]);
			navinfo->ephclk.a_f0 = bgetsingle (&buf4[29]);
			navinfo->ephclk.SVacc = bgetsingle (&buf4[33]);

			navinfo->ephorb.IODE = buf4[37];
			navinfo->ephorb.fit_interval = buf4[38];
			navinfo->ephorb.C_rs = bgetsingle (&buf4[39]);
			navinfo->ephorb.delta_n = bgetsingle (&buf4[43]);
			navinfo->ephorb.M_0 = bgetdouble (&buf4[47]);
			navinfo->ephorb.C_uc = bgetsingle (&buf4[55]);
			navinfo->ephorb.e = bgetdouble (&buf4[59]);
			navinfo->ephorb.C_us = bgetsingle (&buf4[67]);
			navinfo->ephorb.sqrt_A = bgetdouble (&buf4[71]);
			navinfo->ephorb.t_oe = bgetsingle (&buf4[79]);
			navinfo->ephorb.C_ic = bgetsingle (&buf4[83]);
			navinfo->ephorb.OMEGA_0 = bgetdouble (&buf4[87]);
			navinfo->ephorb.C_is = bgetsingle (&buf4[95]);
			navinfo->ephorb.i_0 = bgetdouble (&buf4[99]);
			navinfo->ephorb.C_rc = bgetsingle (&buf4[107]);
			navinfo->ephorb.omega = bgetdouble (&buf4[111]);
			navinfo->ephorb.OMEGADOT=bgetsingle (&buf4[119]);
			navinfo->ephorb.IDOT = bgetsingle (&buf4[123]);
			navinfo->ephorb.Axis = bgetdouble (&buf4[127]);
			navinfo->ephorb.n = bgetdouble (&buf4[135]);
			navinfo->ephorb.r1me2 = bgetdouble (&buf4[143]);
			navinfo->ephorb.OMEGA_n=bgetdouble (&buf4[151]);
			navinfo->ephorb.ODOT_n = bgetdouble (&buf4[159]);
			break;
		}
	}
	return FALSE;
}

short rpt_0x59 (TSIPPKT *rpt,
	unsigned char *code_type,
	unsigned char status_code[32])
/* satellite enable/disable or health heed/ignore list */	
{
	short iprn;
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 33) return TRUE;
	*code_type = buf[0];
	for (iprn = 0; iprn < 32; iprn++)
		status_code[iprn] = buf[iprn + 1];
	return FALSE;
}

short rpt_0x5A (TSIPPKT *rpt,
	unsigned char *sv_prn,
	float *sample_length,
	float *signal_level,
	float *code_phase,
	float *Doppler,
	double *time_of_fix)
/* raw measurement data - code phase/Doppler */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 25) return TRUE;
	*sv_prn = buf[0];
	*sample_length = bgetsingle (&buf[1]);
	*signal_level = bgetsingle (&buf[5]);
	*code_phase = bgetsingle (&buf[9]);
	*Doppler = bgetsingle (&buf[13]);
	*time_of_fix = bgetdouble (&buf[17]);
	return FALSE;
}

short rpt_0x5B (TSIPPKT *rpt,
	unsigned char *sv_prn,
	unsigned char *sv_health,
	unsigned char *sv_iode,
	unsigned char *fit_interval_flag,
	float *time_of_collection,
	float *time_of_eph,
	float *sv_accy)
/* satellite ephorb status */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 16) return TRUE;
	*sv_prn = buf[0];
	*time_of_collection = bgetsingle (&buf[1]);
	*sv_health = buf[5];
	*sv_iode = buf[6];
	*time_of_eph = bgetsingle (&buf[7]);
	*fit_interval_flag = buf[11];
	*sv_accy = bgetsingle (&buf[12]);
	return FALSE;
}

short rpt_0x5C (TSIPPKT *rpt,
	unsigned char *sv_prn,
	unsigned char *slot,
	unsigned char *chan,
	unsigned char *acq_flag,
	unsigned char *eph_flag,
	float *signal_level,
	float *time_of_last_msmt,
	float *elev,
	float *azim,
	unsigned char *old_msmt_flag,
	unsigned char *integer_msec_flag,
	unsigned char *bad_data_flag,
	unsigned char *data_collect_flag)
/* satellite tracking status */
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 24) return TRUE;
	*sv_prn = buf[0];
	*slot = (buf[1] & 0x07) + 1;
	*chan = buf[1] >> 3;
	if (*chan == 0x10) *chan = 2;
	else (*chan)++;
	*acq_flag = buf[2];
	*eph_flag = buf[3];
	*signal_level = bgetsingle (&buf[4]);
	*time_of_last_msmt = bgetsingle (&buf[8]);
	*elev = bgetsingle (&buf[12]);
	*azim = bgetsingle (&buf[16]);
	*old_msmt_flag = buf[20];
	*integer_msec_flag = buf[21];
	*bad_data_flag = buf[22];
	*data_collect_flag = buf[23];
	return FALSE;
}

short rpt_0x5E (TSIPPKT *rpt,
	unsigned char *num_reused_msmts,
	unsigned char *diff_Doppler_unavailable,
	unsigned char *fix_still_converging,
	unsigned char *num_old_msmts)
/* auxiliary fix status */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 2) return TRUE;
	*num_reused_msmts = buf[0] & 0x07;
	*diff_Doppler_unavailable = buf[0] & 0x08;
	*fix_still_converging = buf[0] & 0x10;
	*num_old_msmts = buf[1] & 0x07;
	return FALSE;
}
/**/
short rpt_0x6D (TSIPPKT *rpt,
	unsigned char *manual_mode,
	unsigned char *nsvs,
	unsigned char *ndim,
	unsigned char sv_prn[],
	float *pdop,
	float *hdop,
	float *vdop,
	float *tdop)
/* over-determined satellite selection for position fixes, PDOP, fix mode */
{
	short islot;
	unsigned char *buf;
	buf = rpt->buf;

	*nsvs = (buf[0] & 0xF0) >> 4;
	if ((*nsvs)>8) return TRUE;
	if (rpt->cnt != 17 + (*nsvs) ) return TRUE;

	*manual_mode = buf[0] & 0x08;
	*ndim  = (buf[0] & 0x07);
	*pdop = bgetsingle (&buf[1]);
	*hdop = bgetsingle (&buf[5]);
	*vdop = bgetsingle (&buf[9]);
	*tdop = bgetsingle (&buf[13]);
	for (islot = 0; islot < (*nsvs); islot++)
		sv_prn[islot] = buf[islot + 17];
	return FALSE;
}


/**/
short rpt_0x72 (TSIPPKT *rpt,
	short *pf,
	TSIP_POS_FILT_PARMS *params)
/* position-velocity filter parameters */
{
/*
 * The receiver reports the filter parameters as ints,
 * but in reality they are chars.  To preserve parallel structure
 * with the commands, they are jammed into chars here.
 * If there is an overflow, the routine responds with FALSE.
 */

	unsigned char *buf;
	short temp;
	buf = rpt->buf;

	if (rpt->cnt != 32) return TRUE;

	*pf = bgetint (&buf[0]);
	params->flt1 = bgetsingle (&buf[2]);
	temp = bgetint (&buf[6]);
	params->chr1 = (unsigned char)temp;
	temp = bgetint (&buf[8]);
	params->chr2 = (unsigned char)temp;
	temp = bgetint (&buf[10]);
	params->chr3 = (unsigned char)temp;
	temp = bgetint (&buf[12]);
	params->chr4 = (unsigned char)temp;
	params->flt2 = bgetsingle(&buf[14]);
	params->flt3 = bgetsingle(&buf[18]);
	params->flt4 = bgetsingle(&buf[22]);
	params->flt5 = bgetsingle(&buf[26]);
	temp = bgetint (&buf[30]);
	params->chr5 = (unsigned char)temp;
	return FALSE;
}

short rpt_0x74 (TSIPPKT *rpt,
	float *alt_filter_parm)
/* altitude filter parameters */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 4) return TRUE;
	*alt_filter_parm = bgetsingle (buf);
	return FALSE;
}

short rpt_0x76 (TSIPPKT *rpt,
	unsigned char *mode_6)
/* track and fix mode parameters */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 1) return TRUE;
	*mode_6 = buf[0];
	return FALSE;
}

short rpt_0x78 (TSIPPKT *rpt,
	short *maxage)
/* DC max age parameters */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 2) return TRUE;
	*maxage = bgetint (&buf[0]);
   return FALSE;
}

/**/
short rpt_0x82 (TSIPPKT *rpt,
	unsigned char *diff_mode)
/* differential fix mode */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 1) return TRUE;
	*diff_mode = buf[0];
	return FALSE;
}

short rpt_0x83 (TSIPPKT *rpt,
	double ECEF_pos[3],
	double *clock_bias,
	float *time_of_fix)
/* position, ECEF double precision */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 36) return TRUE;
	ECEF_pos[0] = bgetdouble (buf);
	ECEF_pos[1] = bgetdouble (&buf[8]);
	ECEF_pos[2] = bgetdouble (&buf[16]);
	*clock_bias  = bgetdouble (&buf[24]);
	*time_of_fix = bgetsingle (&buf[32]);
	set_tsip_time (*time_of_fix, 0);
	return FALSE;
}

short rpt_0x84 (TSIPPKT *rpt,
	double *lat,
	double *lon,
	double *alt,
	double *clock_bias,
	float *time_of_fix)
/* position, lat-lon-alt double precision */	
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 36) return TRUE;
	*lat = bgetdouble (buf);
	*lon = bgetdouble (&buf[8]);
	*alt = bgetdouble (&buf[16]);
	*clock_bias = bgetdouble (&buf[24]);
	*time_of_fix = bgetsingle (&buf[32]);
	set_tsip_time (*time_of_fix, 0);
	return FALSE;
}

short rpt_0x85 (TSIPPKT *rpt,
	unsigned char *sv_prn,
	unsigned char *summary_status,
	unsigned char *station_health,
	unsigned char *SV_UDRE_parm,
	unsigned char *iode1,
	unsigned char *iode2,
	float *z_cnt_tow,
	float *range_corr,
	float *range_rate_corr,
	float *delta_range_corr)
/* satellite differential correction info */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 22) return TRUE;
	*sv_prn = buf[0];
	*summary_status = buf[1];
	*station_health = buf[2];
	*SV_UDRE_parm = buf[3];
	*iode1 = buf[4];
	*iode2 = buf[5];
	*z_cnt_tow = bgetsingle (&buf[6]);
	*range_corr = bgetsingle (&buf[10]);
	*range_rate_corr = bgetsingle (&buf[14]);
	*delta_range_corr = bgetsingle (&buf[18]);
	return FALSE;
}

short rpt_0x8F03 (TSIPPKT *rpt,
	unsigned char *tx_baud_index,
	unsigned char *rx_baud_index,
	unsigned char *char_format_index,
	unsigned char *stop_bits,
	unsigned char *tx_mode_index,
	unsigned char *rx_mode_index)
/* Channel B configuration for dual port operation */
{
	unsigned char *buf;
	buf = rpt->buf;
	
	if (rpt->cnt != 7) return TRUE;
	*tx_baud_index = buf[1];
	*rx_baud_index = buf[2];
	*char_format_index = buf[3];
	*stop_bits = (buf[4] == 0x07) ? 1 : 2;
   	*tx_mode_index = buf[5];
	*rx_mode_index = buf[6];
	return FALSE;
}

short rpt_0x8F14 (TSIPPKT *rpt,
	short *datum_idx,
	double datum_coeffs[5])
/*  datum index and coefficients  */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 43) return TRUE;
	*datum_idx = bgetint(&buf[1]);
	datum_coeffs[0] = bgetdouble (&buf[3]);
	datum_coeffs[1] = bgetdouble (&buf[11]);
	datum_coeffs[2] = bgetdouble (&buf[19]);
	datum_coeffs[3] = bgetdouble (&buf[27]);
	datum_coeffs[4] = bgetdouble (&buf[35]);
	return FALSE;
}

short rpt_0x8F15 (TSIPPKT *rpt,
	short *datum_idx,
	double datum_coeffs[5])
/*  datum index and coefficients  */
{
	return rpt_0x8F14 (rpt, datum_idx, datum_coeffs);
}

short rpt_0x8F17 (TSIPPKT *rpt,
	unsigned char *UTM_grid_letter,
	short *UTM_grid_number,
	float *UTM_northing_value,
	float *UTM_easting_value,
	float *UTM_altitude,
	float *clock_bias,
	float *time_of_fix)
/*  UTM Single precision position response  */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 24) return TRUE;
	*UTM_grid_letter   	= buf[1];
	*UTM_grid_number    = bgetint (&buf[2]);
	*UTM_northing_value = bgetsingle (&buf[ 4]);
	*UTM_easting_value  = bgetsingle (&buf[ 8]);
	*UTM_altitude       = bgetsingle (&buf[12]);
	*clock_bias         = bgetsingle (&buf[16]);
	*time_of_fix        = bgetsingle (&buf[20]);
	return FALSE;
}


short rpt_0x8F18 (TSIPPKT *rpt,
	unsigned char *UTM_grid_letter,
	short *UTM_grid_number,
	double *UTM_northing_value,
	double *UTM_easting_value,
	double *UTM_altitude,
	double *clock_bias,
	float *time_of_fix)
/*  UTM double precision position response  */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 40) return TRUE;
	*UTM_grid_letter   	= buf[1];
	*UTM_grid_number    = bgetint (&buf[2]);
	*UTM_northing_value = bgetdouble (&buf[ 4]);
	*UTM_easting_value  = bgetdouble (&buf[12]);
	*UTM_altitude       = bgetdouble (&buf[20]);
	*clock_bias         = bgetdouble (&buf[28]);
	*time_of_fix        = bgetsingle (&buf[36]);
	return FALSE;
}


short rpt_0x8F19 (TSIPPKT *rpt,
   short *UTM_enabled)
/*  UTM position message enabled/disabled response  */
{
	unsigned char *buf;
	buf = rpt->buf;

	if (rpt->cnt != 3) return TRUE;
	*UTM_enabled = (buf[2] == 'E') ? TRUE : FALSE;
	return FALSE;
}

#define MAX_LONG  (2147483648.)   /* 2**31 */

short rpt_0x8F20 (TSIPPKT *rpt,
	unsigned char *subpacket_id,
	unsigned char *info,
	double *lat,
	double *lon,
	double *alt,
	double vel_enu[],
	double *time_of_fix,
	short *week_num,
	unsigned char *nsvs,
	unsigned char sv_prn[],
	short sv_IODC[],
	short *datum_index)
{
	short
		isv;
	unsigned char
		*buf, prnx;
	unsigned long
		ulongtemp;
	long
		longtemp;
	double
		vel_scale;

	buf = rpt->buf;

	if (rpt->cnt != 56) return TRUE;
	*subpacket_id = buf[0];

	vel_scale = (buf[24]&1)? 0.020 : 0.005;
	vel_enu[0] = bgetint (buf+2)*vel_scale;
	vel_enu[1] = bgetint (buf+4)*vel_scale;
	vel_enu[2] = bgetint (buf+6)*vel_scale;

	*time_of_fix = bgetulong (buf+8)*.001;

	longtemp = bgetlong (buf+12);
	*lat = longtemp*(GPS_PI/MAX_LONG);

	ulongtemp = bgetulong (buf+16);
	*lon = ulongtemp*(GPS_PI/MAX_LONG);
	if (*lon > GPS_PI) *lon -= 2.0*GPS_PI;

	*alt = bgetlong (buf+20)*.001;
	/* 25 blank; 29 = UTC */
	*datum_index = (short)buf[26] - 1;
	*info = buf[27];
	*nsvs = buf[28];
	*week_num = bgetint (&buf[30]);
	for (isv = 0; isv < 8; isv++) {
		prnx = buf[32+2*isv];
		sv_prn[isv] = prnx&0x3F;
		sv_IODC[isv] = buf[32 + 2 * isv + 1] + (short)(prnx - sv_prn[isv]) * 4;
	}
	set_tsip_time (*time_of_fix, *week_num);
	return FALSE;
}


