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