/* **************************************************************
 * *                                                            *  
 * *                       DSP Library                          *
 * *  Open routines for info library, not used by info clients  *
 * *                                                            *
 * **************************************************************
 * file: libs/dsp/dsp_open.c
 *
 *      COPYRIGHT (c) 1992, 1994, 1997, 1999, 2002, 2003, 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.  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 <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Note that the dsp_info library has all the extern variables not included.
 */
#define DSP_ALLOCATE 1

#include "sigtypes.h"
#include "sigversion.h"
#include "setup.h"
#include "dsp.h"
#include "dsp_lib.h"
#include "config_lib.h"
#include "user_lib.h"

#include "dsp_def.h"

static MESSAGE dsp_load_setup
( struct dsp_manual_setup *pDspSetup_a );

static MESSAGE zcal_load
( struct dsp_refl_cal *pCal_a );

static MESSAGE zcal_save
( const struct dsp_refl_cal *pCal_a );

static struct dsp_refl_cal zCalibration_c;
/* ======================================================================
 * This subroutine must be called to get the DSP pointer before any
 * dsp library call can be made.
 */
Dsp *DspOpen( MESSAGE *pStatus_a )
{   
  int ii; FLT4 fx ;

  /* If this unit is not open yet, then figure out what kind of DSP it
   * is.  Also do this for opens that come after the unit has been
   * closed, but not if it is reopened without being closed.  This
   * prevents needless file I/O.
   */
  if( dsp_c.iopen & DSP_OPEN) 
    {
      *pStatus_a = SS_NORMAL;
      return &dsp_c;
    }

  /* Load setup information
   */
  *pStatus_a = dsp_load_setup( &dspdrv_setup_c );
  if (*pStatus_a != SS_NORMAL) return NULL;
  
  /* Check that we have a dsp
   */
  if (!dspdrv_setup_c.ldsp_available) { *pStatus_a=DSP_NO_DSP; return NULL; }

  /* Check the interface type to make sure it is valid.
   */
  dsp_c.interface = dspdrv_setup_c.interface;
  switch( dsp_c.interface )
    {
    case DSPIFACE_SOCKET:
      *pStatus_a = DspOpenSocket( &dsp_c, &zCalibration_c );
      if (SS_NORMAL != *pStatus_a) return NULL;
      break ;
    case DSPIFACE_SCSI:
    case DSPIFACE_NATIVE:
      /* Load setup information.  Will move up into socket switch above later */
      *pStatus_a = zcal_load( &zCalibration_c );
      if (*pStatus_a != SS_NORMAL) return NULL;
  
      break;
    default:
      *pStatus_a = DSP_UNK_IFACE;
      return NULL;
      break;
    }
  
  /* Check the dsp type after socket connection.
   */
  dsp_c.itype = dspdrv_setup_c.itype;
  switch( dsp_c.itype )
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
    case DSP_TYPE_RVP7:
    case DSP_TYPE_RVP8:
      break;
    default:
      *pStatus_a = DSP_UNK_TYPE;
      return NULL;
      break;
    }

  dsp_c.iPwRadiateOffWait = dspdrv_setup_c.iPwRadiateOffWait ;
  dsp_c.ipw_wait          = dspdrv_setup_c.ipw_wait ;
  dsp_c.iWidthCount = dspdrv_setup_c.iwidcnt ;

  for (ii=0; ii<4; ii++)
    {
      dsp_c.iMaskSpacing[ii] = dspdrv_setup_c.iRangeMaskSpacing[ii];
      dsp_c.iPWidths[ii]     = dspdrv_setup_c.ipwidths[ii] ;
    }
  dsp_c.iaqclock    = dspdrv_setup_c.iaqclock ;
  
  /* Set the ProcFlags
   */
  dsp_c.iProcFlags  = 0;
  if ( (dsp_c.itype>=DSP_TYPE_RVP7) &&
       (dspdrv_setup_c.lDualReceiver )) dsp_c.iProcFlags |= DSP_PFLG_DUAL_RCV;
  {
    UINT1 iPhaseChoices;
    if (dspdrv_setup_c.iXmtTubeType==XMT_MAGNETRON)
      iPhaseChoices = DSP_PHASE_RANDOM;
    else if (dspdrv_setup_c.lXmtPhaseControl)
      iPhaseChoices = DSP_PHASE_CONTROLLED;
    else
      iPhaseChoices = DSP_PHASE_FIXED;
    if (iPhaseChoices & 0x01) dsp_c.iProcFlags |= DSP_PFLG_PHASE0;
    if (iPhaseChoices & 0x02) dsp_c.iProcFlags |= DSP_PFLG_PHASE1;
  }

  /* Add bits for RVP8 user defined modes
   */
  if( dsp_c.itype >= DSP_TYPE_RVP8 )
    {
      if (dspdrv_setup_c.sUserPModeNames[0][0]) dsp_c.iProcFlags |= DSP_PFLG_MUSER1;
      if (dspdrv_setup_c.sUserPModeNames[1][0]) dsp_c.iProcFlags |= DSP_PFLG_MUSER2;
      if (dspdrv_setup_c.sUserPModeNames[2][0]) dsp_c.iProcFlags |= DSP_PFLG_MUSER3;
      if (dspdrv_setup_c.sUserPModeNames[3][0]) dsp_c.iProcFlags |= DSP_PFLG_MUSER4;
    }
  
  /* Setup the processor data mask flags.  These depend on setup
   * questions and processor type.
   */
  memset( &dsp_c.DataMask, 0, sizeof(dsp_c.DataMask) );
  
  DspMaskSet( &dsp_c.DataMask, DB_DBT);
  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
    DspMaskSet( &dsp_c.DataMask, DB_DBT2);
  
  DspMaskSet( &dsp_c.DataMask, DB_DBZ);
  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
    DspMaskSet( &dsp_c.DataMask, DB_DBZ2);
  
  DspMaskSet( &dsp_c.DataMask, DB_VEL);
  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
    DspMaskSet( &dsp_c.DataMask, DB_VEL2);
  
  DspMaskSet( &dsp_c.DataMask, DB_WIDTH);
  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
    DspMaskSet( &dsp_c.DataMask, DB_WIDTH2);
  
  if( dspdrv_setup_c.lPolarAvailable ) 
    {
      DspMaskSet( &dsp_c.DataMask, DB_ZDR);
      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
	DspMaskSet( &dsp_c.DataMask, DB_ZDR2);
    }
  
  /* Starting here, new data types are only available in the RVP7 or
   * later
   */
  if( dsp_c.itype >= DSP_TYPE_RVP7 ) 
    {
      if( dspdrv_setup_c.lSqiAvailable )
	{
	  DspMaskSet( &dsp_c.DataMask, DB_SQI  ) ;
	  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
	    DspMaskSet( &dsp_c.DataMask, DB_SQI2);
	}
      
      if( dspdrv_setup_c.lPolarAvailable )
	{
	  DspMaskSet( &dsp_c.DataMask, DB_KDP  ) ;
	  if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
	    DspMaskSet( &dsp_c.DataMask, DB_KDP2);
	  
	  if( dspdrv_setup_c.lPhidpAvailable )
	    {
	      DspMaskSet( &dsp_c.DataMask, DB_PHIDP ) ;
	      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
		DspMaskSet( &dsp_c.DataMask, DB_PHIDP2 );
	    }
	  if( dspdrv_setup_c.lRhohvAvailable ) 
	    {
	      DspMaskSet( &dsp_c.DataMask, DB_RHOHV ) ;
	      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
		DspMaskSet( &dsp_c.DataMask, DB_RHOHV2 );
	    }
	  if( dspdrv_setup_c.lLdrAvailable )
	    {
	      DspMaskSet( &dsp_c.DataMask, DB_LDRH ) ;
	      DspMaskSet( &dsp_c.DataMask, DB_LDRV ) ;
	      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
		{
		  DspMaskSet( &dsp_c.DataMask, DB_LDRH2 );
		  DspMaskSet( &dsp_c.DataMask, DB_LDRV2 );
		}
	    }
	  if( dspdrv_setup_c.lPhiAvailable )
	    {
	      DspMaskSet( &dsp_c.DataMask, DB_PHIV ) ;
	      DspMaskSet( &dsp_c.DataMask, DB_PHIH ) ;
	      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
		{
		  DspMaskSet( &dsp_c.DataMask, DB_PHIH2 );
		  DspMaskSet( &dsp_c.DataMask, DB_PHIV2 );
		}
	    }
	  if( dspdrv_setup_c.lRhoAvailable )
	    {
	      DspMaskSet( &dsp_c.DataMask, DB_RHOH ) ;
	      DspMaskSet( &dsp_c.DataMask, DB_RHOV ) ;
	      if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) 
		{
		  DspMaskSet( &dsp_c.DataMask, DB_RHOH2 );
		  DspMaskSet( &dsp_c.DataMask, DB_RHOV2 );
		}
	    }
	}
    }

  /* Add data types that only became available since the RVP8, i.e.,
   * were not provided in the RVP7.
   */
  if( dsp_c.itype >= DSP_TYPE_RVP8 ) {
    DspMaskSet( &dsp_c.DataMask, DB_FLAGS  ) ;
    if (dspdrv_setup_c.iDataWidths == DSP_DATA_8AND16 ) {
      DspMaskSet( &dsp_c.DataMask, DB_FLAGS2 ) ;
    }
  }

  /* On RVP6 only, Verify that the range mask resolution and
   * acquisition clock are within 1% of reasonable values.
   */
  if( dsp_c.itype <= DSP_TYPE_RVP6_2 ) 
    {
      fx = fdsp_stc_res(&dsp_c) / fdsp_mask_res(&dsp_c,0) ;
      if( NINT(100.0 * fx) % 100 ) {
	*pStatus_a = DSP_CLOCK_MASK ; return NULL ;
      }
    }
  
  dsp_c.iopen |= DSP_OPEN ;

  return &dsp_c;
}

