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.FieldVector3D;
22  import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator;
23  import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
24  import org.hipparchus.util.Binary64;
25  import org.hipparchus.util.Binary64Field;
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.FieldEquinoctialOrbit;
34  import org.orekit.orbits.FieldOrbit;
35  import org.orekit.propagation.FieldPropagator;
36  import org.orekit.propagation.FieldSpacecraftState;
37  import org.orekit.propagation.events.handlers.FieldRecordAndContinue;
38  import org.orekit.propagation.numerical.FieldNumericalPropagator;
39  import org.orekit.time.FieldAbsoluteDate;
40  import org.orekit.time.TimeScalesFactory;
41  import org.orekit.utils.FieldPVCoordinates;
42  import org.orekit.utils.FieldPVCoordinatesProvider;
43  
44  public class FieldBetaAngleDetectorTest {
45      private FieldPropagator<Binary64> propagator;
46      private FieldAbsoluteDate<Binary64> date;
47  
48      @BeforeEach
49      void setup() {
50          Utils.setDataRoot("regular-data");
51          final FieldVector3D<Binary64> position  = new FieldVector3D<>(
52              new Binary64(-6142438.668), new Binary64(3492467.560), new Binary64(-25767.25680));
53          final FieldVector3D<Binary64> velocity  = new FieldVector3D<Binary64>(
54              new Binary64(505.8479685), new Binary64(942.7809215), new Binary64(7435.922231));
55          final FieldAbsoluteDate<Binary64> iniDate = new FieldAbsoluteDate<>(Binary64Field.getInstance(), 1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT());
56          final FieldOrbit<Binary64> orbit = 
57                  new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(position,  velocity),
58                                                      FramesFactory.getGCRF(), iniDate, new Binary64(3.9860047e14));
59          final FieldSpacecraftState<Binary64> initialState = new FieldSpacecraftState<>(orbit);
60          double[] absTolerance = {
61              0.001, 1.0e-9, 1.0e-9, 1.0e-6, 1.0e-6, 1.0e-6, 0.001
62          };
63          double[] relTolerance = {
64              1.0e-7, 1.0e-4, 1.0e-4, 1.0e-7, 1.0e-7, 1.0e-7, 1.0e-7
65          };
66          AdaptiveStepsizeFieldIntegrator<Binary64> integrator =
67              new DormandPrince853FieldIntegrator<>(Binary64Field.getInstance(), 0.001, 1000, absTolerance, relTolerance);
68          integrator.setInitialStepSize(60);
69          propagator = new FieldNumericalPropagator<>(Binary64Field.getInstance(), integrator);
70          ((FieldNumericalPropagator<Binary64>) propagator).setInitialState(initialState);
71          date = iniDate;
72      }
73  
74      @Test
75      void evaluate() {
76          final FieldPVCoordinatesProvider<Binary64> sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance());
77          final FieldBetaAngleDetector<Binary64> detector = new FieldBetaAngleDetector<>(Binary64.ZERO);
78          
79          FieldAbsoluteDate<Binary64> d = date;
80          for (int i = 0; i < 50; i++) {
81              final FieldSpacecraftState<Binary64> state = propagator.propagate(d);
82              final Binary64 g = detector.g(state);
83              final Binary64 beta = FieldBetaAngleDetector.calculateBetaAngle(
84                      state, sun, FramesFactory.getGCRF());
85              
86              final Binary64 expectedBeta = FieldVector3D.angle(
87                      state.getPVCoordinates(FramesFactory.getGCRF()).getMomentum().normalize(),
88                      sun.getPosition(state.getDate(), FramesFactory.getGCRF()).normalize()).negate().add(MathUtils.SEMI_PI);
89              assertEquals(beta.negate(), g);
90              assertEquals(expectedBeta.getReal(), beta.getReal(), 1e-9);
91              d = d.shiftedBy(86400);
92          }
93      }
94  
95      @Test
96      void simpleStop() {
97          final FieldPVCoordinatesProvider<Binary64> sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance());
98          final FieldBetaAngleDetector<Binary64> detector = new FieldBetaAngleDetector<>(
99                  Binary64Field.getInstance(),
100                 Binary64.ZERO,
101                 sun,
102                 FramesFactory.getGCRF());
103         
104         propagator.addEventDetector(detector);
105         
106         final FieldSpacecraftState<Binary64> state = propagator.propagate(date, date.shiftedBy(30 * 86400));
107         assertEquals(1883928.588393031, state.getDate().durationFrom(date).getReal(), 1e-9);
108 
109         assertEquals(0, FieldBetaAngleDetector.calculateBetaAngle(state, sun).getReal(), 1e-9);
110     }
111 
112     @Test
113     void record() {
114         final FieldPVCoordinatesProvider<Binary64> sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance());
115         final FieldRecordAndContinue<Binary64> handler = new FieldRecordAndContinue<>();
116         final FieldBetaAngleDetector<Binary64> detector = new FieldBetaAngleDetector<>(
117                 Binary64Field.getInstance(),
118                 Binary64.ZERO,
119                 CelestialBodyFactory.getMoon().toFieldPVCoordinatesProvider(Binary64Field.getInstance()),
120                 FramesFactory.getGCRF())
121             .withBetaThreshold(FastMath.toRadians(Binary64.ONE))
122             .withCelestialProvider(sun)
123             .withInertialFrame(FramesFactory.getEME2000())
124             .withHandler(handler);
125         
126         propagator.addEventDetector(detector);
127         
128         final FieldSpacecraftState<Binary64> state = propagator.propagate(date, date.shiftedBy(30 * 86400));
129         assertEquals(30 * 86400, state.getDate().durationFrom(date).getReal(), 1e-9);
130         assertEquals(1, handler.getEvents().size());
131     }
132 }