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 }