/* ********************************************************
 * *                                                      *
 * *  Function to convert a BUFR file to an IRIS product  *
 * *                                                      *
 * ********************************************************
 * File: utils/bufr/BufrToIris.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.
 */

/*
 * 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>
#include <stdio.h>
#define INCLUDE_STDIO_PROTOTYPES 1
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern "C" {
#include "desc.h"
#include "bufr.h"
#include "bitio.h"
#include "rlenc.h"
}

#include "sigtypes.h"
#include "projection.h"
#include "setup.h"
#include "headers.h"
#include "product.h"
#include "sigversion.h"

#include "config_lib.h"
#include "himath_lib.h"
#include "misc_lib.h"
#include "user_lib.h"

#include "sigbufr.h"

#define MAXDESCS  (80)

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

/* Information from the product useful in the conversion */
struct convert_info
{
  int  iBufrProductType;
  int  iBufrProjection;
  struct ymds_time IngTime;
  union level_control Level;
  char spad_end[2];
};

struct bufr_site
{
  int  iBlockNumber;
  int  iStationNumber;
  int  lDataPresent;
};

/* Information we care about found in the BUFR header */
#define MAX_LEVELS (16)
struct bufr_header8
{
  /* Corner order is: 0:TopLeft, 1:TopRight, 2:BottomLeft, 3:BottomRight */
  struct latlon8 Corners[4];
  struct latlon8 ProjectionRef;
  struct latlon8 Center;
  FLT8 fStdPar1;        /* Radians */
  FLT8 fStdPar2;
  FLT8 fEarthRadiusE;  /* Earth's radius in km at the equator and poles */
  FLT8 fEarthRadiusP;
  int  iCornerCount;
  int  lCornerCount;
  int  lCenter;
  int  lProjectionRef;
  int  iBlockNumber;
  int  lBlockNumber;
  int  iStationNumber;
  int  lStationNumber;
  int  iTimeYear;
  int  lTimeYear;
  int  iTimeMonth;
  int  lTimeMonth;
  int  iTimeDay;
  int  lTimeDay;
  int  iTimeHour;
  int  lTimeHour;
  int  iTimeMinute;
  int  lTimeMinute;
  FLT4 fXScale;  /* Scale in meters/pixel */
  int  lXScale;
  FLT4 fYScale;  /* Scale in meters/pixel */
  int  lYScale;
  int  iXSize;
  int  lXSize;
  int  iYSize;
  int  lYSize;
  int  iProjectionType;
  int  lProjectionType;
  int  iPictureType;
  int  lPictureType;
  int  iDataType;
  int  lDataType;  /* Data type set implies that seams and seam count is set */
  FLT4 fLevelSeams[MAX_LEVELS];
  int  iLevelCount;  /* Number of level seams specified. */

  int  lSiteCount;  /* Site count set implies this is a composite, and */
  int  iSiteCount;  /* the site list is filled in. */
  struct bufr_site Sites[MAX_SITES];
  int  lImageFound;
  char sImagePath[PATHNAME_SIZE];
  int  iImageDepth;
  int  lZRConst;
  FLT4 fZRConst;
  int  lZRExp;
  FLT4 fZRExp;
  int  lFrequency;
  FLT4 fFrequency;
  int  lPrf;
  FLT4 fPrf;
  int  lHeightStation;   /* Height in meters */
  FLT4 fHeightStation;
  int  lHeightData;      /* Height in meters */
  FLT4 fHeightData;
  int  lElevationAngle;
  FLT4 fElevationAngle;  /* Angle in degrees */
  int  lSampleSize;
  int  iSampleSize;
  int  bReflectivityThreshold;  /* Any reflectivity value is stored here */
  FLT4 fReflectivityThreshold;
  int  bAccumulationTime;       /* RAIN1/N Accumulation time store here, negative */
  int  iAccumulationTimeSeconds;
};

struct convert_site_table8
{
  struct latlon8 Center;
  int   iBlockNumber;
  int   iStationNumber;
  SINT4 iMinutesWest;
  char  sSitename16[20];   /* IRIS Sitename, null terminated */
  SINT4 iSiteNumber;     /* IRIS Sitenumber, range 0-30 */
};

/* Information from the config file to customize the convertor */
struct convert_config8
{
  struct convert_site_table8 Sites[ MAX_CONVERT_SITES ];
  struct sig_logging_spec Logging;
  UINT1 lSitesFromStation;     /* Determine IRIS sitename from station ID */
  UINT1 lPinFromCorners;       /* Determine location from corners */
  UINT1 ipad_2x2[2];
  SINT4 iSiteCount;
  char  sBufrTablDir[ PATHNAME_SIZE ]; /* Directory containing BUFR tables */
};

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

static void Usage( void );

static void ConvertToIris
(const char  *pBufr_a,                /* Pointer to BUFR file */
 const SINT4 iBufrSize_a,             /* Size of BUFR file */
 const char *sOutPath_a,              /* Output pathname */
 const struct convert_config8 *pCConfig_a,
 const struct color_setup *pColorSetup_a );

static int decode_it
(const char *dad,
 size_t dadlen, 
 const char *da,
 size_t dalen);

static int ProcessDesc
(varfl val, int ind);

static UINT1 lProcessElementDescriptor
( varfl fVal_a,
  const del *pEl_a,
  struct bufr_header8 *pBHeader_a );

/* Handle the data type changing within a file.  This is normally not
 * allowed, but will happen for the Z threshold in a echo tops file.
 * Returns TRUE on an error.
 */
static UINT1 Handle_Data_Changing
( varfl fVal_a,
  const del *pEl_a,
  struct bufr_header8 *pBHeader_a );

static void ConvertProductHeader
( const struct bufr_header8 *pBHeader_a,
  struct product_hdr    *pProdHeader_a,
  const struct convert_config8 *pCConfig_a,
  const struct color_setup *pColorSetup_a );

static void ConvertData
  (const struct product_hdr *piris_head_a,
   const struct bufr_header8 *pBHeader_a,
   FILE *pIrisStream_a );

static void Convert1ByteData
(const struct product_hdr *piris_head_a, /* Iris product header */
 const struct bufr_header8 *pBHeader_a,
 const UINT1 *pImage_a,                  /* Image file data */
 FILE *pIrisStream_a );                  /* Write iris data here */

static void Convert2ByteData
(const struct product_hdr *pIrisHead_a, /* Iris product header */
 const struct bufr_header8 *pBHeader_a,
 const UINT1 *pBufrImage_a,             /* Image file data */
 FILE *pIrisStream_a );                 /* Write iris data here */

static int get_lens
(const char *buf,
 long len,
 size_t secl[6]);

static const struct convert_site_table8 * FindSiteByWmo
( int iBlockNumber_a,             /* WMO block number */
  int iStationNumber_a,             /* WMO station number */
  const struct convert_config8 *pCConfig_a ); /* Configuration */

static const struct convert_site_table8 * FindSiteByCenter
( const struct latlon8 *pCenter_a,           /* Center location */
  const struct convert_config8 *pCConfig_a );/* Configuration */

static void LoadSetup
( struct convert_config8 *pCConfig_a,
  const char* sFile_a,                  /* Filename of config file */
  int iDevice_a );                      /* Device number */

static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config8 *pConfig_a );

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

static void ExitHandler(void);

static struct convert_config8 CConfig_g;

static struct bufr_header8 BHeader_c;
static dd *pMasterDd_c;
static int iExpandCount_c;   /* Count of how many descriptors a master has expanded to */
static char sLog_c[200];

/* ===============================================================
 * Main program starts here.
 */
int main(int iArgc, char *sArgv[])
{
  struct command_line Command;
  char  *pBufr;
  SINT4 iSize, iChannel;
  MESSAGE istatus;
  struct color_setup ColorSetup;

  setlocale( LC_ALL, "" ) ;

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

  /* Process the setup file, if any */
  LoadSetup( &CConfig_g, "BufrToIris.conf", 0 );

  /* Open the logging file, if any */
  if (CConfig_g.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, "BufrToIris.log" );
      if (CConfig_g.Logging.lAppendToLog)
	CConfig_g.Logging.iLogFile = fopen( sLogPath, "a" );
      else
	CConfig_g.Logging.iLogFile = fopen( sLogPath, "w" );
      if (NULL == CConfig_g.Logging.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, "BufrToIris" );

  if (CConfig_g.Logging.lVerbose) 
    {
      struct full_time_spec Time;
      GetTimeFull( &Time, TIME_FLG_LOCAL );

      sprintf( sLog_c, "Verbose logging on, Version:%s%s%s, TZ=%s\n",
	       IRIS_VERSION, IRIS_MINOREV, IRIS_PATCHREV, Time.sRecordTZName );
      Lprintf( &CConfig_g.Logging, sLog_c );
    }
  

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

  /* Check for no sites in the config file */
  if (CConfig_g.iSiteCount<1)
    {
      Lprintf( &CConfig_g.Logging, "ERROR: No SITE line in the BufrToIris.conf file\n" );
      exit(EXIT_FAILURE);
    }

  /* Read the local color setup file.  It does not really matter if there
   * are problems here.  It just means that the default color scales will be
   * invalid.  They can be overridden at display time. */
  istatus = color_load(NULL, &ColorSetup );
  if( istatus != SS_NORMAL )
    {
      Lprintf( &CConfig_g.Logging, "WARNING: Color setup file is missing.\n" );
    }
  istatus = color_limits( "color_setup", &ColorSetup, SETUP_TEST, stdout ) ;
  if( istatus != SS_NORMAL ) sig_signal( istatus );
  
  /* Open the input BUFR data file */
  istatus = imapopen( Command.sInPath, FALSE, (void**)(void*)&pBufr, &iSize, &iChannel );
  if (istatus!=SS_NORMAL)
    {
      sig_signal( istatus );
      exit(EXIT_FAILURE);
    }

  ConvertToIris( pBufr, iSize, Command.sOutPath, &CConfig_g, &ColorSetup );

  istatus = imapclose( pBufr, iSize, iChannel );
  
  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 );
	}
      
    }
  return;
}
/* ==================================================
 */
