/* **********************************************************************
 * *                                                                    *
 * *       Standalone Utility to Make Changes to a RAW Product          *
 * *                                                                    *
 * **********************************************************************
 * File: utils/examples/change_raw.C
 *
 *         COPYRIGHT (c) 2000, 2001, 2002, 2003, 2004, 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.
 *
 * This routine performs an in-place modification of an IRIS RAW
 * product file, making whatever changes were requested on the command
 * line.
 * 
 *   Use "-cvres" to repair a file whose data were thought to be
 *   recorded at one input bin spacing, when some other input bin
 *   spacing really was selected in the signal processor.
 *
 *   Use "-addbz" to add some number of dB to all of the recorded
 *   data, as if dBZo has been different in the first place.
 *
 *   Use "-native" swap to native byte convention first.  Also changes
 *   V1 to V2 format.
 */
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sigtypes.h"
#include "dsp.h"
#include "setup.h"
#include "headers.h"
#include "iris_task.h"
#include "ingest.h"
#include "product.h"
#include "user_lib.h"
#include "dsp_lib.h"
#include "misc_lib.h"

struct command_line
{
  SINT4 iFromRes;
  SINT4 iToRes;
  FLT4  fAddDbz;
  char  sPath[PATHNAME_SIZE];
  UINT1 lJustScan;      /* Scan the file to see what format it is in */
  /* These two flags should not be both turned on */
  UINT1 lSwapToNative;  /* Switch to native byte swapping format */
  UINT1 lSwapToOther;   /* Switch to the other byte swapping format */
  UINT1 lFrom1;         /* Convert from version 1 */
  UINT1 lNewLat;
  UINT1 lNewLon;
  UINT1 lShipVel;       /* Apply shipboard velocity correction */
  UINT1 lNewTime;       
  char sLat[32];
  char sLon[32];
  char sNewTime[16];      /* New Time YYYYMMDD */
};

static SINT4 iYear, iMon, iDay;

/* Co-Routine to extract bytes, and static variables used by the
 * routine.
 */
static void get_raw_bytes( SINT2 *buf_a, SINT4 icnt_a ) ;

static SINT4  irec_c ;		/* Record number */
static SINT4  ioff_c ;		/* Offset within record */
static UINT1 *prod_c ;		/* Pointer to product */

#define PRODPTR( IREC, IOFF ) \
  ((UINT1 *)(prod_c + (IREC * TAPE_RECORD_LEN) + IOFF))

/* Other local function prototypes
 */
static void product_raw
( struct raw_product *pProduct_a,
  SINT4 iSize_a,            /* Size of the product */
  const struct command_line *pCommand_a );

static void RayChanges
( struct raw_product *pRaw_a,
  const struct command_line *pCommand_a );

static void put_raw_bytes
(const void *buf_a, SINT4 icnt_a, SINT4 irec_a, SINT4 ioff_a ) ;

static UINT1 lChangeDbz
( struct data_ray *pRay_a, SINT4 idtype_a,
  FLT4 fAddDbz_a ) ;

static UINT1 lChangeVel
( struct data_ray *pRay_a, 
  SINT4 idtype_a,
  const SINT2 iVelCor_a[2] );

static void ProcessCommandLine
(int argc_a, 
 char *argv_a[],
 struct command_line *pCommand_a );

void setupNewTime(struct command_line *pCommand_a);

static void usage( void ) ;

/* ==================================================
 * Exit status will be "1" for any kind of error, "0" for successful
 * return.
 */
int main( int argc_a, char** argv_a )
{
  MESSAGE istatus ; SINT4 iSize, iChan ; 
  struct raw_product *pRaw;
  struct command_line Command;

  setlocale( LC_ALL, "" ) ;

  /* Parse command line options
   */
  ProcessCommandLine( argc_a, argv_a, &Command );
  if (Command.lNewTime)
    setupNewTime(&Command);

  /* Open the raw product file in R/W mode.  All of the modifications
   * will be done in-place.
   */
  istatus = imapopen( Command.sPath, TRUE, (void**)&pRaw, &iSize, &iChan ) ;
  if( istatus != SS_NORMAL ) {
    fprintf( stderr,  "Could not open '%s' for Read/Write.\n", Command.sPath ) ;
    sig_signal( istatus ) ; exit(1) ;
  }

  product_raw( pRaw, iSize, &Command ) ;

  istatus = imapclose( pRaw, iSize, iChan ) ;
  if( istatus != SS_NORMAL ) { sig_signal( istatus ) ; exit(EXIT_FAILURE) ; }

  exit( EXIT_SUCCESS ) ;
}

