1 /* Contributed in the public domain.
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
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.NoSuchElementException;
25
26 import org.hipparchus.util.FastMath;
27 import org.orekit.propagation.SpacecraftState;
28 import org.orekit.propagation.events.handlers.ContinueOnEvent;
29 import org.orekit.propagation.events.handlers.EventHandler;
30 import org.orekit.time.AbsoluteDate;
31
32 /**
33 * This class provides AND and OR operations for event detectors. This class treats
34 * positive values of the g function as true and negative values as false.
35 *
36 * <p> One example for an imaging satellite might be to only detect events when a
37 * satellite is overhead (elevation > 0) AND when the ground point is sunlit (Sun
38 * elevation > 0). Another slightly contrived example using the OR operator would be to
39 * detect access to a set of ground stations and only report events when the satellite
40 * enters or leaves the field of view of the set, but not hand-offs between the ground
41 * stations.
42 *
43 * <p> For the BooleanDetector is important that the sign of the g function of the
44 * underlying event detector is not arbitrary, but has a semantic meaning, e.g. in or out,
45 * true or false. This class works well with event detectors that detect entry to or exit
46 * from a region, e.g. {@link EclipseDetector}, {@link ElevationDetector}, {@link
47 * LatitudeCrossingDetector}. Using this detector with detectors that are not based on
48 * entry to or exit from a region, e.g. {@link DateDetector}, {@link
49 * LongitudeCrossingDetector}, will likely lead to unexpected results. To apply conditions
50 * to this latter type of event detectors a {@link EventEnablingPredicateFilter} is
51 * usually more appropriate.
52 *
53 * @author Evan Ward
54 * @see #andCombine(Collection)
55 * @see #orCombine(Collection)
56 * @see #notCombine(EventDetector)
57 * @see EventEnablingPredicateFilter
58 * @see EventSlopeFilter
59 */
60 public class BooleanDetector extends AbstractDetector<BooleanDetector> {
61
62 /** Original detectors: the operands. */
63 private final List<EventDetector> detectors;
64
65 /** The composition function. Should be associative for predictable behavior. */
66 private final Operator operator;
67
68 /**
69 * Private constructor with all the parameters.
70 *
71 * @param detectors the operands.
72 * @param operator reduction operator to apply to value of the g function of the
73 * operands.
74 * @param newMaxCheck max check interval in seconds.
75 * @param newThreshold convergence threshold in seconds.
76 * @param newMaxIter max iterations.
77 * @param newHandler event handler.
78 */
79 protected BooleanDetector(final List<EventDetector> detectors,
80 final Operator operator,
81 final AdaptableInterval newMaxCheck,
82 final double newThreshold,
83 final int newMaxIter,
84 final EventHandler newHandler) {
85 super(newMaxCheck, newThreshold, newMaxIter, newHandler);
86 this.detectors = detectors;
87 this.operator = operator;
88 }
89
90 /**
91 * Create a new event detector that is the logical AND of the given event detectors.
92 *
93 * <p> The created event detector's g function is positive if and only if the g
94 * functions of all detectors in {@code detectors} are positive.
95 *
96 * <p> The starting interval, threshold, and iteration count are set to the most
97 * stringent (minimum) of all the {@code detectors}. The event handlers of the
98 * underlying {@code detectors} are not used, instead the default handler is {@link
99 * ContinueOnEvent}.
100 *
101 * @param detectors the operands. Must contain at least one detector.
102 * @return a new event detector that is the logical AND of the operands.
103 * @throws NoSuchElementException if {@code detectors} is empty.
104 * @see BooleanDetector
105 * @see #andCombine(Collection)
106 * @see #orCombine(EventDetector...)
107 * @see #notCombine(EventDetector)
108 */
109 public static BooleanDetector andCombine(final EventDetector... detectors) {
110 return andCombine(Arrays.asList(detectors));
111 }
112
113 /**
114 * Create a new event detector that is the logical AND of the given event detectors.
115 *
116 * <p> The created event detector's g function is positive if and only if the g
117 * functions of all detectors in {@code detectors} are positive.
118 *
119 * <p> The starting interval, threshold, and iteration count are set to the most
120 * stringent (minimum) of the {@code detectors}. The event handlers of the
121 * underlying {@code detectors} are not used, instead the default handler is {@link
122 * ContinueOnEvent}.
123 *
124 * @param detectors the operands. Must contain at least one detector.
125 * @return a new event detector that is the logical AND of the operands.
126 * @throws NoSuchElementException if {@code detectors} is empty.
127 * @see BooleanDetector
128 * @see #andCombine(EventDetector...)
129 * @see #orCombine(Collection)
130 * @see #notCombine(EventDetector)
131 */
132 public static BooleanDetector andCombine(final Collection<? extends EventDetector> detectors) {
133
134 return new BooleanDetector(new ArrayList<>(detectors), // copy for immutability
135 Operator.AND,
136 s -> {
137 double minInterval = Double.POSITIVE_INFINITY;
138 for (final EventDetector detector : detectors) {
139 minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s));
140 }
141 return minInterval;
142 },
143 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
144 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(),
145 new ContinueOnEvent());
146 }
147
148 /**
149 * Create a new event detector that is the logical OR of the given event detectors.
150 *
151 * <p> The created event detector's g function is positive if and only if at least
152 * one of g functions of the event detectors in {@code detectors} is positive.
153 *
154 * <p> The starting interval, threshold, and iteration count are set to the most
155 * stringent (minimum) of the {@code detectors}. The event handlers of the
156 * underlying EventDetectors are not used, instead the default handler is {@link
157 * ContinueOnEvent}.
158 *
159 * @param detectors the operands. Must contain at least one detector.
160 * @return a new event detector that is the logical OR of the operands.
161 * @throws NoSuchElementException if {@code detectors} is empty.
162 * @see BooleanDetector
163 * @see #orCombine(Collection)
164 * @see #andCombine(EventDetector...)
165 * @see #notCombine(EventDetector)
166 */
167 public static BooleanDetector orCombine(final EventDetector... detectors) {
168 return orCombine(Arrays.asList(detectors));
169 }
170
171 /**
172 * Create a new event detector that is the logical OR of the given event detectors.
173 *
174 * <p> The created event detector's g function is positive if and only if at least
175 * one of g functions of the event detectors in {@code detectors} is positive.
176 *
177 * <p> The starting interval, threshold, and iteration count are set to the most
178 * stringent (minimum) of the {@code detectors}. The event handlers of the
179 * underlying EventDetectors are not used, instead the default handler is {@link
180 * ContinueOnEvent}.
181 *
182 * @param detectors the operands. Must contain at least one detector.
183 * @return a new event detector that is the logical OR of the operands.
184 * @throws NoSuchElementException if {@code detectors} is empty.
185 * @see BooleanDetector
186 * @see #orCombine(EventDetector...)
187 * @see #andCombine(Collection)
188 * @see #notCombine(EventDetector)
189 */
190 public static BooleanDetector orCombine(final Collection<? extends EventDetector> detectors) {
191
192 return new BooleanDetector(new ArrayList<>(detectors), // copy for immutability
193 Operator.OR,
194 s -> {
195 double minInterval = Double.POSITIVE_INFINITY;
196 for (final EventDetector detector : detectors) {
197 minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s));
198 }
199 return minInterval;
200 },
201 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
202 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(),
203 new ContinueOnEvent());
204 }
205
206 /**
207 * Create a new event detector that negates the g function of another detector.
208 *
209 * <p> This detector will be initialized with the same {@link
210 * EventDetector#getMaxCheckInterval()}, {@link EventDetector#getThreshold()}, and
211 * {@link EventDetector#getMaxIterationCount()} as {@code detector}. The event handler
212 * of the underlying detector is not used, instead the default handler is {@link
213 * ContinueOnEvent}.
214 *
215 * @param detector to negate.
216 * @return an new event detector whose g function is the same magnitude but opposite
217 * sign of {@code detector}.
218 * @see #andCombine(Collection)
219 * @see #orCombine(Collection)
220 * @see BooleanDetector
221 */
222 public static NegateDetector notCombine(final EventDetector detector) {
223 return new NegateDetector(detector);
224 }
225
226 @Override
227 public double g(final SpacecraftState s) {
228 // can't use stream/lambda here because g(s) throws a checked exception
229 // so write out and combine the map and reduce loops
230 double ret = Double.NaN; // return value
231 boolean first = true;
232 for (final EventDetector detector : detectors) {
233 if (first) {
234 ret = detector.g(s);
235 first = false;
236 } else {
237 ret = operator.combine(ret, detector.g(s));
238 }
239 }
240 // return the result of applying the operator to all operands
241 return ret;
242 }
243
244 @Override
245 protected BooleanDetector create(final AdaptableInterval newMaxCheck,
246 final double newThreshold,
247 final int newMaxIter,
248 final EventHandler newHandler) {
249 return new BooleanDetector(detectors, operator, newMaxCheck, newThreshold,
250 newMaxIter, newHandler);
251 }
252
253 @Override
254 public void init(final SpacecraftState s0,
255 final AbsoluteDate t) {
256 super.init(s0, t);
257 for (final EventDetector detector : detectors) {
258 detector.init(s0, t);
259 }
260 }
261
262 /**
263 * Get the list of original detectors.
264 * @return the list of original detectors
265 * @since 10.2
266 */
267 public List<EventDetector> getDetectors() {
268 return new ArrayList<EventDetector>(detectors);
269 }
270
271 /** Local class for operator. */
272 private enum Operator {
273
274 /** And operator. */
275 AND() {
276
277 @Override
278 /** {@inheritDoc} */
279 public double combine(final double g1, final double g2) {
280 return FastMath.min(g1, g2);
281 }
282
283 },
284
285 /** Or operator. */
286 OR() {
287
288 @Override
289 /** {@inheritDoc} */
290 public double combine(final double g1, final double g2) {
291 return FastMath.max(g1, g2);
292 }
293
294 };
295
296 /** Combine two g functions evaluations.
297 * @param g1 first evaluation
298 * @param g2 second evaluation
299 * @return combined evaluation
300 */
301 public abstract double combine(double g1, double g2);
302
303 };
304
305 }