/* **********************************
 * *                                *  
 * *   DSP Library Write Routines   *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dspw_subs.c
 *     
 *  COPYRIGHT (c) 1987, 1988, 1995, 1997, 1998, 1999, 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.  
 * 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. 
 */     
#include <math.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define INCLUDE_STDIO_PROTOTYPES 1

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

static SINT4 isim_pulse_count_c = 0 ;
static SINT4 isample_size_c = 0 ;

static void FillDefaultSoprm( Dsp *dsp_a ) ;

static MESSAGE iDspWriteSoprm
( Dsp *dsp_a, struct opprm *opprm_a, UINT1 lSetThresh_a ) ;

static void mergeDataMask
( const struct dsp_data_mask *pMask_a, UINT4 *ixcmd_a,
  SINT4 *iOne_a, SINT4 *iTwo_a ) ;

static MESSAGE dspw_trig_wf		/* Obsolete */
( Dsp *dsp_a,
  const UINT2 iwave_a[2049],
  UINT2 idwf_a ) ;

/* ======================================================================
 * Setup for using simulated time series, and load one pulse of data
 * into the processor.
 */
MESSAGE dspw_sim_init
( Dsp *dsp_a,
  UINT2 lsimulate_a )
{
  icmd_c[0] = OP_LSIMUL ; if( lsimulate_a ) icmd_c[0] |= 0x100 ;
  return( dsp_write( dsp_a, icmd_c, 1 ) ) ;
}

MESSAGE dspw_sim_pulse
( Dsp *dsp_a,
  SINT4 ibincount_a,
  BIN2 iphase_a,		/* Binary angle */
  SINT2 imagnitude_a,		/* db*100 relative to nominal 0dB */
  struct dspSimPoint *simpts_a,
  UINT2 *ldone_a )
{
  MESSAGE istatus ; SINT4 iFmt ;

  /* Check which I/O format we will use to move the data into the
   * signal processor.
   */
  iFmt = 1 ;
  if( ( dsp_a->itype == DSP_TYPE_RVP8               ) &&
      ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
      ( dsp_cmp_version( &gparm_c, 3, 5 )   >= 0    ) ) iFmt = 2 ;

  /* Assemble Opcode and preamble, and write to the DSP.  The opcode
   * is modified according to the format we'll be using.
   */
  icmd_c[0] = OP_LSIMUL + ((iFmt == 1) ? 0x200 : 0x300) ;
  icmd_c[1] = ibincount_a ;
  icmd_c[2] = iphase_a ;
  icmd_c[3] = imagnitude_a ;
  icmd_c[4] = 0 ;

  istatus = dsp_write( dsp_a, icmd_c, 5 ) ;

  if( istatus == SS_NORMAL ) {
    SINT4 iLength, iBin ;
    if( iFmt == 1 ) {
      /* Legacy format using 4-words per sample and fixed point
       * integer representation.
       */
      struct dspSimPointIO_1 *pSimIO = malloc
	( iLength = ibincount_a * sizeof(struct dspSimPointIO_1) ) ;
      if( ! pSimIO ) istatus = errno_to_message( errno ) ;
      else {
	FLT4 fSlope = ((FLT4)MAX( 1, soprm_c.iz_slope )) / 4096.0 ;
	for( iBin=0 ; iBin < ibincount_a ; iBin++ ) {
	  FLT4 fISamp = simpts_a[iBin].fISamp, fQSamp = simpts_a[iBin].fQSamp ;
	  FLT4 fPower = ( SQUARE(fISamp) + SQUARE(fQSamp) ) ;
	  FLT4 fdB = 10.0 * log10( MAX( 1E-20, fPower ) ) ;
	  pSimIO[iBin].isamp   = NINT( fISamp * 2048.0 ) ;
	  pSimIO[iBin].qsamp   = NINT( fQSamp * 2048.0 ) ;
	  pSimIO[iBin].logsamp = NINT( (16.0 * (MAX( fdB, -80.0 ) + 80.0)) / fSlope ) ;
	  pSimIO[iBin].iagc    = 0 ;
	}
	istatus = dsp_write( dsp_a, pSimIO, iLength/sizeof(UINT2) ) ;
	free( pSimIO ) ;
      }
    } else {
      /* Improved format using 2-words per sample and floating point
       * data representation.
       */
      struct dspSimPointIO_2 *pSimIO = malloc
	( iLength = ibincount_a * sizeof(struct dspSimPointIO_2) ) ;
      if( ! pSimIO ) istatus = errno_to_message( errno ) ;
      else {
	UINT4 iFlags = (gparm_c.istat_i4 & GI4S_IQPACKHISNR) ? PACKIQ_HIGHSNR : 0 ;
	for( iBin=0 ; iBin < ibincount_a ; iBin++ ) {
	  pSimIO[iBin].iIPackSamp = iPackIQFromFloatIQ_( simpts_a[iBin].fISamp, iFlags ) ;
	  pSimIO[iBin].iQPackSamp = iPackIQFromFloatIQ_( simpts_a[iBin].fQSamp, iFlags ) ;
	}
	istatus = dsp_write( dsp_a, pSimIO, iLength/sizeof(UINT2) ) ;
	free( pSimIO ) ;
      }
    }
  }
  /* Decrement the internal simulated pulse counter and set the "done"
   * flag accordingly.  Having this flag permits simulated time series
   * to be loaded by routines that do not know the current pulse
   * count.
   */
  if( isim_pulse_count_c > 0 ) isim_pulse_count_c-- ;
  if( isim_pulse_count_c > 0 ) *ldone_a = FALSE ; else *ldone_a = TRUE ;

  return( istatus ) ;
}

/* Get some useful simulation numbers.  The return value is the
 * present sample size that is loaded into the DSP.  Remaining
 * simulation pulse count is optinally stored.
 */
SINT4 iDspGetSimInfo( SINT4 *piSimPulseCount )
{
  if( piSimPulseCount ) *piSimPulseCount = isim_pulse_count_c ;
  return( isample_size_c ) ;
}

/* ======================================================================
 * The RVP8 contains a built-in target simulator tool that can test
 * and debug processing algorithms that work with multiple trip
 * returns.  Several real physical targets can be simulated, each
 * having a range span measured in kilometers, a Doppler shift in
 * Hertz, and an echo power relative to the saturation level of the
 * receiver.  The echoes are correctly placed in range exactly
 * according to how they have been illuminated by whatever sequence
 * of pulses have been transmitted so far.
 */
UINT1 lDspHasTargSim( Dsp *dsp_a )
{
  UINT1 lHasTargSim = FALSE ;
  switch( dsp_a->itype ) {
    case DSP_TYPE_RVP8:
      lHasTargSim = ( ( SS_NORMAL == dspr_pram_internal( dsp_a ) ) &&
		      ( dsp_cmp_version( &gparm_c, 4, 0 ) >= 0  ) ) ;
      break ;
  }
  return( lHasTargSim ) ;
}

MESSAGE dspw_targSimEnable( Dsp *dsp_a, UINT1 lEnable_a )
{
  MESSAGE istatus = SS_NORMAL ;
  if( lDspHasTargSim( dsp_a ) ) {
    icmd_c[0] = XOPC(XOP_TARGSIM) ;
    if( lEnable_a ) icmd_c[0] |= 0x1000 ;
    istatus = dsp_write( dsp_a, icmd_c, 1 ) ;
  }
  return( istatus ) ;
}

MESSAGE dspw_targSimDefine
( Dsp *dsp_a, SINT4 iSlot_a, struct dspPhysTargSim *pTarg_a )
{
  MESSAGE istatus = SS_NORMAL ;
  if( lDspHasTargSim( dsp_a ) ) {
    icmd_c[0] = XOPC(XOP_TARGSIM) | 0x2000 ;

    icmd_c[1] = ( iSlot_a & (DSPPHYSTARGSIMCOUNT - 1) ) ;
    if( pTarg_a->iFlags & DPTS_SIMCOPOL ) icmd_c[1] |= 0x0080 ;
    if( pTarg_a->iFlags & DPTS_SIMCXPOL ) icmd_c[1] |= 0x0040 ;

    icmd_c[ 2] = pTarg_a->iStartKM10 ;
    icmd_c[ 3] = pTarg_a->iLengthKM10 ;
    icmd_c[ 4] = pTarg_a->iPowerDB10 ;
    icmd_c[ 5] = pTarg_a->iPowerSpanDB10 ;
    icmd_c[ 6] = pTarg_a->iDopplerHZ ;
    icmd_c[ 7] = pTarg_a->iDopplerSpanHZ ;
    icmd_c[ 8] = 0 ;
    icmd_c[ 9] = 0 ;
    icmd_c[10] = 0 ;
    icmd_c[11] = 0 ;

    istatus = dsp_write( dsp_a, icmd_c, 12 ) ;
  }
  return( istatus ) ;
}

/* ======================================================================
 * Burst pulse tracking routines.  These entry points were introduced
 * in RVP7 V21.
 */
UINT1 lDspCanAdjustBPFreq( Dsp *dsp_a )
{
  UINT1 lCan;
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP7:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( dsp_cmp_version( &gparm_c, 20, 6 ) >= 0     ) &&
	       ( gparm_c.istat_i3 & GI3S_BPFREQADJ           ) ) ;
      break;

    case DSP_TYPE_RVP8:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( gparm_c.istat_i3 & GI3S_BPFREQADJ           ) ) ;
      break;

    default:
      lCan = FALSE;
      break;
    }
  return lCan;
}

UINT1 lDspCanAdjustBPTime( Dsp *dsp_a )
{
  UINT1 lCan;
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP7:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( dsp_cmp_version( &gparm_c, 20, 6 ) >= 0     ) &&
	       ( gparm_c.istat_i3 & GI3S_BPTIMEADJ           ) ) ;
      break;

    case DSP_TYPE_RVP8:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( gparm_c.istat_i3 & GI3S_BPTIMEADJ           ) ) ;
      break;

    default:
      lCan = FALSE;
      break;
    }
  return lCan;
}

