/**---------------------------------------------------------------------------
 ** 
 ** scan_nbr.c -- 
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/30 11:54:03
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/07/30 13:31:43
 ** Update Count    : 7
 ** Directory       : /pcdata1/jaccard/codas3c/gfi/src/apps/nbr/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    Undocumented.
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

/*

	-Introduction:

	This file contains functions for scanning ADCP raw data files, in order to
	check their integrity. Some tests are performed on the ADCP data. The result
	of these tests, when failing, is output into a log file, so that one may
	locate some of the pings which should not be loaded in the database.

	There are 3 kinds of checking:

	+Error Check:

	   Look for errors from error variables stored inside raw ADCP buffers.

	+Config Check

	   Look for configuration changes from one ping to the other. It could also
		 check the value of configuration parameters and output warnings.

	+Data Check:

	   Calculate statistics of data over a complete scan. Results are output in
		 at the end of the log file, and may be used to detect bad variations of
		 the measurements.

	+Flag Check:

	   Output flag status.

	--Log File:

	   The log file should contain information about the encountered errors, and
		 where they occured, both in time and ensemble number as the location of
		 the considered ping data in the files.

		 In order to locate bad data variation and flag status changes, it should
		 also contain ascii lines with the value of the flags and variables being
		 averaged.

		 The former messages will be commented with lines starting with a '%',
		 while the latter will be written along a single line. By this way, the
		 log file may be either viewed, or parsed and further processed by a
		 processing programme like matlab.

   ##################################################################

	 */

#include "scan_nbr.h"

/* ------------------------------------------------------------------
	 --Data Stat Write: 
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int stat_write_header(FILE *fp, SCAN_NBR_PING_TYPE *curr)
#else
int stat_write_header(fp, curr)
ULONG bits;
SCAN_NBR_PING_TYPE *curr;
#endif
{

	int n = 0;

	/* Beginning of header */
	fprintf(fp, "%% *** NBR_SCAN: Data Description ****\n");
	
	/* Header for ensemble location */
	fprintf(fp, "%% %3d : ensemble_number\n", 1);
	fprintf(fp, "%% %3d : ensemble start time (decimal day in %4d)\n", 
					2, year_base);

	/* Unistat variables information */
	n = fprintf_stat_header(fp, curr->stat_list, 3);
	n = fprintf_flag_header(fp, curr->flag_list, n);

	/* End of header */
	fprintf(fp, "%% ***********************************\n");

	return(0);

}

#if PROTOTYPE_ALLOWED
int stat_write(SCAN_NBR_FILE_TYPE *fps, SCAN_NBR_PING_TYPE *curr)
#else
int stat_write(fp, curr)
ULONG bits;
#endif
{

	UNISTAT_LIST_TYPE *uni;
	FLAG_LIST_TYPE *f;
	NBR_LEADER_TYPE *l;
	double data;
	DOUBLE  blog;
	UBYTE   ascii_log;

	int i, j, len;
	char str[500];

	/* Assign pointers */
	l = &(curr->ping.leader);
	ascii_log = (scan_bits & ASCII_LOG_BIT) ? 1 : 0;

	/* Print location of ensemble */
	if(ascii_log)
		fprintf(fps->log, "%6hu  %14.8f ", l->ensemble_number, curr->yd);
	blog = (DOUBLE) l->ensemble_number;
	fwrite((void *) &blog, sizeof(DOUBLE), 1, fps->bin);
	blog = (DOUBLE) curr->yd;
	fwrite((void *) &blog, sizeof(DOUBLE), 1, fps->bin);

	/* Print data being averaged */
	uni = curr->stat_list;
	while(uni->name != NULL){
		/* Print all data in array of values */
		for(j=0; j<(uni->size); j++){
			/* Special transformations */
			switch(uni->code){
			case 1:
				data = to360(*(uni->data+j));
				break;
			default:
				data = *(uni->data+j);
				break;
			}
			/* Write ascii data in log file */
			if(data < ADJ_BADFLOAT){
				sprintf(str, uni->format, data);
				blog = (DOUBLE) data;
			}
			else{
				sprintf(str, uni->format, 1.0);
				len = strlen(str);
				strcpy(str, "");
				for(i=0; i<len-3; i++)
					strcat(str, " ");
				strcat(str, "NaN");
				blog = BADDOUBLE;
			}
			/* Column separations */
			strcat(str, "  ");
			/* Write data */
			if(ascii_log)
				fprintf(fps->log, "%s", str);
			fwrite((void *) &blog, sizeof(DOUBLE), 1, fps->bin);
		}
			
		/* update pointer */
		uni++;
	}

	/* Print flag status */
	f = curr->flag_list;
	while(f->name != NULL){
		/* Print positive flag status */
		if(f->code > 0){
			for(j=0; j<(f->size); j++){
				if(*(f->flag + j) < BADBYTE){
					sprintf(str, "%3d", *(f->flag + j));
					blog = (DOUBLE) *(f->flag + j);
				}
				else{
					sprintf(str, "NaN");
					blog = BADDOUBLE;
				}
				/* Column separations */
				strcat(str, "  ");
				/* Write data in log files */
				if(ascii_log)
					fprintf(fps->log, "%s", str);
				fwrite((void *) &blog, sizeof(DOUBLE), 1, fps->bin);
			}
		}
		/* update pointer */
		f++;
	}
	/* End of line */
	if(ascii_log)
		fprintf(fps->log, "\n");

	return(0);

}

