/* **********************************************
 * *                                            *
 * *          DSP Driver Library                *
 * *  ISTCTABLELOAD - Load STC table from file  *
 * *                                            *
 * **********************************************
 * File: libs/dsp/stc_table.c
 *
 *          COPYRIGHT (c) 1989, 1992, 1994, 1997, 1999, 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 <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

/*
 * ===================================================================== 
 * This subroutine reads in the STC curve table containing a value for each 
 * number to be loaded into the DSP. 
 */ 

MESSAGE stc_table_load
  (Dsp *dsp_a,                   /* Pointer to DSP structure */
   volatile UINT2 itable_a[8192] /* Calibration table placed here */
   ) 
{

  FILE *stream;
  SINT4 ii, ij, isize;
  char  spath[PATHNAME_SIZE], *sptr, sline[81];

  /* --------------------------------------------------------------------- */
  isize = 8192;

  /* Initialize output to zeros, in case of error */
  memset( (UINT2 *)itable_a, 0, isize );

  sptr = getenv( "IRIS_CONFIG" );
  if (NULL == sptr)
    {
      sig_signal_va( USER_NO_ENV, 1, "IRIS_CONFIG");
      return USER_NO_ENV ;
    }

  strncpy( spath, sptr, PATHNAME_SIZE-20 );
  spath[PATHNAME_SIZE-20]=0;
  strcat( spath, "COSTCTAB1.DAT" );

  stream = fopen( spath,"r" );
  if (NULL == stream) return errno_to_message_va( errno, 1, spath ) ;
      
  for (ii=0; ii<isize; ii+=8)
    {
      sptr = fgets( sline, sizeof(sline), stream );
      if (NULL == sptr) return errno_to_message_va( errno, 1, spath ) ;

      for (ij=0; ij<8; ij++ )
	{
	  itable_a[ii+ij] = atoi( &sline[7*(1+ij)] );
	}
    }
  fclose( stream );

  return SS_NORMAL;
  
}

/*
 * ===================================================================== 
 * This subroutine write out the STC curve table containing a value for each 
 * number to be loaded into the DSP. 
 */
MESSAGE stc_table_save
  (Dsp *dsp_a,                           /* Dsp structure */
   volatile const UINT2 itable_a[8192]   /* Calibration table placed here */
   ) 
{
  FILE *stream;
  SINT4 ii, isize, ij;
  int ierr;
  char spath[PATHNAME_SIZE], *sptr;
      
  /* --------------------------------------------------------------------- */

  isize = 8192;

  sptr = getenv( "IRIS_CONFIG" );
  if (NULL == sptr)
    {
      sig_signal_va( USER_NO_ENV, 1, "IRIS_CONFIG");
      return USER_NO_ENV ;
    }

  strncpy( spath, sptr, PATHNAME_SIZE-20 );
  spath[PATHNAME_SIZE-20]=0;
  strcat( spath, "COSTCTAB1.DAT" );

  stream = fopen( spath,"w" );
  if (NULL == stream) return errno_to_message_va( errno, 1, spath ) ;
  
      
  for (ii=0; ii<isize; ii+=8)
    {
      ierr = fprintf( stream, "%7d", ii );
      if (ierr < 0) return errno_to_message_va( errno, 1, spath ) ;

      for (ij=0; ij<8; ij++ )
	{
	  ierr = fprintf( stream, "%7d", itable_a[ii+ij] );
	  if (ierr < 0) return errno_to_message_va( errno, 1, spath ) ;
	}
      ierr = fprintf( stream, "\n" );
      if (ierr < 0) return errno_to_message_va( errno, 1, spath ) ;
    }
  fclose( stream );

  return SS_NORMAL;
}
      
/* ======================================================================
 * This routine is used to help generate the STC table to be loaded into 
 * the signal processor.  This table can be in two different formats, thus 
 * this utility is needed to help hide that from the outside user.  This 
 * subroutine effectively hides the issues of how many bits, where those bits
 * are, and how they are packed into words in the table.  The table range
 * resolution is not hidden by this subroutine.
 * 
 * This routine takes a given STC voltage value, and stuffs it into the table. 
 * 
 * Entry parameters
 *     dsp_a      - Dsp structure
 *     FVOLTAGE_A - Voltage desired for the bin, range 0 to 1. 
 *     ITABLE_A   - Table which may have other values already in it. 
 * Exit parameters 
 *     ITABLE_A - Table with the appropriate value stuck in
 */      
