/* 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 "general.h"

unsigned long
read_ubnxi
#if KR_C
	(file, rec, n)
FILE_INFO	*file;
unsigned char	*rec, *n;
#else
	(FILE_INFO *file, unsigned char *rec, unsigned char *n)
#endif
{
	*n= 0x00;
	do {
		if ((size_t)fread((void *)&rec[*n], (size_t)1, (size_t)1, file->fp) < 1) {
			*n= 0x00;
			return(0L);
		}
		switch (++(*n)) {
		case 0x01:
		case 0x02:
		case 0x03:
			switch (rec[*n - 0x01] & 0x80) {
			case 0x80:
				break;
			default:
				return(ubnxi_to_uint4(rec, *n));
			}
			break;
		}
	} while (*n < 0x04);

	*n= 0x00;
	return (0L);
}

/********************************************************************/
unsigned long
ubnxi_to_uint4
#if KR_C
	(rec, n)
unsigned char	*rec, n;
#else
	(unsigned char *rec, unsigned char n)
#endif
{
	unsigned char	i;
	unsigned long	ul;

	for (i= 0, ul= 0L; i < n; i++)
		switch (teq.tr.binex.little_endian_record) {
		case TRUE:
			ul|= (unsigned long)(rec[i] & (n < 0x03 ? 0x7f : 0xff)) << 7*i;
			break;
		case FALSE:
			ul|= (unsigned long)(rec[i] & (n < 0x03 ? 0x7f : 0xff)) << 7*(n-0x01-i);
			break;
		}

	return(ul);
}

/********************************************************************/
void
binex_f_stx
#if KR_C
	(_out)
FILE	*_out;
#else
	(FILE *_out)
#endif
{
	unsigned char	buff;
	static bool	first_pass= (bool)TRUE;

	/* "forward-readable only" synchronization and endian byte:
	*/
	switch (teq.tr.little_endian) {
	case TRUE:
		/* little-endian processor/compiler: synchronization byte = 0xc2
		*/
		buff= BINEX_F_stx_LE;
		break;
	case FALSE:
		/* big-endian processor/compiler: synchronization byte = 0xe2
		*/
		buff= BINEX_F_stx_BE;
		break;
	}
	(void)fwrite((void *)&buff, 1, 1, _out);

	if (first_pass) {
		teq.tr.binex.bytes_reversed= (bool)FALSE;
		teq.tr.binex.little_endian_record= teq.tr.little_endian;
		first_pass= (bool)TRUE;
	}
}

/********************************************************************/
void
binex_append_time
#if KR_C
	(message, offset, sec_resolution)
unsigned char	*message;
unsigned long	*offset;
unsigned char	sec_resolution;
#else
	(unsigned char *message, unsigned long *offset, unsigned char sec_resolution)
#endif
{
	unsigned char	s;
	double		frac;

	/* teqc epochs start at 1.0 Jan 1980, whereas GPS time starts at 6.0 Jan 1980,
	   a difference of 5 days = 7200 minutes
	*/
	append_uint4(message, offset, (unsigned long)(obs.observation.epoch.minutes - 7200));

	/* note: cases 0x05 and 0x06 need to be tested
	*/
	switch (sec_resolution) {
	case 0x00:
		/* resolution only to minutes
		*/
		break;
	case 0x01:
		/* resolution to 0.25 seconds
		*/
		append_uint1(message, offset, (unsigned char)floor(obs.observation.epoch.sec*4. + 0.5));
		break;
	case 0x02:
		/* resolution to 0.001 seconds
		*/
		append_uint2(message, offset, (unsigned short)floor(obs.observation.epoch.sec*1000. + 0.5));
		break;
	case 0x04:
		/* resolution to 20 nanoseconds
		*/
		append_uint4(message, offset, (unsigned long)floor(obs.observation.epoch.sec*50e6 + 0.5));
		break;
	case 0x05:
		/* resolution to 0.1 nanoseconds:

			 6 bits used to store integer seconds; "1" = 1 second
			34 bits used to store nearest fractional 0.1 nanoseconds; "1" = 0.1 nanosecond
		*/
		s= (unsigned char)floor(round(obs.observation.epoch.sec, 1e-10));
		frac= floor((obs.observation.epoch.sec - (double)s)*1e10 + 0.5);
		switch (teq.tr.little_endian) {
		case TRUE:
			append_uint1(message, offset, s | (0xc0 & (unsigned char)fmod(frac, 4.) << 6));
			append_uint4(message, offset, (unsigned long)(frac/4.));
			break;
		case FALSE:
			append_uint1(message, offset, (s << 2) | (0x03 & (unsigned char)(frac/4294967296.)));
			append_uint4(message, offset, (unsigned long)fmod(frac, 4294967296.));
			break;
		}
		break;
	case 0x06:
		/* resolution to 0.25 picoseconds:

			 6 bits used to store integer seconds; "1" = 1 second
			42 bits used to store nearest fractional 0.25 picoseconds; "1" = 0.25 picosecond
		*/
		s= (unsigned char)floor(round(obs.observation.epoch.sec, 2.5e-13));
		frac= floor((obs.observation.epoch.sec - (double)s)*4e12 + 0.5);
		switch (teq.tr.little_endian) {
		case TRUE:
			append_uint2(message, offset, (unsigned short)s | (0xffc0 & (unsigned short)fmod(frac, 1024.) << 6));
			append_uint4(message, offset, (unsigned long)(frac/1024.));
			break;
		case FALSE:
			append_uint2(message, offset, ((unsigned short)s << 10) | (0x03ff & (unsigned short)(frac/4294967296.)));
			append_uint4(message, offset, (unsigned long)fmod(frac, 4294967296.));
			break;
		}
		break;
	case 0x08:
		/* 8-byte floating-point resolution
		*/
		append_real8(message, offset, obs.observation.epoch.sec);
		break;
	default:
		fprintf(stderr, "! Error ! binex_append_time(): case 0x%02 not found\n", (int)sec_resolution);
		exit(-1);
	}
}

