/* **********************************************************************
 * *                                                                    *
 * *                 Beam blockage Generation Program                   *
 * *                                                                    *
 * **********************************************************************
 * File: utils/examples/beam_blockage.C
 *
 *                        COPYRIGHT (c) 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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <netinet/in.h>
#include "sigtypes.h"
#include "user_lib.h"

/*
  This program produces an IRIS format beam blockage configuration
  file base on NIMA format DTED data.  Download the NIMA DTED data
  from http://geoengine.nima.mil.  First define an Area of Interest
  (AOI) as a grid relatively centered on the radar location.  The
  grid must be with integer value for lat and lon boundries.  Then
  select DOWNLOAD and download the NIMA Level 0 data in UNIX format.
  This products a DTED*.tar file.  Place this file in the current
  directory and untar the file.  In the same directory, place the
  beam_blockage executable.  The executable must be compiled with
  the below defines set.  The SITECODE is the three letter sitecode
  for the radar.  The NUM_LOG and NUM_LAT is the span of the DTED
  data in degrees.  The LAT_BASE and LON_BASE is the southwest 
  corner of the DTED data.  This must be an integer value.  N and E
  are positive values, S and W are negative values.  Next the 
  RADAR_Lat and RADAR_LON are the lat and lon of the radar site.
  These are float numbers and the sign convention is the same for
  LAT_BASE and LON_BASE.  The ANT_HEIGHT is the height of the 
  center of the antenna AMSL in meters.  The BW is the 3 dB 
  beamwidth of the antenna in degrees.  

  Data in a single DTED file is 120x120 arranged as such:
    2 5 8
    1 4 7
    0 3 6
 
  Data in buffer_p is arranged:
   0 1 2
   3 4 5
   6 7 8
*/

#define SITECODE "BRU"
#define NUM_LON 10
#define NUM_LAT 10 
#define LAT_BASE 46
#define LON_BASE 0
#define RADAR_LAT 50.90550
#define RADAR_LON 4.45500
#define ANT_HEIGHT 80.0
#define BW 1.0

