/* *******************************************************************
 * *                                                                 *
 * *            Serial Line Antenna Transmission Process             *
 * *                                                                 *
 * ******************************************************************* 
 * File: libs/antenna/ant_xmt.c
 *
 * COPYRIGHT (c) 1988, 1989, 1991, 1997, 1999, 2000, 2001, 2002, 2003,
 *                        2004, 2005, 2006 BY
 *           VAISALA INC., 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 <locale.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>

#define INCLUDE_STDIO_PROTOTYPES 1
#define INCLUDE_TERMIOS_PROTOTYPES 1
#include "sigtypes.h"
#include "user_lib.h"
#include "setup.h"
#include "antenna_lib.h"
#include "antenna_int.h"

#include "ant_global.h"

/* ACTV_TIME_P sets an upper bound (in millisecs) after which an
 * antenna will automatically be stopped if no calls to the driver
 * package have been made.  This is a safeguard against user program
 * failures that might otherwise leave the antenna spinning
 * unattended.
 */
#define ACTV_TIME_P (300000)

#define XMT_SNOOZE    (500)	/* Time (ms) between normal transmissions */
#define XMT_SNOOZE_SA (5000)	/* Time (ms) between normal transmissions, SA format */

#define LSET_CROSS_MODE_LIMITS (1)
/* Local function prototypes
 */
static void ant_xmt_loop
(int   iFD_a,
 UINT4 ichannel_a,
 UINT4 iformat_a );

static void wait_for_change
(UINT4 ichange_last_a );

static void set_xmt_template
(UINT1 *str_a,
 UINT4 iformat_a,
 UINT4 ixmt_size_a );

static void set_xmt_servo
(UINT1 str_a[],
 UINT4 iformat_a,
 UINT4 ibtime_a ) ;

static void set_az_velocity
(UINT1 str_a[],
 FLT4 fdazv_a,
 UINT4 iformat_a );

static void set_el_velocity
(UINT1 str_a[],
 FLT4 fdelv_a,
 UINT4 iformat_a );

static void set_az_position
(UINT1 *str_a,
 BIN2 iazimuth_a,
 UINT4 iformat_a );

static void set_el_position
(UINT1 *str_a,
 BIN2 ielevation_a,
 UINT4 iformat_a );

static void ant_bite_poll
(UINT4 ibtime_a,
 int   iFD_a,
 volatile struct io_information *pio_a ) ;

static void SendControlData
(UINT4 ibtime_a,        /* The current time */
 UINT1 iUnit_a, 
 UINT1 lSendAsap_a,
 UINT1 lSendChanged_a,
 int   iFD_a );      /* Channel ID */

static void SendStatusCommand
(UINT1 iUnit_a, 
 int   iFD_a );      /* Channel ID */

/* Possibly send a client originated BITE status command */
static void SendClientStatus
(UINT1 iUnit_a, 
 int   iFD_a );      /* Channel ID */

static void make_bite_control_record
( UINT1 *rec_a, SINT4 iunit_a, SINT4 ilen_a, UINT1 iden_a ) ;

static void ant_nethost_check
(UINT4 iCurrentBTime_a     /* The current time */
 );

static void set_xmt_other
(UINT1 str_a[],
 UINT4 iformat_a,
 UINT4 ibtime_a ) ;

#if 0 /* Scientific Atlanta controler only controls servo power */
static SINT2 ivel_to_sa
(FLT4 fvelocity_a );
#endif

static void send_xmt_data
(const UINT1 *str_a,
 int   iFD_a,
 UINT4 iformat_a,
 volatile struct io_information *pio_a,
 UINT4 ixmt_size_a,
 const char *sFormat_a );

static void wait_for_ack
( volatile struct io_information *pio_a );

static UINT4 get_channel
( int iargc_a,
  char **argv_a );

/* ----------------------------------------
 * Variables shared between routines
 */
static SINT4 isnooze_time_c ;
static UINT2 lsa_send_pos_c ;
static UINT2 lsa_send_rte_c ;
/* static UINT2 lsa_send_mof_c ; */

/* ------------------------------
 * This routine converts requests for antenna motion into a serial
 * output stream that is sent to a Radar/Antenna controller.  All
 * serial trans- mission protocols are handled by this one piece of
 * code.
 */    
int main( int iargc_a, char **argv_a )
{
  MESSAGE istatus ; UINT4 iformat, ichannel ;
  int iFD; volatile struct io_information *pio ;
  int iClientType ; char sLogText[120] ;
  
  /* Map to the global section, and assign process priority.  The
   * calls to sig_signal here are for the benefit of testing by just
   * running the process separately.
   */
  setlocale( LC_ALL, "" ) ;

  istatus = sig_run_as_operator() ;
  if( istatus != SS_NORMAL ) sig_signal(istatus) ;

  ichannel = get_channel( iargc_a, argv_a );
  if( ichannel == 1 ) iClientType = ANT_IAM_ANT_XMT1;
  else                iClientType = ANT_IAM_ANT_XMT0;

  istatus = AntPowerUpMain( iClientType, 0 );
  if ( istatus != SS_NORMAL ) {
    sig_signal( istatus ) ; exit(EXIT_FAILURE) ;
  }
  if( ichannel == 1 ) sig_establish( antSignalHandler, "ANT_XMT1" ) ;
  else                sig_establish( antSignalHandler, "ANT_XMT0" ) ;

  pio = &antGlobal_c->info.io[ichannel] ;

#ifdef HPUX
  if( antGlobal_c->info.lauto ) ignore_next_signal( SIGHUP ) ;
#endif

  istatus = sig_setpriority( (SINT4)antGlobal_c->setup.ipri_antxmt ) ;
  if( istatus != SS_NORMAL ) sig_signal(istatus) ;

  sprintf( sLogText, "XMT%d Process Starting", ichannel ) ;
  antWriteLog( sLogText ) ;

  /* Socket driver does not use this process at all...
   */
  if( ANTIFACE_ANTEXPORT == antGlobal_c->setup.iInterfaceType ) {
    pio->ixmt_status = SS_NORMAL;
    for(;;) sig_microSleep(100*1000) ;
  }

  if( (ANTIFACE_NETWORK == antGlobal_c->setup.iInterfaceType)&&(ichannel==0) )
    {
      iFD = OpenUdpWriter( antGlobal_c->setup.iBaudRate1, 
			   (const char *)antGlobal_c->setup.schannel1,
			   (const char *)antGlobal_c->setup.sNetInterface1,
			   UDP_FLG_MULTICAST ) ;
      if (iFD==-1) exit( EXIT_FAILURE );
    } 
  else
    {
      volatile char *sptr;
      /* Assign I/O stream for this antenna.  A blank device file means
       * use the simulator fifo.
       */
      if( ichannel == 1 ) sptr = antGlobal_c->setup.schannel2 ;
      else                sptr = antGlobal_c->setup.schannel1 ;
      
      if( sptr[0] )
	{
	  struct ttyspec spec ; struct ttydev tty ;
	  memset( &spec, 0, sizeof(spec) ) ; strcpy( spec.sDevice, (char *)sptr ) ;
	  spec.lPreserveAll = TRUE ;
	  spec.lLockDevice  = TRUE ;
	  spec.lWriteOnly   = TRUE ; 
	  
	  istatus = openTTYDev( &tty, &spec ) ;
	  if( istatus != SS_NORMAL ) {
	    pio->ixmt_status = istatus ; sig_signal( istatus ) ;
	    exit( EXIT_FAILURE ) ;
	  }
	  iFD = fileno(tty.Tx) ;
	  
	} 
      else 
	{
	  iFD = -1;
	}
    }

  /* Implement one of the serial protocols.  The format is latched
   * here to prevent problems if it gets changed in shared memory.
   */
  if( ichannel == 1 ) iformat = antGlobal_c->setup.icontrol2_format ;
  else                iformat = antGlobal_c->setup.icontrol_format ;

  ant_xmt_loop( iFD, ichannel, iformat );

  return 0;
}

