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.ode.events.Action;
21  import org.hipparchus.util.FastMath;
22  import org.junit.jupiter.api.AfterEach;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Disabled;
26  import org.junit.jupiter.api.Test;
27  import org.orekit.Utils;
28  import org.orekit.bodies.BodyShape;
29  import org.orekit.bodies.GeodeticPoint;
30  import org.orekit.bodies.OneAxisEllipsoid;
31  import org.orekit.frames.Frame;
32  import org.orekit.frames.FramesFactory;
33  import org.orekit.frames.TopocentricFrame;
34  import org.orekit.models.AtmosphericRefractionModel;
35  import org.orekit.models.earth.EarthStandardAtmosphereRefraction;
36  import org.orekit.orbits.EquinoctialOrbit;
37  import org.orekit.orbits.KeplerianOrbit;
38  import org.orekit.orbits.Orbit;
39  import org.orekit.orbits.PositionAngleType;
40  import org.orekit.propagation.Propagator;
41  import org.orekit.propagation.SpacecraftState;
42  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
43  import org.orekit.propagation.analytical.KeplerianPropagator;
44  import org.orekit.propagation.analytical.tle.TLE;
45  import org.orekit.propagation.analytical.tle.TLEPropagator;
46  import org.orekit.propagation.events.EventsLogger.LoggedEvent;
47  import org.orekit.propagation.events.handlers.ContinueOnEvent;
48  import org.orekit.propagation.events.handlers.EventHandler;
49  import org.orekit.propagation.events.handlers.StopOnIncreasing;
50  import org.orekit.propagation.events.intervals.AdaptableInterval;
51  import org.orekit.propagation.events.intervals.ElevationDetectionAdaptableIntervalFactory;
52  import org.orekit.propagation.sampling.OrekitFixedStepHandler;
53  import org.orekit.time.AbsoluteDate;
54  import org.orekit.time.TimeScale;
55  import org.orekit.time.TimeScalesFactory;
56  import org.orekit.utils.Constants;
57  import org.orekit.utils.ElevationMask;
58  import org.orekit.utils.IERSConventions;
59  import org.orekit.utils.PVCoordinates;
60  
61  import java.util.List;
62  
63  public class ElevationDetectorTest {
64  
65      private double mu;
66      private double ae;
67      private double c20;
68      private double c30;
69      private double c40;
70      private double c50;
71      private double c60;
72  
73      @Test
74      public void testAgata() {
75  
76          final TimeScale utc = TimeScalesFactory.getUTC();
77          final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
78          final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
79          final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
80          final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
81                                                   FramesFactory.getEME2000(), date, mu);
82  
83          Propagator propagator =
84              new EcksteinHechlerPropagator(orbit, ae, mu, c20, c30, c40, c50, c60);
85  
86          // Earth and frame
87          double ae =  6378137.0; // equatorial radius in meter
88          double f  =  1.0 / 298.257223563; // flattening
89          Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // terrestrial frame at an arbitrary date
90          BodyShape earth = new OneAxisEllipsoid(ae, f, itrf);
91          GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(48.833),
92                                                  FastMath.toRadians(2.333),
93                                                  0.0);
94          TopocentricFrame topo = new TopocentricFrame(earth, point, "Gstation");
95          Checking checking = new Checking(topo);
96          ElevationDetector detector =
97                  new ElevationDetector(topo).
98                  withConstantElevation(FastMath.toRadians(5.0)).
99                  withHandler(checking);
100         Assertions.assertNull(detector.getElevationMask());
101         Assertions.assertNull(detector.getRefractionModel());
102         Assertions.assertSame(topo, detector.getTopocentricFrame());
103         Assertions.assertEquals(FastMath.toRadians(5.0), detector.getMinElevation(), 1.0e-15);
104 
105         AbsoluteDate startDate = new AbsoluteDate(2003, 9, 15, 12, 0, 0, utc);
106         propagator.resetInitialState(propagator.propagate(startDate));
107         propagator.addEventDetector(detector);
108         propagator.setStepHandler(10.0, checking);
109         propagator.propagate(startDate.shiftedBy(Constants.JULIAN_DAY));
110 
111     }
112 
113     private static class Checking implements EventHandler, OrekitFixedStepHandler {
114 
115         private final TopocentricFrame topo;
116         private boolean visible;
117 
118         public Checking(final TopocentricFrame topo) {
119             this.topo = topo;
120             this.visible = false;
121         }
122 
123         public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
124             visible = increasing;
125             return Action.CONTINUE;
126         }
127 
128         public void handleStep(SpacecraftState currentState)
129             {
130             BodyShape shape = topo.getParentShape();
131             GeodeticPoint p =
132                             shape.transform(currentState.getPosition(),
133                                             currentState.getFrame(), currentState.getDate());
134             Vector3D subSat = shape.transform(new GeodeticPoint(p.getLatitude(), p.getLongitude(), 0.0));
135             double range = topo.getTrackingCoordinates(subSat, shape.getBodyFrame(), currentState.getDate()).getRange();
136 
137             if (visible) {
138                 Assertions.assertTrue(range < 2.45e6);
139             } else {
140                 Assertions.assertTrue(range > 2.02e6);
141             }
142         }
143 
144     }
145 
146     @Test
147     public void testEventForMask() {
148 
149         final TimeScale utc = TimeScalesFactory.getUTC();
150         final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
151         final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
152         final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
153         final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
154                                                  FramesFactory.getEME2000(), date, mu);
155 
156         Propagator propagator =
157             new EcksteinHechlerPropagator(orbit, ae, mu, c20, c30, c40, c50, c60);
158 
159         // Earth and frame
160         double ae =  6378137.0; // equatorial radius in meter
161         double f  =  1.0 / 298.257223563; // flattening
162         Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // terrestrial frame at an arbitrary date
163         BodyShape earth = new OneAxisEllipsoid(ae, f, itrf);
164         GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(48.833),
165                                                 FastMath.toRadians(2.333),
166                                                 0.0);
167         TopocentricFrame topo = new TopocentricFrame(earth, point, "Gstation");
168         double [][] maskValues = {{ FastMath.toRadians(  0), FastMath.toRadians(5)},
169                                   { FastMath.toRadians( 30), FastMath.toRadians(4)},
170                                   { FastMath.toRadians( 60), FastMath.toRadians(3)},
171                                   { FastMath.toRadians( 90), FastMath.toRadians(2)},
172                                   { FastMath.toRadians(120), FastMath.toRadians(3)},
173                                   { FastMath.toRadians(150), FastMath.toRadians(4)},
174                                   { FastMath.toRadians(180), FastMath.toRadians(5)},
175                                   { FastMath.toRadians(210), FastMath.toRadians(6)},
176                                   { FastMath.toRadians(240), FastMath.toRadians(5)},
177                                   { FastMath.toRadians(270), FastMath.toRadians(4)},
178                                   { FastMath.toRadians(300), FastMath.toRadians(3)},
179                                   { FastMath.toRadians(330), FastMath.toRadians(4)}};
180         ElevationMask mask = new ElevationMask(maskValues);
181         ElevationDetector detector = new ElevationDetector(topo)
182                                             .withElevationMask(mask)
183                                             .withHandler(new StopOnIncreasing());
184         Assertions.assertSame(mask, detector.getElevationMask());
185 
186         AbsoluteDate startDate = new AbsoluteDate(2003, 9, 15, 20, 0, 0, utc);
187         propagator.resetInitialState(propagator.propagate(startDate));
188         propagator.addEventDetector(detector);
189         final SpacecraftState fs = propagator.propagate(startDate.shiftedBy(Constants.JULIAN_DAY));
190         double elevation = topo.getTrackingCoordinates(fs.getPosition(), fs.getFrame(), fs.getDate()).getElevation();
191         Assertions.assertEquals(0.065, elevation, 2.0e-5);
192 
193     }
194 
195 
196     @Test
197     public void testHorizon() {
198 
199         final TimeScale utc = TimeScalesFactory.getUTC();
200         final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
201         final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
202         final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
203         final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
204                                                  FramesFactory.getEME2000(), date, mu);
205 
206         Propagator propagator =
207             new EcksteinHechlerPropagator(orbit, ae, mu, c20, c30, c40, c50, c60);
208 
209         // Earth and frame
210         double ae =  6378137.0; // equatorial radius in meter
211         double f  =  1.0 / 298.257223563; // flattening
212         Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // terrestrial frame at an arbitrary date
213         BodyShape earth = new OneAxisEllipsoid(ae, f, itrf);
214         GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(48.833),
215                                                 FastMath.toRadians(2.333),
216                                                 0.0);
217         TopocentricFrame topo = new TopocentricFrame(earth, point, "Gstation");
218         AtmosphericRefractionModel refractionModel = new EarthStandardAtmosphereRefraction();
219         ElevationDetector detector = new ElevationDetector(topo)
220                                             .withRefraction(refractionModel)
221                                             .withHandler(new StopOnIncreasing());
222         Assertions.assertSame(refractionModel, detector.getRefractionModel());
223 
224         AbsoluteDate startDate = new AbsoluteDate(2003, 9, 15, 20, 0, 0, utc);
225         propagator.resetInitialState(propagator.propagate(startDate));
226         propagator.addEventDetector(detector);
227         final SpacecraftState fs = propagator.propagate(startDate.shiftedBy(Constants.JULIAN_DAY));
228         double elevation = topo.getTrackingCoordinates(fs.getPosition(), fs.getFrame(), fs.getDate()).getElevation();
229         Assertions.assertEquals(FastMath.toRadians(-0.5746255623877098), elevation, 2.0e-5);
230 
231     }
232 
233 
234     @Test
235     public void testIssue136() {
236 
237         //  Initial state definition : date, orbit
238         AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
239         Frame inertialFrame = FramesFactory.getEME2000(); // inertial frame for orbit definition
240         Orbit initialOrbit = new KeplerianOrbit(6828137.005, 7.322641382145889e-10, 1.6967079057368113,
241                                                 0.0, 1.658054062748353,
242                                                 0.0001223149429077902, PositionAngleType.MEAN,
243                                                 inertialFrame, initialDate, Constants.EIGEN5C_EARTH_MU);
244 
245         // Propagator : consider a simple Keplerian motion (could be more elaborate)
246         Propagator kepler = new EcksteinHechlerPropagator(initialOrbit,
247                                                           Constants.EGM96_EARTH_EQUATORIAL_RADIUS, Constants.EGM96_EARTH_MU,
248                                                           Constants.EGM96_EARTH_C20, 0.0, 0.0, 0.0, 0.0);
249 
250         // Earth and frame
251         double ae =  6378137.0; // equatorial radius in meter
252         double f  =  1.0 / 298.257223563; // flattening
253         Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // terrestrial frame at an arbitrary date
254         BodyShape earth = new OneAxisEllipsoid(ae, f, itrf);
255 
256         // Station
257         final double longitude = FastMath.toRadians(-147.5);
258         final double latitude  = FastMath.toRadians(64);
259         final double altitude  = 160;
260         final GeodeticPoint station1 = new GeodeticPoint(latitude, longitude, altitude);
261         final TopocentricFrame sta1Frame = new TopocentricFrame(earth, station1, "station1");
262 
263         // Event definition
264         final double maxcheck  = 120.0;
265         final double elevation = FastMath.toRadians(5.);
266         final double threshold = 10.0;
267         final EventDetector rawEvent = new ElevationDetector(maxcheck, threshold, sta1Frame)
268                                                 .withConstantElevation(elevation)
269                                                 .withHandler(new ContinueOnEvent());
270         final EventsLogger logger = new EventsLogger();
271         kepler.addEventDetector(logger.monitorDetector(rawEvent));
272 
273         // Propagate from the initial date to the first raising or for the fixed duration
274         kepler.propagate(initialDate.shiftedBy(60*60*24.0*40));
275         int countIncreasing = 0;
276         int countDecreasing = 0;
277         for (LoggedEvent le : logger.getLoggedEvents()) {
278             if (le.isIncreasing()) {
279                 ++countIncreasing;
280             } else {
281                 ++countDecreasing;
282             }
283         }
284         Assertions.assertEquals(314, countIncreasing);
285         Assertions.assertEquals(314, countDecreasing);
286 
287     }
288 
289     @Test
290     public void testIssue110() {
291 
292         // KEPLERIAN PROPAGATOR
293         final Frame eme2000Frame = FramesFactory.getEME2000();
294         final AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH;
295         final double a = 7000000.0;
296         final Orbit initialOrbit = new KeplerianOrbit(a, 0.0,
297                 FastMath.PI / 2.2, 0.0, FastMath.PI / 2., 0.0,
298                 PositionAngleType.TRUE, eme2000Frame, initDate,
299                 Constants.EGM96_EARTH_MU);
300         final KeplerianPropagator kProp = new KeplerianPropagator(initialOrbit);
301 
302         // earth shape
303         final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(
304                 Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
305                 Constants.WGS84_EARTH_FLATTENING, FramesFactory.getITRF(IERSConventions.IERS_2010, true));
306         // Ground station
307         final GeodeticPoint stat = new GeodeticPoint(FastMath.toRadians(35.),
308                 FastMath.toRadians(149.8), 0.);
309         final TopocentricFrame station = new TopocentricFrame(earthShape, stat,
310                 "GSTATION");
311 
312         // detector creation
313         // =================
314         final double maxCheck = 600.;
315         final double threshold = 1.0e-3;
316         final EventDetector rawEvent = new ElevationDetector(maxCheck, threshold, station)
317                                                     .withConstantElevation(FastMath.toRadians(5.0))
318                                                     .withHandler(new ContinueOnEvent());
319         final EventsLogger logger = new EventsLogger();
320         kProp.addEventDetector(logger.monitorDetector(rawEvent));
321 
322         // PROPAGATION with DETECTION
323         final AbsoluteDate finalDate = initDate.shiftedBy(30 * 60.);
324 
325         kProp.propagate(finalDate);
326         Assertions.assertEquals(2, logger.getLoggedEvents().size());
327         Assertions.assertTrue(logger.getLoggedEvents().get(0).isIncreasing());
328         Assertions.assertEquals(478.945, logger.getLoggedEvents().get(0).getState().getDate().durationFrom(initDate), 1.0e-3);
329         Assertions.assertFalse(logger.getLoggedEvents().get(1).isIncreasing());
330         Assertions.assertEquals(665.721, logger.getLoggedEvents().get(1).getState().getDate().durationFrom(initDate), 1.0e-3);
331 
332     }
333 
334     @Disabled
335     @Test
336     public void testPresTemp() {
337 
338         final TimeScale utc = TimeScalesFactory.getUTC();
339         final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257);
340         final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922);
341         final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc);
342         final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position,  velocity),
343                                                  FramesFactory.getEME2000(), date, mu);
344 
345         Propagator propagator =
346             new EcksteinHechlerPropagator(orbit, ae, mu, c20, c30, c40, c50, c60);
347 
348         // Earth and frame
349         double ae =  6378137.0; // equatorial radius in meter
350         double f  =  1.0 / 298.257223563; // flattening
351         Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // terrestrial frame at an arbitrary date
352         BodyShape earth = new OneAxisEllipsoid(ae, f, itrf);
353         GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(48.833),
354                                                 FastMath.toRadians(2.333),
355                                                 0.0);
356         TopocentricFrame topo = new TopocentricFrame(earth, point, "Gstation");
357         EarthStandardAtmosphereRefraction refractionModel = new EarthStandardAtmosphereRefraction();
358         ElevationDetector detector = new ElevationDetector(topo)
359                                                  .withRefraction(refractionModel)
360                                                  .withHandler(new StopOnIncreasing());
361         refractionModel.setPressure(101325);
362         refractionModel.setTemperature(290);
363 
364         AbsoluteDate startDate = new AbsoluteDate(2003, 9, 15, 20, 0, 0, utc);
365         propagator.resetInitialState(propagator.propagate(startDate));
366         propagator.addEventDetector(detector);
367         final SpacecraftState fs = propagator.propagate(startDate.shiftedBy(Constants.JULIAN_DAY));
368         double elevation = topo.getTrackingCoordinates(fs.getPosition(), fs.getFrame(), fs.getDate()).getElevation();
369         Assertions.assertEquals(FastMath.toRadians(1.7026104902251749), elevation, 2.0e-5);
370 
371     }
372 
373     @Test
374     public void testIssue203() {
375 
376         //  Initial state definition : date, orbit
377         AbsoluteDate initialDate = new AbsoluteDate("2012-01-26T07:00:00.000", TimeScalesFactory.getUTC());
378 
379         Frame inertialFrame = FramesFactory.getEME2000(); // inertial frame for orbit definition
380 
381         Orbit initialOrbit = new KeplerianOrbit(6828137.5, 7.322641060181212E-8, 1.7082667003713938, 0.0, 1.658054062748353, 1.2231496082116026E-4, PositionAngleType.TRUE , inertialFrame, initialDate, Constants.WGS84_EARTH_MU);
382 
383         Propagator propagator =
384                 new EcksteinHechlerPropagator(initialOrbit,
385                         Constants.EGM96_EARTH_EQUATORIAL_RADIUS,
386                         Constants.EGM96_EARTH_MU,
387                         Constants.EGM96_EARTH_C20,
388                         Constants.EGM96_EARTH_C30,
389                         Constants.EGM96_EARTH_C40,
390                         Constants.EGM96_EARTH_C50,
391                         Constants.EGM96_EARTH_C60);
392 
393         // Earth and frame
394         Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
395         BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
396                                                Constants.WGS84_EARTH_FLATTENING,
397                                                earthFrame);
398 
399         // Station
400         final double longitude = FastMath.toRadians(21.0);
401         final double latitude  = FastMath.toRadians(67.9);
402         final double altitude  = 300.0;
403 
404         final GeodeticPoint station1 = new GeodeticPoint(latitude, longitude, altitude);
405         final TopocentricFrame sta1Frame = new TopocentricFrame(earth, station1, "station1");
406 
407         double [][] maskValues = {
408             {-0.017453292519943098, 0.006981317007977318}, {0.0, 0.006981317007977318},
409             {0.017453292519943295, 0.006981317007977318}, {0.03490658503988659, 0.010471975511965976},
410             {0.05235987755982989, 0.012217304763960306}, {0.06981317007977318, 0.010471975511965976},
411             {0.08726646259971647, 0.010471975511965976}, {0.10471975511965978, 0.012217304763960306},
412             {0.12217304763960307, 0.010471975511965976}, {0.13962634015954636, 0.008726646259971648},
413             {0.15707963267948966, 0.008726646259971648}, {0.17453292519943295, 0.006981317007977318},
414             {0.19198621771937624, 0.006981317007977318}, {0.20943951023931956, 0.006981317007977318},
415             {0.22689280275926285, 0.005235987755982988}, {0.24434609527920614, 0.005235987755982988},
416             {0.2617993877991494, 0.003490658503988659}, {0.2792526803190927, 0.003490658503988659},
417             {0.29670597283903605, 0.0017453292519943296}, {0.3141592653589793, 0.003490658503988659},
418             {0.33161255787892263, 0.0017453292519943296}, {0.3490658503988659, 0.0},
419             {0.3665191429188092, 0.012217304763960306}, {0.3839724354387525, 0.012217304763960306},
420             {0.4014257279586958, 0.0}, {0.4188790204786391, 0.010471975511965976},
421             {0.4363323129985824, 0.029670597283903602}, {0.4537856055185257, 0.029670597283903602},
422             {0.47123889803846897, 0.017453292519943295}, {0.4886921905584123, 0.0},
423             {0.5061454830783556, 0.029670597283903602}, {0.5235987755982988, 0.015707963267948967},
424             {0.5410520681182421, 0.005235987755982988}, {0.5585053606381855, 0.024434609527920613},
425             {0.5759586531581288, 0.041887902047863905}, {0.5934119456780721, 0.06283185307179587},
426             {0.6108652381980153, 0.05235987755982989}, {0.6283185307179586, 0.05759586531581287},
427             {0.6457718232379019, 0.061086523819801536}, {0.6632251157578453, 0.05759586531581287},
428             {0.6806784082777885, 0.04363323129985824}, {0.6981317007977318, 0.059341194567807204},
429             {0.7155849933176751, 0.07504915783575616}, {0.7330382858376184, 0.08726646259971647},
430             {0.7504915783575618, 0.08726646259971647}, {0.767944870877505, 0.07330382858376185},
431             {0.7853981633974483, 0.07853981633974483}, {0.8028514559173916, 0.09075712110370514},
432             {0.8203047484373349, 0.0942477796076938}, {0.8377580409572782, 0.11519173063162574},
433             {0.8552113334772214, 0.11519173063162574}, {0.8726646259971648, 0.12566370614359174},
434             {0.8901179185171081, 0.12566370614359174}, {0.9075712110370514, 0.10122909661567112},
435             {0.9250245035569946, 0.11868238913561441}, {0.9424777960769379, 0.11868238913561441},
436             {0.9599310885968813, 0.11868238913561441}, {0.9773843811168246, 0.10821041362364843},
437             {0.9948376736367679, 0.12217304763960307}, {1.0122909661567112, 0.12740903539558607},
438             {1.0297442586766545, 0.11344640137963143}, {1.0471975511965976, 0.11344640137963143},
439             {1.064650843716541, 0.06632251157578452}, {1.0821041362364843, 0.12391837689159739},
440             {1.0995574287564276, 0.12391837689159739}, {1.117010721276371, 0.10995574287564276},
441             {1.1344640137963142, 0.09250245035569947}, {1.1519173063162575, 0.12740903539558607},
442             {1.1693705988362009, 0.1308996938995747}, {1.1868238913561442, 0.1117010721276371},
443             {1.2042771838760873, 0.1117010721276371}, {1.2217304763960306, 0.0942477796076938},
444             {1.239183768915974, 0.10821041362364843}, {1.2566370614359172, 0.09599310885968812},
445             {1.2740903539558606, 0.09948376736367678}, {1.2915436464758039, 0.09773843811168245},
446             {1.3089969389957472, 0.08726646259971647}, {1.3264502315156905, 0.09250245035569947},
447             {1.3439035240356338, 0.10122909661567112}, {1.361356816555577, 0.09250245035569947},
448             {1.3788101090755203, 0.08552113334772216}, {1.3962634015954636, 0.08726646259971647},
449             {1.413716694115407, 0.08028514559173916}, {1.4311699866353502, 0.05759586531581287},
450             {1.4486232791552935, 0.054105206811824215}, {1.4660765716752369, 0.06632251157578452},
451             {1.4835298641951802, 0.08028514559173916}, {1.5009831567151235, 0.061086523819801536},
452             {1.5184364492350666, 0.048869219055841226}, {1.53588974175501, 0.0715584993317675},
453             {1.5533430342749532, 0.07504915783575616}, {1.5707963267948966, 0.05235987755982989},
454             {1.5882496193148399, 0.05235987755982989}, {1.6057029118347832, 0.06981317007977318},
455             {1.6231562043547265, 0.054105206811824215}, {1.6406094968746698, 0.0645771823237902},
456             {1.6580627893946132, 0.059341194567807204}, {1.6755160819145565, 0.029670597283903602},
457             {1.6929693744344996, 0.03316125578789226}, {1.710422666954443, 0.059341194567807204},
458             {1.7278759594743862, 0.0645771823237902}, {1.7453292519943295, 0.06283185307179587},
459             {1.7627825445142729, 0.061086523819801536}, {1.7802358370342162, 0.06806784082777885},
460             {1.7976891295541595, 0.06632251157578452}, {1.8151424220741028, 0.059341194567807204},
461             {1.8325957145940461, 0.05759586531581287}, {1.8500490071139892, 0.05759586531581287},
462             {1.8675022996339325, 0.0471238898038469}, {1.8849555921538759, 0.059341194567807204},
463             {1.9024088846738192, 0.05061454830783556}, {1.9198621771937625, 0.05061454830783556},
464             {1.9373154697137058, 0.024434609527920613}, {1.9547687622336491, 0.027925268031909273},
465             {1.9722220547535925, 0.041887902047863905}, {1.9896753472735358, 0.024434609527920613},
466             {2.007128639793479, 0.029670597283903602}, {2.0245819323134224, 0.03316125578789226},
467             {2.0420352248333655, 0.03665191429188092}, {2.059488517353309, 0.04537856055185257},
468             {2.076941809873252, 0.041887902047863905}, {2.0943951023931953, 0.05759586531581287},
469             {2.111848394913139, 0.06283185307179587}, {2.129301687433082, 0.06806784082777885},
470             {2.1467549799530254, 0.05759586531581287}, {2.1642082724729685, 0.059341194567807204},
471             {2.181661564992912, 0.06806784082777885}, {2.199114857512855, 0.06283185307179587},
472             {2.2165681500327987, 0.054105206811824215}, {2.234021442552742, 0.05235987755982989},
473             {2.251474735072685, 0.059341194567807204}, {2.2689280275926285, 0.07330382858376185},
474             {2.2863813201125716, 0.06283185307179587}, {2.303834612632515, 0.05235987755982989},
475             {2.321287905152458, 0.061086523819801536}, {2.3387411976724017, 0.048869219055841226},
476             {2.356194490192345, 0.06632251157578452}, {2.3736477827122884, 0.05061454830783556},
477             {2.3911010752322315, 0.07679448708775051}, {2.4085543677521746, 0.06283185307179587},
478             {2.426007660272118, 0.05585053606381855}, {2.443460952792061, 0.061086523819801536},
479             {2.4609142453120048, 0.06806784082777885}, {2.478367537831948, 0.0645771823237902},
480             {2.4958208303518914, 0.06283185307179587}, {2.5132741228718345, 0.06283185307179587},
481             {2.530727415391778, 0.0645771823237902}, {2.548180707911721, 0.07504915783575616},
482             {2.5656340004316642, 0.0715584993317675}, {2.5830872929516078, 0.0645771823237902},
483             {2.600540585471551, 0.059341194567807204}, {2.6179938779914944, 0.06283185307179587},
484             {2.6354471705114375, 0.06632251157578452}, {2.652900463031381, 0.06283185307179587},
485             {2.670353755551324, 0.06632251157578452}, {2.6878070480712677, 0.0645771823237902}};
486         ElevationMask mask = new ElevationMask(maskValues);
487 
488 
489         final AbsoluteDate start = new AbsoluteDate("2012-02-10T22:00:00.000", TimeScalesFactory.getUTC());
490         final AbsoluteDate end   = initialDate.shiftedBy(1000 * Constants.JULIAN_DAY);
491 
492         // Event definition
493         final double maxcheck  = 60.0;
494         final double threshold =  2.0;
495         final EventDetector sta1Visi =
496                 new ElevationDetector(maxcheck, threshold, sta1Frame).
497                 withElevationMask(mask).
498                 withHandler(new EventHandler() {
499 
500                     private int count = 6;
501                     @Override
502                     public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
503                         return (--count > 0) ? Action.CONTINUE : Action.STOP;
504                     }
505 
506                 });
507 
508         // Add event to be detected
509         EventsLogger logger = new EventsLogger();
510         propagator.addEventDetector(logger.monitorDetector(sta1Visi));
511 
512         // Propagate until the sixth event
513         propagator.propagate(start, end);
514 
515         List<LoggedEvent> events = logger.getLoggedEvents();
516         Assertions.assertEquals(6, events.size());
517 
518         // despite the events 2 and 3 are closer to each other than the convergence threshold
519         // the second one is not merged into the first one
520         AbsoluteDate d2 = events.get(2).getDate();
521         AbsoluteDate d3 = events.get(3).getDate();
522         Assertions.assertTrue(d3.durationFrom(d2) > 0.3 * threshold);
523         Assertions.assertTrue(d3.durationFrom(d2) < threshold);
524 
525     }
526 
527     @Test
528     public void testIssue1407() {
529         final TLE tle = new TLE("1 25544U 98067A   03042.38687590  .00035128  00000-0  41387-3 0  9990",
530                                 "2 25544 051.6337 292.0267 0005644 135.6869 224.5096 15.60691825241426");
531         final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
532                                                             Constants.WGS84_EARTH_FLATTENING,
533                                                             FramesFactory.getITRF(IERSConventions.IERS_2010, true));
534         final TopocentricFrame topo = new TopocentricFrame(earth,
535                                                            new GeodeticPoint(FastMath.toRadians(38.832772),
536                                                                              FastMath.toRadians(-104.74755),
537                                                                              1839.0),
538                                                            "Colorado Springs");
539         final AdaptableInterval maxCheck = ElevationDetectionAdaptableIntervalFactory.
540             getAdaptableInterval(topo,
541                                  ElevationDetectionAdaptableIntervalFactory.DEFAULT_ELEVATION_SWITCH_INF,
542                                  ElevationDetectionAdaptableIntervalFactory.DEFAULT_ELEVATION_SWITCH_SUP,
543                                  60.0);
544         final ElevationDetector detector = new ElevationDetector(maxCheck, 1.0e-3, topo).
545                                            withConstantElevation(FastMath.toRadians(10.0)).
546                                            withHandler(new ContinueOnEvent());
547 
548         final EventsLogger logger = new EventsLogger();
549         final Propagator propagator = TLEPropagator.selectExtrapolator(tle);
550         propagator.addEventDetector(logger.monitorDetector(detector));
551         propagator.propagate(tle.getDate().shiftedBy(Constants.JULIAN_DAY));
552         Assertions.assertEquals(8, logger.getLoggedEvents().size());
553 
554     }
555 
556     @BeforeEach
557     public void setUp() {
558         Utils.setDataRoot("regular-data");
559         mu  = 3.9860047e14;
560         ae  = 6.378137e6;
561         c20 = -1.08263e-3;
562         c30 = 2.54e-6;
563         c40 = 1.62e-6;
564         c50 = 2.3e-7;
565         c60 = -5.5e-7;
566     }
567 
568     @AfterEach
569     public void tearDown() {
570         mu   = Double.NaN;
571         ae   = Double.NaN;
572         c20  = Double.NaN;
573         c30  = Double.NaN;
574         c40  = Double.NaN;
575         c50  = Double.NaN;
576         c60  = Double.NaN;
577     }
578 
579 }
580