/* *******************************************************************
 * *                                                                 *
 * *             Serial Line Antenna Reception Process               *
 * *                                                                 *
 * ******************************************************************* 
 * File: libs/antenna/ant_rcv.c
 *
 * COPYRIGHT (c) 1988, 1989, 1991, 1997, 1999, 2001, 2002, 2003, 2004 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 <locale.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

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

#include "ant_global.h"

static void  ant_rcv_loop
( int iFD_a,
  UINT4 ichannel_a,
  UINT2 iformat ) ;

static void rcv_sa_loop
( int iFD_a,
  UINT4 ichannel_a ) ;

static void AntInsertPacket_sa
( const UINT1 str_a[]      /*   Array of data received */
  ) ;

static void process_bite_packet
( const UINT1 str_a[],
  SINT4 ilength_a, SINT4 iunit_a,
  UINT4 iread_time_a ) ;

static UINT2 lcheck_bite_id
( volatile const struct bitex_setup *bitex_a,
  volatile const UINT1 str_a,
  volatile SINT4* ilength_a,
  volatile SINT4* iunit_a ) ;

static void process_time_packet
( const UINT1 str_a[],
  UINT4 iread_time_a ) ;

static void process_chat_packet
( volatile struct io_information *pio_a,
  const UINT1 str_a[]) ;

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

static void ant_rcv_exit_handler( void ) ;

static SINT4 iNetPID_c;

/* ------------------------------
 * This routine receives serial status information from the Radar/Antenna
 * controller and converts the stream to an internal representation.  All
 * reception protocols are handled by this one piece of code.
 */    
int main(int iargc_a, const char **sargv_a )
{
  MESSAGE iStatus ; UINT2 iformat ; UINT4 ichannel ;
  int iClientType, iFD ; volatile struct io_information *pio ;
  char sLogText[120] ;
      
  /* Map to the global section, and assign process priority.  The
   * calls to sig_signal here are for the benifit of testing by just
   * running the process separately.
   */
  setlocale( LC_ALL, "" ) ; iNetPID_c = 0 ;

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

  ichannel = get_channel( iargc_a, sargv_a ) ;
  if( ichannel == 1 ) iClientType = ANT_IAM_ANT_RCV1 ;
  else                iClientType = ANT_IAM_ANT_RCV0 ;

  iStatus = AntPowerUpMain( iClientType, 0 ) ;
  if ( iStatus != SS_NORMAL ) {
    sig_signal( iStatus ) ; exit(EXIT_FAILURE) ;
  }
  if( ichannel == 1 ) sig_establish( antSignalHandler, "ANT_RCV1" ) ;
  else                sig_establish( antSignalHandler, "ANT_RCV0" ) ;

  pio = &antGlobal_c->info.io[ichannel] ;
  atexit( ant_rcv_exit_handler ) ;

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

  iStatus = sig_setpriority( (SINT4)antGlobal_c->setup.ipri_antrcv ) ;
  if( iStatus != SS_NORMAL ) sig_signal(iStatus) ;

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

  /* Assign I/O channel for this antenna.  Use either the specified
   * serial channels, or a remote socket connection.
   */
  if ((ANTIFACE_ANTEXPORT == antGlobal_c->setup.iInterfaceType )&&(ichannel==0))
    {
      iStatus = iAntOpenSocket( &iFD ) ;
      if( iStatus != SS_NORMAL ) {
	pio->ircv_status = iStatus ; sig_signal( iStatus ) ;
	exit( EXIT_FAILURE ) ;
      }
      iStatus = WriteSocketString( iFD, "PollOn", FALSE );
      if (iStatus!=SS_NORMAL)
	{
	  sig_signal( iStatus ) ;
	  exit( EXIT_FAILURE ) ;
	}
    }      

  else if ((ANTIFACE_NETWORK == antGlobal_c->setup.iInterfaceType )&&(ichannel==0))
    {
      struct ant_net_spec Spec;
      Spec.sAddress   = (char*)antGlobal_c->setup.schannel1;
      Spec.iPort      = antGlobal_c->setup.iBaudRate1;
      Spec.iFlags     = ANTNET_FLG_RCV;
      Spec.sInterface = (char*)antGlobal_c->setup.sNetInterface1;
      iFD = AntNetRcvOpen( &Spec, &iNetPID_c ) ;
      sprintf( sLogText, "Network FD:%d, Child PID:%d", iFD, iNetPID_c ) ;
      antWriteLog( sLogText ) ;
      if( iFD == -1 ) exit( EXIT_FAILURE ) ;
    }
  else
    {
      volatile char *sptr ;
      
      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 ) ;
	if( ichannel == 1 ) {
	  spec.iBaud   = antGlobal_c->setup.iBaudRate2 ;
	  spec.iParity = antGlobal_c->setup.iParity2 ;
	} else {
	  spec.iBaud   = antGlobal_c->setup.iBaudRate1 ;
	  spec.iParity = antGlobal_c->setup.iParity1 ;
	}
	spec.lReadOnly = TRUE ; 
	
	iStatus = openTTYDev( &tty, &spec ) ;
	if( iStatus != SS_NORMAL ) {
	  pio->ircv_status = iStatus ; sig_signal( iStatus ) ;
	  exit( EXIT_FAILURE ) ;
	}
	iFD = fileno( tty.Rx ) ;
	
      } else {
	iFD = -1 ;
      }
    }

  /* Implement one of the serial protocols.  The format is latched
   * here to prevent problems if it gets changed in global memory.
   */
  if( ichannel == 1 ) iformat = antGlobal_c->setup.istatus2_format ;
  else                iformat = antGlobal_c->setup.istatus_format ;
  
  ant_rcv_loop( iFD, ichannel, iformat ) ;
  
  return 0 ;
}

