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  
18  package org.orekit.forces.maneuvers.trigger;
19  
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeAll;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.orekit.Utils;
28  import org.orekit.forces.maneuvers.Maneuver;
29  import org.orekit.forces.maneuvers.propulsion.BasicConstantThrustPropulsionModel;
30  import org.orekit.frames.Frame;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.orbits.KeplerianOrbit;
33  import org.orekit.orbits.Orbit;
34  import org.orekit.orbits.OrbitType;
35  import org.orekit.orbits.PositionAngleType;
36  import org.orekit.propagation.SpacecraftState;
37  import org.orekit.propagation.ToleranceProvider;
38  import org.orekit.propagation.numerical.NumericalPropagator;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.utils.Constants;
41  
42  /**
43   * Tests the functionality of starting a propagation in between and on start and end times
44   * of a Constant Thrust Maneuver
45   *
46   * @author Greg Carbott
47   */
48  public abstract class AbstractManeuverTriggersInitializationTest<T extends AbstractManeuverTriggers> {
49  
50      protected abstract T createTrigger(AbsoluteDate start, AbsoluteDate stop);
51  
52      private NumericalPropagator propagator;
53      private AbsoluteDate startDate;
54      private SpacecraftState initialState;
55      /** Propagation duration in seconds */
56      private double propDuration = 1200.;
57  
58      /** Maneuver duration in seconds */
59      private double duration = 60.;
60  
61      /** Maneuver Thrust Force in Newtons */
62      private double thrust = 1e3;
63  
64      /** Maneuver Specific Impulse in seconds */
65      private double isp = 100.;
66  
67      /** Mass of Spacecraft in kilograms */
68      private double mass = 2e3;
69  
70      /** Tolerance of deltaV (m/s) difference */
71      private double dvTolerance = 3e-6;
72  
73      /** Tolerance of mass (kg) difference */
74      private double massTolerance = 5e-6;
75  
76      /** Direction of Maneuver */
77      private Vector3D direction = new Vector3D(1, 0, 1);
78  
79      /** Control Items */
80      private double deltaVControlFullForward;
81      private double massControlFullForward;
82      private double massControlHalfForward;
83      private double deltaVControlFullReverse;
84      private double deltaVControlHalfReverse;
85      private double massControlFullReverse;
86      private double massControlHalfReverse;
87  
88      /** trigger dates. */
89      private AbsoluteDate triggerStart;
90      private AbsoluteDate triggerStop;
91  
92      private T configureTrigger(final AbsoluteDate start, final AbsoluteDate stop) {
93          T trigger = createTrigger(start, stop);
94          trigger.addResetter(new ManeuverTriggersResetter() {
95              @Override
96              public void maneuverTriggered(SpacecraftState state, boolean start) {
97                  if (start) {
98                      triggerStart = state.getDate();
99                  } else {
100                     triggerStop  = state.getDate();
101                 }
102             }
103             @Override
104             public SpacecraftState resetState(SpacecraftState state) {
105                 return state;
106             }
107 
108         });
109         return trigger;
110     }
111 
112     /** set orekit data */
113     @BeforeAll
114     public static void setUpBefore() {
115         Utils.setDataRoot("regular-data");
116     }
117 
118     @BeforeEach
119     public void setUp() {
120         startDate = new AbsoluteDate();
121         double a = Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 400e3;
122         double e = 0.003;
123         double i = (Math.PI / 4);
124         double pa = 0.0;
125         double raan = 0.0;
126         double anomaly = 0.0;
127         PositionAngleType type = PositionAngleType.MEAN;
128         Frame frame = FramesFactory.getEME2000();
129         double mu = Constants.EGM96_EARTH_MU;
130         Orbit orbit = new KeplerianOrbit(a, e, i, pa, raan, anomaly,
131                 type, frame, startDate, mu);
132         initialState = new SpacecraftState(orbit, mass);
133 
134         //Numerical Propagator
135         double minStep = 1.0e-5;
136         double maxStep = 1000.0;
137         double positionTolerance = 1.0e-4;
138         OrbitType propagationType = OrbitType.KEPLERIAN;
139         double[][] tolerances = ToleranceProvider.getDefaultToleranceProvider(positionTolerance).getTolerances(orbit, propagationType);
140         DormandPrince853Integrator integrator =
141                 new DormandPrince853Integrator(minStep, maxStep,
142                         tolerances[0], tolerances[1]);
143         integrator.setInitialStepSize(10.0);
144         integrator.setMaxGrowth(2.0);
145         //Set up propagator
146         propagator = new NumericalPropagator(integrator);
147         propagator.setOrbitType(propagationType);
148 
149         //Control deltaVs and mass changes
150         double flowRate = -thrust / (Constants.G0_STANDARD_GRAVITY * isp);
151         massControlFullForward = mass + (flowRate * duration);
152         deltaVControlFullForward = isp * Constants.G0_STANDARD_GRAVITY *
153                 FastMath.log(mass / massControlFullForward);
154 
155         massControlHalfForward = mass + (flowRate * duration / 2);
156 
157         massControlFullReverse = mass - (flowRate * duration);
158         deltaVControlFullReverse = isp * Constants.G0_STANDARD_GRAVITY *
159                 FastMath.log(massControlFullReverse / mass);
160 
161         massControlHalfReverse = mass - (flowRate * duration / 2);
162         deltaVControlHalfReverse = isp * Constants.G0_STANDARD_GRAVITY *
163                 FastMath.log(massControlHalfReverse / mass);
164 
165         triggerStart = null;
166         triggerStop  = null;
167     }
168 
169     @Test
170     void testInBetween() {
171         //Create test Thrust Maneuver
172         Maneuver ctm = new Maneuver(null,
173                                     configureTrigger(startDate.shiftedBy(-(duration / 2)),
174                                                      startDate.shiftedBy( (duration / 2))),
175                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
176 
177         //Reset and populate propagator
178         propagator.removeForceModels();
179         propagator.addForceModel(ctm);
180         propagator.setInitialState(initialState);
181         SpacecraftState finalStateTest =
182                 propagator.propagate(startDate.shiftedBy(propDuration));
183 
184         Assertions.assertEquals(massControlHalfForward, finalStateTest.getMass(), massTolerance);
185 
186         Assertions.assertNull(triggerStart);
187         Assertions.assertEquals(duration / 2, triggerStop.durationFrom(startDate), 1.0e-10);
188 
189     }
190 
191     @Test
192     void testOnStart() {
193         //Create test Thrust Maneuver
194         Maneuver ctm = new Maneuver(null,
195                                     configureTrigger(startDate,
196                                                      startDate.shiftedBy(duration)),
197                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
198         //Reset and populate propagator
199         propagator.removeForceModels();
200         propagator.addForceModel(ctm);
201         propagator.setInitialState(initialState);
202         SpacecraftState finalStateTest =
203                 propagator.propagate(startDate.shiftedBy(propDuration));
204 
205         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
206                 FastMath.log(mass / finalStateTest.getMass());
207 
208         Assertions.assertEquals(deltaVControlFullForward, deltaVTest, dvTolerance);
209         Assertions.assertEquals(massControlFullForward, finalStateTest.getMass(), massTolerance);
210 
211         Assertions.assertEquals(0.0,      triggerStart.durationFrom(startDate), 1.0e-10);
212         Assertions.assertEquals(duration, triggerStop.durationFrom(startDate),  1.0e-10);
213 
214     }
215 
216     @Test
217     void testOnEnd() {
218         //Create test Thrust Maneuver
219         Maneuver ctm = new Maneuver(null,
220                                     configureTrigger(startDate.shiftedBy(-duration),
221                                                      startDate),
222                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
223         //Reset and populate propagator
224         propagator.removeForceModels();
225         propagator.addForceModel(ctm);
226         propagator.setInitialState(initialState);
227         SpacecraftState finalStateTest =
228                 propagator.propagate(startDate.shiftedBy(propDuration));
229 
230         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
231                 FastMath.log(mass / finalStateTest.getMass());
232 
233         Assertions.assertTrue(deltaVTest == 0.0);
234         Assertions.assertTrue(finalStateTest.getMass() == mass);
235 
236         Assertions.assertNull(triggerStart);
237         Assertions.assertEquals(0.0, triggerStop.durationFrom(startDate), 1.0e-10);
238 
239     }
240 
241     @Test
242     void testOnEndReverse() {
243         //Create test Thrust Maneuver
244         Maneuver ctm = new Maneuver(null,
245                                     configureTrigger(startDate.shiftedBy(-duration),
246                                                      startDate),
247                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
248         //Reset and populate propagator
249         propagator.removeForceModels();
250         propagator.addForceModel(ctm);
251         propagator.setInitialState(initialState);
252         SpacecraftState finalStateTest =
253                 propagator.propagate(startDate.shiftedBy(-propDuration));
254 
255         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
256                 FastMath.log(finalStateTest.getMass() / mass);
257 
258         Assertions.assertEquals(deltaVControlFullReverse, deltaVTest, dvTolerance);
259         Assertions.assertEquals(massControlFullReverse, finalStateTest.getMass(), massTolerance);
260 
261         Assertions.assertEquals(-duration, triggerStart.durationFrom(startDate), 1.0e-10);
262         Assertions.assertEquals(0.0,       triggerStop.durationFrom(startDate),  1.0e-10);
263 
264     }
265 
266     @Test
267     void testOnStartReverse() {
268         //Create test Thrust Maneuver
269         Maneuver ctm = new Maneuver(null,
270                                     configureTrigger(startDate,
271                                                      startDate.shiftedBy(duration)),
272                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
273         //Reset and populate propagator
274         propagator.removeForceModels();
275         propagator.addForceModel(ctm);
276         propagator.setInitialState(initialState);
277         SpacecraftState finalStateTest =
278                 propagator.propagate(startDate.shiftedBy(-propDuration));
279 
280         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
281                 FastMath.log(finalStateTest.getMass() / mass);
282 
283         Assertions.assertEquals(0.0, deltaVTest, dvTolerance);
284         Assertions.assertTrue(finalStateTest.getMass() == mass);
285 
286         Assertions.assertEquals(0.0, triggerStart.durationFrom(startDate), 1.0e-10);
287         Assertions.assertNull(triggerStop);
288     }
289 
290     @Test
291     void testInBetweenReverse() {
292         //Create test Thrust Maneuver
293         Maneuver ctm = new Maneuver(null,
294                                     configureTrigger(startDate.shiftedBy(-duration / 2),
295                                                      startDate.shiftedBy( duration / 2)),
296                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
297         //Reset and populate propagator
298         propagator.removeForceModels();
299         propagator.addForceModel(ctm);
300         propagator.setInitialState(initialState);
301         SpacecraftState finalStateTest =
302                 propagator.propagate(startDate.shiftedBy(-propDuration));
303 
304         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
305                 FastMath.log(finalStateTest.getMass() / mass);
306 
307         Assertions.assertEquals(deltaVControlHalfReverse, deltaVTest, dvTolerance);
308         Assertions.assertEquals(massControlHalfReverse, finalStateTest.getMass(), massTolerance);
309 
310         Assertions.assertEquals(-0.5 * duration, triggerStart.durationFrom(startDate), 1.0e-10);
311         Assertions.assertNull(triggerStop);
312 
313     }
314 
315     @Test
316     void testControlForward() {
317         //Create test Thrust Maneuver
318         Maneuver ctm = new Maneuver(null,
319                                     configureTrigger(startDate.shiftedBy(1.0),
320                                                      startDate.shiftedBy(1.0 + duration)),
321                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
322         //Reset and populate propagator
323         propagator.removeForceModels();
324         propagator.addForceModel(ctm);
325         propagator.setInitialState(initialState);
326         SpacecraftState finalStateTest =
327                 propagator.propagate(startDate.shiftedBy(propDuration));
328 
329         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
330                 FastMath.log(mass / finalStateTest.getMass());
331 
332         Assertions.assertEquals(deltaVControlFullForward, deltaVTest, dvTolerance);
333         Assertions.assertEquals(massControlFullForward, finalStateTest.getMass(), massTolerance);
334 
335         Assertions.assertEquals(1.0,            triggerStart.durationFrom(startDate), 1.0e-10);
336         Assertions.assertEquals(1.0 + duration, triggerStop.durationFrom(startDate),  1.0e-10);
337     }
338 
339     @Test
340     void testControlReverse() {
341         //Create test Thrust Maneuver
342         Maneuver ctm = new Maneuver(null,
343                                     configureTrigger(startDate.shiftedBy(-1.0 - duration),
344                                                      startDate.shiftedBy(-1.0)),
345                                     new BasicConstantThrustPropulsionModel(thrust, isp, direction, ""));
346         //Reset and populate propagator
347         propagator.removeForceModels();
348         propagator.addForceModel(ctm);
349         propagator.setInitialState(initialState);
350         SpacecraftState finalStateTest =
351                 propagator.propagate(startDate.shiftedBy(-propDuration));
352 
353         double deltaVTest = isp * Constants.G0_STANDARD_GRAVITY *
354                 FastMath.log(finalStateTest.getMass() / mass);
355 
356         Assertions.assertEquals(deltaVControlFullReverse, deltaVTest, dvTolerance);
357         Assertions.assertEquals(massControlFullReverse, finalStateTest.getMass(), massTolerance);
358 
359         Assertions.assertEquals(-1.0 - duration, triggerStart.durationFrom(startDate), 1.0e-10);
360         Assertions.assertEquals(-1.0,            triggerStop.durationFrom(startDate),  1.0e-10);
361 
362     }
363 
364 }