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