/* **********************************************************************
 * *	                                                                *
 * *                     Rays Utility Subroutines                       *
 * *                                                                    *
 * **********************************************************************
 * File: iris/examiners/raysubs.C
 *
 *     COPYRIGHT (c) 1988, 1989, 1991, 1996, 1997, 1998, 1999, 2000,
 *                2001, 2002, 2003, 2004, 2005, 2006 BY
 *         SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sigtypes.h"
#include "dsp.h"
#include "headers.h"
#include "iris_task.h"
#include "ingest.h"
#include "setup.h"

#include "user_lib.h"
#include "dsp_lib.h"

#include "examiners.h"


/* #define INU_ANGLE_TEST 1 */
#ifdef INU_ANGLE_TEST
#include <math.h>
#include "rcp8.h"
#include "pcicards_lib.h"
#include "softplane_lib.h"
#include "rcp8main.h"
#endif  /* #ifdef INU_ANGLE_TEST */

static void ShowRayData
(const void *ray_a,
 SINT4 iprm_a,
 SINT4 istart_a,
 const struct data_convert *pConvert_a,
 SINT4 iMaxLength_a );

static void rays_list_xh1
(const struct task_configuration *tco_a,
 const struct extended_header_v1 *hdr_a );

#ifdef INU_ANGLE_TEST
static void fill_inu_params
( const struct extended_header_v1 *pXHdr_a,   /* Input data */
  volatile struct rcp8_inu_params *pInuParams_a );  /* Matrix filled in here */

static void calc_inu_params_ped
( BIN2 iPedAz_a,   /* Pedestal azimuth */
  BIN2 iPedEl_a,   /* Pedestal elevation */
  volatile struct rcp8_inu_params *pPrm_a);
#endif  /* #ifdef INU_ANGLE_TEST */

#define MAX_TOTALS (20)
static FLT8  fTotals[MAX_TOTALS];
static SINT4 iCounts[MAX_TOTALS];

/* ====================================================================
 * Find out which bin to start at.  Returns TRUE if we want performance data
 * only.
 */
UINT1 lAskRange
(FLT4 *fBinStartKm_a,  /* Start range in km, N/A for performance data or XHDR */
 UINT2 iparam_a)
{
  char sanswer[80], *send, *sResult;
  SINT4 iLength;
  UINT1 lPerformance;

  printf( "Do you want just performance information? ");
  fgets( sanswer, 80, stdin );
  STR_UPPER( sanswer, 1 );
  if (sanswer[0] == 'Y') 
    lPerformance = TRUE;
  else
    lPerformance = FALSE;

  if( (!lPerformance) &&( iparam_a != DB_XHDR ) )
    {
      printf( "Enter starting range in km: " );
      sResult = fgets( sanswer, 80, stdin );
      if (NULL == sResult)
	iLength=0;
      else
	iLength=strlen(sanswer)-1;
      sanswer[iLength] = 0;  /* Remove trailing C/R */
      if (iLength>0)
	{
	  *fBinStartKm_a = strtod( sanswer, &send );
	}
      else
	{
	  *fBinStartKm_a = 0.0;
	}
    }
  
  return lPerformance;
}

/* ====================================================================
 * This subroutine asks the user for a moment/data type.
 * 
 * Entry parameters
 *     iavailable_a - Word holding bits to indicate the available data types
 *     idata_a      - data type as specified by the user
 *     idefault_a   - Default value, to be used if nothing was typed.
 */
void askmoments
(const char *sPrompt_a,     /* Prompt before reading the response */
 const struct dsp_data_mask *pAvailable_a,
 UINT2 *idata_a,
 UINT2 idefault_a )
{
  int iError;
  SINT4 ilength;
  char sline[80], *sresult;

  /* Find out what the user wants to do:
   */
 L1000:
  printf( "%s", sPrompt_a );
  sresult = fgets( sline, 70, stdin );
  if (NULL == sresult)
    ilength = 0;
  else
    ilength = strlen(sline)-1;

  /* Remove the trailing Carriage return
   */
  sline[ilength] = 0;

  /* Make upper case
   */
  iError = DataTypeFromStr( idata_a, sline, pAvailable_a, idefault_a );

  if (iError==DATATYPE_INVALID)
    {
      printf("Sorry, unrecognized parameter, try again: \n" );
      goto L1000;
    }

  if( iError==DATATYPE_MISSING)
    {
      printf("Sorry, parameter not in this scan, try again: \n" );
      goto L1000;
    }
  return;
} 

/* ====================================================================
 * Convert a string into the appropriate data type.  Case is ignored.
 */