/********************************************************************/
void
binex_append_mGFZi
#if KR_C
	(message, offset, delta, eight_byte)
unsigned char	*message;
unsigned long	*offset;
double		delta;
bool		eight_byte;
#else
	(unsigned char *message, unsigned long *offset, double delta, bool eight_byte)
#endif
{
	/* value        bits of leading byte
	   -----       -----------------------
			L -> M:  big   endian (absolute value is in the least significant bits)
			M <- L: little endian (absolute value is in the most significant bits)

	   < 64		....00:  +  6-bit int
			....10:  -  6-bit int

	   < 8254	...001:  + 13-bit int
			...101:  - 13-bit int

	   < 268443709	..0011:  + 28-bit int
			..1011:  - 28-bit int

	   else		..0111:  + 60-bit int
			..1111:  - 60-bit int
	*/
	static double	double_limit= MAXFLOAT/10.;
	double		abs_delta;
	unsigned char	sgn_delta;
	unsigned char	uc;
	unsigned short	us;
	unsigned long	ul;

	if (delta > double_limit) {
		/* store in unsigned char: "-0" == 0x02 = 00000010; 0x40 = 01000000
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			uc= 0x02;
			break;
		case FALSE:
			uc= 0x40;
			break;
		}
		append_uint1(message, offset, uc);
		return;
	}

	abs_delta= floor(fabs(delta) + 0.5);
	sgn_delta= delta < 0. ? 0x01 : 0x00;
	if (abs_delta < 64.) {			/* 2^6 */
		/* store in unsigned char: 0xfc = 11111100; 0x3f = 00111111
		   note: any "-0" representation is converted to the "+0" representation
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			uc= (sgn_delta << 1) | 0xfc & (unsigned char)abs_delta << 2;
			switch (uc) {
			case 0x02:
				uc= 0x00;
				break;
			}
			break;
		case FALSE:
			uc= (sgn_delta << 6) | 0x3f & (unsigned char)abs_delta;
			switch (uc) {
			case 0x40:
				uc= 0x00;
				break;
			}
			break;
		}
		append_uint1(message, offset, uc);
	}
	else if (abs_delta < 8254.) {		/* 2^13 + 62 */
		/* store in unsigned short: 0xf9 = 11111001; 0x9f = 10011111
		*/
		abs_delta-= 62.;

		/* note: values of -1, -0, +0, and +1 are reserved for future use
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			us= ((unsigned short)sgn_delta << 2) | 0xfff9 & (0x0001 | (unsigned short)abs_delta << 3);
			break;
		case FALSE:
			us= ((unsigned short)sgn_delta << 13) | 0x9fff & (0x8000 | (unsigned short)abs_delta);
			break;
		}
		append_uint2(message, offset, us);
	}
	else if (abs_delta < 268443709.) {	/* 2^28 + 8253 */
		/* store in unsigned long: 0xf3 = 11110011; 0xcf = 11001111
		*/
		abs_delta-= 8253.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta << 3) | 0xfffffff3 & (0x00000003 | (unsigned long)abs_delta << 4);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 28) | 0xcfffffff & (0xc0000000 | (unsigned long)abs_delta);
			break;
		}
		append_uint4(message, offset, ul);
	}
	else {
		abs_delta-= 268443708.;

		switch (eight_byte) {
		case FALSE:
			/* store in an unsigned long and an unsigned short: 0xf7 = 11110111; 0xef = 11101111; 2^28 = 268435456; 2^16 = 65536
			*/
			switch (teq.tr.little_endian) {
			case TRUE:
				ul= ((unsigned long)sgn_delta << 3) | 0xfffffff7 & (0x00000007 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
				append_uint4(message, offset, ul);
				us= (unsigned short)(abs_delta/268435456.);
				break;
			case FALSE:
				ul= ((unsigned long)sgn_delta << 28) | 0xefffffff & (0xe0000000 | (unsigned long)(abs_delta/65536.));
				append_uint4(message, offset, ul);
				us= (unsigned short)fmod(abs_delta, 65536.);
				break;
			}
			append_uint2(message, offset, us);
			break;
		case TRUE:
			/* store in two unsigned longs: 0xf7 = 11110111; 0xef = 11101111; 2^28 = 268435456; 2^32 = 4294967296
			*/
			switch (teq.tr.little_endian) {
			case TRUE:
				ul= ((unsigned long)sgn_delta << 3) | 0xfffffff7 & (0x00000007 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
				append_uint4(message, offset, ul);
				ul= (unsigned long)(abs_delta/268435456.);
				break;
			case FALSE:
				ul= ((unsigned long)sgn_delta << 28) | 0xefffffff & (0xe0000000 | (unsigned long)(abs_delta/4294967296.));
				append_uint4(message, offset, ul);
				ul= (unsigned long)fmod(abs_delta, 4294967296.);
				break;
			}
			append_uint4(message, offset, ul);
			break;
		}
	}
}

/********************************************************************/
void
binex_append_mGFZI
#if KR_C
	(message, offset, delta)
unsigned char	*message;
unsigned long	*offset;
double		delta;
#else
	(unsigned char *message, unsigned long *offset, double delta)
#endif
{
	/* value        bits of leading byte
	   -----       -----------------------
			L  ->  M:  big   endian (absolute value is in the least significant bits)
			M  <-  L: little endian (absolute value is in the most significant bits)

	   <   2^4	....0000:  +  4-bit int <               16 = 0x0000000000000010 (reserving -0)
			....1000:  -  4-bit int

	   < ~2^12	....0001:  + 12-bit int <             4110 = 0x000000000000100e (reserving +-1, +-0)
			....1001:  - 12-bit int

	   < ~2^20	....0010:  + 20-bit int <          1052685 = 0x000000000010100d (reserving -0)
			....1010:  - 20-bit int

	   < ~2^28	....0011:  + 28-bit int <        269488140 = 0x000000001010100c (reserving -0)
			....1011:  - 28-bit int

	   < ~2^36	....0100:  + 36-bit int <      68988964875 = 0x000000101010100b (reserving -0)
			....1100:  - 36-bit int

	   < ~2^44	....0101:  + 44-bit int <   17661175009290 = 0x000010101010100a (reserving -0)
			....1101:  - 44-bit int

	   < ~2^52	....0110:  + 52-bit int < 4521260802379785 = 0x0010101010101009 (reserving -0)
			....1110:  - 52-bit int

	   < ~2^60	....0111:  + 60-bit int <                    0x1010101010101008 (reserving -0)
			....1111:  - 60-bit int
	*/
	static double	double_limit= MAXFLOAT/10.;
	double		abs_delta;
	unsigned char	sgn_delta;
	unsigned char	uc;
	unsigned short	us;
	unsigned long	ul;

	if (delta > double_limit) {
		/* store in unsigned char: "-0" == 0x08 = 00001000; 0x80 = 10000000
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			uc= 0x08;
			break;
		case FALSE:
			uc= 0x80;
			break;
		}
		append_uint1(message, offset, uc);
		return;
	}

	abs_delta= floor(fabs(delta) + 0.5);
	sgn_delta= delta < 0. ? 0x01 : 0x00;
	if (abs_delta < 16.) {
		/* store in unsigned char: 0xf0 = 11110000; 0x0f = 00001111
		   note: any "-0" representation is converted to the "+0" representation
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			uc= (sgn_delta << 3) | 0xf0 & (unsigned char)abs_delta << 4;
			switch (uc) {
			case 0x08:
				uc= 0x00;
				break;
			}
			break;
		case FALSE:
			uc= (sgn_delta << 7) | 0x0f & (unsigned char)abs_delta;
			switch (uc) {
			case 0x80:
				uc= 0x00;
				break;
			}
			break;
		}
		append_uint1(message, offset, uc);
	}
	else if (abs_delta < 4110.) {
		/* store in unsigned short: 0xf1 = 11110001; 0x1f = 00011111
		*/
		abs_delta-= 14.;

		/* note: values of -1, -0, +0, and +1 are reserved for future use
		*/
		switch (teq.tr.little_endian) {
		case TRUE:
			us= ((unsigned short)sgn_delta <<  3) | 0xfff1 & (0x0001 | (unsigned short)abs_delta << 4);
			break;
		case FALSE:
			us= ((unsigned short)sgn_delta << 15) | 0x1fff & (0x1000 | (unsigned short)abs_delta);
			break;
		}
		append_uint2(message, offset, us);
	}
	else if (abs_delta < 1052685.) {
		/* store in 3 bytes of unsigned long: 0xf2 = 11110010; 0x2f = 00101111
		*/
		abs_delta-= 4109.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0x00fffff2 & (0x00000002 | (unsigned long)abs_delta << 4);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x2fffffff & (0x20000000 | (unsigned long)abs_delta << 8);
			break;
		}
		append_uint3(message, offset, ul);
	}
	else if (abs_delta < 269488140.) {
		/* store in unsigned long: 0xf3 = 11110011; 0x3f = 00111111
		*/
		abs_delta-= 1052684.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0xfffffff3 & (0x00000003 | (unsigned long)abs_delta << 4);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x3fffffff & (0x30000000 | (unsigned long)abs_delta);
			break;
		}
		append_uint4(message, offset, ul);
	}
	else if (abs_delta < 68988964875.) {
		/* store in an unsigned long and an unsigned char: 0xf4 = 11110100; 0x4f = 01001111; 2^28 = 268435456; 2^8 = 256
		*/
		abs_delta-= 269488139.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0xfffffff4 & (0x00000004 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
			append_uint4(message, offset, ul);
			uc= (unsigned char)(abs_delta/268435456.);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x4fffffff & (0x40000000 | (unsigned long)(abs_delta/256.));
			append_uint4(message, offset, ul);
			uc= (unsigned char)fmod(abs_delta, 256.);
			break;
		}
		append_uint1(message, offset, uc);
	}
	else if (abs_delta < 17661175009290.) {
		/* store in an unsigned long and an unsigned short: 0xf5 = 11110101; 0x5f = 01011111; 2^28 = 268435456; 2^16 = 65536
		*/
		abs_delta-= 68988964874.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0xfffffff5 & (0x00000005 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
			append_uint4(message, offset, ul);
			us= (unsigned short)(abs_delta/268435456.);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x5fffffff & (0x50000000 | (unsigned long)(abs_delta/65536.));
			append_uint4(message, offset, ul);
			us= (unsigned short)fmod(abs_delta, 65536.);
			break;
		}
		append_uint2(message, offset, us);
	}
	else if (abs_delta < 4521260802379785.) {
		/* store in an unsigned long and 3 bytes of unsigned long: 0xf6 = 11110110; 0x6f = 01101111; 2^28 = 268435456; 2^24 = 16777216
		*/
		abs_delta-= 17661175009289.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0xfffffff6 & (0x00000006 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
			append_uint4(message, offset, ul);
			ul= (unsigned long)(abs_delta/268435456.);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x6fffffff & (0x60000000 | (unsigned long)(abs_delta/16777216.));
			append_uint4(message, offset, ul);
			ul= (unsigned long)fmod(abs_delta, 16777216.) << 8;
			break;
		}
		append_uint3(message, offset, ul);
	}
	else {
		/* store in two unsigned longs: 0xf7 = 11110111; 0x7f = 01111111; 2^28 = 268435456; 2^32 = 4294967296
		*/
		abs_delta-= 4521260802379784.;

		switch (teq.tr.little_endian) {
		case TRUE:
			ul= ((unsigned long)sgn_delta <<  3) | 0xfffffff7 & (0x00000007 | (unsigned long)fmod(abs_delta, 268435456.) << 4);
			append_uint4(message, offset, ul);
			ul= (unsigned long)(abs_delta/268435456.);
			break;
		case FALSE:
			ul= ((unsigned long)sgn_delta << 31) | 0x7fffffff & (0x70000000 | (unsigned long)(abs_delta/4294967296.));
			append_uint4(message, offset, ul);
			ul= (unsigned long)fmod(abs_delta, 4294967296.);
			break;
		}
		append_uint4(message, offset, ul);
	}
}