/* ################################################################## 
	 -Initializations:
	 ################################################################## */

#if PROTOTYPE_ALLOWED
int init_files(SCAN_NBR_FILE_TYPE *fps)
#else
int init_files(fps)
#endif
{

	FILE_NAME_TYPE file;
	DOUBLE fdata;

	fps->lst = check_fopen((char *) file_list, "r");
	new_extension(file, out_file, ".log");
	fps->log = check_fopen((char *) file, "w");
	new_extension(file, out_file, ".bin");
	fps->bin = check_fopen((char *) file, "wb");
	new_extension(file, out_file, ".sta");
	fps->sta = check_fopen((char *) file, "w");
	set_msg_file(fps->log);

	/* Write a control float in the binary file for automatic selection
		 of opening parameters in Matlab */
	fdata = 1.0;
	fwrite((void *) &fdata, sizeof(DOUBLE), 1, fps->bin);

	return(0);

}

#if PROTOTYPE_ALLOWED
int close_files(SCAN_NBR_FILE_TYPE *fps)
#else
int close_files(fps)
#endif
{

	fclose(fps->lst);
	fclose(fps->sta);
	fclose(fps->log);
	fclose(fps->bin);

	return(0);

}

#if PROTOTYPE_ALLOWED
int free_memory(SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr)
#else
int free_memory(prev, curr)
#endif
{

	free_unistat_list(curr->stat_list);
	free_flag_list(curr->flag_list);
	free_flag_list(prev->flag_list);
	free(curr->stat_list);
	free(curr->flag_list);

	return(0);

}

#if PROTOTYPE_ALLOWED
ULONG check_option_bits(ULONG bits, OPTION_TYPE *list, int imin, int imax)
#else
ULONG check_option_bits(bits, list, imin, imax)
#endif
{

	ULONG mask;
	int i;

	/* Build the bit for the 'ALL' selection */
	mask = (ULONG) to_bit(imax);
	/* Check if 'ALL' has been selected */
	if(bits & mask){
		/* Set all valid bits */
		for(i=imin; i<imax; i++){
			mask = (ULONG) to_bit(i);
			if(get_option_name(list, i) != NULL)
				bits |= mask;
		}
	}

	return(bits);

}