int DataTypeFromStr
(UINT2 *idata_a,
 const char *sString_a,   /* String to convert */
 const struct dsp_data_mask *pAvailable_a,
 UINT2 idefault_a )
{
  SINT4 ii, ibit, icmdnum, iLength;
  char *scommands[NUM_DEFINED_DATA], sarray[NUM_DEFINED_DATA][7], sTrial[7], sLine[80], *sreply;
  UINT2 icmdval[NUM_DEFINED_DATA], icommand, lamb;

  /* Make up a list of available data names
   */
  icmdnum = 0;
  for ( ii = 0; ii<NUM_DEFINED_DATA; ii++)
    {
      strncpy( sTrial, sdata_name6(ii), 6 );
      sTrial[6] = 0;
      STR_UPPER( sTrial, 6 );
      if( strcmp( sTrial, "??????" ) )
	{
	  memmove( sarray[icmdnum], sTrial, 7 );
	  scommands[icmdnum] = sarray[icmdnum];
	  icmdval[icmdnum] = ii;
	  icmdnum++;
	}
    }

  strncpy( sLine, sString_a, 79 );
  sLine[79] = 0;
  iLength = strlen(sLine);
  STR_UPPER( sLine, iLength );
  sreply = strtok( sLine, " \t" );

  *idata_a = idefault_a;
  if (!sreply)
    {
      /* Blank line means take the default.  Check that default
       * is ok, if not, take the lowest bit instead.
       */
      if( !lDspMaskTest( pAvailable_a, idefault_a ) )
	{
	  for (ibit=0; ibit<NUM_DEFINED_DATA; ibit++)
	    {
	      if (lDspMaskTest( pAvailable_a,ibit))
		{
		  *idata_a=ibit;
		  return DATATYPE_OK;
		}
	    }
	  return DATATYPE_MISSING;
	}
      return DATATYPE_OK;
    }
         
  icommand = c_cmd_lookup( sreply, scommands, icmdnum, &lamb );
  if (!icommand) return DATATYPE_INVALID;

  ibit = icmdval[icommand-1];
  if( ! lDspMaskTest( pAvailable_a, ibit)) return DATATYPE_MISSING;

  *idata_a = ibit;
  return DATATYPE_OK;
} 

/* =======================================================================
 * History:
 *     jmh   8 Oct 1990   Minor change to prompt
 * Find out what sweep the user wants
 * Entry parameters:
 *     isweepcnt_a - The number of sweeps available
 *     idefault_a  - The default sweep number to use if no answer
 * Exit parameters:
 *     isweep_a
 */
void asksweep
(SINT2 isweepcnt_a,
 SINT2 *isweep_a,
 UINT2 idefault_a )
{
  char *sresult, *sreply, *send, sline[80];
  SINT4 isweep, ilength;
  /* -------------------------------------------------------------------------- */
 L1000:
  printf( "What sweep do you wish to display? ");
  sresult = fgets( sline, 70, stdin );
  if (NULL == sresult)
    ilength = 0;
  else
    ilength = strlen(sline)-1;

  /* Remove the trailing Carriage return
   */
  sline[ilength] = 0;

  sreply = strtok( sline, " \t" );

  if (!sreply)
    {
      /* Blank line means take the default */
      *isweep_a = idefault_a;
      return;
    }
         
  isweep = strtol( sreply, &send, 10 );
  if ( send == sreply)
    {
      printf( "Sorry, unrecognized sweep number, try again.\n" );
      goto L1000;
    }
  if ( (isweep<1) || (isweep>isweepcnt_a) )
    {
      printf( "Sorry, Sweep number out of range, try again.\n" );
      goto L1000;
    }
  *isweep_a = isweep;
}
/* ==========================================================
 * Clear the averages accumulation arrays.
 */
void ClearRayAverages(void)
{
  int ii;
  for (ii=0; ii<MAX_TOTALS; ii++)
    {
      fTotals[ii] = 0.0;
      iCounts[ii] = 0;
    }
  return;
}
/* ===========================================================
 * Print a line of ray data, using formats that vary with the type of the
 * parameter.
 */
static void ShowRayData
( const void *ray_a,
  SINT4 iprm_a,
  SINT4 istart_a,
  const struct data_convert *pConvert_a,
  SINT4 iMaxLength_a )
{
  const UINT1 *pbyte=(UINT1*)ray_a ;
  const UINT2 *pword=(UINT2*)ray_a ;
  SINT4 iIndex, ival, iResolution ;
  SINT4 iTotalLength, iThisLength;
  char sThreshold[20];    /*String to display for threshold case */
  char sFormat[20];       /*Format string for data printing */
  char sThisString[20];   /*String to print */
  
  fflush(0) ;
  iResolution = idata_resolution( iprm_a );

  switch( iprm_a )
    {
    case DB_KDP:
    case DB_KDP2:
    case DB_RHOHV:
    case DB_RHOHV2:
    case DB_RHOH:
    case DB_RHOH2:
    case DB_RHOV:
    case DB_RHOV2:
    case DB_SQI:
    case DB_SQI2:
    case DB_WIDTH:
    case DB_WIDTH2:
    case DB_ZDR:
    case DB_ZDR2:
      strcpy(sThreshold, " -.-- " ) ;
      strcpy(sFormat, "%5.2f " ) ;
      break ;

    case DB_VELC:
    case DB_VELC2:
    case DB_DBT:
    case DB_DBT2:
    case DB_DBZ:
    case DB_DBZ2:
    case DB_DBZC:
    case DB_DBZC2:
    case DB_LDRH:
    case DB_LDRH2:
    case DB_LDRV:
    case DB_LDRV2:
    case DB_PHIDP:
    case DB_PHIDP2:
    case DB_PHIH:
    case DB_PHIH2:
    case DB_PHIV:
    case DB_PHIV2:
    case DB_VEL:
    case DB_VEL2:
    default:
      strcpy(sThreshold, " --.- " ) ;
      strcpy(sFormat, "%5.1f " ) ;
      break ;

    case DB_FLAGS:
    case DB_FLAGS2:
      strcpy(sThreshold, "----- " ) ;
      strcpy(sFormat, "%5.0f " ) ;
      break ;
    }
  iIndex = 0;
  iTotalLength=0;
  iThisLength = 0;
  while( iTotalLength+iThisLength < iMaxLength_a )
    {
      if (iThisLength) printf( "%s", sThisString );
      iTotalLength += iThisLength;

      if (iResolution==16)
	ival = (SINT4)(UINT4)pword[ istart_a + iIndex ];
      else
	ival = (SINT4)(UINT4)pbyte[ istart_a + iIndex ];
      if( ival == 0 )
	strcpy( sThisString, sThreshold ) ;
      else
	{
	  FLT8 fValue;
	  fValue = fdata_to_user( ival, iprm_a, pConvert_a );
	  sprintf( sThisString, sFormat, fValue ) ;
	  if (iIndex<MAX_TOTALS)
	    {
	      fTotals[iIndex] += fValue;
	      iCounts[iIndex]  += 1;
	    }
	}
      iThisLength = strlen(sThisString);
      iIndex++;
    }
  
  printf( "\n\n" ) ; fflush(0) ;
  return;
}
/* ===========================================================
 * Print a line of ray data, using formats that vary with the type of the
 * parameter.
 */
