/* Copyright 1997, 1998, 1999 University Corporation for Atmospheric Research

	This software is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	Author: Louis H. Estey <lou@unavco.ucar.edu>
	Date:   July 1999
		part of the BINEX source code release, see also:

	http://www.unavco.ucar.edu/software/binex
*/
#include "binex.h"

static unsigned short	ashtech_snr[2][3]= { 1,  90, 210, 1, 150, 250 };

unsigned long
next_BINEX_record
#if KR_C
	(file, rec)
FILE_INFO	*file;
unsigned char	*rec;
#else
	(FILE_INFO *file, unsigned char *rec)
#endif
{
	char		synchronization[5];
	unsigned char	j, n, n_length, crc[16];
	unsigned long	i, id, length, len_offset, mess_offset, crc_offset;
	static bool	first_pass= (bool)TRUE;

	switch (first_pass) {
	case TRUE:
		if (sizeof(char)  != 1 || sizeof(unsigned char)  != 1
		 || sizeof(short) != 2 || sizeof(unsigned short) != 2
		 || sizeof(long)  != 4 || sizeof(unsigned long)  != 4
		 || sizeof(float) != 4 || sizeof(double)         != 8) {
			fprintf(stderr, "next_BINEX_record(): compiler/system configuration results in non-compatible data type sizes\n");
			exit(-1);
		}
		teq.tr.offset= 0;
		first_pass= (bool)FALSE;
		break;
	case FALSE:
		if (file->fp != stdin)
			/* re-position pointer in file (in case of bad read on previous attempt)
			*/
			fseek(file->fp, teq.tr.offset, SEEK_SET);
	}

	/* read stream pointed to by file->fp until potential synchronization/endian byte is found (but might be data)
	*/
	do {
		if ((size_t)fread((void *)&rec[0], (size_t)1, (size_t)1, file->fp) < 1) {
			if (feof(file->fp))
				return(NORMAL_BINEX_EOF);
			else
				return(BAD_BINEX_READ);
		}
	} while (!feof(file->fp)
	  && rec[0] != BINEX_F_stx_LE && rec[0] != BINEX_F_stx_BE && rec[0] != BINEX_FBstx_LE && rec[0] != BINEX_FBstx_BE);

	if (file->fp != stdin)
		/* record position in file
		*/
		teq.tr.offset= ftell(file->fp);

	if (teq.trans.opt_X & DIAGNOSTICS) {
		sprintf(synchronization, "0x%02x", (int)rec[0]);
		file_position(file, -1L, synchronization);
	}

	/* byte reversal needed for this record?
	*/
	switch (teq.tr.little_endian) {
	case TRUE:
		/* processor is little-endian
		*/
		switch (rec[0]) {
		case BINEX_F_stx_BE:
		case BINEX_FBstx_BE:
			teq.tr.binex.bytes_reversed= (bool)TRUE;
			teq.tr.binex.little_endian_record= (bool)FALSE;
			break;
		default:
			teq.tr.binex.bytes_reversed= (bool)FALSE;
			teq.tr.binex.little_endian_record= (bool)TRUE;
		}
		break;
	case FALSE:
		/* processor is big-endian
		*/
		switch (rec[0]) {
		case BINEX_F_stx_LE:
		case BINEX_FBstx_LE:
			teq.tr.binex.bytes_reversed= (bool)TRUE;
			teq.tr.binex.little_endian_record= (bool)TRUE;
			break;
		default:
			teq.tr.binex.bytes_reversed= (bool)FALSE;
			teq.tr.binex.little_endian_record= (bool)FALSE;
		}
		break;
	}

	/* get the record ID
	*/
	id= read_ubnxi(file, &rec[i= 1], &n);
	switch (n) {
	case 0x00:
		return(BAD_BINEX_READ);
	}
	len_offset= i+= (unsigned long)n;

	if (teq.trans.opt_X & DIAGNOSTICS)
		record_type((int)id);

	/* get the record length
	*/
	length= read_ubnxi(file, &rec[i], &n);
	switch (n) {
	case 0x00:
		return(BAD_BINEX_READ);
	}
	n_length= n;

	mess_offset= i+= (unsigned long)n;

	/* get the record message
	*/
	if ((size_t)fread((void *)&rec[i], (size_t)1, (size_t)length, file->fp) < (size_t)length)
		return(BAD_BINEX_READ);

	i+= length;
	if      (length <     128)
		n= 0x01;
	else if (length <    4096)
		n= 0x02;
	else if (length < 1048576)
		n= 0x04;
	else
		n= 0x10;

	/* read checksum/CRC; then verify checksum/CRC: record ID bytes through to end of record message bytes
	*/
	if ((size_t)fread((void *)&rec[i], (size_t)1, (size_t)n, file->fp) < (size_t)n)
		return(BAD_BINEX_READ);

	crc_offset= 0;
	binex_crc(crc, rec + 1, (unsigned char *)NULL, &crc_offset, i-1, 0, teq.tr.binex.bytes_reversed);
	if (memcmp((void *)crc, (void *)&rec[i], (size_t)n))
		/* checksum/CRC mismatch
		*/
		return(BAD_BINEX_CHECKSUM);

	switch (rec[0]) {
	case BINEX_FBstx_LE:
	case BINEX_FBstx_BE:
		/* reversible record:
		   need to read and check reversed length bytes
		   and terminating synchronization byte
		*/
		i+= (unsigned long)n;
		if ((size_t)fread((void *)&rec[i], (size_t)1, (size_t)n_length, file->fp) < (size_t)n_length)
			return(BAD_BINEX_READ);

		for (j= 0; j < n_length; j++)
			if (rec[len_offset + j] != rec[i + n_length - 1 - j])
				return(BAD_BINEX_READ);

		i+= (unsigned long)n_length;
		if ((size_t)fread((void *)&rec[i], (size_t)1, (size_t)1, file->fp) < (size_t)1)
			return(BAD_BINEX_READ);

		switch (rec[0]) {
		case BINEX_FBstx_LE:
			if (rec[i] != BINEX_FBetx_LE)
				return(BAD_BINEX_READ);
			break;
		case BINEX_FBstx_BE:
			if (rec[i] != BINEX_FBetx_BE)
				return(BAD_BINEX_READ);
			break;
		}
	}
	/* at this point, rec points to a complete BINEX record,
	   from the initial synchronization/endian byte to the last byte
	   (which is either the last CRC byte for a forward-readable record,
	   or the terminating synchronization/endian byte for a reversible record)
	*/

	if (file->fp != stdin)
		/* record position in file:
		*/
		teq.tr.offset= ftell(file->fp);

	switch (id) {
	case BINEX_SITE:
		break;
	case BINEX_GNSS_NAV:
		return(decompose_binex_01(file, rec + mess_offset));
	case BINEX_GNSS_DATA:
		return(decompose_binex_02(file, rec + mess_offset));
	case BINEX_GNSS_DATA_PROTO:
		return(decompose_binex_7f(file, rec + mess_offset));
	case BINEX_SITE_DATA:
		break;
	}

	return (NO_KNOWN_BINEX);
}

