/* **********************************************************************
 * *                                                                    *
 * *                 Radar/Antenna Control Routines                     *
 * *                                                                    *
 * **********************************************************************
 * File: libs/antenna/ant_control.c
 *
 *    COPYRIGHT (c) 1989, 1997, 1999, 2000, 2001, 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.  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 <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "sigtypes.h"
#include "event_flags.h"
#include "user_lib.h"
#include "antenna_lib.h"
#include "antenna_int.h"
#include "setup.h"
#include "ant_global.h"

/* ------------------------------
 * Returns input and output byte activity counts for channel 1 of the antenna
 *
 * Exit parameters:
 *   incnt_a   - Number of characters received by antenna driver
 *   ioutcnt_a - Number of characters sent by antenna driver
 *   inerr_a   - Number of errors received by antenna driver
 *   iouterr_a - Number of errors sent by antenna driver
 */
void ant_get_activity( SINT4* incnt_a, SINT4* ioutcnt_a, SINT4* inerr_a,
		       SINT4* iouterr_a )
{
  antGlobal_c->info.iactive += 1;
  *incnt_a   = antGlobal_c->info.io[0].iin_cnt;
  *ioutcnt_a = antGlobal_c->info.io[0].iout_cnt;
  *inerr_a   = antGlobal_c->info.io[0].iin_ercnt;
  *iouterr_a = antGlobal_c->info.io[0].iout_ercnt;
}

/* ------------------------------
 * Returns input and output byte activity counts for channel 2 of the antenna
 */
void ant_get_activity2
(SINT4* incnt_a,    /* Number of characters received */
 SINT4* ioutcnt_a,  /* Number of characters sent */
 SINT4* inerr_a,    /* Number of bad characters received */
 SINT4* iouterr_a ) /* Number of bad characters sent */
{
  antGlobal_c->info.iactive += 1;
  *incnt_a   = antGlobal_c->info.io[1].iin_cnt;
  *ioutcnt_a = antGlobal_c->info.io[1].iout_cnt;
  *inerr_a   = antGlobal_c->info.io[1].iin_ercnt;
  *iouterr_a = antGlobal_c->info.io[1].iout_ercnt;
}

/* ------------------------------
 * Returns a list of the active hosts talking to the antenna library.
 */
void  AntGetNetActivity
( struct ant_nethost_info *pNetHost_a,  /* Table to hold net host info */
  SINT4 iSize_a )                       /* Size of the supplied table */
{
  SINT4 iByteSize, iSize;
  iSize = MIN( iSize_a, ANT_NETHOST_MAX );
  iByteSize = iSize*sizeof(struct ant_nethost_info);
  
  lock_sem( ANTENNA_NETHOST_SEM );
  /* Cast discards volatile */
  memcpy( pNetHost_a, (struct ant_nethost_info*)antGlobal_c->info.NetHost, iByteSize );
  unlock_sem( ANTENNA_NETHOST_SEM );
  
  if (iSize<iSize_a)
    {
      SINT4 iTailSize = iSize_a-iSize;
      iByteSize = iTailSize*sizeof(struct ant_nethost_info);
      /* Zero the tail end of the table */
      memset( pNetHost_a+iSize, 0, iByteSize );
    }
}

/* ---------------------------------------------
 * Register source of network packets 
 */
void AntRegisterNetHost
( UINT4 iFromAddress_a )
{
  UINT4 iBTime;
  int iIndex;
  
  getbtime( &iBTime );

  lock_sem( ANTENNA_NETHOST_SEM );
  for (iIndex=0; iIndex<ANT_NETHOST_MAX; iIndex++)
    {
      /* Cast discards volatile
       */
      struct ant_nethost_info *pInfo =
	(struct ant_nethost_info*)antGlobal_c->info.NetHost+iIndex;

      /* Update time on match
       */
      if (pInfo->iAddress==iFromAddress_a)
	{ pInfo->iBTime=iBTime; break; }

      /* Fill in empty slot.  Do not worry about temporary double slot use.
       */
      if (pInfo->iAddress==0)
	{ pInfo->iAddress=iFromAddress_a; pInfo->iBTime=iBTime; break; }
    }
  unlock_sem( ANTENNA_NETHOST_SEM );
}

/* ---------------------------------------------
 * Prune the network host table for stale entries
 */
