/* ********************************************************
 * *                                                      *
 * *  Pipe to convert an IRIS product to Ericcson EWIS    *
 * *                                                      *
 * ********************************************************
 * File: utils/ewis/IrisToEwis.C
 *
 *                     COPYRIGHT (c) 2005 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.
 */

/*
 * Note if you are modifying this program, and wish to insert a diagnostic
 * printf, be sure to write it to the error output.
 */

#include <errno.h>
#include <locale.h>
#include <math.h>
#define INCLUDE_STDIO_PROTOTYPES 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INCLUDE_SYSTIME_PROTOTYPES 1
#include <sys/time.h>

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

#include "user_lib.h"
#include "misc_lib.h"
#include "dsp_lib.h"
#include "ericsson_ewis.h"

// #define DEBUG_ON 1
struct scan_info
{
  UINT1 lConvertValid;            /* Data will be converted */
  BIN2  iangle;
  SINT4 iRes;
  SINT4 idtype;
  struct ingest_data_header idhdr;
};

struct command_line
{
  char  sInFile[FILENAME_SIZE];
  char  sInPath[PATHNAME_SIZE];
  char  sOutPath[PATHNAME_SIZE];
  UINT4 iSequence;
};

#define MAX_EWIS_FILES (3)
struct convert_info
{
  struct data_convert Convert;      /* Data convert structure */
  struct ymds_time YmdsUtc;         /* Data time of the file in UTC */
  FLT4   fDataMaps[MAX_EWIS_FILES][256]; /* Data Map used for Polar Volume averaging */
  FLT4   fRangeRes;                 /* Ingest Range resolution in km */          
  SINT4  iOutFileCount;             /* Number of EWIS files & Headers produced */
  UINT4  iIrisDataTypes[MAX_EWIS_FILES]; /* Used for polar volumes only */
};

static struct convert_config CConfig_c;

static void get_raw_bytes
( SINT2 *buf_a, SINT4 icnt_a ) ;

static void ProcessCommandLine
(int iargc, 
 char *sargv[], struct command_line *pcommand_a);

static void Usage( void );

static const struct convert_site_table * FindSiteByName
( const char *sSitename_a,             /* IRIS sitename, NULL terminated */
  const struct convert_config *pCConfig_a ); /* Configuration */

static MESSAGE ConvertProductHeader
( const struct product_hdr *pprod_hdr_a,
  struct ewis_header *pEwisHeader_a,
  const struct convert_config *pCConfig_a,
  struct convert_info *pInfo_a );

static SINT4 GetScale
( const struct product_configuration *pPcf_a );

static SINT4 GetPicType
( UINT4 iPType_a );

static SINT4 GetPicFilType
( const struct product_configuration *pPcf_a );

static SINT4 GetDataType
( UINT4 iIrisType_a );

static void WriteToFile
( const struct ewis_header *pHeader_a,
  const UINT1 *pData_a,
  const struct command_line *pCmd_a,
  SINT4 iOutFile_a );                 /* Output file number, origin 0 */

static void WriteToMetaFile
( const struct ewis_header Headers_a[3],
  const struct command_line *pCmd_a,
  SINT4 iOutFileCount_a );   /* Number of files to produce */

static void BuildEwisFileName
( const struct ewis_header *pHeader_a,
  const struct command_line *pCmd_a,
  char  sDesiredFileName_a[FILENAME_SIZE] );

static MESSAGE ConvertIngestHeader
( const struct raw_product *pRawProd_a,
  struct ewis_header EwisHeaders_a[3],
  const struct convert_config *pCConfig_a,
  struct convert_info *pInfo_a );

static MESSAGE ConvertIngestData
( const struct raw_product *pRawProd_a,
  UINT1 *pData_a,                  /* Write output data here */
  const struct ewis_header *pEwisHeader_a,
  const struct convert_config *pCConfig_a,
  const struct convert_info   *pInfo_a,
  SINT4 iOutFile_a );                  /* Output file number, origin zero */

static void FillOneRay
( UINT1 *pEwisRay, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  SINT4 iSweepDelayMs_a,  /* Time offset to the start of the sweep */
  const struct convert_info *pInfo_a,
  SINT4 iOutFile_a );                   /* Output file number, origin zero */

static void RangeAverageRay
( UINT1 *pEwisRay_a, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  const struct convert_info *pInfo_a,
  SINT4 iOutFile_a );                   /* Output file number, origin zero */

static void RangeAverageVel
( UINT1 *pEwisRay_a, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  const struct convert_info *pInfo_a );

static MESSAGE ConvertCartesianData
( const struct cartesian_product *pprod_a,   /* Input IRIS product */
  UINT1 *pData_a,                            /* Output data buffer */
  const struct ewis_header *pEwisHeader_a ); /* Output Ewis header */

static void MakePsuedoCappi
( UINT1 *pEwisDataSlice_a,    /* 2D data array */
  const UINT1 *pIrisData_a,   /* 3D data array */
  const struct ewis_header *pEwisHeader_a );  /* Header for the XYZ volume */

static MESSAGE SignalHandler
(MESSAGE iStatus_a,
 UINT4 icount_a,
 const UINT4 iSigArgs_a[] );

char  sLog_c[200];  /* Log file buffer */

/* ===============================================================
 * Main program starts here.
 */
