FieldParameterDrivenDateIntervalDetector.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.List;
  19. import java.util.stream.Collectors;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.Field;
  22. import org.hipparchus.util.FastMath;
  23. import org.orekit.errors.OrekitException;
  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.AbsoluteDate;
  29. import org.orekit.utils.DateDriver;
  30. import org.orekit.utils.ParameterDriver;
  31. import org.orekit.utils.ParameterObserver;
  32. import org.orekit.utils.TimeSpanMap;
  33. import org.orekit.utils.TimeSpanMap.Span;

  34. /** Detector for date intervals that may be offset thanks to parameter drivers.
  35.  * <p>
  36.  * Two dual views can be used for date intervals: either start date/stop date or
  37.  * median date/duration. {@link #getStartDriver() start}/{@link #getStopDriver() stop}
  38.  * drivers and {@link #getMedianDriver() median}/{@link #getDurationDriver() duration}
  39.  * drivers work in pair. Both drivers in one pair can be selected and their changes will
  40.  * be propagated to the other pair, but attempting to select drivers in both
  41.  * pairs at the same time will trigger an exception. Changing the value of a driver
  42.  * that is not selected should be avoided as it leads to inconsistencies between the pairs.
  43.  * </p>
  44.  * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
  45.  * @param <T> type of the field elements
  46.  * @author Luc Maisonobe
  47.  * @since 11.1
  48.  */
  49. public class FieldParameterDrivenDateIntervalDetector<T extends CalculusFieldElement<T>>
  50.     extends FieldAbstractDetector<FieldParameterDrivenDateIntervalDetector<T>, T> {

  51.     /** Default suffix for start driver. */
  52.     public static final String START_SUFFIX = "_START";

  53.     /** Default suffix for stop driver. */
  54.     public static final String STOP_SUFFIX = "_STOP";

  55.     /** Default suffix for median driver. */
  56.     public static final String MEDIAN_SUFFIX = "_MEDIAN";

  57.     /** Default suffix for duration driver. */
  58.     public static final String DURATION_SUFFIX = "_DURATION";

  59.     /** Reference interval start driver. */
  60.     private DateDriver start;

  61.     /** Reference interval stop driver. */
  62.     private DateDriver stop;

  63.     /** Median date driver. */
  64.     private DateDriver median;

  65.     /** Duration driver. */
  66.     private ParameterDriver duration;

  67.     /** Build a new instance.
  68.      * @param field field to which the elements belong
  69.      * @param prefix prefix to use for parameter drivers names
  70.      * @param refMedian reference interval median date
  71.      * @param refDuration reference duration
  72.      */
  73.     public FieldParameterDrivenDateIntervalDetector(final Field<T> field, final String prefix,
  74.                                                     final AbsoluteDate refMedian, final double refDuration) {
  75.         this(field, prefix,
  76.              refMedian.shiftedBy(-0.5 * refDuration),
  77.              refMedian.shiftedBy(+0.5 * refDuration));
  78.     }

  79.     /** Build a new instance.
  80.      * @param field field to which the elements belong
  81.      * @param prefix prefix to use for parameter drivers names
  82.      * @param refStart reference interval start date
  83.      * @param refStop reference interval stop date
  84.      */
  85.     public FieldParameterDrivenDateIntervalDetector(final Field<T> field, final String prefix,
  86.                                                     final AbsoluteDate refStart, final AbsoluteDate refStop) {
  87.         this(s -> DEFAULT_MAXCHECK,
  88.              field.getZero().newInstance(DEFAULT_THRESHOLD),
  89.              DEFAULT_MAX_ITER,
  90.              new FieldStopOnEvent<>(),
  91.              new DateDriver(refStart, prefix + START_SUFFIX, true),
  92.              new DateDriver(refStop, prefix + STOP_SUFFIX, false),
  93.              new DateDriver(refStart.shiftedBy(0.5 * refStop.durationFrom(refStart)), prefix + MEDIAN_SUFFIX, true),
  94.              new ParameterDriver(prefix + DURATION_SUFFIX, refStop.durationFrom(refStart), 1.0, 0.0, Double.POSITIVE_INFINITY));
  95.     }

  96.     /** Protected constructor with full parameters.
  97.      * <p>
  98.      * This constructor is not public as users are expected to use the builder
  99.      * API with the various {@code withXxx()} methods to set up the instance
  100.      * in a readable manner without using a huge amount of parameters.
  101.      * </p>
  102.      * @param maxCheck maximum checking interval
  103.      * @param threshold convergence threshold (s)
  104.      * @param maxIter maximum number of iterations in the event time search
  105.      * @param handler event handler to call at event occurrences
  106.      * @param start reference interval start driver
  107.      * @param stop reference interval stop driver
  108.      * @param median median date driver
  109.      * @param duration duration driver
  110.      */
  111.     protected FieldParameterDrivenDateIntervalDetector(final FieldAdaptableInterval<T> maxCheck, final T threshold, final int maxIter,
  112.                                                        final FieldEventHandler<T> handler,
  113.                                                        final DateDriver start, final DateDriver stop,
  114.                                                        final DateDriver median, final ParameterDriver duration) {
  115.         super(maxCheck, threshold, maxIter, handler);
  116.         this.start    = start;
  117.         this.stop     = stop;
  118.         this.median   = median;
  119.         this.duration = duration;

  120.         // set up delegation between drivers
  121.         replaceBindingObserver(start,    new StartObserver());
  122.         replaceBindingObserver(stop,     new StopObserver());
  123.         replaceBindingObserver(median,   new MedianObserver());
  124.         replaceBindingObserver(duration, new DurationObserver());

  125.     }

  126.     /** Replace binding observers.
  127.      * @param driver driver for whose binding observers should be replaced
  128.      * @param bindingObserver new binding observer
  129.      */
  130.     private void replaceBindingObserver(final ParameterDriver driver, final BindingObserver bindingObserver) {

  131.         // remove the previous binding observers
  132.         final List<ParameterObserver> original = driver.
  133.                                                  getObservers().
  134.                                                  stream().
  135.                                                  filter(observer -> observer instanceof FieldParameterDrivenDateIntervalDetector.BindingObserver).
  136.                                                  collect(Collectors.toList());
  137.         original.forEach(observer -> driver.removeObserver(observer));

  138.         driver.addObserver(bindingObserver);

  139.     }

  140.     /** {@inheritDoc} */
  141.     @Override
  142.     protected FieldParameterDrivenDateIntervalDetector<T> create(final FieldAdaptableInterval<T> newMaxCheck, final T newThreshold, final int newMaxIter,
  143.                                                                  final FieldEventHandler<T> newHandler) {
  144.         return new FieldParameterDrivenDateIntervalDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler,
  145.                                                               start, stop, median, duration);
  146.     }

  147.     /** Get the driver for start date.
  148.      * <p>
  149.      * Note that the start date is automatically adjusted if either
  150.      * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
  151.      * are {@link ParameterDriver#isSelected() selected} and changed.
  152.      * </p>
  153.      * @return driver for start date
  154.      */
  155.     public DateDriver getStartDriver() {
  156.         return start;
  157.     }

  158.     /** Get the driver for stop date.
  159.      * <p>
  160.      * Note that the stop date is automatically adjusted if either
  161.      * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
  162.      * are {@link ParameterDriver#isSelected() selected} changed.
  163.      * </p>
  164.      * @return driver for stop date
  165.      */
  166.     public DateDriver getStopDriver() {
  167.         return stop;
  168.     }

  169.     /** Get the driver for median date.
  170.      * <p>
  171.      * Note that the median date is automatically adjusted if either
  172.      * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
  173.      * are {@link ParameterDriver#isSelected() selected} changed.
  174.      * </p>
  175.      * @return driver for median date
  176.      */
  177.     public DateDriver getMedianDriver() {
  178.         return median;
  179.     }

  180.     /** Get the driver for duration.
  181.      * <p>
  182.      * Note that the duration is automatically adjusted if either
  183.      * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
  184.      * are {@link ParameterDriver#isSelected() selected} changed.
  185.      * </p>
  186.      * @return driver for duration
  187.      */
  188.     public ParameterDriver getDurationDriver() {
  189.         return duration;
  190.     }

  191.     /** Compute the value of the switching function.
  192.      * <p>
  193.      * The function is positive for dates within the interval defined
  194.      * by applying the parameter drivers shifts to reference dates,
  195.      * and negative for dates outside of this interval. Note that
  196.      * if Δt_start - Δt_stop is less than ref_stop.durationFrom(ref_start),
  197.      * then the interval degenerates to empty and the function never
  198.      * reaches positive values.
  199.      * </p>
  200.      * @param s the current state information: date, kinematics, attitude
  201.      * @return value of the switching function
  202.      */
  203.     public T g(final FieldSpacecraftState<T> s) {
  204.         return FastMath.min(s.getDate().durationFrom(start.getDate()),
  205.                             s.getDate().durationFrom(stop.getDate()).negate());
  206.     }

  207.     /** Base observer. */
  208.     private abstract class BindingObserver implements ParameterObserver {

  209.         /** {@inheritDoc} */
  210.         @Override
  211.         public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
  212.             if (driver.isSelected()) {
  213.                 setDelta(driver.getValue(date) - previousValue, date);
  214.             }
  215.         }

  216.         /** {@inheritDoc} */
  217.         @Override
  218.         public void valueSpanMapChanged(final TimeSpanMap<Double> previousValue, final ParameterDriver driver) {
  219.             if (driver.isSelected()) {
  220.                 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
  221.                     setDelta(span.getData() - previousValue.get(span.getStart()), span.getStart());
  222.                 }
  223.             }
  224.         }

  225.         /** {@inheritDoc} */
  226.         @Override
  227.         public void selectionChanged(final boolean previousSelection, final ParameterDriver driver) {
  228.             if ((start.isSelected()  || stop.isSelected()) &&
  229.                 (median.isSelected() || duration.isSelected())) {
  230.                 throw new OrekitException(OrekitMessages.INCONSISTENT_SELECTION,
  231.                                           start.getName(), stop.getName(),
  232.                                           median.getName(), duration.getName());
  233.             }
  234.         }

  235.         /** Change a value.
  236.          * @param date date for which the value wants to be change
  237.          * @param delta change of value
  238.          */
  239.         protected abstract void setDelta(double delta, AbsoluteDate date);

  240.     }

  241.     /** Observer for start date. */
  242.     private class StartObserver extends BindingObserver {
  243.         /** {@inheritDoc} */
  244.         @Override
  245.         protected void setDelta(final double delta, final AbsoluteDate date) {
  246.             median.setValue(median.getValue(date) + 0.5 * delta, date);
  247.             duration.setValue(duration.getValue(date) - delta, date);
  248.         }
  249.     }

  250.     /** Observer for stop date. */
  251.     private class StopObserver extends BindingObserver {
  252.         /** {@inheritDoc} */
  253.         @Override
  254.         protected void setDelta(final double delta, final AbsoluteDate date) {
  255.             median.setValue(median.getValue(date) + 0.5 * delta, date);
  256.             duration.setValue(duration.getValue(date) + delta, date);
  257.         }
  258.     }

  259.     /** Observer for median date. */
  260.     private class MedianObserver extends BindingObserver {
  261.         /** {@inheritDoc} */
  262.         @Override
  263.         protected void setDelta(final double delta, final AbsoluteDate date) {
  264.             start.setValue(start.getValue(date) + delta, date);
  265.             stop.setValue(stop.getValue(date) + delta, date);
  266.         }
  267.     }

  268.     /** Observer for duration. */
  269.     private class DurationObserver extends BindingObserver {
  270.         /** {@inheritDoc} */
  271.         @Override
  272.         protected void setDelta(final double delta, final AbsoluteDate date) {
  273.             start.setValue(start.getValue(date) - 0.5 * delta, date);
  274.             stop.setValue(stop.getValue(date) + 0.5 * delta, date);
  275.         }
  276.     }

  277. }