/**---------------------------------------------------------------------------
 ** 
 ** read.c -- Low level reading of NBR files 
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/15 12:45:24
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/07/16 20:34:14
 ** Update Count    : 10
 ** Directory       : /home/pego/pcd1/codas3c/gfi/src/libs/nbr/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **
 **	   These are routines for reading narrowband ADCP raw data files. They are
 **	   a modified copies of those found in file read_nbp.c, used to read
 **	   TRANSECT binary processed files. The raw data binary output format is
 **	   described in the ADCP technical manual.
 **
 **    The main purpose of this file is to provide the function
 **    read_ensemble() that reads a complete ensemble from the data files and
 **    fills a raw data structure. Since data may be stored on less than 8
 **    bytes, a preprocessing is necessary to retrieve the measured
 **    values. These are however still in a raw format, i.e. they are not
 **    scaled to their physical representation.
 **	
 **    If not precised, these functions return non-zero on error. 
 **
 **    Reading raw pings cannot synchronize back if one ping is
 **    corrupt. Although this is not happening often, it might be a quite
 **    serious problem when data have to be recovered. A simple solution would
 **    be to synchronize on the leader output buffer size. This is one of my
 **    first priorites. 
 **
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

#include "read.h"

/* ------------------------------------------------------------------
   The following functions are local only
   ------------------------------------------------------------------ */

/* ------------------------------------------------------------------
   Velocity data in a bin is stored on a 6 bytes record. Each velocity has
   therefore to be converted from a 12 bits to a SHORT
   represention. Therefore, for each beam, one has to

     - Select the two bytes containing the 12 bits.
     - Convert these two bits into a short, using function
       convert_array_short() 
     - Get the sign (either bit 15 or bit 3) and shift right if necessary.
     - Set the 5 left most bytes to zero.
     - Put back the sign.
     - Set final data to BADSHORT if necessary. 

	RETURNS: non-zero on error
	------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int convert_velocity(/* ADCP velocity bytes */
                     UBYTE *raw, 
                     /* Converted velocities */
                     SHORT *vel)
#else
int convert_velocity( raw, vel)
UBYTE *raw;
SHORT *vel;
#endif
{

	char func[]="convert_velocity";

	int i, msb;
	char cbuff[2];
	USHORT v, sign;
	
	/* Apply co|nversions into shorts */
	for(i=0; i<4; i++){
		/* Determine the address of the MS byte in the unsigned short */
		msb = i + (i/2);
		/* Copy the short into the character buffer */
		cbuff[0] = (char) *(raw+msb);
		cbuff[1] = (char) *(raw+msb+1);
		/* Do conversion into an USHORT */
		if( convert_array_short((char *) &v, cbuff,
								
						SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1) != 2){
			error_msg(func, "while converting velocity bytes to ushort");
			return(CONVERT_ERROR);
		}
		/* Get the sign and shift if necessary */
		if(!(i%2)){
			sign = v & 0x8000;
			v >>= 4;
		}
		else{
			sign = v & 0x0800;
		}
		/* Put zeros in the MS nibble, as well as the old bit sign */
		v &= 0x07FF;
		/* Test for bad data */
		if (sign && !v) *(vel+i) = BADSHORT;
		else *(vel+i) = (SHORT) v;
		/* Put back the sign, and invert MS byte*/
		if (sign && *(vel+i) != BADSHORT)
			*(vel+i) |= 0xF800;
	}
	return(0);
}	

/* ------------------------------------------------------------------
   Some time data are given in BCD format. These bytes should therefore be
   converted from the hexadecimal format into the decimal one in order to avoid
   later errors. For this purpose, see function byte_hexa_to_dec().
   ------------------------------------------------------------------ */

/* ------------------------------------------------------------------
   Raw bottom track percent good is stored in a single nibble, making up an
   USHORT for the 4 beams. These are converted back into an array of 4
   UBYTE.

   RETURNS: non-zero on error.
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int convert_btpcg(/* ADCP BT percent good bytes */
                  UBYTE *raw, 
                  /* Converted BT percent good data */
                  UBYTE *btpcg)