void AntPruneNetHost
( UINT4 iCurrentBTime_a )  /* The current time */
{
  int iIndex;
  
  lock_sem( ANTENNA_NETHOST_SEM );
  for (iIndex=0; iIndex<ANT_NETHOST_MAX; iIndex++)
    {
      /* Cast discards volatile */
      struct ant_nethost_info *pInfo=(struct ant_nethost_info*)antGlobal_c->info.NetHost+iIndex;
      /* Stop at first empty slot */
      if (pInfo->iAddress==0) break;

      /* Remove after 60 seconds of inactivity */
      if (btimeGrace(iCurrentBTime_a - pInfo->iBTime) > 60000)
	{
	  SINT4 iByteSize, iSize;
	  iSize = (ANT_NETHOST_MAX - iIndex) - 1;
	  iByteSize = iSize*sizeof(struct ant_nethost_info);
	  
	  /* Erase by moving table down one slot */
	  memmove( pInfo, pInfo+1, iByteSize );
	  memset( pInfo+iSize, 0, sizeof(struct ant_nethost_info) );
	  break;
	}
    }
  unlock_sem( ANTENNA_NETHOST_SEM );
}

/* ------------------------------
 * Get the diagnostic buffer containing the last transmission starting with
 * a 80 hex.
 */
void  ant_get_diagnostic
( UINT1* sbuffer_a, 
  SINT4 ibuf_size_a, 
  SINT4* ifill_size_a )
{
  *ifill_size_a = antGlobal_c->info.io[0].iant_diag_size;
  if (*ifill_size_a>ibuf_size_a) *ifill_size_a = ibuf_size_a;

  memmove( sbuffer_a, (void *)antGlobal_c->info.io[0].sant_diagnostic, *ifill_size_a );
}

/* ------------------------------
 * Get the NST report information for all four sites, along with site codes.
 */
void ant_get_nst_cmd
(char site_list_a[4][4],    /* site id list */
 UINT4 *ifaults_a )        /* Nonzero if there is a fault */
{
  UINT4 isite;
  antGlobal_c->info.iactive += 1;

  for (isite=0; isite<4; isite++)
    {
      memmove( site_list_a[isite], (char *)antGlobal_c->setup.sreport_sites[isite], 3 );
      site_list_a[isite][3] = 0;
    }
  *ifaults_a = antGlobal_c->info.icurrent_nst_faults;
}

/* ------------------------------
 * Get the diagnostic buffer containing the largest error transmission starting with
 * a character other than 80, B0, or C0 hex.
 */
void  ant_get_odiagnostic
(UINT1* sbuffer_a, 
 SINT4 ibuf_size_a, 
 SINT4* ifill_size_a )
{
  *ifill_size_a = antGlobal_c->info.io[0].iother_diag_size;
  if (*ifill_size_a>ibuf_size_a) *ifill_size_a = ibuf_size_a;

  memmove( sbuffer_a, (void *)antGlobal_c->info.io[0].sother_diagnostic, *ifill_size_a );

  antGlobal_c->info.io[0].iother_diag_size = 0;
}

/* ------------------------------
 * Get the antenna process IDs
 */
void ant_get_pids
( UINT4 ircv_pid_a[2],		/* RCV process PID, zero means none */
  UINT4 ixmt_pid_a[2],		/*   (Process may have stopped) */
  UINT4 *iLogPID_a )		/* Logging process PID */
{
  antGlobal_c->info.iactive += 1;
  ircv_pid_a[0] = antGlobal_c->info.io[0].iRcvPid;
  ircv_pid_a[1] = antGlobal_c->info.io[1].iRcvPid;
  ixmt_pid_a[0] = antGlobal_c->info.io[0].iXmtPid;
  ixmt_pid_a[1] = antGlobal_c->info.io[1].iXmtPid;
  *iLogPID_a    = antGlobal_c->info.iLogPid ;
}

/* ------------------------------
 * Get the rst mode request from the RCP.
 */