/********************************************************************/
unsigned long
decompose_binex_01
#if KR_C
	(file, rec)
FILE_INFO	*file;
unsigned char	*rec;
#else
	(FILE_INFO *file, unsigned char *rec)
#endif
{
	/* function to decompose a BINEX record 0x01
	   to components of RINEX_NAV struct
	*/
	unsigned char	ret;
	unsigned long	type;

	switch (rec[0]) {
	case 0x01:
		if (!binex_extract_SV_id(rec[1], &nav.satellite.system)) {
			fprintf(stderr, "decompose_binex_01(): unknown satellite constellation (= 0x%02x)\n", (int)(rec[1] >> 5 & 0x03));
			return(NO_KNOWN_BINEX);
		}
		break;
	default:
		return(NO_KNOWN_BINEX);
	}

	if (ret= binary_NAV((int)nav.satellite.system.sys))
		return(ret);

	switch (rec[0]) {
	case 0x01:
		type= (unsigned long)NAV_IS_BINEX_01_01;
		break;
	}
	if (ret= binary_ephemeris(rec+2, type))
		return(ret);

	return (BINEX_GNSS_NAV);
}

/********************************************************************/
unsigned long
decompose_binex_7f
#if KR_C
	(file, rec)
FILE_INFO	*file;
unsigned char	*rec;
#else
	(FILE_INFO *file, unsigned char *rec)
