1 /* Copyright 2020 Exotrail
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 * Exotrail 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.stream.Stream;
20
21 import org.hipparchus.Field;
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.ode.events.Action;
24 import org.orekit.errors.OrekitException;
25 import org.orekit.errors.OrekitMessages;
26 import org.orekit.propagation.SpacecraftState;
27 import org.orekit.propagation.events.AbstractDetector;
28 import org.orekit.propagation.events.EventDetector;
29 import org.orekit.propagation.events.FieldEventDetector;
30 import org.orekit.propagation.events.handlers.EventHandler;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.FieldAbsoluteDate;
33
34 /**
35 * Maneuver triggers based on start and stop detectors. This allow a succession
36 * of burn interval. The thruster starts firing when the start detector becomes
37 * positive. The thruster stops firing when the stop detector becomes positive.
38 * The 2 detectors should not be positive at the same time. A date detector is
39 * not suited as it does not delimit an interval. They can be both negative at
40 * the same time.
41 * @author Mikael Fillastre
42 * @author Andrea Fiorentino
43 * @since 10.2
44 */
45 public class EventBasedManeuverTriggers implements ManeuverTriggers, EventHandler<EventDetector> {
46
47 /** Detector to start firing, only detect increasing sign change. */
48 private final AbstractDetector<? extends EventDetector> startFiringDetector;
49 /**
50 * Detector to stop firing, only detect increasing sign change. e.g. it can be a
51 * negate detector of the start detector
52 */
53 private final AbstractDetector<? extends EventDetector> stopFiringDetector;
54
55 /**
56 * Flag for init method, called several times : force models + each detector.
57 */
58 private boolean initialized;
59
60 /** Triggered date of engine start. */
61 private AbsoluteDate triggeredStart;
62
63 /** Triggered date of engine stop. */
64 private AbsoluteDate triggeredEnd;
65
66 /**
67 * Constructor.
68 * @param startFiringDetector Detector to start firing, only detect increasing
69 * sign change
70 * @param stopFiringDetector Detector to stop firing, only detect increasing
71 * sign change. e.g. it can be a negate detector of
72 * the start detector.
73 */
74 public EventBasedManeuverTriggers(final AbstractDetector<? extends EventDetector> startFiringDetector,
75 final AbstractDetector<? extends EventDetector> stopFiringDetector) {
76 if (startFiringDetector == null) {
77 throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "stopFiringDetector",
78 EventBasedManeuverTriggers.class.getSimpleName());
79 }
80 if (stopFiringDetector == null) {
81 throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "startFiringDetector",
82 EventBasedManeuverTriggers.class.getSimpleName());
83 }
84 this.startFiringDetector = startFiringDetector.withHandler(this);
85 this.stopFiringDetector = stopFiringDetector.withHandler(this);
86 this.triggeredStart = null;
87 this.triggeredEnd = null;
88 initialized = false;
89 }
90
91 /**
92 * Getter for the start firing detector.
93 * @return Detectors to start firing,
94 */
95 public AbstractDetector<? extends EventDetector> getStartFiringDetector() {
96 return startFiringDetector;
97 }
98
99 /**
100 * Getter for the stop firing detector.
101 * @return Detectors to stop firing
102 */
103 public AbstractDetector<? extends EventDetector> getStopFiringDetector() {
104 return stopFiringDetector;
105 }
106
107 /** {@inheritDoc} */
108 @Override
109 public void init(final SpacecraftState initialState, final AbsoluteDate target) {
110
111 if (!initialized) {
112
113 initialized = true;
114 final AbsoluteDate sDate = initialState.getDate();
115 if (sDate.compareTo(target) > 0) {
116 // backward propagation not managed because events on detectors can not be
117 // reversed :
118 // the stop event of the maneuver in forward direction won't be the start in the
119 // backward.
120 // e.g. if a stop detector is combination of orbit position and system
121 // constraint
122 throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED,
123 "EventBasedManeuverTriggers in backward propagation");
124 }
125
126 checkInitialFiringState(initialState);
127
128 } // multiples calls to init : because it is a force model and by each detector
129 }
130
131 /**
132 * Method to set the firing state on initialization. can be overloaded by sub
133 * classes.
134 *
135 * @param initialState initial spacecraft state
136 */
137 protected void checkInitialFiringState(final SpacecraftState initialState) {
138 if (isFiringOnInitialState(initialState)) {
139 setFiring(true, initialState.getDate());
140 }
141 }
142
143 /**
144 * Method to check if the thruster is firing on initialization. can be called by
145 * sub classes
146 *
147 * @param initialState initial spacecraft state
148 * @return true if firing
149 */
150 protected boolean isFiringOnInitialState(final SpacecraftState initialState) {
151 // set the initial value of firing
152 final double insideThrustArcG = getStartFiringDetector().g(initialState);
153 boolean isInsideThrustArc = false;
154
155 if (insideThrustArcG == 0) {
156 // bound of arc
157 // check state for the next second (which can be forward or backward)
158 final double nextSecond = 1;
159 final double nextValue = getStartFiringDetector().g(initialState.shiftedBy(nextSecond));
160 isInsideThrustArc = nextValue > 0;
161 } else {
162 isInsideThrustArc = insideThrustArcG > 0;
163 }
164 return isInsideThrustArc;
165 }
166
167 /** {@inheritDoc} */
168 @Override
169 public Stream<EventDetector> getEventsDetectors() {
170 return Stream.of(getStartFiringDetector(), getStopFiringDetector());
171 }
172
173 /** {@inheritDoc} */
174 @Override
175 public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
176 // not implemented, it depends on the input detectors
177 throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED,
178 "EventBasedManeuverTriggers.getFieldEventsDetectors");
179 }
180
181 /**
182 * Set the firing start or end date depending on the firing flag. There is no
183 * effect if the firing state is not changing.
184 * @param firing true to start a maneuver, false to stop
185 * @param date date of event
186 */
187 public void setFiring(final boolean firing, final AbsoluteDate date) {
188 if (firing != isFiring(date)) {
189 if (firing) {
190 if (!date.equals(triggeredEnd)) {
191 triggeredStart = date;
192 } // else no gap between stop and start, can not handle correctly : skip it
193 } else {
194 triggeredEnd = date;
195 }
196 }
197 }
198
199 /** {@inheritDoc} */
200 @Override
201 public boolean isFiring(final AbsoluteDate date, final double[] parameters) {
202 // Firing state does not depend on a parameter driver here
203 return isFiring(date);
204 }
205
206 /** {@inheritDoc} */
207 @Override
208 public <T extends CalculusFieldElement<T>> boolean isFiring(final FieldAbsoluteDate<T> date, final T[] parameters) {
209 // Firing state does not depend on a parameter driver here
210 return isFiring(date.toAbsoluteDate());
211 }
212
213 /** {@inheritDoc} */
214 @Override
215 public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
216 Action action = Action.CONTINUE; // default not taken into account
217 final boolean detectorManaged = getEventsDetectors()
218 .anyMatch(managedDetector -> managedDetector.equals(detector));
219 if (detectorManaged) {
220 action = Action.RESET_EVENTS;
221 if (increasing) {
222 if (detector.equals(getStartFiringDetector())) { // start of firing arc
223 setFiring(true, s.getDate());
224 action = Action.RESET_DERIVATIVES;
225 } else if (detector.equals(getStopFiringDetector())) { // end of firing arc
226 setFiring(false, s.getDate());
227 action = Action.RESET_DERIVATIVES;
228 }
229 }
230 }
231 return action;
232 }
233
234 /**
235 * Check if maneuvering is on.
236 *
237 * @param date current date
238 * @return true if maneuver is on at this date
239 */
240 public boolean isFiring(final AbsoluteDate date) {
241 if (triggeredStart == null) {
242 // explicitly ignores state date, as propagator did not allow us to introduce
243 // discontinuity
244 return false;
245 } else if (date.isBefore(triggeredStart)) {
246 // we are unambiguously before maneuver start
247 // robustness, we should not pass here
248 return false;
249 } else {
250 // after start date
251 if (getTriggeredEnd() == null) {
252 // explicitly ignores state date, as propagator did not allow us to introduce
253 // discontinuity
254 return true;
255 } else if (getTriggeredStart().isAfter(getTriggeredEnd())) {
256 // last event is a start of maneuver, end not set yet
257 // we are unambiguously before maneuver end
258 return true;
259 } else if (date.isBefore(getTriggeredEnd())) {
260 // we are unambiguously before maneuver end
261 // robustness, we should not pass here
262 return true;
263 } else {
264 // we are at or after maneuver end
265 return false;
266 }
267 }
268 }
269
270 /**
271 * Getter for the triggered date of engine stop.
272 * @return Triggered date of engine stop
273 */
274 public AbsoluteDate getTriggeredEnd() {
275 return triggeredEnd;
276 }
277
278 /**
279 * Getter triggered date of engine start.
280 * @return Triggered date of engine start
281 */
282 public AbsoluteDate getTriggeredStart() {
283 return triggeredStart;
284 }
285
286 }