int main(int iargc, char *sargv[])
{
  MESSAGE istatus;
  SINT4   icount, iwant_count, iOutFile;
  FILE    *in_file;
  struct command_line     cmd;
  struct convert_info     Info;
  struct ewis_header      Headers[3];  /* Might produce up to 3 files */
  UINT1                  *pData;
  union  generic_product *pInProd;

  setlocale( LC_ALL, "" ) ;

  /* Process the command line arguments */
  ProcessCommandLine( iargc, sargv, &cmd);

  /* Process the config file, if any */
  LoadSetup( NULL, &CConfig_c );

  /* Open the logging file, if any */
  if (CConfig_c.logging.lLogToFile)
  {
    char *sptr;
    char sLogPath[PATHNAME_SIZE];
    sptr = getenv( "IRIS_LOG" );
    if (sptr)
    {
      strncpy( sLogPath, sptr, PATHNAME_SIZE-20 );
      sLogPath[PATHNAME_SIZE-20] = 0;
    }
    else
      strcpy( sLogPath, "/tmp/" );
    strcat( sLogPath, "IrisToEwis.log" );
    if (CConfig_c.logging.lAppendToLog)
      CConfig_c.logging.iLogFile = fopen( sLogPath, "a" );
    else
      CConfig_c.logging.iLogFile = fopen( sLogPath, "w" );
    if (NULL == CConfig_c.logging.iLogFile)
    {
      fprintf( stderr, "Can not open log file: '%s'\n", sLogPath );
      exit(EXIT_FAILURE);
    }
  }

  /* Starting here we can use the LoggingPrintf function */
  sig_establish( SignalHandler, "IrisToEwis" );

  if (CConfig_c.logging.lVerbose)
    {
      char sMessage[80];
      struct full_time_spec Time;
      GetTimeFull( &Time, TIME_FLG_LOCAL );

      sprintf( sMessage, "Verbose Logging On, Version=%s%s, TZ=%s\n", IRIS_VERSION, 
	       IRIS_MINOREV, Time.sRecordTZName );
      LoggingPrintf( &CConfig_c.logging, sMessage);
    }

  if (cmd.sInPath[0]==0)
  {
    LoggingPrintf( &CConfig_c.logging, "Error: missing -ip command line option\n" );
    exit(EXIT_FAILURE);
  }

  if (cmd.sOutPath[0]==0)
  {
    LoggingPrintf( &CConfig_c.logging, "Error: missing -op command line option\n" );
    exit(EXIT_FAILURE);
  }
    
  /********************************************************
  ** Malloc and read the product header into the pInProd.
  ** pInProd is a union generic_product.
  ** struct product_hdr is the first element in generic_product.
  **************************************************************/ 
  pInProd  = (union generic_product *)malloc( sizeof(struct product_hdr));
  in_file = fopen( cmd.sInPath, "r" );
  if (!in_file)
    {
      sig_signal( errno_to_message_va( errno, 1, cmd.sInPath ) );
      exit( EXIT_FAILURE );
    }
  icount =  fread(pInProd, 1, sizeof(struct product_hdr), in_file );
  if (icount != sizeof(pInProd->Header)) 
  {
    sprintf( sLog_c, "Bad read, got%d, wanted%d\n", icount, sizeof(pInProd->Header));
    LoggingPrintf( &CConfig_c.logging, sLog_c );
    exit(EXIT_FAILURE);
  }

  if (CConfig_c.logging.lVerbose)
    {
      sprintf( sLog_c, "Processing file: %s\n", cmd.sInPath );
      LoggingPrintf( &CConfig_c.logging, sLog_c );
    }

  /**************************************************************
  ** Realloc pInProd once we get the size. "pInProd->Header.hdr.ibytes"
  ** This size is: "sizeof(struct product_hdr) + the data".
  ** Once we find the size of the data (iwant_count) then store the
  ** data in the result part "(&pInProd->Header)+1" of 
  ** the union generic_product 
  ************************************************************8*/
  pInProd = (union generic_product*)realloc(pInProd, pInProd->Header.hdr.ibytes);
  iwant_count = pInProd->Header.hdr.ibytes - sizeof(struct product_hdr);
  icount =  fread( (&pInProd->Header)+1, 1, iwant_count, in_file );
  if (icount != iwant_count)
  {
    sprintf( sLog_c, "Bad read2, got%d, wanted%d\n", icount, iwant_count);
    LoggingPrintf( &CConfig_c.logging, sLog_c );
    exit(EXIT_FAILURE);
  }
  fclose(in_file);


  /******************** 
   ** Convert the headers, which include figuring out how many files to make
   ********************/
  switch( pInProd->Header.pcf.iptype )
    {
    case PROD_RAW:
      istatus = ConvertIngestHeader( &(pInProd->Raw), Headers, &CConfig_c, &Info );
#ifdef DEBUG_ON
      {
	sprintf( sLog_c, "ConvertIngestHeader returned %d, bytes:%d\n", istatus,
		 Headers[0].pixel_cnt );
	LoggingPrintf( &CConfig_c.logging, sLog_c );
      }
#endif  /* $ifdef */
      
      break;

    default:
      /*****************************
       ** Convert the Product Header 
       ******************************/
      istatus = ConvertProductHeader(&(pInProd->Header), &Headers[0], &CConfig_c, &Info );
#ifdef DEBUG_ON
      {
	sprintf( sLog_c, "ConvertProductHeader returned %d, bytes:%d\n", istatus,
		 Headers[0].pixel_cnt);
	LoggingPrintf( &CConfig_c.logging, sLog_c );
      }
#endif  /* $ifdef */
      
      break;
  }
  if (istatus != SS_NORMAL) exit(EXIT_FAILURE);

  /* 
   * Convert the data, possibly multiple files.
   */
  for (iOutFile=0; iOutFile<Info.iOutFileCount; iOutFile++)
    {
      pData = (UINT1*)malloc(Headers[iOutFile].pixel_cnt);
      if (!pData)
	{
	  sig_signal( errno_to_message_va( errno, 1, "malloc(ipixel_cnt)" ));
	  exit(EXIT_FAILURE);
	}
      memset( pData, 0, Headers[iOutFile].pixel_cnt );
      
      switch( pInProd->Header.pcf.iptype )
	{
	case PROD_RAW:
	  ConvertIngestData(&pInProd->Raw, pData, &Headers[iOutFile], &CConfig_c, &Info,
			    iOutFile );
#ifdef DEBUG_ON
	  LoggingPrintf( &CConfig_c.logging, "ConvertIngestData %d returned\n", iOutFile );
#endif  /* #ifdef DEBUG_ON */
	  break;
	  
	default:
	  ConvertCartesianData(&pInProd->Cartesian, pData, &Headers[iOutFile] );
#ifdef DEBUG_ON 
	  LoggingPrintf( &CConfig_c.logging, "ConvertCartesianData returned\n" );
#endif  /* #ifdef DEBUG_ON */
	  break;
	}
      
      /*********************************************** 
       ** Write EWIS data to File
       ***********************************************/
      WriteToFile( &Headers[iOutFile], pData, &cmd, iOutFile );
      
      free( pData );
    }

  WriteToMetaFile( Headers, &cmd, Info.iOutFileCount );

  exit( EXIT_SUCCESS );
}


/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iargc_a, 
 char *sargv_a[], struct command_line *pcommand_a)
{
  int iArg;
  memset( pcommand_a, 0, sizeof(*pcommand_a) );

  /* Parse command line options
  */
  for( iArg=1 ; iArg<iargc_a ; iArg++ )
  {
    const char *sArg = sargv_a[iArg];
    /* fprintf( stderr, "Processing %d: '%s'\n", iArg, sArg ); */

    if (0 == strncmp( sArg, "-h", 2 ))
    {
      Usage();
    }
    else if (0 == strncmp( sArg, "-if:", 4 ))
    {
      strncpy( pcommand_a->sInFile, &sArg[4], FILENAME_SIZE-1 );
    }
    else if (0 == strncmp( sArg, "-ip:", 4 ))
    {
      strncpy( pcommand_a->sInPath, &sArg[4], PATHNAME_SIZE-1 );
    }
    else if (0 == strncmp( sArg, "-op:", 4 ))
    {
      strncpy( pcommand_a->sOutPath, &sArg[4], PATHNAME_SIZE-1 );
    }
    else if (0 == strncmp( sArg, "-seq:", 5 ))
    {
      char *sEnd;
      pcommand_a->iSequence = strtoul( sArg+5, &sEnd, 10 );
    }
  }
}

/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "  -ip:<path> -- Specify input pathname\n" );
  fprintf( stderr, "  -op:<path> -- Specify output pathname\n" );
  fprintf( stderr, "  -help      -- Print this list\n" );
  fprintf( stderr, "  -seq:#     -- Specify the sequence number\n" );
  fprintf( stderr, "  (all other options ignored)\n" );
  exit(EXIT_FAILURE) ;
}

/* =====================================================
 * Search to find the sitename.
 */
static const struct convert_site_table * FindSiteByName
( const char *sSitename16_a,             /* IRIS sitename, NULL terminated */
  const struct convert_config *pCConfig_a ) /* Configuration */
{
  SINT4 iSite;
  for (iSite=0; iSite<MAX_PIPE_SITES; iSite++)
  {
    const struct convert_site_table *pSite;
    pSite = pCConfig_a->Sites+iSite;
    if (sig_fnmatch( pSite->sIrisSiteName16, sSitename16_a ))
      return pSite;
  }
  return NULL;
}

/* ===================================================
 * Please signal the reason for the error before returning.
 */
static MESSAGE ConvertProductHeader
( const struct product_hdr *pprod_hdr_a,
  struct ewis_header *pEwisHeader_a,
  const struct convert_config *pCConfig_a,
  struct convert_info *pInfo_a )  /* Filled in here */
{
  const char *sptr;
  FLT4 fOffset;
  char sLog[200];

  memset( pEwisHeader_a, 0, sizeof(*pEwisHeader_a) );
  memset( pInfo_a, 0, sizeof(*pInfo_a) );

  /* Normal products produce just one output. */
  pInfo_a->iOutFileCount = 1;

  /* Make sure that this file really looks like a product.  Check
   * the ID in the product.
   */
  if (pprod_hdr_a->hdr.id != ST_PRODUCT_HDR)
    {
      LoggingPrintf( &CConfig_c.logging, "Error: File header contains invalid ID.\n" ) ;
      return USER_ERROR_SIG;
    }

  PendFillDataConvert( &pInfo_a->Convert, &pprod_hdr_a->end, CONVERT_FLAGS_DEFAULT );

  strncpy( pEwisHeader_a->ident, "PMERAWIS", 8 );
  pEwisHeader_a->header_len = 1;

