/* * Copyright (C) 1998 by Southwest Research Institute (SwRI) * * All rights reserved under U.S. Copyright Law and International Conventions. * * The development of this Software was supported by contracts NAG5-3148, * NAG5-6855, NAS8-36840, NAG5-2323, and NAG5-7043 issued on behalf of * the United States Government by its National Aeronautics and Space * Administration. Southwest Research Institute grants to the Government, * and others acting on its behalf, a paid-up nonexclusive, irrevocable, * worldwide license to reproduce, prepare derivative works, and perform * publicly and display publicly, by or on behalf of the Government. * Other than those rights granted to the United States Government, no part * of this Software may be reproduced in any form or by any means, electronic * or mechanical, including photocopying, without permission in writing from * Southwest Research Institute. All inquiries should be addressed to: * * Director of Contracts * Southwest Research Institute * P. O. Drawer 28510 * San Antonio, Texas 78228-0510 * * * Use of this Software is governed by the terms of the end user license * agreement, if any, which accompanies or is included with the Software * (the "License Agreement"). An end user will be unable to install any * Software that is accompanied by or includes a License Agreement, unless * the end user first agrees to the terms of the License Agreement. Except * as set forth in the applicable License Agreement, any further copying, * reproduction or distribution of this Software is expressly prohibited. * Installation assistance, product support and maintenance, if any, of the * Software is available from SwRI and/or the Third Party Providers, as the * case may be. * * Disclaimer of Warranty * * SOFTWARE IS WARRANTED, IF AT ALL, IN ACCORDANCE WITH THESE TERMS OF THE * LICENSE AGREEMENT. UNLESS OTHERWISE EXPLICITLY STATED, THIS SOFTWARE IS * PROVIDED "AS IS", IS EXPERIMENTAL, AND IS FOR NON-COMMERCIAL USE ONLY, * AND ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT * SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. * * Limitation of Liability * * SwRI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED AS A RESULT OF USING, * MODIFYING, CONTRIBUTING, COPYING, DISTRIBUTING, OR DOWNLOADING THIS * SOFTWARE. IN NO EVENT SHALL SwRI BE LIABLE FOR ANY INDIRECT, PUNITIVE, * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGE (INCLUDING LOSS OF BUSINESS, * REVENUE, PROFITS, USE, DATA OR OTHER ECONOMIC ADVANTAGE) HOWEVER IT ARISES, * WHETHER FOR BREACH OF IN TORT, EVEN IF SwRI HAS BEEN PREVIOUSLY ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. YOU HAVE SOLE RESPONSIBILITY FOR ADEQUATE * PROTECTION AND BACKUP OF DATA AND/OR EQUIPMENT USED IN CONNECTION WITH THE * SOFTWARE AND WILL NOT MAKE A CLAIM AGAINST SwRI FOR LOST DATA, RE-RUN TIME, * INACCURATE OUTPUT, WORK DELAYS OR LOST PROFITS RESULTING FROM THE USE OF * THIS SOFTWARE. YOU AGREE TO HOLD SwRI HARMLESS FROM, AND YOU COVENANT NOT * TO SUE SwRI FOR, ANY CLAIMS BASED ON USING THE SOFTWARE. * * Local Laws: Export Control * * You acknowledge and agree this Software is subject to the U.S. Export * Administration Laws and Regulations. Diversion of such Software contrary * to U.S. law is prohibited. You agree that none of the Software, nor any * direct product therefrom, is being or will be acquired for, shipped, * transferred, or reexported, directly or indirectly, to proscribed or * embargoed countries or their nationals, nor be used for nuclear activities, * chemical biological weapons, or missile projects unless authorized by U.S. * Government. Proscribed countries are set forth in the U.S. Export * Administration Regulations. Countries subject to U.S embargo are: Cuba, * Iran, Iraq, Libya, North Korea, Syria, and the Sudan. This list is subject * to change without further notice from SwRI, and you must comply with the * list as it exists in fact. You certify that you are not on the U.S. * Department of Commerce's Denied Persons List or affiliated lists or on the * U.S. Department of Treasury's Specially Designated Nationals List. You agree * to comply strictly with all U.S. export laws and assume sole responsibilities * for obtaining licenses to export or reexport as may be required. * * General * * These Terms represent the entire understanding relating to the use of the * Software and prevail over any prior or contemporaneous, conflicting or * additional, communications. SwRI can revise these Terms at any time * without notice by updating this posting. * * Trademarks * * The SwRI logo is a trademark of SwRI in the United States and other countries. * */ #ident "@(#) fill_aarray.c 1.30 05/08/19 SwRI" #include "libbase_idfs.h" #include "gen_defs.h" #include "ret_codes.h" /**************************************************************************** * * * IR_FILL_ANGLE_ARRAY SUBROUTINE * * * * DESCRIPTION * * This routine is called in order to fill azimuthal start and stop * * angles for each data value returned for a given sensor. If the spin * * rate indicates a parked or otherwise non-rotating status the SUN_SEN * * field holds the experiment angle multiplied by 100. If the experiment * * is spinning, the calulation for the start and stop angles is performed * * and the values are returned to the user in allocated memory. The * * starting angle values are always returned as values between 0.0 and * * 359. However, the ending angle can be negative if the deg_accum value * * is negative (negative direction) or could be greater than 350 * * after adding a positive deg_accum value. * * * * INPUT VARIABLES * * SDDAS_CHAR full_swp flag that indicates if 1 value is being * * requested or all values for the record (for * * a scalar parameter only) * * SDDAS_SHORT sensor the sensor for which data is requested * * void *idf_data_ptr ptr to the memory location for the structure * * that holds returned data values (read_drec) * * * * USAGE * * x = ir_fill_angle_array (full_swp, sensor, idf_data_ptr) * * * * NECESSARY SUBPROGRAMS * * ir_sample_time () returns the time associated with a single * * data sample (row, column) * * * * EXTERNAL VARIABLES * * struct general_info structure that holds information concerning * * ginfo the experiment that is being processed * * * * INTERNAL VARIABLES * * struct idf_data structure that holds all of the currently * * *EXP_DATA returned data values to be processed * * struct experiment_info a pointer to the structure that holds * * *ex specific experiment information * * struct ptr_rec *ptr a pointer to the structure which holds all * * pointers to the header and data for the * * experiment of interest * * reg SDDAS_FLOAT *s_ptr ptr to the starting azimuthal angle array * * reg SDDAS_FLOAT *e_ptr ptr to the ending azimuthal angle array * * reg SDDAS_FLOAT *f_end loop termination variable * * reg SDDAS_FLOAT *fxang fixed angle - non-rotating instrument * * reg SDDAS_SHORT *s1 fast SDDAS_SHORT pointer looper * * SDDAS_FLOAT deg_per_msec number of degrees per millisecond * * SDDAS_FLOAT deg_accum number of degrees covered by acquisition * * of a single measurement * * SDDAS_FLOAT time_accum acquisition time of a single measurement * * SDDAS_FLOAT C1 time correction constant * * SDDAS_FLOAT ftime_ms start time of measurement used in computation * * (floating point to keep precision) * * SDDAS_FLOAT sphi, ephi pre-defined azimuthal range (constants) that * * circumvent the computation of the az. angles * * SDDAS_LONG btime_ms start time of the measurement in milliseconds * * SDDAS_LONG etime_ms end time of the measurement in milliseconds * * SDDAS_LONG dummy_ns dummy placeholder for nanoseconds element * * SDDAS_LONG base_time time correction constant * * SDDAS_LONG angle_adjust integer divisor used to speed up computations * * to get phi between 0 and 360 degrees * * SDDAS_USHORT max_ele the number of angle values to be returned * * SDDAS_USHORT time_row the matrix row being processed * * SDDAS_USHORT n_sample the number of samples returned for each sensor* * SDDAS_SHORT base_swp_off sweep step offset for 1st element in column * * SDDAS_SHORT time_col the matrix column being processed * * SDDAS_CHAR decrement_bday flag indicating if start day needs to be * * modified (if btime ends up negative) * * SDDAS_CHAR decrement_eday flag indicating if end day needs to be * * modified (if etime ends up negative) * * * * SUBSYSTEM * * Display Level * * * ***************************************************************************/ SDDAS_SHORT ir_fill_angle_array (SDDAS_CHAR full_swp, SDDAS_SHORT sensor, void *idf_data_ptr) { extern struct general_info ginfo; struct idf_data *EXP_DATA; struct experiment_info *ex; struct ptr_rec *ptr; register SDDAS_FLOAT *s_ptr, *e_ptr, *f_end, fxang; register SDDAS_SHORT *s1; SDDAS_FLOAT deg_per_msec, deg_accum, time_accum, C1, ftime_ms, sphi, ephi; SDDAS_LONG btime_ms, etime_ms, dummy_ns, base_time, angle_adjust; SDDAS_USHORT max_ele, time_row, n_sample; SDDAS_SHORT base_swp_off, time_col; SDDAS_CHAR decrement_bday, decrement_eday; /***********************************************************************/ /* Set a pointer to the structure which holds all pointers for header */ /* and data information for thxperiment currently being processed. */ /***********************************************************************/ ex = ginfo.expt; ptr = ex->info_ptr; /****************************************************************************/ /* No need to make sure data structure is correct type or that the header */ /* format is correct since this module is indirectly called by read_drec() */ /* which checks for this issue. */ /****************************************************************************/ EXP_DATA = (struct idf_data *) idf_data_ptr; /************************************************************************/ /* For a full swp, max_ele is not just set to N_SAMPLE because the user */ /* could have asked for one at a time and then changed to a full swp to */ /* get the remainder of the elements. We use time_row since we need to */ /* pick up the rest of the elements in that column being processed (for */ /* both SEN_MODE = 0 and 4) and time_row tells us how many have already */ /* been processed for that sensor. */ /************************************************************************/ n_sample = *ptr->hdr_fmt1_ptr->N_SAMPLE; max_ele = (ex->smp_id == 2 && !full_swp) ? 1 : n_sample - ptr->time_row; /************************************************************************/ /* Clear out the old azimuthal angles set to the constant offset angle.*/ /* For optimization purposes, it's advised to use ++x instead of x++. */ /************************************************************************/ s_ptr = EXP_DATA->start_az; e_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->start_az + max_ele; fxang = *(ex->constants + sensor); for (; s_ptr < f_end; ++s_ptr, ++e_ptr) { *s_ptr = fxang; *e_ptr = 0.0; } /************************************************************************/ /* The instrument is not spinning AND has pre-defined constant values */ /* circumvent the algorithmic computation of the azimuthal angles. */ /************************************************************************/ if (ex->phi_method == 1) { s_ptr = EXP_DATA->start_az; e_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->start_az + max_ele; sphi = *(ex->constants + (START_PHI_OFF * ex->num_sensor) + sensor); ephi = *(ex->constants + (STOP_PHI_OFF * ex->num_sensor) + sensor); /**********************************************************************/ /* For optimization purposes, it's advised to use ++x instead of x++.*/ /**********************************************************************/ for (; s_ptr < f_end; ++s_ptr, ++e_ptr) { *s_ptr = sphi; *e_ptr = ephi; } } /************************************************************************/ /* The instrument is not spinning. */ /************************************************************************/ else if (*ptr->SPIN == 0) { s_ptr = EXP_DATA->start_az; e_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->start_az + max_ele; fxang = *(ptr->SUN_SEN) / 100.0; for (; s_ptr < f_end; ++s_ptr, ++e_ptr) { *s_ptr += fxang; /****************************************************************/ /* Ensure that angle goes from 0.0 to 359.999... For negative */ /* angles, return the compliment angle (-90 = 270). */ /****************************************************************/ if (*s_ptr < 0.0) { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr < -3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr < 0.0) *s_ptr += 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr >= 360.0) *s_ptr -= 360.0; } else { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr > 3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr >= 360.0) *s_ptr -= 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr < 0.0) *s_ptr += 360.0; } *e_ptr = *s_ptr; } } else { time_row = (SDDAS_USHORT) ptr->time_row; /*****************************************************************/ /* When computing the angles, take into account fractions of */ /* milliseconds (nanoseconds components). */ /*****************************************************************/ time_accum = ex->accum_ms + ex->accum_ns * 1.0e-6 + ex->lat_ms + ex->lat_ns * 1.0e-6; deg_per_msec = ptr->deg_per_msec; deg_accum = time_accum * deg_per_msec; /*******************************************************************/ /* Determine the starting azimuthal angle for each data sample. */ /* */ /* If time advances down the column and the column timing is */ /* sequential, use DA_METHOD to determine start time of sample. */ /* In all cases, only the millisecond time component is of used. */ /* */ /* The formula for the angle is (Tsam - Tsunsen) * 1/Spin * 360 */ /* which is added to the angle offset for the sensor. This angle */ /* offset has already been preloaded into the start angle array */ /*******************************************************************/ s_ptr = EXP_DATA->start_az; f_end = EXP_DATA->start_az + max_ele; base_time = ex->btime_ms % 86400000; C1 = base_time - *ptr->SUN_SEN + *(ex->start_spin_off + sensor); if (ex->sen_mode == 0 || ex->sen_mode == 2) { /*******************************************************************/ /* DA_METHOD only applies to a vector instrument, not a scalar. */ /*******************************************************************/ if (ex->smp_id != 2) switch (ex->da_method) { case 0: ftime_ms = time_row * time_accum; for ( ; s_ptr < f_end; ++s_ptr) { *s_ptr += (ftime_ms + C1) * deg_per_msec; ftime_ms += time_accum; } break; case 1: s1 = ptr->hdr_fmt1_ptr->SAMP_INDEX + time_row; for ( ; s_ptr < f_end; ++s1, ++s_ptr) { ftime_ms = *s1 * time_accum; *s_ptr += (ftime_ms + C1) * deg_per_msec; } break; case 2: case 3: s1 = ptr->hdr_fmt1_ptr->SAMP_INDEX + time_row; base_swp_off = *ptr->hdr_fmt1_ptr->SAMP_INDEX; if (*ptr->hdr_fmt1_ptr->SAMP_INDEX > *(ptr->hdr_fmt1_ptr->SAMP_INDEX + 1)) { for ( ; s_ptr < f_end; ++s1, ++s_ptr) { ftime_ms = (base_swp_off - *s1) * time_accum; *s_ptr += (ftime_ms + C1) * deg_per_msec; } } else { for ( ; s_ptr < f_end; ++s1, ++s_ptr) { ftime_ms = (*s1 - base_swp_off) * time_accum; *s_ptr += (ftime_ms + C1) * deg_per_msec; } } break; } else { /***********************************************************************/ /* Basically, same code as da_method = 0 EXCEPT since the time tags */ /* already take into account which row IF a scalar source is stepping */ /* through packed data one sample at a time, no need for ftime_ms */ /* factor. */ /***********************************************************************/ ftime_ms = 0; for ( ; s_ptr < f_end; ++s_ptr) { *s_ptr += (ftime_ms + C1) * deg_per_msec; ftime_ms += time_accum; } } } else { /****************************************************************/ /* If time advances down the column and the column timing is */ /* parallel, use start time of first element being returned */ /****************************************************************/ if (ex->sen_mode == 1 || ex->sen_mode == 3) { for ( ; s_ptr < f_end; ++s_ptr) *s_ptr += C1 * deg_per_msec; } else { /****************************************************************/ /* Determine the time for the sample being processed. Day */ /* value is not used so no need to check decrement_day flag. */ /* No need to use ftime_ms since SUN_SEN is in milliseconds. */ /****************************************************************/ time_col = ptr->time_col; for ( ; s_ptr < f_end; ++s_ptr) { ir_sample_time (max_ele, sensor, time_row, time_col, &btime_ms, &dummy_ns, &etime_ms, &dummy_ns, &decrement_bday, &decrement_eday); *s_ptr += (btime_ms - *ptr->SUN_SEN + *(ex->start_spin_off + sensor)) * deg_per_msec; ++time_row; } } } /****************************************************************/ /* Ensure that angle goes from 0.0 to 359.999... For negative */ /* angles, return the compliment angle (-90 = 270). */ /****************************************************************/ s_ptr = EXP_DATA->start_az; f_end = EXP_DATA->start_az + max_ele; for ( ; s_ptr < f_end; ++s_ptr) { if (*s_ptr < 0.0) { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr < -3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr < 0.0) *s_ptr += 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr >= 360.0) *s_ptr -= 360.0; } else { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr > 3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr >= 360.0) *s_ptr -= 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr < 0.0) *s_ptr += 360.0; } } /*******************************************************************/ /* For this case of DA_METHOD, the stopping angle value is equal */ /* to the starting angle of the NEXT sample. For the last sample */ /* simply add the degrees covered by the accumulation time of the */ /* sample to the starting azimuthal angle for the last sample. */ /*******************************************************************/ if ((ex->sen_mode == 0 || ex->sen_mode == 2) && ex->da_method == 3) { s_ptr = EXP_DATA->start_az + 1; e_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->stop_az + max_ele - 1; /**********************************************************************/ /* For optimization purposes, it's advised to use ++x instead of x++.*/ /**********************************************************************/ for (; e_ptr < f_end; ++s_ptr, ++e_ptr) *e_ptr = *s_ptr; *e_ptr = *(--s_ptr) + deg_accum; } /*******************************************************************/ /* Simply add the degrees covered by the accumulation time of the */ /* sample to each starting azimuthal angle value. For */ /* optimization purposes, it's advised to use ++x instead of x++. */ /*******************************************************************/ else { s_ptr = EXP_DATA->start_az; e_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->stop_az + max_ele; for (; e_ptr < f_end; ++s_ptr, ++e_ptr) *e_ptr = *s_ptr + deg_accum; } /****************************************************************/ /* Ensure that angle goes from 0.0 to 359.999... For negative */ /* angles, return the compliment angle (-90 = 270). This is */ /* important since deg_accum could be negative. */ /****************************************************************/ s_ptr = EXP_DATA->stop_az; f_end = EXP_DATA->stop_az + max_ele; for (; s_ptr < f_end; ++s_ptr) { if (*s_ptr < 0.0) { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr < -3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr < 0.0) *s_ptr += 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr >= 360.0) *s_ptr -= 360.0; } else { /***********************************************************/ /* If it will take more than 10 iterations to get within */ /* the positive range, use a combination of division and */ /* += to keep fractional precision. */ /***********************************************************/ if (*s_ptr > 3600.00) { angle_adjust = *s_ptr / 360; *s_ptr -= angle_adjust * 360.0; } while (*s_ptr >= 360.0) *s_ptr -= 360.0; /***********************************************************/ /* Due to precision issues, must add this check. */ /***********************************************************/ if (*s_ptr < 0.0) *s_ptr += 360.0; } } } EXP_DATA->num_angle = max_ele; return (ALL_OKAY); }