/* ======================================================================
 * This subroutine should be called after all I/O to the signal processor
 * has been performed.  It releases the interface so that other programs
 * can use it.
 */      
MESSAGE DspClose
( Dsp *pDsp_a )			/* Dsp structure */
{
  MESSAGE istatus=SS_NORMAL;

  /* Return if not open */
  if( !(pDsp_a->iopen & DSP_OPEN) ) return istatus;

  /* Close down, if the DSP has been opened for I/O
   */
  if(( pDsp_a->iopen & DSP_OPEN_FOR_IO) ||( DSPIFACE_SOCKET==pDsp_a->interface) )
    {
      /* if( pDsp_a->iopen & DSP_OPEN_FOR_IO) lockf( pDsp_a->idsplu, F_ULOCK, 0 ) ; */
      if( close( pDsp_a->idsplu ) )
	istatus = errno_to_message_va( errno, 1, "dsp_close()" ) ;
      
      pDsp_a->iopen &= ~DSP_OPEN_FOR_IO ;
    }

  /* Clear state 
   */
  memset( pDsp_a, 0, sizeof(Dsp) ) ;

  return( istatus ) ;
}

/* =================================================================== 
 * This subroutine reads the saved values from the setup file.
 */
static MESSAGE dsp_load_setup
( struct dsp_manual_setup *pDspSetup_a )
{
  memset( pDspSetup_a, 0, sizeof(struct dsp_manual_setup) ) ;

  return( lim_loadFile( NULL, "setup_dsp.conf", "dsp_manual_setup",
			pDspSetup_a, setup_limits ) ) ;
}

