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.util.FastMath;
21  import org.hipparchus.util.MathUtils;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.BeforeEach;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.Utils;
26  import org.orekit.frames.FramesFactory;
27  import org.orekit.orbits.CartesianOrbit;
28  import org.orekit.orbits.KeplerianOrbit;
29  import org.orekit.orbits.Orbit;
30  import org.orekit.orbits.OrbitType;
31  import org.orekit.propagation.Propagator;
32  import org.orekit.propagation.SpacecraftState;
33  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
34  import org.orekit.propagation.events.EventsLogger.LoggedEvent;
35  import org.orekit.propagation.events.handlers.ContinueOnEvent;
36  import org.orekit.propagation.events.intervals.AdaptableInterval;
37  import org.orekit.propagation.events.intervals.ApsideDetectionAdaptableIntervalFactory;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.TimeScale;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.Constants;
42  import org.orekit.utils.PVCoordinates;
43  
44  class ApsideDetectorTest {
45  
46      private Propagator propagator;
47  
48      @Test
49      void testSimple() {
50          EventDetector detector = new ApsideDetector(propagator.getInitialState().getOrbit()).
51                                   withMaxCheck(600.0).
52                                   withThreshold(1.0e-12).
53                                   withHandler(new ContinueOnEvent());
54  
55          Assertions.assertEquals(600.0, detector.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
56          Assertions.assertEquals(1.0e-12, detector.getThreshold(), 1.0e-15);
57          Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, detector.getMaxIterationCount());
58  
59  
60          EventsLogger logger = new EventsLogger();
61          propagator.addEventDetector(logger.monitorDetector(detector));
62  
63          propagator.propagate(propagator.getInitialState().getOrbit().getDate().shiftedBy(Constants.JULIAN_DAY));
64  
65          Assertions.assertEquals(30, logger.getLoggedEvents().size());
66          for (LoggedEvent e : logger.getLoggedEvents()) {
67              KeplerianOrbit o = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(e.getState().getOrbit());
68              double expected = e.isIncreasing() ? 0.0 : FastMath.PI;
69              Assertions.assertEquals(expected, MathUtils.normalizeAngle(o.getMeanAnomaly(), expected), 4.0e-14);
70          }
71  
72      }
73  
74      @Test
75      void testFixedMaxCheck() {
76          doTestMaxcheck(AdaptableInterval.of(20.0), 4718);
77      }
78  
79      @Test
80      void testAnomalyAwareMaxCheck() {
81          doTestMaxcheck(ApsideDetectionAdaptableIntervalFactory.getApsideDetectionAdaptableInterval(), 663);
82      }
83  
84      private void doTestMaxcheck(final AdaptableInterval maxCheck, int expectedCalls) {
85          CountingApsideDetectorModifier detector = new CountingApsideDetectorModifier(maxCheck);
86          EventsLogger logger = new EventsLogger();
87          propagator.addEventDetector(logger.monitorDetector(detector));
88          propagator.propagate(propagator.getInitialState().getOrbit().getDate().shiftedBy(Constants.JULIAN_DAY));
89          Assertions.assertEquals(30, logger.getLoggedEvents().size());
90          Assertions.assertEquals(expectedCalls, detector.count);
91      }
92  
93      @BeforeEach
94      public void setUp() {
95          Utils.setDataRoot("regular-data");
96          final TimeScale utc = TimeScalesFactory.getUTC();
97          final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
98          final Vector3D velocity = new Vector3D(506.0, 943.0, 7450);
99          final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
100         final Orbit orbit = new CartesianOrbit(new PVCoordinates(position,  velocity),
101                                                FramesFactory.getEME2000(), date,
102                                                Constants.EIGEN5C_EARTH_MU);
103 
104         propagator =
105             new EcksteinHechlerPropagator(orbit,
106                                           Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
107                                           Constants.EIGEN5C_EARTH_MU,
108                                           Constants.EIGEN5C_EARTH_C20,
109                                           Constants.EIGEN5C_EARTH_C30,
110                                           Constants.EIGEN5C_EARTH_C40,
111                                           Constants.EIGEN5C_EARTH_C50,
112                                           Constants.EIGEN5C_EARTH_C60);
113     }
114 
115     private class CountingApsideDetectorModifier implements DetectorModifier {
116 
117         private final ApsideDetector detector;
118         private int count;
119         
120         public CountingApsideDetectorModifier(final AdaptableInterval maxCheck) {
121             this.detector = new ApsideDetector(propagator.getInitialState().getOrbit()).
122                   withMaxCheck(maxCheck).
123                   withThreshold(1.0e-12).
124                   withHandler(new ContinueOnEvent());
125         }
126 
127         @Override
128         public ApsideDetector getDetector() {
129             return detector;
130         }
131 
132         @Override
133         public void init(final SpacecraftState s0, final AbsoluteDate t) {
134             DetectorModifier.super.init(s0, t);
135             count = 0;
136         }
137 
138         @Override
139         public double g(final SpacecraftState s) {
140             ++count;
141             return DetectorModifier.super.g(s);
142         }
143 
144     }
145 
146 }
147