void ant_get_rstmode
(char  *srst_name_a,		/* Put name of RST here */
 SINT4 iname_size_a,		/* Number of characters available in above */
 UINT4 *imode_a )		/* Put mode number here */
{
  UINT4 ibtime, iage, iNewMode ;

  /* Get the age of the last received record in miliseconds.
   */
  antGlobal_c->info.iactive += 1 ; getbtime( &ibtime ) ;
  iage = btimeGrace( ibtime - antGlobal_c->info.iBtimePkt ) ;

  /* If the RCP is alive, then get the requested mode from the last
   * report that we received.  If the RCP is dead, then simulate a
   * 10-second request for the special dead mode.
   */
  if( iage < 5000 ) {
    iNewMode = antGlobal_c->info.irequested_rst_mode ;
  } else {
    if( iage < 15000 ) iNewMode = antGlobal_c->setup.iDeadModeReq ;
    else               iNewMode = 0 ;
  }

  /* Return parameters for the new mode request.
   */
  if( (iNewMode >= 1) && (iNewMode <= 7) ) {
    SINT4 imax_length = MIN(iname_size_a,16) ;
    strncpy( srst_name_a, (char *)antGlobal_c->setup.sreport_mode[iNewMode],
	     imax_length ) ;
    srst_name_a[imax_length-1] = '\0' ;
    *imode_a = iNewMode ;

  } else {
    srst_name_a[0] = '\0' ;
    *imode_a = 0 ;
  }
}

/* ------------------------------
 * Get back a copy of the rst mode report being sent to the RCP
 */
void ant_get_rstmode_cmd
(char  *srst_name_a,   /* Put name of RST here */
 SINT4 iname_size_a,   /* Number of characters available in above */
 UINT4 *imode_a )      /* Put mode number here */
{
  SINT4 imax_length;
  antGlobal_c->info.iactive += 1;

  imax_length = MIN(iname_size_a,20);
  strncpy( srst_name_a, (char *)antGlobal_c->info.scurrent_rst_mode, imax_length );
  srst_name_a[imax_length-1] = 0;
  *imode_a = antGlobal_c->info.icurrent_rst_mode;
}

/* ------------------------------
 * Get the minimum and maximum siggen values in dBm.
 */
void ant_get_siggen_limits
(SINT4 ilimits_a[2]
 )
{
  antGlobal_c->info.iactive += 1;
  ilimits_a[0] = antGlobal_c->setup.isiggen_min;
  ilimits_a[1] = antGlobal_c->setup.isiggen_max;
}

/* ------------------------------
 * Return current time information.
 */
void ant_get_time
(struct ymds_time* time_a,
 char  sTZName_a[TZNAME_SIZE],   /* Assumed time zone of time in packet */
 UINT4* iage_a,
 SINT4* idiff_a, 
 UINT2* lflag_a)
{
  UINT4 ibtime;
  antGlobal_c->info.iactive += 1;

  getbtime( &ibtime );
  *time_a    = antGlobal_c->info.time_in_packet;
  *iage_a    = btimeGrace( ibtime - antGlobal_c->info.itime_btime ) ;
  *idiff_a   = antGlobal_c->info.itime_error;
  *lflag_a   = antGlobal_c->info.ltime_packet_received;
  if (antGlobal_c->setup.lTimePacketUtc)
    strcpy( sTZName_a, "UTC" );
  else
    {
      tzset();
      strncpy( sTZName_a, tzname[0], TZNAME_SIZE-1 );
      sTZName_a[TZNAME_SIZE-1] = 0;
    }
}

/* ------------------------------
 * Turn IRIS auto radiate mode ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_auto( UINT2 lon_a ) { AntSetArg( ANTSET_AUTORAD, lon_a ) ; }
void __ant_set_auto( UINT2 lon_a )
{
  if( lon_a ) antGlobal_c->info.icbits |=  ACF_AUTO_RAD ;
  else        antGlobal_c->info.icbits &= ~ACF_AUTO_RAD ;
}

/* ------------------------------
 * Turn IRIS Control mode ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_iris( UINT2 lon_a ) { AntSetArg( ANTSET_IRISMODE, lon_a ) ; }
void __ant_set_iris( UINT2 lon_a )
{
  if(  lon_a ) antGlobal_c->info.icbits |=  ACF_IRIS ;
  else         antGlobal_c->info.icbits &= ~ACF_IRIS ;
}

/* ------------------------------
 * Turn the noise source generator ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_noise( UINT2 lon_a ) { AntSetArg( ANTSET_NOISEGEN, lon_a ) ; }
void __ant_set_noise( UINT2 lon_a )
{
  if( lon_a ) antGlobal_c->info.icbits |=  ACF_NOISE ;
  else        antGlobal_c->info.icbits &= ~ACF_NOISE ;
}

/* ------------------------------
 * Toggle the noise source on for the specified time.
 */