/* ------------------------------------------------------------------
	-Initialize Scan:

	This function should be called after the options are read from the control
	file.

	   +Initialize files.
	   +Initialize options bits.
		 +Initialize unistat array list.
		 +Initialize flag array list.
		 +Initialize ping variable.
   ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int init_scan(SCAN_NBR_FILE_TYPE *fps, 
							SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr)
#else
int init_scan(fps, prev, curr)
SCAN_NBR_FILE_TYPE *fps;
SCAN_NBR_PING_TYPE *prev;
SCAN_NBR_PING_TYPE *curr;
#endif
{

	char func[]="init_scan";
	
	/* Initialize files */
	init_files(fps);

	/* Initialize options bits */
	error_check_bits |= 
		check_option_bits(error_check_bits, error_check_options, 0, 7);
	error_check_bits |= 
		check_option_bits(error_check_bits, config_check_options, 8, 31);
	data_stat_bits |= 
		check_option_bits(data_stat_bits, data_stat_options, 0, 23);
	data_stat_bits |= 
		check_option_bits(data_stat_bits, data_flag_options, 24, 31);

	/* Initialize required unistat variables */
	curr->stat_list = (UNISTAT_LIST_TYPE *) 
		init_unistat_list(unistat_list, (char *) &data_stat_bits, 0);
	if(curr->stat_list == NULL){
		error_msg(func, "while initializing unistat list [curr]");
		return(INSUFFICIENT_MEMORY);
	}

	/* Initialize required flag variables */
	curr->flag_list = (FLAG_LIST_TYPE *) 
		init_flag_list(flag_list, (char *) &data_stat_bits, 0);
	if(curr->flag_list == NULL){
		error_msg(func, "while initializing flag list [curr]");
		return(INSUFFICIENT_MEMORY);
	}
	prev->flag_list = (FLAG_LIST_TYPE *) 
		init_flag_list(flag_list, (char *) &data_stat_bits, 0);
	if(prev->flag_list == NULL){
		error_msg(func, "while initializing flag list [prev]");
		return(INSUFFICIENT_MEMORY);
	}
	
	/* Initialize rest of scan data */
	init_nbr_ping(NULL, &(prev->fi), &(prev->ping));
	init_nbr_ping(NULL, &(curr->fi), &(curr->ping));
	prev->yd = BADDOUBLE;
	curr->yd = BADDOUBLE;

	/* Put the header in the log file */
	stat_write_header(fps->log, curr);

	return(0);
}

/* ##################################################################
	 -Check Functions:

	 These functions perform the different checking of data. They return 
	 a bit field whose single bits correspond to those in the [[check_bit]]
	 variables that were initialized during option processing.
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Error and Config Check:

	 These checks consist in looking at errors during data collection, and
	 configuration changes of the intrument.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
ULONG error_check(SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr)
#else
ULONG error_check(prev, curr) 
#endif
{

	SCAN_NBR_CONFIG_CHECK_TYPE *cfg;
	NBR_LEADER_TYPE *cl;

	ULONG error_bit=0;
	ULONG bit;
	ULONG offset;
	char *pv, *cv;

	/* Assign the pointers */
	cl= &(curr->ping.leader);

	/* Check the error variables */
	if(error_check_bits & BIT0)
		if(cl->bit_status)
			error_bit |= BIT0;

	/* Check for configuration changes */
	if(get_flag(curr->flag_list, -1, 0) == 1){
		cfg = config_check_list;
		while(cfg->size > 0){
			bit = (ULONG) to_bit(cfg->code);
			if(error_check_bits & bit){
				/* Get offset to variable pointer */
				offset = (ULONG) cfg->ptr - (ULONG) &tmp_ens;
				/* Get pointers to values */
				pv = (char *) (((ULONG) ((char *) &(prev->ping))) + offset);
				cv = (char *) (((ULONG) ((char *) &(curr->ping))) + offset);
				/* Compare values */
				if(memcmp((void *) pv, (void *) cv, cfg->size))
					error_bit |= bit;
			}
			/* Update pointer */
			cfg++;
		}
	}

	return(error_bit);
}

/* ------------------------------------------------------------------
	 --Update Flags:

	 This function takes all flags in order and updates them according to the
	 data found in the ping structure.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
ULONG update_flags(SCAN_NBR_PING_TYPE *curr)
#else
ULONG update_flags(curr) 
#endif
{

	NBR_LEADER_TYPE *l;

	FLAG_LIST_TYPE *f;

	/* Assign Pointers */
	l = &(curr->ping.leader);

	/* Loop through the list of flags */
	f = curr->flag_list;
	while(f->name != NULL){
		switch(f->code){
			/* --------------------------------- */
		case -2: 
			set_flag(f, f->code, 0,  
							 ((cnv_heading_360(l->heading) >= 0.0) 
								&& (cnv_heading_360(l->heading) < 360.0)) ? 1 : 0);
			break;
			/* --------------------------------- */
		case 24:
			if(data_stat_bits & BIT24)
				set_flag(f, f->code, 0, get_bottom_track_status(&(curr->ping)));
			break;
			/* --------------------------------- */
		default:
			/* Skipp these flags */
			break;
		}
		/* Update pointer */
		f++;
	}

	return(0);

}

