RichardsonExpansion.java

  1. /* Copyright 2002-2025 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.geometry.euclidean.threed.Vector3D;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.SinCos;
  21. import org.orekit.bodies.CR3BPSystem;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.utils.LagrangianPoints;
  25. import org.orekit.utils.PVCoordinates;

  26. /** Class implementing the Third-Order Richardson Expansion.
  27.  * @see "Dynamical systems, the three-body problem, and space mission design, Koon, Lo, Marsden, Ross"
  28.  * @author Vincent Mouraux
  29.  * @since 10.2
  30.  */
  31. public class RichardsonExpansion {

  32.     /** Distance between a Lagrangian Point and its closest primary body, meters. */
  33.     private final double gamma;

  34.     /** Mass ratio of the CR3BP System. */
  35.     private final double mu;

  36.     /** Distance between the two primary bodies, meters. */
  37.     private final double dDim;

  38.     /** Halo Orbit frequency. */
  39.     private final double wp;

  40.     /** Simple Halo Orbit coefficient. */
  41.     private final double k;

  42.     /** Simple Richardson coefficient. */
  43.     private final double a21;

  44.     /** Simple Richardson coefficient. */
  45.     private final double a22;

  46.     /** Simple Richardson coefficient. */
  47.     private final double a23;

  48.     /** Simple Richardson coefficient. */
  49.     private final double a24;

  50.     /** Simple Richardson coefficient. */
  51.     private final double b21;

  52.     /** Simple Richardson coefficient. */
  53.     private final double b22;

  54.     /** Simple Richardson coefficient. */
  55.     private final double d21;

  56.     /** Simple Richardson coefficient. */
  57.     private final double a31;

  58.     /** Simple Richardson coefficient. */
  59.     private final double a32;

  60.     /** Simple Richardson coefficient. */
  61.     private final double b31;

  62.     /** Simple Richardson coefficient. */
  63.     private final double b32;

  64.     /** Simple Richardson coefficient. */
  65.     private final double d31;

  66.     /** Simple Richardson coefficient. */
  67.     private final double d32;

  68.     /** Simple Richardson coefficient. */
  69.     private final double s1;

  70.     /** Simple Richardson coefficient. */
  71.     private final double s2;

  72.     /** Simple Richardson coefficient. */
  73.     private final double l1;

  74.     /** Simple Richardson coefficient. */
  75.     private final double l2;

  76.     /** Simple Halo Orbit coefficient. */
  77.     private final double delta;

  78.     /** Orbital Period of the Halo Orbit, seconds. */
  79.     private double orbitalPeriod;

  80.     /** Lagrangian Point considered. */
  81.     private LagrangianPoints point;

  82.     /** CR3BP System considered. */
  83.     private CR3BPSystem cr3bpSystem;

  84.     /** Simple Constructor.
  85.      * @param cr3bpSystem CR3BP System considered
  86.      * @param point Lagrangian Point considered
  87.     */
  88.     public RichardsonExpansion(final CR3BPSystem cr3bpSystem,
  89.                                       final LagrangianPoints point) {

  90.         this.point       = point;
  91.         this.cr3bpSystem = cr3bpSystem;
  92.         this.mu          = cr3bpSystem.getMassRatio();
  93.         this.dDim        = cr3bpSystem.getDdim();
  94.         this.gamma       = cr3bpSystem.getGamma(point);

  95.         final double c2 = getCn(2.0);
  96.         final double c3 = getCn(3.0);
  97.         final double c4 = getCn(4.0);

  98.         wp = FastMath.sqrt(0.5 * (2.0 - c2 + FastMath.sqrt(9.0 * c2 * c2 - 8.0 * c2)));

  99.         final double ld = FastMath.sqrt(0.5 * (2.0 - c2 + FastMath.sqrt(9.0 * c2 * c2 - 8.0 * c2)));

  100.         k = (wp * wp + 1.0 + 2.0 * c2) / (2.0 * wp);

  101.         final double d1 = 3.0 * ld * ld * (k * (6.0 * ld * ld - 1.0) - 2.0 * ld) / k;
  102.         final double d2 = 8.0 * ld * ld * (k * (11.0 * ld * ld - 1.0) - 2.0 * ld) / k;

  103.         a21 = 3.0 * c3 * (k * k - 2.0) / (4.0 * (1.0 + 2.0 * c2));

  104.         a22 = 3.0 * c3 / (4.0 * (1.0 + 2.0 * c2));

  105.         a23 = -3.0 * c3 * ld * (3.0 * k * k * k * ld - 6.0 * k * (k - ld) + 4.0) / (4.0 * k * d1);

  106.         a24 = -3.0 * c3 * ld * (2.0 + 3.0 * k * ld) / (4.0 * k * d1);

  107.         b21 = -3.0 * c3 * ld * (3.0 * k * ld - 4.0) / (2.0 * d1);

  108.         b22 = 3.0 * c3 * ld / d1;

  109.         d21 = -c3 / (2.0 * ld * ld);

  110.         a31 =
  111.             -9.0 * ld * (4.0 * c3 * (k * a23 - b21) + k * c4 * (4.0 + k * k)) / (4.0 * d2) +
  112.             (9.0 * ld * ld + 1.0 - c2) / (2.0 * d2) * (2.0 * c3 * (2.0 * a23 - k * b21) + c4 * (2.0 + 3.0 * k * k));

  113.         a32 =
  114.             -9.0 * ld * (4.0 * c3 * (k * a24 - b22) + k * c4) / (4.0 * d2) -
  115.             3 * (9.0 * ld * ld + 1.0 - c2) * (c3 * (k * b22 + d21 - 2.0 * a24) - c4) / (2.0 * d2);

  116.         b31 =
  117.             3.0 * 8.0 * ld * (3.0 * c3 * (k * b21 - 2.0 * a23) - c4 * (2.0 + 3.0 * k * k)) / (8.0 * d2) +
  118.             3.0 * ((9.0 * ld * ld + 1.0 + 2.0 * c2) * (4.0 * c3 * (k * a23 - b21) + k * c4 * (4.0 + k * k))) / (8.0 * d2);

  119.         b32 =
  120.             9.0 * ld * (c3 * (k * b22 + d21 - 2.0 * a24) - c4) / d2 +
  121.             3.0 * ((9.0 * ld * ld + 1.0 + 2.0 * c2) * (4.0 * c3 * (k * a24 - b22) +  k * c4)) / (8.0 * d2);

  122.         d31 = 3.0 / (64.0 * ld * ld) * (4.0 * c3 * a24 + c4);

  123.         d32 = 3.0 / (64.0 * ld * ld) * (4.0 * c3 * (a23 - d21) + c4 * (4.0 + k * k));

  124.         s1 =
  125.             (3.0 * c3 * (2.0 * a21 * (k * k - 2.0) - a23 * (k * k + 2.0) - 2.0 * k * b21) / 2.0 -
  126.             3.0 * c4 * (3.0 * k * k * k * k - 8.0 * k * k + 8.0) / 8.0) / (2.0 * ld * (ld * (1.0 + k * k) - 2.0 * k));

  127.         s2 =
  128.             (3.0 * c3 * (2.0 * a22 * (k * k - 2.0) + a24 * (k * k + 2.0) + 2.0 * k * b22 + 5.0 * d21) / 2.0 +
  129.             3.0 * c4 * (12.0 - k * k) / 8.0) / (2.0 * ld * (ld * (1.0 + k * k) - 2.0 * k));

  130.         l1 = -3.0 * c3 * (2.0 * a21 + a23 + 5.0 * d21) / 2.0 - 3.0 * c4 * (12.0 - k * k) / 8.0 + 2.0 * ld * ld * s1;

  131.         l2 = 3.0 * c3 * (a24 - 2.0 * a22) / 2.0 + (9.0 * c4 / 8.0) + (2.0 * ld * ld * s2);

  132.         delta = wp * wp - c2;
  133.     }


  134.     /** Calculate Cn Richardson Coefficient.
  135.      * @param order Order 'n' of the coefficient needed.
  136.      * @return cn Cn Richardson Coefficient
  137.     */
  138.     private double getCn(final double order) {
  139.         final double gamma3 = gamma * gamma * gamma;
  140.         final double cn;
  141.         switch (point) {
  142.             case L1:
  143.                 cn = (1.0 / gamma3) * (mu + FastMath.pow(-1, order) * (1 - mu) * FastMath.pow(gamma, order + 1) / FastMath.pow(1 - gamma, order + 1));
  144.                 break;
  145.             case L2:
  146.                 cn = (1.0 / gamma3) * (FastMath.pow(-1, order) * mu + FastMath.pow(-1, order) * (1 - mu) * FastMath.pow(gamma, order + 1) / FastMath.pow(1 + gamma, order + 1));
  147.                 break;
  148.             default:
  149.                 throw new OrekitException(OrekitMessages.CANNOT_COMPUTE_LAGRANGIAN, point);
  150.         }
  151.         return cn;
  152.     }

  153.     /** Calculate first Guess.
  154.      * @param azr z-axis Amplitude of the required Halo Orbit, meters
  155.      * @param type type of the Halo Orbit ("Northern" or "Southern")
  156.      * @param t Orbit time, seconds (must be greater than 0)
  157.      * @param phi Orbit phase, rad
  158.      * @return firstGuess PVCoordinates of the first guess
  159.     */
  160.     public PVCoordinates computeHaloFirstGuess(final double azr, final LibrationOrbitFamily type,
  161.                                            final double t, final double phi) {

  162.         // Z-Axis Halo Orbit Amplitude
  163.         final double az = azr / (gamma * dDim);

  164.         // X-Axis Halo Orbit Amplitude
  165.         final double ax      = FastMath.sqrt((delta + l2 * az * az) / -l1);
  166.         final double nu      = 1.0 + s1 * ax * ax + s2 * az * az;
  167.         final double tau     = nu * t;
  168.         final double tau1    = wp * tau + phi;
  169.         final double m       = (type == LibrationOrbitFamily.NORTHERN) ? 1 : 3;
  170.         final double dm      = 2 - m;
  171.         final SinCos scTau1  = FastMath.sinCos(tau1);
  172.         final SinCos sc2Tau1 = SinCos.sum(scTau1, scTau1);
  173.         final SinCos sc3Tau1 = SinCos.sum(scTau1, sc2Tau1);

  174.         // First guess position relative to its Lagrangian point
  175.         final double firstx =
  176.                         a21 * ax * ax +
  177.                         a22 * az * az - ax * scTau1.cos() +
  178.                         (a23 * ax * ax - a24 * az * az) * sc2Tau1.cos() +
  179.                         (a31 * ax * ax * ax - a32 * ax * az * az) * sc3Tau1.cos();

  180.         final double firsty =
  181.                         k * ax * scTau1.sin() +
  182.                         (b21 * ax * ax - b22 * az * az) * sc2Tau1.sin() +
  183.                         (b31 * ax * ax * ax - b32 * ax * az * az) * sc3Tau1.sin();

  184.         final double firstz =
  185.                         dm * az * scTau1.cos() +
  186.                         dm * d21 * ax * az * (sc2Tau1.cos() - 3.0) +
  187.                         dm * (d32 * az * ax * ax - d31 * az * az * az) * sc3Tau1.cos();

  188.         // First guess Velocity relative to its Lagrangian point
  189.         final double vx =
  190.                         ax * wp * nu * scTau1.sin() -
  191.                         2.0 * (a23 * ax * ax - a24 * az * az) * wp * nu * sc2Tau1.sin() -
  192.                         3.0 * (a31 * ax * ax * ax - a32 * ax * az * az) * wp * nu * sc3Tau1.sin();

  193.         final double vy =
  194.                         k * ax * wp * nu * scTau1.cos() +
  195.                         2.0 * wp * nu * (b21 * ax * ax - b22 * az * az) * sc2Tau1.cos() +
  196.                         3.0 * wp * nu * (b31 * ax * ax * ax - b32 * ax * az * az) * sc3Tau1.cos();

  197.         final double vz =
  198.                         -dm * az * wp * nu * scTau1.sin() -
  199.                         2.0 * dm * d21 * ax * az * wp * nu * sc2Tau1.sin() -
  200.                         3.0 * dm * (d32 * az * ax * ax - d31 * az * az * az) * wp * nu * sc3Tau1.sin();

  201.         // Return PV Coordinates
  202.         return point == LagrangianPoints.L1 ?
  203.                 new PVCoordinates(new Vector3D(firstx * gamma + 1.0 - mu - gamma, firsty * gamma, firstz * gamma), new Vector3D(vx * gamma, vy * gamma, vz * gamma)) :
  204.                 new PVCoordinates(new Vector3D(firstx * gamma + 1.0 - mu + gamma, firsty * gamma, firstz * gamma), new Vector3D(vx * gamma, vy * gamma, vz * gamma));
  205.     }

  206.     /** Calculate first Guess.
  207.      * @param ayr x-axis Amplitude of the required Lyapunov Orbit, meters
  208.      * @param t time
  209.      * @param phi Orbit phase, rad
  210.      * @return firstGuess PVCoordinates of the first guess
  211.     */
  212.     public PVCoordinates computeLyapunovFirstGuess(final double ayr, final double t, final double phi) {

  213.         // Z-Axis Lyapunov Orbit Amplitude
  214.         final double az = 0;

  215.         // y-Axis Lyapunov Orbit Amplitude
  216.         final double ay   = ayr / (gamma * dDim);
  217.         final double ax   = ay / k;
  218.         final double nu   = 1.0 + s1 * ax * ax + s2 * az * az;
  219.         final double tau  = nu * t;
  220.         final double tau1 = wp * tau + phi;

  221.         // First guess position relative to its Lagrangian point
  222.         final double firstx = -ax * FastMath.cos(tau1);
  223.         final double firsty = 0.0;
  224.         final double firstz = 0.0;

  225.         // First guess Velocity relative to its Lagrangian point
  226.         final double vx = 0.0;
  227.         final double vy = k * ax * wp * nu * FastMath.cos(tau1);
  228.         final double vz = 0.0;

  229.         // Return PV Coordinates
  230.         return point == LagrangianPoints.L1 ?
  231.                  new PVCoordinates(new Vector3D(firstx * gamma + 1.0 - mu - gamma, firsty * gamma, firstz * gamma),
  232.                                    new Vector3D(vx * gamma, vy * gamma, vz * gamma)) :
  233.                  new PVCoordinates(new Vector3D(firstx * gamma + 1.0 - mu + gamma, firsty * gamma, firstz * gamma),
  234.                                    new Vector3D(vx * gamma, vy * gamma, vz * gamma));
  235.     }

  236.     /** Return the orbital period of the Halo Orbit.
  237.      * @param azr z-axis Amplitude of the required Halo Orbit, meters
  238.      * @return the orbitalPeriod
  239.      */
  240.     public double getHaloOrbitalPeriod(final double azr) {
  241.         final double az = azr / (gamma * dDim);
  242.         final double ax = FastMath.sqrt((delta + l2 * az * az) / -l1);
  243.         final double nu = 1.0 + s1 * ax * ax + s2 * az * az;
  244.         orbitalPeriod   = FastMath.abs(2.0 * FastMath.PI / (wp * nu));
  245.         return orbitalPeriod;
  246.     }

  247.     /** Return the orbital period of the Halo Orbit.
  248.      * @param axr x-axis Amplitude of the required Lyapunov Orbit, meters
  249.      * @return the orbitalPeriod
  250.      */
  251.     public double getLyapunovOrbitalPeriod(final double axr) {
  252.         final double az = 0;
  253.         final double ax = axr / (gamma * dDim);
  254.         final double nu = 1.0 + s1 * ax * ax + s2 * az * az;
  255.         orbitalPeriod   = FastMath.abs(2.0 * FastMath.PI / (wp * nu));
  256.         return orbitalPeriod;
  257.     }

  258.     /** Get the considered CR3BP system.
  259.      * @return CRR3BP system
  260.      */
  261.     public CR3BPSystem getCr3bpSystem() {
  262.         return cr3bpSystem;
  263.     }

  264.     /** Get the considered lagrangian point.
  265.      * @return lagrangian point
  266.      */
  267.     public LagrangianPoints getLagrangianPoint() {
  268.         return point;
  269.     }

  270. }