/**---------------------------------------------------------------------------
 ** 
 ** fix_nbr.c -- 
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/30 19:37:23
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/07/30 19:59:08
 ** Update Count    : 6
 ** Directory       : /home/pego/pcd1/codas3c/gfi/src/apps/nbr/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    Undocumented.
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

/* ##################################################################

	-Introduction:

	This programme allows to change some data in the NB ADCP raw files, and
	write these data back into NB ADCP raw files. These may be re-processed then
	by the processing programme, such as TRANSECT or DAS.

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

	*/

#include "fix_nbr.h"

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

#if PROTOTYPE_ALLOWED
int init_options()
#else
int init_options()
#endif
{

	fix_nbr_bits = 0x0;
	strcpy(scan_nav_file, "");
	tr_mis.heading = 0.0;
	tr_mis.pitch = 0.0;
	tr_mis.roll = 0.0;

	return(0);

}


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

	fps->lst = check_fopen(file_list, "r");
	fps->log = check_fopen(log_file, "w");
	if(strlen(scan_nav_file))
		fps->nav = check_fopen(scan_nav_file, "r");
	else
		fps->nav = NULL;
	fps->out = NULL;

	set_msg_file(fps->log);

	return(0);

}

#if PROTOTYPE_ALLOWED
int close_files(FIX_NBR_FILES_TYPE *fps, DATA_FILE_INFO_TYPE *fi)
#else
int close_files(fps, fi)
#endif
{

	fclose(fps->lst);
	fclose(fps->log);
	if(fps->nav)
		fclose(fps->nav);
	if(fps->out)
		fclose(fps->out);
	if(fi->fp)
		fclose(fi->fp);

	return(0);

}

/* ------------------------------------------------------------------
	--Initialize FIX_NBR:
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int init_fix(FIX_NBR_FILES_TYPE *fps, FIX_NBR_TYPE *prev, FIX_NBR_TYPE *curr)
#else
int init_fix(fps, prev, curr)
#endif
{

	init_files(fps);
	if(strlen(scan_nav_file))
		fix_nbr_bits |= USE_SCAN_NAV_BIT;

	init_nbr_ping(NULL, &(prev->fi), &(prev->ping));
	init_nbr_ping(NULL, &(curr->fi), &(curr->ping));
	
	init_scan_nav_line(&(prev->nav), -1);
	init_scan_nav_line(&(curr->nav), -1);

	strcpy(prev->out_file_name, "");
	strcpy(curr->out_file_name, "");

	return(0);
	
}

/* ##################################################################
	 -Miscellaneous:
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Set Ensemble Times:

	 This function estimates the ensemble start and end times, according to the
	 options from the control file.

	 adcp time is the start time of current ensemble. However, this time
	 does not contain the year, and the day may be wrong. This is updated
	 with the start_date parameter from the control file. 

	 adcp stop time should be set to the adcp start time of the next
	 ensemble, but this is not known yet. However, if navigation data are
	 not available, there will be no need to correct navigation after
	 [[loadtnbp]] has loaded the data into the data base. Therefore, the end
	 time will be set  to a bad value.

	 scan_nav time is the stop time of current ensemble. We assume here that
	 the start time of current ensemble is the stop time of previous
	 ensemble. 
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int set_ens_times(FIX_NBR_TYPE *curr, int year_base)
#else
int set_ens_times(curr, year_base)
#endif
{

	/* Check if time should be taken from the scan_nav file */
	if((fix_nbr_bits & USE_SCAN_NAV_BIT) 
		 && (fix_nbr_bits & SCAN_NAV_TIME_BIT)){
		curr->time.start = curr->nav.time.start;
		curr->time.stop = curr->nav.time.stop;
	}
	else{
		curr->time.start = 
			rtc_to_yd(&(curr->ping.leader.ensemble_start_time), year_base);
		curr->time.stop = BADDOUBLE;
	}
	
	return(0);
}
	