int main()
{
  int i, h, iaz, irange, lat, lon, cur_lat, cur_lon, angle_index, blocked_ray;
  int index, lat_index, lon_index;
  char in_file_name[100], lat_str[5], lon_str[5];
  short buf[2000], sheight[360][300], max_val, height, temp_height, value;
  short *buffer_p;
  struct latlon8 fradar, fother;
  float deg2rad, rad2deg;
  float angle_lo, angle_mi, angle_hi, h1, h2, h3, h4;
  float arc_len, x1, x2, blockage, db_loss, radius;
  double dheight;
  FILE  *max_file, *out_file, *sri_file;

  radius = BW / 2.0; 
  deg2rad = FPI / 180.0; 
  rad2deg = 180.0 / FPI;
  fradar.fLat = RADAR_LAT * deg2rad;
  fradar.fLon = RADAR_LON * deg2rad;

  for (irange=0; irange<300; irange++)
  {
    for (iaz=0; iaz<360; iaz++)
    {
      sheight[iaz][irange] = 0;
    }
  }

  buffer_p = (short *)malloc(14400 * 2 * NUM_LAT * NUM_LON);
  if (!buffer_p)
  {
    printf("buffer_p malloc failed!\n");
    exit(1);
  }

  for (i=0; i<(14400 * NUM_LAT * NUM_LON); i++) *(buffer_p + i) = 0;

  /* Read in each DTED file and build [NUM_LAT * 120][NUM_LON*120] array of height data */
  for (h=0; h<NUM_LAT; h++)
  {
    for (i=0; i<NUM_LON; i++)
    {
      cur_lat = LAT_BASE +h;
      cur_lon = LON_BASE +i;

      if (abs(cur_lon) < 10) sprintf(lon_str, "e00%d", abs(cur_lon)); 
      else
      { 
        if (abs(cur_lon) < 100) sprintf(lon_str, "e0%d", abs(cur_lon)); 
        else sprintf(lon_str, "e%d", abs(cur_lon)); 
      }
      if (cur_lon < 0) memcpy(lon_str, "w", 1);
 
      if (abs(cur_lat) < 10) sprintf(lat_str, "n0%d", abs(cur_lat)); 
      else sprintf(lat_str, "n%d", abs(cur_lat)); 
      if (cur_lat < 0) memcpy(lat_str, "s", 1);
 
      sprintf(in_file_name, "./dted/%s/%s.max", lon_str, lat_str);
      if ((max_file = fopen(in_file_name, "rb")))
      {
        /* read and discard 1714 bytes of header at beginning of file */
        fread(buf, 2, 1714, max_file); 

        for(lon=0; lon<=119; lon++)
        {
          /* if lat is greater than 50, the input dted lon data is downsized by 2, skip reading this column*/
          if ( (abs(cur_lat < 50)) || ((cur_lat >= 50) && (!(lon % 2))) )
          { 
            /* read and discard 4 shorts of header at end of line */
            fread(buf, 4, sizeof(short), max_file); 
          }
          for(lat=119; lat>=0; lat--)
          {
            index = (120*NUM_LON*lat) + lon + (i*120) + (120*120*NUM_LON*((NUM_LAT-h)-1)); 
            if ( (abs(cur_lat < 50)) || ((cur_lat >= 50) && (!(lon % 2))) )
            {
              fread(&max_val, 1, sizeof(short), max_file); 
              value = htons(max_val);
              if (value < -30000) *(buffer_p + index) = 0;
              else *(buffer_p + index) = value; 
            }
            else
            {
              /* if lat is greater than 50, make even column elements equal to odd column elements */ 
              *(buffer_p + index) = *(buffer_p + ( (120*NUM_LON*lat) + (lon-1) + (i*120) + (120*120*NUM_LON*((NUM_LAT-h)-1)) ) );
            }
          }
          if ( (abs(cur_lat < 50)) || ((cur_lat >= 50) && (!(lon % 2))) )
          {
            /* read and discard 2 shorts of trailer at end of line */
            fread(buf, 2, sizeof(short), max_file); 
          }
        }  
        fclose(max_file);
      }
    }
  }

  /*
     Now that we have read in the DTED data into a carisian array,
     go through each position in the polar array and plug in the
     closest positioned value from the cartisian array.
  */ 
  for (irange=0; irange<300; irange++)
  {
    for (iaz=0; iaz<360; iaz++)
    {
      polar_to_earth(&fradar, (double)irange, ((double)iaz * deg2rad), &fother);
      lat_index = NINT( ( ( (float)(LAT_BASE + NUM_LAT) - (fother.fLat * rad2deg) ) * 120.0) ); 
      lon_index = NINT( ( ( (fother.fLon * rad2deg) - (float)LON_BASE) * 120.0) );
      if ( (lat_index >= 0) && (lat_index < (NUM_LAT * 120)) && 
           (lon_index >= 0) && (lon_index < (NUM_LON * 120)) ) 
      {
        index = (lat_index * 120 * NUM_LON) + lon_index; 
        sheight[iaz][irange] = *(buffer_p + index);  
      }
    }
  }

  /* Write the surface_height.conf file */
  sri_file = fopen("./surface_height.conf", "w");
  fprintf(sri_file, "SITE %s\n", SITECODE);
  fprintf(sri_file, "AZ_RESOLUTION 1.0\n");
  fprintf(sri_file, "RANGE_RESOLUTION 1.0 300.0\n"); 
  for (iaz = 0; iaz < 360; iaz++) 
  {
    fprintf(sri_file,"AZ %d\n", iaz); 
    for (irange = 0; irange < 300; irange++)
    {
      fprintf(sri_file, "%d\n", sheight[iaz][irange]);
    }
  }
  fclose(sri_file);
        
  /* Preform earth curvature correction of [az][range] table data*/
  for (irange=0; irange<300; irange++)
  {
    dheight = sqrt( (double) ((8495 * 8495) + (irange * irange)) );
    dheight -= 8495.0;
    dheight *= 1000.0;
    height = (short)(dheight + 0.5);
    for (iaz=0; iaz<360; iaz++)
    {
      temp_height = sheight[iaz][irange]; 
      if (temp_height > height) sheight[iaz][irange] = temp_height - height;
      else sheight[iaz][irange] = 0;
    }
  }

  /* Now that we have a complete earth curve corrected terrain map,
     check the beam heights against this an build the table. */ 
  out_file = fopen("./beam_blockage.conf", "w");
  fprintf(out_file, "SITE %s\n", SITECODE);

  for (angle_index = 1; angle_index < 21; angle_index++)
  {
    angle_lo = (angle_index * 0.5) - radius;
    angle_mi = (angle_index * 0.5);
    angle_hi = (angle_index * 0.5) + radius; 
    if (angle_lo < 0.0) continue;
    fprintf(out_file, "SWEEP %2.1f   1.0\n", angle_mi);
    for (iaz=0; iaz < 360; iaz++)
    {
      blocked_ray = 0;
      for (irange=0; irange<300; irange++)
      {
        /* if there is already  blockage for this ray, skip it */
        if (blocked_ray >= 2) break;

        h1 = (float)sheight[iaz][irange];
        h2 = ANT_HEIGHT + ((float)irange * 1000.0 * tan(deg2rad * angle_lo));
        h3 = ANT_HEIGHT + ((float)irange * 1000.0 * tan(deg2rad * angle_mi));
        h4 = ANT_HEIGHT + ((float)irange * 1000.0 * tan(deg2rad * angle_hi));

        if (h2 >= h1)
        {
          /* no blockage */
          continue;
        }
        else if (h4 <= h1)
        {       
          /* total blockage */
          if (blocked_ray == 0) fprintf(out_file, "BLOCK %3.1f   %3.1f   12.0", (float)iaz, (float)irange); 
          else fprintf(out_file, "   %3.1f   12.0", (float)irange);
          blocked_ray = 2; 
        }
        else if (h3 >= h1)
        {
          /* less than 50% blockage */
          arc_len = BW * sin( ((h1-h2)/(h3-h2)) * (FPI / 2.0));
          x1 = radius - (arc_len / 2.0);
          x2 = radius + (arc_len / 2.0);
          blockage = ( ((FPI*BW*BW/4.0) * sin((x2 * FPI) / (2.0 * BW))) - \
                       ((FPI*BW*BW/4.0) * sin((x1 * FPI) / (2.0 * BW))) ) / (FPI * BW * BW / 2.0);
          db_loss = -10.0 * log10 (1.0 - blockage); 
          if (db_loss > 0.5)
          {
            /* write blockage amount to file */
            if (blocked_ray == 0) fprintf(out_file, "BLOCK %3.1f   %3.1f   %2.1f", (float)iaz, (float)irange, db_loss); 
            else fprintf(out_file, "   %3.1f   %2.1f", (float)irange, db_loss);
            blocked_ray++; 
          }
        }
        else
        {
          /* more than 50% blockage */
          arc_len = BW * sin( ((h4-h1)/(h4-h3)) * (FPI / 2.0));
          x1 = radius - (arc_len / 2.0);
          x2 = radius + (arc_len / 2.0);
          blockage = 1.0 - ( ( ((FPI*BW*BW/4.0) * sin((x2 * FPI) / (2.0 * BW))) - \
                               ((FPI*BW*BW/4.0) * sin((x1 * FPI) / (2.0 * BW))) ) / (FPI * BW * BW / 2.0) );
          db_loss = -10.0 * log10 (1.0 - blockage); 
          if (db_loss >= 12.0) db_loss = 12.0;
          if (db_loss > 0.5)
          {
            /* write blockage amount to file */
            if (blocked_ray == 0) fprintf(out_file, "BLOCK %3.1f   %3.1f   %2.1f", (float)iaz, (float)irange, db_loss); 
            else fprintf(out_file, "   %3.1f   %2.1f", (float)irange, db_loss);
            blocked_ray++; 
            if(db_loss >= 12.0) blocked_ray++;
          }
        }
      } /* end for each ray */
      if (blocked_ray) fprintf(out_file, "\n");
    } /* end for each range */
  } /* end for each elevation */

  fprintf(out_file, "SWEEP 10.5   1.0\n");
  fclose(out_file);
  exit(0);
}