void stc_entry_gen
(Dsp   *dsp_a,
 volatile UINT2 itable_a[],
 FLT4  fvoltage_a,
 UINT2 ibin_a )  /* Which range bin to put it in (origin 1) */
{
  UINT2 igain;

  /* ---------------------------------------------------------------------- */
  /* Set up simulated sample */
      
  /* There are two different algorithms, depending upon whether the dsp takes
   * its stc data packed one per byte or 1 value in an integer*2.
   */

  igain = 0x0ffc & (NINT(fvoltage_a*4095.)-2048 );
  itable_a[ibin_a-1] = igain;
      
  return;
}

/* ======================================================================
 * This routine is used to help generate the fixed gain table loaded into
 * the signal processor.  This table can be in two different formats, thus 
 * this utility is needed to help hide that from the outside user.  This 
 * subroutine effectively hides the issues of how many bits, where those bits
 * are, and how they are packed into words in the table.  The table range
 * resolution is not hidden by this subroutine.
 *
 * This routine takes a given ST*voltage value, and stuffs it into all the
 * entries in the table. 
 *
 * Entry parameters
 *    dsp_a      - Dsp structure
 *    FVOLTAGE_A - Voltage desired for the bin, range 0 to 1. 
 * Exit parameters 
 *    ITABLE_A - Table with the appropriate value stuck in
 */
void stc_fixed_gen
(Dsp *dsp_a,
 volatile UINT2 itable_a[],
 volatile const FLT4 *fvoltage_a )
{
  UINT2 igain,ibin;
      
  igain = 0x0ffc & (NINT(*fvoltage_a*4095.)-2048 );
  for ( ibin=0; ibin<8192; ibin++)
    itable_a[ibin] = igain;
         
  return;
}
      
/* =================================================================== 
 * This function determines the voltage which will best produce the gain 
 * requested.
 *
 *  jmh  25 Nov 1991   Renamed, and added parameter for size of table
 *  ads  22 Sep 1989   Gain table may be either increasing or decreasing.
 *
 * Entry parameters: 
 *     FCURVE_A - Gain control calibration table of gain values covering
 *                the voltage span of the gain control output.
 *     FGAIN_A  - The gain value we wish to find.
 *     LBAD_A   - Contains previous bad state.
 *
 * Exit parameters:
 *     LBAD_A   - Set to nonzero if the gain value cannot be found, otherwise
 *                unchanged.
 */ 
FLT4 fstc_search
( const FLT4 fcurve_a[IANALOG_GCAL_SIZE],
  const FLT4 *fgain_a,
  UINT2 *lbad_a ) 
{      
  FLT4 rpart;
  SINT4 iresult, ibit, islp, ioff;
  FLT4 fresult;
      
  /* Macro to return table value, reading either forward or
   * backward through the table.
   */
#define FCV(iarg)  (fcurve_a[ (islp * iarg) + ioff ])

  /* ----------------------------------------------------------------------
   * Set up internal variables for reversing curve if necessary
   */
  islp = 1;
  ioff = 0;
  if( fcurve_a[0] > fcurve_a[IANALOG_GCAL_SIZE-1] )
    {
      islp = -1;
      ioff = IANALOG_GCAL_SIZE;
    }

  /* Use a binary search to find the index in the table which selects the value
   * just below the requested gain.  Note that we use enough bits to span a
   * 512 entry table.  The actual table size can be much smaller.
   */  
  iresult = 0;
  for ( ibit = 8; ibit>=0; ibit--)
    {
      iresult = IBSET( iresult, ibit );
      if( iresult >= IANALOG_GCAL_SIZE )
	{
	  iresult = IBCLR( iresult, ibit );
	}
      else if( FCV(iresult) >= *fgain_a )
	{
	  iresult = IBCLR( iresult, ibit );
	}
    }
      
  /* Final result is extrapolated between the closest two points: */
  if( iresult == IANALOG_GCAL_SIZE-1 )
    {
      fresult=1.0;
    }
  else
    {

      /* Check for flat portion of the curve: */
      if( FCV(iresult+1) <= FCV(iresult) )
	rpart = 0.0;
      else
	rpart = ( *fgain_a - FCV(iresult) ) / ( FCV(iresult+1) - FCV(iresult) );
        
      fresult = ( rpart + (FLT4)(iresult) ) / (FLT4)(IANALOG_GCAL_SIZE-1);
    }
      
  if( iresult == 0 ) 
    if( *fgain_a < FCV(0) ) *lbad_a = TRUE;
         
      
  /* If we inverted the curve to begin with, then invert the result too.
   */
  if( islp == -1 ) fresult = 1.0 - fresult;

  return fresult;
}
