/* **********************************
 * *                                *  
 * *    DSP Info Driver Library     *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dsp_info.c
 *     
 * COPYRIGHT (c) 1992, 1997, 1998, 1999, 2000, 2001, 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. 
 */     
#define DSP_INFO_DRIVER 1

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "sigtypes.h"
#include "dsp.h"
#include "rvp8.h"
#include "user_lib.h"
#include "dsp_lib.h"

#include "dsp_def.h"

/* ======================================================================
 * Return (-1,0,+1) according to whether the version of code implied
 * by a GPARM structure is (<,=,>) a given level.
 */
SINT4 dsp_cmp_version
(const struct gparm *gparm_a,
 SINT4 iMajor_a,
 SINT4 iMinor_a )
{
  SINT4 iMajor = DSP_VERSION( gparm_a ) ;

  if     ( iMajor > iMajor_a ) return(  1 ) ;
  else if( iMajor < iMajor_a ) return( -1 ) ;
  else {
    SINT4 iMinor = DSP_MINOREV( gparm_a ) ;

    if     ( iMinor > iMinor_a ) return(  1 ) ;
    else if( iMinor < iMinor_a ) return( -1 ) ;
  }
  return( 0 ) ;
}

/* ======================================================================
 * This function returns a mask including bits set for all the data
 * types possible with the given major mode and polarization.
 * History:
 */
void DspDataMaskFromMode
( Dsp* dsp_a,
  struct dsp_data_mask *pMask_a,
  UINT1 iMajorMode_a,
  UINT1 iPolarization_a )
{
  memset( pMask_a, 0, sizeof(*pMask_a) );

  DspMaskSet( pMask_a, DB_DBT);
  DspMaskSet( pMask_a, DB_DBT2);
  DspMaskSet( pMask_a, DB_DBZ);
  DspMaskSet( pMask_a, DB_DBZ2);
  DspMaskSet( pMask_a, DB_VEL);
  DspMaskSet( pMask_a, DB_VEL2);
  DspMaskSet( pMask_a, DB_WIDTH);
  DspMaskSet( pMask_a, DB_WIDTH2);
  DspMaskSet( pMask_a, DB_SQI);
  DspMaskSet( pMask_a, DB_SQI2);

  /* Special data types supported beginning with the RVP8
   */
  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    DspMaskSet( pMask_a, DB_FLAGS  ) ;
    DspMaskSet( pMask_a, DB_FLAGS2 ) ;
  }
  /* Polarization parameters are only available in PPP mode
   */
  if( iMajorMode_a == PMODE_PPP ) {
    /* The dual receiver has the most options...
     */
    if( dsp_a->iProcFlags & DSP_PFLG_DUAL_RCV ) {
      switch( iPolarization_a ) {
      case POL_HORIZ_FIX:
	DspMaskSet( pMask_a, DB_LDRH) ;
	DspMaskSet( pMask_a, DB_LDRH2) ;
	DspMaskSet( pMask_a, DB_RHOH) ;
	DspMaskSet( pMask_a, DB_RHOH2) ;
	DspMaskSet( pMask_a, DB_PHIH) ;
	DspMaskSet( pMask_a, DB_PHIH2) ;
	break;
	  
      case POL_VERT_FIX:
	DspMaskSet( pMask_a, DB_LDRV) ;
	DspMaskSet( pMask_a, DB_LDRV2) ;
	DspMaskSet( pMask_a, DB_RHOV) ;
	DspMaskSet( pMask_a, DB_RHOV2) ;
	DspMaskSet( pMask_a, DB_PHIV) ;
	DspMaskSet( pMask_a, DB_PHIV2) ;
	break;
	  
      case POL_ALTERNATING:
	DspMaskSet( pMask_a, DB_LDRH) ;
	DspMaskSet( pMask_a, DB_LDRH2) ;
	DspMaskSet( pMask_a, DB_RHOH) ;
	DspMaskSet( pMask_a, DB_RHOH2) ;
	DspMaskSet( pMask_a, DB_PHIH) ;
	DspMaskSet( pMask_a, DB_PHIH2) ;
	DspMaskSet( pMask_a, DB_LDRV) ;
	DspMaskSet( pMask_a, DB_LDRV2) ;
	DspMaskSet( pMask_a, DB_RHOV) ;
	DspMaskSet( pMask_a, DB_RHOV2) ;
	DspMaskSet( pMask_a, DB_PHIV) ;
	DspMaskSet( pMask_a, DB_PHIV2) ;
	/* Also include the simultaneous data which follows: */
      case POL_SIMULTANEOUS:
	DspMaskSet( pMask_a, DB_ZDR) ;
	DspMaskSet( pMask_a, DB_ZDR2) ;
	DspMaskSet( pMask_a, DB_KDP) ;
	DspMaskSet( pMask_a, DB_KDP2) ;
	DspMaskSet( pMask_a, DB_PHIDP) ;
	DspMaskSet( pMask_a, DB_PHIDP2) ;
	DspMaskSet( pMask_a, DB_RHOHV) ;
	DspMaskSet( pMask_a, DB_RHOHV2) ;
	break;
      }
    } else {
      /* For a single receiver radar, you only get interesting
       * polarization data when alternating.
       */
      switch( iPolarization_a ) {
      case POL_HORIZ_FIX:
      case POL_VERT_FIX:
      case POL_SIMULTANEOUS:
      default:
	break;
	  
      case POL_ALTERNATING:
	DspMaskSet( pMask_a, DB_LDRH) ;
	DspMaskSet( pMask_a, DB_LDRH2) ;
	DspMaskSet( pMask_a, DB_RHOH) ;
	DspMaskSet( pMask_a, DB_RHOH2) ;
	DspMaskSet( pMask_a, DB_PHIH) ;
	DspMaskSet( pMask_a, DB_PHIH2) ;
	DspMaskSet( pMask_a, DB_LDRV) ;
	DspMaskSet( pMask_a, DB_LDRV2) ;
	DspMaskSet( pMask_a, DB_RHOV) ;
	DspMaskSet( pMask_a, DB_RHOV2) ;
	DspMaskSet( pMask_a, DB_PHIV) ;
	DspMaskSet( pMask_a, DB_PHIV2) ;
	break;
      }
    }
  }
}