UINT1 lDspCanHuntBP( Dsp *dsp_a )
{
  UINT1 lCan;
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP7:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( dsp_cmp_version( &gparm_c, 20, 6 ) >= 0     ) &&
	       ( gparm_c.istat_i3 & GI3S_BPHUNTENA           ) ) ;
      break;

    case DSP_TYPE_RVP8:
      lCan = ( ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	       ( gparm_c.istat_i3 & GI3S_BPHUNTENA           ) ) ;
      break;

    default:
      lCan = FALSE;
      break;
    }
  return lCan;
}

MESSAGE dspw_afclevel		/* Set AFC level */
( Dsp *dsp_a,			/*   DSP to use */
  SINT2 iLevel_a )		/*   Signed 16-bit level (-100% to +100%) */
{
  MESSAGE istatus = SS_NORMAL ;
  if( lDspCanAdjustBPFreq( dsp_a ) ) {
    icmd_c[0] = XOPC(XOP_SETAFC) ; icmd_c[1] = iLevel_a ;
    istatus = dsp_write( dsp_a, icmd_c, 2 ) ;
  }
  return( istatus ) ;
}

MESSAGE dspw_trigslew		/* Set trigger slew */
( Dsp *dsp_a,			/*   DSP to use */
  FLT4 fSlew_a )		/*   Trigger slew in microseconds */
{
  MESSAGE istatus = SS_NORMAL ;
  if( lDspCanAdjustBPFreq( dsp_a ) ) {
    icmd_c[0] = XOPC(XOP_SETSLEW) ; icmd_c[1] = NINT( 100.0 * fSlew_a ) ;
    istatus = dsp_write( dsp_a, icmd_c, 2 ) ;
  }
  return( istatus ) ;
}

MESSAGE dspw_bphunt		/* Hunt for burst pulse */
( Dsp *dsp_a,			/*   DSP to use */
  UINT1 lForce_a )		/*   Force a hunt, even if burst is present */
{
  MESSAGE istatus = SS_NORMAL ;
  if( lDspCanHuntBP( dsp_a ) ) {
    icmd_c[0] = XOPC(XOP_BPHUNT) ; if( lForce_a ) icmd_c[0] |= 0x1000 ;
    istatus = dsp_write( dsp_a, icmd_c, 1 ) ;
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine loads the AGC table. 
 */
MESSAGE dspw_agc
( Dsp *dsp_a, const UINT2 iagc_a[256] ) 
{
  MESSAGE istatus;

  istatus = SS_NORMAL;
  if (ldsp_has_agc( dsp_a ))
    {
      icmd_c[0] = OP_LDAGC;
      istatus = dsp_write( dsp_a, icmd_c, 1 );
      if (istatus!=SS_NORMAL) return( istatus ) ;
      
      istatus = dsp_write( dsp_a, (void *)iagc_a, 256 );
    }
      
  return( istatus ) ;
}
      
/* ======================================================================
 * This routine configures the calibration numbers for the
 * DSP.
 */
MESSAGE dspw_cal
( Dsp *dsp_a,
  FLT4 flogslope_a,		/* Log slope  */
  FLT4 fcaliref_a,		/* Calibration Reflectivity */
  FLT4 fzdroffset_a,		/* ZDR Calibration offset (GDR Value) */
  FLT4 fldroffset_a,		/* LDR Calibration offset (XDR Value) */
  FLT4 fgas_atten_a )		/* Gaseous attenuation constant (dB/km) */
{    
  MESSAGE istatus;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  if ( dsp_a->itype >= DSP_TYPE_RVP7) {
    soprm_c.iz_slope = RVP7_LOG_SLOPE;
  } else {
    SINT4 itemp;
    itemp = NINT( flogslope_a*4096.);
    if (itemp>4095) itemp = 4095;
    if (itemp<0) itemp = 0;
    soprm_c.iz_slope = itemp;
  }
  soprm_c.iz_calib   = (UINT2)NINT( fcaliref_a * 16.0 );
  soprm_c.gas_atten  = iCode16FromFGasAtten( fgas_atten_a ) ;
  soprm_c.zdr_offset = (UINT2)NINT( fzdroffset_a *  16.0 );
  soprm_c.ldr_offset = (UINT2)NINT( fldroffset_a * 100.0 );

  istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
  return( istatus ) ;
}

/* ============================================================
 * Fill the Soprm array with default values.
 */
static void FillDefaultSoprm( Dsp *dsp_a )
{
  memset( &soprm_c, 0, sizeof( soprm_c ) ) ;
  dspr_pram_internal( dsp_a ) ;

  /* Load up the default state for the operational parameters
   */
  soprm_c.isamp    = 25 ;	        /* Sample Size */
  soprm_c.iflags   = gparm_c.iflags ;   /* Processing options */
  soprm_c.iz_slope = RVP7_LOG_SLOPE;	/* Log Slope */
  soprm_c.izns_thr = 8 ;		/* Log Noise Threshold */
  soprm_c.iccr_thr = -400 ;		/* CCOR Threshold */
  soprm_c.isqi_thr = 128 ;		/* SQI Threshold */
  soprm_c.isig_thr = 160 ;		/* SIG Threshold */
  soprm_c.iz_calib = -352 ;		/* Calibration Reflectivity */
  soprm_c.flg_agc  =			/* Processing Mode, AGC integration */
    (PMODE_PPP << 8) + 3 ;

  soprm_c.filter =		        /* Filter Control */
    OPFLT_FROM_WIN( WIN_HAMMING ) + 10 ;

  soprm_c.dbt_tcf = TCF_LOG ;	        /* Threshold control flags */
  soprm_c.dbz_tcf = TCF_LOG & TCF_CSR ;
  soprm_c.vel_tcf = TCF_SQI & TCF_CSR ;
  soprm_c.wid_tcf = TCF_SQI & TCF_CSR & TCF_WSP ;
  soprm_c.zdr_tcf = TCF_LOG ;

  soprm_c.gas_atten = iCode16FromFGasAtten( 0.016 ) ;

  soprm_c.wavelength =		        /* Wavelength in cm*1000 */
    dspdrv_setup_c.iwavelength * 10 ;

  soprm_c.iCltSigPacks[0] = CLTSIG_DEFAULT ; /* Use default RVP8 residual */
  soprm_c.iCltSigPacks[1] = CLTSIG_DEFAULT ; /*   clutter values. */
}

/* ============================================================
 * Write the Soprm structure to the signal processor, appending the
 * required opcode and XARG list if the DSP supports it.
 */
static MESSAGE iDspWriteSoprm
( Dsp *dsp_a, struct opprm *opprm_a, UINT1 lSetThresh_a )
{
  MESSAGE istatus ; SINT4 index = 0 ; UINT1 lSupportsXargs = FALSE ;

  switch( dsp_a->itype )
    {
    case DSP_TYPE_RVP7:
      lSupportsXargs = ( ( SS_NORMAL == dspr_pram_internal( dsp_a ) ) &&
			 ( dsp_cmp_version( &gparm_c, 22, 3 ) >= 0  ) );
      break;

    case DSP_TYPE_RVP8:
      lSupportsXargs = TRUE;
      break;
    }

  if( lSupportsXargs )
    {
      SINT4 iXargCount = (SOPRM_TOTAL_ARGS - SOPRM_FIXED_ARGS) ;
      icmd_c[0] = (iXargCount << 5) | OP_XARGS ;
      memcpy( &icmd_c[1], SOPRM_FIXED_ARGS+(UINT2 *)opprm_a, 2*iXargCount ) ;
      index += (1 + iXargCount) ;
    }
  icmd_c[index] = OP_SOPRM ;
  if( ! lSetThresh_a ) icmd_c[index] |= 0x0100;
  index++ ;

  memcpy( &icmd_c[index], &soprm_c, 2*SOPRM_FIXED_ARGS ) ;
  index += SOPRM_FIXED_ARGS ;
  
  istatus = dsp_write( dsp_a, icmd_c, index ) ;
  lSoprmValid_c = TRUE ;

  return( istatus ) ;
}

/* ======================================================================
 * The TTYCHAR routine writes a virtual terminal character to the DSP.
 * PLOTMONITOR controls whether plotting data will be output on the
 * host computer interface.
 */
MESSAGE dspw_ttychar
( Dsp * dsp_a,			/* Dsp structure */
  const char char_a )		/* Character to write */
{
  MESSAGE istatus;
  icmd_c[0] = ( char_a << 8 ) | OP_TTYOP;
      
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  return( istatus ) ;
}

MESSAGE dspw_plotmonitor
  (Dsp * dsp_a,			/* Dsp structure */
   UINT2 lenable_a )	/* TRUE to enable plot monitoring */
{
  MESSAGE istatus ;
  switch( dsp_a->itype )
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      istatus = SS_NORMAL ;
      break;
    default:
      icmd_c[0] = ( lenable_a ? 0x20 : 0x40 ) | OP_TTYOP ;
      istatus = dsp_write( dsp_a, icmd_c, 1 ) ;
      break ;
    }
  return istatus ;
}

/* ======================================================================
 * DSPW_FILT() loads into the signal processor an array of clutter
 * filter codes that will be applied at each of the selected range
 * bins.  The legacy entry point works with all versions of signal
 * processors, but does not allow filters that vary according to
 * antenna angle.
 */
MESSAGE dspw_filt( Dsp *dsp_a, const UINT2 ifcode_a[] )
{
  MESSAGE istatus ;

  icmd_c[0] = IBSET( OP_LFILT, 5 ) ;
  istatus = dsp_write( dsp_a, icmd_c, 1 ) ;

  if( istatus == SS_NORMAL ) {
    SINT4 isize = idsp_bin_max( dsp_a ) ;
    istatus = dsp_write( dsp_a, (void *)ifcode_a, isize ) ;
  }
  return( istatus ) ;
}

/* The enhanced version of LFILT allows 2D and 3D filter selections as
 * a function of Range, AZ, and EL.  Each invocation of this command
 * fills in another slot in the signal processor's Range/AZ/EL clutter
 * filter array.  If pFilt_a is NULL, or if pFilt_a->nBins is not in
 * the valid range of [1,RVP8MAXBINS], then the indicated slot will be
 * invalidated.  Moreover, if iSlot_a is not in the valid range of
 * [0,RVP8ASTABSIZE-1], then the entire filter array will be cleared.
 */
MESSAGE dspw_filt3D
( Dsp *dsp_a, SINT4 iSlot_a, const struct rvp8SectorFilters *pFilt_a )
{
  MESSAGE istatus = DSP_FEATURE_MISSING ;
  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    if( (iSlot_a < 0) || (iSlot_a >= RVP8ASTABSIZE) ) {
      icmd_c[0] = OP_LFILT | 0x0060 ;
      istatus = dsp_write( dsp_a, icmd_c, 1 ) ;
    } else {
      SINT4 nBins = pFilt_a ? pFilt_a->nBins : 0 ;
      nBins = MAX( 0, MIN( RVP8MAXBINS, nBins ) ) ;

      icmd_c[0] = (6 << 5) + OP_XARGS ; /* Setup the LFILT XArgs */
      icmd_c[1] = iSlot_a ;
      icmd_c[2] = nBins ;
      icmd_c[3] = pFilt_a ? pFilt_a->iAzLo : 0 ;
      icmd_c[4] = pFilt_a ? pFilt_a->iAzHi : 0 ;
      icmd_c[5] = pFilt_a ? pFilt_a->iElLo : 0 ;
      icmd_c[6] = pFilt_a ? pFilt_a->iElHi : 0 ;
      icmd_c[7] = OP_LFILT | 0x0040 ;   /* Setup the LFILT opcode */

      istatus = dsp_write( dsp_a, icmd_c, 8 ) ;

      if( (istatus == SS_NORMAL) && (nBins > 0) ) {
	UINT2 iPackFilts[ RVP8MAXBINS/2 ] ; SINT4 iBin ;
	for( iBin=0 ; iBin < nBins ; iBin++ ) {
	  UINT2 iBinFilter = pFilt_a->binFilters[ iBin ] ;
	  if( iBin & 1 ) iPackFilts[ iBin/2 ] |= (iBinFilter << 8) ;
	  else           iPackFilts[ iBin/2 ]  =  iBinFilter       ;
	}
	istatus = dsp_write( dsp_a, iPackFilts, (nBins+1)>>1 ) ;
      }
    }
  }
  return( istatus ) ;
}

