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 }