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.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.ode.events.Action;
22  import org.orekit.bodies.OneAxisEllipsoid;
23  import org.orekit.propagation.FieldSpacecraftState;
24  import org.orekit.propagation.events.functions.EventFunctionModifier;
25  import org.orekit.propagation.events.functions.PenumbraEventFunction;
26  import org.orekit.propagation.events.functions.UmbraEventFunction;
27  import org.orekit.propagation.events.handlers.EventHandler;
28  import org.orekit.propagation.events.handlers.FieldEventHandler;
29  import org.orekit.propagation.events.handlers.FieldStopOnIncreasing;
30  import org.orekit.utils.ExtendedPositionProvider;
31  import org.orekit.utils.OccultationEngine;
32  
33  /** Finder for satellite eclipse related events.
34   * <p>This class finds eclipse events, i.e. satellite within umbra (total
35   * eclipse) or penumbra (partial eclipse).</p>
36   * <p>The default implementation behavior is to {@link Action#CONTINUE continue}
37   * propagation when entering the eclipse and to {@link Action#STOP stop} propagation
38   * when exiting the eclipse. This can be changed by calling {@link
39   * #withHandler(FieldEventHandler)} after construction.</p>
40   * @param <T> the type of the field elements
41   * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
42   * @author Pascal Parraud
43   */
44  public class FieldEclipseDetector<T extends CalculusFieldElement<T>> extends FieldAbstractDetector<FieldEclipseDetector<T>, T> {
45  
46      /** Occultation engine.
47       * @since 12.0
48       */
49      private final OccultationEngine occultationEngine;
50  
51      /** Umbra, if true, or penumbra, if false, detection flag. */
52      private final boolean totalEclipse;
53  
54      /** Margin to apply to eclipse angle. */
55      private final T margin;
56  
57      /** Build a new eclipse detector.
58       * <p>The new instance is a total eclipse (umbra) detector with default
59       * values for maximal checking interval ({@link #DEFAULT_MAX_CHECK})
60       * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
61       * @param field field used by default
62       * @param occulted the body to be occulted
63       * @param occultedRadius the radius of the body to be occulted (m)
64       * @param occulting the occulting body
65       * @since 12.0
66       */
67      public FieldEclipseDetector(final Field<T> field,
68                                  final ExtendedPositionProvider occulted, final double occultedRadius,
69                                  final OneAxisEllipsoid occulting) {
70          this(field, new OccultationEngine(occulted, occultedRadius, occulting));
71      }
72  
73      /** Build a new eclipse detector.
74       * <p>The new instance is a total eclipse (umbra) detector with default
75       * values for maximal checking interval ({@link #DEFAULT_MAX_CHECK})
76       * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
77       * @param field field used by default
78       * @param occultationEngine occultation engine
79       * @since 12.0
80       */
81      public FieldEclipseDetector(final Field<T> field, final OccultationEngine occultationEngine) {
82          this(new FieldEventDetectionSettings<>(field, EventDetectionSettings.getDefaultEventDetectionSettings()),
83                  new FieldStopOnIncreasing<>(), occultationEngine, field.getZero(), true);
84      }
85  
86      /** Protected constructor with full parameters.
87       * <p>
88       * This constructor is not public as users are expected to use the builder
89       * API with the various {@code withXxx()} methods to set up the instance
90       * in a readable manner without using a huge amount of parameters.
91       * </p>
92       * @param detectionSettings event detection settings
93       * @param handler event handler to call at event occurrences
94       * @param occultationEngine occultation engine
95       * @param margin to apply to eclipse angle (rad)
96       * @param totalEclipse umbra (true) or penumbra (false) detection flag
97       * @since 13.0
98       */
99      protected FieldEclipseDetector(final FieldEventDetectionSettings<T> detectionSettings, final FieldEventHandler<T> handler,
100                                    final OccultationEngine occultationEngine, final T margin, final boolean totalEclipse) {
101         super(EventFunctionModifier.addFieldValue(totalEclipse ? new UmbraEventFunction(occultationEngine) :
102                 new PenumbraEventFunction(occultationEngine), margin), detectionSettings, handler);
103         this.occultationEngine = occultationEngine;
104         this.margin            = margin;
105         this.totalEclipse      = totalEclipse;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     protected FieldEclipseDetector<T> create(final FieldEventDetectionSettings<T> detectionSettings,
111                                              final FieldEventHandler<T> newHandler) {
112         return new FieldEclipseDetector<>(detectionSettings, newHandler, occultationEngine, margin, totalEclipse);
113     }
114 
115     /**
116      * Setup the detector to full umbra detection.
117      * <p>
118      * This will override a penumbra/umbra flag if it has been configured previously.
119      * </p>
120      * @return a new detector with updated configuration (the instance is not changed)
121      * @see #withPenumbra()
122      * @since 6.1
123      */
124     public FieldEclipseDetector<T> withUmbra() {
125         return new FieldEclipseDetector<>(getDetectionSettings(), getHandler(),
126                                           occultationEngine, margin, true);
127     }
128 
129     /**
130      * Setup the detector to penumbra detection.
131      * <p>
132      * This will override a penumbra/umbra flag if it has been configured previously.
133      * </p>
134      * @return a new detector with updated configuration (the instance is not changed)
135      * @see #withUmbra()
136      * @since 6.1
137      */
138     public FieldEclipseDetector<T> withPenumbra() {
139         return new FieldEclipseDetector<>(getDetectionSettings(), getHandler(),
140                                           occultationEngine, margin, false);
141     }
142 
143     /**
144      * Setup a margin to angle detection.
145      * <p>
146      * A positive margin implies eclipses are "larger" hence entry occurs earlier and exit occurs later
147      * than a detector with 0 margin.
148      * </p>
149      * @param newMargin angular margin to apply to eclipse detection (rad)
150      * @return a new detector with updated configuration (the instance is not changed)
151      * @since 12.0
152      */
153     public FieldEclipseDetector<T> withMargin(final T newMargin) {
154         return new FieldEclipseDetector<>(getDetectionSettings(), getHandler(),
155                                           occultationEngine, newMargin, totalEclipse);
156     }
157 
158     /** Get the angular margin used for eclipse detection.
159      * @return angular margin used for eclipse detection (rad)
160      * @since 12.0
161      */
162     public T getMargin() {
163         return margin;
164     }
165 
166     /** Get the occultation engine.
167      * @return occultation engine
168      * @since 12.0
169      */
170     public OccultationEngine getOccultationEngine() {
171         return occultationEngine;
172     }
173 
174     /** Get the total eclipse detection flag.
175      * @return the total eclipse detection flag (true for umbra events detection,
176      * false for penumbra events detection)
177      */
178     public boolean getTotalEclipse() {
179         return totalEclipse;
180     }
181 
182     /** Compute the value of the switching function.
183      * This function becomes negative when entering the region of shadow
184      * and positive when exiting.
185      * @param s the current state information: date, kinematics, attitude
186      * @return value of the switching function
187      */
188     public T g(final FieldSpacecraftState<T> s) {
189         return getEventFunction().value(s);
190     }
191 
192     /** {@inheritDoc} */
193     @Override
194     public EclipseDetector toEventDetector(final EventHandler eventHandler) {
195         final EclipseDetector baseDetector = new EclipseDetector(occultationEngine).withMargin(margin.getReal())
196                 .withHandler(eventHandler).withDetectionSettings(getDetectionSettings().toEventDetectionSettings());
197         return totalEclipse ? baseDetector.withUmbra() : baseDetector.withPenumbra();
198     }
199 }