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.util.FastMath;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.mockito.Mockito;
24  import org.orekit.Utils;
25  import org.orekit.bodies.CelestialBodyFactory;
26  import org.orekit.bodies.GeodeticPoint;
27  import org.orekit.bodies.OneAxisEllipsoid;
28  import org.orekit.frames.FramesFactory;
29  import org.orekit.frames.TopocentricFrame;
30  import org.orekit.models.AtmosphericRefractionModel;
31  import org.orekit.models.earth.ITURP834AtmosphericRefraction;
32  import org.orekit.orbits.CircularOrbit;
33  import org.orekit.orbits.PositionAngleType;
34  import org.orekit.propagation.Propagator;
35  import org.orekit.propagation.analytical.KeplerianPropagator;
36  import org.orekit.propagation.events.EventsLogger.LoggedEvent;
37  import org.orekit.time.AbsoluteDate;
38  import org.orekit.time.TimeScale;
39  import org.orekit.time.TimeScalesFactory;
40  import org.orekit.utils.Constants;
41  import org.orekit.utils.IERSConventions;
42  import org.orekit.utils.PVCoordinatesProvider;
43  
44  import java.util.List;
45  
46  class GroundAtNightDetectorTest {
47  
48      @Test
49      void testMidLatitudeCivilNoRefraction() {
50          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
51                        GroundAtNightDetector.CIVIL_DAWN_DUSK_ELEVATION,
52                        null, 45037.367);
53      }
54  
55      @Test
56      void testMidLatitudeCivilITURefraction() {
57          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
58                        GroundAtNightDetector.CIVIL_DAWN_DUSK_ELEVATION,
59                        new ITURP834AtmosphericRefraction(0.0), 43909.148);
60      }
61  
62      @Test
63      void testMidLatitudeNauticalNoRefraction() {
64          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
65                        GroundAtNightDetector.NAUTICAL_DAWN_DUSK_ELEVATION,
66                        null, 41045.750);
67      }
68  
69      @Test
70      void testMidLatitudeNauticalITURefraction() {
71          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
72                        GroundAtNightDetector.NAUTICAL_DAWN_DUSK_ELEVATION,
73                        new ITURP834AtmosphericRefraction(0.0), 39933.656);
74      }
75  
76      @Test
77      void testMidLatitudeAstronomicalNoRefraction() {
78          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
79                        GroundAtNightDetector.ASTRONOMICAL_DAWN_DUSK_ELEVATION,
80                        null, 37097.821);
81      }
82  
83      @Test
84      void testMidLatitudeAstronomicalITURefraction() {
85          checkDuration(FastMath.toRadians(43.0), FastMath.toRadians(0.0),
86                        GroundAtNightDetector.ASTRONOMICAL_DAWN_DUSK_ELEVATION,
87                        new ITURP834AtmosphericRefraction(0.0), 35991.314);
88      }
89  
90      @Test
91      void testHighLatitudeAstronomicalITURefraction() {
92          checkDuration(FastMath.toRadians(84.0), FastMath.toRadians(0.0),
93                        GroundAtNightDetector.ASTRONOMICAL_DAWN_DUSK_ELEVATION,
94                        new ITURP834AtmosphericRefraction(0.0), Double.NaN);
95      }
96  
97      private void checkDuration(double latitude, double longitude, double dawnDuskElevation,
98                                 AtmosphericRefractionModel refractionModel,
99                                 double expectedDuration) {
100         OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
101                                                       Constants.WGS84_EARTH_FLATTENING,
102                                                       FramesFactory.getITRF(IERSConventions.IERS_2010, true));
103         TimeScale utc = TimeScalesFactory.getUTC();
104         CircularOrbit o = new CircularOrbit(7200000.0, 1.0e-3, 2.0e-4,
105                                             FastMath.toRadians(50.0), FastMath.toRadians(134.0),
106                                             FastMath.toRadians(21.0), PositionAngleType.MEAN, FramesFactory.getGCRF(),
107                                             new AbsoluteDate("2003-02-14T14:02:03.000", utc),
108                                             Constants.EIGEN5C_EARTH_MU);
109 
110         TopocentricFrame topo = new TopocentricFrame(earth, new GeodeticPoint(latitude, longitude, 0.0), "");
111         Propagator p = new KeplerianPropagator(o);
112         EventsLogger logger = new EventsLogger();
113         p.addEventDetector(logger.monitorDetector(new GroundAtNightDetector(topo,
114                                                                             CelestialBodyFactory.getSun(),
115                                                                             dawnDuskElevation, refractionModel).
116                                                   withMaxCheck(120.0)));
117         p.propagate(o.getDate().shiftedBy(Constants.JULIAN_DAY));
118         List<LoggedEvent> events = logger.getLoggedEvents();
119         if (Double.isNaN(expectedDuration)) {
120             Assertions.assertEquals(0, events.size());
121         } else {
122             Assertions.assertEquals(2, events.size());
123             Assertions.assertEquals(expectedDuration,
124                                 events.get(1).getState().getDate().durationFrom(events.get(0).getState().getDate()),
125                                 1.0e-3);
126         }
127 
128     }
129 
130     @Test
131     void testDependsOnlyOnTime() {
132         // GIVEN
133         final GroundAtNightDetector detector = new GroundAtNightDetector(Mockito.mock(TopocentricFrame.class),
134                 Mockito.mock(PVCoordinatesProvider.class), 1, null);
135         // WHEN
136         final boolean value = detector.dependsOnTimeOnly();
137         // THEN
138         Assertions.assertTrue(value);
139     }
140 
141     @BeforeEach
142     void setUp() {
143         Utils.setDataRoot("regular-data");
144     }
145 
146 }
147