/* **********************************************************************
 * *                                                                    *
 * *    Utility to generate a PPI product with an airplane track        *
 * *                                                                    *
 * **********************************************************************
 * File: /utils/examples/track_sim.C
 *
 *                        COPYRIGHT (c) 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 <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "sigtypes.h"
#include "headers.h"
#include "product.h"
#include "projection.h"
#include "misc_lib.h"
#include "user_lib.h"

#define RESOLUTION (300)

static void FillProductHeader
( struct product_hdr *phdr, 
  const struct full_time_spec *p_time,
  const struct track_spec *pTrack_a );

static void FillProductData
( UINT1 ppi_data_a[RESOLUTION][RESOLUTION],
  const struct track_spec *pTrack_a );

static void PromptForFloat
( const char *sPrompt_a,
  float *fValue_a );

static void PromptForString
( const char *sPrompt_a,
  char *sString_a,
  int iStringLength_a );   /* Length of storage for the string */

struct track_spec
{
  float fdist_north, fdist_east;
  float fHeading, fSpeed;
  float fStrength;
  float fSpeckleCount;
  float fTrackProbability;
  int use_clutter, clutter_n, clutter_s, clutter_e, clutter_w;
  char  sSiteName16[20];    /* Null terminated site name */
  char  sProductName[16];   /* Null terminated product name */
};

