/* **********************************
 * *                                *  
 * *           DSP Driver           *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dsp_set_prf.c
 *     
 *          COPYRIGHT (c) 1994, 1997, 1999, 2001, 2002, 2003 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 "sigtypes.h"
#include "setup.h"
#include "dsp.h"
#include "user_lib.h"
#include "dsp_lib.h"
#include "antenna_lib.h"
#include "antenna_int.h"

#include "dsp_def.h"

/* This function must be in a separate file because it requires the
 * antenna driver!
 */
static UINT2 iRcpFromDspPolarization   /* File: dsp_set_prf.c */
(const struct dsp_manual_setup *dspsetup_a,
 UINT2 iDspPolarization_a);

/* ========================================================================
 * This subroutine sets the PRF and pulse width of the DSP.  It should
 * be used throughout IRIS.  It uses both the DSP driver, and antenna
 * driver if required.  If the pulsewidth is being changed, it will
 * set the PRF to the lower of the two PRFs for the wait time.
 */
MESSAGE dsp_set_prf
  (Dsp *dsp_a,           /* Dsp structure */
   FLT4  fPrf_a,         /* PRF in Hertz to set */
   UINT2 ipulsewidth_a,  /* Pulse width index, origin zero */
   UINT1 lUseDsp_a )     /* Use the DSP (otherwise just the RCP) */
{
  MESSAGE istatus = SS_NORMAL, istatus2 ;
  UINT2 iNewPulseWidth; FLT4 fNewPrf;
  UINT2 iOldAntPulseWidth;
  UINT1 lpw_sensed, lRadiateMightToggle, lRadiateDidToggle;
  UINT4 iStatusMask, iCommandState;

  iNewPulseWidth = ipulsewidth_a;
  fNewPrf        = fPrf_a;

  iStatusMask   = iant_stat_mask();
  iCommandState = iant_cmd();
  lpw_sensed = 0 != (iStatusMask & ASF_PWBW);
  iOldAntPulseWidth = iant_pwbw( );
	  
  lRadiateMightToggle = ((0 != (iCommandState & ACF_RADIATE))&&(dsp_a->iPwRadiateOffWait>0));
  lRadiateDidToggle=FALSE;
      
  if (lUseDsp_a)
    {
      UINT2 iOldDspPulseWidth;
      FLT4 fOldPrf, fTempPrf;
      /* Read the old values from the DSP
       */
      istatus = dspr_prf( dsp_a, &fOldPrf, &iOldDspPulseWidth );
      if (istatus!=SS_NORMAL) return istatus;
      
      /* If the pulsewidth has changed, then set the PRF low first.  Then
       * wait the delay time.
       */
      if ( (iOldDspPulseWidth!=iNewPulseWidth) || (!lpw_setting_valid_c) ||
	   (lpw_sensed &&(iOldAntPulseWidth!=iNewPulseWidth)) )
	{
	  /* First possible turning off the radiate */
	  if (lRadiateMightToggle)
	    {
	      UINT1 lRadiateSensed = (0 != (iStatusMask & ASF_RADIATE));
	      ant_set_radiate( FALSE );
	      lRadiateDidToggle=TRUE;
	      sig_microSleep( 1000*dsp_a->iPwRadiateOffWait );

	      /* If possible check that the radiate is indeed off. */
	      if (lRadiateSensed)
		{
		  UINT4 iStatusState = iant_stat();
		  if (iStatusState & ASF_RADIATE)
		    {
		      istatus = iMessageMod_va( DSP_RADIATE_WRONG, 1, "Pulse Width not set" );
		      goto SKIP_PW_SETTING;
		    }
		}
	    }
	  /* Early setting of just the pulsewidth
	   */
	  fTempPrf = MIN(fOldPrf,fNewPrf);
	  istatus = dspw_prf( dsp_a, fTempPrf, iNewPulseWidth );
	  ant_set_pwbw( iNewPulseWidth );
	  if (istatus!=SS_NORMAL) return istatus;
	  
	  /* In the future, we might want to overlap this sleep with
	   * positioning the antenna for a noise sample 
	   */
	  if (dsp_a->ipw_wait > 0) sig_microSleep( 1000*dsp_a->ipw_wait );
	}
    }      
  else 
    {
      /* This is the case of the simulated dsp, but RCP reports PW */
      if (lpw_sensed)
	{
	  /* If the pulsewidth has changed, then set the PRF low first.  Then
	   * wait the delay time.
	   */
	  if ( (iOldAntPulseWidth!=iNewPulseWidth) || !lpw_setting_valid_c )
	    {
	      /* First possible turning off the radiate */
	      if (lRadiateMightToggle)
		{
		  UINT1 lRadiateSensed = (0 != (iStatusMask & ASF_RADIATE));
		  ant_set_radiate( FALSE );
		  lRadiateDidToggle=TRUE;
		  sig_microSleep( 1000*dsp_a->iPwRadiateOffWait );
		  /* If possible check that the radiate is indeed off. */
		  if (lRadiateSensed)
		    {
		      UINT4 iStatusState = iant_stat();
		      if (iStatusState & ASF_RADIATE)
			{
			  istatus = iMessageMod_va( DSP_RADIATE_WRONG, 1, "Pulse Width not set" );
			  goto SKIP_PW_SETTING;
			}
		    }
		}
	      /* Early setting of just the pulsewidth
	       */
	      ant_set_pwbw( iNewPulseWidth );
	      if (istatus!=SS_NORMAL) return istatus;	      
	      if (dsp_a->ipw_wait > 0) sig_microSleep( 1000*dsp_a->ipw_wait );
	    }
	}
      else
	{
	  ant_set_pwbw( iNewPulseWidth );
	}
    }


  /* If we can sense the pulse width, now we can check to make sure
   * it is set correctly.
   */
  if ( lpw_sensed )
    {
      UINT2 icurrent_pulsewidth;
      icurrent_pulsewidth = iant_pwbw();
      
      /* If the current pulsewidth does not match the command, then
       * generate the error, and also set the new PRF to a safe value.
       */
      if (icurrent_pulsewidth != iNewPulseWidth)
	{
	  FLT4 fMaxPrf; SINT4 ii;
	  istatus = DSP_WRONG_PW ;
	  
	  /* Determine the maximum legal PRF.  If the reported pulsewidth is
	   * outside the legal range, then use the lowest of all the PRFs.
	   */
	  if (icurrent_pulsewidth < dspdrv_setup_c.iwidcnt)
	    {
	      fMaxPrf = (FLT4)dspdrv_setup_c.imaxprf[icurrent_pulsewidth];
	    }
	  else
	    {
	      fMaxPrf = (FLT4)dspdrv_setup_c.imaxprf[0];
	      for (ii=1; ii<dspdrv_setup_c.iwidcnt; ii++)
		{
		  fMaxPrf = MIN(fMaxPrf,(FLT4)dspdrv_setup_c.imaxprf[ii]);
		}
	    }
	  /* Protect against negative numbers
	   */
	  fMaxPrf = MAX( 1.0, fMaxPrf );
	  fNewPrf = MIN( fNewPrf, fMaxPrf );
	}
    }
  
  /* Final setting of the desired PRF
   */
  if (lUseDsp_a)
    {
      istatus2 = dspw_prf( dsp_a, fNewPrf, iNewPulseWidth );
      if( istatus == SS_NORMAL ) istatus = istatus2 ;
    }

  lpw_setting_valid_c = TRUE;
  
 SKIP_PW_SETTING:
  /* Radiate back on if it was toggled */
  if (lRadiateDidToggle)
    {
      ant_set_radiate( TRUE );
    }
  
  return( istatus ) ;
}