/* ------------------------------
 * Loops outputing antenna control data in the specified format.
 */
static void ant_xmt_loop
(int   iFD_a,
 UINT4 ichannel_a,
 UINT4 iformat_a )
{
  UINT4 iactive_last, ichange_last, iactive_time, ibtime, ixmt_size ;
  char sFormat[8];

  UINT1 str[ ANTXMT_MAX_LENGTH ];
  volatile struct antenna_info *pant;
  volatile struct io_information *pio;
  
  pant = &antGlobal_c->info;
  pio = &antGlobal_c->info.io[ichannel_a];
  switch (iformat_a)
    {
    case ANTFMT_XMT01:
      isnooze_time_c = XMT_SNOOZE ;
      ixmt_size = 11;
      strcpy( sFormat, ANT_PKT_TYPE_XMT01 );
      break;
      
    case ANTFMT_XMT02:
    case ANTFMT_XMT04:
      isnooze_time_c = XMT_SNOOZE ;
      ixmt_size = 14;
      strcpy( sFormat, ANT_PKT_TYPE_XMT02 );
      break;
      
    case ANTFMT_XMTSA:
      isnooze_time_c = XMT_SNOOZE_SA ;
      ixmt_size = 3*19 + 14;
      strcpy( sFormat, ANT_PKT_TYPE_XMTSA );
      break;

    case ANTFMT_XMT05:
      isnooze_time_c = XMT_SNOOZE ;
      ixmt_size = 18 ;
      strcpy( sFormat, ANT_PKT_TYPE_XMT05 );
      break;

    default:
      pio->ixmt_status = ANT_BAD_CONTROL;
      sig_signal( ANT_BAD_CONTROL );
      exit(EXIT_FAILURE) ;
      break;
    }
  pio->ixmt_status = SS_NORMAL;

  getbtime( &iactive_time );
  iactive_last = pant->iactive;
  ichange_last = pant->ichange_count;
  
  while (TRUE)
    {
      wait_for_change( ichange_last );
      ichange_last = pant->ichange_count;
      getbtime( &ibtime );
      
      /* Format chat-mode records for any serial data that are waiting
       * to be sent.  Do this first to reduce transmission latency.
       */
      if( ichannel_a == 0 )
	{
	  while( bfifo_used( (void *)&antGlobal_c->vdat.ChatOut ) ) 
	    {
	      SINT4 index;
	      str[0] = ANT_SYNC_CHAT ; str[7] = ANT_SYNC_ENDREC ;
	      for( index=1 ; index < 7 ; index++ ) {
		SINT4 iich = bfifo_read( (void *)&antGlobal_c->vdat.ChatOut ) ;
		if( iich == -1 ) break ; str[index] = iich ;
	      }
	      for( ; index < 7 ; index++ ) str[index] = 0 ;
	      WriteInt( iFD_a, str, 8, ANT_PKT_TYPE_XMTCHAT, 
			antGlobal_c->info.iChatDestination );
	    }
	}

      /* If we have received a request to set the system time, then do
       * it now.  Double check that the request really makes sense.
       */
      if( (ichannel_a == 0) && pant->lset_system_time )
	{
	  if( lokay_to_set_time_int( pant ) ) 
	    { 
	      MESSAGE istatus ; struct ymds_time time ; SINT4 iMs ;
	      pant->iLastTimeChange = pant->itime_error;
	      if ((pant->itime_error>= MAX_SUBTRACT_MILLISECONDS)||
		  (pant->itime_error<=-MAX_SUBTRACT_MILLISECONDS))
		{
		  /* Get the time since the last time report.  Add that
		   * to the last report, then set the system time to
		   * that value.
		   */
		  time = pant->time_in_packet ;
		  iMs = btimeGrace(ibtime - pant->itime_btime);
		}
	      else
		{
		  GetTimeYmds( &time, antGlobal_c->setup.lTimePacketUtc );
		  iMs = pant->itime_error;
		}
	      YmdsAddMs( iMs, &time );
		  
	      istatus = SetTimeYmds( &time ) ;
	      if (istatus == SS_NORMAL ) {
		pant->ltime_set_from_packet = TRUE ;
		pant->itime_error = 0 ;
	      }
	    }
	  else
	    {
	      pant->iLastTimeChange = 0;
	    }
	  pant->lset_system_time = FALSE ;
	}
	  
      /* If the user has not done anything with the antenna for
       * long time, then stop the antenna motion.
       */
      if( (ichannel_a == 0) && (pant->iactive == iactive_last) ) 
	{
	  if( btimeGrace(ibtime - iactive_time) > ACTV_TIME_P)
	    {
	      pant->iazmode = ANTMODE_STOP;
	      pant->ielmode = ANTMODE_STOP;
	    }
	}
      else
	{
	  iactive_last = pant->iactive;
	  iactive_time = ibtime;
	}

      /* Format and send an antenna control packet.  Note that a
       * special write is required here.
       */
      set_xmt_template( str, iformat_a, ixmt_size );
      set_xmt_servo( str, iformat_a, ibtime ) ;	/* AZ/EL position/velocity/stop servos */
      set_xmt_other( str, iformat_a, ibtime ) ;	/* Set up other states */

      send_xmt_data( str, iFD_a, iformat_a, pio, ixmt_size, sFormat );
      
      /* Check the BITE unit
       */
      if( ichannel_a == 0 )
	{
	  ant_bite_poll( ibtime, iFD_a, pio ) ;
	  ant_nethost_check( ibtime ) ;
	}
    } /* end while */
}

