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.propagation.events.intervals;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.Binary64;
21  import org.hipparchus.util.Binary64Field;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.Test;
25  import org.junit.jupiter.params.ParameterizedTest;
26  import org.junit.jupiter.params.provider.ValueSource;
27  import org.mockito.Mockito;
28  import org.orekit.frames.FramesFactory;
29  import org.orekit.orbits.CartesianOrbit;
30  import org.orekit.orbits.FieldCartesianOrbit;
31  import org.orekit.propagation.SpacecraftState;
32  import org.orekit.propagation.analytical.FieldKeplerianPropagator;
33  import org.orekit.propagation.analytical.KeplerianPropagator;
34  import org.orekit.propagation.events.DateDetector;
35  import org.orekit.propagation.events.FieldDateDetector;
36  import org.orekit.propagation.events.handlers.FieldRecordAndContinue;
37  import org.orekit.propagation.events.handlers.RecordAndContinue;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.FieldAbsoluteDate;
40  import org.orekit.time.FieldTimeStamped;
41  import org.orekit.time.TimeStamped;
42  import org.orekit.utils.PVCoordinates;
43  
44  class DateDetectionAdaptableIntervalFactoryTest {
45  
46      @ParameterizedTest
47      @ValueSource(booleans = {true, false})
48      void testGetSingleDateDetectionAdaptableInterval(final boolean isForward) {
49          // GIVEN
50          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
51          // WHEN
52          final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getSingleDateDetectionAdaptableInterval();
53          // THEN
54          final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, isForward);
55          Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
56      }
57  
58      @Test
59      void testGetDatesDetectionConstantIntervalEmpty() {
60          // GIVEN
61          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
62          // WHEN
63          final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionConstantInterval();
64          // THEN
65          final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, true);
66          Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
67      }
68  
69      @Test
70      void testGetDatesDetectionIntervalEmpty() {
71          // GIVEN
72          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
73          // WHEN
74          final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionInterval();
75          // THEN
76          final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, false);
77          Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
78      }
79  
80      @Test
81      void testGetDatesDetectionIntervalOne() {
82          // GIVEN
83          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
84          // WHEN
85          final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory
86                  .getDatesDetectionInterval(Mockito.mock(AbsoluteDate.class));
87          // THEN
88          final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, false);
89          Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
90      }
91  
92      @Test
93      void testGetDatesDetectionConstantInterval() {
94          // GIVEN
95          final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
96          final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
97          final AbsoluteDate otherDate = date.shiftedBy(3);
98          // WHEN
99          final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionConstantInterval(date, otherDate);
100         // THEN
101         final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, false);
102         Assertions.assertEquals(FastMath.abs(otherDate.durationFrom(date)) / 2, actualMaxCheck);
103     }
104 
105     @Test
106     void testGetDatesDetectionConstantIntervalSameDates() {
107         // GIVEN
108         final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
109         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
110         // WHEN
111         final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionConstantInterval(date, date);
112         // THEN
113         final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, false);
114         Assertions.assertNotEquals(0., actualMaxCheck);
115     }
116 
117     @ParameterizedTest
118     @ValueSource(booleans = {true, false})
119     void testGetSingleDateDetectionFieldAdaptableInterval(final boolean isForward) {
120         // GIVEN
121 
122         // WHEN
123         final FieldAdaptableInterval<?> adaptableInterval = DateDetectionAdaptableIntervalFactory.getSingleDateDetectionFieldAdaptableInterval();
124         // THEN
125         final double actualMaxCheck = adaptableInterval.currentInterval(null, isForward);
126         Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
127     }
128 
129     @Test
130     void testGetDatesDetectionFieldConstantIntervalEmpty() {
131         // GIVEN
132 
133         // WHEN
134         final FieldAdaptableInterval<?> adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionFieldConstantInterval();
135         // THEN
136         final double actualMaxCheck = adaptableInterval.currentInterval(null, true);
137         Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
138     }
139 
140     @Test
141     @SuppressWarnings("unchecked")
142     void testGetDatesDetectionFieldConstantIntervalOne() {
143         // GIVEN
144 
145         // WHEN
146         final FieldAdaptableInterval<?> adaptableInterval = DateDetectionAdaptableIntervalFactory
147                 .getDatesDetectionFieldConstantInterval(Mockito.mock(FieldAbsoluteDate.class));
148         // THEN
149         final double actualMaxCheck = adaptableInterval.currentInterval(null, true);
150         Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
151     }
152 
153     @Test
154     void testGetDatesDetectionFieldConstantInterval() {
155         // GIVEN
156         final Binary64Field field = Binary64Field.getInstance();
157         final FieldAbsoluteDate<Binary64> date = FieldAbsoluteDate.getArbitraryEpoch(field);
158         final FieldAbsoluteDate<Binary64> otherDate = date.shiftedBy(3);
159         // WHEN
160         final FieldAdaptableInterval<Binary64> adaptableInterval = DateDetectionAdaptableIntervalFactory
161                 .getDatesDetectionFieldConstantInterval(date, otherDate);
162         // THEN
163         final double actualMaxCheck = adaptableInterval.currentInterval(null, false);
164         Assertions.assertEquals(FastMath.abs(otherDate.durationFrom(date)).getReal() / 2, actualMaxCheck);
165     }
166 
167     @Test
168     void testGetDatesDetectionIntervalSameDates() {
169         // GIVEN
170         final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class);
171         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
172         Mockito.when(mockedState.getDate()).thenReturn(date);
173         // WHEN
174         final AdaptableInterval adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionInterval(date, date);
175         // THEN
176         final double actualMaxCheck = adaptableInterval.currentInterval(mockedState, false);
177         Assertions.assertNotEquals(0., actualMaxCheck);
178     }
179 
180     @ParameterizedTest
181     @ValueSource(doubles = {-100, 100, 1000})
182     void testGetDatesDetectionInterval(final double initialStep) {
183         // GIVEN
184         final KeplerianPropagator propagator = getKeplerianPropagator();
185         final DateDetector detector = new DateDetector();
186         final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH;
187         final int size = 10;
188         for (int i = 1; i < size + 1; i++) {
189             detector.addEventDate(date.shiftedBy(initialStep * i));
190         }
191         final RecordAndContinue recordAndContinue = new RecordAndContinue();
192         final AdaptableInterval interval = DateDetectionAdaptableIntervalFactory
193                 .getDatesDetectionInterval(detector.getDates().stream().map(TimeStamped.class::cast).toArray(TimeStamped[]::new));
194         // WHEN
195         propagator.addEventDetector(detector.withMaxCheck(interval).withHandler(recordAndContinue));
196         propagator.propagate(propagator.getInitialState().getDate().shiftedBy(initialStep * (size + 1)));
197         // THEN
198         Assertions.assertEquals(size, recordAndContinue.getEvents().size());
199     }
200 
201     private static KeplerianPropagator getKeplerianPropagator() {
202         return new KeplerianPropagator(getOrbit());
203     }
204 
205     private static CartesianOrbit getOrbit() {
206         return new CartesianOrbit(new PVCoordinates(Vector3D.PLUS_I, Vector3D.PLUS_J),
207                 FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.);
208     }
209 
210     @Test
211     void testGetDatesDetectionFieldIntervalEmpty() {
212         // GIVEN
213 
214         // WHEN
215         final FieldAdaptableInterval<?> adaptableInterval = DateDetectionAdaptableIntervalFactory.getDatesDetectionFieldInterval();
216         // THEN
217         final double actualMaxCheck = adaptableInterval.currentInterval(null, true);
218         Assertions.assertEquals(DateDetectionAdaptableIntervalFactory.DEFAULT_MAX_CHECK, actualMaxCheck);
219     }
220 
221     @ParameterizedTest
222     @SuppressWarnings("unchecked")
223     @ValueSource(doubles = {-100, 100, 1000})
224     void testGetDatesDetectionFieldInterval(final double initialStep) {
225         final FieldKeplerianPropagator<Binary64> propagator = getFieldKeplerianPropagator();
226         final Binary64Field field = Binary64Field.getInstance();
227         final FieldDateDetector<Binary64> fieldDateDetector = new FieldDateDetector<>(field);
228         final FieldAbsoluteDate<Binary64> date = FieldAbsoluteDate.getArbitraryEpoch(field);
229         final int size = 10;
230         for (int i = 1; i < size + 1; i++) {
231             fieldDateDetector.addEventDate(date.shiftedBy(initialStep * i));
232         }
233         final FieldRecordAndContinue<Binary64> recordAndContinue = new FieldRecordAndContinue<>();
234         final FieldAdaptableInterval<Binary64> interval = DateDetectionAdaptableIntervalFactory
235                 .getDatesDetectionFieldInterval(fieldDateDetector.getDates().stream().map(FieldTimeStamped.class::cast).toArray(FieldTimeStamped[]::new));
236         propagator.addEventDetector(fieldDateDetector.withMaxCheck(interval).withHandler(recordAndContinue));
237         propagator.propagate(propagator.getInitialState().getDate().shiftedBy(initialStep * (size + 1)));
238         Assertions.assertEquals(size, recordAndContinue.getEvents().size());
239     }
240 
241     private static FieldKeplerianPropagator<Binary64> getFieldKeplerianPropagator() {
242         return new FieldKeplerianPropagator<>(new FieldCartesianOrbit<>(Binary64Field.getInstance(), getOrbit()));
243     }
244 }