/* ******************************************************************
 * *                                                                *
 * *  Function to convert a rainbow file to an IRIS RAW product     *
 * *                                                                *
 * ******************************************************************
 * File: utils/rainbow/RainbowToIris.C
 *
 *           COPYRIGHT (c) 2002, 2003, 2004, 2005, 2006 BY
 *             VAISALA INC, 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.
 */

/* This program is a pipe which takes an input Gematronik Rainbow Scan Data
 * file and converts it into an IRIS RAW product containing that data.
 *
 * Note if you are modifying this program, and wish to insert a diagnostic
 * printf, be sure to write it to the error output.
 */

#include <locale.h>
#include <math.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 "sigversion.h"
#include "product.h"
#include "dsp.h"
#include "user_lib.h"
#include "dsp_lib.h"
#include "misc_lib.h"
#include "rainbow_h.h"


/* This holds information to customize the convertor */
struct command_line
{
  char  sInFile[FILENAME_SIZE];
  char  sInPath[PATHNAME_SIZE];
  char  sOutPath[PATHNAME_SIZE];
  int   iDevice;    /* Device number */
  int   lRerun;     /* Rerun to output saved state */
};
struct sweep_scan
{
  SINT4 iRayStepCount;
  FLT8  fRayStepSum;
  struct ymds_time SweepTime;
};

static struct convert_config CSetup_c;
static char sMessage_c[200];

/* Local function prototypes
 */
static int Convert
(const struct command_line *pCommand_a,   /* Command line info */
 const char *sStartPath_a );              /* Root for temporary state */

static int Rerun
(const struct command_line *pCommand_a,   /* Command line info */
 const char *sStartPath_a );              /* Root for temporary state */

static int Different
(const struct command_line *pCommand_a,   /* Command line information */
 struct ingest_header *pSavedIHeader_a,   /* Info from the state file */
 const char *sStartPath_a );              /* Root for temporary state */

#define STATE_NONE      (0)
#define STATE_DUPLICATE (1)
#define STATE_MERGEABLE (2)
#define STATE_DIFFERENT (3)
static UINT1 CompareCurrentWithState
(const struct ingest_header *pCurrentIHeader_a,
 const struct ingest_header *pSavedIHeader_a,
 UINT1 lError_a );          /* Flag indicating that there was no previous state */

static int ConvertToProduct
( const char *sOutPath_a,
  struct ingest_header *pIHeader_a,
  const char *sStartPath_a,
  const char *sPrefix_a );   /* Prefix string to add to comment */

static UINT1 ReadStateFile
(struct ingest_header *pSavedIHeader_a,
 const char *sStartPath_a );

static UINT1 WriteStateFile
(struct ingest_header *pSavedIHeader_a,
 const char *sStartPath_a );

static UINT1 RemoveStateFile
( const char *sStartPath_a );

static UINT1 MapSite
( struct rainbow_info8 *pInfo_a );

struct convert_task_list *BestStartingAngle
( struct convert_task_list *pTask1_a,   /* Use this one if equal, can be NULL */
  struct convert_task_list *pTask2_a,
  FLT8 fFixedAngle_a );

static UINT1 lMatchTaskData
( const struct convert_task_list *pTask_a,
  const struct rainbow_info8 *pInfo_a );

static void BuildIngestHeader
(struct ingest_header *pIHeader_a,
 const struct rainbow_info8 *pInfo_a );

static void BuildIngestConfig
(struct ingest_configuration *pIngCon_a,
 const struct rainbow_info8 *pInfo_a );

static UINT1 BuildTaskConfig
(struct task_configuration *pTaskCon_a,
 const struct rainbow_info8 *pInfo_a );

static UINT1 BuildGparm
(struct gparm *pGparm_a,
 const struct rainbow_info8 *pInfo_a );

static void ScanAndWrite
( const UINT1 iData_a[],           /* Data portion of rainbow file */
  const char *sPathStart_a,        /* Start of pathnames for ingest files */
  const struct ingest_header *pIHeader_a,
  const struct rainbow_info8 *pInfo_a );

static void ConvertRayData
( UINT1 *pIrisData_a,
  const UINT1 *pRainbowData_a,
  UINT2 iBinCount_a,
  UINT1 iIrisDataType_a,
  const UINT1 iZdrMap_a[256] );

static void OpenNewFile
( struct ingest_file_state Files_a[NUM_DEFINED_DATA],
  const struct ingest_header *pIHeader_a,  /* Ingest header */
  SINT2  iSweepNumber_a,      /* New sweep number, origin 1 */
  UINT2  iDataType_a,         /* Data type */
  const struct ymds_time *SweepTimeLocal_a,
  const char *sPathStart_a ); /* Start of pathnames */

static void CloseOldFile
(struct ingest_file_state *pFiles_a,
 const struct dsp_data_mask *pDataMask_a );

static void RemoveIngestFiles
( const struct ingest_header *pIHeader_a,  /* Ingest header */
  const char *sPathStart_a );              /* Start of pathnames */

static void GenerateRayHeader
( struct ray_header *pRayHeader_a,
  const struct rainbow_info8 *pInfo_a,
  SINT4 iSweep_a,   /* Sweep number, origin 0 */
  SINT4 ir_ptr_a    /* ray number within sweep, origin 0 */
  );

static void ProcessCommandLine
(int iArgc, 
 char *sArgv[],
 struct command_line *pCommand_a );

static void Usage( void );

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