void   ant_toggle_noise( void ) { AntSet( ANTSET_NOISETOG ) ; }
void __ant_toggle_noise( void )
{
  SINT4 idelay = (SINT4)(1000.0 * antGlobal_c->setup.fnoise_toggle_time) ;
  if( idelay > 0 ) {
    antGlobal_c->info.icbits |= ACF_NOISE ;
    antGlobal_c->info.ichange_count += 1;
    sig_microSleep( 1000 * idelay ) ;
    antGlobal_c->info.icbits &= ~ACF_NOISE ;
  }
}

/* ------------------------------
 * Set timeout for network status updates.  A site will be considered
 * faulted if its status has not been updated for a long time.
 */
void   ant_set_nst_timeout( UINT4 imilisec_a ) { AntSetArg( ANTSET_NSTOUT, imilisec_a ) ; }
void __ant_set_nst_timeout( UINT4 imilisec_a )
{
  antGlobal_c->info.iNstTimeout = imilisec_a ;
}

UINT4 ant_get_nst_timeout( void )
{
  return( antGlobal_c->info.iNstTimeout ) ;
}

/* ------------------------------
 * Set the NST report information for one site to report to the RCP
 */
void ant_set_nst
( const char *snst_site3_a,	/* 3-letter site id */
  UINT2 lfault_a )		/* Nonzero if there is a fault */
{
  char sSetCmd[ ANTSETCMDLEN ] ;
  sprintf( sSetCmd, "%s %s %d", ANTSET_NSTFLT, snst_site3_a, lfault_a ) ;
  AntSet( sSetCmd ) ;
}
void __ant_set_nst( const char *snst_site3_a, UINT2 lfault_a )
{
  UINT4 isite ;
  
  /* Skip if not reporting NST information
   */
  if ( !(antGlobal_c->info.ic_mask & ACF_NST) ) return ;
  
  /* Note that the same site code may be set for more than 1 bit
   */
  for( isite=0 ; isite < 4 ; isite++ ) {
    if( 0 == strncmp
	(snst_site3_a,(char *)antGlobal_c->setup.sreport_sites[isite], 3 ) ) {
      /* If we find a site match, then set or clear the bit
       */
      UINT4 imask = IBSET( 0, isite ) ;
      if( lfault_a ) antGlobal_c->info.icurrent_nst_faults |=  imask ;
      else           antGlobal_c->info.icurrent_nst_faults &= ~imask ;

      getbtime( &antGlobal_c->info.iNstUpdateTimes[ isite ] ) ;
    }
  }
}

/* ------------------------------
 * Set the RST mode to report to the RCP
 */
void ant_set_rstmode
( const char *srst_name_a,
  UINT2 lwait_a )
{
  char sSetCmd[ ANTSETCMDLEN ] ;
  sprintf( sSetCmd, "%s %s %d", ANTSET_RSTMODE, srst_name_a, lwait_a ) ;
  AntSet( sSetCmd ) ;
}
void __ant_set_rstmode( const char *srst_name_a, UINT2 lwait_a )
{
  UINT4 imode ; SINT4 idelay ;

  /* Wait for the delay both before and after setting the report */
  if (lwait_a)
    idelay = NINT( 1000.0*antGlobal_c->setup.freport_mode_delay );
  else
    idelay = 0;

  sig_microSleep( 1000 * idelay ) ;
  strncpy( (char *)antGlobal_c->info.scurrent_rst_mode, srst_name_a, 20 );
  antGlobal_c->info.scurrent_rst_mode[19] = 0;

  for (imode=1; imode<8; imode++)
    {
      if (0 == strncmp((char *)antGlobal_c->info.scurrent_rst_mode,
		       (char *)antGlobal_c->setup.sreport_mode[imode], 16 ))
	{
	  antGlobal_c->info.icurrent_rst_mode = imode;
	  antGlobal_c->info.ichange_count += 1;
	  sig_microSleep( 1000 * idelay ) ;
	  return;
	}
    }
  antGlobal_c->info.icurrent_rst_mode = 0;

  sig_microSleep( 1000 * idelay );
}