void ShowRayAverages
( SINT4 iprm_a,
  SINT4 istart_bin_a,   /* Bin number to start displaying at, origin 1 */
  SINT4 iMaxLength_a )
{
  SINT4 iIndex, iTotalLength, iThisLength;
  char sThreshold[20];    /*String to display for threshold case */
  char sFormat[20];       /*Format string for data printing */
  char sThisString[20];   /*String to print */

  if (istart_bin_a<=0) return;

  switch( iprm_a )
    {
    case DB_KDP:
    case DB_KDP2:
    case DB_RHOHV:
    case DB_RHOHV2:
    case DB_RHOH:
    case DB_RHOH2:
    case DB_RHOV:
    case DB_RHOV2:
    case DB_SQI:
    case DB_SQI2:
    case DB_WIDTH:
    case DB_WIDTH2:
    case DB_ZDR:
    case DB_ZDR2:
      strcpy(sThreshold, " -.-- " ) ;
      strcpy(sFormat, "%5.2f " ) ;
      break ;

    case DB_VELC:
    case DB_VELC2:
    case DB_DBT:
    case DB_DBT2:
    case DB_DBZ:
    case DB_DBZ2:
    case DB_DBZC:
    case DB_DBZC2:
    case DB_LDRH:
    case DB_LDRH2:
    case DB_LDRV:
    case DB_LDRV2:
    case DB_PHIDP:
    case DB_PHIDP2:
    case DB_PHIH:
    case DB_PHIH2:
    case DB_PHIV:
    case DB_PHIV2:
    case DB_VEL:
    case DB_VEL2:
    default:
      strcpy(sThreshold, " --.- " ) ;
      strcpy(sFormat, "%5.1f " ) ;
      break ;

    case DB_FLAGS:
    case DB_FLAGS2:
    case DB_XHDR:
      return;
      break;
    }
  printf( "Average Values:\n" );
  iIndex = 0;
  iTotalLength=0;
  iThisLength = 0;
  while( (iTotalLength+iThisLength < iMaxLength_a) && (iIndex<MAX_TOTALS))
    {
      if (iThisLength) printf( "%s", sThisString );
      iTotalLength += iThisLength;

      if( iCounts[iIndex] == 0 )
	strcpy( sThisString, sThreshold ) ;
      else
	{
	  FLT8 fValue;
	  fValue = fTotals[iIndex] / (FLT8)iCounts[iIndex] ;
	  sprintf( sThisString, sFormat, fValue ) ;
	}
      iThisLength = strlen(sThisString);
      iIndex++;
    }
  
  printf( "\n\n" ) ; 
  return;
}

/* ====================================================================
 * Show some information about a summary file.
 * Entry parameters:
 *     ingsum_a - An ingest summary
 */