#define MAX_LINELENGTH (120)
/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  char *sptr;
  int iExitStatus;
  char sStartPath[PATHNAME_SIZE];
  struct command_line Command;
  setlocale( LC_ALL, "" ) ;

  /* Process the command line arguments */
  ProcessCommandLine( iArgc, sArgv, &Command );

  /* Process the setup file, if any */
  LoadSetup( &CSetup_c );

  /* Open the logging file, if any */
  if (CSetup_c.lLogToFile)
    {
      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, "RainbowToIris.log" );
      if (CSetup_c.lAppendToLog)
	CSetup_c.iLogFile = fopen( sLogPath, "a" );
      else
	CSetup_c.iLogFile = fopen( sLogPath, "w" );
      if (NULL == CSetup_c.iLogFile)
	{
	  fprintf( stderr, "Can not open log file: '%s'\n", sLogPath );
	  exit(EXIT_FAILURE);
	}
    }

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

  if (CSetup_c.lVerbose)
    {
      struct full_time_spec Time;
      char sString[100];

      GetTimeFull( &Time, TIME_FLG_LOCAL );
      sprintf( sString, "Verbose Logging On, Version=%s%s%s, TZ=%s\n", IRIS_VERSION, 
	       IRIS_MINOREV, IRIS_PATCHREV, Time.sRecordTZName );
      Lprintf( &CSetup_c, sString );
    }


  /* Now check for missing data in the command line */
  if (Command.sOutPath[0]==0)
    {
      Lprintf( &CSetup_c, "ERROR: missing -op command line option\n" );
      exit(EXIT_FAILURE);
    }

  /* Generate the starting path for the state and temporary ingest files */
  sptr = getenv( "IRIS_TEMP" );
  if (NULL == sptr) 
    strcpy( sStartPath, "/tmp/" );
  else
    strncpy( sStartPath, sptr, DIRECTORY_SIZE-1 );
  sStartPath[DIRECTORY_SIZE-1]=0;
  {
    char sName[40];
    sprintf( sName, "RainbowToIris%d", Command.iDevice );
    strcat( sStartPath, sName );
  }

  if (Command.lRerun)
    {
      iExitStatus = Rerun( &Command, sStartPath );
    }
  else
    {
      /* More possible missing data */
      if (Command.sInFile[0]==0)
	{
	  Lprintf( &CSetup_c, "ERROR: missing -if command line option\n" );
	  exit(EXIT_FAILURE);
	}
      if (Command.sInPath[0]==0)
	{
	  Lprintf( &CSetup_c, "ERROR: missing -ip command line option\n" );
	  exit(EXIT_FAILURE);
	}
      
      iExitStatus = Convert( &Command, sStartPath );
    }

  exit( iExitStatus );
}
/* =============================================================
 * Convert the Rainbow data to IRIS.  Return exit status.
 */
static int Convert
(const struct command_line *pCommand_a,   /* Command line information */
 const char *sStartPath_a )               /* Root for temporary state */
{
  MESSAGE iStatus;
  int iExitStatus;
  UINT1 lError, iStateAction, lComplete;
  SINT4 iInSize, iInChannel;
  struct rainbow_info8 Info;
  struct ingest_header IHeader, SavedIHeader;
  char  sIngestPath[PATHNAME_SIZE], sname[FILENAME_SIZE];
  char  sSiteCode3[4];
  char *sInData;

  if (CSetup_c.lVerbose)
    {
      char sString[PATHNAME_SIZE+50];

      sprintf( sString, "Processing file=%s\n", pCommand_a->sInFile );
      Lprintf( &CSetup_c, sString );
    }

  /* Open the input file */
  iStatus = imapopen( pCommand_a->sInPath, FALSE, (void**)&sInData, &iInSize, &iInChannel );
  if (iStatus != SS_NORMAL)
    {
      sig_signal( iStatus );
      return EXIT_FAILURE;
    }

  /* Parse the rainbow file header
   */
  RainbowParse( sInData, iInSize, &Info, &CSetup_c );

  /* Print out obvious errors */
  lError = RainbowWarnings( &Info, &CSetup_c );
  if (lError) return EXIT_FAILURE;

  /* Map to IRIS site/task */
  lError = MapSite( &Info );
  if (lError) return EXIT_FAILURE;

  BuildIngestHeader(&IHeader, &Info );

  /* Check to see if the new file has already been received */
  lError = ReadStateFile( &SavedIHeader, sStartPath_a );
  iStateAction = CompareCurrentWithState( &IHeader, &SavedIHeader, lError );

  switch ( iStateAction )
    {
    case STATE_DUPLICATE:
      Lprintf( &CSetup_c, "Duplicate data file sent!\n" );
      return EXIT_FAILURE;
      break;

    case STATE_DIFFERENT:
      Different( pCommand_a, &SavedIHeader, sStartPath_a );
      break;
    }

  /* Add on site and date to the start path */
  strcpy( sIngestPath, sStartPath_a );
  nullify_ncpy( sSiteCode3, IHeader.icf.sSitename, 3 );
  lstart_name_int( sname, sSiteCode3, &IHeader.icf.VolumeYmds ) ;
  strcat( sIngestPath, sname );

  ScanAndWrite( (UINT1*)(sInData+Info.iEtxOffset), sIngestPath, &IHeader, &Info );

  iStatus = imapclose( sInData, iInSize, iInChannel );
  if (iStatus != SS_NORMAL) sig_signal( iStatus );

  /* Optionally merge the new data with the saved data */
  if (iStateAction==STATE_MERGEABLE)
    {
      DspMaskOr( &IHeader.tcf.dsp.DataMask, &SavedIHeader.tcf.dsp.DataMask );
    }

  /* Check to see if the volume scan is complete */
  lComplete = lDspMaskEqual( &IHeader.tcf.dsp.DataMask, &(Info.DataMask) );

  if (lComplete)
    {
      /* Volume scan is complete.  Remove the state file, and write out the data
       * unless we already wrote out a file this time.
       */
      switch ( iStateAction )
	{
	default:
	case STATE_MERGEABLE:
	  RemoveStateFile( sStartPath_a );
	case STATE_NONE:
	  /* Convert to product */
	  iExitStatus = ConvertToProduct( pCommand_a->sOutPath, &IHeader, sIngestPath,
					  "Complete:" );
	  break;
	case STATE_DIFFERENT:
	  /* We already made a product, so save current state and tell it to rerun */
	  WriteStateFile( &IHeader, sStartPath_a );
	  iExitStatus = EXIT_RERUN;
	  break;
	}

    }
  else
    {
      /* Volume scan not complete, save current state */
      WriteStateFile( &IHeader, sStartPath_a );
      iExitStatus = EXIT_NOTHING;
    }


  return iExitStatus;
}
/* =============================================================
 * Output the current saved state files to IRIS because of the
 * Rerun command line option.  Return exit status.
 */
