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