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.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.hipparchus.ode.events.Action;
24 import org.orekit.errors.OrekitIllegalArgumentException;
25 import org.orekit.errors.OrekitMessages;
26 import org.orekit.propagation.SpacecraftState;
27 import org.orekit.propagation.events.handlers.EventHandler;
28 import org.orekit.propagation.events.handlers.StopOnEvent;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.TimeStamped;
31
32 /** Finder for date events.
33 * <p>This class finds date events (i.e. occurrence of some predefined dates).</p>
34 * <p>As of version 5.1, it is an enhanced date detector:</p>
35 * <ul>
36 * <li>it can be defined without prior date ({@link #DateDetector(double, double, TimeStamped...)})</li>
37 * <li>several dates can be added ({@link #addEventDate(AbsoluteDate)})</li>
38 * </ul>
39 * <p>The gap between the added dates must be more than the maxCheck.</p>
40 * <p>The default implementation behavior is to {@link Action#STOP stop}
41 * propagation at the first event date occurrence. This can be changed by calling
42 * {@link #withHandler(EventHandler)} after construction.</p>
43 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
44 * @author Luc Maisonobe
45 * @author Pascal Parraud
46 */
47 public class DateDetector extends AbstractDetector<DateDetector> implements TimeStamped {
48
49 /** Last date for g computation. */
50 private AbsoluteDate gDate;
51
52 /** List of event dates. */
53 private final ArrayList<EventDate> eventDateList;
54
55 /** Current event date. */
56 private int currentIndex;
57
58 /** Build a new instance.
59 * <p>First event dates are set here, but others can be
60 * added later with {@link #addEventDate(AbsoluteDate)}.</p>
61 * @param maxCheck maximum checking interval (s)
62 * @param threshold convergence threshold (s)
63 * @param dates list of event dates
64 * @see #addEventDate(AbsoluteDate)
65 */
66 public DateDetector(final double maxCheck, final double threshold, final TimeStamped... dates) {
67 this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnEvent<DateDetector>(), dates);
68 }
69
70 /** Build a new instance.
71 * <p>This constructor is dedicated to single date detection.
72 * {@link #getMaxCheckInterval() max check interval} is set to 1.0e10, so almost
73 * no other date can be added. Tolerance is set to 1.0e-9.</p>
74 * @param target target date
75 * @see #addEventDate(AbsoluteDate)
76 */
77 public DateDetector(final AbsoluteDate target) {
78 this(1.0e10, 1.e-9, target);
79 }
80
81 /** Private constructor with full parameters.
82 * <p>
83 * This constructor is private as users are expected to use the builder
84 * API with the various {@code withXxx()} methods to set up the instance
85 * in a readable manner without using a huge amount of parameters.
86 * </p>
87 * @param maxCheck maximum checking interval (s)
88 * @param threshold convergence threshold (s)
89 * @param maxIter maximum number of iterations in the event time search
90 * @param handler event handler to call at event occurrences
91 * @param dates list of event dates
92 * @since 6.1
93 */
94 private DateDetector(final double maxCheck, final double threshold,
95 final int maxIter, final EventHandler<? super DateDetector> handler,
96 final TimeStamped... dates) {
97 super(maxCheck, threshold, maxIter, handler);
98 this.currentIndex = -1;
99 this.gDate = null;
100 this.eventDateList = new ArrayList<DateDetector.EventDate>(dates.length);
101 for (final TimeStamped ts : dates) {
102 addEventDate(ts.getDate());
103 }
104 }
105
106 /** {@inheritDoc} */
107 @Override
108 protected DateDetector create(final double newMaxCheck, final double newThreshold,
109 final int newMaxIter, final EventHandler<? super DateDetector> newHandler) {
110 return new DateDetector(newMaxCheck, newThreshold, newMaxIter, newHandler,
111 eventDateList.toArray(new EventDate[eventDateList.size()]));
112 }
113
114 /** Get all event dates currently managed, in chronological order.
115 * @return all event dates currently managed, in chronological order
116 * @since 11.1
117 */
118 public List<TimeStamped> getDates() {
119 return Collections.unmodifiableList(eventDateList);
120 }
121
122 /** Compute the value of the switching function.
123 * This function measures the difference between the current and the target date.
124 * @param s the current state information: date, kinematics, attitude
125 * @return value of the switching function
126 */
127 public double g(final SpacecraftState s) {
128 gDate = s.getDate();
129 if (currentIndex < 0) {
130 return -1.0;
131 } else {
132 final EventDate event = getClosest(gDate);
133 return event.isgIncrease() ? gDate.durationFrom(event.getDate()) : event.getDate().durationFrom(gDate);
134 }
135 }
136
137 /** Get the current event date according to the propagator.
138 * @return event date
139 */
140 public AbsoluteDate getDate() {
141 return currentIndex < 0 ? null : eventDateList.get(currentIndex).getDate();
142 }
143
144 /** Add an event date.
145 * <p>The date to add must be:</p>
146 * <ul>
147 * <li>less than the smallest already registered event date minus the maxCheck</li>
148 * <li>or more than the largest already registered event date plus the maxCheck</li>
149 * </ul>
150 * @param target target date
151 * @throws IllegalArgumentException if the date is too close from already defined interval
152 * @see #DateDetector(double, double, TimeStamped...)
153 */
154 public void addEventDate(final AbsoluteDate target) throws IllegalArgumentException {
155 final boolean increasing;
156 if (currentIndex < 0) {
157 increasing = (gDate == null) ? true : target.durationFrom(gDate) > 0.0;
158 currentIndex = 0;
159 eventDateList.add(new EventDate(target, increasing));
160 } else {
161 final int lastIndex = eventDateList.size() - 1;
162 final AbsoluteDate firstDate = eventDateList.get(0).getDate();
163 final AbsoluteDate lastDate = eventDateList.get(lastIndex).getDate();
164 if (firstDate.durationFrom(target) > getMaxCheckInterval()) {
165 increasing = !eventDateList.get(0).isgIncrease();
166 eventDateList.add(0, new EventDate(target, increasing));
167 currentIndex++;
168 } else if (target.durationFrom(lastDate) > getMaxCheckInterval()) {
169 increasing = !eventDateList.get(lastIndex).isgIncrease();
170 eventDateList.add(new EventDate(target, increasing));
171 } else {
172 throw new OrekitIllegalArgumentException(OrekitMessages.EVENT_DATE_TOO_CLOSE,
173 target,
174 firstDate,
175 lastDate,
176 getMaxCheckInterval(),
177 firstDate.durationFrom(target),
178 target.durationFrom(lastDate));
179 }
180 }
181 }
182
183 /** Get the closest EventDate to the target date.
184 * @param target target date
185 * @return current EventDate
186 */
187 private EventDate getClosest(final AbsoluteDate target) {
188 final double dt = target.durationFrom(eventDateList.get(currentIndex).getDate());
189 if (dt < 0.0 && currentIndex > 0) {
190 boolean found = false;
191 while (currentIndex > 0 && !found) {
192 if (target.durationFrom(eventDateList.get(currentIndex - 1).getDate()) < eventDateList.get(currentIndex).getDate().durationFrom(target)) {
193 currentIndex--;
194 } else {
195 found = true;
196 }
197 }
198 } else if (dt > 0.0 && currentIndex < eventDateList.size() - 1) {
199 final int maxIndex = eventDateList.size() - 1;
200 boolean found = false;
201 while (currentIndex < maxIndex && !found) {
202 if (target.durationFrom(eventDateList.get(currentIndex + 1).getDate()) > eventDateList.get(currentIndex).getDate().durationFrom(target)) {
203 currentIndex++;
204 } else {
205 found = true;
206 }
207 }
208 }
209 return eventDateList.get(currentIndex);
210 }
211
212 /** Event date specification. */
213 private static class EventDate implements TimeStamped {
214
215 /** Event date. */
216 private final AbsoluteDate eventDate;
217
218 /** Flag for g function way around event date. */
219 private final boolean gIncrease;
220
221 /** Simple constructor.
222 * @param date date
223 * @param increase if true, g function increases around event date
224 */
225 EventDate(final AbsoluteDate date, final boolean increase) {
226 this.eventDate = date;
227 this.gIncrease = increase;
228 }
229
230 /** Getter for event date.
231 * @return event date
232 */
233 public AbsoluteDate getDate() {
234 return eventDate;
235 }
236
237 /** Getter for g function way at event date.
238 * @return g function increasing flag
239 */
240 public boolean isgIncrease() {
241 return gIncrease;
242 }
243
244 }
245
246 }