static int Rerun
(const struct command_line *pCommand_a,   /* Command line information */
 const char *sStartPath_a )               /* Root for temporary state */
{
  int iExitStatus;
  UINT1 lError;
  struct ingest_header SavedIHeader;
  char  sIngestPath[PATHNAME_SIZE], sname[FILENAME_SIZE];
  char  sSiteCode3[4];

  lError = ReadStateFile( &SavedIHeader, sStartPath_a );
  if (lError)
    {
      Lprintf( &CSetup_c, "Rerun error, no state file found!\n" );
      return EXIT_FAILURE;
    }

  /* Add on site and date to the start path */
  strcpy( sIngestPath, sStartPath_a );
  nullify_ncpy( sSiteCode3, SavedIHeader.icf.sSitename, 3 );
  lstart_name_int( sname, sSiteCode3, &SavedIHeader.icf.VolumeYmds ) ;
  strcat( sIngestPath, sname );

  iExitStatus = ConvertToProduct( pCommand_a->sOutPath, &SavedIHeader, sIngestPath,
				  "Rerun:" );

  RemoveStateFile( sStartPath_a );

  return iExitStatus;
}
/* =============================================================
 * Output the current saved state files to IRIS because a new file 
 * from a different volume scan was received.  Some data types were
 * missing.  Return exit status.
 */
static int Different
(const struct command_line *pCommand_a, /* Command line information */
 struct ingest_header *pSavedIHeader_a, /* Info from the state file */
 const char *sStartPath_a )             /* Root for temporary state */
{
  int iExitStatus;
  char  sIngestPath[PATHNAME_SIZE], sname[FILENAME_SIZE];
  char  sSiteCode3[4];

  /* Add on site and date to the start path */
  strcpy( sIngestPath, sStartPath_a );
  nullify_ncpy( sSiteCode3, pSavedIHeader_a->icf.sSitename, 3 );
  lstart_name_int( sname, sSiteCode3, &(pSavedIHeader_a->icf.VolumeYmds) ) ;
  strcat( sIngestPath, sname );

  /* Convert to product */
  iExitStatus = ConvertToProduct( pCommand_a->sOutPath, pSavedIHeader_a, sIngestPath,
				  "Different:" );

  RemoveStateFile( sStartPath_a );

  return iExitStatus;
}
/* ==================================================
 * Compare the currently received file with the saved state
 * file to see what needs to be done.
 */
static UINT1 CompareCurrentWithState
(const struct ingest_header *pCurrentIHeader_a,
 const struct ingest_header *pSavedIHeader_a,
 UINT1 lError_a )           /* Flag indicating that there was no previous state */
{
  /* No previous state */
  if (lError_a) return STATE_NONE;

  /* Tasks are sorted by sitename and time only */
  if ((0 == memcmp( pCurrentIHeader_a->icf.sSitename, pSavedIHeader_a->icf.sSitename, 16 ))&&
      ( 0 == iymds_compare(&pCurrentIHeader_a->icf.VolumeYmds,&pSavedIHeader_a->icf.VolumeYmds)))
    {
      struct dsp_data_mask DataMask;
      /* If we are in the same task, then we need to check to see it the current
       * data has already been received.  This is how we detect a duplicat transmission.
       */
      DataMask = pSavedIHeader_a->tcf.dsp.DataMask;
      DspMaskAnd( &DataMask, &pCurrentIHeader_a->tcf.dsp.DataMask );
      if (lDspMaskEmpty( &DataMask ))
	return STATE_MERGEABLE;
      else
	return STATE_DUPLICATE;
    }
  else
    {
      /* Different task */
      return STATE_DIFFERENT;
    }
}

/* ===================================================
 * Convert ingest filest to a raw product, and also delete the ingest files.
 */
static int ConvertToProduct
( const char *sOutPath_a,
  struct ingest_header *pIHeader_a,
  const char *sStartPath_a,
  const char *sPrefix_a )
{
  MESSAGE iStatus;
  struct product_hdr   PHeader;
  UINT1 lSwpMask[MAX_SWEEPS];
  int iExitStatus=EXIT_SUCCESS;

  if (CSetup_c.lVerbose)
    {
      char sString[80], sTimeBuf[TIMENAME_SIZE];
      shhmmddmonyyyy_r(&(pIHeader_a->icf.VolumeYmds), sTimeBuf );
      sprintf( sString, "%s Generating for site: %s, date: %s\n", sPrefix_a,
	       nullify( pIHeader_a->icf.sSitename, 16), sTimeBuf );
      Lprintf( &CSetup_c, sString );
    }

  memset( &PHeader, 0, PRODUCT_HDR_SIZE );
  PcfFromScratch( &PHeader.pcf, &pIHeader_a->icf.VolumeYmds,
		  nullify(pIHeader_a->tcf.end.stname, 12 ) );
  PHeader.pcf.iptype = PROD_RAW; 

  /* The rest of the product header is filled in below in RawFromIngest */
  /* Make up a mask of the sweeps to be processed by this run.  This consists
   * either of all sweeps for a complete raw product.
   */
  memset( lSwpMask, TRUE, MAX_SWEEPS );
  iStatus = RawFromIngest( sOutPath_a, sStartPath_a, &PHeader, pIHeader_a, lSwpMask,
			   "RainbowToIris   "/* Product gen site */,
			   0 /*Reference height */ );
  if (iStatus!=SS_NORMAL) { sig_signal( iStatus ); iExitStatus=EXIT_FAILURE; }

  RemoveIngestFiles( pIHeader_a, sStartPath_a );
  return iExitStatus;
}
/* ===================================================
 * Read the state file, and return the results.  Return nonzero
 * if file not there, or error.
 */
static UINT1 ReadStateFile
(struct ingest_header *pSavedIHeader_a,
 const char *sStartPath_a )
{
  char sPath[PATHNAME_SIZE];
  SINT4 iSize;
  FILE* stream;
  strcpy( sPath, sStartPath_a );
  strcat( sPath, ".state" );

  stream = fopen( sPath, "r" );
  if (!stream) return TRUE;

  iSize = fread( pSavedIHeader_a, 1, sizeof(*pSavedIHeader_a), stream );

  fclose( stream );
  if (iSize != sizeof(*pSavedIHeader_a)) return TRUE;

  return FALSE;
}
/* ===================================================
 * Write the state file.  Return nonzero on error.
 */
