/* **********************************************************************
 * *	                                                               *
 * *                 Equations for Earth Corrections                    *
 * *                                                                    *
 * **********************************************************************
 * File: libs/user/earth.c
 *
 *                 COPYRIGHT (c) 1989, 1995, 1999, 2000  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 "sigtypes.h"
#include "user_lib.h"

/*
 * ========================================================================
 * This function returns the height in kilometers above the trigonometric
 * position that the radar beam will be due to curvature of the earth and
 * average diffraction 
 * Entry parameters: 
 *    FRANGE_A    - The horizontal range in kilometers. 
 * Exit parameters:
 *    FEARTHCURVE - the height in kilometers
 */
FLT4 fearthcurve( FLT4 frange_a )
{
  FLT4 fresult;

  fresult = (frange_a*frange_a) / (2. * FEARTH_RADAR_RADIUS );
  return fresult;
}

/* ========================================================================
 * This function returns the horizontal range in kilometers that would cause 
 C the beam to be off by the specified height.  This height error is the 
 C difference between the height assuming a flat earth and no atmospheric
 C refraction, and the actual height above the curved earths surface, assuming 
 C average refraction. 
 C
 C Entry parameters: 
 C    FHEIGHT_A   - Height error in kilometers
 C Exit parameters:
 C    FCURVERANGE - Horizontal range that will give that error. 
 */
FLT4 fcurverange( FLT4 fheight_a )
{

  FLT4 fresult;

  if (fheight_a > 0.0)
    fresult = sqrt( 2.0 * FEARTH_RADAR_RADIUS * fheight_a );
  else
    fresult = 0.0;

  return fresult;
}

/* ======================================================================
 * Collection of real functions to compute coordinates on a curved earth.
 * There are four variables:
 *
 *   SLR : Slant range measured along a radar ray, in km.
 *   ELV : Elevation angle of a radar ray, in radians, range -PI/2 to +3PI/2.
 *   EAR : Earth range measured along the surface, in km.
 *   HGT : Earth height measured upward from the ground, in km.
 *   TOW : Height of the radar above the ground. (Tower height)
 *
 * There are 12 possible functions of two variables from the above set of
 * four.  There is a uniform way of expressing the name of the functions
 * defined, and the arguments that are required.  For example:
 * "RSLR_FROM_EAR_HGT" computes slant range from earth range and height.
 */
/* = = = = = = = = = = = = = = = */
FLT8 fslr_from_ear_hgt( FLT8 fear_a, FLT8 fhgt_a, FLT8 fTow_a )
{
  FLT8 fresult, fRadHgt;

  fRadHgt = fhgt_a-fTow_a;
  fresult = sqrt( (fRadHgt * fRadHgt) +
		 ( fear_a * fear_a * ( 1.0 + fRadHgt / FEARTH_RADAR_RADIUS )));

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 felv_from_ear_hgt( FLT8 fear_a, FLT8 fhgt_a, FLT8 fTow_a )
{
  FLT8 fresult, fRadHgt;

  fRadHgt = fhgt_a-fTow_a;
  if( fear_a <= 0.0 )
    fresult = FPI/2.0;
  else
    fresult = atan( ( fRadHgt / fear_a ) - ( fear_a / ( 2. * FEARTH_RADAR_RADIUS )));

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 fslr_from_ear_elv
( FLT8 fear_a,
  FLT8 felv_a )
{
  FLT8 ff, fresult, fEarthRange, fElevation;

  fEarthRange = fabs(fear_a);
  fElevation  = felv_a;
  /* Elevation forced into the range +/- PI/2. */
  if (fElevation > (FPI/2.0)) fElevation = FPI - fElevation;

  ff = fElevation + (fEarthRange / FEARTH_RADAR_RADIUS) ;
  fresult = fEarthRange / MAX( .001, cos(ff) );

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 fhgt_from_ear_elv
( FLT8 fear_a,  /* Abs of range used only */
  FLT8 felv_a,  /* Elevation angle clipped to range -90 to +270 degrees */
  FLT8 fTow_a ) /* Tower height in km */
{
  FLT8 fresult, fEarthRange, fElevation;

  fEarthRange = fabs(fear_a);
  fElevation  = felv_a;
  /* Elevation forced into the range +/- PI/2.  Avoid the endpoints */
  if (fElevation > (FPI/2.0)) fElevation = FPI - fElevation;
  fElevation = MIN( fElevation, ((FPI/2.0) - 0.0002) );
  fElevation = MAX( fElevation, (0.0002 - (FPI/2.0)) );
  fresult = fEarthRange * (tan( fElevation ) +
			   ( fEarthRange / ( 2. * FEARTH_RADAR_RADIUS )));
  fresult += fTow_a;

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 fear_from_slr_elv( FLT8 fslr_a, FLT8 felv_a )
{
  FLT8 fresult;

  fresult = fslr_a * cos( felv_a + fslr_a / FEARTH_RADAR_RADIUS );

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 fhgt_from_slr_elv( FLT8 fslr_a, FLT8 felv_a, FLT8 fTow_a )
{
  FLT8 fresult;

  fresult =  fslr_a * ( sin(felv_a) + ( fslr_a / ( 2. * FEARTH_RADAR_RADIUS )) );
  fresult += fTow_a;

  return fresult;
}

/* = = = = = = = = = = = = = = = */
/* Caution: Somewhat numerially unstable...
 * Works for negative elevation angles, but not for negative heights.  All
 * negative heights will be treated as zero.
 */
FLT8 fslr_from_hgt_elv( FLT8 fhgt_a, FLT8 felv_a, FLT8 fTower_a )
{
  FLT8 rr, rs, fresult;

  rr = sin( felv_a );
  rs = (fhgt_a-fTower_a) / FEARTH_RADAR_RADIUS;
  if (rs < 0.0) rs = 0.0;
  fresult = FEARTH_RADAR_RADIUS * ( sqrt( rr*rr + rs*(rs+2.) ) - rr );

  return fresult;
}

/* = = = = = = = = = = = = = = = */
FLT8 fhgt_from_slr_ear( FLT8 fslr_a, FLT8 fear_a, FLT8 fTower_a )
{
  FLT8 ff, fresult;

  ff = fear_a * fear_a;
  fresult = sqrt( fslr_a * fslr_a - ff ) - ( ff / (2. * FEARTH_RADAR_RADIUS ) );
  fresult += fTower_a;

  return fresult;
}

/* = = = = = = = = = = = = = = = 
 * Slant range is always positive.  This constains the result 
 * to the range +/- PI, so it works in only 2 quadrents.
 * If the slant range is zero, then return 0,
 * If the magnitude of height is larger than the slant range, 
 * then we return +or-PI.
 */
FLT8 felv_from_slr_hgt( FLT8 fslr_a, FLT8 fhgt_a, FLT8 fTower_a )
{
  FLT8 fB, fC, fTop, fBottom, fResult;

  fB = FEARTH_RADAR_RADIUS+fhgt_a;
  fC = FEARTH_RADAR_RADIUS+fTower_a;
  fTop = fC*fC + fslr_a*fslr_a - fB*fB;
  fBottom = 2.0 * fslr_a * fC;

  if (fBottom == 0.0) return 0.0;

  fResult = fTop/fBottom;
  if (fResult>=1.0)  return -FPI/2.0;
  if (fResult<=-1.0) return FPI/2.0;

  fResult = acos( fResult );

  fResult -= FPI/2.0;

  return fResult;
}