  /* Convert time to UTC, and pack in ASCII format YYMMDDhhmmss */
  pInfo_a->YmdsUtc = pprod_hdr_a->pcf.ing_time;
  {
    UINT2 iDay, iHour, iMinute, iSecond;
    char sTime[16];
    if (!(pInfo_a->YmdsUtc.imills&YMDS_FLG_UTC))
      {
	SINT4 iSecondsWest = 60*(SINT4)pprod_hdr_a->end.iMinutesWest;
	ymds_add_seconds( iSecondsWest, &pInfo_a->YmdsUtc );
	pInfo_a->YmdsUtc.imills |= YMDS_FLG_UTC;  pInfo_a->YmdsUtc.imills &= ~YMDS_FLG_DST;
      }
    unpacktime( pInfo_a->YmdsUtc.isec, &iDay, &iHour, &iMinute, &iSecond );
    snprintf( sTime, 16, "%02d%02d%02d%02d%02d%02d", pInfo_a->YmdsUtc.iyear%100, 
	      pInfo_a->YmdsUtc.imon, pInfo_a->YmdsUtc.iday, iHour, iMinute, iSecond );
    memcpy( pEwisHeader_a->date_time, sTime, 12 );
  }


  pEwisHeader_a->scale     = GetScale(&pprod_hdr_a->pcf);
  pEwisHeader_a->pic_type  = GetPicType(pprod_hdr_a->pcf.iptype);

  memset( pEwisHeader_a->comment1, ' ', 10 );
  memset( pEwisHeader_a->comment2, ' ', 30 );
  memset( pEwisHeader_a->signature, ' ', 2 );
  {
    char sSitename20[20];
    const struct convert_site_table *pSite;

    nullify_ncpy( sSitename20, pprod_hdr_a->end.sing_sitename, 16 );
    pSite = FindSiteByName( sSitename20, &CConfig_c );
    /* Check to see if we can find the sitename in the site mapping table.  Note
     * that wildcards are allowed.
     */
    if (!pSite)
      {
	sprintf( sLog, "Error: IRIS sitename '%s' not found in IrisToEwis.conf SITE list\n", 
		 sSitename20 );
	LoggingPrintf( &CConfig_c.logging, sLog );
	return USER_ERROR_SIG;
      }
    spacify_ncpy( pEwisHeader_a->place, pSite->sEwisPlace20, 20 );
  }
  if (pCConfig_a->lLatitudeFirst)
    {
      pEwisHeader_a->geo_lat   = fDegFromBin4(pprod_hdr_a->end.ilat_center);      
      pEwisHeader_a->geo_lon   = fDegFromBin4(pprod_hdr_a->end.ilon_center);
    }
  else
    {
      pEwisHeader_a->geo_lat   = fDegFromBin4(pprod_hdr_a->end.ilon_center);      
      pEwisHeader_a->geo_lon   = fDegFromBin4(pprod_hdr_a->end.ilat_center);
    }

  fOffset = ((FLT4)pprod_hdr_a->pcf.ixrad-(500*pprod_hdr_a->pcf.ixsize))/1000.0;
  pEwisHeader_a->radar_x     = fOffset*((FLT4)pprod_hdr_a->pcf.ixscale)/100000.0;
  fOffset = ((FLT4)pprod_hdr_a->pcf.iyrad-(500*pprod_hdr_a->pcf.iysize))/1000.0;
  pEwisHeader_a->radar_y     = fOffset*((FLT4)pprod_hdr_a->pcf.iyscale)/100000.0;
  pEwisHeader_a->ne          = pprod_hdr_a->pcf.ixsize;
  pEwisHeader_a->e_inc       = ((FLT4)pprod_hdr_a->pcf.ixscale)/100000.0;
  if (pEwisHeader_a->e_inc > 1.5 )
    pEwisHeader_a->radar_type = EWIS_RADAR_TYPE_NONDOP;
  else
    pEwisHeader_a->radar_type = EWIS_RADAR_TYPE_DOPPLER;
  pEwisHeader_a->nn          = pprod_hdr_a->pcf.iysize;
  pEwisHeader_a->n_inc       = ((FLT4)pprod_hdr_a->pcf.iyscale)/100000.0;
  pEwisHeader_a->nv          = pprod_hdr_a->pcf.izsize;
  /* 3-D products get an extra slice */
  if (pEwisHeader_a->nv>1) pEwisHeader_a->nv+=1;
  pEwisHeader_a->v_inc       = ((FLT4)pprod_hdr_a->pcf.izscale)/100000.0;
  /* Pixel count is set below depending on the product type */
  pEwisHeader_a->store_min   = 0;
  pEwisHeader_a->store_max   = 255;
  pEwisHeader_a->store_bits  = 8;     /* Number of bits per datum */
  pEwisHeader_a->store_align = 8;    /* Number of allocated bits per datum */
  pEwisHeader_a->rain_const1 = ((FLT4)pprod_hdr_a->pcf.izr_const)/1000.0;
  pEwisHeader_a->rain_const2 = ((FLT4)pprod_hdr_a->pcf.izr_exp)/1000.0;

  pEwisHeader_a->geo_height = 
    ((FLT4)(pprod_hdr_a->end.ignd_hgt + pprod_hdr_a->end.irad_hgt))/1000.0;

  pEwisHeader_a->pic_fil_typ = GetPicFilType(&pprod_hdr_a->pcf);
  switch(pEwisHeader_a->pic_fil_typ)
    {
    case EWIS_PIC_FIL_POLAR_VOLUME:
      /* Zero the pixel sizes, since they are not used */
      pEwisHeader_a->ne = 0;
      pEwisHeader_a->nn = 0;
      pEwisHeader_a->nv = 0;
      /* This is all filled in in ConvertIngestHeader() */
      break;

    case EWIS_PIC_FIL_HORIZ_PICTURE:
      pEwisHeader_a->store_slope = 
	fDataScalePerBit( pprod_hdr_a->pcf.idata_out, &pInfo_a->Convert );
      pEwisHeader_a->store_ord   = 
	fDataStartValue( pprod_hdr_a->pcf.idata_out, &pInfo_a->Convert );
      sptr = sdata_units6( pprod_hdr_a->pcf.idata_out, CONVERT_FLAGS_DEFAULT );
      spacify_ncpy( pEwisHeader_a->store_quant, sptr, 8 );
      pEwisHeader_a->quant       = GetDataType(pprod_hdr_a->pcf.idata_out);

      pEwisHeader_a->pixel_cnt   = pprod_hdr_a->pcf.ixsize;
      pEwisHeader_a->pixel_cnt  *= pprod_hdr_a->pcf.iysize;
      pEwisHeader_a->pixel_cnt   = 512*((pEwisHeader_a->pixel_cnt+511)/512);

      /* Check that the data type is valid */
      if (pEwisHeader_a->quant == EWIS_DTYPE_NONE )
	{
	  sprintf( sLog, "Error: Data type %s is not supported.\n", 
		   sdata_name6(pprod_hdr_a->pcf.idata_out) );
	  LoggingPrintf( &CConfig_c.logging, sLog );
	  return USER_ERROR_SIG;
	}
      switch( pprod_hdr_a->pcf.iptype)
	{
	case PROD_CAPPI:
	  pEwisHeader_a->Type.horiz.hei = ((FLT4)pprod_hdr_a->pcf.psi.cappi.icappi_hgt)/100000.0;
	  if (pprod_hdr_a->pcf.psi.cappi.icappi_flags&CAPPI_FLG_PSEUDO)
	    pEwisHeader_a->Type.horiz.hei_nr = -1;
	  else
	    pEwisHeader_a->Type.horiz.hei_nr = NINT( pEwisHeader_a->Type.horiz.hei_nr );
	  break;
	case PROD_RAIN1:
	  pEwisHeader_a->Type.horiz.acc_int = (FLT4)pprod_hdr_a->pcf.psi.rain.iSeconds;
	  if (pEwisHeader_a->Type.horiz.acc_int == 60.0)
	    pEwisHeader_a->Type.horiz.acc_int = 3600.0;
	  break;
	case PROD_RAINN:
	  pEwisHeader_a->Type.horiz.acc_int = 
	    3600.0*(FLT4)pprod_hdr_a->pcf.psi.rain.irainn_hours;
	  break;
	case PROD_TOPS:
	  pEwisHeader_a->Type.horiz.threshold = 
	    ((FLT4)pprod_hdr_a->pcf.psi.top.iZThreshold)/16.0;
	  break;
	}
      break;

    case EWIS_PIC_FIL_XYZ_VOLUME:
      pEwisHeader_a->store_slope = 
	fDataScalePerBit( pprod_hdr_a->pcf.idata_out, &pInfo_a->Convert );
      pEwisHeader_a->store_ord   = 
	fDataStartValue( pprod_hdr_a->pcf.idata_out, &pInfo_a->Convert );
      sptr = sdata_units6( pprod_hdr_a->pcf.idata_out, CONVERT_FLAGS_DEFAULT );
      spacify_ncpy( pEwisHeader_a->store_quant, sptr, 8 );
      pEwisHeader_a->quant       = GetDataType(pprod_hdr_a->pcf.idata_out);
      /* Check that the data type is valid */
      if (pEwisHeader_a->quant == EWIS_DTYPE_NONE )
	{
	  sprintf( sLog, "Error: Data type %s is not supported.\n", 
		   sdata_name6(pprod_hdr_a->pcf.idata_out) );
	  LoggingPrintf( &CConfig_c.logging, sLog );
	  return USER_ERROR_SIG;
	}

      {
	SINT4 iSliceSizeBytes, iSliceSizeBlocks;
	FLT4 fCappiStart, fCappiStep;
	/* Each slice contains a 512-byte header */
	iSliceSizeBytes = 512 + (pprod_hdr_a->pcf.ixsize * pprod_hdr_a->pcf.iysize);
	iSliceSizeBlocks = (iSliceSizeBytes+511)/512;
	/* Each slice is positioned on a 512 block boundary. */
	pEwisHeader_a->pixel_cnt = (pprod_hdr_a->pcf.izsize+1)*iSliceSizeBlocks*512;
	fCappiStart = ((FLT4)pprod_hdr_a->pcf.psi.cappi.icappi_hgt)/100000.0;
	fCappiStep  = ((FLT4)pprod_hdr_a->pcf.izscale)/100000.0;

	for (int iSlice=0; iSlice<pEwisHeader_a->nv; iSlice++)
	  {
	    pEwisHeader_a->Type.xyz.block_no[iSlice] = 2+(iSlice*iSliceSizeBlocks);
	    if (iSlice==0)
	      pEwisHeader_a->Type.xyz.pic_level[iSlice] = 2.0;
	    else
	      pEwisHeader_a->Type.xyz.pic_level[iSlice] = fCappiStart + 
		(fCappiStep*(FLT4)(iSlice-1));
	  }
      }
      break;

    default:
      sprintf( sLog, "Error: Product type %s (%d) not supported.\n", 
	       sproduct_name5(pprod_hdr_a->pcf.iptype), pprod_hdr_a->pcf.iptype );
      LoggingPrintf( &CConfig_c.logging, sLog );
      return USER_ERROR_SIG;
    }
    
