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