1   /* Copyright 2023-2025 Alberto Ferrero
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    * Alberto Ferrero 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.orekit.orbits.PositionAngleType.MEAN;
20  
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
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.GeodeticPoint;
28  import org.orekit.bodies.OneAxisEllipsoid;
29  import org.orekit.frames.FramesFactory;
30  import org.orekit.orbits.EquinoctialOrbit;
31  import org.orekit.orbits.KeplerianOrbit;
32  import org.orekit.orbits.Orbit;
33  import org.orekit.propagation.Propagator;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
36  import org.orekit.propagation.analytical.KeplerianPropagator;
37  import org.orekit.propagation.events.EventsLogger.LoggedEvent;
38  import org.orekit.propagation.events.handlers.ContinueOnEvent;
39  import org.orekit.time.AbsoluteDate;
40  import org.orekit.time.TimeScale;
41  import org.orekit.time.TimeScalesFactory;
42  import org.orekit.utils.Constants;
43  import org.orekit.utils.IERSConventions;
44  import org.orekit.utils.PVCoordinates;
45  
46  public class LongitudeRangeCrossingDetectorTest {
47  
48      @Test
49      public void testRegularCrossing() {
50  
51          final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
52              Constants.WGS84_EARTH_FLATTENING,
53              FramesFactory.getITRF(IERSConventions.IERS_2010, true));
54  
55          LongitudeRangeCrossingDetector d =
56              new LongitudeRangeCrossingDetector(earth,
57                  FastMath.toRadians(10.0),
58                  FastMath.toRadians(18.0)).
59                  withMaxCheck(60).
60                  withThreshold(1.e-6).
61                  withHandler(new ContinueOnEvent());
62  
63          Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
64          Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15);
65          Assertions.assertEquals(10.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-14);
66          Assertions.assertEquals(18.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-14);
67          Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
68          Assertions.assertSame(earth, d.getBody());
69  
70          final TimeScale utc = TimeScalesFactory.getUTC();
71          final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
72          final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
73          final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
74          final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity),
75              FramesFactory.getEME2000(), date,
76              Constants.EIGEN5C_EARTH_MU);
77  
78          Propagator propagator =
79              new EcksteinHechlerPropagator(orbit,
80                  Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
81                  Constants.EIGEN5C_EARTH_MU,
82                  Constants.EIGEN5C_EARTH_C20,
83                  Constants.EIGEN5C_EARTH_C30,
84                  Constants.EIGEN5C_EARTH_C40,
85                  Constants.EIGEN5C_EARTH_C50,
86                  Constants.EIGEN5C_EARTH_C60);
87  
88          EventsLogger logger = new EventsLogger();
89          propagator.addEventDetector(logger.monitorDetector(d));
90  
91          propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY));
92          for (LoggedEvent e : logger.getLoggedEvents()) {
93              SpacecraftState state = e.getState();
94              double longitude = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(),
95                  earth.getBodyFrame(), date).getLongitude();
96              if (e.isIncreasing()) {
97                  // retrograde orbit, enter
98                  Assertions.assertEquals(18.0, FastMath.toDegrees(longitude), 3.5e-7);
99              } else {
100                 // retrograde orbit, exit
101                 Assertions.assertEquals(10.0, FastMath.toDegrees(longitude), 3.5e-7);
102             }
103             Assertions.assertEquals(28, logger.getLoggedEvents().size());
104         }
105     }
106 
107     @Test
108     public void testZigZag() {
109 
110         final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
111                                                             Constants.WGS84_EARTH_FLATTENING,
112                                                             FramesFactory.getITRF(IERSConventions.IERS_2010, true));
113 
114         LongitudeRangeCrossingDetector d =
115                 new LongitudeRangeCrossingDetector(600.0, 1.e-6, earth,
116                     FastMath.toRadians(-120.0),
117                     FastMath.toRadians(-100.0)).
118                 withHandler(new ContinueOnEvent());
119 
120         Assertions.assertEquals(600.0, d.getMaxCheckInterval().currentInterval(null, true), 1.0e-15);
121         Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15);
122         Assertions.assertEquals(-120.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-8);
123         Assertions.assertEquals(-100.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-8);
124         Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount());
125 
126         AbsoluteDate date = AbsoluteDate.J2000_EPOCH;
127         KeplerianOrbit orbit =
128                         new KeplerianOrbit(26464560.0, 0.8311, 0.122138, 3.10686, 1.00681,
129                                            0.048363, MEAN,
130                                            FramesFactory.getEME2000(),
131                                            date,
132                                            Constants.EIGEN5C_EARTH_MU);
133 
134 
135         Propagator propagator = new KeplerianPropagator(orbit);
136 
137         EventsLogger logger = new EventsLogger();
138         propagator.addEventDetector(logger.monitorDetector(d));
139 
140         propagator.propagate(orbit.getDate().shiftedBy(1 * Constants.JULIAN_DAY));
141         Assertions.assertEquals(4, logger.getLoggedEvents().size());
142 
143         // eccentric orbit, at apogee Earth rotation makes as reversed effect
144         double[] expectedLongitude = new double[]{d.getFromLongitude(), d.getToLongitude(),
145             d.getToLongitude(), d.getFromLongitude()};
146         for (int i = 0; i < 4; ++i) {
147             SpacecraftState state = logger.getLoggedEvents().get(i).getState();
148             GeodeticPoint gp = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(),
149                 earth.getBodyFrame(), date);
150             Assertions.assertEquals(expectedLongitude[i], gp.getLongitude(), 1.2e-9);
151         }
152 
153     }
154 
155     @BeforeEach
156     public void setUp() {
157         Utils.setDataRoot("regular-data");
158     }
159 
160 }
161