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