1 /* Copyright 2002-2021 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.Field;
20 import org.hipparchus.CalculusFieldElement;
21 import org.hipparchus.geometry.euclidean.threed.Vector3D;
22 import org.hipparchus.ode.events.Action;
23 import org.hipparchus.util.FastMath;
24 import org.orekit.propagation.FieldSpacecraftState;
25 import org.orekit.propagation.events.handlers.FieldEventHandler;
26 import org.orekit.propagation.events.handlers.FieldStopOnIncreasing;
27 import org.orekit.utils.PVCoordinatesProvider;
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 * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
37 * @author Pascal Parraud
38 */
39 public class FieldEclipseDetector<T extends CalculusFieldElement<T>> extends FieldAbstractDetector<FieldEclipseDetector<T>, T> {
40
41
42 /** Occulting body. */
43 private final PVCoordinatesProvider occulting;
44
45 /** Occulting body radius (m). */
46 private final double occultingRadius;
47
48 /** Occulted body. */
49 private final PVCoordinatesProvider occulted;
50
51 /** Occulted body radius (m). */
52 private final double occultedRadius;
53
54 /** Umbra, if true, or penumbra, if false, detection flag. */
55 private boolean totalEclipse;
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_MAXCHECK})
60 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
61 * @param occulted the body to be occulted
62 * @param occultedRadius the radius of the body to be occulted (m)
63 * @param occulting the occulting body
64 * @param occultingRadius the occulting body radius (m)
65 * @param field field used by default
66 */
67 public FieldEclipseDetector(final PVCoordinatesProvider occulted, final double occultedRadius,
68 final PVCoordinatesProvider occulting, final double occultingRadius, final Field<T> field) {
69 this(field.getZero().add(DEFAULT_MAXCHECK), field.getZero().add(DEFAULT_THRESHOLD),
70 occulted, occultedRadius, occulting, occultingRadius);
71 }
72
73 /** Build a new eclipse detector.
74 * <p>The new instance is a total eclipse (umbra) detector with default
75 * value for convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
76 * <p>The maximal interval between eclipse checks should be smaller than
77 * the half duration of the minimal pass to handle, otherwise some short
78 * passes could be missed.</p>
79 * @param maxCheck maximal checking interval (s)
80 * @param occulted the body to be occulted
81 * @param occultedRadius the radius of the body to be occulted in meters
82 * @param occulting the occulting body
83 * @param occultingRadius the occulting body radius in meters
84 */
85 public FieldEclipseDetector(final T maxCheck,
86 final PVCoordinatesProvider occulted, final double occultedRadius,
87 final PVCoordinatesProvider occulting, final double occultingRadius) {
88 this(maxCheck, maxCheck.getField().getZero().add(DEFAULT_THRESHOLD),
89 occulted, occultedRadius, occulting, occultingRadius);
90 }
91
92 /** Build a new eclipse detector.
93 * <p>The new instance is a total eclipse (umbra) detector.</p>
94 * <p>The maximal interval between eclipse checks should be smaller than
95 * the half duration of the minimal pass to handle, otherwise some short
96 * passes could be missed.</p>
97 * @param maxCheck maximal checking interval (s)
98 * @param threshold convergence threshold (s)
99 * @param occulted the body to be occulted
100 * @param occultedRadius the radius of the body to be occulted in meters
101 * @param occulting the occulting body
102 * @param occultingRadius the occulting body radius in meters
103 */
104 public FieldEclipseDetector(final T maxCheck, final T threshold,
105 final PVCoordinatesProvider occulted, final double occultedRadius,
106 final PVCoordinatesProvider occulting, final double occultingRadius) {
107 this(maxCheck, threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<FieldEclipseDetector<T>, T>(),
108 occulted, occultedRadius, occulting, occultingRadius, true);
109 }
110
111 /** Private constructor with full parameters.
112 * <p>
113 * This constructor is private as users are expected to use the builder
114 * API with the various {@code withXxx()} methods to set up the instance
115 * in a readable manner without using a huge amount of parameters.
116 * </p>
117 * @param maxCheck maximum checking interval (s)
118 * @param threshold convergence threshold (s)
119 * @param maxIter maximum number of iterations in the event time search
120 * @param handler event handler to call at event occurrences
121 * @param occulted the body to be occulted
122 * @param occultedRadius the radius of the body to be occulted in meters
123 * @param occulting the occulting body
124 * @param occultingRadius the occulting body radius in meters
125 * @param totalEclipse umbra (true) or penumbra (false) detection flag
126 * @since 6.1
127 */
128 private FieldEclipseDetector(final T maxCheck, final T threshold,
129 final int maxIter, final FieldEventHandler<? super FieldEclipseDetector<T>, T> handler,
130 final PVCoordinatesProvider occulted, final double occultedRadius,
131 final PVCoordinatesProvider occulting, final double occultingRadius,
132 final boolean totalEclipse) {
133 super(maxCheck, threshold, maxIter, handler);
134 this.occulted = occulted;
135 this.occultedRadius = FastMath.abs(occultedRadius);
136 this.occulting = occulting;
137 this.occultingRadius = FastMath.abs(occultingRadius);
138 this.totalEclipse = totalEclipse;
139 }
140
141 /** {@inheritDoc} */
142 @Override
143 protected FieldEclipseDetector<T> create(final T newMaxCheck, final T newThreshold,
144 final int nawMaxIter, final FieldEventHandler<? super FieldEclipseDetector<T>, T> newHandler) {
145 return new FieldEclipseDetector<>(newMaxCheck, newThreshold, nawMaxIter, newHandler,
146 occulted, occultedRadius, occulting, occultingRadius, totalEclipse);
147 }
148
149 /**
150 * Setup the detector to full umbra detection.
151 * <p>
152 * This will override a penumbra/umbra flag if it has been configured previously.
153 * </p>
154 * @return a new detector with updated configuration (the instance is not changed)
155 * @see #withPenumbra()
156 * @since 6.1
157 */
158 public FieldEclipseDetector<T> withUmbra() {
159 return new FieldEclipseDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
160 occulted, occultedRadius, occulting, occultingRadius,
161 true);
162 }
163
164 /**
165 * Setup the detector to penumbra detection.
166 * <p>
167 * This will override a penumbra/umbra flag if it has been configured previously.
168 * </p>
169 * @return a new detector with updated configuration (the instance is not changed)
170 * @see #withUmbra()
171 * @since 6.1
172 */
173 public FieldEclipseDetector<T> withPenumbra() {
174 return new FieldEclipseDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
175 occulted, occultedRadius, occulting, occultingRadius,
176 false);
177 }
178
179 /** Get the occulting body.
180 * @return the occulting body
181 */
182 public PVCoordinatesProvider getOcculting() {
183 return occulting;
184 }
185
186 /** Get the occulting body radius (m).
187 * @return the occulting body radius
188 */
189 public double getOccultingRadius() {
190 return occultingRadius;
191 }
192
193 /** Get the occulted body.
194 * @return the occulted body
195 */
196 public PVCoordinatesProvider getOcculted() {
197 return occulted;
198 }
199
200 /** Get the occulted body radius (m).
201 * @return the occulted body radius
202 */
203 public double getOccultedRadius() {
204 return occultedRadius;
205 }
206
207 /** Get the total eclipse detection flag.
208 * @return the total eclipse detection flag (true for umbra events detection,
209 * false for penumbra events detection)
210 */
211 public boolean getTotalEclipse() {
212 return totalEclipse;
213 }
214
215 /** Compute the value of the switching function.
216 * This function becomes negative when entering the region of shadow
217 * and positive when exiting.
218 * @param s the current state information: date, kinematics, attitude
219 * @return value of the switching function
220 */
221 public T g(final FieldSpacecraftState<T> s) {
222 final T zero = s.getOrbit().getA().getField().getZero();
223 final Vector3D pted = occulted.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition();
224 final Vector3D ping = occulting.getPVCoordinates(s.getDate().toAbsoluteDate(), s.getFrame()).getPosition();
225 final Vector3D psat = s.toSpacecraftState().getPVCoordinates().getPosition();
226 final Vector3D ps = pted.subtract(psat);
227 final Vector3D po = ping.subtract(psat);
228 final double angle = Vector3D.angle(ps, po);
229 final double rs = FastMath.asin(occultedRadius / ps.getNorm());
230 if (Double.isNaN(rs)) {
231 return zero.getPi();
232 }
233 final double ro = FastMath.asin(occultingRadius / po.getNorm());
234 if (Double.isNaN(ro)) {
235 return zero.getPi().negate();
236 }
237 return totalEclipse ? (zero.add(angle - ro + rs)) : (zero.add(angle - ro - rs));
238 }
239
240 }