/* ------------------------------
 * Wait for a while
 */
static void wait_for_change
(UINT4 ichange_last_a )
{
  UINT4 ielapsed_time;
  /* Waits in 20 ms steps until either a new command is issued, or the
   * timeout is passed.
   */
  ielapsed_time = 0;
  while( ( ielapsed_time  < isnooze_time_c ) && 
	 ( ichange_last_a == antGlobal_c->info.ichange_count ) ) {
    sig_microSleep( 20000 ) ; ielapsed_time += 20 ;
  }
}

/* ------------------------------
 * Fill in the blank template
 */
#define SA_RTE_OFFSET (0)
#define SA_MOD_OFFSET (19)
#define SA_POS_OFFSET (38)
#define SA_MOF_OFFSET (57)

static void set_xmt_template
(UINT1 *str_a,
 UINT4 iformat_a,
 UINT4 ixmt_size_a )
{
  if (iformat_a == ANTFMT_XMTSA)
    {
      strcpy( (char *)&str_a[SA_RTE_OFFSET], "@8RTE W0000,0000\r\n" );
      strcpy( (char *)&str_a[SA_MOD_OFFSET], "@8MOD W000B,000B\r\n" );
      strcpy( (char *)&str_a[SA_POS_OFFSET], "@8POS W0000,0000\r\n" );
      strcpy( (char *)&str_a[SA_MOF_OFFSET], "@8MOF W0010\r\n" );
      lsa_send_rte_c = FALSE;
      lsa_send_pos_c = FALSE;
      /* lsa_send_mof_c = FALSE; */
    }
  else
    {
      memset( str_a, 0, ixmt_size_a );
      str_a[ 0             ] = ANT_SYNC_ANT   ; /* SYNC character for Control packet */
      str_a[ ixmt_size_a-1 ] = ANT_SYNC_ENDREC; /* End-of-Record character */
    }
}

/* ------------------------------
 * Set the antenna motion commands
 */
static void set_xmt_servo
( UINT1 str_a[], UINT4 iformat_a, UINT4 ibtime_a )
{
  volatile struct antenna_info* pant = &antGlobal_c->info ;
  struct rtpedx pedx ;

  /* The following variables are used for the stopped servo for all
   * serial protocols.  LAZSTOPPED and LELSTOPPED are NZ whenever a
   * position servo is being held on the appropriate axis.  ISTOPAZ
   * and ISTOPEL indicate what that position is.
   */
  static SINT2 istopaz, istopel ;
  static UINT2 lazstopped=FALSE, lelstopped=FALSE ;
  UINT1 lTrackPos ;

#define STOPVEL (1.0)		/* deg/sec threshold for stopping */

  /* Pick up the current antenna pos/vel
   */
  ant_get_rtpedx( &pedx, ibtime_a ) ;

  /* The stopped servo will need to track the antenna whenever we are
   * not actually in control.  This way we will not swing it around
   * when control is regained.
   */
  lTrackPos = FALSE ;

  lTrackPos |= ( 0 != ( pant->isbits & pant->is_mask & ASF_LOCAL )) ;

  lTrackPos |= ( pant->drcp.lSetupDual && pant->drcp.lRunningDual &&
		 ( ( (  pant->drcp.lUnitA) &&
		     (pant->drcp.sysA.iActivity != DRCP_ACT_ACTIVE) ) ||
		   ( (! pant->drcp.lUnitA) &&
		     (pant->drcp.sysB.iActivity != DRCP_ACT_ACTIVE) ) ) ) ;

  /* Process azimuth first
   */
  switch( pant->iazmode )
    {
    case ANTMODE_POS:
      lazstopped = FALSE ;
      set_az_position( str_a, pant->idaz, iformat_a );
      break;

    case ANTMODE_VEL:
      lazstopped = FALSE ;
      set_az_velocity( str_a, pant->fdazv, iformat_a );
      break;
	    
    case ANTMODE_STOP:
    default:
	
      /* Track the position when we are not actually in control.
       */
      if( lTrackPos )
	{
	  lazstopped = FALSE ;
	  set_az_velocity( str_a, 0.0, iformat_a );
	}
      else if( lazstopped ) 
	{
	  set_az_position( str_a, istopaz, iformat_a );
	}
      /* Graceful stop not available in XMT01 format.
       */
      else if((antGlobal_c->setup.icontrol_format == ANTFMT_XMT01) ||
	      ((pedx.fazv > -STOPVEL) && (pedx.fazv < STOPVEL)) ) 
	{
	  lazstopped = TRUE ; istopaz = iBin2FromFDeg( pedx.faz ) ;
	  set_az_position( str_a, istopaz, iformat_a );
	} 
      else
	{
	  set_az_velocity( str_a, 0.0, iformat_a );
	}
      break;
    }
      
  /* Process elevation next
   */
  switch ( pant->ielmode ) 
    {
    case ANTMODE_POS:
      lelstopped = FALSE ;
      set_el_position( str_a, pant->idel, iformat_a );
      break;

    case ANTMODE_VEL:
      lelstopped = FALSE ;
      set_el_velocity( str_a, pant->fdelv, iformat_a );
      break;
	
    case ANTMODE_STOP:
    default:
      if( lTrackPos )
	{
	  lelstopped = FALSE ;
	  set_el_velocity( str_a, 0.0, iformat_a );
	}
      else if( lelstopped ) 
	{
	  set_el_position( str_a, istopel, iformat_a );
	} 
      else if((antGlobal_c->setup.icontrol_format == ANTFMT_XMT01) ||
	      ((pedx.felv > -STOPVEL) && (pedx.felv < STOPVEL)) ) 
	{
	  lelstopped = TRUE ; istopel = iBin2FromFDeg( pedx.fel ) ;
	  set_el_position( str_a, istopel, iformat_a );
	}
      else
	{
	  set_el_velocity( str_a, 0.0, iformat_a );
	}
      break;
    }
}