/* ======================================================================
 * Load a new clutter filter definition into the signal processor,
 * overwriting one of the existing filter slots.  Valid filter slots
 * are in the range [1,NFILTS-1], i.e., you can not overwrite the all
 * pass filter #0.
 */
MESSAGE dspw_filterSpecs
( Dsp *dsp_a, SINT4 iSlot_a, const struct rvp8SpecFilt *pFilt_a )
{
  MESSAGE istatus = DSP_FEATURE_MISSING ;
  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    struct rvp8SpecFiltIO specIO ; UINT2 *pCmd = icmd_c ;
    SINT4 iWord, nWords = sizeof(specIO)/sizeof(UINT2) ;

    /* Translate the filter specs to I/O form, and set them up as
     * XARGS for the LFSPECS opcode.
     */
    *pCmd++ = (nWords << 5) + OP_XARGS ;

    rvp8EncodeSpecFilt( &specIO, pFilt_a ) ;
    for( iWord=0 ; iWord < nWords ; iWord++ ) {
      *pCmd++ = *( iWord + (UINT2 *)&specIO ) ;
    }
    *pCmd++ = XOPC( XOP_LFSPECS ) ;
    *pCmd++ = iSlot_a ;

    istatus = dsp_write( dsp_a, icmd_c, nWords+3 ) ;
  }
  return( istatus ) ;
}

/* ======================================================================
 * Control whether the signal processor is phase locking its (I,Q)
 * data to the measured burst phase, whether pulse-to-pulse amplitude
 * correction is being applied, etc.  The argument should be some
 * OR-combination of the BPL_xxx parameters.
 */
MESSAGE dspw_burstPulseOpts( Dsp *dsp_a, UINT2 iBPLOpts_a )
{
  MESSAGE istatus ;
  icmd_c[0] = XOPC( XOP_BPOPTS ) ;
  icmd_c[1] = iBPLOpts_a ;
  istatus = dsp_write( dsp_a, icmd_c, 2 ) ;  
  return( istatus ) ;
}

/* ======================================================================
 * Routines to configure point clutter detection and point clutter
 * censoring.
 */
MESSAGE dspw_pointClutter
( Dsp *dsp_a, UINT4 iFlags_a, FLT4 fThreshDB_a, SINT4 iSideSkip_a )
{
  MESSAGE istatus ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  soprm_c.iPtCltDetect = 0 ;
  soprm_c.iPtCltCensor = 0 ;

  if( iFlags_a & PTCLT_DETECT ) {
    SINT4 iSideSkip = MAX( 0, MIN(  7, iSideSkip_a ) ) ;
    SINT4 iThreshDB = MAX( 0, MIN( 63, NINT( 1.0 * fThreshDB_a ) ) ) ;
    soprm_c.iPtCltDetect = 0x8000 | iSideSkip | (iThreshDB << 3) ;

    if( iFlags_a & PTCLT_CENSOR ) {
      soprm_c.iPtCltCensor = 0x8000 ;
      if( iFlags_a & PTCLT_FLAGALL ) soprm_c.iPtCltCensor |= 0x4000 ;
    }
  }
  istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
  return( istatus ) ;
}

/* ======================================================================
 * This routine sets the options for the headers.  This feature is not
 * available on the RVP6, or the RVP7 before release 7.
 */
MESSAGE dspw_header_cfg( Dsp *dsp_a, UINT4 imask_a )
{
  MESSAGE istatus;
  
  /* Quality check to make sure we do not ask for an output that
   * the processor cannot handle.
   */
  if (!lDspHasHeaderCfg( dsp_a, imask_a )) return DSP_FEATURE_MISSING;

  /* RVP6 supports only tags, so skip the command */
  switch (dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      return SS_NORMAL;
      break;
    }
  
  icmd_c[0] = XOPC(XOP_CFGHDR) ;
  icmd_c[1] = imask_a & 0xFFFF ;
  icmd_c[2] = imask_a >> 16 ;
      
  istatus = dsp_write( dsp_a, icmd_c, 3 ) ;
  return( istatus ) ;
}

/* ======================================================================
 * Set/Clr optional user bits that are included in the pulse headers
 * of the signal processor's (I,Q) data stream.  The separate CLEAR
 * and SET args allows the library to fiddle with subsets of the bits
 * without affecting others.  Args may be NULL if unneeded.
 *
 * The permanent Set/Clr bits are updated in the signal processor and
 * retain their value from the last time they were defined.  The bits
 * are repeated into all pulse headers.  The ONCE bits, however, are
 * transitory and will appear in only one pulse header each time they
 * are set.
 *
 * The user bits from separate calls will never be collapsed into a
 * single pulse header, even if the header and bit times indicate that
 * they could.  This means that each XOP_UIQBITS opcode will always
 * result in at least one pulse header being tagged with exactly those
 * data.  This is generally what you want, since no other exact
 * outcome could be guaranteed based on time-of-arrival alone.
 */
MESSAGE dspw_uiqBits
( Dsp *dsp_a, struct uiqbits *set_a, struct uiqbits *clr_a,
  struct uiqbits *once_a )
{
  struct uiqbits clr, set, once ; MESSAGE istatus ;
  SINT4 nWords = sizeof(struct uiqbits)/sizeof(UINT2), index=0 ;

  if(  set_a )  set = *set_a  ; else memset(  &set, 0, sizeof(set ) ) ;
  if(  clr_a )  clr = *clr_a  ; else memset(  &clr, 0, sizeof(clr ) ) ;
  if( once_a ) once = *once_a ; else memset( &once, 0, sizeof(once) ) ;

  icmd_c[ index++ ] = ((3 * nWords) << 5) + OP_XARGS ;

  memcpy( &icmd_c[index],  &set, sizeof(set ) ) ; index += nWords ;
  memcpy( &icmd_c[index],  &clr, sizeof(clr ) ) ; index += nWords ;
  memcpy( &icmd_c[index], &once, sizeof(once) ) ; index += nWords ;

  icmd_c[ index++ ] = XOPC(XOP_UIQBITS) ;

  istatus = dsp_write( dsp_a, icmd_c, index ) ;
  return( istatus ) ;
}

/* ======================================================================
 * This routine sets or clears the LED. 
 */    
MESSAGE dspw_led
( Dsp *dsp_a,
  UINT2 lon_a ) 
{    
  MESSAGE istatus;

  icmd_c[0] = OP_SLED ;
  if ( lon_a ) icmd_c[0] = IBSET( icmd_c[0], 8 );
      
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  return( istatus ) ;
}

/* ======================================================================
 * This routine estimates the current noise levels of the
 * receiver.
 *
 * Entry parameters
 *     ISTARTRNG_A  Starting range in km. A negative number indicates
 *                  the previous value will be used.
 *     IPRF_A       Trigger rate(Hz) during noise sampling.
 *                  Zero indicates the previous value will be used.
 */