static void Usage( void )
{
  fprintf( stderr, "UfToIris options:\n" );
  fprintf( stderr, "  -help      -- Print this list\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, "   (all other arguments ignored)\n" );
  exit(EXIT_FAILURE) ;
}

/* ==============================================================
 * Convert product to BUFR.  Entered with a pointer to the beginning 
 * of the full product.  Should exit if it gets an error.
 */
static void ConvertToIris
(const char  *pBufr_a,                /* Pointer to BUFR file */
 const SINT4 iBufrSize_a,             /* Size of BUFR file */
 const char *sOutPath_a,              /* Output pathname */
 const struct convert_config8 *pCConfig_a,
 const struct color_setup *pColorSetup_a )
{
  const char *pBufrStart, *pBufrEnd, *ptr;
  char sProdTime[TIMENAME_SIZE];
  SINT4 iBufrSize;
  const char *sec[6];
  size_t secl[6], iOffset;
  int ii;
  struct product_hdr ProdHeader;
  FILE *pIrisStream;
  MESSAGE istatus;

  pBufrEnd = pBufr_a+iBufrSize_a;

  /****** Find first place where file has the text "BUFR" */
  pBufrStart = NULL;
  for (ptr = pBufr_a; ptr<pBufrEnd - 3; ptr++)
    {
      if (*(ptr)     == 'B' && 
	  *(ptr + 1) == 'U' &&
	  *(ptr + 2) == 'F' &&
	  *(ptr + 3) == 'R') {pBufrStart = ptr; goto FOUND_BUFR; }
    }
  Lprintf( &pCConfig_a->Logging, "'BUFR' not found in BUFR-message!\n");
  exit( EXIT_FAILURE );

 FOUND_BUFR:

  /****** Find last place where file has the text "7777" */
  for (ptr = pBufrEnd-1; ptr>pBufrStart+4; ptr--) 
    {
      if (*(ptr)   == '7' && 
	  *(ptr-1) == '7' &&
	  *(ptr-2) == '7' &&
	  *(ptr-3) == '7') { pBufrEnd = ptr+1; goto B7777_FOUND; }
    }
  Lprintf( &pCConfig_a->Logging, "'7777' not found in BUFR-message!\n");

  exit( EXIT_FAILURE );
  
 B7777_FOUND:
  
  /* Correct length to skip data before the "BUFR". */
  iBufrSize = (pBufrEnd-pBufrStart);
  
  /****** Get length of all 6 sections */
  
  if (!get_lens (pBufrStart, iBufrSize, secl)) 
    {
      Lprintf ( &pCConfig_a->Logging, "Unable to read lengths of BUFR-sections!\n");
      exit( EXIT_FAILURE );
    }

  /******* calculate a pointer to the beginning of each section */

  iOffset = 0;
  for (ii = 0; ii < 6; ii ++) 
    {
      sec[ii] = pBufrStart + iOffset;
      iOffset += secl[ii];
    }

  memset( &BHeader_c,  0, sizeof(BHeader_c) );

  ReadBufrTables(CConfig_g.sBufrTablDir, &pCConfig_a->Logging, sec[1], secl[1]);

  /* Make pathname for temporary image file */
  {
    char sImageFile[FILENAME_SIZE], *sptr;
    sptr = getenv( "IRIS_TEMP" );
    if (NULL == sptr) 
      strcpy( BHeader_c.sImagePath, "/tmp/" );
    else
      strncpy( BHeader_c.sImagePath, sptr, DIRECTORY_SIZE-1 );
    BHeader_c.sImagePath[DIRECTORY_SIZE-1]=0;
    sprintf( sImageFile, "BufrToIris%d", getpid() );
    strcat( BHeader_c.sImagePath, sImageFile );
  }

  /* Starting here we need to remove the image file on exit */
  atexit( ExitHandler );

  /******* decode data descriptor- and data-section now */
  if (!decode_it(sec[3], secl[3], sec[4], secl[4])) 
    {
      Lprintf( &pCConfig_a->Logging, "Unable to decode BUFR-message!\n");
      exit(EXIT_FAILURE);
    }

  ConvertProductHeader( &BHeader_c, &ProdHeader, pCConfig_a, pColorSetup_a );
  
  pIrisStream = fopen( sOutPath_a, "w" );
  if (pIrisStream)
    {
      int lError;
      fwrite( &ProdHeader, 1, sizeof(ProdHeader), pIrisStream );

      ConvertData( &ProdHeader, &BHeader_c, pIrisStream );
      
      lError = fclose( pIrisStream );
      if (lError) 
	{
	  istatus = errno_to_message(errno);
	  sig_signal( istatus );
	  exit(EXIT_FAILURE);
	}
    }
  else
    {
      istatus = errno_to_message(errno);
      istatus = iMessageMod_va( istatus, 1, sOutPath_a );
      sig_signal( istatus );
      exit(EXIT_FAILURE);
    }

  shhmmssddmonyyyy_r( &ProdHeader.pcf.ing_time, sProdTime );
  sprintf( sLog_c, "Converted %s product: %s, Date: %s\n", 
	   sproduct_name5(ProdHeader.pcf.iptype),
	   nullify(ProdHeader.pcf.spname,12), sProdTime );
  Lprintf( &pCConfig_a->Logging, sLog_c );

  return;
}

/*===========================================================================*/
static int decode_it
(const char *dad,
 size_t dadlen, 
 const char *da,
 size_t dalen)

/* This function decodes a BUFR-message consiting of a data-descriptor-section
   and a data-section and saves the result by calling ASCII_OUT

   parameters:
   DAD:      Is where the data-descriptor-section is stored.
   DADLEN:   Number of baytes of the data-descriptor-section.
   DA:       Is where the data-section is stored.
   DALEN:    Number of bytes of the data-section.

   The function returns 1 on success, 0 on a fault.
*/

{
  /* Data Descriptors */
  dd *dds;

  int ndds, i;
  unsigned int vali;
  size_t nvals, n;
  varfl *vals;

  i = bufr_read_msg ((void *)da, (void *)dad, dalen, dadlen, &dds, &ndds, &vals, &nvals);
  if (!i) return 0;

  /******* Save data read to an ASCII-File */

  vali = 0;
  i = 0;
  while (i < ndds) 
    {
      pMasterDd_c = dds + i;
      iExpandCount_c = 0;
      
      /****** There is a radar image in the BUFR-message... */
      
      if ((pMasterDd_c->f == 3 && pMasterDd_c->x == 21 && pMasterDd_c->y == 192)||  /* For 4-bit-pixels top view */
	  (pMasterDd_c->f == 3 && pMasterDd_c->x == 21 && pMasterDd_c->y == 193))   /* For 8-bit-pixels top view */
	{
	  if (pMasterDd_c->y == 193)
	    BHeader_c.iImageDepth = 8;
	  else
	    BHeader_c.iImageDepth = 4;

	  if (!rldec (BHeader_c.sImagePath, vals + vali, &n)) return 0;
	  vali += n;

	  BHeader_c.lImageFound = TRUE;
	}      
      
      /******* "add associated field" descriptor */
      
      else if (pMasterDd_c->f == 2 && pMasterDd_c->x == 04) 
	{
	  if (!bufr_parse (pMasterDd_c, 0, 0, vals, &vali, ProcessDesc)) return 0;
	}
      
      /******* "ordinary" data */
      
      else 
	{
	  if (!bufr_parse (pMasterDd_c, 0, 0, vals, &vali, ProcessDesc)) return 0;
	}
      
      /******* If there is a replication factor -> increase i by the number of 
	       descriptors that were already processed */
      
      if      (pMasterDd_c->f == 1 && pMasterDd_c->y == 0) i += pMasterDd_c->x + 2;
      else if (pMasterDd_c->f == 1 && pMasterDd_c->y != 0) i += pMasterDd_c->x + 1;
      else                             i ++;
    }
  
  free (dds);
  return 1;
}

/*===========================================================================
 * The function returns 1 on success, 0 on a fault.
 */

/* ==============================================================
 * Callback function called for each individual descriptor.
 */
static int ProcessDesc
(varfl fVal_a,    /* Data value read */
 int iIndex_a     /* Index to the global array DES[] holding the description of
		   * known data-descriptors. */
 )
{
  desc *pDesc;

  pDesc = des[iIndex_a];

  if (iIndex_a >= ndes)
    {
      Lprintf( &CConfig_g.Logging, "ProcessDesc Overflow!\n" );
    }
  else if (pDesc->id == ELDESC)
    {
      UINT1 lUnknownDescriptor=TRUE;
      del *pEl;
      pEl = pDesc->el;

      if (pEl->d.f==0)
	lUnknownDescriptor = lProcessElementDescriptor( fVal_a, pEl, &BHeader_c );

      if (CConfig_g.Logging.lVerbose&&lUnknownDescriptor)
	{
	  sprintf (sLog_c, "Unparsed Descriptor: Master: %1d %2d %3d, Value: %12.5f  %1d %2d %3d %s\n",
		   pMasterDd_c->f, pMasterDd_c->x, pMasterDd_c->y, fVal_a, 
		   pEl->d.f, pEl->d.x, pEl->d.y, pEl->elname);
	  Lprintf( &CConfig_g.Logging, sLog_c );
	}
    }
  else
    {
      dseq *pSeq;
      pSeq = pDesc->seq;

      sprintf( sLog_c, "Sequence descriptor, id=%2d %2d %3d!\n", 
	       pSeq->d.f, pSeq->d.x, pSeq->d.y );
      Lprintf( &CConfig_g.Logging, sLog_c );

    }
  iExpandCount_c++;
  return 1;
}
/* =====================================================================
 * Parse an element descriptor.  Return TRUE if it was not recognized.
 */
static UINT1 lProcessElementDescriptor
( varfl fVal_a,
  const del *pEl_a,
  struct bufr_header8 *pBHeader_a )
{
  /* First switch on the descriptor class */
  switch(pEl_a->d.x)
    {
    case BUFR_CLASS_IDENTIFICATION:  /* 1 */
      switch (pEl_a->d.y)
	{
	case 1:
	  /* Block number could be either just for this radar, or 
	   * from a list of sites.  Check the master. */
	  if ((pMasterDd_c->f==3) && (pMasterDd_c->x==21) && (pMasterDd_c->y==250) ) 
	    {
	      /* From list of sites, turn on composite bit */
	      if (!pBHeader_a->lSiteCount) pBHeader_a->lSiteCount = TRUE;

	      /* Assume block number or station number comes first, increment
	       * pointer if already filled in.
	       */
	      if (pBHeader_a->Sites[pBHeader_a->iSiteCount].iBlockNumber)
		{
		  pBHeader_a->iSiteCount++;
		  if (pBHeader_a->iSiteCount>= MAX_SITES)
		    {
		      Lprintf( &CConfig_g.Logging,  "Warning: Max site count exceeded\n" );
		      return TRUE;
		    }
		  /* Default to data present */
		  pBHeader_a->Sites[pBHeader_a->iSiteCount].lDataPresent = 1;
		}

	      pBHeader_a->Sites[pBHeader_a->iSiteCount].iBlockNumber = NINT(fVal_a);
	      return FALSE;
	    }
	  else
	    {
	      /* Tag for this product */
	      pBHeader_a->iBlockNumber = NINT(fVal_a);
	      pBHeader_a->lBlockNumber = TRUE;
	      return FALSE;
	    }


	  break;
	case 2:
	  /* Station number could be either just for this radar, or from
	   * a list of sites.  Check the master. */
	  if ((pMasterDd_c->f==3) && (pMasterDd_c->x==21) && (pMasterDd_c->y==250) ) 
	    {
	      /* From list of sites, turn on composite bit */
	      if (!pBHeader_a->lSiteCount) pBHeader_a->lSiteCount = TRUE;

	      /* Assume block number or station number comes first, increment
	       * pointer if already filled in.
	       */
	      if (pBHeader_a->Sites[pBHeader_a->iSiteCount].iStationNumber)
		{
		  pBHeader_a->iSiteCount++;
		  
		  if (pBHeader_a->iSiteCount>= MAX_SITES)
		    {
		      Lprintf( &CConfig_g.Logging,  "Warning: Max site count exceeded\n" );
		      return TRUE;
		    }
		  /* Default to data present */
		  pBHeader_a->Sites[pBHeader_a->iSiteCount].lDataPresent = 1;
		}

	      pBHeader_a->Sites[pBHeader_a->iSiteCount].iStationNumber = NINT(fVal_a);
	      return FALSE;
	    }
	  else
	    {
	      /* Tag for this product */
	      pBHeader_a->iStationNumber = NINT(fVal_a);
	      pBHeader_a->lStationNumber = TRUE;
	      return FALSE;
	    }
	  break;
	}
      break;
    case BUFR_CLASS_IMAGE: /* 30 */
      switch (pEl_a->d.y)
	{
	case 21:
	  pBHeader_a->iXSize = NINT(fVal_a);
	  pBHeader_a->lXSize = TRUE;
	  return FALSE;
	  break;
	case 22:
	  pBHeader_a->iYSize = NINT(fVal_a);
	  pBHeader_a->lYSize = TRUE;
	  return FALSE;
	  break;
	case 31:
	  pBHeader_a->iPictureType = NINT(fVal_a);
	  pBHeader_a->lPictureType = TRUE;
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_INSTRUMENTATION:  /* 2 */
      switch (pEl_a->d.y)
	{
	case 3:
	  /* Ignore type of measuring equipement */
	  return FALSE;
	  break;
	case 121:
	  pBHeader_a->fFrequency = fVal_a;
	  pBHeader_a->lFrequency = TRUE;
	  return FALSE;
	  break;
	case 125:
	  pBHeader_a->fPrf = fVal_a;
	  pBHeader_a->lPrf = TRUE;
	  return FALSE;
	  break;
	case 135:
	  pBHeader_a->fElevationAngle = fVal_a;
	  pBHeader_a->lElevationAngle = TRUE;
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_LOCATION1:  /* 5 */
      switch (pEl_a->d.y)
	{
	case 1:
	case 2:
	  if (CConfig_g.lPinFromCorners)
	    {
	      if (pBHeader_a->lCornerCount)
		{
		  /* Check for to many corners */
		  if (pBHeader_a->iCornerCount >= 4)
		    {
		      Lprintf( &CConfig_g.Logging,
			       "Warning: More than 4 corners in file.\n" );
		      return TRUE;
		    }
		}
	      else
		{
		  pBHeader_a->iCornerCount = 0;
		  pBHeader_a->lCornerCount = TRUE;
		}
	      pBHeader_a->Corners[pBHeader_a->iCornerCount].fLat = (FPI/180.0)*fVal_a;
	      return FALSE;
	    }
	  else
	    {
	      pBHeader_a->lCenter     = TRUE;
	      pBHeader_a->Center.fLat = (FPI/180.0)*fVal_a;
	      return FALSE;
	    }

	  break;
	case 33:
	  pBHeader_a->fXScale = fVal_a;
	  pBHeader_a->lXScale = TRUE;
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_LOCATION2:  /* 6 */
      switch (pEl_a->d.y)
	{
	case 1:
	case 2:
	  if (CConfig_g.lPinFromCorners)
	    {
	      if (pBHeader_a->lCornerCount)
		{
		  /* Check for to many corners */
		  if (pBHeader_a->iCornerCount >= 4)
		    {
		      Lprintf( &CConfig_g.Logging,
			       "Warning: More than 4 corners in file.\n" );
		      return TRUE;
		    }
		}
	      else
		{
		  pBHeader_a->iCornerCount = 0;
		  pBHeader_a->lCornerCount = TRUE;
		}
	      pBHeader_a->Corners[pBHeader_a->iCornerCount++].fLon = (FPI/180.0)*fVal_a;
	      return FALSE;
	    }
	  else
	    {
	      pBHeader_a->lCenter = TRUE;
	      pBHeader_a->Center.fLon = (FPI/180.0)*fVal_a;
	      return FALSE;
	    }

	  break;
	case 33:
	  pBHeader_a->fYScale = fVal_a;
	  pBHeader_a->lYScale = TRUE;
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_LOCATION_VERT: /* 7 */
      switch (pEl_a->d.y)
	{
	case 1:  /* Height of station (radome) */
	  pBHeader_a->fHeightStation = fVal_a;
	  pBHeader_a->lHeightStation = TRUE;
	  return FALSE;
	  break;
	case 7:  /* Height of data */
	  pBHeader_a->fHeightData = fVal_a;
	  pBHeader_a->lHeightData = TRUE;
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_MAP: /* 29 */
      switch (pEl_a->d.y)
	{
	case 1:
	case 201:
	  pBHeader_a->iProjectionType = NINT(fVal_a);
	  pBHeader_a->lProjectionType = TRUE;
	  return FALSE;
	  break;
	case 2:   /* Coordinate grid type ignored */
	  return FALSE;
	case 193:
	  pBHeader_a->ProjectionRef.fLon = (FPI/180.0)*fVal_a;
	  pBHeader_a->lProjectionRef = TRUE;
	  return FALSE;
	  break;
	case 194:
	  pBHeader_a->ProjectionRef.fLat = (FPI/180.0)*fVal_a;
	  pBHeader_a->lProjectionRef = TRUE;
	  return FALSE;
	  break;
	case 195:  /* False Easting ignored */
	  return FALSE;
	case 196:  /* False Northing ignored */
	  return FALSE;
	case 197:  /* Standard Parallel 1 */
	  pBHeader_a->fStdPar1 = (FPI/180.0)*fVal_a;
	  return FALSE;
	case 198:  /* Standard Parallel 2 */
	  pBHeader_a->fStdPar2 = (FPI/180.0)*fVal_a;
	  return FALSE;
	case 199:  /* Semi-major axis */
	  pBHeader_a->fEarthRadiusE = fVal_a/1000.0;
	  return FALSE;
	case 200:  /* Semi-minor axis */
	  pBHeader_a->fEarthRadiusP = fVal_a/1000.0;
	  return FALSE;
	case 202:  /* Azimuth of initial line ignored */
	  return FALSE;
	}
      break;
    case BUFR_CLASS_PROCESSING:  /* 25 */
      switch (pEl_a->d.y)
	{
	case 3:
	  pBHeader_a->iSampleSize = NINT(fVal_a);
	  pBHeader_a->lSampleSize = TRUE;
	  return FALSE;
	case 6:    /* Z to R conversion type ignored */
	  return FALSE;
	case 7:
	  pBHeader_a->fZRConst = fVal_a;
	  pBHeader_a->lZRConst = TRUE;
	  return FALSE;
	case 8:
	  pBHeader_a->fZRExp = fVal_a;
	  pBHeader_a->lZRExp = TRUE;
	  return FALSE;
	  break;
	case 9:   /* Calibration method ignored */
	  return FALSE;
	case 10:  /* Clutter treatment ignored */
	  return FALSE;
	case 11:  /* Ground occultation correction ignored */
	  return FALSE;
	case 12:  /* Range attenuation ignored */
	  return FALSE;
	case 13:  /* Bright band correction ignored */
	  return FALSE;
	case 15:  /* Radome attenuation correction ignored */
	  return FALSE;
	case 16:  /* Clear air attenuation correction ignored */
	  return FALSE;
	case 17:  /* Attenuation correction ignored */
	  return FALSE;
	}
      break;
    case BUFR_CLASS_QUALIFIERS:  /* 31 */
      switch (pEl_a->d.y)
	{
	case 1:  /* Delayed descriptor replication factor is ignored */
	  return FALSE;
	  break;

	case 31: /* Data present indicator */
	  /* Must be from a list of sites.  Check the master. */
	  if ((pMasterDd_c->f==3) && (pMasterDd_c->x==21) && (pMasterDd_c->y==250) ) 
	    {
	      /* Block or station number must come first */
	      if (!pBHeader_a->lSiteCount)
		{
		  Lprintf( &CConfig_g.Logging,  "Warning: Site table must start with block or station.\n" );
		  return TRUE;
		}

	      pBHeader_a->Sites[pBHeader_a->iSiteCount].lDataPresent = NINT(fVal_a);
	      return FALSE;
	    }
	  break;
	}
      break;
    case BUFR_CLASS_QUALITY:  /* 33 */
      switch (pEl_a->d.y)
	{

	case 3:  /* Quality info ignored */
	  return FALSE;
	  break;
	}
      break;
    case BUFR_CLASS_RADAR_DATA:  /* 21 */
      /* Store first reflectivity as a possible TOPS threshold */
      switch (pEl_a->d.y)
	{
	case 1:
	  if (!pBHeader_a->bReflectivityThreshold)
	    {
	      pBHeader_a->fReflectivityThreshold = fVal_a;
	      pBHeader_a->bReflectivityThreshold = TRUE;
	    }
	  break;
	}
      /* Check for error because the data type has changed in the file */
      if (pBHeader_a->lDataType)
	{
	  if (pBHeader_a->iDataType != pEl_a->d.y )
	    {
	      UINT1 bReturn;
	      bReturn = Handle_Data_Changing( fVal_a, pEl_a, pBHeader_a );
	      return bReturn;
	    }
	  if (pBHeader_a->iLevelCount >= MAX_LEVELS)
	    {
	      Lprintf( &CConfig_g.Logging,
		       "Warning: Max level count supported by pipe exceeded\n" );
	      return TRUE;
	    }
	}
      else
	{
	  pBHeader_a->iLevelCount = 0;
	  pBHeader_a->iDataType = pEl_a->d.y;
	  pBHeader_a->lDataType = TRUE;
	}
      pBHeader_a->fLevelSeams[pBHeader_a->iLevelCount++] = fVal_a;
      return FALSE;
      break;
    case BUFR_CLASS_TIME:  /* 4 */
      switch (pEl_a->d.y)
	{
	case 1:
	  pBHeader_a->iTimeYear = NINT(fVal_a);
	  pBHeader_a->lTimeYear = TRUE;
	  return FALSE;
	  break;
	case 2:
	  pBHeader_a->iTimeMonth = NINT(fVal_a);
	  pBHeader_a->lTimeMonth = TRUE;
	  return FALSE;
	  break;
	case 3:
	  pBHeader_a->iTimeDay = NINT(fVal_a);
	  pBHeader_a->lTimeDay = TRUE;
	  return FALSE;
	  break;
	case 4:
	  pBHeader_a->iTimeHour = NINT(fVal_a);
	  pBHeader_a->lTimeHour = TRUE;
	  return FALSE;
	  break;
	case 5:
	  pBHeader_a->iTimeMinute = NINT(fVal_a);
	  pBHeader_a->lTimeMinute = TRUE;
	  return FALSE;
	  break;
	case 23:
	  pBHeader_a->iAccumulationTimeSeconds += 24*3600*NINT(fVal_a); /* Days */
	  pBHeader_a->bAccumulationTime = TRUE;
	  return FALSE;
	  break;
	case 24:
	  pBHeader_a->iAccumulationTimeSeconds += 3600*NINT(fVal_a);  /* Hours */
	  pBHeader_a->bAccumulationTime = TRUE;
	  return FALSE;
	  break;
	case 25:
	  pBHeader_a->iAccumulationTimeSeconds += 60*NINT(fVal_a);  /* Minutes */
	  pBHeader_a->bAccumulationTime = TRUE;
	  return FALSE;
	  break;
	case 26:
	  pBHeader_a->iAccumulationTimeSeconds += NINT(fVal_a);   /* Seconds */
	  pBHeader_a->bAccumulationTime = TRUE;
	  return FALSE;
	  break;
	}
      break;
    }
  return TRUE;
}

/* ================================================================
 * Handle the data type changing within a file.  This is normally not
 * allowed, but will happen for the Z threshold in a echo tops file.
 * Returns TRUE on an error.
 */
static UINT1 Handle_Data_Changing
( varfl fVal_a,
  const del *pEl_a,
  struct bufr_header8 *pBHeader_a )
{
  /* There are two legal cases of this:
   * 1) After height scale, we get a reflectivity threshold.
   * 2) After 1 reflectivity number, we get a height scale.
   */

  /* Detect Case 1:  We can just skip any warning */
  if ((pBHeader_a->iDataType==21)&&(pEl_a->d.y==1)) return FALSE;


  /* Detect Case 2:  We restart the data scale assembly */
  if ((pBHeader_a->iDataType==1)&&(pEl_a->d.y==21)&&(pBHeader_a->iLevelCount==1))
    {
      pBHeader_a->iLevelCount = 0;
      pBHeader_a->iDataType = pEl_a->d.y;
      pBHeader_a->fLevelSeams[pBHeader_a->iLevelCount++] = fVal_a;
      return FALSE;
    }

  /* Default case is to log a warning: */
  sprintf (sLog_c, "Warning: Radar data changed from %d to %d\n",
	   pBHeader_a->iDataType, pEl_a->d.y );
  Lprintf( &CConfig_g.Logging, sLog_c );
  return TRUE;
}

/* ================================================================
 * Convert the product header.  Exit if there is a fatal error.
 */
static void ConvertProductHeader
( const struct bufr_header8 *pBHeader_a,
  struct product_hdr    *pProdHeader_a,    /* Header completely empty */
  const struct convert_config8 *pCConfig_a,
  const struct color_setup *pColorSetup_a )
{  
  const struct convert_site_table8 *pSite=NULL;
  struct mpj_region8     Region;

  memset( pProdHeader_a, 0, sizeof(*pProdHeader_a) );
  HeaderFromScratch( pProdHeader_a, NULL, "UNKNOWN         ", "UNKNOWN         ", "BUFR" );

  /* We do not care about lYScale, and lPictureType */

  /* Determine sitename if we are using station number for this purpose,
   * else do it later.
   */
  if (pCConfig_a->lSitesFromStation)
    {
      /* Pull sitename from the WMO block and station numbers */
      if (!pBHeader_a->lBlockNumber)
	{
	  Lprintf( &CConfig_g.Logging,  "Error: WMO Block number missing.\n" );
	  exit( EXIT_FAILURE );
	}
      if (!pBHeader_a->lStationNumber)
	{
	  Lprintf( &CConfig_g.Logging,  "Error: WMO station number missing.\n" );
	  exit( EXIT_FAILURE );
	}
      pSite = FindSiteByWmo( pBHeader_a->iBlockNumber, pBHeader_a->iStationNumber, pCConfig_a );
      if (!pSite)
	{
	  sprintf( sLog_c,  "WMO Block:%d and Station:%d not in table.\n", 
		   pBHeader_a->iBlockNumber, pBHeader_a->iStationNumber );
	  Lprintf( &pCConfig_a->Logging,  sLog_c );
	  exit( EXIT_FAILURE );
	}
    }

  /* Convert the data type, in some cases deduce the product type also */
  if (!pBHeader_a->lDataType)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Data type missing.\n" );
      exit( EXIT_FAILURE );
    }
  switch( pBHeader_a->iDataType )
    {
    case 1:  /* Horizontal reflectivity */
      pProdHeader_a->pcf.idata_in  = DB_DBZ;
      pProdHeader_a->pcf.idata_out = DB_DBZ;
      break;
    case 3:  /* ZDR */
      pProdHeader_a->pcf.idata_in  = DB_ZDR;
      pProdHeader_a->pcf.idata_out = DB_ZDR;
      break;
    case 5:  /* LDR */
      pProdHeader_a->pcf.idata_in  = DB_LDRH;
      pProdHeader_a->pcf.idata_out = DB_LDRH;
      break;
    case 14: /* V */
      pProdHeader_a->pcf.idata_in  = DB_VEL;
      pProdHeader_a->pcf.idata_out = DB_VEL;
      break;
    case 17: /* Width */
      pProdHeader_a->pcf.idata_in  = DB_WIDTH;
      pProdHeader_a->pcf.idata_out = DB_WIDTH;
      break;
    case 21: /* TOPS */
      pProdHeader_a->pcf.idata_in  = DB_DBZ;
      pProdHeader_a->pcf.idata_out = DB_HEIGHT;
      pProdHeader_a->pcf.iptype    = PROD_TOPS;
      pProdHeader_a->pcf.psi.top.iZThreshold = 0*16;
      break;
    case 31: /* VIL */
      pProdHeader_a->pcf.idata_in  = DB_DBZ;
      pProdHeader_a->pcf.idata_out = DB_VIL2;
      pProdHeader_a->pcf.iptype    = PROD_VIL;
      pProdHeader_a->pcf.psi.vil.ivil_bottom = 0*100000;
      pProdHeader_a->pcf.psi.vil.ivil_top   = 10*100000;
      /* Needs conversion factor */
      break;
    case 36: /* Rainrate */
      pProdHeader_a->pcf.idata_in  = DB_DBZ;
      pProdHeader_a->pcf.idata_out = DB_RAINRATE2;
      /* Rainrate implies SRI product unless explicitly PPI or CAPPI */
      if ((!pBHeader_a->lPictureType) ||
	  ( (pBHeader_a->iPictureType!=BUFR_PTYPE_PPI) &&
	    (pBHeader_a->iPictureType!=BUFR_PTYPE_CAPPI) ) )
	pProdHeader_a->pcf.iptype    = PROD_SRI;
      break;
    default:
      sprintf( sLog_c, "Sorry, Radar data type %d not supported.\n", pBHeader_a->iDataType );
      Lprintf( &pCConfig_a->Logging, sLog_c );
      exit( EXIT_FAILURE );
      break;
    }

  /* Set the default color scale */
  SetDefaultColors( pProdHeader_a, pColorSetup_a );
  
  /* If the product type was not set yet, then use what little
   * information is in the picture type.  Set it to CAPPI if
   * so flagged, all other cases set it to PPI.
   */
  if (pProdHeader_a->pcf.iptype == 0 )
    {
      pProdHeader_a->pcf.iptype = PROD_PPI;
      pProdHeader_a->pcf.psi.ppi.ippi_el = 0;
      pProdHeader_a->pcf.psi.ppi.iMaxRange = 0;
      pProdHeader_a->pcf.psi.ppi.iMaxHeight = 0;

      /* Check now for picture type */
      if (pBHeader_a->lPictureType)
	{
	  switch( pBHeader_a->iPictureType )
	    {
	    case BUFR_PTYPE_CAPPI:
	      pProdHeader_a->pcf.iptype = PROD_CAPPI;
	      pProdHeader_a->pcf.psi.cappi.icappi_hgt = 0;
	      pProdHeader_a->pcf.psi.cappi.icappi_flags = 0;
	      break;
	    }
	}
    }

  /* Check pixel dimensions */
  if (!pBHeader_a->lXSize)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Width in pixels missing.\n" );
      exit( EXIT_FAILURE );
    }
  if (!pBHeader_a->lYSize)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Height in pixels missing.\n" );
      exit( EXIT_FAILURE );
    }
  if ((pBHeader_a->iXSize>MAX_PRODUCT_SIZE) ||
      (pBHeader_a->iYSize>MAX_PRODUCT_SIZE) )
    {
      sprintf( sLog_c, "Pixel dimensions (%dx%d) are to large for IRIS.\n",  
	       pBHeader_a->iXSize, pBHeader_a->iYSize );
      Lprintf( &pCConfig_a->Logging, sLog_c );
      exit( EXIT_FAILURE );
    }
  if ((pBHeader_a->iXSize<MIN_PRODUCT_SIZE) ||
      (pBHeader_a->iYSize<MIN_PRODUCT_SIZE) )
    {
      sprintf( sLog_c, "Pixel dimensions (%dx%d) are to small for IRIS.\n",  
	       pBHeader_a->iXSize, pBHeader_a->iYSize );
      Lprintf( &pCConfig_a->Logging, sLog_c );
      exit( EXIT_FAILURE );
    }
  pProdHeader_a->pcf.ixsize = pBHeader_a->iXSize;
  pProdHeader_a->pcf.iysize = pBHeader_a->iYSize;
  pProdHeader_a->pcf.izsize = 1;

  pProdHeader_a->pcf.ixrad = 500*pBHeader_a->iXSize;
  pProdHeader_a->pcf.iyrad = 500*pBHeader_a->iYSize;

  /* Determine location */
  {
    MESSAGE istatus;
    struct mpj_projection8 Projection;
    UINT4 iMask;
    FLT8  fYPix;
    
    memset( &Projection, 0, sizeof(Projection) );
    memset( &Region,     0, sizeof(Region) );
    if (!pBHeader_a->lProjectionType)
      {
	Lprintf( &CConfig_g.Logging,  "Error: Projection type missing.\n" );
	exit( EXIT_FAILURE );
      }
    switch( pBHeader_a->iProjectionType)
      {
      case BUFR_PROJ_AZIMUTHAL:
	Projection.iprojection_type = MAPROJ_AZIMUTHAL;
	break;
      case BUFR_PROJ_MERCATOR:
	Projection.iprojection_type = MAPROJ_MERCATOR;
	break;
      case BUFR_PROJ_POLSTEREO:
	Projection.iprojection_type = MAPROJ_POLSTEREO;
	break;
      case BUFR_PROJ_LAMBERTS:
	Projection.iprojection_type = MAPROJ_LAMBERT;
	break;
      case BUFR_PROJ_GNOMONIC:
	Projection.iprojection_type = MAPROJ_GNOMONIC;
	break;
      default:
	sprintf( sLog_c, "ERROR: Projection type (%d) not supported.\n", 
		 pBHeader_a->iProjectionType );
	Lprintf( &pCConfig_a->Logging, sLog_c );
	exit( EXIT_FAILURE );
      }

    if (pCConfig_a->lPinFromCorners)
      {
	/* Compute projection region from the 4 corners */
	if (!pBHeader_a->lCornerCount)
	  {
	    Lprintf( &CConfig_g.Logging,  "ERROR: No corners specified in file.\n" );
	    exit( EXIT_FAILURE );
	  }
	if (pBHeader_a->iCornerCount<3)
	  {
	    Lprintf( &CConfig_g.Logging,  "ERROR: Not enough corners specified in file.\n" );
	    exit( EXIT_FAILURE );
	  }
	if (!pBHeader_a->lProjectionRef)
	  {
	    Lprintf( &CConfig_g.Logging,  "ERROR: Projection reference point missing.\n" );
	    exit( EXIT_FAILURE );
	  }
	Projection.Ref      = pBHeader_a->ProjectionRef;
	Projection.fStdPar1 = pBHeader_a->fStdPar1;
	Projection.fStdPar2 = pBHeader_a->fStdPar2;
	Projection.fEarthRadius = pBHeader_a->fEarthRadiusE;

	/* Fill in flattening only if both radii are there.*/
	if ((pBHeader_a->fEarthRadiusE>1000.0)&&(pBHeader_a->fEarthRadiusP>1000.0))
	  {
	    FLT8 fFlat = 1.0 - (pBHeader_a->fEarthRadiusP/pBHeader_a->fEarthRadiusE);
	    if (fFlat>0.0) Projection.fFlattening = 1.0/fFlat;
	  }
	
	Region.Loc0 = pBHeader_a->Corners[3];
	Region.Loc2 = pBHeader_a->Corners[1];
	Region.fxpix = (FLT8)pProdHeader_a->pcf.ixsize;
	fYPix = (FLT8)pProdHeader_a->pcf.iysize;
	
	iMask = MPJ_LOC0 | MPJ_LOC2;
	istatus = mpj_pin_region( &Projection, &Region, iMask );
	if (istatus!=SS_NORMAL) { sig_signal( istatus ); exit(EXIT_FAILURE); }
      }
    else
      {
	/* Compute projection information from center and scale */
	if (!pBHeader_a->lXScale)
	  {
	    Lprintf( &CConfig_g.Logging,  "ERROR: Horizontal scale missing.\n" );
	    exit( EXIT_FAILURE );
	  }
	if (!pBHeader_a->lYScale)
	  {
	    Lprintf( &CConfig_g.Logging,  "ERROR: Vertical scale missing.\n" );
	    exit( EXIT_FAILURE );
	  }
	/* If no center, then use the reference location */
	if (pBHeader_a->lCenter)
	  {
	    Region.Loc1  = pBHeader_a->Center;
	  }
	else
	  {
	    if (pBHeader_a->lProjectionRef)
	      {
		Region.Loc1  = pBHeader_a->ProjectionRef;
	      }
	    else
	      {
		if (pSite)
		  {
		    Region.Loc1 = pSite->Center;
		  }
		else
		  {
		    Lprintf( &CConfig_g.Logging,  "ERROR: Center and reference locations missing.\n" );
		    exit( EXIT_FAILURE );
		  }
	      }
	  }

	/* If no reference, then use the center location instead */
	if (pBHeader_a->lProjectionRef)
	  Projection.Ref = pBHeader_a->ProjectionRef;
	else
	  Projection.Ref = Region.Loc1;

	Region.fxpix = (FLT8)pProdHeader_a->pcf.ixsize;
	fYPix        = (FLT8)pProdHeader_a->pcf.iysize;
	Region.fde   = (pBHeader_a->fXScale * Region.fxpix)/2000.0;
	Region.fdn   = (pBHeader_a->fYScale * fYPix)/2000.0;
	
	iMask = MPJ_LOC1 | MPJ_FDE | MPJ_FDN;
	istatus = mpj_pin_region( &Projection, &Region, iMask );
	if (istatus!=SS_NORMAL) { sig_signal( istatus ); exit(EXIT_FAILURE); }
      }

    /* Now rescale to product the correct Y-size.  It may be correct already for
     * some AED projections.
     */
    if ( (fabs( Region.fypix - fYPix )) > 0.5 )
      {
	FLT8 fRatio;
	iMask = MPJ_LOC1 | MPJ_FDE;
	fRatio = fYPix/Region.fxpix;
	istatus = mpj_rescale_region( &Projection, &Region, iMask, fRatio );
	if (istatus != SS_NORMAL) { sig_signal(istatus); exit(EXIT_FAILURE); }
      }
    
    /* Fill in header */
    pProdHeader_a->end.ilat_center   = iBin4FromRadian( Region.Loc1.fLat );
    pProdHeader_a->end.ilon_center   = iBin4FromRadian( Region.Loc1.fLon );
    pProdHeader_a->end.ilat_ref      = iBin4FromRadian( Projection.Ref.fLat );
    pProdHeader_a->end.ilon_ref      = iBin4FromRadian( Projection.Ref.fLon );
    pProdHeader_a->end.iStdParallel1 = iBin4FromRadian( Projection.fStdPar1 );
    pProdHeader_a->end.iStdParallel2 = iBin4FromRadian( Projection.fStdPar2 );
    pProdHeader_a->end.iEarthRadius  = NINT(100000.0* Projection.fEarthRadius );
    pProdHeader_a->end.iFlattening   = NINT(1000000.0* Projection.fFlattening );

    pProdHeader_a->pcf.iprojection_type = Projection.iprojection_type;
    pProdHeader_a->pcf.ixscale = NINT(200000.0*Region.fde/Region.fxpix);
    pProdHeader_a->pcf.iyscale = NINT(200000.0*Region.fdn/Region.fypix);
  }

  /* Determine sitename from the center.  Must be done after the center is determined. */
  if (!pCConfig_a->lSitesFromStation)
    {
      /* Pull sitename from the nearest entry in the site table. */
      pSite = FindSiteByCenter( &Region.Loc1, pCConfig_a );
      if (!pSite)
	{
	  char sBuffer[20];
	  strcpy( sLog_c,  "Center " );
	  strcat( sLog_c, sLatFromRadian( Region.Loc1.fLat, sBuffer, 20 ) );
	  strcat( sLog_c, sLonFromRadian( Region.Loc1.fLon, sBuffer, 20 ) );
	  strcat( sLog_c, " not in table\n" );
	  Lprintf( &pCConfig_a->Logging,  sLog_c );
	  exit( EXIT_FAILURE );
	}
    }
  spacify_ncpy( pProdHeader_a->end.sing_sitename, pSite->sSitename16, 16 );
  memcpy( pProdHeader_a->end.sIngHardwareName, pProdHeader_a->end.sing_sitename, 16 );


  /* Process data time, must be done after site to get time offset */
  if (!pBHeader_a->lTimeYear)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Year missing.\n" );
      exit( EXIT_FAILURE );
    }
  if (!pBHeader_a->lTimeMonth)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Month missing.\n" );
      exit( EXIT_FAILURE );
    }
  if (!pBHeader_a->lTimeDay)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Day missing.\n" );
      exit( EXIT_FAILURE );
    }
  if (!pBHeader_a->lTimeHour)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Hour missing.\n" );
      exit( EXIT_FAILURE );
    }
  if (!pBHeader_a->lTimeMinute)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Minute missing.\n" );
      exit( EXIT_FAILURE );
    }
  pProdHeader_a->end.iMinutesWest = pSite->iMinutesWest;
  pProdHeader_a->pcf.ing_time.iyear = pBHeader_a->iTimeYear;
  pProdHeader_a->pcf.ing_time.imon  = pBHeader_a->iTimeMonth;
  pProdHeader_a->pcf.ing_time.iday  = pBHeader_a->iTimeDay;
  pProdHeader_a->pcf.ing_time.isec  = 
    iPackTime( pBHeader_a->iTimeHour, pBHeader_a->iTimeMinute, 0 );
  ymds_add_seconds( -60*pProdHeader_a->end.iMinutesWest, &pProdHeader_a->pcf.ing_time );
  pProdHeader_a->pcf.epoch = pProdHeader_a->pcf.ing_time;

  /* Fill in Z/R numbers */
  if (pBHeader_a->lZRConst)
    pProdHeader_a->pcf.izr_const = NINT(1000.0*pBHeader_a->fZRConst);
  if (pBHeader_a->lZRExp)
    pProdHeader_a->pcf.izr_exp   = NINT(1000.0*pBHeader_a->fZRExp);

  /* RF Frequency */
  if (pBHeader_a->lFrequency)
    {
      FLT8 fWavelength;
      fWavelength = 100000.0*FSPEED_OF_LIGHT/pBHeader_a->fFrequency;
      pProdHeader_a->end.ilambda = NINT(100.0*fWavelength);
    }

  /* PRF */
  if (pBHeader_a->lPrf)
    pProdHeader_a->end.iprf = NINT(pBHeader_a->fPrf);

  /* Sample size */
  if (pBHeader_a->lSampleSize)
    pProdHeader_a->end.isamp = pBHeader_a->iSampleSize;

  /* Height of radome */
  if (pBHeader_a->lHeightStation)
    {
      SINT2 iHeight = NINT(pBHeader_a->fHeightStation);
      pProdHeader_a->end.irad_hgt = 10;
      pProdHeader_a->end.ignd_hgt = iHeight-10;
    }

  /* If composited, then convert the composite mask and count */
  
  if (pBHeader_a->lSiteCount)
    {
      SINT4 iSiteIndex, iFullCount;

      pProdHeader_a->pcf.iflags |= PF_COMPOSITED;

      /* Site count is index, not incremented, but it may have overflown */
      iFullCount = pBHeader_a->iSiteCount+1;
      if (iFullCount>MAX_SITES) iFullCount=MAX_SITES;

      /* Count the sites which can be converted.  Errors are logged, but do
       * not stop the conversion. */
      for (iSiteIndex=0; iSiteIndex<iFullCount; iSiteIndex++)
	{
	  const struct bufr_site *pBufrSite;
	  pBufrSite = &(pBHeader_a->Sites[iSiteIndex]);
	  if (!pBufrSite->lDataPresent) continue;

	  pSite = FindSiteByWmo( pBufrSite->iBlockNumber, pBufrSite->iStationNumber, pCConfig_a );
	  if (pSite)
	    {
	      pProdHeader_a->end.iSiteMask |= (1<<pSite->iSiteNumber);
	    }
	  else
	    {
	      sprintf( sLog_c,  "Composite input Block:%d and Station:%d not in table.\n", 
		       pBufrSite->iBlockNumber, pBufrSite->iStationNumber );
	      Lprintf( &pCConfig_a->Logging,  sLog_c );
	    }
	}
    }

  /* Print warning if level count exceeds data resolution */
  /* ImageFound implies data res known, while DataType (checked above) implies
   * level count known.
   */
  if (pBHeader_a->lImageFound) 
    {
      if (pBHeader_a->iImageDepth == 4)
	{
	  if (pBHeader_a->iLevelCount>14)
	    {
	      sprintf( sLog_c,  "Warning: Level count of %d exceeds max in 4 bits.\n", 
		       pBHeader_a->iLevelCount );
	      Lprintf( &pCConfig_a->Logging,  sLog_c );
	    }
	}
    }

  if (pBHeader_a->iLevelCount < 2)
    {
      Lprintf( &CConfig_g.Logging,  "Error: Level count must be at least 2\n" );
      exit(EXIT_FAILURE);
    }
  
  /* Fill in correct size */
  {
    SINT4 ires;
    ires = idata_resolution( pProdHeader_a->pcf.idata_out );
    pProdHeader_a->hdr.ibytes = 
      pProdHeader_a->pcf.ixsize * pProdHeader_a->pcf.iysize * (ires/8);
    pProdHeader_a->hdr.ibytes += PRODUCT_HDR_SIZE;
  }
  
  /* Fill in product type dependent info */

  switch( pProdHeader_a->pcf.iptype )
    {
    case PROD_CAPPI:
      /* Height of CAPPI */
      if ( pBHeader_a->lHeightData )
	pProdHeader_a->pcf.psi.cappi.icappi_hgt = NINT( 100.0 * pBHeader_a->fHeightData );
      break;
    case PROD_PPI:
      /* Elevation angle of PPI */
      if ( pBHeader_a->lElevationAngle )
	pProdHeader_a->pcf.psi.ppi.ippi_el = iBin2FromFDeg( pBHeader_a->fElevationAngle );
      break;
    case PROD_TOPS:
      if ( pBHeader_a->bReflectivityThreshold )
	pProdHeader_a->pcf.psi.top.iZThreshold = 
	  NINT(16.0*pBHeader_a->fReflectivityThreshold);
    }

  return;

}

