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 AbstractTopocentricDetector<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      /** Provider for Sun position. */
57      private final PVCoordinatesProvider sun;
58  
59      /** Sun elevation below which we consider night is dark enough. */
60      private final double dawnDuskElevation;
61  
62      /** Atmospheric Model used for calculations, if defined. */
63      private final AtmosphericRefractionModel refractionModel;
64  
65      /** Simple constructor.
66       * <p>
67       * Beware that {@link org.orekit.models.earth.EarthStandardAtmosphereRefraction Earth
68       * standard refraction model} does apply only for elevations above -2°. It is therefore
69       * not suitable for used with {@link #CIVIL_DAWN_DUSK_ELEVATION} (-6°), {@link
70       * #NAUTICAL_DAWN_DUSK_ELEVATION} (-12°) or {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION} (-18°).
71       * The {@link ITURP834AtmosphericRefraction ITU-R P.834 refraction model}
72       * which can compute refraction at large negative elevations should be preferred.
73       * </p>
74       * @param groundLocation ground location to check
75       * @param sun provider for Sun position
76       * @param dawnDuskElevation Sun elevation below which we consider night is dark enough (rad)
77       * (typically {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION})
78       * @param refractionModel reference to refraction model (null if refraction should be ignored)
79       */
80      public GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun,
81                                   final double dawnDuskElevation,
82                                   final AtmosphericRefractionModel refractionModel) {
83          this(groundLocation, sun, dawnDuskElevation, refractionModel,
84                  EventDetectionSettings.getDefaultEventDetectionSettings(), new ContinueOnEvent());
85      }
86  
87      /** Private constructor.
88       * @param groundLocation ground location from which measurement is performed
89       * @param sun provider for Sun position
90       * @param dawnDuskElevation Sun elevation below which we consider night is dark enough (rad)
91       * (typically {@link #ASTRONOMICAL_DAWN_DUSK_ELEVATION})
92       * @param refractionModel reference to refraction model (null if refraction should be ignored),
93       * @param detectionSettings event detection settings
94       * @param handler   event handler to call at event occurrences
95       * @since 13.0
96       */
97      protected GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoordinatesProvider sun,
98                                      final double dawnDuskElevation,
99                                      final AtmosphericRefractionModel refractionModel,
100                                     final EventDetectionSettings detectionSettings,
101                                     final EventHandler handler) {
102         super(detectionSettings, handler, groundLocation);
103         this.sun               = sun;
104         this.dawnDuskElevation = dawnDuskElevation;
105         this.refractionModel   = refractionModel;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     protected GroundAtNightDetector create(final EventDetectionSettings detectionSettings,
111                                            final EventHandler newHandler) {
112         return new GroundAtNightDetector(getTopocentricFrame(), sun, dawnDuskElevation, refractionModel,
113                                          detectionSettings, newHandler);
114     }
115 
116     @Override
117     public boolean dependsOnTimeOnly() {
118         return true;
119     }
120 
121     /** {@inheritDoc}
122      * <p>
123      * The {@code g} function of this detector is positive when ground is at night
124      * (i.e. Sun is below dawn/dusk elevation angle).
125      * </p>
126      * <p>
127      * This function only depends on date, not on the actual position of the spacecraft.
128      * </p>
129      */
130     @Override
131     public double g(final SpacecraftState state) {
132 
133         final AbsoluteDate  date     = state.getDate();
134         final Frame         frame    = state.getFrame();
135         final Vector3D      position = sun.getPosition(date, frame);
136         final double trueElevation   = getTopocentricFrame().getElevation(position, frame, date);
137 
138         final double calculatedElevation;
139         if (refractionModel != null) {
140             calculatedElevation = trueElevation + refractionModel.getRefraction(trueElevation);
141         } else {
142             calculatedElevation = trueElevation;
143         }
144 
145         return dawnDuskElevation - calculatedElevation;
146 
147     }
148 
149 }