MESSAGE dspw_noise_sample
( Dsp *dsp_a,
  SINT2 istartrng_a,
  UINT2 iprf_a )
{
  MESSAGE istatus; struct gparm Gparm;

  /* Set up noise sample command.  The two arguments must always be
   * written, but they may be ignored based on op code bits
   */      
  icmd_c[0] = OP_SNOISE;
  icmd_c[1] = 0;
  icmd_c[2] = 0;

  if ( istartrng_a>=0 )
    {
      icmd_c[0] = IBSET( icmd_c[0], 8 );
      icmd_c[1] = istartrng_a;
    }

  if ( iprf_a>0 )
    {
      icmd_c[0] = IBSET( icmd_c[0], 9 );
      icmd_c[2] = idsp_prt( dsp_a, iprf_a ) ;
    }
      
  /* Take the noise measurement(s)
   */
  istatus = dsp_write( dsp_a, icmd_c, 3 );
  if (istatus!= SS_NORMAL) return( istatus ) ;

  /* Get DSP status and insure that there were no errors.
   */
  istatus = dspr_gparm( dsp_a, &Gparm, GPARM_LSTATUS_KEEP );
  if (istatus != SS_NORMAL ) return( istatus ) ;

  if (BTEST(Gparm.istat_l, 0)) return DSP_NSE_TRIG;
    
  if (BTEST(Gparm.istat_l, 1)) return DSP_NSE_RANGE;

  if (BTEST(Gparm.istat_l,14)) return DSP_NSE_LINEAR;
    
  /* Now test the linear noise on the RVP6 to detect interference
   */
  if ((dsp_a->itype == DSP_TYPE_RVP6)||(dsp_a->itype == DSP_TYPE_RVP6_2))
    {
      FLT4 fLogNoise, fLinNoise;
      fLogNoise = ((FLT4)(Gparm.log_nse   )) / 16384.0;
      fLinNoise = ((FLT4)(Gparm.zlin_noise)) / 16384.0;
      if (fLinNoise > (dspdrv_setup_c.fRvp6InterferenceThreshold * fLogNoise) ) 
	{
	  char sMessage[40];
	  sprintf( sMessage, "Log: %.4f, Lin: %.4f", fLogNoise, fLinNoise );
	  istatus = iMessageMod_va( DSP_NSE_INTERFERE, 1, sMessage ) ;
	  return( istatus ) ;
	}
    }

  return SS_NORMAL;
}

/* ======================================================================
 * This routine sets the current noise level of the receiver.  It will
 * not actually take a noise sample.  It does not bother to check and
 * report all the possible errors.  The assumption here is that the
 * program checked these errors on the initial noise sample.  Command
 * only available on the RVP8 and later.
 */
MESSAGE DspWriteNoiseValues
( Dsp *dsp_a,
  SINT2 istartrng_a,     /* Start range in km.  Negative means previous value remains. */
  UINT2 iprf_a,          /* Trigger rate in hz.  Zero means previous value remains. */
  UINT2 iNoiseLevel_a,   /* Noise level, to be placed in gparm word 6 */
  SINT2 iStandardDev_a,  /* Noise STD, to be place in gparm word 49 */
  SINT2 iHVRatio_a,      /* Horiz/Vert noise ratio, to be placed in gparm work 50 */
  UINT2 iLStatus_a )     /* Latched status bits, to be placed in gparm word 9 */
{
  MESSAGE istatus;

  if ( dsp_a->itype < DSP_TYPE_RVP8 ) return DSP_FEATURE_MISSING;

  /* Set up noise sample command.  The two arguments must always be
   * written, but they may be ignored based on op code bits
   */      
  icmd_c[0] = OP_SNOISE | (1 << 10) ;
  icmd_c[1] = 0;
  icmd_c[2] = 0;

  if ( istartrng_a>=0 )
    {
      icmd_c[0] = IBSET( icmd_c[0], 8 );
      icmd_c[1] = istartrng_a;
    }

  if ( iprf_a>0 )
    {
      icmd_c[0] = IBSET( icmd_c[0], 9 );
      icmd_c[2] = idsp_prt( dsp_a, iprf_a ) ;
    }

  icmd_c[3] = iNoiseLevel_a;
  icmd_c[4] = iStandardDev_a;
  icmd_c[5] = iHVRatio_a;
  icmd_c[6] = iLStatus_a;

  /* Set the noise values
   */
  istatus = dsp_write( dsp_a, icmd_c, 7 );
  return( istatus ) ;
}

/* ======================================================================
 * This routine restores the original powerup default noise levels.
 * Command only available on the RVP8 and later.
 */
MESSAGE DspSetDefaultNoise
( Dsp *dsp_a )
{
  MESSAGE istatus;

  if ( dsp_a->itype < DSP_TYPE_RVP8 ) return DSP_FEATURE_MISSING ;

  /* Set up and write the noise sample command.  The two arguments
   * must always be written, but they will be ignored.
   */      
  icmd_c[0] = OP_SNOISE | (2 << 10) ;
  icmd_c[1] = 0 ;
  icmd_c[2] = 0 ;

  istatus = dsp_write( dsp_a, icmd_c, 3 );
  return( istatus ) ;
}
      
/* ======================================================================
 * This routine configures the DSP processing options.
 */
MESSAGE dspw_options
( Dsp *dsp_a,
  UINT2 ipmode_a,	        /* Major Mode (See PMODE_... constants) */
  SINT2 ismpsize_a,		/* Sample size */
  UINT2 ioptions_a,	        /* Processing flags (See OPF_... constants) */
  UINT2 iagcfil_a,		/* AGC filter code  */
  UINT2 idelay_a,		/* Dual PRF stabilization delay */
  UINT2 iunfold_a,		/* Choices: One of PRF_XXX */
  UINT2 iwindow_a,	        /* Window choice (See WIN_... constants) */
  SINT2 iRangeSmooth_a )        /* Optional range smoothing (0:None, 1:Pairs, etc) */
{
  MESSAGE istatus;
  UINT4 itsformats, ipmodes ; UINT2 lretry_major_mode=TRUE ;

  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  /* Save away some information that we may need later.
   */
  isample_size_c = (ismpsize_a & 4095) ;
  iunfold_c      = iunfold_a ;
      
  /* Fill in the operational parameter structure with the new args
   * that were given to us.
   */
  soprm_c.isamp   = isample_size_c ;
  soprm_c.flg_agc = 0x00FF & iagcfil_a ;
  soprm_c.ibin_smooth = MAX( 0, MIN( (DSP_MAXAVG-1), iRangeSmooth_a ) ) ;
      
  soprm_c.iflags &= (OPF_POL | OPF_ANYSPEC) ;
  soprm_c.iflags |= (ioptions_a & ~(OPF_POL | OPF_ANYSPEC)) ;
  soprm_c.iflags |=  OPF_2DPASS ; /* Always set (for now) */

  soprm_c.filter  = 0x00FF & idelay_a ;
  soprm_c.filter |= OPFLT_FROM_WIN( iwindow_a ) ;

  /* If the processor does not support the requested major mode, then
   * force into pulse pair mode, and return an error.
   */
  retry_major_mode:
  istatus = dspr_formats( dsp_a, &itsformats, &ipmodes ) ;
  if( istatus == SS_NORMAL && BTEST( ipmodes, (ipmode_a & 0xF) ) ) 
    {
      soprm_c.flg_agc |= ((ipmode_a & 0xF) << 8) ;
      istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
    }
  else
    {
      if( lretry_major_mode )
	{
	  /* Reread the DSP parameters just in case power was cycled
	   * since the first time we did it.
	   */
	  lgparm_valid_c = FALSE ; lSoprmValid_c = FALSE ;
	  lpw_setting_valid_c = FALSE ; lpol_setting_valid_c = FALSE ;
	  lretry_major_mode = FALSE ;
	  goto retry_major_mode;
	}
      else
	{
	  soprm_c.flg_agc |= (PMODE_PPP << 8) ;
	  istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
	  if( istatus == SS_NORMAL ) istatus = DSP_MAJOR_MODE ;
	}
    }
  
  return( istatus ) ;
}

/* ======================================================================
 * Setup options for the spectra that are output by the DSP.  We can
 * request that the spectra size either match the sample size, or be
 * constrained to the greatest power of two that is less than or equal
 * to the sample size.  Return value indicates whether the DSP can
 * honor the request.
 */
MESSAGE dspw_specOpts( Dsp *dsp_a, UINT1 lWantAnySize_a, UINT1 *plGetAnySize_a )
{
  MESSAGE istatus = SS_NORMAL ; UINT1 lGetAnySize = FALSE ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    soprm_c.iflags &= ~OPF_ANYSPEC ;
    if( lWantAnySize_a ) soprm_c.iflags |= OPF_ANYSPEC ;

    istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;

    if( istatus == SS_NORMAL ) {
      istatus = dspr_gparm( dsp_a, NULL, GPARM_LSTATUS_KEEP ) ;
      if( istatus == SS_NORMAL ) {
	lGetAnySize = (gparm_c.istat_i4 & GI4S_ANYSPECOUT) != 0 ;
      }
    }
    break ;
  }
  *plGetAnySize_a = lGetAnySize ; return( istatus ) ;
}

/* ======================================================================
 * Setup special options for timeseries and timeseries playback.
 */