/********************************************************************/
double
binex_extract_mGFZi
#if KR_C
	(message, offset, valid, eight_byte)
unsigned char	*message;
unsigned long	*offset;
bool		*valid, eight_byte;
#else
	(unsigned char *message, unsigned long *offset, bool *valid, bool eight_byte)
#endif
{
	static double	abs_delta;
	unsigned char	uc, i, flag;
	unsigned short	us;
	unsigned long	ul;
	short		sgn_delta;

	switch (teq.tr.binex.little_endian_record) {
	case TRUE:
		flag= message[*offset] & 0x0f;
		break;
	case FALSE:
		/* rotate the 4 flag bits in the byte:
		*/
		for (uc= 0x01, i= 0x04, flag= 0x00; uc < 0x10; uc+= 0x02, i+= 0x01)
			flag|= (unsigned char)(((unsigned long)message[*offset] & (0x01 << i)) >> uc);
		break;
	}

	*valid= (bool)TRUE;
	sgn_delta= 1;
	abs_delta= 0.;
	switch (flag & 0x03) {
	case 0x02:
		sgn_delta= -1;
		/* no break
		*/
	case 0x00:
		/* use 1 byte:
		*/
		extract_uint1(message, offset, &uc);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)((unsigned long)uc >> 2);
			break;
		case 0x00:
			abs_delta= (double)((unsigned long)uc & 0x3f);
			break;
		}
		if (sgn_delta == -1 && abs_delta == 0.) {
			/* "-0" reserved for "no data" indicator
			*/
			*valid= (bool)FALSE;
			return(D_NO_DATA);
		}

		return((double)sgn_delta * abs_delta);
	default:
		switch (flag & 0x07) {
		case 0x05:
			sgn_delta= -1;
			/* no break
			*/
		case 0x01:
			/* use 2 bytes:
			*/
			extract_uint2(message, offset, &us);
			switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
			case 0x01:
				abs_delta= (double)((unsigned long)us >> 3);
				break;
			case 0x00:
				abs_delta= (double)((unsigned long)us & 0x1fff);
				break;
			}
			return((double)sgn_delta * (62. + abs_delta));
		default:
			switch (flag & 0x0f) {
			case 0x0b:
				sgn_delta= -1;
				/* no break
				*/
			case 0x03:
				/* use 4 bytes:
				*/
				extract_uint4(message, offset, &ul);
				switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
				case 0x01:
					abs_delta= (double)(ul >> 4);
					break;
				case 0x00:
					abs_delta= (double)(ul & 0x0fffffff);
					break;
				}
				return((double)sgn_delta * (8253. + abs_delta));
			case 0x0f:
				sgn_delta= -1;
				/* no break
				*/
			case 0x07:
				extract_uint4(message, offset, &ul);
				switch (eight_byte) {
				case FALSE:
					/* use 6 bytes:
					*/
					switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
					case 0x01:
						abs_delta= (double)(ul >> 4);
						extract_uint2(message, offset, &us);
						abs_delta+= (double)us*268435456.;
						break;
					case 0x00:
						abs_delta= (double)(ul & 0x0fffffff)*65536.;
						extract_uint2(message, offset, &us);
						abs_delta+= (double)us;
						break;
					}
					break;
				case TRUE:
					/* use 8 bytes:
					*/
					switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
					case 0x01:
						abs_delta= (double)(ul >> 4);
						extract_uint4(message, offset, &ul);
						abs_delta+= (double)ul*268435456.;
						break;
					case 0x00:
						abs_delta= (double)(ul & 0x0fffffff)*4294967296.;
						extract_uint4(message, offset, &ul);
						abs_delta+= (double)ul;
						break;
					}
					break;
				}
				return((double)sgn_delta * (268443708. + abs_delta));
			default:
				/* something really screwed up!
				*/
				fprintf(stderr, "binex_extract_mGFZi(): leading byte = 0x%02\n", (int)uc);
				*valid= (bool)FALSE;
				return(0.);
			}
		}
	}
}

