#include "SGP4.h" #include "Vector.h" #include "SatelliteException.h" #include #include SGP4::SGP4(void) { Reset(); } SGP4::SGP4(const Tle& tle) { SetTle(tle); } SGP4::~SGP4(void) { } void SGP4::SetTle(const Tle& tle) { /* * reset all constants etc */ Reset(); /* * extract and format tle data */ mean_anomoly_ = tle.MeanAnomaly(false); ascending_node_ = tle.RightAscendingNode(false); argument_perigee_ = tle.ArgumentPerigee(false); eccentricity_ = tle.Eccentricity(); inclination_ = tle.Inclination(false); mean_motion_ = tle.MeanMotion() * kTWOPI / kMINUTES_PER_DAY; bstar_ = tle.BStar(); epoch_ = tle.Epoch(); /* * error checks */ if (eccentricity_ < 0.0 || eccentricity_ > 1.0 - 1.0e-3) { throw SatelliteException("Eccentricity out of range"); } if (inclination_ < 0.0 || eccentricity_ > kPI) { throw SatelliteException("Inclination out of range"); } Initialize(); } void SGP4::Initialize() { /* * recover original mean motion (xnodp) and semimajor axis (aodp) * from input elements */ const double a1 = pow(kXKE / MeanMotion(), kTWOTHIRD); common_consts_.cosio = cos(Inclination()); common_consts_.sinio = sin(Inclination()); const double theta2 = common_consts_.cosio * common_consts_.cosio; common_consts_.x3thm1 = 3.0 * theta2 - 1.0; const double eosq = Eccentricity() * Eccentricity(); const double betao2 = 1.0 - eosq; const double betao = sqrt(betao2); const double temp = (1.5 * kCK2) * common_consts_.x3thm1 / (betao * betao2); const double del1 = temp / (a1 * a1); const double a0 = a1 * (1.0 - del1 * (1.0 / 3.0 + del1 * (1.0 + del1 * 134.0 / 81.0))); const double del0 = temp / (a0 * a0); recovered_mean_motion_ = MeanMotion() / (1.0 + del0); /* * alternative way to calculate * doesnt affect final results * recovered_semi_major_axis_ = pow(XKE / RecoveredMeanMotion(), TWOTHIRD); */ recovered_semi_major_axis_ = a0 / (1.0 - del0); /* * find perigee and period */ perigee_ = (RecoveredSemiMajorAxis() * (1.0 - Eccentricity()) - kAE) * kXKMPER; period_ = kTWOPI / RecoveredMeanMotion(); if (Period() >= 225.0) { use_deep_space_ = true; } else { use_deep_space_ = false; use_simple_model_ = false; /* * for perigee less than 220 kilometers, the simple_model flag is set and * the equations are truncated to linear variation in sqrt a and * quadratic variation in mean anomly. also, the c3 term, the * delta omega term and the delta m term are dropped */ if (Perigee() < 220.0) { use_simple_model_ = true; } } /* * for perigee below 156km, the values of * s4 and qoms2t are altered */ double s4 = kS; double qoms24 = kQOMS2T; if (Perigee() < 156.0) { s4 = Perigee() - 78.0; if (Perigee() < 98.0) { s4 = 20.0; } qoms24 = pow((120.0 - s4) * kAE / kXKMPER, 4.0); s4 = s4 / kXKMPER + kAE; } /* * generate constants */ const double pinvsq = 1.0 / (RecoveredSemiMajorAxis() * RecoveredSemiMajorAxis() * betao2 * betao2); const double tsi = 1.0 / (RecoveredSemiMajorAxis() - s4); common_consts_.eta = RecoveredSemiMajorAxis() * Eccentricity() * tsi; const double etasq = common_consts_.eta * common_consts_.eta; const double eeta = Eccentricity() * common_consts_.eta; const double psisq = fabs(1.0 - etasq); const double coef = qoms24 * pow(tsi, 4.0); const double coef1 = coef / pow(psisq, 3.5); const double c2 = coef1 * RecoveredMeanMotion() * (RecoveredSemiMajorAxis() * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.75 * kCK2 * tsi / psisq * common_consts_.x3thm1 * (8.0 + 3.0 * etasq * (8.0 + etasq))); common_consts_.c1 = BStar() * c2; common_consts_.a3ovk2 = -kXJ3 / kCK2 * pow(kAE, 3.0); common_consts_.x1mth2 = 1.0 - theta2; common_consts_.c4 = 2.0 * RecoveredMeanMotion() * coef1 * RecoveredSemiMajorAxis() * betao2 * (common_consts_.eta * (2.0 + 0.5 * etasq) + Eccentricity() * (0.5 + 2.0 * etasq) - 2.0 * kCK2 * tsi / (RecoveredSemiMajorAxis() * psisq) * (-3.0 * common_consts_.x3thm1 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + 0.75 * common_consts_.x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * ArgumentPerigee()))); const double theta4 = theta2 * theta2; const double temp1 = 3.0 * kCK2 * pinvsq * RecoveredMeanMotion(); const double temp2 = temp1 * kCK2 * pinvsq; const double temp3 = 1.25 * kCK4 * pinvsq * pinvsq * RecoveredMeanMotion(); common_consts_.xmdot = RecoveredMeanMotion() + 0.5 * temp1 * betao * common_consts_.x3thm1 + 0.0625 * temp2 * betao * (13.0 - 78.0 * theta2 + 137.0 * theta4); const double x1m5th = 1.0 - 5.0 * theta2; common_consts_.omgdot = -0.5 * temp1 * x1m5th + 0.0625 * temp2 * (7.0 - 114.0 * theta2 + 395.0 * theta4) + temp3 * (3.0 - 36.0 * theta2 + 49.0 * theta4); const double xhdot1 = -temp1 * common_consts_.cosio; common_consts_.xnodot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * theta2) + 2.0 * temp3 * (3.0 - 7.0 * theta2)) * common_consts_.cosio; common_consts_.xnodcf = 3.5 * betao2 * xhdot1 * common_consts_.c1; common_consts_.t2cof = 1.5 * common_consts_.c1; if (fabs(common_consts_.cosio + 1.0) > 1.5e-12) common_consts_.xlcof = 0.125 * common_consts_.a3ovk2 * common_consts_.sinio * (3.0 + 5.0 * common_consts_.cosio) / (1.0 + common_consts_.cosio); else common_consts_.xlcof = 0.125 * common_consts_.a3ovk2 * common_consts_.sinio * (3.0 + 5.0 * common_consts_.cosio) / 1.5e-12; common_consts_.aycof = 0.25 * common_consts_.a3ovk2 * common_consts_.sinio; common_consts_.x7thm1 = 7.0 * theta2 - 1.0; if (use_deep_space_) { deepspace_consts_.gsto = Epoch().ToGreenwichSiderealTime(); DeepSpaceInitialize(eosq, common_consts_.sinio, common_consts_.cosio, betao, theta2, betao2, common_consts_.xmdot, common_consts_.omgdot, common_consts_.xnodot); } else { double c3 = 0.0; if (Eccentricity() > 1.0e-4) { c3 = coef * tsi * common_consts_.a3ovk2 * RecoveredMeanMotion() * kAE * common_consts_.sinio / Eccentricity(); } nearspace_consts_.c5 = 2.0 * coef1 * RecoveredSemiMajorAxis() * betao2 * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq); nearspace_consts_.omgcof = BStar() * c3 * cos(ArgumentPerigee()); nearspace_consts_.xmcof = 0.0; if (Eccentricity() > 1.0e-4) nearspace_consts_.xmcof = -kTWOTHIRD * coef * BStar() * kAE / eeta; nearspace_consts_.delmo = pow(1.0 + common_consts_.eta * (cos(MeanAnomoly())), 3.0); nearspace_consts_.sinmo = sin(MeanAnomoly()); if (!use_simple_model_) { const double c1sq = common_consts_.c1 * common_consts_.c1; nearspace_consts_.d2 = 4.0 * RecoveredSemiMajorAxis() * tsi * c1sq; const double temp = nearspace_consts_.d2 * tsi * common_consts_.c1 / 3.0; nearspace_consts_.d3 = (17.0 * RecoveredSemiMajorAxis() + s4) * temp; nearspace_consts_.d4 = 0.5 * temp * RecoveredSemiMajorAxis() * tsi * (221.0 * RecoveredSemiMajorAxis() + 31.0 * s4) * common_consts_.c1; nearspace_consts_.t3cof = nearspace_consts_.d2 + 2.0 * c1sq; nearspace_consts_.t4cof = 0.25 * (3.0 * nearspace_consts_.d3 + common_consts_.c1 * (12.0 * nearspace_consts_.d2 + 10.0 * c1sq)); nearspace_consts_.t5cof = 0.2 * (3.0 * nearspace_consts_.d4 + 12.0 * common_consts_.c1 * nearspace_consts_.d3 + 6.0 * nearspace_consts_.d2 * nearspace_consts_.d2 + 15.0 * c1sq * (2.0 * nearspace_consts_.d2 + c1sq)); } } first_run_ = false; } void SGP4::FindPosition(Eci* eci, double tsince) const { if (use_deep_space_) FindPositionSDP4(eci, tsince); else FindPositionSGP4(eci, tsince); } void SGP4::FindPosition(Eci* eci, const Julian& date) const { Timespan diff = date - Epoch(); FindPosition(eci, diff.GetTotalMinutes()); } void SGP4::FindPositionSDP4(Eci* eci, double tsince) const { /* * the final values */ double e; double a; double omega; double xl; double xnode; double xincl; /* * update for secular gravity and atmospheric drag */ double xmdf = MeanAnomoly() + common_consts_.xmdot * tsince; double omgadf = ArgumentPerigee() + common_consts_.omgdot * tsince; const double xnoddf = AscendingNode() + common_consts_.xnodot * tsince; const double tsq = tsince * tsince; xnode = xnoddf + common_consts_.xnodcf * tsq; double tempa = 1.0 - common_consts_.c1 * tsince; double tempe = BStar() * common_consts_.c4 * tsince; double templ = common_consts_.t2cof * tsq; double xn = RecoveredMeanMotion(); e = Eccentricity(); xincl = Inclination(); DeepSpaceSecular(tsince, &xmdf, &omgadf, &xnode, &e, &xincl, &xn); if (xn <= 0.0) { throw SatelliteException("Error: #2 (xn <= 0.0)"); } a = pow(kXKE / xn, kTWOTHIRD) * pow(tempa, 2.0); e -= tempe; double xmam = xmdf + RecoveredMeanMotion() * templ; /* * fix tolerance for error recognition */ if (e >= 1.0 || e < -0.001) { throw SatelliteException("Error: #1 (e >= 1.0 || e < -0.001)"); } /* * fix tolerance to avoid a divide by zero */ if (e < 1.0e-6) e = 1.0e-6; DeepSpacePeriodics(tsince, &e, &xincl, &omgadf, &xnode, &xmam); /* * keeping xincl positive important unless you need to display xincl * and dislike negative inclinations */ if (xincl < 0.0) { xincl = -xincl; xnode += kPI; omgadf -= kPI; } xl = xmam + omgadf + xnode; omega = omgadf; if (e < 0.0 || e > 1.0) { throw SatelliteException("Error: #3 (e < 0.0 || e > 1.0)"); } /* * re-compute the perturbed values */ const double perturbed_sinio = sin(xincl); const double perturbed_cosio = cos(xincl); const double perturbed_theta2 = perturbed_cosio * perturbed_cosio; const double perturbed_x3thm1 = 3.0 * perturbed_theta2 - 1.0; const double perturbed_x1mth2 = 1.0 - perturbed_theta2; const double perturbed_x7thm1 = 7.0 * perturbed_theta2 - 1.0; double perturbed_xlcof; if (fabs(perturbed_cosio + 1.0) > 1.5e-12) perturbed_xlcof = 0.125 * common_consts_.a3ovk2 * perturbed_sinio * (3.0 + 5.0 * perturbed_cosio) / (1.0 + perturbed_cosio); else perturbed_xlcof = 0.125 * common_consts_.a3ovk2 * perturbed_sinio * (3.0 + 5.0 * perturbed_cosio) / 1.5e-12; const double perturbed_aycof = 0.25 * common_consts_.a3ovk2 * perturbed_sinio; /* * using calculated values, find position and velocity */ CalculateFinalPositionVelocity(eci, tsince, e, a, omega, xl, xnode, xincl, perturbed_xlcof, perturbed_aycof, perturbed_x3thm1, perturbed_x1mth2, perturbed_x7thm1, perturbed_cosio, perturbed_sinio); } void SGP4::FindPositionSGP4(Eci* eci, double tsince) const { /* * the final values */ double e; double a; double omega; double xl; double xnode; double xincl; /* * update for secular gravity and atmospheric drag */ const double xmdf = MeanAnomoly() + common_consts_.xmdot * tsince; const double omgadf = ArgumentPerigee() + common_consts_.omgdot * tsince; const double xnoddf = AscendingNode() + common_consts_.xnodot * tsince; const double tsq = tsince * tsince; xnode = xnoddf + common_consts_.xnodcf * tsq; double tempa = 1.0 - common_consts_.c1 * tsince; double tempe = BStar() * common_consts_.c4 * tsince; double templ = common_consts_.t2cof * tsq; xincl = Inclination(); omega = omgadf; double xmp = xmdf; if (!use_simple_model_) { const double delomg = nearspace_consts_.omgcof * tsince; const double delm = nearspace_consts_.xmcof * (pow(1.0 + common_consts_.eta * cos(xmdf), 3.0) - nearspace_consts_.delmo); const double temp = delomg + delm; xmp += temp; omega -= temp; const double tcube = tsq * tsince; const double tfour = tsince * tcube; tempa = tempa - nearspace_consts_.d2 * tsq - nearspace_consts_.d3 * tcube - nearspace_consts_.d4 * tfour; tempe += BStar() * nearspace_consts_.c5 * (sin(xmp) - nearspace_consts_.sinmo); templ += nearspace_consts_.t3cof * tcube + tfour * (nearspace_consts_.t4cof + tsince * nearspace_consts_.t5cof); } a = RecoveredSemiMajorAxis() * pow(tempa, 2.0); e = Eccentricity() - tempe; xl = xmp + omega + xnode + RecoveredMeanMotion() * templ; if (xl <= 0.0) { throw SatelliteException("Error: #2 (xl <= 0.0)"); } /* * fix tolerance for error recognition */ if (e >= 1.0 || e < -0.001) { throw SatelliteException("Error: #1 (e >= 1.0 || e < -0.001)"); } /* * fix tolerance to avoid a divide by zero */ if (e < 1.0e-6) e = 1.0e-6; /* * using calculated values, find position and velocity * we can pass in constants from Initialize() as these dont change */ CalculateFinalPositionVelocity(eci, tsince, e, a, omega, xl, xnode, xincl, common_consts_.xlcof, common_consts_.aycof, common_consts_.x3thm1, common_consts_.x1mth2, common_consts_.x7thm1, common_consts_.cosio, common_consts_.sinio); } void SGP4::CalculateFinalPositionVelocity(Eci* eci, const double& tsince, const double& e, const double& a, const double& omega, const double& xl, const double& xnode, const double& xincl, const double& xlcof, const double& aycof, const double& x3thm1, const double& x1mth2, const double& x7thm1, const double& cosio, const double& sinio) const { double temp; double temp1; double temp2; double temp3; const double beta = sqrt(1.0 - e * e); const double xn = kXKE / pow(a, 1.5); /* * long period periodics */ const double axn = e * cos(omega); temp = 1.0 / (a * beta * beta); const double xll = temp * xlcof * axn; const double aynl = temp * aycof; const double xlt = xl + xll; const double ayn = e * sin(omega) + aynl; const double elsq = axn * axn + ayn * ayn; /* * solve keplers equation * - solve using Newton-Raphson root solving * - here capu is almost the mean anomoly * - initialise the eccentric anomaly term epw * - The fmod saves reduction of angle to +/-2pi in sin/cos() and prevents * convergence problems. */ const double capu = fmod(xlt - xnode, kTWOPI); double epw = capu; double sinepw = 0.0; double cosepw = 0.0; double ecose = 0.0; double esine = 0.0; /* * sensibility check for N-R correction */ const double max_newton_naphson = 1.25 * fabs(sqrt(elsq)); bool kepler_running = true; for (int i = 0; i < 10 && kepler_running; i++) { sinepw = sin(epw); cosepw = cos(epw); ecose = axn * cosepw + ayn * sinepw; esine = axn * sinepw - ayn * cosepw; double f = capu - epw + esine; if (fabs(f) < 1.0e-12) { kepler_running = false; } else { /* * 1st order Newton-Raphson correction */ const double fdot = 1.0 - ecose; double delta_epw = f / fdot; /* * 2nd order Newton-Raphson correction. * f / (fdot - 0.5 * d2f * f/fdot) */ if (i == 0) { if (delta_epw > max_newton_naphson) delta_epw = max_newton_naphson; else if (delta_epw < -max_newton_naphson) delta_epw = -max_newton_naphson; } else { delta_epw = f / (fdot + 0.5 * esine * delta_epw); } /* * Newton-Raphson correction of -F/DF */ epw += delta_epw; } } /* * short period preliminary quantities */ temp = 1.0 - elsq; const double pl = a * temp; if (pl < 0.0) { throw SatelliteException("Error: #4 (pl < 0.0)"); } const double r = a * (1.0 - ecose); temp1 = 1.0 / r; const double rdot = kXKE * sqrt(a) * esine * temp1; const double rfdot = kXKE * sqrt(pl) * temp1; temp2 = a * temp1; const double betal = sqrt(temp); temp3 = 1.0 / (1.0 + betal); const double cosu = temp2 * (cosepw - axn + ayn * esine * temp3); const double sinu = temp2 * (sinepw - ayn - axn * esine * temp3); const double u = atan2(sinu, cosu); const double sin2u = 2.0 * sinu * cosu; const double cos2u = 2.0 * cosu * cosu - 1.0; temp = 1.0 / pl; temp1 = kCK2 * temp; temp2 = temp1 * temp; /* * update for short periodics */ const double rk = r * (1.0 - 1.5 * temp2 * betal * x3thm1) + 0.5 * temp1 * x1mth2 * cos2u; const double uk = u - 0.25 * temp2 * x7thm1 * sin2u; const double xnodek = xnode + 1.5 * temp2 * cosio * sin2u; const double xinck = xincl + 1.5 * temp2 * cosio * sinio * cos2u; const double rdotk = rdot - xn * temp1 * x1mth2 * sin2u; const double rfdotk = rfdot + xn * temp1 * (x1mth2 * cos2u + 1.5 * x3thm1); if (rk < 1.0) { throw SatelliteException("Error: #6 Satellite decayed (rk < 1.0)"); } /* * orientation vectors */ const double sinuk = sin(uk); const double cosuk = cos(uk); const double sinik = sin(xinck); const double cosik = cos(xinck); const double sinnok = sin(xnodek); const double cosnok = cos(xnodek); const double xmx = -sinnok * cosik; const double xmy = cosnok * cosik; const double ux = xmx * sinuk + cosnok * cosuk; const double uy = xmy * sinuk + sinnok * cosuk; const double uz = sinik * sinuk; const double vx = xmx * cosuk - cosnok * sinuk; const double vy = xmy * cosuk - sinnok * sinuk; const double vz = sinik * cosuk; /* * position and velocity */ const double x = rk * ux * kXKMPER; const double y = rk * uy * kXKMPER; const double z = rk * uz * kXKMPER; Vector position(x, y, z); const double xdot = (rdotk * ux + rfdotk * vx) * kXKMPER / 60.0; const double ydot = (rdotk * uy + rfdotk * vy) * kXKMPER / 60.0; const double zdot = (rdotk * uz + rfdotk * vz) * kXKMPER / 60.0; Vector velocity(xdot, ydot, zdot); Timespan diff(tsince); Julian julian = Epoch() + diff; (*eci) = Eci(julian, position, velocity); } static inline double EvaluateCubicPolynomial(const double x, const double constant, const double linear, const double squared, const double cubed) { return constant + x * (linear + x * (squared + x * cubed)); } /* * deep space initialization */ void SGP4::DeepSpaceInitialize(const double& eosq, const double& sinio, const double& cosio, const double& betao, const double& theta2, const double& betao2, const double& xmdot, const double& omgdot, const double& xnodot) { double se = 0.0; double si = 0.0; double sl = 0.0; double sgh = 0.0; double shdq = 0.0; double bfact = 0.0; static const double ZNS = 1.19459E-5; static const double C1SS = 2.9864797E-6; static const double ZES = 0.01675; static const double ZNL = 1.5835218E-4; static const double C1L = 4.7968065E-7; static const double ZEL = 0.05490; static const double ZCOSIS = 0.91744867; static const double ZSINI = 0.39785416; static const double ZSINGS = -0.98088458; static const double ZCOSGS = 0.1945905; static const double Q22 = 1.7891679E-6; static const double Q31 = 2.1460748E-6; static const double Q33 = 2.2123015E-7; static const double ROOT22 = 1.7891679E-6; static const double ROOT32 = 3.7393792E-7; static const double ROOT44 = 7.3636953E-9; static const double ROOT52 = 1.1428639E-7; static const double ROOT54 = 2.1765803E-9; const double aqnv = 1.0 / RecoveredSemiMajorAxis(); const double xpidot = omgdot + xnodot; const double sinq = sin(AscendingNode()); const double cosq = cos(AscendingNode()); const double sing = sin(ArgumentPerigee()); const double cosg = cos(ArgumentPerigee()); /* * initialize lunar / solar terms */ const double jday = Epoch().FromJan1_12h_1900(); const double xnodce = 4.5236020 - 9.2422029e-4 * jday; const double xnodce_temp = fmod(xnodce, kTWOPI); const double stem = sin(xnodce_temp); const double ctem = cos(xnodce_temp); const double zcosil = 0.91375164 - 0.03568096 * ctem; const double zsinil = sqrt(1.0 - zcosil * zcosil); const double zsinhl = 0.089683511 * stem / zsinil; const double zcoshl = sqrt(1.0 - zsinhl * zsinhl); const double c = 4.7199672 + 0.22997150 * jday; const double gam = 5.8351514 + 0.0019443680 * jday; deepspace_consts_.zmol = Fmod2p(c - gam); double zx = 0.39785416 * stem / zsinil; double zy = zcoshl * ctem + 0.91744867 * zsinhl * stem; zx = atan2(zx, zy); zx = fmod(gam + zx - xnodce, kTWOPI); const double zcosgl = cos(zx); const double zsingl = sin(zx); deepspace_consts_.zmos = Fmod2p(6.2565837 + 0.017201977 * jday); /* * do solar terms */ double zcosg = ZCOSGS; double zsing = ZSINGS; double zcosi = ZCOSIS; double zsini = ZSINI; double zcosh = cosq; double zsinh = sinq; double cc = C1SS; double zn = ZNS; double ze = ZES; const double xnoi = 1.0 / RecoveredMeanMotion(); for (int cnt = 0; cnt < 2; cnt++) { /* * solar terms are done a second time after lunar terms are done */ const double a1 = zcosg * zcosh + zsing * zcosi * zsinh; const double a3 = -zsing * zcosh + zcosg * zcosi * zsinh; const double a7 = -zcosg * zsinh + zsing * zcosi * zcosh; const double a8 = zsing * zsini; const double a9 = zsing * zsinh + zcosg * zcosi*zcosh; const double a10 = zcosg * zsini; const double a2 = cosio * a7 + sinio * a8; const double a4 = cosio * a9 + sinio * a10; const double a5 = -sinio * a7 + cosio * a8; const double a6 = -sinio * a9 + cosio * a10; const double x1 = a1 * cosg + a2 * sing; const double x2 = a3 * cosg + a4 * sing; const double x3 = -a1 * sing + a2 * cosg; const double x4 = -a3 * sing + a4 * cosg; const double x5 = a5 * sing; const double x6 = a6 * sing; const double x7 = a5 * cosg; const double x8 = a6 * cosg; const double z31 = 12.0 * x1 * x1 - 3. * x3 * x3; const double z32 = 24.0 * x1 * x2 - 6. * x3 * x4; const double z33 = 12.0 * x2 * x2 - 3. * x4 * x4; double z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * eosq; double z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * eosq; double z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * eosq; const double z11 = -6.0 * a1 * a5 + eosq * (-24. * x1 * x7 - 6. * x3 * x5); const double z12 = -6.0 * (a1 * a6 + a3 * a5) + eosq * (-24. * (x2 * x7 + x1 * x8) - 6. * (x3 * x6 + x4 * x5)); const double z13 = -6.0 * a3 * a6 + eosq * (-24. * x2 * x8 - 6. * x4 * x6); const double z21 = 6.0 * a2 * a5 + eosq * (24. * x1 * x5 - 6. * x3 * x7); const double z22 = 6.0 * (a4 * a5 + a2 * a6) + eosq * (24. * (x2 * x5 + x1 * x6) - 6. * (x4 * x7 + x3 * x8)); const double z23 = 6.0 * a4 * a6 + eosq * (24. * x2 * x6 - 6. * x4 * x8); z1 = z1 + z1 + betao2 * z31; z2 = z2 + z2 + betao2 * z32; z3 = z3 + z3 + betao2 * z33; const double s3 = cc * xnoi; const double s2 = -0.5 * s3 / betao; const double s4 = s3 * betao; const double s1 = -15.0 * Eccentricity() * s4; const double s5 = x1 * x3 + x2 * x4; const double s6 = x2 * x3 + x1 * x4; const double s7 = x2 * x4 - x1 * x3; se = s1 * zn * s5; si = s2 * zn * (z11 + z13); sl = -zn * s3 * (z1 + z3 - 14.0 - 6.0 * eosq); sgh = s4 * zn * (z31 + z33 - 6.0); /* * replaced * sh = -zn * s2 * (z21 + z23 * with * shdq = (-zn * s2 * (z21 + z23)) / sinio */ if (Inclination() < 5.2359877e-2 || Inclination() > kPI - 5.2359877e-2) { shdq = 0.0; } else { shdq = (-zn * s2 * (z21 + z23)) / sinio; } deepspace_consts_.ee2 = 2.0 * s1 * s6; deepspace_consts_.e3 = 2.0 * s1 * s7; deepspace_consts_.xi2 = 2.0 * s2 * z12; deepspace_consts_.xi3 = 2.0 * s2 * (z13 - z11); deepspace_consts_.xl2 = -2.0 * s3 * z2; deepspace_consts_.xl3 = -2.0 * s3 * (z3 - z1); deepspace_consts_.xl4 = -2.0 * s3 * (-21.0 - 9.0 * eosq) * ze; deepspace_consts_.xgh2 = 2.0 * s4 * z32; deepspace_consts_.xgh3 = 2.0 * s4 * (z33 - z31); deepspace_consts_.xgh4 = -18.0 * s4 * ze; deepspace_consts_.xh2 = -2.0 * s2 * z22; deepspace_consts_.xh3 = -2.0 * s2 * (z23 - z21); if (cnt == 1) break; /* * do lunar terms */ deepspace_consts_.sse = se; deepspace_consts_.ssi = si; deepspace_consts_.ssl = sl; deepspace_consts_.ssh = shdq; deepspace_consts_.ssg = sgh - cosio * deepspace_consts_.ssh; deepspace_consts_.se2 = deepspace_consts_.ee2; deepspace_consts_.si2 = deepspace_consts_.xi2; deepspace_consts_.sl2 = deepspace_consts_.xl2; deepspace_consts_.sgh2 = deepspace_consts_.xgh2; deepspace_consts_.sh2 = deepspace_consts_.xh2; deepspace_consts_.se3 = deepspace_consts_.e3; deepspace_consts_.si3 = deepspace_consts_.xi3; deepspace_consts_.sl3 = deepspace_consts_.xl3; deepspace_consts_.sgh3 = deepspace_consts_.xgh3; deepspace_consts_.sh3 = deepspace_consts_.xh3; deepspace_consts_.sl4 = deepspace_consts_.xl4; deepspace_consts_.sgh4 = deepspace_consts_.xgh4; zcosg = zcosgl; zsing = zsingl; zcosi = zcosil; zsini = zsinil; zcosh = zcoshl * cosq + zsinhl * sinq; zsinh = sinq * zcoshl - cosq * zsinhl; zn = ZNL; cc = C1L; ze = ZEL; } deepspace_consts_.sse += se; deepspace_consts_.ssi += si; deepspace_consts_.ssl += sl; deepspace_consts_.ssg += sgh - cosio * shdq; deepspace_consts_.ssh += shdq; deepspace_consts_.resonance_flag = false; deepspace_consts_.synchronous_flag = false; bool initialize_integrator = true; if (RecoveredMeanMotion() < 0.0052359877 && RecoveredMeanMotion() > 0.0034906585) { /* * 24h synchronous resonance terms initialization */ deepspace_consts_.resonance_flag = true; deepspace_consts_.synchronous_flag = true; const double g200 = 1.0 + eosq * (-2.5 + 0.8125 * eosq); const double g310 = 1.0 + 2.0 * eosq; const double g300 = 1.0 + eosq * (-6.0 + 6.60937 * eosq); const double f220 = 0.75 * (1.0 + cosio) * (1.0 + cosio); const double f311 = 0.9375 * sinio * sinio * (1.0 + 3.0 * cosio) - 0.75 * (1.0 + cosio); double f330 = 1.0 + cosio; f330 = 1.875 * f330 * f330 * f330; deepspace_consts_.del1 = 3.0 * RecoveredMeanMotion() * RecoveredMeanMotion() * aqnv * aqnv; deepspace_consts_.del2 = 2.0 * deepspace_consts_.del1 * f220 * g200 * Q22; deepspace_consts_.del3 = 3.0 * deepspace_consts_.del1 * f330 * g300 * Q33 * aqnv; deepspace_consts_.del1 = deepspace_consts_.del1 * f311 * g310 * Q31 * aqnv; integrator_consts_.xlamo = MeanAnomoly() + AscendingNode() + ArgumentPerigee() - deepspace_consts_.gsto; bfact = xmdot + xpidot - kTHDT; bfact += deepspace_consts_.ssl + deepspace_consts_.ssg + deepspace_consts_.ssh; } else if (RecoveredMeanMotion() < 8.26e-3 || RecoveredMeanMotion() > 9.24e-3 || Eccentricity() < 0.5) { initialize_integrator = false; } else { /* * geopotential resonance initialization for 12 hour orbits */ deepspace_consts_.resonance_flag = true; double g211; double g310; double g322; double g410; double g422; double g520; double g201 = -0.306 - (Eccentricity() - 0.64) * 0.440; if (Eccentricity() <= 0.65) { g211 = EvaluateCubicPolynomial(Eccentricity(), 3.616, -13.247, +16.290, 0.0); g310 = EvaluateCubicPolynomial(Eccentricity(), -19.302, 117.390, -228.419, 156.591); g322 = EvaluateCubicPolynomial(Eccentricity(), -18.9068, 109.7927, -214.6334, 146.5816); g410 = EvaluateCubicPolynomial(Eccentricity(), -41.122, 242.694, -471.094, 313.953); g422 = EvaluateCubicPolynomial(Eccentricity(), -146.407, 841.880, -1629.014, 1083.435); g520 = EvaluateCubicPolynomial(Eccentricity(), -532.114, 3017.977, -5740.032, 3708.276); } else { g211 = EvaluateCubicPolynomial(Eccentricity(), -72.099, 331.819, -508.738, 266.724); g310 = EvaluateCubicPolynomial(Eccentricity(), -346.844, 1582.851, -2415.925, 1246.113); g322 = EvaluateCubicPolynomial(Eccentricity(), -342.585, 1554.908, -2366.899, 1215.972); g410 = EvaluateCubicPolynomial(Eccentricity(), -1052.797, 4758.686, -7193.992, 3651.957); g422 = EvaluateCubicPolynomial(Eccentricity(), -3581.69, 16178.11, -24462.77, 12422.52); if (Eccentricity() <= 0.715) { g520 = EvaluateCubicPolynomial(Eccentricity(), 1464.74, -4664.75, 3763.64, 0.0); } else { g520 = EvaluateCubicPolynomial(Eccentricity(), -5149.66, 29936.92, -54087.36, 31324.56); } } double g533; double g521; double g532; if (Eccentricity() < 0.7) { g533 = EvaluateCubicPolynomial(Eccentricity(), -919.2277, 4988.61, -9064.77, 5542.21); g521 = EvaluateCubicPolynomial(Eccentricity(), -822.71072, 4568.6173, -8491.4146, 5337.524); g532 = EvaluateCubicPolynomial(Eccentricity(), -853.666, 4690.25, -8624.77, 5341.4); } else { g533 = EvaluateCubicPolynomial(Eccentricity(), -37995.78, 161616.52, -229838.2, 109377.94); g521 = EvaluateCubicPolynomial(Eccentricity(), -51752.104, 218913.95, -309468.16, 146349.42); g532 = EvaluateCubicPolynomial(Eccentricity(), -40023.88, 170470.89, -242699.48, 115605.82); } const double sini2 = sinio * sinio; const double f220 = 0.75 * (1.0 + 2.0 * cosio + theta2); const double f221 = 1.5 * sini2; const double f321 = 1.875 * sinio * (1.0 - 2.0 * cosio - 3.0 * theta2); const double f322 = -1.875 * sinio * (1.0 + 2.0 * cosio - 3.0 * theta2); const double f441 = 35.0 * sini2 * f220; const double f442 = 39.3750 * sini2 * sini2; const double f522 = 9.84375 * sinio * (sini2 * (1.0 - 2.0 * cosio - 5.0 * theta2) + 0.33333333 * (-2.0 + 4.0 * cosio + 6.0 * theta2)); const double f523 = sinio * (4.92187512 * sini2 * (-2.0 - 4.0 * cosio + 10.0 * theta2) + 6.56250012 * (1.0 + 2.0 * cosio - 3.0 * theta2)); const double f542 = 29.53125 * sinio * (2.0 - 8.0 * cosio + theta2 * (-12.0 + 8.0 * cosio + 10.0 * theta2)); const double f543 = 29.53125 * sinio * (-2.0 - 8.0 * cosio + theta2 * (12.0 + 8.0 * cosio - 10.0 * theta2)); const double xno2 = RecoveredMeanMotion() * RecoveredMeanMotion(); const double ainv2 = aqnv * aqnv; double temp1 = 3.0 * xno2 * ainv2; double temp = temp1 * ROOT22; deepspace_consts_.d2201 = temp * f220 * g201; deepspace_consts_.d2211 = temp * f221 * g211; temp1 = temp1 * aqnv; temp = temp1 * ROOT32; deepspace_consts_.d3210 = temp * f321 * g310; deepspace_consts_.d3222 = temp * f322 * g322; temp1 = temp1 * aqnv; temp = 2.0 * temp1 * ROOT44; deepspace_consts_.d4410 = temp * f441 * g410; deepspace_consts_.d4422 = temp * f442 * g422; temp1 = temp1 * aqnv; temp = temp1 * ROOT52; deepspace_consts_.d5220 = temp * f522 * g520; deepspace_consts_.d5232 = temp * f523 * g532; temp = 2.0 * temp1 * ROOT54; deepspace_consts_.d5421 = temp * f542 * g521; deepspace_consts_.d5433 = temp * f543 * g533; integrator_consts_.xlamo = MeanAnomoly() + AscendingNode() + AscendingNode() - deepspace_consts_.gsto - deepspace_consts_.gsto; bfact = xmdot + xnodot + xnodot - kTHDT - kTHDT; bfact = bfact + deepspace_consts_.ssl + deepspace_consts_.ssh + deepspace_consts_.ssh; } if (initialize_integrator) { /* * initialize integrator */ integrator_consts_.xfact = bfact - RecoveredMeanMotion(); integrator_params_.atime = 0.0; integrator_params_.xni = RecoveredMeanMotion(); integrator_params_.xli = integrator_consts_.xlamo; /* * precompute dot terms for epoch */ DeepSpaceCalcDotTerms(&integrator_consts_.xndot_0, &integrator_consts_.xnddt_0, &integrator_consts_.xldot_0); } } void SGP4::DeepSpaceCalculateLunarSolarTerms(const double t, double* pe, double* pinc, double* pl, double* pgh, double* ph) const { static const double ZES = 0.01675; static const double ZNS = 1.19459E-5; static const double ZNL = 1.5835218E-4; static const double ZEL = 0.05490; /* * calculate solar terms for time t */ double zm = deepspace_consts_.zmos + ZNS * t; if (first_run_) zm = deepspace_consts_.zmos; double zf = zm + 2.0 * ZES * sin(zm); double sinzf = sin(zf); double f2 = 0.5 * sinzf * sinzf - 0.25; double f3 = -0.5 * sinzf * cos(zf); const double ses = deepspace_consts_.se2 * f2 + deepspace_consts_.se3 * f3; const double sis = deepspace_consts_.si2 * f2 + deepspace_consts_.si3 * f3; const double sls = deepspace_consts_.sl2 * f2 + deepspace_consts_.sl3 * f3 + deepspace_consts_.sl4 * sinzf; const double sghs = deepspace_consts_.sgh2 * f2 + deepspace_consts_.sgh3 * f3 + deepspace_consts_.sgh4 * sinzf; const double shs = deepspace_consts_.sh2 * f2 + deepspace_consts_.sh3 * f3; /* * calculate lunar terms for time t */ zm = deepspace_consts_.zmol + ZNL * t; if (first_run_) zm = deepspace_consts_.zmol; zf = zm + 2.0 * ZEL * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); const double sel = deepspace_consts_.ee2 * f2 + deepspace_consts_.e3 * f3; const double sil = deepspace_consts_.xi2 * f2 + deepspace_consts_.xi3 * f3; const double sll = deepspace_consts_.xl2 * f2 + deepspace_consts_.xl3 * f3 + deepspace_consts_.xl4 * sinzf; const double sghl = deepspace_consts_.xgh2 * f2 + deepspace_consts_.xgh3 * f3 + deepspace_consts_.xgh4 * sinzf; const double shl = deepspace_consts_.xh2 * f2 + deepspace_consts_.xh3 * f3; /* * merge calculated values */ (*pe) = ses + sel; (*pinc) = sis + sil; (*pl) = sls + sll; (*pgh) = sghs + sghl; (*ph) = shs + shl; } /* * calculate lunar / solar periodics and apply */ void SGP4::DeepSpacePeriodics(const double& t, double* em, double* xinc, double* omgasm, double* xnodes, double* xll) const { /* * storage for lunar / solar terms set by DeepSpaceCalculateLunarSolarTerms() */ double pe = 0.0; double pinc = 0.0; double pl = 0.0; double pgh = 0.0; double ph = 0.0; /* * calculate lunar / solar terms for current time */ DeepSpaceCalculateLunarSolarTerms(t, &pe, &pinc, &pl, &pgh, &ph); if (!first_run_) { (*xinc) += pinc; (*em) += pe; /* Spacetrack report #3 has sin/cos from before perturbations * added to xinc (oldxinc), but apparently report # 6 has then * from after they are added. * use for strn3 * if (Inclination() >= 0.2) * use for gsfc * if (xinc >= 0.2) * (moved from start of function) */ const double sinis = sin(*xinc); const double cosis = cos(*xinc); if ((*xinc) >= 0.2) { /* * apply periodics directly */ const double tmp_ph = ph / sinis; (*omgasm) += pgh - cosis * tmp_ph; (*xnodes) += tmp_ph; (*xll) += pl; } else { /* * apply periodics with lyddane modification */ const double sinok = sin(*xnodes); const double cosok = cos(*xnodes); double alfdp = sinis * sinok; double betdp = sinis * cosok; const double dalf = ph * cosok + pinc * cosis * sinok; const double dbet = -ph * sinok + pinc * cosis * cosok; alfdp += dalf; betdp += dbet; (*xnodes) = fmod((*xnodes), kTWOPI); if ((*xnodes) < 0.0) (*xnodes) += kTWOPI; double xls = (*xll) + (*omgasm) + cosis * (*xnodes); double dls = pl + pgh - pinc * (*xnodes) * sinis; xls += dls; /* * save old xnodes value */ const double oldxnodes = (*xnodes); (*xnodes) = atan2(alfdp, betdp); if ((*xnodes) < 0.0) (*xnodes) += kTWOPI; /* * Get perturbed xnodes in to same quadrant as original. * RAAN is in the range of 0 to 360 degrees * atan2 is in the range of -180 to 180 degrees */ if (fabs(oldxnodes - (*xnodes)) > kPI) { if ((*xnodes) < oldxnodes) (*xnodes) += kTWOPI; else (*xnodes) -= kTWOPI; } (*xll) += pl; (*omgasm) = xls - (*xll) - cosis * (*xnodes); } } } /* * deep space secular effects */ void SGP4::DeepSpaceSecular(const double& t, double* xll, double* omgasm, double* xnodes, double* em, double* xinc, double* xn) const { static const double STEP = 720.0; static const double STEP2 = 259200.0; (*xll) += deepspace_consts_.ssl * t; (*omgasm) += deepspace_consts_.ssg * t; (*xnodes) += deepspace_consts_.ssh * t; (*em) += deepspace_consts_.sse * t; (*xinc) += deepspace_consts_.ssi * t; if (!deepspace_consts_.resonance_flag) return; /* * 1st condition (if t is less than one time step from epoch) * 2nd condition (if integrator_params_.atime and t are of opposite signs, so zero crossing required) * 3rd condition (if t is closer to zero than integrator_params_.atime, only integrate away from zero) */ if (fabs(t) < STEP || t * integrator_params_.atime <= 0.0 || fabs(t) < fabs(integrator_params_.atime)) { /* * restart from epoch */ integrator_params_.atime = 0.0; integrator_params_.xni = RecoveredMeanMotion(); integrator_params_.xli = integrator_consts_.xlamo; /* * restore precomputed values for epoch */ integrator_params_.xndot_t = integrator_consts_.xndot_0; integrator_params_.xnddt_t = integrator_consts_.xnddt_0; integrator_params_.xldot_t = integrator_consts_.xldot_0; } double ft = t - integrator_params_.atime; /* * if time difference (ft) is greater than the time step (720.0) * loop around until integrator_params_.atime is within one time step of t */ if (fabs(ft) >= STEP) { /* * calculate step direction to allow integrator_params_.atime * to catch up with t */ double delt = -STEP; if (ft >= 0.0) delt = STEP; do { /* * integrate using current dot terms */ DeepSpaceIntegrator(delt, STEP2, integrator_params_.xndot_t, integrator_params_.xnddt_t, integrator_params_.xldot_t); /* * calculate dot terms for next integration */ DeepSpaceCalcDotTerms(&integrator_params_.xndot_t, &integrator_params_.xnddt_t, &integrator_params_.xldot_t); ft = t - integrator_params_.atime; } while (fabs(ft) >= STEP); } /* * integrator */ (*xn) = integrator_params_.xni + integrator_params_.xndot_t * ft + integrator_params_.xnddt_t * ft * ft * 0.5; const double xl = integrator_params_.xli + integrator_params_.xldot_t * ft + integrator_params_.xndot_t * ft * ft * 0.5; const double temp = -(*xnodes) + deepspace_consts_.gsto + t * kTHDT; if (deepspace_consts_.synchronous_flag) (*xll) = xl + temp - (*omgasm); else (*xll) = xl + temp + temp; } /* * calculate dot terms */ void SGP4::DeepSpaceCalcDotTerms(double* xndot, double* xnddt, double* xldot) const { static const double G22 = 5.7686396; static const double G32 = 0.95240898; static const double G44 = 1.8014998; static const double G52 = 1.0508330; static const double G54 = 4.4108898; static const double FASX2 = 0.13130908; static const double FASX4 = 2.8843198; static const double FASX6 = 0.37448087; if (deepspace_consts_.synchronous_flag) { (*xndot) = deepspace_consts_.del1 * sin(integrator_params_.xli - FASX2) + deepspace_consts_.del2 * sin(2.0 * (integrator_params_.xli - FASX4)) + deepspace_consts_.del3 * sin(3.0 * (integrator_params_.xli - FASX6)); (*xnddt) = deepspace_consts_.del1 * cos(integrator_params_.xli - FASX2) + 2.0 * deepspace_consts_.del2 * cos(2.0 * (integrator_params_.xli - FASX4)) + 3.0 * deepspace_consts_.del3 * cos(3.0 * (integrator_params_.xli - FASX6)); } else { const double xomi = ArgumentPerigee() + common_consts_.omgdot * integrator_params_.atime; const double x2omi = xomi + xomi; const double x2li = integrator_params_.xli + integrator_params_.xli; (*xndot) = deepspace_consts_.d2201 * sin(x2omi + integrator_params_.xli - G22) + deepspace_consts_.d2211 * sin(integrator_params_.xli - G22) + deepspace_consts_.d3210 * sin(xomi + integrator_params_.xli - G32) + deepspace_consts_.d3222 * sin(-xomi + integrator_params_.xli - G32) + deepspace_consts_.d4410 * sin(x2omi + x2li - G44) + deepspace_consts_.d4422 * sin(x2li - G44) + deepspace_consts_.d5220 * sin(xomi + integrator_params_.xli - G52) + deepspace_consts_.d5232 * sin(-xomi + integrator_params_.xli - G52) + deepspace_consts_.d5421 * sin(xomi + x2li - G54) + deepspace_consts_.d5433 * sin(-xomi + x2li - G54); (*xnddt) = deepspace_consts_.d2201 * cos(x2omi + integrator_params_.xli - G22) + deepspace_consts_.d2211 * cos(integrator_params_.xli - G22) + deepspace_consts_.d3210 * cos(xomi + integrator_params_.xli - G32) + deepspace_consts_.d3222 * cos(-xomi + integrator_params_.xli - G32) + deepspace_consts_.d5220 * cos(xomi + integrator_params_.xli - G52) + deepspace_consts_.d5232 * cos(-xomi + integrator_params_.xli - G52) + 2.0 * (deepspace_consts_.d4410 * cos(x2omi + x2li - G44) + deepspace_consts_.d4422 * cos(x2li - G44) + deepspace_consts_.d5421 * cos(xomi + x2li - G54) + deepspace_consts_.d5433 * cos(-xomi + x2li - G54)); } (*xldot) = integrator_params_.xni + integrator_consts_.xfact; (*xnddt) *= (*xldot); } /* * deep space integrator for time period of delt */ void SGP4::DeepSpaceIntegrator(const double delt, const double step2, const double xndot, const double xnddt, const double xldot) const { /* * integrator */ integrator_params_.xli += xldot * delt + xndot * step2; integrator_params_.xni += xndot * delt + xnddt * step2; /* * increment integrator time */ integrator_params_.atime += delt; } void SGP4::Reset() { /* * common variables */ first_run_ = true; use_simple_model_ = false; use_deep_space_ = false; mean_anomoly_ = ascending_node_ = argument_perigee_ = eccentricity_ = inclination_ = mean_motion_ = bstar_ = recovered_semi_major_axis_ = recovered_mean_motion_ = perigee_ = period_ = 0.0; epoch_ = Julian(); }