/* ======================================================================
 * This function returns a mask including bits set for all the data
 * types that the specified processor can produce.
 */
void dsp_data_set( Dsp* dsp_a, struct dsp_data_mask *pMask_a )
{
  *pMask_a = dsp_a->DataMask;
}

/* ======================================================================
 * Return a mask of window types that are supported for this DSP
 */
UINT4 idsp_windowTypeMask( Dsp* dsp_a )
{
  UINT4 iMask = BIT( WIN_RECT ) | BIT( WIN_HAMMING ) | BIT( WIN_BLACKMAN ) ;
  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    iMask |= BIT( WIN_BLKMANEX ) | BIT( WIN_VONHANN ) | BIT( WIN_ADAPTIVE ) ;
    break ;
  }
  return( iMask ) ;
}

/* ======================================================================
 * Return TRUE if the spectral output from this DSP represent the true
 * power levels of the original data, rather than hacked so that the
 * peak power is preserved regardless of window selection.
 */
UINT1 ldsp_trueSpectralLevels( Dsp* dsp_a )
{
  UINT1 lTrueLevels = FALSE ;
  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    lTrueLevels = TRUE ; break ;
  }
  return( lTrueLevels ) ;
}

/* ======================================================================
 * This function returns the resolution of the range mask (in Km) for
 * the signal processor type specified.  Note that this routine does
 * not do any I/O to the DSP, and as a result does not require that
 * dsp_open_for_io() has been run.
 */
