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.geometry.euclidean.threed.Vector3D;
20 import org.hipparchus.ode.events.Action;
21 import org.hipparchus.util.FastMath;
22 import org.orekit.bodies.OneAxisEllipsoid;
23 import org.orekit.propagation.SpacecraftState;
24 import org.orekit.propagation.events.handlers.EventHandler;
25 import org.orekit.propagation.events.handlers.StopOnIncreasing;
26 import org.orekit.utils.PVCoordinatesProvider;
27
28 /** Finder for satellite eclipse related events.
29 * <p>This class finds eclipse events, i.e. satellite within umbra (total
30 * eclipse) or penumbra (partial eclipse).</p>
31 * <p>The occulted body is given through a {@link PVCoordinatesProvider} and its radius in meters. It is modeled as a sphere.
32 * </p>
33 * <p>Since v10.0 the occulting body is a {@link OneAxisEllipsoid}, before it was modeled as a sphere.
34 * <br>It was changed to precisely model Solar eclipses by the Earth, especially for Low Earth Orbits.
35 * <br>If you want eclipses by a spherical occulting body, set its flattening to 0. when defining its OneAxisEllipsoid model..
36 * </p>
37 * <p>The {@link #withUmbra} or {@link #withPenumbra} methods will tell you if the event is triggered when complete umbra/lighting
38 * is achieved or when entering/living the penumbra zone.
39 * <br>The default behavior is detecting complete umbra/lighting events.
40 * <br>If you want to have both, you'll need to set up two distinct detectors.
41 * </p>
42 * <p>The default implementation behavior is to {@link Action#CONTINUE continue}
43 * propagation when entering the eclipse and to {@link Action#STOP stop} propagation
44 * when exiting the eclipse.
45 * <br>This can be changed by calling {@link #withHandler(EventHandler)} after construction.
46 * </p>
47 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
48 * @author Pascal Parraud
49 * @author Luc Maisonobe
50 */
51 public class EclipseDetector extends AbstractDetector<EclipseDetector> {
52
53 /** Occulting body. */
54 private final OneAxisEllipsoid occulting;
55
56 /** Occulted body. */
57 private final PVCoordinatesProvider occulted;
58
59 /** Occulted body radius (m). */
60 private final double occultedRadius;
61
62 /** Umbra, if true, or penumbra, if false, detection flag. */
63 private final boolean totalEclipse;
64
65 /** Build a new eclipse detector.
66 * <p>The new instance is a total eclipse (umbra) detector with default
67 * values for maximal checking interval ({@link #DEFAULT_MAXCHECK})
68 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p>
69 * @param occulted the body to be occulted
70 * @param occultedRadius the radius of the body to be occulted (m)
71 * @param occulting the occulting body
72 * @since 10.0
73 */
74 public EclipseDetector(final PVCoordinatesProvider occulted, final double occultedRadius,
75 final OneAxisEllipsoid occulting) {
76 this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER,
77 new StopOnIncreasing<EclipseDetector>(),
78 occulted, occultedRadius, occulting, true);
79 }
80
81 /** Private constructor with full parameters.
82 * <p>
83 * This constructor is private as users are expected to use the builder
84 * API with the various {@code withXxx()} methods to set up the instance
85 * in a readable manner without using a huge amount of parameters.
86 * </p>
87 * @param maxCheck maximum checking interval (s)
88 * @param threshold convergence threshold (s)
89 * @param maxIter maximum number of iterations in the event time search
90 * @param handler event handler to call at event occurrences
91 * @param occulted the body to be occulted
92 * @param occultedRadius the radius of the body to be occulted in meters
93 * @param occulting the occulting body
94 * @param totalEclipse umbra (true) or penumbra (false) detection flag
95 * @since 10.0
96 */
97 private EclipseDetector(final double maxCheck, final double threshold,
98 final int maxIter, final EventHandler<? super EclipseDetector> handler,
99 final PVCoordinatesProvider occulted, final double occultedRadius,
100 final OneAxisEllipsoid occulting, final boolean totalEclipse) {
101 super(maxCheck, threshold, maxIter, handler);
102 this.occulted = occulted;
103 this.occultedRadius = FastMath.abs(occultedRadius);
104 this.occulting = occulting;
105 this.totalEclipse = totalEclipse;
106 }
107
108 /** {@inheritDoc} */
109 @Override
110 protected EclipseDetector create(final double newMaxCheck, final double newThreshold,
111 final int nawMaxIter, final EventHandler<? super EclipseDetector> newHandler) {
112 return new EclipseDetector(newMaxCheck, newThreshold, nawMaxIter, newHandler,
113 occulted, occultedRadius, occulting, totalEclipse);
114 }
115
116 /**
117 * Setup the detector to full umbra detection.
118 * <p>
119 * This will override a penumbra/umbra flag if it has been configured previously.
120 * </p>
121 * @return a new detector with updated configuration (the instance is not changed)
122 * @see #withPenumbra()
123 * @since 6.1
124 */
125 public EclipseDetector withUmbra() {
126 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
127 occulted, occultedRadius, occulting, true);
128 }
129
130 /**
131 * Setup the detector to penumbra detection.
132 * <p>
133 * This will override a penumbra/umbra flag if it has been configured previously.
134 * </p>
135 * @return a new detector with updated configuration (the instance is not changed)
136 * @see #withUmbra()
137 * @since 6.1
138 */
139 public EclipseDetector withPenumbra() {
140 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(),
141 occulted, occultedRadius, occulting, false);
142 }
143
144 /** Getter for the occulting body.
145 * @return the occulting body
146 */
147 public OneAxisEllipsoid getOcculting() {
148 return occulting;
149 }
150
151 /** Getter for the occulted body.
152 * @return the occulted body
153 */
154 public PVCoordinatesProvider getOcculted() {
155 return occulted;
156 }
157
158 /** Getter for the occultedRadius.
159 * @return the occultedRadius
160 */
161 public double getOccultedRadius() {
162 return occultedRadius;
163 }
164
165 /** Get the total eclipse detection flag.
166 * @return the total eclipse detection flag (true for umbra events detection,
167 * false for penumbra events detection)
168 */
169 public boolean getTotalEclipse() {
170 return totalEclipse;
171 }
172
173 /** Compute the value of the switching function.
174 * This function becomes negative when entering the region of shadow
175 * and positive when exiting.
176 * @param s the current state information: date, kinematics, attitude
177 * @return value of the switching function
178 */
179 public double g(final SpacecraftState s) {
180 final Vector3D pted = occulted.getPVCoordinates(s.getDate(), occulting.getBodyFrame()).getPosition();
181 final Vector3D psat = s.getPVCoordinates(occulting.getBodyFrame()).getPosition();
182 final Vector3D plimb = occulting.pointOnLimb(psat, pted);
183 final Vector3D ps = psat.subtract(pted);
184 final Vector3D pi = psat.subtract(plimb);
185 final double angle = Vector3D.angle(ps, psat);
186 final double rs = FastMath.asin(occultedRadius / ps.getNorm());
187 if (Double.isNaN(rs)) {
188 return FastMath.PI;
189 }
190 final double ro = Vector3D.angle(pi, psat);
191 return totalEclipse ? (angle - ro + rs) : (angle - ro - rs);
192 }
193 }