/* ------------------------------------------------------------------
	 --Update Statistics:
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int update_stat(SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr)
#else
int update_stat(prev, curr)
#endif
{

	char func[]="update_stat";

	NBR_LEADER_TYPE *cl, *pl;
	UNISTAT_LIST_TYPE *uni;
	FLAG_LIST_TYPE *pf;
	UNISTAT_TYPE *s;

	double ref_heading;
	static double previous_heading=0.0;

	/* Assign pointers */
	pl = &(prev->ping.leader);
	cl = &(curr->ping.leader);
	pf = prev->flag_list;

	/* Generate the bit list of possible corrections */
	uni = curr->stat_list;
	while(uni->name != NULL){
		/* Get unistat variable definitions */
		s = uni->stat;
		/* ---------------------------------
			 Conversions prior updating
			 --------------------------------- */
		switch(uni->code){
		case 0:
			/* Need a previous ensemble time */
			if(get_flag(pf, -1, 0) == 1)
				*(uni->data) = 
          (double) rtc_diff(&pl->ensemble_start_time, year_base,
                            &cl->ensemble_start_time, year_base);
			else
				*(uni->data) = BADDOUBLE;
				break;
		case 1:
			if(get_flag(pf, -2, 0) == 1)
				ref_heading = previous_heading;
			else
				ref_heading = 0.0;
			*(uni->data) = 
				nearest_angle(ref_heading, 
											to360(cnv_heading_360(cl->heading) - tr_mis.heading));
			/* Update previous heading */
			previous_heading = *(uni->data);
			break;
		case 2:
			*(uni->data) = pitch_sign(cl->adcp_config) 
				* (cnv_pitch_180(cl->pitch_tilt) - tr_mis.pitch);
			break;
		case 3:
			*(uni->data) = roll_sign(cl->adcp_config) 
				* (cnv_roll_180(cl->roll_tilt) - tr_mis.roll);
			break;
		case 4:
			*(uni->data) = cnv_heading_sdev(cl->heading_std_dev);
			break;
		case 5:
			*(uni->data) = cnv_pitch_sdev(cl->pitch_std_dev);
			break;
		case 6:
			*(uni->data) = cnv_roll_sdev(cl->roll_std_dev);
			break;
		case 7:
			*(uni->data) = cnv_adcp_temp(cl->adcp_temp);
			break;
		default:
			warning_msg(func, "No action defined for this cude");
			break;
		}
		/* ---------------------------------
			 Update statistics
			 --------------------------------- */
		update_unistat_d(s, uni->data, uni->size);
		
		/* Update pointer */
		uni++;
	}

	
	return(0);

}

/* ##################################################################
	 -Output Functions:
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Calculate Statistics:

	 This function calculate statistics of all unistat cells, and write the
	 output in the log file. 
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int calculate_stat(FILE *fp, UNISTAT_LIST_TYPE *list, char *label)
#else
int calculate_stat(fp, list, label)
#endif
{

	UNISTAT_LIST_TYPE *uni;
	int i;

	uni = list;
	while(uni->name != NULL){
		
		/* Calculate statistics */
		calculate_unistat(uni->stat);
		
		/* Special transformations */
		switch(uni->code){
		case 1:
			for(i=0; i<(uni->stat->n); i++){
				*(uni->stat->sum+i) = to360(*(uni->stat->sum+i));
				*(uni->stat->max+i) = to360(*(uni->stat->max+i));
				*(uni->stat->min+i) = to360(*(uni->stat->min+i));
			}
			break;
		default:
			/* Skipp other variables */
			break;
		}

		/* Write results in output file */
		write_unistat(fp, uni->stat, 1.0, NULL, label, 0); 

		/* Update list pointer */
		uni++;
	}

	return(0);

}

