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.cr3bp;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
21  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.BeforeEach;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.Utils;
26  import org.orekit.bodies.CR3BPFactory;
27  import org.orekit.bodies.CR3BPSystem;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.frames.Frame;
30  import org.orekit.propagation.SpacecraftState;
31  import org.orekit.propagation.numerical.NumericalPropagator;
32  import org.orekit.propagation.numerical.cr3bp.CR3BPForceModel;
33  import org.orekit.propagation.numerical.cr3bp.STMEquations;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.time.TimeScalesFactory;
36  import org.orekit.utils.AbsolutePVCoordinates;
37  import org.orekit.utils.LagrangianPoints;
38  import org.orekit.utils.PVCoordinates;
39  
40  class LyapunovOrbitTest {
41  
42  
43      @Test
44      void testLyapunovOrbit() {
45          CR3BPSystem syst = CR3BPFactory.getEarthMoonCR3BP();
46  
47          final PVCoordinates firstGuess = new PVCoordinates(new Vector3D(0.0, 1.0, 2.0), new Vector3D(3.0, 4.0, 5.0));
48          final LyapunovOrbit h1 = new LyapunovOrbit(new RichardsonExpansion(syst, LagrangianPoints.L1), 8E6);
49          final LyapunovOrbit h2 = new LyapunovOrbit(syst, firstGuess, 2.0);
50          final LyapunovOrbit h3 = new LyapunovOrbit(new RichardsonExpansion(syst, LagrangianPoints.L2), 8E6);
51  
52          final double orbitalPeriod1 = h1.getOrbitalPeriod();
53          final double orbitalPeriod2 = h2.getOrbitalPeriod();
54          final double orbitalPeriod3 = h3.getOrbitalPeriod();
55  
56          final PVCoordinates firstGuess1 = h1.getInitialPV();
57          final PVCoordinates firstGuess2 = h2.getInitialPV();
58          final PVCoordinates firstGuess3 = h3.getInitialPV();
59  
60          Assertions.assertNotEquals(0.0, orbitalPeriod1, 0.5);
61          Assertions.assertNotEquals(0.0, orbitalPeriod3, 0.5);
62          Assertions.assertEquals(2.0, orbitalPeriod2, 1E-15);
63  
64          Assertions.assertNotEquals(0.0, firstGuess1.getPosition().getX(), 0.6);
65          Assertions.assertEquals(0.0, firstGuess1.getPosition().getY(), 1E-15);
66          Assertions.assertEquals(0.0, firstGuess1.getVelocity().getX(), 1E-15);
67          Assertions.assertNotEquals(0.0, firstGuess1.getVelocity().getY(), 0.01);
68          Assertions.assertEquals(0.0, firstGuess1.getVelocity().getZ(), 1E-15);
69  
70          Assertions.assertNotEquals(0.0, firstGuess3.getPosition().getX(), 1);
71          Assertions.assertEquals(0.0, firstGuess3.getPosition().getY(), 1E-15);
72          Assertions.assertEquals(0.0, firstGuess3.getVelocity().getX(), 1E-15);
73          Assertions.assertNotEquals(0.0, firstGuess3.getVelocity().getY(), 0.01);
74          Assertions.assertEquals(0.0, firstGuess3.getVelocity().getZ(), 1E-15);
75  
76          Assertions.assertEquals(firstGuess.getPosition().getX(), firstGuess2.getPosition().getX(), 1E-15);
77          Assertions.assertEquals(firstGuess.getPosition().getY(), firstGuess2.getPosition().getY(), 1E-15);
78          Assertions.assertEquals(firstGuess.getPosition().getZ(), firstGuess2.getPosition().getZ(), 1E-15);
79          Assertions.assertEquals(firstGuess.getVelocity().getX(), firstGuess2.getVelocity().getX(), 1E-15);
80          Assertions.assertEquals(firstGuess.getVelocity().getY(), firstGuess2.getVelocity().getY(), 1E-15);
81          Assertions.assertEquals(firstGuess.getVelocity().getZ(), firstGuess2.getVelocity().getZ(), 1E-15);
82      }
83  
84      @Test
85          void testLagrangianError() {
86          Assertions.assertThrows(OrekitException.class, () -> {
87              CR3BPSystem syst = CR3BPFactory.getEarthMoonCR3BP();
88              final HaloOrbit h = new HaloOrbit(new RichardsonExpansion(syst, LagrangianPoints.L3), 8E6, LibrationOrbitFamily.NORTHERN);
89              h.getClass();
90          });
91      }
92  
93      @Test
94      void testManifolds() {
95  
96          // Time settings
97          final AbsoluteDate initialDate =
98              new AbsoluteDate(1996, 06, 25, 0, 0, 00.000,
99                               TimeScalesFactory.getUTC());
100         CR3BPSystem syst = CR3BPFactory.getEarthMoonCR3BP();
101 
102         final Frame Frame = syst.getRotatingFrame();
103 
104         LyapunovOrbit h = new LyapunovOrbit(new RichardsonExpansion(syst, LagrangianPoints.L1), 8E6);
105         h.applyDifferentialCorrection();
106         final double orbitalPeriod = h.getOrbitalPeriod();
107         double integrationTime = orbitalPeriod * 0.1;
108         final PVCoordinates initialConditions = h.getInitialPV();
109 
110         final AbsolutePVCoordinates initialAbsPV =
111             new AbsolutePVCoordinates(Frame, initialDate, initialConditions);
112 
113         // Creating the initial spacecraftstate that will be given to the
114         // propagator
115         final SpacecraftState initialState = new SpacecraftState(initialAbsPV);
116 
117         // Integration parameters
118         // These parameters are used for the Dormand-Prince integrator, a
119         // variable step integrator,
120         // these limits prevent the integrator to spend too much time when the
121         // equations are too stiff,
122         // as well as the reverse situation.
123         final double minStep = 1E-10;
124         final double maxstep = 1E-2;
125 
126         // tolerances for integrators
127         // Used by the integrator to estimate its variable integration step
128         final double positionTolerance = 0.0001;
129         final double velocityTolerance = 0.0001;
130         final double massTolerance = 1.0e-6;
131         final double[] vecAbsoluteTolerances = { positionTolerance, positionTolerance, positionTolerance,
132                 velocityTolerance, velocityTolerance, velocityTolerance, massTolerance };
133         final double[] vecRelativeTolerances = new double[vecAbsoluteTolerances.length];
134 
135         // Defining the numerical integrator that will be used by the propagator
136         AdaptiveStepsizeIntegrator integrator = new DormandPrince853Integrator(minStep, maxstep, vecAbsoluteTolerances,
137                 vecRelativeTolerances);
138 
139         final STMEquations stm = new STMEquations(syst);
140         final SpacecraftState augmentedInitialState =
141                         stm.setInitialPhi(initialState);
142         NumericalPropagator propagator = new NumericalPropagator(integrator);
143         propagator.setOrbitType(null);
144         propagator.setIgnoreCentralAttraction(true);
145         propagator.addForceModel(new CR3BPForceModel(syst));
146         propagator.addAdditionalDerivativesProvider(stm);
147         propagator.setInitialState(augmentedInitialState);
148         final SpacecraftState finalState = propagator.propagate(initialDate.shiftedBy(integrationTime));
149 
150         final PVCoordinates initialUnstableManifold = h.getManifolds(finalState, false);
151         final PVCoordinates initialStableManifold = h.getManifolds(finalState, true);
152 
153         Assertions.assertNotEquals(finalState.getPosition().getX(), initialUnstableManifold.getPosition().getX(), 1E-7);
154         Assertions.assertNotEquals(finalState.getPosition().getY(), initialUnstableManifold.getPosition().getY(), 1E-7);
155 
156         Assertions.assertNotEquals(finalState.getPosition().getX(), initialStableManifold.getPosition().getX(), 1E-7);
157         Assertions.assertNotEquals(finalState.getPosition().getY(), initialStableManifold.getPosition().getY(), 1E-7);
158     }
159 
160     @Test
161     void testDifferentialCorrectionError() {
162         Assertions.assertThrows(OrekitException.class, () -> {
163             CR3BPSystem syst = CR3BPFactory.getEarthMoonCR3BP();
164 
165             final double orbitalPeriod = 1;
166 
167             final PVCoordinates firstGuess = new PVCoordinates(new Vector3D(0.0, 1.0, 2.0), new Vector3D(3.0, 4.0, 5.0));
168 
169             final PVCoordinates initialConditions =
170                     new CR3BPDifferentialCorrection(firstGuess, syst, orbitalPeriod).compute(LibrationOrbitType.LYAPUNOV);
171             initialConditions.toString();
172         });
173     }
174 
175     @Test
176     void testSTMError() {
177         Assertions.assertThrows(OrekitException.class, () -> {
178             // Time settings
179             final AbsoluteDate initialDate =
180                     new AbsoluteDate(1996, 06, 25, 0, 0, 00.000,
181                             TimeScalesFactory.getUTC());
182             CR3BPSystem syst = CR3BPFactory.getEarthMoonCR3BP();
183 
184             final Frame Frame = syst.getRotatingFrame();
185 
186             // Define a Northern Halo orbit around Earth-Moon L1 with a Z-amplitude
187             // of 8 000 km
188             LyapunovOrbit h = new LyapunovOrbit(new RichardsonExpansion(syst, LagrangianPoints.L1), 8E6);
189 
190             final PVCoordinates pv = new PVCoordinates(new Vector3D(0.0, 1.0, 2.0), new Vector3D(3.0, 4.0, 5.0));
191 
192             final AbsolutePVCoordinates initialAbsPV =
193                     new AbsolutePVCoordinates(Frame, initialDate, pv);
194 
195             // Creating the initial spacecraftstate that will be given to the
196             // propagator
197             final SpacecraftState s = new SpacecraftState(initialAbsPV);
198 
199             final PVCoordinates manifold = h.getManifolds(s, true);
200             manifold.getMomentum();
201         });
202     }
203 
204     @BeforeEach
205     void setUp() {
206         Utils.setDataRoot("cr3bp:regular-data");
207     }
208 }