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