/* ------------------------------------------------------------------
	--Ensemble Information:

	Each time a message from checks is reported, a header giving information
	about the current ensemble is output in order to be able to track it back in
	the data file, or to unselect it from the list of ensembles to scan and
	load. This function uses [[report_msg]] so that messages go to screen and to
	the log file.
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int ens_info(SCAN_NBR_PING_TYPE *curr)
#else
int ens_info(curr) 
#endif
{

	NBR_LEADER_TYPE *l;
	YMDHMS_TIME_TYPE time;
	char str[100], msg[100];

	/* Assign pointers */
	l=&(curr->ping.leader);

	/* Convert ensemble start time into string */
	rtc_to_ymdhms(&(l->ensemble_start_time), &time, year_base);
	ymdhms_to_str(str, &time, 1);

	/* Print header */
	sprintf(msg, "\n%%%% CHECK:\n");
	report_msg(msg);
	sprintf(msg, "%%%%   ensemble_number    : %hu\n",     l->ensemble_number);
	report_msg(msg);
	sprintf(msg, "%%%%   data_file_name     : \n%%%%\t%s\n", curr->fi.name);
	report_msg(msg);
	sprintf(msg, "%%%%   start_address      : %8lXh\n",   curr->fi.data_pos);
	report_msg(msg);
	sprintf(msg, "%%%%   ensemble_start_time: %s\n",     str);
	report_msg(msg);

	return(0);
}
 
/* ------------------------------------------------------------------
	 --Error Check Output: 

	 The error codes from the [[bit_status]] variable are stored in the global
	 variable [[error_bit_codes]], and may be retrieved with
	 [[get_error_bit_code]], which returns the human description of the error
	 code.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
char *get_error_bit_code(UBYTE bit)
#else
char *get_error_bit_code(bit)
UBYTE bit; 
#endif
{

	NBR_ERROR_BIT_CODE_TYPE *err;

	err = error_bit_codes;
	while( err->name != NULL ){
		if( err->code == bit)
			return(err->name);
		err++;
	}
	return("NO NAME DEFINED FOR CODE");
}

#if PROTOTYPE_ALLOWED
int check_write(SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr, 
								ULONG bits)
#else
int check_write(prev, curr, bits) 
#endif
{

	char func[]="check_write";

	SCAN_NBR_CONFIG_CHECK_TYPE *cfg;
	NBR_LEADER_TYPE *l;
	ULONG mask, offset;
	USHORT beam;
	int i, j;
	char str[100], msg[100], *name;
	char s1[2000], s2[2000];

	/* Assign pointers */
	l = &(curr->ping.leader);

	/* Check for errors */
	i = 0;
	while(i<32){
		mask = (ULONG) to_bit(i);
		if((error_check_bits & mask) && (bits & mask)){
			/* Write the information header */
			ens_info(curr);
			i = 32;
		}
		i++;
	}
	if(i == 32)
		return(0);
		
	/* ---------------------------------
		 Error Check
		 --------------------------------- */
	for(i=0; i<7; i++){
		mask = (ULONG) to_bit(i);
		/* Check if this check has been selected */
		if((error_check_bits & mask) && (bits & mask)){
			/* ---------------------------------
				 ADCP BIT status error:
				 --------------------------------- */
			switch(i){
			case 0:
					/* Get the name of the bit result code */
					beam = l->bit_status & 0xF0;
					beam >>= 4;
					beam &= 0x0F;
					if((beam>0) && (beam<5)){
						/* This is a beam related problem */
						sprintf(str, "[Beam#%1hu]", beam);
						for(j=0; j<4; j++){
							mask= (ULONG) to_bit(j);
							if(l->bit_status & mask){
								strcat(str, "[");
								strcat(str, get_error_bit_code(0xF0 | (l->bit_status & mask)));
								strcat(str, "]");
							}
						}
					}
					/* This is not a beam related problem */
					else
						strcpy(str, get_error_bit_code(0xF0 | (l->bit_status & mask))); 
					/* finish building the message */
					sprintf(msg, "bit_status = %02Xh  %s", 
									l->bit_status, str);
					break;
			default:
				warning_msg(func, "No error defined for this bit");
				break;
			}
			/* Print message */
			strcpy(str, msg);
			sprintf(msg, "%%%% error_check: %s\n", str);
			report_msg(msg);
		}
	}

	/* ---------------------------------
		 Config Check: only if a previous
		               ensemble is here
		 --------------------------------- */
	if(get_flag(curr->flag_list, -1, 0) == 1){
		cfg = config_check_list;
		while(cfg->code > 0){
			mask = (ULONG) to_bit(cfg->code);
			if((error_check_bits & mask) && (bits & mask)){
				/* Get name of variable from option list */
				name = get_option_name(config_check_options, cfg->code);
				/* Offset to variable pointer */
				offset = (ULONG) cfg->ptr - (ULONG) &tmp_ens;
				/* Build message */
				strncpy(s1, (char *) (((ULONG) prev) + offset), cfg->size);
				*(s1 + cfg->size) = '\0';
				strncpy(s2, (char *) (((ULONG) curr) + offset), cfg->size);
				*(s2 + cfg->size) = '\0';
				sprintf(msg, "[%s] changed from [%s] to [%s]", name, s1, s2);
				/* Finish building message */
				sprintf(msg, "%%%% config_check: %s\n", str);
				report_msg(msg);
			}
			/* Update pointer */
			cfg++;
		}
	}
	
	/* Empty line to finish */
	report_msg("\n");
	
	return(0);
}
	