FLT4  fdsp_mask_res
( Dsp* dsp_a,
  SINT4 iPwIndex_a )   /* The desired pulse width index */
{
  SINT4 iPwIndex = iPwIndex_a;
  if (iPwIndex<0) iPwIndex=0;
  else if (iPwIndex>3) iPwIndex=0;

  if( dsp_a->iMaskSpacing[iPwIndex] > 0 )
    return 1E-5 * (float)dsp_a->iMaskSpacing[iPwIndex];
  else
    return 1.0 ;
}

/* =================================================================
 * This function computes the noise power based on the results of a
 * GPARM command.
 */
FLT8  fdsp_noise_power
( Dsp* dsp_a,
  UINT1 lHorizontalPolarization_a,  /* Which noise level to return */
  const struct gparm *pGparm_a )
{
  return fdsptype_noise_power( dsp_a->itype, lHorizontalPolarization_a, pGparm_a );
}
/* =================================================================
 * This function computes the noise power based on the results of a
 * GPARM command.
 */
FLT8  fdsptype_noise_power
( UINT2 iDspType_a,
  UINT1 lHorizontalPolarization_a,  /* Which noise level to return */
  const struct gparm *pGparm_a )
{
  FLT8 fPower, fSlope;

  switch( iDspType_a )
    {
      /* The RVP6 noise level is expressed in 8-bit A/D units.
       */
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
    default:
      fPower = ((FLT8)pGparm_a->log_nse)/64.0;
      break;

      /* The RVP7 noise level is expressed in dBm.  The saturation
       * power level is a function of which type of RVP7/IFD is
       * attached to the system.
       */
    case DSP_TYPE_RVP7:
    case DSP_TYPE_RVP8:
      fSlope = ((FLT8)pGparm_a->iz_slope)/65536.0;
      fPower = frvp7_power_max( pGparm_a ) +
	(fSlope * (((FLT8)pGparm_a->log_nse)/4.0 - 3584.0));
      if (!lHorizontalPolarization_a)
	{
	  FLT4 fHVRatio;
	  fHVRatio = ((FLT8)pGparm_a->inse_hv_ratio)/100.0;
	  fPower -= fHVRatio;
	}
      break;

    }  

  return fPower;
}
/* =================================================================
 * This function computes the noise sample standard deviation based
 * on the results of the GPARM command.
 */
FLT4  fDspNoiseStd
( Dsp* dsp_a,
  UINT1 lHorizontalPolarization_a,  /* Which noise level to return */
  const struct gparm *pGparm_a )
{
  FLT4 fPower;

  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
    case DSP_TYPE_RVP7:
    default:
      fPower = 0.0;
      break;

      /* The RVP8 noise standard deviation in dB. 
       */
    case DSP_TYPE_RVP8:
      if (lHorizontalPolarization_a)
	{
	  /* H is signed number in the lower byte */
	  SINT2 iNoiseRms = pGparm_a->zlin_rms<<8;
	  fPower = ((FLT4)(iNoiseRms>>8))/10.0;
	}
      else
	{
	  /* V is signed number in the upper byte */
	  SINT2 iNoiseRms = pGparm_a->zlin_rms&0xff00;
	  fPower = ((FLT4)(iNoiseRms>>8))/10.0;
	}
      break;

    }  
  return fPower;
}

/* ======================================================================
 * Return the saturation A/D input power for an RVP7 or 8.  This can be
 * deduced from a 4-bit code in the GPARM structure.
 */
FLT8 frvp7_power_max( const struct gparm *pGparm_a )
{
  FLT8 fPower ;
  switch( (pGparm_a->intflt >> 8) & 0xF ) {
  default: fPower = 4.5 ; break ;
  case 1:  fPower = 6.0 ; break ;
  }
  return( fPower ) ;
}

/* ======================================================================
 * Return the measured DC offset in either the "I" or "Q" channel.
 * This routine deals with the two possible formats that might have
 * been used to encode the DC levels.  Return value is in the range
 * -1.0 to +1.0.
 */