/* ------------------------------
 * Set azimuth velocity to the specified value, set azimuth axis to velocity mode.
 */
static void set_az_velocity
(UINT1 str_a[],
 FLT4 fdazv_a,
 UINT4 iformat_a )
{
  SINT1 ivelocity1;
  SINT2 ivelocity2;
  switch (iformat_a)
    {
    case ANTFMT_XMTSA:
      ivelocity2 = (SINT2)(ANTSA_VEL_UNITS * fdazv_a);
      sprintf( (char *)&str_a[SA_RTE_OFFSET+7], "%04X", (UINT2)ivelocity2 );
      str_a[SA_RTE_OFFSET+11] = ',';  /* Patch back trailing null */
      str_a[SA_MOD_OFFSET+10] = '9';
      lsa_send_rte_c = TRUE;
      break;

    case ANTFMT_XMT01:
      str_a[5] |= 0x01;
      ivelocity1 = (SINT1)( ANT01_VEL_UNITS * fdazv_a );
      if (ivelocity1 < -64) ivelocity1 = -64;
      if (ivelocity1 > 63)  ivelocity1 = 63;
      str_a[9] = ivelocity1 & 0x7F;

      /* Make sure that non-zero desired velocities translate into a
       * non-zero velocity command.
       */
      if( str_a[9] == 0 )
	{
	  if( fdazv_a < 0. ) str_a[9] = 0x7F;
	  if( fdazv_a > 0. ) str_a[9] = 0x01;
	}
      break;

    default:
      str_a[5] |= 0x01;
      ivelocity2 = (SINT2)( ANT02_VEL_UNITS * fdazv_a );
      if (ivelocity2 < -8192) ivelocity2 = -8192;
      if (ivelocity2 > 8191)  ivelocity2 = 8191;

      /* Make sure that non-zero desired velocities translate into a
       * non-zero velocity command.
       */
      if( ivelocity2 == 0 )
	{
	  if( fdazv_a < 0. ) ivelocity2 = -1;
	  if( fdazv_a > 0. ) ivelocity2 = 1;
	}
      str_a[9]  = (ivelocity2   ) & 0x7F;
      str_a[10] = (ivelocity2>>7) & 0x7F;
      break;
    }
}

/* ------------------------------
 * Set elevation velocity to the specified value 
 */
static void set_el_velocity
(UINT1 str_a[],
 FLT4 fdelv_a,
 UINT4 iformat_a )
{
  SINT1 ivelocity1;
  SINT2 ivelocity2;
  switch (iformat_a)
    {
    case ANTFMT_XMTSA:
      ivelocity2 = (SINT2)(ANTSA_VEL_UNITS * fdelv_a);
      sprintf( (char *)&str_a[SA_RTE_OFFSET+12], "%04X", (UINT2)ivelocity2 );
      str_a[SA_RTE_OFFSET+16] = '\r';  /* Patch back trailing null */
      str_a[SA_MOD_OFFSET+15] = '9';
      lsa_send_rte_c = TRUE;
      break;

    case ANTFMT_XMT01:
      str_a[5] |= 0x02;
      ivelocity1 = (SINT1)( ANT01_VEL_UNITS * fdelv_a );

      /* Clip the velocity at the maximum values that can be represented
       */
      if (ivelocity1 < -64) ivelocity1 = -64;
      if (ivelocity1 > 63)  ivelocity1 = 63;
      str_a[9] = ivelocity1 & 0x7F;

      /* Make sure that non-zero desired velocities translate into a
       * non-zero velocity command.
       */
      if( str_a[9] == 0 )
	{
	  if( fdelv_a < 0. ) str_a[9] = 0x7F;
	  if( fdelv_a > 0. ) str_a[9] = 0x01;
	}
      break;

    default:
      str_a[5] |= 0x02;
      ivelocity2 = (SINT2)( ANT02_VEL_UNITS * fdelv_a );
      if (ivelocity2 < -8192) ivelocity2 = -8192;
      if (ivelocity2 > 8191)  ivelocity2 = 8191;

      /* Make sure that non-zero desired velocities translate into a
       * non-zero velocity command.
       */
      if( ivelocity2 == 0 )
	{
	  if( fdelv_a < 0. ) ivelocity2 = -1;
	  if( fdelv_a > 0. ) ivelocity2 = 1;
	}
      str_a[11] = (ivelocity2   ) & 0x7F;
      str_a[12] = (ivelocity2>>7) & 0x7F;

      /* Whenever elevation mode is set to velocity, fill in the maximum
       * or minimum elevation angle (depending on the velocity sign) in
       * the elevation position slot.
       */
      if (LSET_CROSS_MODE_LIMITS)
	{
	  BIN2 iElLimit;
	  if (fdelv_a > 0.0)
	    iElLimit = antGlobal_c->setup.ielmax;
	  else
	    iElLimit = antGlobal_c->setup.ielmin;

	  str_a[3] = ( iElLimit>>2 ) & 0x7F;
	  str_a[4] = ( iElLimit>>9 ) & 0x7F;
	}

      break;
    }
}
/* ------------------------------
 * Set azimuth position mode in all formats.
 */