  return SS_NORMAL;
}

/* ==============================================================
 * The EWIS scale number is based on the max range and resolution
 * of the product. 
 */
static SINT4 GetScale
( const struct product_configuration *pPcf_a )
{
  
  /* Sort by max range first, find the nearest from the set
   * 60, 120, 240, 480, 960, and 1920 */
  FLT4 fMaxRange = fMaxRangeFromPCF( pPcf_a );
  if (fMaxRange<90.0)
    {
      /* 60 km */
      return  EWIS_SCALE_60KM_BY025;
    }
  else if (fMaxRange<180.0)
    {
      /* 120 km */
      if (pPcf_a->ixscale<75000)
	return EWIS_SCALE_120KM_BY05;
      else
	return EWIS_SCALE_120KM_BY1;
    }
  else if (fMaxRange<360.0)
    {
      /* 240 km */
      if (pPcf_a->ixscale<150000)
	return EWIS_SCALE_240KM_BY1;
      else
	return EWIS_SCALE_240KM_BY2;
    }
  else if (fMaxRange<720.0)
    {
      /* 480 km */
      if (pPcf_a->ixscale<300000)
	return EWIS_SCALE_480KM_BY2;
      else
	return EWIS_SCALE_480KM_BY4;
    }
  else if (fMaxRange<1440.0)
    {
      /* 960 km */
      if (pPcf_a->ixscale<300000)
	return EWIS_SCALE_960KM_BY2;
      else
	return EWIS_SCALE_960KM_BY4;
    }
  else
    {
      /* 1920 km */
      if (pPcf_a->ixscale<600000)
	return EWIS_SCALE_1920KM_BY4;
      else
	return EWIS_SCALE_1920KM_BY8;
    }
}

/* ==============================================================
 * The EWIS picture type is based primarily on the IRIS product type
 */
static SINT4 GetPicType
( UINT4 iPType_a )
{
  
  switch( iPType_a )
    {
    case PROD_CAPPI:
      return EWIS_PICTYPE_CAPPI;
      break;
    case PROD_MAX:
      return EWIS_PICTYPE_MAX;
      break;
    case PROD_HMAX:
      return EWIS_PICTYPE_HEIGHT_MAX;
      break;
    case PROD_TOPS:
      return EWIS_PICTYPE_ECHO_TOP;
      break;
    case PROD_RAIN1:
      return EWIS_PICTYPE_ACCUM_HOURLY;
      break;
    case PROD_RAINN:
      return EWIS_PICTYPE_ACCUM;
      break;
    default:
      return EWIS_PICTYPE_NONE;
      break;
    }
}
/* ==============================================================
 * The EWIS picture file type is based primarily on the IRIS product type,
 * with some additional info.
 */
static SINT4 GetPicFilType
( const struct product_configuration *pPcf_a )
{

  switch( pPcf_a->iptype )
    {
    case PROD_RAW: return EWIS_PIC_FIL_POLAR_VOLUME; break;

      /* RAIN1, RAINN, and VIL support in the future due to 16-bit format */
    case PROD_CAPPI: 
      if (pPcf_a->izsize>EWIS_XYZ_MAX_SLICES)
	{
	  sprintf( sLog_c, "Error: Maximum of %d slices in 3D CAPPIs.\n",
		   EWIS_XYZ_MAX_SLICES);
	  LoggingPrintf( &CConfig_c.logging, sLog_c );
	  return EWIS_PIC_FIL_UNKNOWN;
	}
      if (pPcf_a->izsize>1) return EWIS_PIC_FIL_XYZ_VOLUME;

      return EWIS_PIC_FIL_HORIZ_PICTURE;
      break;

    case PROD_TOPS: 
    case PROD_HMAX: 
    case PROD_MAX: 
      return EWIS_PIC_FIL_HORIZ_PICTURE; break;

    default:
      return EWIS_PIC_FIL_UNKNOWN; break;
    }
}
/* ==============================================================
 * The EWIS data type is based primarily on the IRIS data type.
 */
static SINT4 GetDataType
( UINT4 iDataType_a )
{
  switch( iDataType_a )
    {
    case DB_DBZ:
    case DB_DBT:
    case DB_DBZC:
      return EWIS_DTYPE_REFLECTIVITY;  break;

    case DB_VEL:
    case DB_VELC:
      return EWIS_DTYPE_VELOCITY; break;

    case DB_WIDTH:
      return EWIS_DTYPE_WIDTH;  break;

    case DB_HEIGHT:
      return EWIS_DTYPE_HEIGHT_REF; break;

    case DB_ZDR:
      return EWIS_DTYPE_ZDR; break;

    default:
      return EWIS_DTYPE_NONE; break;
    }
}

/* =======================================================
 * Write the data to an EWIS file, do byte swapping as required.
 */