/* ------------------------------------------------------------------
	--Update Output File:

	This function looks if the files for the previous and current pings are
	different. If this is the case, the current output file is closed and a new
	file for the current is initialized.
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int update_output_file(FIX_NBR_FILES_TYPE *fps, FIX_NBR_TYPE *prev, 
											 FIX_NBR_TYPE *curr)
#else
int update_output_file(fps, prev, curr)
#endif
{

	char func[]="update_output_file";

	FILE_NAME_TYPE path, name, all;
	char msg[200];

	/* Check if file names have changed */
	if((strcmp(prev->fi.name, curr->fi.name) == 0)
		 && (fps->out != NULL))
		return(0);
	
	/* Ping file has changed: close current output file */
	if(fps->out != NULL){
		/* Message */
		split_file_path(curr->out_file_name, path, name);
		sprintf(msg, "\n%%%%%% WARNING: Closing output file: %s\n", name);
		report_msg(msg);
		fclose(fps->out);
		/* Set pointer to NULL */
		fps->out = NULL;
	}

	/* Open output file for current record */
	if(fps->out == NULL){
		strcpy(all, curr->fi.name);
		/* Split path of file */
		split_file_path(all, path, name);
		/* Build new file name */
		strcpy(all, out_dir);
		strcat(all, name);
		/* Check if the new file is really different */
		if(strcmp(curr->fi.name, all) == 0){
			error_msg(func, "Output and input files are identical");
			return(CONVERT_ERROR);
		}
		/* Assign the new file name and initialize it */
		strcpy(curr->out_file_name, all);
		fps->out = check_fopen(curr->out_file_name, "wb");
		/* Message */
		sprintf(msg, "\n%%%%%% WARNING: Opening output file: %s\n", name);
		report_msg(msg);
	}

	return(0);

}

/* ##################################################################
	 -Read Data:
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Next Ping:

	 This function read ADCP ping data until the ensemble number corresponds
	 with that from the navigation file. If [[USE_SCAN_NAV_BIT]] is not set,
	 then it just read the next ping
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int next_ping(FILE *fp, FIX_NBR_TYPE *curr, int verbose)
#else
int next_ping(fp, curr, verbose)
#endif
{

	char func[]="next_ping";

	int error;

	if(fix_nbr_bits & USE_SCAN_NAV_BIT){
		while(curr->ping.leader.ensemble_number != curr->nav.number){

			/* Read next adcp ensemble */
			error = next_nbr_ping(fp, &(curr->fi), &(curr->ping), 1);
			if(error){
				if(error != EOF)
					error_msg(func, "While reading next ADCP ping");
				return(READ_ERROR);
			}
			if(verbose)
				short_info(&(curr->ping.leader.ensemble_number), -USHORT_VALUE_CODE);
		}
		/* Update ensemble number */
		curr->number = curr->nav.number;
	}
	else{

		/* Read next adcp ensemble */
		error = next_nbr_ping(fp, &(curr->fi), &(curr->ping), 1);
		if(error){
			if(error != EOF)
				error_msg(func, "While reading next ADCP ping");
			return(READ_ERROR);
		}
		/* Update ensemble number */
		curr->number = curr->ping.leader.ensemble_number;
	}

	return(0);

}

/* ------------------------------------------------------------------
	 --Next Record:

	 This function reads the next record of data, i.e. adcp raw data and output
	 of scan_nav programmes.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int next_record(FIX_NBR_FILES_TYPE *fps, FIX_NBR_TYPE *curr, int year_base,
								int verbose)
#else
int next_record(fps, curr, year_base, verbose)
#endif
{

	char func[]="next_record";

	int error=0;

	/* Read next scan_nav line */
	if(fix_nbr_bits & USE_SCAN_NAV_BIT){
		error = read_scan_nav_line(fps->nav, &(curr->nav), year_base);
		if(error){
			if(error != EOF)
				error_msg(func, "While reading first lines of [[scan_nav]]");
			return(READ_ERROR);
		}
	}

	/* Read next adcp ensembles until correct time */
	error = next_ping(fps->lst, curr, verbose);
	if(error){
		if(error != EOF)
			error_msg(func, "While reading next ADCP ping");
		return(READ_ERROR);
	}
	
	return(0);

}

/* ##################################################################
	 -Fixing Data:
	 ################################################################## */