#else
int convert_btpcg(raw, btpcg)
UBYTE *raw;
UBYTE *btpcg;
#endif
{

	int i, b;
	UBYTE *ptr, nib;

	ptr = raw;
	for(i=0; i<4; i++){
		/* Select the MS or LS byte */
		b = i/2;
		/* Select MS or LS nibble. */
		if(!(i%2)){
			nib = *(ptr+b) & 0xF0;
			nib >>= 4;
		}
		else{
			nib = *(ptr+b) & 0x0F;
		}
		/* Insert zeros in the MS part */
		nib &= 0x0F;
		/* Save the value: By its definition, a variable of type
			 [[NBR_BTPCG_TYPE]] is an array of [[shorts]], and therefore a pointer
			 to a [[short]]. Hence, passing the pointer of this variable as the
			 function argument is equivalent to passing a pointer to a pointer to a
			 [[short]]. */
		*(btpcg + i) = (UBYTE) nib;
	}

	return(0);

}

/* ------------------------------------------------------------------
   CTD type data are made up of 3 UBYTES. These should be converted into an
   ULONG. This is done by adding a zero byte in front of the most
   significant one, and using a union to retrieve the ULONG.

   RETURNS: non-zero on error
   ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int convert_ctd_type(/* ADCP CTD data */
                     UBYTE *raw, 
                     /* Converted CTD data */
                     ULONG *ctd_var)
#else
int convert_ctd_type(raw, ctd_var)
UBYTE *raw;
ULONG *ctd_var;
#endif
{

	int i;
	union {
		UBYTE ub[4];
		ULONG ui;
	} uctd;
	
	/* Set the MS byte to zero */
	uctd.ub[3] = 0x0;
	/* Fill the other bytes with raw data: The same remark as in
		 [[convert_btpcg]] for the pointer [[btpcg]] applies here for the pointer
		 [[raw]]. */
	for(i=0; i<3; i++) uctd.ub[3-i-1] = *(raw+i);
	/* Copy the unsigned integer */
	*ctd_var = uctd.ui;

	return(0);

}

/* ------------------------------------------------------------------ 
	 Status data is made up of 4 nibble (2 bytes) that have to be converted to a
	 single byte for each beam.

	 RETURNS: non-zero on error
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int convert_status(/* ADCP Status byte */
                   UBYTE *raw, 
                   /* Converted status data */
                   UBYTE *status)
#else
int convert_status(raw, status)
UBYTE *raw;
UBYTE *status;
#endif
{

	int mask, shift, index, j;

	/* Convert status */
	for(j=0; j<4; j++){
		/* Choose current byte */
		index = j/2;
		/* Choose MS nibble or LS nibble in current byte */
		mask = (j%2) ? 0xF0 : 0x0F;
		shift= (j%2) ? 1 : 0;
		/* Calculate status for current beam */
		*(status+j) = *(raw+index) & mask;
		if(shift) *(status+j) >>= 4;
	}

	return(0);
}

/* ------------------------------------------------------------------
	Differences here from read_nbp.c is that it is not possible to use a
	similar while loop to recover data. The only information about the next
	ensemble is that it starts at the end of the current one. Hence, if the
	current ensemble does not contain the indicated number of bytes, all the
	rest of the data file may be lost.
	
	RETURNS: non-zero on error
	------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED 
int read_header(/* Pointer to ADCP file */
                FILE *fp, 
                /* Place for ensemble data */
                NBR_ENSEMBLE_TYPE *ens, 
                /* Position of ensemble start relative to beginning of the
                   data file */
                long *ens_start)
#else 
     int read_header(fp, ens, ens_start)
     FILE *fp;
     NBR_ENSEMBLE_TYPE *ens;
     long *ens_start;				