/* ==============================================================
 * Print information about a RAW product.  Entered with a pointer
 * to the beginning (first 6144-byte record) of the full product.
 */
static void product_raw
( struct raw_product *pRaw_a,
  SINT4 iSize_a,            /* Size of the product */
  const struct command_line *pCommand_a )
{
  struct ingest_header *inghdr ;
  UINT1 lChangeResolution = (pCommand_a->iFromRes != pCommand_a->iToRes) &&
    (pCommand_a->iFromRes > 0) && (pCommand_a->iToRes > 0) ;
  UINT1 lAddDbz = ( pCommand_a->fAddDbz != 0.0 ) ;
  UINT1 lRayChanges = (lAddDbz||pCommand_a->lShipVel||pCommand_a->lNewTime);

  if (pCommand_a->lJustScan)
    {
      MESSAGE iStatus; SINT4 iVersion; UINT2 lSwap;
      iStatus = vcv_version( &pRaw_a->Record[0].PHeader.hdr, &iVersion, &lSwap );
      if( iStatus != SS_NORMAL )
	{ sig_signal(iStatus); exit(EXIT_FAILURE); }
      
      printf( "Scan results: Version:%d, Swap:%d\n", iVersion, lSwap );
      return;
    }

  /* Must byte swap first.  Other things will be wrong. */
  if ((pCommand_a->lSwapToNative)||(pCommand_a->lFrom1))
    {
      MESSAGE istatus;
      UINT4 iOpCode=0;

      if (pCommand_a->lSwapToNative) iOpCode|=VCV_NATIVE;
      if (pCommand_a->lFrom1) iOpCode|=VCV_FROM_V1|VCV_TO_V2;
      istatus = vcv_product( (generic_product *)(void*)pRaw_a, NULL, iSize_a, iOpCode );
      if (istatus != SS_NORMAL)
	{ sig_signal(istatus); exit(EXIT_FAILURE); }
    }

  /* The first two "records" of the product consist of a product
   * header structure and an ingest header structure.  Each is padded
   * out to one record.
   */
  prod_c = (UINT1 *) pRaw_a ;
  inghdr = &(pRaw_a->Record[1].IHeader);

  /* Make sure that this file really looks like a raw product.  Check
   * the ID's in the product and ingest headers.
   */
  if((pRaw_a->Record[0].PHeader.hdr.id    != ST_PRODUCT_HDR) ||
     (inghdr->hdr.id     != ST_INGEST_HDR ) ||
     (inghdr->tcf.hdr.id != ST_TASK_CONF  ) ) {
       fprintf( stderr,  "ERROR: File headers contain invalid ID's\n" ) ; exit(1) ;
     }

  /* Do all quality checking before making more changes. */

  /* If we are changing range resolution, then verify that the input
   * bin spacing of the product matches the bin spacing that the user
   * is expecting to be converting from.
   */
  if( lChangeResolution ) 
    {
      if( inghdr->tcf.rng.ibin_in_step != pCommand_a->iFromRes ) {
	fprintf( stderr,  "ERROR: Product input bin spacing is %.2f meters (not %.2f).\n",
		 0.01 * (FLT4)inghdr->tcf.rng.ibin_in_step,
		 0.01 * pCommand_a->iFromRes ) ; exit(EXIT_FAILURE) ;
      }
    }

  /* Platform motion correction requires velocity and extended headers
   * recorded.
   */
  if (pCommand_a->lShipVel)
    {
      if( !lDspMaskTest( &inghdr->tcf.dsp.DataMask, DB_XHDR) )
	{
	  fprintf( stderr,  "ERROR: No XHDR, cannot apply shipboard velocity correction.\n" );
	  exit(EXIT_FAILURE);
	}
      if( !lDspMaskTest( &inghdr->tcf.dsp.DataMask, DB_VEL) )
	{
	  fprintf( stderr,  "ERROR: No VEL, cannot apply shipboard velocity correction.\n" );
	  exit(EXIT_FAILURE);
	}

      if( 1 != inghdr->tcf.dsp.DataMask.iXhdrType )
	{
	  fprintf( stderr,  "ERROR: XHDR not type 1, cannot apply shipboard velocity correction.\n" );
	  exit(EXIT_FAILURE);
	}
    }
  

  /* If we are changing range resolution, fix the range entries
   * in the task range info structure and product header structure.
   */
  if( lChangeResolution ) 
    {
      SINT4 iStartBinNum = inghdr->tcf.rng.ibin_first / inghdr->tcf.rng.ibin_in_step ;
      
      inghdr->tcf.rng.ibin_in_step  = pCommand_a->iToRes ;
      inghdr->tcf.rng.ibin_out_step = pCommand_a->iToRes * (SINT4)(1 + inghdr->tcf.rng.ibin_avg) ;
      
      inghdr->tcf.rng.ibin_first = pCommand_a->iToRes * iStartBinNum ;
      inghdr->tcf.rng.ibin_last  = pCommand_a->iToRes *
	( iStartBinNum + inghdr->tcf.rng.ibin_in_num - 1 ) ;
      
      pRaw_a->Record[0].PHeader.end.ibin_first = inghdr->tcf.rng.ibin_first ;
      pRaw_a->Record[0].PHeader.end.ibin_last  = inghdr->tcf.rng.ibin_last ;
      
      pRaw_a->Record[0].PHeader.pcf.psi.raw.iraw_bin_last = pRaw_a->Record[0].PHeader.end.ibin_last ;
    }
  
  /* If we are changing dBZo, then make the change in the stored value
   * from the task configuration structure.
   */
  if( lAddDbz ) {
    inghdr->tcf.cal.iz_calib += NINT( 16.0 * pCommand_a->fAddDbz ) ;
  }

  /* If we are applying the platform velocity correction, then turn on
   * the bit in the header.
   */
  if (pCommand_a->lShipVel)
    {
      inghdr->tcf.cal.iflags |= TCF_CAL_SHIP_COR;

    }

  /* Apply individual ray changes, if desired.*/
  if (lRayChanges) RayChanges( pRaw_a, pCommand_a );


  /* Optionally byte swap to non-native at the end. */
  if (pCommand_a->lSwapToOther)
    {
      MESSAGE istatus;
      istatus = vcv_product( (generic_product *)(void*)pRaw_a, NULL, iSize_a, 
			     VCV_FROM_V2|VCV_TO_V2|VCV_SWAP ) ;
      if (istatus != SS_NORMAL)
	{ sig_signal(istatus); exit(EXIT_FAILURE); }
    }

/*
  printf("Ingest Hdr Lat %s \n",srdeg_to_lat(fDegFromBin4(inghdr->icf.ilat)));
  printf("Ingest Hdr Lon %s \n",srdeg_to_lon(fDegFromBin4(inghdr->icf.ilon)));

  printf("Product Hdr Lat %s \n",
   srdeg_to_lat(fDegFromBin4(pRaw_a->Record[0].PHeader.end.ilat_center)));
  printf("Product Hhr Lon %s \n",
   srdeg_to_lon(fDegFromBin4(pRaw_a->Record[0].PHeader.end.ilon_center)));
*/

  if (pCommand_a->lNewLat)
  {
    FLT8 flat = fDegFromText(pCommand_a->sLat);
    BIN4 ilat = iBin4FromFDeg(flat);

    inghdr->icf.ilat = ilat;
    pRaw_a->Record[0].PHeader.end.ilat_center = ilat;
    printf("New Latitude %s\n",srdeg_to_lat(fDegFromBin4(ilat)));
  }

  if (pCommand_a->lNewLon)
  {
    FLT8 flon = fDegFromText(pCommand_a->sLon);
    BIN4 ilon = iBin4FromFDeg(flon);

    inghdr->icf.ilon = ilon;
    pRaw_a->Record[0].PHeader.end.ilon_center = ilon;
    printf("New Longitude %s\n",srdeg_to_lon(fDegFromBin4(ilon)));
  }

  //This goes after RayChanges
  if (pCommand_a->lNewTime)
  {
    SINT4 itime_diff_secs;
    struct ymds_time new_ing_time;

    new_ing_time = inghdr->icf.VolumeYmds;
    new_ing_time.iyear = iYear;
    new_ing_time.imon = iMon;
    new_ing_time.iday = iDay;

/*
    printf("OLD TIME Year %d Month %d Day %d\n", 
       inghdr->icf.VolumeYmds.iyear,  inghdr->icf.VolumeYmds.imon,  inghdr->icf.VolumeYmds.iday);
*/

    //////////////////////////////////////////
    //product_configruation
    //////////////////////////////////////////
    //prod_time
    itime_diff_secs = iymds_subtract(&pRaw_a->Record[0].PHeader.pcf.prod_time, &inghdr->icf.VolumeYmds);
    pRaw_a->Record[0].PHeader.pcf.prod_time = new_ing_time;
    ymds_add_seconds(itime_diff_secs, &pRaw_a->Record[0].PHeader.pcf.prod_time);

    //ing_time
    itime_diff_secs = iymds_subtract(&pRaw_a->Record[0].PHeader.pcf.ing_time, &inghdr->icf.VolumeYmds);
    pRaw_a->Record[0].PHeader.pcf.ing_time = new_ing_time;
    ymds_add_seconds(itime_diff_secs, &pRaw_a->Record[0].PHeader.pcf.ing_time);

    //epoch_time
    itime_diff_secs = iymds_subtract(&pRaw_a->Record[0].PHeader.pcf.epoch, &inghdr->icf.VolumeYmds);
    pRaw_a->Record[0].PHeader.pcf.epoch = new_ing_time;
    ymds_add_seconds(itime_diff_secs, &pRaw_a->Record[0].PHeader.pcf.epoch);

    //////////////////////////////////////////
    //product_end
    //////////////////////////////////////////
    //old_ing
    itime_diff_secs = iymds_subtract(&pRaw_a->Record[0].PHeader.end.old_ing, &inghdr->icf.VolumeYmds);
    pRaw_a->Record[0].PHeader.end.old_ing = new_ing_time;
    ymds_add_seconds(itime_diff_secs, &pRaw_a->Record[0].PHeader.end.old_ing);

    //////////////////////////////////// 
    //ingest_header 
    //task_configruation 
    //////////////////////////////////// 
    //old_ing
    itime_diff_secs = iymds_subtract(&inghdr->tcf.end.ing_time, &inghdr->icf.VolumeYmds);
    inghdr->tcf.end.ing_time = new_ing_time;
    ymds_add_seconds(itime_diff_secs, &inghdr->tcf.end.ing_time);

    //////////////////////////////////////////
    //ingest header
    //////////////////////////////////////////
    inghdr->icf.VolumeYmds = new_ing_time;
  }

}
/* ==============================================================
 * Make changes to the individual rays of the raw product.
 */
