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.ode.nonstiff.AdaptiveStepsizeIntegrator;
22  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
23  import org.junit.jupiter.api.AfterEach;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.junit.jupiter.params.ParameterizedTest;
28  import org.junit.jupiter.params.provider.EnumSource;
29  import org.orekit.Utils;
30  import org.orekit.bodies.CelestialBodyFactory;
31  import org.orekit.bodies.OneAxisEllipsoid;
32  import org.orekit.errors.OrekitException;
33  import org.orekit.frames.FramesFactory;
34  import org.orekit.orbits.EquinoctialOrbit;
35  import org.orekit.orbits.Orbit;
36  import org.orekit.propagation.SpacecraftState;
37  import org.orekit.propagation.events.handlers.CountAndContinue;
38  import org.orekit.propagation.events.handlers.EventHandler;
39  import org.orekit.propagation.numerical.NumericalPropagator;
40  import org.orekit.time.AbsoluteDate;
41  import org.orekit.time.TimeScalesFactory;
42  import org.orekit.utils.IERSConventions;
43  import org.orekit.utils.PVCoordinates;
44  
45  import java.util.List;
46  
47  import static org.mockito.Mockito.mock;
48  import static org.mockito.Mockito.when;
49  
50  class EventsLoggerTest {
51  
52      private double               mu;
53      private AbsoluteDate         iniDate;
54      private SpacecraftState      initialState;
55      private NumericalPropagator  propagator;
56      private CountAndContinue countAndContinue;
57      private AbstractDetector<EclipseDetector>        umbraDetector;
58      private AbstractDetector<EclipseDetector>         penumbraDetector;
59  
60      @Test
61      void testMonitorDetectorClass() {
62          // GIVEN
63          final DateDetector dateDetector = new DateDetector();
64          final EventsLogger eventsLogger = new EventsLogger();
65          // WHEN
66          final EventDetector detector = eventsLogger.monitorDetector(dateDetector);
67          // THEN
68          Assertions.assertInstanceOf(DetectorModifier.class, detector);
69          final DetectorModifier modifier = (DetectorModifier) detector;
70          Assertions.assertEquals(dateDetector, modifier.getDetector());
71      }
72  
73      @Test
74      void testMonitorDetectorHandlerEventOccurred() {
75          // GIVEN
76          final CountAndContinue counterHandler = new CountAndContinue();
77          final DateDetector dateDetector = new DateDetector().withHandler(counterHandler);
78          final EventsLogger eventsLogger = new EventsLogger();
79          final EventDetector detector = eventsLogger.monitorDetector(dateDetector);
80          final EventHandler handler = detector.getHandler();
81          final SpacecraftState mockedState = mock();
82          when(mockedState.getDate()).thenReturn(AbsoluteDate.ARBITRARY_EPOCH);
83          // WHEN
84          final Action action = handler.eventOccurred(mockedState, dateDetector, true);
85          // THEN
86          Assertions.assertEquals(Action.CONTINUE, action);
87          final List<EventsLogger.LoggedEvent> loggedEvents = eventsLogger.getLoggedEvents();
88          Assertions.assertEquals(loggedEvents.size(), counterHandler.getCount());
89          final EventsLogger.LoggedEvent event = loggedEvents.get(0);
90          Assertions.assertEquals(mockedState, event.getState());
91          Assertions.assertEquals(mockedState, event.getResetState());
92      }
93  
94      @ParameterizedTest
95      @EnumSource(Action.class)
96      void testMonitorDetectorHandlerResetState(final Action action) {
97          // GIVEN
98          final DateDetector dateDetector = new DateDetector().withHandler(new Handler(action));
99          final EventsLogger eventsLogger = new EventsLogger();
100         final EventDetector detector = eventsLogger.monitorDetector(dateDetector);
101         final EventHandler handler = detector.getHandler();
102         final SpacecraftState mockedState = mock();
103         when(mockedState.getDate()).thenReturn(AbsoluteDate.ARBITRARY_EPOCH);
104         // WHEN
105         final SpacecraftState state = handler.resetState(dateDetector, mockedState);
106         // THEN
107         if (action == Action.RESET_STATE) {
108             Assertions.assertNotEquals(mockedState, state);
109         } else {
110             Assertions.assertEquals(mockedState, state);
111         }
112     }
113 
114     private static class Handler implements EventHandler {
115 
116         private final Action action;
117 
118         Handler(final Action action) {
119             this.action = action;
120         }
121 
122         @Override
123         public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
124             return action;
125         }
126 
127         @Override
128         public SpacecraftState resetState(EventDetector detector, SpacecraftState oldState) {
129             if (action == Action.RESET_STATE) {
130                 return oldState.withMass(oldState.getMass());
131             }
132             return oldState;
133         }
134     }
135 
136     @Test
137     void testLogUmbra() {
138         EventsLogger logger = new EventsLogger();
139         EventDetector monitored = logger.monitorDetector(umbraDetector.withMaxIter(200));
140         Assertions.assertEquals(100, umbraDetector.getMaxIterationCount());
141         Assertions.assertEquals(200, monitored.getMaxIterationCount());
142         propagator.addEventDetector(monitored);
143         propagator.addEventDetector(penumbraDetector);
144         countAndContinue.reset();
145         propagator.propagate(iniDate.shiftedBy(16215)).getDate();
146         Assertions.assertEquals(11, countAndContinue.getCount());
147         checkCounts(logger, 3, 3, 0, 0);
148     }
149 
150     @Test
151     void testLogPenumbra() {
152         EventsLogger logger = new EventsLogger();
153         propagator.addEventDetector(umbraDetector);
154         propagator.addEventDetector(logger.monitorDetector(penumbraDetector));
155         countAndContinue.reset();
156         propagator.propagate(iniDate.shiftedBy(16215)).getDate();
157         Assertions.assertEquals(11, countAndContinue.getCount());
158         checkCounts(logger, 0, 0, 2, 3);
159     }
160 
161     @Test
162     void testLogAll() {
163         EventsLogger logger = new EventsLogger();
164         propagator.addEventDetector(logger.monitorDetector(umbraDetector));
165         propagator.addEventDetector(logger.monitorDetector(penumbraDetector));
166         countAndContinue.reset();
167         propagator.propagate(iniDate.shiftedBy(16215));
168         Assertions.assertEquals(11, countAndContinue.getCount());
169         checkCounts(logger, 3, 3, 2, 3);
170     }
171 
172     @Test
173     void testImmutableList() {
174         EventsLogger logger = new EventsLogger();
175         propagator.addEventDetector(logger.monitorDetector(umbraDetector));
176         propagator.addEventDetector(logger.monitorDetector(penumbraDetector));
177         countAndContinue.reset();
178         propagator.propagate(iniDate.shiftedBy(16215));
179         List<EventsLogger.LoggedEvent> firstList = logger.getLoggedEvents();
180         Assertions.assertEquals(11, firstList.size());
181         propagator.propagate(iniDate.shiftedBy(30000));
182         List<EventsLogger.LoggedEvent> secondList = logger.getLoggedEvents();
183         Assertions.assertEquals(11, firstList.size());
184         Assertions.assertEquals(20, secondList.size());
185         for (int i = 0; i < firstList.size(); ++i) {
186 
187             EventsLogger.LoggedEvent e1 = firstList.get(i);
188             EventsLogger.LoggedEvent e2 = secondList.get(i);
189             PVCoordinates pv1 = e1.getState().getPVCoordinates();
190             PVCoordinates pv2 = e2.getState().getPVCoordinates();
191 
192             Assertions.assertSame(e1.getEventDetector(), e2.getEventDetector());
193             Assertions.assertEquals(0, pv1.getPosition().subtract(pv2.getPosition()).getNorm(), 1.0e-10);
194             Assertions.assertEquals(0, pv1.getVelocity().subtract(pv2.getVelocity()).getNorm(), 1.0e-10);
195             Assertions.assertEquals(e1.isIncreasing(), e2.isIncreasing());
196 
197         }
198     }
199 
200     @Test
201     void testClearLog() {
202         EventsLogger logger = new EventsLogger();
203         propagator.addEventDetector(logger.monitorDetector(umbraDetector));
204         propagator.addEventDetector(logger.monitorDetector(penumbraDetector));
205         countAndContinue.reset();
206         propagator.propagate(iniDate.shiftedBy(16215));
207         List<EventsLogger.LoggedEvent> firstList = logger.getLoggedEvents();
208         Assertions.assertEquals(11, firstList.size());
209         logger.clearLoggedEvents();
210         propagator.propagate(iniDate.shiftedBy(30000));
211         List<EventsLogger.LoggedEvent> secondList = logger.getLoggedEvents();
212         Assertions.assertEquals(11, firstList.size());
213         Assertions.assertEquals( 9, secondList.size());
214     }
215 
216     private void checkCounts(EventsLogger logger,
217                              int expectedUmbraIncreasingCount, int expectedUmbraDecreasingCount,
218                              int expectedPenumbraIncreasingCount, int expectedPenumbraDecreasingCount) {
219         int umbraIncreasingCount = 0;
220         int umbraDecreasingCount = 0;
221         int penumbraIncreasingCount = 0;
222         int penumbraDecreasingCount = 0;
223         for (EventsLogger.LoggedEvent event : logger.getLoggedEvents()) {
224             final EclipseDetector eclipseDetector = (EclipseDetector) (event.getEventDetector());
225             if (eclipseDetector.getTotalEclipse()) {
226                 if (event.isIncreasing()) {
227                     ++umbraIncreasingCount;
228                 } else {
229                     ++umbraDecreasingCount;
230                 }
231             }
232             else {
233                 if (event.isIncreasing()) {
234                     ++penumbraIncreasingCount;
235                 } else {
236                     ++penumbraDecreasingCount;
237                 }
238             }
239         }
240         Assertions.assertEquals(expectedUmbraIncreasingCount,    umbraIncreasingCount);
241         Assertions.assertEquals(expectedUmbraDecreasingCount,    umbraDecreasingCount);
242         Assertions.assertEquals(expectedPenumbraIncreasingCount, penumbraIncreasingCount);
243         Assertions.assertEquals(expectedPenumbraDecreasingCount, penumbraDecreasingCount);
244     }
245 
246     private EclipseDetector buildDetector(final boolean totalEclipse) {
247 
248         EclipseDetector detector =
249                 new EclipseDetector(CelestialBodyFactory.getSun(), 696000000,
250                                     new OneAxisEllipsoid(6400000,
251                                                          0.0,
252                                                          FramesFactory.getITRF(IERSConventions.IERS_2010, true))).
253                 withMaxCheck(60.0).
254                 withThreshold(1.0e-3);
255 
256         if (totalEclipse) {
257             detector = detector.withUmbra();
258         } else {
259             detector = detector.withPenumbra();
260         }
261 
262         detector = detector.withHandler(countAndContinue);
263 
264         return detector;
265 
266     }
267 
268     @BeforeEach
269     public void setUp() {
270         try {
271             Utils.setDataRoot("regular-data");
272             mu  = 3.9860047e14;
273             final Vector3D position  = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
274             final Vector3D velocity  = new Vector3D(505.8479685, 942.7809215, 7435.922231);
275             iniDate = new AbsoluteDate(1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT());
276             final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
277                                                      FramesFactory.getEME2000(), iniDate, mu);
278             initialState = new SpacecraftState(orbit);
279             double[] absTolerance = {
280                 0.001, 1.0e-9, 1.0e-9, 1.0e-6, 1.0e-6, 1.0e-6, 0.001
281             };
282             double[] relTolerance = {
283                 1.0e-7, 1.0e-4, 1.0e-4, 1.0e-7, 1.0e-7, 1.0e-7, 1.0e-7
284             };
285             AdaptiveStepsizeIntegrator integrator =
286                 new DormandPrince853Integrator(0.001, 1000, absTolerance, relTolerance);
287             integrator.setInitialStepSize(60);
288             propagator = new NumericalPropagator(integrator);
289             propagator.setInitialState(initialState);
290             countAndContinue = new CountAndContinue();
291             umbraDetector = buildDetector(true);
292             penumbraDetector = buildDetector(false);
293         } catch (OrekitException oe) {
294             Assertions.fail(oe.getLocalizedMessage());
295         }
296     }
297 
298     @AfterEach
299     public void tearDown() {
300         iniDate = null;
301         initialState = null;
302         propagator = null;
303         countAndContinue = null;
304         umbraDetector = null;
305         penumbraDetector = null;
306     }
307 
308 }
309