/* ============================================================= 
 * Return a pointer to the setup information the library has.
 */
const struct dsp_manual_setup *DspGetSetupPointer
(Dsp *pDsp_a            /* Dsp structure */
 )
{
  return &dspdrv_setup_c;
}

/* =================================================================== 
 * This subroutine reads the values from the Z calibration file. 
 */
static MESSAGE zcal_load
( struct dsp_refl_cal *pCal_a )
{
  memset( pCal_a, 0, sizeof(struct dsp_refl_cal) ) ;

  return( lim_loadFile( NULL, "zcalib.conf", "dsp_refl_cal",
			pCal_a, setup_limits ) ) ;
}

/* =================================================================== 
 * This subroutine writes new values to the Z calibration file. 
 */
static MESSAGE zcal_save
( const struct dsp_refl_cal *pCal_a )
{
  struct dsp_refl_cal Cal = *pCal_a ;

  strncpy( Cal.sVersion, IRIS_VERSION, 8 ) ; Cal.sVersion[7] = '\0' ;

  return( lim_saveFile( NULL, "zcalib.conf", "dsp_refl_cal",
			&Cal, setup_limits ) ) ;
}

/* ============================================================= 
 * Update the Z calibration information in the library and on disk.
 */
MESSAGE DspSetZcal
(Dsp *pDsp_a,            /* Dsp structure */
 const struct dsp_refl_cal *pzCalibration_a
 )
{
  MESSAGE iStatus;
  if( dsp_c.iopen & DSP_OPEN_FOR_IO) 
    {
      zCalibration_c = *pzCalibration_a;
      switch( dsp_c.interface )
	{
	case DSPIFACE_SOCKET:
	  
	  iStatus = DspSendZcalSocket( pDsp_a, "WCAL|", &zCalibration_c, "dsp_lib",
				       pDsp_a->idsplu );
	  if (iStatus==SS_NORMAL)
	    {
	      /* Read back the acknowledge */
	      iStatus = ReadSocketAck( pDsp_a->idsplu, FALSE, DSP_TIMEOUT_MS );
	    }
	  break ;

	default:
	  iStatus = zcal_save( &zCalibration_c );
	  break;
	}
    }
  else
    iStatus = iMessageMod_va( DSP_NOT_OPEN_IO, 1, "DspSetZcal()" );
  return iStatus;
}
/* ================================================================
 * Send the supplied ZCAL structure in ASCII format over the socket.
 */