FLT4 fGetNoiseLevelDC( const struct gparm *pGparm_a, UINT1 lGetQ_a )
{
  FLT4 fLevel ; UINT2 iCode = ( lGetQ_a ? pGparm_a->q_nse_ : pGparm_a->i_nse_ ) ;
  if( pGparm_a->istat_i4 & GI4S_NSEFMTPACK ) {
    fLevel = fFloatIQFromPackIQ_( iCode, PACKIQ_HIGHSNR ) ;
  } else {
    fLevel = ((FLT4)(SINT2)iCode) / 32768.0 ;
  }
  return( fLevel ) ;
}

/* ======================================================================
 * This function computes the appropriate range to take a noise sample
 * at, given the PRF it will be taken at.
 */
FLT4  fdsp_noise_range( Dsp* dsp_a, FLT4 fprf_a )
{
#define EXTRAKM_P (10.0)                   /* Spare distance for safety*/

  FLT4 frange;

  /* Compute max range, but ignore bad PRF's
   */
  if (fprf_a > 0.0)
    frange = (FSPEED_OF_LIGHT / 2.0) / fprf_a ;
  else
    frange = 0.0 ;
      
  switch( dsp_a->itype)
    {
      /* The RVP6 takes 84 km to process the noise sample, and it uses 32 km
       * worth of range from the range mask.  The RVP6-2 has not been measured
       * yet, it is set to the same as the RVP6
       */
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      frange = MIN( 1024.0-32.0, frange-84.0 );
      break;

      /* The RVP7 takes uses 32 km worth of range from the range mask. 
       * Only the first 5600 bins of the mask are available.
       */
    case DSP_TYPE_RVP7:
    case DSP_TYPE_RVP8:
    default:
      frange = MIN( 682.0-32.0, frange-32.0 );
      break;

    }  

  /* It cannot go below zero km
   */
  frange = MAX( 0.0, frange-EXTRAKM_P );

  return frange;
}

/* ======================================================================
 * This function returns the resolution of the STC waveform table for
 * the processor type specified.
 */
FLT4 fdsp_stc_res( Dsp* dsp_a )
{
  FLT4 flength = 1.0 ;

  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP6:
  case DSP_TYPE_RVP6_2:
    /* Deduce the spacing as 1/6 the clock period, converted to
     * length.
     */
    if( dsp_a->iaqclock )
      flength = FSPEED_OF_LIGHT / ( ((float)dsp_a->iaqclock) / 6.0 ) ;
    break;

  default:
    /* Meaningless, so just return a value that is consistent
     * with the mask resolution.
     */
    flength = 1E-5 * (float)dsp_a->iMaskSpacing[0];
    break;
  }
  return( flength ) ;
}

/* ======================================================================
 * This function returns the resolution of the trigger PRT setting
 * command in microseconds.  This resolution is also used by the
 * define minimum trigger period command.
 */
FLT4 fdsp_trig_in_res( Dsp *dsp_a )
{
  return( 1E6 / TREQSCL ) ;
} 

/* ======================================================================
 * This function returns the resolution (in microseconds) of the
 * trigger waveform table for the processor specified.
 */
FLT4 fdsp_trig_wave_res( Dsp* dsp_a )
{
  FLT4 fperiod = 1.0 ;

  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP6:
  case DSP_TYPE_RVP6_2:
    /* Produces a new trigger bit pattern every two clocks.
     */
    if( dsp_a->iaqclock )
      fperiod = 1E6 / ( ((float)dsp_a->iaqclock) / 2.0 ) ;
    break ;

  default:
    /* Adheres to old RVP6 standard spacing.
     */
    fperiod = 1.0 / (14.390 / 2.0) ;
    break ;
  }  
  return( fperiod ) ;
}

/* ======================================================================
 * This function returns the resolution of the measured trigger read from the
 * signal processor.  Note that this is different from the resolution specified
 * for input.  This value is in microseconds, so multiplying the DSP output by
 * this value and dividing by 10^6 gives a PRT in seconds, and inverting the
 * result gives PRF in Hertz.
 */