/* ==================================================
 * Routine to convert the cartesian data array field of a product to 
 * IRIS format and write it out.  Should exit on error.
 */
static void ConvertData
  (const struct product_hdr *pIrisHead_a,
   const struct bufr_header8 *pBHeader_a,
   FILE *pIrisStream_a )
{
  SINT4 ires, iImageSize, iImageChannel, iExpectedSize;
  UINT1 *pImage;
  MESSAGE istatus;

  if (!pBHeader_a->lImageFound)
    {
      Lprintf( &CConfig_g.Logging,  "Error: No image found in file.\n" );
      exit( EXIT_FAILURE );
    }
  istatus = imapopen( pBHeader_a->sImagePath, FALSE, (void**)(void*)&pImage,
		      &iImageSize, &iImageChannel );
  if (istatus!=SS_NORMAL)
    {
      sig_signal( istatus );
      exit(EXIT_FAILURE);
    }
  iExpectedSize = pIrisHead_a->pcf.ixsize*pIrisHead_a->pcf.iysize;
  /* Check the size of the image file */
  if (iImageSize != iExpectedSize )
    {
      sprintf( sLog_c,  "Error: Image size %d does not match expected size %d.\n",
	       iImageSize, iExpectedSize );
      Lprintf( &CConfig_g.Logging, sLog_c );
      exit( EXIT_FAILURE );
    }

  ires = idata_resolution( pIrisHead_a->pcf.idata_out);
  switch( ires )
    {
    case 16:
      Convert2ByteData( pIrisHead_a, pBHeader_a, pImage, pIrisStream_a );
      break;

    default:
      Convert1ByteData( pIrisHead_a, pBHeader_a, pImage, pIrisStream_a );
      break;
    }

  istatus = imapclose( pImage, iImageSize, iImageChannel );

  return;
}
/* ==================================================
 * Routine to convert the data field of a product to IRIS format
 */