#endif
{

	char func[]="read_header";

  static char buff[NBR_HEADER_FIXED_SIZE], *buf_ptr;
	NBR_HEADER_TYPE *header;

	/* Initializations: 
		 This is may be not necessary. But returning the start of the current
		 ensemble may be usefull if a data recover procedure is available. Keep
		 position of current ensemble start in file.
		 */
	*ens_start = ftell(fp);
	header=&(ens->header);
	/* Since the size of the header is known, read it completely. */
	if( fread(buff, NBR_HEADER_FIXED_SIZE, 1, fp) != 1){
		if(feof(fp))
			return(EOF);
		error_msg(func, "while reading header");
		return(READ_ERROR);
	}

	/* The data in the buffer has then to be converted in a machine dependent
		 format. The conversion functions used here are defined in [[mc0.c]].
		 Converted data are stored in the [[header]] variable.
		*/
	buf_ptr = buff;
	buf_ptr += convert_array_short((char *) &(header->output_buffer_size), 
																 buf_ptr, 
																 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 7);
		
	/* The only possible control before ending is to check if the leader output
		 buffer has the correct size.
		 */

	if(header->leader_buffer_size != NBR_LEADER_FIXED_SIZE){ 
		error_msg(func, "wrong size of leader output buffer");
		return(READ_ERROR);
	}
	
	/* Finished reading the header */
	ens->presence_bits |= HEADER_PRESENT;
	return(0);
}

/* ------------------------------------------------------------------
   Read the leader.
 
   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
  int read_leader(/* Pointer to ADCP data file */
                  FILE *fp, 
                  /* Place for ensemble data */
                  NBR_ENSEMBLE_TYPE *ens)
#else
int read_leader(fp, ens)
FILE *fp;
NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="read_leader";

  NBR_LEADER_TYPE *leader;
  static char buff[NBR_LEADER_FIXED_SIZE], *buf_ptr;

	/* Since the leader output buffer size is known and fixed, read the complete
		 buffer. 
		 */
  leader = &(ens->leader); 
  if(fread(buff, NBR_LEADER_FIXED_SIZE, 1, fp) != 1){ 
		error_msg(func, "while reading leader"); 
		return(READ_ERROR); 
	}
	/* Machine dependent	format conversions */
  buf_ptr = buff;
  buf_ptr += copy_byte((char *) &(leader->ensemble_start_time), 
											 buf_ptr, 8, AS_IS, NULL);
  buf_ptr += convert_array_short((char *) &(leader->pings_per_ensemble), 
																 buf_ptr, 
																 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
  buf_ptr += copy_byte((char *) &(leader->number_of_bins), 
											 buf_ptr, 5, AS_IS, NULL);
  buf_ptr += convert_array_short((char *) &(leader->ensemble_number), 
																 buf_ptr, 
																 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
  buf_ptr += copy_byte((char *) &(leader->bit_status), 
											 buf_ptr, 4, AS_IS, NULL);
  buf_ptr += convert_array_short((char *) &(leader->pitch_tilt), 
																 buf_ptr, 
																 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 4);
  buf_ptr += copy_byte((char *) &(leader->high_voltage_in), 
											 buf_ptr, 18, AS_IS, NULL);
  buf_ptr += convert_array_short((char *) leader->bottom_track_range, 
																 buf_ptr, 
																 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 4);
  buf_ptr += copy_byte((char *) &(leader->pitch_std_dev), 
											 buf_ptr, 8, AS_IS, NULL);
	
	/* Finally, some further conversions are necessary in order to store each
		 data into a single variable */
		 
	/*  Convert bin length */
	ens->bin_length = 0x01 << leader->raw_bin_length;
  /* Convert bottom track velocity */
  if( convert_velocity( leader->raw_bottom_track_velocities,
												ens->bottom_track_velocity) != 0){
		error_msg(func, "while converting bottom track velocities");
		return(CONVERT_ERROR);
	}
	/* Convert times */
	if( byte_hexa_to_dec( (UBYTE *) &(leader->ensemble_start_time), 
												(UBYTE *) &(leader->ensemble_start_time), 8) != 0){
		error_msg(func, "while converting times");
		return(CONVERT_ERROR);
	}
	/* Convert bottom track percent good */
	if( convert_btpcg(leader->raw_bottom_track_percent_good,
										ens->bottom_track_percent_good) != 0){
		error_msg(func, "while converting bottom track percent good");
		return(CONVERT_ERROR);
	}

	/** 97/11/28 PJ
		  These conversion are not necessary at GFI, since ADCP is not equiped
			with CTD.
			*/
	goto no_ctd_conversion;

	/* Convert CTD types data */

	/*
	if( convert_ctd_type(leader->raw_ctd_conductivity_count,
											 &(ens->ctd_conductivity_count)) != 0){ 
		error_msg(func, "while converting ctd conductivity count");
		return(CONVERT_ERROR);
	}
	if( convert_ctd_type(leader->raw_ctd_temperature_count,
											 &(ens->ctd_temperature_count)) != 0){
		error_msg(func, "while converting ctd temperature count");
		return(CONVERT_ERROR);
	}
	if( convert_ctd_type(leader->raw_ctd_depth_count,
											 &(ens->ctd_depth_count)) != 0){
		error_msg(func, "while converting ctd depth count");
		return(CONVERT_ERROR);
	}
	if( convert_ctd_type(leader->raw_ctd_measurement_interval,
											 &(ens->ctd_measurement_interval)) != 0){
		error_msg(func, "while converting ctd measurement interval");
		return(CONVERT_ERROR);
	}
	*/

no_ctd_conversion:
		
  /* Finished reading the leader */
  ens->presence_bits |= LEADER_PRESENT;
  return (0);
}

/* ------------------------------------------------------------------
   Read velocity buffer. Beam velocity is converted in a more adequate machine
   variable before storing it into the ensemble structure.
 
   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
	
#if PROTOTYPE_ALLOWED 
int read_velocity(/* Pointer to ADCP data file */
                  FILE *fp, 
                  /* Place for ensemble data */
                  NBR_ENSEMBLE_TYPE *ens)