static UINT1 WriteStateFile
(struct ingest_header *pSavedIHeader_a,
 const char *sStartPath_a )
{
  char sPath[PATHNAME_SIZE];
  FILE* stream;
  SINT4 iSize;
  strcpy( sPath, sStartPath_a );
  strcat( sPath, ".state" );

  stream = fopen( sPath, "w" );
  if (!stream) return TRUE;

  iSize = fwrite( pSavedIHeader_a, 1, sizeof(*pSavedIHeader_a), stream );

  fclose( stream );
  if (iSize != sizeof(*pSavedIHeader_a)) return TRUE;

  return FALSE;
}
/* ===================================================
 * Remove the state file.  Return nonzero on error.
 */
static UINT1 RemoveStateFile
( const char *sStartPath_a )
{
  char sPath[PATHNAME_SIZE];
  strcpy( sPath, sStartPath_a );
  strcat( sPath, ".state" );

  remove( sPath );
  return FALSE;
}
/* ===================================================
 * Map the Rainbow sitename to the IRIS sitename, etc.
 * Returns TRUE on error.
 */
static UINT1 MapSite
( struct rainbow_info8 *pInfo_a )
{

  SINT4 iSite, iSweep;

  /* Search to see if we can find the sitename in the site mapping table.  Note
   * that wildcards are allowed.
   */
  for (iSite=0; iSite<CSetup_c.iSiteCount; iSite++)
    {
      if (sig_fnmatch( CSetup_c.Sites[iSite].sRainbowSitename, pInfo_a->sRadarId ))
	{
	  pInfo_a->pSite = &CSetup_c.Sites[iSite];
	  goto GOT_SITELIST;
	}
    }
  sprintf( sMessage_c, "ERROR, Rainbow H8='%s' not found in config site list\n", 
	   pInfo_a->sRadarId );
  Lprintf( &CSetup_c, sMessage_c );
  return TRUE;

 GOT_SITELIST:

  /* Search to see if we can find the sitename in the task mapping table.  Note
   * that wildcards are allowed for sitename.
   */
  pInfo_a->pTask=NULL;
  for (iSite=0; iSite<CSetup_c.iTaskCount; iSite++)
    {
      struct convert_task_list *pOneTask=&CSetup_c.Tasks[iSite];
      if (sig_fnmatch( pOneTask->sRainbowSitename, pInfo_a->sRadarId ))
	{
	  if (pOneTask->iProductType == pInfo_a->iProductType )
	    {
	      if (lMatchTaskData( pOneTask, pInfo_a ))
		{
		  if (sig_fnmatch( pOneTask->sRainbowScanFile, pInfo_a->sScanFile ))
		    {
		      /* Find the best match for the starting angle */
		      pInfo_a->pTask = BestStartingAngle( pInfo_a->pTask, pOneTask, 
							  pInfo_a->Sweep[0].fFixedAngle );
		    }
		}
	    }
	}
    }
  if (!pInfo_a->pTask)
    {
      sprintf( sMessage_c, "ERROR, Rainbow H8='%s', N=%d, F8='%s' not found in config task list\n", 
	       pInfo_a->sRadarId, pInfo_a->iProductType, pInfo_a->sScanFile );
      Lprintf( &CSetup_c, sMessage_c );
      return TRUE;
    }


  /* Fill in the volume scan time */
  pInfo_a->VolumeTime.iyear = pInfo_a->iYear;
  pInfo_a->VolumeTime.imon  = pInfo_a->iMonth;
  pInfo_a->VolumeTime.iday  = pInfo_a->iDay;
  pInfo_a->VolumeTime.isec  = iPackTime( pInfo_a->iHour, pInfo_a->iMinute, pInfo_a->iSecond );
  /* Convert Rainbow time to UTC */
  if (pInfo_a->pSite->lRainbowUtc)
    {
      pInfo_a->VolumeTime.imills |= YMDS_FLG_UTC;
    }
  else
    {
      ymds_add_seconds( 60*(SINT4)pInfo_a->pSite->iLocalWest, &pInfo_a->VolumeTime );
    }

  /* Fill in the sweep times.  (add 1 second for each) */
  for (iSweep=0; iSweep<pInfo_a->iSweepCount; iSweep++)
    {
      pInfo_a->Sweep[iSweep].SweepTime = pInfo_a->VolumeTime;
      ymds_add_seconds( iSweep, &pInfo_a->Sweep[iSweep].SweepTime );
    }

  /* Make up the data mask. */
  if ( pInfo_a->Sweep[0].lZData ) DspMaskSet( &pInfo_a->DataMask, DB_DBZ );
  if ( pInfo_a->Sweep[0].lVData ) DspMaskSet( &pInfo_a->DataMask, DB_VEL );
  if ( pInfo_a->Sweep[0].lWData ) DspMaskSet( &pInfo_a->DataMask, DB_WIDTH );
  if ( pInfo_a->Sweep[0].lTData ) DspMaskSet( &pInfo_a->DataMask, DB_DBT );
  if ( pInfo_a->Sweep[0].lZdrData ) DspMaskSet( &pInfo_a->DataMask, DB_ZDR );
 
  /* Check that the specified data type is in the full mask */
  if (!lDspMaskTest( &pInfo_a->DataMask, pInfo_a->iIrisDataType ) )
    {
      sprintf( sMessage_c, "ERROR, Recorded Data type %d (F9) not in sweep types (W*).\n", 
	       pInfo_a->iRbDataType );
      Lprintf( &CSetup_c, sMessage_c );
      return TRUE;
    }

  return FALSE;

}
/* =========================================================
 * Compare two proposed task list entries to see which is the
 * closest to the desired angle.
 */
struct convert_task_list *BestStartingAngle
( struct convert_task_list *pTask1_a,   /* Use this one if equal, can be NULL */
  struct convert_task_list *pTask2_a,
  FLT8 fFixedAngle_a )
{
  FLT8 fError1, fError2;
  if (!pTask1_a) return pTask2_a;

  fError1 = pTask1_a->fFirstFixedAngle - fFixedAngle_a;
  if (fError1 > 180.0)
    fError1 -= 360.0;
  else if (fError1 < -180.0)
    fError1 += 360.0;
  fError1 = fabs(fError1);

  fError2 = pTask2_a->fFirstFixedAngle - fFixedAngle_a;
  if (fError2 > 180.0)
    fError2 -= 360.0;
  else if (fError2 < -180.0)
    fError2 += 360.0;
  fError2 = fabs(fError2);

  if (fError2 < fError1) return pTask2_a;

  return pTask1_a;

}
/* =============================================
 * Check the data in the task entry against that found in the
 * current data file.  Return TRUE if it is OK.
 */