MESSAGE dspw_tsOpts
( Dsp *dsp_a,
  UINT2 iFlagsTS_a )		/* Timeseries flags (See OPTS_xxx constants) */
{
  MESSAGE istatus = SS_NORMAL ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  switch( dsp_a->itype ) {
  case DSP_TYPE_RVP8:
    soprm_c.iFlagsTS = iFlagsTS_a ;

    istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine writes an I/O diagnostic pattern to the DSP.
 */
MESSAGE dspw_pat
( Dsp *dsp_a,
  const UINT2 idiag_a[16] )
{
  MESSAGE istatus;
      
  icmd_c[0] = OP_IOTEST;
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if (istatus!=SS_NORMAL) return( istatus ) ;
      
  istatus = dsp_write( dsp_a, (void *)idiag_a, 16 );
  return( istatus ) ;
}

/* ======================================================================
 * This routine selects the polarization For private use within libary
 * only.
 */
MESSAGE dspw_polarization
( Dsp *dsp_a,
  UINT2 iPolarization_a )
{
  MESSAGE istatus ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  soprm_c.iflags &= ~OPF_POL ;
  soprm_c.iflags |=  OPF_FROM_POL(iPolarization_a) ;

  istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
  return( istatus ) ;
}

/* ======================================================================
 * This routine selects the pulse width, trigger waveform, and PRF.
 * For private use within library only.
 */
MESSAGE dspw_prf
( Dsp *dsp_a, FLT4 fPrf_a, UINT2 ipwcode_a )
{
  MESSAGE istatus ; UINT2 ipwcode ; SINT4 iSeq, iNumPeriods ;
  UINT1 lCanDoTriggerSequences;
      
  /* Setup the SETPWF opcode word itself
   */
  ipwcode =  3 & ipwcode_a ;
  icmd_c[0] = (ipwcode << 10) | (ipwcode << 8) | OP_SETPWF ;

  /* Use the trigger unfolding code to decide what kind of trigger the
   * user wants.
   */
  switch( iunfold_c ) 
    {
    default:
      /* For normal triggers, fill in the first arg with the encoded
       * trigger period and write the SETPWF instruction directly.
       */
      icmd_c[1] = iDspPrtInFromPrf( dsp_a, fPrf_a ) ;
      istatus = dsp_write( dsp_a, icmd_c, 2 ) ;
      break ;
      
    case PRF_SEQ0: iSeq = 0 ; goto sequence ;
    case PRF_SEQ1: iSeq = 1 ; goto sequence ;
    case PRF_SEQ2: iSeq = 2 ; goto sequence ;
    case PRF_SEQ3: iSeq = 3 ; goto sequence ;
    sequence:
    /* For special trigger sequences, first make sure that there is a
     * valid trigger sequence defined, and that our signal processor
     * is able to handle it.
     */
    iNumPeriods = dspdrv_setup_c.customTrig[iSeq].iNumPeriods ;
    lCanDoTriggerSequences = FALSE;
    switch(dsp_a->itype)
      {
      case DSP_TYPE_RVP7:
	lCanDoTriggerSequences = ( ( SS_NORMAL == dspr_pram_internal( dsp_a ) ) &&
				   ( dsp_cmp_version( &gparm_c, 17, 0 ) >= 0  ) ) ;
	break;
      case DSP_TYPE_RVP8:
	lCanDoTriggerSequences = TRUE;
	break;
      }
    if( (iNumPeriods<=0) || (iNumPeriods>MAXTRIGPERIODS) )
      lCanDoTriggerSequences = FALSE;

    if (lCanDoTriggerSequences)
      {
	UINT2 xcmd[ 1 + (2*MAXTRIGPERIODS) ], *pXcmd = xcmd ; SINT4 iPeriod ;
	
	/* Looks good.  First setup the XARG list consisting of a list
	 * of 32-bit trigger periods in nanoseconds.
	 */
	*pXcmd++ = ((2 * iNumPeriods) << 5) | OP_XARGS ;
	
	for( iPeriod=0 ; iPeriod < iNumPeriods ; iPeriod++ ) {
	  SINT4 iTime = dspdrv_setup_c.customTrig[iSeq].iPeriods[iPeriod] ;
	  *pXcmd++ = (UINT2)(iTime  & 0x0000FFFF) ;
	  *pXcmd++ = (UINT2)(iTime >> 16        ) ;
	}
	istatus = dsp_write( dsp_a, &xcmd[0], 1 + (2 * iNumPeriods) ) ;
	
	/* That is followed by the SETPWF opcode with the "PRT" argument
	 * set to zero to indicate a custom trigger sequence.
	 */
	if( istatus == SS_NORMAL ) {
	  icmd_c[1] = 0 ;
	  istatus = dsp_write( dsp_a, icmd_c, 2 ) ;
	}
      }
    else
      {
	istatus = DSP_CANT_TSEQ ;
      }
    break ;
    }      
      
  return( istatus ) ;
}

/* ======================================================================
 * Define an alternate trigger rate that will take effect when the
 * antenna is within a prescribed solid sector.  This command must be
 * issued after dspw_prf(), as that routine clears all of the
 * information that we supply here.
 */
MESSAGE dspw_prfSect
( Dsp *dsp_a, FLT4 fPrf_a, SINT4 iSector_a,
  BIN2 iAzLo_a, BIN2 iAzHi_a, BIN2 iElLo_a, BIN2 iElHi_a )
{
  MESSAGE istatus = DSP_FEATURE_MISSING ;
  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    SINT4 iPRTns = NINT( 1E9 / fPrf_a ) ;
    icmd_c[0] = XOPC( XOP_PRFSECT ) ;
    icmd_c[1] = iSector_a ;
    icmd_c[2] = iAzLo_a ; icmd_c[3] = iAzHi_a ;
    icmd_c[4] = iElLo_a ; icmd_c[5] = iElHi_a ;
    icmd_c[6] = iPRTns & 0xFFFF ;
    icmd_c[7] = iPRTns >> 16 ;
    istatus = dsp_write( dsp_a, icmd_c, 8 );
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine invokes the actual processing and output of radar
 * data.
 */
MESSAGE dspw_proc
( Dsp *dsp_a,
  UINT2 larch_a,         /* Choose "archive" format output */
  const struct dsp_data_mask *pDataMask_a, 
                         /* Which set of data to output.  For the Doppler output 
		          * this is some combination of the DB_...  parameter 
		          * constants; Unused for time series.
		          */
  UINT2 iSpectraType_a,  /* Only used time series mode */
  UINT2 imode_a          /* One of DSP_PROC_xxx */
  )
{    
  MESSAGE istatus=SS_NORMAL ; UINT4 ixcmd ;

  /* Build up the PROC command based on the collection of parameters
   * that have been selected.  Begin with the opcode and mode bits.
   */
  ixcmd = OP_PROC | ((imode_a & 3) << 5) ;

  /* Merge in the unfolding (Dual PRF) request bits.
   */
  switch( iunfold_c ) {
  case PRF_FIXED: case PRF_2_3: case PRF_3_4: case PRF_4_5:
    ixcmd |= ( (iunfold_c & 3) << 8 ) ;
    break ;
  }

  if( imode_a == DSP_PROC_TIME_SERIES )	/* ===== Time Series / Spectra Output ===== */
    {
      switch( iSpectraType_a & 0x0F ) {
      case TSOUT_TS8: default:
	ixcmd |= 0x00000000 ; break ;
      case TSOUT_TS16:
	ixcmd |= 0x00008000 ; break ;
      case TSOUT_SPEC:
	ixcmd |= (0x00004000 + ((iSpectraType_a & SPEC_MASK) << 6)) ; break ;
      }
    }
  else				/* ======== Doppler Parameter Output ======== */
    {
      struct dsp_data_mask TestMask, AvailableMask;
      SINT4 iOne=0, iTwo=0 ;
      dsp_data_set( dsp_a, &AvailableMask ) ;
      TestMask = *pDataMask_a;
      DspMaskAndNot( &TestMask, &AvailableMask );

      /* Check for any invalid bits, and then assemble all of the
       * parameter request bits.
       */
      if( !lDspMaskEmpty( &TestMask ) ) return DSP_CANT_PROC;

      if( larch_a ) { ixcmd |= ( 1 << 15 ) ; iOne++ ; }
      mergeDataMask( pDataMask_a, &ixcmd, &iOne, &iTwo ) ;

      /* Verify that we are not requesting one-byte and two-byte
       * parameters at the same time, and that the current operating
       * parameters match what we are asking for.  Issue a new SOPRM
       * command if the current one is not correct.
       */
      if( iOne )
	{
	  if( iTwo ) 
	    {		/* Both one-byte and two-byte */
	      return DSP_PROC_BADMIX ;
	    } 
	  else
	    {		/* One-byte only */
	      if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;
	      if( 0 != (soprm_c.iflags & OPF_16B) ) {
		soprm_c.iflags &= ~OPF_16B ;
		istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
	      }
	    }
	}
      else
	{
	  if( iTwo )
	    {		/* Two-byte only */
	      if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;
	      if( 0 == (soprm_c.iflags & OPF_16B) ) {
		soprm_c.iflags |= OPF_16B ;
		istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
	      }
	    }
	}
    }

  /* If we have gotten this far without any errors, then write the
   * PROC command to the DSP.
   */
  if( istatus == SS_NORMAL ) {
    UINT2 xcmd[3] ;

    xcmd[0] = (1 << 5) + OP_XARGS ;  /* XARGS opcode, plus one arg follows */
    xcmd[1] = (ixcmd >> 16) ;	     /* High 16-bits of extended PROC opcode */
    xcmd[2] = (ixcmd & 0x0000FFFF) ; /* Low 16-bits of normal PROC opcode */

    if( 0xFFFF0000 & ixcmd ) {
      /* Extended XARG bits are required.  Verify that the signal
       * processor really can handle it.
       */
      if( (dsp_a->itype==DSP_TYPE_RVP8) ||
	  ( ( dsp_a->itype == DSP_TYPE_RVP7               ) &&
	    ( SS_NORMAL    == dspr_pram_internal( dsp_a ) ) &&
	    ( dsp_cmp_version( &gparm_c, 15, 4 ) >= 0   ) )){
	istatus = dsp_write( dsp_a, &xcmd[0], 3 ) ;
      } else {
	istatus = DSP_CANT_PROC ;
      }
    } else {
      /* Normal PROC opcode is all that we need.
       */
      istatus = dsp_write( dsp_a, &xcmd[2], 1 ) ;
    }
  }

  isim_pulse_count_c = isample_size_c ;
  return( istatus ) ;
}

static void mergeDataMask
( const struct dsp_data_mask *pMask_a, UINT4 *ixcmd_a,
  SINT4 *iOne_a, SINT4 *iTwo_a )
{
  if( lDspMaskTest( pMask_a, DB_DBZ    )) { *ixcmd_a |= OP_PROC_DBZ   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_DBT    )) { *ixcmd_a |= OP_PROC_DBT   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_VEL    )) { *ixcmd_a |= OP_PROC_VEL   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_WIDTH  )) { *ixcmd_a |= OP_PROC_WID   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_ZDR    )) { *ixcmd_a |= OP_PROC_ZDR   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_KDP    )) { *ixcmd_a |= OP_PROC_KDP   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIDP  )) { *ixcmd_a |= OP_PROC_PHIDP ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOHV  )) { *ixcmd_a |= OP_PROC_RHOHV ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_SQI    )) { *ixcmd_a |= OP_PROC_SQI   ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_LDRH   )) { *ixcmd_a |= OP_PROC_LDRH  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOH   )) { *ixcmd_a |= OP_PROC_RHOH  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIH   )) { *ixcmd_a |= OP_PROC_PHIH  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_LDRV   )) { *ixcmd_a |= OP_PROC_LDRV  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOV   )) { *ixcmd_a |= OP_PROC_RHOV  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIV   )) { *ixcmd_a |= OP_PROC_PHIV  ; (*iOne_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_FLAGS  )) { *ixcmd_a |= OP_PROC_FLAGS ; (*iOne_a)++ ; }

  if( lDspMaskTest( pMask_a, DB_DBZ2   )) { *ixcmd_a |= OP_PROC_DBZ   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_DBT2   )) { *ixcmd_a |= OP_PROC_DBT   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_VEL2   )) { *ixcmd_a |= OP_PROC_VEL   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_WIDTH2 )) { *ixcmd_a |= OP_PROC_WID   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_ZDR2   )) { *ixcmd_a |= OP_PROC_ZDR   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_KDP2   )) { *ixcmd_a |= OP_PROC_KDP   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIDP2 )) { *ixcmd_a |= OP_PROC_PHIDP ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOHV2 )) { *ixcmd_a |= OP_PROC_RHOHV ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_SQI2   )) { *ixcmd_a |= OP_PROC_SQI   ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_LDRH2  )) { *ixcmd_a |= OP_PROC_LDRH  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOH2  )) { *ixcmd_a |= OP_PROC_RHOH  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIH2  )) { *ixcmd_a |= OP_PROC_PHIH  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_LDRV2  )) { *ixcmd_a |= OP_PROC_LDRV  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_RHOV2  )) { *ixcmd_a |= OP_PROC_RHOV  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_PHIV2  )) { *ixcmd_a |= OP_PROC_PHIV  ; (*iTwo_a)++ ; }
  if( lDspMaskTest( pMask_a, DB_FLAGS2 )) { *ixcmd_a |= OP_PROC_FLAGS ; (*iTwo_a)++ ; }
}