#endif
{
	/* function to decompose a BINEX record 0x7f
	   to components of RINEX_OBS struct
	*/

	static unsigned char	ret;
	static double		receive_time;
	static unsigned long	minutes, offset;
	static unsigned short	msec;

	switch (rec[0]) {
	case 0x00:
		/* subrecord 0x00: prototype for JPL LEO mission support
		*/
		offset= 1L;
		extract_uint4(rec, &offset, &minutes);
		if (!(teq.trans.set_X & SET_GPS_WEEK))
			teq.tr.GPS_week= (int)floor((double)minutes/10080.);
		extract_uint2(rec, &offset, &msec);
		receive_time= 60.*fmod((double)minutes, 10080.) + (double)msec/1000.;

		switch (obs_epoch_clock_adjustment(file, &receive_time, 0., &obs.observation.epoch)) {
		case VALID_EPOCH:
			break;
		case CORRUPTED_EPOCH:
			return(NO_KNOWN_BINEX);
		default:
			binary_read_failure(INVALID_EPOCH, (unsigned char)BINEX_GNSS_DATA_PROTO, file);
		}

		if (ret= binary_GPS_only_OBS(file, rec, OBS_IS_BINEX_7f_00))
			return(NO_KNOWN_BINEX);

		if (!constellation_total(file, (rec[7] & 0x1f) + 0x01, 0x20))
			return(NO_KNOWN_BINEX);

		if (!constellation_array(file))
			return(NO_KNOWN_BINEX);

		(void)constellation_processing(file, (unsigned char *)NULL, rec, OBS_IS_BINEX_7f_00);
		break;
	default:
		return(NO_KNOWN_BINEX);
	}

	return (BINEX_GNSS_DATA_PROTO);
}

/********************************************************************/
void
binex_01_01_ephemeris
#if KR_C
	(rec)
unsigned char	*rec;
#else
	(unsigned char *rec)
