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.Binary64;
21  import org.hipparchus.util.Binary64Field;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.Utils;
27  import org.orekit.bodies.OneAxisEllipsoid;
28  import org.orekit.frames.FramesFactory;
29  import org.orekit.orbits.FieldEquinoctialOrbit;
30  import org.orekit.orbits.FieldOrbit;
31  import org.orekit.propagation.FieldPropagator;
32  import org.orekit.propagation.FieldSpacecraftState;
33  import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator;
34  import org.orekit.propagation.events.FieldEventsLogger.FieldLoggedEvent;
35  import org.orekit.propagation.events.handlers.FieldContinueOnEvent;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.time.FieldAbsoluteDate;
38  import org.orekit.time.TimeScale;
39  import org.orekit.time.TimeScalesFactory;
40  import org.orekit.utils.Constants;
41  import org.orekit.utils.FieldPVCoordinates;
42  import org.orekit.utils.IERSConventions;
43  import org.orekit.utils.PVCoordinates;
44  
45  /** Unit tests for {@link FieldLatitudeCrossingDetector}. */
46  public class FieldLatitudeCrossingDetectorTest {
47  
48      /** Arbitrary Field. */
49      private static final Binary64Field field = Binary64Field.getInstance();
50  
51      @Test
52      public void testRegularCrossing() {
53  
54          final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
55                                                              Constants.WGS84_EARTH_FLATTENING,
56                                                              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
57  
58          FieldLatitudeCrossingDetector<Binary64> d =
59                  new FieldLatitudeCrossingDetector<>(v(60.0), v(1.e-6), earth, FastMath.toRadians(60.0)).
60                  withHandler(new FieldContinueOnEvent<>());
61  
62          Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
63          Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15);
64          Assertions.assertEquals(60.0, FastMath.toDegrees(d.getLatitude()), 1.0e-14);
65          Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
66  
67          final TimeScale utc = TimeScalesFactory.getUTC();
68          final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
69          final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
70          final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc);
71          final FieldOrbit<Binary64> orbit = new FieldEquinoctialOrbit<>(
72                  new FieldPVCoordinates<>(v(1), new PVCoordinates(position,  velocity)),
73                  FramesFactory.getEME2000(), date,
74                  v(Constants.EIGEN5C_EARTH_MU));
75  
76          FieldPropagator<Binary64> propagator =
77              new FieldEcksteinHechlerPropagator<>(orbit,
78                                            Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
79                                            v(Constants.EIGEN5C_EARTH_MU),
80                                            Constants.EIGEN5C_EARTH_C20,
81                                            Constants.EIGEN5C_EARTH_C30,
82                                            Constants.EIGEN5C_EARTH_C40,
83                                            Constants.EIGEN5C_EARTH_C50,
84                                            Constants.EIGEN5C_EARTH_C60);
85  
86          FieldEventsLogger<Binary64> logger = new FieldEventsLogger<>();
87          propagator.addEventDetector(logger.monitorDetector(d));
88  
89          propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY));
90          AbsoluteDate previous = null;
91          for (FieldLoggedEvent<Binary64> e : logger.getLoggedEvents()) {
92              FieldSpacecraftState<Binary64> state = e.getState();
93              double latitude = earth.transform(state.getPosition(earth.getBodyFrame()),
94                                                earth.getBodyFrame(), date).getLatitude().getReal();
95              Assertions.assertEquals(60.0, FastMath.toDegrees(latitude), 3.0e-10);
96              if (previous != null) {
97                  if (e.isIncreasing()) {
98                      // crossing northward
99                      Assertions.assertTrue(state.getPVCoordinates().getVelocity().getZ().getReal() > 3611.0);
100                     Assertions.assertEquals(4954.70, state.getDate().durationFrom(previous).getReal(), 0.01);
101                 } else {
102                     // crossing southward
103                     Assertions.assertTrue(state.getPVCoordinates().getVelocity().getZ().getReal() < -3615.0);
104                     Assertions.assertEquals(956.17, state.getDate().durationFrom(previous).getReal(), 0.01);
105                 }
106             }
107             previous = state.getDate().toAbsoluteDate();
108         }
109         Assertions.assertEquals(30, logger.getLoggedEvents().size());
110 
111     }
112 
113     @Test
114     public void testNoCrossing() {
115 
116         final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
117                                                             Constants.WGS84_EARTH_FLATTENING,
118                                                             FramesFactory.getITRF(IERSConventions.IERS_2010, true));
119 
120         FieldLatitudeCrossingDetector<Binary64> d =
121                 new FieldLatitudeCrossingDetector<>(v(10.0), v(1.e-6), earth, FastMath.toRadians(82.0)).
122                 withHandler(new FieldContinueOnEvent<>());
123 
124         Assertions.assertEquals(10.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
125         Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15);
126         Assertions.assertEquals(82.0, FastMath.toDegrees(d.getLatitude()), 1.0e-14);
127         Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
128 
129         final TimeScale utc = TimeScalesFactory.getUTC();
130         final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
131         final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
132         final FieldAbsoluteDate<Binary64> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc);
133         final FieldOrbit<Binary64> orbit = new FieldEquinoctialOrbit<>(
134                 new FieldPVCoordinates<>(v(1), new PVCoordinates(position,  velocity)),
135                 FramesFactory.getEME2000(), date,
136                 v(Constants.EIGEN5C_EARTH_MU));
137 
138         FieldPropagator<Binary64> propagator =
139             new FieldEcksteinHechlerPropagator<>(orbit,
140                                           Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
141                                           v(Constants.EIGEN5C_EARTH_MU),
142                                           Constants.EIGEN5C_EARTH_C20,
143                                           Constants.EIGEN5C_EARTH_C30,
144                                           Constants.EIGEN5C_EARTH_C40,
145                                           Constants.EIGEN5C_EARTH_C50,
146                                           Constants.EIGEN5C_EARTH_C60);
147 
148         FieldEventsLogger<Binary64> logger = new FieldEventsLogger<Binary64>();
149         propagator.addEventDetector(logger.monitorDetector(d));
150 
151         propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY));
152         Assertions.assertEquals(0, logger.getLoggedEvents().size());
153 
154     }
155 
156     /**
157      * Convert double to field value.
158      *
159      * @param value to box.
160      * @return boxed value.
161      */
162     private static Binary64 v(double value) {
163         return new Binary64(value);
164     }
165 
166     @BeforeEach
167     public void setUp() {
168         Utils.setDataRoot("regular-data");
169     }
170 
171 }
172