1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF 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  
18  package org.orekit.propagation.events;
19  
20  import java.lang.reflect.Array;
21  import java.util.Arrays;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.ode.events.Action;
25  import org.orekit.propagation.FieldSpacecraftState;
26  import org.orekit.propagation.events.functions.EventFunction;
27  import org.orekit.propagation.events.functions.EventFunctionModifier;
28  import org.orekit.propagation.events.handlers.FieldEventHandler;
29  import org.orekit.time.FieldAbsoluteDate;
30  
31  /** Wrapper used to detect only increasing or decreasing events.
32   *
33   * <p>This class is heavily based on the class EventFilter from the
34   * Hipparchus library. The changes performed consist in replacing
35   * raw types (double and double arrays) with space dynamics types
36   * ({@link FieldAbsoluteDate}, {@link FieldSpacecraftState}).</p>
37   *
38   * <p>General {@link FieldEventDetector events} are defined implicitly
39   * by a {@link FieldEventDetector#g(FieldSpacecraftState) g function} crossing
40   * zero. This function needs to be continuous in the event neighborhood,
41   * and its sign must remain consistent between events. This implies that
42   * during an orbit propagation, events triggered are alternately events
43   * for which the function increases from negative to positive values,
44   * and events for which the function decreases from positive to
45   * negative values.
46   * </p>
47   *
48   * <p>Sometimes, users are only interested in one type of event (say
49   * increasing events for example) and not in the other type. In these
50   * cases, looking precisely for all events location and triggering
51   * events that will later be ignored is a waste of computing time.</p>
52   *
53   * <p>Users can wrap a regular {@link FieldEventDetector event detector} in
54   * an instance of this class and provide this wrapping instance to
55   * a {@link org.orekit.propagation.FieldPropagator}
56   * in order to avoid wasting time looking for uninteresting events.
57   * The wrapper will intercept the calls to the {@link
58   * FieldEventDetector#g(FieldSpacecraftState) g function} and to the {@link
59   * FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean)
60   * eventOccurred} method in order to ignore uninteresting events. The
61   * wrapped regular {@link FieldEventDetector event detector} will then see only
62   * the interesting events, i.e. either only {@code increasing} events or
63   * only {@code decreasing} events. The number of calls to the {@link
64   * FieldEventDetector#g(FieldSpacecraftState) g function} will also be reduced.</p>
65   * @see FieldEventEnablingPredicateFilter
66   * @param <D> type of the detector
67   * @param <T> type of the field elements
68   */
69  
70  public class FieldEventSlopeFilter<D extends FieldEventDetector<T>, T extends CalculusFieldElement<T>>
71      implements FieldEventDetector<T> {
72  
73      /** Number of past transformers updates stored. */
74      private static final int HISTORY_SIZE = 100;
75  
76      /** Wrapped event detector. */
77      private final D rawDetector;
78  
79      /** Filter to use. */
80      private final FilterType filterType;
81  
82      /** Transformers of the g function. */
83      private final Transformer[] transformers;
84  
85      /** Update time of the transformers. */
86      private final FieldAbsoluteDate<T>[] updates;
87  
88      /** Event detection settings. */
89      private final FieldEventDetectionSettings<T> detectionSettings;
90  
91      /** Specialized event handler. */
92      private final LocalHandler<D, T> handler;
93  
94      /** Specialized event function. */
95      private final EventFunction eventFunction;
96  
97      /** Indicator for forward integration. */
98      private boolean forward;
99  
100     /** Extreme time encountered so far. */
101     private FieldAbsoluteDate<T> extremeT;
102 
103     /** Wrap an {@link EventDetector event detector}.
104      * @param rawDetector event detector to wrap
105      * @param filterType filter to use
106      */
107     public FieldEventSlopeFilter(final D rawDetector, final FilterType filterType) {
108         this(rawDetector.getDetectionSettings(), rawDetector, filterType);
109     }
110 
111     /** Constructor with full parameters.
112      * @param detectionSettings event detection settings
113      * @param rawDetector event detector to wrap
114      * @param filterType filter to use
115      * since 13.0
116      */
117     @SuppressWarnings("unchecked")
118     public FieldEventSlopeFilter(final FieldEventDetectionSettings<T> detectionSettings,
119                                  final D rawDetector, final FilterType filterType) {
120         this.detectionSettings = detectionSettings;
121         this.handler = new LocalHandler<>();
122         this.rawDetector  = rawDetector;
123         this.filterType = filterType;
124         this.transformers = new Transformer[HISTORY_SIZE];
125         this.updates      = (FieldAbsoluteDate<T>[]) Array.newInstance(FieldAbsoluteDate.class, HISTORY_SIZE);
126         this.eventFunction = new LocalEventFunction();
127     }
128 
129     /**
130      * Builds a new instance from the input detection settings.
131      * @param settings event detection settings to be used
132      * @return a new detector
133      */
134     public FieldEventSlopeFilter<D, T> withDetectionSettings(final FieldEventDetectionSettings<T> settings) {
135         return new FieldEventSlopeFilter<>(settings, rawDetector, filterType);
136     }
137 
138     /** Get filter type.
139      * @return filter type
140      * @since 13.0
141      */
142     public FilterType getFilterType() {
143         return filterType;
144     }
145 
146     @Override
147     public EventFunction getEventFunction() {
148         return eventFunction;
149     }
150 
151     @Override
152     public FieldEventHandler<T> getHandler() {
153         return handler;
154     }
155 
156     @Override
157     public FieldEventDetectionSettings<T> getDetectionSettings() {
158         return detectionSettings;
159     }
160 
161     /**
162      * Get the wrapped raw detector.
163      * @return the wrapped raw detector
164      */
165     public D getDetector() {
166         return rawDetector;
167     }
168 
169     /**  {@inheritDoc} */
170     @Override
171     public void init(final FieldSpacecraftState<T> s0,
172                      final FieldAbsoluteDate<T> t) {
173         FieldEventDetector.super.init(s0, t);
174 
175         // delegate to raw detector
176         rawDetector.init(s0, t);
177 
178         // initialize events triggering logic
179         forward  = FieldAbstractDetector.checkIfForward(s0, t);
180         extremeT = forward ?
181                    FieldAbsoluteDate.getPastInfinity(t.getField()) :
182                    FieldAbsoluteDate.getFutureInfinity(t.getField());
183         Arrays.fill(transformers, Transformer.UNINITIALIZED);
184         Arrays.fill(updates, extremeT);
185 
186     }
187 
188     /**  {@inheritDoc} */
189     @Override
190     public void reset(final FieldSpacecraftState<T> state, final FieldAbsoluteDate<T> target) {
191         FieldEventDetector.super.reset(state, target);
192         rawDetector.reset(state, target);
193     }
194 
195     /**  {@inheritDoc} */
196     @Override
197     public void finish(final FieldSpacecraftState<T> state) {
198         FieldEventDetector.super.finish(state);
199         rawDetector.finish(state);
200     }
201 
202     /**  {@inheritDoc} */
203     @Override
204     public T g(final FieldSpacecraftState<T> s) {
205 
206         final T rawG = rawDetector.g(s);
207 
208         // search which transformer should be applied to g
209         if (isForward()) {
210             final int last = transformers.length - 1;
211             if (extremeT.compareTo(s.getDate()) < 0) {
212                 // we are at the forward end of the history
213 
214                 // check if a new rough root has been crossed
215                 final Transformer previous = transformers[last];
216                 final Transformer next     = filterType.selectTransformer(previous, rawG.getReal(), forward);
217                 if (next != previous) {
218                     // there is a root somewhere between extremeT and t.
219                     // the new transformer is valid for t (this is how we have just computed
220                     // it above), but it is in fact valid on both sides of the root, so
221                     // it was already valid before t and even up to previous time. We store
222                     // the switch at extremeT for safety, to ensure the previous transformer
223                     // is not applied too close of the root
224                     System.arraycopy(updates,      1, updates,      0, last);
225                     System.arraycopy(transformers, 1, transformers, 0, last);
226                     updates[last]      = extremeT;
227                     transformers[last] = next;
228                 }
229 
230                 extremeT = s.getDate();
231 
232                 // apply the transform
233                 return next.transformed(rawG);
234 
235             } else {
236                 // we are in the middle of the history
237 
238                 // select the transformer
239                 for (int i = last; i > 0; --i) {
240                     if (updates[i].compareTo(s.getDate()) <= 0) {
241                         // apply the transform
242                         return transformers[i].transformed(rawG);
243                     }
244                 }
245 
246                 return transformers[0].transformed(rawG);
247 
248             }
249         } else {
250             if (s.getDate().compareTo(extremeT) < 0) {
251                 // we are at the backward end of the history
252 
253                 // check if a new rough root has been crossed
254                 final Transformer previous = transformers[0];
255                 final Transformer next     = filterType.selectTransformer(previous, rawG.getReal(), forward);
256                 if (next != previous) {
257                     // there is a root somewhere between extremeT and t.
258                     // the new transformer is valid for t (this is how we have just computed
259                     // it above), but it is in fact valid on both sides of the root, so
260                     // it was already valid before t and even up to previous time. We store
261                     // the switch at extremeT for safety, to ensure the previous transformer
262                     // is not applied too close of the root
263                     System.arraycopy(updates,      0, updates,      1, updates.length - 1);
264                     System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
265                     updates[0]      = extremeT;
266                     transformers[0] = next;
267                 }
268 
269                 extremeT = s.getDate();
270 
271                 // apply the transform
272                 return next.transformed(rawG);
273 
274             } else {
275                 // we are in the middle of the history
276 
277                 // select the transformer
278                 for (int i = 0; i < updates.length - 1; ++i) {
279                     if (s.getDate().compareTo(updates[i]) <= 0) {
280                         // apply the transform
281                         return transformers[i].transformed(rawG);
282                     }
283                 }
284 
285                 return transformers[updates.length - 1].transformed(rawG);
286 
287             }
288         }
289 
290     }
291 
292     /** Check if the current propagation is forward or backward.
293      * @return true if the current propagation is forward
294      */
295     public boolean isForward() {
296         return forward;
297     }
298 
299     /**
300      * Local event function.
301      * @since 14.0
302      */
303     private class LocalEventFunction implements EventFunctionModifier {
304 
305         /** Wrapped event function. */
306         private final EventFunction eventSlopeEventFunction;
307 
308         LocalEventFunction() {
309             eventSlopeEventFunction = EventFunction.of(getThreshold().getField(), FieldEventSlopeFilter.this::g);
310         }
311 
312         @Override
313         public EventFunction getBaseFunction() {
314             return eventSlopeEventFunction;
315         }
316 
317         @Override
318         public boolean dependsOnTimeOnly() {
319             return rawDetector.getEventFunction().dependsOnTimeOnly();
320         }
321 
322         @Override
323         public boolean dependsOnMainVariablesOnly() {
324             return rawDetector.getEventFunction().dependsOnMainVariablesOnly();
325         }
326     }
327 
328     /** Local handler. */
329     private static class LocalHandler<D extends FieldEventDetector<T>, T extends CalculusFieldElement<T>> implements FieldEventHandler<T> {
330 
331         /** {@inheritDoc} */
332         public Action eventOccurred(final FieldSpacecraftState<T> s, final FieldEventDetector<T> detector, final boolean increasing) {
333             final FieldEventSlopeFilter<D, T> esf = castDetector(detector);
334             return esf.rawDetector.getHandler().eventOccurred(s, esf.rawDetector, esf.filterType.getTriggeredIncreasing());
335         }
336 
337         /** {@inheritDoc} */
338         @Override
339         public FieldSpacecraftState<T> resetState(final FieldEventDetector<T> detector, final FieldSpacecraftState<T> oldState) {
340             final FieldEventSlopeFilter<D, T> esf = castDetector(detector);
341             return esf.rawDetector.getHandler().resetState(esf.rawDetector, oldState);
342         }
343 
344         /**
345          * Cast underlying detector.
346          * @param detector sloped filter detector
347          * @return cast detector
348          */
349         private FieldEventSlopeFilter<D, T> castDetector(final FieldEventDetector<T> detector) {
350             return (FieldEventSlopeFilter<D, T>) detector;
351         }
352     }
353 
354 }