static UINT1 lMatchTaskData
( const struct convert_task_list *pTask_a,
  const struct rainbow_info8 *pInfo_a )
{
  if (pTask_a->lWildData) return TRUE;

  if (pTask_a->lZData != pInfo_a->Sweep[0].lZData ) return FALSE;
  if (pTask_a->lVData != pInfo_a->Sweep[0].lVData ) return FALSE;
  if (pTask_a->lWData != pInfo_a->Sweep[0].lWData ) return FALSE;
  if (pTask_a->lTData != pInfo_a->Sweep[0].lTData ) return FALSE;
  if (pTask_a->lZdrData != pInfo_a->Sweep[0].lZdrData ) return FALSE;

  return TRUE;
}

/* ======================================================================
 * Build the ingest header based on information from the first pass.
 */
static void BuildIngestHeader
(struct ingest_header *pIHeader_a,
 const struct rainbow_info8 *pInfo_a )
{
  memset( pIHeader_a, 0, sizeof(*pIHeader_a) );

  /* Fill in most of the Ingest Header Structure
   */
  pIHeader_a->hdr.id     = ST_INGEST_HDR;
  pIHeader_a->hdr.ivers  = SVER_INGEST_HDR_P;
  pIHeader_a->hdr.ibytes = sizeof(struct ingest_header);
  pIHeader_a->hdr.iflags = HD_STRUCTURE_COMPLETE;    /* Full RAW is always 'done' */
      
  /* Fill in the ingest_configuration
   */
  BuildIngestConfig( &pIHeader_a->icf, pInfo_a );
  
  BuildTaskConfig( &pIHeader_a->tcf, pInfo_a );

  BuildGparm( &pIHeader_a->GParm, pInfo_a );

  pIHeader_a->tcf.end.ing_time = pIHeader_a->icf.VolumeYmds;

  return;
}
/* ================================================================
 * Fills in the ingest configuration structure.
 */
static void BuildIngestConfig
(struct ingest_configuration *pIngCon_a,
 const struct rainbow_info8 *pInfo_a )
{
  SINT4 iRaysInRev;

  iRaysInRev = 360; /* Default 1 degree resolution */
  if (pInfo_a->Sweep[0].fSectorStep != 0.0)
    {
      iRaysInRev = NINT(360.0/pInfo_a->Sweep[0].fSectorStep);
      if (iRaysInRev > MAX_INGEST_RAYS) iRaysInRev = MAX_INGEST_RAYS;
    }
  
  pIngCon_a->inrev  = iRaysInRev;    /* # rays in one revolution */
  pIngCon_a->isndx  = 0;             /* First ray's angle expressed as index*/
  pIngCon_a->irtotl = iRaysInRev;    /* total # rays in sweep */
  
  /* Volume scan start date and time
   */
  pIngCon_a->VolumeYmds = pInfo_a->VolumeTime;

  /* Convert UTC to local recorded time */
  if (pInfo_a->pSite->lIrisUtc)
    {
      pIngCon_a->VolumeYmds.imills |= YMDS_FLG_UTC;
      pIngCon_a->iMinutesWest     =  0;
    }
  else
    {
      pIngCon_a->iMinutesWest = pInfo_a->pSite->iLocalWest;
      ymds_add_seconds( -60*(SINT4)pInfo_a->pSite->iLocalWest, &pIngCon_a->VolumeYmds );
      pIngCon_a->VolumeYmds.imills &= ~YMDS_FLG_UTC;
    }
  pIngCon_a->iLocalWest = pInfo_a->pSite->iLocalWest;
    
  pIngCon_a->isweeps_done = 0 ;               /* # of sweeps done */
  pIngCon_a->idfnum    = 0;                   /* # associated data files, and */
  pIngCon_a->idfsize   = 0;                   /* total size of same. */
  pIngCon_a->ib_rayhed = RAY_HSIZE;           /* Ray header size */
  pIngCon_a->ib_task   = TASK_CONFIG_SIZE;    /* Task conf size */
  pIngCon_a->ib_gparm  = DSP_GPARM_SIZE;      /* GPARM size */
  
  pIngCon_a->ib_xhdr   = 0;                   /* We are not making extended headers */
  
  memmove( pIngCon_a->sSitename, pInfo_a->pSite->sIrisSitename, 16 );

  memmove( pIngCon_a->sHardwareName, pIngCon_a->sSitename, 16 );
  strncpy( pIngCon_a->siris_version, IRIS_VERSION, 8 );
  pIngCon_a->siris_version[7] = 0;

  pIngCon_a->ilat      = iBin4FromFDeg( pInfo_a->fRadarLatitude );

  pIngCon_a->ilon      = iBin4FromFDeg( pInfo_a->fRadarLongitude );

  pIngCon_a->ialtitude = NINT( 100.0*pInfo_a->pSite->fAltitude );
  pIngCon_a->ignd_hgt  = NINT( pInfo_a->pSite->fAltitude );
  pIngCon_a->irad_hgt  = 0;
      
  pIngCon_a->inu_vel[0] = 0;
  pIngCon_a->inu_vel[1] = 0;
  pIngCon_a->inu_vel[2] = 0;
  pIngCon_a->inu_offset[0] = 0;
  pIngCon_a->inu_offset[1] = 0;
  pIngCon_a->inu_offset[2] = 0;
  memcpy( pIngCon_a->sLocalTZName, pInfo_a->pSite->sLocalTZName, TZNAME_SIZE );
  return;
}
/* ================================================================
 * Fills in the task configuration structure.
 */