static void RayChanges
( struct raw_product *pRaw_a,
  const struct command_line *pCommand_a )
{
  SINT4 ii, iprms, irptr, idtypes[NUM_DEFINED_DATA], isweep, isweeplo, isweephi ;
  struct ingest_header *inghdr ;
  struct data_convert Convert;
  UINT1 lAddDbz = ( pCommand_a->fAddDbz != 0.0 ) ;

  inghdr = &(pRaw_a->Record[1].IHeader) ;

  PendFillDataConvert( &Convert, &pRaw_a->Record[0].PHeader.end, CONVERT_FLAGS_DEFAULT );
  /* Count up the number of parameters that were recorded.  This is
   * needed in order to skip through the sweeps properly.
   */
  for( iprms=ii=0 ; ii < NUM_DEFINED_DATA ; ii++ )
    {
      if( lDspMaskTest( &inghdr->tcf.dsp.DataMask, ii) )
	{ idtypes[iprms] = ii ; iprms++ ; }
    }

  /* Get sweep limits for the data in this particular RAW product.  If
   * it is a single sweep product, then use just the one that is
   * selected in the PSI structure.  Otherwise convert the entire set.
   */
  if( pRaw_a->Record[0].PHeader.pcf.psi.raw.iflags & RAW_FLG_SWEEP ) {
    isweeplo  = pRaw_a->Record[0].PHeader.pcf.psi.raw.isweep ;
    isweephi  = pRaw_a->Record[0].PHeader.pcf.psi.raw.isweep ;
  } else {
    isweeplo = 1 ;
    isweephi = inghdr->tcf.scan.isweeps ;
  }

  /* 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 perhaps print
   * some subset of it.
   */
  irec_c = 2 ;			/* Record number */
  ioff_c = 0 ;			/* Offset within record */

  for( isweep = isweeplo ; isweep <= isweephi ; isweep++ ) {
    struct ingest_data_header idhdrs[32] ; SINT4 iprm ;
    char sTimeBuf[TIMENAME_SIZE];

    /* 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( iprm=0 ; iprm < iprms ; iprm++ ) 
    {
      SINT4 irecHold = irec_c, ioffHold = ioff_c ;
      get_raw_bytes( (SINT2*)&idhdrs[iprm], INGEST_DATA_HEADER_SIZE ) ;
      if ( pCommand_a->lNewTime )
      {
        struct ymds_time new_ing_time;
        SINT4 itime_diff_secs;

        new_ing_time = inghdr->icf.VolumeYmds;
        new_ing_time.iyear = iYear;
        new_ing_time.imon = iMon;
        new_ing_time.iday = iDay;

        ///////////////////////////////////
        //old_ing
        ///////////////////////////////////
        itime_diff_secs = iymds_subtract(&idhdrs[iprm].time, &inghdr->icf.VolumeYmds);
        idhdrs[iprm].time = new_ing_time;
        ymds_add_seconds(itime_diff_secs, &idhdrs[iprm].time);
        put_raw_bytes( &idhdrs[iprm], INGEST_DATA_HEADER_SIZE, irecHold, ioffHold ) ;
      }  
    }
    printf( "Sweep %2.2d began at: %s\n",
	    isweep, shhmmssddmonyyyy_r( &idhdrs[0].time, sTimeBuf ) ) ;

    /* Read the data from each of the angles, and for each of the
     * parameters that were recorded.
     */
    for( irptr=0 ; irptr < inghdr->icf.irtotl ; irptr++ ) {
      SINT2 iPlatformVelCor[2]={0,0};

      for( iprm=0 ; iprm < iprms ; iprm++ ) {

	/* Code only works for 1-byte data, but buffer allocated for 2 */
	struct data_ray ray, newray ;
	SINT4 inlen, ioutlen, irecHold = irec_c, ioffHold = ioff_c ;

	uncompress_cowords
	  ( get_raw_bytes,
	    pRaw_a->Record[0].PHeader.hdr.ibytes - ((irec_c * TAPE_RECORD_LEN) + ioff_c),
	    &inlen, (SINT2 *)&ray, sizeof(ray)/2, &ioutlen ) ;

	/* If there is a ray here, then see if any repairs need to be
	 * done.  If so, then recompress the repaired ray and replace
	 * the original version in the RAW product.
	 */
	if( ioutlen > 0 ) {
	  UINT1 lChanged = FALSE ;

	  /* Grab the velocity correction from the extended header when it flys by
	   * if we want it.
	   */
	  if ((pCommand_a->lShipVel)&&(DB_XHDR==idtypes[iprm]))
	    {
	      FLT4 fVelocity;
	      iPlatformVelCor[0] = ray.data.xh1.ivel_correction;
	      /* Array contains both 1-byte and 2-byte values */
	      fVelocity = fdata_to_user( iPlatformVelCor[0],  DB_VEL, &Convert );
	      iPlatformVelCor[1] = iuser_to_data( fVelocity, DB_VEL2, &Convert );
	    }

	  if( lAddDbz )
	    lChanged |= lChangeDbz( &ray, idtypes[iprm], pCommand_a->fAddDbz ) ;

	  if (pCommand_a->lShipVel)
	    lChanged |= lChangeVel( &ray, idtypes[iprm], iPlatformVelCor ) ;

	  if( lChanged ) {
	    SINT4 iNewLen ;
	    compress_words( (SINT2 *)&ray, ioutlen, (SINT2 *)&newray, &iNewLen ) ;

	    if( inlen != iNewLen ) {
	      fprintf( stderr,  "Compressed ray length mismatch (%d, %d)\n",
		       inlen, iNewLen ) ; exit(1) ;
	    }
	    put_raw_bytes( &newray, 2*iNewLen, irecHold, ioffHold ) ;
	  }
	}
      }
    }

    /* Done with this sweep.  Discard the remainder of this block, if
     * any.
     */
    if( ioff_c ) { ioff_c = 0 ; irec_c++ ; }
  }
}