void showsummary
(const struct ingest_header *ihead_a)
{
  SINT2 isweeps, ithisline, istart;
  SINT4 ii, iLowPRF;
  FLT4 fangle;
  char sRecordTZName[TZNAME_SIZE], sLocalTZName[TZNAME_SIZE], sString[16];
  char sTimeBuffer[TIMENAME_SIZE];

  /* Task name and type of scan
   */
  printf( "Site name: '%s'", nullify( ihead_a->icf.sSitename, 16 ) ) ;
  printf( ",  Task name: '%s'\n", nullify(ihead_a->tcf.end.stname,12) );

  printf( "Scan: %s", scan_name10( ihead_a->tcf.scan.iscan ) );
  /* Now follow with scan dependent information */
  switch( ihead_a->tcf.scan.iscan )
    {
    case TASK_SCAN_RHI:
      printf( ",   Lowest Speed: %.2f deg/sec,  Table Resolution:%.2f deg\n",
	      fDegFromBin2(ihead_a->tcf.scan.iscan_speed),
	      ((FLT4)ihead_a->tcf.scan.ires1000)/1000.0 );
      break;

    case TASK_SCAN_PPI:
    case TASK_SCAN_CONT:
      printf( ",   Speed: %.2f deg/sec,  Resolution:%.2f deg\n",
	      fDegFromBin2(ihead_a->tcf.scan.iscan_speed),
	      ((FLT4)ihead_a->tcf.scan.ires1000)/1000.0 );
      break;

    default:
    case TASK_SCAN_MANUAL:
      printf( "\n" ) ;
      break;
    }
  switch( ihead_a->tcf.scan.iscan )
    {
    case TASK_SCAN_RHI:
      printf( "Elevation limits: %.1f to %.1f deg\n",
	      fElDegFromBin2(ihead_a->tcf.scan.u.rhi.istartel),
	      fElDegFromBin2(ihead_a->tcf.scan.u.rhi.iendel) );
      break;

    case TASK_SCAN_PPI:
      printf( "Azimuth limits: %.1f to %.1f deg\n",
	      fPDegFromBin2(ihead_a->tcf.scan.u.ppi.istartaz),
	      fPDegFromBin2(ihead_a->tcf.scan.u.ppi.iendaz) );
      break;

    }

  /* Description of task
   */
  printf( "Description: '%s'\n", nullify( ihead_a->tcf.end.sdscript, 80 ) ) ;

  /* Latitude and longitude, and altitude
   */
  printf( "Location: %s %s,  Altitude: %d meters, Melting height:", 
	  srdeg_to_lat( fDegFromBin4( ihead_a->icf.ilat ) ),
	  srdeg_to_lon( fDegFromBin4( ihead_a->icf.ilon ) ),
	  (50+ihead_a->icf.ialtitude)/100 );

  if (IC_MELTING_UNKNOWN == ihead_a->icf.iMeltingHeight)
    printf( "Unknown\n" );
  else
    {
      SINT2 iMeltingHeight = (0x8000 ^ ihead_a->icf.iMeltingHeight );
      printf( "%d meters\n", iMeltingHeight );
    }

  /* Starting time */
  TZNameBuild( sRecordTZName, ihead_a->icf.sLocalTZName, ihead_a->icf.iMinutesWest,
	       ihead_a->icf.VolumeYmds.imills );
  TZNameBuild( sLocalTZName, ihead_a->icf.sLocalTZName, ihead_a->icf.iLocalWest, 0);
  printf( "Volume Time: %s %s (%d min. west) (LT: %s %d min.)\n",
	  shhmmssmmm_ddmonyyyy( &ihead_a->icf.VolumeYmds, sTimeBuffer ), sRecordTZName, 
	  ihead_a->icf.iMinutesWest, sLocalTZName, ihead_a->icf.iLocalWest ) ;

  /* Processing flags and noise levels
   */
  printf("Flags: " );
  if( TCF_CAL_ZSPECKLE       & ihead_a->tcf.cal.iflags ) printf( "SPT, "   );
  else                                                   printf( "spt, "   );
  if( TCF_CAL_VSPECKLE       & ihead_a->tcf.cal.iflags ) printf( "SPV, "   );
  else                                                   printf( "spv, "   );
  if( TCF_CAL_3LAG_WIDTHS    & ihead_a->tcf.cal.iflags ) printf( "3LAG, "  );
  else                                                   printf( "3lag, "  );
  if( TCF_CAL_SHIP_COR       & ihead_a->tcf.cal.iflags ) printf( "SHIP, "  ); 
  else                                                   printf( "ship, "  );
  if( TCF_CAL_VC_UNFOLD      & ihead_a->tcf.cal.iflags ) printf( "UNFOLD, "); 
  else                                                   printf( "unfold, ");
  if( TCF_CAL_VC_FALLSPD     & ihead_a->tcf.cal.iflags ) printf( "FALL, "  ); 
  else                                                   printf( "fall, "  );
  if( TCF_CAL_ZC_BEAMBLOCK   & ihead_a->tcf.cal.iflags ) printf( "BLOCK, " ); 
  else                                                   printf( "block, " );
  if( TCF_CAL_ZC_ATTENUATION & ihead_a->tcf.cal.iflags ) printf( "ATTN, "  ); 
  else                                                   printf( "attn, "  );
  if( TCF_CAL_ZC_TARGET_DET  & ihead_a->tcf.cal.iflags ) printf( "TARGET\n"  ); 
  else                                                   printf( "target\n"  );

  /* PRF and Wavelength
   */
  switch( ihead_a->tcf.dsp.itrig )
    {
    default:
      printf( "PRF: %dHz", ihead_a->tcf.dsp.iprf );
      break;
    case PRF_2_3:
      iLowPRF = (1+2*ihead_a->tcf.dsp.iprf)/3;
      printf( "PRF: %d/%dHz", ihead_a->tcf.dsp.iprf, iLowPRF );
      break;
    case PRF_3_4:
      iLowPRF = (2+3*ihead_a->tcf.dsp.iprf)/4;
      printf( "PRF: %d/%dHz", ihead_a->tcf.dsp.iprf, iLowPRF );
      break;
    case PRF_4_5:
      iLowPRF = (2+4*ihead_a->tcf.dsp.iprf)/5;
      printf( "PRF: %d/%dHz", ihead_a->tcf.dsp.iprf, iLowPRF );
      break;
    case PRF_SEQ0: printf( "PRF: Seq#0" ) ; break ;
    case PRF_SEQ1: printf( "PRF: Seq#1" ) ; break ;
    case PRF_SEQ2: printf( "PRF: Seq#2" ) ; break ;
    case PRF_SEQ3: printf( "PRF: Seq#3" ) ; break ;
    }
  printf( ",  PulseWidth: %.2f usec (%d)\n",
	  ((FLT4)ihead_a->tcf.dsp.ipw) / 100.0,
	  (ihead_a->GParm.ipw_now & 3) ) ;

  printf( "BeamWidth: %.2f/%.2f deg.\n",
	  fPDegFromBin4(ihead_a->tcf.misc.iHorzBeamWidth),
	  fPDegFromBin4(ihead_a->tcf.misc.iVertBeamWidth) );
  
  printf( "Radar constant: %.2f/%.2f dB, Receiver bandwidth %d kHz.\n",
	  ((FLT4)ihead_a->tcf.cal.iRadarConstantHoriz)/100.0,
	  ((FLT4)ihead_a->tcf.cal.iRadarConstantVert)/100.0,
	  ihead_a->tcf.cal.iReceiverBandwidth );
  
  printf( "Calibration I0: %.2f/%.2f dBm, with noise %.2f/%.2f dBm.\n",
	  ((FLT4)ihead_a->tcf.cal.iI0Horiz)/100.0,
	  ((FLT4)ihead_a->tcf.cal.iI0Vert)/100.0,
	  ((FLT4)ihead_a->tcf.cal.iCalNoiseHoriz)/100.0,
	  ((FLT4)ihead_a->tcf.cal.iCalNoiseVert)/100.0 );
  
  printf( "LOG-Noise: %.4f, Lin-Noise: %.4f, I-Off: %.4f, Q-Off: %.4f\n",
	  ((FLT8)(ihead_a->GParm.log_nse      )) / 16384.0,
	  ((FLT8)(ihead_a->GParm.zlin_noise   )) / 16384.0,
	  fGetNoiseLevelDC( &ihead_a->GParm, FALSE ),
	  fGetNoiseLevelDC( &ihead_a->GParm, TRUE  ) ) ;

  printf( "SOPRM Flags: 0x%4.4x, LOG Slope: %.3f, Z-Cal: %.2fdBZ, H/V: %.2f dB\n",
	  ihead_a->GParm.iflags,
	  ((FLT4)ihead_a->GParm.iz_slope) / 4096.0,
	  ((FLT4)ihead_a->GParm.iz_calib) / 16.0,
	  ((FLT4)(ihead_a->GParm.inse_hv_ratio)) / 100.0 ) ;

  /* Clutter filter and thresholding
   */
  printf("Filters: Dop:%d, Log:%d;  PntClt: %d, Thresh: %3.1f dB;  Samples: %d\n",
	 ihead_a->tcf.dsp.idop_filter_first,
	 ihead_a->tcf.dsp.ilog_filter_first,
	 0x000f&ihead_a->tcf.cal.iPointClutterSkip,
	 (1.0/100.0)*(FLT4)ihead_a->tcf.cal.iPointClutterThreshold,
	 ihead_a->tcf.dsp.isamp ) ;
  printf("Processing Mode: %s\n",
	 DspStringFromPMode( sString, ihead_a->tcf.dsp.imajor_mode, NULL ) );
  if (PMODE_BATCH == ihead_a->tcf.dsp.imajor_mode )
    {
      FLT4 fLowPrf, fdBRefl, fdBVel, fdBWid;
      fLowPrf  =  (FLT4)ihead_a->tcf.dsp.u.batch.iLoPrf;
      fLowPrf += ((FLT4)ihead_a->tcf.dsp.u.batch.iLoPrfFract)/65536.0;
      fdBRefl  = ((FLT4)ihead_a->tcf.dsp.u.batch.idBOverRefl)/100.0;
      fdBVel   = ((FLT4)ihead_a->tcf.dsp.u.batch.idBOverVel)/100.0;
      fdBWid   = ((FLT4)ihead_a->tcf.dsp.u.batch.idBOverWid)/100.0;
      printf("Batch Prf: %4.2f, Samples: %d, Avg: %d, dB Over: %3.1fZ, %3.1fV, %3.1fW\n",
	     fLowPrf, ihead_a->tcf.dsp.u.batch.iLoSampleSize,
	     ihead_a->tcf.dsp.u.batch.iLoAvgSide, fdBRefl, fdBVel, fdBWid );
    }

  printf("Zdr Threshold: %-15.15s", dsp_string_from_tcf(ihead_a->tcf.cal.izdr_tcf) );
  printf("      GDR = %4.2f dB,  XDR = %4.2f dB\n",
	 (1.0/16.0 )*(FLT4)ihead_a->tcf.cal.izdr_bias,
	 (1.0/100.0)*(FLT4)ihead_a->tcf.cal.ildr_bias );

  printf("  T Threshold: %-15.15s", dsp_string_from_tcf(ihead_a->tcf.cal.iuz_tcf) );
  printf("      LOG = %4.1f dB\n", (1.0/16.0)*(FLT4)ihead_a->tcf.cal.izns_thr );

  printf("  Z Threshold: %-15.15s", dsp_string_from_tcf(ihead_a->tcf.cal.icz_tcf) );
  printf("      SIG = %4.1f dB\n", (1.0/16.0)*(FLT4)ihead_a->tcf.cal.isig_thr );

  printf("  V Threshold: %-15.15s", dsp_string_from_tcf(ihead_a->tcf.cal.ivl_tcf) );
  printf("      CSR = %4.1f dB\n",-(1.0/16.0)*(FLT4)ihead_a->tcf.cal.iccr_thr );

  printf("  W Threshold: %-15.15s", dsp_string_from_tcf(ihead_a->tcf.cal.iwd_tcf) );
  printf("      SQI = %4.2f\n", (1.0/256.0)*(FLT4)ihead_a->tcf.cal.isqi_thr );

  /* List the available data
   */

  printf( "Available moments are:" );
  for ( ii=0; ii<NUM_DEFINED_DATA; ii++)
    {
      if( lDspMaskTest( &ihead_a->tcf.dsp.DataMask, ii ) ) printf( " %s", sdata_name6(ii) );
    }
  printf( "\n" );

  if (!lDspMaskEmpty(&ihead_a->tcf.dsp.OriginalDataMask))
    {
      printf( "Original moments were:" );
      for ( ii=0; ii<NUM_DEFINED_DATA; ii++)
	{
	  if( lDspMaskTest( &ihead_a->tcf.dsp.OriginalDataMask, ii ) ) printf( " %s", sdata_name6(ii) );
	}
      printf( "\n" );
    }

  printf( "Starting range %5.3f km, range bin spacing %2.0f meters\n",
	  ((FLT4)ihead_a->tcf.rng.ibin_first)/100000.0,
	  ((FLT4)ihead_a->tcf.rng.ibin_out_step)/100.0 ) ;
  /* Now follow with scan dependent information */
  switch( ihead_a->tcf.scan.iscan )
    {
    case TASK_SCAN_PPI:
    case TASK_SCAN_RHI:
    case TASK_SCAN_CONT:
      /* Sweep count, ray count, and bin count.
       */
      isweeps = ihead_a->tcf.scan.isweeps;

      printf( "There are %d sweeps, each having %d rays and %d bins\n",
	     isweeps, ihead_a->icf.irtotl,
	     ihead_a->tcf.rng.ibin_out_num ) ;

      /* list of the sweep angles.  This may take more than one line
       */
      printf( "Angle list: " ) ;
      for (istart=0; istart<isweeps; istart+=13 )
	{
	  ithisline = MIN( 13, isweeps-istart );

	  /* Convert angles into a local array to allow optional sign conversion
	   */
	  for ( ii=0; ii<ithisline; ii++)
	    {
	      if (ihead_a->tcf.scan.iscan == TASK_SCAN_RHI)
		fangle = fPDegFromBin2(ihead_a->tcf.scan.u.rhi.iazlst[istart+ii]);
	      else
		fangle = fElDegFromBin2(ihead_a->tcf.scan.u.ppi.iellst[istart+ii]);
	      printf( "%6.1f", fangle );
	    }
	  printf("\n");
	}
      break;

    case TASK_SCAN_MANUAL:
      if (1&ihead_a->tcf.scan.u.man.iman_flags)
	printf( "Ray count: %d, Continuous\n", ihead_a->tcf.scan.ires1000 );
      else
	printf( "Ray count: %d, Non-Continuous\n", ihead_a->tcf.scan.ires1000 );
      break;
    }
}
/* ====================================================================
 * This is a program to print out ray data from a single ray passed to it.
 * Entry parameters:
 *     tco_a        - Task Configuration structure
 *     ray_a        - Ray of data
 *     header_a     - The ingest_data_header
 *     inumber_a    - Ray number
 *     lmissing_a   - True if the ray is missing
 *     ing_sum_a    - INGEST header structure
 */
