KeplerianAnomalyUtility.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.orbits;

  18. import org.hipparchus.exception.MathIllegalStateException;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.MathUtils;
  21. import org.hipparchus.util.SinCos;
  22. import org.orekit.errors.OrekitMessages;

  23. /**
  24.  * Utility methods for converting between different Keplerian anomalies.
  25.  * @author Luc Maisonobe
  26.  * @author Andrew Goetz
  27.  */
  28. public final class KeplerianAnomalyUtility {

  29.     /** First coefficient to compute elliptic Kepler equation solver starter. */
  30.     private static final double A;

  31.     /** Second coefficient to compute elliptic Kepler equation solver starter. */
  32.     private static final double B;

  33.     static {
  34.         final double k1 = 3 * FastMath.PI + 2;
  35.         final double k2 = FastMath.PI - 1;
  36.         final double k3 = 6 * FastMath.PI - 1;
  37.         A = 3 * k2 * k2 / k1;
  38.         B = k3 * k3 / (6 * k1);
  39.     }

  40.     /** Private constructor for utility class. */
  41.     private KeplerianAnomalyUtility() {
  42.         // Nothing to do
  43.     }

  44.     /**
  45.      * Computes the elliptic true anomaly from the elliptic mean anomaly.
  46.      * @param e eccentricity such that 0 ≤ e < 1
  47.      * @param M elliptic mean anomaly (rad)
  48.      * @return elliptic true anomaly (rad)
  49.      */
  50.     public static double ellipticMeanToTrue(final double e, final double M) {
  51.         final double E = ellipticMeanToEccentric(e, M);
  52.         final double v = ellipticEccentricToTrue(e, E);
  53.         return v;
  54.     }

  55.     /**
  56.      * Computes the elliptic mean anomaly from the elliptic true anomaly.
  57.      * @param e eccentricity such that 0 ≤ e < 1
  58.      * @param v elliptic true anomaly (rad)
  59.      * @return elliptic mean anomaly (rad)
  60.      */
  61.     public static double ellipticTrueToMean(final double e, final double v) {
  62.         final double E = ellipticTrueToEccentric(e, v);
  63.         final double M = ellipticEccentricToMean(e, E);
  64.         return M;
  65.     }

  66.     /**
  67.      * Computes the elliptic true anomaly from the elliptic eccentric anomaly.
  68.      * @param e eccentricity such that 0 ≤ e < 1
  69.      * @param E elliptic eccentric anomaly (rad)
  70.      * @return elliptic true anomaly (rad)
  71.      */
  72.     public static double ellipticEccentricToTrue(final double e, final double E) {
  73.         final double beta = e / (1 + FastMath.sqrt((1 - e) * (1 + e)));
  74.         final SinCos scE = FastMath.sinCos(E);
  75.         return E + 2 * FastMath.atan(beta * scE.sin() / (1 - beta * scE.cos()));
  76.     }

  77.     /**
  78.      * Computes the elliptic eccentric anomaly from the elliptic true anomaly.
  79.      * @param e eccentricity such that 0 ≤ e < 1
  80.      * @param v elliptic true anomaly (rad)
  81.      * @return elliptic eccentric anomaly (rad)
  82.      */
  83.     public static double ellipticTrueToEccentric(final double e, final double v) {
  84.         final double beta = e / (1 + FastMath.sqrt(1 - e * e));
  85.         final SinCos scv = FastMath.sinCos(v);
  86.         return v - 2 * FastMath.atan(beta * scv.sin() / (1 + beta * scv.cos()));
  87.     }

  88.     /**
  89.      * Computes the elliptic eccentric anomaly from the elliptic mean anomaly.
  90.      * <p>
  91.      * The algorithm used here for solving hyperbolic Kepler equation is from Odell,
  92.      * A.W., Gooding, R.H. "Procedures for solving Kepler's equation." Celestial
  93.      * Mechanics 38, 307–334 (1986). https://doi.org/10.1007/BF01238923
  94.      * </p>
  95.      * @param e eccentricity such that 0 &le; e &lt; 1
  96.      * @param M elliptic mean anomaly (rad)
  97.      * @return elliptic eccentric anomaly (rad)
  98.      */
  99.     public static double ellipticMeanToEccentric(final double e, final double M) {
  100.         // reduce M to [-PI PI) interval
  101.         final double reducedM = MathUtils.normalizeAngle(M, 0.0);

  102.         // compute start value according to A. W. Odell and R. H. Gooding S12 starter
  103.         double E;
  104.         if (FastMath.abs(reducedM) < 1.0 / 6.0) {
  105.             E = reducedM + e * (FastMath.cbrt(6 * reducedM) - reducedM);
  106.         } else {
  107.             if (reducedM < 0) {
  108.                 final double w = FastMath.PI + reducedM;
  109.                 E = reducedM + e * (A * w / (B - w) - FastMath.PI - reducedM);
  110.             } else {
  111.                 final double w = FastMath.PI - reducedM;
  112.                 E = reducedM + e * (FastMath.PI - A * w / (B - w) - reducedM);
  113.             }
  114.         }

  115.         final double e1 = 1 - e;
  116.         final boolean noCancellationRisk = (e1 + E * E / 6) >= 0.1;

  117.         // perform two iterations, each consisting of one Halley step and one
  118.         // Newton-Raphson step
  119.         for (int j = 0; j < 2; ++j) {
  120.             final double f;
  121.             double fd;
  122.             final SinCos sc = FastMath.sinCos(E);
  123.             final double fdd = e * sc.sin();
  124.             final double fddd = e * sc.cos();
  125.             if (noCancellationRisk) {
  126.                 f = (E - fdd) - reducedM;
  127.                 fd = 1 - fddd;
  128.             } else {
  129.                 f = eMeSinE(e, E) - reducedM;
  130.                 final double s = FastMath.sin(0.5 * E);
  131.                 fd = e1 + 2 * e * s * s;
  132.             }
  133.             final double dee = f * fd / (0.5 * f * fdd - fd * fd);

  134.             // update eccentric anomaly, using expressions that limit underflow problems
  135.             final double w = fd + 0.5 * dee * (fdd + dee * fddd / 3);
  136.             fd += dee * (fdd + 0.5 * dee * fddd);
  137.             E -= (f - dee * (fd - w)) / fd;

  138.         }

  139.         // expand the result back to original range
  140.         E += M - reducedM;

  141.         return E;
  142.     }

  143.     /**
  144.      * Accurate computation of E - e sin(E).
  145.      * <p>
  146.      * This method is used when E is close to 0 and e close to 1, i.e. near the
  147.      * perigee of almost parabolic orbits
  148.      * </p>
  149.      * @param e eccentricity
  150.      * @param E eccentric anomaly (rad)
  151.      * @return E - e sin(E)
  152.      */
  153.     private static double eMeSinE(final double e, final double E) {
  154.         double x = (1 - e) * FastMath.sin(E);
  155.         final double mE2 = -E * E;
  156.         double term = E;
  157.         double d = 0;
  158.         // the inequality test below IS intentional and should NOT be replaced by a
  159.         // check with a small tolerance
  160.         for (double x0 = Double.NaN; !Double.valueOf(x).equals(Double.valueOf(x0));) {
  161.             d += 2;
  162.             term *= mE2 / (d * (d + 1));
  163.             x0 = x;
  164.             x = x - term;
  165.         }
  166.         return x;
  167.     }

  168.     /**
  169.      * Computes the elliptic mean anomaly from the elliptic eccentric anomaly.
  170.      * @param e eccentricity such that 0 &le; e &lt; 1
  171.      * @param E elliptic eccentric anomaly (rad)
  172.      * @return elliptic mean anomaly (rad)
  173.      */
  174.     public static double ellipticEccentricToMean(final double e, final double E) {
  175.         return E - e * FastMath.sin(E);
  176.     }

  177.     /**
  178.      * Computes the hyperbolic true anomaly from the hyperbolic mean anomaly.
  179.      * @param e eccentricity &gt; 1
  180.      * @param M hyperbolic mean anomaly
  181.      * @return hyperbolic true anomaly (rad)
  182.      */
  183.     public static double hyperbolicMeanToTrue(final double e, final double M) {
  184.         final double H = hyperbolicMeanToEccentric(e, M);
  185.         final double v = hyperbolicEccentricToTrue(e, H);
  186.         return v;
  187.     }

  188.     /**
  189.      * Computes the hyperbolic mean anomaly from the hyperbolic true anomaly.
  190.      * @param e eccentricity &gt; 1
  191.      * @param v hyperbolic true anomaly (rad)
  192.      * @return hyperbolic mean anomaly
  193.      */
  194.     public static double hyperbolicTrueToMean(final double e, final double v) {
  195.         final double H = hyperbolicTrueToEccentric(e, v);
  196.         final double M = hyperbolicEccentricToMean(e, H);
  197.         return M;
  198.     }

  199.     /**
  200.      * Computes the hyperbolic true anomaly from the hyperbolic eccentric anomaly.
  201.      * @param e eccentricity &gt; 1
  202.      * @param H hyperbolic eccentric anomaly
  203.      * @return hyperbolic true anomaly (rad)
  204.      */
  205.     public static double hyperbolicEccentricToTrue(final double e, final double H) {
  206.         return 2 * FastMath.atan(FastMath.sqrt((e + 1) / (e - 1)) * FastMath.tanh(0.5 * H));
  207.     }

  208.     /**
  209.      * Computes the hyperbolic eccentric anomaly from the hyperbolic true anomaly.
  210.      * @param e eccentricity &gt; 1
  211.      * @param v hyperbolic true anomaly (rad)
  212.      * @return hyperbolic eccentric anomaly
  213.      */
  214.     public static double hyperbolicTrueToEccentric(final double e, final double v) {
  215.         final SinCos scv = FastMath.sinCos(v);
  216.         final double sinhH = FastMath.sqrt(e * e - 1) * scv.sin() / (1 + e * scv.cos());
  217.         return FastMath.asinh(sinhH);
  218.     }

  219.     /**
  220.      * Computes the hyperbolic eccentric anomaly from the hyperbolic mean anomaly.
  221.      * <p>
  222.      * The algorithm used here for solving hyperbolic Kepler equation is from
  223.      * Gooding, R.H., Odell, A.W. "The hyperbolic Kepler equation (and the elliptic
  224.      * equation revisited)." Celestial Mechanics 44, 267–282 (1988).
  225.      * https://doi.org/10.1007/BF01235540
  226.      * </p>
  227.      * @param e eccentricity &gt; 1
  228.      * @param M hyperbolic mean anomaly
  229.      * @return hyperbolic eccentric anomaly
  230.      */
  231.     public static double hyperbolicMeanToEccentric(final double e, final double M) {
  232.         // Solve L = S - g * asinh(S) for S = sinh(H).
  233.         final double L = M / e;
  234.         final double g = 1.0 / e;
  235.         final double g1 = 1.0 - g;

  236.         // Starter based on Lagrange's theorem.
  237.         double S = L;
  238.         if (L == 0.0) {
  239.             return 0.0;
  240.         }
  241.         final double cl = FastMath.sqrt(1.0 + L * L);
  242.         final double al = FastMath.asinh(L);
  243.         final double w = g * g * al / (cl * cl * cl);
  244.         S = 1.0 - g / cl;
  245.         S = L + g * al / FastMath.cbrt(S * S * S + w * L * (1.5 - (4.0 / 3.0) * g));

  246.         // Two iterations (at most) of Halley-then-Newton process.
  247.         for (int i = 0; i < 2; ++i) {
  248.             final double s0 = S * S;
  249.             final double s1 = s0 + 1.0;
  250.             final double s2 = FastMath.sqrt(s1);
  251.             final double s3 = s1 * s2;
  252.             final double fdd = g * S / s3;
  253.             final double fddd = g * (1.0 - 2.0 * s0) / (s1 * s3);

  254.             double f;
  255.             double fd;
  256.             if (s0 / 6.0 + g1 >= 0.5) {
  257.                 f = (S - g * FastMath.asinh(S)) - L;
  258.                 fd = 1.0 - g / s2;
  259.             } else {
  260.                 // Accurate computation of S - (1 - g1) * asinh(S)
  261.                 // when (g1, S) is close to (0, 0).
  262.                 final double t = S / (1.0 + FastMath.sqrt(1.0 + S * S));
  263.                 final double tsq = t * t;
  264.                 double x = S * (g1 + g * tsq);
  265.                 double term = 2.0 * g * t;
  266.                 double twoI1 = 1.0;
  267.                 double x0;
  268.                 int j = 0;
  269.                 do {
  270.                     if (++j == 1000000) {
  271.                         // This isn't expected to happen, but it protects against an infinite loop.
  272.                         throw new MathIllegalStateException(
  273.                                 OrekitMessages.UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY, j);
  274.                     }
  275.                     twoI1 += 2.0;
  276.                     term *= tsq;
  277.                     x0 = x;
  278.                     x -= term / twoI1;
  279.                 } while (x != x0);
  280.                 f = x - L;
  281.                 fd = (s0 / (s2 + 1.0) + g1) / s2;
  282.             }
  283.             final double ds = f * fd / (0.5 * f * fdd - fd * fd);
  284.             final double stemp = S + ds;
  285.             if (S == stemp) {
  286.                 break;
  287.             }
  288.             f += ds * (fd + 0.5 * ds * (fdd + ds / 3.0 * fddd));
  289.             fd += ds * (fdd + 0.5 * ds * fddd);
  290.             S = stemp - f / fd;
  291.         }

  292.         final double H = FastMath.asinh(S);
  293.         return H;
  294.     }

  295.     /**
  296.      * Computes the hyperbolic mean anomaly from the hyperbolic eccentric anomaly.
  297.      * @param e eccentricity &gt; 1
  298.      * @param H hyperbolic eccentric anomaly
  299.      * @return hyperbolic mean anomaly
  300.      */
  301.     public static double hyperbolicEccentricToMean(final double e, final double H) {
  302.         return e * FastMath.sinh(H) - H;
  303.     }

  304. }