1 /* Copyright 2002-2023 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.handlers.EventHandler; 23 import org.orekit.propagation.events.handlers.StopOnIncreasing; 24 import org.orekit.utils.ExtendedPVCoordinatesProvider; 25 import org.orekit.utils.OccultationEngine; 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 /** Occultation engine. 54 * @since 12.0 55 */ 56 private final OccultationEngine occultationEngine; 57 58 /** Umbra, if true, or penumbra, if false, detection flag. */ 59 private final boolean totalEclipse; 60 61 /** Margin to apply to eclipse angle. */ 62 private final double margin; 63 64 /** Build a new eclipse detector. 65 * <p>The new instance is a total eclipse (umbra) detector with default 66 * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) 67 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p> 68 * @param occulted the body to be occulted 69 * @param occultedRadius the radius of the body to be occulted (m) 70 * @param occulting the occulting body 71 * @since 12.0 72 */ 73 public EclipseDetector(final ExtendedPVCoordinatesProvider occulted, final double occultedRadius, 74 final OneAxisEllipsoid occulting) { 75 this(new OccultationEngine(occulted, occultedRadius, occulting)); 76 } 77 78 /** Build a new eclipse detector. 79 * <p>The new instance is a total eclipse (umbra) detector with default 80 * values for maximal checking interval ({@link #DEFAULT_MAXCHECK}) 81 * and convergence threshold ({@link #DEFAULT_THRESHOLD}).</p> 82 * @param occultationEngine occultation engine 83 * @since 12.0 84 */ 85 public EclipseDetector(final OccultationEngine occultationEngine) { 86 this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, 87 new StopOnIncreasing(), 88 occultationEngine, 0.0, true); 89 } 90 91 /** Protected constructor with full parameters. 92 * <p> 93 * This constructor is not public as users are expected to use the builder 94 * API with the various {@code withXxx()} methods to set up the instance 95 * in a readable manner without using a huge amount of parameters. 96 * </p> 97 * @param maxCheck maximum checking interval 98 * @param threshold convergence threshold (s) 99 * @param maxIter maximum number of iterations in the event time search 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.0 105 */ 106 protected EclipseDetector(final AdaptableInterval maxCheck, final double threshold, 107 final int maxIter, final EventHandler handler, 108 final OccultationEngine occultationEngine, final double margin, final boolean totalEclipse) { 109 super(maxCheck, threshold, maxIter, handler); 110 this.occultationEngine = occultationEngine; 111 this.margin = margin; 112 this.totalEclipse = totalEclipse; 113 } 114 115 /** {@inheritDoc} */ 116 @Override 117 protected EclipseDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, 118 final int nawMaxIter, final EventHandler newHandler) { 119 return new EclipseDetector(newMaxCheck, newThreshold, nawMaxIter, newHandler, 120 occultationEngine, margin, totalEclipse); 121 } 122 123 /** 124 * Setup the detector to full umbra detection. 125 * <p> 126 * This will override a penumbra/umbra flag if it has been configured previously. 127 * </p> 128 * @return a new detector with updated configuration (the instance is not changed) 129 * @see #withPenumbra() 130 * @since 6.1 131 */ 132 public EclipseDetector withUmbra() { 133 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), 134 occultationEngine, margin, true); 135 } 136 137 /** 138 * Setup the detector to penumbra detection. 139 * <p> 140 * This will override a penumbra/umbra flag if it has been configured previously. 141 * </p> 142 * @return a new detector with updated configuration (the instance is not changed) 143 * @see #withUmbra() 144 * @since 6.1 145 */ 146 public EclipseDetector withPenumbra() { 147 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), 148 occultationEngine, margin, false); 149 } 150 151 /** 152 * Setup a margin to angle detection. 153 * <p> 154 * A positive margin implies eclipses are "larger" hence entry occurs earlier and exit occurs later 155 * than a detector with 0 margin. 156 * </p> 157 * @param newMargin angular margin to apply to eclipse detection (rad) 158 * @return a new detector with updated configuration (the instance is not changed) 159 * @since 12.0 160 */ 161 public EclipseDetector withMargin(final double newMargin) { 162 return new EclipseDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), getHandler(), 163 occultationEngine, newMargin, totalEclipse); 164 } 165 166 /** Get the angular margin used for eclipse detection. 167 * @return angular margin used for eclipse detection (rad) 168 * @since 12.0 169 */ 170 public double getMargin() { 171 return margin; 172 } 173 174 /** Get the occultation engine. 175 * @return occultation engine 176 * @since 12.0 177 */ 178 public OccultationEngine getOccultationEngine() { 179 return occultationEngine; 180 } 181 182 /** Get the total eclipse detection flag. 183 * @return the total eclipse detection flag (true for umbra events detection, 184 * false for penumbra events detection) 185 */ 186 public boolean getTotalEclipse() { 187 return totalEclipse; 188 } 189 190 /** Compute the value of the switching function. 191 * This function becomes negative when entering the region of shadow 192 * and positive when exiting. 193 * @param s the current state information: date, kinematics, attitude 194 * @return value of the switching function 195 */ 196 public double g(final SpacecraftState s) { 197 final OccultationEngine.OccultationAngles angles = occultationEngine.angles(s); 198 return totalEclipse ? 199 (angles.getSeparation() - angles.getLimbRadius() + angles.getOccultedApparentRadius() + margin) : 200 (angles.getSeparation() - angles.getLimbRadius() - angles.getOccultedApparentRadius() + margin); 201 } 202 203 }