void raylist
(const struct task_configuration *tco_a,
 const struct data_ray *ray_a,
 const struct ingest_data_header *header_a,
 SINT4 inumber_a,
 UINT2 lmissing_a,
 SINT4 istart_bin_a,   /* Bin number to start displaying at, origin 1 */
 const struct ingest_header *ing_sum_a,
 SINT4 iMaxLength_a )     /* Maximum line length */
{      
      
  FLT4 fazstart, fazstop, felstart, felstop;
  UINT2 iday, ihour, iminute, isecond;
  SINT4 ii, imills, itimems;
  struct ymds_time time;

/* ------------------------------------------------------------- */

  if (lmissing_a)
    {
      printf("#%3d  Missing\n", inumber_a );
      if (istart_bin_a > 0) printf("\n" );  /* Help printout look nice */
      return;
    }

  fazstart = fPDegFromBin2( ray_a->hdr.iaz_start );
  fazstop  = fPDegFromBin2( ray_a->hdr.iaz_end );
  felstart = fElDegFromBin2( ray_a->hdr.iel_start );
  felstop  = fElDegFromBin2( ray_a->hdr.iel_end );
  printf( "#%3d  Az:%7.2f,%7.2f   El:%7.2f,%7.2f   Size:%4d  ", inumber_a,
	  fazstart, fazstop, felstart, felstop, ray_a->hdr.ibincount );
  time = header_a->time;
  if (header_a->idata==DB_XHDR)
    {

      /* Copy to int*4, since it may me misaligned */
      memmove( &itimems, &ray_a->data.xh0.itimems, 4 );

      ymds_add_seconds( itimems/1000, &time );
      unpacktime( time.isec, &iday, &ihour, &iminute, &isecond );
      imills = itimems % 1000;
      printf( "%2d:%02d:%02d.%03d\n", ihour, iminute, isecond, imills );
    }
  else
    {
      ii = ray_a->hdr.itime;
      ymds_add_seconds( ii, &time );
      unpacktime( time.isec, &iday, &ihour, &iminute, &isecond );
      printf( "%2d:%02d:%02d\n", ihour, iminute, isecond );
    }

  /* Skip if start bin is zero */
  if (istart_bin_a > 0)
    {

      if (header_a->idata == DB_XHDR)
	{
	  if (1 == ing_sum_a->tcf.dsp.DataMask.iXhdrType )
	    rays_list_xh1( tco_a, &ray_a->data.xh1 );
	}
      else
	{
	  struct data_convert convert;

	  FillDataConvert( &convert, ing_sum_a->tcf.dsp.iprf, ing_sum_a->tcf.dsp.itrig, 
			   ing_sum_a->tcf.misc.ilambda, ing_sum_a->tcf.misc.ipolar,
			   0 ) ;

	  ShowRayData( &ray_a->data, header_a->idata, istart_bin_a-1, &convert,
		       iMaxLength_a );
	}
    }
  return;
}