/* ==================================================
 * Add a fixed offset to all reflectivity values in a ray.
 */
static UINT1 lChangeDbz
( struct data_ray *pRay_a, 
  SINT4 idtype_a,
  FLT4 fAddDbz_a )
{
  UINT1 lChanged = FALSE ;

  switch( idtype_a ) 
    {
    case DB_DBZ: case DB_DBT:
      {
	SINT4 ibin ; UINT1 *pbin = pRay_a->data.iData1;
	
	for( ibin=0 ; ibin < pRay_a->hdr.ibincount ; ibin++, pbin++ ) {
	  if( *pbin != 0 ) {
	    FLT4 fdbz = fdata_to_user( *pbin, DB_DBZ, NULL ) ;
	    *pbin = iuser_to_data( (fdbz + fAddDbz_a), DB_DBZ, NULL ) ;
	    lChanged = TRUE ;
	  }
	}
      }
      break ;
    case DB_DBZ2: case DB_DBT2:
      {
	SINT4 ibin ; UINT2 *pbin = pRay_a->data.iData2;
	
	for( ibin=0 ; ibin < pRay_a->hdr.ibincount ; ibin++, pbin++ ) {
	  if( *pbin != 0 ) {
	    FLT4 fdbz = fdata_to_user( *pbin, DB_DBZ, NULL ) ;
	    *pbin = iuser_to_data( (fdbz + fAddDbz_a), DB_DBZ, NULL ) ;
	    lChanged = TRUE ;
	  }
	}
      }
      break ;
    }
  return( lChanged ) ; 
}
/* ==================================================
 * Apply platform motion velocity correction to 1-byte velocity data
 */