/* ------------------------------
 * Turn servo amplifier ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_servo( UINT2 lon_a ) { AntSetArg( ANTSET_SERVO, lon_a ) ; }
void __ant_set_servo( UINT2 lon_a )
{
  if(  lon_a ) antGlobal_c->info.icbits |=  ACF_SERVO ;
  else         antGlobal_c->info.icbits &= ~ACF_SERVO ;
}

/* ------------------------------
 * Turn transmitter ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_radiate( UINT2 lon_a ) { AntSetArg( ANTSET_RADIATE, lon_a ) ; }
void __ant_set_radiate( UINT2 lon_a )
{
  if( 0 != lon_a ) 
    {
      if (!(antGlobal_c->info.icbits & ACF_RADIATE))
	{
	  antGlobal_c->info.icbits |=  ACF_RADIATE;
	  GetTimeYmds( &antGlobal_c->info.RadiateOnYmds, TIME_FLG_UTC );
	}
    }
  else
    antGlobal_c->info.icbits &= ~ACF_RADIATE;
}
/* ------------------------------
 * Toggle the radiate if needed.  Do not toggle if the radiate has not
 * had time to fully warm up and turn on.  
 */
void   ant_toggle_radiate(SINT4 iWarmTimeMs_a) { AntSetArg(ANTSET_TOGGLE,iWarmTimeMs_a) ; }
void __ant_toggle_radiate(SINT4 iWarmTimeMs_a)
{
  SINT4 iDelayMs = (SINT4)(1000.0 * antGlobal_c->setup.fRadiateTogglePeriod) ;
  SINT4 iTimeoutSec = antGlobal_c->setup.itoggle_time;
  UINT1 lToggle=FALSE;
  
  /* Skip everything if the bit cannot be controlled
   */
  if( !(antGlobal_c->info.ic_mask&ACF_RADIATE) ) return;
  
  /* If the radiate toggle time is nonzero, and the radiate toggle
   * timeout is nonzero, and the radiate status bit is available, and
   * old command was on, and the status is off, then we desire to
   * toggle the command.
   */
  if( (iDelayMs>0) && (iTimeoutSec > 0) &&
      (antGlobal_c->info.is_mask&ASF_RADIATE) && 
      (antGlobal_c->info.icbits&ACF_RADIATE) &&
      (!(antGlobal_c->info.isbits&ASF_RADIATE)) )
    {
      SINT4 iOnTimeMs;
      struct ymds_time Ymds;
      GetTimeYmds( &Ymds, TIME_FLG_UTC ) ;	/* Get the current time */
      
      iOnTimeMs = iYmdsSubtractMs( &Ymds, &antGlobal_c->info.RadiateOnYmds );

      /* Also skip the whole thing if the radiate has not been on long
       * enough to realistically be on.
       */
      if ( iOnTimeMs > iWarmTimeMs_a )
	{

	  if (antGlobal_c->info.lwanted_to_toggle)
	    /* If the previous call also wanted to toggle the radiate,
	     * then compare times, and skip the toggling if it has
	     * been more than toggle time of continuous toggling.
	     */
	    {
	      SINT4 iAge = iYmdsSubtractMs( &Ymds, &antGlobal_c->info.ToggleYmds );
	      if ( iAge < (iTimeoutSec*1000) ) lToggle = TRUE;
	    }
	  else
	    /* If the previous call did not want to toggle the
	     * radiate, then save the time, and toggle it.
	     */
	    {
	      antGlobal_c->info.lwanted_to_toggle = TRUE;
	      antGlobal_c->info.ToggleYmds        = Ymds;
	      lToggle = TRUE;
	    }
	}
      else
	{
	  antGlobal_c->info.lwanted_to_toggle = FALSE;
	}
    }
  else
    {
      antGlobal_c->info.lwanted_to_toggle = FALSE;
    }

  if (lToggle)
    {
      antGlobal_c->info.icbits &= ~ACF_RADIATE;
      antGlobal_c->info.ichange_count += 1;
      sig_microSleep( 1000 * iDelayMs ) ;

      /* Finally turn it back on */
      antGlobal_c->info.icbits |=  ACF_RADIATE;
      GetTimeYmds( &antGlobal_c->info.RadiateOnYmds, TIME_FLG_UTC );
    }

}
/* ------------------------------
 * Wait until the radiate has been on the specified time.  This allows
 * slow ramp up and time for the burst pulse detection to go true.  
 */