/********************************************************************/
double
binex_extract_mGFZI
#if KR_C
	(message, offset, valid)
unsigned char	*message;
unsigned long	*offset;
bool		*valid;
#else
	(unsigned char *message, unsigned long *offset, bool *valid)
#endif
{
	static double	abs_delta;
	unsigned char	uc, i, flag;
	unsigned short	us;
	unsigned long	ul;
	short		sgn_delta;

	switch (teq.tr.binex.little_endian_record) {
	case TRUE:
		flag= message[*offset] & 0x0f;
		break;
	case FALSE:
		flag= message[*offset] >> 4 & 0x0f;
		break;
	}

	*valid= (bool)TRUE;
	sgn_delta= 1;
	abs_delta= 0.;
	switch (flag) {
	case 0x08:
		sgn_delta= -1;
		/* no break
		*/
	case 0x00:
		/* use 1 byte:
		*/
		extract_uint1(message, offset, &uc);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)((unsigned long)uc >> 4);
			break;
		case 0x00:
			abs_delta= (double)((unsigned long)uc & 0x0f);
			break;
		}
		if (sgn_delta == -1 && abs_delta == 0.) {
			/* "-0" reserved for "no data" indicator
			*/
			*valid= (bool)FALSE;
			return(D_NO_DATA);
		}

		return((double)sgn_delta * abs_delta);

	case 0x09:
		sgn_delta= -1;
		/* no break
		*/
	case 0x01:
		/* use 2 bytes:
		*/
		extract_uint2(message, offset, &us);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)((unsigned long)us >> 4);
			break;
		case 0x00:
			abs_delta= (double)((unsigned long)us & 0x0fff);
			break;
		}
		return((double)sgn_delta * (14. + abs_delta));

	case 0x0a:
		sgn_delta= -1;
		/* no break
		*/
	case 0x02:
		/* use 3 bytes:
		*/
		extract_uint3(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)((unsigned long)ul >> 4);
			break;
		case 0x00:
			abs_delta= (double)((unsigned long)ul >> 8 & 0x000fffff);
			break;
		}
		return((double)sgn_delta * (4109. + abs_delta));

	case 0x0b:
		sgn_delta= -1;
		/* no break
		*/
	case 0x03:
		/* use 4 bytes:
		*/
		extract_uint4(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)(ul >> 4);
			break;
		case 0x00:
			abs_delta= (double)(ul & 0x0fffffff);
			break;
		}
		return((double)sgn_delta * (1052684. + abs_delta));

	case 0x0c:
		sgn_delta= -1;
		/* no break
		*/
	case 0x04:
		/* use 5 bytes:
		*/
		extract_uint4(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)(ul >> 4);
			extract_uint1(message, offset, &uc);
			abs_delta+= (double)uc*268435456.;
			break;
		case 0x00:
			abs_delta= (double)(ul & 0x0fffffff)*256.;
			extract_uint1(message, offset, &uc);
			abs_delta+= (double)uc;
			break;
		}
		return((double)sgn_delta * (269488139. + abs_delta));

	case 0x0d:
		sgn_delta= -1;
		/* no break
		*/
	case 0x05:
		/* use 6 bytes:
		*/
		extract_uint4(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)(ul >> 4);
			extract_uint2(message, offset, &us);
			abs_delta+= (double)us*268435456.;
			break;
		case 0x00:
			abs_delta= (double)(ul & 0x0fffffff)*65536.;
			extract_uint2(message, offset, &us);
			abs_delta+= (double)us;
			break;
		}
		return((double)sgn_delta * (68988964874. + abs_delta));

	case 0x0e:
		sgn_delta= -1;
		/* no break
		*/
	case 0x06:
		/* use 7 bytes:
		*/
		extract_uint4(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)(ul >> 4);
			extract_uint4(message, offset, &ul);
			abs_delta+= (double)ul*268435456.;
			break;
		case 0x00:
			abs_delta= (double)(ul & 0x0fffffff)*16777216.;
			extract_uint4(message, offset, &ul);
			abs_delta+= (double)(ul >> 8);
			break;
		}
		return((double)sgn_delta * (17661175009289. + abs_delta));

	case 0x0f:
		sgn_delta= -1;
		/* no break
		*/
	case 0x07:
		/* use 8 bytes:
		*/
		extract_uint4(message, offset, &ul);
		switch (teq.tr.binex.bytes_reversed & 0x01 ^ teq.tr.little_endian & 0x01) {
		case 0x01:
			abs_delta= (double)(ul >> 4);
			extract_uint4(message, offset, &ul);
			abs_delta+= (double)ul*268435456.;
			break;
		case 0x00:
			abs_delta= (double)(ul & 0x0fffffff)*4294967296.;
			extract_uint4(message, offset, &ul);
			abs_delta+= (double)ul;
			break;
		}
		return((double)sgn_delta * (4521260802379784. + abs_delta));

	default:
		/* something really screwed up!
		*/
		fprintf(stderr, "binex_extract_mGFZI(): leading byte = 0x%02\n", (int)uc);
		*valid= (bool)FALSE;
		return(0.);
	}
}