static void set_az_position
(UINT1 *str_a,
 BIN2 iazimuth_a,
 UINT4 iformat_a )
{
  switch (iformat_a)
    {
    case ANTFMT_XMTSA:
      sprintf( (char *)&str_a[SA_POS_OFFSET+7], "%04X", iazimuth_a );
      str_a[SA_POS_OFFSET+11] = ',';  /* Patch back trailing null */
      lsa_send_pos_c = TRUE;
      break;

    case ANTFMT_XMT01:
      str_a[1] = ( iazimuth_a>>2 ) & 0x7F;
      str_a[2] = ( iazimuth_a>>9 ) & 0x7F;
      break;

    default:
      str_a[1] = ( iazimuth_a>>2 ) & 0x7F;
      str_a[2] = ( iazimuth_a>>9 ) & 0x7F;
      /* Whenever azimuth mode is set to position, fill in the maximum
       * positive azimuth velocity in the azimuth velocity slot.
       */
      if (LSET_CROSS_MODE_LIMITS)
	{
	  SINT2 iVelocity2;
	  iVelocity2 = (SINT2)( ANT02_VEL_UNITS * antGlobal_c->setup.fazvelmax );
	  if (iVelocity2 > 8191)  iVelocity2 = 8191;
	  str_a[ 9] = (iVelocity2   ) & 0x7F;
	  str_a[10] = (iVelocity2>>7) & 0x7F;
	}
      break;
    }
}

/* ------------------------------
 * Set the elvation position
 */
static void set_el_position
(UINT1 *str_a,
 BIN2 ielevation_a,
 UINT4 iformat_a )
{
  switch (iformat_a)
    {
    case ANTFMT_XMTSA:
      sprintf( (char *)&str_a[SA_POS_OFFSET+12], "%04X", ielevation_a );
      str_a[SA_POS_OFFSET+16] = '\r';  /* Patch back trailing null */
      lsa_send_pos_c = TRUE;
      break;

    case ANTFMT_XMT01:
      str_a[3] = ( ielevation_a>>2 ) & 0x7F;
      str_a[4] = ( ielevation_a>>9 ) & 0x7F;
      break;

    default:
      str_a[3] = ( ielevation_a>>2 ) & 0x7F;
      str_a[4] = ( ielevation_a>>9 ) & 0x7F;
      /* Whenever elevation mode is set to position, fill in the maximum
       * positive elevation velocity in the elevation velocity slot.
       */
      if (LSET_CROSS_MODE_LIMITS)
	{
	  SINT2 iVelocity2;

	  iVelocity2 = (SINT2)( ANT02_VEL_UNITS * antGlobal_c->setup.felvelmax );
	  if (iVelocity2 > 8191)  iVelocity2 = 8191;
	  str_a[11] = (iVelocity2   ) & 0x7F;
	  str_a[12] = (iVelocity2>>7) & 0x7F;
	}
      break;
    }
}

/* ------------------------------
 * Function to poll to see if there is something that should be
 * done regarding the BITE.
 */
static void ant_bite_poll
(UINT4 ibtime_a,     /* The current time */
 int   iFD_a,        /* Channel ID */
 volatile struct io_information *pio_a
 )
{
  static UINT4 ibite_interrogate_time ;
  UINT1 str[5] ; SINT4 icount ;
  
  /* Update all outgoing control BITE packets.  This is done either on
   * demand, when something changes, or after a certain time has
   * elapsed since the last transmissions.
   */
  {
    UINT1 iunit ;
    UINT1 lcnt_send_asap    = antGlobal_c->binfo.lcnt_send_asap    ;
    UINT1 lcnt_send_changed = antGlobal_c->binfo.lcnt_send_changed ;
    UINT1 lHasStatus = FALSE;

    if( lcnt_send_asap    ) antGlobal_c->binfo.lcnt_send_asap    = FALSE ;
    if( lcnt_send_changed ) antGlobal_c->binfo.lcnt_send_changed = FALSE ;

    for( iunit=0 ; iunit < (SINT4)(UINT4)antGlobal_c->bitex.iunit_cnt ; iunit++ ) 
      {
	if( antGlobal_c->bitex.units[iunit].lcontrol_unit ) 
	  {
	    SendControlData( ibtime_a, iunit, lcnt_send_asap, lcnt_send_changed, iFD_a );
	  }
	else
	  {
	    SendStatusCommand( iunit, iFD_a );
	    SendClientStatus( iunit, iFD_a );
	    lHasStatus = TRUE ;
	  }
      }
    /* If there are no status packets, then skip all the commands
     * below.
     */
    if (!lHasStatus) return;
  }

  /* Send a BITE interrogate if we are asked to do so, or if a maximum
   * time has elapsed since the last one.
   */
  if( antGlobal_c->binfo.lInterrogateBite ||
      (btimeGrace(ibtime_a - ibite_interrogate_time) > 60000) )
    {
      antGlobal_c->binfo.lInterrogateBite = FALSE;
      ibite_interrogate_time = ibtime_a;
      str[0] = ANT_SYNC_BITE_RQST;
      str[1] = BITE_INTERROGATE;
      str[2] = ANT_SYNC_ENDREC;
      icount = WriteInt( iFD_a, str, 3, ANT_PKT_TYPE_XMTBINTROG, htonl(INADDR_ANY) );
      if (icount==3)
	pio_a->iout_cnt += 3;
      else
	pio_a->iout_ercnt += 3;

      /* Send Q-BITE request also */
      str[0] = ANT_SYNC_QBITE_RQST;
      str[1] = 0x01;
      str[2] = ANT_SYNC_ENDREC;
      icount = WriteInt( iFD_a, str, 3, ANT_PKT_TYPE_XMTBINTROG, htonl(INADDR_ANY) );
      if (icount==3)
	pio_a->iout_cnt += 3;
      else
	pio_a->iout_ercnt += 3;

    }

  /* Send a BITE Take Sample request if we are asked to do so
   */
  if (antGlobal_c->binfo.lSampleBite )
    {
      antGlobal_c->binfo.lSampleBite = FALSE;
      str[0] = ANT_SYNC_BITE_RQST;
      str[1] = BITE_TAKE_SAMPLE;
      str[2] = ANT_SYNC_ENDREC;
      icount = WriteInt( iFD_a, str, 3, ANT_PKT_TYPE_XMTBSAMPLE, htonl(INADDR_ANY) );
      if (icount==3)
	pio_a->iout_cnt += 3;
      else
	pio_a->iout_ercnt += 3;
    }
  /* Send a BITE Set Values request if we are asked to do so
   */
  if (antGlobal_c->binfo.lSetValuesBite )
    {
      antGlobal_c->binfo.lSetValuesBite = FALSE;
      str[0] = ANT_SYNC_BITE_RQST;
      str[1] = BITE_SET_VALUES;
      str[2] = ANT_SYNC_ENDREC;
      icount = WriteInt( iFD_a, str, 3, ANT_PKT_TYPE_XMTBSETVAL, htonl(INADDR_ANY) );
      if (icount==3)
	pio_a->iout_cnt += 3;
      else
	pio_a->iout_ercnt += 3;
    }
  /* Send a BITE Reset request if we are asked to do so
   */
  if (antGlobal_c->binfo.lResetBite )
    {
      antGlobal_c->binfo.lResetBite = FALSE;
      str[0] = ANT_SYNC_BITE_RQST;
      str[1] = BITE_RESET;
      str[2] = ANT_SYNC_ENDREC;
      icount = WriteInt( iFD_a, str, 3, ANT_PKT_TYPE_XMTBRESET, htonl(INADDR_ANY) );
      if (icount==3)
	pio_a->iout_cnt += 3;
      else
	pio_a->iout_ercnt += 3;
    }

}
/* ------------------------------
 * Possibly send BITE output
 */
