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 &gt; 0) AND when the ground point is sunlit (Sun
40   * elevation &gt; 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 }