/********************************************************************/
void
append_uint1
#if KR_C
	(buf, off, uc)
unsigned char	*buf;
unsigned long	*off;
unsigned char	uc;
#else
	(unsigned char *buf, unsigned long *off, unsigned char uc)
#endif
{
	buf[*off]= uc;
	(*off)++;
}

/********************************************************************/
void
append_uint2
#if KR_C
	(buf, off, us)
unsigned char	*buf;
unsigned long	*off;
unsigned short	us;
#else
	(unsigned char *buf, unsigned long *off, unsigned short us)
#endif
{
	memcpy((void *)(buf + *off), (void *)&us, (size_t)2);
	(*off)+= 2;
}

/********************************************************************/
void
append_sint2
#if KR_C
	(buf, off, s)
unsigned char	*buf;
unsigned long	*off;
short		s;
#else
	(unsigned char *buf, unsigned long *off, short s)
#endif
{
	memcpy((void *)(buf + *off), (void *)&s, (size_t)2);
	(*off)+= 2;
}

/********************************************************************/
void
append_uint3
#if KR_C
	(buf, off, ul)
unsigned char	*buf;
unsigned long	*off;
unsigned long	ul;
#else
	(unsigned char *buf, unsigned long *off, unsigned long ul)
#endif
{
	memcpy((void *)(buf + *off), (void *)&ul, (size_t)3);
	(*off)+= 3;
}

