/* **********************************************************************
 * *                                                                    *
 * *           Simulation of Interference Rejection Filters             *
 * *                                                                    *
 * **********************************************************************
 * File: utils/examples/interference.C
 *
 *                  COPYRIGHT (c) 2000, 2002, 2003  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.
 */

/* This collection of routines evaluates the performance of pulse
 * interference filters that could be incorporated into weather radar
 * signal processing algorithms.  The simulation is done using time
 * series from realistic weather targets in the presence of external
 * interference.
 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "sigtypes.h"
#include "himath_lib.h"
#include "user_lib.h"

/* Tunable parameters
 */
#define SIMSIZE_LOG    (10)	/* Base-2 LOG of simulation length */
#define SIMSIZE  (1 << SIMSIZE_LOG)

#define ITERS          150	/* Number of times to repeat the whole test */

#define FWIDTH         0.1	/* Spectrum width of weather (0-1) */
#define FSIGPOWER      1.0	/* Signal and noise powers are in */
#define FNSEPOWER      0.1	/*   linear units (not dB) */

#define FINTPROB0      0.05	/* Interference probability if last pulse had none */
#define FINTPROB1      0.25	/* Conditional probability when last pulse had some */
#define FINTRDBS    { 6.0, 10.0, 16.0, 26.0 }

#define NCONSTS        10	/* Trial constants for use by the alorithms */
#define FCONSTS  { 6.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 16.0, 20.0 }

#define NVERSIONS      5	/* Number of versions of the algorithm being tried */

/* Function prototypes for internal routines.
 */
struct results {
  SINT4 iSamps ;		/* Total number of samples processed */
  SINT4 iInterferes ;		/* Total number of actual interference points */
  SINT4 iErrors ;		/* Did not reject a sample that was interference */
  SINT4 iFalseHits ;		/* Rejected a sample that was not interference */
} ;

void algorJma
  (struct results *results_a,	/* Returned results */
   SINT4 iversion_a,
   FLT4 fRcvLin_a[SIMSIZE],	/* Linear received powers */
   FLT4 fRcvLog_a[SIMSIZE],	/* LOG received powers */
   FLT4 finterfere_a[SIMSIZE],	/* Interference powers */
   FLT4 fConst_a) ;		/* Threshold constant (dB) */

void printResults( struct results *results_a ) ;

static void initResults
  (struct results *results_a,
   SINT4 idim_a ) ;

static void tsgen_gaussian
  (FLT4 fspec_a[SIMSIZE],	/* Power Spectrum In/Out */
   FLT4 fvel_a,			/* Velocity: -1 to +1 */
   FLT4 fwidth_a,		/*    Width:  0 to  1 */
   FLT4 fpower_a,		/* Signal Power */
   FLT4 fnoise_a) ;		/*  Noise Power */

void tsgen_mkseries
  (FLT4 fseries_a[SIMSIZE][2],
   FLT4 fspec_a[SIMSIZE]) ;

#define FLT4SIZE(ARRAY) ( sizeof(ARRAY) / sizeof(FLT4) )

/* ==================================================
 */