#endif
{
	short		s;
	unsigned short	us, word;
	long		l, ToC, ToE, ToW;
	unsigned long	offset, day, ul;
	float		f;
	double		d;

	offset= 0;
	switch (nav.satellite.system.sys) {
	case NAVSTAR_GPS:
		extract_uint2(rec, &offset, &us);
		nav.satellite.ephemeris.type.GPS.gps_week= (double)adjust_week("Ashtech SNV record", (int)us, 1024);

		extract_sint4(rec, &offset, &ToW);
		nav.satellite.ephemeris.type.GPS.ToW= (double)ToW;
		GPS_to_epoch(&nav.satellite.ephemeris.ToW, (unsigned int)nav.satellite.ephemeris.type.GPS.gps_week, (double)ToW);

		extract_sint4(rec, &offset, &ToE);
		ToC= ToE;
		nav.satellite.ephemeris.type.GPS.ToE= (double)ToE;
		GPS_to_epoch(&nav.satellite.ephemeris.ToE, (unsigned int)nav.satellite.ephemeris.type.GPS.gps_week, (double)ToE);
		GPS_to_epoch(&nav.satellite.ephemeris.ToC, (unsigned int)nav.satellite.ephemeris.type.GPS.gps_week, (double)ToC);

		correct_nav_times(ToC, ToE, ToW);

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.TGD= (double)f;

		extract_sint4(rec, &offset, &l);
		nav.satellite.ephemeris.type.GPS.IODC= (double)l;
		
		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.af2= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.af1= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.af0= (double)f;

		extract_sint4(rec, &offset, &l);
		nav.satellite.ephemeris.type.GPS.IODE= (double)l;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.dn= (double)f * GPS_PI;

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.M);

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.e);

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.sqrt_a);

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Cic= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Crc= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Cis= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Crs= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Cuc= (double)f;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.Cus= (double)f;

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.OMEGA);

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.omega);

		extract_real8(rec, &offset, &nav.satellite.ephemeris.type.GPS.i);

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.OMEGA_dot= (double)f * GPS_PI;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.i_dot= (double)f * GPS_PI;

		extract_real4(rec, &offset, &f);
		nav.satellite.ephemeris.type.GPS.URA= (double)f/10.;

		extract_sint2(rec, &offset, &s);
		switch (teq.edit.opt_X & BINEX_OUT) {
		case BINEX_OUT:
			nav.satellite.ephemeris.type.GPS.health= (double)s;			/* store everything */
			break;
		default:
			nav.satellite.ephemeris.type.GPS.health= (double)(s >> 5 & 0x0001);	/* to obtain the MSB of the 6-bit word */
		}

		extract_sint2(rec, &offset, &s);
		switch (teq.edit.opt_X & BINEX_OUT) {
		case BINEX_OUT:
			nav.satellite.ephemeris.type.GPS.fit_interval= (double)s;		/* store everything */
			break;
		default:
			nav.satellite.ephemeris.type.GPS.fit_interval= (double)(s & 0x0001);
		}

		break;

	case GLONASS:
		fprintf(stderr, "binex_01_01_ephemeris(): GLONASS navigation message code not complete\n");
		exit(-1);
	}
}

/********************************************************************/
unsigned char
binex_7f_00_constellation
#if KR_C
	(rec, data_array)
unsigned char	*rec;
bool		data_array;
#else
	(unsigned char *rec, bool data_array)
#endif
{
	unsigned char	i, n, total;

	/* function to determine the current list of (GPS) SVs (the constellation):
	*/

	/* loop through the SVs
	*/
	total= (rec[7] & 0x1f) + 0x01;
	for (i= n= 0; i < total; i++)
		switch (binex_extract_SV_id(rec[8 + i], &obs.observation.constellation.satellite[n].system)) {
		case TRUE:
			n++;
			break;
		case FALSE:
			fprintf(stderr, "binex_7f_00_constellation(): unknown satellite constellation (= 0x%02x)\n", (int)(rec[8 + i] >> 5 & 0x03));
			break;
		}

	return (n);
}

/********************************************************************/
void
binex_7f_00_obs
#if KR_C
	(rec)
unsigned char	*rec;
#else
	(unsigned char *rec)
