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