void   ant_wait_for_radiate(SINT4 iWarmTimeMs_a) { AntSetArg(ANTSET_WAITRAD,iWarmTimeMs_a) ; }
void __ant_wait_for_radiate(SINT4 iWarmTimeMs_a)
{
  SINT4 iOnTimeMs, iRemainingMs;
  struct ymds_time Ymds;

  /* Skip everything if the bit cannot be controlled
   */
  if ( !(antGlobal_c->info.ic_mask&ACF_RADIATE) ) return;

  /* Also skip if no warmup time is required
   */
  if ( iWarmTimeMs_a<=0 ) return;

  /* Also skip if radiate is not requested on
   */
  if ( !(antGlobal_c->info.icbits&ACF_RADIATE) ) return;

  GetTimeYmds( &Ymds, TIME_FLG_UTC ) ;	/* Get the current time */
  
  iOnTimeMs = iYmdsSubtractMs( &Ymds, &antGlobal_c->info.RadiateOnYmds );
  iRemainingMs = iWarmTimeMs_a - iOnTimeMs;

  /* Only wait if the desired time has not elapsed.
   */
  if ( iRemainingMs>0 )
    {
      /* Wait a maximum of 60 seconds
       */
      if (iRemainingMs>60000) iRemainingMs=60000;
      sig_microSleep( 1000 * iRemainingMs ) ;
    }
}

/* ------------------------------
 * Turn the signal generator CW mode ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_sigcw( UINT2 lon_a ) { AntSetArg( ANTSET_SIGGENCW, lon_a ) ; }
void __ant_set_sigcw( UINT2 lon_a )
{
  if( lon_a ) antGlobal_c->info.icbits |=  ACF_SIGCW;
  else        antGlobal_c->info.icbits &= ~ACF_SIGCW;
}

/* ------------------------------
 * Turn the signal generator ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_siggen( UINT2 lon_a ) { AntSetArg( ANTSET_SIGGENOUT, lon_a ) ; }
void __ant_set_siggen( UINT2 lon_a )
{
  if( lon_a ) antGlobal_c->info.icbits |=  ACF_SIGGEN ;
  else        antGlobal_c->info.icbits &= ~ACF_SIGGEN ;
}

/* ------------------------------
 * Set the attenuation setting of the signal generator.
 */
void ant_set_siggen_value
( const SINT4 ivalue_a,
  const UINT2 lwait_a )
{
  char sSetCmd[ ANTSETCMDLEN ] ;
  sprintf( sSetCmd, "%s %d %d", ANTSET_SIGGENVAL, ivalue_a, lwait_a ) ;
  AntSet( sSetCmd ) ;
}
void __ant_set_siggen_value( const SINT4 ivalue_a, const UINT2 lwait_a )
{
  SINT4 ivalue ;

  ivalue = MIN( antGlobal_c->setup.isiggen_max, ivalue_a );
  ivalue = MAX( antGlobal_c->setup.isiggen_min, ivalue );
  antGlobal_c->info.idsiggen_value = ivalue;

  if( lwait_a ) {
    UINT4 idelay = NINT(1000.*antGlobal_c->setup.fsiggen_settle);
    antGlobal_c->info.ichange_count += 1;
    sig_microSleep( 1000 * idelay ) ;
  }
}

/* ------------------------------
 * Turn transmitter/receiver chassis power ON/OFF
 *   lon_a   - nonzero means ON
 */
void   ant_set_trpower( UINT2 lon_a ) { AntSetArg( ANTSET_TRPOWER, lon_a ) ; }
void __ant_set_trpower( UINT2 lon_a )
{
  if( lon_a ) antGlobal_c->info.icbits |=  ACF_TRPOWER ;
  else        antGlobal_c->info.icbits &= ~ACF_TRPOWER ;
}

/* ------------------------------
 * Reset the antenna controller
 */
void   ant_set_reset( void ) { AntSet( ANTSET_RESET ) ; }
void __ant_set_reset( void ) 
{
  antGlobal_c->info.icbits |= ACF_RESET ;
}

/* ------------------------------
 * Set Pulse/Band Width Lines
 */
void   ant_set_pwbw( UINT2 ipwbw_a ) { AntSetArg( ANTSET_PWBW, ipwbw_a ) ; }
void __ant_set_pwbw( UINT2 ipwbw_a )
{
  antGlobal_c->info.iDesiredPwbw = (ANT_PWBW_MASK & ipwbw_a);
}

/* ------------------------------
 * Set Polarization XMT control
 */