int main()
{
  struct track_spec Track;
  float center_n, center_e, len_ns, len_ew;
  char sdone_str[16];
  int i=0, imin, isec, pcount = 1000;
  char smin[4], ssec[4], out_file_name[100];
  FILE *out_file;
  UINT1 ppi_data[RESOLUTION][RESOLUTION];
  struct product_hdr phdr;
  struct full_time_spec p_time;

  memset( &Track, 0, sizeof(Track) );

  strcpy( Track.sSiteName16, "SBX-1" );
  PromptForString("Enter site name (default %s): ", Track.sSiteName16, 20 );

  strcpy( Track.sProductName, "TRACK_SIM" );
  PromptForString("Enter product name (default %s): ", Track.sProductName, 16 );

  Track.fdist_north = 50.0;
  PromptForFloat("Enter Start Position North of Radar (default %3.0f km): ", 
		 &Track.fdist_north);

  Track.fdist_east = -65.0;
  PromptForFloat("Enter Start Position East  of Radar (default %3.0f km): ", &Track.fdist_east);

  Track.fHeading = 90.0;
  PromptForFloat("Enter Start heading (default %2.0f Degrees): ", &Track.fHeading );

  
  Track.fSpeed = 400.0;
  PromptForFloat("Enter Start Speed (default %2.0f Knots): ", &Track.fSpeed );

  Track.fStrength = 20.0;
  PromptForFloat("Enter strength of Target (default %2.0f dBZ): ", &Track.fStrength );

  Track.fSpeckleCount = 20.0;
  PromptForFloat("How many speckles (default %2.0f): ", &Track.fSpeckleCount );

  Track.fTrackProbability = 90.0;
  PromptForFloat("Detection probability (default %2.0f): ", &Track.fTrackProbability );

  strcpy( sdone_str, "No" );
  PromptForString("Use a clutter field? (default %s): ", sdone_str, 16 );
  if ((sdone_str[0]=='y')||(sdone_str[0]=='Y'))
    {
      Track.use_clutter = 1;
      center_n = 50.0;
      PromptForFloat("Center Position of Field North of Radar (default %2.0f km): ",
		     &center_n );
      center_e = -50.0;
      PromptForFloat("Center Position of Field East  of Radar (default %2.0f km): ",
		     &center_e );
      len_ns = 15.0;
      PromptForFloat("North/South width of Field (default %2.0f km): ", &len_ns);
      len_ew = 25.0;
      PromptForFloat("East/West width of Field (default %2.0f km): ", &len_ew);

      Track.clutter_n = (int)(RESOLUTION/2 + (center_n * 2) + len_ns + 0.5);
      if (Track.clutter_n >= RESOLUTION) Track.clutter_n = RESOLUTION-1;
      if (Track.clutter_n < 0) Track.use_clutter = 0;
      Track.clutter_s = (int)(RESOLUTION/2 + (center_n * 2) - len_ns + 0.5);
      if (Track.clutter_s >= RESOLUTION) Track.use_clutter = RESOLUTION-1;
      if (Track.clutter_s < 0) Track.clutter_s = 0;
      Track.clutter_w = (int)(RESOLUTION/2 + (center_e * 2) - len_ew + 0.5);
      if (Track.clutter_w >= RESOLUTION) Track.use_clutter = RESOLUTION-1;
      if (Track.clutter_w < 0) Track.clutter_w = 0;
      Track.clutter_e = (int)(RESOLUTION/2 + (center_e * 2) + len_ew + 0.5);
      if (Track.clutter_e >= RESOLUTION) Track.clutter_e = RESOLUTION-1;
      if (Track.clutter_e < 0) Track.use_clutter = 0;
    }

  strcpy(&p_time.sLocalTZName[0], "UTC");
  strcpy(&p_time.sRecordTZName[0], "UTC");
  p_time.iRelDay = 0;
  p_time.iMinutesWest = 0;
  p_time.iLocalWest = 0;
  p_time.iJulianDay = 0;
  p_time.Ymds.iyear = 2004;
  p_time.Ymds.imon = 3;
  p_time.Ymds.iday = 1;
  p_time.Ymds.isec = 0;
  p_time.Ymds.imills = 0;

  FillProductHeader(&phdr, &p_time, &Track );

  FillProductData( ppi_data, &Track );
 
  isec = p_time.Ymds.isec % 60;
  sprintf(ssec, "0%d", isec);
   
  imin = p_time.Ymds.isec / 60; 
  sprintf(smin, "0%d", imin);
  sprintf(out_file_name, "SBX04030100%s%s.PPI%d", 
      &smin[strlen(smin)-2], &ssec[strlen(ssec)-2], pcount++);
  out_file = fopen(out_file_name, "wb"); 
  fwrite(&phdr, sizeof(struct product_hdr), 1, out_file);
  fwrite(ppi_data, 1, (RESOLUTION * RESOLUTION), out_file);
  fclose(out_file);

  while (TRUE)
    {
      float accel, turn, fTime;
      printf("\nManeuver %d\n\n", ++i);
      accel = 0.0;
      PromptForFloat("Enter Accelaration (default %1.0f knots/sec): ", &accel ); 
      turn = 0.0;
      PromptForFloat("Enter turn amount (default %1.0f deg/sec): ", &turn );
      fTime = 60.0;
      PromptForFloat("Enter Time in Maneuver (default %1.0f Seconds): ", &fTime);
      
      while (fTime > 0.0) 
	{
	  Track.fSpeed += (accel * 10.0); 
	  Track.fHeading += (turn * 10.0);
	  Track.fdist_north += Track.fSpeed * cos(Track.fHeading * FPI / 180.0) / 360.0 * 1.852;
	  Track.fdist_east  += Track.fSpeed * sin(Track.fHeading * FPI / 180.0) / 360.0 * 1.852;
	  
	  strcpy(&p_time.sLocalTZName[0], "UTC");
	  strcpy(&p_time.sRecordTZName[0], "UTC");
	  p_time.iRelDay = 0;
	  p_time.iMinutesWest = 0;
	  p_time.iLocalWest = 0;
	  p_time.iJulianDay = 0;
	  p_time.Ymds.iyear = 2004;
	  p_time.Ymds.imon = 3;
	  p_time.Ymds.iday = 1;
	  p_time.Ymds.isec += 10;
	  p_time.Ymds.imills = 0;
	  FillProductHeader(&phdr, &p_time, &Track );
	  
	  FillProductData( ppi_data, &Track );
	  
	  isec = p_time.Ymds.isec % 60;
	  sprintf(ssec, "0%d", isec);
	  
	  imin = p_time.Ymds.isec / 60; 
	  sprintf(smin, "0%d", imin);
	  sprintf(out_file_name, "SBX04030100%s%s.PPI%d", 
		  &smin[strlen(smin)-2], &ssec[strlen(ssec)-2], pcount++);
	  out_file = fopen(out_file_name, "wb"); 
	  fwrite(&phdr, sizeof(struct product_hdr), 1, out_file);
	  fwrite(ppi_data, 1, (RESOLUTION * RESOLUTION), out_file);
	  fclose(out_file);
	  
	  fTime -= 10.0;
	}
      
      strcpy( sdone_str, "No" );
      PromptForString("\nMore Maneuvers (default %s ): ", sdone_str, 16 ); 
      if ((sdone_str[0]!='y')&&(sdone_str[0]!='Y')) break;
    } 

  return(0);   
}
/* =====================================================
 * Fill in an array of product data
 */