/* ======================================================================
 * Setup the phase modulation hardware in the signal processor.  The
 * phase control output lines will be configured for the requested
 * type of sequence.  In addition, knowing that this sequence is being
 * transmitted, the DSP algorithms will then handle the received data
 * properly.
 */
MESSAGE dspw_phaseSeq
( Dsp * dsp_a,
  UINT2 iPhaseSeq_a,		/* One of PHSEQ_xxx */
  SINT4 iAngleListLength_a,	/* Angle list is generally optional */
  const BIN2 iAngleList_a[] ) 	/*   for most phase sequences. */
{
  MESSAGE istatus = SS_NORMAL ;

  if( iDspXmtPhaseChoices(dsp_a) == DSP_PHASE_CONTROLLED ) {
    UINT1 lHasPhaseModOpcode = FALSE ; 

    switch( dsp_a->itype ) {
    case DSP_TYPE_RVP7:
      lHasPhaseModOpcode = ( ( SS_NORMAL == dspr_pram_internal( dsp_a ) ) &&
			     ( dsp_cmp_version( &gparm_c, 23, 1 ) >= 0  ) ) ;
      break;
    case DSP_TYPE_RVP8:
      lHasPhaseModOpcode = TRUE;
      break;
    }
    if( lHasPhaseModOpcode ) {
      SINT4 iLen = MAX( 0, MIN( 1024, iAngleListLength_a ) ) ;
      if( iLen && iAngleList_a ) {
	icmd_c[0] = (iLen << 5) + OP_XARGS ;
	istatus = dsp_write( dsp_a, icmd_c, 1 );
	if( istatus == SS_NORMAL )
	  istatus = dsp_write( dsp_a, (void *)iAngleList_a, iLen ) ;
      }
      if( istatus == SS_NORMAL ) {
	icmd_c[0] = ((iPhaseSeq_a & 0x7) << 12) + XOPC(XOP_CFGPHZ) ;
	istatus = dsp_write( dsp_a, icmd_c, 1 );
      }
    }
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine defines the pulse width control bits and PRT limits.
 *
 * Entry parameters
 *     IPLSCODE_A    Pulse/bandwidth control bits
 *     IMAXPRF_A     Maximum trigger PRF
 */
MESSAGE dspw_pwinfo
( Dsp *dsp_a,
  volatile const UINT2 iplscode_a[4],
  const UINT2 imaxprf_a[4] ) 
{   
  MESSAGE istatus;

  icmd_c[0] = OP_PWINFO;
  icmd_c[1] = ( iplscode_a[3]<<12 )|( iplscode_a[2]<<8 )|
    ( iplscode_a[1]<<4 )| iplscode_a[0];
      
  icmd_c[2] = idsp_prt( dsp_a, imaxprf_a[0] );
  icmd_c[3] = idsp_prt( dsp_a, imaxprf_a[1] );
  icmd_c[4] = idsp_prt( dsp_a, imaxprf_a[2] );
  icmd_c[5] = idsp_prt( dsp_a, imaxprf_a[3] );
      
  istatus = dsp_write( dsp_a, icmd_c, 6 );
  return( istatus ) ;
}
/* ======================================================================
 * This routine defines the pulse width control bits and PRT limits.
 * It uses the internal dsp_setup structure to get the info.
 */
MESSAGE DspWritePwInfo
( Dsp *dsp_a )
{   
  MESSAGE istatus;

  icmd_c[0] = OP_PWINFO;
  icmd_c[1] = ( dspdrv_setup_c.ipwbits[3]<<12 )|( dspdrv_setup_c.ipwbits[2]<<8 )|
    ( dspdrv_setup_c.ipwbits[1]<<4 )| dspdrv_setup_c.ipwbits[0];
      
  icmd_c[2] = idsp_prt( dsp_a, dspdrv_setup_c.imaxprf[0] );
  icmd_c[3] = idsp_prt( dsp_a, dspdrv_setup_c.imaxprf[1] );
  icmd_c[4] = idsp_prt( dsp_a, dspdrv_setup_c.imaxprf[2] );
  icmd_c[5] = idsp_prt( dsp_a, dspdrv_setup_c.imaxprf[3] );
      
  istatus = dsp_write( dsp_a, icmd_c, 6 );
  return( istatus ) ;
}

/* ======================================================================
 * This routine informs the DSP of the ranges at which data are to be
 * collected.
 *
 * Entry parameters
 * 
 *     IMASK_A - Range mask array. (512 words for and RVP6, 2048 words for PPP
 *     IPRAM_A - Range averaging parameter used in and RVP6
 */
MESSAGE dspw_range_mask
( Dsp *dsp_a,
  const UINT2 imask_a[],
  UINT2 ipram_a ) 
{
  MESSAGE istatus;
  SINT4 iwcnt, ibitcnt;

  /* ---------------------------------------------------------------------- */
  ibitcnt = idsp_mask_size( dsp_a );
  iwcnt = ibitcnt/16;

  icmd_c[0]  = ( ipram_a<<8 )|OP_LRMSK;
      
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if( istatus != SS_NORMAL ) return( istatus ) ;
      
  istatus = dsp_write( dsp_a, (void *)imask_a, iwcnt );
  return( istatus ) ;
}

/* ======================================================================
 * This routine resets the DSP.  It uses the software command.
 */
MESSAGE dspw_reset
( Dsp *dsp_a,			/* Dsp structure */
  UINT2 lzoff_a,		/* Zeros I, Q, and LOG offsets */
  UINT2 lfifo_a,		/* Clears output FIFOs */
  UINT2 lpwrp_a )		/* Full power up reset */
{      
  MESSAGE istatus;
      
  /* Put together the reset op code
   */
  icmd_c[0] = OP_RESET;
  if ( lzoff_a ) icmd_c[0] |= 0x100;
  if ( lfifo_a ) icmd_c[0] |= 0x080;
  if ( lpwrp_a ) icmd_c[0] |= 0x020;

  istatus = dsp_write( dsp_a, icmd_c, 1 );
      
  /* A side effect of most resets is that the TTY readahead buffer is
   * also flushed.  This prevents old data from showing up in later
   * calls to dspr_ttyop().
   */
  if( lfifo_a || lpwrp_a ) iTTYCount_c = 0 ;

  return( istatus ) ;
}

/* ======================================================================
 * This routine loads the STC table.  This table has a different
 * format depending on the DSP.
 */
MESSAGE dspw_stcb
( Dsp *dsp_a,			/* Dsp structure */
  const UINT2 istc_a[8192] )	/* STC table to load */
{
  MESSAGE istatus;

  icmd_c[0] = OP_LSTCB;
      
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if (istatus!=SS_NORMAL) return( istatus ) ;
      
  istatus = dsp_write( dsp_a, (void *)istc_a, 8192 );
  return( istatus ) ;
}

/* ======================================================================
 * This routine sets the options of antenna angle synchronization.
 */
MESSAGE dspw_syn_mode
( Dsp *dsp_a,
  SINT2 isyn_mode_a,		/* One of ANGSYN_xxx */
  UINT2 lbcd_a )		/* BCD angles (else binary) */
{
  MESSAGE istatus;
      
  icmd_c[0] = OP_LSYNC;

  lAngleSynced_c = (isyn_mode_a!=ANGSYN_NONE);

  switch( isyn_mode_a ) {
  case ANGSYN_STATIC : icmd_c[0] |= 0x0800 ; break ;
  case ANGSYN_DYNAMIC: icmd_c[0] |= 0x2800 ; break ;
  }
  if (lbcd_a) icmd_c[0] = IBSET( icmd_c[0],  9 );
      
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  return( istatus ) ;
}

/* ======================================================================
 * Return information about how many CPI reads are required to
 * get to valid data rays.  This peeps at information from recent
 * configuration commands.
 */
SINT4 iDspPipelineDepth( Dsp *dsp_a )
{
  SINT4 iPipelineDepth;
  switch( dsp_a->itype )
    {
    case DSP_TYPE_RVP8:
      iPipelineDepth = 0;
      if (iunfold_c != PRF_FIXED ) iPipelineDepth+=1;
      break ;
      
    default:
      iPipelineDepth = 1;
      /* Dual PRF requires valid previous velocities */
      if (iunfold_c != PRF_FIXED ) iPipelineDepth+=1; 
      /* Angle sync: 1st immediate, 2nd partial span */
      if (lAngleSynced_c) iPipelineDepth+=2;
      break;
    }
  return iPipelineDepth;
}

/* ======================================================================
 * This routine loads the antenna synchronization table. 
 */
MESSAGE dspw_syn_table_
( Dsp *dsp_a,
  const UINT2 isync_a[],	/* Array holding sync table */
  SINT2 iwcnt_a,		/* Number of words in the table */
  UINT2 ifuzz_a )		/* Fuzz angle */
{   
  MESSAGE istatus;
      
  /* Note that this command will disable sync mode, and load the table. */
  icmd_c[0] = IBSET( OP_LSYNC, 8 );
  icmd_c[1] = iwcnt_a;
  icmd_c[2] = ifuzz_a ;
  istatus = dsp_write( dsp_a, icmd_c, 3 );
  if (istatus!=SS_NORMAL) return( istatus ) ;
      
  istatus = dsp_write( dsp_a, (void *)isync_a, iwcnt_a );
  return( istatus ) ;
}

/* ======================================================================
 * This routine configures the data thresholding levels for all of the
 * processed parameters that are computed in the DSP.
 */
MESSAGE dspw_thresh
( Dsp *dsp_a,
  FLT4 fnoithre_a,		/* Log receiver noise threshold in dB */
  FLT4 rcorthre_a,		/* Clutter Correction in dB */
  FLT4 rsqithre_a,		/* SQI threshold */
  FLT4 rpowthre_a,		/* Log power threshold */
  UINT2 iczflag_a,		/* Corrected Z threshold flag */
  UINT2 iuzflag_a,		/* Uncorrected Z threshold flag */
  UINT2 ivelflag_a,		/* Velocity threshold flag */
  UINT2 iwidflag_a,		/* Width threshold flag */
  UINT2 izdrflag_a )		/* ZDR threshold flag */
{
  MESSAGE istatus ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  soprm_c.izns_thr = NINT( fnoithre_a * 16. ) ;
  soprm_c.iccr_thr = NINT( rcorthre_a * 16. ) ;
  soprm_c.isig_thr = NINT( rpowthre_a * 16. ) ;
  {
    SINT4 iSQI = NINT( rsqithre_a * 256. ) ;
    soprm_c.isqi_thr = MIN( MAX( iSQI, 0 ), 255 ) ;
  }
  soprm_c.dbt_tcf = iuzflag_a;
  soprm_c.dbz_tcf = iczflag_a;
  soprm_c.vel_tcf = ivelflag_a ;
  soprm_c.wid_tcf = iwidflag_a;
  soprm_c.zdr_tcf = izdrflag_a;

  istatus = iDspWriteSoprm( dsp_a, &soprm_c, TRUE ) ;
  return( istatus ) ;
}

/* ======================================================================
 * Set options that pertain to timeseries playback.  The signal
 * processor generally will not attempt to produce output rays when
 * certain critical processing parameters do not match those of the
 * playback data in the timeseries API.  This routine gives finer
 * control over how those decisions are made by allowing a mask of
 * MMTS_xxx bits specifying which mismatch criteria to ignore.
 */
MESSAGE dspw_playbackTSOpts
( Dsp *dsp_a,
  UINT2 iMismatchTSIgnore_a )
{
  MESSAGE istatus ;
  if( ! lSoprmValid_c ) FillDefaultSoprm( dsp_a ) ;

  soprm_c.iMismatchTSIgnore = iMismatchTSIgnore_a ;

  istatus = iDspWriteSoprm( dsp_a, &soprm_c, FALSE ) ;
  return( istatus ) ;
}

/* ======================================================================
 * Set the thresholding criteria for a collection of individual data
 * parameter types.
 */
MESSAGE dspw_thresh_types
( Dsp *dsp_a,
  const struct dsp_data_mask *pDataMask_a, 
  FLT4 fnoithre_a,	 	/* Log receiver noise threshold in dB */
  FLT4 rcorthre_a,		/* Clutter Correction in dB */
  FLT4 rsqithre_a,		/* SQI threshold */
  FLT4 rpowthre_a,		/* Log power threshold */
  UINT2 iTCF_a )		/* Threshold control bits */
{
  MESSAGE istatus ;
  if( dsp_a->itype < DSP_TYPE_RVP8 ) istatus = DSP_FEATURE_MISSING ;
  else {
    UINT4 ixcmd = 0 ; SINT4 iOne = 0, iTwo = 0 ;
    mergeDataMask( pDataMask_a, &ixcmd, &iOne, &iTwo ) ;

    icmd_c[0] = XOPC( XOP_THRESH ) ;
    icmd_c[1] = (ixcmd & 0xFFFF) ;
    icmd_c[2] = (ixcmd >> 16   ) ;
    icmd_c[3] = 0 ;
    icmd_c[4] = NINT( fnoithre_a * 16.  ) ;
    icmd_c[5] = NINT( rcorthre_a * 16.  ) ;
    {
      SINT4 iSQI = NINT( rsqithre_a * 256. ) ;
      icmd_c[6] = MAX( 0, MIN( 255, iSQI )) ;
    }
    icmd_c[7] = NINT( rpowthre_a * 16.  ) ;
    icmd_c[8] = iTCF_a ;
    icmd_c[9]  = 0 ;
    icmd_c[10] = 0 ;

    istatus = dsp_write( dsp_a, icmd_c, 11 );
  }
  return( istatus ) ;
}

/* ============================================================
 * Setup the breakpoint and slope that will modify the LOG threshold
 * according to the Clutter-To-Noise ratio of the target.  This makes
 * the LOG threshold behave properly even as the noise floor becomes
 * elevated due to very strong clutter targets.  The default signal
 * processor settings will be restored if pMod_a is NULL.
 */
MESSAGE dspw_LOGModifiers( Dsp *dsp_a, struct logModifiers *pMod_a )
{
  MESSAGE istatus = SS_NORMAL ; SINT4 ii ;
  for( ii=0 ; ii < 2 ; ii++ ) {
    soprm_c.iCltSigPacks[ii] = ( pMod_a == NULL ) ? CLTSIG_DEFAULT :
      CLTSIG_PACK( pMod_a->fBegDB[ii], pMod_a->fSlope[ii] ) ;
  }
  istatus = iDspWriteSoprm( dsp_a, &soprm_c, TRUE ) ;
  return( istatus ) ;
}

/* ============================================================
 * Configure the interference filter.
 */
MESSAGE DspWriteIntFilter
( Dsp *dsp_a, 
  UINT2 iType_a,     /* Which type of interference filter to run */
  FLT4  fC1_a,       /* Parameter C1 in dB */
  FLT4  fC2_a )      /* Parameter C2 in dB */
{
  MESSAGE istatus ; UINT1 lSupportsFeature;

  switch( dsp_a->itype )
    {
    case DSP_TYPE_RVP7:
      lSupportsFeature = ( ( SS_NORMAL == dspr_pram_internal( dsp_a ) ) &&
			 ( dsp_cmp_version( &gparm_c, 22, 3 ) >= 0  ) );
      break;

    case DSP_TYPE_RVP8:
      lSupportsFeature = TRUE;
      break;

    default:
      lSupportsFeature = FALSE ;
      break;
    }

  if (lSupportsFeature)
    {
      SINT4 iXargCount = 2;
      icmd_c[0] = (iXargCount << 5) | OP_XARGS ;
      icmd_c[1] = NINT( 100.0*fC1_a );
      icmd_c[2] = NINT( 100.0*fC2_a );
      icmd_c[3] = (iType_a<<12) | XOPC(XOP_CFGINTF);
      istatus = dsp_write( dsp_a, icmd_c, 4) ;
    }
  else
    {
      istatus = DSP_FEATURE_MISSING ;
    }
  return( istatus ) ;
}

/* ======================================================================
 * This routine is used to load the trigger generator output waveforms.
 * Only called for RVP6 systems.
 *
 * Entry parameters
 *     IWAVE_A   Array hold the waveform.
 *     IDWF_A    Select one of the four waveform tables. (VALUE 0-3) 
 */
static MESSAGE dspw_trig_wf
( Dsp *dsp_a,
  const UINT2 iwave_a[2049],
  UINT2 idwf_a )
{
  MESSAGE istatus;

  /* Write the six-pack waveform into one of the four tables.
   */
  icmd_c[0] = ( 1 << ((3 & idwf_a)+8) ) | OP_TRIGWF ;
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if (istatus!=SS_NORMAL) return( istatus ) ;
      
  istatus = dsp_write( dsp_a, (void *)iwave_a, 2049 );
  return( istatus ) ;
} 

/* ====================================================================
 * This subroutine loads all the trigger waveforms into the DSP. 
 * Entry parameters: 
 *     fstart_a    - Starting times
 *     fwidth_a    - Widths of trigger pulses
 *     fpol_time_a - Time of polarization control transistion
 *     ilevel_a    - levels of trigger pulses
 *     dsp_a       - Dsp structure pointer
 *
 * Exit parameters:
 *    IBUF_A   - Temporary buffer 
 */
MESSAGE load_dsp_trig
( Dsp *dsp_a,
  FLT4 fstart_a[4][6],
  volatile const FLT4 fwidth_a[4][6],
  volatile const FLT4 fpol_time_a[4],
  volatile const UINT2 *ilevel_a,
  UINT2 ibuf_a[2049] )
{
  MESSAGE istatus; SINT2 ipw;

  /* Skip for RVP7
   */
  if (dsp_a->itype >= DSP_TYPE_RVP7) return SS_NORMAL;

  /* Loop through all four pulse widths
   */
  for ( ipw=0; ipw<4; ipw++)
    {
      trig_wform_gen( dsp_a, ibuf_a, fstart_a[ipw], fwidth_a[ipw], 
		      fpol_time_a[ipw], *ilevel_a );

      istatus = dspw_trig_wf( dsp_a, ibuf_a, ipw );
      if (istatus !=SS_NORMAL) return( istatus ) ;
    }
      
  return( istatus ) ;
}

/* ======================================================================
 * This routine informs the DSP about the current progress of the
 * application's task.  This information is then included with the
 * time series data, which will help with eventual interpretation.
 * Command only available on the RVP8 and later.  There is no error
 * returned for older processors, because this is only a hint, and not
 * critical for proper operation of older processors.
 *
 * Issuing this call increments the iAqMode field of the rvp8PulseHdr.
 * The continuous flow of data is not otherwise interrupted.  SIGMET
 * strongly encourages all control programs and time series recording
 * programs to use this mechanism.  This will allow customers to mix
 * and match their programs.
 */
MESSAGE DspWriteTaskId
( Dsp *dsp_a,
  const char *sTaskName_a,   /* Name of task, only first 16 chars used */
  UINT2 iSweepNumber_a,      /* Sweep number within the task, origin 1, 0=N/A */
  UINT2 iAuxNumber_a,        /* Aux number within the task */
  UINT1 iScanType_a )	     /* One of SCAN_xxx */
{
  MESSAGE istatus = SS_NORMAL ;

  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    char sName[DSPTASKNAMELEN] ; SINT4 ii ;

    icmd_c[0] = (1 << 5) + OP_XARGS ;  /* XARGS opcode, plus one arg follows */
    icmd_c[1] = iScanType_a ;

    icmd_c[2] = XOPC(XOP_TASKID) ; 
    icmd_c[3] = iSweepNumber_a ;
    icmd_c[4] = iAuxNumber_a ;

    strncpy( sName, sTaskName_a, DSPTASKNAMELEN ) ; /* Encode the task ID as */
    for( ii=0 ; ii < DSPTASKNAMELEN/2 ; ii++ ) {    /*   a NULL padded array. */
      UINT1 *pCh = (UINT1*)&sName[ 2*ii ] ;
      icmd_c[ 5+ii ] = ( (UINT2)pCh[0] ) | ( ((UINT2)pCh[1]) << 8 ) ;
    }
    istatus = dsp_write( dsp_a, icmd_c, (5 + DSPTASKNAMELEN/2) ) ;
  }
  return( istatus ) ;
}

/* **********************
 * *                    *
 * * RVP7 Boot Routines *
 * *                    *
 * **********************
 */

/* ====================================================================
 * Load a ROM image into the DSP
 */
MESSAGE dspw_romload
( Dsp *dsp_a, const char *sPathName_a,
  UINT1 lOnlyIfDifferent_a, UINT1 lFailOkay_a )
{
  MESSAGE istatus = SS_NORMAL ; FILE *fdes ; SINT4 ii ;

  /* Try to open the ROM data file, and possibly allow for it not
   * being there, i.e., if the file is missing, we may take that as
   * evidence that no upgrade was ever intended.
   */
  if( (fdes = fopen( sPathName_a, "r" )) == NULL ) {
    if( ! lFailOkay_a )
      istatus = errno_to_message_va( errno, 1, sPathName_a ) ;
  } else {
    UINT2 cmdbuf[ 3 + RVP7_MAX_XARGS ] ;
    UINT1 iRomMajor, iRomMinor, iRvpMajor, iRvpMinor ;

    /* If we were able to open the ROM file, then the user is probably
     * trying to actually use it.  Check for a variety of things that
     * might go wrong from the start.
     */
    if( dsp_a->itype != DSP_TYPE_RVP7 ) {
      istatus = iMessageMod_va( DSP_NOBOOT, 1, "DSP is not an RVP7" ) ;
    } else {
      istatus = dspr_pram_internal( dsp_a ) ;
      if( istatus == SS_NORMAL ) {
	iRvpMajor = DSP_VERSION( &gparm_c ) ;
	iRvpMinor = DSP_MINOREV( &gparm_c ) ;
	if( dsp_cmp_version( &gparm_c, 17, 3 ) < 0 ) {
	  istatus = iMessageMod_va( DSP_NOBOOT, 1, "RVP7 must be V17.3 or higher" ) ;
	} else {
	  cmdbuf[0] = 0x8000 + XOPC(XOP_BOOT) ;
	  istatus = dsp_write( dsp_a, cmdbuf, 1 ) ;
	}
      }
    }

    if( istatus == SS_NORMAL ) {
      UINT1 lContinue = TRUE, lLoaded = FALSE, lExit = FALSE ;
      SINT4 iline = 0, iRomLen = 0 ; UINT4 iRomAddr, iDecOffset=0 ;
      char sRecord[ IHEX_MAXRECL + 10 ] ;

      /* Run through the entire contents of the file.  Accumulate
       * records into longer runs of data that will transfer more
       * efficiently.  Write the boot data to the signal processor as
       * we go.
       */
      while( lContinue &&
	     (NULL != fgets( sRecord, sizeof(sRecord), fdes )) ) {
	UINT1 iData[IHEX_MAXDATA] ; SINT4 iLength ; UINT4 iAddress ;

	iline++ ; if( sRecord[0] != ':' ) continue ;
	switch( intelHexDecode( sRecord, iData, &iLength,
				&iAddress, &iDecOffset ) ) {
	case IHEX_EOF:		/* Normal EOF */
	  lContinue = FALSE ; lLoaded = TRUE ;
	  goto write_boot ;
	case IHEX_BADREC:	/* Junk in record */
	  istatus = iMessageMod_va( DSP_BADROM, 2, "Junk at line %d", iline ) ;
	  lContinue = FALSE ;
	  break ;
	case IHEX_CHECKSUM:	/* Checksum error in record */
	  istatus = iMessageMod_va( DSP_BADROM, 2, "Checksum at line %d", iline ) ;
	  lContinue = FALSE ;
	  break ;

	case IHEX_NORMAL:	/* Process Another Record */

	  /* Watch for the ROM addresses that contain the major and
	   * minor revision numbers.  If we are only supposed to
	   * install a changed version, then quit if the new code
	   * matches the code that's now running.
	   */
	  if( iAddress == RVP7_ROMRVP ) {
	    iRomMajor = iData[4] ; iRomMinor = iData[8] ;
	    if( lOnlyIfDifferent_a ) {
	      if( (iRomMajor == iRvpMajor) && (iRomMinor == iRvpMinor) ) {
		lContinue = FALSE ; lExit = TRUE ; break ;
	      }
	    }
	  }
	  /* Write out the data we've accumulated so far whenever the
	   * address has jumped, or the new record will not fit.
	   */
	  if( ((iRomAddr + iRomLen) != iAddress        ) ||
	      ((iRomLen  + iLength)  > (2 * RVP7_MAX_XARGS) ) ) {

	      write_boot:
	    if( iRomLen > 0 ) {
	      UINT2 *pend = &cmdbuf[ 1 + (iRomLen/2) ] ;
	      cmdbuf[0] = ((iRomLen/2) << 5) + OP_XARGS ;
	      pend[0] = ((iRomAddr >> 6) & 0x7000) + XOPC(XOP_BOOT) ;
	      pend[1] = ((iRomAddr >> 2) & 0xFFFF) ;
	      istatus = dsp_write( dsp_a, cmdbuf, 3+(iRomLen/2) ) ;
	      if( istatus != SS_NORMAL ) lContinue = FALSE ;
	    }
	    iRomLen = 0 ; if( ! lContinue ) break ;
	  }
	  /* Enter the new record that we just read from the file
	   */
	  if( iRomLen == 0 ) iRomAddr = iAddress ;

	  for( ii=0 ; ii < iLength ; ii += 4 ) {
	    SINT4 iaddr = 1 + ((ii + iAddress - iRomAddr) / 2) ;
	    cmdbuf[iaddr  ] = ((UINT2)iData[ii  ]) + (((UINT2)iData[ii+1]) << 8) ;
	    cmdbuf[iaddr+1] = ((UINT2)iData[ii+2]) + (((UINT2)iData[ii+3]) << 8) ;
	  }
	  iRomLen += iLength ;
	  break ;
	}
      }

      /* If everything worked properly up to here, check that the
       * image is valid in the RVP7, and reboot if all is well.
       */
      if( (! lExit) && (istatus == SS_NORMAL) ) {
	if( ! lLoaded ) {
	  istatus = iMessageMod_va( DSP_BADROM, 1,
				    "Inappropriate file contents" ) ;
	} else {
	  cmdbuf[0] = 0x9000 + XOPC(XOP_BOOT) ;
	  istatus = dsp_write( dsp_a, cmdbuf, 1 ) ;

	  if( istatus == SS_NORMAL ) {
	    UINT2 readbuf[1] ; SINT4 iReadCount ;
	    istatus = dsp_read( dsp_a, readbuf, 1, &iReadCount ) ;

	    if( istatus == SS_NORMAL ) {
	      if( (iReadCount == 1) && (readbuf[0] == 1) ) {
		cmdbuf[0] = 0xA000 + XOPC(XOP_BOOT) ;
		istatus = dsp_write( dsp_a, cmdbuf, 1 ) ;
		if( istatus == SS_NORMAL ) {
		  char text[80] ;
		  sprintf( text, "Old:%d.%d, New:%d.%d",
			   iRvpMajor, iRvpMinor, iRomMajor, iRomMinor ) ;
		  lgparm_valid_c = FALSE;  lSoprmValid_c = FALSE;
		  lpw_setting_valid_c = FALSE ; lpol_setting_valid_c = FALSE ;
		  sig_microSleep( 3500000 ) ;
		  istatus = iMessageMod_va( DSP_NEWCODE, 1, text ) ;
		}
	      } else {
		istatus = iMessageMod_va( DSP_BADROM, 1, "Rejected by RVP7" ) ;
	      }
	    }
	  }
	}
      }
    }
    fclose( fdes ) ;
  }
  return( istatus ) ;
}