static void Convert1ByteData
(const struct product_hdr *pIrisHead_a, /* Iris product header */
 const struct bufr_header8 *pBHeader_a,
 const UINT1 *pBufrImage_a,             /* Image file data */
 FILE *pIrisStream_a )                  /* Write iris data here */
{
  SINT4 iIndex, iValue4, iLine, iColumn;
  UINT1 iTable[256];
  UINT1 iValue1;
  FLT4  fScale = 1.0, fValue = 0.0, fStep;
  struct data_convert Convert;
  const UINT1 *pBufrValue;

  PendFillDataConvert( &Convert, &pIrisHead_a->end, CONVERT_FLAGS_DEFAULT );

  /* Rescale some data types */
  switch( pIrisHead_a->pcf.idata_out )
    {
    case DB_HEIGHT:
      fScale = 0.001;
      break;
    default:
      fScale = 1.0;
      break;
    }

  /* Build a conversion table mapping bytes to bytes.
   */
  memset( iTable, DV_NOSCAN1, 256 );
  iTable[0] = DV_THRESH1;
  for (iIndex=1; iIndex<pBHeader_a->iLevelCount; iIndex++)
    {
      /* Average the 2 seams to get a center value */
      fValue  = pBHeader_a->fLevelSeams[iIndex-1];
      fValue += pBHeader_a->fLevelSeams[iIndex];
      fValue *= fScale/2.0;
      iValue4 = iuser_to_data( fValue, pIrisHead_a->pcf.idata_out, &Convert );
      iTable[iIndex]=(UINT1)(UINT4)iValue4;
    }
  /* Special code for the last value since there is no upper value:
   * Take the difference between the last 2 seams, and add it to the 
   * last value.  This is possible because we require a minimum of 2
   * seams.
   */
  iIndex=pBHeader_a->iLevelCount;
  fStep = pBHeader_a->fLevelSeams[iIndex-1] - pBHeader_a->fLevelSeams[iIndex-2];
  iValue4 = iuser_to_data( fValue+fStep, pIrisHead_a->pcf.idata_out, &Convert );
  iTable[iIndex]=(UINT1)(UINT4)iValue4;
  
  /* BUFR spec requires all ones to be NOSCAN */
  if (pBHeader_a->iImageDepth == 8)
    iTable[255] = DV_NOSCAN1;
  else
    iTable[15]  = DV_NOSCAN1;

  /* Now perform the mapping. 
   */
  for (iLine=0; iLine<pIrisHead_a->pcf.iysize; iLine++ )
    {
      SINT4 iBufrLine = (pIrisHead_a->pcf.iysize-1)-iLine;
      for (iColumn=0; iColumn<pIrisHead_a->pcf.ixsize; iColumn++ )
	{
	  pBufrValue = pBufrImage_a + (iBufrLine*pIrisHead_a->pcf.ixsize) + iColumn;
	  iValue1 =iTable[*pBufrValue];
	  fwrite( &iValue1, 1, 1, pIrisStream_a );
	}
    }
  return;
}
/* ==================================================
 * Routine to convert the data field of a product to IRIS format
 */
