KeplerianAnomalyUtility.java

  1. /* Copyright 2002-2022 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 KeplerianAnomalyUtility() {
  41.     }

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

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

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

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

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

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

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

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

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

  136.         }

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

  139.         return E;
  140.     }

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

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

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

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

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

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

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

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

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

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

  290.         final double H = FastMath.asinh(S);
  291.         return H;
  292.     }

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

  302. }