From 45dbdd87d4a00a4b06566565564dde3078d438e4 Mon Sep 17 00:00:00 2001 From: Don Cross Date: Sat, 12 Mar 2022 17:08:56 -0500 Subject: [PATCH] Implemented C# Lagrange point functions. --- generate/dotnet/csharp_test/csharp_test.cs | 208 ++++++++++---- generate/template/astronomy.c | 4 +- generate/template/astronomy.cs | 315 +++++++++++++++++++++ source/c/README.md | 4 +- source/c/astronomy.c | 4 +- source/csharp/README.md | 96 +++++++ source/csharp/astronomy.cs | 315 +++++++++++++++++++++ 7 files changed, 878 insertions(+), 68 deletions(-) diff --git a/generate/dotnet/csharp_test/csharp_test.cs b/generate/dotnet/csharp_test/csharp_test.cs index e148a8fc..27acabd4 100644 --- a/generate/dotnet/csharp_test/csharp_test.cs +++ b/generate/dotnet/csharp_test/csharp_test.cs @@ -2720,11 +2720,15 @@ namespace csharp_test return sqrt(diff_squared); } + interface IStateVectorFunc + { + StateVector Eval(AstroTime time); + } + static int VerifyState( - Func func, + IStateVectorFunc func, ref double max_rdiff, ref double max_vdiff, - Body body, string filename, int lnum, AstroTime time, @@ -2733,7 +2737,7 @@ namespace csharp_test double r_thresh, double v_thresh) { - StateVector state = func(body, time); + StateVector state = func.Eval(time); double rdiff = StateVectorDiff((r_thresh > 0.0), pos, state.x, state.y, state.z); if (rdiff > max_rdiff) @@ -2759,8 +2763,7 @@ namespace csharp_test } static int VerifyStateBody( - Func func, - Body body, + IStateVectorFunc func, string filename, double r_thresh, double v_thresh) @@ -2777,7 +2780,7 @@ namespace csharp_test vel[0] = rec.state.vx; vel[1] = rec.state.vy; vel[2] = rec.state.vz; - if (0 != VerifyState(func, ref max_rdiff, ref max_vdiff, body, filename, rec.lnum, rec.state.t, pos, vel, r_thresh, v_thresh)) + if (0 != VerifyState(func, ref max_rdiff, ref max_vdiff, filename, rec.lnum, rec.state.t, pos, vel, r_thresh, v_thresh)) return 1; ++count; } @@ -2788,89 +2791,124 @@ namespace csharp_test // Constants for use inside unit tests only; they doesn't make sense for public consumption. const Body Body_GeoMoon = (Body)(-100); const Body Body_Geo_EMB = (Body)(-101); - static StateVector BaryState(Body body, AstroTime time) + + class BaryStateFunc : IStateVectorFunc { - if (body == Body_GeoMoon) - return Astronomy.GeoMoonState(time); + private Body body; - if (body == Body_Geo_EMB) - return Astronomy.GeoEmbState(time); + public BaryStateFunc(Body body) + { + this.body = body; + } - return Astronomy.BaryState(body, time); + public StateVector Eval(AstroTime time) + { + if (body == Body_GeoMoon) + return Astronomy.GeoMoonState(time); + + if (body == Body_Geo_EMB) + return Astronomy.GeoEmbState(time); + + return Astronomy.BaryState(body, time); + } } static int BaryStateTest() { - if (0 != VerifyStateBody(BaryState, Body.Sun, "../../barystate/Sun.txt", -1.224e-05, -1.134e-07)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Mercury, "../../barystate/Mercury.txt", 1.672e-04, 2.698e-04)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Venus, "../../barystate/Venus.txt", 4.123e-05, 4.308e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Earth, "../../barystate/Earth.txt", 2.296e-05, 6.359e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Mars, "../../barystate/Mars.txt", 3.107e-05, 5.550e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Jupiter, "../../barystate/Jupiter.txt", 7.389e-05, 2.471e-04)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Saturn, "../../barystate/Saturn.txt", 1.067e-04, 3.220e-04)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Uranus, "../../barystate/Uranus.txt", 9.035e-05, 2.519e-04)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Neptune, "../../barystate/Neptune.txt", 9.838e-05, 4.446e-04)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Pluto, "../../barystate/Pluto.txt", 4.259e-05, 7.827e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body.Moon, "../../barystate/Moon.txt", 2.354e-05, 6.604e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body.EMB, "../../barystate/EMB.txt", 2.353e-05, 6.511e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body_GeoMoon, "../../barystate/GeoMoon.txt", 4.086e-05, 5.347e-05)) return 1; - if (0 != VerifyStateBody(BaryState, Body_Geo_EMB, "../../barystate/GeoEMB.txt", 4.076e-05, 5.335e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Sun), "../../barystate/Sun.txt", -1.224e-05, -1.134e-07)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Mercury), "../../barystate/Mercury.txt", 1.672e-04, 2.698e-04)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Venus), "../../barystate/Venus.txt", 4.123e-05, 4.308e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Earth), "../../barystate/Earth.txt", 2.296e-05, 6.359e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Mars), "../../barystate/Mars.txt", 3.107e-05, 5.550e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Jupiter), "../../barystate/Jupiter.txt", 7.389e-05, 2.471e-04)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Saturn), "../../barystate/Saturn.txt", 1.067e-04, 3.220e-04)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Uranus), "../../barystate/Uranus.txt", 9.035e-05, 2.519e-04)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Neptune), "../../barystate/Neptune.txt", 9.838e-05, 4.446e-04)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Pluto), "../../barystate/Pluto.txt", 4.259e-05, 7.827e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.Moon), "../../barystate/Moon.txt", 2.354e-05, 6.604e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body.EMB), "../../barystate/EMB.txt", 2.353e-05, 6.511e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body_GeoMoon), "../../barystate/GeoMoon.txt", 4.086e-05, 5.347e-05)) return 1; + if (0 != VerifyStateBody(new BaryStateFunc(Body_Geo_EMB), "../../barystate/GeoEMB.txt", 4.076e-05, 5.335e-05)) return 1; Console.WriteLine("C# BaryStateTest: PASS"); return 0; } + class HelioStateFunc : IStateVectorFunc + { + private Body body; + + public HelioStateFunc(Body body) + { + this.body = body; + } + + public StateVector Eval(AstroTime time) + { + return Astronomy.HelioState(body, time); + } + } + static int HelioStateTest() { - if (0 != VerifyStateBody(Astronomy.HelioState, Body.SSB, "../../heliostate/SSB.txt", -1.209e-05, -1.125e-07)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Mercury, "../../heliostate/Mercury.txt", 1.481e-04, 2.756e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Venus, "../../heliostate/Venus.txt", 3.528e-05, 4.485e-05)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Earth, "../../heliostate/Earth.txt", 1.476e-05, 6.105e-05)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Mars, "../../heliostate/Mars.txt", 3.154e-05, 5.603e-05)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Jupiter, "../../heliostate/Jupiter.txt", 7.455e-05, 2.562e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Saturn, "../../heliostate/Saturn.txt", 1.066e-04, 3.150e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Uranus, "../../heliostate/Uranus.txt", 9.034e-05, 2.712e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Neptune, "../../heliostate/Neptune.txt", 9.834e-05, 4.534e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Pluto, "../../heliostate/Pluto.txt", 4.271e-05, 1.198e-04)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.Moon, "../../heliostate/Moon.txt", 1.477e-05, 6.195e-05)) return 1; - if (0 != VerifyStateBody(Astronomy.HelioState, Body.EMB, "../../heliostate/EMB.txt", 1.476e-05, 6.106e-05)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.SSB), "../../heliostate/SSB.txt", -1.209e-05, -1.125e-07)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Mercury), "../../heliostate/Mercury.txt", 1.481e-04, 2.756e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Venus), "../../heliostate/Venus.txt", 3.528e-05, 4.485e-05)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Earth), "../../heliostate/Earth.txt", 1.476e-05, 6.105e-05)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Mars), "../../heliostate/Mars.txt", 3.154e-05, 5.603e-05)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Jupiter), "../../heliostate/Jupiter.txt", 7.455e-05, 2.562e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Saturn), "../../heliostate/Saturn.txt", 1.066e-04, 3.150e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Uranus), "../../heliostate/Uranus.txt", 9.034e-05, 2.712e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Neptune), "../../heliostate/Neptune.txt", 9.834e-05, 4.534e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Pluto), "../../heliostate/Pluto.txt", 4.271e-05, 1.198e-04)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.Moon), "../../heliostate/Moon.txt", 1.477e-05, 6.195e-05)) return 1; + if (0 != VerifyStateBody(new HelioStateFunc(Body.EMB), "../../heliostate/EMB.txt", 1.476e-05, 6.106e-05)) return 1; Console.WriteLine("C# HelioStateTest: PASS"); return 0; } - static StateVector TopoStateFunc(Body body, AstroTime time) + class TopoStateFunc : IStateVectorFunc { - var observer = new Observer(30.0, -80.0, 1000.0); + private Body body; - StateVector observer_state = Astronomy.ObserverState(time, observer, EquatorEpoch.J2000); - StateVector state; - if (body == Body_Geo_EMB) + public TopoStateFunc(Body body) { - state = Astronomy.GeoEmbState(time); - } - else if (body == Body.Earth) - { - state = new StateVector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, time); - } - else - { - throw new ArgumentException($"C# TopoStateFunction: unsupported body {body}"); + this.body = body; } - state.x -= observer_state.x; - state.y -= observer_state.y; - state.z -= observer_state.z; - state.vx -= observer_state.vx; - state.vy -= observer_state.vy; - state.vz -= observer_state.vz; + public StateVector Eval(AstroTime time) + { + var observer = new Observer(30.0, -80.0, 1000.0); - return state; + StateVector observer_state = Astronomy.ObserverState(time, observer, EquatorEpoch.J2000); + StateVector state; + if (body == Body_Geo_EMB) + { + state = Astronomy.GeoEmbState(time); + } + else if (body == Body.Earth) + { + state = new StateVector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, time); + } + else + { + throw new ArgumentException($"C# TopoStateFunction: unsupported body {body}"); + } + + state.x -= observer_state.x; + state.y -= observer_state.y; + state.z -= observer_state.z; + state.vx -= observer_state.vx; + state.vy -= observer_state.vy; + state.vz -= observer_state.vz; + + return state; + } } - static int TopoStateTest() { - if (0 != VerifyStateBody(TopoStateFunc, Body.Earth, "../../topostate/Earth_N30_W80_1000m.txt", 2.108e-04, 2.430e-04)) return 1; - if (0 != VerifyStateBody(TopoStateFunc, Body_Geo_EMB, "../../topostate/EMB_N30_W80_1000m.txt", 7.195e-04, 2.497e-04)) return 1; + if (0 != VerifyStateBody(new TopoStateFunc(Body.Earth), "../../topostate/Earth_N30_W80_1000m.txt", 2.108e-04, 2.430e-04)) return 1; + if (0 != VerifyStateBody(new TopoStateFunc(Body_Geo_EMB), "../../topostate/EMB_N30_W80_1000m.txt", 7.195e-04, 2.497e-04)) return 1; Console.WriteLine("C# TopoStateTest: PASS"); return 0; } @@ -3298,8 +3336,54 @@ namespace csharp_test } } + //----------------------------------------------------------------------------------------- + + class LagrangeFunc : IStateVectorFunc + { + private int point; + private Body major_body; + private Body minor_body; + + public LagrangeFunc(int point, Body major_body, Body minor_body) + { + this.point = point; + this.major_body = major_body; + this.minor_body = minor_body; + } + + public StateVector Eval(AstroTime time) + { + return Astronomy.LagrangePoint(point, time, major_body, minor_body); + } + } + + static int VerifyStateLagrange( + Body major_body, + Body minor_body, + int point, + string filename, + double r_thresh, + double v_thresh) + { + var func = new LagrangeFunc(point, major_body, minor_body); + return VerifyStateBody(func, filename, r_thresh, v_thresh); + } + static int LagrangeTest() { + // Test Sun/EMB Lagrange points. + if (0 != VerifyStateLagrange(Body.Sun, Body.EMB, 1, "../../lagrange/semb_L1.txt", 1.33e-5, 6.13e-5)) return 1; + if (0 != VerifyStateLagrange(Body.Sun, Body.EMB, 2, "../../lagrange/semb_L2.txt", 1.33e-5, 6.13e-5)) return 1; + if (0 != VerifyStateLagrange(Body.Sun, Body.EMB, 4, "../../lagrange/semb_L4.txt", 3.75e-5, 5.28e-5)) return 1; + if (0 != VerifyStateLagrange(Body.Sun, Body.EMB, 5, "../../lagrange/semb_L5.txt", 3.75e-5, 5.28e-5)) return 1; + + // Test Earth/Moon Lagrange points. + if (0 != VerifyStateLagrange(Body.Earth, Body.Moon, 1, "../../lagrange/em_L1.txt", 3.79e-5, 5.06e-5)) return 1; + if (0 != VerifyStateLagrange(Body.Earth, Body.Moon, 2, "../../lagrange/em_L2.txt", 3.79e-5, 5.06e-5)) return 1; + if (0 != VerifyStateLagrange(Body.Earth, Body.Moon, 4, "../../lagrange/em_L4.txt", 3.79e-5, 1.59e-3)) return 1; + if (0 != VerifyStateLagrange(Body.Earth, Body.Moon, 5, "../../lagrange/em_L5.txt", 3.79e-5, 1.59e-3)) return 1; + + Console.WriteLine("C# LagrangeTest: PASS"); return 0; // not yet implemented } diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index ffa52646..88b4a9a7 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -3480,9 +3480,9 @@ astro_state_vector_t Astronomy_LagrangePoint( * * @param point A value 1..5 that selects which of the Lagrange points to calculate. * @param major_state The state vector of the major (more massive) of the pair of bodies. - * @param major_mass The relative mass of the major body. + * @param major_mass The mass product GM of the major body. * @param minor_state The state vector of the minor (less massive) of the pair of bodies. - * @param minor_mass The relative mass of the minor body. + * @param minor_mass The mass product GM of the minor body. * @return The position and velocity of the selected Lagrange point with respect to the major body's center. */ astro_state_vector_t Astronomy_LagrangePointFast( diff --git a/generate/template/astronomy.cs b/generate/template/astronomy.cs index 34d15e03..db16d54c 100644 --- a/generate/template/astronomy.cs +++ b/generate/template/astronomy.cs @@ -2181,10 +2181,56 @@ $ASTRO_ADDSOL() the products GM[i] are known extremely accurately. */ private const double SUN_GM = 0.2959122082855911e-03; + private const double MERCURY_GM = 0.4912547451450812e-10; + private const double VENUS_GM = 0.7243452486162703e-09; + private const double EARTH_GM = 0.8887692390113509e-09; + private const double MARS_GM = 0.9549535105779258e-10; private const double JUPITER_GM = 0.2825345909524226e-06; private const double SATURN_GM = 0.8459715185680659e-07; private const double URANUS_GM = 0.1292024916781969e-07; private const double NEPTUNE_GM = 0.1524358900784276e-07; + private const double PLUTO_GM = 0.2188699765425970e-11; + + private const double MOON_GM = EARTH_GM / EARTH_MOON_MASS_RATIO; + + /// + /// Returns the product of mass and universal gravitational constant of a Solar System body. + /// + /// + /// For problems involving the gravitational interactions of Solar System bodies, + /// it is helpful to know the product GM, where G = the universal gravitational constant + /// and M = the mass of the body. In practice, GM is known to a higher precision than + /// either G or M alone, and thus using the product results in the most accurate results. + /// This function returns the product GM in the units au^3/day^2. + /// The values come from page 10 of a + /// [JPL memorandum regarding the DE405/LE405 ephemeris](https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf). + /// + /// + /// The body for which to find the GM product. + /// Allowed to be the Sun, Moon, EMB (Earth/Moon Barycenter), or any planet. + /// Any other value will cause an exception to be thrown. + /// + /// The mass product of the given body in au^3/day^2. + public static double MassProduct(Body body) + { + switch (body) + { + case Body.Sun: return SUN_GM; + case Body.Mercury: return MERCURY_GM; + case Body.Venus: return VENUS_GM; + case Body.Earth: return EARTH_GM; + case Body.Moon: return MOON_GM; + case Body.EMB: return EARTH_GM + MOON_GM; + case Body.Mars: return MARS_GM; + case Body.Jupiter: return JUPITER_GM; + case Body.Saturn: return SATURN_GM; + case Body.Uranus: return URANUS_GM; + case Body.Neptune: return NEPTUNE_GM; + case Body.Pluto: return PLUTO_GM; + default: + throw new InvalidBodyException(body); + } + } /// Counter used for performance testing. public static int CalcMoonCount; @@ -7090,6 +7136,275 @@ $ASTRO_IAU_DATA() throw new Exception("Peak magnitude search failed."); } + /// + /// Calculates one of the 5 Lagrange points for a pair of co-orbiting bodies. + /// + /// + /// Given a more massive "major" body and a much less massive "minor" body, + /// calculates one of the five Lagrange points in relation to the minor body's + /// orbit around the major body. The parameter `point` is an integer that + /// selects the Lagrange point as follows: + /// + /// 1 = the Lagrange point between the major body and minor body. + /// 2 = the Lagrange point on the far side of the minor body. + /// 3 = the Lagrange point on the far side of the major body. + /// 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. + /// 5 = the Lagrange point 60 degrees behind the minor body's orbital position. + /// + /// The function returns the state vector for the selected Lagrange point + /// in equatorial J2000 coordinates (EQJ), with respect to the center of the + /// major body. + /// + /// To calculate Sun/Earth Lagrange points, pass in `Body.Sun` for `major_body` + /// and `Body.EMB` (Earth/Moon barycenter) for `minor_body`. + /// For Lagrange points of the Sun and any other planet, pass in just that planet by itself, + /// e.g. `Body.Jupiter` for `minor_body`. + /// To calculate Earth/Moon Lagrange points, pass in `Body.Earth` and `Body.Moon` + /// for the major and minor bodies respectively. + /// + /// In some cases, it may be more efficient to call #Astronomy.LagrangePointFast, + /// especially when the state vectors have already been calculated, or are needed + /// for some other purpose. + /// + /// A value 1..5 that selects which of the Lagrange points to calculate. + /// The time at which the Lagrange point is to be calculated. + /// The more massive of the co-orbiting bodies: `Body.Sun` or `Body.Earth`. + /// The less massive of the co-orbiting bodies. See main remarks. + /// + public static StateVector LagrangePoint( + int point, + AstroTime time, + Body major_body, + Body minor_body) + { + double major_mass = MassProduct(major_body); + double minor_mass = MassProduct(minor_body); + + StateVector major_state; + StateVector minor_state; + + /* Calculate the state vectors for the major and minor bodies. */ + if (major_body == Body.Earth && minor_body == Body.Moon) + { + /* Use geocentric calculations for more precision. */ + + /* The Earth's geocentric state is trivial. */ + major_state.t = time; + major_state.x = major_state.y = major_state.z = 0.0; + major_state.vx = major_state.vy = major_state.vz = 0.0; + + minor_state = GeoMoonState(time); + } + else + { + major_state = HelioState(major_body, time); + minor_state = HelioState(minor_body, time); + } + + return LagrangePointFast( + point, + major_state, + major_mass, + minor_state, + minor_mass + ); + } + + /// + /// Calculates one of the 5 Lagrange points from body masses and state vectors. + /// + /// + /// Given a more massive "major" body and a much less massive "minor" body, + /// calculates one of the five Lagrange points in relation to the minor body's + /// orbit around the major body. The parameter `point` is an integer that + /// selects the Lagrange point as follows: + /// + /// 1 = the Lagrange point between the major body and minor body. + /// 2 = the Lagrange point on the far side of the minor body. + /// 3 = the Lagrange point on the far side of the major body. + /// 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. + /// 5 = the Lagrange point 60 degrees behind the minor body's orbital position. + /// + /// The caller passes in the state vector and mass for both bodies. + /// The state vectors can be in any orientation and frame of reference. + /// The body masses are expressed as GM products, where G = the universal + /// gravitation constant and M = the body's mass. Thus the units for + /// `major_mass` and `minor_mass` must be au^3/day^2. + /// Use #Astronomy.MassProduct to obtain GM values for various solar system bodies. + /// + /// The function returns the state vector for the selected Lagrange point + /// using the same orientation as the state vector parameters `major_state` and `minor_state`, + /// and the position and velocity components are with respect to the major body's center. + /// + /// Consider calling #Astronomy.LagrangePoint, instead of this function, for simpler usage in most cases. + /// + /// A value 1..5 that selects which of the Lagrange points to calculate. + /// The state vector of the major (more massive) of the pair of bodies. + /// The mass product GM of the major body. + /// The state vector of the minor (less massive) of the pair of bodies. + /// The mass product GM of the minor body. + /// The position and velocity of the selected Lagrange point with respect to the major body's center. + public static StateVector LagrangePointFast( + int point, + StateVector major_state, + double major_mass, + StateVector minor_state, + double minor_mass) + { + const double cos_60 = 0.5; + const double sin_60 = 0.8660254037844386; /* sqrt(3) / 2 */ + + if (point < 1 || point > 5) + throw new ArgumentException($"Invalid lagrange point {point}"); + + if (double.IsNaN(major_mass) || double.IsInfinity(major_mass) || major_mass <= 0.0) + throw new ArgumentException("Major mass must be a positive number."); + + if (double.IsNaN(minor_mass) || double.IsInfinity(minor_mass) || minor_mass <= 0.0) + throw new ArgumentException("Minor mass must be a positive number."); + + /* Find the relative position vector . */ + double dx = minor_state.x - major_state.x; + double dy = minor_state.y - major_state.y; + double dz = minor_state.z - major_state.z; + double R2 = (dx*dx + dy*dy + dz*dz); + + /* R = Total distance between the bodies. */ + double R = Math.Sqrt(R2); + + /* Find the velocity vector . */ + double vx = minor_state.vx - major_state.vx; + double vy = minor_state.vy - major_state.vy; + double vz = minor_state.vz - major_state.vz; + + StateVector p; + if (point == 4 || point == 5) + { + /* + For L4 and L5, we need to find points 60 degrees away from the + line connecting the two bodies and in the instantaneous orbital plane. + Define the instantaneous orbital plane as the unique plane that contains + both the relative position vector and the relative velocity vector. + */ + + /* Take the cross product of position and velocity to find a normal vector . */ + double nx = dy*vz - dz*vy; + double ny = dz*vx - dx*vz; + double nz = dx*vy - dy*vx; + + /* Take the cross product normal*position to get a tangential vector . */ + double ux = ny*dz - nz*dy; + double uy = nz*dx - nx*dz; + double uz = nx*dy - ny*dx; + + /* Convert the tangential direction vector to a unit vector. */ + double U = Math.Sqrt(ux*ux + uy*uy + uz*uz); + ux /= U; + uy /= U; + uz /= U; + + /* Convert the relative position vector into a unit vector. */ + dx /= R; + dy /= R; + dz /= R; + + /* Now we have two perpendicular unit vectors in the orbital plane: 'd' and 'u'. */ + + /* Create new unit vectors rotated (+/-)60 degrees from the radius/tangent directions. */ + double vert = (point == 4) ? +sin_60 : -sin_60; + + /* Rotated radial vector */ + double Dx = cos_60*dx + vert*ux; + double Dy = cos_60*dy + vert*uy; + double Dz = cos_60*dz + vert*uz; + + /* Rotated tangent vector */ + double Ux = cos_60*ux - vert*dx; + double Uy = cos_60*uy - vert*dy; + double Uz = cos_60*uz - vert*dz; + + /* Calculate L4/L5 positions relative to the major body. */ + p.x = R * Dx; + p.y = R * Dy; + p.z = R * Dz; + + /* Use dot products to find radial and tangential components of the relative velocity. */ + double vrad = vx*dx + vy*dy + vz*dz; + double vtan = vx*ux + vy*uy + vz*uz; + + /* Calculate L4/L5 velocities. */ + p.vx = vrad*Dx + vtan*Ux; + p.vy = vrad*Dy + vtan*Uy; + p.vz = vrad*Dz + vtan*Uz; + } + else + { + /* + Calculate the distances of each body from their mutual barycenter. + r1 = negative distance of major mass from barycenter (e.g. Sun to the left of barycenter) + r2 = positive distance of minor mass from barycenter (e.g. Earth to the right of barycenter) + */ + double r1 = -R * (minor_mass / (major_mass + minor_mass)); + double r2 = +R * (major_mass / (major_mass + minor_mass)); + + /* Calculate the square of the angular orbital speed in [rad^2 / day^2]. */ + double omega2 = (major_mass + minor_mass) / (R2*R); + + /* + Use Newton's Method to numerically solve for the location where + outward centrifugal acceleration in the rotating frame of reference + is equal to net inward gravitational acceleration. + First derive a good initial guess based on approximate analysis. + */ + double scale, numer1, numer2; + if (point == 1 || point == 2) + { + scale = (major_mass / (major_mass + minor_mass)) * Math.Cbrt(minor_mass / (3.0 * major_mass)); + numer1 = -major_mass; /* The major mass is to the left of L1 and L2 */ + if (point == 1) + { + scale = 1.0 - scale; + numer2 = +minor_mass; /* The minor mass is to the right of L1. */ + } + else + { + scale = 1.0 + scale; + numer2 = -minor_mass; /* The minor mass is to the left of L2. */ + } + } + else /* point == 3 */ + { + scale = ((7.0/12.0)*minor_mass - major_mass) / (minor_mass + major_mass); + numer1 = +major_mass; /* major mass is to the right of L3. */ + numer2 = +minor_mass; /* minor mass is to the right of L3. */ + } + + /* Iterate Newton's Method until it converges. */ + double x = R*scale - r1; + double deltax; + do + { + double dr1 = x - r1; + double dr2 = x - r2; + double accel = omega2*x + numer1/(dr1*dr1) + numer2/(dr2*dr2); + double deriv = omega2 - 2*numer1/(dr1*dr1*dr1) - 2*numer2/(dr2*dr2*dr2); + deltax = accel/deriv; + x -= deltax; + } + while (Math.Abs(deltax/R) > 1.0e-14); + scale = (x - r1) / R; + + p.x = scale * dx; + p.y = scale * dy; + p.z = scale * dz; + p.vx = scale * vx; + p.vy = scale * vy; + p.vz = scale * vz; + } + p.t = major_state.t; + return p; + } + /// Calculates the inverse of a rotation matrix. /// /// Given a rotation matrix that performs some coordinate transform, diff --git a/source/c/README.md b/source/c/README.md index f97767f0..19eabd48 100644 --- a/source/c/README.md +++ b/source/c/README.md @@ -1047,9 +1047,9 @@ Consider calling [`Astronomy_LagrangePoint`](#Astronomy_LagrangePoint), instead | --- | --- | --- | | `int` | `point` | A value 1..5 that selects which of the Lagrange points to calculate. | | [`astro_state_vector_t`](#astro_state_vector_t) | `major_state` | The state vector of the major (more massive) of the pair of bodies. | -| `double` | `major_mass` | The relative mass of the major body. | +| `double` | `major_mass` | The mass product GM of the major body. | | [`astro_state_vector_t`](#astro_state_vector_t) | `minor_state` | The state vector of the minor (less massive) of the pair of bodies. | -| `double` | `minor_mass` | The relative mass of the minor body. | +| `double` | `minor_mass` | The mass product GM of the minor body. | diff --git a/source/c/astronomy.c b/source/c/astronomy.c index feab020b..663ab606 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -4719,9 +4719,9 @@ astro_state_vector_t Astronomy_LagrangePoint( * * @param point A value 1..5 that selects which of the Lagrange points to calculate. * @param major_state The state vector of the major (more massive) of the pair of bodies. - * @param major_mass The relative mass of the major body. + * @param major_mass The mass product GM of the major body. * @param minor_state The state vector of the minor (less massive) of the pair of bodies. - * @param minor_mass The relative mass of the minor body. + * @param minor_mass The mass product GM of the minor body. * @return The position and velocity of the selected Lagrange point with respect to the major body's center. */ astro_state_vector_t Astronomy_LagrangePointFast( diff --git a/source/csharp/README.md b/source/csharp/README.md index 17ee53c7..99b93b3d 100644 --- a/source/csharp/README.md +++ b/source/csharp/README.md @@ -755,6 +755,83 @@ to convert to geocentric positions. **Returns:** Position and velocity vectors of Jupiter's largest 4 moons. + +### Astronomy.LagrangePoint(point, time, major_body, minor_body) ⇒ [`StateVector`](#StateVector) + +**Calculates one of the 5 Lagrange points for a pair of co-orbiting bodies.** + +Given a more massive "major" body and a much less massive "minor" body, +calculates one of the five Lagrange points in relation to the minor body's +orbit around the major body. The parameter `point` is an integer that +selects the Lagrange point as follows: + +1 = the Lagrange point between the major body and minor body. +2 = the Lagrange point on the far side of the minor body. +3 = the Lagrange point on the far side of the major body. +4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. +5 = the Lagrange point 60 degrees behind the minor body's orbital position. + +The function returns the state vector for the selected Lagrange point +in equatorial J2000 coordinates (EQJ), with respect to the center of the +major body. + +To calculate Sun/Earth Lagrange points, pass in `Body.Sun` for `major_body` +and `Body.EMB` (Earth/Moon barycenter) for `minor_body`. +For Lagrange points of the Sun and any other planet, pass in just that planet by itself, +e.g. `Body.Jupiter` for `minor_body`. +To calculate Earth/Moon Lagrange points, pass in `Body.Earth` and `Body.Moon` +for the major and minor bodies respectively. + +In some cases, it may be more efficient to call [`Astronomy.LagrangePointFast`](#Astronomy.LagrangePointFast), +especially when the state vectors have already been calculated, or are needed +for some other purpose. + +| Type | Parameter | Description | +| --- | --- | --- | +| `int` | `point` | A value 1..5 that selects which of the Lagrange points to calculate. | +| [`AstroTime`](#AstroTime) | `time` | The time at which the Lagrange point is to be calculated. | +| [`Body`](#Body) | `major_body` | The more massive of the co-orbiting bodies: `Body.Sun` or `Body.Earth`. | +| [`Body`](#Body) | `minor_body` | The less massive of the co-orbiting bodies. See main remarks. | + + +### Astronomy.LagrangePointFast(point, major_state, major_mass, minor_state, minor_mass) ⇒ [`StateVector`](#StateVector) + +**Calculates one of the 5 Lagrange points from body masses and state vectors.** + +Given a more massive "major" body and a much less massive "minor" body, +calculates one of the five Lagrange points in relation to the minor body's +orbit around the major body. The parameter `point` is an integer that +selects the Lagrange point as follows: + +1 = the Lagrange point between the major body and minor body. +2 = the Lagrange point on the far side of the minor body. +3 = the Lagrange point on the far side of the major body. +4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. +5 = the Lagrange point 60 degrees behind the minor body's orbital position. + +The caller passes in the state vector and mass for both bodies. +The state vectors can be in any orientation and frame of reference. +The body masses are expressed as GM products, where G = the universal +gravitation constant and M = the body's mass. Thus the units for +`major_mass` and `minor_mass` must be au^3/day^2. +Use [`Astronomy.MassProduct`](#Astronomy.MassProduct) to obtain GM values for various solar system bodies. + +The function returns the state vector for the selected Lagrange point +using the same orientation as the state vector parameters `major_state` and `minor_state`, +and the position and velocity components are with respect to the major body's center. + +Consider calling [`Astronomy.LagrangePoint`](#Astronomy.LagrangePoint), instead of this function, for simpler usage in most cases. + +| Type | Parameter | Description | +| --- | --- | --- | +| `int` | `point` | A value 1..5 that selects which of the Lagrange points to calculate. | +| [`StateVector`](#StateVector) | `major_state` | The state vector of the major (more massive) of the pair of bodies. | +| `double` | `major_mass` | The mass product GM of the major body. | +| [`StateVector`](#StateVector) | `minor_state` | The state vector of the minor (less massive) of the pair of bodies. | +| `double` | `minor_mass` | The mass product GM of the minor body. | + +**Returns:** The position and velocity of the selected Lagrange point with respect to the major body's center. + ### Astronomy.Libration(time) ⇒ [`LibrationInfo`](#LibrationInfo) @@ -780,6 +857,25 @@ and the apparent angular diameter of the Moon `diam_deg`. **Returns:** The Moon's ecliptic position and libration angles as seen from the Earth. + +### Astronomy.MassProduct(body) ⇒ `double` + +**Returns the product of mass and universal gravitational constant of a Solar System body.** + +For problems involving the gravitational interactions of Solar System bodies, +it is helpful to know the product GM, where G = the universal gravitational constant +and M = the mass of the body. In practice, GM is known to a higher precision than +either G or M alone, and thus using the product results in the most accurate results. +This function returns the product GM in the units au^3/day^2. +The values come from page 10 of a +[JPL memorandum regarding the DE405/LE405 ephemeris](https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf). + +| Type | Parameter | Description | +| --- | --- | --- | +| [`Body`](#Body) | `body` | The body for which to find the GM product. Allowed to be the Sun, Moon, EMB (Earth/Moon Barycenter), or any planet. Any other value will cause an exception to be thrown. | + +**Returns:** The mass product of the given body in au^3/day^2. + ### Astronomy.MoonPhase(time) ⇒ `double` diff --git a/source/csharp/astronomy.cs b/source/csharp/astronomy.cs index 360c5115..94b0e61b 100644 --- a/source/csharp/astronomy.cs +++ b/source/csharp/astronomy.cs @@ -2286,10 +2286,56 @@ namespace CosineKitty the products GM[i] are known extremely accurately. */ private const double SUN_GM = 0.2959122082855911e-03; + private const double MERCURY_GM = 0.4912547451450812e-10; + private const double VENUS_GM = 0.7243452486162703e-09; + private const double EARTH_GM = 0.8887692390113509e-09; + private const double MARS_GM = 0.9549535105779258e-10; private const double JUPITER_GM = 0.2825345909524226e-06; private const double SATURN_GM = 0.8459715185680659e-07; private const double URANUS_GM = 0.1292024916781969e-07; private const double NEPTUNE_GM = 0.1524358900784276e-07; + private const double PLUTO_GM = 0.2188699765425970e-11; + + private const double MOON_GM = EARTH_GM / EARTH_MOON_MASS_RATIO; + + /// + /// Returns the product of mass and universal gravitational constant of a Solar System body. + /// + /// + /// For problems involving the gravitational interactions of Solar System bodies, + /// it is helpful to know the product GM, where G = the universal gravitational constant + /// and M = the mass of the body. In practice, GM is known to a higher precision than + /// either G or M alone, and thus using the product results in the most accurate results. + /// This function returns the product GM in the units au^3/day^2. + /// The values come from page 10 of a + /// [JPL memorandum regarding the DE405/LE405 ephemeris](https://web.archive.org/web/20120220062549/http://iau-comm4.jpl.nasa.gov/de405iom/de405iom.pdf). + /// + /// + /// The body for which to find the GM product. + /// Allowed to be the Sun, Moon, EMB (Earth/Moon Barycenter), or any planet. + /// Any other value will cause an exception to be thrown. + /// + /// The mass product of the given body in au^3/day^2. + public static double MassProduct(Body body) + { + switch (body) + { + case Body.Sun: return SUN_GM; + case Body.Mercury: return MERCURY_GM; + case Body.Venus: return VENUS_GM; + case Body.Earth: return EARTH_GM; + case Body.Moon: return MOON_GM; + case Body.EMB: return EARTH_GM + MOON_GM; + case Body.Mars: return MARS_GM; + case Body.Jupiter: return JUPITER_GM; + case Body.Saturn: return SATURN_GM; + case Body.Uranus: return URANUS_GM; + case Body.Neptune: return NEPTUNE_GM; + case Body.Pluto: return PLUTO_GM; + default: + throw new InvalidBodyException(body); + } + } /// Counter used for performance testing. public static int CalcMoonCount; @@ -8302,6 +8348,275 @@ namespace CosineKitty throw new Exception("Peak magnitude search failed."); } + /// + /// Calculates one of the 5 Lagrange points for a pair of co-orbiting bodies. + /// + /// + /// Given a more massive "major" body and a much less massive "minor" body, + /// calculates one of the five Lagrange points in relation to the minor body's + /// orbit around the major body. The parameter `point` is an integer that + /// selects the Lagrange point as follows: + /// + /// 1 = the Lagrange point between the major body and minor body. + /// 2 = the Lagrange point on the far side of the minor body. + /// 3 = the Lagrange point on the far side of the major body. + /// 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. + /// 5 = the Lagrange point 60 degrees behind the minor body's orbital position. + /// + /// The function returns the state vector for the selected Lagrange point + /// in equatorial J2000 coordinates (EQJ), with respect to the center of the + /// major body. + /// + /// To calculate Sun/Earth Lagrange points, pass in `Body.Sun` for `major_body` + /// and `Body.EMB` (Earth/Moon barycenter) for `minor_body`. + /// For Lagrange points of the Sun and any other planet, pass in just that planet by itself, + /// e.g. `Body.Jupiter` for `minor_body`. + /// To calculate Earth/Moon Lagrange points, pass in `Body.Earth` and `Body.Moon` + /// for the major and minor bodies respectively. + /// + /// In some cases, it may be more efficient to call #Astronomy.LagrangePointFast, + /// especially when the state vectors have already been calculated, or are needed + /// for some other purpose. + /// + /// A value 1..5 that selects which of the Lagrange points to calculate. + /// The time at which the Lagrange point is to be calculated. + /// The more massive of the co-orbiting bodies: `Body.Sun` or `Body.Earth`. + /// The less massive of the co-orbiting bodies. See main remarks. + /// + public static StateVector LagrangePoint( + int point, + AstroTime time, + Body major_body, + Body minor_body) + { + double major_mass = MassProduct(major_body); + double minor_mass = MassProduct(minor_body); + + StateVector major_state; + StateVector minor_state; + + /* Calculate the state vectors for the major and minor bodies. */ + if (major_body == Body.Earth && minor_body == Body.Moon) + { + /* Use geocentric calculations for more precision. */ + + /* The Earth's geocentric state is trivial. */ + major_state.t = time; + major_state.x = major_state.y = major_state.z = 0.0; + major_state.vx = major_state.vy = major_state.vz = 0.0; + + minor_state = GeoMoonState(time); + } + else + { + major_state = HelioState(major_body, time); + minor_state = HelioState(minor_body, time); + } + + return LagrangePointFast( + point, + major_state, + major_mass, + minor_state, + minor_mass + ); + } + + /// + /// Calculates one of the 5 Lagrange points from body masses and state vectors. + /// + /// + /// Given a more massive "major" body and a much less massive "minor" body, + /// calculates one of the five Lagrange points in relation to the minor body's + /// orbit around the major body. The parameter `point` is an integer that + /// selects the Lagrange point as follows: + /// + /// 1 = the Lagrange point between the major body and minor body. + /// 2 = the Lagrange point on the far side of the minor body. + /// 3 = the Lagrange point on the far side of the major body. + /// 4 = the Lagrange point 60 degrees ahead of the minor body's orbital position. + /// 5 = the Lagrange point 60 degrees behind the minor body's orbital position. + /// + /// The caller passes in the state vector and mass for both bodies. + /// The state vectors can be in any orientation and frame of reference. + /// The body masses are expressed as GM products, where G = the universal + /// gravitation constant and M = the body's mass. Thus the units for + /// `major_mass` and `minor_mass` must be au^3/day^2. + /// Use #Astronomy.MassProduct to obtain GM values for various solar system bodies. + /// + /// The function returns the state vector for the selected Lagrange point + /// using the same orientation as the state vector parameters `major_state` and `minor_state`, + /// and the position and velocity components are with respect to the major body's center. + /// + /// Consider calling #Astronomy.LagrangePoint, instead of this function, for simpler usage in most cases. + /// + /// A value 1..5 that selects which of the Lagrange points to calculate. + /// The state vector of the major (more massive) of the pair of bodies. + /// The mass product GM of the major body. + /// The state vector of the minor (less massive) of the pair of bodies. + /// The mass product GM of the minor body. + /// The position and velocity of the selected Lagrange point with respect to the major body's center. + public static StateVector LagrangePointFast( + int point, + StateVector major_state, + double major_mass, + StateVector minor_state, + double minor_mass) + { + const double cos_60 = 0.5; + const double sin_60 = 0.8660254037844386; /* sqrt(3) / 2 */ + + if (point < 1 || point > 5) + throw new ArgumentException($"Invalid lagrange point {point}"); + + if (double.IsNaN(major_mass) || double.IsInfinity(major_mass) || major_mass <= 0.0) + throw new ArgumentException("Major mass must be a positive number."); + + if (double.IsNaN(minor_mass) || double.IsInfinity(minor_mass) || minor_mass <= 0.0) + throw new ArgumentException("Minor mass must be a positive number."); + + /* Find the relative position vector . */ + double dx = minor_state.x - major_state.x; + double dy = minor_state.y - major_state.y; + double dz = minor_state.z - major_state.z; + double R2 = (dx*dx + dy*dy + dz*dz); + + /* R = Total distance between the bodies. */ + double R = Math.Sqrt(R2); + + /* Find the velocity vector . */ + double vx = minor_state.vx - major_state.vx; + double vy = minor_state.vy - major_state.vy; + double vz = minor_state.vz - major_state.vz; + + StateVector p; + if (point == 4 || point == 5) + { + /* + For L4 and L5, we need to find points 60 degrees away from the + line connecting the two bodies and in the instantaneous orbital plane. + Define the instantaneous orbital plane as the unique plane that contains + both the relative position vector and the relative velocity vector. + */ + + /* Take the cross product of position and velocity to find a normal vector . */ + double nx = dy*vz - dz*vy; + double ny = dz*vx - dx*vz; + double nz = dx*vy - dy*vx; + + /* Take the cross product normal*position to get a tangential vector . */ + double ux = ny*dz - nz*dy; + double uy = nz*dx - nx*dz; + double uz = nx*dy - ny*dx; + + /* Convert the tangential direction vector to a unit vector. */ + double U = Math.Sqrt(ux*ux + uy*uy + uz*uz); + ux /= U; + uy /= U; + uz /= U; + + /* Convert the relative position vector into a unit vector. */ + dx /= R; + dy /= R; + dz /= R; + + /* Now we have two perpendicular unit vectors in the orbital plane: 'd' and 'u'. */ + + /* Create new unit vectors rotated (+/-)60 degrees from the radius/tangent directions. */ + double vert = (point == 4) ? +sin_60 : -sin_60; + + /* Rotated radial vector */ + double Dx = cos_60*dx + vert*ux; + double Dy = cos_60*dy + vert*uy; + double Dz = cos_60*dz + vert*uz; + + /* Rotated tangent vector */ + double Ux = cos_60*ux - vert*dx; + double Uy = cos_60*uy - vert*dy; + double Uz = cos_60*uz - vert*dz; + + /* Calculate L4/L5 positions relative to the major body. */ + p.x = R * Dx; + p.y = R * Dy; + p.z = R * Dz; + + /* Use dot products to find radial and tangential components of the relative velocity. */ + double vrad = vx*dx + vy*dy + vz*dz; + double vtan = vx*ux + vy*uy + vz*uz; + + /* Calculate L4/L5 velocities. */ + p.vx = vrad*Dx + vtan*Ux; + p.vy = vrad*Dy + vtan*Uy; + p.vz = vrad*Dz + vtan*Uz; + } + else + { + /* + Calculate the distances of each body from their mutual barycenter. + r1 = negative distance of major mass from barycenter (e.g. Sun to the left of barycenter) + r2 = positive distance of minor mass from barycenter (e.g. Earth to the right of barycenter) + */ + double r1 = -R * (minor_mass / (major_mass + minor_mass)); + double r2 = +R * (major_mass / (major_mass + minor_mass)); + + /* Calculate the square of the angular orbital speed in [rad^2 / day^2]. */ + double omega2 = (major_mass + minor_mass) / (R2*R); + + /* + Use Newton's Method to numerically solve for the location where + outward centrifugal acceleration in the rotating frame of reference + is equal to net inward gravitational acceleration. + First derive a good initial guess based on approximate analysis. + */ + double scale, numer1, numer2; + if (point == 1 || point == 2) + { + scale = (major_mass / (major_mass + minor_mass)) * Math.Cbrt(minor_mass / (3.0 * major_mass)); + numer1 = -major_mass; /* The major mass is to the left of L1 and L2 */ + if (point == 1) + { + scale = 1.0 - scale; + numer2 = +minor_mass; /* The minor mass is to the right of L1. */ + } + else + { + scale = 1.0 + scale; + numer2 = -minor_mass; /* The minor mass is to the left of L2. */ + } + } + else /* point == 3 */ + { + scale = ((7.0/12.0)*minor_mass - major_mass) / (minor_mass + major_mass); + numer1 = +major_mass; /* major mass is to the right of L3. */ + numer2 = +minor_mass; /* minor mass is to the right of L3. */ + } + + /* Iterate Newton's Method until it converges. */ + double x = R*scale - r1; + double deltax; + do + { + double dr1 = x - r1; + double dr2 = x - r2; + double accel = omega2*x + numer1/(dr1*dr1) + numer2/(dr2*dr2); + double deriv = omega2 - 2*numer1/(dr1*dr1*dr1) - 2*numer2/(dr2*dr2*dr2); + deltax = accel/deriv; + x -= deltax; + } + while (Math.Abs(deltax/R) > 1.0e-14); + scale = (x - r1) / R; + + p.x = scale * dx; + p.y = scale * dy; + p.z = scale * dz; + p.vx = scale * vx; + p.vy = scale * vy; + p.vz = scale * vz; + } + p.t = major_state.t; + return p; + } + /// Calculates the inverse of a rotation matrix. /// /// Given a rotation matrix that performs some coordinate transform,