//
// 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;
};
//
// cNoradBase.h
//
// This class provides a base class for the NORAD SGP4/SDP4
// orbit models.
//
// Copyright (c) 2003 Michael F. Henry
//
#pragma once

//////////////////////////////////////////////////////////////////////////////

class cEci;
class cOrbit;

//////////////////////////////////////////////////////////////////////////////

class cNoradBase
{
public:
   cNoradBase(const cOrbit&);
   virtual ~cNoradBase() {};

   virtual bool getPosition(double tsince, cEci &eci) = 0;

protected:
   cNoradBase& operator=(const cNoradBase&);

   void Initialize();
   bool FinalPosition(double  incl, double omega,  double     e,   
                      double     a, double    xl,  double xnode, 
                      double    xn, double tsince, cEci &eci);

   const cOrbit &m_Orbit;

   // Orbital parameter variables which need only be calculated one
   // time for a given orbit (ECI position time-independent).
   double m_satInc;  // inclination
   double m_satEcc;  // eccentricity

   double m_cosio;   double m_theta2;  double m_x3thm1;  double m_eosq;  
   double m_betao2;  double m_betao;   double m_aodp;    double m_xnodp;
   double m_s4;      double m_qoms24;  double m_perigee; double m_tsi;
   double m_eta;     double m_etasq;   double m_eeta;    double m_coef;
   double m_coef1;   double m_c1;      double m_c2;      double m_c3;
   double m_c4;      double m_sinio;   double m_a3ovk2;  double m_x1mth2;
   double m_xmdot;   double m_omgdot;  double m_xhdot1;  double m_xnodot;
   double m_xnodcf;  double m_t2cof;   double m_xlcof;   double m_aycof;
   double m_x7thm1;
};
//
// cOrbit.h
//
// This is the header file for the class cOrbit. This class accepts a
// single satellite's NORAD two-line element set and provides information 
// regarding the satellite's orbit such as period, axis length,
// ECI coordinates/velocity, etc., using the SGP4/SDP4 orbital models.
// 
// Copyright (c) 2002-2003 Michael F. Henry
//
#pragma once

#include "math.h"

using namespace std;
//////////////////////////////////////////////////////////////////////////////

class cVector;
class cGeoCoord;
class cEci;

//////////////////////////////////////////////////////////////////////////////
class cOrbit  
{
public:
   cOrbit(const cTle &tle);
   virtual ~cOrbit();

   // Return satellite ECI data at given minutes since element's epoch.
   bool getPosition(double tsince, cEci *pEci) const;
   
   double Inclination()  const { return radGet(cTle::FLD_I);                 }
   double Eccentricity() const { return m_tle.getField(cTle::FLD_E);         }
   double RAAN()         const { return radGet(cTle::FLD_RAAN);              }
   double ArgPerigee()   const { return radGet(cTle::FLD_ARGPER);            }
   double BStar()        const { return m_tle.getField(cTle::FLD_BSTAR) / AE;}
   double Drag()         const { return m_tle.getField(cTle::FLD_MMOTIONDT); }
   double mnMotion()     const { return m_tle.getField(cTle::FLD_MMOTION);   }
   double mnAnomaly()    const { return radGet(cTle::FLD_M);                 }
   double mnAnomaly(cJulian t) const;  // mean anomaly (in radians) at time t

   cJulian Epoch() const { return m_jdEpoch; }

   double TPlusEpoch(const cJulian &t) const;    // time span [t - epoch] in secs

   string SatName(bool fAppendId = false) const;
   
   // "Recovered" from the input elements
   double SemiMajor()   const { return m_aeAxisSemiMajorRec; }
   double SemiMinor()   const { return m_aeAxisSemiMinorRec; }
   double mnMotionRec() const { return m_mnMotionRec; }  // mn motion, rads/min
   double Major()   const { return 2.0 * SemiMajor(); }  // major axis in AE
   double Minor()   const { return 2.0 * SemiMinor(); }  // minor axis in AE
   double Perigee() const { return m_kmPerigeeRec;    }  // perigee in km
   double Apogee()  const { return m_kmApogeeRec;     }  // apogee in km
   double Period()  const;                               // period in seconds

protected:
   double radGet(cTle::eField fld) const
      { return m_tle.getField(fld, cTle::U_RAD); }

   double degGet(cTle::eField fld) const
      { return m_tle.getField(fld, cTle::U_DEG); }

private:
   cTle        m_tle;
   cJulian     m_jdEpoch;
   cNoradBase *m_pNoradModel;

   // Caching variables; note units are not necessarily the same as tle units
   mutable double m_secPeriod;

