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 }