static void WriteToFile
( const struct ewis_header *pHeader_a,
  const UINT1 *pData_a,
  const struct command_line *pCmd_a,
  SINT4 iOutFile_a )                 /* Output file number, origin 0 */
{
  SINT4   icount;
  FILE    *pFile;
  struct  ewis_header_file HeaderFile;
  char    sOutPath[PATHNAME_SIZE];

  FileFromHeader( &HeaderFile, pHeader_a );

  strncpy( sOutPath, pCmd_a->sOutPath, PATHNAME_SIZE-4 );
  /* Modify the output path if we are producing multiple output files */
  if (iOutFile_a>0)
    {
      char sNumber[16];
      sprintf( sNumber, ".%d", iOutFile_a );
      strcat( sOutPath, sNumber );
    }
  
  pFile = fopen( sOutPath, "w" );
  if (!pFile)
    {
      sig_signal( errno_to_message_va( errno, 1, pCmd_a->sOutPath ) );
      exit( EXIT_FAILURE );
    }
  icount = fwrite( &HeaderFile, 1, sizeof(HeaderFile), pFile );
  if (icount != sizeof(HeaderFile)) 
    {
      sprintf( sLog_c, "Bad write, got%d, wanted%d\n", icount, sizeof(HeaderFile));
      LoggingPrintf( &CConfig_c.logging, sLog_c );
      exit(EXIT_FAILURE);
    }

  icount = fwrite( pData_a, 1, pHeader_a->pixel_cnt, pFile );
  if (icount != pHeader_a->pixel_cnt ) 
    {
      sprintf( sLog_c, "Bad write, got%d, wanted%d\n", icount, pHeader_a->pixel_cnt );
      LoggingPrintf( &CConfig_c.logging, sLog_c );
      exit(EXIT_FAILURE);
    }

  icount = fclose( pFile );
  if (icount) sig_signal( errno_to_message_va(errno, 1, "fclose(data)" ) );

}
/* =======================================================
 * Write the meta file which contains suggested output file names.
 */
static void WriteToMetaFile
( const struct ewis_header Headers_a[3],
  const struct command_line *pCmd_a,
  SINT4 iOutFileCount_a )    /* Number of files produced */
{
  SINT4   icount, iOutFile;
  FILE    *pFile;
  char    sMetaPath[PATHNAME_SIZE], sDesiredFileName[FILENAME_SIZE];


  /* Write out the meta file */
  strncpy( sMetaPath, pCmd_a->sOutPath, PATHNAME_SIZE-6 );
  strcat( sMetaPath, ".meta" );
  pFile = fopen( sMetaPath, "w" );
  if (!pFile)
    {
      sig_signal( errno_to_message_va( errno, 1, sMetaPath ) );
    }

  for (iOutFile=0; iOutFile<iOutFileCount_a; iOutFile++)
    {
      BuildEwisFileName( &Headers_a[iOutFile], pCmd_a, sDesiredFileName );

      icount = fprintf( pFile, "sFileName[%d] = %s\n", iOutFile, sDesiredFileName );
      if (icount <=0 ) 
	{
	  LoggingPrintf( &CConfig_c.logging, "Bad write to meta file\n" );
	}
    }
  icount = fclose( pFile );
  if (icount) sig_signal( errno_to_message_va(errno, 1, "fclose(meta)" ) );

}
/* =======================================================
 * Write the meta file which contains suggested output file names.
 */
static void BuildEwisFileName
( const struct ewis_header *pHeader_a,
  const struct command_line *pCmd_a,
  char  sDesiredFileName_a[FILENAME_SIZE] )
{

  /* Make up the desired file name.  The original names consist of:
   * ZDP0PXXXX.PIC, where:
   * Z    = Data type, Z=reflectivity, W=Velocity, T=Width
   * DP0  = Product code, DP0=Doppler Polar volume
   *                      NP0=Non-Doppler polar volume
   *                      2X0=XYZ volume, 1 km res
   *                      1X0=XYZ volume, 2 km res
   *                      2MV=Vertical picture, max, 1 km res
   *                      1MV=Vertical picture, max, 2 km res
   *                      2SH=Horizontal picture, cappi, 1 km res
   *                      1SH=Horizontal picture, cappi, 2 km res
   *                      2MH=Horizontal picture, max, 1 km res
   *                      1MH=Horizontal picture, max, 2 km res
   * P    = Always a "P"
   * XXXX = 4 digit random number
   * .PIC = Always a ".PIC"
   */
  switch( pHeader_a->quant)
    {
    case EWIS_DTYPE_REFLECTIVITY:
    default:
      strcpy( sDesiredFileName_a, "Z" ); break;

    case EWIS_DTYPE_VELOCITY:
      strcpy( sDesiredFileName_a, "W" ); break;

    case EWIS_DTYPE_WIDTH:
      strcpy( sDesiredFileName_a, "T" ); break;
    }

  switch(pHeader_a->pic_fil_typ)
    {
    case EWIS_PIC_FIL_POLAR_VOLUME:
      if (pHeader_a->radar_type == EWIS_RADAR_TYPE_NONDOP)
	strcat( sDesiredFileName_a, "NP0P" );
      else
	strcat( sDesiredFileName_a, "DP0P" );
      break;

    case EWIS_PIC_FIL_HORIZ_PICTURE:
      if (pHeader_a->radar_type == EWIS_RADAR_TYPE_NONDOP)
	strcat( sDesiredFileName_a, "1" );
      else
	strcat( sDesiredFileName_a, "2" );
      switch (pHeader_a->pic_type)
	{
	default:
	case EWIS_PICTYPE_CAPPI:
	  strcat( sDesiredFileName_a, "SHP" );
	  break;
	case EWIS_PICTYPE_MAX:
	  strcat( sDesiredFileName_a, "MHP" );
	  break;
	}
      break;

    case EWIS_PIC_FIL_XYZ_VOLUME:
      if (pHeader_a->radar_type == EWIS_RADAR_TYPE_NONDOP)
	strcat( sDesiredFileName_a, "1X0P" );
      else
	strcat( sDesiredFileName_a, "2X0P" );
      break;

    default:
      strcat( sDesiredFileName_a, "000P" );
      break;
    }

  /* Pull the place name from the data */
  {
    char sPlace[24];
    nullify_ncpy( sPlace, pHeader_a->place, 20 );

    /* Take up to the first 3 characters */
    strncat( sDesiredFileName_a, sPlace, 3 );
  }

  /* Put in time, break with an underscore to help parsing */
  strcat( sDesiredFileName_a, "_" );
  strncat( sDesiredFileName_a, pHeader_a->date_time, 12 );

  /* Add a 4-digit sequence number, and .PIC extension */
  {
    char sSequence[30];
    sprintf( sSequence, "%04u.PIC", pCmd_a->iSequence );
    strcat( sDesiredFileName_a, sSequence );
  }

}

/* ========================================================
 * Fill in the EWIS header based on both the RAW product
 * header and the ingest header within the RAW product.
 */
static MESSAGE ConvertIngestHeader
( const struct raw_product *pRawProd_a,
  struct ewis_header EwisHeaders_a[3],
  const struct convert_config *pCConfig_a,
  struct convert_info *pInfo_a )
{
  MESSAGE iStatus;
  SINT4 isweep ;
  char sLog[200];
  const struct product_hdr *pprod_hdr;
  const struct ingest_header *ping_hdr;
  const struct task_configuration *pTask;
  float fTemp;
  FLT4  fOutRangeRes;
  SINT4 iOutBinCount, iOutFile;


  pprod_hdr = &(pRawProd_a->Record[0].PHeader);
  ping_hdr  = &(pRawProd_a->Record[1].IHeader);
  pTask     = &(ping_hdr->tcf);

  iStatus = ConvertProductHeader( pprod_hdr, &EwisHeaders_a[0], pCConfig_a, pInfo_a ) ;
  if (iStatus!=SS_NORMAL) return iStatus;

  if((ping_hdr->hdr.id != ST_INGEST_HDR ) ||
     (pTask->hdr.id != ST_TASK_CONF  ) )
    {
      LoggingPrintf( &CConfig_c.logging, "Error: Ingest headers contain invalid ID's\n" );
      return USER_ERROR_SIG;
    }
  
  if(pTask->scan.iscan != SCAN_CONT )
    {
      LoggingPrintf( &CConfig_c.logging, "Error: Raw product must have full PPI scans.\n" );
      return USER_ERROR_SIG;
    }

  /* Count up the number of parameters that were recorded.  This is
   * needed in order to skip through the sweeps properly.  Build filter
   * for what will be converted.
   */
  if (!lDspMaskTest( &pTask->dsp.DataMask, DB_DBZ ))
    {
      sprintf( sLog, "Error: Task does not contain %s data.\n", sDataName3(DB_DBZ) );
      LoggingPrintf( &CConfig_c.logging, sLog );
      return USER_ERROR_SIG;
    }

