1   /* Copyright 2022-2025 Romain Serra
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.forces.maneuvers;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.Gradient;
22  import org.hipparchus.analysis.differentiation.GradientField;
23  import org.hipparchus.complex.Complex;
24  import org.hipparchus.complex.ComplexField;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.hipparchus.util.Binary64;
28  import org.hipparchus.util.Binary64Field;
29  import org.hipparchus.util.MathArrays;
30  import org.junit.jupiter.api.Assertions;
31  import org.junit.jupiter.api.DisplayName;
32  import org.junit.jupiter.api.Test;
33  import org.mockito.Mockito;
34  import org.orekit.attitudes.*;
35  import org.orekit.forces.maneuvers.propulsion.BasicConstantThrustPropulsionModel;
36  import org.orekit.forces.maneuvers.propulsion.PropulsionModel;
37  import org.orekit.forces.maneuvers.propulsion.ThrustPropulsionModel;
38  import org.orekit.forces.maneuvers.trigger.DateBasedManeuverTriggers;
39  import org.orekit.forces.maneuvers.trigger.ManeuverTriggers;
40  import org.orekit.frames.Frame;
41  import org.orekit.frames.FramesFactory;
42  import org.orekit.orbits.CartesianOrbit;
43  import org.orekit.propagation.FieldSpacecraftState;
44  import org.orekit.propagation.SpacecraftState;
45  import org.orekit.time.AbsoluteDate;
46  import org.orekit.time.FieldAbsoluteDate;
47  import org.orekit.utils.PVCoordinates;
48  import org.orekit.utils.ParameterDriver;
49  
50  import java.util.ArrayList;
51  import java.util.List;
52  
53  class ManeuverTest {
54  
55      @Test
56      void testGetName() {
57          // GIVEN
58          final String tooShortName = "a";
59          final String expectedName = "aa";
60          final double arbitraryDuration = 0.;
61          final DateBasedManeuverTriggers triggers = new DateBasedManeuverTriggers(tooShortName, AbsoluteDate.ARBITRARY_EPOCH, arbitraryDuration);
62          final double arbitraryThrust = 1.;
63          final double arbitraryIsp = 1.;
64          final PropulsionModel propulsion = new BasicConstantThrustPropulsionModel(arbitraryThrust, arbitraryIsp, Vector3D.PLUS_I, expectedName);
65          // WHEN
66          final Maneuver maneuver = new Maneuver(null, triggers, propulsion);
67          final String actualName = maneuver.getName();
68          // THEN
69          Assertions.assertEquals(expectedName, actualName);
70      }
71  
72      @Test
73      void testGetParametersDrivers() {
74          // GIVEN
75          final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
76          final PropulsionModel mockedPropulsion = Mockito.mock(PropulsionModel.class);
77          final AttitudeProvider mocedkAttitudeProvider = Mockito.mock(AttitudeProvider.class);
78          final List<ParameterDriver> driverList = new ArrayList<>();
79          driverList.add(Mockito.mock(ParameterDriver.class));
80          Mockito.when(mocedkAttitudeProvider.getParametersDrivers()).thenReturn(driverList);
81          // WHEN
82          final Maneuver maneuver = new Maneuver(mocedkAttitudeProvider, mockedTriggers, mockedPropulsion);
83          final List<ParameterDriver> actualDrivers = maneuver.getParametersDrivers();
84          // THEN
85          Assertions.assertEquals(driverList.size(), actualDrivers.size());
86      }
87  
88      @Test
89      void testGetMassDerivativeNotFiring() {
90          // GIVEN
91          final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
92          final double[] array = new double[0];
93          final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
94          Mockito.when(mockedTriggers.isFiring(date, array)).thenReturn(false);
95          final PropulsionModel mockedPropulsion = Mockito.mock(PropulsionModel.class);
96          final SpacecraftState mockedState = Mockito.mock();
97          Mockito.when(mockedState.getDate()).thenReturn(date);
98          // WHEN
99          final Maneuver maneuver = new Maneuver(null, mockedTriggers, mockedPropulsion);
100         final double rate = maneuver.getMassDerivative(mockedState, array);
101         // THEN
102         Assertions.assertEquals(0, rate);
103     }
104 
105     @Test
106     void testFieldGetMassDerivativeNotFiring() {
107         // GIVEN
108         final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
109         final Binary64[] array = new Binary64[0];
110         final FieldAbsoluteDate<Binary64> date = FieldAbsoluteDate.getArbitraryEpoch(Binary64Field.getInstance());
111         Mockito.when(mockedTriggers.isFiring(date, array)).thenReturn(false);
112         final PropulsionModel mockedPropulsion = Mockito.mock(PropulsionModel.class);
113         @SuppressWarnings("unchecked")
114         final FieldSpacecraftState<Binary64> mockedState = Mockito.mock();
115         Mockito.when(mockedState.getDate()).thenReturn(date);
116         Mockito.when(mockedState.getMass()).thenReturn(Binary64.ONE);
117         // WHEN
118         final Maneuver maneuver = new Maneuver(null, mockedTriggers, mockedPropulsion);
119         final Binary64 rate = maneuver.getMassDerivative(mockedState, array);
120         // THEN
121         Assertions.assertEquals(Binary64.ZERO, rate);
122     }
123 
124     @Test
125     void testGetControl3DVectorCostType() {
126         // GIVEN
127         final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
128         final PropulsionModel mockedPropulsion = Mockito.mock(PropulsionModel.class);
129         Mockito.when(mockedPropulsion.getControl3DVectorCostType()).thenReturn(Control3DVectorCostType.TWO_NORM);
130         // WHEN
131         final Maneuver maneuver = new Maneuver(null, mockedTriggers, mockedPropulsion);
132         final Control3DVectorCostType actualCostType = maneuver.getControl3DVectorCostType();
133         // THEN
134         final Control3DVectorCostType expectedCostType = mockedPropulsion.getControl3DVectorCostType();
135         Assertions.assertEquals(expectedCostType, actualCostType);
136     }
137 
138     @Test
139     @DisplayName("Test comparing Field acceleration w/ and w/o attitude override.")
140     @SuppressWarnings("unchecked")
141     void testAccelerationFieldAttitudeOverride() {
142         // GIVEN
143         final ComplexField field = ComplexField.getInstance();
144 
145         // mock triggers
146         final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
147         Mockito.when(mockedTriggers.isFiring(Mockito.any(AbsoluteDate.class), Mockito.any(double[].class))).
148                 thenReturn(true);
149         Mockito.when(mockedTriggers.isFiring(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Complex[].class))).
150                 thenReturn(true);
151         Mockito.when(mockedTriggers.getParametersDrivers()).thenReturn(new ArrayList<>());
152 
153         // mock propulsion model
154         final ThrustPropulsionModel mockedPropulsion = Mockito.mock(ThrustPropulsionModel.class);
155         final Vector3D returnedVector = Vector3D.MINUS_I;
156         final FieldVector3D<Complex> returnedFieldVector = new FieldVector3D<>(field, returnedVector);
157         Mockito.when(mockedPropulsion.getAcceleration(Mockito.any(SpacecraftState.class), Mockito.any(Attitude.class),
158                 Mockito.any(double[].class))).thenReturn(returnedVector);
159         Mockito.when(mockedPropulsion.getAcceleration(Mockito.any(FieldSpacecraftState.class), Mockito.any(FieldAttitude.class),
160                 Mockito.any(Complex[].class))).thenReturn(returnedFieldVector);
161         Mockito.when(mockedPropulsion.getParametersDrivers()).thenReturn(new ArrayList<>());
162 
163         // create FieldSpacecraftState
164         final int arbitraryNumber = 6;
165         final AbsoluteDate epoch = AbsoluteDate.ARBITRARY_EPOCH;
166         final Frame frame = FramesFactory.getGCRF();
167         final FieldSpacecraftState<Complex> fieldState = createFieldState(field, frame, epoch);
168 
169         // create Maneuver
170         final FrameAlignedProvider attitudeOverride = new FrameAlignedProvider(frame);
171         final Maneuver maneuverWithOverride = new Maneuver(attitudeOverride, mockedTriggers, mockedPropulsion);
172         final Complex[] fieldParameters = MathArrays.buildArray(field, arbitraryNumber);
173         
174         // WHEN
175         final FieldVector3D<Complex> actualAcceleration = maneuverWithOverride.acceleration(fieldState, fieldParameters);
176         
177         // THEN
178         final Maneuver maneuverWithoutOverride = new Maneuver(null, mockedTriggers, mockedPropulsion);
179         Assertions.assertNotEquals(0., actualAcceleration.toVector3D().getNorm());
180         final Vector3D expectedAcceleration = maneuverWithoutOverride.acceleration(fieldState.toSpacecraftState(),
181                 new double[fieldParameters.length]);
182         Assertions.assertEquals(expectedAcceleration, actualAcceleration.toVector3D());
183         Assertions.assertEquals(returnedVector, actualAcceleration.toVector3D());
184     }
185 
186     @Test
187     @DisplayName("Test comparing Field acceleration to standard one.")
188     @SuppressWarnings("unchecked")
189     void testAccelerationField() {
190         // GIVEN
191         final GradientField field = GradientField.getField(1);
192 
193         // mock triggers
194         final ManeuverTriggers mockedTriggers = Mockito.mock(ManeuverTriggers.class);
195         Mockito.when(mockedTriggers.isFiring(Mockito.any(AbsoluteDate.class), Mockito.any(double[].class))).
196                 thenReturn(true);
197         Mockito.when(mockedTriggers.isFiring(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Gradient[].class))).
198                 thenReturn(true);
199         Mockito.when(mockedTriggers.getParametersDrivers()).thenReturn(new ArrayList<>());
200 
201         // mock propulsion model
202         final ThrustPropulsionModel mockedPropulsion = Mockito.mock(ThrustPropulsionModel.class);
203         final Vector3D returnedVector = Vector3D.MINUS_I;
204         final FieldVector3D<Gradient> returnedFieldVector = new FieldVector3D<>(field, returnedVector);
205         Mockito.when(mockedPropulsion.getAcceleration(Mockito.any(SpacecraftState.class), Mockito.any(Attitude.class),
206                         Mockito.any(double[].class))).thenReturn(returnedVector);
207         Mockito.when(mockedPropulsion.getAcceleration(Mockito.any(FieldSpacecraftState.class), Mockito.any(FieldAttitude.class),
208                 Mockito.any(Gradient[].class))).thenReturn(returnedFieldVector);
209         Mockito.when(mockedPropulsion.getParametersDrivers()).thenReturn(new ArrayList<>());
210 
211         // create FieldSpacecraftState
212         final int arbitraryNumber = 6;
213         final AbsoluteDate epoch = AbsoluteDate.ARBITRARY_EPOCH;
214         final FieldSpacecraftState<Gradient> fieldState = createFieldState(field, FramesFactory.getEME2000(), epoch);
215 
216         // create Maneuver
217         final Maneuver maneuver = new Maneuver(null, mockedTriggers, mockedPropulsion);
218         final Gradient[] fieldParameters = MathArrays.buildArray(field, arbitraryNumber);
219         
220         // WHEN
221         final FieldVector3D<Gradient> acceleration = maneuver.acceleration(fieldState, fieldParameters);
222         
223         // THEN
224         final Vector3D expectedAcceleration = maneuver.acceleration(fieldState.toSpacecraftState(), new double[arbitraryNumber]);
225         Assertions.assertNotEquals(0., acceleration.toVector3D().getNorm());
226         Assertions.assertEquals(expectedAcceleration, acceleration.toVector3D());
227     }
228 
229     private <T extends CalculusFieldElement<T>> FieldSpacecraftState<T> createFieldState(final Field<T> field, 
230                                                                                          final Frame frame, 
231                                                                                          final AbsoluteDate epoch) {
232         final double arbitraryMu = 1.;
233         final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.PLUS_I, Vector3D.PLUS_J);
234         final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, frame, epoch, arbitraryMu);
235         final SpacecraftState state = new SpacecraftState(orbit);
236         return new FieldSpacecraftState<>(field, state);
237     }
238 
239     @Test
240     void testGetAttitudeModelParametersNull() {
241         final double[] parameters = new double[] {1};
242         final Maneuver maneuver = new Maneuver(null, null, null);
243         final double[] actualDrivers = maneuver.getAttitudeModelParameters(parameters);
244         Assertions.assertArrayEquals(new double[0], actualDrivers);
245     }
246 
247     @Test
248     void testGetAttitudeModelParameters() {
249         final AttitudeRotationModel mockedRotationModel = Mockito.mock(AttitudeRotationModel.class);
250         final double[] parameters = new double[] {1};
251         Mockito.when(mockedRotationModel.getParameters()).thenReturn(parameters);
252         final List<ParameterDriver> drivers = new ArrayList<>();
253         drivers.add(Mockito.mock(ParameterDriver.class));
254         Mockito.when(mockedRotationModel.getParametersDrivers()).thenReturn(drivers);
255         final Maneuver maneuver = new Maneuver(mockedRotationModel, null, null);
256         final double[] actualDrivers = maneuver.getAttitudeModelParameters(parameters);
257         Assertions.assertArrayEquals(parameters, actualDrivers);
258     }
259 
260     @Test
261     void testGetAttitudeModelParametersFieldNull() {
262         final Binary64Field field = Binary64Field.getInstance();
263         final Binary64[] parameters = MathArrays.buildArray(field, 1);
264         parameters[0] = Binary64.ONE;
265         final Maneuver maneuver = new Maneuver(null, null, null);
266         final Binary64[] actualDrivers = maneuver.getAttitudeModelParameters(parameters);
267         Assertions.assertEquals(0, actualDrivers.length);
268     }
269 
270     @Test
271     void testGetAttitudeModelParametersField() {
272         final AttitudeRotationModel mockedRotationModel = Mockito.mock(AttitudeRotationModel.class);
273         final Binary64Field field = Binary64Field.getInstance();
274         final Binary64[] parameters = MathArrays.buildArray(field, 1);
275         parameters[0] = Binary64.ONE;
276         Mockito.when(mockedRotationModel.getParameters(field)).thenReturn(parameters);
277         final List<ParameterDriver> drivers = new ArrayList<>();
278         drivers.add(Mockito.mock(ParameterDriver.class));
279         Mockito.when(mockedRotationModel.getParametersDrivers()).thenReturn(drivers);
280         final Maneuver maneuver = new Maneuver(mockedRotationModel, null, null);
281         final Binary64[] actualDrivers = maneuver.getAttitudeModelParameters(parameters);
282         Assertions.assertArrayEquals(parameters, actualDrivers);
283     }
284 }