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.orekit.frames.Frame;
22  import org.orekit.frames.TopocentricFrame;
23  import org.orekit.models.AtmosphericRefractionModel;
24  import org.orekit.models.earth.ITURP834AtmosphericRefraction;
25  import org.orekit.propagation.SpacecraftState;
26  import org.orekit.propagation.events.handlers.ContinueOnEvent;
27  import org.orekit.propagation.events.handlers.EventHandler;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.utils.PVCoordinatesProvider;
30  
31  
32  /** Detector for ground location being at night.
33   * <p>
34   * This detector is mainly useful for scheduling optical measurements
35   * (either passive telescope observation of satellites against the stars background
36   *  or active satellite laser ranging).
37   * </p>
38   * <p>
39   * The {@code g} function of this detector is positive when ground is at night
40   * (i.e. Sun is below dawn/dusk elevation angle).
41   * </p>
42   * @author Luc Maisonobe
43   * @since 9.3
44   */
45  public class GroundAtNightDetector extends AbstractDetector<GroundAtNightDetector> {
46  
47      /** Sun elevation at civil dawn/dusk (6° below horizon). */
48      public static final double CIVIL_DAWN_DUSK_ELEVATION = FastMath.toRadians(-6.0);
49  
50      /** Sun elevation at nautical dawn/dusk (12° below horizon). */
51      public static final double NAUTICAL_DAWN_DUSK_ELEVATION = FastMath.toRadians(-12.0);
52  
53      /** Sun elevation at astronomical dawn/dusk (18° below horizon). */
54      public static final double ASTRONOMICAL_DAWN_DUSK_ELEVATION = FastMath.toRadians(-18.0);
55  
56      /** Ground location to check. */
57      private final TopocentricFrame groundLocation;
58  
59      /** Provider for Sun position. */
60      private final PVCoordinatesProvider sun;
61  
62      /** Sun elevation below which we consider night is dark enough. */
63      private final double dawnDuskElevation;
64  
65      /** Atmospheric Model used for calculations, if defined. */
66      private final AtmosphericRefractionModel refractionModel;
67  
68      /** Simple constructor.
69       * <p>
70       * Beware that {@link org.orekit.models.earth.EarthStandardAtmosphereRefraction Earth
71       * standard refraction model} does apply only for elevations above -2°. It is therefore
72       * not suitable for used with {@link #CIVIL_DAWN_DUSK_ELEVATION} (-6°), {@link
73       * #NAUTICAL_DAWN_DUSK_ELEVATION} (-12°) or {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION} (-18°).
74       * The {@link ITURP834AtmosphericRefraction ITU-R P.834 refraction model}
75       * which can compute refraction at large negative elevations should be preferred.
76       * </p>
77       * @param groundLocation ground location to check
78       * @param sun provider for Sun position
79       * @param dawnDuskElevation Sun elevation below which we consider night is dark enough (rad)
80       * (typically {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION})
81       * @param refractionModel reference to refraction model (null if refraction should be ignored)
82       */
83      public GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun,
84                                   final double dawnDuskElevation,
85                                   final AtmosphericRefractionModel refractionModel) {
86          this(groundLocation, sun, dawnDuskElevation, refractionModel,
87                  EventDetectionSettings.getDefaultEventDetectionSettings(), new ContinueOnEvent());
88      }
89  
90      /** Private constructor.
91       * @param groundLocation ground location from which measurement is performed
92       * @param sun provider for Sun position
93       * @param dawnDuskElevation Sun elevation below which we consider night is dark enough (rad)
94       * (typically {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION})
95       * @param refractionModel reference to refraction model (null if refraction should be ignored),
96       * @param detectionSettings event detection settings
97       * @param handler   event handler to call at event occurrences
98       * @since 13.0
99       */
100     protected GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun,
101                                     final double dawnDuskElevation,
102                                     final AtmosphericRefractionModel refractionModel,
103                                     final EventDetectionSettings detectionSettings,
104                                     final EventHandler handler) {
105         super(detectionSettings, handler);
106         this.groundLocation    = groundLocation;
107         this.sun               = sun;
108         this.dawnDuskElevation = dawnDuskElevation;
109         this.refractionModel   = refractionModel;
110     }
111 
112     /** {@inheritDoc} */
113     @Override
114     protected GroundAtNightDetector create(final EventDetectionSettings detectionSettings,
115                                            final EventHandler newHandler) {
116         return new GroundAtNightDetector(groundLocation, sun, dawnDuskElevation, refractionModel,
117                                          detectionSettings, newHandler);
118     }
119 
120     /** {@inheritDoc}
121      * <p>
122      * The {@code g} function of this detector is positive when ground is at night
123      * (i.e. Sun is below dawn/dusk elevation angle).
124      * </p>
125      * <p>
126      * This function only depends on date, not on the actual position of the spacecraft.
127      * </p>
128      */
129     @Override
130     public double g(final SpacecraftState state) {
131 
132         final AbsoluteDate  date     = state.getDate();
133         final Frame         frame    = state.getFrame();
134         final Vector3D      position = sun.getPosition(date, frame);
135         final double trueElevation   = groundLocation.getElevation(position, frame, date);
136 
137         final double calculatedElevation;
138         if (refractionModel != null) {
139             calculatedElevation = trueElevation + refractionModel.getRefraction(trueElevation);
140         } else {
141             calculatedElevation = trueElevation;
142         }
143 
144         return dawnDuskElevation - calculatedElevation;
145 
146     }
147 
148 }