FieldDateDetector.java

  1. /* Copyright 2002-2024 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. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;

  21. import org.hipparchus.CalculusFieldElement;
  22. import org.hipparchus.Field;
  23. import org.hipparchus.ode.events.Action;
  24. import org.orekit.errors.OrekitIllegalArgumentException;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.propagation.FieldSpacecraftState;
  27. import org.orekit.propagation.events.handlers.FieldEventHandler;
  28. import org.orekit.propagation.events.handlers.FieldStopOnEvent;
  29. import org.orekit.time.FieldAbsoluteDate;
  30. import org.orekit.time.FieldTimeStamped;

  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(Field, 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 minGap.</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.  * @param <T> type of the field elements
  46.  */
  47. public class FieldDateDetector<T extends CalculusFieldElement<T>> extends FieldAbstractDetector<FieldDateDetector<T>, T>
  48.     implements FieldTimeStamped<T> {

  49.     /** Default value for max check.
  50.      * @since 12.0
  51.      */
  52.     public static final double DEFAULT_MAX_CHECK = 1.0e10;

  53.     /** Default value for minimum gap between added dates.
  54.      * @since 12.0
  55.      */
  56.     public static final double DEFAULT_MIN_GAP = 1.0;

  57.     /** Default value for convergence threshold.
  58.      * @since 12.0
  59.      */
  60.     public static final double DEFAULT_THRESHOLD = 1.0e-13;

  61.     /** Minimum gap between added dates.
  62.      * @since 12.0
  63.      */
  64.     private final double minGap;

  65.     /** Last date for g computation. */
  66.     private FieldAbsoluteDate<T> gDate;

  67.     /** List of event dates. */
  68.     private final ArrayList<FieldEventDate<T>> eventDateList;

  69.     /** Current event date. */
  70.     private int currentIndex;

  71.     /** Build a new instance.
  72.      * <p>First event dates are set here, but others can be
  73.      * added later with {@link #addEventDate(FieldAbsoluteDate)}.</p>
  74.      * @param field field to which dates belong
  75.      * @param dates list of event dates
  76.      * @see #addEventDate(FieldAbsoluteDate)
  77.      * @since 12.0
  78.      */
  79.     @SafeVarargs
  80.     public FieldDateDetector(final Field<T> field, final FieldTimeStamped<T>... dates) {
  81.         this(s -> DEFAULT_MAX_CHECK, field.getZero().newInstance(DEFAULT_THRESHOLD),
  82.              DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), DEFAULT_MIN_GAP, dates);
  83.     }

  84.     /** Protected constructor with full parameters.
  85.      * <p>
  86.      * This constructor is not public as users are expected to use the builder
  87.      * API with the various {@code withXxx()} methods to set up the instance
  88.      * in a readable manner without using a huge amount of parameters.
  89.      * </p>
  90.      * @param maxCheck maximum checking interval
  91.      * @param threshold convergence threshold (s)
  92.      * @param maxIter maximum number of iterations in the event time search
  93.      * @param handler event handler to call at event occurrences
  94.      * @param minGap minimum gap between added dates (s)
  95.      * @param dates list of event dates
  96.      */
  97.     @SafeVarargs
  98.     protected FieldDateDetector(final FieldAdaptableInterval<T> maxCheck, final T threshold,
  99.                                 final int maxIter, final FieldEventHandler<T> handler,
  100.                                 final double minGap, final FieldTimeStamped<T>... dates) {
  101.         super(maxCheck, threshold, maxIter, handler);
  102.         this.currentIndex  = -1;
  103.         this.gDate         = null;
  104.         this.eventDateList = new ArrayList<FieldDateDetector.FieldEventDate<T>>(dates.length);
  105.         for (final FieldTimeStamped<T> ts : dates) {
  106.             addEventDate(ts.getDate());
  107.         }
  108.         this.minGap        = minGap;
  109.     }

  110.     /**
  111.      * Setup minimum gap between added dates.
  112.      * @param newMinGap new minimum gap between added dates
  113.      * @return a new detector with updated configuration (the instance is not changed)
  114.      * @since 12.0
  115.      */
  116.     public FieldDateDetector<T> withMinGap(final double newMinGap) {
  117.         @SuppressWarnings("unchecked")
  118.         final FieldTimeStamped<T>[] dates = eventDateList.toArray(new FieldEventDate[eventDateList.size()]);
  119.         return new FieldDateDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(),
  120.                                        getHandler(), newMinGap, dates);
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     protected FieldDateDetector<T> create(final FieldAdaptableInterval<T> newMaxCheck, final T newThreshold,
  125.                                           final int newMaxIter, final FieldEventHandler<T> newHandler) {
  126.         @SuppressWarnings("unchecked")
  127.         final FieldTimeStamped<T>[] dates = eventDateList.toArray(new FieldEventDate[eventDateList.size()]);
  128.         return new FieldDateDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, minGap, dates);
  129.     }

  130.     /** Get all event field dates currently managed, in chronological order.
  131.      * @return all event field dates currently managed, in chronological order
  132.      * @since 12.0
  133.      */
  134.     public List<FieldTimeStamped<T>> getDates() {
  135.         return Collections.unmodifiableList(eventDateList);
  136.     }

  137.     /** Compute the value of the switching function.
  138.      * This function measures the difference between the current and the target date.
  139.      * @param s the current state information: date, kinematics, attitude
  140.      * @return value of the switching function
  141.      */
  142.     public T g(final FieldSpacecraftState<T> s) {
  143.         gDate = s.getDate();
  144.         if (currentIndex < 0) {
  145.             return s.getA().getField().getZero().add(-1);
  146.         } else {
  147.             final FieldEventDate<T> event = getClosest(gDate);
  148.             return event.isgIncrease() ? gDate.durationFrom(event.getDate()) : event.getDate().durationFrom(gDate);
  149.         }
  150.     }

  151.     /** Get the current event date according to the propagator.
  152.      * @return event date
  153.      */
  154.     public FieldAbsoluteDate<T> getDate() {
  155.         return currentIndex < 0 ? null : eventDateList.get(currentIndex).getDate();
  156.     }

  157.     /** Add an event date.
  158.      * <p>The date to add must be:</p>
  159.      * <ul>
  160.      *   <li>less than the smallest already registered event date minus the maxCheck</li>
  161.      *   <li>or more than the largest already registered event date plus the maxCheck</li>
  162.      * </ul>
  163.      * @param target target date
  164.      * @throws IllegalArgumentException if the date is too close from already defined interval
  165.      * @see #FieldDateDetector(Field, FieldTimeStamped...)
  166.      */
  167.     public void addEventDate(final FieldAbsoluteDate<T> target) throws IllegalArgumentException {
  168.         final boolean increasing;
  169.         if (currentIndex < 0) {
  170.             increasing = (gDate == null) ? true : target.durationFrom(gDate).getReal() > 0.0;
  171.             currentIndex = 0;
  172.             eventDateList.add(new FieldEventDate<>(target, increasing));
  173.         } else {
  174.             final                      int lastIndex = eventDateList.size() - 1;
  175.             final FieldAbsoluteDate<T> firstDate     = eventDateList.get(0).getDate();
  176.             final FieldAbsoluteDate<T> lastDate      = eventDateList.get(lastIndex).getDate();
  177.             if (firstDate.durationFrom(target).getReal() > minGap) {
  178.                 increasing = !eventDateList.get(0).isgIncrease();
  179.                 eventDateList.add(0, new FieldEventDate<>(target, increasing));
  180.                 currentIndex++;
  181.             } else if (target.durationFrom(lastDate).getReal() > minGap) {
  182.                 increasing = !eventDateList.get(lastIndex).isgIncrease();
  183.                 eventDateList.add(new FieldEventDate<>(target, increasing));
  184.             } else {
  185.                 throw new OrekitIllegalArgumentException(OrekitMessages.EVENT_DATE_TOO_CLOSE,
  186.                                                          target,
  187.                                                          firstDate,
  188.                                                          lastDate,
  189.                                                          minGap,
  190.                                                          firstDate.durationFrom(target),
  191.                                                          target.durationFrom(lastDate));
  192.             }
  193.         }
  194.     }

  195.     /** Get the closest EventDate to the target date.
  196.      * @param target target date
  197.      * @return current EventDate
  198.      */
  199.     private FieldEventDate<T> getClosest(final FieldAbsoluteDate<T> target) {
  200.         final T dt = target.durationFrom(eventDateList.get(currentIndex).getDate());
  201.         if (dt.getReal() < 0.0 && currentIndex > 0) {
  202.             boolean found = false;
  203.             while (currentIndex > 0 && !found) {
  204.                 if (target.durationFrom(eventDateList.get(currentIndex - 1).getDate()).getReal() < eventDateList.get(currentIndex).getDate().durationFrom(target).getReal()) {
  205.                     currentIndex--;
  206.                 } else {
  207.                     found = true;
  208.                 }
  209.             }
  210.         } else if (dt.getReal() > 0.0 && currentIndex < eventDateList.size() - 1) {
  211.             final int maxIndex = eventDateList.size() - 1;
  212.             boolean found = false;
  213.             while (currentIndex < maxIndex && !found) {
  214.                 if (target.durationFrom(eventDateList.get(currentIndex + 1).getDate()).getReal() > eventDateList.get(currentIndex).getDate().durationFrom(target).getReal()) {
  215.                     currentIndex++;
  216.                 } else {
  217.                     found = true;
  218.                 }
  219.             }
  220.         }
  221.         return eventDateList.get(currentIndex);
  222.     }

  223.     /** Event date specification. */
  224.     private static class FieldEventDate<T extends CalculusFieldElement<T>> implements FieldTimeStamped<T> {

  225.         /** Event date. */
  226.         private final FieldAbsoluteDate<T> eventDate;

  227.         /** Flag for g function way around event date. */
  228.         private final boolean gIncrease;

  229.         /** Simple constructor.
  230.          * @param date date
  231.          * @param increase if true, g function increases around event date
  232.          */
  233.         FieldEventDate(final FieldAbsoluteDate<T> date, final boolean increase) {
  234.             this.eventDate = date;
  235.             this.gIncrease = increase;
  236.         }

  237.         /** Getter for event date.
  238.          * @return event date
  239.          */
  240.         public FieldAbsoluteDate<T> getDate() {
  241.             return eventDate;
  242.         }

  243.         /** Getter for g function way at event date.
  244.          * @return g function increasing flag
  245.          */
  246.         public boolean isgIncrease() {
  247.             return gIncrease;
  248.         }

  249.     }

  250. }