BooleanDetector.java

  1. /* Contributed in the public domain.
  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.Arrays;
  20. import java.util.Collection;
  21. import java.util.List;
  22. import java.util.NoSuchElementException;

  23. import org.hipparchus.util.FastMath;
  24. import org.orekit.propagation.SpacecraftState;
  25. import org.orekit.propagation.events.handlers.ContinueOnEvent;
  26. import org.orekit.propagation.events.handlers.EventHandler;
  27. import org.orekit.propagation.events.intervals.AdaptableInterval;
  28. import org.orekit.time.AbsoluteDate;

  29. /**
  30.  * This class provides AND and OR operations for event detectors. This class treats
  31.  * positive values of the g function as true and negative values as false.
  32.  *
  33.  * <p> One example for an imaging satellite might be to only detect events when a
  34.  * satellite is overhead (elevation &gt; 0) AND when the ground point is sunlit (Sun
  35.  * elevation &gt; 0). Another slightly contrived example using the OR operator would be to
  36.  * detect access to a set of ground stations and only report events when the satellite
  37.  * enters or leaves the field of view of the set, but not hand-offs between the ground
  38.  * stations.
  39.  *
  40.  * <p> For the BooleanDetector is important that the sign of the g function of the
  41.  * underlying event detector is not arbitrary, but has a semantic meaning, e.g. in or out,
  42.  * true or false. This class works well with event detectors that detect entry to or exit
  43.  * from a region, e.g. {@link EclipseDetector}, {@link ElevationDetector}, {@link
  44.  * LatitudeCrossingDetector}. Using this detector with detectors that are not based on
  45.  * entry to or exit from a region, e.g. {@link DateDetector}, {@link
  46.  * LongitudeCrossingDetector}, will likely lead to unexpected results. To apply conditions
  47.  * to this latter type of event detectors a {@link EventEnablingPredicateFilter} is
  48.  * usually more appropriate.
  49.  *
  50.  * @author Evan Ward
  51.  * @see #andCombine(Collection)
  52.  * @see #orCombine(Collection)
  53.  * @see #notCombine(EventDetector)
  54.  * @see EventEnablingPredicateFilter
  55.  * @see EventSlopeFilter
  56.  */
  57. public class BooleanDetector extends AbstractDetector<BooleanDetector> {

  58.     /** Original detectors: the operands. */
  59.     private final List<EventDetector> detectors;

  60.     /** The composition function. Should be associative for predictable behavior. */
  61.     private final Operator operator;

  62.     /**
  63.      * Protected constructor with all the parameters.
  64.      *
  65.      * @param detectors    the operands.
  66.      * @param operator     reduction operator to apply to value of the g function of the
  67.      *                     operands.
  68.      * @param detectionSettings event detection settings.
  69.      * @param newHandler   event handler.
  70.      */
  71.     protected BooleanDetector(final List<EventDetector> detectors,
  72.                               final Operator operator,
  73.                               final EventDetectionSettings detectionSettings,
  74.                               final EventHandler newHandler) {
  75.         super(detectionSettings, newHandler);
  76.         this.detectors = detectors;
  77.         this.operator = operator;
  78.     }

  79.     /**
  80.      * Create a new event detector that is the logical AND of the given event detectors.
  81.      *
  82.      * <p> The created event detector's g function is positive if and only if the g
  83.      * functions of all detectors in {@code detectors} are positive.
  84.      *
  85.      * <p> The starting interval, threshold, and iteration count are set to the most
  86.      * stringent (minimum) of all the {@code detectors}. The event handlers of the
  87.      * underlying {@code detectors} are not used, instead the default handler is {@link
  88.      * ContinueOnEvent}.
  89.      *
  90.      * @param detectors the operands. Must contain at least one detector.
  91.      * @return a new event detector that is the logical AND of the operands.
  92.      * @throws NoSuchElementException if {@code detectors} is empty.
  93.      * @see BooleanDetector
  94.      * @see #andCombine(Collection)
  95.      * @see #orCombine(EventDetector...)
  96.      * @see #notCombine(EventDetector)
  97.      */
  98.     public static BooleanDetector andCombine(final EventDetector... detectors) {
  99.         return andCombine(Arrays.asList(detectors));
  100.     }

  101.     /**
  102.      * Create a new event detector that is the logical AND of the given event detectors.
  103.      *
  104.      * <p> The created event detector's g function is positive if and only if the g
  105.      * functions of all detectors in {@code detectors} are positive.
  106.      *
  107.      * <p> The starting interval, threshold, and iteration count are set to the most
  108.      * stringent (minimum) of the {@code detectors}. The event handlers of the
  109.      * underlying {@code detectors} are not used, instead the default handler is {@link
  110.      * ContinueOnEvent}.
  111.      *
  112.      * @param detectors the operands. Must contain at least one detector.
  113.      * @return a new event detector that is the logical AND of the operands.
  114.      * @throws NoSuchElementException if {@code detectors} is empty.
  115.      * @see BooleanDetector
  116.      * @see #andCombine(EventDetector...)
  117.      * @see #orCombine(Collection)
  118.      * @see #notCombine(EventDetector)
  119.      */
  120.     public static BooleanDetector andCombine(final Collection<? extends EventDetector> detectors) {

  121.         return new BooleanDetector(new ArrayList<>(detectors), // copy for immutability
  122.                 Operator.AND,
  123.                 new EventDetectionSettings(AdaptableInterval.of(Double.POSITIVE_INFINITY, detectors.stream()
  124.                         .map(EventDetector::getMaxCheckInterval).toArray(AdaptableInterval[]::new)),
  125.                 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
  126.                 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get()),
  127.                 new ContinueOnEvent());
  128.     }

  129.     /**
  130.      * Create a new event detector that is the logical OR of the given event detectors.
  131.      *
  132.      * <p> The created event detector's g function is positive if and only if at least
  133.      * one of g functions of the event detectors in {@code detectors} is positive.
  134.      *
  135.      * <p> The starting interval, threshold, and iteration count are set to the most
  136.      * stringent (minimum) of the {@code detectors}. The event handlers of the
  137.      * underlying EventDetectors are not used, instead the default handler is {@link
  138.      * ContinueOnEvent}.
  139.      *
  140.      * @param detectors the operands. Must contain at least one detector.
  141.      * @return a new event detector that is the logical OR of the operands.
  142.      * @throws NoSuchElementException if {@code detectors} is empty.
  143.      * @see BooleanDetector
  144.      * @see #orCombine(Collection)
  145.      * @see #andCombine(EventDetector...)
  146.      * @see #notCombine(EventDetector)
  147.      */
  148.     public static BooleanDetector orCombine(final EventDetector... detectors) {
  149.         return orCombine(Arrays.asList(detectors));
  150.     }

  151.     /**
  152.      * Create a new event detector that is the logical OR of the given event detectors.
  153.      *
  154.      * <p> The created event detector's g function is positive if and only if at least
  155.      * one of g functions of the event detectors in {@code detectors} is positive.
  156.      *
  157.      * <p> The starting interval, threshold, and iteration count are set to the most
  158.      * stringent (minimum) of the {@code detectors}. The event handlers of the
  159.      * underlying EventDetectors are not used, instead the default handler is {@link
  160.      * ContinueOnEvent}.
  161.      *
  162.      * @param detectors the operands. Must contain at least one detector.
  163.      * @return a new event detector that is the logical OR of the operands.
  164.      * @throws NoSuchElementException if {@code detectors} is empty.
  165.      * @see BooleanDetector
  166.      * @see #orCombine(EventDetector...)
  167.      * @see #andCombine(Collection)
  168.      * @see #notCombine(EventDetector)
  169.      */
  170.     public static BooleanDetector orCombine(final Collection<? extends EventDetector> detectors) {

  171.         return new BooleanDetector(new ArrayList<>(detectors), // copy for immutability
  172.                 Operator.OR,
  173.                 new EventDetectionSettings(AdaptableInterval.of(Double.POSITIVE_INFINITY, detectors.stream()
  174.                         .map(EventDetector::getMaxCheckInterval).toArray(AdaptableInterval[]::new)),
  175.                 detectors.stream().map(EventDetector::getThreshold).min(Double::compareTo).get(),
  176.                 detectors.stream().map(EventDetector::getMaxIterationCount).min(Integer::compareTo).get()),
  177.                 new ContinueOnEvent());
  178.     }

  179.     /**
  180.      * Create a new event detector that negates the g function of another detector.
  181.      *
  182.      * <p> This detector will be initialized with the same {@link
  183.      * EventDetector#getMaxCheckInterval()}, {@link EventDetector#getThreshold()}, and
  184.      * {@link EventDetector#getMaxIterationCount()} as {@code detector}. The event handler
  185.      * of the underlying detector is not used, instead the default handler is {@link
  186.      * ContinueOnEvent}.
  187.      *
  188.      * @param detector to negate.
  189.      * @return a new event detector whose g function is the same magnitude but opposite
  190.      * sign of {@code detector}.
  191.      * @see #andCombine(Collection)
  192.      * @see #orCombine(Collection)
  193.      * @see BooleanDetector
  194.      */
  195.     public static NegateDetector notCombine(final EventDetector detector) {
  196.         return new NegateDetector(detector);
  197.     }

  198.     @Override
  199.     public boolean dependsOnTimeOnly() {
  200.         return getDetectors().stream().allMatch(EventDetector::dependsOnTimeOnly);
  201.     }

  202.     @Override
  203.     public double g(final SpacecraftState s) {
  204.         // can't use stream/lambda here because g(s) throws a checked exception
  205.         // so write out and combine the map and reduce loops
  206.         double ret = Double.NaN; // return value
  207.         boolean first = true;
  208.         for (final EventDetector detector : detectors) {
  209.             if (first) {
  210.                 ret = detector.g(s);
  211.                 first = false;
  212.             } else {
  213.                 ret = operator.combine(ret, detector.g(s));
  214.             }
  215.         }
  216.         // return the result of applying the operator to all operands
  217.         return ret;
  218.     }

  219.     @Override
  220.     protected BooleanDetector create(final EventDetectionSettings detectionSettings,
  221.                                      final EventHandler newHandler) {
  222.         return new BooleanDetector(detectors, operator, detectionSettings, newHandler);
  223.     }

  224.     @Override
  225.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  226.         super.init(s0, t);
  227.         for (final EventDetector detector : detectors) {
  228.             detector.init(s0, t);
  229.         }
  230.     }

  231.     @Override
  232.     public void reset(final SpacecraftState state, final AbsoluteDate target) {
  233.         super.init(state, target);
  234.         for (final EventDetector detector : detectors) {
  235.             detector.reset(state, target);
  236.         }
  237.     }

  238.     @Override
  239.     public void finish(final SpacecraftState state) {
  240.         super.finish(state);
  241.         for (final EventDetector detector : detectors) {
  242.             detector.finish(state);
  243.         }
  244.     }

  245.     /**
  246.      * Get the list of original detectors.
  247.      * @return the list of original detectors
  248.      * @since 10.2
  249.      */
  250.     public List<EventDetector> getDetectors() {
  251.         return new ArrayList<>(detectors);
  252.     }

  253.     /** Local class for operator. */
  254.     private enum Operator {

  255.         /** And operator. */
  256.         AND() {

  257.             @Override
  258.             /** {@inheritDoc} */
  259.             public double combine(final double g1, final double g2) {
  260.                 return FastMath.min(g1, g2);
  261.             }

  262.         },

  263.         /** Or operator. */
  264.         OR() {

  265.             @Override
  266.             /** {@inheritDoc} */
  267.             public double combine(final double g1, final double g2) {
  268.                 return FastMath.max(g1, g2);
  269.             }

  270.         };

  271.         /** Combine two g functions evaluations.
  272.          * @param g1 first evaluation
  273.          * @param g2 second evaluation
  274.          * @return combined evaluation
  275.          */
  276.         public abstract double combine(double g1, double g2);

  277.     }

  278. }