/* ------------------------------------------------------------------
	--Fix Record:

	This function replaces original ADCP data by the desired fixes.

	The 8 bytes of time at the beginning of the leader buffer must be recoded
	into hexadecimal
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int fix_record(FIX_NBR_TYPE *curr, int year_base)
#else
int fix_record(curr, year_base)
#endif
{

	char func[]="fix_record";

	NBR_LEADER_TYPE *l;
	SCAN_NAV_LINE_TYPE *nav;

	SHORT  s, sign;
	DOUBLE d;

	YMDHMS_TIME_TYPE time;

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

	/* Check for fixes from scan_nav */
	if(fix_nbr_bits & USE_SCAN_NAV_BIT){
		/* Fix heading */
		if(fix_nbr_bits & SCAN_NAV_HEADING_BIT){
			d = nav->prh.heading.mean + tr_mis.heading;
			s = (SHORT) round(to_nbr_heading(d));
			l->heading = (USHORT) s;
			l->heading_std_dev = 
				(UBYTE) round(to_nbr_hdg_sdev(nav->prh.heading.sdev));
		}
		/* Fix roll */
		if(fix_nbr_bits & SCAN_NAV_ROLL_BIT){
			d = nav->prh.roll.mean + tr_mis.roll;
			s = (SHORT) to_nbr_roll(abs_val(d));
			sign = (SHORT) roll_sign(l->adcp_config);
			s = (d < 0.0) ? s * -1 * sign : s * sign;
			l->roll_tilt = (SHORT) s;
			l->roll_std_dev = (UBYTE) round(to_nbr_hdg_sdev(nav->prh.roll.sdev));
		}
		/* Fix pitch */
		if(fix_nbr_bits & SCAN_NAV_PITCH_BIT){
			d = nav->prh.pitch.mean + tr_mis.pitch;
			s = (SHORT) to_nbr_pitch(abs_val(d));
			sign = (SHORT) pitch_sign(l->adcp_config);
			s = (d < 0.0) ? s * -1 * sign : s * sign;
			l->pitch_tilt = (SHORT) s; 
			l->pitch_std_dev = (UBYTE) round(to_nbr_hdg_sdev(nav->prh.pitch.sdev));
		}

		/* Fix time */
		if(fix_nbr_bits & SCAN_NAV_TIME_BIT){
			yd_to_ymdhms_time(curr->time.start, year_base, &time);
			l->ensemble_start_time.month = (UBYTE) time.month;
			l->ensemble_start_time.day = (UBYTE) time.day;
			l->ensemble_start_time.hour = (UBYTE) time.hour;
			l->ensemble_start_time.minute = (UBYTE) time.minute;
			l->ensemble_start_time.second = (UBYTE) time.second;
		}
	}

	/* Convert times into BCD hexadecimal format: this must always be done */
	if( byte_dec_to_hexa((UBYTE *) &(l->ensemble_start_time), 
											 (UBYTE *) &(l->ensemble_start_time), 8) != 0){
		error_msg(func, "while converting times");
		return(CONVERT_ERROR);
	}

	return(0);

}

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

