/* **********************************
 * *                                *  
 * *     DSP Support Library        *
 * *                                *  
 * **********************************  
 * File: libs/dsp/trig_wform_gen.c
 *    
 *      COPYRIGHT (c) 1987, 1988, 1989, 1997, 1999, 2001, 2002  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.  
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED. 
 */
#include <stdio.h>
#include <math.h>
#include <string.h>

#define DSP_INFO_DRIVER 1
#include "sigtypes.h"
#include "dsp_lib.h"

/* ------------------------------
 * This routine is used to define the trigger generator output
 * waveforms.  It makes the array used to define one pulsewidth's
 * worth of info.
 */
void trig_wform_gen
(Dsp *dsp_a,			   /* Dsp structure */
 volatile UINT2 iwave_a[2049],	   /* Array to hold waveforms. */
 volatile const FLT4 fbegin_a[6],  /* Beginning of trigger pulse. */
 volatile const FLT4 fwidth_a[6],  /* Width of trigger pulse. */
 FLT4 fpol_time_a,                 /* Time of polarization control signal transition */
 UINT2 ihold_a )                   /* Pattern during Load-Up and inactive state */
{
  SINT4 ii;
  UINT2 istart,iend, imask, itrig;
  FLT4 fresolution ;

  fresolution = fdsp_trig_wave_res(dsp_a);

  /* First set all values to the inactive state
   */
  for ( ii=0; ii<2049 ; ii++) iwave_a[ii] = (ihold_a & 0x3F) ;
      
  /* Loop through all the triggers.
   */
  for ( itrig=0; itrig<6; itrig++ )
    {
      /* ISTART, and IEND are the first active, and first inactive
       * points in the waveform.  Note the unusual limits on the span.
       * The first two and last two points in the array are forced to
       * always be the inactive level.
       */
      istart = NINT(fbegin_a[itrig]/fresolution)+1024;
      istart = MIN( MAX(istart,2), 2047);

      iend  = NINT((fbegin_a[itrig]+fwidth_a[itrig])/fresolution)+1024 ;
      iend  = MIN( MAX(iend,istart+1), 2048);

      /* Check active level of signal
       */
      if (BTEST(ihold_a,itrig))
	{
	  imask = IBCLR( -1, itrig );
	  for (ii=istart; ii<iend ; ii++) iwave_a[ii] &= imask;
	}
      else
	{
	  imask = IBSET( 0, itrig );
	  for ( ii=istart; ii<iend; ii++) iwave_a[ii] |= imask;
	}
    }
      
  /* The first word is not the load-up pattern, but it instead
   * contains the time of the polarization signal, and the MSB
   * contains the sense of the polarization control signal.  Note that
   * this calculation is origin 0, while the above calculation was
   * origin 1;
   */
  istart = NINT(fpol_time_a/fresolution)+1023 ;
  istart = MIN( MAX(istart,0), 2047 );
  if (BTEST(ihold_a,15)) istart = IBSET( istart, 15 );
  iwave_a[0] = istart;
}

/* ------------------------------
 * RANGE_MASK_GEN() creates a range mask having a given starting point
 * and bin spacing; DSPW_RANGE_MASK() can then be used to write that
 * mask to the signal processor.  Return value is a mask of input
 * parameters that were changed from their requested values.  The
 * routine attempts to preserve the bin count at all costs, but even
 * this sometimes can not be done.
 */
UINT4 range_mask_gen
( Dsp *dsp_a,
  UINT2 imask_a[],		/* Array to hold the range mask.  */
  FLT4  fbegin_a,		/* The range at which the sampling starts.  */
  SINT4 icount_a,		/* The number of range bins required  */
  FLT4  fres_a,			/* Range between bins in kilometers */
  SINT4 iPwIndex_a )
{
  /* Pull out some useful numbers and check the arguments.
   */
  FLT4   fMaskRes = fdsp_mask_res( dsp_a, iPwIndex_a ) ;
  SINT4 iMaskSize = idsp_mask_size( dsp_a ), iBin, iCount = icount_a ;
  FLT4  fMaxRange = fMaskRes * (FLT4)( iMaskSize-1 ) ;
  FLT4  fBegin = fbegin_a, fRes = fres_a ;
  UINT4 iRet = range_mask_check( dsp_a, &fBegin, &iCount, &fRes, iPwIndex_a ) ;

  /* Reset the entire range mask
   */
  memset( imask_a, 0, 2*(SINT4)(iMaskSize/16) ) ;
    
  /* Compute the range mask by looping through each range, and setting
   * the nearest range bin.
   */
  for( iBin = 0 ; iBin < iCount ; iBin++ ) {
    FLT4 fRange = fBegin + ( fRes * (FLT4)iBin ) ;
    if( fRange > fMaxRange ) break ;
    else {
      SINT4  iBit = NINT( fRange / fMaskRes ) ;
      SINT4 iWord = (iBit / 16) ;
      imask_a[ iWord ] |= BIT( iBit - (iWord * 16) ) ;
    }
  }
  if( iBin != iCount ) iRet |= RMC_ALTER_COUNT ;
  return( iRet ) ;
}