/* ===============================================================================
 * List all the information in the extended header version 1
 */
static void rays_list_xh1
(const struct task_configuration *tco_a,
 const struct extended_header_v1 *hdr_a )
{
  FLT4 fnyquist;
  struct extended_header_v1 hdr;
#ifdef INU_ANGLE_TEST
  struct rcp8_inu_params InuParams;  /* Rotation Matrix */
#endif  /* #ifdef INU_ANGLE_TEST */

  /* -------------------------------------------------------------------------- */
  /* Copy to local version, since it may be misaligned. */
  memmove( &hdr, hdr_a, EXT_RHED_V1_SIZE );

  fnyquist = fNyquistVelocity
    ( tco_a->dsp.iprf, tco_a->dsp.itrig, tco_a->misc.ilambda, tco_a->misc.ipolar ) ;


#ifdef INU_ANGLE_TEST
  memset( &InuParams, 0, sizeof(InuParams) );
  fill_inu_params( hdr_a, &InuParams );  /* Matrix filled in here */
  calc_inu_params_ped( hdr.itrain, hdr.ielor, &InuParams );

  printf("   PAz:%7.2f PEl:%7.2f Pitch:%7.2f Roll:%7.2f Head:%7.2f EAz:%5.1f EEl:%6.2f\n",
	 fPDegFromBin2(hdr.itrain), fElDegFromBin2(hdr.ielor), 
	 fDegFromBin2(hdr.ipitch),
	 fDegFromBin2(hdr.iroll), fPDegFromBin2(hdr.iheading),
	 (180.0/FPI)*InuParams.fEarthAz,
	 (180.0/FPI)*InuParams.fEarthEl );
#else

  printf("      Az:%7.2f    El:%7.2f Pitch:%10.2f Roll:%10.2f Head:%7.2f\n",
	 fPDegFromBin2(hdr.iaz), fElDegFromBin2(hdr.iel), fDegFromBin2(hdr.ipitch),
	 fDegFromBin2(hdr.iroll), fPDegFromBin2(hdr.iheading) );

#endif  /* #ifdef INU_ANGLE_TEST */

  printf("     Vel:%7.2f deg/s %7.2f       %10.2f      %10.2f      %7.2f\n",
	 fDegFromBin2(hdr.ivel_az), fDegFromBin2(hdr.ivel_el),
	 fDegFromBin2(hdr.ivel_pitch), fDegFromBin2(hdr.ivel_roll),
	 fDegFromBin2(hdr.ivel_heading) );

  printf("      Tr:%7.2f El_or:%7.2f   Lat:%s Long:%s  Alt:%7d\n",
	 fPDegFromBin2(hdr.itrain), fElDegFromBin2(hdr.ielor), 
	 srdeg_to_lat( fDegFromBin4(hdr.ilatitude) ),
	 srdeg_to_lon( fDegFromBin4(hdr.ilongitude) ), hdr.ialtitude );

  printf( "     Cor:%7.2f   Age:%7d   Vel:%10.2f m/s  %10.2f      %7.2f\n",
	 (fnyquist/128.0)*(FLT4)(hdr.ivel_correction), hdr.iupdate_age,
	 0.01*(FLT4)(hdr.ivel_north), 0.01*(FLT4)(hdr.ivel_east), 
	 0.01*(FLT4)(hdr.ivel_up) );

  printf( "\n" );

  return;
}