static void Convert2ByteData
(const struct product_hdr *pIrisHead_a, /* Iris product header */
 const struct bufr_header8 *pBHeader_a,
 const UINT1 *pBufrImage_a,             /* Image file data */
 FILE *pIrisStream_a )                  /* Write iris data here */
{
  SINT4 iIndex, iValue4, iLine, iColumn;
  UINT2 iTable[256];
  UINT2 iValue2;
  FLT4  fValue = 0.0,fStep;
  struct data_convert Convert;
  const UINT1 *pBufrValue;

  PendFillDataConvert( &Convert, &pIrisHead_a->end, CONVERT_FLAGS_DEFAULT );

  /* Build a conversion table mapping bytes to words.
   */
  iTable[0] = DV_THRESH2;
  for (iIndex=1; iIndex<256; iIndex++)
    {
      iTable[iIndex] = DV_NOSCAN2;
    }
  for (iIndex=1; iIndex<pBHeader_a->iLevelCount; iIndex++)
    {
      /* Average the 2 seams to get a center value */
      fValue  = pBHeader_a->fLevelSeams[iIndex-1];
      fValue += pBHeader_a->fLevelSeams[iIndex];
      fValue /= 2.0;
      iValue4 = iuser_to_data( fValue, pIrisHead_a->pcf.idata_out, &Convert );
      iTable[iIndex]=(UINT2)(UINT4)iValue4;
    }
  /* Special code for the last value since there is no upper value:
   * Take the difference between the last 2 seams, and add it to the 
   * last value.  This is possible because we require a minimum of 2
   * seams.
   */
  iIndex=pBHeader_a->iLevelCount;
  fStep = pBHeader_a->fLevelSeams[iIndex-1] - pBHeader_a->fLevelSeams[iIndex-2];
  iValue4 = iuser_to_data( fValue+fStep, pIrisHead_a->pcf.idata_out, &Convert );
  iTable[iIndex]=(UINT2)(UINT4)iValue4;
  
  /* BUFR spec requires all ones to be NOSCAN */
  if (pBHeader_a->iImageDepth == 8)
    iTable[255] = DV_NOSCAN2;
  else
    iTable[15]  = DV_NOSCAN2;
    
  /* Now perform the mapping. 
   */
  for (iLine=0; iLine<pIrisHead_a->pcf.iysize; iLine++ )
    {
      SINT4 iBufrLine = (pIrisHead_a->pcf.iysize-1)-iLine;
      for (iColumn=0; iColumn<pIrisHead_a->pcf.ixsize; iColumn++ )
	{
	  pBufrValue = pBufrImage_a + (iBufrLine*pIrisHead_a->pcf.ixsize) + iColumn;
	  iValue2 =iTable[*pBufrValue];
	  fwrite( &iValue2, 2, 1, pIrisStream_a );
	}
    }
  return;
}

