//
// stdafx.h 
//
#ifndef sgp4_h 
#define sgp4_h

//#define WIN32_LEAN_AND_MEAN   // Exclude rarely-used stuff from Windows headers
#include <stdio.h>
//#include <tchar.h>
#include <ctype.h>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <time.h>
#include <math.h>

using namespace std;
//
// globals.h
//

const double PI           = 3.141592653589793;
const double TWOPI        = 2.0 * PI;
const double RADS_PER_DEG = PI / 180.0;

const double GM           = 398601.2;   // Earth gravitational constant, km^3/sec^2
const double GEOSYNC_ALT  = 42241.892;  // km
const double EARTH_DIA    = 12800.0;    // km
const double DAY_SIDERAL  = (23 * 3600) + (56 * 60) + 4.09;  // sec
const double DAY_24HR     = (24 * 3600);   // sec

const double AE           = 1.0;
const double AU           = 149597870.0;  // Astronomical unit (km) (IAU 76)
const double SR           = 696000.0;     // Solar radius (km)      (IAU 76)
const double TWOTHRD      = 2.0 / 3.0;
const double XKMPER_WGS72 = 6378.135;     // Earth equatorial radius - km (WGS '72)
const double F            = 1.0 / 298.26; // Earth flattening (WGS '72)
const double GE           = 398600.8;     // Earth gravitational constant (WGS '72)
const double J2           = 1.0826158E-3; // J2 harmonic (WGS '72)
const double J3           = -2.53881E-6;  // J3 harmonic (WGS '72)
const double J4           = -1.65597E-6;  // J4 harmonic (WGS '72)
const double CK2          = J2 / 2.0;
const double CK4          = -3.0 * J4 / 8.0;
const double XJ3          = J3;
const double E6A          = 1.0e-06;
const double QO           = AE + 120.0 / XKMPER_WGS72;
const double S            = AE + 78.0  / XKMPER_WGS72;
const double HR_PER_DAY   = 24.0;          // Hours per day   (solar)
const double MIN_PER_DAY  = 1440.0;        // Minutes per day (solar)
const double SEC_PER_DAY  = 86400.0;       // Seconds per day (solar)
const double OMEGA_E      = 1.00273790934; // earth rotation per sideral day
const double XKE          = sqrt(3600.0 * GE /           //sqrt(ge) ER^3/min^2
                                (XKMPER_WGS72 * XKMPER_WGS72 * XKMPER_WGS72)); 
const double QOMS2T       = pow((QO - S), 4);            //(QO - S)^4 ER^4

// Utility functions
double sqr   (const double x);
double Fmod2p(const double arg);
double AcTan (const double sinx, double cosx);

double rad2deg(const double);
double deg2rad(const double);
//
// coord.h
//
// Copyright 2002-2003 Michael F. Henry
//
//////////////////////////////////////////////////////////////////////
// Geocentric coordinates.
class cCoordGeo  
{
public:
   cCoordGeo();
   cCoordGeo(double lat, double lon, double alt) :
      m_Lat(lat), m_Lon(lon), m_Alt(alt) {}
   virtual ~cCoordGeo() {};

   double m_Lat;   // Latitude,  radians (negative south)
   double m_Lon;   // Longitude, radians (negative west)
   double m_Alt;   // Altitude,  km      (above mean sea level)
};

//////////////////////////////////////////////////////////////////////
// Topocentric-Horizon coordinates.
class cCoordTopo  
{
public:
   cCoordTopo();
   cCoordTopo(double az, double el, double rng, double rate) :
      m_Az(az), m_El(el), m_Range(rng), m_RangeRate(rate) {}
   virtual ~cCoordTopo() {};

   double m_Az;         // Azimuth, radians
   double m_El;         // Elevation, radians
   double m_Range;      // Range, kilometers
   double m_RangeRate;  // Range rate of change, km/sec
                        // Negative value means "towards observer"
};

// cVector.h: interface for the cVector class.
//
// Copyright 2003 (c) Michael F. Henry
//
//////////////////////////////////////////////////////////////////////

class cVector  
{
public:
   cVector(double x = 0.0, double y = 0.0, double z = 0.0, double w = 0.0) :
      m_x(x), m_y(y), m_z(z), m_w(w) {}
   virtual ~cVector() {};

   void Sub(const cVector&);     // subtraction
   void Mul(double factor);      // multiply each component by 'factor'

