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 private BooleanDetector(final List<EventDetector> detectors,
80 final Operator operator,
81 final double newMaxCheck,
82 final double newThreshold,
83 final int newMaxIter,
84 final EventHandler<? super BooleanDetector> 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 detectors.stream().map(EventDetector::getMaxCheckInterval).min(Double::compareTo).get(),
137 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
138 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(),
139 new ContinueOnEvent<>());
140 }
141
142 /**
143 * Create a new event detector that is the logical OR or the given event detectors.
144 *
145 * <p> The created event detector's g function is positive if and only if at least
146 * one of g functions of the event detectors in {@code detectors} is positive.
147 *
148 * <p> The starting interval, threshold, and iteration count are set to the most
149 * stringent (minimum) of the {@code detectors}. The event handlers of the
150 * underlying EventDetectors are not used, instead the default handler is {@link
151 * ContinueOnEvent}.
152 *
153 * @param detectors the operands. Must contain at least one detector.
154 * @return a new event detector that is the logical OR of the operands.
155 * @throws NoSuchElementException if {@code detectors} is empty.
156 * @see BooleanDetector
157 * @see #orCombine(Collection)
158 * @see #andCombine(EventDetector...)
159 * @see #notCombine(EventDetector)
160 */
161 public static BooleanDetector orCombine(final EventDetector... detectors) {
162 return orCombine(Arrays.asList(detectors));
163 }
164
165 /**
166 * Create a new event detector that is the logical OR or the given event detectors.
167 *
168 * <p> The created event detector's g function is positive if and only if at least
169 * one of g functions of the event detectors in {@code detectors} is positive.
170 *
171 * <p> The starting interval, threshold, and iteration count are set to the most
172 * stringent (minimum) of the {@code detectors}. The event handlers of the
173 * underlying EventDetectors are not used, instead the default handler is {@link
174 * ContinueOnEvent}.
175 *
176 * @param detectors the operands. Must contain at least one detector.
177 * @return a new event detector that is the logical OR of the operands.
178 * @throws NoSuchElementException if {@code detectors} is empty.
179 * @see BooleanDetector
180 * @see #orCombine(EventDetector...)
181 * @see #andCombine(Collection)
182 * @see #notCombine(EventDetector)
183 */
184 public static BooleanDetector orCombine(final Collection<? extends EventDetector> detectors) {
185
186 return new BooleanDetector(new ArrayList<>(detectors), // copy for immutability
187 Operator.OR,
188 detectors.stream().map(EventDetector::getMaxCheckInterval).min(Double::compareTo).get(),
189 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
190 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get(),
191 new ContinueOnEvent<>());
192 }
193
194 /**
195 * Create a new event detector that negates the g function of another detector.
196 *
197 * <p> This detector will be initialized with the same {@link
198 * EventDetector#getMaxCheckInterval()}, {@link EventDetector#getThreshold()}, and
199 * {@link EventDetector#getMaxIterationCount()} as {@code detector}. The event handler
200 * of the underlying detector is not used, instead the default handler is {@link
201 * ContinueOnEvent}.
202 *
203 * @param detector to negate.
204 * @return an new event detector whose g function is the same magnitude but opposite
205 * sign of {@code detector}.
206 * @see #andCombine(Collection)
207 * @see #orCombine(Collection)
208 * @see BooleanDetector
209 */
210 public static NegateDetector notCombine(final EventDetector detector) {
211 return new NegateDetector(detector);
212 }
213
214 @Override
215 public double g(final SpacecraftState s) {
216 // can't use stream/lambda here because g(s) throws a checked exception
217 // so write out and combine the map and reduce loops
218 double ret = Double.NaN; // return value
219 boolean first = true;
220 for (final EventDetector detector : detectors) {
221 if (first) {
222 ret = detector.g(s);
223 first = false;
224 } else {
225 ret = operator.combine(ret, detector.g(s));
226 }
227 }
228 // return the result of applying the operator to all operands
229 return ret;
230 }
231
232 @Override
233 protected BooleanDetector create(final double newMaxCheck,
234 final double newThreshold,
235 final int newMaxIter,
236 final EventHandler<? super BooleanDetector> newHandler) {
237 return new BooleanDetector(detectors, operator, newMaxCheck, newThreshold,
238 newMaxIter, newHandler);
239 }
240
241 @Override
242 public void init(final SpacecraftState s0,
243 final AbsoluteDate t) {
244 super.init(s0, t);
245 for (final EventDetector detector : detectors) {
246 detector.init(s0, t);
247 }
248 }
249
250 /**
251 * Get the list of original detectors.
252 * @return the list of original detectors
253 * @since 10.2
254 */
255 public List<EventDetector> getDetectors() {
256 return new ArrayList<EventDetector>(detectors);
257 }
258
259 /** Local class for operator. */
260 private enum Operator {
261
262 /** And operator. */
263 AND() {
264
265 @Override
266 /** {@inheritDoc} */
267 public double combine(final double g1, final double g2) {
268 return FastMath.min(g1, g2);
269 }
270
271 },
272
273 /** Or operator. */
274 OR() {
275
276 @Override
277 /** {@inheritDoc} */
278 public double combine(final double g1, final double g2) {
279 return FastMath.max(g1, g2);
280 }
281
282 };
283
284 /** Combine two g functions evaluations.
285 * @param g1 first evaluation
286 * @param g2 second evaluation
287 * @return combined evaluation
288 */
289 public abstract double combine(double g1, double g2);
290
291 };
292
293 }