#endif
{
	static unsigned long	offset;
	static unsigned char	index, i, n, chn_as_lli, snr_l1, snr_l2, perr, mperr;
	static double		d, delta;
	static double		L1_to_L2= m_L2/m_L1;		/* converts L1-cycles to L2-cycles */
	bool			valid;

	/* function to determine get the observables from BINEX record 02 01
	*/

	/* loop through the SVs
	*/
	for (i= n= 0, offset= (unsigned long)obs.observation.constellation.total + 8L; i < obs.observation.constellation.total; i++)
		if (binex_extract_SV_id(rec[8 + i], &obs.observation.constellation.satellite[n].system)) {
			/* handling of "possible error" byte:
			*/
			switch (n) {
			case 0x00:
				extract_uint1(rec, &offset, &perr);
				mperr= perr;
				break;
			default:
				switch (mperr & 0x01) {
				case 0x00:
					perr= mperr;
					break;
				case 0x01:
					extract_uint1(rec, &offset, &perr);
					break;
				}
			}

			/* receiver channel ID, A/S, LLI:
			*/
			extract_uint1(rec, &offset, &chn_as_lli);
			obs.observation.constellation.satellite[n].rx_channel= (chn_as_lli & 0x1f) + 0x01;

			/* extract the observables:
			*/
			d= binex_extract_mGFZI(rec, &offset, &valid)/1000.;
			if ((index= obs.observation.observable.index[C1]) != 0xff) {
				obs.observation.constellation.satellite[n].data[index].u.pseudorange= d;
				obs.observation.constellation.satellite[n].data[index].lli= chn_as_lli >> 3 & BIT_2;
				obs.observation.constellation.satellite[n].data[index].err= perr >> 3 & BIT_0;
			}
			delta= binex_extract_mGFZI(rec, &offset, &valid)/1000.;
			if ((index= obs.observation.observable.index[P1]) != 0xff) {
				switch (valid) {
				case TRUE:
					obs.observation.constellation.satellite[n].data[index].u.pseudorange= d - delta;
					obs.observation.constellation.satellite[n].data[index].lli= chn_as_lli >> 3 & BIT_2;
					break;
				case FALSE:
					obs.observation.constellation.satellite[n].data[index].u.pseudorange= D_NO_DATA;
				}
				obs.observation.constellation.satellite[n].data[index].err= perr >> 5 & BIT_0;
			}
			delta= binex_extract_mGFZI(rec, &offset, &valid)/1000.;
			if ((index= obs.observation.observable.index[P2]) != 0xff) {
				switch (valid) {
				case TRUE:
					obs.observation.constellation.satellite[n].data[index].u.pseudorange= d - delta;
					obs.observation.constellation.satellite[n].data[index].lli= chn_as_lli >> 3 & BIT_2;
					break;
				case FALSE:
					obs.observation.constellation.satellite[n].data[index].u.pseudorange= D_NO_DATA;
				}
				obs.observation.constellation.satellite[n].data[index].err= perr >> 7 & BIT_0;
			}

			extract_uint1(rec, &offset, &snr_l1);
			if ((index= obs.observation.observable.index[S1]) != 0xff)
				obs.observation.constellation.satellite[n].data[index].u.snr= (double)snr_l1;
			extract_uint1(rec, &offset, &snr_l2);
			if ((index= obs.observation.observable.index[S2]) != 0xff)
				obs.observation.constellation.satellite[n].data[index].u.snr= (double)snr_l2;

			/* currently storing: L1(P1), L1(P1) - L1(CA), L1(P1) - L2(P2)
			*/
			d= binex_extract_mGFZI(rec, &offset, &valid)/10000.;
			delta= binex_extract_mGFZI(rec, &offset, &valid)/10000.;
			if ((index= obs.observation.observable.index[LA]) != 0xff) {
				switch (valid) {
				case TRUE:
					obs.observation.constellation.satellite[n].data[index].u.phase= d - delta;
					break;
				case FALSE:
					obs.observation.constellation.satellite[n].data[index].u.phase= D_NO_DATA;
				}
				obs.observation.constellation.satellite[n].data[index].lli=  chn_as_lli >> 3 & BIT_2;
				obs.observation.constellation.satellite[n].data[index].lli|= chn_as_lli >> 6 & BIT_0;
				obs.observation.constellation.satellite[n].data[index].sn= bernese_snr(ashtech_snr[0], (double)snr_l1);
				obs.observation.constellation.satellite[n].data[index].err= perr >> 2 & BIT_0;
			}
			if ((index= obs.observation.observable.index[L1]) != 0xff) {
				switch (teq.trans.opt_X & USE_CA_L1) {
				case USE_CA_L1:
					/* store L1(CA) as RINEX L1
					*/
					switch (valid) {
					case TRUE:
						obs.observation.constellation.satellite[n].data[index].u.phase= d - delta;
						break;
					case FALSE:
						obs.observation.constellation.satellite[n].data[index].u.phase= D_NO_DATA;
					}
					obs.observation.constellation.satellite[n].data[index].err= perr >> 2 & BIT_0;
					break;
				default:
					/* store L1(P1) as RINEX L1
					*/
					obs.observation.constellation.satellite[n].data[index].u.phase= d;
					obs.observation.constellation.satellite[n].data[index].err= perr >> 4 & BIT_0;
				}
				obs.observation.constellation.satellite[n].data[index].lli=  chn_as_lli >> 3 & BIT_2;
				obs.observation.constellation.satellite[n].data[index].lli|= chn_as_lli >> 6 & BIT_0;
				obs.observation.constellation.satellite[n].data[index].sn= bernese_snr(ashtech_snr[0], (double)snr_l1);
			}
			delta= binex_extract_mGFZI(rec, &offset, &valid)/10000.;
			if ((index= obs.observation.observable.index[L2]) != 0xff) {
				switch (valid) {
				case TRUE:
					obs.observation.constellation.satellite[n].data[index].u.phase= (d - delta)*L1_to_L2;
					obs.observation.constellation.satellite[n].data[index].lli=  chn_as_lli >> 3 & BIT_2;
					obs.observation.constellation.satellite[n].data[index].lli|= chn_as_lli >> 7 & BIT_0;
					obs.observation.constellation.satellite[n].data[index].sn= bernese_snr(ashtech_snr[1], (double)snr_l2);
					break;
				case FALSE:
					obs.observation.constellation.satellite[n].data[index].u.phase= D_NO_DATA;
				}
				obs.observation.constellation.satellite[n].data[index].err= perr >> 6 & BIT_0;
			}

			n++;
		}
}