#else
int read_velocity(fp, ens)
FILE *fp; 
NBR_ENSEMBLE_TYPE *ens;		
#endif
{

	char func[]="read_velocity";

	int i, j;
  UBYTE *raw;
  SHORT *velocity;
  static UBYTE buff[MAX_BINS][6];
  int buf_size; 

	/* The size of the velocity output buffer is not fixed, but it is given in
		 the header ouptut buffer. There is no way to know if it is corrupt or
		 not. Therefore, one has to rely on this information and read the complete
		 buffer.
		 */
	buf_size = ens->header.velocity_buffer_size / 6;
	if (fread(buff, 6, buf_size, fp) != buf_size){
		error_msg(func, "while reading velocity buffer");
	  return(READ_ERROR);
	}

	/* Velocities may now be converted so that they each fit into a variable.
		 This is done with the routine~\ref{code:conv:vel}. Before conversion, 
		 the converted velocity data is initialized to [[BADSHORT]]. */

	/* Assign the pointers */
	for(i=0; i<buf_size; i++){
		/* Assign pointer for raw data */
		raw = *(ens->raw_velocity + i);
		/* Copy the binary bytes into the raw data variable: note that since
			 [[raw_velocity]] and [[buf_ptr]] are pointers to pointers, the value
			 they are pointing to must be passed to the conversion function, and not
			 just their own value.
			 */
		if( copy_byte((char *) raw,
									(char *) *(buff+i), 6, AS_IS, NULL) != 6){
			error_msg(func, "while converting raw velocity bytes");
			return(CONVERT_ERROR);
		}
		/* Assign pointer for converted data */
		velocity = *(ens->velocity + i); 
		/* Initialize velocity data */
		for(j=0; j<4; j++) *(velocity+j) = BADSHORT;
		/* Convert velocity data */
		if( convert_velocity(raw, velocity) != 0){
			error_msg(func, "while converting velocity buffer");
			return(CONVERT_ERROR);
		}
	}
	/* The rest of the velocity cells are set to [[BADSHORT]] */
	while(i<MAX_BINS){
		velocity = *(ens->velocity + i);
		for(j=0; j<4; j++) *(velocity+j) = BADSHORT;
		i++;
	}
	
	/* Update presence bit */
	ens->presence_bits |= VELOCITY_PRESENT;

	return (0);
}

/* ------------------------------------------------------------------
   Read the spectral width buffer. The size of the spectral width output
   buffer is not fixed, but it is given in the header ouptut buffer. There is
   no way to know if it is corrupt or	not. Therefore, one has to rely on this
   information and read the complete buffer. Bad spectral width data is
   detected and flagged using the more conventionnal BADBYTE value. 

   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int read_spectral_width(/* Pointer to ADCP data file */
                        FILE *fp, 
                        /* Place for ensemble data */
                        NBR_ENSEMBLE_TYPE *ens)