  /* Check data types, flag as Doppler if velocity is available */
  pInfo_a->iIrisDataTypes[0] = DB_DBZ;
  if (lDspMaskTest( &pTask->dsp.DataMask, DB_VEL ))
    {
      /* For Doppler tasks, we dBZ and Velocity, and optionally width outputs */
      EwisHeaders_a[0].radar_type = EWIS_RADAR_TYPE_DOPPLER;
      fOutRangeRes = pCConfig_a->fDopPolarRangeRes;
      iOutBinCount = pCConfig_a->iDopPolarBinCount;
      pInfo_a->iOutFileCount = 2;
      pInfo_a->iIrisDataTypes[1] = DB_VEL;
      if (lDspMaskTest( &pTask->dsp.DataMask, DB_WIDTH ))
	{
	  pInfo_a->iOutFileCount = 3;
	  pInfo_a->iIrisDataTypes[2] = DB_WIDTH;
	}
    }
  else
    {
      /* For non-doppler tasks, we just make a single dBZ output */
      EwisHeaders_a[0].radar_type = EWIS_RADAR_TYPE_NONDOP;
      fOutRangeRes = pCConfig_a->fPolarRangeRes;
      iOutBinCount = pCConfig_a->iPolarBinCount;
      pInfo_a->iOutFileCount = 1;
    }
  
  if (pprod_hdr->pcf.psi.raw.iflags & RAW_FLG_SWEEP)
    {
      LoggingPrintf( &CConfig_c.logging, "Error: RAW product is single sweep\n" );
      return USER_ERROR_SIG;
    }

  /**************************************************************
  ** Fill in fields that we couldn't get from the product header
  ***************************************************************/

  EwisHeaders_a[0].Type.polar.elev_uppb = pTask->scan.isweeps;
  if (EwisHeaders_a[0].Type.polar.elev_uppb>EWIS_POLAR_MAX_SWEEPS)
    {
      LoggingPrintf( &CConfig_c.logging, "Warning: Polar volume limited to the first 20 cust\n" );
      EwisHeaders_a[0].Type.polar.elev_uppb = EWIS_POLAR_MAX_SWEEPS;
    }

  EwisHeaders_a[0].Type.polar.azim_uppb = ping_hdr->icf.inrev-1;  /* Number of azimuths in a sweep, minus 1 */
  if (iOutBinCount)
    EwisHeaders_a[0].Type.polar.range_uppb = iOutBinCount;
  else
    EwisHeaders_a[0].Type.polar.range_uppb = pTask->rng.ibin_out_num;
  EwisHeaders_a[0].Type.polar.range_lim[0] = 40;
  EwisHeaders_a[0].Type.polar.range_lim[1] = 80;
  EwisHeaders_a[0].Type.polar.range_lim[2] = 120;
  pInfo_a->fRangeRes = ((FLT4)pTask->rng.ibin_out_step)/100000.0;
  if (0.0 == fOutRangeRes)
    {
      EwisHeaders_a[0].Type.polar.range_siz[0] = pInfo_a->fRangeRes;
      EwisHeaders_a[0].Type.polar.range_siz[1] = pInfo_a->fRangeRes;
      EwisHeaders_a[0].Type.polar.range_siz[2] = pInfo_a->fRangeRes;
    }
  else
    {
      EwisHeaders_a[0].Type.polar.range_siz[0] = fOutRangeRes;
      EwisHeaders_a[0].Type.polar.range_siz[1] = fOutRangeRes;
      EwisHeaders_a[0].Type.polar.range_siz[2] = fOutRangeRes;
    }
  EwisHeaders_a[0].Type.polar.scan_size = EwisHeaders_a[0].Type.polar.range_uppb +4;

  SINT4 iSingleSizeBytes = EwisHeaders_a[0].Type.polar.scan_size*ping_hdr->icf.inrev;
  SINT4 iSingleSizeBlocks = (iSingleSizeBytes+511)/512;
  for (isweep=0; isweep<EwisHeaders_a[0].Type.polar.elev_uppb; isweep++)
    {
      /* Only coding up the PPI case here */
      fTemp = fElDegFromBin2(pTask->scan.u.ppi.iellst[isweep]);
      /* Round to the nearest 1/100 of a degree */
      fTemp = 0.01 * floor(0.5 + (100.0 * fTemp));
      EwisHeaders_a[0].Type.polar.elev[isweep] = fTemp;
      EwisHeaders_a[0].Type.polar.elev_block_nr[isweep] = 2+(isweep*iSingleSizeBlocks);
    }
  EwisHeaders_a[0].pixel_cnt = EwisHeaders_a[0].Type.polar.elev_uppb*512*iSingleSizeBlocks;

  /* Fill in fields which are different for the different output files */
  for (iOutFile=0; iOutFile<pInfo_a->iOutFileCount; iOutFile++ )
    {
      struct ewis_header *pEwisHeader = &EwisHeaders_a[iOutFile];
      UINT4 iIrisDataType = pInfo_a->iIrisDataTypes[iOutFile];

      /* Copy the base header */
      if (iOutFile>0) *pEwisHeader = EwisHeaders_a[0];

      /* Data type dependent fields are different */
      pEwisHeader->store_slope = fDataScalePerBit( iIrisDataType, &pInfo_a->Convert );
      pEwisHeader->store_ord   = fDataStartValue( iIrisDataType, &pInfo_a->Convert );
      char *sptr = sdata_units6( iIrisDataType, CONVERT_FLAGS_DEFAULT );
      spacify_ncpy( pEwisHeader->store_quant, sptr, 8 );
      pEwisHeader->quant       = GetDataType(iIrisDataType);
      
      for (int iData=0; iData<256; iData++)
	{
	  pInfo_a->fDataMaps[iOutFile][iData] = 
	    fdata_to_user( iData, iIrisDataType, &pInfo_a->Convert );
	}
    }


  return SS_NORMAL;
}


static SINT4  irec_c ;          /* Record number */
static SINT4  ioff_c ;          /* Offset within record */
static const struct raw_product *pRaw_c;  /* Pointer to product */

#define PRODPTR (((const UINT1 *)(pRaw_c->Record[irec_c].Data2)) + ioff_c)


/* ========================================================
 *
 */