/********************************************************************/
void
binex_observables_7f_00
#if KR_C
	(message, offset)
unsigned char	*message;
unsigned long	*offset;
#else
	(unsigned char *message, unsigned long *offset)
#endif
{
	unsigned char	n, chn_as_lli, CA_index, P1_index, P2_index, LA_index, L1_index, L2_index, S1_index, S2_index, snr, perr, mperr;
	double		delta, d, error;
	static double	L2_to_L1= m_L1/m_L2;		/* converts L2-cycles to L1-cycles */
	bool		valid;
	unsigned long	perr_offset;

	/* index values for the observables (for code readability only)
	*/
	CA_index= obs.observation.observable.index[C1];
	P1_index= obs.observation.observable.index[P1];
	P2_index= obs.observation.observable.index[P2];
	LA_index= obs.observation.observable.index[LA];
	L1_index= obs.observation.observable.index[L1];
	L2_index= obs.observation.observable.index[L2];
	S1_index= obs.observation.observable.index[S1];
	S2_index= obs.observation.observable.index[S2];

	/* initial byte for SV total:
	*/
	append_uint1(message, offset, obs.observation.constellation.total - 0x01);

	/* the constellation:
	*/
	for (n= 0, mperr= 0x00; n < obs.observation.constellation.total; n++) {
		append_uint1(message, offset, binex_build_SV_id(&obs.observation.constellation.satellite[n].system));
		switch (mperr & 0x01) {
		case 0x00:
			perr= mperr & 0x03;
			switch (LA_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[LA_index].err << 2;
			}
			switch (CA_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[CA_index].err << 3;
			}
			switch (L1_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[L1_index].err << 4;
			}
			switch (P1_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[P1_index].err << 5;
			}
			switch (L2_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[L2_index].err << 6;
			}
			switch (P2_index) {
			case 0xff:
				break;
			default:
				perr|= obs.observation.constellation.satellite[n].data[P2_index].err << 7;
			}
			switch (n) {
			case 0x00:
				mperr= perr;
			break;
			default:
				if (mperr != perr)
					mperr|= 0x01;
			}
			break;
		}
	}

	/* the data for each SV:
	*/
	for (n= 0; n < obs.observation.constellation.total; n++) {

		switch (mperr & 0x01) {
		case 0x00:
			/* all "possible error" bytes for all SVs are the same; store the master
			*/
			switch (n) {
			case 0x00:
				append_uint1(message, offset, mperr);
				break;
			}
			break;
		case 0x01:
			/* all "possible error" bytes for all SVs are different;
			   save the position and advance the message offset by one
			*/
			perr_offset= *offset;
			(*offset)++;
			break;
		}

		/* receiver channel ID, A/S, LLI:
		*/
		chn_as_lli=  (obs.observation.constellation.satellite[n].rx_channel - 0x01) & 0x1f;
		if (LA_index != 0xff) {
			/* A/S in bit 5; L1 loss-of-lock in bit 6
			*/
			chn_as_lli|= obs.observation.constellation.satellite[n].data[LA_index].lli << 3 & BIT_5;
			chn_as_lli|= obs.observation.constellation.satellite[n].data[LA_index].lli << 6 & BIT_6;
		}
		if (L1_index != 0xff) {
			/* A/S in bit 5; L1 loss-of-lock in bit 6
			*/
			chn_as_lli|= obs.observation.constellation.satellite[n].data[L1_index].lli << 3 & BIT_5;
			chn_as_lli|= obs.observation.constellation.satellite[n].data[L1_index].lli << 6 & BIT_6;
		}
		if (L2_index != 0xff) {
			/* A/S in bit 5; L2 loss-of-lock in bit 7
			*/
			chn_as_lli|= obs.observation.constellation.satellite[n].data[L2_index].lli << 3 & BIT_5;
			chn_as_lli|= obs.observation.constellation.satellite[n].data[L2_index].lli << 7 & BIT_7;
		}
		append_uint1(message, offset, chn_as_lli);

		switch (CA_index) {
		case 0xff:
			d= D_NO_DATA;
			perr= 0x00;
			break;
		default:
			d= round(obs.observation.constellation.satellite[n].data[CA_index].u.pseudorange, 1e-3);
			perr= obs.observation.constellation.satellite[n].data[CA_index].err << 3;
			break;
		}
		binex_append_mGFZI(message, offset, d*1000.);

		switch (P1_index) {
		case 0xff:
			delta= MAXFLOAT;
			break;
		default:
			if (obs.observation.constellation.satellite[n].data[P1_index].u.pseudorange != D_NO_DATA)
				delta= 1000.*(d - round(obs.observation.constellation.satellite[n].data[P1_index].u.pseudorange, 1e-3));
			else
				delta= MAXFLOAT;
			perr|= obs.observation.constellation.satellite[n].data[P1_index].err << 5;
		}
		binex_append_mGFZI(message, offset, delta);

		switch (P2_index) {
		case 0xff:
			delta= MAXFLOAT;
			break;
		default:
			if (obs.observation.constellation.satellite[n].data[P2_index].u.pseudorange != D_NO_DATA)
				delta= 1000.*(d - round(obs.observation.constellation.satellite[n].data[P2_index].u.pseudorange, 1e-3));
			else
				delta= MAXFLOAT;
			perr|= obs.observation.constellation.satellite[n].data[P2_index].err << 7;
		}
		binex_append_mGFZI(message, offset, delta);

		switch (S1_index) {
		case 0xff:
			snr= 0x00;
			break;
		default:
			snr= (unsigned char)obs.observation.constellation.satellite[n].data[S1_index].u.snr;
		}
		append_uint1(message, offset, snr);

		switch (S2_index) {
		case 0xff:
			snr= 0x00;
			break;
		default:
			snr= (unsigned char)obs.observation.constellation.satellite[n].data[S2_index].u.snr;
		}
		append_uint1(message, offset, snr);

		/* currently storing: L1(P1), L1(P1) - L1(CA), L1(P1) - L2(P2)
		*/
		switch (L1_index) {
		case 0xff:
			d= D_NO_DATA;
			break;
		default:
			d= round(obs.observation.constellation.satellite[n].data[L1_index].u.phase, 1e-4);
			perr|= obs.observation.constellation.satellite[n].data[L1_index].err << 4;
			break;
		}
		binex_append_mGFZI(message, offset, d*10000.);

		switch (LA_index) {
		case 0xff:
			delta= MAXFLOAT;
			break;
		default:
			if (obs.observation.constellation.satellite[n].data[LA_index].u.phase != D_NO_DATA)
				delta= 10000.*(d - round(obs.observation.constellation.satellite[n].data[LA_index].u.phase, 1e-4));
			else
				delta= MAXFLOAT;
			perr|= obs.observation.constellation.satellite[n].data[LA_index].err << 2;
		}
		binex_append_mGFZI(message, offset, delta);

		switch (L2_index) {
		case 0xff:
			delta= MAXFLOAT;
			break;
		default:
			if (obs.observation.constellation.satellite[n].data[L2_index].u.phase != D_NO_DATA)
				delta= 10000.*((d - round(obs.observation.constellation.satellite[n].data[L2_index].u.phase, 1e-4)*L2_to_L1));
			else
				delta= MAXFLOAT;
			perr|= obs.observation.constellation.satellite[n].data[L2_index].err << 6;
		}
		binex_append_mGFZI(message, offset, delta);

		/* "possible error" byte
		*/
		switch (mperr & 0x01) {
		case 0x00:
			/* all "possible error" bytes for all SVs are the same; first already saved
			*/
			break;
		case 0x01:
			/* store each "possible error" byte for each SV, flagging that they differ
			*/
			append_uint1(message, &perr_offset, perr | 0x01);
			break;
		}
	}
}