#ifdef INU_ANGLE_TEST
/* ------------------------------
 * Routine to setup the local INU storage with all of the matrix terms
 * that are needed to transform vectors later.  This routine should be
 * called once for each new INU record that is received.  RcpStatusMutex_c
 * should be locked when this is called.
 */
static void fill_inu_params
( const struct extended_header_v1 *pXHdr_a,   /* Input data */
  volatile struct rcp8_inu_params *pInuParams_a )  /* Matrix filled in here */
{
  FLT4 fRoll, fPitch, fTheta;  /* Theta is heading minus 90 degrees */

  FLT4 fSinR;    /* SR : sin(roll) */
  FLT4 fCosR;    /* CR : cos(roll) */
  FLT4 fSinP;    /* SP : sin(pitch) */
  FLT4 fCosP;    /* CP : cos(pitch) */
  FLT4 fSinT;    /* ST : sin(theta) */
  FLT4 fCosT;    /* CT : cos(theta) */

  FLT4 fCRCT;    /* Double product terms... */
  FLT4 fCRST;
  FLT4 fCPCT;
  FLT4 fCPST;
  FLT4 fSRCT;
  FLT4 fSRST;
  FLT4 fSPCT;
  FLT4 fSPST;
  FLT4 fCRCP;
  FLT4 fCRSP;
  FLT4 fSRCP;
  FLT4 fSRSP;

  FLT4 fCRCPCT;  /* Triple product terms... */
  FLT4 fSRSPCT;
  FLT4 fCRSPCT;
  FLT4 fSRCPCT;
  FLT4 fCRSPST;
  FLT4 fSRSPST;
  FLT4 fCRCPST;
  FLT4 fSRCPST;
  
  fRoll  = fRadianFromBin2( pXHdr_a->iroll );
  fPitch = fRadianFromBin2( pXHdr_a->ipitch ) - ((FPI/180.0)*0.4);
  fTheta = fRadianFromBin2( pXHdr_a->iheading-0x4000 );

  /* Compute sines and cosines of the INU attitude angles.
   */
  fSinR = sin( fRoll );
  fCosR = cos( fRoll );
  fSinP = sin( fPitch );
  fCosP = cos( fPitch );
  fSinT = sin( fTheta );
  fCosT = cos( fTheta );

  /* Compute all of the double trig product terms
   */
  fCRCT = fCosR*fCosT;
  fCRST = fCosR*fSinT;
  fCPCT = fCosP*fCosT;
  fCPST = fCosP*fSinT;
  fSRCT = fSinR*fCosT;
  fSRST = fSinR*fSinT;
  fSPCT = fSinP*fCosT;
  fSPST = fSinP*fSinT;
  fCRCP = fCosR*fCosP;
  fCRSP = fCosR*fSinP;
  fSRCP = fSinR*fCosP;
  fSRSP = fSinR*fSinP;

  /* Compute all of the triple trig product terms
   */
  fCRCPCT = fCRCP*fCosT;
  fSRSPCT = fSRSP*fCosT;
  fCRSPCT = fCRSP*fCosT;
  fSRCPCT = fSRCP*fCosT;
  fCRSPST = fCRSP*fSinT;
  fSRSPST = fSRSP*fSinT;
  fCRCPST = fCRCP*fSinT;
  fSRCPST = fSRCP*fSinT;

  /* Rotation matrix terms...
   */
  pInuParams_a->fA11 = fCPCT;
  pInuParams_a->fA12 = -fCPST;	/* (Thats the negative of A12) */
  pInuParams_a->fA13 = -fSinP;
  pInuParams_a->fA21 = fCRST - fSRSPCT;
  pInuParams_a->fA22 = fCRCT + fSRSPST;
  pInuParams_a->fA23 = -fSRCP;
  pInuParams_a->fA31 = fSRST + fCRSPCT;
  pInuParams_a->fA32 = fSRCT - fCRSPST;
  pInuParams_a->fA33 = fCRCP;

}

