/// <summary> /// Calculate satellite ECI position/velocity for a given time. /// </summary> /// <param name="tsince">Target time, in minutes-past-epoch format.</param> /// <returns>AU-based position/velocity ECI coordinates.</returns> /// <remarks> /// This procedure returns the ECI position and velocity for the satellite /// in the orbit at the given number of minutes since the TLE epoch time. /// The algorithm uses NORAD's Simplified General Perturbation 4 deep space /// orbit model. /// </remarks> public override EciTime GetPosition(double tsince) { // Update for secular gravity and atmospheric drag double xmdf = Orbit.MeanAnomaly + m_xmdot * tsince; double omgadf = Orbit.ArgPerigee + m_omgdot * tsince; double xnoddf = Orbit.RAAN + m_xnodot * tsince; double tsq = tsince * tsince; double xnode = xnoddf + m_xnodcf * tsq; double tempa = 1.0 - m_c1 * tsince; double tempe = Orbit.BStar * m_c4 * tsince; double templ = m_t2cof * tsq; double xn = m_xnodp; double em = 0.0; double xinc = 0.0; DeepSecular(ref xmdf, ref omgadf, ref xnode, ref em, ref xinc, ref xn, ref tsince); double a = Math.Pow(Globals.Xke / xn, 2.0 / 3.0) * Globals.Sqr(tempa); double e = em - tempe; double xmam = xmdf + m_xnodp * templ; DeepPeriodics(ref e, ref xinc, ref omgadf, ref xnode, ref xmam, tsince); double xl = xmam + omgadf + xnode; xn = Globals.Xke / Math.Pow(a, 1.5); return(FinalPosition(xinc, omgadf, e, a, xl, xnode, xn, tsince)); }
// /////////////////////////////////////////////////////////////////////////// // getPosition() // This procedure returns the ECI position and velocity for the satellite // in the orbit at the given number of minutes since the TLE epoch time // using the NORAD Simplified General Perturbation 4, "deep space" orbit // model. // // tsince - Time in minutes since the TLE epoch (GMT). // pECI - pointer to location to store the ECI data. // To convert the returned ECI position vector to km, // multiply each component by: // (XKMMPER / Globals.AE). // To convert the returned ECI velocity vector to km/sec, // multiply each component by: // (XKMPER / Globals.AE) * (MIN_PER_DAY / 86400). public override Eci getPosition(double tsince) { DeepInit(ref m_eosq, ref m_sinio, ref m_cosio, ref m_betao, ref m_aodp, ref m_theta2, ref m_sing, ref m_cosg, ref m_betao2, ref m_xmdot, ref m_omgdot, ref m_xnodot); // Update for secular gravity and atmospheric drag double xmdf = m_Orbit.mnAnomaly() + m_xmdot * tsince; double omgadf = m_Orbit.ArgPerigee + m_omgdot * tsince; double xnoddf = m_Orbit.RAAN + m_xnodot * tsince; double tsq = tsince * tsince; double xnode = xnoddf + m_xnodcf * tsq; double tempa = 1.0 - m_c1 * tsince; double tempe = m_Orbit.BStar * m_c4 * tsince; double templ = m_t2cof * tsq; double xn = m_xnodp; double em = 0.0; double xinc = 0.0; DeepSecular(ref xmdf, ref omgadf, ref xnode, ref em, ref xinc, ref xn, ref tsince); double a = Math.Pow(Globals.XKE / xn, Globals.TWOTHRD) * Globals.Sqr(tempa); double e = em - tempe; double xmam = xmdf + m_xnodp * templ; DeepPeriodics(ref e, ref xinc, ref omgadf, ref xnode, ref xmam); double xl = xmam + omgadf + xnode; xn = Globals.XKE / Math.Pow(a, 1.5); return(FinalPosition(xinc, omgadf, e, a, xl, xnode, xn, tsince)); }
// /////////////////////////////////////////////////////////////////// // Calculate the ECI coordinates of the location "geo" at time "date". // Assumes geo coordinates are km-based. // Assumes the earth is an oblate spheroid as defined in WGS '72. // Reference: The 1992 Astronomical Almanac, page K11 // Reference: www.celestrak.com (Dr. TS Kelso) public Eci(CoordGeo geo, Julian date) { m_VectorUnits = VectorUnits.Km; double mfactor = Globals.TWOPI * (Globals.OMEGA_E / Globals.SEC_PER_DAY); double lat = geo.Latitude; double lon = geo.Longitude; double alt = geo.Altitude; // Calculate Local Mean Sidereal Time (theta) double theta = date.toLMST(lon); double c = 1.0 / Math.Sqrt(1.0 + Globals.F * (Globals.F - 2.0) * Globals.Sqr(Math.Sin(lat))); double s = Globals.Sqr(1.0 - Globals.F) * c; double achcp = (Globals.XKMPER * c + alt) * Math.Cos(lat); m_Date = date; m_Position = new Vector(); m_Position.X = achcp * Math.Cos(theta); // km m_Position.Y = achcp * Math.Sin(theta); // km m_Position.Z = (Globals.XKMPER * s + alt) * Math.Sin(lat); // km m_Position.W = Math.Sqrt(Globals.Sqr(m_Position.X) + Globals.Sqr(m_Position.Y) + Globals.Sqr(m_Position.Z)); // range, km m_Velocity = new Vector(); m_Velocity.X = -mfactor * m_Position.Y; // km / sec m_Velocity.Y = mfactor * m_Position.X; m_Velocity.Z = 0.0; m_Velocity.W = Math.Sqrt(Globals.Sqr(m_Velocity.X) + // range rate km/sec^2 Globals.Sqr(m_Velocity.Y)); }
// /////////////////////////////////////////////////////////////////////////// // Return the corresponding geodetic position (based on the current ECI // coordinates/Julian date). // Assumes the earth is an oblate spheroid as defined in WGS '72. // Side effects: Converts the position and velocity vectors to km-based units. // Reference: The 1992 Astronomical Almanac, page K12. // Reference: www.celestrak.com (Dr. TS Kelso) public CoordGeo toGeo() { ae2km(); // Vectors must be in kilometer-based units double theta = Globals.AcTan(m_Position.Y, m_Position.X); double lon = (theta - m_Date.toGMST()) % Globals.TWOPI; if (lon < 0.0) { lon += Globals.TWOPI; // "wrap" negative modulo } double r = Math.Sqrt(Globals.Sqr(m_Position.X) + Globals.Sqr(m_Position.Y)); double e2 = Globals.F * (2.0 - Globals.F); double lat = Globals.AcTan(m_Position.Z, r); const double DELTA = 1.0e-07; double phi; double c; do { phi = lat; c = 1.0 / Math.Sqrt(1.0 - e2 * Globals.Sqr(Math.Sin(phi))); lat = Globals.AcTan(m_Position.Z + Globals.XKMPER * c * e2 * Math.Sin(phi), r); }while (Math.Abs(lat - phi) > DELTA); double alt = r / Math.Cos(lat) - Globals.XKMPER * c; return(new CoordGeo(lat, lon, alt)); // radians, radians, kilometers }
/// <summary> /// Creates a instance of the class from geodetic coordinates. /// </summary> /// <param name="geo">The geocentric coordinates.</param> /// <param name="date">The Julian date.</param> /// <remarks> /// Assumes the Earth is an oblate spheroid. /// Reference: The 1992 Astronomical Almanac, page K11 /// Reference: www.celestrak.com (Dr. T.S. Kelso) /// </remarks> public Eci(Geo geo, Julian date) { double lat = geo.LatitudeRad; double lon = geo.LongitudeRad; double alt = geo.Altitude; // Calculate Local Mean Sidereal Time (theta) double theta = date.ToLmst(lon); double c = 1.0 / Math.Sqrt(1.0 + Globals.F * (Globals.F - 2.0) * Globals.Sqr(Math.Sin(lat))); double s = Globals.Sqr(1.0 - Globals.F) * c; double achcp = (Globals.Xkmper * c + alt) * Math.Cos(lat); Position = new Vector(); Position.X = achcp * Math.Cos(theta); // km Position.Y = achcp * Math.Sin(theta); // km Position.Z = (Globals.Xkmper * s + alt) * Math.Sin(lat); // km Position.W = Math.Sqrt(Globals.Sqr(Position.X) + Globals.Sqr(Position.Y) + Globals.Sqr(Position.Z)); // range, km Velocity = new Vector(); double mfactor = Globals.TwoPi * (Globals.OmegaE / Globals.SecPerDay); Velocity.X = -mfactor * Position.Y; // km / sec Velocity.Y = mfactor * Position.X; // km / sec Velocity.Z = 0.0; // km / sec Velocity.W = Math.Sqrt(Globals.Sqr(Velocity.X) + // range rate km/sec^2 Globals.Sqr(Velocity.Y)); }
/// <summary> /// Creates a new instance of the class given XYZ coordinates. /// </summary> private Geo(Vector pos, double theta) { theta = theta % Globals.TwoPi; if (theta < 0.0) { // "wrap" negative modulo theta += Globals.TwoPi; } double r = Math.Sqrt(Globals.Sqr(pos.X) + Globals.Sqr(pos.Y)); double e2 = Globals.F * (2.0 - Globals.F); double lat = Globals.AcTan(pos.Z, r); const double DELTA = 1.0e-07; double phi; double c; do { phi = lat; c = 1.0 / Math.Sqrt(1.0 - e2 * Globals.Sqr(Math.Sin(phi))); lat = Globals.AcTan(pos.Z + Globals.Xkmper * c * e2 * Math.Sin(phi), r); }while(Math.Abs(lat - phi) > DELTA); LatitudeRad = lat; LongitudeRad = theta; Altitude = (r / Math.Cos(lat)) - Globals.Xkmper * c; }
/// <summary> /// Standard constructor. /// </summary> /// <param name="tle">Two-line element orbital parameters.</param> public Orbit(Tle tle) { Tle = tle; Epoch = Tle.EpochJulian; m_Inclination = GetRad(Tle.Field.Inclination); m_Eccentricity = Tle.GetField(Tle.Field.Eccentricity); m_RAAN = GetRad(Tle.Field.Raan); m_ArgPerigee = GetRad(Tle.Field.ArgPerigee); m_BStar = Tle.GetField(Tle.Field.BStarDrag); m_Drag = Tle.GetField(Tle.Field.MeanMotionDt); m_MeanAnomaly = GetRad(Tle.Field.MeanAnomaly); m_TleMeanMotion = Tle.GetField(Tle.Field.MeanMotion); // Recover the original mean motion and semimajor axis from the // input elements. double mm = TleMeanMotion; double rpmin = mm * Globals.TwoPi / Globals.MinPerDay; // rads per minute double a1 = Math.Pow(Globals.Xke / rpmin, 2.0 / 3.0); double e = Eccentricity; double i = Inclination; double temp = (1.5 * Globals.Ck2 * (3.0 * Globals.Sqr(Math.Cos(i)) - 1.0) / Math.Pow(1.0 - e * e, 1.5)); double delta1 = temp / (a1 * a1); double a0 = a1 * (1.0 - delta1 * ((1.0 / 3.0) + delta1 * (1.0 + 134.0 / 81.0 * delta1))); double delta0 = temp / (a0 * a0); m_rmMeanMotionRec = rpmin / (1.0 + delta0); m_aeAxisSemiMajorRec = a0 / (1.0 - delta0); m_aeAxisSemiMinorRec = m_aeAxisSemiMajorRec * Math.Sqrt(1.0 - (e * e)); m_kmPerigeeRec = Globals.Xkmper * (m_aeAxisSemiMajorRec * (1.0 - e) - Globals.Ae); m_kmApogeeRec = Globals.Xkmper * (m_aeAxisSemiMajorRec * (1.0 + e) - Globals.Ae); if (Period.TotalMinutes >= 225.0) { // SDP4 - period >= 225 minutes. NoradModel = new NoradSDP4(this); } else { // SGP4 - period < 225 minutes NoradModel = new NoradSGP4(this); } }
// /////////////////////////////////////////////////////////////////// public Orbit(Tle tle) { m_tle = tle; m_jdEpoch = m_tle.EpochJulian; // Recover the original mean motion and semimajor axis from the // input elements. double mm = mnMotion; double rpmin = mm * Globals.TWOPI / Globals.MIN_PER_DAY; // rads per minute double a1 = Math.Pow(Globals.XKE / rpmin, Globals.TWOTHRD); double e = Eccentricity; double i = Inclination; double temp = (1.5 * Globals.CK2 * (3.0 * Globals.Sqr(Math.Cos(i)) - 1.0) / Math.Pow(1.0 - e * e, 1.5)); double delta1 = temp / (a1 * a1); double a0 = a1 * (1.0 - delta1 * ((1.0 / 3.0) + delta1 * (1.0 + 134.0 / 81.0 * delta1))); double delta0 = temp / (a0 * a0); m_rmMeanMotionRec = rpmin / (1.0 + delta0); m_aeAxisSemiMajorRec = a0 / (1.0 - delta0); m_aeAxisSemiMinorRec = m_aeAxisSemiMajorRec * Math.Sqrt(1.0 - (e * e)); m_kmPerigeeRec = Globals.XKMPER * (m_aeAxisSemiMajorRec * (1.0 - e) - Globals.AE); m_kmApogeeRec = Globals.XKMPER * (m_aeAxisSemiMajorRec * (1.0 + e) - Globals.AE); if (Period.TotalMinutes >= 225.0) { // SDP4 - period >= 225 minutes. m_NoradModel = new NoradSDP4(this); } else { // SGP4 - period < 225 minutes m_NoradModel = new NoradSGP4(this); } }
bool gp_sync; // geopotential synchronous // /////////////////////////////////////////////////////////////////////////// public NoradSDP4(Orbit orbit) : base(orbit) { double sinarg = Math.Sin(Orbit.ArgPerigee); double cosarg = Math.Cos(Orbit.ArgPerigee); // Deep space initialization Julian jd = Orbit.Epoch; dp_thgr = jd.ToGmst(); double eq = Orbit.Eccentricity; double aqnv = 1.0 / Orbit.SemiMajor; dp_xqncl = Orbit.Inclination; double xmao = Orbit.MeanAnomaly; double xpidot = m_omgdot + m_xnodot; double sinq = Math.Sin(Orbit.RAAN); double cosq = Math.Cos(Orbit.RAAN); dp_omegaq = Orbit.ArgPerigee; #region Lunar / Solar terms // Initialize lunar solar terms double day = jd.FromJan0_12h_1900(); double dpi_xnodce = 4.5236020 - 9.2422029E-4 * day; double dpi_stem = Math.Sin(dpi_xnodce); double dpi_ctem = Math.Cos(dpi_xnodce); double dpi_zcosil = 0.91375164 - 0.03568096 * dpi_ctem; double dpi_zsinil = Math.Sqrt(1.0 - dpi_zcosil * dpi_zcosil); double dpi_zsinhl = 0.089683511 * dpi_stem / dpi_zsinil; double dpi_zcoshl = Math.Sqrt(1.0 - dpi_zsinhl * dpi_zsinhl); double dpi_c = 4.7199672 + 0.22997150 * day; double dpi_gam = 5.8351514 + 0.0019443680 * day; dp_zmol = Globals.Fmod2p(dpi_c - dpi_gam); double dpi_zx = 0.39785416 * dpi_stem / dpi_zsinil; double dpi_zy = dpi_zcoshl * dpi_ctem + 0.91744867 * dpi_zsinhl * dpi_stem; dpi_zx = Globals.AcTan(dpi_zx, dpi_zy) + dpi_gam - dpi_xnodce; double dpi_zcosgl = Math.Cos(dpi_zx); double dpi_zsingl = Math.Sin(dpi_zx); dp_zmos = 6.2565837 + 0.017201977 * day; dp_zmos = Globals.Fmod2p(dp_zmos); const double zcosis = 0.91744867; const double zsinis = 0.39785416; const double zsings = -0.98088458; const double zcosgs = 0.1945905; const double c1ss = 2.9864797E-6; double zcosg = zcosgs; double zsing = zsings; double zcosi = zcosis; double zsini = zsinis; double zcosh = cosq; double zsinh = sinq; double cc = c1ss; double zn = zns; double ze = zes; double xnoi = 1.0 / Orbit.MeanMotion; double a1; double a3; double a7; double a8; double a9; double a10; double a2; double a4; double a5; double a6; double x1; double x2; double x3; double x4; double x5; double x6; double x7; double x8; double z31; double z32; double z33; double z1; double z2; double z3; double z11; double z12; double z13; double z21; double z22; double z23; double s3; double s2; double s4; double s1; double s5; double s6; double s7; double se = 0.0; double si = 0.0; double sl = 0.0; double sgh = 0.0; double sh = 0.0; double eosq = Globals.Sqr(Orbit.Eccentricity); // Apply the solar and lunar terms on the first pass, then re-apply the // solar terms again on the second pass. for (int pass = 1; pass <= 2; pass++) { // Do solar terms a1 = zcosg * zcosh + zsing * zcosi * zsinh; a3 = -zsing * zcosh + zcosg * zcosi * zsinh; a7 = -zcosg * zsinh + zsing * zcosi * zcosh; a8 = zsing * zsini; a9 = zsing * zsinh + zcosg * zcosi * zcosh; a10 = zcosg * zsini; a2 = m_cosio * a7 + m_sinio * a8; a4 = m_cosio * a9 + m_sinio * a10; a5 = -m_sinio * a7 + m_cosio * a8; a6 = -m_sinio * a9 + m_cosio * a10; x1 = a1 * cosarg + a2 * sinarg; x2 = a3 * cosarg + a4 * sinarg; x3 = -a1 * sinarg + a2 * cosarg; x4 = -a3 * sinarg + a4 * cosarg; x5 = a5 * sinarg; x6 = a6 * sinarg; x7 = a5 * cosarg; x8 = a6 * cosarg; z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * eosq; z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * eosq; z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * eosq; z11 = -6.0 * a1 * a5 + eosq * (-24.0 * x1 * x7 - 6.0 * x3 * x5); z12 = -6.0 * (a1 * a6 + a3 * a5) + eosq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5)); z13 = -6.0 * a3 * a6 + eosq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); z21 = 6.0 * a2 * a5 + eosq * (24.0 * x1 * x5 - 6.0 * x3 * x7); z22 = 6.0 * (a4 * a5 + a2 * a6) + eosq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); z23 = 6.0 * a4 * a6 + eosq * (24.0 * x2 * x6 - 6.0 * x4 * x8); z1 = z1 + z1 + m_betao2 * z31; z2 = z2 + z2 + m_betao2 * z32; z3 = z3 + z3 + m_betao2 * z33; s3 = cc * xnoi; s2 = -0.5 * s3 / m_betao; s4 = s3 * m_betao; s1 = -15.0 * eq * s4; s5 = x1 * x3 + x2 * x4; s6 = x2 * x3 + x1 * x4; 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); if (Orbit.Inclination < 5.2359877E-2) { sh = 0.0; } else { sh = -zn * s2 * (z21 + z23); } dp_ee2 = 2.0 * s1 * s6; dp_e3 = 2.0 * s1 * s7; dp_xi2 = 2.0 * s2 * z12; dp_xi3 = 2.0 * s2 * (z13 - z11); dp_xl2 = -2.0 * s3 * z2; dp_xl3 = -2.0 * s3 * (z3 - z1); dp_xl4 = -2.0 * s3 * (-21.0 - 9.0 * eosq) * ze; dp_xgh2 = 2.0 * s4 * z32; dp_xgh3 = 2.0 * s4 * (z33 - z31); dp_xgh4 = -18.0 * s4 * ze; dp_xh2 = -2.0 * s2 * z22; dp_xh3 = -2.0 * s2 * (z23 - z21); if (pass == 1) { // Do lunar terms dp_sse = se; dp_ssi = si; dp_ssl = sl; dp_ssh = sh / m_sinio; dp_ssg = sgh - m_cosio * dp_ssh; dp_se2 = dp_ee2; dp_si2 = dp_xi2; dp_sl2 = dp_xl2; dp_sgh2 = dp_xgh2; dp_sh2 = dp_xh2; dp_se3 = dp_e3; dp_si3 = dp_xi3; dp_sl3 = dp_xl3; dp_sgh3 = dp_xgh3; dp_sh3 = dp_xh3; dp_sl4 = dp_xl4; dp_sgh4 = dp_xgh4; zcosg = dpi_zcosgl; zsing = dpi_zsingl; zcosi = dpi_zcosil; zsini = dpi_zsinil; zcosh = dpi_zcoshl * cosq + dpi_zsinhl * sinq; zsinh = sinq * dpi_zcoshl - cosq * dpi_zsinhl; zn = znl; const double c1l = 4.7968065E-7; cc = c1l; ze = zel; } } #endregion dp_sse = dp_sse + se; dp_ssi = dp_ssi + si; dp_ssl = dp_ssl + sl; dp_ssg = dp_ssg + sgh - m_cosio / m_sinio * sh; dp_ssh = dp_ssh + sh / m_sinio; // Geopotential resonance initialization for 12 hour orbits gp_reso = false; gp_sync = false; double g310; double f220; double bfact = 0.0; // Determine if orbit is 12- or 24-hour resonant. // Mean motion is given in radians per minute. if ((Orbit.MeanMotion > 0.0034906585) && (Orbit.MeanMotion < 0.0052359877)) { // Orbit is within the Clarke Belt (period is 24-hour resonant). // Synchronous resonance terms initialization gp_reso = true; gp_sync = true; #region 24-hour resonant double g200 = 1.0 + eosq * (-2.5 + 0.8125 * eosq); g310 = 1.0 + 2.0 * eosq; double g300 = 1.0 + eosq * (-6.0 + 6.60937 * eosq); f220 = 0.75 * (1.0 + m_cosio) * (1.0 + m_cosio); double f311 = 0.9375 * m_sinio * m_sinio * (1.0 + 3 * m_cosio) - 0.75 * (1.0 + m_cosio); double f330 = 1.0 + m_cosio; f330 = 1.875 * f330 * f330 * f330; const double q22 = 1.7891679e-06; const double q33 = 2.2123015e-07; const double q31 = 2.1460748e-06; dp_del1 = 3.0 * m_xnodp * m_xnodp * aqnv * aqnv; dp_del2 = 2.0 * dp_del1 * f220 * g200 * q22; dp_del3 = 3.0 * dp_del1 * f330 * g300 * q33 * aqnv; dp_del1 = dp_del1 * f311 * g310 * q31 * aqnv; dp_xlamo = xmao + Orbit.RAAN + Orbit.ArgPerigee - dp_thgr; bfact = m_xmdot + xpidot - thdt; bfact = bfact + dp_ssl + dp_ssg + dp_ssh; #endregion } else if (((Orbit.MeanMotion >= 8.26E-3) && (Orbit.MeanMotion <= 9.24E-3)) && (eq >= 0.5)) { // Period is 12-hour resonant gp_reso = true; #region 12-hour resonant double eoc = eq * eosq; double g201 = -0.306 - (eq - 0.64) * 0.440; double g211; double g322; double g410; double g422; double g520; if (eq <= 0.65) { g211 = 3.616 - 13.247 * eq + 16.290 * eosq; g310 = -19.302 + 117.390 * eq - 228.419 * eosq + 156.591 * eoc; g322 = -18.9068 + 109.7927 * eq - 214.6334 * eosq + 146.5816 * eoc; g410 = -41.122 + 242.694 * eq - 471.094 * eosq + 313.953 * eoc; g422 = -146.407 + 841.880 * eq - 1629.014 * eosq + 1083.435 * eoc; g520 = -532.114 + 3017.977 * eq - 5740.0 * eosq + 3708.276 * eoc; } else { g211 = -72.099 + 331.819 * eq - 508.738 * eosq + 266.724 * eoc; g310 = -346.844 + 1582.851 * eq - 2415.925 * eosq + 1246.113 * eoc; g322 = -342.585 + 1554.908 * eq - 2366.899 * eosq + 1215.972 * eoc; g410 = -1052.797 + 4758.686 * eq - 7193.992 * eosq + 3651.957 * eoc; g422 = -3581.69 + 16178.11 * eq - 24462.77 * eosq + 12422.52 * eoc; if (eq <= 0.715) { g520 = 1464.74 - 4664.75 * eq + 3763.64 * eosq; } else { g520 = -5149.66 + 29936.92 * eq - 54087.36 * eosq + 31324.56 * eoc; } } double g533; double g521; double g532; if (eq < 0.7) { g533 = -919.2277 + 4988.61 * eq - 9064.77 * eosq + 5542.21 * eoc; g521 = -822.71072 + 4568.6173 * eq - 8491.4146 * eosq + 5337.524 * eoc; g532 = -853.666 + 4690.25 * eq - 8624.77 * eosq + 5341.4 * eoc; } else { g533 = -37995.78 + 161616.52 * eq - 229838.2 * eosq + 109377.94 * eoc; g521 = -51752.104 + 218913.95 * eq - 309468.16 * eosq + 146349.42 * eoc; g532 = -40023.88 + 170470.89 * eq - 242699.48 * eosq + 115605.82 * eoc; } double sini2 = m_sinio * m_sinio; double cosi2 = m_cosio * m_cosio; f220 = 0.75 * (1.0 + 2.0 * m_cosio + cosi2); double f221 = 1.5 * sini2; double f321 = 1.875 * m_sinio * (1.0 - 2.0 * m_cosio - 3.0 * cosi2); double f322 = -1.875 * m_sinio * (1.0 + 2.0 * m_cosio - 3.0 * cosi2); double f441 = 35.0 * sini2 * f220; double f442 = 39.3750 * sini2 * sini2; double f522 = 9.84375 * m_sinio * (sini2 * (1.0 - 2.0 * m_cosio - 5.0 * cosi2) + 0.33333333 * (-2.0 + 4.0 * m_cosio + 6.0 * cosi2)); double f523 = m_sinio * (4.92187512 * sini2 * (-2.0 - 4.0 * m_cosio + 10.0 * cosi2) + 6.56250012 * (1.0 + 2.0 * m_cosio - 3.0 * cosi2)); double f542 = 29.53125 * m_sinio * (2.0 - 8.0 * m_cosio + cosi2 * (-12.0 + 8.0 * m_cosio + 10.0 * cosi2)); double f543 = 29.53125 * m_sinio * (-2.0 - 8.0 * m_cosio + cosi2 * (12.0 + 8.0 * m_cosio - 10.0 * cosi2)); double xno2 = m_xnodp * m_xnodp; double ainv2 = aqnv * aqnv; double temp1 = 3.0 * xno2 * ainv2; const double root22 = 1.7891679E-6; const double root32 = 3.7393792E-7; const double root44 = 7.3636953E-9; const double root52 = 1.1428639E-7; const double root54 = 2.1765803E-9; double temp = temp1 * root22; dp_d2201 = temp * f220 * g201; dp_d2211 = temp * f221 * g211; temp1 = temp1 * aqnv; temp = temp1 * root32; dp_d3210 = temp * f321 * g310; dp_d3222 = temp * f322 * g322; temp1 = temp1 * aqnv; temp = 2.0 * temp1 * root44; dp_d4410 = temp * f441 * g410; dp_d4422 = temp * f442 * g422; temp1 = temp1 * aqnv; temp = temp1 * root52; dp_d5220 = temp * f522 * g520; dp_d5232 = temp * f523 * g532; temp = 2.0 * temp1 * root54; dp_d5421 = temp * f542 * g521; dp_d5433 = temp * f543 * g533; dp_xlamo = xmao + Orbit.RAAN + Orbit.RAAN - dp_thgr - dp_thgr; bfact = m_xmdot + m_xnodot + m_xnodot - thdt - thdt; bfact = bfact + dp_ssl + dp_ssh + dp_ssh; #endregion } if (gp_reso || gp_sync) { dp_xfact = bfact - m_xnodp; // Initialize integrator dp_xli = dp_xlamo; dp_xni = m_xnodp; // dp_atime = 0.0; // performed by runtime dp_stepp = 720.0; dp_stepn = -720.0; dp_step2 = 259200.0; } }
/// <summary> /// Returns the topo-centric (azimuth, elevation, etc.) coordinates for /// a target object described by the given ECI coordinates. /// </summary> /// <param name="eci">The ECI coordinates of the target object.</param> /// <returns>The look angle to the target object.</returns> public TopoTime GetLookAngle(EciTime eci) { // Calculate the ECI coordinates for this Site object at the time // of interest. Julian date = eci.Date; EciTime eciSite = PositionEci(date); Vector vecRgRate = new Vector(eci.Velocity.X - eciSite.Velocity.X, eci.Velocity.Y - eciSite.Velocity.Y, eci.Velocity.Z - eciSite.Velocity.Z); double x = eci.Position.X - eciSite.Position.X; double y = eci.Position.Y - eciSite.Position.Y; double z = eci.Position.Z - eciSite.Position.Z; double w = Math.Sqrt(Globals.Sqr(x) + Globals.Sqr(y) + Globals.Sqr(z)); Vector vecRange = new Vector(x, y, z, w); // The site's Local Mean Sidereal Time at the time of interest. double theta = date.ToLmst(LongitudeRad); double sin_lat = Math.Sin(LatitudeRad); double cos_lat = Math.Cos(LatitudeRad); double sin_theta = Math.Sin(theta); double cos_theta = Math.Cos(theta); double top_s = sin_lat * cos_theta * vecRange.X + sin_lat * sin_theta * vecRange.Y - cos_lat * vecRange.Z; double top_e = -sin_theta * vecRange.X + cos_theta * vecRange.Y; double top_z = cos_lat * cos_theta * vecRange.X + cos_lat * sin_theta * vecRange.Y + sin_lat * vecRange.Z; double az = Math.Atan(-top_e / top_s); if (top_s > 0.0) { az += Globals.Pi; } if (az < 0.0) { az += 2.0 * Globals.Pi; } double el = Math.Asin(top_z / vecRange.W); double rate = (vecRange.X * vecRgRate.X + vecRange.Y * vecRgRate.Y + vecRange.Z * vecRgRate.Z) / vecRange.W; TopoTime topo = new TopoTime(az, // azimuth, radians el, // elevation, radians vecRange.W, // range, km rate, // rate, km / sec eci.Date); #if WANT_ATMOSPHERIC_CORRECTION // Elevation correction for atmospheric refraction. // Reference: Astronomical Algorithms by Jean Meeus, pp. 101-104 // Note: Correction is meaningless when apparent elevation is below horizon topo.ElevationRad += Globals.ToRadians((1.02 / Math.Tan(Globals.ToRadians(Globals.ToDegrees(el) + 10.3 / (Globals.ToDegrees(el) + 5.11)))) / 60.0); if (topo.ElevationRad < 0.0) { topo.ElevationRad = el; // Reset to true elevation } if (topo.ElevationRad > (Math.PI / 2.0)) { topo.ElevationRad = (Math.PI / 2.0); } #endif return(topo); }
/// <summary> /// Calculate satellite ECI position/velocity for a given time. /// </summary> /// <param name="tsince">Target time, in minutes-past-epoch format.</param> /// <returns>AU-based position/velocity ECI coordinates.</returns> /// <remarks> /// This procedure returns the ECI position and velocity for the satellite /// in the orbit at the given number of minutes since the TLE epoch time. /// The algorithm uses NORAD's Simplified General Perturbation 4 near earth /// orbit model. /// </remarks> public override EciTime GetPosition(double tsince) { // For m_perigee less than 220 kilometers, the isimp flag is set and // the equations are truncated to linear variation in square root of a // and quadratic variation in mean anomaly. Also, the m_c3 term, the // delta omega term, and the delta m term are dropped. bool isimp = false || (m_aodp * (1.0 - m_satEcc) / Globals.Ae) < (220.0 / Globals.Xkmper + Globals.Ae); double d2 = 0.0; double d3 = 0.0; double d4 = 0.0; double t3cof = 0.0; double t4cof = 0.0; double t5cof = 0.0; if (!isimp) { double c1sq = m_c1 * m_c1; d2 = 4.0 * m_aodp * m_tsi * c1sq; double temp = d2 * m_tsi * m_c1 / 3.0; d3 = (17.0 * m_aodp + m_s4) * temp; d4 = 0.5 * temp * m_aodp * m_tsi * (221.0 * m_aodp + 31.0 * m_s4) * m_c1; t3cof = d2 + 2.0 * c1sq; t4cof = 0.25 * (3.0 * d3 + m_c1 * (12.0 * d2 + 10.0 * c1sq)); t5cof = 0.2 * (3.0 * d4 + 12.0 * m_c1 * d3 + 6.0 * d2 * d2 + 15.0 * c1sq * (2.0 * d2 + c1sq)); } // Update for secular gravity and atmospheric drag. double xmdf = Orbit.MeanAnomaly + m_xmdot * tsince; double omgadf = Orbit.ArgPerigee + m_omgdot * tsince; double xnoddf = Orbit.RAAN + m_xnodot * tsince; double omega = omgadf; double xmp = xmdf; double tsq = tsince * tsince; double xnode = xnoddf + m_xnodcf * tsq; double tempa = 1.0 - m_c1 * tsince; double tempe = Orbit.BStar * m_c4 * tsince; double templ = m_t2cof * tsq; if (!isimp) { double delomg = m_omgcof * tsince; double delm = m_xmcof * (Math.Pow(1.0 + m_eta * Math.Cos(xmdf), 3.0) - m_delmo); double temp = delomg + delm; xmp = xmdf + temp; omega = omgadf - temp; double tcube = tsq * tsince; double tfour = tsince * tcube; tempa = tempa - d2 * tsq - d3 * tcube - d4 * tfour; tempe = tempe + Orbit.BStar * m_c5 * (Math.Sin(xmp) - m_sinmo); templ = templ + t3cof * tcube + tfour * (t4cof + tsince * t5cof); } double a = m_aodp * Globals.Sqr(tempa); double e = m_satEcc - tempe; double xl = xmp + omega + xnode + m_xnodp * templ; double xn = Globals.Xke / Math.Pow(a, 1.5); return(FinalPosition(m_satInc, omgadf, e, a, xl, xnode, xn, tsince)); }
// /////////////////////////////////////////////////////////////////////////// // getLookAngle() // Return the topocentric (azimuth, elevation, etc.) coordinates for a target // object described by the input ECI coordinates. public CoordTopo getLookAngle(Eci eci) { // Calculate the ECI coordinates for this Site object at the time // of interest. Julian date = eci.Date; Eci eciSite = new Eci(m_geo, date); // The Site ECI units are km-based; ensure target ECI units are same if (!eci.UnitsAreKm()) { throw new Exception("ECI units must be kilometer-based"); } Vector vecRgRate = new Vector(eci.Velocity.X - eciSite.Velocity.X, eci.Velocity.Y - eciSite.Velocity.Y, eci.Velocity.Z - eciSite.Velocity.Z); double x = eci.Position.X - eciSite.Position.X; double y = eci.Position.Y - eciSite.Position.Y; double z = eci.Position.Z - eciSite.Position.Z; double w = Math.Sqrt(Globals.Sqr(x) + Globals.Sqr(y) + Globals.Sqr(z)); Vector vecRange = new Vector(x, y, z, w); // The site's Local Mean Sidereal Time at the time of interest. double theta = date.toLMST(Longitude); double sin_lat = Math.Sin(Latitude); double cos_lat = Math.Cos(Latitude); double sin_theta = Math.Sin(theta); double cos_theta = Math.Cos(theta); double top_s = sin_lat * cos_theta * vecRange.X + sin_lat * sin_theta * vecRange.Y - cos_lat * vecRange.Z; double top_e = -sin_theta * vecRange.X + cos_theta * vecRange.Y; double top_z = cos_lat * cos_theta * vecRange.X + cos_lat * sin_theta * vecRange.Y + sin_lat * vecRange.Z; double az = Math.Atan(-top_e / top_s); if (top_s > 0.0) { az += Globals.PI; } if (az < 0.0) { az += 2.0 * Globals.PI; } double el = Math.Asin(top_z / vecRange.W); double rate = (vecRange.X * vecRgRate.X + vecRange.Y * vecRgRate.Y + vecRange.Z * vecRgRate.Z) / vecRange.W; CoordTopo topo = new CoordTopo(az, // azimuth, radians el, // elevation, radians vecRange.W, // range, km rate); // rate, km / sec #if WANT_ATMOSPHERIC_CORRECTION // Elevation correction for atmospheric refraction. // Reference: Astronomical Algorithms by Jean Meeus, pp. 101-104 // Note: Correction is meaningless when apparent elevation is below horizon topo.m_El += Globals.Deg2Rad((1.02 / Math.Tan(Globals.Deg2Rad(Globals.Rad2Deg(el) + 10.3 / (Globals.Rad2Deg(el) + 5.11)))) / 60.0); if (topo.m_El < 0.0) { topo.m_El = el; // Reset to true elevation } if (topo.m_El > (Globals.PI / 2)) { topo.m_El = (Globals.PI / 2); } #endif return(topo); }