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