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