void   ant_set_polar( UINT2 iPolar_a ) { AntSetArg( ANTSET_POLAR, iPolar_a ) ; }
void __ant_set_polar( UINT2 iPolar_a )
{
  antGlobal_c->info.iDesiredPolar = iPolar_a;
}

/* ------------------------------
 * Set the system time clock.  A request is made to the antenna transmit
 * process to actually do the work.  This is necessary because the calling
 * process probably does not have the required priveleges to actually set
 * the system time.
 */
void   ant_set_time( void ) { AntSet( ANTSET_TIME ) ; }
void __ant_set_time( void ) 
{
  if( lokay_to_set_time_int( &antGlobal_c->info ) )
    {
      antGlobal_c->info.lset_system_time = TRUE ;
    }
}

/* ------------------------------
 * Return the height of the ground at the antenna, in meters.
 */
FLT4 fant_ground_height( void )
{
  antGlobal_c->info.iactive += 1;
  return (FLT4)antGlobal_c->setup.ignd_hgt;
}

/* ------------------------------
 * Get/Put a chat-mode character.  A return value of -1 indicates that
 * no characters were available for getting.
 */
SINT4 iant_get_chat( void )
{
  if (antClient_c.iClientType == ANT_IAM_CHATTER)
    return( bfifo_read( (void *)&antGlobal_c->vdat.ChatIn ) ) ;
  else
    return -1;
}

void AntSetChatDestination
( UINT4 iDestination_a )  /* Destination in network byte order */
{
  if (antClient_c.iClientType == ANT_IAM_CHATTER)
    antGlobal_c->info.iChatDestination = iDestination_a;
}

void   ant_put_chat( UINT1 ichar_a )
{
  if (antClient_c.iClientType == ANT_IAM_CHATTER)
    AntSetArg( ANTSET_CHAT, ichar_a ) ;
}
void __ant_put_chat( UINT1 ichar_a )
{
  while( -1 == bfifo_write( (void *)&antGlobal_c->vdat.ChatOut, ichar_a ) )
    sig_microSleep( 10000 ) ;
}

/* ------------------------------
 * Get summary time since the last update.  Since the Packet information may
 * be arriving separately from the Angle information, and INU information,
 * 'Age' is defined as whichever data are oldest. 
 * If there are no sources, then return 0.
 */
UINT4 iAntAgeSummary( void )
{
  UINT4 iAge, iAgePkt, iAgeAng, iAgeInu ;
  UINT1 iMask;

  iMask = AntGetAges( &iAgePkt, &iAgeAng, &iAgeInu );
  iAge=0;

  if (iMask & ANT_EXPECT_PKT)  iAge = MAX( iAge, iAgePkt );
    
  if (iMask & ANT_EXPECT_ANG)  iAge = MAX( iAge, iAgeAng ) ;
    
  if (iMask & ANT_EXPECT_INU)  iAge = MAX( iAge, iAgeInu );
  
  return( iAge ) ;
}
/* ------------------------------
 * Get time since the last updates.  Also a mask of which ages are applicable
 * to this system. Only turn on bits if the data is independent of the packet
 * data stream.
 */
UINT1 AntGetAges
( UINT4 *pAgePkt_a,
  UINT4 *pAgeAng_a,
  UINT4 *pAgeInu_a )
{
  UINT1 iMask=0;
  UINT4 iBtimeNow;
  antGlobal_c->info.iactive += 1 ; getbtime( &iBtimeNow ) ;

  /* The only way to not have angles is if we pick no packets, 
   * and pick packets for angle source.
   */
  if (ANTIFACE_NONE != antGlobal_c->setup.iInterfaceType)
    {
      iMask |= ANT_EXPECT_PKT;
    }
  if ( ANTANGSRC_NORMAL != antGlobal_c->setup.iAngleSource)
    {
      iMask |= ANT_EXPECT_ANG;
      if ( lant_moving_platform( ) )
	{
	  iMask |= ANT_EXPECT_INU;
	}
    }

  *pAgePkt_a  = btimeGrace( iBtimeNow - antGlobal_c->info.iBtimePkt ) ;
  *pAgeAng_a  = btimeGrace( iBtimeNow - antGlobal_c->info.iBtimeAng ) ;
  *pAgeInu_a = btimeGrace( iBtimeNow - antGlobal_c->info.iBtimeShip ) ;

  return iMask;
}

/* ------------------------------
 * Return current radar/antenna command word
 */
UINT4 iant_cmd( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.icbits & antGlobal_c->info.ic_mask ) ;
}