/********************************************************************/
void
append_uint4
#if KR_C
	(buf, off, ul)
unsigned char	*buf;
unsigned long	*off;
unsigned long	ul;
#else
	(unsigned char *buf, unsigned long *off, unsigned long ul)
#endif
{
	memcpy((void *)(buf + *off), (void *)&ul, (size_t)4);
	(*off)+= 4;
}

/********************************************************************/
void
append_sint4
#if KR_C
	(buf, off, sl)
unsigned char	*buf;
unsigned long	*off;
long		sl;
#else
	(unsigned char *buf, unsigned long *off, long sl)
#endif
{
	memcpy((void *)(buf + *off), (void *)&sl, (size_t)4);
	(*off)+= 4;
}

/********************************************************************/
void
append_real4
#if KR_C
	(buf, off, f)
unsigned char	*buf;
unsigned long	*off;
float		f;
#else
	(unsigned char *buf, unsigned long *off, float f)
#endif
{
	memcpy((void *)(buf + *off), (void *)&f, (size_t)4);
	(*off)+= 4;
}

/********************************************************************/
void
append_real8
#if KR_C
	(buf, off, d)
unsigned char	*buf;
unsigned long	*off;
double		d;
#else
	(unsigned char *buf, unsigned long *off, double d)
#endif
{
	memcpy((void *)(buf + *off), (void *)&d, (size_t)8);
	(*off)+= 8;
}

/********************************************************************/
void
extract_uint1
#if KR_C
	(buf, off, uc)
unsigned char	*buf;
unsigned long	*off;
unsigned char	*uc;
#else
	(unsigned char *buf, unsigned long *off, unsigned char *uc)
#endif
{
	*uc= buf[*off];
	(*off)++;
}

/********************************************************************/
void
extract_uint2
#if KR_C
	(buf, off, us)
unsigned char	*buf;
unsigned long	*off;
unsigned short	*us;
#else
	(unsigned char *buf, unsigned long *off, unsigned short *us)
#endif
{
	memcpy((void *)us, (void *)(buf + *off), (size_t)2);
	(*off)+= 2;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)us, 2);
}

/********************************************************************/
void
extract_sint2
#if KR_C
	(buf, off, s)
unsigned char	*buf;
unsigned long	*off;
short		*s;
#else
	(unsigned char *buf, unsigned long *off, short *s)
#endif
{
	memcpy((void *)s, (void *)(buf + *off), (size_t)2);
	(*off)+= 2;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)s, 2);
}

/********************************************************************/
void
extract_uint3
#if KR_C
	(buf, off, ul)