static UINT1 BuildTaskConfig
(struct task_configuration *pTaskCon_a,
 const struct rainbow_info8 *pInfo_a )
{
  SINT4 iSweep;

  /* Header */
  pTaskCon_a->hdr.id     = ST_TASK_CONF;
  pTaskCon_a->hdr.ivers  = SVER_TASK_CONF_P;
  pTaskCon_a->hdr.ibytes = TASK_CONFIG_SIZE;

  /* Scheduling info */
  /* None of this is important in the data */

  /* DSP info */
  pTaskCon_a->dsp.imajor_mode = PMODE_PPP;     /* Just a guess */
  DspMaskSet( &pTaskCon_a->dsp.DataMask, pInfo_a->iIrisDataType );

  pTaskCon_a->dsp.iprf = 1000;                 /* Default if no PRT supplied */
  if (pInfo_a->Sweep[0].iPrfHigh)
    pTaskCon_a->dsp.iprf = pInfo_a->Sweep[0].iPrfHigh;

  pTaskCon_a->dsp.itrig = pInfo_a->Sweep[0].iDualCase;

  if (pInfo_a->Sweep[0].iPulseWidth==2)
    pTaskCon_a->dsp.ipw = NINT(100.0*CSetup_c.fPwList[1]);
  else
    pTaskCon_a->dsp.ipw = NINT(100.0*CSetup_c.fPwList[0]);

  pTaskCon_a->dsp.itrig = pInfo_a->Sweep[0].iDualCase;
  pTaskCon_a->dsp.isamp = pInfo_a->iSampleSize;
  pTaskCon_a->dsp.idop_filter_first = pInfo_a->iClutterFilter;

  /* Calibration info */
  pTaskCon_a->cal.izns_thr = NINT( 16.0*pInfo_a->fLogThreshold );
  pTaskCon_a->cal.iccr_thr = NINT( -16.0*pInfo_a->fCsrThreshold );
  pTaskCon_a->cal.isqi_thr = NINT( 256.0*pInfo_a->fSqiThreshold );
  pTaskCon_a->cal.iz_calib = NINT( 16.0*pInfo_a->pTask->fIrisZCalib );
  /* Just a guess of their threshold flags */
  pTaskCon_a->cal.iuz_tcf  = TCF_LOG;
  pTaskCon_a->cal.icz_tcf  = TCF_LOG & TCF_SQI & TCF_CSR;
  pTaskCon_a->cal.ivl_tcf  =           TCF_SQI & TCF_CSR;
  pTaskCon_a->cal.iwd_tcf  =           TCF_SQI & TCF_CSR;
  pTaskCon_a->cal.izdr_tcf = TCF_LOG;
  if (pInfo_a->lLogSpeckle) pTaskCon_a->cal.iflags |= TCF_CAL_ZSPECKLE;
  if (pInfo_a->lDopSpeckle) pTaskCon_a->cal.iflags |= TCF_CAL_VSPECKLE;

  /* range info */
  pTaskCon_a->rng.ibin_first    = NINT(100000.0 * pInfo_a->fRangeStart);
  pTaskCon_a->rng.ibin_out_num  = pInfo_a->iRangeBinCount;
  pTaskCon_a->rng.ibin_in_num   = pInfo_a->iRangeBinCount/pInfo_a->iRangeAvg;
  pTaskCon_a->rng.ibin_out_step = NINT(100000.0*pInfo_a->fRangeStep);
  pTaskCon_a->rng.ibin_in_step  = NINT(pInfo_a->fRangeStep/(FLT4)pInfo_a->iRangeAvg);
  pTaskCon_a->rng.ibin_last     = pTaskCon_a->rng.ibin_first +
    (pTaskCon_a->rng.ibin_out_num-1)*pTaskCon_a->rng.ibin_out_step;
  pTaskCon_a->rng.ibin_avg = pInfo_a->iRangeAvg-1;

  /* scan info */
  pTaskCon_a->scan.isweeps = pInfo_a->iSweepCount;
  pTaskCon_a->scan.ires1000 = NINT(1000.*pInfo_a->Sweep[0].fSectorStep);
  pTaskCon_a->scan.iscan_speed = iBin2FromFDeg(pInfo_a->Sweep[0].fScanSpeed);
  switch( pInfo_a->iProductType)
    {
    case RB_PROD_EL_SCAN:
      pTaskCon_a->scan.iscan = TASK_SCAN_RHI;
      for (iSweep=0; iSweep<pInfo_a->iSweepCount; iSweep++)
	{
	  pTaskCon_a->scan.u.rhi.istartel = iBin2FromFDeg(pInfo_a->Sweep[0].fSectorStart);
	  pTaskCon_a->scan.u.rhi.iendel = iBin2FromFDeg(pInfo_a->Sweep[0].fSectorEnd);
	  pTaskCon_a->scan.u.rhi.iazlst[iSweep] = 
	    iBin2FromFDeg(pInfo_a->Sweep[iSweep].fFixedAngle);
	}
      break;
    default:
      pTaskCon_a->scan.iscan = TASK_SCAN_CONT;
      for (iSweep=0; iSweep<pInfo_a->iSweepCount; iSweep++)
	{
	  pTaskCon_a->scan.u.ppi.iellst[iSweep] = 
	    iBin2FromFDeg(pInfo_a->Sweep[iSweep].fFixedAngle);
	}
      break;
    }

  /* misc info */
  pTaskCon_a->misc.ipolar = POL_HORIZ_FIX;

  {
    FLT4 fNyquistTest;
    /* Figure out the wavelength computing the Nyquist velocity assuming
     * 1 cm, then scaling by the difference.
     */
    fNyquistTest = fNyquistVelocity( pTaskCon_a->dsp.iprf, pTaskCon_a->dsp.itrig,  
				     100, POL_HORIZ_FIX );
    
    pTaskCon_a->misc.ilambda = NINT(100.0*pInfo_a->fNyquistVel/fNyquistTest);
  }

  /* Height truncation is assumed off, set to the maximum range */
  pTaskCon_a->misc.itrunc = pTaskCon_a->rng.ibin_last;
  pTaskCon_a->misc.iHorzBeamWidth = iBin4FromFDeg( 1.0 );
  pTaskCon_a->misc.iVertBeamWidth = iBin4FromFDeg( 1.0 );

  /* end info */
  spacify_ncpy( pTaskCon_a->end.stname, pInfo_a->pTask->sIrisTaskName, 12 );
  strcpy( pTaskCon_a->end.sdscript, "Converted from Rainbow" );


  return TRUE;
}
/* ================================================================
 * Fills in the gparm structure.
 */