/* ------------------------------
 * Return current radar/antenna command word
 */
UINT4 iant_cmd_mask( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.ic_mask ) ;
}

/* ------------------------------
 * Return current state for polarization
 */
UINT2 iant_polar( UINT1 *lOk_a )
{
  antGlobal_c->info.iactive += 1;
  *lOk_a = (0 != (antGlobal_c->info.isbits & ASF_POLAR));
  return( antGlobal_c->info.iActualPolar ) ;
}

/* ------------------------------
 * Return current request for pulsewidth/bandwidth control
 */
UINT2 iant_polar_cmd( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.iDesiredPolar ) ;
}

/* ------------------------------
 * Return current state of the pulsewidth/bandwidth
 */
UINT4 iant_pwbw( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.iActualPwbw ) ;
}

/* ------------------------------
 * Return current request for pulsewidth/bandwidth control
 */
UINT4 iant_pwbw_cmd( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.iDesiredPwbw ) ;
}

/* ------------------------------
 * Return current signal generator value
 */
SINT4 iant_siggen( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.isiggen_value ) ;
}

/* ------------------------------
 * Return current request for signal generator value
 */
SINT4 iant_siggen_cmd( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.idsiggen_value ) ;
}

/* ------------------------------
 * Return current radar/antenna status word
 */
UINT4 iant_stat( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.isbits & antGlobal_c->info.is_mask ) ;
}

/* ------------------------------
 * Return current radar/antenna status word mask of bits available
 */
UINT4 iant_stat_mask( void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.is_mask ) ;
}

/* ------------------------------
 * Return current antenna status word mask of bits which can be
 * critical faults
 */
UINT4 iant_stat_critical(void )
{
  antGlobal_c->info.iactive += 1;
  return( antGlobal_c->info.iStatusCritical ) ;
}

/* ------------------------------
 * Determine whether the antenna format contains information
 * appropriate for a moving platform system
 */
UINT2 lant_moving_platform( void )
{
  if (ANTFMT_RCV03 == antGlobal_c->setup.istatus_format) return TRUE;
  return FALSE;
}

/* ------------------------------
 * Logical function to return TRUE iff the system time could be set from
 * the time information that we currently have.
 */
UINT2 lant_okay_to_set_time(void)
{
  antGlobal_c->info.iactive += 1;
  return( lokay_to_set_time_int( &antGlobal_c->info ) ) ;
}

/* ------------------------------
 * Logical function to return TRUE iff the system time is currently has a
 * pending time set.
 */
UINT4 lant_set_time_wait
(SINT4 *pTimeChange_a)   /* Returns last time change, valid only if returns FALSE */
{
  antGlobal_c->info.iactive += 1;
  *pTimeChange_a = antGlobal_c->info.iLastTimeChange;
  return( antGlobal_c->info.lset_system_time );
}

/* ------------------------------
 * Dual RCP System control and status calls
 */
void ant_get_drcp( struct drcp_status *drcp_a )
{
  *drcp_a = antGlobal_c->info.drcp ;
}

void   ant_set_drcp_mode( UINT2 iDrcpMode_a ) {
  AntSetArg( ANTSET_DRCPMODE, iDrcpMode_a ) ; }
void __ant_set_drcp_mode( UINT2 iDrcpMode_a )
{
  antGlobal_c->info.iDrcpModeReq = iDrcpMode_a ;
}

void   ant_set_drcp_relinquish( UINT2 lRelinquish_a ) {
  AntSetArg( ANTSET_DRCPRLNQ, lRelinquish_a ) ; }
void __ant_set_drcp_relinquish( UINT2 lRelinquish_a )
{
  antGlobal_c->info.lDrcpRelinquishReq = lRelinquish_a ;
}

void   ant_set_drcp_wouldUse( UINT2 lWouldUse_a ) {
  AntSetArg( ANTSET_DRCPWUSE, lWouldUse_a ) ; }
void __ant_set_drcp_wouldUse( UINT2 lWouldUse_a )
{
  antGlobal_c->info.lDrcpWouldUseReq = lWouldUse_a ;
}

/* ------------------------------
 * Returns the fault bits reported from the INU.
 */
UINT4 AntGetInuFaults( void )
{
  antGlobal_c->info.iactive += 1;
  return( (UINT4)antGlobal_c->info.iInuFaultBits ) ;

}