static UINT1 lChangeVel
( struct data_ray *pRay_a, 
  SINT4 idtype_a,
  const SINT2 iVelCor_a[2] )
{
  UINT1 lChanged = FALSE ;

  switch( idtype_a ) 
    {
    case DB_VEL:
      PlatformMotionCorrection1( (SINT1*)pRay_a->data.iData1, iVelCor_a[0], 
				 pRay_a->hdr.ibincount );
      lChanged = TRUE ;
      break ;
    case DB_VEL2:
      PlatformMotionCorrection2( (SINT2*)pRay_a->data.iData2, iVelCor_a[1], 
				 pRay_a->hdr.ibincount );
      lChanged = TRUE ;
      break ;
    }
  return( lChanged ) ; 
}

/* ==================================================
 * 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(irec_c, ioff_c), RAW_PROD_BHDR_SIZE ) ;
      if( bhdr.irec != irec_c ) {
	fprintf( stderr,  "Block header mismatch (%d) at block %d\n",
		 bhdr.irec, irec_c ) ; exit(1) ;
      }
      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(irec_c, ioff_c), icnt ) ;
    iremain -= icnt ; ioff_c += icnt ; pbuf += icnt ;

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

/* ==================================================
 * Routine to insert a run of bytes into the raw product file,
 * skipping the record headers as we go.
 */
