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.ode.events.Action;
20 import org.orekit.bodies.OneAxisEllipsoid;
21 import org.orekit.propagation.SpacecraftState;
22 import org.orekit.propagation.events.handlers.EventHandler;
23 import org.orekit.propagation.events.handlers.StopOnIncreasing;
24 import org.orekit.utils.ExtendedPVCoordinatesProvider;
25 import org.orekit.utils.OccultationEngine;
26 import org.orekit.utils.PVCoordinatesProvider;
27
28 /** Finder for satellite eclipse related events.
29 * <p>This class finds eclipse events, i.e. satellite within umbra (total
30 * eclipse) or penumbra (partial eclipse).</p>
31 * <p>The occulted body is given through a {@link PVCoordinatesProvider} and its radius in meters. It is modeled as a sphere.
32 * </p>
33 * <p>Since v10.0 the occulting body is a {@link OneAxisEllipsoid}, before it was modeled as a sphere.
34 * <br>It was changed to precisely model Solar eclipses by the Earth, especially for Low Earth Orbits.
35 * <br>If you want eclipses by a spherical occulting body, set its flattening to 0. when defining its OneAxisEllipsoid model..
36 * </p>
37 * <p>The {@link #withUmbra} or {@link #withPenumbra} methods will tell you if the event is triggered when complete umbra/lighting
38 * is achieved or when entering/living the penumbra zone.
39 * <br>The default behavior is detecting complete umbra/lighting events.
40 * <br>If you want to have both, you'll need to set up two distinct detectors.
41 * </p>
42 * <p>The default implementation behavior is to {@link Action#CONTINUE continue}
43 * propagation when entering the eclipse and to {@link Action#STOP stop} propagation
44 * when exiting the eclipse.
45 * <br>This can be changed by calling {@link #withHandler(EventHandler)} after construction.
46 * </p>
47 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
48 * @author Pascal Parraud
49 * @author Luc Maisonobe
50 */
51 public class EclipseDetector extends AbstractDetector<EclipseDetector> {
52
53 /** Occultation engine.
54 * @since 12.0
55 */
56 private final OccultationEngine occultationEngine;
57
58 /** Umbra, if true, or penumbra, if false, detection flag. */
59 private final boolean totalEclipse;
60
61 /** Margin to apply to eclipse angle. */
62 private final double margin;
63
64 /** Build a new eclipse detector.
65 * <p>The new instance is a total eclipse (umbra) detector with default
66 * values for maximal checking interval ({@link #DEFAULT_MAXCHECK})
67 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
68 * @param occulted the body to be occulted
69 * @param occultedRadius the radius of the body to be occulted (m)
70 * @param occulting the occulting body
71 * @since 12.0
72 */
73 public EclipseDetector(final ExtendedPVCoordinatesProvider occulted, final double occultedRadius,
74 final OneAxisEllipsoid occulting) {
75 this(new OccultationEngine(occulted, occultedRadius, occulting));
76 }
77
78 /** Build a new eclipse detector.
79 * <p>The new instance is a total eclipse (umbra) detector with default
80 * values for maximal checking interval ({@link #DEFAULT_MAXCHECK})
81 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
82 * @param occultationEngine occultation engine
83 * @since 12.0
84 */
85 public EclipseDetector(final OccultationEngine occultationEngine) {
86 this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER,
87 new StopOnIncreasing(),
88 occultationEngine, 0.0, true);
89 }
90
91 /** Protected constructor with full parameters.
92 * <p>
93 * This constructor is not public as users are expected to use the builder
94 * API with the various {@code withXxx()} methods to set up the instance
95 * in a readable manner without using a huge amount of parameters.
96 * </p>
97 * @param maxCheck maximum checking interval
98 * @param threshold convergence threshold (s)
99 * @param maxIter maximum number of iterations in the event time search
100 * @param handler event handler to call at event occurrences
101 * @param occultationEngine occultation engine
102 * @param margin to apply to eclipse angle (rad)
103 * @param totalEclipse umbra (true) or penumbra (false) detection flag
104 * @since 12.0
105 */
106 protected EclipseDetector(final AdaptableInterval maxCheck, final double threshold,
107 final int maxIter, final EventHandler handler,
108 final OccultationEngine occultationEngine, final double margin, final boolean totalEclipse) {
109 super(maxCheck, threshold, maxIter, handler);
110 this.occultationEngine = occultationEngine;
111 this.margin = margin;
112 this.totalEclipse = totalEclipse;
113 }
114
115 /** {@inheritDoc} */
116 @Override
117 protected EclipseDetector create(final AdaptableInterval newMaxCheck, final double newThreshold,
118 final int nawMaxIter, final EventHandler newHandler) {
119 return new EclipseDetector(newMaxCheck, newThreshold, nawMaxIter, newHandler,
120 occultationEngine, margin, totalEclipse);
121 }
122
123 /**
124 * Setup the detector to full umbra detection.
125 * <p>
126 * This will override a penumbra/umbra flag if it has been configured previously.
127 * </p>
128 * @return a new detector with updated configuration (the instance is not changed)
129 * @see #withPenumbra()
130 * @since 6.1
131 */
132 public EclipseDetector withUmbra() {
133 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
134 occultationEngine, margin, true);
135 }
136
137 /**
138 * Setup the detector to penumbra detection.
139 * <p>
140 * This will override a penumbra/umbra flag if it has been configured previously.
141 * </p>
142 * @return a new detector with updated configuration (the instance is not changed)
143 * @see #withUmbra()
144 * @since 6.1
145 */
146 public EclipseDetector withPenumbra() {
147 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
148 occultationEngine, margin, false);
149 }
150
151 /**
152 * Setup a margin to angle detection.
153 * <p>
154 * A positive margin implies eclipses are "larger" hence entry occurs earlier and exit occurs later
155 * than a detector with 0 margin.
156 * </p>
157 * @param newMargin angular margin to apply to eclipse detection (rad)
158 * @return a new detector with updated configuration (the instance is not changed)
159 * @since 12.0
160 */
161 public EclipseDetector withMargin(final double newMargin) {
162 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
163 occultationEngine, newMargin, totalEclipse);
164 }
165
166 /** Get the angular margin used for eclipse detection.
167 * @return angular margin used for eclipse detection (rad)
168 * @since 12.0
169 */
170 public double getMargin() {
171 return margin;
172 }
173
174 /** Get the occultation engine.
175 * @return occultation engine
176 * @since 12.0
177 */
178 public OccultationEngine getOccultationEngine() {
179 return occultationEngine;
180 }
181
182 /** Get the total eclipse detection flag.
183 * @return the total eclipse detection flag (true for umbra events detection,
184 * false for penumbra events detection)
185 */
186 public boolean getTotalEclipse() {
187 return totalEclipse;
188 }
189
190 /** Compute the value of the switching function.
191 * This function becomes negative when entering the region of shadow
192 * and positive when exiting.
193 * @param s the current state information: date, kinematics, attitude
194 * @return value of the switching function
195 */
196 public double g(final SpacecraftState s) {
197 final OccultationEngine.OccultationAngles angles = occultationEngine.angles(s);
198 return totalEclipse ?
199 (angles.getSeparation() - angles.getLimbRadius() + angles.getOccultedApparentRadius() + margin) :
200 (angles.getSeparation() - angles.getLimbRadius() - angles.getOccultedApparentRadius() + margin);
201 }
202
203 }