/* ------------------------------------------------------------------
	--Write Fix:

	This function writes the fixed NB raw data into the new output file. Note
	that in order for these files to be averaged by transect, the checksum at
	the end of each ensemble must be correct in order for TRANSECT to read the
	generated files.
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int write_fix(FILE *fp, FIX_NBR_TYPE *curr)
#else
int write_fix(fp, curr)
#endif
{

	char func[]="write_fix";

	NBR_ENSEMBLE_TYPE raw;
	NBR_ENSEMBLE_TYPE *ens;
	NBR_HEADER_TYPE *h;
	NBR_LEADER_TYPE *l;

	USHORT checksum;
	ULONG sum;
	char *buff_ptr, buff[NBR_ENSEMBLE_SIZE_MAX];
	UBYTE *p;
	int i;

	/* Assign pointers */
	ens = &(curr->ping);
	h = &(ens->header);
	l = &(ens->leader);

	/* Control file pointer */
	if(fp == NULL){
		error_msg(func, "File pointer to output file is [[NULL]]");
		return(WRITE_ERROR);
	}

	/* Copy data from the raw data from the [[curr]] structure into the [[raw]]
		 structure. This for security reasons. */
	memcpy((char *) &raw, (char *) ens, sizeof(NBR_ENSEMBLE_TYPE));
	
	/* Conversion into machine independent format. Only the data that are going
		 to be written are converted */

	/* ---------------------------------
		 Header */
	buff_ptr = buff;
	buff_ptr += convert_array_short((char *) buff_ptr, 
																	(char *) &(raw.header.output_buffer_size), 
																 HOST_ENVIRONMENT, SUN3_COMPATIBLE_HOST, 7);
	/* ---------------------------------
		 Leader */
  buff_ptr += copy_byte((char *) buff_ptr,
												(char *) &(raw.leader.ensemble_start_time), 
												8, AS_IS, NULL);
  buff_ptr += convert_array_short((char *) buff_ptr,
																	(char *) &(raw.leader.pings_per_ensemble), 
																	HOST_ENVIRONMENT, SUN3_COMPATIBLE_HOST, 1);
  buff_ptr += copy_byte((char *) buff_ptr,
												(char *) &(raw.leader.number_of_bins), 
												5, AS_IS, NULL);
  buff_ptr += convert_array_short((char *) buff_ptr,
																	(char *) &(raw.leader.ensemble_number), 
																 HOST_ENVIRONMENT, SUN3_COMPATIBLE_HOST, 1);
  buff_ptr += copy_byte((char *) buff_ptr,
												(char *) &(raw.leader.bit_status), 
												4, AS_IS, NULL);
  buff_ptr += convert_array_short((char *) buff_ptr,
																	(char *) &(raw.leader.pitch_tilt), 
																 HOST_ENVIRONMENT, SUN3_COMPATIBLE_HOST, 4);
  buff_ptr += copy_byte((char *) buff_ptr,
												(char *) &(raw.leader.high_voltage_in), 
												18, AS_IS, NULL);
  buff_ptr += convert_array_short((char *) buff_ptr,
																	(char *) raw.leader.bottom_track_range, 
																 HOST_ENVIRONMENT, SUN3_COMPATIBLE_HOST, 4);
  buff_ptr += copy_byte((char *) buff_ptr,
												(char *) &(raw.leader.pitch_std_dev), 
												8, AS_IS, NULL);

	/* ---------------------------------
		 Rest of data */
	
	buff_ptr += copy_byte((char *) buff_ptr, (char *) raw.raw_velocity,
												(int) h->velocity_buffer_size, AS_IS, NULL);
	buff_ptr += copy_byte((char *) buff_ptr, (char *) raw.spectral_width,
												(int) h->spectral_width_buffer_size, AS_IS, NULL);
	buff_ptr += copy_byte((char *) buff_ptr, (char *) raw.echo_intensity,
												(int) h->echo_intensity_buffer_size, AS_IS, NULL);
	buff_ptr += copy_byte((char *) buff_ptr, (char *) raw.percent_good,
												(int) h->percent_good_buffer_size, AS_IS, NULL);
	buff_ptr += copy_byte((char *) buff_ptr, (char *) raw.raw_status,
												(int) h->status_buffer_size, AS_IS, NULL);



	/* ---------------------------------
		 Checksum */
	sum = 0;
	i = 0;
	p = (UBYTE *) buff;
	while((ULONG) p < (ULONG) buff_ptr){
		sum += (ULONG) *p;
		p++;
		i++;
	}
	sum = sum % (ULONG) 65536;
	checksum = (USHORT) sum;
	if(convert_array_short((char *) buff_ptr, (char *) &checksum,
												 SUN3_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1) != 2){
		error_msg(func, "while converting checksum to the machine format");
		return(CONVERT_ERROR);
	}
	i += 2;
	/* Write data */
	if(fwrite(buff, i, 1, fp) != 1){
		error_msg(func, "while writing data");
		return(WRITE_ERROR);
	}

	return(0);

}