static void put_raw_bytes
( const void *buf_a, SINT4 icnt_a, SINT4 irec_a, SINT4 ioff_a )
{
  SINT4 icnt, iremain = icnt_a, irec = irec_a, ioff = ioff_a ;
  const UINT1 *pbuf = (UINT1*)buf_a ;

  while( iremain > 0 ) {
    /* If the offset is zero, then advance past the next record header.
     */
    if( ioff == 0 ) ioff += RAW_PROD_BHDR_SIZE ;

    /* Insert as many bytes into this record as we can.  Bump record
     * number and zero the offset if we write it all.
     */
    icnt = TAPE_RECORD_LEN - ioff ;
    if( icnt > iremain ) icnt = iremain ;

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

    if( ioff == TAPE_RECORD_LEN ) { ioff = 0 ; irec++ ; }
  }
}
/* ================================================
 * Process the command line args.
 */
static void ProcessCommandLine
(int argc_a, 
 char *argv_a[],
 struct command_line *pCommand_a )
{
  int ii;
  memset( pCommand_a, 0, sizeof(*pCommand_a) );
  
  for( ii=1 ; ii<argc_a ; ii++ )
    {
      char *sarg = argv_a[ii] ; FLT4 fValue ;
      int ilen = strlen( sarg );
      
      if( 0 == strncmp( sarg, "-cvres", ilen ) ) {
	if( (++ii < argc_a) &&
	    (sscanf( argv_a[ii], "%f", &fValue ) == 1) ) {
	  pCommand_a->iFromRes = NINT( 100.0 * fValue ) ;
	  if( (++ii < argc_a) &&
	      (sscanf( argv_a[ii], "%f", &fValue ) == 1) ) {
	    pCommand_a->iToRes = NINT( 100.0 * fValue ) ; continue ;
	  } else {
	    fprintf( stderr,  "Missing output resolution for '-cvres'\n" ) ; exit(1) ;
	  }
	} else {
	  fprintf( stderr,  "Missing input resolution for '-cvres'\n" ) ; exit(1) ;
	}
      }
      
      if( 0 == strncmp( sarg, "-addbz", ilen ) ) {
	if( (++ii < argc_a) &&
	    (sscanf( argv_a[ii], "%f", &pCommand_a->fAddDbz ) == 1) ) continue ;
	else {
	  fprintf( stderr, "Missing dBZ change for '-addbz'\n" ) ; exit(EXIT_FAILURE) ;
	}
      }
      
      if( 0 == strncmp( sarg, "-file", ilen ) ) {
	if( (++ii < argc_a) ) {
	  strncpy( pCommand_a->sPath, argv_a[ii], sizeof(pCommand_a->sPath) );
	  continue ;
	} else {
	  fprintf( stderr,  "Missing file name for '-file'\n" ) ; exit(EXIT_FAILURE) ;
	}
      }
      
      if( 0 == strncmp( sarg, "-help", ilen ) ) {
	usage() ; exit(EXIT_SUCCESS) ;
      }

      if( 0 == strncmp( sarg, "-lat", ilen ) ) {
	if( (++ii < argc_a) ) {
	  strncpy( pCommand_a->sLat, argv_a[ii], sizeof(pCommand_a->sLat) );
	  pCommand_a->lNewLat = TRUE;
	  continue ;
	} else {
	  fprintf( stderr, "Missing Latitude for '-lat'\n" ) ; exit(EXIT_FAILURE) ;
	}
      }
      
      if( 0 == strncmp( sarg, "-lon", ilen ) ) {
	if( (++ii < argc_a) ) {
	  strncpy( pCommand_a->sLon, argv_a[ii], sizeof(pCommand_a->sLon) );
	  pCommand_a->lNewLon = TRUE;
	  continue ;
	} else {
	  fprintf( stderr, "Missing Longitude for '-lon'\n" ) ; exit(EXIT_FAILURE) ;
	}
      }
      
      if( 0 == strncmp( sarg, "-native", ilen ) )
	{
	  pCommand_a->lSwapToNative = TRUE;  continue;
	}
      
      if( 0 == strncmp( sarg, "-scan", ilen ) )
	{
	  pCommand_a->lJustScan = TRUE;  continue;
	}
      
      if( 0 == strncmp( sarg, "-shipvel", ilen ) )
	{
	  pCommand_a->lShipVel = TRUE;  continue;
	}
      
      if( 0 == strncmp( sarg, "-swap", ilen ) )
	{
	  pCommand_a->lSwapToOther = TRUE;  continue;
	}

      if( 0 == strncmp( sarg, "-1", ilen ) )
	{
	  pCommand_a->lFrom1 = TRUE;  continue;
	}

      if( 0 == strncmp( sarg, "-time", ilen ) ) {
	if( (++ii < argc_a) ) {
	  strncpy( pCommand_a->sNewTime, argv_a[ii], sizeof(pCommand_a->sNewTime) );
          printf("strlen %d\n", strlen(pCommand_a->sNewTime)); 
          if (strlen(pCommand_a->sNewTime) != 8) 
          {
	    fprintf( stderr, "Time format is YYYYHHMM\n" ) ;
            usage() ; exit(EXIT_FAILURE) ;
          }
	  pCommand_a->lNewTime = TRUE;
	  continue ;
	} else {
	  fprintf( stderr, "Missing Time (YYYYHHMM) for '-time'\n" ) ; exit(EXIT_FAILURE) ;
	}
      }
      
      fprintf( stderr,  "Unknown command line option '%s'\n", sarg ) ;
      usage() ; exit(EXIT_FAILURE) ;
      
    }
  return;
}