MESSAGE DspSendZcalSocket
( Dsp *pDsp_a,
  const char* sPrefix_a,              /* Prefix string */
  const struct dsp_refl_cal *pData_a, /* Data to send */
  const char* sWho_a,                 /* Who I am */
  int iSocket_a )                     /* Socket to write on */
{
  MESSAGE iStatus;
  char *sFile = NULL;
  SINT4 iSize, iChannel;
  char sFileName[FILENAME_SIZE];
  char sPath[PATHNAME_SIZE];
  UINT1 lTempFile = FALSE;
  
  /* Get directory in which to place the temporary file */

  char *sDir = getenv( "IRIS_TEMP" ) ;
  if( NULL == sDir )
    {
      iStatus = iMessageMod_va( USER_NO_ENV, 1, "IRIS_TEMP" );
      goto END;
    }
  
  sprintf( sFileName, "dsp_zcal.%s%d", sWho_a, getpid() );

  /* Write setup info to a temporary file */
  iStatus = lim_saveFileDir( NULL, "IRIS_TEMP", sFileName, "dsp_refl_cal", pData_a,
			     setup_limits );
  if (iStatus!=SS_NORMAL) goto END;

  /* Starting here, we must be sure to delete the temporary file */
  lTempFile = TRUE;

  strcpy( sPath, sDir );
  strcat( sPath, sFileName );
  iStatus = imapopen( sPath, FALSE, (void**)&sFile, &iSize, &iChannel );
  if (iStatus!=SS_NORMAL) goto END;
  /* Starting here, we must be sure to close the temporary file */

  iStatus = WriteSocketData2( iSocket_a, sPrefix_a, strlen(sPrefix_a), sFile, iSize, FALSE );

 END:
  /* Close the file */
  if (sFile)
    {
      MESSAGE iStatus2 = imapclose( sFile, iSize, iChannel );
      if (iStatus2!=SS_NORMAL) sig_signal(iStatus2);
    }

  /* Delete the file */
  if (lTempFile)
    {
      MESSAGE iStatus2 = sig_delete_file( sPath );
      if (iStatus2!=SS_NORMAL) sig_signal(iStatus2);
    }

  return iStatus;
}

/* ============================================================= 
 * Return a pointer to the Z calibration information the library has.
 */
const struct dsp_refl_cal *DspGetZcalPointer
(Dsp *pDsp_a            /* Dsp structure */
 )
{
  return &zCalibration_c;
}

/* ======================================================================
 * Returns the setup flag indicating digital gain.
 * Part of info library, but not callable from a process which did not
 * do the DspOpen().
 */
UINT1 lDspHasDigitalGain
( Dsp *pDsp_a )
{
  return dspdrv_setup_c.ldigital_gain ;
}

/* ======================================================================
 * Returns flag indicating if we are open for I/O
 */
UINT1 lDspOpenForIo
( Dsp *pDsp_a )
{
  return (0 != ( pDsp_a->iopen & DSP_OPEN_FOR_IO));
}
/* ======================================================================
 * Returns the default PRF from setup.
 * Part of info library, but not callable from a process which did not
 * do the DspOpen().
 */
FLT4 fDspDefaultPrf
( Dsp *pDsp_a )
{
  return (FLT4)dspdrv_setup_c.idspprf;
}