static void SendControlData
(UINT4 ibtime_a,      /* The current time */
 UINT1 iUnit_a, 
 UINT1 lSendAsap_a,
 UINT1 lSendChanged_a,
 int   iFD_a )     /* Channel ID */
{

  static UINT4 XmtBTimes[BITE_MAX_UNITS] ;
  static UINT1 LastRecArray[BITE_MAX_UNITS][BITE_MAX_LENGTH] ;
  UINT1 *pLastRec = LastRecArray[iUnit_a];
  UINT1 ThisRec[BITE_MAX_LENGTH];
  UINT2 lSend = lSendAsap_a ;
  SINT4 ilen  = antGlobal_c->bitex.units[iUnit_a].iunit_length ;
  UINT1 iden  = antGlobal_c->bitex.units[iUnit_a].iunit_id ;

  /* Force send periodically */
  if( (btimeGrace(ibtime_a - XmtBTimes[iUnit_a]) > 60000) ) lSend = TRUE ;

  if( lSend || lSendChanged_a )
    {
      make_bite_control_record( ThisRec, iUnit_a, ilen, iden ) ;
      if( lSend || (lSendChanged_a && (0 != memcmp(pLastRec, ThisRec, ilen))) ) 
	{
	  volatile struct bite_unit_info *pBite=&(antGlobal_c->binfo.Unit[iUnit_a]);
	  XmtBTimes[iUnit_a] = ibtime_a ; 
	  memcpy( pLastRec, ThisRec, ilen ) ;
	  WriteInt( iFD_a, ThisRec, ilen, ANT_PKT_TYPE_XMTBITEC, htonl(INADDR_ANY) ) ;
	  pBite->iout_cnt += ilen;
	  pBite->ibtime = ibtime_a ;
	}
    }
}
/* ------------------------------
 * Possibly send BITE unit command
 */
static void SendStatusCommand
(UINT1 iUnit_a, 
 int   iFD_a )      /* Channel ID */
{
  UINT1 ThisRec[4];
  volatile struct bitex_unit_info *pBitex=&(antGlobal_c->bitex.units[iUnit_a]);
  volatile struct bite_unit_info  *pBite =&(antGlobal_c->binfo.Unit[iUnit_a]);

  /* Send an individual BITE Interrogate request
   */
  if (pBite->lInterrogate )
    {
      pBite->lInterrogate = FALSE;
      ThisRec[0] = ANT_SYNC_1BITE_RQST;
      ThisRec[1] = pBitex->iunit_id;
      ThisRec[2] = BITE_INTERROGATE;
      ThisRec[3] = ANT_SYNC_ENDREC;
      WriteInt( iFD_a, ThisRec, 4, ANT_PKT_TYPE_XMTBINTROG, htonl(INADDR_ANY) );
      pBite->iout_cnt += 4;
    }
  /* Send an individual BITE Take Sample request
   */
  if (pBite->lSample )
    {
      pBite->lSample = FALSE;
      ThisRec[0] = ANT_SYNC_1BITE_RQST;
      ThisRec[1] = pBitex->iunit_id;
      ThisRec[2] = BITE_TAKE_SAMPLE;
      ThisRec[3] = ANT_SYNC_ENDREC;
      WriteInt( iFD_a, ThisRec, 4, ANT_PKT_TYPE_XMTBSAMPLE, htonl(INADDR_ANY) );
      pBite->iout_cnt += 4;
    }
  /* Send an individual BITE Set Values request
   */
  if (pBite->lSetValues )
    {
      pBite->lSetValues = FALSE;
      ThisRec[0] = ANT_SYNC_1BITE_RQST;
      ThisRec[1] = pBitex->iunit_id;
      ThisRec[2] = BITE_SET_VALUES;
      ThisRec[3] = ANT_SYNC_ENDREC;
      WriteInt( iFD_a, ThisRec, 4, ANT_PKT_TYPE_XMTBSETVAL, htonl(INADDR_ANY) );
      pBite->iout_cnt += 4;
    }
  /* Send an individual BITE Reset request
   */
  if (pBite->lReset )
    {
      pBite->lReset = FALSE;
      ThisRec[0] = ANT_SYNC_1BITE_RQST;
      ThisRec[1] = pBitex->iunit_id;
      ThisRec[2] = BITE_RESET;
      ThisRec[3] = ANT_SYNC_ENDREC;
      WriteInt( iFD_a, ThisRec, 4, ANT_PKT_TYPE_XMTBRESET, htonl(INADDR_ANY) );
      pBite->iout_cnt += 4;
    }
}
/* ------------------------------
 * Possibly send a client originated BITE status command 
 */
static void SendClientStatus
(UINT1 iUnit_a, 
 int   iFD_a )      /* Channel ID */
{
  volatile struct bitex_unit_info *pBitex=&(antGlobal_c->bitex.units[iUnit_a]);
  volatile struct bite_unit_info  *pBite =&(antGlobal_c->binfo.Unit[iUnit_a]);

  /* Send an individual BITE Interrogate request
   */
  if (pBite->bClientPacket )
    {
      WriteInt( iFD_a, (UINT1*)pBite->sClientPacket,  /* Cast discards volatile */
		pBitex->iunit_length, ANT_PKT_TYPE_RCVBITES, htonl(INADDR_ANY) );
      pBite->iout_cnt += pBitex->iunit_length;
      pBite->bClientPacket = FALSE;  /* This will free up reuse of packet buffer */
    }
}

