Exemplo n.º 1
0
int main(int argc, char **argv)
{
	/* check the number of argument */
	if (argc != 2)
	{
		IAS_LOG_ERROR("Wrong parameter!");
		return ERROR;
	}

	/* declare some parameters */
	PARAMETERS parameters;			//structure of parameters
	MQ_PARAMS mq_params;			//structure of MQ parameters
	IAS_L0R_EPHEMERIS *l0r_ephemeris = NULL;   //pointer to L0R ephemeris data structure
	long long num_frame_of_ephemeris;			// number of ephemeris frames
	IAS_LOS_MODEL *model;						//pointer to the model structure
	IAS_CPF *cpf = NULL;          				/* Structure for CPF */
	double ephemeris_start_time;	//start time of ephemeris data
	double ephemeris_end_time;		//end time of ephemeris data
	IAS_ANC_EPHEMERIS_DATA *anc_ephemeris_data = NULL;
	int invalid_ephemeris_count = 0;
	MWDIMAGE_BUFFER_INFO *mwdImage_buffer_info;
	size_t process_times_needed;
	int status = ERROR;
	int i,j;


	memset(&parameters, 0, sizeof(parameters));
	memset(&mq_params, 0, sizeof(mq_params));
	/* Read parameters and MQ params from ODL */
	status = read_parameters(argv[1],&parameters,&mq_params);
	if(status != SUCCESS)
	{
		IAS_LOG_ERROR("failed to read parameters!\n");
		exit(EXIT_FAILURE);
	}

	/*MQ connection */
	status = MQ_Init();
	if(status != SUCCESS)
	{
		IAS_LOG_WARNING("MQ connect Error !\n");
	}

	status = MQSend(4,"ADP module started !\n");
	if(status != SUCCESS)
	{
		IAS_LOG_WARNING("module started , MQSend Error !\n");
	}



	status = ias_sat_attr_initialize(parameters.satellite_id);
	if (status != SUCCESS)
	{
		IAS_LOG_ERROR("Initializing IAS Satellite Attributes Library");
		return ERROR;
	}


	/* Initialize the model structure */
	IAS_ACQUISITION_TYPE acquisition_type = IAS_EARTH;
	model = ias_los_model_initialize(acquisition_type);
	if (!model)
	{
		IAS_LOG_ERROR("Initializing model");
		return EXIT_FAILURE;
	}


	/* read information from cpf, and set it into model*/
	cpf = ias_cpf_read(parameters.cpf_filename);

	if(ias_los_model_set_cpf_for_MWD(cpf,model) != SUCCESS)
	{
		IAS_LOG_ERROR("Copy cpf value into model");
		return ERROR;
	}


	/* read ephemeris file into lor_ephemeris */
	status = read_ephemeris_data_for_MWD(&parameters,&l0r_ephemeris,&num_frame_of_ephemeris);
	if(status != SUCCESS )
	{
		IAS_LOG_ERROR("Could not read ephemeris file into lor_ephemeris.\n");
		exit(EXIT_FAILURE);
	}



	long long l0r_ephemeris_count = num_frame_of_ephemeris;
	/* Preprocess the ephemeris data. */
	if (ias_ancillary_preprocess_ephemeris_for_MWD(cpf, l0r_ephemeris,
			l0r_ephemeris_count,acquisition_type,&anc_ephemeris_data,
			&invalid_ephemeris_count,
			&ephemeris_start_time,&ephemeris_end_time) != SUCCESS)
	{
		IAS_LOG_ERROR("Processing ephemeris data");
		return ERROR;
	}


	if(ias_sc_model_set_ancillary_ephemeris(anc_ephemeris_data,
			&model->spacecraft) != SUCCESS)
	{
		IAS_LOG_ERROR("Setting ephemeris data into model");
		return ERROR;
	}


	mwdImage_buffer_info = malloc(sizeof(*mwdImage_buffer_info));
	memset(mwdImage_buffer_info,0,sizeof(*mwdImage_buffer_info));

	UPDATE_LONGITUDE_LATITUDE_ARGS *update_longitude_latitude_args;
	update_longitude_latitude_args = (UPDATE_LONGITUDE_LATITUDE_ARGS*)malloc
										(sizeof(UPDATE_LONGITUDE_LATITUDE_ARGS));
	/* Read mwdImage through several times */

	/* Decide how many times to read file to the buffer */
	status = get_process_time_needed(&parameters,&process_times_needed);
	if(status != SUCCESS)
	{
		IAS_LOG_ERROR("failed to get the process times needed!\n");
		return ERROR;
	}


	for(i = 0; i < process_times_needed; i++)
	{
		status = read_mwdImage(&parameters,i,mwdImage_buffer_info,
				ephemeris_start_time,ephemeris_end_time);

		threadpool_t* pool;
		pool = threadpool_create(NUM_THREAD);
		for(j = 0; j < NUM_THREAD; j++)
		{
			update_longitude_latitude_args->start_oli_frame_to_update = j*mwdImage_buffer_info->num_oli_frame/NUM_THREAD;
			if(j == NUM_THREAD-1)
			{
				update_longitude_latitude_args->end_oli_frame_to_update = mwdImage_buffer_info->num_oli_frame;
			}
			else
			{
				update_longitude_latitude_args->end_oli_frame_to_update = j*mwdImage_buffer_info->num_oli_frame/NUM_THREAD
																			+mwdImage_buffer_info->num_oli_frame/NUM_THREAD;
			}
			update_longitude_latitude_args->model = model;
			update_longitude_latitude_args->mwdImage_buffer_info = mwdImage_buffer_info;

			threadpool_add(pool,(void*)&update_longitude_latitude,(void*)update_longitude_latitude_args);
		}

//		/* Added the remain data to the last thread */
//		update_longitude_latitude_args->start_oli_frame_to_update = j*(mwdImage_buffer_info->num_oli_frame/(NUM_THREAD-1))*(NUM_THREAD-2);
//		update_longitude_latitude_args->end_oli_frame_to_update = j*mwdImage_buffer_info->num_oli_frame;
//		threadpool_add(pool,(void*)&update_longitude_latitude,(void*)update_longitude_latitude_args);

		threadpool_destroy(pool);

		status = write_mwdImage(&parameters,i,mwdImage_buffer_info);
	}

//
//
//	IAS_SATELLITE_ID satellite_id;
//	int satellite_number = 8;
//	/* Get the satellite ID */
//	satellite_id = ias_sat_attr_get_satellite_id_from_satellite_number(
//			satellite_number);
//	if (satellite_id == ERROR)
//	{
//		IAS_LOG_ERROR("Unable to determine satellite ID from satellite "
//				"number %d", satellite_number);
//		return ERROR;
//	}

	/* Since the only purpose for retrieving the satellite ID is to
	   initialize the library, do that here */

//	const char *system_model_file = "/home/cqw/Desktop/systematic_model.140032.h5";
//	IAS_LOS_MODEL *model1;
//	model1 = ias_model_read(system_model_file);

//	double n_lines, n_sample;
//	int n_band, n_sca;
//	double target_elev;
//	double target_latd, target_long;
//	IAS_SENSOR_DETECTOR_TYPE dettype;
//
//	n_lines = 7500;
//	n_sample = 100;
//	n_band = 7;
//	n_sca = 9;
//	target_elev = 0;
//	dettype = IAS_NOMINAL_DETECTOR;
//
//	ias_los_model_input_line_samp_to_geodetic
//		(n_lines, n_sample,n_band, n_sca,target_elev,model,dettype, NULL ,&target_latd,&target_long);

	return SUCCESS;
}
int ias_ancillary_preprocess_ephemeris
(
    IAS_CPF *cpf,                      /* I: CPF structure */
    const IAS_L0R_EPHEMERIS *l0r_ephemeris, /* I: L0R ephemeris structure */
    int l0r_ephemeris_count,           /* I: number of records in L0R data */
    const double *interval_start_time, /* I: interval start YEAR, DOY, SOD */
    const double *interval_stop_time,  /* I: interval stop YEAR, DOY, SOD */
    IAS_ACQUISITION_TYPE acquisition_type, /* I: Image acquisition type */
    IAS_ANC_EPHEMERIS_DATA **anc_ephemeris_data, /* O: pointer to ephem data */
    int *invalid_ephemeris_count       /* O: number of bad ephemeris points 
                                             detected */
)
{
    int valid_ephemeris_count = 0;       /* valid ephemeris count */
    double *ephemeris_seconds_since_j2000 = NULL; /* array of ephemeris seconds
                                                     since j2000 */
    IAS_VECTOR *smoothed_eph_pos = NULL; /* smoothed ephemeris position data */
    IAS_VECTOR *smoothed_eph_vel = NULL; /* smoothed ephemeris velocity data */
    int number_ephemeris_records;        /* size of ephemeris structures */

    *anc_ephemeris_data = NULL;

    /* Allocate memory for the ephemeris seconds */
    if (l0r_ephemeris_count < IAS_LAGRANGE_PTS)
    {
        number_ephemeris_records = IAS_LAGRANGE_PTS;
    } 
    else
    {
        number_ephemeris_records = l0r_ephemeris_count;
    } 

    ephemeris_seconds_since_j2000 =
        (double *)malloc(sizeof(double) * number_ephemeris_records);
    if (ephemeris_seconds_since_j2000 == NULL)
    {
        IAS_LOG_ERROR("Allocating ephemeris_seconds_since_j2000");
        return ERROR;
    }

    smoothed_eph_pos =
        (IAS_VECTOR *)malloc(sizeof(IAS_VECTOR) * number_ephemeris_records);
    if (smoothed_eph_pos == NULL)
    {
        IAS_LOG_ERROR("Allocating smoothed_eph_pos");
        free(ephemeris_seconds_since_j2000);
        return ERROR;
    }

    smoothed_eph_vel =
        (IAS_VECTOR *)malloc(sizeof(IAS_VECTOR) * number_ephemeris_records);
    if (smoothed_eph_vel == NULL)
    {
        IAS_LOG_ERROR("Allocating smoothed_eph_vel");
        free(ephemeris_seconds_since_j2000);
        free(smoothed_eph_pos);
        return ERROR;
    }

    /* Compute the smoothed ephemeris values from the L0R ephemeris data.
       The memory for ephemeris_seconds_since_j2000 is allocated inside
       the smoothing routine. */
    if (ias_ancillary_smooth_ephemeris(
            acquisition_type, cpf, l0r_ephemeris, l0r_ephemeris_count,
            interval_start_time, interval_stop_time, &valid_ephemeris_count,
            invalid_ephemeris_count, ephemeris_seconds_since_j2000,
            smoothed_eph_pos, smoothed_eph_vel) != SUCCESS)
    {
        IAS_LOG_ERROR("Computing smoothed ephemeris");
        free(ephemeris_seconds_since_j2000);
        free(smoothed_eph_pos);
        free(smoothed_eph_vel);
        return ERROR;
    }

    /* Allocate the ancillary data structure */
    *anc_ephemeris_data =
        ias_ancillary_allocate_ephemeris(valid_ephemeris_count);
    if ((*anc_ephemeris_data) == NULL)
    {
        IAS_LOG_ERROR("Allocating ancillary ephemeris records");
        free(ephemeris_seconds_since_j2000);
        free(smoothed_eph_pos);
        free(smoothed_eph_vel);
        return ERROR;
    }

    /* Build/Populate the anc_ephemeris_data from the smoothed ephemeris data.
       The ephemeris model is also updated with the smoothed ephemeris data.
       The memory for the ephemeris records in the model structure are
       allocated inside the build routine. */
    if (ias_ancillary_build_ephemeris(
            cpf, valid_ephemeris_count, ephemeris_seconds_since_j2000,
            smoothed_eph_pos, smoothed_eph_vel, *anc_ephemeris_data) != SUCCESS)
    {
        IAS_LOG_ERROR("Building ancillary ephemeris");
        free(ephemeris_seconds_since_j2000);
        free(smoothed_eph_pos);
        free(smoothed_eph_vel);
        free(*anc_ephemeris_data);
        return ERROR;
    }

    /* Free the memory allocated by the smoothing routine */
    free(ephemeris_seconds_since_j2000);
    free(smoothed_eph_pos);
    free(smoothed_eph_vel);

    return SUCCESS;
}
int ias_sensor_smooth_ssm_data
(
    IAS_SENSOR_SCENE_SELECT_MIRROR_MODEL *ssm_model /* I/O: SSM data to smooth*/
)
{
    int window_size;            /* window size to use for filtering */
    int half_window;            /* half the window size */
    int record;                  /* current record index */
    double *filtered_angles;    /* filtered angle buffer */
    IAS_SENSOR_SSM_RECORD *records = ssm_model->records;

    /* Set up the smoother window */
    window_size = SSM_SMOOTH_WINDOW;
    half_window = window_size/2;

    /* verify there are enough angles to filter */
    if (ssm_model->ssm_record_count < window_size)
    {
        IAS_LOG_ERROR("At least %d SSM records required for filtering. Only %d"
                      " present", window_size, ssm_model->ssm_record_count);
        return ERROR;
    }

    /* allocate space for the filtered angles */
    filtered_angles = malloc(ssm_model->ssm_record_count 
                             * sizeof(*filtered_angles));
    if (filtered_angles == NULL)
    {
        IAS_LOG_ERROR("Failed to allocate a buffer to smooth SSM angles");
        return ERROR;
    }

    /* filter all the values */
    for (record = 0; record < ssm_model->ssm_record_count; record++)
    {
        int start_index;
        int index;
        double filtered_angle;

        start_index = record - half_window;

        /* force the filtering window to fall within the data */
        if ( start_index < 0 )
            start_index = 0;
        if ((start_index + window_size) > ssm_model->ssm_record_count)
            start_index = ssm_model->ssm_record_count - window_size;

        /* filter the data */
        filtered_angle = 0.0;
        for (index = start_index; index < start_index + window_size; index++)
            filtered_angle += records[index].mirror_angle;
        filtered_angle /= (double)window_size;

        /* save the filtered value */
        filtered_angles[record] = filtered_angle;
    }

    /* replace the original data with the filtered angles */
    for (record = 0; record < ssm_model->ssm_record_count; record++)
    {
        records[record].mirror_angle = filtered_angles[record];
    }

    /* Release the temporary buffer */
    free(filtered_angles);

    return SUCCESS;
}
/******************************************************************************
NAME:        ias_gcp_read_gcplib_filtered

PURPOSE:
Read the GCPLib file.  Put the Ground Control Points information
into the GCPLib structure.  Allows providing GCP filtering inputs.

RETURN VALUE:
type=int
Value        Description
-----        -----------
SUCCESS      Successfully retrieved the GCP information.
ERROR        Failure to get the GCP information.

******************************************************************************/
int ias_gcp_read_gcplib_filtered
(
    const char *gcplib_file_name, /* I: Name of the GCPLIB file */
    /* For dates, 1 = January, etc. Year is YYYY */
    const int *begin_date,        /* I: Beginning date [0] = month [1] = year */
    const int *end_date,          /* I: Ending date [0] = month [1] = year */
                                  /* I: Season of chip */
    char season[IAS_GCP_NUM_SEASONS][IAS_GCP_SEASON_LEN],
                                  /* I: Source of chip */
    char chip_source[IAS_GCP_NUM_CHIP_SOURCES][IAS_GCP_SOURCE_SIZE],
    const char *chip_type,        /* I: Type of chip */
    IAS_GCP_RECORD **gcp_lib,     /* O: Structure of chip information */
    int *num_gcp                  /* O: Number of ground control points */
)
{
    IAS_GCP_RECORD tmp_lib;     /* Temporary GCPLib info */
    IAS_GCP_RECORD *allocated_ptr; /* Pointer to allocate memory */
    FILE *gcplib_file_ptr;      /* Pointer to the GCPLib file */
    char *line_ptr;             /* Pointer to line of GCPLib file from fgets */
    char buffer[GCP_REC_SIZE];  /* String read in from file */
    char data_type[IAS_GCP_DATA_TYPE_MAX_SIZE]; /* GCP data type */
    int allocated_count;        /* Number of gcp_lib buffers allocated */
    int count_used = 0;         /* Count of chips used */
    int count_read = 0;         /* Count of chips read */
    int count_in_file;          /* Count of chips expected in the file */
    int day;                    /* Satellite day of the month */
    int month;                  /* Satellite month */
    int year;                   /* Satellite year */
    int status;                 /* Status of return from function */
    int i,j;                    /* Loop control variables */
    int season_flag = FALSE;    /* Flag indicating if seasons are specified */
    int source_flag = FALSE;    /* Flag indicating if sources are specified */
    int type_flag = FALSE;      /* Flag indicating if types are specified */
    int source_found;           /* Temporary flag in source search */
    int num_source = 0;         /* Number of chip source entered */
    int quarter[4];             /* Specific quarters */
    int use_point = TRUE;       /* Flag indicating if point should be used */

    /* Initialize the returned count read */
    *num_gcp = 0;

    /* Initialize the structure to NULL */
    *gcp_lib = NULL;

    /* Open the GCPLib file */
    gcplib_file_ptr = fopen(gcplib_file_name, "r");
    if (gcplib_file_ptr == NULL)
    {
        IAS_LOG_ERROR("Getting GCP library file");
        return ERROR;
    }

    /* This routine can be used to get the entire GCPLib file when no GCP
       filter parameters are defined. Using the filter parameters will check
       each GCP record read from the file against the filters given and gather
       all those that match the filters. Tracking the use_point status is only
       affected when filters are defined. They won't be defined when this
       routine is called by the ias_gcp_read_gcplib wrapper that doesn't
       allow providing filter definitions to it. */

    /* Initialize the quarters */
    for (i = 0; i < 4; i++)
        quarter[i] = FALSE;

    /* Check the source parameter to see if a check is necessary and get the
       number of sources */
    if (strcmp(chip_source[0], "") != 0)
    {
        source_flag = TRUE;
        while ((strcmp(chip_source[num_source], "") != 0) 
                && (num_source < IAS_GCP_NUM_CHIP_SOURCES))
        {
            num_source++;
        }
    }

    /* Check the GCP type parameter to see if a check is necessary */
    if (strcmp(chip_type, "") != 0)
        type_flag = TRUE;

    /* Read the file until end of file or BEGIN flag to bypass header */
    for (;;)
    {
        line_ptr = fgets(buffer, sizeof(buffer), gcplib_file_ptr);
        if (line_ptr == NULL) 
        {
            IAS_LOG_ERROR("Unexpected end of gcplib file");
            fclose(gcplib_file_ptr);
            return ERROR;
        }

        /* Skip the data starter mark "BEGIN" */
        if (strncmp(buffer, "BEGIN", 5) == 0)
        {
            /* Read the next line with total number of records */
            line_ptr = fgets(buffer, sizeof(buffer), gcplib_file_ptr);
            if (line_ptr == NULL)
            {
                IAS_LOG_ERROR("Reading total number of records");
                fclose(gcplib_file_ptr);
                return ERROR;
            }

            /* Get the count of GCPs expected in the file */
            count_in_file = atoi(buffer);
            break;
        }
    }

    /* Start without allocating a gcp_lib buffer */
    allocated_count = 0;

    /* Get the chip information from file */
    for (j = 0; j < count_in_file; j++)
    {
        use_point = TRUE;

        line_ptr = fgets(buffer, sizeof(buffer), gcplib_file_ptr);
        if (line_ptr == NULL)
        {
            IAS_LOG_ERROR("Reading GCPLib data: reading line %d",
                    count_read + 1);
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = sscanf(line_ptr,
                "%s %s "
                "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf "
                "%s %s %s %d %s %s %s\n",
                tmp_lib.point_id,
                tmp_lib.chip_name,
                &tmp_lib.reference_line,
                &tmp_lib.reference_sample,
                &tmp_lib.latitude,
                &tmp_lib.longitude,
                &tmp_lib.projection_y,
                &tmp_lib.projection_x,
                &tmp_lib.elevation,
                &tmp_lib.pixel_size_x,
                &tmp_lib.pixel_size_y,
                &tmp_lib.chip_size_lines,
                &tmp_lib.chip_size_samples,
                tmp_lib.source,
                tmp_lib.chip_type,
                tmp_lib.projection,
                &tmp_lib.zone,
                tmp_lib.date,
                tmp_lib.absolute_or_relative,
                data_type);

        if (status == EOF) /* (unexpected) end of file or matching failure */
        {
            IAS_LOG_ERROR("Reading GCPLib data: unexpected end of file");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        /* Confirm matched GCP_CHIPREC values */
        if (status != GCP_CHIPREC)
        {
            IAS_LOG_ERROR("Reading GCPLib data: too few values");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        /* Convert the data type string to an IAS_DATA_TYPE for GCP record */
        status = ias_misc_convert_string_to_data_type(data_type,
                &tmp_lib.chip_data_type);
        if (status != SUCCESS)
        {
            IAS_LOG_ERROR("Getting GCP data type for %s on line %d of %s",
                    data_type, count_read + 1, gcplib_file_name);
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        /* Check size of fields read in */
        status = strlen(tmp_lib.point_id);
        if (status >= IAS_GCP_ID_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'point_id' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.chip_name);
        if (status >= IAS_GCP_CHIP_NAME_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'chip_name' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.source);
        if (status >= IAS_GCP_SOURCE_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'source' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.chip_type);
        if (status >= IAS_GCP_TYPE_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'chip_type' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.projection);
        if (status >= IAS_GCP_PROJECTION_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'projection' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.date);
        if (status >= IAS_GCP_DATE_LEN) 
        {
            IAS_LOG_ERROR("Variable 'date' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = strlen(tmp_lib.absolute_or_relative);
        if (status >= IAS_GCP_ABS_REL_SIZE) 
        {
            IAS_LOG_ERROR("Variable 'absolute_or_relative' is too long");
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        /* Check the date of the GCPLib */
        /* The format is MM-DD-YYYY */
        status = sscanf(tmp_lib.date, IAS_GCP_DATE_FORMAT, &month, &day, &year);
        if (status != 3)
        {
            IAS_LOG_ERROR("In GCPLib date \"%s\" failed date format check for "
                    "id %s", tmp_lib.date, tmp_lib.point_id);
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        status = ias_misc_check_year_month_day(year, month, day);
        if (status != SUCCESS)
        {
            IAS_LOG_ERROR("In GCPLib date \"%s\" failed date check for id %s",
                    tmp_lib.date, tmp_lib.point_id);
            fclose(gcplib_file_ptr);
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }

        /* Check the filters that may have been specified to reduce
           the set to only those wanted */

        /* Since the chips are grouped together, this only needs to be done
           once with the first chip */
        if (count_read == 0)
        {
            /* Get season input.  The season is dependent on the latitude 
               therefore a test of the latitude is included to determine 
               southern or northern hemisphere. */

            for (i = 0; i < IAS_GCP_NUM_SEASONS; i++)
            {
                if (strcmp(season[i], "") != 0)
                {
                    ias_misc_convert_to_uppercase(season[i]);
                    season_flag = TRUE;
                    if (strcmp(season[i], "SUMMER") == 0)
                    {
                        if (tmp_lib.latitude < 0)
                            quarter[0] = TRUE;
                        else
                            quarter[2] = TRUE;
                    }
                    else if (strcmp(season[i], "SPRING") == 0)
                    {
                        if (tmp_lib.latitude < 0)
                            quarter[3] = TRUE;
                        else
                            quarter[1] = TRUE;
                    }
                    else if (strcmp(season[i], "FALL") == 0)
                    {
                        if (tmp_lib.latitude < 0)
                            quarter[1] = TRUE;
                        else
                            quarter[3] = TRUE;
                    }
                    else if (strcmp(season[i], "WINTER") == 0)
                    {
                        if (tmp_lib.latitude < 0)
                            quarter[2] = TRUE;
                        else
                            quarter[0] = TRUE;
                    }
                    else
                    {
                        IAS_LOG_ERROR("Invalid season entered");
                        fclose(gcplib_file_ptr);
                        free(*gcp_lib);
                        *gcp_lib = NULL;
                        return(ERROR);
                    }
                }
            }
        }

        /* Test beginning and ending year and season */
        if ((begin_date[0] != 0) || (end_date[0] != 0)
                || (begin_date[1] != 0) || (end_date[1] != 0)
                || (season_flag))
        {
            if (month < begin_date[0])
                use_point = FALSE;
            else if ((month > end_date[0]) && (end_date[0] != 0))
                use_point = FALSE;
            else if (year < begin_date[1])
                use_point = FALSE;
            else if ((year > end_date[1]) && (end_date[1] != 0))
                use_point = FALSE;
            /* Test season */
            else if (season_flag)
            {
                if ((!quarter[0]) && (month <= 3))
                    use_point = FALSE;
                else if ((!quarter[1]) && (month >= 4) && (month <= 6))
                    use_point = FALSE;
                else if ((!quarter[2]) && (month >= 7) && (month <= 9))
                    use_point = FALSE;
                else if ((!quarter[3]) && (month >= 10))
                    use_point = FALSE;
            }
        }

        /* Test the source */
        if (source_flag)
        {
            source_found = FALSE;
            for (i = 0; i < num_source; i++)
            {
                if (strcasecmp(chip_source[i], tmp_lib.source) == 0)
                {
                    source_found = TRUE;
                    break;
                }
            }
            if (!source_found)
                use_point = FALSE;
        }

        /* Test the chip_type */
        if (type_flag)
        {
            if (strcasecmp(chip_type, tmp_lib.chip_type) != 0)
            {
                use_point = FALSE;
            }
        }

        /* If the point is used, save it to the gcp_lib array */
        if (use_point == TRUE)
        {
            /* If needed, allocate more memory for the GCPs being kept */
            if (count_used >= allocated_count)
            {
                if (allocated_count == 0)
                {
                    /* Start by allocating a buffer for 100 records */
                    allocated_count = 100;
                }
                else
                {
                    /* Otherwise, double the memory allocated */
                    allocated_count *= 2;
                }
                allocated_ptr = (IAS_GCP_RECORD *)realloc(*gcp_lib,
                        allocated_count * sizeof(IAS_GCP_RECORD));
                if (allocated_ptr == NULL)
                {
                    IAS_LOG_ERROR("Allocating memory");
                    fclose(gcplib_file_ptr);
                    free(*gcp_lib);
                    *gcp_lib = NULL;
                    return ERROR;
                }
                *gcp_lib = allocated_ptr;
            }

            /* Copy to final library structure */
            memcpy(&((*gcp_lib)[count_used]), &tmp_lib, sizeof(tmp_lib));

            /* Increment counter */
            count_used++;
        }

        count_read++;
    }

    fclose(gcplib_file_ptr);

    /* If any records were kept, trim the memory for the gcps to the actual
       number of GCPs used.  If the count_used is zero, no memory was
       allocated. */
    if (count_used > 0)
    {
        allocated_ptr = (IAS_GCP_RECORD *)realloc(*gcp_lib,
                count_used * sizeof(IAS_GCP_RECORD));
        if (allocated_ptr == NULL)
        {
            IAS_LOG_ERROR("Allocating memory");
            free(*gcp_lib);
            *gcp_lib = NULL;
            return ERROR;
        }
        *gcp_lib = allocated_ptr;
    }

    /* Make sure the total listed at top of GCPLib file matches GCPs counted */
    if (count_read != count_in_file)
    {
        IAS_LOG_ERROR("Count of GCPs read (%d) doesn't match the number "
                "expected (%d)", count_read, count_in_file);
        free(*gcp_lib);
        *gcp_lib = NULL;
        return ERROR;
    }

    /* Set the number of GCPs that passed the filtering */
    *num_gcp = count_used;

    return SUCCESS;
}
/****************************************************************************
Name: ias_db_get_login_for_satellite_sensor

Purpose: Given a satellite name and sensor (i.e. L8 and OLITIRS), extracts
    the schema name and password from the associated environment variable
    named IAS_DB_<satellite>_<sensor>.

Returns:
    SUCCESS or ERROR

Notes:
    - The caller of the routine is responsible for freeing the memory that
      this routine allocates for the returned schema and password.

****************************************************************************/
int ias_db_get_login_for_satellite_sensor
(
    const char *satellite,      /* I: Satellite of the schema */
    const char *sensor,         /* I: Sensor of the schema */
    char **schema,              /* O: Schema (user) name */
    char **password             /* O: Password for the schema */
)
{
    char env_var_name[200];
    const char *env_var = NULL;
    const char *temp_ptr;
    int length;
    int status;

    /* Create the name of the environment variable that stores the login
       info */
    status = snprintf(env_var_name, sizeof(env_var_name), "IAS_DB_%s_%s",
        satellite, sensor);
    if (status <= 0 || status >= sizeof(env_var_name))
    {
        IAS_LOG_ERROR("Environment variable name buffer is too small");
        return ERROR;
    }

    /* get the schema(username)/password for the database */
    env_var = getenv(env_var_name);
    if (!env_var)
    {
        IAS_LOG_ERROR("%s environment variable not set", env_var_name);
        return ERROR;
    }

    /* split the schema(username) and password up */
    temp_ptr = strchr(env_var, '/');
    if (!temp_ptr)
    {
        IAS_LOG_ERROR("Parsing schema name and password from environment "
            "variable %s", env_var_name);
        return ERROR;
    }
    length = temp_ptr - env_var + 1;

    *schema = malloc(length * sizeof(char));
    if (!*schema)
    {
        IAS_LOG_ERROR("Allocating memory for schema name");
        return ERROR;
    }
    strncpy(*schema, env_var, length);
    (*schema)[length - 1] = '\0';

    *password = strdup(temp_ptr + 1);
    if (!*password)
    {
        free(*schema);
        *schema = NULL;
        IAS_LOG_ERROR("Allocating memory for database password");
        return ERROR;
    }

    return SUCCESS;
}
/*******************************************************************************
Name: ias_angle_gen_write_image

Purpose: Write out the solar illumination or the satellite viewing 
         angle images as a binary file.

Returns: 
    Type = integer
    SUCCESS / ERROR
 ******************************************************************************/
int ias_angle_gen_write_image
(
    const char *image_filename, /* I: Image file name */
    const short *azimuth,       /* I: Array of azimuth angles */
    const short *zenith,        /* I: Array of zenith angles */
    IAS_ANGLE_GEN_TYPE sat_or_sun_type, /* I: Image type to write */
    int band_index,             /* I: Outpue band index */
    int num_lines,              /* I: Number of image lines */
    int num_samps,              /* I: Number of image samples */
    IAS_DBL_XY ul_corner,       /* I: Image upper left corner */
    double pixel_size,          /* I: Image pixel size */
    const IAS_PROJECTION *projection /* I: Image framing information */
)        
{
    FILE *output_file;           /* Output file pointer */
    char ang_filename[PATH_MAX]; /* Output angle file name */
    int count;                   /* Total number of samples */
    int status;                  /* Status placeholder */
    const char *description;     /* Envi header description */
    int band_number;             /* Ouput band number */

    /* Check the input band index */
    if (band_index < 0 || band_index > IAS_MAX_NBANDS)
    {
        IAS_LOG_ERROR("Invalid band index %d", band_index);
        return ERROR;
    }

    band_number = ias_sat_attr_convert_band_index_to_number(band_index);
    if (band_number == ERROR)
    {
        IAS_LOG_ERROR("Converting band index %d to band number", band_index);
        return ERROR;
    }

    /* Calculate the total number of samples */
    count = num_lines * num_samps;
    
    if (sat_or_sun_type == IAS_ANGLE_GEN_SATELLITE)
    {
        description = "View Angle Band File";

        /* Construct satellite view angle output file name */
        status = snprintf(ang_filename, sizeof(ang_filename), 
            "%s_sensor_B%02d.img", image_filename, band_number);
    }
    else
    {
        description = "Sun Angle Band File";

        /* Construct solar angle output file name */
        status = snprintf(ang_filename, sizeof(ang_filename), 
            "%s_solar_B%02d.img", image_filename, band_number);
    }

    if (status < 0 || status >= sizeof(ang_filename))
    {
        IAS_LOG_ERROR("Creating the image filename from filename %s", 
            image_filename);
        return ERROR;
    }

    /* Open output file */
    output_file = fopen(ang_filename, "wb");
    if (!output_file)
    {
        IAS_LOG_ERROR("Opening output view angle band file %s", 
            ang_filename);
        return ERROR;
    }

    /* Write the Azimuth Angle layer */
    if (fwrite(azimuth, sizeof(short), count, output_file) != count)
    {
        IAS_LOG_ERROR("Writing azimuth angle layer for band %d to file %s", 
            band_number, ang_filename);
        fclose(output_file);
        return ERROR;
    }

    /* Write the Zenith Angle layer */
    if (fwrite(zenith, sizeof(short), count, output_file) != count)
    {
        IAS_LOG_ERROR("Writing zenith angle layer for band %d to file %s", 
            band_number, ang_filename);
        fclose(output_file);
        return ERROR;
    }

    /* Close the output image file */
    if (fclose(output_file) != 0)
    {
        IAS_LOG_ERROR("Closing the image file");
        return ERROR;
    }
   
    /* Write the envi header */
    if (ias_misc_write_envi_header(ang_filename, projection, description,
        num_lines, num_samps, NUM_OUTPUT_SCAS, ul_corner.x,ul_corner.y,
        pixel_size, pixel_size, "Azimuth, Zenith", IAS_I2) != SUCCESS)
    {
        IAS_LOG_ERROR("Creating the image header");
        return ERROR;
    }

    return SUCCESS;
}
int ias_misc_parse_datetime_string
(
    const char *datetime_string,            /* I: Date/time string */
    IAS_DATETIME_FORMAT_TYPE format_type,   /* I: Either L0R-format or
                                                  CPF/BPF/RLUT-format */
    IAS_DATETIME *time                      /* I/O: Decomposed date/time
                                                    data */
)
{
    char year_string[5];                    /* Date/time substrings */
    char month_string[3];
    char day_of_month_string[3];
    char day_of_year_string[4];
    char hour_string[3];
    char minute_string[3];
    char second_string[MAX_SECONDS_DIGITS]; /* Supports fractional seconds */
    long month_days[13] =                   /* Days in each month */
        { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    long year;                              /* Date/time values */
    long month;
    long day_of_month;
    long day_of_year;
    long hour;
    long minute;
    double second;

    int hour_offset;                        /* Offsets to time components
                                               and component separator
                                               characters */
    int day_of_year_offset;
    int day_of_year_separator_offset;
    int month_offset;
    int month_separator_offset;
    int day_of_month_offset;
    int day_of_month_separator_offset;
    int hour_separator_offset;
    int minute_offset;
    int minute_separator_offset;
    int second_offset;
    int seconds_length;
    int status;

    /* Do we have a valid format ID */
    if ((format_type != IAS_DATETIME_L0R_FORMAT)
            && (format_type != IAS_DATETIME_CPF_FORMAT))
    {
        IAS_LOG_ERROR("Invalid date/time string format identifier");
        return ERROR;
    }

    /* Some sanity checks on the input date/time string to make sure it's
       in an expected format. Make sure only numbers are in the number
       fields, and there's only one 'valid' separator character separating
       them.  Again, the valid formats for a date/time string input to this
       routine are:

       YYYY[sep]mm[sep]dd[sep]HH[sep]MM[sep]SS.SSSSS, or

       YYYY[:]ddd[:]HH[:]MM[:]SS.SSSSS

       The year/month/day data can be separated by either a '-' or a ':'.
       The full date can be separated from the full time by a ':' or a 'T'.
       The hour/minute/second data can only be separated by a ':'.*/
    memset(second_string, 0, sizeof(second_string));

    /* The year format is the same in both cases and is always 4 digits */
    if ((strspn(datetime_string, "0123456789") != 4))
    {
        IAS_LOG_ERROR("Malformed year in date/time string %s", datetime_string);
        return ERROR;
    }
    strncpy(year_string, datetime_string, sizeof(year_string));
    year_string[4] = '\0';

    if (strspn(&datetime_string[4], "-:") != 1)
    {
        IAS_LOG_ERROR("Invalid separator %c in date/time string %s",
                datetime_string[4], datetime_string);
        return ERROR;
    }

    /* After the year, account for the differences. For CPF/BPF-format
       strings, parse the the day and the month. For L0R-format
       strings, parse the day of the year. */
    if (format_type == IAS_DATETIME_L0R_FORMAT)
    {
        /* Here we parse out just day of year; expect 3 digits at most */
        day_of_year_offset = 5;
        day_of_year_separator_offset = strcspn(&datetime_string[5], "-:")
            + day_of_year_offset;
        if (((day_of_year_separator_offset - day_of_year_offset) == 0)
                || ((day_of_year_separator_offset - day_of_year_offset) > 3))
        {
            IAS_LOG_ERROR("Malformed day-of-year in date/time string %s",
                    datetime_string);
            return ERROR;
        }
        strncpy(day_of_year_string, &datetime_string[day_of_year_offset], 
                sizeof(day_of_year_string));
        day_of_year_string[day_of_year_separator_offset - day_of_year_offset]
            = '\0';

        if (is_string_numeric(day_of_year_string) == FALSE)
        {
            IAS_LOG_ERROR("Malformed day-of-year in date/time string %s",
                    datetime_string);
            return ERROR;
        }

        hour_offset = day_of_year_separator_offset + 1;
    }
    else
    {
        /* Parse out month and then day of month */
        /* Parse month; expect 2 digits at most */
        month_offset = 5;
        month_separator_offset = strcspn(&datetime_string[month_offset], "-:")
            + month_offset;
        if (((month_separator_offset - month_offset) == 0)
                || ((month_separator_offset - month_offset) > 2))
        {
            IAS_LOG_ERROR("Malformed month in date/time string %s",
                    datetime_string);
            return ERROR;
        }
        strncpy(month_string, &datetime_string[month_offset], 
                sizeof(month_string));
        month_string[month_separator_offset - month_offset] = '\0';

        if (is_string_numeric(month_string) == FALSE)
        {
            IAS_LOG_ERROR("Malformed month in date/time string %s",
                    datetime_string);
            return ERROR;
        }

        /* Parse day of month; expect 2 digits at most */
        day_of_month_offset = month_separator_offset + 1;

        day_of_month_separator_offset
            = strcspn(&datetime_string[day_of_month_offset], ":Tt")
            + day_of_month_offset;

        if (((day_of_month_separator_offset - day_of_month_offset) == 0)
                || ((day_of_month_separator_offset - day_of_month_offset) > 2))
        {
            IAS_LOG_ERROR("Malformed day of month in date/time string %s",
                    datetime_string);
            return ERROR;
        }

        strncpy(day_of_month_string, &datetime_string[day_of_month_offset], 
                sizeof(day_of_month_string));
        day_of_month_string[day_of_month_separator_offset - day_of_month_offset]
            = '\0';

        if (is_string_numeric(day_of_month_string) == FALSE)
        {
            IAS_LOG_ERROR("Malformed day of month in date/time string %s",
                    datetime_string);
            return ERROR;
        }

        if (strspn(&datetime_string[day_of_month_separator_offset], ":Tt") != 1)
        {
            IAS_LOG_ERROR("Invalid separator %c in date/time string %s",
                    datetime_string[10], datetime_string);
            return ERROR;
        }
        hour_offset = day_of_month_separator_offset + 1;
    }

    /* Now that we have the time component offsets determined for the
       specified format type, parse the time components */
    /* Parse hour; expect two digits at most */
    if ((strspn(&datetime_string[hour_offset], "0123456789") == 0)
            || (strspn(&datetime_string[hour_offset], "0123456789") > 2))
    {
        IAS_LOG_ERROR("Malformed hour in date/time string %s", datetime_string);
        return ERROR;
    }

    hour_separator_offset = strcspn(&datetime_string[hour_offset], ":")
        + hour_offset;

    strncpy(hour_string, &datetime_string[hour_offset], sizeof(hour_string));
    hour_string[hour_separator_offset - hour_offset] = '\0';
    if (is_string_numeric(hour_string) == FALSE)
    {
        IAS_LOG_ERROR("Malformed hour in date/time string %s", datetime_string);
        return ERROR;
    }

    if (strspn(&datetime_string[hour_separator_offset], ":") != 1)
    {
        IAS_LOG_ERROR("Invalid separator %c in date/time string %s",
                datetime_string[hour_separator_offset], datetime_string);
        return ERROR;
    }

    /* Parse minutes; expect two digits at most */
    minute_offset = hour_separator_offset + 1;
    minute_separator_offset = strcspn(&datetime_string[minute_offset], ":")
        + minute_offset;
    if ((strspn(&datetime_string[minute_offset], "0123456789") == 0)
            || (strspn(&datetime_string[minute_offset], "0123456789") > 2))
    {
        IAS_LOG_ERROR("Malformed minute in date/time string %s",
                datetime_string);
        return ERROR;
    }

    strncpy(minute_string, &datetime_string[minute_offset],
            sizeof(minute_string));
    minute_string[minute_separator_offset - minute_offset] = '\0';

    if (is_string_numeric(minute_string) == FALSE)
    {
        IAS_LOG_ERROR("Malformed minute in date/time string %s",
                datetime_string);
        return ERROR;
    }

    if (strspn(&datetime_string[minute_separator_offset], ":") != 1)
    {
        IAS_LOG_ERROR("Invalid separator %c in date/time string %s",
                datetime_string[hour_separator_offset], datetime_string);
        return ERROR;
    }

    /* Parse seconds */
    second_offset = minute_separator_offset + 1;
    seconds_length = strspn(&datetime_string[second_offset], "0123456789.");

    if ((seconds_length != 0) && (seconds_length < MAX_SECONDS_DIGITS))
    {
        strncpy(second_string, &datetime_string[second_offset], 
                sizeof(second_string));
        second_string[seconds_length] = '\0';
    }
    else
    {
        IAS_LOG_ERROR("Malformed second in date/time string %s",
                datetime_string);
        return ERROR;
    }

    if (strspn(second_string, "0123456789.") != strlen(second_string))
    {
        IAS_LOG_ERROR("Malformed second in date/time string %s",
                datetime_string);
        return ERROR;
    }

    /* The whole string was in a valid format, so convert the date/time
       substrings to numbers and store them. After each call, check to
       see whether the conversion produced a valid number. Start with
       the date information */
    errno = 0;
    year = strtol(year_string, (char **)NULL, 10);
    if ((errno == EINVAL) || (errno == ERANGE))
    {
        IAS_LOG_ERROR("Converting year to integer value");
        return ERROR;
    }
    time->year = (int)year;

    errno = 0;
    if (format_type == IAS_DATETIME_L0R_FORMAT)
    {
        day_of_year = strtol(day_of_year_string, (char **)NULL, 10);
        if ((errno == EINVAL) || (errno == ERANGE))
        {
            IAS_LOG_ERROR("Converting day of year to integer value");
            return ERROR;
        }

        /* Check the day of year range */
        if ( ias_math_is_leap_year(year) )
        {
            if ( day_of_year < MIN_DAY || day_of_year > MAX_DAY_LEAP )
            {
                IAS_LOG_ERROR("Day of year out of range - leap year");
                return ERROR;
            }
        }
        else
        {
            if ( day_of_year < MIN_DAY || day_of_year > MAX_DAY )
            {
                IAS_LOG_ERROR("Day of year out of range");
                return ERROR;
            }
        }

        time->day_of_year = (int)day_of_year;

        /* Get the corresponding month and day of the month and fill these in */
        status = ias_math_convert_doy_to_month_day(time->day_of_year,
                time->year, &(time->month), &(time->day_of_month));
        if (status != SUCCESS)
        {
            IAS_LOG_ERROR("Retrieving month and day-of-month information "
                    "from corresponding day-of-year");
            return ERROR;
        }
    }
    else
    {
        month = strtol(month_string, (char **)NULL, 10);

        /* Check the month range */
        if ( month < MIN_MONTH || month > MAX_MONTH )
        {
            IAS_LOG_ERROR("Month out of range");
            return ERROR;
        }

        day_of_month = strtol(day_of_month_string, (char **)NULL, 10);
        if ((errno == EINVAL) || (errno == ERANGE))
        {
            IAS_LOG_ERROR("Converting month and/or day of month to integer "
                    "values");
            return ERROR;
        }

        /* Set number of days in Feb. for leap year */
        if ( ias_math_is_leap_year(year) )
           month_days[2] = 29;
        /* Check the day of month range */
        if ( day_of_month < MIN_MONTH || day_of_month > month_days[month] )
        {
            IAS_LOG_ERROR("Day of month out of range");
            return ERROR;
        }

        time->month = (int)month;
        time->day_of_month = (int)day_of_month;

        /* Get the corresponding day-of-year and fill this in */
        status = ias_math_convert_month_day_to_doy(time->month,
                time->day_of_month, time->year, &(time->day_of_year));
        if (status != SUCCESS)
        {
            IAS_LOG_ERROR("Retrieving day-of-year information from "
                    "corresponding month and day-of-month");
            return ERROR;
        }
    }

    /* Now convert the time information */
    errno = 0;
    hour = strtol(hour_string, (char **)NULL, 10);

    /* Check the hour range */
    if ( hour < MIN_TIME || hour > MAX_HOUR )
    {
        IAS_LOG_ERROR("Hour out of range");
        return ERROR;
    }

    minute = strtol(minute_string, (char **)NULL, 10);
    if ((errno == EINVAL) || (errno == ERANGE))
    {
        IAS_LOG_ERROR("Converting hour and/or minute to integer value ");
        return ERROR;
    }

    /* Check the minute range */
    if ( minute < MIN_TIME || minute > MAX_MINUTE )
    {
        IAS_LOG_ERROR("Minute out of range");
        return ERROR;
    }

    time->hour = (int)hour;
    time->minute = (int)minute;

    /* Convert the seconds component, and check if an error code was set */
    errno = 0;
    second = strtod(second_string, (char **)NULL);
    if ((errno == EINVAL) || (errno == ERANGE))
    {
        IAS_LOG_ERROR("Converting second to floating point value");
        return ERROR;
    }

    /* Check the second range */
    if ( second < MIN_TIME || second >= MAX_SECOND )
    {
        IAS_LOG_ERROR("Second out of range");
        return ERROR;
    }

    time->second = second;

    /* Done */
    return SUCCESS;
}
int ias_ancillary_build_attitude_table_definition
(
    const char *field_names[ATTITUDE_NFIELDS], /* I/O: Names of table
                                                  fields */
    size_t field_offsets[ATTITUDE_NFIELDS],    /* I/O: Field offsets */
    hid_t field_types[ATTITUDE_NFIELDS],       /* I/O: Field datatypes */
    size_t field_sizes[ATTITUDE_NFIELDS],      /* I/O: Field sizes  */
    hid_t fields_to_close[ATTITUDE_NFIELDS]    /* I/O: Array of open field 
                                                  datatypes  */
)

{
    hsize_t quaternion_dims = QDIMS;          /* Number of quaternion
                                                 components */

    int i;                                    /* Field counter */

    IAS_ANC_ATTITUDE_RECORD attitude_record;  /* "Template" for record
                                                 data structure */


    /* Initialize all datatype fields as 'closed' */
    for (i = 0; i < ATTITUDE_NFIELDS; i++)
         fields_to_close[i] = -1;

    /* Dig into the record data structure.  Start with the elapsed time
       in seconds from the given epoch. */
    i = 0;
    field_offsets[i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, seconds_from_epoch);
    field_names[i] = "Time From Epoch";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.seconds_from_epoch);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, eci_quaternion);
    field_names[i] = "ECI Quaternion";
    field_types[i] = H5Tarray_create(H5T_NATIVE_DOUBLE, 1, &quaternion_dims);
    if (field_types[i] < 0)
    {
        IAS_LOG_ERROR("Creating ECI quaternion array type");
        ias_ancillary_cleanup_table_definition(fields_to_close,
            ATTITUDE_NFIELDS);
        return ERROR;
    }
    fields_to_close[i] = field_types[i];
    field_sizes[i] = sizeof(attitude_record.eci_quaternion);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, ecef_quaternion);
    field_names[i] = "ECEF Quaternion";
    field_types[i] = H5Tarray_create(H5T_NATIVE_DOUBLE, 1, &quaternion_dims);
    if (field_types[i] < 0)
    {
        IAS_LOG_ERROR("Creating ECEF quaternion array type");
        ias_ancillary_cleanup_table_definition(fields_to_close,
            ATTITUDE_NFIELDS);
        return ERROR;
    }
    fields_to_close[i] = field_types[i];
    field_sizes[i] = sizeof(attitude_record.ecef_quaternion);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, roll);
    field_names[i] = "Roll";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.roll);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, roll_rate);
    field_names[i] = "Roll Rate";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.roll_rate);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, pitch);
    field_names[i] = "Pitch";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.pitch);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, pitch_rate);
    field_names[i] = "Pitch Rate";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.pitch_rate);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, yaw);
    field_names[i] = "Yaw";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.yaw);

    field_offsets[++i] = HOFFSET(IAS_ANC_ATTITUDE_RECORD, yaw_rate);
    field_names[i] = "Yaw Rate";
    field_types[i] = H5T_NATIVE_DOUBLE;
    field_sizes[i] = sizeof(attitude_record.yaw_rate);

    /* Final error checks */
    if (i < (ATTITUDE_NFIELDS - 1))
    {
        /* fewer fields were added than expected */
        IAS_LOG_ERROR("Defined %d fields, but expected %d", i + 1,
            ATTITUDE_NFIELDS);
        ias_ancillary_cleanup_table_definition(fields_to_close,
            ATTITUDE_NFIELDS);
        return ERROR;
    }
    else if (i >= ATTITUDE_NFIELDS)
    {
        /* more fields than expected.  The stack is probably now corrupt so
           just exit since this is an obvious programming error that was just
           introduced. */
        IAS_LOG_ERROR("Too many fields found - stack probably "
                      "corrupted - exiting");
        exit(EXIT_FAILURE);
    }


    /* Done */
    return SUCCESS;
}
/*************************************************************************
Name: ias_los_model_create_lunar_projection

Purpose: Creates a lunar projection with the indicated parameters.  If
    requested with the use_cache parameter, it creates a cache of moon 
    positions for each line of the image since calculating the moon position
    is slow.  The cache should be used if a lot of projection transformations
    need to be done.

Returns: Pointer to the created projection or NULL if an error.

**************************************************************************/
IAS_LUNAR_PROJECTION *ias_los_model_create_lunar_projection
(
    const IAS_LOS_MODEL *model,  /* I: model information */
    int band_index,              /* I: Band index */
    int sca_index,               /* I: SCA index */
    IAS_SENSOR_DETECTOR_TYPE dettype, /* I: Detector type */
    double unit_scale,           /* I: Scale for output coordinate units */
    int use_cache                /* I: Flag to use caching to speed up
                                       transformations when a lot of
                                       transformations will be done */
)
{
    int ref_band_index;     /* Reference location on the focal plane */
    int ref_sca_index;
    int ref_image_line;
    int ref_detector;
    int line;               /* Line in the input image */
    double samp;            /* Sample in the input image */
    IAS_LUNAR_PROJECTION *proj = NULL;
    double pi = ias_math_get_pi();

    /* Get the reference location */
    if (find_reference_location(model, &ref_band_index, &ref_sca_index,
            &ref_image_line, &ref_detector) != SUCCESS)
    {
        IAS_LOG_ERROR("Could not find the focal plane reference location");
        return NULL;
    }
    samp = ref_detector;

    /* Allocate the memory for the cached information */
    proj = calloc(1, sizeof(*proj));
    if (!proj)
    {
        IAS_LOG_ERROR("Failed allocating memory Lunar projection cache");
        return NULL;
    }

    /* Fill in the constants in the projection structure */
    proj->model = model;
    proj->band_index = band_index;
    proj->sca_index = sca_index;
    proj->dettype = dettype;
    proj->unit_scale = unit_scale;

    /* assume we won't cache information */
    proj->img_lines = 0;

    /* Find moon's position at the center of L0R. This will serve as the
       reference.  All other points are adjusted according to it's moon
       position and this location.  Note the reference always uses the nominal
       detector type */
    if (ias_los_model_get_moon_position_at_location(model, ref_band_index,
            ref_sca_index, ref_image_line, ref_detector, IAS_NOMINAL_DETECTOR,
            &proj->ref_rasc, &proj->ref_dec, &proj->ref_dist) != SUCCESS)
    {
        IAS_LOG_ERROR("Error in the calculating moon position for L/S");
        free(proj);
        return NULL;
    }

    IAS_LOG_DEBUG("Ref RASC: %13.8lf  Ref DEC: %13.8lf  Ref Dist: %17.6lf",
            proj->ref_rasc, proj->ref_dec, proj->ref_dist);

    /* Set up the cache if it was requested */
    if (use_cache)
    {
        /* Calculate the number of lines in the input image using the model
           information */
        proj->img_lines = model->sensor.bands[band_index].frame_count
            * model->sensor.bands[band_index].sampling_char.lines_per_frame;

        /* Allocate the buffers for the moon position at each line of the
           image */
        proj->img_rasc = (double *)malloc(sizeof(double) * proj->img_lines);
        if (!proj->img_rasc)
        {
            IAS_LOG_ERROR("Error allocating RA look up table");
            ias_los_model_free_lunar_projection(proj);
            return NULL;
        }

        proj->img_dec = (double *)malloc(sizeof(double) * proj->img_lines);
        if (!proj->img_dec)
        {
            IAS_LOG_ERROR("Error allocating DEC look up table");
            ias_los_model_free_lunar_projection(proj);
            return NULL;
        }

        proj->img_dist = (double *)malloc(sizeof(double) * proj->img_lines);
        if (!proj->img_dist)
        {
            IAS_LOG_ERROR("Error allocating Dist look up table");
            ias_los_model_free_lunar_projection(proj);
            return NULL;
        }

        /* Build look up table for each line in the image */
        for (line = 0; line < proj->img_lines; line++)
        {
            if (ias_los_model_get_moon_position_at_location(model, band_index,
                    sca_index, line, samp, dettype, 
                    &proj->img_rasc[line], &proj->img_dec[line],
                    &proj->img_dist[line]) != SUCCESS)
            {
                IAS_LOG_ERROR("Error calculating moon position look up table "
                    "for line %d", line);
                ias_los_model_free_lunar_projection(proj);
                return NULL;
            }

            /* Make sure the RASC doesn't walk across +/- 180 degree line */
            if (line > 0)
            {
                if ((proj->img_rasc[line] - proj->img_rasc[line - 1]) >  pi/2.0)
                    proj->img_rasc[line] -= 2.0 * pi;
                if ((proj->img_rasc[line] - proj->img_rasc[line - 1]) < -pi/2.0)
                    proj->img_rasc[line] += 2.0 * pi;
            }
        }
    }

    return proj;
}
int read_mwdImage(PARAMETERS* param, int process_times,
					MWDIMAGE_BUFFER_INFO *mwdImage_buffer_info,
					double ephemeris_start_time, double ephemeris_end_time)
{
	char* memblock;
	long long mem_block_size;
	int fd;
	int status = ERROR;
	FRAME_HEADER *frame_head;
	frame_head = (FRAME_HEADER*)malloc(sizeof(FRAME_HEADER));
	long long start_sync_position = 0;

	/* To obtain file size */
	fd = open(param->mwdImage_filename,O_RDWR);
	IAS_LOG_DEBUG("fd = %d\n", fd);
	if(fd == -1)
	{
		IAS_LOG_ERROR("failed to open the file!\n");
		return ERROR;
	}

	/* If it is the first time to read file */
	if(process_times == 0)
	{
		/* Allocate memory for memblock */
		memblock = (char*)malloc(MEM_BLOCK_SIZE*sizeof(char));

		/* Read file into memblock */
		mem_block_size = read(fd,memblock,MEM_BLOCK_SIZE*sizeof(char));
		if(mem_block_size == -1)
		{
			IAS_LOG_ERROR("failed in pread!\n");
			return ERROR;
		}

		/* check the image time, if image time > ephemeris time,
		 * move to next oli frame, return the position of the
		 * oli frame whoes image time < ephemeris time
		 */
		long long block_index = 0;
		long long num_frame = 0;
		while(block_index < mem_block_size)
		{
			memcpy(&(frame_head->frame_length),memblock+6+block_index,
							sizeof(int));
			memcpy(&(frame_head->mode),memblock+36+block_index,
					sizeof(char)*4);
			if(frame_head->mode[0] == 'O' && frame_head->mode[1] == 'L'
					&& frame_head->mode[2] == 'I')
			{
				if((frame_head->time - J2000_SUB_UTC_EPOCH) < ephemeris_start_time*1000)
				{
					mwdImage_buffer_info->oli_frame_start_bytes_in_buffer[num_frame++]
														 = block_index;
					block_index += frame_head->frame_length;
				}
				else
				{
					block_index += frame_head->frame_length;
				}

			}
			else //frame mode is TIRS or PAN
			{
				block_index += frame_head->frame_length;
			}
		}
		mwdImage_buffer_info->num_oli_frame = num_frame;
	}
	/* If it is the last time to read file */
	else if(process_times == PROCESS_TIME - 1)
	{
		/* Allocate memory for memblock */
		memblock = (char*)malloc((MEM_BLOCK_SIZE+REDUNDANCY)*sizeof(char));

		/* Read file into memblock */
		lseek(fd,MEM_BLOCK_SIZE*process_times-REDUNDANCY,SEEK_SET);
		mem_block_size = read(fd,memblock,(MEM_BLOCK_SIZE+REDUNDANCY)*sizeof(char));
		if(mem_block_size == -1)
		{
			IAS_LOG_ERROR("failed in pread!\n");
			return ERROR;
		}

		/* Sync the frame */
		status = frame_header_sync(memblock,mem_block_size,&start_sync_position);
		if(status != SUCCESS)
		{
			IAS_LOG_ERROR("Cann't sync the frame header!\n");
			return ERROR;
		}

		long long block_index = 0;
		long long num_frame = 0;
		while(block_index < mem_block_size)
		{
			memcpy(&(frame_head->frame_length),memblock+start_sync_position+6+block_index,
					sizeof(int));
			memcpy(&(frame_head->mode),memblock+start_sync_position+36+block_index,
					sizeof(char)*4);
			if(frame_head->mode[0] == 'O' && frame_head->mode[1] == 'L'
					&& frame_head->mode[2] == 'I')
			{
				if(frame_head->time - J2000_SUB_UTC_EPOCH < ephemeris_end_time*1000)
				{
					mwdImage_buffer_info->oli_frame_start_bytes_in_buffer[num_frame++]
										 = start_sync_position+block_index;
					block_index += frame_head->frame_length;
				}
				else
				{
					break;
				}
			}
			else //frame mode is TIRS or PAN
			{
				block_index += frame_head->frame_length;
			}
		}
		mwdImage_buffer_info->num_oli_frame = num_frame;
	}
	/* Others time to read the file */
	else
	{
		/* Allocate memory for memblock */
		memblock = (char*)malloc((MEM_BLOCK_SIZE+REDUNDANCY)*sizeof(char));

		/* Read file into memblock */
		lseek(fd,MEM_BLOCK_SIZE*process_times-REDUNDANCY,SEEK_SET);
		mem_block_size = read(fd,memblock,(MEM_BLOCK_SIZE+REDUNDANCY)*sizeof(char));
		if(mem_block_size == -1)
		{
			IAS_LOG_ERROR("failed in pread!\n");
			return ERROR;
		}

		/* Sync the frame */
		status = frame_header_sync(memblock,mem_block_size,&start_sync_position);
		if(status != SUCCESS)
		{
			IAS_LOG_ERROR("Cann't sync the frame header!\n");
			return ERROR;
		}


		long long block_index = 0;
		long long num_frame = 0;
		while(block_index < mem_block_size)
		{
			memcpy(&(frame_head->frame_length),memblock+start_sync_position+6+block_index,
					sizeof(int));
			memcpy(&(frame_head->mode),memblock+start_sync_position+36+block_index,
					sizeof(char)*4);
			if(frame_head->mode[0] == 'O' && frame_head->mode[1] == 'L'
					&& frame_head->mode[2] == 'I')
			{
				mwdImage_buffer_info->oli_frame_start_bytes_in_buffer[num_frame++]
									 = start_sync_position+block_index;
				block_index += frame_head->frame_length;
			}
			else //frame mode is TIRS or PAN
			{
				block_index += frame_head->frame_length;
			}
		}
		mwdImage_buffer_info->num_oli_frame = num_frame;
	}


	/* store the buffer information into mwdImage_buffer_info */
	mwdImage_buffer_info->mem_mapped_buffer = memblock;
	mwdImage_buffer_info->num_bytes_in_buffer = mem_block_size;


	close(fd);
	return 0;
}
/****************************************************************************
NAME:     ias_math_compute_grey_cross                   

PURPOSE:  Compute the unnormalized (raw) sum of pixel-by-pixel cross products 
          between the reference and search images for every combination of 
          horizontal and vertical offsets of the reference relative to the 
          search image

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           Error allocating memory for cross product calculation
SUCCESS         Calculated cross products

*****************************************************************************/
int ias_math_compute_grey_cross
(
    const float *images, /* I: Search subimage */
    const float *imager, /* I: Reference subimage */
    const int *srch_size,/* I: Actual size of search subimage:  samps/lines */
    const int *ref_size, /* I: Actual size of reference subimage: samps/lines */
    int ncol,           /* I: # of columns in cross-product sums array(unormc)*/
    int nrow,           /* I: # of rows in cross-product sums array (unormc) */
    double *unormc      /* O: Array of unnormalized (raw) counts of edge 
                              coincidences for each alignment of reference
                              image relative to search image */
)
{
    float *membfr;      /* Buffer in which reference subimage is properly 
                           aligned and zero padded                           */
    float *membfs;      /* Buffer for aligning and padding search subimage*/
    int imgptr;         /* Pointer into subimage arrays                   */
    int line;           /* Loop index:  current buffer line               */
    int lncont;         /* Pointer to start of next segment of line       */
    int lnstrt;         /* Pointer to first segment of line               */
    int memptr;         /* Loop index:  pointer into AP transfer buffers  */
    int ndxout;         /* Pointer into array for correlation output      */
    int nsnew[2];       /* Size of search window                          */
    int nrnew[2];       /* Size of reference window                       */
    int memdim[2];      /* Power-of-2 dimensions for AP arrays            */
    int i,j;            /* Loop counters                                  */
    double denom;       /* Denominator to calc unnormalized xcorr values  */
    double tempim;      /* Temporary imaginary value when replacing cser  */
    IAS_COMPLEX *cser;  /* Complex of search subimage                     */
    IAS_COMPLEX *cref;  /* Complex of reference subimage                  */

    nsnew[0] = srch_size[0];
    nsnew[1] = srch_size[1];
    nrnew[0] = ref_size[0];
    nrnew[1] = ref_size[1];

    /* Zero extend search image to next higher power of 2
       Minimum window size is 64x64 */
    for (i = 0; i < 2; i++)
    {
        memdim[i] = 64;
        for (;;)
        {
            if (nsnew[i] <= memdim[i])
                break;
            memdim[i] *= 2;
        }
    }

    /* Fix for incorrect handling of non-square arrays */
    memdim[0] = memdim[0] > memdim[1] ? memdim[0] : memdim[1];
    memdim[1] = memdim[0];

    membfr = (float *)malloc(memdim[0] * memdim[1] * sizeof(float));
    if (membfr == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }
    membfs = (float *)malloc(memdim[0] * memdim[1] * sizeof(float));
    if (membfs == NULL)
    {
        free(membfr);
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }
    cser = (IAS_COMPLEX *)malloc(memdim[0] * memdim[1] * sizeof(IAS_COMPLEX));
    if (cser == NULL)
    {
        free(membfr);
        free(membfs);
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }
    cref = (IAS_COMPLEX *)malloc(memdim[0] * memdim[1] * sizeof(IAS_COMPLEX));
    if (cref == NULL)
    {
        free(membfr);
        free(membfs);
        free(cser);
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }

    for (lnstrt = 0, imgptr = 0, line = 0; line < memdim[1]; line++)
    {
        if (line < nsnew[1])
        {
            lncont = lnstrt + nsnew[0];
            for (memptr = lnstrt; memptr < lncont; imgptr++, memptr++)
                membfs[memptr] = images[imgptr];
        }
        else
            lncont = lnstrt;
        lnstrt += memdim[0];
        if (lncont < lnstrt)
        {
            for (memptr = lncont; memptr < lnstrt; memptr++)
                membfs[memptr] = 0.0;
        }
    }

    /* Make complex 2-d of search data
       Note that the data are put into cser in column-major order since the 
       fft routine wants the data that way (see ias_math_fft2d). */
    for (memptr = 0, i = 0; i < memdim[1]; i++)
    {
        for (j = 0; j < memdim[0]; j++, memptr++)
        {
            cser[j * memdim[1] + i].re = membfs[memptr];
            cser[j * memdim[1] + i].im = 0.0;
        }
    }

    /* Now zero extend reference image */
    for (lnstrt = 0, imgptr = 0, line = 0; line < memdim[1]; line++)
    {
        if (line < nrnew[1]) 
        {
            lncont = lnstrt + nrnew[0];
            for (memptr = lnstrt; memptr < lncont; imgptr++, memptr++)
                membfr[memptr] = imager[imgptr];
        }
        else
            lncont = lnstrt;
        lnstrt += memdim[0];
        if (lncont < lnstrt) 
        {
            for (memptr = lncont; memptr < lnstrt; memptr++)
                membfr[memptr] = 0.0;
        }
    }

    /* Make complex 2-d of reference data
       Note that the data are put into cser in column-major order since the 
       fft routine wants the data that way (see ias_math_fft2d). */
    for (memptr = 0, i = 0; i < memdim[1]; i++)
    {
        for (j = 0; j < memdim[0]; j++, memptr++)
        {
            cref[j * memdim[1] + i].re = membfr[memptr];
            cref[j * memdim[1] + i].im = 0.0;
        }
    }

    /* Take fft of search and reference data */
    ias_math_fft2d((double *)cser,memdim[1],memdim[0],1);
    ias_math_fft2d((double *)cref,memdim[1],memdim[0],1);

    /* Make point by multiplication of search subimage ft with conjugate of 
       reference subimage ft */
    for (i = 0; i < memdim[0] * memdim[1]; i++)
    {
        tempim = cref[i].re * cser[i].im-cref[i].im * cser[i].re;
        cser[i].re = cref[i].re * cser[i].re + cref[i].im * cser[i].im;
        cser[i].im = tempim;
    }

    /* Take inverse fft of cser */
    ias_math_fft2d((double *)cser,memdim[1],memdim[0],-1);

    /* Extract part of correlation array which is valid */
    denom = memdim[0] * memdim[1];
    ndxout = 0;
    for (i = 0; i < nrow; i++)
    {
        for (j = 0; j < ncol; j++, ndxout++)
            unormc[ndxout] = cser[j * memdim[1] + i].re / denom;
    }

    free(membfr);
    free(membfs);
    free(cser);
    free(cref);

    return SUCCESS;
}
int ias_odl_add_or_replace_field
(
    OBJDESC *curr_object,       /* I/O: Object tree to populate */
    const char *p_LabelName,    /* I: Field to add */
    IAS_ODL_TYPE ValueType,     /* I: What type the field is */
    const int p_MemorySize,     /* I: Total memory size of attribues values */
    void *p_MemoryAddr,         /* I: Pointer to the attribute information */
    const int nelements,        /* I: Number of attribute values */
    const int action_flag       /* I: Flag indicated the attribute name will 
                                      be replaced */
)
{
    int i;                       /* Looping variable */
    int sz;                      /* Memory size base on data type */
    int count = 0;               /* Count of attribute values */
    int total_indentation;       /* Total number of indent characters */
    int curr_length;             /* Current length of keyword string */
    int tmp_len;                 /* Length of the temporary string */
    char tmp_string[TB_MAXLINE]; /* Temporary string for converting values */
    char *value_to_paste = NULL; /* Pointer to ODL keyword values */
    char newline_blanks[MAX_LEN] = "\n"; /* String for newline and indenting */
    const char *p_ClassName = NULL; /* Group/Object name */
    KEYWORD *new_keyword = NULL; /* Pointer to new ODL keyword */
    KEYWORD *curr_keyword = NULL;/* Pointer to current ODL keyword */
    KEYWORD *ptr_keyword = NULL; /* Pointer to cut ODL keyword */
    char *cptr = NULL;           /* Pointer to the current value */
    OBJDESC *p_lp = NULL;        /* Pointer to ODL object */

    /* Check to make sure a valid Label Name was specified */
    if ((p_LabelName == NULL) || (strlen(p_LabelName) == 0))
    {
        IAS_LOG_ERROR("Invalid attribute name");
        return ERROR;
    }

    /* Check to make sure there are values to convert */
    if ((p_MemorySize <= 0) || (nelements <= 0))
    {
        IAS_LOG_ERROR("Undefined attribute size");
        return ERROR;
    }

    /* Calculate the indentation size */
    total_indentation = curr_object->level * INDENT_SIZE;
    
    /* ensure indentation does not overrun newline buffer size */
    if (total_indentation > 78)
        total_indentation = 78;

    /* Calculate the length of the output string.  This includes the
       indentation, label name, and equal sign surrounded by spaces " = " */
    curr_length = total_indentation + strlen(p_LabelName) + 3;

    /* Add blanks for the indentation */
    for(i = 0; i < total_indentation ; i++)
        strcat(newline_blanks," ");

    /* Determine the size of the data input */
    sz = p_MemorySize / nelements;
    cptr = (char *)p_MemoryAddr;

    if (nelements > 1)
    {
        /* Start the array of strings with a left parenthesis */
        CopyString(value_to_paste, "(");
        curr_length++;
    }

    while (count < nelements)
    {
        /* Convert the value at the current position to a
           formatted string */
        switch (ValueType)
        {
            case IAS_ODL_String:
                sprintf(tmp_string, "\"%s\"", (char *)cptr);
                break;
            case IAS_ODL_ArrayOfString:
                sprintf(tmp_string, "\"%s\"", (char *)cptr);
                cptr += sz;
                break;
            case IAS_ODL_Int:
                sprintf(tmp_string, "%d", *(int *)cptr);
                cptr += sizeof(int);
                break;
            case IAS_ODL_Long:
                sprintf(tmp_string, "%ld", *(long *)cptr);
                cptr += sizeof(long);
                break;
            case IAS_ODL_Float:
                sprintf(tmp_string, "%G", *(float *)cptr);
                cptr += sizeof(float);
                break;
            case IAS_ODL_Double:
                sprintf(tmp_string, "%-4.14G", *(double *)cptr);
                cptr += sizeof(double);
                break;
            case IAS_ODL_Sci_Not:
                sprintf(tmp_string, "%-4.11G", *(double *)cptr);
                cptr += sizeof(double);
                break;
        }

        /* Remove any extra spaces exceptt in array of strings type */
          if ( ValueType != IAS_ODL_String )
             ias_odl_remove_character(tmp_string,' ');

        /* Determine if the line needs to be wrapped.
           The 2 accounts for the comma and space or
           the ending paranthesis.
        */
        tmp_len = strlen(tmp_string);
        if ((curr_length + tmp_len + 2) > MAX_LEN)
        {
            /* Add a new line and indentation */
            AppendString(value_to_paste, newline_blanks);

            /* Reset the current string length */
            curr_length = tmp_len + strlen(newline_blanks);
        }

        /* Append the formatted string to the value to paste */
        AppendString(value_to_paste, tmp_string);
        curr_length += tmp_len;

        /* Increment the element counter */
        count++;

        if (count < nelements)
        {
            /* Append a comma and space to separate values */
            AppendString(value_to_paste, ", ");
            curr_length += 2;
        }
    }

    if (nelements > 1)
    {
        /* End the array of strings with a left parenthesis */
        AppendString(value_to_paste, ")");
        curr_length++;
    }
    
    if (action_flag == TRUE)
    { 
        /* find ODL object */
        if ((p_lp = OdlFindObjDesc(curr_object, p_ClassName, 
                    p_LabelName, NULL, 1, 
                    ODL_RECURSIVE_DOWN)) == NULL)
        {
            if ((long)strlen(ODLErrorMessage) <= 1 )
            {
                IAS_LOG_ERROR("%s", ODLErrorMessage);
                IAS_LOG_ERROR("Object %s not found with %s keyword", 
                    (p_ClassName) ? (p_ClassName) : "null", p_LabelName);
            }
            free(value_to_paste);
            return ERROR;
        }

        /* search for keyword */
        if ((curr_keyword = OdlFindKwd(p_lp, p_LabelName, NULL, 1,
            ODL_RECURSIVE_DOWN)) == NULL)
        {
            if ((long)strlen(ODLErrorMessage) <= 1 )
            {
                IAS_LOG_ERROR("%s", ODLErrorMessage);
                IAS_LOG_ERROR("Keyword %s not found", p_LabelName);
            }
            free(value_to_paste);
            return ERROR;
        }

        /* cut old keyword */
        ptr_keyword = OdlCutKwd(curr_keyword);
    } /* end of replace field */

    /* Paste the new label, with values, into the ODL Object Tree */
    new_keyword = OdlNewKwd((char *)p_LabelName, (char *)value_to_paste,
                            NULL, NULL, NULL, 0);

    /* Free the space allocated to the value_to_paste pointer */
    free(value_to_paste);

    curr_keyword = OdlPasteKwd(new_keyword, curr_object);
    if (!curr_keyword)
    {
        IAS_LOG_ERROR("Add attribute to tree failed");
        return ERROR ;
    }
    /* free old keyword */
    OdlFreeKwd(ptr_keyword);

    return SUCCESS;
}
/******************************************************************************

UNIT NAME: convert_string.c

PURPOSE: To convert the string to the new type using the memory address given by
         caller

INVOCATION METHOD:
    status = convert_string(p_destination, parm_type, kvalue)

RETURN VALUE:
    Type = int

Value                           Description
---------------                 ------------------------------------------------
SUCCESS                         Converted the String in to requested type
ERROR                           Allocation error or unknown parm_type
IAS_ODL_INVALID_DATA_TYPE       Invalid data type

******************************************************************************/
int convert_string
( 
    void *p_destination,            /* I/O: converted value location */ 
    IAS_ODL_TYPE parm_type,         /* I; ODL data type */
    char *kvalue)                   /* I: word to convert */
{
    char *p_endptr;
    char *p_a;

    switch(parm_type)
    {
        case IAS_ODL_Long:
            (*(long *)p_destination) = strtol(kvalue,&p_endptr,10);
            if (*p_endptr != '\0')
            {
                return IAS_ODL_INVALID_DATA_TYPE;
            }
            break;

        case IAS_ODL_Int:
            (*(int *)p_destination) = (int)strtol(kvalue,&p_endptr,10);
            if (*p_endptr != '\0')
            {
                return IAS_ODL_INVALID_DATA_TYPE;
            }
            break;

        case IAS_ODL_Float:
            (*(float *)p_destination) = (float)strtod(kvalue,&p_endptr);
            if (*p_endptr != '\0')
            {
                return IAS_ODL_INVALID_DATA_TYPE;
            }
            break;

        case IAS_ODL_Sci_Not:
        case IAS_ODL_Double:
            (*(double *)p_destination) = strtod(kvalue,&p_endptr);
            if (*p_endptr != '\0')
            {
                return IAS_ODL_INVALID_DATA_TYPE;
            }
            break;

        case IAS_ODL_ArrayOfString:
            p_a = malloc(strlen(kvalue)+1);
            if (!p_a)
            {
                IAS_LOG_ERROR("Allocating Memory ... ");
                return ERROR;
            }
            (void)strcpy(p_a,kvalue);
            (*(char **)p_destination) = p_a;
            break;

        default:
            IAS_LOG_ERROR("Invalid Conversion type %s ...", kvalue);
            return ERROR;
    }

    return SUCCESS;
}
int ias_odl_get_field
( 
    void *p_MemoryAddr,             /* I/O: List of attributes to retrieve */
    int MemorySize,                 /* I: mem size of attributes */ 
    IAS_ODL_TYPE ValueType,         /* I: ODL data type */
    IAS_OBJ_DESC *p_ODLTree,        /* I: ODL tree */
    const char *p_ClassName,        /* I: Group/Object name */
    const char *p_LabelName,        /* I: Field to get */
    int *p_Count                    /* I: number of values in attribute */
)
{
    OBJDESC *p_lp;              /* Object Descriptor */
    KEYWORD *p_kw;              /* Keyword Name */
    char *p_kwv;                /* Keyword Value */
    char *p_keyword;            /* Copy of the Keyword Value */
    int i;                      /* loop counter */
    char *p_word;               /* word to convert */
    int ret_code = 0;           /* function return value */

    *p_Count = 0;

    if ( (p_LabelName == NULL) || (strlen(p_LabelName) == 0) )
    {
        IAS_LOG_ERROR("Attribute name missing");
        return ERROR;
    }

    if ((p_lp = OdlFindObjDesc(p_ODLTree, p_ClassName, p_LabelName, NULL, 1, 
        ODL_RECURSIVE_DOWN)) == NULL)
    {
        /* it's possible that we have NULL for a group(p_ClassName), that's OK;
           no need to log an error, just return */
        return IAS_ODL_NOT_FOUND;
    }

    if ((p_kw = OdlFindKwd(p_lp, p_LabelName, NULL, 1 ,ODL_RECURSIVE_DOWN)) 
        == NULL)
    {
        if ((long)strlen(ODLErrorMessage) <= 1 )
        {
            IAS_LOG_ERROR("%s", ODLErrorMessage);
            IAS_LOG_ERROR("Keyword '%s' not found", p_LabelName);
        }
        return IAS_ODL_NOT_FOUND;
    }

    if ((p_kwv = OdlGetKwdValue(p_kw)) == NULL)
    {
        if ((long)strlen(ODLErrorMessage) <= 1 )
        {
            IAS_LOG_ERROR("%s", ODLErrorMessage);
            IAS_LOG_ERROR("Keyword %s not found", p_LabelName);
        }
        return IAS_ODL_NOT_FOUND;
    }
    if ((p_keyword= malloc(strlen(p_kwv)+1)) == NULL)
    {
        IAS_LOG_ERROR("Malloc error");
        return ERROR;
    }
    (void)strcpy(p_keyword,p_kwv);

    /* When working with a set or a sequence, all of the newline characters
       should be removed from the incoming string.  The ODL library is placing
       newlines into these strings around character 2025 even though they don't
       exist in the ODL file. */
    if ((OdlGetKwdValueType(p_kw) == ODL_SET) ||
        (OdlGetKwdValueType(p_kw) == ODL_SEQUENCE))
    {
        char * p_newline;

        while ((p_newline = strchr(p_keyword, '\n')) != NULL)
        {
            while (*p_newline != '\0')
            {
                *p_newline = *(p_newline + 1);
                p_newline++;
            }
        }
    }

    if (OdlGetKwdValueType(p_kw) == ODL_SET)
    {
        p_word = strtok(p_keyword,"(,) \"\n");
        while(p_word != NULL)
        {
            if (strlen(p_word))
            {
                switch (ValueType)
                {
                case IAS_ODL_Long :
                    MemorySize -= sizeof(long);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s",p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (long *)p_MemoryAddr + 1;
                    break;

                case IAS_ODL_Int :
                    MemorySize -= sizeof(int);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s",p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (int *)p_MemoryAddr + 1;
                    break;

                case IAS_ODL_Float :
                    MemorySize -= sizeof(float);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s",p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (float *)p_MemoryAddr + 1;
                    break;

                case IAS_ODL_Double :
                case IAS_ODL_Sci_Not :
                    MemorySize -= sizeof(double);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s",p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (double *)p_MemoryAddr + 1;
                    break;

                case IAS_ODL_ArrayOfString :
                    MemorySize -= sizeof(char *);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return  IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        for( i=0;i<*p_Count;i++)
                            free((char *)p_MemoryAddr + i);
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s",p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (char *)p_MemoryAddr + sizeof(char *);
                    break;

                default:
                    (void)strncpy( p_MemoryAddr, p_word, MemorySize);
                    IAS_LOG_ERROR("Type IAS_ODL_String is not valid for arrays "
                        "of strings");
                    free(p_keyword);
                    return ERROR;
                }
                *p_Count += 1;
                p_word = strtok(NULL,",() \"\n");
            }
        }
    }
    else if (OdlGetKwdValueType(p_kw) == ODL_SEQUENCE)
    {
        p_word = strtok(p_keyword,"{,} \"\n");
        while(p_word != NULL)
        {
            if (strlen(p_word))
            {
                switch (ValueType)
                {
                case IAS_ODL_ArrayOfString :
                    MemorySize -= sizeof(char *);
                    if (MemorySize < 0 )
                    {
                        IAS_LOG_ERROR("Input ODL value overflows allocated "
                            "space ");
                        free(p_keyword);
                        return  IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
                    }
                    if ( ( ret_code = convert_string(p_MemoryAddr,
                        ValueType,p_word) ) != SUCCESS )
                    {
                        for( i=0;i<*p_Count;i++)
                            free((char *)p_MemoryAddr + i);
                        IAS_LOG_ERROR("Converting %s keyword's value "
                            "%s", p_LabelName, p_word);
                        free(p_keyword);
                        return ret_code;
                    }
                    p_MemoryAddr = (char *)p_MemoryAddr + sizeof(char *);
                    break;

                default:
                    (void)strncpy( p_MemoryAddr, p_word, MemorySize);
                    IAS_LOG_ERROR("An ODL sequence is of type "
                        "IAS_ODL_ArrayOfString only");
                    free(p_keyword);
                    return ERROR;
                }
                *p_Count += 1;
                p_word = strtok(NULL,",{} \"\n");
            }
        }
    }
    else
    {
        if (ValueType == IAS_ODL_String)
        {
            char * p_start = p_kwv;
            int    len     = strlen(p_kwv);

            /* If we are working with a double quoted string, we'll need to
               remove the double quotes in the string that is passed back.
               Adjust the length of the string and the starting point for
               copying, if necessary. */
            if (p_kwv[0] == '\"')
            {
                len -= 2;
                p_start++;
            }

            if (MemorySize < (len + 1))
            {
                IAS_LOG_ERROR("Input ODL value overflows allocated space");
                (void)strncpy(p_MemoryAddr, p_start, MemorySize);
                ((char *)p_MemoryAddr)[MemorySize-1] = '\0';
                free(p_keyword);
                return IAS_ODL_NOT_ENOUGH_MEMORY_SUPPLIED;
            }

            (void)strncpy(p_MemoryAddr, p_start, len);
            ((char *)p_MemoryAddr)[len] = '\0';
        }
        else
        {
            switch(ValueType)
            {
            case IAS_ODL_Long:
                MemorySize -= sizeof(long);
                break;

            case IAS_ODL_Int:
                MemorySize -= sizeof(int);
                break;

            case IAS_ODL_Float:
                MemorySize -= sizeof(float);
                break;

            case IAS_ODL_Double:
            case IAS_ODL_Sci_Not:
                MemorySize -= sizeof(double);
                break;

            default:
                IAS_LOG_ERROR("Invalid Type specified");
                free(p_keyword);
                return ERROR;
            }

            if (MemorySize < 0 )
            {
                IAS_LOG_ERROR("Input ODL value overflows allocated space ");
                free(p_keyword);
                return ERROR;
            }

            if ( ( ret_code = convert_string(p_MemoryAddr,ValueType,
                p_kwv) ) != SUCCESS )
            {
                IAS_LOG_ERROR("Converting %s keyword's value %s",
                    p_LabelName,p_kwv);
                free(p_keyword);
                return ret_code;
            }
        }
        *p_Count += 1;
    }
    free(p_keyword);
    return SUCCESS;
}
int ias_l1r_read_image 
(
    const L1R_BAND_IO *l1r_band, /* I: HDF IO band structure */
    int sca_index,       /* I: SCA to read (0-rel) */
    int line_start,      /* I: Line to start reading (0-rel) */
    int sample_start,    /* I: Sample to start reading (0-rel) */
    int lines,           /* I: Number of lines to read */
    int samples,         /* I: Number of samples to read */
    void *data           /* O: Data buffer */
)
{
    hid_t data_space;       /* dataspace for the data buffer dimensions */
    hsize_t data_dims[2] = {lines, samples}; /* size of the data buffer */
    hsize_t file_size[3] = {1, lines, samples}; /* slab size to read from the 
                                                   file */
    /* location to read in the file */
    hsize_t file_offset[3] = {sca_index, line_start, sample_start}; 
    int status;

    /* check for various errors in the input */
    if (l1r_band == NULL)
    {
        IAS_LOG_ERROR("NULL band pointer passed in");
        return ERROR;
    }

    if (l1r_band->id < 0)
    {
        IAS_LOG_ERROR("Band is not open for reading in file %s",
                      l1r_band->l1r_file->filename);
        return ERROR;
    }

    /* verify the window of data being read actually falls within the band */
    if ((sca_index < 0) || (sca_index >= l1r_band->scas)
        || (line_start < 0) || ((line_start + lines) > l1r_band->lines)
        || (sample_start < 0) || ((sample_start + samples) > l1r_band->samples))
    {
        IAS_LOG_ERROR("Attempted to read imagery from SCA index %d, band "
            "number %d of %s at line %d, sample %d for a window %d lines "
            "x %d samples when the band has %d SCAs, %d lines, %d samples", 
            sca_index, l1r_band->number, l1r_band->l1r_file->filename, 
            line_start, sample_start, lines, samples, l1r_band->scas,  
            l1r_band->lines, l1r_band->samples);
        return ERROR;
    }

    /* define the memory dataspace to read data into */
    data_space = H5Screate_simple(2, data_dims, NULL);
    if (data_space < 0)
    {
        IAS_LOG_ERROR("Creating memory dataspace for file %s",
                      l1r_band->l1r_file->filename);
        return ERROR;
    }

    status = H5Sselect_hyperslab(l1r_band->dataspace_id, H5S_SELECT_SET, 
                file_offset, NULL, file_size, NULL);
    if (status < 0)
    {
        IAS_LOG_ERROR("Selecting hyperslab for file %s, band %d",
                      l1r_band->l1r_file->filename, l1r_band->number);
        H5Sclose(data_space);
        return ERROR;
    }

    /* read the data from the dataset */
    status = H5Dread(l1r_band->id, l1r_band->memory_data_type,
                     data_space, l1r_band->dataspace_id, H5P_DEFAULT, data);
    H5Sclose(data_space);
    if (status < 0)
    {
        IAS_LOG_ERROR("Reading from file %s, band number %d, SCA index %d, "
            "line %d, sample %d, number of lines %d, number of samples %d",
            l1r_band->l1r_file->filename, l1r_band->number, sca_index, 
            line_start, sample_start, lines, samples);
        return ERROR;
    }

    return SUCCESS;
}
/*************************************************************************
Name: ias_los_model_transform_lunar_projection

Purpose: Transform the input lat/long to account for the apparent lunar motion.

Returns: SUCCESS or ERROR

**************************************************************************/
int ias_los_model_transform_lunar_projection
(
    const IAS_LUNAR_PROJECTION *proj, /* I: Lunar projection information */
    double iline,          /* I: Input line location */
    double isamp,          /* I: Input sample location */
    double *lunar_lat,     /* I/O: Declination of LOS */
    double *lunar_long,    /* I/O: Right ascension of LOS */
    double *distance_scale /* O: Scale factor for pixel size to apply */
)
{
    int line_index;          /* Index into lunar position look up table */
    double right_ascension;  /* Apparent right ascension of the moon */
    double declination;      /* Declination of the moon            */
    double moon_earth_dist;  /* Earth-moon distance in meters  */
    double out_lat;          /* output latitude calculated */
    double out_long;         /* output longitude calculated */
    double pi = ias_math_get_pi();

    /* Find moon's position for current line/sample time.  Note that the line
       index is rounded to the nearest integer.  That is acceptable since
       this routine should only ever be called with whole number lines. */
    line_index = (int)floor(iline + 0.5);
    if (line_index >= 0 && line_index < proj->img_lines)
    {
        /* The requested line is in the cache, so get the value from there */
        right_ascension = proj->img_rasc[line_index];
        declination = proj->img_dec[line_index];
        moon_earth_dist = proj->img_dist[line_index];
    }
    else
    {
        /* The requested value is outside the cached set, so calculate it */
        if (ias_los_model_get_moon_position_at_location(proj->model,
                proj->band_index, proj->sca_index, iline, isamp, proj->dettype,
                &right_ascension, &declination, &moon_earth_dist) != SUCCESS)
        {
            IAS_LOG_ERROR("Failed to calculate the Moon's position at "
                "line %f, sample %f", iline, isamp);
            return ERROR;
        }
    }

    /* Adjust for the apparent lunar motion */
    *distance_scale = moon_earth_dist / proj->ref_dist;

    /* Check for +/-180 right ascension wrap around */
    if ((right_ascension - proj->ref_rasc) > pi/2.0)
        right_ascension -= 2.0 * pi;
    if ((right_ascension - proj->ref_rasc) < -pi/2.0)
        right_ascension += 2.0 * pi;
    if ((*lunar_long - proj->ref_rasc) > pi/2.0)
        *lunar_long -= 2.0 * pi;
    if ((*lunar_long - proj->ref_rasc) < -pi/2.0)
        *lunar_long += 2.0 * pi;

    out_lat = (*lunar_lat - declination) * (*distance_scale);
    out_lat += proj->ref_dec;
    out_lat /= proj->unit_scale;

    out_long = (*lunar_long - right_ascension) * (*distance_scale);
    out_long += proj->ref_rasc;
    out_long /= proj->unit_scale;

    /* copy the output values to return */
    *lunar_lat = out_lat;
    *lunar_long = out_long;

    return SUCCESS;
}
int ias_ancillary_get_quaternion_at_time
(
    const IAS_ANC_EPHEMERIS_DATA *anc_ephemeris_data,
                                     /* I: Pointer to ephem data */
    IAS_ACQUISITION_TYPE acq_type,   /* I: Image acquisition type */
    IAS_COORDINATE_SYSTEM coordinate_type, /* I: Coordinate system to generate
                                                 values for */
    double delta_time,               /* I: Time from the current record */
    double error_tolerance,          /* I: error tolerance for the conversion */
    double euler[3][3],              /* I/O: Euler rotational matrix */
    IAS_QUATERNION *euler_quat       /* O: Euler matrix in quaternion */
)
{
    IAS_VECTOR pos;                     /* Satellite positional vector */
    IAS_VECTOR vel;                     /* Satellite velocity vector */
    double orbital_trans_matrix[3][3];  /* Orbital transformation matrix */
    double ecef_eci_trans_matrix[3][3]; /* ECEF/ECI transformation matrix */
    double quat_trans_matrix[3][3];     /* Quaternion transformation matrix */
    IAS_QUATERNION converted_quat;      /* Quaternion from Euler matrix */ 

    ias_ancillary_get_position_and_velocity_at_time(anc_ephemeris_data, 
        coordinate_type, delta_time, &pos, &vel);

    /* Get the orbital transformation matrix */
    if (ias_geo_create_transformation_matrix(&pos, &vel, orbital_trans_matrix)
        != SUCCESS)
    {
        IAS_LOG_ERROR("Determining orbital transformation matix");
        return ERROR;
    }

    /* For the ECI coordinate type, update the euler matrix for lunar and
       stellar acquisitions */
    if (coordinate_type == IAS_ECI
        && (acq_type == IAS_LUNAR || acq_type == IAS_STELLAR))
    {
        double temp_matrix[3][3];       

        /* Multiply ECI-to-orb with ACS-to-ECI (euler) to get ACS-to-orb */
        ias_math_multiply_3x3_matrix(orbital_trans_matrix, euler,
                    temp_matrix);
        memcpy(euler, temp_matrix, sizeof(temp_matrix));
    }

    /* Transpose the orbital matrix to get the ECEF/ECI transformation matrix */
    ias_math_transpose_matrix(*orbital_trans_matrix, *ecef_eci_trans_matrix,
                              3, 3);
 
    /* Multiply the ECEF/ECI transformation matrix with the euler rotational 
       matrix to get the quaternion transformation matrix */
    ias_math_multiply_3x3_matrix(ecef_eci_trans_matrix, euler,
                                 quat_trans_matrix);

    if (ias_math_convert_euler_to_quaternion(
            error_tolerance, quat_trans_matrix, &converted_quat) != SUCCESS)
    {
        IAS_LOG_ERROR("Converting euler to quaternion");
        return ERROR;
    }

    /* Convert the ACS to ECEF/ECI quaternion to ECEF/ECI to ACS */
    ias_math_conjugate_quaternion(&converted_quat, euler_quat); 

    return SUCCESS;
}
int ias_misc_read_gcp_residuals
(
    const char *residuals_filename,     /* I: Precision residual file name */
    int iteration_number,               /* I: The iteration number to get */
    IAS_MISC_GCP_RESIDUAL **gcp_res,    /* O: GCP residual information */
    int *number_of_residuals            /* O: # of residuals read from file */
)
{
    FILE *res_fptr;                 /* File pointer for residual file */
    int iteration;                  /* The iteration read from the file */
    int residuals_count = 0;        /* Count of the residuals read */
    int band_number = 0;            /* Band number read from file */
    int band_flag;                  /* Flag if a band number was read from the
                                       file: 1 band read, 0 band not read. */
    char keyword[GCP_RECLEN];       /* Key word when searching file */
    char buf1[GCP_RECLEN];          /* Buffer for 1st word when getting band */
    char buf2[GCP_RECLEN];          /* Buffer for 2nd word when getting band */
    char buffer[GCP_RECLEN];        /* Buffer for data */
    IAS_MISC_GCP_RESIDUAL tmp_res;  /* Temporary GCP residual info */
    int param_num;                  /* Number of parameters per line to read */
    int num_scanned;

    /* Initialize */
    *gcp_res = NULL;
    *number_of_residuals = 0;

    /* Open the file */
    res_fptr = fopen(residuals_filename, "r");
    if (!res_fptr)
    {
        IAS_LOG_ERROR("Opening residuals file: %s", residuals_filename);
        return ERROR;
    }

    /* Find the beginning of the specific group. Check if specific number or
       final iteration was specified. */
    band_flag = 0;
    for(;;)
    {
        if (fgets(buffer, GCP_RECLEN, res_fptr) == NULL)
        {
            IAS_LOG_ERROR("Reading from file: %s", residuals_filename);
            fclose(res_fptr);
            return ERROR;
        }

        /* Get the first word and see if it's a band */
        sscanf(buffer,"%s",keyword);
        if ((strcmp(keyword, "Band") == 0) && !band_flag)
        {
            sscanf(buffer, "%s%s%d", buf1, buf2, &band_number);
            band_flag = 1;
        }
        else if (!strcmp(keyword, "Iteration") || !strcmp (keyword, "Final"))
        {
            sscanf(buffer, "%s %d", keyword, &iteration);
            if (((strcmp(keyword,"Iteration") == 0)
                        && (iteration == iteration_number))
                    || ((strcmp(keyword,"Final") == 0)
                        && (iteration_number < 0)))
            {
                break;
            }
        }
    }

    if (!band_flag)
    {
        IAS_LOG_ERROR("Band number not found in: %s", residuals_filename);
        fclose(res_fptr);
        return ERROR;
    }

    /* Loop over all GCPs to read the residual information. Break out
       of the for loop when no data is found. */
    param_num = 14;
    for(;;)
    {
        /* If all parameters aren't read break out of the loop */
        num_scanned = fscanf(res_fptr,
                "%s%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d%s\n",
                tmp_res.point_id, &tmp_res.predicted_line,
                &tmp_res.predicted_sample, &tmp_res.seconds_from_epoch,
                &tmp_res.latitude, &tmp_res.longitude, &tmp_res.height,
                &tmp_res.across_track_angle, &tmp_res.along_track_residual,
                &tmp_res.across_track_residual, &tmp_res.residual_y,
                &tmp_res.residual_x, &tmp_res.outlier_flag, tmp_res.gcp_source);
        if (num_scanned != param_num)
            break;
        else if (!strcmp (tmp_res.point_id, "Iteration")
                || !strcmp (tmp_res.point_id, "Final"))
        {
            break;
        }
        else
        {
            *gcp_res= (IAS_MISC_GCP_RESIDUAL *)realloc(*gcp_res,
                    (residuals_count + 1) * sizeof(IAS_MISC_GCP_RESIDUAL));
            if (*gcp_res == NULL)
            {
                IAS_LOG_ERROR("Allocating memory for %d residuals",
                        residuals_count + 1);
                free(*gcp_res);
                *gcp_res = NULL;
                fclose(res_fptr);
                return ERROR;
            }

            /* Assign the band number to the structure */
            tmp_res.band_number = band_number;

            /* Copy the temporary structure to the actual structure */
            memcpy((void *)&((*gcp_res)[residuals_count]), (void *)&tmp_res,
                    sizeof(tmp_res));
        }
        residuals_count++;
    }

    *number_of_residuals = residuals_count;

    if (fclose(res_fptr) != 0)
        IAS_LOG_WARNING("Closing residuals file: %s", residuals_filename);

    return SUCCESS;
}
int ias_grid_compute_rough_polynomial
( 
   IAS_GRID_BAND_TYPE *grid_band_ptr  /* I/O: Grid band to fill in */
)
{
    double *b;           /* Array for least squares */
    double *v;           /* Array for least squares */
    double *A;           /* Array for least squares */
    int i,n,m;           /* Counters */
    int numpts;          /* Number of points used in least squares */
    int cnt;             /* Counter for least squares */
    int ncoeff;          /* Number of coefficients used in least squares */
    int ncols;           /* Number of columns */
    int nrows;           /* Number of rows */
    int pdegree;         /* Degree of the rough transformatioanal coeffs */
    int qflag = 0;       /* flag for c_qrdecomp do not order A by columns */
    int zplane;          /* elevation plane loop counter */
    int nzplanes;        /* number of elevation planes */

    /* extract some information from the grid */
    pdegree = grid_band_ptr->degree;
    ncoeff = (pdegree + 1) * (pdegree + 1);
    nrows = grid_band_ptr->ngrid_lines;
    ncols = grid_band_ptr->ngrid_samps;
    nzplanes = grid_band_ptr->nzplanes;
    numpts = ncols * nrows;

    /* allocate buffer space for least squares routine */
    A = (double *)malloc(sizeof(double) * numpts * ncoeff);
    if (A == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }
    b = (double *)malloc(sizeof(double) * numpts);
    if (b == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        free(A);
        return ERROR;
    }
    v = (double *)malloc(sizeof(double) * numpts);
    if (v == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        free(A);
        free(b);
        return ERROR;
    }

    /* loop over the planes present in the grid to calculate the rough 
       polynomial for each elevation plane */
    for (zplane = 0; zplane < nzplanes; zplane++)
    {
        int zoffset = zplane * numpts;

        double *osamps = &grid_band_ptr->out_samps[zoffset];
        double *olines = &grid_band_ptr->out_lines[zoffset];
        int *ilines = grid_band_ptr->in_lines;
        int *isamps = grid_band_ptr->in_samps;

        /* Fill least squares routine according to degree of polynomial.  This
           mapping is from output image lines/samples (all lines/samples in
           grid output space) to input image lines/samples (all lines/samples
           in grid input space). */
        cnt = 0;
        for (i = 0; i <= pdegree; i++)
        {
            for (m = 0; m <= pdegree; m++)
            {
                for (n = 0; n < numpts; n++)
                {
                    double samp = osamps[n];
                    double line = olines[n];
                    double x = pow(samp,m);
                    double y = pow(line,i);
                    A[cnt] = x*y;
                    cnt++;
                }
            }
        }

        /* Perform least squares routine.  First the coefficients for output
           lines to input lines are found and stored.  Next the coefficients
           for output samples to input samples are found and stored. */

        ias_math_matrix_QRfactorization(A, numpts, ncoeff, v, qflag);

        for (i = 0, m = 0; m < nrows; m++)
            for (n = 0; n < ncols; n++, i++)
                b[i] = ilines[m];

        ias_math_matrix_QRsolve(A, numpts, ncoeff, v, b, 0);

        for (n = 0; n < ncoeff; n++)
            grid_band_ptr->poly_lines[n+zplane*ncoeff] = b[n];

        for (i = 0, m = 0; m < nrows; m++)
            for (n = 0; n < ncols; n++, i++)
                b[i] = isamps[n];

        ias_math_matrix_QRsolve(A, numpts, ncoeff, v, b, 0);

        for (n = 0; n < ncoeff; n++)
            grid_band_ptr->poly_samps[n+zplane*ncoeff] = b[n];
    }

    free(A);
    free(b);
    free(v);

    return SUCCESS;
}
int ias_geo_find_target_position
(
    const IAS_VECTOR *satpos, /* I: Satellite location vector */
    const IAS_VECTOR *losv,   /* I: Line of sight to target vector */
    const IAS_EARTH_CHARACTERISTICS *earth, /* I: Earth parameters */
    double tarelev,         /* I: Elevation of target above the ellipsoid */
    IAS_VECTOR *tarvec,     /* O: Target vector */
    double *tarlat,         /* O: Target latitude */
    double *tarlong,        /* O: Target longitude */
    double *tarrad          /* O: Radius of the target */
)
{
    IAS_VECTOR u;          /* Scaled LOS vector */
    IAS_VECTOR p;          /* Scaled satellite position vector */
    IAS_VECTOR n;          /* Unit vector normal to surface */
    double up;             /* u dot p vector */
    double pp;             /* p dot p vector (satpos vector length squared) */
    double uu;             /* u dot u vector (LOS vector length squared) */   
    double d;              /* LOS adjustment */
    double q;              /* u dot n vector */
    double dh;             /* Delta height */
    double height;         /* Height of target */
    double tarlatd;        /* Target geodetic latitude */
    double dist_x,dist_y,dist_z;
    double temp;
    int iter;              /* Iteration */

    u.x = losv->x / earth->semi_major_axis;
    u.y = losv->y / earth->semi_major_axis;
    u.z = losv->z / earth->semi_minor_axis;

    p.x = satpos->x / earth->semi_major_axis;
    p.y = satpos->y / earth->semi_major_axis;
    p.z = satpos->z / earth->semi_minor_axis;

    up = ias_math_compute_3dvec_dot(&u,&p);

    /* Get the squared length of the LOS and satellite position vectors. */
    uu = ias_math_compute_3dvec_dot(&u,&u);
    pp = ias_math_compute_3dvec_dot(&p,&p);

    /* if the sensor is not viewing the earth, return an error.  The sensor is
       not viewing the earth if up is > 0 (viewing within +/- 90 degrees of
       directly away from the earth) or temp < 0 (in the direction of the earth
       but off to one side or the other for a stellar collect) */
    temp = up * up - (uu * (pp - 1.0));
    if ((up > 0.0) || (temp < 0.0))
    {
        IAS_LOG_ERROR("LOS does not target Earth");
        return ERROR;
    }

    /* Solve for distance of LOS vector to target */
    d = (-up - sqrt(up * up - (uu * (pp - 1.0)))) / uu;

    /* Find target vector for current estimate */
    tarvec->x = (p.x + d * u.x) * earth->semi_major_axis;
    tarvec->y = (p.y + d * u.y) * earth->semi_major_axis;
    tarvec->z = (p.z + d * u.z) * earth->semi_minor_axis;

    if (ias_geo_convert_cart2sph(tarvec, tarlat, tarlong, tarrad) != SUCCESS)
    {
        IAS_LOG_ERROR("Failed to convert cartesian coordinates to spherical");
        return ERROR;
    }

    /* If not looking for Earth ellipsoid answer */
    if (tarelev != 0.0)
    {
        p.x = satpos->x;
        p.y = satpos->y;
        p.z = satpos->z;

        u.x = losv->x;
        u.y = losv->y;
        u.z = losv->z;

        iter = 0;

        if (ias_geo_convert_geocentric_height_to_geodetic(*tarlat, *tarrad, 
                    earth, &tarlatd, &height) != SUCCESS)
        {
            IAS_LOG_ERROR("In targeting");
            return ERROR;
        }

        for(;;)
        {
            dh = height - tarelev;
            if (fabs(dh) < CONV_TOLERANCE)
                break;

            dist_x = tarvec->x - p.x;
            dist_y = tarvec->y - p.y;
            dist_z = tarvec->z - p.z;
            d = sqrt(dist_x * dist_x + dist_y * dist_y + dist_z * dist_z);

            /* Calculate normal to surface */
            n.x = cos(tarlatd) * cos(*tarlong);
            n.y = cos(tarlatd) * sin(*tarlong);
            n.z = sin(tarlatd);

            q = -ias_math_compute_3dvec_dot(&u,&n);

            /* Update length of LOS vector */
            d = d + dh / q;

            /* Re-adjust target vector based on new distance */
            tarvec->x = p.x + d * u.x;
            tarvec->y = p.y + d * u.y;
            tarvec->z = p.z + d * u.z;

            if (ias_geo_convert_cart2sph(tarvec, tarlat, tarlong, tarrad)
                != SUCCESS)
            {
                IAS_LOG_ERROR("Failed to convert cartesian coordinates to "
                    "spherical");
                return ERROR;
            }

            if (ias_geo_convert_geocentric_height_to_geodetic(*tarlat, *tarrad,
                    earth, &tarlatd, &height) != SUCCESS)
            {
                IAS_LOG_ERROR("Failed to convert geocentric height to "
                              "geodetic");
                return ERROR;
            }
            if (iter > NUM_ITERATIONS)
            {
                IAS_LOG_ERROR("Exceeded %d iterations without converging to a "
                              "solution", NUM_ITERATIONS);
                IAS_LOG_DEBUG("Height %f tarelev %f", height, tarelev);
                return ERROR;
            }
            iter++;
        }
    }

    return SUCCESS;
}
int ias_l1r_write_image
(
    const L1R_BAND_IO *l1r_band, /* I: HDF IO band structure */
    int sca_index,               /* I: SCA to write to (0-rel) */
    int line_start,              /* I: Line to start writing (0-rel) */
    int start_sample,            /* I: Sample to start writing (0-rel) */
    int lines,                   /* I: Number of lines to write */
    int samples,                 /* I: Number of samples to write */
    void *data                   /* I: Data buffer */
)
{
    hid_t data_space;       /* dataspace for the data buffer dimensions */
    hsize_t data_dims[2] = {lines, samples}; /* size of the data buffer */
    hsize_t file_size[3] = {1, lines, samples}; /* slab size */
    /* hyperslab start location */
    hsize_t file_offset[3] = {sca_index, line_start, start_sample}; 
    int status;

    /* check for various errors in the input */
    if (l1r_band == NULL)
    {
        IAS_LOG_ERROR("NULL band pointer passed in");
        return ERROR;
    }

    if (l1r_band->id < 0)
    {
        IAS_LOG_ERROR("Band is not open for writing in file %s",
                      l1r_band->l1r_file->filename);
        return ERROR;
    }

    /* verify the window of data being written actually falls within the band */
    if ((sca_index < 0) || (sca_index >= l1r_band->scas)
        || (line_start < 0) || ((line_start + lines) > l1r_band->lines)
        || (start_sample < 0) || ((start_sample + samples) > l1r_band->samples))
    {
        IAS_LOG_ERROR("Attempted to write imagery to SCA index %d, band number "
            "%d of %s at line %d, sample %d for a window %d lines x %d samples "
            "when the band has %d SCAs, %d lines, %d samples", sca_index,
            l1r_band->number, l1r_band->l1r_file->filename, line_start, 
            start_sample, lines, samples, l1r_band->scas, l1r_band->lines, 
            l1r_band->samples);
        return ERROR;
    }

    /* verify the access mode allows writing */
    if (l1r_band->l1r_file->access_mode == IAS_READ)
    {
        IAS_LOG_ERROR("Attempted write to file %s opened in read mode",
                      l1r_band->l1r_file->filename);
        return ERROR;
    }

    /* define the memory dataspace for the buffer that will be written */
    data_space = H5Screate_simple(2, data_dims, NULL);
    if (data_space < 0)
    {
        IAS_LOG_ERROR("Creating memory dataspace");
        return ERROR;
    }

    status = H5Sselect_hyperslab(l1r_band->dataspace_id, H5S_SELECT_SET, 
                file_offset, NULL, file_size, NULL);
    if (status < 0)
    {
        IAS_LOG_ERROR("Selecting hyperslab for file %s, band %d",
                      l1r_band->l1r_file->filename, l1r_band->number);
        H5Sclose(data_space);
        return ERROR;
    }

    /* write the data to the dataset */
    status = H5Dwrite(l1r_band->id, l1r_band->memory_data_type,
                      data_space, l1r_band->dataspace_id, H5P_DEFAULT, data);
    H5Sclose(data_space);
    if (status < 0)
    {
        IAS_LOG_ERROR("Writing to file %s, band number %d, SCA index %d, line "
            "%d, sample %d, number of lines %d, number of samples %d",
            l1r_band->l1r_file->filename, l1r_band->number, sca_index, 
            line_start, start_sample, lines, samples);
        return ERROR;
    }

    return SUCCESS;
}
int ias_geo_compute_proj2proj_poly 
(
    const double source_proj_x[4], /*I: source projection x corner coordinates*/
    const double source_proj_y[4], /*I: source projection y corner coordinates*/
    const double target_proj_x[4], /*I: target projection x corner coordinates*/
    const double target_proj_y[4], /*I: target projection y corner coordinates*/
    int include_xy_term,           /*I: include x*y term if not zero */
    double poly_x[4],              /*O: calculated x polynomial coefficients */
    double poly_y[4]               /*O: calculated y polynomial coefficients */
)
{
    int i;                      /* Loop variable */
    int stat;                   /* Return status */
    double A[16];               /* Matrix used by QRsolve for LSQ fit */
    double v[4];                /* Vector used in above operation */
    int terms = 3;              /* number of terms in solution (set to 4 if the
                                   xy term is included in the solution */

    /* Calculate the first order polynomial coefficients which map output
     * line/samples to output projection coordinates.  The 4x3 matrix is
     * populated as shown below, QR-factorized and then Ax = b is solved
     * for two different vectors b; one for samples, the other for lines. */
    for (i = 0; i < 4; i++)
    {
        A[i] = 1.0;
    }
    A[4]  = source_proj_x[0];
    A[5]  = source_proj_x[1];    /*     +-                    -+ */
    A[6]  = source_proj_x[2];    /*     |  1  X0   Y0   X0*Y0  | */
    A[7]  = source_proj_x[3];    /* A = |  1  X1   Y1   X1*Y1  | */
    A[8]  = source_proj_y[0];    /*     |  1  X2   Y2   X2*Y2  | */
    A[9]  = source_proj_y[1];    /*     |  1  X3   Y3   X3*Y3  | */
    A[10] = source_proj_y[2];    /*     +-                    -+ */
    A[11] = source_proj_y[3];
    if (include_xy_term)
    {
        A[12] = A[4] * A[8];
        A[13] = A[5] * A[9];
        A[14] = A[6] * A[10];
        A[15] = A[7] * A[11];
        terms = 4;
    }

    stat = ias_math_matrix_QRfactorization(A, 4, terms, v, 0); 
                                            /* Obtain QR factorization */
    if (stat != SUCCESS)
    {
        IAS_LOG_ERROR ("Error performing QR factorization");
        return ERROR;
    }

    /* Vector one: Find the coefficients to transform output coordinates
       in projection space to output samples.  Note that the call
       to ias_math_matrix_QRsolve() modifies the values at poly_x for part of
       the output (this vector becomes the before-mentioned coefficients.) */
    poly_x[0] = target_proj_x[0];
    poly_x[1] = target_proj_x[1];
    poly_x[2] = target_proj_x[2];
    poly_x[3] = target_proj_x[3];
    stat = ias_math_matrix_QRsolve(A, 4, terms, v, poly_x, 0);
    if (stat != SUCCESS)
    {
        IAS_LOG_ERROR ("Error solving for X polynomial");
        return ERROR;
    }

    /* Vector two: Find the coefficients to transform output
     * coordinates in projection space to output lines. */
    poly_y[0] = target_proj_y[0];
    poly_y[1] = target_proj_y[1];
    poly_y[2] = target_proj_y[2];
    poly_y[3] = target_proj_y[3];
    stat = ias_math_matrix_QRsolve(A, 4, terms, v, poly_y, 0);
    if (stat != SUCCESS)
    {
        IAS_LOG_ERROR ("Error solving for Y polynomial");
        return ERROR;
    }

    return SUCCESS;
}
int ias_cpf_parse_tirs_det_offsets
(
    const IAS_CPF *cpf,                     /* I: CPF structure */
    struct IAS_CPF_DETECTOR_OFFSETS *detector_offsets 
                                            /* O: CPF tirs det offsets data */
)
{
    int nbands;                    /* total number bands */
    int band_index;                /* band loop var */
    int band_number;               /* Actual band number */
    int band_list[IAS_MAX_NBANDS]; /* list of band numbers */
    int status;                    /* Function return value */
    int nscas;                     /* total number scas */
    int sca_index;                 /* SCA loop var */
    int ndet;                      /* band detector count */
    int normal_band_index = -1;    /* normal band number converted to index */
    int count = 0;                 /* number of list buckets */

    char group_name[] = "TIRS_DETECTOR_OFFSETS"; /* group to retrieve */

    IAS_OBJ_DESC *odl_tree;        /* ODL tree */

    /* get sca count */
    nscas = ias_sat_attr_get_sensor_sca_count(IAS_TIRS);
    if (nscas == ERROR)
    {
        IAS_LOG_ERROR("Getting sat sca count");
        return ERROR;
    }

    /* get tirs band count */
    status = ias_sat_attr_get_sensor_band_numbers(IAS_TIRS, IAS_NORMAL_BAND, 
                                                  0, band_list, IAS_MAX_NBANDS, 
                                                  &nbands);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Getting band attributes");
        return ERROR;
    }

    /* Full attribute name with band and sca names */
    char attribute[nbands * nscas * NUMBER_ATTRIBUTES][ATTRIB_STRLEN];

    ODL_LIST_TYPE list[nbands * nscas * NUMBER_ATTRIBUTES];

    /* set the pointers to null, lets be safe and do them all */
    for (band_index = 0; band_index < nbands; band_index++)
    {
        /* get the index of the normal band number */
        normal_band_index 
            = ias_sat_attr_convert_band_number_to_index(band_list[band_index]);
        if (normal_band_index == ERROR)
        {
            IAS_LOG_ERROR("Converting the band number to an index");
            return ERROR;
        }

        for (sca_index = 0; sca_index < nscas; sca_index++)
        {
            detector_offsets
                ->along_per_detector[normal_band_index][sca_index] = NULL;
            detector_offsets
                ->across_per_detector[normal_band_index][sca_index] = NULL;
        }
    }

    /* get the tirs offsets */
    for (band_index = 0; band_index < nbands; band_index++)
    {
        /* get band number from band list */
        band_number = band_list[band_index]; 

        /* get the index of the normal band number */
        normal_band_index 
            = ias_sat_attr_convert_band_number_to_index(band_number);
        if (normal_band_index == ERROR)
        {
            IAS_LOG_ERROR("Converting the band number to an index");
            return ERROR;
        }

        /* get number of scas this band */
        nscas = ias_sat_attr_get_scas_per_band(band_number);
        if (nscas == ERROR)
        {
            IAS_LOG_ERROR("Getting sca count for band number: %d", 
                 band_number);
            free_det_offset_memory(nbands, nscas, band_list, detector_offsets);
            return ERROR;
        }

        /* get detector count of current band */
        ndet = ias_sat_attr_get_detectors_per_sca(band_number);
        if (ndet == ERROR)
        {
            IAS_LOG_ERROR("Getting detector count for  band number: %d", 
                           band_number);
            free_det_offset_memory(nbands, nscas, band_list, detector_offsets);
            return ERROR;
        }

        /* Loop through the scas */
        for (sca_index = 0; sca_index < nscas; sca_index++)
        {
            /* Add the band and sca information to the CPF attribute name */
            status = snprintf(attribute[count], sizeof(attribute[count]), 
                     "Along_Detector_Offsets_B%02d_SCA%02d",
                     band_number, sca_index + 1);
            if (status < 0 || status >= sizeof(attribute[count]))
            { 
                IAS_LOG_ERROR("Creating Along_Detector_Offsets attribute "
                              "string");
                free_det_offset_memory(nbands, nscas, band_list, 
                                       detector_offsets);
                return ERROR;
            }

            /* allocate space for Along Det Offsets in CPF */
            detector_offsets->along_per_detector[normal_band_index][sca_index] 
                                            = malloc(ndet * sizeof(double));
            if (detector_offsets
                ->along_per_detector[normal_band_index][sca_index] == NULL)
            {
                IAS_LOG_ERROR("Allocating memory tirs along det offsets "
                              "group: %s", group_name);
                free_det_offset_memory(nbands, nscas, band_list, 
                                       detector_offsets);
                return ERROR;
            }

            /* populate list with oli along det delay info */
            list[count].group_name = group_name;
            list[count].attribute = attribute[count];
            list[count].parm_ptr= detector_offsets
                ->along_per_detector[normal_band_index][sca_index];
            list[count].parm_size = ndet * sizeof(double);
            list[count].parm_type = IAS_ODL_Double;
            list[count].parm_count = ndet;
            count++;
         
            /* Add the band and sca information to the CPF attribute name */
            status = snprintf(attribute[count], sizeof(attribute[count]), 
                    "Across_Detector_Offsets_B%02d_SCA%02d",
                    band_number, sca_index + 1);
            if (status < 0 || status >= sizeof(attribute[count]))
            { 
                IAS_LOG_ERROR("Creating Across_Detector_Offsets attribute "
                              "string");
                free_det_offset_memory(nbands, nscas, band_list, 
                                       detector_offsets);
                return ERROR;
            }

            /* allocate space for Across Odd Det Offsets in CPF */
            detector_offsets->across_per_detector[normal_band_index][sca_index] 
                                            = malloc(ndet * sizeof(double));
            if (detector_offsets
                ->across_per_detector[normal_band_index][sca_index] == NULL)
            {
                IAS_LOG_ERROR("Allocating memory Across det offsets "
                              "group: %s", group_name);
                free_det_offset_memory(nbands, nscas, band_list, 
                                       detector_offsets);
                return ERROR;
            }

            /* populate list with tirs along det offset info */ 
            list[count].group_name = group_name;
            list[count].attribute = attribute[count];
            list[count].parm_ptr= detector_offsets
                    ->across_per_detector[normal_band_index][sca_index];
            list[count].parm_size = ndet * sizeof(double);
            list[count].parm_type = IAS_ODL_Double;
            list[count].parm_count = ndet;
            count++;
         }
    }

    /* make a sanity check of number of parameters to retrieve */
    if ((nbands * nscas * NUMBER_ATTRIBUTES) != count)
    {
        IAS_LOG_ERROR("Number of parameters does not match number to retrieve");
        return ERROR;
    }

    GET_GROUP_FROM_CACHE(cpf, group_name, odl_tree);

    /* Populate the list from the odl tree */
    status = ias_odl_get_field_list(odl_tree, list, count);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Getting group: %s from CPF", group_name);
        DROP_ODL_TREE(odl_tree);
        free_det_offset_memory(nbands, nscas, band_list, detector_offsets);
        return ERROR;
    }

    DROP_ODL_TREE(odl_tree);

    return SUCCESS;
}
/*----------------------------------------------------------------------
 NAME:                      read_ephemeris_data

 PURPOSE:  Reads ephemeris data from HDF5-formatted tables in the
           ancillary data file

 RETURNS:  Pointer to an allocated/populated IAS_ANC_EPHEMERIS_DATA
           structure if successful, NULL pointer if allocation fails
           or data read fails

------------------------------------------------------------------------*/
static IAS_ANC_EPHEMERIS_DATA *read_ephemeris_data
(
    hid_t hdf_file_id,              /* I: HDF5 ancillary data file handle */
    int file_format_version         /* I: current file format version */
)
{
    const char *field_names[EPHEMERIS_NFIELDS];

    double epoch_time[3] = {0.0, 0.0, 0.0};
                                    /* Temporary buffer for epoch time data */

    int status = SUCCESS;           /* Function return status code  */

    size_t field_offsets[EPHEMERIS_NFIELDS];
    size_t field_sizes[EPHEMERIS_NFIELDS];

    herr_t hdf_error_status = -1;   /* HDF5 I/O error status   */

    hid_t field_types[EPHEMERIS_NFIELDS];
    hid_t fields_to_close[EPHEMERIS_NFIELDS];

    hsize_t nfields = 0;            /* Number of table fields per record */
    hsize_t nrecords = 0;           /* Number of records in table */
    hsize_t type_size = 0;          /* Size of data structure to read */

    IAS_ANC_EPHEMERIS_DATA *data = NULL;
                                    /* Pointer to attitude data buffer */


    /* Read the attitude epoch time from the ancillary data file. */
    status = read_epoch_time(hdf_file_id,
        EPHEMERIS_EPOCH_TIME_ATTRIBUTE_NAME, epoch_time);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Reading ephemeris epoch time attribute");
        return NULL;
    }

    /* Build the ephemeris table definition. */
    status = ias_ancillary_build_ephemeris_table_definition(field_names,
        field_offsets, field_types, field_sizes, fields_to_close);  
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Building ephemeris table definition");
        return NULL;
    }

    /* Get the number of records in the ephemeris data table.  We need
       this before we can allocate the proper-sized IAS_ANC_EPHEMERIS_DATA
       buffer.   If the table doesn't exist or there's a table defined
       with 0 records, consider it an error. */
    hdf_error_status = H5TBget_table_info(hdf_file_id,
        EPHEMERIS_DATA_DATASET_NAME, &nfields, &nrecords);
    if (hdf_error_status < 0)
    {
        IAS_LOG_ERROR("Obtaining ephemeris table information");
        ias_ancillary_cleanup_table_definition(fields_to_close,
            EPHEMERIS_NFIELDS);
        return NULL;
    }
    else if (nrecords < 1)
    {
        IAS_LOG_ERROR("No records found in ephemeris data table");
        ias_ancillary_cleanup_table_definition(fields_to_close,
            EPHEMERIS_NFIELDS);
        return NULL;
    }

    /* Allocate the ephemeris data buffer. */
    data = ias_ancillary_allocate_ephemeris(nrecords);
    if (data == NULL)
    {
        IAS_LOG_ERROR("Allocating ephemeris data buffer");
        ias_ancillary_cleanup_table_definition(fields_to_close,
            EPHEMERIS_NFIELDS);
        return NULL;
    }
    else
    {
        /* Copy the attitude epoch time info to the data structure. */
        memcpy(data->utc_epoch_time, epoch_time, sizeof(epoch_time));

        /* Read the table contents into the data structure. */
        type_size = sizeof(data->records);
        hdf_error_status = H5TBread_table(hdf_file_id,
            EPHEMERIS_DATA_DATASET_NAME, type_size,
            field_offsets, field_sizes, data->records);
        if (hdf_error_status < 0)
        {
            IAS_LOG_ERROR("Reading ephemeris data table");
            ias_ancillary_free_ephemeris(data);
            ias_ancillary_cleanup_table_definition(fields_to_close,
                EPHEMERIS_NFIELDS);
            return NULL;
        }
    }

    /* Close any "open" datatype field objects. */
    ias_ancillary_cleanup_table_definition(fields_to_close,
        EPHEMERIS_NFIELDS);

    /* Done */
    return data;
}
IAS_RLUT_LINEARIZATION_PARAMS *ias_rlut_read_linearization_params
(
    const IAS_RLUT_IO *rlut,         /* I: Open RLUT file */
    int band_number,                 /* I: Current RLUT band number */
    int sca_number,                  /* I: Current SCA number */
    int num_detectors                /* I: Number of detectors in the
                                        current band/SCA */
)
{
    IAS_RLUT_LINEARIZATION_PARAMS *linearization_params = NULL;
                                               /* Pointer to an array of
                                                  data structures containing
                                                  the linearization
                                                  parameters for all detectors
                                                  in the current band/SCA */
    char bandsca_parameter_name[IAS_RLUT_BANDSCA_GROUP_NAME_LENGTH + 1];
                                               /* Linearization parameter
                                                  group name for the current
                                                  band/SCA */
    const char *field_names[IAS_RLUT_PARAM_NFIELDS];
                                               /* Name of each linearization
                                                  parameter */
    size_t offsets[IAS_RLUT_PARAM_NFIELDS];    /* Data offsets in
                                                  LINEARIZATION_PARAMS
                                                  data structure for each
                                                  field*/
    size_t field_sizes[IAS_RLUT_PARAM_NFIELDS];/* Size of each field */
    hid_t field_types[IAS_RLUT_PARAM_NFIELDS]; /* Data type for each field */
    hid_t fields_to_close[IAS_RLUT_PARAM_NFIELDS];
                                               /* Flags indicating open
                                                  fields needing to be
                                                  closed */
    hid_t linearization_param_group_id;        /* Root
                                                  LINEARIZATION_PARAMETERS
                                                  group */
    hid_t bandsca_group_id;                    /* SCA group handle */
    hsize_t nfields = 0;                       /* Number of fields in the
                                                  table description */
    hsize_t nrecords = 0;                      /* Number of records in the
                                                  table description (should
                                                  equal the number of
                                                  detectors) */
    hsize_t type_size;                         /* Size of entire data
                                                  structure to be read into */
    herr_t hdf_status;                         /* HDF5 error status flag */
    int status;                                /* IAS status */


    /* Make sure the RLUT file is actually open */
    if ((rlut == NULL) || (rlut->file_id < 0))
    {
        IAS_LOG_ERROR("NULL pointer to IAS_RLUT_IO data block, or no RLUT "
            "file has been opened");
        return NULL; 
    }

    /* Construct the group name for the current band/SCA */
    status = snprintf(bandsca_parameter_name, sizeof(bandsca_parameter_name),
        "%s/Band%02d_SCA%02d", LINEARIZATION_PARAMS_GROUP_NAME, band_number,
        sca_number);
    if ((status < 0) || (status >= sizeof(bandsca_parameter_name)))
    {
        IAS_LOG_ERROR("Creating group name for band %d SCA %d "
            "linearization parameters", band_number, sca_number);
        return NULL;
    }

    /* Open the root group*/
    linearization_param_group_id = H5Gopen(rlut->file_id,
        LINEARIZATION_PARAMS_GROUP_NAME, H5P_DEFAULT);
    if (linearization_param_group_id < 0)
    {
        IAS_LOG_ERROR("Opening root linearization parameters group");
        return NULL;
    }

    /* Try to open the group for the current band/SCA */
    bandsca_group_id = H5Gopen(linearization_param_group_id,
        bandsca_parameter_name, H5P_DEFAULT);
    if (bandsca_group_id < 0)
    {
        IAS_LOG_ERROR("Opening band %d SCA %d linearization parameter group",
            band_number, sca_number);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }

    /* Build the table definition */
    status = ias_rlut_build_linearization_params_table_description(offsets,
        field_names, field_types, fields_to_close, field_sizes);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Building linearization parameter table description");
        H5Gclose(bandsca_group_id);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }

    /* Get the number of fields and records */
    hdf_status = H5TBget_table_info(bandsca_group_id,
        LINEARIZATION_PARAMS_DATASET_NAME, &nfields, &nrecords);
    if (hdf_status < 0)
    {
        IAS_LOG_ERROR("Getting parameter table information for band %d SCA "
            "%d", band_number, sca_number);
        ias_rlut_cleanup_table_description(fields_to_close,
            IAS_RLUT_PARAM_NFIELDS);
        H5Gclose(bandsca_group_id);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }
    else if (nfields != IAS_RLUT_PARAM_NFIELDS)
    {
        IAS_LOG_ERROR("Number of defined fields %d not equal to number of "
            "returned fields %d", IAS_RLUT_PARAM_NFIELDS, (int)nfields);
        ias_rlut_cleanup_table_description(fields_to_close,
            IAS_RLUT_PARAM_NFIELDS);
        H5Gclose(bandsca_group_id);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }
    else if (nrecords != num_detectors)
    {
        IAS_LOG_ERROR("Band %d SCA %d parameter table should have %d "
            "records, found %d records instead", band_number, sca_number,
            num_detectors, (int)nrecords);
        ias_rlut_cleanup_table_description(fields_to_close,
            IAS_RLUT_PARAM_NFIELDS);
        H5Gclose(bandsca_group_id);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }

    /* Allocate the parameter data buffer for the current band/SCA */
    linearization_params = malloc(
        num_detectors * sizeof(IAS_RLUT_LINEARIZATION_PARAMS));
    if (linearization_params == NULL)
    {
        IAS_LOG_ERROR("Allocating linearization parameter data buffer for "
            "band %d SCA %d", band_number, sca_number);
        ias_rlut_cleanup_table_description(fields_to_close,
            IAS_RLUT_PARAM_NFIELDS);
        H5Gclose(bandsca_group_id);
        H5Gclose(linearization_param_group_id);
        return NULL;
    }

    /* Read the parameter set for the current band/SCA */
    type_size = sizeof(*linearization_params);
    hdf_status = H5TBread_table(bandsca_group_id,
        LINEARIZATION_PARAMS_DATASET_NAME, type_size, offsets,
        field_sizes, linearization_params);

    /* Cleanup the table description */
    ias_rlut_cleanup_table_description(fields_to_close,
        IAS_RLUT_PARAM_NFIELDS);

    /* Check the return status from the read */
    if (hdf_status < 0)
    {
        IAS_LOG_ERROR("Reading parameters for band %d SCA %d", band_number,
            sca_number);
        free(linearization_params);
        linearization_params = NULL;
    }

    /* Close the group for the current band/SCA */
    hdf_status = H5Gclose(bandsca_group_id);
    if (hdf_status < 0)
    {
        IAS_LOG_ERROR("Closing band %d SCA %d linearization parameter group",
            band_number, sca_number);
        if (linearization_params)
        {
            free(linearization_params);
            linearization_params = NULL;
        }
    }

    /* Close the main linearization parameter group */
    hdf_status = H5Gclose(linearization_param_group_id);
    if (hdf_status < 0)
    {
        IAS_LOG_ERROR("Closing root linearization parameters group");
        if (linearization_params)
        {
            free(linearization_params);
            linearization_params = NULL;
        }
    }

    return linearization_params;
}   /* END ias_rlut_read_linearization_params */
/*----------------------------------------------------------------------
 NAME:                     read_epoch_time

 PURPOSE:  Reads the requested epoch time attribute from the ancillary
           data file

 RETURNS:  Integer status code of SUCCESS or ERROR

-------------------------------------------------------------------------*/
static int read_epoch_time
(
    hid_t hdf_file_id,       /* I:  Open ancillary data file handle */
    const char *epoch_name,  /* I:  Epoch time name  */
    double epoch_time[3]     /* O:  Array containing epoch time components */
)
{
    herr_t hdf_error_status = -1;    /* HDF5 error status code    */

    hsize_t epoch_dims = 0;          /* Epoch array dimension */

    H5T_class_t type_class;          /* Type class identifier */

    size_t type_size = 0;            /* Size of datatype in bytes */

    int rank = 0;                    /* Number of dimensions for the
                                        epoch time attribute   */


    /* Verify the epoch time array attribute has an array dimension of 1 */
    hdf_error_status = H5LTget_attribute_ndims(hdf_file_id, "/",
        epoch_name, &rank);
    if (hdf_error_status < 0)
    {
        IAS_LOG_ERROR("Retrieving epoch time attribute dimensions");
        return ERROR;
    }
    if (rank != 1)      /* Should only be 1D array */
    {
        IAS_LOG_ERROR("Invalid rank %d detected for epoch time array, "
            "should be 1", rank);
        return ERROR;
    }

    /* The epoch time attribute should be a 1D, 3-element array of datatype
       double */
    hdf_error_status = H5LTget_attribute_info(hdf_file_id, "/",
        epoch_name, &epoch_dims, &type_class, &type_size);
    if ((hdf_error_status < 0) || (epoch_dims != 3)
            || (type_class != H5T_FLOAT)
            || (type_size != sizeof(double)))
    {
        IAS_LOG_ERROR("Invalid epoch time array dimensions/datatype class/"
            "datatype size information");
        return ERROR;
    }

    /* Now get the epoch time components */
    hdf_error_status = H5LTget_attribute_double(hdf_file_id, "/",
        epoch_name, epoch_time);
    if (hdf_error_status < 0)
    {
        IAS_LOG_ERROR("Retrieving epoch time attribute values");
        return ERROR;
    }

   

    /* Done */
    return SUCCESS;
}
/******************************************************************************
NAME: ias_math_smooth_gain

PURPOSE: Smooth the gain matrix

RETURN VALUE:
Type = int
Value    Description
-----    -----------
SUCCESS  Successful completion
ERROR    Operation failed

******************************************************************************/
int ias_math_smooth_gain
(
    const double *P,   /* I: Filtered covariance matrix */
    const double *Pn,  /* I: predicted error covariance matrix at time K+1 */
    const double *S,   /* I: state transition matrix */
    double *A,         /* O: smoothing gain matrix */
    int m              /* I: size in m direction */
)
{
    double *t1, *St, *inv;
    int status;
   
    t1  = malloc( m * m * sizeof(double) );
    if (t1 == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        return ERROR;
    }
    St  = malloc( m * m * sizeof(double) );
    if (St == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        free(t1);
        return ERROR;
    }
    inv = malloc( m * m * sizeof(double) );
    if (inv == NULL)
    {
        IAS_LOG_ERROR("Error allocating memory");
        free(t1);
        free(St);
        return ERROR;
    }

    ias_math_transpose_matrix( S, St, m, m );
    status = ias_math_invert_matrix( Pn, inv, m );
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Error returned from ias_math_multiply_matrix");
        free(t1);
        free(St);
        free(inv);
        return ERROR;
    }

    status = ias_math_multiply_matrix( P, St, t1, m, m, m, m );
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Error returned from ias_math_multiply_matrix");
        free(t1);
        free(St);
        free(inv);
        return ERROR;
    }

    status = ias_math_multiply_matrix( t1, inv, A, m, m, m, m );
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Error returned from ias_math_multiply_matrix");
        free(t1);
        free(St);
        free(inv);
        return ERROR;
    }

    free(t1);
    free(St);
    free(inv);
    return SUCCESS;
}
/***********************************************************************
 NAME:                     ias_ancillary_read

 PURPOSE:  Calls routines to read ancillary and ephemeris data

 RETURNS:  Integer status code of SUCCESS or ERROR

 NOTES:    Calling routine is responsible for ensuring a valid filename

************************************************************************/
int ias_ancillary_read
(
    const char *ancillary_filename,          /* I: Name of data file */
    IAS_ANC_ATTITUDE_DATA **attitude_data,   /* I/O: Pointer to attitude
                                                data */
    IAS_ANC_EPHEMERIS_DATA **ephemeris_data  /* I/O: Pointer to ephemeris
                                                data */
)
{
    hid_t hdf_file_id = -1;          /* HDF5 file handle */

    herr_t hdf_error_status = -1;    /* HDF5 error status code; negative
                                        values indicate an error */
    int file_format_version = 0;     /* File format version */
    int rank = 0;                    /* Number of dimensions for the
                                        format version attribute */

    H5T_class_t type_class;          /* Type class identifier */

    size_t type_size = 0;            /* Size of datatype in bytes */

    hsize_t version_dims = 0;        /* Format version array dimension */

    IAS_ANC_ATTITUDE_DATA *attitude_read = NULL;
    /* working attitude data buffer */

    IAS_ANC_EPHEMERIS_DATA *ephemeris_read = NULL;
    /* working ephemeris data buffer */


    /* Initialize the pointers so caller has something to check against */
    *attitude_data = NULL;
    *ephemeris_data = NULL;

    /* Make sure the ancillary file is "valid" */
    if (!ias_ancillary_is_ancillary_file(ancillary_filename))
    {
        IAS_LOG_ERROR("Ancillary file %s not valid", ancillary_filename);
        return ERROR;
    }

    /* Open the file for reading. */
    hdf_file_id = H5Fopen(ancillary_filename, H5F_ACC_RDONLY, H5P_DEFAULT);
    if (hdf_file_id < 0)
    {
        IAS_LOG_ERROR("Opening ancillary data file %s", ancillary_filename);
        return ERROR;
    }

    /* Verify the file format version attribute has a dimension of 1 */
    hdf_error_status = H5LTget_attribute_ndims(hdf_file_id, "/",
        FILE_FORMAT_VERSION_ATTRIBUTE_NAME, &rank);
    if (hdf_error_status < 0)
    {
        IAS_LOG_ERROR("Retrieving file format version attribute dimensions "
            "in ancillary file %s", ancillary_filename);
        return ERROR;
    }
    if (rank != 1)      /* Should only be 1D array */
    {
        IAS_LOG_ERROR("Invalid rank %d detected for file format version "
            "attribute in ancillary file %s, should be 1",
            rank, ancillary_filename);
        return ERROR;
    }

    /* The file format version attribute should be a scalar of integer
       data type*/
    hdf_error_status = H5LTget_attribute_info(hdf_file_id, "/",
        FILE_FORMAT_VERSION_ATTRIBUTE_NAME, &version_dims,
        &type_class, &type_size);
    if ((hdf_error_status < 0) || (version_dims != 1)
            || (type_class != H5T_INTEGER)
            || (type_size != sizeof(int)))
    {
        IAS_LOG_ERROR("Invalid file format version attribute information "
            "in ancillary file %s", ancillary_filename);
        return ERROR;
    }

    /* Read the file format version information */
    hdf_error_status = H5LTget_attribute_int(hdf_file_id, "/",
        FILE_FORMAT_VERSION_ATTRIBUTE_NAME, &file_format_version);
    if (hdf_error_status < 0) 
    {
        IAS_LOG_ERROR("Reading file format version attribute in ancillary "
            "file %s", ancillary_filename);
        H5Fclose(hdf_file_id);
        return ERROR;
    }

    /* Read the attitude data from the file.   */
    attitude_read = read_attitude_data(hdf_file_id, file_format_version);
    if (attitude_read == NULL)
    {
        IAS_LOG_ERROR("Reading attitude data from ancillary data file %s",
            ancillary_filename);
        H5Fclose(hdf_file_id);
        return ERROR;
    }

    /* Read the ephemeris data from the file.  */
    ephemeris_read = read_ephemeris_data(hdf_file_id, file_format_version);
    if (ephemeris_read == NULL)
    {
        IAS_LOG_ERROR("Reading ephemeris data from ancillary data file %s",
            ancillary_filename);
        ias_ancillary_free_attitude(attitude_read);
        H5Fclose(hdf_file_id);
        return ERROR;
    }

    /* Close the file now that the attitude and ephemeris data have been
       read from it. */
    hdf_error_status = H5Fclose(hdf_file_id);
    if (hdf_error_status < 0)
    {
        IAS_LOG_ERROR("Closing ancillary data file %s", ancillary_filename);
        ias_ancillary_free_attitude(attitude_read);
        ias_ancillary_free_ephemeris(ephemeris_read);
        return ERROR;
    }

    /* Point the input pointers to the working buffers. */
    *attitude_data = attitude_read;
    *ephemeris_data = ephemeris_read;

    /* Done */
    return SUCCESS;
}
void ias_cpf_free
(
    IAS_CPF *cpf
)
{
    int index;                  /* loop counter */
    int sca_index;                  /* sca loop counter */
    int band_index;                 /* band loop counter */
    int band_number;                /* actural band number */
    int nbands;                     /* number of bands */
    int band_list[IAS_MAX_NBANDS];  /* list of band numbers */
    int nscas = 0;                  /* number of scas */
    int status;                     /* function return status */

    status = ias_sat_attr_get_sensor_band_numbers(IAS_OLI, IAS_NORMAL_BAND,
                                                  0, band_list, IAS_MAX_NBANDS,
                                                  &nbands);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Getting OLI band numbers");
        return;
    }

    /* free up the pan band average bias */
    for (sca_index = 0; sca_index < IAS_MAX_NSCAS; sca_index++)
    {
        FREE_AND_NULL(cpf->oli_avg_bias.bias_odd_pan[sca_index]);
        FREE_AND_NULL(cpf->oli_avg_bias.bias_even_pan[sca_index]);
    }

    /* free up groups shared by OLI and TIRS */
    for (band_index = 0; band_index < IAS_MAX_NBANDS; band_index++)
    {
        FREE_AND_NULL(cpf->abs_gains.gain[band_index]);
        FREE_AND_NULL(cpf->tirs_abs_gains_blind.gain[band_index]);
        /* sca loop */
        for (sca_index = 0; sca_index < IAS_MAX_NSCAS; sca_index++)
        {
            FREE_AND_NULL(cpf->nonuniformity.scale_factor_1
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->nonuniformity.scale_factor_2
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->post_rel_gains.per_detector
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->pre_rel_gains.per_detector
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->temp_sens.temp_sensitivity_coeff
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->rel_gains.per_detector[band_index][sca_index]);
            FREE_AND_NULL(cpf->detector_offsets.along_per_detector
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->detector_offsets.across_per_detector
                [band_index][sca_index]);
        }
    }

    for (band_index = 0; band_index < nbands; band_index++)
    {
        /* free the band level elements */
        band_number = band_list[band_index];

        /* get number of scas */
        nscas = ias_sat_attr_get_scas_per_band(band_number);
        if (nscas == ERROR)
        {
            IAS_LOG_ERROR("Getting sca count for band: %d", band_number);
            return;
        }

        for (sca_index = 0; sca_index < nscas; sca_index++)
        {
            FREE_AND_NULL(cpf->oli_avg_bias.bias_vnir[band_index][sca_index]);
            FREE_AND_NULL(cpf->oli_avg_bias.bias_swir[band_index][sca_index]);
            FREE_AND_NULL(cpf->lamp_rad.effective_rad_backup
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->lamp_rad.effective_rad_pristine
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->lamp_rad.effective_rad_working
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->diffuser_rad.diff_rad_prim
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->diffuser_rad.diff_rad_pris
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->diffuser_rad.diff_bidir_refl_prim
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->diffuser_rad.diff_bidir_refl_pris
                [band_index][sca_index]);
        }
    }

    /* free those elements that use total bands, normal, vrp and blind */
    for (band_index = 0; band_index < IAS_MAX_TOTAL_BANDS; band_index++)
    {
        for (sca_index = 0; sca_index < IAS_MAX_NSCAS; sca_index++)
        {
            FREE_AND_NULL(cpf->detector_noise.per_detector
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->saturation.analog_low_saturation_level
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->saturation.analog_high_saturation_level
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->saturation.digital_low_saturation_level
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->saturation.digital_high_saturation_level
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->detector_status.out_of_spec
                [band_index][sca_index]);
            FREE_AND_NULL(cpf->detector_status.inoperable
                [band_index][sca_index]);
        }
    }

    /* Get the TIRS band attributs */
    status = ias_sat_attr_get_sensor_band_numbers(IAS_TIRS, IAS_NORMAL_BAND,
                                                  0, band_list, IAS_MAX_NBANDS,
                                                  &nbands);
    if (status != SUCCESS)
    {
        IAS_LOG_ERROR("Getting sat TIRS band numbers");
        return;
    }

    /* get sca count for tirs sensor */
    nscas = ias_sat_attr_get_sensor_sca_count(IAS_TIRS);
    if (nscas == ERROR)
    {
        IAS_LOG_ERROR("Getting sat sca count");
        return;
    }

    /* free up the tirs allocations */
    for (band_index = 0; band_index < nbands; band_index++)
    {
        for (sca_index = 0; sca_index < nscas; sca_index++)
        {
            FREE_AND_NULL(cpf->tirs_det_response_blind
                .baseline_dark_response[band_index][sca_index]);
            FREE_AND_NULL(cpf->tirs_det_response_blind
                .background_response[band_index][sca_index]);
            FREE_AND_NULL(cpf->tirs_det_response_blind
                .gain_offsets[band_index][sca_index]);
            FREE_AND_NULL(cpf->tirs_det_response
                .baseline_dark_response[band_index][sca_index]);
            FREE_AND_NULL(cpf->tirs_det_response
                .background_response[band_index][sca_index]);
            FREE_AND_NULL(cpf->tirs_det_response
                .gain_offsets[band_index][sca_index]);
        }
    }

    /* get tirs sca count */
    nscas = ias_sat_attr_get_sensor_sca_count(IAS_TIRS);
    if (nscas == ERROR)
    {
        IAS_LOG_ERROR("Getting sat sca count");
        return;
    }

    /* free tirs blind band allocations */
    /* NOTE: The tirs blind band is the only band loaded in this structure
       the band number and to make use of existing rel gains parse function
       another instance of the rel gains structure was declared, therefore,
       requiring the band element of the array.
     */
    for (sca_index = 0; sca_index < nscas; sca_index++)
    {
        FREE_AND_NULL(cpf->tirs_rel_gains_blind.per_detector[0][sca_index]);
        FREE_AND_NULL(cpf->tirs_pre_rel_gains_blind.per_detector[0][sca_index]);
        FREE_AND_NULL(cpf->tirs_post_rel_gains_blind.per_detector
            [0][sca_index]);
        FREE_AND_NULL(cpf->tirs_det_status_blind.inoperable[0][sca_index]);
        FREE_AND_NULL(cpf->tirs_det_status_blind.out_of_spec[0][sca_index]);
    }

    /* free non band and sca level elements */
    FREE_AND_NULL(cpf->earth.leap_seconds_data.leap_years);
    FREE_AND_NULL(cpf->earth.leap_seconds_data.leap_months);
    FREE_AND_NULL(cpf->earth.leap_seconds_data.leap_days);
    FREE_AND_NULL(cpf->earth.leap_seconds_data.num_leap_seconds);

    /* free cloud cover assessment elements */
    if (cpf->cc_assessment.run_if_thermal != NULL)
    {
        FREE_AND_NULL(cpf->cc_assessment.run_if_thermal);
    }
    if (cpf->cc_assessment.cca_class_type != NULL)
    {
        for (index = 0; index < cpf->cc_assessment.number_of_classes; index++)
        {
            FREE_AND_NULL(cpf->cc_assessment.cca_class_type[index]);
        }
        FREE_AND_NULL(cpf->cc_assessment.cca_class_type);
    }
    if (cpf->cc_assessment.algorithm_names != NULL)
    {
        for (index = 0;index < cpf->cc_assessment.number_of_algorithms;index++)
        {
            FREE_AND_NULL(cpf->cc_assessment.algorithm_names[index]);
            FREE_AND_NULL(cpf->cc_assessment.weights[index]);
        }
        FREE_AND_NULL(cpf->cc_assessment.algorithm_names);
        FREE_AND_NULL(cpf->cc_assessment.weights);
    }
    /* free the buffer with the file contents */
    FREE_AND_NULL(cpf->raw_file_buffer);

    /* free the CPF structure */
    free(cpf);
}
/*------------------------------------------------------------------------------
NAME:    write_array

PURPOSE: This utility function will write a 2D array of values to a data set
         in an HDF5 file for the TIRS secondary linearization function.  This
         function can be used to write the lookup DN values as well as
         correction values.

RETURNS: SUCCESS or ERROR
------------------------------------------------------------------------------*/
static int write_array
(
    hid_t group_id,       /* I: HDF5 group ID being written to */
    hsize_t dims[2],      /* I: Dimensions of the 'values' parameter */
    const char *hdf_path, /* I: Full path of the data set */
    double *values        /* I: 2D array to be written to the file */
)
{
    herr_t h_status;      /* HDF5 status */
    hid_t  dataset_id;
    hid_t  dataspace_id;

    /* Create a new HDF5 dataspace for the table of values.  The first argument
       indicates the number of dimensions in the data space.  The last argument
       is the maximum dimensions of the data space, but by passing in a NULL
       pointer, the maximum dimensions will be set to the same as the initial
       dimensions. */
    dataspace_id = H5Screate_simple(2, dims, NULL);
    if (dataspace_id < 0)
    {
        IAS_LOG_ERROR("Creating an HDF5 data space for %s", hdf_path);
        return ERROR;
    }

    /* Create the data set for the table of values within the HDF5 file */
    dataset_id = H5Dcreate(group_id, hdf_path, H5T_IEEE_F64LE,
        dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    if (dataset_id < 0)
    {
        IAS_LOG_ERROR("Creating an HDF5 data space for %s", hdf_path);
        H5Sclose(dataspace_id);
        return ERROR;
    }

    /* Write the 2D array of values to the HDF5 file */
    h_status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
        H5P_DEFAULT, values);
    if (h_status < 0)
    {
        IAS_LOG_ERROR("Writing to HDF5 data space for %s", hdf_path);
        H5Dclose(dataset_id);
        H5Sclose(dataspace_id);
        return ERROR;
    }

    /* Close the data set we just wrote */
    h_status = H5Dclose(dataset_id);
    if (h_status < 0)
    {
        IAS_LOG_ERROR("Closing data set for %s", hdf_path);
        H5Sclose(dataspace_id);
        return ERROR;
    }

    /* Close the data space we created for the table */
    h_status = H5Sclose(dataspace_id);
    if (h_status < 0)
    {
        IAS_LOG_ERROR("Closing data space for %s", hdf_path);
        return ERROR;
    }

    return SUCCESS;
}  /* END write_array */