unsigned char	*buf;
unsigned long	*off;
unsigned long	*ul;
#else
	(unsigned char *buf, unsigned long *off, unsigned long *ul)
#endif
{
	*ul= 0L;
	memcpy((void *)ul, (void *)(buf + *off), (size_t)3);
	(*off)+= 3;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)ul, 4);
}

/********************************************************************/
void
extract_uint4
#if KR_C
	(buf, off, ul)
unsigned char	*buf;
unsigned long	*off;
unsigned long	*ul;
#else
	(unsigned char *buf, unsigned long *off, unsigned long *ul)
#endif
{
	memcpy((void *)ul, (void *)(buf + *off), (size_t)4);
	(*off)+= 4;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)ul, 4);
}

/********************************************************************/
void
extract_sint4
#if KR_C
	(buf, off, l)
unsigned char	*buf;
unsigned long	*off;
long		*l;
#else
	(unsigned char *buf, unsigned long *off, long *l)
#endif
{
	memcpy((void *)l, (void *)(buf + *off), (size_t)4);
	(*off)+= 4;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)l, 4);
}

/********************************************************************/
void
extract_real4
#if KR_C
	(buf, off, f)
unsigned char	*buf;
unsigned long	*off;
float		*f;
#else
	(unsigned char *buf, unsigned long *off, float *f)
#endif
{
	memcpy((void *)f, (void *)(buf + *off), (size_t)4);
	(*off)+= 4;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)f, 4);
}

/********************************************************************/
void
extract_real8
#if KR_C
	(buf, off, d)
unsigned char	*buf;
unsigned long	*off;
double		*d;
#else
	(unsigned char *buf, unsigned long *off, double *d)
#endif
{
	memcpy((void *)d, (void *)(buf + *off), (size_t)8);
	(*off)+= 8;
	if (teq.tr.binex.bytes_reversed)
		reverse_bytes((unsigned char *)d, 8);
}

/********************************************************************/
unsigned char
binex_build_SV_id
#if KR_C
	(system)
SAT_ID	*system;
#else
	(SAT_ID *system)
#endif
{
	unsigned char	id;

	if (0x00 < system->PRN && system->PRN < 0x21)
		/* number must be 1-32 only
		*/
		id= system->PRN - 0x01;
	else {
		fprintf(stderr, "! Error ! binex_build_SV_id(): invalid satellite number (%s)\n", SV_id(system));
		exit(-1);
	}

	switch (system->sys) {
	case NNSS_Transit:
		fprintf(stderr, "! Error ! BINEX does not allow formatting of NNSS Transit data\n");
		exit(-1);
	case NAVSTAR_GPS:
		id|= 0x00 << 5;
		break;
	case GLONASS:
		id|= 0x01 << 5;
		break;
	default:
		fprintf(stderr, "! Error ! binex_build_SV_id(): unknown satellite constellation (= 0x%02x)\n", (int)system->sys);
		exit(-1);
	}

	return(id);
}

/********************************************************************/
bool
binex_extract_SV_id
#if KR_C
	(id, system)
unsigned char	id;
SAT_ID		*system;
#else
	(unsigned char id, SAT_ID *system)
#endif
{
	switch (id >> 5 & 0x03) {
	case 0x00:
		system->sys= (unsigned char)NAVSTAR_GPS;
		break;
	case 0x01:
		system->sys= (unsigned char)GLONASS;
		break;
	default:
		return ((bool)FALSE);
	}
	system->PRN= (id & 0x1f) + 0x01;

	return ((bool)TRUE);
}

/********************************************************************/
unsigned long
uint4_to_ubnxi
#if KR_C
	(buf, offset, value)
unsigned char	*buf;
unsigned long	*offset, value;
#else
	(unsigned char *buf, unsigned long *offset, unsigned long value)
#endif
{
	/* convert an unsigned long which is < 536870912 (2^29)
	   to the standard BINEX 1 to 4 byte sequence
	*/
	unsigned char	uc;
	unsigned short	us, us1;
	unsigned long	ul, ul1, ul2, ul3, bytes;

	if (value < 128) {
		/* one byte needed:
		*/
		bytes= 1;
		uc= (unsigned char)value;
		memcpy((void *)(buf + *offset), (void *)&uc, (size_t)bytes);
	}
	else if (value < 16384) {
		/* two bytes needed:
		*/
		bytes= 2;
		us= (unsigned short)value;
		us1= 0x7f00 & us << 1;
		us&= 0x007f;
		us|= us1;
		switch (teq.tr.little_endian) {
		case TRUE:
			us|= 0x0080;
			break;
		case FALSE:
			us|= 0x8000;
			break;
		}
		memcpy((void *)(buf + *offset), (void *)&us, (size_t)bytes);
	}
	else if (value < 2097152) {
		/* three bytes needed:
		*/
		bytes= 3;
		ul= (unsigned long)value;
		ul2= 0x007f0000 & ul << 2;
		ul1= 0x00007f00 & ul << 1;
		ul&= 0x0000007f;
		ul|= ul1 | ul2;
		switch (teq.tr.little_endian) {
		case TRUE:
			ul|= 0x00008080;
			break;
		case FALSE:
			ul|= 0x00808000;
			ul<<= 8;
			break;
		}
		memcpy((void *)(buf + *offset), (void *)&ul, (size_t)bytes);
	}
	else if (value < 536870912) {
		/* four bytes needed:
		*/
		bytes= 4;
		ul= (unsigned long)value;
		ul3= 0xff000000 & ul << 3;
		ul2= 0x007f0000 & ul << 2;
		ul1= 0x00007f00 & ul << 1;
		ul&= 0x0000007f;
		ul|= ul1 | ul2 | ul3;
		switch (teq.tr.little_endian) {
		case TRUE:
			ul|= 0x00808080;
			break;
		case FALSE:
			ul|= 0x80808000;
			break;
		}
		memcpy((void *)(buf + *offset), (void *)&ul, (size_t)bytes);
	}
	else {
		bytes= 0;
		fprintf(stderr, "! Error ! uint4_to_ubnxi(): value is %d >= 536870912\n", value);
		exit(-1);
	}

	(*offset)+= bytes;
	return(bytes);
}