static MESSAGE ConvertIngestData
( const struct raw_product *pRawProd_a,
  UINT1 *pEwisData_a,                  /* Write output data here */
  const struct ewis_header *pEwisHeader_a,
  const struct convert_config *pCConfig_a,
  const struct convert_info   *pInfo_a,
  SINT4 iOutFile_a )                   /* Output file number, origin zero */
{
  SINT4 iIrisMoments, irptr, isweep;
  char sLog[200];
  struct scan_info MInfo[MAX_MOMENTS];
  const struct product_hdr *pprod_hdr;
  const struct ingest_header *ping_hdr;
  const struct task_configuration *pTask;
  UINT4 iEwisMoments = 0;
  UINT4 iIrisDataType = pInfo_a->iIrisDataTypes[iOutFile_a];
  UINT4 ii;

  pRaw_c = pRawProd_a; 
  pprod_hdr = &(pRaw_c->Record[0].PHeader);
  ping_hdr  = &(pRaw_c->Record[1].IHeader);
  pTask     = &(ping_hdr->tcf);

  /* Count up the number of parameters that were recorded.  This is
   * needed in order to skip through the sweeps properly.  Build filter
   * for what will be converted.
   */

  iEwisMoments=0;
  iIrisMoments=0;
  for( ii=0 ; ii <NUM_DEFINED_DATA ; ii++ )
    {
    if( lDspMaskTest( &pTask->dsp.DataMask, ii ) && (iIrisMoments<MAX_MOMENTS) )
      {
	memset( MInfo+iIrisMoments, 0, sizeof(struct scan_info) );
	MInfo[iIrisMoments].idtype = ii ;
	MInfo[iIrisMoments].lConvertValid = (ii==iIrisDataType);
	//   printf("data type %d VALID %d\n", ii, MInfo[iIrisMoments].lHDF5Valid);
	if (MInfo[iIrisMoments].lConvertValid) iEwisMoments++;
	iIrisMoments++ ;
      }
    }
  

  /* Initialize record pointer to the first data record (third one in)
   * and loop for each sweep that was recorded.  We will scan through
   * the entire product (to check for errors), and will convert all of it.
   */
  irec_c = 2 ;                  /* Record number */
  ioff_c = 0 ;                  /* Offset within record */

  for( isweep=0; isweep<pEwisHeader_a->Type.polar.elev_uppb; isweep++ )
    {
      SINT4 iIrisMoment, iSweepDelayMs;
      UINT1 *pEwisSweep;

      pEwisSweep = pEwisData_a + 512*(pEwisHeader_a->Type.polar.elev_block_nr[isweep]-2);
      
      /* Extract the INGEST data file headers for each of the parameters
       * that were recorded.  The headers appear sequentially in the
       * first record of each sweep.
       */
      for( iIrisMoment=0 ; iIrisMoment<iIrisMoments ; iIrisMoment++ )
	{
	  get_raw_bytes( (SINT2 *)&MInfo[iIrisMoment].idhdr, INGEST_DATA_HEADER_SIZE ) ;
	  if( MInfo[iIrisMoment].lConvertValid )
	    {
	      MInfo[iIrisMoment].iRes = idata_resolution( MInfo[iIrisMoment].idtype );
	    }
	}
      /* Check for a valid sweep */
      if (!MInfo[0].idhdr.isweep)
	{
	  sprintf( sLog, "Error, Sweep %d missing from RAW product\n", isweep+1 );
	  LoggingPrintf( &pCConfig_a->logging, sLog );
	  exit(EXIT_FAILURE);
	}

      if (CConfig_c.logging.lVerbose)
	{
	  char sTimeBuf[TIMENAME_SIZE];
	  sprintf( sLog, "Sweep %d began at: %s\n", MInfo[0].idhdr.isweep,
		   shhmmssddmonyyyy_r( &MInfo[0].idhdr.time, sTimeBuf ) ) ;
	  LoggingPrintf( &pCConfig_a->logging, sLog );
	  
	}
      /* Convert sweep start time to UTC */
      {
	struct ymds_time SweepYmdsUtc = MInfo[0].idhdr.time;
	SINT4 iSecondsWest = 60*(SINT4)ping_hdr->icf.iMinutesWest;
	if (!(SweepYmdsUtc.imills&YMDS_FLG_UTC))
	  {
	    ymds_add_seconds( iSecondsWest, &SweepYmdsUtc );
	    SweepYmdsUtc.imills |= YMDS_FLG_UTC; SweepYmdsUtc.imills &= ~YMDS_FLG_DST;
	  }
	/* Compute the time delay to this sweep's start from the beginning */
	iSweepDelayMs = iYmdsSubtractMs( &SweepYmdsUtc, &pInfo_a->YmdsUtc );
      }

      /* Read the data from each of the angles, and for each of the
       *  parameters that were recorded.
       */
      for( irptr=0 ; irptr<ping_hdr->icf.irtotl ; irptr++ )
	{
	  UINT1 *pEwisRay;
	  pEwisRay = pEwisSweep+(irptr*pEwisHeader_a->Type.polar.scan_size);
	  for( iIrisMoment=0; iIrisMoment<iIrisMoments; iIrisMoment++ )
	    {
	      struct data_ray ray ;
	      SINT4 inlen, ioutlen ;
	      UINT2 lmissing ;
	      
	      uncompress_cowords
		(get_raw_bytes,
		 pprod_hdr->hdr.ibytes - ((irec_c * TAPE_RECORD_LEN) + ioff_c),
		 &inlen, (SINT2 *)&ray, sizeof(ray)/2, &ioutlen ) ;
	      
	      lmissing = (ioutlen == 0) ;
	      if( MInfo[iIrisMoment].lConvertValid )
		{
		  /******************************************
		   ** Fill data buffer for each moment.
		   *****************************************/
		  if (!lmissing)
		    {
#ifdef DEBUG_ON
		      if ((iIrisMoment==1)&&(isweep==0))
			{
			  BIN2 iAz=iBin2NarrowMidPt(ray.hdr.iaz_start,ray.hdr.iaz_end);
			  FLT4 fAz = fPDegFromBin2(iAz);
			  sprintf( sLog_c, "irptr:%d, Az:%5.2f, bins:%d\n", irptr, fAz,
				   ray.hdr.ibincount );
			  LoggingPrintf( &pCConfig_a->logging, sLog_c );
			}
#endif  /* #ifdef DEBUG_ON */
		      FillOneRay( pEwisRay, &ray, pEwisHeader_a, iSweepDelayMs, pInfo_a,
				  iOutFile_a );
		    }
		}
	    }
	  /* Write data out to the standard output only if all data was there.*/
	}  /* end  for( irptr=0 ... */
      
      /* Done with this sweep.  Discard the remainder of this block, if
       * any.
       */
      if( ioff_c ) { ioff_c = 0 ; irec_c++ ; }
      
    }  /* end for( isweep = isweeplo... */

  return SS_NORMAL;
}
/* ==========================================================
 * Convert and fill one ray of data from IRIS to EWIS.
 * This function will do range averaging.
 */
static void FillOneRay
( UINT1 *pEwisRay_a, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  SINT4 iSweepDelayMs_a,
  const struct convert_info *pInfo_a,
  SINT4 iOutFile_a )                   /* Output file number, origin zero */
{
  SINT4 iRayDelay10 = ((iSweepDelayMs_a+50)/100)+(10*pIrisRay_a->hdr.itime);

  pEwisRay_a[0] = 0x000000ff & iRayDelay10;
  pEwisRay_a[1] = 0x000000ff & (iRayDelay10>>8);

  /* If the range resolution is unchanged, then we can simply copy the data */
  if ( pEwisHeader_a->Type.polar.range_siz[0] == pInfo_a->fRangeRes )
    {
      /* Do not overflow the input array */
      SINT4 iOutBinCount = 
	MIN( pIrisRay_a->hdr.ibincount, pEwisHeader_a->Type.polar.range_uppb );
      memcpy( pEwisRay_a+4, pIrisRay_a->data.iData1, iOutBinCount );
    }
  else
    {
      if (DB_VEL == pInfo_a->iIrisDataTypes[iOutFile_a] )
	RangeAverageVel( pEwisRay_a, pIrisRay_a, pEwisHeader_a, pInfo_a );
      else
	RangeAverageRay( pEwisRay_a, pIrisRay_a, pEwisHeader_a, pInfo_a, iOutFile_a );
    }
}

/* ==========================================================
 * Range average one ray of data.
 */
static void RangeAverageRay
( UINT1 *pEwisRay_a, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  const struct convert_info *pInfo_a,
  SINT4 iOutFile_a )                   /* Output file number, origin zero */
{

  struct accum_results
  {
    SINT4 iHitCount;
    SINT4 iValidCount;
    FLT4  fDataAccum;
  };
  UINT4 iIrisDataType = pInfo_a->iIrisDataTypes[iOutFile_a];
  struct accum_results Accum[MAX_BINS];
  SINT4 iInBin, iOutBin;
  memset( &Accum, 0, sizeof(Accum) );
  SINT4 iOutBinCount = pEwisHeader_a->Type.polar.range_uppb;
  FLT4 fOutRangeRes = pEwisHeader_a->Type.polar.range_siz[0];
  
  /* Loop through accumulating results */
  for (iInBin=0; iInBin<pIrisRay_a->hdr.ibincount; iInBin++)
    {
      iOutBin = (SINT4)((pInfo_a->fRangeRes/fOutRangeRes)*(FLT4)iInBin);
      struct accum_results *pAccum = Accum+iOutBin;
      /* Only process if we have not overflowed the input array */
      if (iOutBin<iOutBinCount)
	{
	  UINT1 iData = pIrisRay_a->data.iData1[iInBin];
	  pAccum->iHitCount++;
	  if (iData)
	    {
	      pAccum->fDataAccum += pInfo_a->fDataMaps[iOutFile_a][iData];
	      pAccum->iValidCount++;
	    }
	}
    }
  /* Loop through extracting data from the accumulated results */
  for (iOutBin=0; iOutBin<iOutBinCount; iOutBin++)
    {
      struct accum_results *pAccum = Accum+iOutBin;
      /* Pass threshold only if greater than half the input bins passed */
      if (pAccum->iValidCount>(pAccum->iHitCount/2))
	{
	  FLT4 fData = pAccum->fDataAccum/(FLT4)pAccum->iValidCount;
	  pEwisRay_a[4+iOutBin] = 
	    iuser_to_data( fData, iIrisDataType, &pInfo_a->Convert );
	}
    }
}