int main( int argc_a, char **argv_a )
{
  FLT4 fspec[SIMSIZE], fiqseries[SIMSIZE][2] ;
  FLT4 finterfere[SIMSIZE], fRcvLin[SIMSIZE], fRcvLog[SIMSIZE] ;
  FLT4 fIntrDBs[] = FINTRDBS, fConsts[NCONSTS] = FCONSTS ;
  struct results resJma[NVERSIONS][NCONSTS] ;
  SINT4 ii, iters, iPwrIntr ;

  /* Calculate the theoretical error probabilities for the traditional
   * JMA algorithm, and the variations we will be testing.
   */
  {
    FLT4 fp = FINTPROB0 ;	      /* Prob of 1, given last was 0 */
    FLT4 fq = FINTPROB1 ;	      /* Prob of 1, given last was 1 */
    FLT4 fa = fp / (1.0 + fp - fq) ;  /* Prob of 1 in overall sample set */

    FLT4 f001 = (1.0 - fa) * (1.0 - fp) * fp ;
    FLT4 f011 = (1.0 - fa) *        fp  * fq ;
    FLT4 f101 =        fa  * (1.0 - fq) * fp ;

    printf( "JMA-0,1 algorithm theoretical error rate: %.1f%%\n",
	    1E2 * (1.0 - ( f001                / fa) ) ) ;
    printf( "JMA-2   algorithm theoretical error rate: %.1f%%\n",
	    1E2 * (1.0 - ((f001 + f011 + f101) / fa) ) ) ;
  }

  /* Run simulations with several different power levels of the
   * interfering signal.
   */
  for( iPwrIntr=0 ; iPwrIntr < (SINT4)FLT4SIZE(fIntrDBs) ; iPwrIntr++ ) {
    FLT4 fintpower = pow( 10.0, (0.1 * fIntrDBs[ iPwrIntr ]) ) ;
    SINT4 iversion ;

    /* Initialize results structures for each algorithm.
     */
    for( ii=0 ; ii < NVERSIONS ; ii++ )
      initResults( resJma[ii], NCONSTS ) ;

    /* Loop the analysis several times to get good statistics.
     */
    for( iters=0 ; iters < ITERS ; iters++ ) {

      /* Make up the time series for the simulated weather target.
       * The velocity is fixed at 0.5, as it will not affect the
       * interference algorithms, all of which are based only on (I,Q)
       * magnitudes.
       */
      for( ii=0 ; ii < SIMSIZE ; ii++ ) fspec[ii] = 0.0 ;

      tsgen_gaussian( fspec, 0.5, FWIDTH, FSIGPOWER, FNSEPOWER ) ;
      tsgen_mkseries( fiqseries, fspec ) ;

      /* Generate simulated interference signal.  This consists of a
       * sparce array of non-zero additive power levels, perhaps
       * having a tendency to arrive in bursts.
       */
      finterfere[0] = 0.0 ;
      for( ii=1 ; ii < SIMSIZE ; ii++ ) {
/*  	FLT4 fprob ; */

	static SINT4 iphase = 0 ;
	if( (ii > 5) && ((iphase++ % 59) == 0) ) finterfere[ii] = fintpower ;
	else                                     finterfere[ii] = 0.0 ;

/*  	if( finterfere[ii-1] > 0.0 ) fprob = FINTPROB1 ; */
/*  	else                         fprob = FINTPROB0 ; */

/*  	if( drand48() < fprob ) finterfere[ii] = fintpower ; */
/*  	else                    finterfere[ii] = 0.0 ; */
      }

      /* Compute the array of power levels that would actually be
       * measured.  This is the sum of the weather target plus the
       * random interference term.
       */
      for( ii=0 ; ii < SIMSIZE ; ii++ ) {
	FLT4 fpower ;

	fpower = SQUARE( fiqseries[ii][0] ) + SQUARE( fiqseries[ii][1] ) ;
	fpower += finterfere[ii] ;

	fRcvLin[ii] = fpower ;
	fRcvLog[ii] = 10.0 * log10( fpower ) ;
      }

      /* Evaluate various trial algorithms on the received signal.
       */
      for( iversion=0 ; iversion < NVERSIONS ; iversion++ ) {
	for( ii=0 ; ii < NCONSTS ; ii++ ) {
	  algorJma( &resJma[iversion][ii], iversion,
		    fRcvLin, fRcvLog, finterfere, fConsts[ii] ) ;
	}
      }

    } /* End loop in ITERS */

    /* Print results for this run
     */
    printf(
      "\n"
      "=================== Results for %4.1fdB Interference ==================\n"
      "\n"
      "             JMA-0          JMA-1          JMA-2          JMA-3          JMA-4    \n"
      "  Const   Missed/False   Missed/False   Missed/False   Missed/False   Missed/False\n"
      "  -----   ------------   ------------   ------------   ------------   ------------\n",
      fIntrDBs[ iPwrIntr ] ) ;

    for( ii=0 ; ii < NCONSTS ; ii++ ) {
      printf( "%5.1fdB", fConsts[ii] ) ;
      for( iversion=0 ; iversion < NVERSIONS ; iversion++ )
	printResults( &resJma[iversion][ii] ) ;
      printf( "\n" ) ;
    }

  } /* End loop on interference powers */

  return( EXIT_SUCCESS ) ;
}

/* ==================================================
 * Traditional JMA Interference Algorithm
 */