   double Angle(const cVector&) const;    // angle between two vectors
   double Magnitude() const;              // vector magnitude
   double Dot(const cVector& vec) const;  // dot product

// protected:
   double m_x;
   double m_y;
   double m_z;
   double m_w;
};
//
// cTle.h 
//
// This class will accept a single set of two-line elements and then allow
// a client to request specific fields, such as epoch, mean motion, 
// etc., from the set.
//
// Copyright 1996-2003 Michael F. Henry
//
/////////////////////////////////////////////////////////////////////////////
class cTle
{
public:
   cTle(string&, string&, string&);
   cTle(const cTle &tle);
   ~cTle();
   
   enum eTleLine
   {
      LINE_ZERO,
      LINE_ONE,
      LINE_TWO
   };

   enum eField
   {
      FLD_FIRST,
      FLD_NORADNUM = FLD_FIRST,
      FLD_INTLDESC,
      FLD_SET,       // TLE set number
      FLD_EPOCHYEAR, // Epoch: Last two digits of year
      FLD_EPOCHDAY,  // Epoch: Fractional Julian Day of year
      FLD_ORBITNUM,  // Orbit at epoch
      FLD_I,         // Inclination
      FLD_RAAN,      // R.A. ascending node
      FLD_E,         // Eccentricity
      FLD_ARGPER,    // Argument of perigee
      FLD_M,         // Mean anomaly
      FLD_MMOTION,   // Mean motion
      FLD_MMOTIONDT, // First time derivative of mean motion
      FLD_MMOTIONDT2,// Second time derivative of mean motion
      FLD_BSTAR,     // BSTAR Drag
      FLD_LAST       // MUST be last
   };

   enum eUnits
   {
      U_FIRST,
      U_RAD = U_FIRST,  // radians
      U_DEG,            // degrees
      U_NATIVE,         // TLE format native units (no conversion)
      U_LAST            // MUST be last
   };
   
   void Initialize();
   
   static int    CheckSum(const string&);
   static bool   IsValidLine(string&, eTleLine);
   static string ExpToDecimal(const string&);

   static void TrimLeft(string&);
   static void TrimRight(string&);
   
   double getField(eField fld,               // which field to retrieve
                   eUnits unit  = U_NATIVE,  // return units in rad, deg etc.
                   string *pstr = NULL,      // return ptr for str value
                   bool bStrUnits = false)   // 'true': append units to str val
                   const;
   string getName()  const { return m_strName; }
   string getLine1() const { return m_strLine1;}
   string getLine2() const { return m_strLine2;}

protected:
   static double ConvertUnits(double val, eField fld, eUnits units); 

private:
   string getUnits(eField) const;
   double getFieldNumeric(eField) const;

   // Satellite name and two data lines
   string m_strName;
   string m_strLine1;
   string m_strLine2;

   // Converted fields, in atof()-readable form
   string m_Field[FLD_LAST];

   // Cache of field values in "double" format
   typedef int FldKey;
   FldKey Key(eUnits u, eField f) const { return (u * 100) + f; }
   mutable map<FldKey, double>  m_mapCache;
};

///////////////////////////////////////////////////////////////////////////
//
// TLE data format
//
// [Reference: T.S. Kelso]
//
// Two line element data consists of three lines in the following format:
//
//  AAAAAAAAAAAAAAAAAAAAAA
//  1 NNNNNU NNNNNAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN
//  2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN
//  
//  Line 0 is a twenty-two-character name.
// 
//   Lines 1 and 2 are the standard Two-Line Orbital Element Set Format identical
//   to that used by NORAD and NASA.  The format description is:
//      
//     Line 1
//     Column    Description
//     01-01     Line Number of Element Data
//     03-07     Satellite Number
//     10-11     International Designator (Last two digits of launch year)
//     12-14     International Designator (Launch number of the year)
//     15-17     International Designator (Piece of launch)
//     19-20     Epoch Year (Last two digits of year)
//     21-32     Epoch (Julian Day and fractional portion of the day)
//     34-43     First Time Derivative of the Mean Motion
//               or Ballistic Coefficient (Depending on ephemeris type)
//     45-52     Second Time Derivative of Mean Motion (decimal point assumed;
//               blank if N/A)
//     54-61     BSTAR drag term if GP4 general perturbation theory was used.
//               Otherwise, radiation pressure coefficient.  (Decimal point assumed)
//     63-63     Ephemeris type
//     65-68     Element number
//     69-69     Check Sum (Modulo 10)
//               (Letters, blanks, periods, plus signs = 0; minus signs = 1)
//
//     Line 2
//     Column    Description
//     01-01     Line Number of Element Data
//     03-07     Satellite Number
//     09-16     Inclination [Degrees]
//     18-25     Right Ascension of the Ascending Node [Degrees]
//     27-33     Eccentricity (decimal point assumed)
//     35-42     Argument of Perigee [Degrees]
//     44-51     Mean Anomaly [Degrees]
//     53-63     Mean Motion [Revs per day]
//     64-68     Revolution number at epoch [Revs]
//     69-69     Check Sum (Modulo 10)
//        
//     All other columns are blank or fixed.
//          
// Example:
//      
// NOAA 6
// 1 11416U          86 50.28438588 0.00000140           67960-4 0  5293
// 2 11416  98.5105  69.3305 0012788  63.2828 296.9658 14.24899292346978