/* ========================================================================
 * This subroutine sets the XMT Polarization.  It should be used
 * throughout IRIS.  It uses both the DSP driver, and antenna driver
 * if required.
 */
MESSAGE dsp_set_polarization
  (Dsp *dsp_a,             /* Dsp structure */
   UINT2 iPolarization_a,  /* Polarization to set */
   UINT1 lUseDsp_a )       /* Use the DSP (otherwise just the RCP) */
{
  MESSAGE istatus;
  UINT1 lPolarSensed, lOk, lRadiateWillToggle;
  UINT4 iStatusMask, iCommandState;
  UINT2 iRcpPolarization;
  UINT2 iCurrentPolarization;

  /* Skip this code entirely on single polarization systems
   */
  if( !dspdrv_setup_c.lPolarAvailable ) return SS_NORMAL;

  iStatusMask = iant_stat_mask();
  iCommandState = iant_cmd();
  lPolarSensed   = (0 != (iStatusMask & ASF_POLAR));

  /* Convert DSP polarization to RCP XMT polarization
   */
  iRcpPolarization = iRcpFromDspPolarization( &dspdrv_setup_c, iPolarization_a );

  if (lPolarSensed)
    {
      iCurrentPolarization = iant_polar( &lOk );
    }
  else
    {
      iCurrentPolarization = iant_polar_cmd( );
      lOk = TRUE;
    }
  /* Skip this code also if the polarization is valid and is already
   * where we want it.
   */
  if( lOk && lpol_setting_valid_c &&
      (iCurrentPolarization == iRcpPolarization) ) return SS_NORMAL;

  lRadiateWillToggle = ( (0!=(iCommandState&ACF_RADIATE))&&
			 (dspdrv_setup_c.iPolarRadiateOffWait>0) );

  /* Radiate off first, then wait
   */
  if (lRadiateWillToggle)
    {
      UINT1 lRadiateSensed = (0 != (iStatusMask & ASF_RADIATE));
      ant_set_radiate(FALSE);
      sig_microSleep( 1000*dspdrv_setup_c.iPolarRadiateOffWait );
      /* If possible check that the radiate is indeed off. */
      if (lRadiateSensed)
	{
	  UINT4 iStatusState = iant_stat();
	  if (iStatusState & ASF_RADIATE)
	    {
	      istatus = iMessageMod_va( DSP_RADIATE_WRONG, 1, "Polarization not set" );
	      goto SKIP_POL_SETTING;
	    }
	}
    }
  /* Now set the polarization
   */
  if (lUseDsp_a) istatus = dspw_polarization( dsp_a, iPolarization_a );
  else           istatus = SS_NORMAL;

  ant_set_polar( iRcpPolarization );
  if( istatus != SS_NORMAL ) return istatus ;
         
  /* Now wait the after time
   */
  if ( lPolarSensed ) {
    UINT4 iTimeWaited=0;
    iCurrentPolarization = iant_polar( &lOk );

    while( (!lOk) || (iCurrentPolarization!=iRcpPolarization))
      {
	if (iTimeWaited>dspdrv_setup_c.iMaxPolarWait) return(DSP_POLAR_WRONG);
	sig_microSleep( 100*1000);
	iTimeWaited += 100;
	iCurrentPolarization = iant_polar( &lOk );
      }
  } else {
    sig_microSleep( 1000*dspdrv_setup_c.iMaxPolarWait );
  }

  lpol_setting_valid_c = TRUE ;

 SKIP_POL_SETTING:
  /* Radiate back on if toggled
   */
  if (lRadiateWillToggle) ant_set_radiate(TRUE);

  return( istatus ) ;
}