#else
int read_spectral_width(fp, ens)
FILE *fp;
NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="read_spectral_width";

	int i, j;
	static UBYTE buff[MAX_BINS][4];
	int buf_size;

	/* Initialize all spectral width cells to bad data */
	for(i=0; i<MAX_BINS; i++){
		for(j=0; j<4; j++){
			ens->spectral_width[i][j] = BADUBYTE;
			buff[i][j] = BADUBYTE;
		}
	}

	/* Read the data */
	buf_size = ens->header.spectral_width_buffer_size / 4;
	if (fread(buff, 4, buf_size, fp) != buf_size){
		error_msg(func, "while reading spectral width buffer");
	  return(READ_ERROR);
	}

	/* Convert the data */
	if( (int) copy_byte((char *) *(ens->spectral_width), (char *) *buff, 
								4*buf_size, AS_IS, NULL) != 4*buf_size){
		error_msg(func, "while converting spectral width data");
		return(CONVERT_ERROR);
	}

	/* According to RDI, a spectral width of zero indicates bad data. However,
		 tests made on beam #1 while this beam was almost not working showed that
		 bad spectral beam data is flagged with 0xFF values. Furthermore it does
		 not make sense to have a negative spectral width. A look at the data
		 indicates that these are more probably always positive.  */
	
	ens->presence_bits |= SPECTRAL_WIDTH_PRESENT;
	return(0);
}

/* ------------------------------------------------------------------
   Read echo intensity.
   The size of the echo intensity output buffer is not fixed, but it is given in
   the header ouptut buffer. There is no way to know if it is corrupt or not.
   Therefore, one has to rely on this information and read the complete
   buffer. The echo intensity array is initialized to the BADUBYTE value.

   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int read_echo_intensity(/* Pointer to ADCP data file */
                        FILE *fp, 
                        /* Place for ensemble data */
                        NBR_ENSEMBLE_TYPE *ens )
#else
     int read_echo_intensity(fp, ens)
     FILE *fp;
     NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="read_echo_intensity";

	int i, j, n;
	static UBYTE buff[MAX_BINS][4];
	int buf_size;

	/* Initialize to bad data */ 
	for(i=0; i<MAX_BINS; i++)
		for(j=0; j<4; j++)
			ens->echo_intensity[i][j] = BADUBYTE;

	/* Read the data */
	buf_size = ens->header.echo_intensity_buffer_size / 4;
  /*
  fprintf(stderr, "\nLocation: 0x%-X\n", ftell(fp));
  */
  n = fread(buff, 4, buf_size, fp);
  if(n != buf_size){
		error_msg(func, "while reading echo intensity buffer");
    fprintf(stderr, "\nGot only %-d bytes instead of %-d\n", n, buf_size); 
	  return(READ_ERROR);
	}

	/* Convert the data */
	if( (int) copy_byte((char *) *(ens->echo_intensity),
								(char *) buff, 4*buf_size, AS_IS, NULL) != 4*buf_size){
		error_msg(func, "while converting echo intensity data");
		return(CONVERT_ERROR);
	}
	
	ens->presence_bits |= ECHO_INTENSITY_PRESENT;
	return(0);
}

/* ------------------------------------------------------------------
   Read the percent good buffer.
   The size of the percent good output buffer is not fixed, but it is given in
   the header ouptut buffer. There is no way to know if it is corrupt or not.
   Therefore, one has to rely on this information and read the complete
   buffer. The percent good array is initialized to the BADUBYTE value.
   
   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int read_percent_good(/* Pointer to ADCP data file */
                      FILE *fp, 
                      /* Place for ensemble data */
                      NBR_ENSEMBLE_TYPE *ens )
