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.propagation.events;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.ode.events.Action;
21  import org.hipparchus.util.FastMath;
22  import org.junit.jupiter.api.AfterEach;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.mockito.Mockito;
27  import org.orekit.Utils;
28  import org.orekit.bodies.CelestialBodyFactory;
29  import org.orekit.bodies.OneAxisEllipsoid;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.frames.FramesFactory;
32  import org.orekit.orbits.EquinoctialOrbit;
33  import org.orekit.orbits.Orbit;
34  import org.orekit.propagation.Propagator;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
37  import org.orekit.propagation.events.handlers.EventHandler;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.TimeScalesFactory;
40  import org.orekit.utils.IERSConventions;
41  import org.orekit.utils.PVCoordinates;
42  
43  import java.util.ArrayList;
44  import java.util.List;
45  
46  class EventShifterTest {
47  
48      private double           mu;
49      private AbsoluteDate     iniDate;
50      private Propagator       propagator;
51      private List<EventEntry> log;
52  
53      private double sunRadius = 696000000.;
54      private double earthRadius = 6400000.;
55  
56      @Test
57      void testWithDetectionSettings() {
58          // GIVEN
59          final EventDetector detector = new DateDetector();
60          final EventShifter template = new EventShifter(detector, true, 1., 2.);
61          final EventDetectionSettings detectionSettings = Mockito.mock();
62          // WHEN
63          final EventShifter shifter = template.withDetectionSettings(detectionSettings);
64          // THEN
65          Assertions.assertEquals(detector, shifter.getDetector());
66          Assertions.assertEquals(detectionSettings, shifter.getDetectionSettings());
67          Assertions.assertEquals(template.getIncreasingTimeShift(), shifter.getIncreasingTimeShift());
68          Assertions.assertEquals(template.getDecreasingTimeShift(), shifter.getDecreasingTimeShift());
69      }
70  
71      @Test
72      void testInit() {
73          // GIVEN
74          final TestDetector detector = new TestDetector();
75          final EventShifter shifter = new EventShifter(detector, true, 1., 1.);
76          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
77          Mockito.when(mockedState.getDate()).thenReturn(iniDate);
78          // WHEN
79          shifter.init(mockedState, AbsoluteDate.ARBITRARY_EPOCH);
80          // THEN
81          Assertions.assertTrue(detector.initialized);
82      }
83  
84      private static class TestDetector extends DateDetector {
85          boolean initialized = false;
86  
87          @Override
88          public void init(SpacecraftState s0, AbsoluteDate t) {
89              super.init(s0, t);
90              initialized = true;
91          }
92      }
93  
94      @Test
95      void testNegNeg() {
96          propagator.addEventDetector(createRawDetector("raw increasing", "raw decreasing", 1.0e-9));
97          EclipseDetector raw = createRawDetector("shifted increasing", "shifted decreasing", 1.0e-3);
98          final EventHandler h = raw.getHandler();
99          raw = raw.withHandler(new EventHandler() {
100 
101             @Override
102             public Action eventOccurred(SpacecraftState s,
103                                         EventDetector detector,
104                                         boolean increasing) {
105                 h.eventOccurred(s, detector, increasing);
106                 return Action.RESET_STATE;
107             }
108 
109             @Override
110             public SpacecraftState resetState(EventDetector detector, SpacecraftState oldState)
111                                                   {
112                 return h.resetState(detector, oldState);
113             }
114 
115         });
116         final EventDetectionSettings settings = EventDetectionSettings.getDefaultEventDetectionSettings().withMaxIter(200);
117         EventShifter shifter = new EventShifter(raw, true, -15, -20).withDetectionSettings(settings);
118         Assertions.assertEquals(-15, shifter.getIncreasingTimeShift(), 1.0e-15);
119         Assertions.assertEquals(-20, shifter.getDecreasingTimeShift(), 1.0e-15);
120         Assertions.assertEquals(200, shifter.getMaxIterationCount());
121         Assertions.assertEquals(100, raw.getMaxIterationCount());
122         propagator.addEventDetector(shifter);
123         propagator.addEventDetector(new EventShifter(createRawDetector("unshifted increasing", "unshifted decreasing", 1.0e-3),
124                                                      false, -5, -10));
125         propagator.propagate(iniDate.shiftedBy(6000));
126         Assertions.assertEquals(6, log.size());
127         log.get(0).checkExpected(log.get(2).getDT() - 20, "shifted decreasing");
128         log.get(1).checkExpected(log.get(2).getDT(),      "unshifted decreasing");
129         log.get(3).checkExpected(log.get(5).getDT() - 15, "shifted increasing");
130         log.get(4).checkExpected(log.get(5).getDT(),      "unshifted increasing");
131     }
132 
133     @Test
134     void testNegPos() {
135         propagator.addEventDetector(createRawDetector("raw increasing", "raw decreasing", 1.0e-9));
136         propagator.addEventDetector(new EventShifter(createRawDetector("shifted increasing", "shifted decreasing", 1.0e-3),
137                                                      true, -15,  20));
138         propagator.addEventDetector(new EventShifter(createRawDetector("unshifted increasing", "unshifted decreasing", 1.0e-3),
139                                                      false, -5,  10));
140         propagator.propagate(iniDate.shiftedBy(6000));
141         Assertions.assertEquals(6, log.size());
142         log.get(1).checkExpected(log.get(0).getDT(),      "unshifted decreasing");
143         log.get(2).checkExpected(log.get(0).getDT() + 20, "shifted decreasing");
144         log.get(3).checkExpected(log.get(5).getDT() - 15, "shifted increasing");
145         log.get(4).checkExpected(log.get(5).getDT(),      "unshifted increasing");
146     }
147 
148     @Test
149     void testPosNeg() {
150         propagator.addEventDetector(createRawDetector("raw increasing", "raw decreasing", 1.0e-9));
151         propagator.addEventDetector(new EventShifter(createRawDetector("shifted increasing", "shifted decreasing", 1.0e-3),
152                                                      true,  15, -20));
153         propagator.addEventDetector(new EventShifter(createRawDetector("unshifted increasing", "unshifted decreasing", 1.0e-3),
154                                                      false,  5, -10));
155         propagator.propagate(iniDate.shiftedBy(6000));
156         Assertions.assertEquals(6, log.size());
157         log.get(0).checkExpected(log.get(2).getDT() - 20, "shifted decreasing");
158         log.get(1).checkExpected(log.get(2).getDT(),      "unshifted decreasing");
159         log.get(4).checkExpected(log.get(3).getDT(),      "unshifted increasing");
160         log.get(5).checkExpected(log.get(3).getDT() + 15, "shifted increasing");
161     }
162 
163     @Test
164     void testPosPos() {
165         propagator.addEventDetector(createRawDetector("raw increasing", "raw decreasing", 1.0e-9));
166         propagator.addEventDetector(new EventShifter(createRawDetector("shifted increasing", "shifted decreasing", 1.0e-3),
167                                                      true,  15,  20));
168         propagator.addEventDetector(new EventShifter(createRawDetector("unshifted increasing", "unshifted decreasing", 1.0e-3),
169                                                      false,  5,  10));
170         propagator.propagate(iniDate.shiftedBy(6000));
171         Assertions.assertEquals(6, log.size());
172         log.get(1).checkExpected(log.get(0).getDT(),      "unshifted decreasing");
173         log.get(2).checkExpected(log.get(0).getDT() + 20, "shifted decreasing");
174         log.get(4).checkExpected(log.get(3).getDT(),      "unshifted increasing");
175         log.get(5).checkExpected(log.get(3).getDT() + 15, "shifted increasing");
176     }
177 
178     @Test
179     void testIncreasingError() {
180         final EclipseDetector raw0000 = createRawDetector("raw increasing",    "raw decreasing", 2.0e-9);
181         final EclipseDetector raw0010 = createRawDetector("-10s increasing",   "-10s decreasing", 2.0e-3);
182         final EclipseDetector raw0100 = createRawDetector("-100s increasing",  "-100s decreasing", 3.0e-2);
183         final EclipseDetector raw1000 = createRawDetector("-1000s increasing", "-1000s decreasing", 5.0);
184         final EventShifter shift0010 = new EventShifter(raw0010, true,   -10,   -10);
185         final EventShifter shift0100 = new EventShifter(raw0100, true,  -100,  -100);
186         final EventShifter shift1000 = new EventShifter(raw1000, true, -1000, -1000);
187         Assertions.assertSame(raw0010, shift0010.getDetector());
188         Assertions.assertSame(raw0100, shift0100.getDetector());
189         Assertions.assertSame(raw1000, shift1000.getDetector());
190         propagator.addEventDetector(raw0000);
191         propagator.addEventDetector(shift0010);
192         propagator.addEventDetector(shift0100);
193         propagator.addEventDetector(shift1000);
194         propagator.propagate(iniDate.shiftedBy(20100));
195 
196         // the raw eclipses (not all within the propagation range) are at times:
197         // [ 2300.238,  4376.986]
198         // [ 8210.859, 10287.573]
199         // [14121.478, 16198.159]
200         // [20032.098, 22108.745]
201         // [25942.717, 28019.331]
202         // [31853.335, 33929.916]
203         // [37763.954, 39840.500]
204         Assertions.assertEquals(28, log.size());
205         for (int i = 0; i < log.size() / 4; ++i) {
206             EventEntry ref = log.get(4 * i + 3);
207             String increasingOrDecreasing = ref.getName().split(" ")[1];
208             log.get(4 * i + 0).checkExpected(ref.getDT() - 1000, "-1000s " + increasingOrDecreasing);
209             log.get(4 * i + 1).checkExpected(ref.getDT() -  100, "-100s "  + increasingOrDecreasing);
210             log.get(4 * i + 2).checkExpected(ref.getDT() -   10, "-10s "   + increasingOrDecreasing);
211         }
212 
213         for (EventEntry entry : log) {
214             double error = entry.getTimeError();
215             if (entry.name.contains("10s")) {
216                 Assertions.assertTrue(error > 1.0e-6);
217                 Assertions.assertTrue(error < 3.0e-6);
218             } else if (entry.name.contains("100s")) {
219                 Assertions.assertTrue(error > 0.001);
220                 Assertions.assertTrue(error < 0.003);
221             } else if (entry.name.contains("1000s")) {
222                 Assertions.assertTrue(error > 0.7);
223                 Assertions.assertTrue(error < 1.1);
224             }
225         }
226     }
227 
228     private EclipseDetector createRawDetector(final String nameIncreasing, final String nameDecreasing,
229                                               final double tolerance) {
230         return new EclipseDetector(CelestialBodyFactory.getSun(), sunRadius,
231                                    new OneAxisEllipsoid(earthRadius,
232                                                         0.0,
233                                                         FramesFactory.getITRF(IERSConventions.IERS_2010, true))).
234                withMaxCheck(60.0).
235                withThreshold(1.0e-10).
236                withHandler(new EventHandler() {
237                                        public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
238                                            log.add(new EventEntry(s.getDate().durationFrom(iniDate), tolerance,
239                                                                   increasing ? nameIncreasing : nameDecreasing));
240                                            return Action.CONTINUE;
241                                        }
242                                    });
243     }
244 
245     @BeforeEach
246     public void setUp() {
247         try {
248             Utils.setDataRoot("regular-data");
249             mu  = 3.9860047e14;
250             double ae  = 6.378137e6;
251             double c20 = -1.08263e-3;
252             double c30 = 2.54e-6;
253             double c40 = 1.62e-6;
254             double c50 = 2.3e-7;
255             double c60 = -5.5e-7;
256             final Vector3D position  = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
257             final Vector3D velocity  = new Vector3D(505.8479685, 942.7809215, 7435.922231);
258             iniDate = new AbsoluteDate(1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT());
259             final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
260                                                      FramesFactory.getGCRF(), iniDate, mu);
261             propagator =
262                 new EcksteinHechlerPropagator(orbit, ae, mu, c20, c30, c40, c50, c60);
263             log = new ArrayList<EventEntry>();
264         } catch (OrekitException oe) {
265             Assertions.fail(oe.getLocalizedMessage());
266         }
267     }
268 
269     @AfterEach
270     public void tearDown() {
271         iniDate = null;
272         propagator = null;
273         log = null;
274     }
275 
276     private static class EventEntry {
277 
278         private final double dt;
279         private double expectedDT;
280         private final double tolerance;
281         private final String name;
282 
283         public EventEntry(final double dt, final double tolerance, final String name) {
284             this.dt         = dt;
285             this.expectedDT = Double.NaN;
286             this.tolerance  = tolerance;
287             this.name       = name;
288         }
289 
290         public void checkExpected(final double expectedDT, final String name) {
291             this.expectedDT = expectedDT;
292             Assertions.assertEquals(expectedDT, dt, tolerance);
293             Assertions.assertEquals(name, this.name);
294         }
295 
296         public double getDT() {
297             return dt;
298         }
299 
300         public String getName() {
301             return name;
302         }
303 
304         public double getTimeError() {
305             return FastMath.abs(dt - expectedDT);
306         }
307 
308     }
309 
310 }
311