//
// cJulian.h
//
// Copyright (c) 2003 Michael F. Henry
//
//
// See note in cJulian.cpp for information on this class and the epoch dates
//
const double EPOCH_JAN1_00H_1900 = 2415019.5; // Jan 1.0 1900 = Jan 1 1900 00h UTC
const double EPOCH_JAN1_12H_1900 = 2415020.0; // Jan 1.5 1900 = Jan 1 1900 12h UTC
const double EPOCH_JAN1_12H_2000 = 2451545.0; // Jan 1.5 2000 = Jan 1 2000 12h UTC

//////////////////////////////////////////////////////////////////////////////
class cJulian  
{
public:
   cJulian() { Initialize(2000, 1); }
   explicit cJulian(time_t t);              // Create from time_t
   explicit cJulian(int year, double day);  // Create from year, day of year
   explicit cJulian(int year,               // i.e., 2004
                    int mon,                // 1..12
                    int day,                // 1..31
                    int hour,               // 0..23
                    int min,                // 0..59
                    double sec = 0.0);      // 0..(59.999999...)
   ~cJulian() {};

   double toGMST() const;           // Greenwich Mean Sidereal Time
   double toLMST(double lon) const; // Local Mean Sideral Time
   time_t toTime() const;           // To time_t type - avoid using

   double FromJan1_00h_1900() const { return m_Date - EPOCH_JAN1_00H_1900; }
   double FromJan1_12h_1900() const { return m_Date - EPOCH_JAN1_12H_1900; }
   double FromJan1_12h_2000() const { return m_Date - EPOCH_JAN1_12H_2000; }

   void getComponent(int *pYear, int *pMon = NULL, double *pDOM = NULL) const;
   double getDate() const { return m_Date; }

   void addDay (double day) { m_Date += day;                 }
   void addHour(double hr ) { m_Date += (hr  / HR_PER_DAY ); }
   void addMin (double min) { m_Date += (min / MIN_PER_DAY); }
   void addSec (double sec) { m_Date += (sec / SEC_PER_DAY); }

   double spanDay (const cJulian& b) const { return m_Date - b.m_Date;        }
   double spanHour(const cJulian& b) const { return spanDay(b) * HR_PER_DAY;  }
   double spanMin (const cJulian& b) const { return spanDay(b) * MIN_PER_DAY; }
   double spanSec (const cJulian& b) const { return spanDay(b) * SEC_PER_DAY; }

   static bool IsLeapYear(int y)
      { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }

protected:
   void Initialize(int year, double day);

   double m_Date; // Julian date
};
//
// cEci.h
//
// Copyright (c) 2003 Michael F. Henry
//
//////////////////////////////////////////////////////////////////////
// class cEci
// Encapsulates an Earth-Centered Inertial position, velocity, and time.
class cEci
{
public:
   cEci() { m_VecUnits = UNITS_NONE; }
   cEci(const cCoordGeo &geo, const cJulian &cJulian);
   cEci(const cVector &pos, const cVector &vel, 
        const cJulian &date, bool IsAeUnits = true);
   virtual ~cEci() {};

   cCoordGeo toGeo(); 

   cVector getPos()  const { return m_pos;  }
   cVector getVel()  const { return m_vel;  }
   cJulian getDate() const { return m_date; }

   void setUnitsAe() { m_VecUnits = UNITS_AE; }
   void setUnitsKm() { m_VecUnits = UNITS_KM; }
   bool UnitsAreAe() const { return m_VecUnits == UNITS_AE; }
   bool UnitsAreKm() const { return m_VecUnits == UNITS_KM; }
   void ae2km();  // Convert position, velocity vector units from AE to km

protected:
   void MulPos(double factor) { m_pos.Mul(factor); }
   void MulVel(double factor) { m_vel.Mul(factor); }

   enum VecUnits
   {
      UNITS_NONE, // not initialized
      UNITS_AE,
      UNITS_KM,
   };

   cVector  m_pos;
   cVector  m_vel;
   cJulian  m_date;
   VecUnits m_VecUnits;
};
#endif