/* ------------------------------
 * Check the starting range, resolution, and bin count to verify that
 * a valid range mask could be created from those parameters.  Return
 * mask of any values that were changed along with those new values.
 */
UINT4 range_mask_check
( Dsp *dsp_a,
  FLT4  *fbegin_a,		/* The range at which the sampling starts.  */
  SINT4 *icount_a,		/* The number of range bins required  */
  FLT4  *fres_a,		/* Range between bins in kilometers */
  SINT4 iPwIndex_a )
{
  /* Pull out some useful numbers and check the arguments.  Reduce the
   * resolution and starting range if necessary in order to get all
   * the bins to fit.
   */
  FLT4   fMaskRes = fdsp_mask_res( dsp_a, iPwIndex_a ) ;
  SINT4 iMaskSize = idsp_mask_size( dsp_a ), iCount = *icount_a ;
  FLT4  fMaxRange = fMaskRes * (FLT4)( iMaskSize-1 ) ;
  FLT4  fBegin = *fbegin_a, fRes = *fres_a ; UINT4 iRet = 0 ;

  if( fBegin < 0.0       ) { fBegin = 0.0       ; iRet |= RMC_ALTER_BEGIN ; }
  if( fBegin > fMaxRange ) { fBegin = fMaxRange ; iRet |= RMC_ALTER_BEGIN ; }
  if( fRes   < fMaskRes  ) { fRes   = fMaskRes  ; iRet |= RMC_ALTER_RESOL ; }
  if( iCount < 1         ) { iCount = 1         ; iRet |= RMC_ALTER_COUNT ; }
  if( iCount > iMaskSize ) { iCount = iMaskSize ; iRet |= RMC_ALTER_COUNT ; }

#define RANGEFAILURE ( (fBegin + (fRes * (FLT4)(iCount-1) )) > fMaxRange )

  if( RANGEFAILURE ) {
    FLT4 fTryRes = (fMaxRange - fBegin) / (FLT4)(iCount-1) ;
    fTryRes = MAX( fTryRes, fMaskRes ) ;
    if( fTryRes < fRes ) { fRes = fTryRes ; iRet |= RMC_ALTER_RESOL ; }
    if( RANGEFAILURE ) {
      FLT4 fTryBegin = fMaxRange - (fRes * (FLT4)(iCount-1) ) ;
      fTryBegin = MAX( fTryBegin, 0.0 ) ;
      if( fTryBegin < fBegin ) { fBegin = fTryBegin ; iRet |= RMC_ALTER_BEGIN ; }
    }    
  }
  /* If the starting range or resolution were altered, then quantize
   * the new values (down) to integer multiples of the range mask
   * spacing.  By rounding down we're guaranteed not to cause any
   * violations.
   */
  if( iRet & RMC_ALTER_RESOL ) {
    SINT4 iTicks = (SINT4)floor( (fRes / fMaskRes) + 1E-5 ) ;
    fRes = fMaskRes * (FLT4)iTicks ;
  }
  if( iRet & RMC_ALTER_BEGIN ) {
    SINT4 iTicks = (SINT4)floor( (fBegin / fMaskRes) + 1E-5 ) ;
    fBegin = fMaskRes * (FLT4)iTicks ;
  }
  /* Return updated values and 'changed' mask.
   */
  *fbegin_a = fBegin ; *icount_a = iCount ; *fres_a = fRes ;
  return( iRet ) ;
}