#else
int read_percent_good(fp, ens)
FILE *fp;
NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="read_percent_good";

	int i, j;
	static UBYTE buff[MAX_BINS][4];
	int buf_size;

	/* Initialize to bad data */ 
	for(i=0; i<MAX_BINS; i++)
		for(j=0; j<4; j++)
			ens->percent_good[i][j] = BADUBYTE;
	
	/* Read the data */
	buf_size = ens->header.percent_good_buffer_size / 4;
	if (fread(buff, 4, buf_size, fp) != buf_size){
		error_msg(func, "while reading percent good buffer");
	  return(READ_ERROR);
	}

	/* Convert the data */
	if( (int) copy_byte((char *) *(ens->percent_good),
								(char *) buff, 4*buf_size, AS_IS, NULL) != 4*buf_size){
		error_msg(func, "while converting percent good data");
		return(CONVERT_ERROR);
	}
	
	ens->presence_bits |= PERCENT_GOOD_PRESENT;
	return(0);
}

/* ------------------------------------------------------------------
   Read status buffer.
   The size of the status output buffer is not fixed, but it is given in
   the header ouptut buffer. There is no way to know if it is corrupt or not.
   Therefore, one has to rely on this information and read the complete buffer.
   Status data holds on 2 bytes, each nibble containing status information
   for each beam. A special conversion is used here to fit each status
   information in one single variable. 

   WARNING: This function has not been tested yet. If you do it, please,
	          disable the warning message below.

   RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int read_status(/* Pointer to ADCP data file */
                FILE *fp, 
                /* Place for ensemble data */
                NBR_ENSEMBLE_TYPE *ens)
#else
     int read_status(fp, ens)
     FILE *fp;
     NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="read_status";
	
	static UBYTE buff[MAX_BINS][2];
	UBYTE *raw, *status;
	int i, j, buf_size;
	
	/* Warning message */
	warning_msg(func, "Attempt to read status buffer from raw NBR ADCP files:");
	warning_msg(NULL, "   Be carefull, this function has not been really");
	warning_msg(NULL, "   tested yet, and could still have some errors.");

	/* Initialize to bad status: since bit 1 of each nibble should always be 0,
		 bad status data is defined as being [[0xF]], or [[0xFF]] each byte.  
		 */ 
	for(i=0; i<MAX_BINS; i++)
		for(j=0; j<4; j++)
			if(ens->status[i][j] == 0)
				ens->status[i][j] = 0xFF;

	/* Read the data */
	buf_size = ens->header.status_buffer_size / 2;
	if (fread(buff, 2, buf_size, fp) != buf_size){
		error_msg(func, "while reading status buffer");
	  return(READ_ERROR);
	}

	/* Convert the data in machine format */
	for(i=0; i<buf_size; i++){
		/* Assign the pointers to raw values */
		raw = *(ens->raw_status + i);
		/* Convert the data into machine format */
		if (copy_byte((char *) raw, (char *) *(buff+i) , 2, AS_IS, NULL) != 2){
			error_msg(func, "while converting raw raw status data");
			return(CONVERT_ERROR);
		}
		/* Assign pointer to processed values */
		status = *(ens->status + i);
		/* Convert status data in a variable for each beam */ 
		if(convert_status( raw, status) != 0){
			error_msg(func, "while converting status data");
			return(CONVERT_ERROR);
		}
	}				
	
	ens->presence_bits |= STATUS_PRESENT;
	return(0);
}

int sync_ensemble(FILE *fp)
{
  
  char func[]="sync_ensemble";

  NBR_ENSEMBLE_TYPE ens;
  long pos0, n;
  long ens_start;
  int error=0;

  /*
   * Check for end of file
   */
  if(feof(fp))
    return(EOF);

  /*
   * Keep position here
   */
  pos0 = ftell(fp);
  
  /*
   * Read comming header
   */
	error = read_header(fp, &ens, &ens_start);
	if(error){
		if(error != EOF)
			error_msg(func, "while sync header");
		return(error);
	}
  
  /*
   * Check size of leader buffer
   */
  if(ens.header.leader_buffer_size != 63){
    error_msg(func, "Bad size of leader buffer size");
    return(READ_ERROR);
  }
  
  /*
   * Move to next ensemble
   */
  n = ens.header.output_buffer_size - 14 + 2;
  if(fseek(fp, n, SEEK_CUR) != 0){
    error_msg(func, "Seeking to next ensemble");
    return(READ_ERROR);
  }
  
  /*
   * Try to read a next header
   */
	error = read_header(fp, &ens, &ens_start);
	if(error){
		if(error != EOF)
			error_msg(func, "while sync with next header");
		return(error);
	}

  /*
   * Put back at beginning of ensemble
   */
  fseek(fp, pos0, SEEK_SET);
  
  return(0);
}
   