/* ------------------------------------------------------------------
	 --Backup Function:

	 See also information in [[do_it]].
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int swap_data(SCAN_NBR_PING_TYPE *prev, SCAN_NBR_PING_TYPE *curr)
#else
int swap_data(prev, curr)
#endif
{

	char func[]="swap_data";

	FLAG_LIST_TYPE *flags;
	int error;

	/* Backup location of previous flag state */
	flags = prev->flag_list;

	/* Backup current data */
	memcpy((void *) prev, (void *) curr, sizeof(SCAN_NBR_PING_TYPE));

	/* Place previous flag state into current ping and initialize it */
	curr->flag_list = flags;
	error = set_bad_flags(curr->flag_list);
	if(error){
		error_msg(func, "Initializing flag values");
		return(error);
	}
		
	return(0);

}

/* ##################################################################
	 -Do It:

	 This is the main routine called from [[main_cnt]].

	 NOTE: On Backups
	       ----------

	 Pointers to statistics need to be initialized only for current
	 ping. The location of stat and data values will be automatically copied
	 into previous ping by [[swap_data]], through a memory copy. However,
	 pointers to flags need to be initialized both in current and prvious ping,
	 since information from both ping state is necessary for error checks.

	 
	 NOTE: On Pitch and Roll
	       -----------------

	 According to the ADCP User's Manual, pitch and roll tilt angles from the
	 raw files should be recalculated, depending on the rotation axes of the
	 sensors. However, Transect does not seem to do this, especially because
	 there is no setting allowing to specify the kind of the sensors axes.
	 Furthermore, what Transect outputs is really just the averged data of the
	 raw files content.
	 ################################################################## */