static UINT1 BuildGparm
(struct gparm *pGparm_a,
 const struct rainbow_info8 *pInfo_a )
{
  /* Calibration info */
  pGparm_a->iz_calib = NINT( 16.0*pInfo_a->pTask->fIrisZCalib );

  return TRUE;
}

struct ingest_file_state
{
  struct ingest_data_header *pIDH;
  UINT4                     *pTable;
  UINT1                     *pData;
  SINT4 iSize;
  SINT4 iChannel;
  SINT4 iDataUsed;
  UINT1 iMapTable[65536];
  UINT1 lMapTableFilled;
};
/* =====================================================================
 * Scan through the data and write it to the IRIS product.
 */
static void ScanAndWrite
( const UINT1 iData_a[],           /* Data portion of rainbow file */
  const char *sPathStart_a,        /* Start of pathnames for ingest files */
  const struct ingest_header *pIHeader_a,
  const struct rainbow_info8 *pInfo_a )
{
  SINT4 iPosition, iRayCount, iSweep;
  struct ingest_file_state File;
  UINT1 iZdrMap[256];

  /* 
   * Fill the mapping table for ZDR data.
   */
  {
    FLT4 fValue, fScale;
    SINT4 iIndex;
    UINT1 iValue;
    struct data_convert Convert;
    FillDataConvert( &Convert, pIHeader_a->tcf.dsp.iprf, pIHeader_a->tcf.dsp.itrig,
		     pIHeader_a->tcf.misc.ilambda, pIHeader_a->tcf.misc.ipolar,
		     CONVERT_FLAGS_DEFAULT );
    
    fScale = (pInfo_a->fMaxZdr-pInfo_a->fMinZdr)/254.0;
    for (iIndex=1; iIndex<256; iIndex++)
      {
 	fValue = pInfo_a->fMinZdr + fScale * (FLT4)(iIndex-1);
	iValue = iuser_to_data( fValue, DB_ZDR, &Convert );
	iZdrMap[iIndex] = iValue;
      }
    iZdrMap[0] = DV_THRESH1;
  }

  memset( &File, 0, sizeof(struct ingest_file_state));

  for (iSweep=0; iSweep<pInfo_a->iSweepCount; iSweep++)
    {
      struct ray_header RayHeader;
      struct ymds_time SweepTimeLocal;
      SINT4 ir_ptr;

      SweepTimeLocal = pInfo_a->Sweep[iSweep].SweepTime;
      /* Convert UTC to recorded local time */
      if (pInfo_a->pSite->lIrisUtc)
	{
	  SweepTimeLocal.imills |= YMDS_FLG_UTC;
	}
      else
	{
	  ymds_add_seconds( -60*(SINT4)pInfo_a->pSite->iLocalWest, &SweepTimeLocal );
	  SweepTimeLocal.imills &= ~YMDS_FLG_UTC;
	}
      OpenNewFile( &File, pIHeader_a, iSweep+1, pInfo_a->iIrisDataType, &SweepTimeLocal, 
		   sPathStart_a );

      for (ir_ptr=0; ir_ptr<pIHeader_a->icf.inrev; ir_ptr++)
	{
	  struct data_ray Ray;
	  SINT4 iRaySize;

	  GenerateRayHeader( &RayHeader, pInfo_a, iSweep, ir_ptr );
		  
	  /* Compute position in the file */
	  iPosition = ((pIHeader_a->icf.inrev * iSweep) + ir_ptr)*RayHeader.ibincount;
		  
	  Ray.hdr = RayHeader;

	  ConvertRayData( Ray.data.iData1, &iData_a[iPosition], RayHeader.ibincount,
			  pInfo_a->iIrisDataType, iZdrMap );

	  iRaySize = RayHeader.ibincount + sizeof(RayHeader);
	  /* Rays must always be even # bytes */
	  if (iRaySize&1) iRaySize++;
	  
	  File.pTable[ir_ptr] = File.iDataUsed+1;
	  memcpy( &File.pData[File.iDataUsed], &Ray, iRaySize );
	  File.iDataUsed += iRaySize;
	  File.pIDH->iwritn++;

	  /* Skip to the next ray */
	  iRayCount++;
	}
      CloseOldFile( &File, &pInfo_a->DataMask );
    }

  return;

}

/* ====================================================
 * Convert the data in a ray from Rainbow to IRIS format.  This is just
 * a simple memcpy, except for ZDR data.
 */
static void ConvertRayData
( UINT1 *pIrisData_a,
  const UINT1 *pRainbowData_a,
  UINT2 iBinCount_a,
  UINT1 iIrisDataType_a,
  const UINT1 iZdrMap_a[256] )
{

  if (DB_ZDR == iIrisDataType_a )
    {
      SINT4 iBin;
      for (iBin=0; iBin<iBinCount_a; iBin++)
	{
	  pIrisData_a[iBin] = iZdrMap_a[pRainbowData_a[iBin]];
	}
    }
  else
    {
      memcpy( pIrisData_a, pRainbowData_a, iBinCount_a );
    }

  /* Odd rays always padded by a zero */
  if (1&iBinCount_a) pIrisData_a[iBinCount_a] = 0;
  
  return;
}

/* ======================================================
 * Open all the sweep mapped files.
 */