FLT4 fdsp_trig_out_res( Dsp* dsp_a )
{      
  return( 1E6 / TMESSCL ) ;
}

/* ======================================================================
 * This function returns the maximum number of bins that are allowed
 * for the signal processor type specified.
 */ 
SINT4 idsp_bin_max( Dsp* dsp_a )
{
  switch( dsp_a->itype )
    {
    case DSP_TYPE_RVP6:
      return 1024;
      break;
    case DSP_TYPE_RVP7:
    default:
      return 2048;
      break;
    case DSP_TYPE_RVP8:
      return RVP8MAXBINS ;
      break;
    }
}
  
/* ======================================================================
 * This function returns the number of trigger pulses that must be
 * transmitted by the signal processor before data will be valid.
 */
SINT4 idsp_fill_cnt
(Dsp* pdsp_a )
{
  /* This function needs to be moved */
  switch( pdsp_a->itype)
    {
    case DSP_TYPE_RVP8:
    case DSP_TYPE_RVP7:
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return 11;
      break;

    default:
      return 0;
      break;
    }  
}

/* ======================================================================
 * This function returns the maximum filter number that is allowed for
 * the signal processor specified.  Note that this routine does not do
 * any I/O to the DSP, and as a result does not require that
 * dsp_open_for_io() has been run.
 */
SINT4 idsp_filter_max( Dsp* dsp_a )
{
  return 7;
}

/* ======================================================================
 * This function returns the type of interface used to connect between
 * the DSP and computer.
 */ 
UINT4 idsp_interface( Dsp* dsp_a )
{
  return dsp_a->interface;
}
      
/* ======================================================================
 * This function returns the number of bits in the range mask for the
 * signal processor type specified.
 */
SINT4 idsp_mask_size( Dsp *dsp_a )
{      
  switch (dsp_a->itype)
    {
    default:
      return 8192;
      break;
    }  
}

/* ======================================================================
 * This function converts the PRF into the appropriate PRT.  Used
 * internally only!  The PRT units are those used by the signal
 * processor to set the PRF.  This routine can be used for converting
 * either way.
 */ 
UINT2 idsp_prt( Dsp *dsp_a, UINT2 iprf_a )
{
  FLT4 funits = fdsp_trig_in_res( dsp_a ) ; UINT2 iResult = 0xFFFF ;

  if( iprf_a > 0 ) {
    SINT4 iVal = NINT( 1E6 / (funits * (FLT4)iprf_a ) ) ;
    iResult = MAX( 1, MIN( 0xFFFF, iVal ) ) ;
  }
  return( iResult ) ;
}
/* ======================================================================
 * This function converts the PRF into the appropriate PRT.  Used
 * internally only!  The PRT units are those used by the signal
 * processor to set the PRF. 
 */ 
UINT2 iDspPrtInFromPrf( Dsp *dsp_a, FLT4 fPrf_a )
{
  FLT4 funits = fdsp_trig_in_res( dsp_a ) ;
  UINT2 iResult = 0xFFFF ;

  if( (funits*fPrf_a) > 0 )
    {
      SINT4 iVal = NINT( 1E6 / (funits * fPrf_a ) ) ;
      iResult = MAX( 1, MIN( 0xFFFF, iVal ) ) ;
    }
  return( iResult ) ;
}

/* ======================================================================
 * This function converts the PRT into the appropriate PRF.  Used
 * internally only!  The PRT units are those used by the signal
 * processor to set the PRF. 
 */ 
FLT4 fDspPrfFromPrtIn( Dsp *dsp_a, UINT2 iPrt_a )
{
  FLT4 funits = fdsp_trig_in_res( dsp_a ) ;
  FLT4 fResult = 65535.0 ;

  if( iPrt_a > 0 )
    {
      fResult =  1E6 / (funits * (FLT4)iPrt_a ) ;
      if (fResult<1.0) fResult=1.0;
      if (fResult>65535.0) fResult=65535.0;
    }
  return( fResult ) ;
}

