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 &gt; 0) AND when the ground point is sunlit (Sun
42   * elevation &gt; 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     @SuppressWarnings("unchecked")
139     public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> andCombine(final Collection<? extends FieldEventDetector<T>> detectors) {
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     @SuppressWarnings("unchecked")
195     public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> orCombine(final Collection<? extends FieldEventDetector<T>> detectors) {
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     /** {@inheritDoc} */
227     @Override
228     public boolean dependsOnTimeOnly() {
229         return getDetectors().stream().allMatch(FieldEventDetector::dependsOnTimeOnly);
230     }
231 
232     @Override
233     public T g(final FieldSpacecraftState<T> s) {
234         // can't use stream/lambda here because g(s) throws a checked exception
235         // so write out and combine the map and reduce loops
236         T ret = s.getDate().getField().getZero().newInstance(Double.NaN); // return value
237         boolean first = true;
238         for (final FieldEventDetector<T> detector : detectors) {
239             if (first) {
240                 ret = detector.g(s);
241                 first = false;
242             } else {
243                 ret = operator.combine(ret, detector.g(s));
244             }
245         }
246         // return the result of applying the operator to all operands
247         return ret;
248     }
249 
250     @Override
251     protected FieldBooleanDetector<T> create(final FieldEventDetectionSettings<T> detectionSettings,
252                                              final FieldEventHandler<T> newHandler) {
253         return new FieldBooleanDetector<>(detectors, operator, detectionSettings, newHandler);
254     }
255 
256     @Override
257     public void init(final FieldSpacecraftState<T> s0,
258                      final FieldAbsoluteDate<T> t) {
259         super.init(s0, t);
260         for (final FieldEventDetector<T> detector : detectors) {
261             detector.init(s0, t);
262         }
263     }
264 
265     @Override
266     public void reset(final FieldSpacecraftState<T> state, final FieldAbsoluteDate<T> target) {
267         super.reset(state, target);
268         for (final FieldEventDetector<T> detector : detectors) {
269             detector.reset(state, target);
270         }
271     }
272 
273     @Override
274     public void finish(final FieldSpacecraftState<T> state) {
275         super.finish(state);
276         for (final FieldEventDetector<T> detector : detectors) {
277             detector.finish(state);
278         }
279     }
280 
281     /**
282      * Get the list of original detectors.
283      * @return the list of original detectors
284      */
285     public List<FieldEventDetector<T>> getDetectors() {
286         return new ArrayList<>(detectors);
287     }
288 
289     /** Local class for operator. */
290     private enum Operator {
291 
292         /** And operator. */
293         AND() {
294 
295             @Override
296             /** {@inheritDoc} */
297             public <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) {
298                 return FastMath.min(g1, g2);
299             }
300 
301         },
302 
303         /** Or operator. */
304         OR() {
305 
306             @Override
307             /** {@inheritDoc} */
308             public <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) {
309                 return FastMath.max(g1, g2);
310             }
311 
312         };
313 
314         /** Combine two g functions evaluations.
315          * @param <T> type of the field elements
316          * @param g1 first evaluation
317          * @param g2 second evaluation
318          * @return combined evaluation
319          */
320         public abstract <T extends CalculusFieldElement<T>> T combine(T g1, T g2);
321 
322     }
323 
324     /** Comparator for field elements.
325      * @param <T> type of the field elements
326      */
327     private static class FieldComparator<T extends CalculusFieldElement<T>> implements Comparator<T>, Serializable {
328         public int compare(final T t1, final T t2) {
329             return Double.compare(t1.getReal(), t2.getReal());
330         }
331     }
332 
333 }