static void OpenNewFile
( struct ingest_file_state *pFile_a,
  const struct ingest_header *pIHeader_a,  /* Ingest header */
  SINT2  iSweepNumber_a,      /* New sweep number, origin 1 */
  UINT2  iDataType_a,         /* Data type */
  const struct ymds_time *pSweepTimeLocal_a,
  const char *sPathStart_a )  /* Start of pathnames */
{
  SINT4 iFileSize, iRaySize;
  MESSAGE istatus;
  char sPath[PATHNAME_SIZE];
  iRaySize   = sizeof(struct ray_header) + pIHeader_a->tcf.rng.ibin_out_num;
  if (iRaySize&1) iRaySize++;
  iFileSize  = INGEST_DATA_HEADER_SIZE;
  iFileSize += 4*pIHeader_a->icf.irtotl;
  iFileSize += ((SINT4)pIHeader_a->icf.irtotl) * iRaySize;


  strcpy( sPath, sPathStart_a );
  strcat( sPath, sIngestEnd( iDataType_a, iSweepNumber_a ) );
  istatus = imapcreate( sPath, iFileSize, (void **)&pFile_a->pIDH, &pFile_a->iSize, 
			&pFile_a->iChannel );
  if (istatus!= SS_NORMAL) sig_signal( istatus );
  pFile_a->pTable = (UINT4 *)(pFile_a->pIDH+1);
  pFile_a->pData  = (UINT1 *)(pFile_a->pTable+pIHeader_a->icf.irtotl);
  pFile_a->iDataUsed = 0;
  memset( pFile_a->pIDH, 0, sizeof(*pFile_a->pIDH) );
  memset( pFile_a->pData, 0, 4*pIHeader_a->icf.irtotl );
  
  pFile_a->pIDH->hdr.id     = ST_INGEST_DATA;
  pFile_a->pIDH->hdr.ivers  = SVER_INGEST_DATA_P;
  pFile_a->pIDH->hdr.ibytes = INGEST_DATA_HEADER_SIZE;
  pFile_a->pIDH->hdr.iflags = HD_STRUCTURE_COMPLETE;   
  pFile_a->pIDH->time       = *pSweepTimeLocal_a;
  pFile_a->pIDH->isweep     = iSweepNumber_a;
  pFile_a->pIDH->inrev      = pIHeader_a->icf.inrev;
  pFile_a->pIDH->isndx      = pIHeader_a->icf.isndx;
  pFile_a->pIDH->irtotl     = pIHeader_a->icf.irtotl;
  pFile_a->pIDH->iwritn     = 0;
  pFile_a->pIDH->iangle     = pIHeader_a->tcf.scan.u.ppi.iellst[iSweepNumber_a-1];
  pFile_a->pIDH->ibits_bin  = 8;
  pFile_a->pIDH->idata      = iDataType_a;

  return;

}
/* ======================================================
 * Close all the old mapped files.
 */
static void CloseOldFile
(struct ingest_file_state *pFile_a,
 const struct dsp_data_mask *pDataMask_a )
{
  MESSAGE istatus;
  pFile_a->pIDH->hdr.ibytes = INGEST_DATA_HEADER_SIZE + 
    (4*(SINT4)pFile_a->pIDH->irtotl) + pFile_a->iDataUsed;
  istatus = imapclose( pFile_a->pIDH, pFile_a->iSize, pFile_a->iChannel );
  if (istatus!= SS_NORMAL) sig_signal( istatus );
  pFile_a->pIDH = NULL;

  return;
}
/* ======================================================
 * Remove all the ingest sweep files.
 */
static void RemoveIngestFiles
( const struct ingest_header *pIHeader_a,  /* Ingest header */
  const char *sPathStart_a )               /* Start of pathnames */
{
  SINT2 iSweepNumber, iDataType;

  for (iSweepNumber=1; iSweepNumber<=pIHeader_a->tcf.scan.isweeps; iSweepNumber++)
    {

      for (iDataType=0; iDataType<NUM_DEFINED_DATA; iDataType++)
	{
	  if (lDspMaskTest( &pIHeader_a->tcf.dsp.DataMask, iDataType ) )
	    {
	      char sPath[PATHNAME_SIZE];
	      int ierror;
	      
	      strcpy( sPath, sPathStart_a );
	      strcat( sPath, sIngestEnd( iDataType, iSweepNumber ) );
	      ierror = remove( sPath );
	      if (ierror!=0) perror(sPath);
	    }
	}
    }
  return;
}

/* =====================================================================
 * Generates an IRIS ray header from Rainbow info and index.
 * Only implemented for PPIF mode, 1 degree resolution.
 */
static void GenerateRayHeader
( struct ray_header *pRayHeader_a,
  const struct rainbow_info8 *pInfo_a,
  SINT4 iSweep_a,       /* Sweep number, origin 0 */
  SINT4 ir_ptr_a )      /* Ray pointer, origin 0 */
{
  FLT8 fAzCenter, fElCenter;
  const struct rainbow_sweep_info8 *pSweep=&pInfo_a->Sweep[iSweep_a];
  /* We do not know the direction of scanning, so do not try to fake
   * the starting and ending angles.
   */
  if (pInfo_a->iProductType == RB_PROD_EL_SCAN)
    {
      fAzCenter = pSweep->fFixedAngle;
      fElCenter = pSweep->fSectorStart + pSweep->fSectorStep*(FLT8)ir_ptr_a;
    }
  else
    {
      fAzCenter = pSweep->fSectorStart + pSweep->fSectorStep*(FLT8)ir_ptr_a;
      fElCenter = pSweep->fFixedAngle;
    }

  pRayHeader_a->iaz_start = iBin2FromFDeg(fAzCenter);
  pRayHeader_a->iel_start = iBin2FromFDeg(fElCenter);
  pRayHeader_a->iaz_end   = pRayHeader_a->iaz_start;
  pRayHeader_a->iel_end   = pRayHeader_a->iel_start;

  pRayHeader_a->itime = 0;
  pRayHeader_a->ibincount = pInfo_a->iRangeBinCount;

  return;
}

/* ======================================================================
 * Function to process the command line arguments.
 */
static void ProcessCommandLine
(int iArgc_a, 
 char *sArgv_a[],
 struct command_line *pCommand_a )
{
  
  int iArg;
  char *pEnd;
  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, "-device:", 8 ))
	{
	  pCommand_a->iDevice = strtol( &sArg[8], &pEnd, 10 );
	}
      else if (0 == strncmp( sArg, "-rerun", 6 ))
	{
	  pCommand_a->lRerun = TRUE;
	}
      
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "UfToIris options:\n" );
  fprintf( stderr, "  -help      -- Print this list\n" );
  fprintf( stderr, "  -device:#  -- Device number\n" );
  fprintf( stderr, "  -if:<file> -- Specify input filename\n" );
  fprintf( stderr, "  -ip:<path> -- Specify input pathname\n" );
  fprintf( stderr, "  -op:<path> -- Specify output pathname\n" );
  fprintf( stderr, "  -rerun     -- Rerun to write out saved state\n" );
  fprintf( stderr, "   (all other arguments ignored)\n" );
  exit(EXIT_FAILURE) ;
}
/* ======================================================================
 * 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" );

  Lprintf( &CSetup_c, sMessage );
  return(SS_NORMAL);
}