/********************************************************************/
void
binex_crc
#if KR_C
	(crc, buff, mess, crc_offset, buff_length, mess_length, reverse)
unsigned char	*crc, *buff, *mess;
unsigned long	*crc_offset, buff_length, mess_length;
bool		reverse;
#else
	(unsigned char *crc, unsigned char *buff, unsigned char *mess,
	 unsigned long *crc_offset, unsigned long buff_length, unsigned long mess_length, bool reverse)
#endif
{
	unsigned char	uc, bytes;
	unsigned short	us;
	unsigned long	ul, length;

	/* compute BINEX CRC for this message length
	*/
	length= buff_length + mess_length;
	if (length < 128) {
		/* 1-byte checksum
		*/
		bytes= 1;
		uc= cks08(buff, mess, buff_length, mess_length);
		memcpy((void *)(crc + *crc_offset), (void *)&uc, (size_t)bytes);
	}
	else if (length < 4096) {
		/* 2-byte CRC
		*/
		bytes= 2;
		us= crc16(buff, mess, buff_length, mess_length);
		if (reverse)
			reverse_bytes((unsigned char *)&us, 2);
		memcpy((void *)(crc + *crc_offset), (void *)&us, (size_t)bytes);
	}
	else if (length < 1048576) {
		/* 4-byte CRC
		*/
		bytes= 4;
		ul= crc32(buff, mess, buff_length, mess_length);
		if (reverse)
			reverse_bytes((unsigned char *)&ul, 4);
		memcpy((void *)(crc + *crc_offset), (void *)&ul, (size_t)bytes);
	}
	else {
		/* 16-byte MD5
		*/
		bytes= 16;
		/* code for md5() not yet complete;
		   the next block is just a placeholder until md5() is finished
		*/
		fprintf(stderr, "binex_crc(): md5() not completed yet\n");
		exit(-1);
	}
	(*crc_offset)+= (unsigned long)bytes;
}

/********************************************************************/
void
reverse_bytes
#if KR_C
	(rec, n)
unsigned char	*rec;
int		n;
#else
	(unsigned char *rec, int n)
#endif
{
	switch (n) {
	case 2:
		swap_uc(&rec[0], &rec[1]);
		return;
	case 3:
		swap_uc(&rec[0], &rec[2]);
		return;
	case 4:
		swap_uc(&rec[0], &rec[3]);
		swap_uc(&rec[1], &rec[2]);
		return;
	case 6:
		swap_uc(&rec[0], &rec[1]);
		swap_uc(&rec[2], &rec[5]);
		swap_uc(&rec[3], &rec[4]);
		return;
	case 8:
		swap_uc(&rec[0], &rec[7]);
		swap_uc(&rec[1], &rec[6]);
		swap_uc(&rec[2], &rec[5]);
		swap_uc(&rec[3], &rec[4]);
		return;
	default:
		fprintf(stderr, "reverse_bytes(): uncoded case of %d bytes", n);
		exit(-1);
	}
}

/********************************************************************/
void
swap_pp
#if KR_C
	(a, b)
void	**a, **b;
#else
	(void **a, void **b)
#endif
{
	void	*tmp;
	tmp= *a;
	*a= *b;
	*b= tmp;
}

/********************************************************************/
void
swap_c
#if KR_C
	(a, b)
char	*a, *b;
#else
	(char *a, char *b)
#endif
{
	char	tmp;
	tmp= *a;
	*a= *b;
	*b= tmp;
}

/********************************************************************/
void
swap_uc
#if KR_C
	(a, b)
unsigned char	*a, *b;
#else
	(unsigned char *a, unsigned char *b)
#endif
{
	unsigned char	tmp;
	tmp= *a;
	*a= *b;
	*b= tmp;
}

/********************************************************************/
void
swap_i
#if KR_C
	(a, b)
int	*a, *b;
#else
	(int *a, int *b)
#endif
{
	int	tmp;
	tmp= *a;
	*a= *b;
	*b= tmp;
}

/********************************************************************/
void
swap_d
#if KR_C
	(a, b)
double	*a, *b;
#else
	(double *a, double *b)
#endif
{
	double	tmp;
	tmp= *a;
	*a= *b;
	*b= tmp;
}

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