/* ------------------------------
 * Make a BITE control record for a particular unit.
 */
static void make_bite_control_record
( UINT1 *rec_a, SINT4 iunit_a, SINT4 ilen_a, UINT1 iden_a )
{
  SINT4 ifield ;

  /* Fill in sync byte, identification byte, and end byte.
   */
  memset( rec_a, 0, ilen_a ) ;
  rec_a[          0 ] = ANT_SYNC_BITE ;
  rec_a[          1 ] = iden_a ;
  rec_a[ ilen_a - 1 ] = ANT_SYNC_ENDREC ;

  /* Fill in any data fields that refer to this unit and which are
   * enabled.
   */
  for( ifield=0 ; ifield < antGlobal_c->bitex.ifield_cnt ; ifield++ ) {
    volatile struct bitex_field_def *pfield = &antGlobal_c->bitex.fields[ifield] ;

    if( (iunit_a == pfield->iunit) && (pfield->ldisabled == FALSE) ) {
      if( BTEST( antGlobal_c->binfo.data_mask[ ifield >> 5 ], (ifield & 0x1F)) ) {
	rec_a[ pfield->ibyte ] |= (1 << pfield->ibit) ;
      }
    }
  }
}
/* ------------------------------
 * Function to prune the net host table every 10 seconds.
 */
static void ant_nethost_check
(UINT4 iCurrentBTime_a     /* The current time */
 )
{
  static UINT4 iLastPruneTime=0;
  
  /* Prune the network host table if the maximum time has elapsed 
   * since we last did.  Note that on powerup, it might wait a random
   * time up to the max time.
   */
  if( (btimeGrace(iCurrentBTime_a - iLastPruneTime) > 10000) )
    {
      iLastPruneTime = iCurrentBTime_a;
      AntPruneNetHost(iCurrentBTime_a);
    }

}

/* ------------------------------
 * Set up control bits common to all formats
 */
static void set_xmt_other
(UINT1* str_a,
 UINT4 iformat_a,
 UINT4 ibtime_a )
{
  volatile struct antenna_info* pant;

  UINT4 ibits, ibit_mask;
  SINT4 ivalue;
  UINT2 lbset ;
  UINT1 imode;

  pant = &antGlobal_c->info;
  ibits     = pant->icbits;
  ibit_mask = pant->ic_mask;
  
  /* Scientific Atlanta controler only controls servo power
   */
  if (iformat_a == ANTFMT_XMTSA)
    {
      /* Servo power on/off
       */
      if( ibit_mask & ACF_SERVO )
	{
	  /* lsa_send_mof_c = TRUE; */
	  lbset = (0 != (ibits & ACF_SERVO)) ==
	          (0 != antGlobal_c->setup.cont_servo_power.lhigh) ;
	  if( lbset ) str_a[SA_MOF_OFFSET+4] = 'N';
	}
      return;
    }
  

  /* Signal Generator set to CW mode
   */
  if ( ibit_mask & ACF_SIGCW )
    {
      lbset = ( (0!=( ibits & ACF_SIGCW) ) ==
		(0!=antGlobal_c->setup.cont_sigcw.lhigh) );
      if( lbset ) str_a[5] |=  0x04;
    }

  /* Signal Generator on/off
   */
  if ( ibit_mask & ACF_SIGGEN )
    {
      lbset = (0 != (ibits & ACF_SIGGEN)) ==
	      (0 != antGlobal_c->setup.cont_siggen.lhigh) ;
      if( lbset ) str_a[5] |=  0x08;
    }

  /* Signal Generator desired setting is always sent
   */
  ivalue =  antGlobal_c->setup.isiggen_max - pant->idsiggen_value;
  str_a[8] = 0x7F & ivalue;

  /* Servo power on/off
   */
  if ( ibit_mask & ACF_SERVO )
    {
      lbset = (0 != (ibits & ACF_SERVO)) ==
	      (0 != antGlobal_c->setup.cont_servo_power.lhigh) ;
      if( lbset ) str_a[6] |= 0x02;
    }
  
  /* Radate on/off
   */
  if ( ibit_mask & ACF_RADIATE )
    {
      lbset = (0 != (ibits & ACF_RADIATE)) ==
 	      (0 != antGlobal_c->setup.cont_radiate.lhigh) ;
      if( lbset )
	str_a[6] |= 0x04;
      else
	str_a[6] |= 0x08;

      if (iformat_a == ANTFMT_XMTSA)
	isnooze_time_c = XMT_SNOOZE_SA ;
      else
	isnooze_time_c = XMT_SNOOZE ;
    }
  
  /* T/R power on/off
   */
  if ( ibit_mask & ACF_TRPOWER )
    {
      lbset = (0 != (ibits & ACF_TRPOWER)) ==
	      (0 != antGlobal_c->setup.cont_tr_power.lhigh) ;
      if( lbset ) str_a[6] |=  0x01;
    }
  
  /* Noise Source Generator on/off
   */
  if ( ibit_mask & ACF_NOISE )
    {
      lbset = (0 != (ibits & ACF_NOISE)) ==
	      (0 != antGlobal_c->setup.cont_noise.lhigh) ;
      if( lbset ) str_a[6] |=  0x20;
    }
  
  /* Set the reset line
   */
  if ( ibit_mask & ACF_RESET )
    {
      lbset = ( ( 0 != (ibits & ACF_RESET ) ) ==
		(0!=antGlobal_c->setup.cont_reset.lhigh) );
      if( lbset ) str_a[6] |=  0x40;

      /* Clear the reset bit after each transmission to make it into a pulse
       */
      if (ibits & ACF_RESET) pant->icbits &= ~ACF_RESET;
    }
  
  /* Control the pulsewidth
   */
  if( BTEST( pant->iDesiredPwbw, 0 )) str_a[6] |= 0x10;
  if( BTEST( pant->iDesiredPwbw, 1 )) str_a[5] |= 0x40;
  if ( ibit_mask & ACF_PW_UNSET )
    {
      if( BTEST( pant->iDesiredPwbw, 2 )) str_a[5] |= 0x20;
    }
  else
    {
      if (antGlobal_c->setup.cont_pw_unset.linitial_high) str_a[5] |= 0x20;
    }

  /* Add in RST mode information
   */
  imode = (pant->icurrent_rst_mode & 7) << 4;
  str_a[7] |= imode;

  /* Add in NST bits.  Force fault bits on for any site that has not
   * been updated within the maximum timeout period.
   */
  if (ibit_mask&ACF_NST)
    {
      UINT4 ifaults = pant->icurrent_nst_faults, isite ;

      /* Zero means disable timeout feature */
      if (pant->iNstTimeout)
	{
	  for( isite=0 ; isite < 4 ; isite++ ) {
	    if( btimeGrace( ibtime_a - pant->iNstUpdateTimes[isite] ) >
		pant->iNstTimeout ) ifaults |= IBSET(0,isite) ;
	  }
	}

      if( antGlobal_c->setup.cont_nst.lhigh ) imode = 0x0F &  ifaults ;
      else                                   imode = 0x0F & ~ifaults ;

      str_a[7] |= imode ;
    }

  /* Handle extended control packets.
   */
  switch( iformat_a ) {
  case ANTFMT_XMT05:
    /* Fill in the Dual RCP mode bits (if a non void request has been
     * made) for just a single transmission.
     */
    {
      UINT2 iDrcpMode = DRCP_MODE_VOID ;
      if( antGlobal_c->info.iDrcpModeReq != DRCP_MODE_VOID ) {
	iDrcpMode = ( antGlobal_c->info.iDrcpModeReq & 0x03 ) ;
	antGlobal_c->info.iDrcpModeReq = DRCP_MODE_VOID ;
      }
      str_a[13] |= (iDrcpMode << 5) ;
    }
    /* Fill in bits describing how and when voluntary flips between
     * systems can occur.
     */
    if( antGlobal_c->info.lDrcpRelinquishReq ) str_a[13] |= 0x10 ;
    if( antGlobal_c->info.lDrcpWouldUseReq   ) str_a[13] |= 0x08 ;

    /* Control the polarization
     */
    str_a[14] |= (0x07 & pant->iDesiredPolar);

    break ;
  }
}