/* ======================================================================
 * Returns the size of the Dsp structure in bytes.
 */ 
SINT4 idsp_struct_size( void )
{
  return sizeof(Dsp);
}

/* ======================================================================
 * Returns the type of DSP.
 */ 
UINT4 idsp_type( Dsp* dsp_a )
{
  return dsp_a->itype;
}

/* ======================================================================
 * Returns the maximum number of pulses (sample size) that can be
 * requested for each ray, and the maximum length of internally
 * generated spectra.
 */
SINT4 idsp_maxSampleSize( Dsp* dsp_a )
{
  SINT4 iMaxSampleSize = 256 ;
  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    iMaxSampleSize = RVP8MAXPULSES ; break ;
  }
  return( iMaxSampleSize ) ;
}

SINT4 idsp_maxSpectrumSize( Dsp* dsp_a )
{
  SINT4 iMaxSpectrumSize = 128 ;
  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    iMaxSpectrumSize = RVP8MAXPULSES ; break ;
  }
  return( iMaxSpectrumSize ) ;
}

/* ======================================================================
 * This function what type of phase control choices are 
 * available for your radar.
 */
UINT1 iDspXmtPhaseChoices
( Dsp* dsp_a )
{
  UINT1 iChoices;
  iChoices = 0;

  if (dsp_a->iProcFlags&DSP_PFLG_PHASE0) iChoices |= 0x01;
  if (dsp_a->iProcFlags&DSP_PFLG_PHASE1) iChoices |= 0x02;
  return iChoices;
}

/* ======================================================================
 * This function returns true if the signal processor supports
 * Automatic Frequency Control.
 */
UINT1 lDspHasAFC( Dsp* dsp_a )
{
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return FALSE;
      break;

    default:
      return TRUE;
      break;
    }  
}

/* ======================================================================
 * This function returns true if the signal processor supports
 * Automatic Gain Control.
 */
UINT2 ldsp_has_agc( Dsp* dsp_a )
{
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return TRUE;
      break;

    default:
      return FALSE;
      break;
    }  
}

/* ======================================================================
 * This function returns TRUE if the signal processor supports control
 * of header words specified.
 */
UINT1 lDspHasHeaderCfg( Dsp* dsp_a, UINT4 imask_a )
{
  switch (dsp_a->itype)
    {
    default:
      if ( imask_a & ~(DSP_HDR_TAGS) ) return FALSE;
      break;

    case DSP_TYPE_RVP7:
      if ( imask_a & ~(DSP_HDR_TAGS | DSP_HDR_PRT | DSP_HDR_PULSES | DSP_HDR_TSTAMP) ) 
	return FALSE;
      break;

    case DSP_TYPE_RVP8:
      if ( imask_a & ~(DSP_HDR_TAGS   | DSP_HDR_PRT   | DSP_HDR_PULSES | DSP_HDR_TSTAMP |
		       DSP_HDR_GPARM  | DSP_HDR_FLAGS | DSP_HDR_UTC    | DSP_HDR_MMTS   |
		       DSP_HDR_SYTIME | DSP_HDR_TSVERS| DSP_HDR_TASKID ) ) 
	return FALSE;
      break;
    }
  return TRUE;
  
}
/* ====================================================================
 * Returns the number of bytes in the optional ray headers read from 
 * the DSP based on the supplied mask.
 */
UINT4 iDspHeaderCfgSize
( UINT4 iCfgHdrMask_a )
{
  return iDspHeaderCfgLoc( 0, iCfgHdrMask_a );
}
/* ====================================================================
 * Returns the byte offset in the header to the specified
 * field.  Returns the total size if called with 0 arg.
 */