/* =======================================================
 * Convert between polarizations.
 */
static UINT2 iRcpFromDspPolarization
(const struct dsp_manual_setup *dspsetup_a,
 UINT2 iDspPolarization_a)
{
  UINT2 iRcpPolarization;
  /* Convert DSP polarization to RCP XMT polarization
   */
  iRcpPolarization = iDspPolarization_a;
  if (iRcpPolarization == POL_HORIZ_FIX)
    if ( !dspsetup_a->lXmtHorizontalOnly ) iRcpPolarization = POL_SIMULTANEOUS;

  if (iRcpPolarization == POL_VERT_FIX)
    if ( !dspsetup_a->lXmtVerticalOnly ) iRcpPolarization = POL_SIMULTANEOUS;

  return iRcpPolarization;
}

/* ========================================================
 * Return the correct Z calibration value for the
 * requested polarization.
 */
FLT4 fDspGetZCal
(Dsp* pDsp_a,
 const struct dsp_cal *pStoredCal_a,  /* Calibration file to use */
 UINT2 iPW_a,                         /* Pulse width index */
 UINT2 iDspPolarization_a )           /* Polarization to use */
{
  FLT4 fZCal;
  /*  UINT2 iRcpPolarization;

  iRcpPolarization = iDspPolarization_a;
  */

  switch (iDspPolarization_a)
    {
    case POL_VERT_FIX:
      fZCal = pStoredCal_a->Vert.PW[iPW_a].fZCal;
      break;

    case POL_SIMULTANEOUS:
      /* If we are transmitting simultaneously on both channels, and
       * the radar is capable of transmitting horizontal only, then
       * we assume the calibration for the horizontal channel is based
       * on the H only, so we need to make a correction.
       */
      fZCal = pStoredCal_a->Horiz.PW[iPW_a].fZCal;
      if ( dspdrv_setup_c.lXmtHorizontalOnly )
	{
	  SINT2 iCorrection;
	  iCorrection = dspdrv_setup_c.Horiz.iXmtLossSingle - 
	    dspdrv_setup_c.Horiz.iXmtLossHAndV;
	  fZCal -= ((FLT4)iCorrection)/10.0;
	}
      break;

    case POL_HORIZ_FIX:
    case POL_ALTERNATING:
    default:
      fZCal = pStoredCal_a->Horiz.PW[iPW_a].fZCal;
      break;
    }
  return fZCal;

}