/* ------------------------------
 * Fill in the remaining INU parameters which can be calculated once
 * the pedestal angle is known.  This includes the conversion of
 * pedestal AZ and EL to Earth AZ and EL.  This routine must be called
 * only after the previous inu_params() has been called.
 * rcpStatusMutex_c must be locked when called.
 */
static void calc_inu_params_ped
( BIN2 iPedAz_a,   /* Pedestal azimuth */
  BIN2 iPedEl_a,   /* Pedestal elevation */
  volatile struct rcp8_inu_params *pPrm_a)
{
  FLT4 fPedAz = fRadianFromBin2( iPedAz_a )+((FPI/180.0)*89.7);
  FLT4 fPedEl = fRadianFromBin2( iPedEl_a );

  /* Compute the pedestal antenna vector in (D2,D3,D4) starting from
   * the pedestal AZ and EL angles.
   */
  pPrm_a->fSinPAz = sin(fPedAz);
  pPrm_a->fCosPAz = cos(fPedAz);
  pPrm_a->fSinPEl = sin(fPedEl);
  pPrm_a->fCosPEl = cos(fPedEl);

  pPrm_a->fPedX   = pPrm_a->fCosPAz*pPrm_a->fCosPEl;
  pPrm_a->fPedY   =-pPrm_a->fSinPAz*pPrm_a->fCosPEl;
  pPrm_a->fPedZ   = pPrm_a->fSinPEl;

  /* Compute the earth antenna vector by multiplying the pedestal
   * vector by the transpose of the A matrix.
   */
  pPrm_a->fEarthX = pPrm_a->fA11*pPrm_a->fPedX + pPrm_a->fA21*pPrm_a->fPedY +
                    pPrm_a->fA31*pPrm_a->fPedZ;
  pPrm_a->fEarthY = pPrm_a->fA12*pPrm_a->fPedX + pPrm_a->fA22*pPrm_a->fPedY +
                    pPrm_a->fA32*pPrm_a->fPedZ;
  pPrm_a->fEarthZ = pPrm_a->fA13*pPrm_a->fPedX + pPrm_a->fA23*pPrm_a->fPedY +
                    pPrm_a->fA33*pPrm_a->fPedZ;

  /* Convert Earth vector to Earth AZ. 
   */
  if ((pPrm_a->fEarthX==0.0)&&(pPrm_a->fEarthY==0.0))
    pPrm_a->fEarthAz = 0.0;
  else
    pPrm_a->fEarthAz = (FPI/2.0) - atan2( pPrm_a->fEarthY, pPrm_a->fEarthX );

  /* Convert Earth vector to Earth EL.  Different formulas must be
   * used for high elevation angles versus low angles.
   */
  if (pPrm_a->fEarthZ<0.9)
    pPrm_a->fEarthEl = asin( pPrm_a->fEarthZ );
  else
    {
      FLT4 fEarthH = sqrt(pPrm_a->fEarthX*pPrm_a->fEarthX+pPrm_a->fEarthY*pPrm_a->fEarthY);
      pPrm_a->fEarthEl = atan2( pPrm_a->fEarthZ, fEarthH );
    }
}
#endif  /* #ifdef INU_ANGLE_TEST */
