1 /* Copyright 2002-2022 CS GROUP
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.forces.maneuvers.trigger;
18
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.stream.Stream;
22
23 import org.hipparchus.CalculusFieldElement;
24 import org.hipparchus.Field;
25 import org.hipparchus.ode.events.Action;
26 import org.orekit.propagation.FieldSpacecraftState;
27 import org.orekit.propagation.SpacecraftState;
28 import org.orekit.propagation.events.AbstractDetector;
29 import org.orekit.propagation.events.EventDetector;
30 import org.orekit.propagation.events.FieldAbstractDetector;
31 import org.orekit.propagation.events.FieldEventDetector;
32 import org.orekit.propagation.events.handlers.EventHandler;
33 import org.orekit.propagation.events.handlers.FieldEventHandler;
34 import org.orekit.time.AbsoluteDate;
35 import org.orekit.time.FieldAbsoluteDate;
36
37 /**
38 * Maneuver triggers based on a single event detector that defines firing intervals.
39 * <p>
40 * Firing intervals correspond to time spans with positive value of the event detector
41 * {@link EventDetector#g(SpacecraftState) g} function.
42 * </p>
43 * @param <T> type of the interval detector
44 * @see StartStopEventsTrigger
45 * @author Luc Maisonobe
46 * @since 11.1
47 */
48 public abstract class IntervalEventTrigger<T extends AbstractDetector<T>> extends AbstractManeuverTriggers {
49
50 /** Intervals detector. */
51 private final T firingIntervalDetector;
52
53 /** Cached field-based detectors. */
54 private final transient Map<Field<? extends CalculusFieldElement<?>>, FieldEventDetector<? extends CalculusFieldElement<?>>> cached;
55
56 /** Simple constructor.
57 * <p>
58 * Note that the {@code intervalDetector} passed as an argument is used only
59 * as a <em>prototype</em> from which a new detector will be built using its
60 * {@link AbstractDetector#withHandler(EventHandler) withHandler} method to
61 * set up an internal handler. The original event handler from the prototype
62 * will be <em>ignored</em> and never called.
63 * </p>
64 * <p>
65 * If the trigger is used in a {@link org.orekit.propagation.FieldPropagator field-based propagation},
66 * the detector will be automatically converted to a field equivalent. Beware however that the
67 * {@link FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) eventOccurred}
68 * of the converted propagator <em>will</em> call the method with the same name in the prototype
69 * detector, in order to get the correct return value.
70 * </p>
71 * @param prototypeFiringIntervalDetector prototype detector for firing interval
72 */
73 public IntervalEventTrigger(final T prototypeFiringIntervalDetector) {
74 this.firingIntervalDetector = prototypeFiringIntervalDetector.withHandler(new Handler());
75 this.cached = new HashMap<>();
76 }
77
78 /**
79 * Getter for the firing interval detector.
80 * @return firing interval detector
81 */
82 public T getFiringIntervalDetector() {
83 return firingIntervalDetector;
84 }
85
86 /** {@inheritDoc} */
87 @Override
88 protected boolean isFiringOnInitialState(final SpacecraftState initialState, final boolean isForward) {
89
90 // set the initial value of firing
91 final double insideThrustArcG = firingIntervalDetector.g(initialState);
92 if (insideThrustArcG == 0) {
93 // bound of arc
94 // check state for the upcoming times
95 final double shift = (isForward ? 2 : -2) * firingIntervalDetector.getThreshold();
96 if (firingIntervalDetector.g(initialState.shiftedBy(shift)) > 0) {
97 // we are entering the firing interval, from start if forward, from end if backward
98 notifyResetters(initialState, isForward);
99 return true;
100 } else {
101 // we are leaving the firing interval, from end if forward, from start if backward
102 notifyResetters(initialState, !isForward);
103 return false;
104 }
105 } else {
106 return insideThrustArcG > 0;
107 }
108
109 }
110
111 /** {@inheritDoc} */
112 @Override
113 public Stream<EventDetector> getEventsDetectors() {
114 return Stream.of(firingIntervalDetector);
115 }
116
117 /** {@inheritDoc} */
118 public <S extends CalculusFieldElement<S>> Stream<FieldEventDetector<S>> getFieldEventsDetectors(final Field<S> field) {
119
120 @SuppressWarnings("unchecked")
121 FieldEventDetector<S> fd = (FieldEventDetector<S>) cached.get(field);
122 if (fd == null) {
123 fd = convertAndSetUpHandler(field);
124 cached.put(field, fd);
125 }
126
127 return Stream.of(fd);
128
129 }
130
131 /** Convert a detector and set up check interval, threshold and new handler.
132 * <p>
133 * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the
134 * parameterized types confuses the Java compiler.
135 * </p>
136 * @param field field to which the state belongs
137 * @param <D> type of the event detector
138 * @param <S> type of the field elements
139 * @return converted firing intervals detector
140 */
141 private <D extends FieldAbstractDetector<D, S>, S extends CalculusFieldElement<S>> D convertAndSetUpHandler(final Field<S> field) {
142 final FieldAbstractDetector<D, S> converted = convertIntervalDetector(field, firingIntervalDetector);
143 return converted.
144 withMaxCheck(field.getZero().newInstance(firingIntervalDetector.getMaxCheckInterval())).
145 withThreshold(field.getZero().newInstance(firingIntervalDetector.getThreshold())).
146 withHandler(new FieldHandler<>());
147 }
148
149 /** Convert a primitive firing intervals detector into a field firing intervals detector.
150 * <p>
151 * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck},
152 * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or
153 * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler}
154 * in the converted detector, this will be done by caller.
155 * </p>
156 * <p>
157 * A skeleton implementation of this method to convert some {@code XyzDetector} into {@code FieldXyzDetector},
158 * considering these detectors are created from a date and a number parameter is:
159 * </p>
160 * <pre>{@code
161 * protected <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>>
162 * FieldAbstractDetector<D, S> convertIntervalDetector(final Field<S> field, final XyzDetector detector) {
163 *
164 * final FieldAbsoluteDate<S> date = new FieldAbsoluteDate<>(field, detector.getDate());
165 * final S param = field.getZero().newInstance(detector.getParam());
166 *
167 * final FieldAbstractDetector<D, S> converted = (FieldAbstractDetector<D, S>) new FieldXyzDetector<>(date, param);
168 * return converted;
169 *
170 * }
171 * }
172 * </pre>
173 * @param field field to which the state belongs
174 * @param detector primitive firing intervals detector to convert
175 * @param <D> type of the event detector
176 * @param <S> type of the field elements
177 * @return converted firing intervals detector
178 */
179 protected abstract <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>>
180 FieldAbstractDetector<D, S> convertIntervalDetector(Field<S> field, T detector);
181
182 /** Local handler for both start and stop triggers. */
183 private class Handler implements EventHandler<T> {
184
185 /** Propagation direction. */
186 private boolean forward;
187
188 /** {@inheritDoc} */
189 @Override
190 public void init(final SpacecraftState initialState, final AbsoluteDate target, final T detector) {
191 forward = target.isAfterOrEqualTo(initialState);
192 initializeResetters(initialState, target);
193 }
194
195 /** {@inheritDoc} */
196 @Override
197 public Action eventOccurred(final SpacecraftState s, final T detector, final boolean increasing) {
198 if (forward) {
199 getFirings().addValidAfter(increasing, s.getDate(), false);
200 } else {
201 getFirings().addValidBefore(!increasing, s.getDate(), false);
202 }
203 notifyResetters(s, increasing);
204 return Action.RESET_STATE;
205 }
206
207 /** {@inheritDoc} */
208 @Override
209 public SpacecraftState resetState(final T detector, final SpacecraftState oldState) {
210 return applyResetters(oldState);
211 }
212
213 }
214
215 /** Local handler for both start and stop triggers.
216 * @param <S> type of the field elements
217 */
218 private class FieldHandler<D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>> implements FieldEventHandler<D, S> {
219
220 /** Propagation direction. */
221 private boolean forward;
222
223 /** {@inheritDoc} */
224 @Override
225 public void init(final FieldSpacecraftState<S> initialState,
226 final FieldAbsoluteDate<S> target,
227 final D detector) {
228 forward = target.isAfterOrEqualTo(initialState);
229 initializeResetters(initialState, target);
230 }
231
232 /** {@inheritDoc} */
233 @Override
234 public Action eventOccurred(final FieldSpacecraftState<S> s, final D detector, final boolean increasing) {
235 if (forward) {
236 getFirings().addValidAfter(increasing, s.getDate().toAbsoluteDate(), false);
237 } else {
238 getFirings().addValidBefore(!increasing, s.getDate().toAbsoluteDate(), false);
239 }
240 notifyResetters(s, increasing);
241 return Action.RESET_STATE;
242 }
243
244 /** {@inheritDoc} */
245 @Override
246 public FieldSpacecraftState<S> resetState(final D detector, final FieldSpacecraftState<S> oldState) {
247 return applyResetters(oldState);
248 }
249
250 }
251
252 }