void algorJma
  (struct results *results_a,	/* Returned results */
   SINT4 iversion_a,
   FLT4 fRcvLin_a[SIMSIZE],	/* Linear received powers */
   FLT4 fRcvLog_a[SIMSIZE],	/* LOG received powers */
   FLT4 finterfere_a[SIMSIZE],	/* Interference powers */
   FLT4 fConst_a)		/* Threshold constant (dB) */
{
  SINT4 ipulse ;

  for( ipulse=4 ; ipulse < SIMSIZE ; ipulse++ ){
    UINT1 lReject = FALSE ;
    FLT4 fDBa = fRcvLog_a[ ipulse   ] ;
    FLT4 fDBb = fRcvLog_a[ ipulse-1 ], fb = fRcvLin_a[ ipulse-1 ] ;
    FLT4 fDBc = fRcvLog_a[ ipulse-2 ], fc = fRcvLin_a[ ipulse-2 ] ;
/*      FLT4 fDBd = fRcvLog_a[ ipulse-3 ], fd = fRcvLin_a[ ipulse-3 ] ; */
/*      FLT4 fDBe = fRcvLog_a[ ipulse-4 ], fe = fRcvLin_a[ ipulse-4 ] ; */

    FLT4 fDBbc = 10.0 * log10( 0.5 * (fb + fc) ) ;

    switch( iversion_a ) {
    case 0:
      /* The traditional JMA algorithm uses three pulses, and tests
       * whether the first two powers (in dB) are sufficiently close
       * together, and the last two are sufficiently far apart.
       */
      lReject =
	( fabs(fDBb - fDBc) < fConst_a ) &&
	( fabs(fDBa - fDBb) > fConst_a ) ;
      break ;

    case 1:
      /* Variation #1 uses the same three pulses, but the knowledge
       * that interference is additive, hence the second absolute
       * value term really is making things worse.  Replace it with a
       * signed comparison against the mean of the two (assumed) clean
       * pulses.
       */
      lReject =
	( fabs(fDBb - fDBc) < fConst_a ) &&
	(     (fDBa - fDBb) > fConst_a ) ;
      break ;

    case 2:
      lReject =
	( fabs(fDBb - fDBc ) < (fConst_a - 0.0) ) &&
	(     (fDBa - fDBbc) > (fConst_a + 0.0) ) ;

      /* Variation #2 uses the same three pulses, but uses the minimum
       * value of the first two pulses as a guess of the non interfered
       * signal power.
       */
/*        lReject = */
/*  	( fDBa - MIN( fDBb, fDBc ) ) > fConst_a ; */
      break ;

    case 3:
      lReject =
	( fabs(fDBb - fDBc ) < (fConst_a + 2.0) ) &&
	(     (fDBa - fDBbc) > (fConst_a - 0.0) ) ;

      /* Variation #3 is similar to #1, but uses one extra pulse and
       * looks for any pair of prior values that are close enough
       * together, and whose mean is far enough from the test bin.
       */
/*        lReject |= */
/*  	( fabs(fDBb - fDBc)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBb + fDBc)) > fConst_a ) ; */

/*        lReject |= */
/*  	( fabs(fDBb - fDBd)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBb + fDBd)) > fConst_a ) ; */

/*        lReject |= */
/*  	( fabs(fDBc - fDBd)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBc + fDBd)) > fConst_a ) ; */
      break ;

    case 4:
      lReject =
	( fabs(fDBb - fDBc ) < (fConst_a + 0.0) ) &&
	(     (fDBa - fDBbc) > (fConst_a + 5.0) ) ;

      /* Like #3, except always uses all three history points.
       */
/*        lReject |= */
/*  	( fabs(fDBb - fDBc) < fConst_a ) && */
/*  	( fabs(fDBb - fDBd) < fConst_a ) && */
/*  	( fabs(fDBc - fDBd) < fConst_a ) && */
/*  	( (fDBa - ((fDBb + fDBc + fDBd) / 3.0)) > fConst_a ) ; */

/*        lReject |= */
/*  	( fabs(fDBb - fDBc)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBb + fDBc)) > fConst_a ) && */
/*  	( (fDBd - 0.5*(fDBb + fDBc)) > fConst_a ) ; */

/*        lReject |= */
/*  	( fabs(fDBb - fDBd)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBb + fDBd)) > fConst_a ) && */
/*  	( (fDBc - 0.5*(fDBb + fDBd)) > fConst_a ) ; */

/*        lReject |= */
/*  	( fabs(fDBc - fDBd)          < fConst_a ) && */
/*  	( (fDBa - 0.5*(fDBc + fDBd)) > fConst_a ) && */
/*  	( (fDBb - 0.5*(fDBc + fDBd)) > fConst_a ) ; */
      break ;
    }

    /* False alarms are rejected points that we actually okay.  Errors
     * are passed points that actually were interference.
     */
    if( lReject ) {
      if( finterfere_a[ipulse] == 0.0 ) results_a->iFalseHits++ ;
    } else {
      if( finterfere_a[ipulse] >  0.0 ) results_a->iErrors++ ;
    }

    /* Increment sample and interference counts
     */
    if( finterfere_a[ipulse] >  0.0 ) results_a->iInterferes++ ;
    results_a->iSamps++ ;
  }
}

/* ==================================================
 * Print percentage of missed interference pulses, and false alarms
 * (pulses incorrectly identified as interference
 */
