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  
18  package org.orekit.propagation.events;
19  
20  import static org.orekit.orbits.PositionAngleType.MEAN;
21  
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.util.Binary64;
24  import org.hipparchus.util.Binary64Field;
25  import org.hipparchus.util.FastMath;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.BeforeEach;
28  import org.junit.jupiter.api.Test;
29  import org.mockito.Mockito;
30  import org.orekit.Utils;
31  import org.orekit.bodies.FieldGeodeticPoint;
32  import org.orekit.bodies.OneAxisEllipsoid;
33  import org.orekit.frames.FramesFactory;
34  import org.orekit.orbits.FieldEquinoctialOrbit;
35  import org.orekit.orbits.FieldKeplerianOrbit;
36  import org.orekit.orbits.FieldOrbit;
37  import org.orekit.propagation.FieldPropagator;
38  import org.orekit.propagation.FieldSpacecraftState;
39  import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator;
40  import org.orekit.propagation.analytical.FieldKeplerianPropagator;
41  import org.orekit.propagation.events.handlers.FieldContinueOnEvent;
42  import org.orekit.time.FieldAbsoluteDate;
43  import org.orekit.time.TimeScale;
44  import org.orekit.time.TimeScalesFactory;
45  import org.orekit.utils.Constants;
46  import org.orekit.utils.FieldPVCoordinates;
47  import org.orekit.utils.IERSConventions;
48  import org.orekit.utils.PVCoordinates;
49  
50  /** Unit tests for {@link FieldLongitudeCrossingDetector}. */
51  public class FieldLongitudeCrossingDetectorTest {
52  
53      /**
54       * Arbitrary Field.
55       */
56      private static final Binary64Field field = Binary64Field.getInstance();
57  
58      @Test
59      void testConstructor() {
60          // GIVEN
61          final OneAxisEllipsoid ellipsoid = Mockito.mock(OneAxisEllipsoid.class);
62          // WHEN
63          final FieldLongitudeCrossingDetector<Binary64> detector = new FieldLongitudeCrossingDetector<>(field, ellipsoid, 1.);
64          // THEN
65          final FieldEventDetectionSettings<Binary64> detectionSettings = detector.getDetectionSettings();
66          Assertions.assertEquals(EventDetectionSettings.DEFAULT_MAX_ITER, detectionSettings.getMaxIterationCount());
67          Assertions.assertEquals(EventDetectionSettings.DEFAULT_THRESHOLD, detectionSettings.getThreshold().getReal());
68      }
69  
70      @Test
71      public void testRegularCrossing() {
72  
73          final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
74              Constants.WGS84_EARTH_FLATTENING,
75              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
76  
77          FieldLongitudeCrossingDetector<Binary64> d =
78              new FieldLongitudeCrossingDetector<>(v(60.0), v(1.e-6), earth,
79                  FastMath.toRadians(10.0)).
80                  withHandler(new FieldContinueOnEvent<>());
81  
82          Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
83          Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15);
84          Assertions.assertEquals(10.0, FastMath.toDegrees(d.getLongitude()), 1.0e-14);
85          Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
86          Assertions.assertSame(earth, d.getBody());
87  
88          final TimeScale utc = TimeScalesFactory.getUTC();
89          final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
90          final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
91          final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc);
92          final FieldOrbit<Binary64> orbit = new FieldEquinoctialOrbit<>(
93              new FieldPVCoordinates<>(v(1), new PVCoordinates(position, velocity)),
94              FramesFactory.getEME2000(), date,
95              v(Constants.EIGEN5C_EARTH_MU));
96  
97          FieldPropagator<Binary64> propagator =
98              new FieldEcksteinHechlerPropagator<>(orbit,
99                  Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
100                 v(Constants.EIGEN5C_EARTH_MU),
101                 Constants.EIGEN5C_EARTH_C20,
102                 Constants.EIGEN5C_EARTH_C30,
103                 Constants.EIGEN5C_EARTH_C40,
104                 Constants.EIGEN5C_EARTH_C50,
105                 Constants.EIGEN5C_EARTH_C60);
106 
107         FieldEventsLogger<Binary64> logger = new FieldEventsLogger<>();
108         propagator.addEventDetector(logger.monitorDetector(d));
109 
110         propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY));
111         FieldAbsoluteDate<Binary64> previous = null;
112         for (FieldEventsLogger.FieldLoggedEvent<Binary64> e : logger.getLoggedEvents()) {
113             FieldSpacecraftState<Binary64> state = e.getState();
114             double longitude = earth.transform(state.getPosition(earth.getBodyFrame()),
115                 earth.getBodyFrame(), date).getLongitude().getReal();
116             Assertions.assertEquals(10.0, FastMath.toDegrees(longitude), 3.5e-7);
117             if (previous != null) {
118                 // same time interval regardless of increasing/decreasing,
119                 // as increasing/decreasing flag is irrelevant for this detector
120                 Assertions.assertEquals(4954.70, state.getDate().durationFrom(previous).getReal(), 1e10);
121             }
122             previous = state.getDate();
123         }
124         Assertions.assertEquals(16, logger.getLoggedEvents().size());
125 
126     }
127 
128     @Test
129     public void testZigZag() {
130 
131         final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
132             Constants.WGS84_EARTH_FLATTENING,
133             FramesFactory.getITRF(IERSConventions.IERS_2010, true));
134 
135         FieldLongitudeCrossingDetector<Binary64> d =
136             new FieldLongitudeCrossingDetector<>(v(600.0), v(1.e-6), earth,
137                 FastMath.toRadians(-100.0)).
138                 withHandler(new FieldContinueOnEvent<>());
139 
140         Assertions.assertEquals(600.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
141         Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15);
142         Assertions.assertEquals(-100.0, FastMath.toDegrees(d.getLongitude()), 1.0e-14);
143         Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
144 
145         FieldAbsoluteDate<Binary64> date = FieldAbsoluteDate.getJ2000Epoch(field);
146         final FieldOrbit<Binary64> orbit =
147             new FieldKeplerianOrbit<>(v(24464560.0), v(0.7311), v(0.122138), v(3.10686), v(1.00681),
148                 v(0.048363), MEAN,
149                 FramesFactory.getEME2000(),
150                 date,
151                 v(Constants.EIGEN5C_EARTH_MU));
152 
153 
154         FieldPropagator<Binary64> propagator = new FieldKeplerianPropagator<>(orbit);
155 
156         FieldEventsLogger<Binary64> logger = new FieldEventsLogger<>();
157         propagator.addEventDetector(logger.monitorDetector(d));
158 
159         propagator.propagate(orbit.getDate().shiftedBy(Constants.JULIAN_DAY));
160         double[] expectedLatitudes = new double[] { -6.5394381901, -0.4918760372, +6.5916016832 };
161         Assertions.assertEquals(3, logger.getLoggedEvents().size());
162         for (int i = 0; i < 3; ++i) {
163             FieldSpacecraftState<Binary64> state = logger.getLoggedEvents().get(i).getState();
164             FieldGeodeticPoint<Binary64> gp = earth.transform(state.getPosition(earth.getBodyFrame()),
165                 earth.getBodyFrame(), date);
166             Assertions.assertEquals(expectedLatitudes[i], FastMath.toDegrees(gp.getLatitude()).getReal(),  1.0e-10);
167             Assertions.assertEquals(-100.0, FastMath.toDegrees(gp.getLongitude().getReal()), 1.2e-9);
168         }
169 
170     }
171 
172     /**
173      * Convert double to field value.
174      *
175      * @param value to box.
176      * @return boxed value.
177      */
178     private static Binary64 v(double value) {
179         return new Binary64(value);
180     }
181 
182     @BeforeEach
183     public void setUp() {
184         Utils.setDataRoot("regular-data");
185     }
186 
187 }