/*
 * *************************************************************************
 *
 * 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
 * 5a10  18 jul 97   matched 5.10 manual         pvwl
 * *************************************************************************
 *
 * TSIP_RPT.C consists of a primary function rpt_packet() called by main().
 * This function takes a character buffer that has been received as a report
 * from a TSIP device and interprets it.  The character buffer has been
 * assembled using tsip_input_proc() in TSIP_IFC.C.
 *
 * A large case statement directs processing to one of many mid-level
 * functions.  The mid-level functions specific to the current report
 * code passes the report buffer to the appropriate report decoder
 * rpt_0x?? () in TSIP_IFC.C, which outputs the variable values in the
 * report buffer rpt.buf.
 *
 * *************************************************************************
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <conio.h>
#include "tsip_ifc.h"
#include "tsipincl.h"

/* TCHAT uses printf(); TSIPCHAT uses cprintf() */
#ifndef xprintf
#define xprintf printf
#endif

#define GOOD_PARSE 0
#define BADID_PARSE 1
#define BADLEN_PARSE 2
short parsed;

/* convert time of week into day-hour-minute-second and print */
static void show_time (float time_of_week)
{
	short	days, hours, minutes;
	float seconds;
	double tow;
	const char
		*dname[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

	if (time_of_week == -1.0) {
		xprintf ("   <No time yet>   ");
		return;
	}
	if (time_of_week < 0.0F) time_of_week += 604800.0F;
	if ((time_of_week >= 604800.0) || (time_of_week < 0.0)) {
		xprintf ("     <Bad time>     ");
		return;
	}
	tow = time_of_week;
	days = (short)(tow / 86400.0);
	hours = (short)fmod(tow / 3600., 24.);
	minutes =  (short) fmod(tow/60., 60.);
	seconds = (float)fmod(tow, 60.);
	xprintf (" %s %02d:%02d:%05.2f   ", dname[days], hours, minutes, seconds);
}

/**/
/* 0x3D */
#define MAX_INPUT_MODE     2
#define MAX_OUTPUT_MODE    6
static void show_chan_A_config (TSIPPKT *rpt)
{
	const char
		baudnum[MAX_BAUD]       =  {0, 1, 4, 5, 6, 8, 9, 11, 12, 28},
		*parity_text [5]        =  {"even", "odd", "", "", "no"},
		*baud_text[MAX_BAUD]    =  {
			"50 bps", "110 bps", "300 bps", "600 bps",
			"1200 bps", "2400 bps", "4800 bps", "9600 bps",
			"38.4k bps", "19.2k bps"},
		*input_cha[MAX_INPUT_MODE]    =  {"Packets", "Raw RTCM data"},
		*output_cha[MAX_OUTPUT_MODE]  =  {
			"Packets",	"No output",
			"undefined", "undefined",
			"undefined", "NMEA 0183"};
	unsigned char
		tx_baud_index, rx_baud_index,
		char_format_index, databits, parity,
		stop_bits, tx_mode_index, rx_mode_index;
	short
		i;

	/* unload rptbuf */
	if (rpt_0x3D (rpt,
		&tx_baud_index, &rx_baud_index, &char_format_index,
		&stop_bits, &tx_mode_index, &rx_mode_index)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Channel A Configuration");
	show_crlf ();
	for (i = 0; i < 10; ++i) { /* 10 baud choices */
		if (tx_baud_index == baudnum[i]) break;
	}
	xprintf ("Transmit speed: %s at %s",
		(tx_mode_index >= MAX_OUTPUT_MODE) ? "undefined":
		output_cha[tx_mode_index], (i == 10) ? "undefined" : baud_text[i]);
	show_crlf ();
	for (i = 0; i < 10; ++i) { /* 10 baud choices */
		if (rx_baud_index == baudnum[i]) break;
	}
	xprintf("Receive speed: %s at %s",
		(rx_mode_index >= MAX_INPUT_MODE) ? "undefined":
		input_cha[rx_mode_index], (i == 10) ? "undefined" : baud_text[i]);
	show_crlf ();
	databits = (char_format_index & 0x03) + 5;
	parity = char_format_index >> 2;
	if (parity > 4) parity = 2;
	xprintf("Character format (bits/char, parity, stop bits): %d-%s-%d",
		databits, parity_text[parity], stop_bits);
}

/**/
/* 0x40 */
static void rpt_almanac_data_page (TSIPPKT *rpt)
{
	unsigned char
		sv_prn;
	short
		week_num;
	float
		t_zc,
		eccentricity,
		t_oa,
		i_0,
		OMEGA_dot,
		sqrt_A,
		OMEGA_0,
		omega,
		M_0;

	/* unload rptbuf */
	if (rpt_0x40 (rpt,
		&sv_prn, &week_num, &t_zc, &eccentricity, &t_oa,
		&i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) {
		parsed = BADLEN_PARSE;
		return;
	}
	
	xprintf ("Almanac for SV #%2d", sv_prn);
	show_crlf ();
	xprintf ("       Captured:%15.0f", t_zc);
	show_time (t_zc);
	xprintf ("   week:%5d", week_num);
	show_crlf ();
	xprintf ("   Eccentricity:%15g", eccentricity);
	show_crlf ();
	xprintf ("           T_oa:%15.0f", t_oa);
	show_time (t_oa);
	show_crlf ();
	xprintf ("            i 0:%15g", i_0);
	show_crlf ();
	xprintf ("      OMEGA dot:%15g", OMEGA_dot);
	show_crlf ();
	xprintf ("  square root A:%15g", sqrt_A);
	show_crlf ();
	xprintf ("        OMEGA 0:%15g", OMEGA_0);
	show_crlf ();
	xprintf ("          omega:%15g", omega);
	show_crlf ();
	xprintf ("            M 0:%15g", M_0);
}

/* 0x41 */
static void rpt_GPS_time (TSIPPKT *rpt)
{
	float
		time_of_week, UTC_offset;
	short
		week_num;

	/* unload rptbuf */
	if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("GPS time:");
	show_time (time_of_week);
	xprintf ("GPS week: %d   UTC offset %.1f", week_num, UTC_offset);

	/* This function will reset PC clock if a request has been previously
	* generated in a 'z' command.  Record of the request is kept by
	* time_set_request in TSIP_UTL.C, set by request_PC_time_set()
	* which is called from the 'z' command handler in TSIP_CMD.C
	* This function is not used in TCHAT; a dummy call is in TCHAT.C
	*/
	GPS_time_to_PC (time_of_week + week_num*604800. - UTC_offset);
}

/* 0x42 */
static void rpt_single_ECEF_position (TSIPPKT *rpt)
{
	float
		ECEF_pos[3], time_of_fix;

	/* unload rptbuf */
	if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("SPos: %15.2f%15.2f%15.2f    ",
		ECEF_pos[0], ECEF_pos[1], ECEF_pos[2]);
	show_time (time_of_fix);
}

/* 0x43 */
static void rpt_single_ECEF_velocity (TSIPPKT *rpt)
{

	float
		ECEF_vel[3], freq_offset, time_of_fix;

	/* unload rptbuf */
	if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("VelECEF: %11.3f%11.3f%11.3f%12.3f    ",
		ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset);
	show_time (time_of_fix);
}

/* 0x44 */
static void rpt_4SV_selection (TSIPPKT *rpt) {

	unsigned char
		manual_mode, nsvs, sv_prn[4];
	float
		pdop, hdop, vdop, tdop;

	/* unload rptbuf */
	if (rpt_0x44 (rpt,
		&manual_mode, &nsvs, sv_prn, &pdop, &hdop, &vdop, &tdop)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Mode: %s %d SV (%d-D): %2d %2d %2d %2d",
		(manual_mode & 0x10)?"Manual":"Auto  ",
		nsvs, nsvs - 1,
		sv_prn[0], sv_prn[1], sv_prn[2], sv_prn[3]);
	show_crlf ();

	xprintf ("      P, H, V, TDOP = %.2f, %.2f, %.2f, %.2f",
		pdop, hdop, vdop, tdop);
}

/*  0x45  */
static void rpt_SW_version (TSIPPKT *rpt) {
	unsigned char
		major_nav_version, minor_nav_version,
		nav_day, nav_month, nav_year,
		major_dsp_version, minor_dsp_version,
		dsp_day, dsp_month, dsp_year;

	/* unload rptbuf */
	if (rpt_0x45 (rpt,
		&major_nav_version, &minor_nav_version,
		&nav_day, &nav_month, &nav_year,
		&major_dsp_version, &minor_dsp_version,
		&dsp_day, &dsp_month, &dsp_year)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf (
"FW Versions:  Nav Proc %2d.%02d  %2d/%2d/%2d  Sig Proc %2d.%02d  %2d/%2d/%2d",
		major_nav_version, minor_nav_version, nav_day, nav_month, nav_year,
		major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year);
}

/* 0x46 */
#define MAX_SC_MESSAGE 13
#define MAX_EC_MESSAGE 6
static void rpt_rcvr_health (TSIPPKT *rpt)
{
	unsigned char
		status_code, error_code;
	short
		i;

	const char
		*sc_text[MAX_SC_MESSAGE] = {
		"Doing position fixes",
		"Don't have GPS time yet",
		"Waiting for almanac collection",
		"DOP too high          ",
		"No satellites available",
		"Only 1 satellite available",
		"Only 2 satellites available",
		"Only 3 satellites available",
		"No satellites usable   ",
		"Only 1 satellite usable",
		"Only 2 satellites usable",
		"Only 3 satellites usable",
		"Chosen satellite unusable"},

		*ec_text[MAX_EC_MESSAGE] = {
		"RAM cleared at power-up",
		"Signal processor error ",
		"Channel 1 Misalignment ",
		"Channel 2 Misalignment ",
		"Antenna line open/short",
		"Excessive Osc Freq Bias"};


	/* unload rptbuf */
	if (rpt_0x46 (rpt, &status_code, &error_code))
	{
		parsed = BADLEN_PARSE;
		return;
	}

	if (status_code > MAX_SC_MESSAGE)
	{
		xprintf ("Unknown status code %d", status_code);
	}
	else
	{
		xprintf ("Health of GPS: %s", sc_text[rpt->buf[0]]);
		for (i = 0; i < MAX_EC_MESSAGE; i++) {
			if (error_code & (1 << i)) {
				show_crlf ();
				xprintf ("               %s", ec_text[i]);
			}
		}
	}
}

/* 0x47 */
static void rpt_SNR_all_SVs (TSIPPKT *rpt)
{
	unsigned char
		nsvs, sv_prn[12];
	short
		isv;
	float
		snr[12];

	/* unload rptbuf */
	if (rpt_0x47 (rpt, &nsvs, sv_prn, snr)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("SNR for satellites: %d", nsvs);
	for (isv = 0; isv < nsvs; isv++) {
		show_crlf ();
		xprintf ("   SV %2d   %6.2f", sv_prn[isv], snr[isv]);
	}
}

/* 0x48 */
static void rpt_GPS_system_message (TSIPPKT *rpt)
{
	unsigned char
		message[23];

	/* unload rptbuf */
	if (rpt_0x48 (rpt, message)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("GPS message: %s", message);
}

/* 0x49 */
static void rpt_almanac_health_page (TSIPPKT *rpt)
{
	short
		iprn;
	unsigned char
		sv_health [32];

	/* unload rptbuf */
	if (rpt_0x49 (rpt, sv_health)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Almanac health page:");
	for (iprn = 0; iprn < 32; iprn++) {
		if (!(iprn%5)) show_crlf ();
		xprintf ("    SV%2.2d  %2X", (iprn+1) , sv_health[iprn]);
	}
}

/* 0x4A */
static void rpt_single_lla_position (TSIPPKT *rpt) {
	short
		lat_deg, lon_deg;
	float
		lat, lon,
		alt, clock_bias, time_of_fix;
	double lat_min, lon_min;
	unsigned char
		north_south, east_west;

	if (rpt_0x4A (rpt,
		&lat, &lon, &alt, &clock_bias, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	/* convert from radians to degrees */
	lat *= (float)R2D;
	north_south = 'N';
	if (lat < 0.0) {
		north_south = 'S';
		lat = -lat;
	}
	lat_deg = (short)lat;
	lat_min = (lat - lat_deg) * 60.0;

	lon *= (float)R2D;
	east_west = 'E';
	if (lon < 0.0) {
		east_west = 'W';
		lon = -lon;
	}
	lon_deg = (short)lon;
	lon_min = (lon - lon_deg) * 60.0;

	xprintf ("Position:%4d:%06.3f %c%5d:%06.3f %c%10.2f%12.2f",
		lat_deg, lat_min, north_south,
		lon_deg, lon_min, east_west,
		alt, clock_bias);
	show_time (time_of_fix);
}

/* 0x4B */
#define MAX_AS1_MESSAGE 4
#define MAX_MID_MESSAGE 41
static void rpt_rcvr_id_and_status (TSIPPKT *rpt)
{
	const char
		*as1_text[MAX_AS1_MESSAGE] = {
		"Synthesizer fault         ",
		"No RTC Timeset at power-up",
		"A-to-D converter fault    ",
		"Almanac not complete      "} ;

	unsigned char
		machine_id, status_1, status_2;
	short
		imsg;

	/* unload rptbuf */
	if (rpt_0x4B (rpt, &machine_id, &status_1, &status_2)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Machine/Code ID: %d   Status: %d %d", machine_id,
		status_1, status_2);
	for (imsg = 0; imsg < MAX_AS1_MESSAGE; imsg++) {
		if (status_1 & (1 << imsg)) {
			show_crlf ();
			xprintf ("               %s", as1_text[imsg]);
		}
	}
	if (status_2 & 0x01) {
		xprintf ("        Superpackets supported");
	}
}

/* 0x4C */
#define MAX_DYN_MESSAGE 5
static void rpt_operating_parameters (TSIPPKT *rpt)
{
	unsigned char
		dyn_code;
	float
		el_mask, snr_mask, dop_mask, dop_switch;
	const char
		*dyn_text[MAX_DYN_MESSAGE] = {"Unchanged",
		"Land", "Sea", "Air", "Static"};

	/* unload rptbuf */
	if (rpt_0x4C (rpt, &dyn_code, &el_mask,
		&snr_mask, &dop_mask, &dop_switch)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Operating Parameters:");
	show_crlf ();
	xprintf ("    Dynamics code = %d %s", dyn_code,
		(dyn_code < MAX_DYN_MESSAGE)?
		dyn_text[dyn_code] : "unknown dynamics code");
	show_crlf ();
	xprintf ("   Elevation mask = %.2fø", el_mask * R2D);
	show_crlf ();
	xprintf ("         SNR mask = %.2f", snr_mask);
	show_crlf ();
	xprintf ("        DOP mask = %.2f", dop_mask);
	show_crlf ();
	xprintf ("      DOP switch = %.2f", dop_switch);
}

/* 0x4D */
static void rpt_oscillator_offset (TSIPPKT *rpt)
{
	float
		osc_offset;

	/* unload rptbuf */
	if (rpt_0x4D (rpt, &osc_offset)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Oscillator offset: %.2f Hz.", osc_offset);
}

/* 0x4E */
static void rpt_GPS_time_set_response (TSIPPKT *rpt)
{

	unsigned char
		response;

	/* unload rptbuf */
	if (rpt_0x4E (rpt, &response)) {
		parsed = BADLEN_PARSE;
		return;
	}

	switch (response) {
	case 'Y':
		xprintf ("Time set accepted");
		break;

	case 'N':
		xprintf ("Time set rejected or not required");
		break;

	default:
		xprintf ("Time set response error");
	}
}

/* 0x4F */
static void rpt_UTC_offset (TSIPPKT *rpt)
{
	double
		a0;
	float
		a1, time_of_data;
	short
		dt_ls, wn_t, wn_lsf, dn, dt_lsf;

	/* unload rptbuf */
	if (rpt_0x4F (rpt, &a0, &a1, &time_of_data,
		&dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("UTC Correction Data");
	show_crlf ();
	xprintf ("           A 0 = %15g", a0);
	show_crlf ();
	xprintf ("           A 1 = %15g", a1);
	show_crlf ();
	xprintf ("    delta t LS = %6d", dt_ls);
	show_crlf ();
	xprintf ("          t ot = %6.0f  ", time_of_data);
	show_time (time_of_data);
	show_crlf ();
	xprintf ("          WN t = %6d", wn_t);
	show_crlf ();
	xprintf ("        WN LSF = %6d", wn_lsf);
	show_crlf ();
	xprintf ("            DN = %6d", dn);
	show_crlf ();
	xprintf ("   delta t LSF = %6d", dt_lsf);
}

/**/
/* 0x54 */
static void rpt_1SV_bias (TSIPPKT *rpt)
{
	float
		clock_bias, freq_offset, time_of_fix;

	/* unload rptbuf */
	if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Time Only Mode   Bias: %6.2f   Bias Rate: %6.2f",
		clock_bias, freq_offset);
	show_time (time_of_fix);
}

/* 0x55 */
static void rpt_io_opt (TSIPPKT *rpt)
{
	unsigned char
		pos_code, vel_code, time_code, aux_code;

	/* unload rptbuf */
	if (rpt_0x55 (rpt,
		&pos_code, &vel_code, &time_code, &aux_code)) {
		parsed = BADLEN_PARSE;
		return;
	}
	/* rptbuf unloaded */

	xprintf ("I/O Options:%2X%2X%2X%2X",
		pos_code, vel_code, time_code, aux_code);

	if (pos_code & 0x01) {
		show_crlf ();
		xprintf ("    ECEF XYZ position output");
	}
	if (pos_code & 0x02) {
		show_crlf ();
		xprintf ("    LLA position output");
	}
	show_crlf ();
	xprintf ((pos_code & 0x04)?
		"    MSL altitude output (Geoid height) ":
		"    WGS-84 altitude output");
	show_crlf ();
	xprintf ((pos_code & 0x08)?
		"    MSL altitude input":"    WGS-84 altitude input");
	show_crlf ();
	xprintf ((pos_code & 0x10)?
		"    Double precision":"    Single precision");
	if (pos_code & 0x20) {
		show_crlf ();
		xprintf ("   All Enabled Superpackets");
	}
	if (vel_code & 0x01) {
		show_crlf ();
		xprintf ("    ECEF XYZ velocity output");
	}
	if (vel_code & 0x02) {
		show_crlf ();
		xprintf ("    ENU velocity output");
	}
	show_crlf ();
	xprintf ((time_code & 0x01)?
		  "    Time tags in UTC":"    Time tags in GPS time");
	if (time_code & 0x02) {
		show_crlf ();
		xprintf ("    Fixes delayed to integer seconds");
	}
	if (time_code & 0x04) {
		show_crlf ();
		xprintf ("    Fixes sent only on request");
	}
	if (time_code & 0x08) {
		show_crlf ();
		xprintf ("    Synchronized measurements");
	}
	if (time_code & 0x10) {
		show_crlf ();
		xprintf ("    Minimize measurement propagation");
	}
	if (aux_code & 0x01) {
		show_crlf ();
		xprintf ("    Raw measurement output");
	}
	if (aux_code & 0x02) {
		show_crlf ();
		xprintf ("    Code-phase smoothed before output");
	}
	if (aux_code & 0x04) {
		show_crlf ();
		xprintf ("    Additional fix status");
	}
}

/* 0x56 */
static void rpt_ENU_velocity (TSIPPKT *rpt)
{
	float
		vel_ENU[3], freq_offset, time_of_fix;

	/* unload rptbuf */
	if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Vel ENU:%11.2f%11.2f%11.2f%12.2f    ",
		vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset);
	show_time (time_of_fix);
}

/* 0x57 */
#define MAX_SOURCE_MESSAGE 9
static void rpt_last_fix_info (TSIPPKT *rpt)
{
	unsigned char
		source_code, diag_code;
	short
		week_num;
	float
		time_of_fix;
	const char
		*source_text[MAX_SOURCE_MESSAGE] = {
		"temporary no fix",
		"good current fix",
		"converging fix",
		"",
		"shadow fix",
		"user-supplied coarse fix",
		"user-supplied accurate fix",
		"image fix",
		"no fix available"} ;


	/* unload rptbuf */
	if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("%s;   diag code: %2Xh", source_text[source_code], diag_code);
	show_crlf ();
	xprintf ("Time of last fix:");
	show_time (time_of_fix);
	show_crlf ();
	xprintf ("Week of last fix: %d", week_num);
}

/* 0x58 */
#define MAX_DAT_MESSAGE 7
static void rpt_GPS_system_data (TSIPPKT *rpt)
{
	unsigned char
		op_code, data_type, sv_prn,
		data_length, data_packet[250];
	ALM_INFO
		*almanac;
	ALH_PARMS
		*almh;
	UTC_INFO
		*utc;
	ION_INFO
		*ionosphere;
	EPHEM_CLOCK
		*cdata;
	EPHEM_ORBIT
		*edata;
	NAV_INFO
		*nav_data;
	unsigned char
		curr_t_oa;
	short
		curr_wn_oa;
	const char
		*datname[MAX_DAT_MESSAGE] =
		{"", "", "Almanac",
		"Health Page, T_oa, WN_oa", "Ionosphere", "UTC ",
		"Ephemeris"};

	/* unload rptbuf */
	if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn,
		&data_length, data_packet)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("System data [%d]:  %s  SV%2.2d",
		data_type, datname[data_type], sv_prn);
	switch (op_code)
	{
	case 1:
		xprintf ("  Acknowledged");
		show_crlf ();
		break;
	case 2:
		xprintf ("  length = %d bytes", data_length);
		show_crlf ();
		switch (data_type) {
		case 2:
			/* Almanac */
			if (sv_prn == 0 || sv_prn > 32) {
				xprintf ("Binary PRN invalid");
				return;
			}
			almanac = (ALM_INFO*)data_packet;
			xprintf ("t_oa_raw = %12d  ", almanac->t_oa_raw );
			xprintf ("SV_hlth  = %12d  ", almanac->SV_health );
			show_crlf ();
			xprintf ("e        = % -12g  ", almanac->e        );
			xprintf ("t_oa     = % -12g  ", almanac->t_oa     );
			show_crlf ();
			xprintf ("i_0      = % -12g  ", almanac->i_0      );
			xprintf ("OMEGADOT = % -12g  ", almanac->OMEGADOT );
			show_crlf ();
			xprintf ("sqrt_A   = % -12g  ", almanac->sqrt_A   );
			xprintf ("OMEGA_0  = % -12g  ", almanac->OMEGA_0  );
			show_crlf ();
			xprintf ("omega    = % -12g  ", almanac->omega    );
			xprintf ("M_0      = % -12g  ", almanac->M_0      );
			show_crlf ();
			xprintf ("a_f0     = % -12g  ", almanac->a_f0     );
			xprintf ("a_f1     = % -12g  ", almanac->a_f1     );
			show_crlf ();
			xprintf ("Axis     = % -12g  ", almanac->Axis     );
			xprintf ("n        = % -12g  ", almanac->n        );
			show_crlf ();
			xprintf ("OMEGA_n  = % -12g  ", almanac->OMEGA_n  );
			xprintf ("ODOT_n   = % -12g  ", almanac->ODOT_n   );
			show_crlf ();
			xprintf ("t_zc     = % -12g  ", almanac->t_zc     );
			xprintf ("weeknum  = %12d  ", almanac->weeknum  );
			show_crlf ();
			xprintf ("wn_oa    = %12d", almanac->wn_oa    );
			break;

		case 3:
			/* Almanac health page */
			almh = (ALH_PARMS*)data_packet;
			xprintf ("t_oa = %d, wn_oa&0xFF = %d  ", almh->t_oa, almh->WN_a);
			show_crlf ();
			xprintf ("%02X %02X %02X %02X %02X %02X %02X %02X  ",
				almh->SV_health[0], almh->SV_health[1], almh->SV_health[2],
				almh->SV_health[3], almh->SV_health[4], almh->SV_health[5],
				almh->SV_health[6], almh->SV_health[7]);
			show_crlf ();
			xprintf ("%02X %02X %02X %02X %02X %02X %02X %02X  ",
				almh->SV_health[8], almh->SV_health[9], almh->SV_health[10],
				almh->SV_health[11], almh->SV_health[12], almh->SV_health[13],
				almh->SV_health[14], almh->SV_health[15]);
			show_crlf ();
			xprintf ("%02X %02X %02X %02X %02X %02X %02X %02X  ",
				almh->SV_health[16], almh->SV_health[17], almh->SV_health[18],
				almh->SV_health[19], almh->SV_health[20], almh->SV_health[21],
				almh->SV_health[22], almh->SV_health[23]);
			show_crlf ();
			xprintf ("%02X %02X %02X %02X %02X %02X %02X %02X  ",
				almh->SV_health[24], almh->SV_health[25], almh->SV_health[26],
				almh->SV_health[27], almh->SV_health[28], almh->SV_health[29],
				almh->SV_health[30], almh->SV_health[31]);
			show_crlf ();
			curr_t_oa = data_packet[34];
			curr_wn_oa = (data_packet[35]<<8) + data_packet[36];
			xprintf ("current t_oa = %d, wn_oa = %d  ", curr_t_oa, curr_wn_oa);
			show_crlf();
			break;

		case 4:
			/* Ionosphere */
			ionosphere = (ION_INFO*)data_packet;
			xprintf ("alpha_0 = % -12g ", ionosphere->alpha_0);
			xprintf ("alpha_1 = % -12g ", ionosphere->alpha_1);
			show_crlf ();
			xprintf ("alpha_2 = % -12g ", ionosphere->alpha_2);
			xprintf ("alpha_3 = % -12g ", ionosphere->alpha_3);
			show_crlf ();
			xprintf ("beta_0  = % -12g  ", ionosphere->beta_0);
			xprintf ("beta_1  = % -12g  ", ionosphere->beta_1);
			show_crlf ();
			xprintf ("beta_2  = % -12g  ", ionosphere->beta_2);
			xprintf ("beta_3  = % -12g  ", ionosphere->beta_3);
			show_crlf ();
			break;

		case 5:
			/* UTC */
			show_crlf ();
			utc = (UTC_INFO*)data_packet;
			xprintf ("A_0        = %g  ", utc->A_0);
			show_crlf ();
			xprintf ("A_1        = %g  ", utc->A_1);
			show_crlf ();
			xprintf ("delta_t_LS = %d  ", utc->delta_t_LS);
			show_crlf ();
			xprintf ("t_ot       = %f  ", utc->t_ot );
			show_crlf ();
			xprintf ("WN_t       = %d  ", utc->WN_t );
			show_crlf ();
			xprintf ("WN_LSF     = %d  ", utc->WN_LSF );
			show_crlf ();
			xprintf ("DN         = %d  ", utc->DN );
			show_crlf ();
			xprintf ("delta_t_LSF = %d  ", utc->delta_t_LSF );
			break;

		case 6: /* Ephemeris */
			show_crlf ();
			if (sv_prn == 0 || sv_prn > 32) {
				xprintf ("Binary PRN invalid");
				show_crlf ();
				return;
			}
			nav_data = (NAV_INFO*)data_packet;

			xprintf ("   SV_PRN = % -12d . ", nav_data->sv_number );
			xprintf ("  t_ephem = % -12g . ", nav_data->t_ephem );
			show_crlf ();
			cdata = &(nav_data->ephclk);
			xprintf ("  weeknum = % -12d . ", cdata->weeknum );
			xprintf ("   codeL2 = % -12d . ", cdata->codeL2 );
			xprintf ("  L2Pdata = % -12d", cdata->L2Pdata );
			show_crlf ();
			xprintf ("SVacc_raw = % -12d . ", cdata->SVacc_raw );
			xprintf ("SV_health = % -12d . ", cdata->SV_health );
			xprintf ("     IODC = % -12d", cdata->IODC );
			show_crlf ();
			xprintf ("     T_GD = % -12g . ", cdata->T_GD );
			xprintf ("     t_oc = % -12g . ", cdata->t_oc );
			xprintf ("     a_f2 = % -12g", cdata->a_f2 );
			show_crlf ();
			xprintf ("     a_f1 = % -12g . ", cdata->a_f1 );
			xprintf ("     a_f0 = % -12g . ", cdata->a_f0 );
			xprintf ("    SVacc = % -12g", cdata->SVacc );
			show_crlf ();
			edata = &(nav_data->ephorb);
			xprintf ("     IODE = % -12d . ", edata->IODE );
			xprintf ("fit_intvl = % -12d . ", edata->fit_interval );
			xprintf ("     C_rs = % -12g", edata->C_rs );
			show_crlf ();
			xprintf ("  delta_n = % -12g . ", edata->delta_n );
			xprintf ("      M_0 = % -12g . ", edata->M_0 );
			xprintf ("     C_uc = % -12g", edata->C_uc );
			show_crlf ();
			xprintf ("      ecc = % -12g . ", edata->e );
			xprintf ("     C_us = % -12g . ", edata->C_us );
			xprintf ("   sqrt_A = % -12g", edata->sqrt_A );
			show_crlf ();
			xprintf ("     t_oe = % -12g . ", edata->t_oe );
			xprintf ("     C_ic = % -12g . ", edata->C_ic );
			xprintf ("  OMEGA_0 = % -12g", edata->OMEGA_0 );
			show_crlf ();
			xprintf ("     C_is = % -12g . ", edata->C_is );
			xprintf ("      i_0 = % -12g . ", edata->i_0 );
			xprintf ("     C_rc = % -12g", edata->C_rc );
			show_crlf ();
			xprintf ("    omega = % -12g . ", edata->omega );
			xprintf (" OMEGADOT = % -12g . ", edata->OMEGADOT );
			xprintf ("     IDOT = % -12g", edata->IDOT );
			show_crlf ();
			xprintf ("     Axis = % -12g . ", edata->Axis );
			xprintf ("        n = % -12g . ", edata->n );
			xprintf ("    r1me2 = % -12g", edata->r1me2 );
			show_crlf ();
			xprintf ("  OMEGA_n = % -12g . ", edata->OMEGA_n );
			xprintf ("   ODOT_n = % -12g", edata->ODOT_n );
			break;
		}
	}
}


/* 0x59: */
static void rpt_SVs_enabled (TSIPPKT *rpt)
{
	unsigned char
		code_type, status_code[32];
	short
		iprn;

	/* unload rptbuf */
	if (rpt_0x59 (rpt, &code_type, status_code)) {
		parsed = BADLEN_PARSE;
		return;
	}

	if (code_type == 3) {
		xprintf ("SVs Enable/Disable");
		show_crlf ();

		for (iprn=0; iprn<32; iprn++) {
			xprintf ("%c%c", (iprn%10) ? ' ' : '.',
				status_code[iprn] ? 'D' : 'E');
		}
	}
	if (code_type == 6) {
		xprintf ("SVs Heed/Ignore Health");
		show_crlf ();

		for (iprn=0; iprn<32; iprn++) {
			xprintf ("%c%c", (iprn%10) ? ' ' : '.',
				status_code[iprn] ? 'I' : 'H');
		}
	}
}


/* 0x5A */
static void rpt_raw_msmt (TSIPPKT *rpt)
{
	unsigned char
		sv_prn;
	float
		sample_length, signal_level, code_phase, Doppler;
	double
		time_of_fix;

	/* unload rptbuf */
	if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level,
		&code_phase, &Doppler, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("   %02d %5.0f %7.1f %10.2f %10.2f %12.3f ",
		sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix);
	show_time ((float)time_of_fix);
}

/* 0x5B */
static void rpt_SV_ephemeris_status (TSIPPKT *rpt)
{
	unsigned char
		sv_prn, sv_health, sv_iode, fit_interval_flag;
	float
		time_of_collection, time_of_eph, sv_accy;

	/* unload rptbuf */
	if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag,
		&time_of_collection, &time_of_eph, &sv_accy)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("    %2d  ", sv_prn);
	show_time (time_of_collection);
	xprintf ("   %2Xh     %2Xh ", sv_health, sv_iode);
	show_time (time_of_eph);
	xprintf ("   %1d   %4.1f", fit_interval_flag, sv_accy);
}

/* 0x5C */
static void rpt_SV_tracking_status (TSIPPKT *rpt)
{
	unsigned char
		sv_prn, chan, slot, acq_flag, eph_flag,
		old_msmt_flag, integer_msec_flag, bad_data_flag,
		data_collect_flag;
	float
		signal_level, time_of_last_msmt,
		elev, azim;

	/* unload rptbuf */
	if (rpt_0x5C (rpt,
		&sv_prn, &slot, &chan, &acq_flag, &eph_flag,
		&signal_level, &time_of_last_msmt, &elev, &azim,
		&old_msmt_flag, &integer_msec_flag, &bad_data_flag,
		&data_collect_flag)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("   %2d  %1d %1d  %1d   %1d   %4.1f  ",
		sv_prn, chan, slot, acq_flag, eph_flag, signal_level);
	show_time (time_of_last_msmt);
	xprintf ("  %5.1f  %5.1f   %d    %d    %d    %d",
		elev*R2D, azim*R2D, old_msmt_flag,
		integer_msec_flag, bad_data_flag, data_collect_flag);
}

/* 0x5E */
static void rpt_addl_fix_status (TSIPPKT *rpt)
{
	unsigned char
		num_reused_msmts,
		diff_Doppler_unavailable,
		fix_still_converging,
		num_old_msmts;


	/* unload rptbuf */
	if (rpt_0x5E (rpt,
		&num_reused_msmts, &diff_Doppler_unavailable,
		&fix_still_converging, &num_old_msmts)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("%3d measurements re-used", num_reused_msmts);
	show_crlf ();
	xprintf ("%3d measurements old", num_old_msmts);
	show_crlf ();
	xprintf (diff_Doppler_unavailable ? "Differential Doppler unavailable" :
      "Differential Doppler available");
	show_crlf ();
	xprintf (fix_still_converging ? "Fix converging" : "Fix converged");
}

/**/
/* 0x6D */
static void rpt_allSV_selection (TSIPPKT *rpt)
{
	unsigned char
		manual_mode, nsvs, sv_prn[8], ndim;
	short
		islot;
	float
		pdop, hdop, vdop, tdop;

	/* unload rptbuf */
	if (rpt_0x6D (rpt,
		&manual_mode, &nsvs, &ndim, sv_prn,
		&pdop, &hdop, &vdop, &tdop)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Mode: %s  %d SV (%d-D):",
		manual_mode ? "Manual" : "Auto",
		nsvs, ndim - 1);
	for (islot = 0; islot < nsvs; islot++) {
		if (sv_prn[islot]) xprintf (" %2d", sv_prn[islot]);
	}
	show_crlf ();
	xprintf ("      P, H, V, TDOP = %.2f, %.2f, %.2f, %.2f",
		pdop, hdop, vdop, tdop);
}

/**/

/* 0x72 */
static void show_pos_filter (TSIPPKT *rpt)
{
	short
		pf;
	TSIP_POS_FILT_PARMS
		params;

	if (rpt_0x72 (rpt, &pf, &params)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Position filter %s (%d)", (pf & 0x01) ? "enabled" : "disabled", pf);
	show_crlf ();
	xprintf("float 1: %.5f", params.flt1);
	show_crlf ();
	xprintf ("byte 1: %d", params.chr1);
	show_crlf ();
	xprintf ("byte 2: %d", params.chr2);
	show_crlf ();
	xprintf ("byte 3: %d", params.chr3);
	show_crlf ();
	xprintf ("byte 4: %d", params.chr4);
	show_crlf ();
	xprintf("float 2: %.3f", params.flt2);
	show_crlf ();
	xprintf ("float 3: %.0f", params.flt3);
	show_crlf ();
	xprintf ("float 4: %.3f", params.flt4);
	show_crlf ();
	xprintf ("float 5: %.0f", params.flt5);
	show_crlf ();
	xprintf ("byte 5 : %d %s", params.chr5,
		(params.chr5 == 0xFF) ? "static filter disabled":"");
}

/* 0x74 */
static void show_altitude_filter (TSIPPKT *rpt)
{
	float
		alt_filter_parm;

	/* unload rptbuf */
	if (rpt_0x74 (rpt, &alt_filter_parm)) {
		parsed = BADLEN_PARSE;
		return;
	}

	if (alt_filter_parm == -1.0) {
		xprintf ("Altitude filter disabled");
	} else {
		xprintf("Alt filter max N = %4.0f fixes", alt_filter_parm);
	}
}

/* 0x76 */
static void show_track_mode (TSIPPKT *rpt)
{
	const char
		*trackmode_text[3] = {"Track high 8, best-4 fix",
		"Track high 6, over-determined fix", "Track high 8, over-determined fix"};
	unsigned char
		trackmode;

	/* unload rptbuf */
	if (rpt_0x76 (rpt, &trackmode)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf("Current tracking mode: %s", trackmode_text[trackmode]);
}

/* 0x78 */
static void rpt_DGPS_maxage (TSIPPKT *rpt)
{
	short
		max_age;

	/* unload rptbuf */
	if (rpt_0x78 (rpt, &max_age)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Differential Correction Max Age: %3d sec", max_age);
}

/**/
/* 0x82 */
static void rpt_DGPS_position_mode (TSIPPKT *rpt)
{
	unsigned char
		diff_mode;

	/* unload rptbuf */
	if (rpt_0x82 (rpt, &diff_mode)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("DGPS mode is %d, ", diff_mode);
	switch (diff_mode) {
	case 0:
		xprintf ("diff off");
		break;

	case 1:
		xprintf ("report diff fixes only");
		break;

	case 2:
		xprintf ("auto diff, diff off");
		break;

	case 3:
		xprintf ("auto diff, diff on");
		break;
	}
}

/* 0x83 */
static void rpt_double_ECEF_position (TSIPPKT *rpt)
{

	double
		ECEF_pos[3], clock_bias;
	float
		time_of_fix;

	/* unload rptbuf */
	if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("DPos:%12.2f %13.2f %13.2f %12.2f",
		ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias);
	show_time (time_of_fix);
}

/* 0x84 */
static void rpt_double_lla_position (TSIPPKT *rpt)
{
	short
		lat_deg, lon_deg;
	double
		lat, lon, lat_min, lon_min,
		alt, clock_bias;
	float
		time_of_fix;
	unsigned char
		north_south, east_west;

	/* unload rptbuf */
	if (rpt_0x84 (rpt,
		&lat, &lon, &alt, &clock_bias, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	lat *= R2D;
	lon *= R2D;
	if (lat < 0.0) {
		north_south = 'S';
		lat = -lat;
	} else {
		north_south = 'N';
	}
	lat_deg = (short)lat;
	lat_min = (lat - lat_deg) * 60.0;

	if (lon < 0.0) {
		east_west = 'W';
		lon = -lon;
	} else {
		east_west = 'E';
	}
	lon_deg = (short)lon;
	lon_min = (lon - lon_deg) * 60.0;
	xprintf ("DPos: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f",
		lat_deg, lat_min, north_south,
		lon_deg, lon_min, east_west,
		alt, clock_bias);
	show_time (time_of_fix);
}

/* 0x85 */
static void rpt_dgps_info (TSIPPKT *rpt)
{
	const char
		*dm_text[6] = {"Good", "Delt", "StaH", " Age", "UDRE", "IODE"  };
	unsigned char
		sv_prn, summary_status, station_health, SV_UDRE_parm,
		iode1, iode2;
	float
		z_cnt_tow, range_corr, range_rate_corr, delta_range_corr;

	/* unload rptbuf */
	if (rpt_0x85 (rpt,
		&sv_prn, &summary_status, &station_health,
		&SV_UDRE_parm, &iode1, &iode2,
		&z_cnt_tow, &range_corr,
		&range_rate_corr, &delta_range_corr)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("    %2d  %d=%s    %d    %d    %02X    %02X  ", 
		sv_prn, summary_status,
		(summary_status < 6) ? dm_text[summary_status] : "Unkn",
		station_health, SV_UDRE_parm, iode1, iode2);
	show_time (z_cnt_tow);
	xprintf (" %6.1f %6.1f %6.1f", range_corr, range_rate_corr, delta_range_corr);
}

/* 0x8F */
static void rpt_8F_03 (TSIPPKT *rpt)
/* channel B configuration parameters */
{
	const char
		baudnum[MAX_BAUD]       =  {0, 1, 4, 5, 6, 8, 9, 11, 12, 28},
		*parity_text[5]         =  {"even", "odd", "", "", "no"},
		*baud_text[MAX_BAUD]    =  {"50 bps", "110 bps", "300 bps", "600 bps",
											"1200 bps", "2400 bps", "4800 bps", "9600 bps",
											"38.4 Kbps", "19.2 Kbps"},
		*input_chb[MAX_INPUT_MODE]    =  {"Packets", "Raw RTCM data"},
		*output_chb[MAX_OUTPUT_MODE]  =  {"Packets",	"No output",
													"undefined", "undefined",
													"undefined", "NMEA 0183"};
	unsigned char
		tx_baud_index, rx_baud_index,
		char_format_index, databits,
		parity, stop_bits, tx_mode_index, rx_mode_index;
	short
		i;

	/* unload rptbuf */
	if (rpt_0x8F03 (rpt,
		&tx_baud_index, &rx_baud_index, &char_format_index,
		&stop_bits, &tx_mode_index, &rx_mode_index)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Channel B Configuration");
	show_crlf ();
	for (i = 0; i < 10; ++i) { /* 10 baud choices */
		if (tx_baud_index == baudnum[i]) break;
	}
	xprintf ("Transmit speed: %s at %s",
		(tx_mode_index > MAX_OUTPUT_MODE) ? "undefined" :
		output_chb[tx_mode_index], (i == 10) ? "undefined" : baud_text[i]);
	show_crlf ();
	for (i = 0; i < 10; ++i) { /* 10 baud choices */
		if (rx_baud_index == baudnum[i]) break;
	}
	xprintf("Receive speed: %s at %s",
		(rx_mode_index >= MAX_INPUT_MODE) ? "undefined":
		input_chb[rx_mode_index], (i == 10) ? "undefined" : baud_text[i]);
	show_crlf ();
	databits = (char_format_index & 0x03) + 5;
	parity = char_format_index >> 2;
	if (parity > 4) parity = 2;
	xprintf("Character format (bits/char, parity, stop bits): %d-%s-%d",
		databits, parity_text[parity], stop_bits);
}


static void rpt_8F_14 (TSIPPKT *rpt)
/* Datum parameters */
{
	double
		datum_coeffs[5];
	short
		datum_idx;

	/* unload rptbuf */
	if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("Datum Index %2d ", datum_idx);
	show_crlf ();
	xprintf ("    dx     dy     dz       a-axis          e-squared");
	show_crlf ();
	xprintf("%6.1f %6.1f %6.1f    %10.3f    %16.14f", datum_coeffs[0], datum_coeffs[1],
			datum_coeffs[2], datum_coeffs[3],datum_coeffs[4]);
}

static void rpt_8F_15 (TSIPPKT *rpt)
/* Datum parameters */
{
	rpt_8F_14 (rpt);
}

/* 0x8F17 */
static void rpt_8F_17 (TSIPPKT *rpt)
/* UTM single precision position response */
{
	unsigned char UTM_grid_letter;
	short UTM_grid_number;
	float UTM_northing_value,
			   UTM_easting_value,
			   UTM_altitude,
			   clock_bias,
			time_of_fix;

	/* unload rptbuf */
	if (rpt_0x8F17 (rpt,
	  &UTM_grid_letter, &UTM_grid_number, &UTM_northing_value,
		&UTM_easting_value, &UTM_altitude, &clock_bias, &time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("UTM:%-3d%c Northing:%8.2f Easting:%8.2f UTM Alt:%8.2f",
		UTM_grid_number,
		UTM_grid_letter,
		UTM_northing_value,
		UTM_easting_value,
		UTM_altitude);
	show_crlf ();
	xprintf ("Clock bias:%11.2f", clock_bias);
	show_time (time_of_fix);
}

static void rpt_8F_18 (TSIPPKT *rpt)
/* UTM double precision position response */
{
	unsigned char UTM_grid_letter;
	short UTM_grid_number;

	double UTM_northing_value,
			   UTM_easting_value,
			   UTM_altitude,
			   clock_bias;

	float time_of_fix;

	/* unload rptbuf */
	if (rpt_0x8F18 (rpt, &UTM_grid_letter, &UTM_grid_number, &UTM_northing_value,
					&UTM_easting_value, &UTM_altitude, &clock_bias,
					&time_of_fix)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("UTM:%-3d%c Northing:%8.2lf Easting:%8.2lf UTM Alt:%8.2lf",
		UTM_grid_number,
		UTM_grid_letter,
		UTM_northing_value,
		UTM_easting_value,
		UTM_altitude);
	show_crlf ();
	xprintf ("Clock bias:%11.2f", clock_bias);
	show_time (time_of_fix);
}

static void rpt_8F_19 (TSIPPKT *rpt)
/* UTM message enable status response */
{
	short UTM_enabled;

	if (rpt_0x8F19 (rpt, &UTM_enabled)) {
		parsed = BADLEN_PARSE;
		return;
	}

	xprintf ("UTM Position Output is %s",
		UTM_enabled ? "Enabled" : "Disabled");
}


#define INFO_DGPS       0x02
#define INFO_2D         0x04
#define INFO_ALTSET     0x08
#define INFO_FILTERED   0x10
static void rpt_8F_20 (TSIPPKT *rpt)
{
	unsigned char
		subpacket_id, info, nsvs, *buf, sv_prn[32];
	short
		week_num, datum_index, sv_IODC[32];
	double
		lat, lon, alt, time_of_fix;
	double
		londeg, latdeg, vel[3];
	short
		isv;

	buf = rpt->buf;
	/* unload rptbuf */
	if (rpt_0x8F20 (rpt,
		&subpacket_id, &info, &lat, &lon, &alt, vel,
		&time_of_fix,
		&week_num, &nsvs, sv_prn, sv_IODC, &datum_index))
	{
		parsed = BADLEN_PARSE;
		return;
	}
	/* convert from radians to degrees */
	latdeg = R2D * fabs(lat);
	londeg = R2D * fabs(lon);
	xprintf ("SPkt 8F%c20: %02d:%02d:%05.2f %4d:%09.6f %c %5d:%09.6f %c %10.2f ",
		 buf[1]?'*':'-',
		(short)fmod(time_of_fix/3600., 24.),
		(short)fmod(time_of_fix/60., 60.),
		fmod(time_of_fix, 60.),
		(short)latdeg, fmod (latdeg, 1.)*60.0,
		(lat<0.0)?'S':'N',
		(short)londeg, fmod (londeg, 1.)*60.0,
		(lon<0.0)?'W':'E',
		alt);
	show_crlf ();

	xprintf ("*   *%10.3f E %10.3f N %10.3f U         %s%s%s    Datum%3d",
		vel[0], vel[1], vel[2],
		(info & INFO_DGPS)?"Diff-":"",
		(info & INFO_2D)?((info&INFO_ALTSET)?"2D-AltSet":"2D-AltHold"):"3D",
		(info & INFO_FILTERED)?"-Filt":"",
		datum_index);
	show_crlf ();

	xprintf ("*   *SVs");
	for (isv = 0; isv < nsvs; isv++) {
		xprintf (" %02d", sv_prn[isv]);
	}
	xprintf ("          ( IODEs ");
	for (isv = 0; isv < nsvs; isv++) {
		xprintf ("%02X ", sv_IODC[isv]&0xFF);
	}
	xprintf (")");
}

static void rpt_8F (TSIPPKT *rpt)
{
	switch (rpt->buf[0]) {
	case 0x03:
		rpt_8F_03 (rpt);
		break;

	case 0x14:
		rpt_8F_14 (rpt);
		break;

	case 0x15:
		rpt_8F_15 (rpt);
		break;

	case 0x17:
		rpt_8F_17 (rpt);
		break;

	case 0x18:
		rpt_8F_18 (rpt);
		break;

	case 0x19:
		rpt_8F_19 (rpt);
		break;

	case 0x20:
		rpt_8F_20 (rpt);
		break;

	default:
		parsed = BADID_PARSE;
		break;
	}
}

void print_msg_table_header (unsigned char rptcode)
{
	/* force header is to help auto-output function */
	/* last_rptcode is to determine whether to print a header */
	/* for the first occurence of a series of reports */
	static unsigned char
		last_rptcode = 0;

	if (rptcode!=last_rptcode) switch (rptcode)
	{
	case 0x5A:
		/* supply a header in console output */
		xprintf ("Raw Measurement Data");
		show_crlf ();
		xprintf (
"   SV  Sample   SNR  Code Phase   Doppler    Seconds     Time of Meas");
		show_crlf();
		break;

	case 0x5B:
		xprintf ("Ephemeris Status");
		show_crlf ();
		xprintf (
"    SV     Time collected     Health  IODE        t oe           Fit   URA");
		show_crlf();
		break;

	case 0x5C:
		xprintf ("Tracking Info");
		show_crlf ();
		xprintf (
"   SV  C S Acq Eph   SNR     Time of Meas      Elev   Azim   Old Msec BadD DCol");
		show_crlf();
		break;

	case 0x85:
		xprintf ("Differential Correction Status");
		show_crlf ();
		xprintf (
"    SV  Status  StaH  SV H  IODE1  IODE2  Z-count time        PRC    RRC    DRC");
		show_crlf();
		break;
	}
	last_rptcode = rptcode;
}

static void unknown_rpt (TSIPPKT *rpt)
{
	short i;
	xprintf ("TSIP report packet ID %2Xh, length %d", rpt->code, rpt->cnt);
	if (parsed == BADLEN_PARSE) xprintf (": Bad length");
	if (parsed == BADID_PARSE) xprintf (": translation not supported");
	show_crlf ();
	for (i = 0; i < rpt->cnt; i++) {
		if ((i % 20) == 0) show_crlf ();
		xprintf (" %02X", rpt->buf[i]);
	}
}
/**/
void rpt_packet (TSIPPKT *rpt)
{

	parsed = GOOD_PARSE;

	show_crlf();

	/* print a header if this is the first of a series of messages */
	print_msg_table_header (rpt->code);

	switch (rpt->code) {
	case 0x3D:
		show_chan_A_config (rpt);
		break;

	case 0x40:
		rpt_almanac_data_page (rpt);
		break;

	case 0x41:
		rpt_GPS_time (rpt);
		break;

	case 0x42:
		rpt_single_ECEF_position (rpt);
		break;

	case 0x43:
		rpt_single_ECEF_velocity (rpt);
		break;

	case 0x44:
		rpt_4SV_selection (rpt);
		break;

	case 0x45:
		rpt_SW_version (rpt);
		break;

	case 0x46:
		rpt_rcvr_health (rpt);
		break;

	case 0x47:
		rpt_SNR_all_SVs (rpt);
		break;

	case 0x48:
		rpt_GPS_system_message (rpt);
		break;

	case 0x49:
		rpt_almanac_health_page (rpt);
		break;

	case 0x4A:
		rpt_single_lla_position (rpt);
		break;

	case 0x4B:
		rpt_rcvr_id_and_status (rpt);
		break;

	case 0x4C:
		rpt_operating_parameters (rpt);
		break;


	case 0x4D:
		rpt_oscillator_offset (rpt);
		break;

	case 0x4E:
		rpt_GPS_time_set_response (rpt);
		break;

	case 0x4F:
		rpt_UTC_offset (rpt);
		break;

	case 0x54:
		rpt_1SV_bias (rpt);
		break;

	case 0x55:
		rpt_io_opt (rpt);
		break;

	case 0x56:
		rpt_ENU_velocity (rpt);
		break;

	case 0x57:
		rpt_last_fix_info (rpt);
		break;

	case 0x58:
		rpt_GPS_system_data (rpt);
		break;

	case 0x59:
		rpt_SVs_enabled (rpt);
		break;

	case 0x5A:
		rpt_raw_msmt (rpt);
		break;

	case 0x5B:
		rpt_SV_ephemeris_status (rpt);
		break;

	case 0x5C:
		rpt_SV_tracking_status (rpt);
		break;

	case 0x5E:
		rpt_addl_fix_status (rpt);
		break;

	case 0x6D:
		rpt_allSV_selection (rpt);
		break;

	case 0x72:
		show_pos_filter (rpt);
		break;

	case 0x74:
		show_altitude_filter (rpt);
		break;

	case 0x76:
		show_track_mode (rpt);
		break;

	case 0x78:
		rpt_DGPS_maxage (rpt);
		break;

	case 0x82:
		rpt_DGPS_position_mode (rpt);
		break;

	case 0x83:
		rpt_double_ECEF_position (rpt);
		break;

	case 0x84:
		rpt_double_lla_position (rpt);
		break;

	case 0x85:
		rpt_dgps_info (rpt);
		break;

	case 0x8F:
		rpt_8F (rpt);
		break;

	default:
		parsed = BADID_PARSE;
		break;
	}

	if (parsed != GOOD_PARSE)
	{
		unknown_rpt (rpt);
	}
}