/*===========================================================================*/
static int get_lens
(const char *buf,
 long len,
 size_t secl[6])
{
  /* This function reads from a bufr-message the length of data- and
     data-descriptor-section. Therefore the buffer is opened as a bitstream
     and data is read.
     
     parameters:
     BUF:   Memory-area containing the BUFR-message.
     LEN:   Number of bytes of the complete BUFR-message determined from
     the length ob the input-file.
     SECL:  Array containing the lengths of the BUFR-sections.
     
     The return-value is 1 on success, 0 on a fault.
  */
  
  int h, co;
  unsigned long l;
  long sum;

/******* The length of section 0 is constant */

  secl[0] = 8;
  co = 8;
  sum = 8;

  secl[1] = 0,  secl[2] = 0, secl[3] = 0, secl[4] = 0, secl[5] = 0;
/******* length of section 1 */

  h = bitio_i_open ((void*)(buf + co), 20);
  if (h == -1) return 0;
  bitio_i_input (h, &l, 24);
  secl[1] = (size_t) l;
  co += secl[1];
  bitio_i_close (h);
  sum += l;
  if (sum > len) goto err;

/******* there is no section 2 */

  secl[2] = 0;

/******* length of section 3 */

  h = bitio_i_open ((void*)(buf + co), 20);
  if (h == -1) return 0;
  bitio_i_input (h, &l, 24);
  secl[3] = (size_t) l;
  co += secl[3];
  bitio_i_close (h);
  sum += l;
  if (sum > len) goto err;

/******* length of section 4 */

  h = bitio_i_open ((void*)(buf + co), 20);
  if (h == -1) return 0;
  bitio_i_input (h, &l, 24);
  secl[4] = (size_t) l;
  co += secl[4];
  bitio_i_close (h);
  sum += l;
  if (sum > len) goto err;

/******* length of section 5 is constant */

  secl[5] = 4;
  sum += 4;
  if (sum > len) goto err;

  return 1;

/******* Lengths of BUFR-sections nor correct */

err:
  fprintf (stderr, "Lengths of BUFR-sections "
	   "(%u + %u + %u + %u + %u + %u) > size of input-file (%ld)!\n",
	   secl[0], secl[1], secl[2], secl[3], secl[4], secl[5], len);
  return 0;
}
/* =====================================================
 * Search to find the sitename from the WMO block and station numbers
 */
