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.propagation.events;
18
19 import java.util.List;
20 import java.util.stream.Collectors;
21
22 import org.hipparchus.util.FastMath;
23 import org.orekit.errors.OrekitException;
24 import org.orekit.errors.OrekitMessages;
25 import org.orekit.propagation.SpacecraftState;
26 import org.orekit.propagation.events.handlers.EventHandler;
27 import org.orekit.propagation.events.handlers.StopOnDecreasing;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.utils.DateDriver;
30 import org.orekit.utils.ParameterDriver;
31 import org.orekit.utils.ParameterObserver;
32
33 /** Detector for date intervals that may be offset thanks to parameter drivers.
34 * <p>
35 * Two dual views can be used for date intervals: either start date/stop date or
36 * median date/duration. {@link #getStartDriver() start}/{@link #getStopDriver() stop}
37 * drivers and {@link #getMedianDriver() median}/{@link #getDurationDriver() duration}
38 * drivers work in pair. Both drivers in one pair can be selected and their changes will
39 * be propagated to the other pair, but attempting to select drivers in both
40 * pairs at the same time will trigger an exception. Changing the value of a driver
41 * that is not selected should be avoided as it leads to inconsistencies between the pairs.
42 * </p>
43 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
44 * @author Luc Maisonobe
45 * @since 11.1
46 */
47 public class ParameterDrivenDateIntervalDetector extends AbstractDetector<ParameterDrivenDateIntervalDetector> {
48
49 /** Default suffix for start driver. */
50 public static final String START_SUFFIX = "_START";
51
52 /** Default suffix for stop driver. */
53 public static final String STOP_SUFFIX = "_STOP";
54
55 /** Default suffix for median driver. */
56 public static final String MEDIAN_SUFFIX = "_MEDIAN";
57
58 /** Default suffix for duration driver. */
59 public static final String DURATION_SUFFIX = "_DURATION";
60
61 /** Detection threshold. */
62 private static final double THRESHOLD = 1.0e-10;
63
64 /** Reference interval start driver. */
65 private DateDriver start;
66
67 /** Reference interval stop driver. */
68 private DateDriver stop;
69
70 /** Median date driver. */
71 private DateDriver median;
72
73 /** Duration driver. */
74 private ParameterDriver duration;
75
76 /** Build a new instance.
77 * @param prefix prefix to use for parameter drivers names
78 * @param refMedian reference interval median date
79 * @param refDuration reference duration
80 */
81 public ParameterDrivenDateIntervalDetector(final String prefix,
82 final AbsoluteDate refMedian, final double refDuration) {
83 this(prefix,
84 refMedian.shiftedBy(-0.5 * refDuration),
85 refMedian.shiftedBy(+0.5 * refDuration));
86 }
87
88 /** Build a new instance.
89 * @param prefix prefix to use for parameter drivers names
90 * @param refStart reference interval start date
91 * @param refStop reference interval stop date
92 */
93 public ParameterDrivenDateIntervalDetector(final String prefix,
94 final AbsoluteDate refStart, final AbsoluteDate refStop) {
95 this(FastMath.max(0.5 * refStop.durationFrom(refStart), THRESHOLD),
96 THRESHOLD, DEFAULT_MAX_ITER,
97 new StopOnDecreasing<ParameterDrivenDateIntervalDetector>(),
98 new DateDriver(refStart, prefix + START_SUFFIX, true),
99 new DateDriver(refStop, prefix + STOP_SUFFIX, false),
100 new DateDriver(refStart.shiftedBy(0.5 * refStop.durationFrom(refStart)), prefix + MEDIAN_SUFFIX, true),
101 new ParameterDriver(prefix + DURATION_SUFFIX, refStop.durationFrom(refStart), 1.0, 0.0, Double.POSITIVE_INFINITY));
102 }
103
104 /** Private constructor with full parameters.
105 * <p>
106 * This constructor is private as users are expected to use the builder
107 * API with the various {@code withXxx()} methods to set up the instance
108 * in a readable manner without using a huge amount of parameters.
109 * </p>
110 * @param maxCheck maximum checking interval (s)
111 * @param threshold convergence threshold (s)
112 * @param maxIter maximum number of iterations in the event time search
113 * @param handler event handler to call at event occurrences
114 * @param start reference interval start driver
115 * @param stop reference interval stop driver
116 * @param median median date driver
117 * @param duration duration driver
118 */
119 private ParameterDrivenDateIntervalDetector(final double maxCheck, final double threshold, final int maxIter,
120 final EventHandler<? super ParameterDrivenDateIntervalDetector> handler,
121 final DateDriver start, final DateDriver stop,
122 final DateDriver median, final ParameterDriver duration) {
123 super(maxCheck, threshold, maxIter, handler);
124 this.start = start;
125 this.stop = stop;
126 this.median = median;
127 this.duration = duration;
128
129 // set up delegation between drivers
130 replaceBindingObserver(start, new StartObserver());
131 replaceBindingObserver(stop, new StopObserver());
132 replaceBindingObserver(median, new MedianObserver());
133 replaceBindingObserver(duration, new DurationObserver());
134
135 }
136
137 /** Replace binding observers.
138 * @param driver driver for whose binding observers should be replaced
139 * @param bindingObserver new binding observer
140 */
141 private void replaceBindingObserver(final ParameterDriver driver, final BindingObserver bindingObserver) {
142
143 // remove the previous binding observers
144 final List<ParameterObserver> original = driver.
145 getObservers().
146 stream().
147 filter(observer -> observer instanceof ParameterDrivenDateIntervalDetector.BindingObserver).
148 collect(Collectors.toList());
149 original.forEach(observer -> driver.removeObserver(observer));
150
151 driver.addObserver(bindingObserver);
152
153 }
154
155 /** {@inheritDoc} */
156 @Override
157 protected ParameterDrivenDateIntervalDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
158 final EventHandler<? super ParameterDrivenDateIntervalDetector> newHandler) {
159 return new ParameterDrivenDateIntervalDetector(newMaxCheck, newThreshold, newMaxIter, newHandler,
160 start, stop, median, duration);
161 }
162
163 /** Get the driver for start date.
164 * <p>
165 * Note that the start date is automatically adjusted if either
166 * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
167 * are {@link ParameterDriver#isSelected() selected} and changed.
168 * </p>
169 * @return driver for start date
170 */
171 public DateDriver getStartDriver() {
172 return start;
173 }
174
175 /** Get the driver for stop date.
176 * <p>
177 * Note that the stop date is automatically adjusted if either
178 * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
179 * are {@link ParameterDriver#isSelected() selected} changed.
180 * </p>
181 * @return driver for stop date
182 */
183 public DateDriver getStopDriver() {
184 return stop;
185 }
186
187 /** Get the driver for median date.
188 * <p>
189 * Note that the median date is automatically adjusted if either
190 * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
191 * are {@link ParameterDriver#isSelected() selected} changed.
192 * </p>
193 * @return driver for median date
194 */
195 public DateDriver getMedianDriver() {
196 return median;
197 }
198
199 /** Get the driver for duration.
200 * <p>
201 * Note that the duration is automatically adjusted if either
202 * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
203 * are {@link ParameterDriver#isSelected() selected} changed.
204 * </p>
205 * @return driver for duration
206 */
207 public ParameterDriver getDurationDriver() {
208 return duration;
209 }
210
211 /** Compute the value of the switching function.
212 * <p>
213 * The function is positive for dates within the interval defined
214 * by applying the parameter drivers shifts to reference dates,
215 * and negative for dates outside of this interval. Note that
216 * if Δt_start - Δt_stop is less than ref_stop.durationFrom(ref_start),
217 * then the interval degenerates to empty and the function never
218 * reaches positive values.
219 * </p>
220 * @param s the current state information: date, kinematics, attitude
221 * @return value of the switching function
222 */
223 public double g(final SpacecraftState s) {
224 return FastMath.min(s.getDate().durationFrom(start.getDate()),
225 stop.getDate().durationFrom(s.getDate()));
226 }
227
228 /** Base observer. */
229 private abstract class BindingObserver implements ParameterObserver {
230
231 /** {@inheritDoc} */
232 @Override
233 public void valueChanged(final double previousValue, final ParameterDriver driver) {
234 if (driver.isSelected()) {
235 setDelta(driver.getValue() - previousValue);
236 }
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public void selectionChanged(final boolean previousSelection, final ParameterDriver driver) {
242 if ((start.isSelected() || stop.isSelected()) &&
243 (median.isSelected() || duration.isSelected())) {
244 throw new OrekitException(OrekitMessages.INCONSISTENT_SELECTION,
245 start.getName(), stop.getName(),
246 median.getName(), duration.getName());
247 }
248 }
249
250 /** Change a value.
251 * @param delta change of value
252 */
253 protected abstract void setDelta(double delta);
254
255 }
256
257 /** Observer for start date. */
258 private class StartObserver extends BindingObserver {
259 /** {@inheritDoc} */
260 @Override
261 protected void setDelta(final double delta) {
262 median.setValue(median.getValue() + 0.5 * delta);
263 duration.setValue(duration.getValue() - delta);
264 }
265 }
266
267 /** Observer for stop date. */
268 private class StopObserver extends BindingObserver {
269 /** {@inheritDoc} */
270 @Override
271 protected void setDelta(final double delta) {
272 median.setValue(median.getValue() + 0.5 * delta);
273 duration.setValue(duration.getValue() + delta);
274 }
275 }
276
277 /** Observer for median date. */
278 private class MedianObserver extends BindingObserver {
279 /** {@inheritDoc} */
280 @Override
281 protected void setDelta(final double delta) {
282 start.setValue(start.getValue() + delta);
283 stop.setValue(stop.getValue() + delta);
284 }
285 }
286
287 /** Observer for duration. */
288 private class DurationObserver extends BindingObserver {
289 /** {@inheritDoc} */
290 @Override
291 protected void setDelta(final double delta) {
292 start.setValue(start.getValue() - 0.5 * delta);
293 stop.setValue(stop.getValue() + 0.5 * delta);
294 }
295 }
296
297 }