void setupNewTime(struct command_line *pCommand_a)
{
    char  *sendptr;
    char  syear[5], smon[3], sday[3];

    strncpy(syear, pCommand_a->sNewTime, 4);
    syear[4] = 0;
    strncpy(smon, &pCommand_a->sNewTime[4], 2);
    smon[2] = 0;
    strncpy(sday, &pCommand_a->sNewTime[6], 2);
    sday[2] = 0;
    
    iYear = strtol(syear, &sendptr, 10);
    iMon = strtol(smon, &sendptr, 10);
    iDay = strtol(sday, &sendptr, 10);

    if ((iYear < 1980 ) || (iYear > 2010))
    {
      printf("Year = %d, Should be between 1980 - 2010\n",iYear);
      exit(EXIT_FAILURE);
    }
    if ((iMon < 1 ) || (iMon > 12))
    {
      printf("Month = %d, Should be between 1 - 11\n",iMon);
      exit(EXIT_FAILURE);
    }
    if ((iDay < 1 ) || (iDay > 31))
    {
      printf("Day = %d, Should be between 1 - 31\n",iDay);
      exit(EXIT_FAILURE);
    }
}

/* ==================================================
 * Print command line options
 */
static void usage( void )
{
  printf( "Command line options:\n" ) ;
  printf( "     -addbz <Offset> : Add dB offset to all data (changes dBZo)\n" ) ;
  printf( "  -cvres <From> <To> : Convert input bin resolutions (meters)\n" ) ;
  printf( "        -file <Name> : Name of file to convert\n" ) ;
  printf( "               -help : Print command line options\n" ) ;
  printf( "      -lat <degrees> : Change Latitude of radar\n" ) ;
  printf( "      -lon <degrees> : Change Longitude of radar\n" ) ;
  printf( "             -native : Byte swap to native format and V2 first\n" ) ;
  printf( "               -scan : Scan the file, report back what we find\n" ) ;
  printf( "            -shipvel : Apply shipboard velocity correction\n" ) ;
  printf( "               -swap : Byte swap to non-native format after\n" ) ;
  printf( "    -time <yyyymmdd> : Change time\n" ) ;
  // printf( "                  -1 : Convert from version 1\n" ) ; does not work right!
}