void printResults( struct results *results_a )
{
  printf( "  %5.1f%% %5.2f%%",
	  1E2 * ((FLT4)results_a->iErrors)    / (FLT4)results_a->iInterferes,
	  1E2 * ((FLT4)results_a->iFalseHits) / (FLT4)results_a->iSamps ) ;
}

/* ==================================================
 * Initialize a "results" structure.
 */
static void initResults
  (struct results *results_a,
   SINT4 idim_a )
{
  struct results *presults = results_a ; SINT4 idim ;
  for( idim=0 ; idim < idim_a ; idim++, presults++ ) {
    presults->iSamps      = 0 ;
    presults->iInterferes = 0 ;
    presults->iErrors     = 0 ;
    presults->iFalseHits  = 0 ;
  }
}

/* ==================================================
 * Add a Gaussian target to a power spectrum.
 */
static void tsgen_gaussian
  (FLT4 fspec_a[SIMSIZE],	/* Power Spectrum In/Out */
   FLT4 fvel_a,			/* Velocity: -1 to +1 */
   FLT4 fwidth_a,		/*    Width:  0 to  1 */
   FLT4 fpower_a,		/* Signal Power */
   FLT4 fnoise_a)		/*  Noise Power */
{
  SINT4 ii ; FLT4 fx ;

  if( fpower_a > 0.0 ) {
    SINT4 ivel ; FLT4 fwidth, ftotal, fspec[SIMSIZE] ;

    /* Determine center velocity, and scale factors for width.  If the width
     * is very narrow then only fill in the center point.  Otherwise compute
     * the normal distribution and fill in the curve.
     */
    ivel = (NINT( fvel_a * (FLT4)SIMSIZE/2 )) & (SIMSIZE-1) ;

    for( ii=0 ; ii < SIMSIZE ; ii++ ) fspec[ii] = 0.0 ;

    fspec[ivel] = 1.0 ; ftotal = 1.0 ;
    fwidth = MIN( 10.0, MAX( 0.0, fwidth_a ) ) * (FLT4)(SIMSIZE/2) ;
    if( fwidth > 0.01 ) {
      FLT4 fy, fpow ; SINT4 iup, idn ;

      iup = ivel+1 ; idn = ivel-1 ;
      for( fx = 1.0 ; ; fx += 1.0 ) {
	fpow = exp( -0.5 * (fx*fx) / (fwidth*fwidth) ) ; if( fpow < 1E-6 ) break ;

	if( iup > SIMSIZE ) iup = 0         ;
	if( idn < 0       ) idn = SIMSIZE-1 ;

	/* Apply Rayleigh distribution to the Gaussian values.
	 */
	fy = fpow * -log( drand48() ) ; fspec[iup++] += fy ; ftotal += fy ;
	fy = fpow * -log( drand48() ) ; fspec[idn--] += fy ; ftotal += fy ;
      }
    }
    /* Normalize so that the total power is scaled to what was requested,
     * then add into the spectrum so far.
     */
    fx = fpower_a / ftotal ;
    for( ii=0 ; ii < SIMSIZE ; ii++ ) fspec_a[ii] += fx * fspec[ii] ;
  }

  /* If noise power has been requested, then add in a uniform value
   * at all frequencies
   */
  if( fnoise_a > 0.0 ) {
    fx = fnoise_a / (FLT4)SIMSIZE ;
    for( ii=0 ; ii < SIMSIZE ; ii++ ) fspec_a[ii] += fx ;
  }
}

/* ==================================================
 * Generate floating complex time series from a power spectrum
 */
void tsgen_mkseries
  (FLT4 fiqseries_a[SIMSIZE][2],
   FLT4 fspec_a[SIMSIZE])
{
  SINT4 ii ; FLT4 fx, famp ; MESSAGE istatus ;

  /* Convert the power spectrum to an amplitude spectrum via a square
   * root.  Then multiply by a random phasor to get the complex DFT of
   * the desired time series.  Finally, do an inverse DFT to get the
   * time series themselves.
   */
  for( ii=0 ; ii<SIMSIZE ; ii++ ) {
    famp = sqrt( fspec_a[ii] ) ; fx = 2.0 * FPI_P * drand48() ;
    fiqseries_a[ii][0] = famp * cos(fx) ;
    fiqseries_a[ii][1] = famp * sin(fx) ;
  }

  istatus = fft_complex(&fiqseries_a[0][0], &fiqseries_a[0][1], 2,
			&fiqseries_a[0][0], &fiqseries_a[0][1], 2,
			SIMSIZE_LOG, FFT_INVERSE ) ;

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