/* ------------------------------
 * Process input data for all formats
 */
#define RCVREADSIZE   48	/* Default serial max read size */

static void ant_rcv_loop
( int iFD_a,			/* File descriptor for I/O */
  UINT4 ichannel_a,
  UINT2 iformat_a )		/* Antenna packet format */
{
  UINT1 str[200] ; UINT4 iptr, iend, ii, iReadTimeMs ;
  SINT4 iRcvPktSize, iLength, iBiteUnit, icount ;
  FLT4 fCharTimeMS ; UINT1 lUseTimeStamp ;
  volatile struct io_information *pio ;

  /* -------------------- */
  iptr = 0 ; iend = 0 ;

  /* Calculate the transmission time for a single character at the
   * stream's baud rate, on the assumption of 10-bits per character.
   */

  if ( ANTIFACE_SERIAL  == antGlobal_c->setup.iInterfaceType)
    {
      SINT4 iBaudRate ;
      if( ichannel_a ) iBaudRate = antGlobal_c->setup.iBaudRate2 ;
      else             iBaudRate = antGlobal_c->setup.iBaudRate1 ;
      fCharTimeMS = 1E3 * 10.0 / (FLT4)iBaudRate ;
    }
  else
    fCharTimeMS = 0.0;

  /* Set the status to OK, This tells iant_pwrp that the process is
   * running OK
   */
  pio = &(antGlobal_c->info.io[ichannel_a]) ;
  switch ( iformat_a )
    {
    case ANTFMT_RCVSA:
      rcv_sa_loop( iFD_a, ichannel_a ) ;
      return ;
      break ;

    case ANTFMT_RCV01:
      iRcvPktSize =  8 ; lUseTimeStamp = FALSE ;
      break ;

    case ANTFMT_RCV02:
    case ANTFMT_RCV04:
      iRcvPktSize = 16 ; lUseTimeStamp = antGlobal_c->setup.lUseTimeStamp ;
      break ;

    case ANTFMT_RCV03:
      iRcvPktSize = 47 ; lUseTimeStamp = antGlobal_c->setup.lUseTimeStamp ;
      break ;

    case ANTFMT_RCV05:
      iRcvPktSize = 24 ; lUseTimeStamp = antGlobal_c->setup.lUseTimeStamp ;
      break ;
      
    default:
      pio->ircv_status = ANT_BAD_STATUS ;
      sig_signal( ANT_BAD_STATUS ) ;
      exit( EXIT_FAILURE ) ;
      break ;
    }

  pio->ircv_status = SS_NORMAL ;

  while (1) {
    /* Make sure there is at least one character to do the test on
     */
    if (iptr+1 > iend )
      {
	ii = iend - iptr ;
	get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
      }

    switch( str[iptr] ) {
    case (UINT1)ANT_SYNC_ANT: /* ---- Antenna Status Packet ---- */
      /* Test that there are enough characters to complete the packet.
       * If not, then read some more:
       */
      if( (iptr + iRcvPktSize) > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Test that the format is valid for an antenna packet.
       */
      if ( lcheck_packet( &str[iptr], iRcvPktSize ) )
	{
	  /* First calculate a rough estimate of the time of arrival
	   * of the initial SYNC byte of the packet based solely on
	   * our wakeup time after the last read.  Then refine this
	   * estimate using the packet time stamp and an evolving
	   * association between it and BTIMEs.
	   */
	  UINT4 iSyncTimeMs;
	  if ( ANTIFACE_SERIAL == antGlobal_c->setup.iInterfaceType)
	    {
	      SINT4 iCharDelay = 3 + (iend - iptr) ;
	      iSyncTimeMs = iReadTimeMs - NINT( fCharTimeMS * (FLT4)iCharDelay ) ;
	    }
	  else
	    iSyncTimeMs = iReadTimeMs - (UINT4)antGlobal_c->setup.iNetDelayMs;
	      
	  /* Optionally adjust the time based on the time stamp
	   */
	  if( lUseTimeStamp )
	    {
	      static UINT1 lFirstCall=TRUE;
	      static UINT4 iLastSyncTimeMs = 0, iLastTimeStampMs = 0 ;
	      static UINT4 iLastReadTimeMs = 0;
	      static FLT4  fAverageErrorMs = 0.0;

	      /* Get the 14-bit timestamp from the packet
	       */
	      UINT4 iTimeStampMs = iParseTimeStamp( &str[iptr], iformat_a ) ;

	      /* On the first call, only initialize the saved state
	       */
	      if (!lFirstCall)
		{
		  SINT4 iReadTimeDiffMs = (iReadTimeMs-iLastReadTimeMs) ;

		  /* Also, no correction is applied if the period has been more 
		   * than 1/2 a second.  In such a case, reset the first flag.
		   */
		  if ( iReadTimeDiffMs < 500 )
		    {			  
		      UINT4 iPredictTimeMs = iLastSyncTimeMs +
			( (iTimeStampMs - iLastTimeStampMs) & 0x3FFF ) ;
		      SINT4 iPredictErrorMs = (SINT4)(iPredictTimeMs - iSyncTimeMs) ;

		      /* Also, no correction is applied if the prediction is way off.
		       */
		      if( abs( iPredictErrorMs ) < 100 )
			{
			  /* Apply the correction */
			  iSyncTimeMs = iPredictTimeMs ;

			  /* Compute IIR average prediction error */
			  fAverageErrorMs = (0.92*fAverageErrorMs) + 
			    (0.08*(FLT4)iPredictErrorMs);
			      
			  /* Adjust time by 1 ms to get near to zero offset */
			  if (fAverageErrorMs>0.75)
			    {
			      iSyncTimeMs--;
			      fAverageErrorMs-=1.0;
			    }
			  else if (fAverageErrorMs<-0.75)
			    {
			      iSyncTimeMs++;
			      fAverageErrorMs+=1.0;
			    }
			}
		    }
		  else
		    {
		      lFirstCall = TRUE;
		      fAverageErrorMs = 0.0;
		    }
		}
	      iLastSyncTimeMs  = iSyncTimeMs ; 
	      iLastTimeStampMs = iTimeStampMs ;
	      iLastReadTimeMs  = iReadTimeMs;
	      lFirstCall       = FALSE;
	    }
	  AntInsertPacket( &str[iptr], iSyncTimeMs, iformat_a, iRcvPktSize ) ;
	  iptr += iRcvPktSize ;
	}
      else
	{
	  /* Reception error, store away the current date
	   */
	  icount = (SINT4)(iend-iptr) ;
	  if (icount>ANTRCV_MAX_LENGTH) icount=ANTRCV_MAX_LENGTH ;
	  memmove( (void *)pio->sant_diagnostic, &str[iptr], icount ) ;
	  pio->iant_diag_size = icount ;

	  pio->iin_ercnt += 1 ;              /* Reception Error */
	  iptr += 1 ;
	}
      break ;

    case (UINT1)ANT_SYNC_BITE:  /* ---- BITE Status Packet ---- */
    case (UINT1)ANT_SYNC_QBITE: /* ---- Q-BITE Status Packet ---- */
      /* Get at least the ID byte
       */
      if( iptr+2 > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Put the received characters into the diagnostic buffer
       */
      icount = (SINT4)(iend-iptr) ;
      if( icount > BITE_MAX_LENGTH ) icount = BITE_MAX_LENGTH ;
      memmove( (void *)&antGlobal_c->binfo.sbite_diagnostic[0], &str[iptr], icount ) ;
      antGlobal_c->binfo.idiag_size = icount ;

      if ( lcheck_bite_id( &antGlobal_c->bitex, str[iptr+1],
			   &iLength, &iBiteUnit ) )
	{
	  /* Get at least the required number of bytes
	   */
	  if( iptr+iLength > iend )
	    {
	      ii = iend - iptr ;
	      get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	      getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	      break ;
	    }

	  if ( lcheck_packet( &str[iptr], iLength ) )
	    {
	      process_bite_packet
		( &str[iptr], iLength, iBiteUnit, iReadTimeMs ) ;
	      iptr += iLength ;
	    }
	  else
	    {
	      pio->iin_ercnt += 1 ; /* Reception Error */
	      iptr += 1 ;
	    }
	}
      else
	{
	  pio->iin_ercnt += 1 ;     /* Reception Error */
	  iptr += 1 ;
	}
      break ;
	  
    case (UINT1)ANT_SYNC_TIME: /* ---- Time Report Packet ---- */
      /* Make sure there are at least the 11 bytes in the packet
       */
      if( iptr+11 > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Test that the format is valid for a packet
       */
      if ( lcheck_packet( &str[iptr], 11 ) )
	{
	  process_time_packet( &str[iptr], iReadTimeMs ) ;
	  iptr += 11 ;
	}
      else
	{
	  pio->iin_ercnt += 1 ; /* Reception Error */
	  iptr += 1 ;
	}
      break ;
	  
    case (UINT1)ANT_SYNC_CHAT: /* ---- Chat-Mode Packet ---- */
      /* Make sure there are at least the 8 bytes in the packet
       */
      if( iptr+8 > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Test that the format is valid for a packet
       */
      if ( lcheck_packet( &str[iptr], 8 ) )
	{
	  process_chat_packet( pio, &str[iptr] ) ;
	  iptr += 8 ;
	}
      else
	{
	  pio->iin_ercnt += 1 ; /* Reception Error */
	  iptr += 1 ;
	}
      break ;
	  
    case (UINT1)ANT_SYNC_ASCMD: /* ---- Embedded ASCII Command ---- */
      /* Get at least the packet length
       */
      if( iptr+2 > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Get at least the required number of bytes
       */
      iLength = str[ iptr+1 ] + 3 ;
      if( iptr+iLength > iend )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, RCVREADSIZE ) ;
	  getbtime( &iReadTimeMs ) ; pio->iin_cnt += iend - ii ;
	  break ;
	}
      /* Handle the complete command string
       */
      if ( lcheck_packet( &str[iptr], iLength ) )
	{
	  str[ iptr + (iLength-1) ] = '\0' ;
	  AntSet( (const char *)&str[ iptr + 2 ] ) ; iptr += iLength ;
	}
      else
	{
	  pio->iin_ercnt += 1 ; /* Reception Error */
	  iptr += 1 ;
	}
      break ;

    default:		/* Unknown junk */
      /* Store away the current data only if longer than the last
       * reported record, minus the number of iterations through this
       * code.  The latter removes multiple reports of the same data
       * but permits new reports to follow later.
       */
      {
	SINT4 iErrPtr = iptr - ((iptr > 0) ? 1 : 0) ;
	SINT4 iErrCnt = MIN( (SINT4)(iend - iErrPtr), ANTRCV_MAX_LENGTH ) ;
	static SINT4 iHoldOff_c = 0 ;

	if (iErrCnt >= ( pio->iother_diag_size - iHoldOff_c++ ) ) {
	  if( antGlobal_c->setup.lLogPacketsErr ) {
	    antWriteLogBinary( "?", &str[iErrPtr], iErrCnt ) ;
	  }
	  memmove( (void *)pio->sother_diagnostic, &str[iErrPtr], iErrCnt ) ;
	  pio->iother_diag_size = iErrCnt ; iHoldOff_c = 0 ;
	}
      }
      pio->iin_ercnt += 1 ; /* Reception Error */
      iptr += 1 ;

      break ;
    }
  }
}

/* ------------------------------
 * Process input data for all formats
 */
static void rcv_sa_loop( int iFD_a, UINT4 ichannel_a )
{
  UINT1 str[200] ; UINT4 iptr, iend, ii ;
  SINT4 iRcvPktSize, icount, ineeded ;
  volatile struct io_information *pio ;

  iptr = 0 ; iend = 0 ;

  /* Set the status to OK, This tells iant_pwrp that the process is
   * running OK
   */
  pio = &(antGlobal_c->info.io[ichannel_a]) ;
  iRcvPktSize = 5 ;

  pio->ircv_status = SS_NORMAL ;

  while (1)
    {
      /* Test that there are enough characters to complete the packet.
       * If not, then read some more:
       */
      ineeded = (iptr+iRcvPktSize) - iend ;
      if( ineeded > 0 )
	{
	  ii = iend - iptr ;
	  get_more_chars( iFD_a, str, &iptr, &iend, ineeded ) ;
	  pio->iin_cnt += iend - ii ;
	  continue ;
	}
      
      /* Test that the format is valid, for an antenna packet.
       */
      if ( lcheck_packet_sa( &str[iptr], iRcvPktSize ) )
	{
	  AntInsertPacket_sa( &str[iptr] ) ;
	  iptr += iRcvPktSize ;
	}
      else
	{
	  /* Reception error, store away the current date
	   */
	  icount = (SINT4)(iend-iptr) ;
	  if (icount>ANTRCV_MAX_LENGTH) icount=ANTRCV_MAX_LENGTH ;
	  memmove( (void *)pio->sant_diagnostic, &str[iptr], icount ) ;
	  pio->iant_diag_size = icount ;
	  
	  pio->iin_ercnt += 1 ;              /* Reception Error */
	  iptr += 1 ;
	}

    }  /* end while */
}

/* ------------------------------
 */
static void AntInsertPacket_sa
( const UINT1 str_a[]		/* Array of data received */
  )
{
  volatile struct antenna_info *pant = &antGlobal_c->info ;

  if( str_a[2] == 0x06 ) pant->lsa_ack  = TRUE ;
  else                   pant->lsa_nack = TRUE ;
}

/* ------------------------------
 * Checks a character to see if it matches the ID character of any of the
 * BITE units expected.  If so, return true, and return the transmission
 * length
 */
#define FAKEUNIT_INTERROGATE -1	/* A nonreal unit number */

static UINT2 lcheck_bite_id
( volatile const struct bitex_setup *bitex_a,
  volatile const UINT1 str_a,
  volatile SINT4* ilength_a,
  volatile SINT4* iunit_a )
{
  if( str_a == BITE_INTERROGATE ) {
    /* Handle special case of an interrogate command being sent to
     * us from an external device.
     */
    *ilength_a = 3 ;
    *iunit_a   = FAKEUNIT_INTERROGATE ;
    return( TRUE ) ;

  } else {
    /* Check for normal BITE packed from an external device
     */
    SINT2 icount, iunit ;

    icount = bitex_a->iunit_cnt ;
    if (icount>BITE_MAX_UNITS) icount=BITE_MAX_UNITS ;
    if (icount<1) icount=1 ;

    /* Loop through all the units
     */
    for ( iunit=0 ; iunit<icount ; iunit++ )
      {
	/* If the unit id matches, then return length and TRUE
	 */
	if (bitex_a->units[iunit].iunit_id == str_a)
	  {
	    *ilength_a = bitex_a->units[iunit].iunit_length ;
	    *iunit_a   = iunit ;
	    return( TRUE ) ;
	  }
      }
  }
  return( FALSE ) ;
}

/* ------------------------------
 * Process a correctly formatted packet of information from a BITE unit.
 */
static void  process_bite_packet
( const UINT1 str_a[], SINT4 ilength_a, SINT4 iunit_a,
  UINT4 iread_time_a )
{
  if( iunit_a == FAKEUNIT_INTERROGATE ) {
    /* Handle special case of interrogate packet
     */
    antGlobal_c->binfo.lcnt_send_asap = TRUE ;

  } else {
    /* Update the input count, current time, and last string received
     */
    antGlobal_c->binfo.Unit[iunit_a].iin_cnt += ilength_a ;
    antGlobal_c->binfo.Unit[iunit_a].ibtime = iread_time_a ;
    memmove( (void *)antGlobal_c->binfo.Unit[iunit_a].sbite_reception, str_a, ilength_a ) ;

    /* Optionally log the I/O packet.
     */
    if( antGlobal_c->setup.lLogPacketsBITE ) {
      antWriteLogBinary( ":", str_a, ilength_a ) ;
    }
    /* Make a history entry holding the most recent packet from each
     * individual BITE unit.
     */
    antMakeHistoryEntry( str_a, ilength_a, ANTGET_RCV_MASK, 
			 &antGlobal_c->vdat.updHist.bite[ iunit_a] ) ;

    /* Now recompute all derived information
     */
    bite_local_update(iunit_a) ;
    bite_fault_update() ;
  }
}

/* ------------------------------
 * Process a packet conveying the current time.
 */
static void process_time_packet
( const UINT1 str_a[],
  UINT4 iread_time_a )
{
  SINT2 ihour, iminute, isecond ;
  struct ymds_time time ;

  /* Update the time received, current time
   */
  antGlobal_c->info.itime_btime = iread_time_a ;
  antGlobal_c->info.time_in_packet.iyear = 
    (SINT2) ( (((UINT2)str_a[2])<<7) | ((UINT2)str_a[1]) ) ;
  antGlobal_c->info.time_in_packet.imon = (SINT2)(UINT2)str_a[3] ;
  antGlobal_c->info.time_in_packet.iday = (SINT2)(UINT2)str_a[4] ;
  ihour   = (SINT2)(UINT2)str_a[5] ;
  iminute = (SINT2)(UINT2)str_a[6] ;
  isecond = (SINT2)(UINT2)str_a[7] ;
  antGlobal_c->info.time_in_packet.isec = iPackTime( ihour, iminute, isecond ) ;
  antGlobal_c->info.time_in_packet.imills = YMDS_MASK_MS & ( 10*(UINT2)str_a[8]) ;

  if (antGlobal_c->setup.lTimePacketUtc)
    antGlobal_c->info.time_in_packet.imills|=YMDS_FLG_UTC ;

  GetTimeYmds( &time, antGlobal_c->setup.lTimePacketUtc ) ;  

  antGlobal_c->info.itime_error =
    iYmdsSubtractMs( &antGlobal_c->info.time_in_packet, &time ) ;

  antGlobal_c->info.ltime_packet_received = TRUE ;
  antGlobal_c->info.ltime_set_from_packet = FALSE ;

  /* Optionally log the packet
   */
  if( antGlobal_c->setup.lLogPacketsTime ) {
    antWriteLogBinary( ":", str_a, 11 ) ;
  }
}

/* ------------------------------
 * Process a chat-mode received packet.  These contain up to six 7-bit
 * bytes.
 */
static void process_chat_packet
( volatile struct io_information *pio_a,
  const UINT1 str_a[] )
{
  SINT4 ii ;
  for( ii=1 ; ii < 7 ; ii++ ) {
    if( 0x00 == str_a[ii] ) break ;
    if(   -1 == bfifo_write( (void *)&antGlobal_c->vdat.ChatIn, str_a[ii] ) )
      pio_a->iin_ercnt += 1 ;
  }
}

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

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

/* ------------------------------
 * RCV exit handler
 */
static void ant_rcv_exit_handler( void )
{
  MESSAGE istatus ; char sLogText[120] ;

  sprintf( sLogText, "ant_rcv_exit_handler PID:%d NetPID:%d",
	   getpid(), iNetPID_c ) ;
  antWriteLog( sLogText ) ;

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

  if( iNetPID_c ) {
    if( kill( (pid_t)iNetPID_c, SIGTERM ) == -1 )
      sig_signal( errno_to_message( errno ) ) ;
  }
  istatus = sig_kill_process(iNetPID_c);
  if( istatus != SS_NORMAL ) sig_signal( istatus ) ;
}