UINT4 iDspHeaderCfgLoc
(UINT4 iCfgHdrBit_a,
 UINT4 iCfgHdrMask_a )
{
  UINT4 iLoc=0;
  switch(iCfgHdrBit_a)
    {
    case 0:
      if (iCfgHdrMask_a&DSP_HDR_TASKID) iLoc+=28;
    case DSP_HDR_TASKID:
      if (iCfgHdrMask_a&DSP_HDR_TSVERS) iLoc+=2;
    case DSP_HDR_TSVERS:
      if (iCfgHdrMask_a&DSP_HDR_SYTIME) iLoc+=4;
    case DSP_HDR_SYTIME:
      if (iCfgHdrMask_a&DSP_HDR_MMTS  ) iLoc+=2;
    case DSP_HDR_MMTS:
      if (iCfgHdrMask_a&DSP_HDR_UTC   ) iLoc+=6;
    case DSP_HDR_UTC:
      if (iCfgHdrMask_a&DSP_HDR_FLAGS ) iLoc+=2;
    case DSP_HDR_FLAGS:
      if (iCfgHdrMask_a&DSP_HDR_GPARM ) iLoc+=128;
    case DSP_HDR_GPARM:
      if (iCfgHdrMask_a&DSP_HDR_TSTAMP) iLoc+=2;
    case DSP_HDR_TSTAMP:
      if (iCfgHdrMask_a&DSP_HDR_PULSES) iLoc+=2;
    case DSP_HDR_PULSES:
      if (iCfgHdrMask_a&DSP_HDR_PRT   ) iLoc+=2;
    case DSP_HDR_PRT:
      if (iCfgHdrMask_a&DSP_HDR_TAGS  ) iLoc+=8;
      /* case DSP_HDR_TAGS: */
      /* default: */
    }
  return iLoc; 
}

/* ======================================================================
 * This function returns TRUE if the signal processor supports TS playback
 * options.
 */
UINT1 lDspHasTsOpts( Dsp* dsp_a )
{
  switch (dsp_a->itype)
    {
    default:
      return FALSE;
      break;

    case DSP_TYPE_RVP8:
      return TRUE;
      break;
    }
}
/* ======================================================================
 * This function returns true if the signal processor supports the LOG
 * Clutter filter option.
 */
UINT2 ldsp_has_log_filter( Dsp* dsp_a )
{
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return TRUE;
      break;

    default:
      return FALSE;
      break;
    }  
}

/* ======================================================================
 * This function returns TRUE if the signal processor samples a log receiver
 * and thus requires slope calibration.  Otherwise returns zero.
 */
UINT2 ldsp_has_log_rcv( Dsp* dsp_a )
{      
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return TRUE;
      break;

    default:
      return FALSE;
      break;
    }  
  return TRUE;
}

/* ======================================================================
 * Convert gas attenuation constant to/from the 16-bit interface
 * format code used by the RVP7.  Codes from 0-10000 have a slope of
 * 1E-5; subsequent codes have a slope of 1E-4.  The overall span is
 * from 0.0 to approx 5.6535.
 */
FLT4 fGasAttenFromCode16( UINT2 icode16_a )
{
  FLT4 fGasAtten ;
  if( icode16_a < 10000 ) fGasAtten =       1E-5 * (FLT4)(icode16_a        ) ;
  else                    fGasAtten = 0.1 + 1E-4 * (FLT4)(icode16_a - 10000) ;

  return( fGasAtten ) ;
}

UINT2 iCode16FromFGasAtten( FLT4 fGasAtten_a )
{
  UINT2 iCode16 ;
  if     ( fGasAtten_a < 0.0000 ) iCode16 = 0x0000 ;
  else if( fGasAtten_a < 0.1000 ) iCode16 = NINT( 1E5 * (fGasAtten_a      ) ) ;
  else if( fGasAtten_a < 5.6535 ) iCode16 = NINT( 1E4 * (fGasAtten_a - 0.1) ) + 10000 ;
  else                            iCode16 = 0xFFFF ;

  return( iCode16 ) ;
}