/* ==========================================================
 * Range average one ray of velocity data.
 */
static void RangeAverageVel
( UINT1 *pEwisRay_a, 
  const struct data_ray *pIrisRay_a,
  const struct ewis_header *pEwisHeader_a,
  const struct convert_info *pInfo_a )
{
  #define MAX_VEL_COEF (31)
  SINT4 iCoefs[MAX_VEL_COEF];
  UINT4 iCoefNum;
  SINT4 iOutBin;
  SINT4 iOutBinCount = pEwisHeader_a->Type.polar.range_uppb;
  FLT4 fOutRangeRes = pEwisHeader_a->Type.polar.range_siz[0];
  UINT1 iSmoothVels[MAX_BINS];
  
  /* First smooth the array at input resolution */
  FLT4 fWidth = pInfo_a->fRangeRes/fOutRangeRes;
  make_triangular_window( fWidth, MAX_VEL_COEF, &iCoefNum, iCoefs );

  one_dim_vel_filter( pIrisRay_a->hdr.ibincount, (SINT4)iCoefNum, iCoefs,
		      pIrisRay_a->data.iData1, iSmoothVels, 128 );

  /* Loop through extracting data from the accumulated results */
  for (iOutBin=0; iOutBin<iOutBinCount; iOutBin++)
    {
      SINT4 iInBin = (SINT4)((fOutRangeRes/pInfo_a->fRangeRes)*(FLT4)iOutBin);
      if (iInBin<pIrisRay_a->hdr.ibincount)
	{
	  pEwisRay_a[4+iOutBin] = iSmoothVels[iInBin];
	}
    }
}

/* ====================================
 * Convert data for cartesian products.
 */
static MESSAGE ConvertCartesianData
( const struct cartesian_product *pProd_a,
  UINT1 *pData_a,  /* Pointer to data buffer */
  const struct ewis_header *pEwisHeader_a )
{
  SINT4 iSliceSize, iByteCount;
  iByteCount = pEwisHeader_a->pixel_cnt;
  memset( pData_a, 0, iByteCount );

  iSliceSize = pProd_a->Header.pcf.ixsize*pProd_a->Header.pcf.iysize;

  /* Horizantal pictures are easy */
  if (pEwisHeader_a->pic_fil_typ!=EWIS_PIC_FIL_XYZ_VOLUME)
    {
      iSliceSize = MIN( iSliceSize, iByteCount );
      memcpy( pData_a, pProd_a->Data, iSliceSize );
      return SS_NORMAL;
    }

  /* Now do XYZ volumes */
  for (int iSlice=0; iSlice<pEwisHeader_a->nv; iSlice++)
    {
      struct ewis_header EwisHeaderSlice;
      UINT1 *pEwisDataSlice = pData_a + 512*(pEwisHeader_a->Type.xyz.block_no[iSlice]-1);
      struct ewis_header_file *pEwisHeaderSliceFile = 
	(struct ewis_header_file*)(pEwisDataSlice-512);
      /* Copy the header over, then zero the type specific field.
       * We will fill this in with horizonatal picture info.
       */
      EwisHeaderSlice = *pEwisHeader_a;
      EwisHeaderSlice.pic_fil_typ = EWIS_PIC_FIL_HORIZ_PICTURE;
      EwisHeaderSlice.pixel_cnt   = 512*((iSliceSize+511)/512);
      memset( &EwisHeaderSlice.Type, 0, sizeof(union ewis_header_type) );
      EwisHeaderSlice.Type.horiz.hei = pEwisHeader_a->Type.xyz.pic_level[iSlice];
      EwisHeaderSlice.Type.horiz.hei_nr = iSlice-1;

      /* copy header to data buffer */
      FileFromHeader( pEwisHeaderSliceFile, &EwisHeaderSlice );

      /* now copy data to data buffer */
      if (iSlice==0)
	{
	  MakePsuedoCappi( pEwisDataSlice, pProd_a->Data, pEwisHeader_a );
	}
      else
	{
	  const UINT1 *pIrisDataSlice = pProd_a->Data + iSliceSize*(iSlice-1);
	  memcpy( pEwisDataSlice, pIrisDataSlice, iSliceSize );
	}
    }

  return SS_NORMAL;
}
/* ===================================================
 * Make an EWIS style Psuedo CAPPI.
 */
static void MakePsuedoCappi
( UINT1 *pEwisDataSlice_a,    /* 2D data array */
  const UINT1 *pIrisData_a,   /* 3D data array */
  const struct ewis_header *pEwisHeader_a )  /* Header for the XYZ volume */
{
  SINT4 iSliceSize = pEwisHeader_a->ne * pEwisHeader_a->nn;
  FLT4  fTargetHeight = pEwisHeader_a->Type.xyz.pic_level[0];
  int iSlice, iPixel, iSearchCount;

  /* Figure out how high we need to go in the cappi.  We will start
   * with the lowest data.  If area-not-scanned, then we will go up
   * until we hit either data or hit the CAPPI height.  This algorithm
   * can be changed, if required.
   */

  iSearchCount=1;  /* Minimum is to use just the lowest */
  for (iSlice=1; iSlice<pEwisHeader_a->nv; iSlice++)
    {
      if (pEwisHeader_a->Type.xyz.pic_level[iSlice]<=fTargetHeight) iSearchCount=iSlice;
    }

  for (iPixel=0; iPixel<iSliceSize; iPixel++)
    {
      UINT1 iValue = pIrisData_a[iPixel];
      iSlice = 1;
      while( (iValue==DV_NOSCAN1) && (iSlice<iSearchCount))
	{
	  iValue = pIrisData_a[iPixel+(iSlice*iSliceSize)];
	  iSlice++;
	}
      pEwisDataSlice_a[iPixel] = iValue;
    }

}

/* ======================================================================
 * Function to print signaled errors to the loging file or terminal.
 */
static MESSAGE SignalHandler
(MESSAGE iStatus_a,
 UINT4 icount_a,
 const UINT4 iSigArgs_a[] )
{
  char sMessage[170];

  strncpy( sMessage, message_to_text( iStatus_a, icount_a, iSigArgs_a ), 165 );
  sMessage[165]=0;
  strcat( sMessage, "\n" );

  LoggingPrintf( &CConfig_c.logging, sMessage );
  return(SS_NORMAL);
}

/* ==================================================
 * Co-Routine to read the next run of bytes from the raw product file,
 * skipping and checking the record headers as we go.
 */
static void get_raw_bytes( SINT2 *buf_a, SINT4 icnt_a )
{
  SINT4 icnt, iremain = icnt_a ; UINT1 *pbuf = (UINT1 *)buf_a ;

  while( iremain > 0 ) {
    /* If the offset is zero, then get the next record header and
     * compare the record numbers.  Exit if there is a mismatch.
     */
    if( ioff_c == 0 ) {
      struct raw_prod_bhdr bhdr ;
      memcpy( (void *)&bhdr, PRODPTR, RAW_PROD_BHDR_SIZE ) ;
      if( bhdr.irec != irec_c )
        {
          char sLog[200];
          sprintf( sLog, "Block header mismatch (%d) at block %d\n", bhdr.irec, irec_c ) ;
          LoggingPrintf( &CConfig_c.logging, sLog );
          exit(EXIT_FAILURE) ;
        }
      ioff_c += RAW_PROD_BHDR_SIZE ;
    }

    /* Extract as many bytes out of this record as we can.  Bump record
     * number and zero the offset if we read it all.
     */
    icnt = TAPE_RECORD_LEN - ioff_c ;
    if( icnt > iremain ) icnt = iremain ;

    memcpy( pbuf, PRODPTR, icnt ) ;
    iremain -= icnt ; ioff_c += icnt ; pbuf += icnt ;

    if( ioff_c == TAPE_RECORD_LEN ) { ioff_c = 0 ; irec_c++ ; }
  }
  return;
}



