ParameterDrivenDateIntervalDetector.java

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

  20. import org.hipparchus.util.FastMath;
  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.propagation.SpacecraftState;
  24. import org.orekit.propagation.events.handlers.EventHandler;
  25. import org.orekit.propagation.events.handlers.StopOnDecreasing;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.utils.DateDriver;
  28. import org.orekit.utils.ParameterDriver;
  29. import org.orekit.utils.ParameterObserver;

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

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

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

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

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

  53.     /** Detection threshold. */
  54.     private static final double THRESHOLD = 1.0e-10;

  55.     /** Reference interval start driver. */
  56.     private DateDriver start;

  57.     /** Reference interval stop driver. */
  58.     private DateDriver stop;

  59.     /** Median date driver. */
  60.     private DateDriver median;

  61.     /** Duration driver. */
  62.     private ParameterDriver duration;

  63.     /** Build a new instance.
  64.      * @param prefix prefix to use for parameter drivers names
  65.      * @param refMedian reference interval median date
  66.      * @param refDuration reference duration
  67.      */
  68.     public ParameterDrivenDateIntervalDetector(final String prefix,
  69.                                                final AbsoluteDate refMedian, final double refDuration) {
  70.         this(prefix,
  71.              refMedian.shiftedBy(-0.5 * refDuration),
  72.              refMedian.shiftedBy(+0.5 * refDuration));
  73.     }

  74.     /** Build a new instance.
  75.      * @param prefix prefix to use for parameter drivers names
  76.      * @param refStart reference interval start date
  77.      * @param refStop reference interval stop date
  78.      */
  79.     public ParameterDrivenDateIntervalDetector(final String prefix,
  80.                                                final AbsoluteDate refStart, final AbsoluteDate refStop) {
  81.         this(FastMath.max(0.5 * refStop.durationFrom(refStart), THRESHOLD),
  82.              THRESHOLD, DEFAULT_MAX_ITER,
  83.              new StopOnDecreasing<ParameterDrivenDateIntervalDetector>(),
  84.              new DateDriver(refStart, prefix + START_SUFFIX, true),
  85.              new DateDriver(refStop, prefix + STOP_SUFFIX, false),
  86.              new DateDriver(refStart.shiftedBy(0.5 * refStop.durationFrom(refStart)), prefix + MEDIAN_SUFFIX, true),
  87.              new ParameterDriver(prefix + DURATION_SUFFIX, refStop.durationFrom(refStart), 1.0, 0.0, Double.POSITIVE_INFINITY));
  88.     }

  89.     /** Private constructor with full parameters.
  90.      * <p>
  91.      * This constructor is private as users are expected to use the builder
  92.      * API with the various {@code withXxx()} methods to set up the instance
  93.      * in a readable manner without using a huge amount of parameters.
  94.      * </p>
  95.      * @param maxCheck maximum checking interval (s)
  96.      * @param threshold convergence threshold (s)
  97.      * @param maxIter maximum number of iterations in the event time search
  98.      * @param handler event handler to call at event occurrences
  99.      * @param start reference interval start driver
  100.      * @param stop reference interval stop driver
  101.      * @param median median date driver
  102.      * @param duration duration driver
  103.      */
  104.     private ParameterDrivenDateIntervalDetector(final double maxCheck, final double threshold, final int maxIter,
  105.                                                 final EventHandler<? super ParameterDrivenDateIntervalDetector> handler,
  106.                                                 final DateDriver start, final DateDriver stop,
  107.                                                 final DateDriver median, final ParameterDriver duration) {
  108.         super(maxCheck, threshold, maxIter, handler);
  109.         this.start    = start;
  110.         this.stop     = stop;
  111.         this.median   = median;
  112.         this.duration = duration;

  113.         // set up delegation between drivers
  114.         replaceBindingObserver(start,    new StartObserver());
  115.         replaceBindingObserver(stop,     new StopObserver());
  116.         replaceBindingObserver(median,   new MedianObserver());
  117.         replaceBindingObserver(duration, new DurationObserver());

  118.     }

  119.     /** Replace binding observers.
  120.      * @param driver driver for whose binding observers should be replaced
  121.      * @param bindingObserver new binding observer
  122.      */
  123.     private void replaceBindingObserver(final ParameterDriver driver, final BindingObserver bindingObserver) {

  124.         // remove the previous binding observers
  125.         final List<ParameterObserver> original = driver.
  126.                                                  getObservers().
  127.                                                  stream().
  128.                                                  filter(observer -> observer instanceof ParameterDrivenDateIntervalDetector.BindingObserver).
  129.                                                  collect(Collectors.toList());
  130.         original.forEach(observer -> driver.removeObserver(observer));

  131.         driver.addObserver(bindingObserver);

  132.     }

  133.     /** {@inheritDoc} */
  134.     @Override
  135.     protected ParameterDrivenDateIntervalDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
  136.                                                          final EventHandler<? super ParameterDrivenDateIntervalDetector> newHandler) {
  137.         return new ParameterDrivenDateIntervalDetector(newMaxCheck, newThreshold, newMaxIter, newHandler,
  138.                                                        start, stop, median, duration);
  139.     }

  140.     /** Get the driver for start date.
  141.      * <p>
  142.      * Note that the start date is automatically adjusted if either
  143.      * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
  144.      * are {@link ParameterDriver#isSelected() selected} and changed.
  145.      * </p>
  146.      * @return driver for start date
  147.      */
  148.     public DateDriver getStartDriver() {
  149.         return start;
  150.     }

  151.     /** Get the driver for stop date.
  152.      * <p>
  153.      * Note that the stop date is automatically adjusted if either
  154.      * {@link #getMedianDriver() median date} or {@link #getDurationDriver() duration}
  155.      * are {@link ParameterDriver#isSelected() selected} changed.
  156.      * </p>
  157.      * @return driver for stop date
  158.      */
  159.     public DateDriver getStopDriver() {
  160.         return stop;
  161.     }

  162.     /** Get the driver for median date.
  163.      * <p>
  164.      * Note that the median date is automatically adjusted if either
  165.      * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
  166.      * are {@link ParameterDriver#isSelected() selected} changed.
  167.      * </p>
  168.      * @return driver for median date
  169.      */
  170.     public DateDriver getMedianDriver() {
  171.         return median;
  172.     }

  173.     /** Get the driver for duration.
  174.      * <p>
  175.      * Note that the duration is automatically adjusted if either
  176.      * {@link #getStartDriver()} start date or {@link #getStopDriver() stop date}
  177.      * are {@link ParameterDriver#isSelected() selected} changed.
  178.      * </p>
  179.      * @return driver for duration
  180.      */
  181.     public ParameterDriver getDurationDriver() {
  182.         return duration;
  183.     }

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

  200.     /** Base observer. */
  201.     private abstract class BindingObserver implements ParameterObserver {

  202.         /** {@inheritDoc} */
  203.         @Override
  204.         public void valueChanged(final double previousValue, final ParameterDriver driver) {
  205.             if (driver.isSelected()) {
  206.                 setDelta(driver.getValue() - previousValue);
  207.             }
  208.         }

  209.         /** {@inheritDoc} */
  210.         @Override
  211.         public void selectionChanged(final boolean previousSelection, final ParameterDriver driver) {
  212.             if ((start.isSelected()  || stop.isSelected()) &&
  213.                 (median.isSelected() || duration.isSelected())) {
  214.                 throw new OrekitException(OrekitMessages.INCONSISTENT_SELECTION,
  215.                                           start.getName(), stop.getName(),
  216.                                           median.getName(), duration.getName());
  217.             }
  218.         }

  219.         /** Change a value.
  220.          * @param delta change of value
  221.          */
  222.         protected abstract void setDelta(double delta);

  223.     }

  224.     /** Observer for start date. */
  225.     private class StartObserver extends BindingObserver {
  226.         /** {@inheritDoc} */
  227.         @Override
  228.         protected void setDelta(final double delta) {
  229.             median.setValue(median.getValue() + 0.5 * delta);
  230.             duration.setValue(duration.getValue() - delta);
  231.         }
  232.     }

  233.     /** Observer for stop date. */
  234.     private class StopObserver extends BindingObserver {
  235.         /** {@inheritDoc} */
  236.         @Override
  237.         protected void setDelta(final double delta) {
  238.             median.setValue(median.getValue() + 0.5 * delta);
  239.             duration.setValue(duration.getValue() + delta);
  240.         }
  241.     }

  242.     /** Observer for median date. */
  243.     private class MedianObserver extends BindingObserver {
  244.         /** {@inheritDoc} */
  245.         @Override
  246.         protected void setDelta(final double delta) {
  247.             start.setValue(start.getValue() + delta);
  248.             stop.setValue(stop.getValue() + delta);
  249.         }
  250.     }

  251.     /** Observer for duration. */
  252.     private class DurationObserver extends BindingObserver {
  253.         /** {@inheritDoc} */
  254.         @Override
  255.         protected void setDelta(final double delta) {
  256.             start.setValue(start.getValue() - 0.5 * delta);
  257.             stop.setValue(stop.getValue() + 0.5 * delta);
  258.         }
  259.     }

  260. }