/* ======================================================================
 * Return bits set for major modes that this DSP supports.  Based on
 * the info driver, so cannot check gparm.
 *
 * Bits set in ipmodes_a: Selections from PMODE_xxx
 */
void DspiFormats
( Dsp *pDsp_a,			/* Dsp structure */
  UINT4 *ipmodes_a)	        /* Processing mode options returned here */
{
  *ipmodes_a  = BIT( PMODE_PPP ) ; /* They all support these modes */
  *ipmodes_a |= BIT( PMODE_FFT ) ;
  *ipmodes_a |= BIT( PMODE_RPH ) ;

  if ( pDsp_a->itype < DSP_TYPE_RVP7 )
    {
      *ipmodes_a |= BIT( PMODE_KNMI ) ;
      return;
    }

  *ipmodes_a |= BIT( PMODE_DPT1 ) ;
  *ipmodes_a |= BIT( PMODE_DPT2 ) ;

  if ( pDsp_a->itype < DSP_TYPE_RVP8) return;

  *ipmodes_a |= BIT( PMODE_BATCH ) ;

  if (pDsp_a->iProcFlags&DSP_PFLG_MUSER1) *ipmodes_a |= BIT( PMODE_USER1 ) ;
  if (pDsp_a->iProcFlags&DSP_PFLG_MUSER2) *ipmodes_a |= BIT( PMODE_USER2 ) ;
  if (pDsp_a->iProcFlags&DSP_PFLG_MUSER3) *ipmodes_a |= BIT( PMODE_USER3 ) ;
  if (pDsp_a->iProcFlags&DSP_PFLG_MUSER4) *ipmodes_a |= BIT( PMODE_USER4 ) ;
}

/* ======================================================================
 * Convert a pulsewidth (in 1/100 microseconds) to the nearest table
 * index.  Part of info library.
 */
SINT4 iDspPwIndexFromPwidth
( Dsp *pDsp_a, SINT4 ipwidth_a )
{
  FLT4 fPwidth = ((FLT4)ipwidth_a)/100.0;

  return iDspPwIndexFromfPwidth( pDsp_a, fPwidth );
}
/* ======================================================================
 * Convert a pulsewidth (in microseconds) to the nearest table index.  
 * Part of info library,
 */
SINT4 iDspPwIndexFromfPwidth
( Dsp *pDsp_a, FLT4 fPwidth_a )
{
  SINT4 ipwindex = 0, index, iminerror = 0x10000000 ;
  SINT2 iPwidth=NINT(100.0*fPwidth_a);
  SINT4 iWidthCount = iDspPwCount( pDsp_a );

  for( index=0 ; index < iWidthCount ; index++ ) {
    SINT4 ierror = abs( iPwidth - pDsp_a->iPWidths[index] ) ;
    if( ierror < iminerror ) {
      iminerror = ierror ; ipwindex = index ;
    }
  }
  return( ipwindex ) ;
}
/* ======================================================================
 * Return the pulsewidth (in microseconds) for the specified index.
 * Part of info library.
 */
FLT4 fDspPwidthFromIndex
( Dsp *pDsp_a, SINT4 iPwIndex_a )
{
  SINT4 iPwIndex = iPwIndex_a;
  SINT4 iWidthCount = iDspPwCount( pDsp_a );
  if (iPwIndex<0) iPwIndex=0;
  if (iPwIndex>=iWidthCount) iPwIndex=iWidthCount-1;

  return ((FLT4)pDsp_a->iPWidths[iPwIndex] )/100.0 ;

}
/* ======================================================================
 * Returns the number of pulsewidths available.  Part of info library.
 */
SINT4 iDspPwCount
( Dsp *pDsp_a )
{
  SINT4 iCount = pDsp_a->iWidthCount;
  /* Because of possible server problems, we must bounds check this */
  if (iCount<1) iCount=1;
  if (iCount>4) iCount=4;
  return iCount;
}