#if PROTOTYPE_ALLOWED
void do_it(FILE *fp_cnt)
#else
void do_it(fp_cnt) 
FILE *fp_cnt;
#endif
{

	char func[]="do_it";

	SCAN_NBR_FILE_TYPE fps;
	SCAN_NBR_PING_TYPE curr, prev;
	UNISTAT_LIST_TYPE *uni;

	char str[200], msg[200];

	YMDHMS_TIME_TYPE start_time, stop_time, time;
	double start_day, stop_day;
  ULONG error_bit=0;
	int error=1, stat_count=0;

	/* Get the parameters */
	if (get_parameters(fp_cnt, scan_nbr_params, NULL)) exit(-1);
	print_parameters(stdout, scan_nbr_params, NULL, "\n");

	/* Read and process all options */
	if (execute_options(fp_cnt, scan_options, ECHO)<=0){
		error_msg(func, "while reading scan options.");
		exit(-1);
	}

	if(init_scan(&fps, &prev, &curr)){
		error_msg(func, "While initializing scan");
		goto on_error;
	}

	/* Read first ensemble */
	if( init_nbr_ping(fps.lst, &(curr.fi), &(curr.ping)) ){
		error_msg(func, "While reading first ensemble");
		goto on_error;
	}
	/* Calculate ensemble day */
	curr.yd = rtc_to_yd(&(curr.ping.leader.ensemble_start_time), year_base);
	
	/* Loop through time ranges */
	while(get_time_range(fp_cnt, &start_time, &stop_time)==1){

		/* Convert times into minutes from start of */
		start_day = year_day(&start_time, year_base);
		stop_day  = year_day(&stop_time,  year_base);

		/* Message */
		time_range_to_str(str, &start_time, &stop_time);
		sprintf(msg, "\n%%%%%% Time range: %s\n", str);
		report_msg(msg);
		rtc_to_ymdhms(&(curr.ping.leader.ensemble_start_time), &time, year_base);
		ymdhms_to_str(str, &time, 1);
		sprintf(msg, "\n%%%%%% %s Outside time range ...\n", str);
		report_msg(msg);

		/* Set scan_status flag */
		if(set_flag(curr.flag_list, -1, 0, 0))
			warning_msg(func, "No code defined for this flag");

		/* Loop outside range */
		while(curr.yd < start_day){

			/* Short information */
			short_info(&(curr.ping.leader.ensemble_number), -USHORT_VALUE_CODE);
						
			/* Update flags */
			update_flags(&curr);
			
			/* Backup current data */
			error = swap_data(&prev, &curr);
			if(error){
				error_msg(func, "Swapping pings");
				goto on_error;
			}
			
			/* Read next ping */
			error = next_nbr_ping(fps.lst, &(curr.fi), &(curr.ping), 1);
			if(error){
				error_msg(func, "While reading next ensemble [Outside]");
				goto on_error;
			}
      report_file_change(prev.fi.name, curr.fi.name);
			
			/* Calculate ensemble day */
			curr.yd = rtc_to_yd(&(curr.ping.leader.ensemble_start_time), 
													year_base);
		}
		
		/* Message */
		rtc_to_ymdhms(&(curr.ping.leader.ensemble_start_time), &time, year_base);
		ymdhms_to_str(str, &time, 1);
		sprintf(msg, "\n%%%%%% %s Inside time range ...\n\n", str);
		report_msg(msg);
		
		/* Loop inside time range */
		error = 0;
		while((curr.yd <= stop_day) && (!error)){
			
			/* Short information */
			short_info(&(curr.ping.leader.ensemble_number), -USHORT_VALUE_CODE);

			/* Check for errors */
			error_bit = error_check(&prev, &curr);

			/* Update flags */
			update_flags(&curr);

			/* Update statistics */
			update_stat(&prev, &curr);

			/* write error check results */
			check_write(&prev, &curr, error_bit);

			/* Set scan_status flag */
			if(set_flag(curr.flag_list, -1, 0, 1))
				warning_msg(func, "No code defined for this flag");

			/* write value of data being averaged */
			stat_write(&fps, &curr);
			
			/* Backup current data */
			error = swap_data(&prev, &curr);
			if(error){
				error_msg(func, "Swapping pings");
				goto on_error;
			}
			
			/* Read next ping */
			error = next_nbr_ping(fps.lst, &(curr.fi), &(curr.ping), 1);
			if(error){
				if(error != EOF){
					error_msg(func, "While reading next ensemble [Inside]");
					goto on_error;
				}
			}
      report_file_change(prev.fi.name, curr.fi.name);
			
			/* Calculate ensemble day */
			if(!error)
				curr.yd = rtc_to_yd(&(curr.ping.leader.ensemble_start_time), 
														year_base);
		}
		
		/* Calculate statistics */
		rtc_to_ymdhms(&(curr.ping.leader.ensemble_start_time), &time, year_base);
		ymdhms_to_str(str, &time, 1);
		sprintf(msg, "\n%%%% %s Calculating Statistics", str);
		report_msg(msg);
		sprintf(str, "%03d", stat_count);
		sprintf(msg, "\n%%%% Statistics number %s\n", str);
		report_msg(msg);
		calculate_stat(fps.sta, curr.stat_list, str);
		stat_count++;

		/* Initialize statistics */
		uni = curr.stat_list;
		while(uni->name != NULL){
			zero_unistat(uni->stat);
			uni++;
		}

	}
	
	if(!error)
		error = 0;

on_error:
	/* Close the files */
	close_files(&fps);
	/* Free all variables */
	free_memory(&prev, &curr);

	/* Check for errors */
	if(error != 0){
		if(error != EOF)
			printf("\n\n\t !!! Function `do_it' ended with error !!!\n\n");
		else
			printf("\n\n\t Function `do_it' ended at EOF \n\n");
	}
	else
		printf("\n\n\t Function `do_it' ended normally \n\n");
	
	return;
}