   // Caching variables recovered from the input TLE elements
   double m_aeAxisSemiMinorRec;  // semi-minor axis, in AE units
   double m_aeAxisSemiMajorRec;  // semi-major axis, in AE units
   double m_mnMotionRec;         // radians per minute
   double m_kmPerigeeRec;        // perigee, in km
   double m_kmApogeeRec;         // apogee, in km
};

//
// cNoradSGP4.h
//
// This class implements the NORAD Simple General Perturbation 4 orbit
// model. This model provides the ECI coordiantes/velocity of satellites
// with orbit periods less than 225 minutes.
//
// Copyright (c) 2003 Michael F. Henry
//
#pragma once

class cOrbit;

//////////////////////////////////////////////////////////////////////////////
class cNoradSGP4 : public cNoradBase
{
public:
   cNoradSGP4(const cOrbit &orbit);
   virtual ~cNoradSGP4() {};

   virtual bool getPosition(double tsince, cEci &eci);

protected:
   double m_c5; 
   double m_omgcof;
   double m_xmcof;
   double m_delmo;
   double m_sinmo;
};

//
// cNoradSDP4.h
//
// This class implements the NORAD Simple Deep Perturbation 4 orbit
// model. This model provides the ECI coordinates/velocity of satellites
// with periods >= 225 minutes.
//
// Copyright (c) 2003 Michael F. Henry
//
#pragma once

class cOrbit;

//////////////////////////////////////////////////////////////////////////////
class cNoradSDP4 : public cNoradBase
{
public: 
   cNoradSDP4(const cOrbit &orbit);
   virtual ~cNoradSDP4() {};

   virtual bool getPosition(double tsince, cEci &eci);

protected:
   bool DeepInit(double *eosq,    double *sinio,    double *cosio,  double *m_betao, 
                 double *m_aodp,  double *m_theta2, double *m_sing, double *m_cosg,
                 double *m_betao2,double *xmdot,    double *omgdot, double *xnodott);

   bool DeepSecular(double *xmdf,  double *omgadf,double *xnode, double *emm, 
                    double *xincc, double *xnn,   double *tsince);
   bool DeepCalcDotTerms  (double *pxndot, double *pxnddt, double *pxldot);
   void DeepCalcIntegrator(double *pxndot, double *pxnddt, double *pxldot, 
                           const double &delt);
   bool DeepPeriodics(double *e,     double *xincc,  double *omgadf, 
                      double *xnode, double *xmam);
   double m_sing;
   double m_cosg;

   // Deep Initialization
   double eqsq;   double siniq;  double cosiq;  double rteqsq; double ao;   
   double cosq2;  double sinomo; double cosomo; double bsq;    double xlldot;
   double omgdt;  double xnodot;
   
   // Deep Secular, Periodic
   double xll;    double omgasm; double xnodes; double _em;    
   double xinc;   double xn;     double t;

   // Variables shared by "Deep" routines
   double dp_e3;     double dp_ee2;    double dp_savtsn; double dp_se2;
   double dp_se3;    double dp_sgh2;   double dp_sgh3;   double dp_sgh4;
   double dp_sghs;   double dp_sh2;    double dp_sh3;    double dp_si2;
   double dp_si3;    double dp_sl2;    double dp_sl3;    double dp_sl4;
   double dp_xgh2;   double dp_xgh3;   double dp_xgh4;   double dp_xh2;
   double dp_xh3;    double dp_xi2;    double dp_xi3;    double dp_xl2;
   double dp_xl3;    double dp_xl4;    double dp_xqncl;  double dp_zmol;
   double dp_zmos;

   double dp_atime;  double dp_d2201;  double dp_d2211;  double dp_d3210;
   double dp_d3222;  double dp_d4410;  double dp_d4422;  double dp_d5220;
   double dp_d5232;  double dp_d5421;  double dp_d5433;  double dp_del1;
   double dp_del2;   double dp_del3;   double dp_fasx2;  double dp_fasx4;
   double dp_fasx6;  double dp_omegaq; double dp_sse;    double dp_ssg;
   double dp_ssh;    double dp_ssi;    double dp_ssl;    double dp_step2;
   double dp_stepn;  double dp_stepp;  double dp_thgr;   double dp_xfact;
   double dp_xlamo;  double dp_xli;    double dp_xni;

   bool dp_iresfl;
   bool dp_isynfl;

   // DeepInit vars that change with epoch
   double dpi_c;      double dpi_ctem;   double dpi_day;    double dpi_gam;
   double dpi_stem;   double dpi_xnodce; double dpi_zcosgl; double dpi_zcoshl;
   double dpi_zcosil; double dpi_zsingl; double dpi_zsinhl; double dpi_zsinil;
   double dpi_zx;     double dpi_zy;

};

#endif
