/// <summary> /// Gets precessional elements to convert equatorial coordinates of a point from one epoch to another. /// Equatorial coordinates of a point must be reffered to system FK5. /// </summary> /// <param name="jd0">Initial epoch, in Julian Days.</param> /// <param name="jd">Target (final) epoch, in Julian Days.</param> /// <returns> /// <see cref="PrecessionalElements"/> to convert equatorial coordinates of a point from /// one epoch (<paramref name="jd0"/>) to another (<paramref name="jd"/>). /// </returns> /// <remarks> /// This method is taken from AA(I), chapter 20 ("Precession", topic "Rigorous method"). /// </remarks> public static PrecessionalElements ElementsFK5(double jd0, double jd) { PrecessionalElements p = new PrecessionalElements(); p.InitialEpoch = jd0; p.TargetEpoch = jd; double T = (jd0 - 2451545.0) / 36525.0; double t = (jd - jd0) / 36525.0; double T2 = T * T; double t2 = t * t; double t3 = t2 * t; // all values in seconds of arc p.zeta = (2306.2181 + 1.39656 * T - 0.000139 * T2) * t + (0.30188 - 0.000344 * T) * t2 + 0.017998 * t3; p.z = (2306.2181 + 1.39656 * T - 0.000139 * T2) * t + (1.09468 + 0.000066 * T) * t2 + 0.018203 * t3; p.theta = (2004.3109 - 0.85330 * T - 0.000217 * T2) * t - (0.42665 + 0.000217 * T) * t2 - 0.041833 * t3; // convert to degress p.zeta /= 3600; p.z /= 3600; p.theta /= 3600; return(p); }
/// <summary> /// Performs reduction of equatorial coordinates from one epoch to another /// with using of precessional elements. /// </summary> /// <param name="eq0">Equatorial coordinates for initial epoch.</param> /// <param name="p">Precessional elements for reduction from initial epoch to target (final) epoch.</param> /// <returns>Equatorial coordinates for target (final) epoch.</returns> /// <remarks> /// This method is taken from AA(I), formula 20.4. /// </remarks> public static CrdsEquatorial GetEquatorialCoordinates(CrdsEquatorial eq0, PrecessionalElements p) { CrdsEquatorial eq = new CrdsEquatorial(); double sinDelta0 = Math.Sin(Angle.ToRadians(eq0.Delta)); double cosDelta0 = Math.Cos(Angle.ToRadians(eq0.Delta)); double sinTheta = Math.Sin(Angle.ToRadians(p.theta)); double cosTheta = Math.Cos(Angle.ToRadians(p.theta)); double sinAlpha0Zeta = Math.Sin(Angle.ToRadians(eq0.Alpha + p.zeta)); double cosAlpha0Zeta = Math.Cos(Angle.ToRadians(eq0.Alpha + p.zeta)); double A = cosDelta0 * sinAlpha0Zeta; double B = cosTheta * cosDelta0 * cosAlpha0Zeta - sinTheta * sinDelta0; double C = sinTheta * cosDelta0 * cosAlpha0Zeta + cosTheta * sinDelta0; eq.Alpha = Angle.ToDegrees(Math.Atan2(A, B)) + p.z; eq.Alpha = Angle.To360(eq.Alpha); if (Math.Abs(C) == 1) { eq.Delta = Angle.ToDegrees(Math.Acos(A * A + B * B)); } else { eq.Delta = Angle.ToDegrees(Math.Asin(C)); } return(eq); }
/// <summary> /// Calculates ecliptical coordinates of Triton, largest moon of Neptune. /// </summary> /// <param name="jd">Julian Day of calculation</param> /// <param name="neptune">Ecliptical coordinates of Neptune for the Julian Day specified.</param> /// <returns>Ecliptical coordinates of Triton for specified date.</returns> /// <remarks> /// /// The method is based on following works: /// /// 1. Harris, A.W. (1984), "Physical Properties of Neptune and Triton Inferred from the Orbit of Triton" NASA CP-2330, pages 357-373: /// http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1984NASCP2330..357H&defaultprint=YES&filetype=.pdf /// /// 2. Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac, /// University Science Book, Mill Valley (California), 1992, /// Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 373, 6.61-1 Triton /// https://archive.org/download/131123ExplanatorySupplementAstronomicalAlmanac/131123-explanatory-supplement-astronomical-almanac.pdf /// /// </remarks> private static CrdsEcliptical TritonPosition(double jd, CrdsEcliptical neptune) { NutationElements ne = Nutation.NutationElements(jd); double epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon); // convert current coordinates to J1950 epoch, as algorithm requires CrdsEquatorial eq = neptune.ToEquatorial(epsilon); PrecessionalElements pe1950 = Precession.ElementsFK5(jd, Date.EPOCH_J1950); CrdsEquatorial eqNeptune1950 = Precession.GetEquatorialCoordinates(eq, pe1950); const double t0 = 2433282.5; // 1.0 Jan 1950 const double a = 0.0023683; // semimajor axis of Triton, in a.u. const double n = 61.2588532; // nodal mean motion, degrees per day const double lambda0 = 200.913; // longitude from ascending node through the invariable plane at epoch const double i = 158.996; // inclination of orbit to the invariable plane const double Omega0 = 151.401; // angle from the intersection of invariable plane with the earth's // equatorial plane of 1950.0 to the ascending node // of the orbit through the invariable plane const double OmegaDot = 0.57806; // nodal precision rate, degrees per year // Calculate J2000.0 RA and Declination of the pole of the invariable plane // These formulae are taken from the book: // Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac, // University Science Book, Mill Valley (California), 1992, // Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 373, 6.61-1 Triton double T = (jd - 2451545.0) / 36525.0; double N = ToRadians(359.28 + 54.308 * T); double ap = 298.72 + 2.58 * Sin(N) - 0.04 * Sin(2 * N); double dp = 42.63 - 1.90 * Cos(N) + 0.01 * Cos(2 * N); // Convert pole coordinates to J1950 CrdsEquatorial eqPole1950 = Precession.GetEquatorialCoordinates(new CrdsEquatorial(ap, dp), pe1950); ap = eqPole1950.Alpha; dp = eqPole1950.Delta; // take light-time effect into account double tau = PlanetPositions.LightTimeEffect(neptune.Distance); double lambda = To360(lambda0 + n * (jd - t0 - tau)); double omega = Omega0 + OmegaDot * (jd - t0 - tau) / 365.25; // cartesian state vector of Triton var r = Matrix.R3(ToRadians(-ap - 90)) * Matrix.R1(ToRadians(dp - 90)) * Matrix.R3(ToRadians(-omega)) * Matrix.R1(ToRadians(-i)) * new Matrix(new[, ] { { a *Cos(ToRadians(lambda)) }, { a *Sin(ToRadians(lambda)) }, { 0 } }); // normalize by distance to Neptune r.Values[0, 0] /= neptune.Distance; r.Values[1, 0] /= neptune.Distance; r.Values[2, 0] /= neptune.Distance; // offsets vector var d = Matrix.R2(ToRadians(-eqNeptune1950.Delta)) * Matrix.R3(ToRadians(eqNeptune1950.Alpha)) * r; // radial component, positive away from observer // converted to degrees double x = ToDegrees(d.Values[0, 0]); // semimajor axis, expressed in degrees, as visible from Earth double theta = ToDegrees(Atan(a / neptune.Distance)); // offsets values in degrees double dAlphaCosDelta = ToDegrees(d.Values[1, 0]); double dDelta = ToDegrees(d.Values[2, 0]); double delta = eqNeptune1950.Delta + dDelta; double dAlpha = dAlphaCosDelta / Cos(ToRadians(eqNeptune1950.Delta)); double alpha = eqNeptune1950.Alpha + dAlpha; CrdsEquatorial eqTriton1950 = new CrdsEquatorial(alpha, delta); // convert J1950 equatorial coordinates to current epoch // and to ecliptical PrecessionalElements pe = Precession.ElementsFK5(Date.EPOCH_J1950, jd); CrdsEquatorial eqTriton = Precession.GetEquatorialCoordinates(eqTriton1950, pe); CrdsEcliptical eclTriton = eqTriton.ToEcliptical(epsilon); // calculate distance to Earth eclTriton.Distance = neptune.Distance + x / theta * a; return(eclTriton); }
/// <summary> /// Calculates ecliptical coordinates of Nereid, the third-largest moon of Neptune. /// </summary> /// <param name="jd">Julian Day of calculation</param> /// <param name="neptune">Ecliptical coordinates of Neptune for the Julian Day specified.</param> /// <returns>Ecliptical coordinates of Nereid for specified date.</returns> /// <remarks> /// /// The method is based on work of F. Mignard (1981), "The Mean Elements of Nereid", /// The Astronomical Journal, Vol 86, Number 11, pages 1728-1729 /// The work can be found by link: http://adsabs.harvard.edu/full/1981AJ.....86.1728M /// /// There are some changes from the original algorithm were made, /// to be compliant with ephemeris provided by Nasa JPL Horizons system (https://ssd.jpl.nasa.gov/?ephemerides): /// /// 1. Other value of mean motion (n) is used: /// - original work : n = 0.999552 /// - implementation: n = 360.0 / 360.1362 (where 360.1362 is an orbital period) /// /// 2. Rotation around Z axis by angle OmegaN should by taken with NEGATIVE sign, /// insted of POSITIVE sign in original work (possible typo?), /// note the NEGATIVE sign for "Ne" angle (same meaning as "OmegaN" in original work) in the book: /// Seidelmann, P. K.: Explanatory Supplement to The Astronomical Almanac, /// University Science Book, Mill Valley (California), 1992, /// Chapter 6 "Orbital Ephemerides and Rings of Satellites", page 376, formula 6.62-3 /// /// </remarks> private static CrdsEcliptical NereidPosition(double jd, CrdsEcliptical neptune) { NutationElements ne = Nutation.NutationElements(jd); double epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon); // convert current coordinates to J1950 epoch, as algorithm requires CrdsEquatorial eq = neptune.ToEquatorial(epsilon); PrecessionalElements pe1950 = Precession.ElementsFK5(jd, Date.EPOCH_J1950); CrdsEquatorial eqNeptune1950 = Precession.GetEquatorialCoordinates(eq, pe1950); const double jd0 = 2433680.5; // Initial Epoch: 3.0 Feb 1951 const double a = 0.036868; // Semi-major axis, in a.u. const double e0 = 0.74515; // Orbit eccentricity for jd0 epoch const double i0 = 10.041; // Inclination of the orbit for jd0 epoch, in degrees const double Omega0 = 329.3; // Longitude of the node of the orbit for jd0 epoch, in degrees const double M0 = 358.91; // Mean anomaly for jd0 epoch, in degrees const double n = 360.0 / 360.1362; // Mean motion, in degrees per day const double OmegaN = 3.552; // Longitude of ascending node of the orbit of Neptune, for J1950.0 epoch, in degrees const double gamma = 22.313; // Inclination of the orbit of Neptune, for J1950.0 epoch, in degrees // take light-time effect into account double tau = PlanetPositions.LightTimeEffect(neptune.Distance); double t = jd - tau - jd0; // in days double T = t / 36525.0; // in Julian centuries double psi = ToRadians(To360(282.9 + 2.68 * T)); double twoTheta = ToRadians(To360(107.4 + 0.01196 * t)); // Equation to found omega, argument of pericenter Func <double, double> omegaEquation = (om) => To360(282.9 + 2.68 * T - 19.25 * Sin(2 * psi) + 3.23 * Sin(4 * psi) - 0.725 * Sin(6 * psi) - 0.351 * Sin(twoTheta) - 0.7 * Sin(ToRadians(2 * om) - twoTheta)) - om; // Solve equation (find root: omega value) double omega = ToRadians(FindRoots(omegaEquation, 0, 360, 1e-8)); // Find longitude of the node double Omega = Omega0 - 2.4 * T + 19.7 * Sin(2 * psi) - 3.3 * Sin(4 * psi) + 0.7 * Sin(6 * psi) + 0.357 * Sin(twoTheta) + 0.276 * Sin(2 * omega - twoTheta); // Find orbit eccentricity double e = e0 - 0.006 * Cos(2 * psi) + 0.0056 * Cos(2 * omega - twoTheta); // Find mean anomaly double M = To360(M0 + n * t - 0.38 * Sin(2 * psi) + 1.0 * Sin(2 * omega - twoTheta)); // Find inclination double cosi = Cos(ToRadians(i0)) - 9.4e-3 * Cos(2 * psi); double i = Acos(cosi); // Find eccentric anomaly by solving Kepler equation double E = SolveKepler(M, e); double X = a * (Cos(E) - e); double Y = a * Sqrt(1 - e * e) * Sin(E); Matrix d = Matrix.R2(ToRadians(-eqNeptune1950.Delta)) * Matrix.R3(ToRadians(eqNeptune1950.Alpha)) * Matrix.R3(ToRadians(-OmegaN)) * Matrix.R1(ToRadians(-gamma)) * Matrix.R3(ToRadians(-Omega)) * Matrix.R1(-i) * Matrix.R3(-omega) * new Matrix(new double[, ] { { X / neptune.Distance }, { Y / neptune.Distance }, { 0 } }); // radial component, positive away from observer // converted to degrees double x = ToDegrees(d.Values[0, 0]); // offsets values in degrees double dAlphaCosDelta = ToDegrees(d.Values[1, 0]); double dDelta = ToDegrees(d.Values[2, 0]); double delta = eqNeptune1950.Delta + dDelta; double dAlpha = dAlphaCosDelta / Cos(ToRadians(eqNeptune1950.Delta)); double alpha = eqNeptune1950.Alpha + dAlpha; CrdsEquatorial eqNereid1950 = new CrdsEquatorial(alpha, delta); // convert J1950 equatorial coordinates to current epoch // and to ecliptical PrecessionalElements pe = Precession.ElementsFK5(Date.EPOCH_J1950, jd); CrdsEquatorial eqNereid = Precession.GetEquatorialCoordinates(eqNereid1950, pe); CrdsEcliptical eclNereid = eqNereid.ToEcliptical(epsilon); // semimajor axis, expressed in degrees, as visible from Earth double theta = ToDegrees(Atan(a / neptune.Distance)); // calculate distance to Earth eclNereid.Distance = neptune.Distance + x / theta * a; return(eclNereid); }
public static CrdsEcliptical Position(double jd, GenericSatelliteOrbit orbit, CrdsEcliptical planet) { NutationElements ne = Nutation.NutationElements(jd); double epsilon = Date.TrueObliquity(jd, ne.deltaEpsilon); // convert current coordinates to epoch, as algorithm requires CrdsEquatorial eq = planet.ToEquatorial(epsilon); PrecessionalElements peEpoch = Precession.ElementsFK5(jd, Date.EPOCH_J2000); CrdsEquatorial eqPlanetEpoch = Precession.GetEquatorialCoordinates(eq, peEpoch); // ecliptical pole CrdsEquatorial pole = new CrdsEcliptical(0, 90).ToEquatorial(epsilon); double distance0; double distance = planet.Distance; CrdsEcliptical eclSatellite; do { distance0 = distance; // take light-time effect into account double tau = PlanetPositions.LightTimeEffect(distance); double t = jd - tau - orbit.jd; double M = To360(orbit.M + orbit.n * t); double omega = To360(orbit.w + t * 360.0 / (orbit.Pw * 365.25)); double node = To360(orbit.Om + t * 360.0 / (orbit.POm * 365.25)); // Find eccentric anomaly by solving Kepler equation double E = SolveKepler(M, orbit.e); double X = orbit.a * (Cos(E) - orbit.e); double Y = orbit.a * Sqrt(1 - orbit.e * orbit.e) * Sin(E); // cartesian state vector of satellite var d = Matrix.R2(ToRadians(-eqPlanetEpoch.Delta)) * Matrix.R3(ToRadians(eqPlanetEpoch.Alpha)) * Matrix.R3(ToRadians(-pole.Alpha - 90)) * Matrix.R1(ToRadians(pole.Delta - 90)) * Matrix.R3(ToRadians(-node)) * Matrix.R1(ToRadians(-orbit.i)) * Matrix.R3(ToRadians(-omega)) * new Matrix(new double[, ] { { X / distance }, { Y / distance }, { 0 } }); // radial component, positive away from observer // converted to degrees double x = ToDegrees(d.Values[0, 0]); // semimajor axis, expressed in degrees, as visible from Earth double theta = ToDegrees(Atan(orbit.a / distance)); // offsets values in degrees double dAlphaCosDelta = ToDegrees(d.Values[1, 0]); double dDelta = ToDegrees(d.Values[2, 0]); double delta = eqPlanetEpoch.Delta + dDelta; double dAlpha = dAlphaCosDelta / Cos(ToRadians(delta)); double alpha = eqPlanetEpoch.Alpha + dAlpha; CrdsEquatorial eqSatelliteEpoch = new CrdsEquatorial(alpha, delta); // convert jd0 equatorial coordinates to current epoch // and to ecliptical PrecessionalElements pe = Precession.ElementsFK5(Date.EPOCH_J2000, jd); CrdsEquatorial eqSatellite = Precession.GetEquatorialCoordinates(eqSatelliteEpoch, pe); eclSatellite = eqSatellite.ToEcliptical(epsilon); // calculate distance to Earth distance = planet.Distance + x / theta * orbit.a; }while (Abs(distance - distance0) > 1e-6); eclSatellite.Distance = distance; return(eclSatellite); }