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