/* ------------------------------
 * Convert velocity to Scientific Atlanta format
 */

#if 0 /* Scientific Atlanta controler only controls servo power */
static SINT2 ivel_to_sa
(FLT4 fvelocity_a )
{
  SINT4 ivelocity;
  ivelocity = NINT((32768.0/45.0) * fvelocity_a);
  return ivelocity;
}
#endif

static void send_xmt_data
(const UINT1 *str_a,
 int   iFD_a,
 UINT4 iformat_a,
 volatile struct io_information *pio_a,
 UINT4 ixmt_size_a,
 const char *sFormat_a )
{
  int icount;
  if (iformat_a == ANTFMT_XMTSA)
    {
      if (lsa_send_rte_c)
	{
	  antGlobal_c->info.lsa_ack = FALSE;
	  antGlobal_c->info.lsa_nack = FALSE;
	  icount = WriteInt( iFD_a, &str_a[SA_RTE_OFFSET], 18, sFormat_a, htonl(INADDR_ANY) );
	  if (icount==18)
	    pio_a->iout_cnt += 18;
	  else
	    pio_a->iout_ercnt += 18;
	  wait_for_ack( pio_a );
	}

      antGlobal_c->info.lsa_ack = FALSE;
      antGlobal_c->info.lsa_nack = FALSE;
      icount = WriteInt( iFD_a, &str_a[SA_MOD_OFFSET], 18, sFormat_a, htonl(INADDR_ANY) );
      if (icount==18)
	pio_a->iout_cnt += 18;
      else
	pio_a->iout_ercnt += 18;
      wait_for_ack( pio_a );

      if (lsa_send_pos_c)
	{
	  antGlobal_c->info.lsa_ack = FALSE;
	  antGlobal_c->info.lsa_nack = FALSE;
	  icount = WriteInt( iFD_a, &str_a[SA_POS_OFFSET], 18, sFormat_a, htonl(INADDR_ANY) );
	  if (icount==18)
	    pio_a->iout_cnt += 18;
	  else
	    pio_a->iout_ercnt += 18;
	  wait_for_ack( pio_a );
	}

      /*
	if (lsa_send_mof_c)
	{
	antGlobal_c->info.lsa_ack = FALSE;
	antGlobal_c->info.lsa_nack = FALSE;
	icount = WriteInt( iFD_a, &str_a[SA_MOF_OFFSET], 13,  sFormat_a, htonl(INADDR_ANY) );
	if (icount==13)
	pio_a->iout_cnt += 13;
	else
	pio_a->iout_ercnt += 13;
	wait_for_ack( pio_a );
	}
      */
    }
  else
    {
      icount = WriteInt( iFD_a, str_a, ixmt_size_a, sFormat_a, htonl(INADDR_ANY) );
      if (icount==ixmt_size_a)
	pio_a->iout_cnt += ixmt_size_a;
      else
	pio_a->iout_ercnt += ixmt_size_a;
    }
}

/* ------------------------------
 * Wait for response back from the SA controller.
 */
static void wait_for_ack
( volatile struct io_information *pio_a )
{
  UINT4 iwait_time;
  iwait_time = 0;
  while( iwait_time < 1000 )
    {
      if (antGlobal_c->info.lsa_ack) return;
      if (antGlobal_c->info.lsa_nack) 
	{
	  pio_a->iout_ercnt += 18;
	  return;
	}
      sig_microSleep( 100000 ) ; iwait_time += 100 ;
    }
}

/* ------------------------------
 * Get channel number, origin 0 from command line.
 */
static UINT4 get_channel
( int iargc_a,
  char **argv_a )
{
  int ichannel;

  if (iargc_a >= 2)
    {
      if (!strncmp( argv_a[1], "ANT_XMT", 7 ))
	{
	  ichannel = atol( &argv_a[1][7] );
	  if (ichannel == 2) return 1;
	}

    }
  return 0;
}