static void FillProductData
( UINT1 ppi_data_a[RESOLUTION][RESOLUTION],
  const struct track_spec *pTrack_a )
{
  int inorth, ieast;
  UINT1 iStrength = NINT( (2.0*pTrack_a->fStrength) + 64.0);  


  for (inorth=0; inorth<RESOLUTION; inorth++)
    {
      for (ieast=0; ieast<RESOLUTION; ieast++)
        {
          ppi_data_a[inorth][ieast] = 0;
        }
    }
  
  if (pTrack_a->use_clutter)
    {
      for (inorth = pTrack_a->clutter_s; inorth < pTrack_a->clutter_n; inorth++)
        {
          for (ieast = pTrack_a->clutter_w; ieast < pTrack_a->clutter_e; ieast++)
	    {
	      ppi_data_a[inorth][ieast] = 100;
	    }
        }
    }
  
  if ( (fabs(pTrack_a->fdist_north) < 75.0) && (fabs(pTrack_a->fdist_east) < 75.0) )
    {
      FLT8 fValue = drand48();
      /* Optionally draw the plane */
      if ((fValue*100.0) <= (FLT8)pTrack_a->fTrackProbability)
	{
	  FLT4 fScale  = ((FLT4)RESOLUTION)/150.0;
	  FLT4 fOffset = ((FLT4)RESOLUTION)/2.0;
	  inorth = NINT((fScale*pTrack_a->fdist_north) + fOffset);
	  ieast  = NINT((fScale*pTrack_a->fdist_east ) + fOffset);
	  if ( (inorth>=0) && (inorth < RESOLUTION) && (ieast>=0) && (ieast < RESOLUTION) )
	    {
	      ppi_data_a[inorth][ieast] = iStrength;
	    }
	}
    }
  if (pTrack_a->fSpeckleCount>0.5)
    {
      FLT8 fThreshold = ((FLT8)pTrack_a->fSpeckleCount)/(FLT8)(RESOLUTION*RESOLUTION);
      for (inorth=0; inorth<RESOLUTION; inorth++)
	{
	  for (ieast=0; ieast<RESOLUTION; ieast++)
	    {
	      FLT8 fValue = drand48();
	      if (fValue<fThreshold)
		ppi_data_a[inorth][ieast] += iStrength;
	    }
	}
    }

}

/* =========================================
 * Prompt for a number, with a supplied default.
 */
static void PromptForFloat
( const char *sPrompt_a,
  float *fValue_a )
{
  char sLine[80], *sResponse, *sEnd;

 TRY_AGAIN:
  printf( sPrompt_a, *fValue_a );
  sResponse = fgets( sLine, 80, stdin );
  if (sResponse)
    {
      int iLength = strlen(sResponse)-1;
      if (iLength>0)
	{
	  FLT8 fValue = strtod( sResponse, &sEnd );
	  if (sResponse==sEnd) goto TRY_AGAIN;
	  *fValue_a = fValue;
	}
    }
  return;
}
/* =========================================
 * Prompt for a string, with a supplied default.
 */
static void PromptForString
( const char *sPrompt_a,
  char *sString_a,
  int iStringLength_a )   /* Length of storage for the string */
{
  char sLine[80], *sResponse;

  /* Protect against funny string sizes */
  if (iStringLength_a<2) return;

  printf( sPrompt_a, sString_a );
  sResponse = fgets( sLine, 80, stdin );
  if (sResponse)
    {
      int iLineLength = strlen(sResponse)-1;
      if (iLineLength>=iStringLength_a) iLineLength = iStringLength_a-1;
      if (iLineLength>0)
	{
	  memcpy( sString_a, sResponse, iLineLength );
	  sString_a[iLineLength]=0;
	}
    }
  return;
}
/* =========================================
 * Fill in the product header
 */
static void FillProductHeader
( struct product_hdr *phdr, 
  const struct full_time_spec *p_time,
  const struct track_spec *pTrack_a )
{
  char sSiteName16Space[16];

  spacify_ncpy( sSiteName16Space, pTrack_a->sSiteName16, 16 );
  HeaderFromScratch( phdr, p_time, sSiteName16Space, sSiteName16Space, pTrack_a->sProductName);
  
  phdr->pcf.iptype = PROD_PPI;
  phdr->pcf.ixscale = 15000000/RESOLUTION;
  phdr->pcf.iyscale = 15000000/RESOLUTION;
  phdr->pcf.izscale = 0;
  phdr->pcf.ixsize = RESOLUTION;
  phdr->pcf.iysize = RESOLUTION;
  phdr->pcf.izsize = 0;
  phdr->pcf.ixrad = 500*RESOLUTION;
  phdr->pcf.iyrad = 500*RESOLUTION;
  phdr->pcf.izrad = 0;
  phdr->pcf.irange_last_v20 = 0;
  phdr->pcf.idata_out = DB_DBT;
  phdr->pcf.idata_in  = DB_DBT;
  phdr->pcf.iprojection_type = MAPROJ_MERCATOR;
  phdr->pcf.colors.iflags = (COLOR_SCALE_TOP_SAT | COLOR_SCALE_BOT_SAT);
  phdr->pcf.colors.istart = 500;
  phdr->pcf.colors.istep  = 100;
  phdr->pcf.colors.icolcnt = 16;
  phdr->pcf.colors.iset_and_scale = 0x0101;
  
  phdr->end.ilon_center = iBin4FromFDeg(-178.0);
  phdr->end.ilat_center = iBin4FromFDeg(52.0);
  phdr->end.ilon_ref    = phdr->end.ilon_center;
}
