| 1 | // | 
| 2 | // stdafx.h | 
| 3 | // | 
| 4 | #ifndef sgp4_h | 
| 5 | #define sgp4_h | 
| 6 | #pragma once | 
| 7 |  | 
| 8 | //#define WIN32_LEAN_AND_MEAN   // Exclude rarely-used stuff from Windows headers | 
| 9 | #include <stdio.h> | 
| 10 | //#include <tchar.h> | 
| 11 |  | 
| 12 | #include <string> | 
| 13 | #include <map> | 
| 14 | #include <vector> | 
| 15 | #include <algorithm> | 
| 16 | #include <assert.h> | 
| 17 | #include <time.h> | 
| 18 | #include <math.h> | 
| 19 |  | 
| 20 | using namespace std; | 
| 21 | // | 
| 22 | // globals.h | 
| 23 | // | 
| 24 |  | 
| 25 | const double PI           = 3.141592653589793; | 
| 26 | const double TWOPI        = 2.0 * PI; | 
| 27 | const double RADS_PER_DEG = PI / 180.0; | 
| 28 |  | 
| 29 | const double GM           = 398601.2;   // Earth gravitational constant, km^3/sec^2 | 
| 30 | const double GEOSYNC_ALT  = 42241.892;  // km | 
| 31 | const double EARTH_DIA    = 12800.0;    // km | 
| 32 | const double DAY_SIDERAL  = (23 * 3600) + (56 * 60) + 4.09;  // sec | 
| 33 | const double DAY_24HR     = (24 * 3600);   // sec | 
| 34 |  | 
| 35 | const double AE           = 1.0; | 
| 36 | const double AU           = 149597870.0;  // Astronomical unit (km) (IAU 76) | 
| 37 | const double SR           = 696000.0;     // Solar radius (km)      (IAU 76) | 
| 38 | const double TWOTHRD      = 2.0 / 3.0; | 
| 39 | const double XKMPER_WGS72 = 6378.135;     // Earth equatorial radius - km (WGS '72) | 
| 40 | const double F            = 1.0 / 298.26; // Earth flattening (WGS '72) | 
| 41 | const double GE           = 398600.8;     // Earth gravitational constant (WGS '72) | 
| 42 | const double J2           = 1.0826158E-3; // J2 harmonic (WGS '72) | 
| 43 | const double J3           = -2.53881E-6;  // J3 harmonic (WGS '72) | 
| 44 | const double J4           = -1.65597E-6;  // J4 harmonic (WGS '72) | 
| 45 | const double CK2          = J2 / 2.0; | 
| 46 | const double CK4          = -3.0 * J4 / 8.0; | 
| 47 | const double XJ3          = J3; | 
| 48 | const double E6A          = 1.0e-06; | 
| 49 | const double QO           = AE + 120.0 / XKMPER_WGS72; | 
| 50 | const double S            = AE + 78.0  / XKMPER_WGS72; | 
| 51 | const double HR_PER_DAY   = 24.0;          // Hours per day   (solar) | 
| 52 | const double MIN_PER_DAY  = 1440.0;        // Minutes per day (solar) | 
| 53 | const double SEC_PER_DAY  = 86400.0;       // Seconds per day (solar) | 
| 54 | const double OMEGA_E      = 1.00273790934; // earth rotation per sideral day | 
| 55 | const double XKE          = sqrt(3600.0 * GE /           //sqrt(ge) ER^3/min^2 | 
| 56 | (XKMPER_WGS72 * XKMPER_WGS72 * XKMPER_WGS72)); | 
| 57 | const double QOMS2T       = pow((QO - S), 4);            //(QO - S)^4 ER^4 | 
| 58 |  | 
| 59 | // Utility functions | 
| 60 | double sqr   (const double x); | 
| 61 | double Fmod2p(const double arg); | 
| 62 | double AcTan (const double sinx, double cosx); | 
| 63 |  | 
| 64 | double rad2deg(const double); | 
| 65 | double deg2rad(const double); | 
| 66 | // | 
| 67 | // coord.h | 
| 68 | // | 
| 69 | // Copyright 2002-2003 Michael F. Henry | 
| 70 | // | 
| 71 | ////////////////////////////////////////////////////////////////////// | 
| 72 | // Geocentric coordinates. | 
| 73 | class cCoordGeo | 
| 74 | { | 
| 75 | public: | 
| 76 | cCoordGeo(); | 
| 77 | cCoordGeo(double lat, double lon, double alt) : | 
| 78 | m_Lat(lat), m_Lon(lon), m_Alt(alt) {} | 
| 79 | virtual ~cCoordGeo() {}; | 
| 80 |  | 
| 81 | double m_Lat;   // Latitude,  radians (negative south) | 
| 82 | double m_Lon;   // Longitude, radians (negative west) | 
| 83 | double m_Alt;   // Altitude,  km      (above mean sea level) | 
| 84 | }; | 
| 85 |  | 
| 86 | ////////////////////////////////////////////////////////////////////// | 
| 87 | // Topocentric-Horizon coordinates. | 
| 88 | class cCoordTopo | 
| 89 | { | 
| 90 | public: | 
| 91 | cCoordTopo(); | 
| 92 | cCoordTopo(double az, double el, double rng, double rate) : | 
| 93 | m_Az(az), m_El(el), m_Range(rng), m_RangeRate(rate) {} | 
| 94 | virtual ~cCoordTopo() {}; | 
| 95 |  | 
| 96 | double m_Az;         // Azimuth, radians | 
| 97 | double m_El;         // Elevation, radians | 
| 98 | double m_Range;      // Range, kilometers | 
| 99 | double m_RangeRate;  // Range rate of change, km/sec | 
| 100 | // Negative value means "towards observer" | 
| 101 | }; | 
| 102 |  | 
| 103 | // cVector.h: interface for the cVector class. | 
| 104 | // | 
| 105 | // Copyright 2003 (c) Michael F. Henry | 
| 106 | // | 
| 107 | ////////////////////////////////////////////////////////////////////// | 
| 108 |  | 
| 109 | class cVector | 
| 110 | { | 
| 111 | public: | 
| 112 | cVector(double x = 0.0, double y = 0.0, double z = 0.0, double w = 0.0) : | 
| 113 | m_x(x), m_y(y), m_z(z), m_w(w) {} | 
| 114 | virtual ~cVector() {}; | 
| 115 |  | 
| 116 | void Sub(const cVector&);     // subtraction | 
| 117 | void Mul(double factor);      // multiply each component by 'factor' | 
| 118 |  | 
| 119 | double Angle(const cVector&) const;    // angle between two vectors | 
| 120 | double Magnitude() const;              // vector magnitude | 
| 121 | double Dot(const cVector& vec) const;  // dot product | 
| 122 |  | 
| 123 | // protected: | 
| 124 | double m_x; | 
| 125 | double m_y; | 
| 126 | double m_z; | 
| 127 | double m_w; | 
| 128 | }; | 
| 129 | // | 
| 130 | // cTle.h | 
| 131 | // | 
| 132 | // This class will accept a single set of two-line elements and then allow | 
| 133 | // a client to request specific fields, such as epoch, mean motion, | 
| 134 | // etc., from the set. | 
| 135 | // | 
| 136 | // Copyright 1996-2003 Michael F. Henry | 
| 137 | // | 
| 138 | ///////////////////////////////////////////////////////////////////////////// | 
| 139 | class cTle | 
| 140 | { | 
| 141 | public: | 
| 142 | cTle(string&, string&, string&); | 
| 143 | cTle(const cTle &tle); | 
| 144 | ~cTle(); | 
| 145 |  | 
| 146 | enum eTleLine | 
| 147 | { | 
| 148 | LINE_ZERO, | 
| 149 | LINE_ONE, | 
| 150 | LINE_TWO | 
| 151 | }; | 
| 152 |  | 
| 153 | enum eField | 
| 154 | { | 
| 155 | FLD_FIRST, | 
| 156 | FLD_NORADNUM = FLD_FIRST, | 
| 157 | FLD_INTLDESC, | 
| 158 | FLD_SET,       // TLE set number | 
| 159 | FLD_EPOCHYEAR, // Epoch: Last two digits of year | 
| 160 | FLD_EPOCHDAY,  // Epoch: Fractional Julian Day of year | 
| 161 | FLD_ORBITNUM,  // Orbit at epoch | 
| 162 | FLD_I,         // Inclination | 
| 163 | FLD_RAAN,      // R.A. ascending node | 
| 164 | FLD_E,         // Eccentricity | 
| 165 | FLD_ARGPER,    // Argument of perigee | 
| 166 | FLD_M,         // Mean anomaly | 
| 167 | FLD_MMOTION,   // Mean motion | 
| 168 | FLD_MMOTIONDT, // First time derivative of mean motion | 
| 169 | FLD_MMOTIONDT2,// Second time derivative of mean motion | 
| 170 | FLD_BSTAR,     // BSTAR Drag | 
| 171 | FLD_LAST       // MUST be last | 
| 172 | }; | 
| 173 |  | 
| 174 | enum eUnits | 
| 175 | { | 
| 176 | U_FIRST, | 
| 177 | U_RAD = U_FIRST,  // radians | 
| 178 | U_DEG,            // degrees | 
| 179 | U_NATIVE,         // TLE format native units (no conversion) | 
| 180 | U_LAST            // MUST be last | 
| 181 | }; | 
| 182 |  | 
| 183 | void Initialize(); | 
| 184 |  | 
| 185 | static int    CheckSum(const string&); | 
| 186 | static bool   IsValidLine(string&, eTleLine); | 
| 187 | static string ExpToDecimal(const string&); | 
| 188 |  | 
| 189 | static void TrimLeft(string&); | 
| 190 | static void TrimRight(string&); | 
| 191 |  | 
| 192 | double getField(eField fld,               // which field to retrieve | 
| 193 | eUnits unit  = U_NATIVE,  // return units in rad, deg etc. | 
| 194 | string *pstr = NULL,      // return ptr for str value | 
| 195 | bool bStrUnits = false)   // 'true': append units to str val | 
| 196 | const; | 
| 197 | string getName()  const { return m_strName; } | 
| 198 | string getLine1() const { return m_strLine1;} | 
| 199 | string getLine2() const { return m_strLine2;} | 
| 200 |  | 
| 201 | protected: | 
| 202 | static double ConvertUnits(double val, eField fld, eUnits units); | 
| 203 |  | 
| 204 | private: | 
| 205 | string getUnits(eField) const; | 
| 206 | double getFieldNumeric(eField) const; | 
| 207 |  | 
| 208 | // Satellite name and two data lines | 
| 209 | string m_strName; | 
| 210 | string m_strLine1; | 
| 211 | string m_strLine2; | 
| 212 |  | 
| 213 | // Converted fields, in atof()-readable form | 
| 214 | string m_Field[FLD_LAST]; | 
| 215 |  | 
| 216 | // Cache of field values in "double" format | 
| 217 | typedef int FldKey; | 
| 218 | FldKey Key(eUnits u, eField f) const { return (u * 100) + f; } | 
| 219 | mutable map<FldKey, double>  m_mapCache; | 
| 220 | }; | 
| 221 |  | 
| 222 | /////////////////////////////////////////////////////////////////////////// | 
| 223 | // | 
| 224 | // TLE data format | 
| 225 | // | 
| 226 | // [Reference: T.S. Kelso] | 
| 227 | // | 
| 228 | // Two line element data consists of three lines in the following format: | 
| 229 | // | 
| 230 | //  AAAAAAAAAAAAAAAAAAAAAA | 
| 231 | //  1 NNNNNU NNNNNAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN | 
| 232 | //  2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN | 
| 233 | // | 
| 234 | //  Line 0 is a twenty-two-character name. | 
| 235 | // | 
| 236 | //   Lines 1 and 2 are the standard Two-Line Orbital Element Set Format identical | 
| 237 | //   to that used by NORAD and NASA.  The format description is: | 
| 238 | // | 
| 239 | //     Line 1 | 
| 240 | //     Column    Description | 
| 241 | //     01-01     Line Number of Element Data | 
| 242 | //     03-07     Satellite Number | 
| 243 | //     10-11     International Designator (Last two digits of launch year) | 
| 244 | //     12-14     International Designator (Launch number of the year) | 
| 245 | //     15-17     International Designator (Piece of launch) | 
| 246 | //     19-20     Epoch Year (Last two digits of year) | 
| 247 | //     21-32     Epoch (Julian Day and fractional portion of the day) | 
| 248 | //     34-43     First Time Derivative of the Mean Motion | 
| 249 | //               or Ballistic Coefficient (Depending on ephemeris type) | 
| 250 | //     45-52     Second Time Derivative of Mean Motion (decimal point assumed; | 
| 251 | //               blank if N/A) | 
| 252 | //     54-61     BSTAR drag term if GP4 general perturbation theory was used. | 
| 253 | //               Otherwise, radiation pressure coefficient.  (Decimal point assumed) | 
| 254 | //     63-63     Ephemeris type | 
| 255 | //     65-68     Element number | 
| 256 | //     69-69     Check Sum (Modulo 10) | 
| 257 | //               (Letters, blanks, periods, plus signs = 0; minus signs = 1) | 
| 258 | // | 
| 259 | //     Line 2 | 
| 260 | //     Column    Description | 
| 261 | //     01-01     Line Number of Element Data | 
| 262 | //     03-07     Satellite Number | 
| 263 | //     09-16     Inclination [Degrees] | 
| 264 | //     18-25     Right Ascension of the Ascending Node [Degrees] | 
| 265 | //     27-33     Eccentricity (decimal point assumed) | 
| 266 | //     35-42     Argument of Perigee [Degrees] | 
| 267 | //     44-51     Mean Anomaly [Degrees] | 
| 268 | //     53-63     Mean Motion [Revs per day] | 
| 269 | //     64-68     Revolution number at epoch [Revs] | 
| 270 | //     69-69     Check Sum (Modulo 10) | 
| 271 | // | 
| 272 | //     All other columns are blank or fixed. | 
| 273 | // | 
| 274 | // Example: | 
| 275 | // | 
| 276 | // NOAA 6 | 
| 277 | // 1 11416U          86 50.28438588 0.00000140           67960-4 0  5293 | 
| 278 | // 2 11416  98.5105  69.3305 0012788  63.2828 296.9658 14.24899292346978 | 
| 279 |  | 
| 280 | // | 
| 281 | // cJulian.h | 
| 282 | // | 
| 283 | // Copyright (c) 2003 Michael F. Henry | 
| 284 | // | 
| 285 | // | 
| 286 | // See note in cJulian.cpp for information on this class and the epoch dates | 
| 287 | // | 
| 288 | const double EPOCH_JAN1_00H_1900 = 2415019.5; // Jan 1.0 1900 = Jan 1 1900 00h UTC | 
| 289 | const double EPOCH_JAN1_12H_1900 = 2415020.0; // Jan 1.5 1900 = Jan 1 1900 12h UTC | 
| 290 | const double EPOCH_JAN1_12H_2000 = 2451545.0; // Jan 1.5 2000 = Jan 1 2000 12h UTC | 
| 291 |  | 
| 292 | ////////////////////////////////////////////////////////////////////////////// | 
| 293 | class cJulian | 
| 294 | { | 
| 295 | public: | 
| 296 | cJulian() { Initialize(2000, 1); } | 
| 297 | explicit cJulian(time_t t);              // Create from time_t | 
| 298 | explicit cJulian(int year, double day);  // Create from year, day of year | 
| 299 | explicit cJulian(int year,               // i.e., 2004 | 
| 300 | int mon,                // 1..12 | 
| 301 | int day,                // 1..31 | 
| 302 | int hour,               // 0..23 | 
| 303 | int min,                // 0..59 | 
| 304 | double sec = 0.0);      // 0..(59.999999...) | 
| 305 | ~cJulian() {}; | 
| 306 |  | 
| 307 | double toGMST() const;           // Greenwich Mean Sidereal Time | 
| 308 | double toLMST(double lon) const; // Local Mean Sideral Time | 
| 309 | time_t toTime() const;           // To time_t type - avoid using | 
| 310 |  | 
| 311 | double FromJan1_00h_1900() const { return m_Date - EPOCH_JAN1_00H_1900; } | 
| 312 | double FromJan1_12h_1900() const { return m_Date - EPOCH_JAN1_12H_1900; } | 
| 313 | double FromJan1_12h_2000() const { return m_Date - EPOCH_JAN1_12H_2000; } | 
| 314 |  | 
| 315 | void getComponent(int *pYear, int *pMon = NULL, double *pDOM = NULL) const; | 
| 316 | double getDate() const { return m_Date; } | 
| 317 |  | 
| 318 | void addDay (double day) { m_Date += day;                 } | 
| 319 | void addHour(double hr ) { m_Date += (hr  / HR_PER_DAY ); } | 
| 320 | void addMin (double min) { m_Date += (min / MIN_PER_DAY); } | 
| 321 | void addSec (double sec) { m_Date += (sec / SEC_PER_DAY); } | 
| 322 |  | 
| 323 | double spanDay (const cJulian& b) const { return m_Date - b.m_Date;        } | 
| 324 | double spanHour(const cJulian& b) const { return spanDay(b) * HR_PER_DAY;  } | 
| 325 | double spanMin (const cJulian& b) const { return spanDay(b) * MIN_PER_DAY; } | 
| 326 | double spanSec (const cJulian& b) const { return spanDay(b) * SEC_PER_DAY; } | 
| 327 |  | 
| 328 | static bool IsLeapYear(int y) | 
| 329 | { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); } | 
| 330 |  | 
| 331 | protected: | 
| 332 | void Initialize(int year, double day); | 
| 333 |  | 
| 334 | double m_Date; // Julian date | 
| 335 | }; | 
| 336 | // | 
| 337 | // cEci.h | 
| 338 | // | 
| 339 | // Copyright (c) 2003 Michael F. Henry | 
| 340 | // | 
| 341 | ////////////////////////////////////////////////////////////////////// | 
| 342 | // class cEci | 
| 343 | // Encapsulates an Earth-Centered Inertial position, velocity, and time. | 
| 344 | class cEci | 
| 345 | { | 
| 346 | public: | 
| 347 | cEci() { m_VecUnits = UNITS_NONE; } | 
| 348 | cEci(const cCoordGeo &geo, const cJulian &cJulian); | 
| 349 | cEci(const cVector &pos, const cVector &vel, | 
| 350 | const cJulian &date, bool IsAeUnits = true); | 
| 351 | virtual ~cEci() {}; | 
| 352 |  | 
| 353 | cCoordGeo toGeo(); | 
| 354 |  | 
| 355 | cVector getPos()  const { return m_pos;  } | 
| 356 | cVector getVel()  const { return m_vel;  } | 
| 357 | cJulian getDate() const { return m_date; } | 
| 358 |  | 
| 359 | void setUnitsAe() { m_VecUnits = UNITS_AE; } | 
| 360 | void setUnitsKm() { m_VecUnits = UNITS_KM; } | 
| 361 | bool UnitsAreAe() const { return m_VecUnits == UNITS_AE; } | 
| 362 | bool UnitsAreKm() const { return m_VecUnits == UNITS_KM; } | 
| 363 | void ae2km();  // Convert position, velocity vector units from AE to km | 
| 364 |  | 
| 365 | protected: | 
| 366 | void MulPos(double factor) { m_pos.Mul(factor); } | 
| 367 | void MulVel(double factor) { m_vel.Mul(factor); } | 
| 368 |  | 
| 369 | enum VecUnits | 
| 370 | { | 
| 371 | UNITS_NONE, // not initialized | 
| 372 | UNITS_AE, | 
| 373 | UNITS_KM, | 
| 374 | }; | 
| 375 |  | 
| 376 | cVector  m_pos; | 
| 377 | cVector  m_vel; | 
| 378 | cJulian  m_date; | 
| 379 | VecUnits m_VecUnits; | 
| 380 | }; | 
| 381 | #endif |