1   /* Copyright 2002-2025 Joseph Reed
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    * Joseph Reed 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 static org.junit.jupiter.api.Assertions.assertEquals;
20  
21  import org.hamcrest.MatcherAssert;
22  import org.hamcrest.Matchers;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
25  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
26  import org.hipparchus.util.FastMath;
27  import org.hipparchus.util.MathUtils;
28  import org.junit.jupiter.api.BeforeEach;
29  import org.junit.jupiter.api.Test;
30  import org.orekit.Utils;
31  import org.orekit.bodies.CelestialBodyFactory;
32  import org.orekit.frames.FramesFactory;
33  import org.orekit.orbits.EquinoctialOrbit;
34  import org.orekit.orbits.Orbit;
35  import org.orekit.propagation.Propagator;
36  import org.orekit.propagation.SpacecraftState;
37  import org.orekit.propagation.events.handlers.RecordAndContinue;
38  import org.orekit.propagation.numerical.NumericalPropagator;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.PVCoordinates;
42  
43  public class BetaAngleDetectorTest {
44      private Propagator propagator;
45      private AbsoluteDate date;
46  
47      @BeforeEach
48      void setup() {
49          Utils.setDataRoot("regular-data");
50          final Vector3D position  = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
51          final Vector3D velocity  = new Vector3D(505.8479685, 942.7809215, 7435.922231);
52          final AbsoluteDate iniDate = new AbsoluteDate(1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT());
53          final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
54                                                      FramesFactory.getGCRF(), iniDate, 3.9860047e14);
55          final SpacecraftState initialState = new SpacecraftState(orbit);
56          double[] absTolerance = {
57              0.001, 1.0e-9, 1.0e-9, 1.0e-6, 1.0e-6, 1.0e-6, 0.001
58          };
59          double[] relTolerance = {
60              1.0e-7, 1.0e-4, 1.0e-4, 1.0e-7, 1.0e-7, 1.0e-7, 1.0e-7
61          };
62          AdaptiveStepsizeIntegrator integrator =
63              new DormandPrince853Integrator(0.001, 1000, absTolerance, relTolerance);
64          integrator.setInitialStepSize(60);
65          propagator = new NumericalPropagator(integrator);
66          ((NumericalPropagator) propagator).setInitialState(initialState);
67          date = iniDate;
68      }
69  
70      @Test
71      void evaluate() {
72          final BetaAngleDetector detector = new BetaAngleDetector(0);
73          
74          AbsoluteDate d = date;
75          for (int i = 0; i < 50; i++) {
76              final SpacecraftState state = propagator.propagate(d);
77              final double g = detector.g(state);
78              final double beta = BetaAngleDetector.calculateBetaAngle(
79                      state, CelestialBodyFactory.getSun(), FramesFactory.getGCRF());
80  
81              final double expectedBeta = MathUtils.SEMI_PI - Vector3D.angle(
82                      state.getPVCoordinates(FramesFactory.getGCRF()).getMomentum().normalize(),
83                      CelestialBodyFactory.getSun().getPosition(state.getDate(), FramesFactory.getGCRF()).normalize());
84              assertEquals(-beta, g, 1e-9);
85              assertEquals(expectedBeta, beta, 1e-9);
86  
87              d = d.shiftedBy(86400);
88          }
89      }
90  
91      @Test
92      void simpleStop() {
93          final BetaAngleDetector detector = new BetaAngleDetector(0,
94                  CelestialBodyFactory.getSun(),
95                  FramesFactory.getGCRF());
96          
97          propagator.addEventDetector(detector);
98          
99          final SpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400));
100         // expected value was 1883928.588393031 before fixing CelestialBody time
101         MatcherAssert.assertThat(state.getDate().durationFrom(date),
102                 Matchers.closeTo(1883928.588771722, 1e-9));
103 
104         assertEquals(0, BetaAngleDetector.calculateBetaAngle(state, propagator), 1e-9);
105     }
106 
107     @Test
108     void record() {
109         final RecordAndContinue handler = new RecordAndContinue();
110         final BetaAngleDetector detector = new BetaAngleDetector(0,
111                 CelestialBodyFactory.getMoon(),
112                 FramesFactory.getGCRF())
113             .withBetaThreshold(FastMath.toRadians(1))
114             .withCelestialProvider(CelestialBodyFactory.getSun())
115             .withInertialFrame(FramesFactory.getEME2000())
116             .withHandler(handler);
117         
118         propagator.addEventDetector(detector);
119         
120         final SpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400));
121         assertEquals(30 * 86400, state.getDate().durationFrom(date), 1e-9);
122         assertEquals(1, handler.getEvents().size());
123     }
124 }