static const struct convert_site_table8 * FindSiteByWmo
( int iBlockNumber_a,             /* WMO block number */
  int iStationNumber_a,             /* WMO station number */
  const struct convert_config8 *pCConfig_a ) /* Configuration */
{
  SINT4 iSite;
  for (iSite=0; iSite<pCConfig_a->iSiteCount; iSite++)
    {
      const struct convert_site_table8 *pSite;
      pSite = &(pCConfig_a->Sites[iSite]);
      if ( pSite->iBlockNumber == iBlockNumber_a )
	{
	  if ( pSite->iStationNumber == iStationNumber_a )
	    {
	      return pSite;
	    }
	}
    }
  return NULL;
}

/* =====================================================
 * Search to find the sitename from the nearest center.  It must be
 * less than 1000 km.
 */
static const struct convert_site_table8 * FindSiteByCenter
( const struct latlon8 *pCenter_a,           /* Center location */
  const struct convert_config8 *pCConfig_a ) /* Configuration */
{
  SINT4 iSite;
  const struct convert_site_table8 *pNearestSite=NULL;
  FLT8 fNearestRange = 1000.0;
  for (iSite=0; iSite<pCConfig_a->iSiteCount; iSite++)
    {
      const struct convert_site_table8 *pSite;
      FLT8 fRange, fAz1, fAz2;
      pSite = &(pCConfig_a->Sites[iSite]);
      earth_to_polar( pCenter_a, &pSite->Center, &fRange, &fAz1, &fAz2 );
      if ( fRange < fNearestRange )
	{
	  fNearestRange = fRange;
	  pNearestSite  = pSite;
	}
    }
  return pNearestSite;
}
/* ============================================================
 * Function to load the convert setup from a configuration file.
 */