/* ##################################################################
	 -Reading a Complete Ensemble:

	 +Reads a complete raw NBR ADCP ensemble from a data file.
	 +The file must be opened and positionned at the start of the ensemble.
	 ################################################################## */

/* ------------------------------------------------------------------
   The following functions are made available for external utilities.  
   ------------------------------------------------------------------ */
/* ------------------------------------------------------------------
   Read a complete ensemble.

   RETURN: non-zero on error, EOF if end of file is reached.
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int read_ensemble(/* Pointer to ADCP data file */
                  FILE *fp, 
                  /* Place for ensemble data */
                  NBR_ENSEMBLE_TYPE *ens, 
                  /* Position in file of ensemble start */
                  long *ens_start, 
									/* Poisiton in file of ensemble stop */
                  long *ens_stop)
#else
     int read_ensemble( fp, ens, ens_start, ens_stop)
     FILE *fp;
     NBR_ENSEMBLE_TYPE *ens;
     long *ens_start, *ens_start;
#endif
{

	char func[]="read_ensemble";

	int error=0;
	USHORT checksum;
	static char buff[2];
	 
	/* Initialize the presence flags */
	ens->presence_bits = 0x0;
	 
  /*
   * Try to synchronize
   */
  error = sync_ensemble(fp);
  if(error == EOF)
    return(EOF);
  if(error != 0){
    error_msg(func, "Synchronizing ensemble");
    /*
    fprintf(stderr, "\nCODE=%-d\n", error);
    */
    return(READ_ERROR);
  }
   
	/* Read the header */
	error = read_header(fp, ens, ens_start);
	if(error){
		if(error != EOF)
			error_msg(func, "while reading the header");
		return(error);
	}
  /*
  fprintf(stderr, "VSIZE=%-d ASIZE=%-d\n", 
          ens->header.velocity_buffer_size,
          ens->header.echo_intensity_buffer_size);
  */

	 /* Read the leader */
	 if (ens->header.leader_buffer_size > 0){
		 error = read_leader(fp, ens);
		 if(error){
			 error_msg(func, "while reading the leader");
			 return(error);
		 }
	 }
	 else{
		 error_msg(func, "leader buffer size is not positive");
		 return(error);
	 }

	 /* Read the velocity */
	 if(ens->header.velocity_buffer_size >0){
		 error = read_velocity(fp, ens);
		 if(error){
			 error_msg(func, "while reading the velocity");
			 return(error);
		 }
	 }
	 /* Read the spectral width */
	 if(ens->header.spectral_width_buffer_size >0){
		 error = read_spectral_width(fp, ens);
		 if(error){
			 error_msg(func, "while reading the spectral width");
			 return(error);
		 }
	 }

	 /* Read the echo intensity */
	 if(ens->header.echo_intensity_buffer_size >0){
		 error = read_echo_intensity(fp, ens);
		 if(error){
			 error_msg(func, "while reading the echo intensity"); 
			 return(error);
		 }
	 }
	 
	 /* Read the percent good */
	 if(ens->header.percent_good_buffer_size >0){	 
		 error = read_percent_good(fp, ens);
		 if(error){
			 error_msg(func, "while reading the percent good");
			 return(error);
		 }
	 }

	 /* Read the status */
	 if(ens->header.status_buffer_size >0){
		 error = read_status(fp, ens);
		 if(error){
			 error_msg(func, "while reading the status");
			 return(error);
		 }
	 }
	 
	 /* Read the checksum */
	 if(fread(buff, 2, 1, fp) != 1){
		 error_msg(func, "while reading checksum");
		 return(READ_ERROR);
	 }
	 if(convert_array_short((char *) &checksum, buff, 
													SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1) != 2){
		 error_msg(func, "while converting checksum to the machine format");
		 return(CONVERT_ERROR);
	 }
	 /* Save the checksum in the ensemble variable */
	 ens->checksum = checksum;

	 /* Save the address of ensemble end */
	 *ens_stop = ftell(fp);

	 /*
		 This is the place where processing of the checksum for checking
		 of data integrity would be carried out
		 */

	 /* Finished reading an ensemble */
	 ens->presence_bits |= ENSEMBLE_PRESENT;
	 return(0);
}