/* ------------------------------------------------------------------
	 -Main:

	 Main routine called from [[main_cnt]].
	 ------------------------------------------------------------------ */

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

	char func[]="do_it";

	FIX_NBR_FILES_TYPE fps;
	FIX_NBR_TYPE prev, curr;

	YMDHMS_TIME_TYPE start_time, stop_time;
	double start_day, stop_day, yd;

	int error=0;
	char str[100], msg[200];

	/* First Initializations */
	init_options();
	
	/* Read the parameters */
	if (get_parameters(fp_cnt, fix_nbr_params, NULL))
		exit(-1);
	print_parameters(stdout, fix_nbr_params, NULL, "\n");

	/* Read and process all options */
	if (execute_options(fp_cnt, fix_nbr_options, ECHO)<=0){
		error_msg(func, "while reading fix_nbr options.");
		exit(-1);
	}
	/* Initialize variables and options */
	init_fix(&fps, &prev, &curr);

	/* Read the first adcp ping */
	report_msg("\n%%% Reading first ADCP ping...\n");
	if( init_nbr_ping(fps.lst, &(curr.fi), &(curr.ping)) ){
		error_msg(func, "While reading first ensemble");
		goto on_error;
	}
	curr.number = curr.ping.leader.ensemble_number;
	sprintf(msg, "\n%%%%%% FIRST ENSEMBLE: %hu", 
					curr.number);
	report_msg(msg);

	/* Read scan_nav until first valid line, then read again adcp until ensemble
	 numbers are the same */
	if(fix_nbr_bits & USE_SCAN_NAV_BIT){
		report_msg("\n%%% Reading first valid scan_nav line...\n");
		error = read_scan_nav_line(fps.nav, &(curr.nav), year_base);
		if(error){
			error_msg(func, "While reading first lines of| [[scan_nav]]");
			goto on_error;
		}
		curr.number = curr.nav.number;
		/* Message */
		sprintf(msg, "\n%%%%%% FIRST ENSEMBLE: %hu", curr.number);
		report_msg(msg);
	}

	/* Read until correct adcp ensemble number */
	report_msg("\n%%% Read until correct ADCP ensemble number...\n");
	error = next_ping(fps.lst, &curr, 1);
	if(error){
		error_msg(func, "While reading next ensemble [Initialization]");
		goto on_error;
	}

	/* Set the correct ensemble start and stop times */
	set_ens_times(&curr, year_base);
	
	/* Message */
	yd_to_ymdhms_str(str, curr.nav.time.start, year_base);
	sprintf(msg, "  FROM: %s", str);
	report_msg(msg);
	yd_to_ymdhms_str(str, curr.nav.time.stop, year_base);
	sprintf(msg, "  TO: %s\n", str);
	report_msg(msg);
	
	/* -------------------------------------------------------
		 Loop through the time ranges
		 ------------------------------------------------------- */
	while(get_time_range(fp_cnt, &start_time, &stop_time)==1){

		/* Convert time range into decimal day */
		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);
		sprintf(msg, "\n%%%%%% ENSEMBLE: %hu", curr.number);
		report_msg(msg);
		yd_to_ymdhms_str(str, curr.time.start, year_base);
		sprintf(msg, "  FROM: %s", str);
		report_msg(msg);
		yd_to_ymdhms_str(str, curr.time.stop, year_base);
		sprintf(msg, "  TO: %s\n", str);
		report_msg(msg);
		report_msg("%%% Outside time range...\n");

		/* Get year day of current ping start */
		yd = curr.time.start;
		
		/* ------------------------------------------------------- 
			 Loop outside time time range
			 ------------------------------------------------------- */
		while(yd < start_day){

			/* Read next record */
			error = next_record(&fps, &curr, year_base, 1);
			if(error != 0){
				if(error != EOF)
					error_msg(func, "while reading next record [outside]");
				goto on_error;
			}

			short_info(&(curr.number), -USHORT_VALUE_CODE);
			
			set_ens_times(&curr, year_base);
			yd = curr.time.start;
			
		}

		/* Message */
		sprintf(msg, "\n%%%%%% ENSEMBLE: %hu", curr.number);
		report_msg(msg);
		yd_to_ymdhms_str(str, curr.time.start, year_base);
		sprintf(msg, "  FROM: %s", str);
		report_msg(msg);
		yd_to_ymdhms_str(str, curr.time.stop, year_base);
		sprintf(msg, "  TO: %s\n", str);
		report_msg(msg);
		printf("\nInside time range ... \n");

		/* -------------------------------------------------------
			 Loop inside time range 
			 ------------------------------------------------------- */
		while(yd < stop_day){
			
			/* Fix current record */
			error = fix_record(&curr, year_base);
			if(error != 0){
				error_msg(func, "while fixing record");
				goto on_error;
			}

			/* Update output file name */
			error = update_output_file(&fps, &prev, &curr);
			if(error != 0){
				error_msg(func, "while updateing new output file");
				goto on_error;
			}

			/* Write back the current record */
			error = write_fix(fps.out, &curr);
			if(error != 0){
				error_msg(func, "while writing output");
				goto on_error;
			}

			/* Backup current record into previous */
			memcpy(&prev, &curr, sizeof(FIX_NBR_TYPE));

			/* Read next record */
			error = next_record(&fps, &curr, year_base, 1);
			if(error != 0){
				if(error != EOF)
					error_msg(func, "while reading next record (inside time range)");
				goto on_error;
			}
			
			short_info(&(curr.number), -USHORT_VALUE_CODE);
			
			set_ens_times(&curr, year_base);
			yd = curr.time.start;
		}

		/* Next time range */
	}

	error = 0;

on_error:

	/* Close all files */
	close_files(&fps, &(curr.fi));

	/* Check error status */
	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;
}