static void LoadSetup
( struct convert_config8 *pCConfig_a,
  const char* sFile_a,                  /* Filename of config file */
  int iDevice_a )                       /* Device number */
{
  char sPath[PATHNAME_SIZE];
  char sline[81], *sptr;
  FILE* stream;
  SINT4 iline;
  UINT2 lerror;

  /* Initialize the data to the default values */
  memset( pCConfig_a, 0, sizeof(*pCConfig_a) );

  /* Default logging to file */
  pCConfig_a->Logging.lLogToFile=TRUE;

  /* Generate the file name and open the file */
  sptr = getenv( "IRIS_CONFIG" );
  if (NULL == sptr) 
    {
      sig_signal_va( USER_NO_ENV, 1, "IRIS_CONFIG" );
      exit(EXIT_FAILURE);
    }

  strncpy( sPath, sptr, DIRECTORY_SIZE-1 );
  sPath[DIRECTORY_SIZE-1] = 0;

  strncpy( pCConfig_a->sBufrTablDir, sPath, DIRECTORY_SIZE - 1 );
  strcat( pCConfig_a->sBufrTablDir, "bufr" );

  strcat( sPath, sFile_a );
  stream = fopen( sPath, "r" );

  /* Don't worry about a missing config file */
  if ( NULL == stream ) return;

  /* now loop through the file reading and parsing lines */
  iline = 1;
  sptr = fgets( sline, 81, stream );
  while (NULL != sptr)
    {
      lerror = ProcessConfigLine( sptr, pCConfig_a );
      if (lerror) fprintf( stderr, "Syntax error on .conf file line %d\n", iline );
      iline +=1;
      sptr = fgets( sline, 81, stream );
    }

  fclose( stream );

  return;
}

/* ===============================================================
 * Process one line from the configuration file.  Returns nonzero if there
 * is a syntax error.
 *
 * The config can contain the following commands.  All blank lines and
 * lines beginning with "#" are ignored.  Commands can be in any order.
 * Commands take effect in the order received.  For example, syntax errors
 * before the LOG command will not be logged.
 *
 * LOG              <FILE TERM BOTH NONE>
 * MIN_REFLECTIVITY #
 * PROJECT_NAME     <name>
 * VERBOSE
 */
static UINT2 ProcessConfigLine
(char *sline_a,
 struct convert_config8 *pConfig_a )
{
  char *stokens[9], *sptr;
  UINT4 icount, ioffset;
  
  ioffset = 0;
  icount  = 0;
  /* Process up to 9 tokens, stop when the line is exhausted. */
  while (icount<9) 
    {
      stokens[icount] = sigtok( sline_a, &ioffset );
      if (NULL == stokens[icount])
	break;
      else
	icount++;
    }
  
  /* Blank lines are allowed and skipped */
  if (icount == 0) return FALSE;
  
  /* Skip commented lines */
  if (stokens[0][0] == '#') return FALSE;
  
  if ( 0 == strcmp( "APPEND_TO_LOG", stokens[0] ) )
    {
      pConfig_a->Logging.lAppendToLog=TRUE;
      return FALSE;
    }

  /* Select how we get BUFR location information */
  if ( 0 == strcmp ( "BUFR_TAB_PATH", stokens[0] ) )
      strncpy( pConfig_a->sBufrTablDir, stokens[1], PATHNAME_SIZE - 1 );

  /* Select how we get BUFR location information */
  if ( 0 == strcmp( "BUFR_LOCATION", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      if ( 0 == strcmp( "CORNERS", stokens[1] ) )
	{
	  pConfig_a->lPinFromCorners = TRUE;
	  return FALSE;
	}
      if ( 0 == strcmp( "CENTER", stokens[1] ) )
	{
	  pConfig_a->lPinFromCorners = FALSE;
	  return FALSE;
	}
      return TRUE;
    }

  /* See if the line contains the LOG file command */
  if ( 0 == strcmp( "LOG", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      
      if      ( 0 == strcmp( "BOTH", stokens[1] ) ) 
	{pConfig_a->Logging.lLogToFile=TRUE;  pConfig_a->Logging.lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "FILE", stokens[1] ) ) 
	{pConfig_a->Logging.lLogToFile=TRUE;  pConfig_a->Logging.lLogToTerm=FALSE;}
      else if ( 0 == strcmp( "TERM", stokens[1] ) )
	{pConfig_a->Logging.lLogToFile=FALSE; pConfig_a->Logging.lLogToTerm=TRUE;}
      else if ( 0 == strcmp( "NONE", stokens[1] ) )
	{pConfig_a->Logging.lLogToFile=FALSE; pConfig_a->Logging.lLogToTerm=FALSE;}
      else
	return TRUE;
      return FALSE;
    }

  if ( 0 == strcmp( "VERBOSE", stokens[0] ) )
    {
      pConfig_a->Logging.lVerbose = TRUE;
      return FALSE;
    }

  /* Read an entry in the site table.
   * Syntax is: SITE <WMO block> <WMO Station> <IRIS Sitename> <IRIS Sitenumber> <minutes west> <lat> <lon> */
  if ( 0 == strcmp( "SITE", stokens[0] ) )
    {
      long iValue;
      struct convert_site_table8 *pOneSite;
      if ( icount < 8 ) return TRUE;
      if (pConfig_a->iSiteCount >= MAX_CONVERT_SITES) return TRUE;

      pOneSite = &(pConfig_a->Sites[pConfig_a->iSiteCount]);
      
      /* Read Block Number */
      iValue = strtol( stokens[1], &sptr, 10 );
      if (sptr == stokens[1]) return TRUE;
      /* Block number is Unsigned 7-bit value */
      if (iValue < 0 ) return TRUE;
      if (iValue > 127 ) return TRUE;
      pOneSite->iBlockNumber = iValue;
      
      /* Read Station Number */
      iValue = strtol( stokens[2], &sptr, 10 );
      if (sptr == stokens[2]) return TRUE;
      /* Block number is Unsigned 10-bit value */
      if (iValue < 0 ) return TRUE;
      if (iValue > 1023 ) return TRUE;
      pOneSite->iStationNumber = iValue;
      
      /* Read Site Name */
      strncpy( pOneSite->sSitename16, stokens[3], 16 );
      pOneSite->sSitename16[16] = 0;
            
      /* Read Site Number */
      pOneSite->iSiteNumber = strtol( stokens[4], &sptr, 10 );
      if (sptr == stokens[4]) return TRUE;
      /* Block number is Unsigned 10-bit value */
      if (pOneSite->iSiteNumber <  0 ) return TRUE;
      if (pOneSite->iSiteNumber > 30 ) return TRUE;

      /* Read Time Zone */
      pOneSite->iMinutesWest = strtol( stokens[5], &sptr, 10 );
      if (sptr == stokens[5]) return TRUE;
      /* Allow 16 hours either way */
      if (pOneSite->iMinutesWest < -16*60 ) return TRUE;
      if (pOneSite->iMinutesWest >  16*60 ) return TRUE;

      /* Read Latitude */
      pOneSite->Center.fLat = (FPI/180.0)*strtod( stokens[6], &sptr );
      if (sptr == stokens[6]) return TRUE;
      /* Allow +/- PI/2 */
      if (pOneSite->Center.fLat < -FPI/2.00 ) return TRUE;
      if (pOneSite->Center.fLat >  FPI/2.00 ) return TRUE;

      /* Read Longitude */
      pOneSite->Center.fLon = (FPI/180.0)*strtod( stokens[7], &sptr );
      if (sptr == stokens[7]) return TRUE;
      /* Allow +/- PI */
      if (pOneSite->Center.fLon < -FPI ) return TRUE;
      if (pOneSite->Center.fLon >  FPI ) return TRUE;

      pConfig_a->iSiteCount++;
      return FALSE;
    }

  /* Select site source */
  if ( 0 == strcmp( "SITE_SOURCE", stokens[0] ) )
    {
      if ( icount < 2 ) return TRUE;
      if ( 0 == strcmp( "STATION", stokens[1] ) )
	{
	  pConfig_a->lSitesFromStation = TRUE;
	  return FALSE;
	}
      if ( 0 == strcmp( "CENTER", stokens[1] ) )
	{
	  pConfig_a->lSitesFromStation = FALSE;
	  return FALSE;
	}
      return TRUE;
    }
  
  /* Unrecognized lines will generate a syntax error */
  return TRUE;
}

/* ======================================================================
 * 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( &CConfig_g.Logging, sMessage );
  return(SS_NORMAL);
}

/* ==============================
 * Exit handler to remove the temporary file, if any.
 */
static void ExitHandler(void)
{
  if (BHeader_c.sImagePath[0]) unlink( BHeader_c.sImagePath );

}