/********************************************************************/
void
binex_nav_message_01_01
#if KR_C
	(message, offset)
unsigned char	*message;
unsigned long	*offset;
#else
	(unsigned char *message, unsigned long *offset)
#endif
{
	append_uint1(message, offset, binex_build_SV_id(&nav.satellite.system));

	switch (nav.satellite.system.sys) {
	case NAVSTAR_GPS:
		append_uint2(message, offset, (unsigned short)nav.satellite.ephemeris.type.GPS.gps_week);
		append_sint4(message, offset,           (long)nav.satellite.ephemeris.type.GPS.ToW);
		/* ToC assumed equal to ToE:
		*/
		append_sint4(message, offset,           (long)nav.satellite.ephemeris.type.GPS.ToE);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.TGD);
		append_sint4(message, offset,           (long)nav.satellite.ephemeris.type.GPS.IODC);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.af2);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.af1);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.af0);
		append_sint4(message, offset,           (long)nav.satellite.ephemeris.type.GPS.IODE);
		append_real4(message, offset,         (float)(nav.satellite.ephemeris.type.GPS.dn/GPS_PI));
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.M);
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.e);
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.sqrt_a);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Cic);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Crc);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Cis);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Crs);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Cuc);
		append_real4(message, offset,          (float)nav.satellite.ephemeris.type.GPS.Cus);
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.OMEGA);
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.omega);
		append_real8(message, offset,         (double)nav.satellite.ephemeris.type.GPS.i);
		append_real4(message, offset,         (float)(nav.satellite.ephemeris.type.GPS.OMEGA_dot/GPS_PI));
		append_real4(message, offset,         (float)(nav.satellite.ephemeris.type.GPS.i_dot/GPS_PI));
		append_real4(message, offset,         (float)(nav.satellite.ephemeris.type.GPS.URA*10.));
		append_uint2(message, offset, (unsigned short)nav.satellite.ephemeris.type.GPS.health);
		append_uint2(message, offset, (unsigned short)nav.satellite.ephemeris.type.GPS.fit_interval);
		break;

	case GLONASS:
		fprintf(stderr, "binex_nav_message_01_01(): GLONASS navigation message code not complete\n");
		exit(-1);
	}
}

/********************************************************************/