/* ------------------------------------------------------------------
   Process Status Information.

   This function is called once a whole ensemble has been read. It looks at the
   information stored in the status output buffer and sets bad data where
   required. 

   It is stand alone, i.e. you must call it explicitly in your programms if you
   whish to apply status data information. I did it so, because sometimes it is
   better to view the data as they are stored in the files (for example
   view_nbr.)

   WARNING: Status data has not been encountered yet, and so have the status
            processing functions not been tested for eventual errors. If this
            would be done once, disable the warning nmessage below.

	 RETURNS: non-zero on error
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int process_status_data(/* Pointr to ensemble data */
                        NBR_ENSEMBLE_TYPE *ens)
#else
     int process_status_data(ens)
     NBR_ENSEMBLE_TYPE *ens;
#endif
{

	char func[]="process_status_data";

	UBYTE *stat, *echo;
	SHORT *vel;

	UBYTE bin_stat;
	int i, j;

	/* Check if status output buffer is present */
	if(!(ens->presence_bits & STATUS_PRESENT))
		return(0);

	/* Warning message */
	warning_msg(func, "Attempt to process status buffer information:");
	warning_msg(NULL, "   Be carefull, this function has not been really");
	warning_msg(NULL, "   tested yet, and could still have some errors.");

	/* Process status data for each cell */
	for(i=0; i<MAX_BINS; i++){
		/* Initialize pointers */
		stat = *(ens->status + i);
		vel  = *(ens->velocity + i);
		echo = *(ens->echo_intensity + i);
		/* Initialize the bin_status */
		bin_stat = 0x0;
		/* Process status for each beam */
		for(j=0; j<4; j++){
			/* BIT0: echo intensity was too low
				 
				 ==>   bad echo intensity
				 ==>   bad velocity
				 */
			if(((ULONG) *(stat+j)) & BIT0){
				if(ens->presence_bits & VELOCITY_PRESENT)
					*(vel+j) = BADSHORT;
				if(ens->presence_bits & ECHO_INTENSITY_PRESENT)
					*(echo + j) = BADUBYTE;
			}
			/* BIT1: always 0 */
			/* BIT2: velocity beyond bottom or surface

				 ==>   bad velocity 
				 */
			if(((ULONG) *(stat+j)) & BIT2){
				if(ens->presence_bits & VELOCITY_PRESENT)
					*(vel+j) = BADSHORT;
			}
			/* BIT3: beam_coord : 0
							 earth_coord: bin_stat

				 ==>   building bin_status with third bit of each beam 
				 */
			if(((ULONG) *(stat+j)) & BIT3)
				bin_stat |= ((UBYTE) *(stat + j)) & ((UBYTE) 0x1 << (3-j));
		}
		/* Processing bin_status for each cell */
		if(((ULONG) ens->leader.adcp_config) & BIT1){  
			for(j=0; j<4; j++){
				/* BIT0: bad 4-beam solution, but good 3-beam solution
				 
				 ==>   don't what to do, really!
				 */
				/* BIT1: 0 */
				/* BIT2: bad error velocity

					 ==>   bad velocity
					 */
				if(((ULONG) bin_stat) & BIT2){
					if(ens->presence_bits & VELOCITY_PRESENT)
						*(vel+j) = BADSHORT;
				}
				/* BIT3: bad 4- and 3-beam solution

				 ==>     bad velocity
				 */
				if(((ULONG) bin_stat) & BIT3){
					if(ens->presence_bits & VELOCITY_PRESENT)
						*(vel+j) = BADSHORT;
				}
			}
		}
		else{
			/* In beam coordinate, the bin_status should be zero. Making here a
				 security check 
				 */
			if(bin_stat != 0){
				error_msg(func, "velocity in beam_coord and bin_status not zero");
				return(READ_ERROR);
			}
		}
	}

	return(0);
	
}







