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 19 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 import java.util.Collection; 23 import java.util.Comparator; 24 import java.util.List; 25 import java.util.NoSuchElementException; 26 27 import org.hipparchus.CalculusFieldElement; 28 import org.hipparchus.util.FastMath; 29 import org.orekit.propagation.FieldSpacecraftState; 30 import org.orekit.propagation.events.handlers.FieldContinueOnEvent; 31 import org.orekit.propagation.events.handlers.FieldEventHandler; 32 import org.orekit.time.FieldAbsoluteDate; 33 34 /** 35 * This class provides AND and OR operations for event detectors. This class treats 36 * positive values of the g function as true and negative values as false. 37 * 38 * <p> One example for an imaging satellite might be to only detect events when a 39 * satellite is overhead (elevation > 0) AND when the ground point is sunlit (Sun 40 * elevation > 0). Another slightly contrived example using the OR operator would be to 41 * detect access to a set of ground stations and only report events when the satellite 42 * enters or leaves the field of view of the set, but not hand-offs between the ground 43 * stations. 44 * 45 * <p> For the FieldBooleanDetector is important that the sign of the g function of the 46 * underlying event detector is not arbitrary, but has a semantic meaning, e.g. in or out, 47 * true or false. This class works well with event detectors that detect entry to or exit 48 * from a region, e.g. {@link FieldEclipseDetector}, {@link FieldElevationDetector}, {@link 49 * FieldLatitudeCrossingDetector}. Using this detector with detectors that are not based on 50 * entry to or exit from a region, e.g. {@link FieldDateDetector}, will likely lead to 51 * unexpected results. To apply conditions to this latter type of event detectors a 52 * {@link FieldEventEnablingPredicateFilter} is usually more appropriate. 53 * 54 * @param <T> type of the field elements 55 * @since 12.0 56 * @author Evan Ward 57 * @author luc Luc Maisonobe 58 * @see #andCombine(Collection) 59 * @see #orCombine(Collection) 60 * @see #notCombine(FieldEventDetector) 61 * @see EventEnablingPredicateFilter 62 * @see EventSlopeFilter 63 */ 64 public class FieldBooleanDetector<T extends CalculusFieldElement<T>> extends FieldAbstractDetector<FieldBooleanDetector<T>, T> { 65 66 /** Original detectors: the operands. */ 67 private final List<FieldEventDetector<T>> detectors; 68 69 /** The composition function. Should be associative for predictable behavior. */ 70 private final Operator operator; 71 72 /** 73 * Private constructor with all the parameters. 74 * 75 * @param detectors the operands. 76 * @param operator reduction operator to apply to value of the g function of the 77 * operands. 78 * @param newMaxCheck max check interval. 79 * @param newThreshold convergence threshold in seconds. 80 * @param newMaxIter max iterations. 81 * @param newHandler event handler. 82 */ 83 protected FieldBooleanDetector(final List<FieldEventDetector<T>> detectors, 84 final Operator operator, 85 final FieldAdaptableInterval<T> newMaxCheck, 86 final T newThreshold, 87 final int newMaxIter, 88 final FieldEventHandler<T> newHandler) { 89 super(newMaxCheck, newThreshold, newMaxIter, newHandler); 90 this.detectors = detectors; 91 this.operator = operator; 92 } 93 94 /** 95 * Create a new event detector that is the logical AND of the given event detectors. 96 * 97 * <p> The created event detector's g function is positive if and only if the g 98 * functions of all detectors in {@code detectors} are positive. 99 * 100 * <p> The starting interval, threshold, and iteration count are set to the most 101 * stringent (minimum) of all the {@code detectors}. The event handlers of the 102 * underlying {@code detectors} are not used, instead the default handler is {@link 103 * FieldContinueOnEvent}. 104 * 105 * @param <T> type of the field elements 106 * @param detectors the operands. Must contain at least one detector. 107 * @return a new event detector that is the logical AND of the operands. 108 * @throws NoSuchElementException if {@code detectors} is empty. 109 * @see FieldBooleanDetector 110 * @see #andCombine(Collection) 111 * @see #orCombine(FieldEventDetector...) 112 * @see #notCombine(FieldEventDetector) 113 */ 114 @SafeVarargs 115 public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> andCombine(final FieldEventDetector<T>... detectors) { 116 return andCombine(Arrays.asList(detectors)); 117 } 118 119 /** 120 * Create a new event detector that is the logical AND of the given event detectors. 121 * 122 * <p> The created event detector's g function is positive if and only if the g 123 * functions of all detectors in {@code detectors} are positive. 124 * 125 * <p> The starting interval, threshold, and iteration count are set to the most 126 * stringent (minimum) of the {@code detectors}. The event handlers of the 127 * underlying {@code detectors} are not used, instead the default handler is {@link 128 * FieldContinueOnEvent}. 129 * 130 * @param <T> type of the field elements 131 * @param detectors the operands. Must contain at least one detector. 132 * @return a new event detector that is the logical AND of the operands. 133 * @throws NoSuchElementException if {@code detectors} is empty. 134 * @see FieldBooleanDetector 135 * @see #andCombine(FieldEventDetector...) 136 * @see #orCombine(Collection) 137 * @see #notCombine(FieldEventDetector) 138 */ 139 public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> andCombine(final Collection<? extends FieldEventDetector<T>> detectors) { 140 141 return new FieldBooleanDetector<>(new ArrayList<>(detectors), // copy for immutability 142 Operator.AND, 143 s -> { 144 double minInterval = Double.POSITIVE_INFINITY; 145 for (final FieldEventDetector<T> detector : detectors) { 146 minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); 147 } 148 return minInterval; 149 }, 150 detectors.stream().map(FieldEventDetector::getThreshold).min(new FieldComparator<>()).get(), 151 detectors.stream().map(FieldEventDetector::getMaxIterationCount).min(Integer::compareTo).get(), 152 new FieldContinueOnEvent<>()); 153 } 154 155 /** 156 * Create a new event detector that is the logical OR of the given event detectors. 157 * 158 * <p> The created event detector's g function is positive if and only if at least 159 * one of g functions of the event detectors in {@code detectors} is positive. 160 * 161 * <p> The starting interval, threshold, and iteration count are set to the most 162 * stringent (minimum) of the {@code detectors}. The event handlers of the 163 * underlying EventDetectors are not used, instead the default handler is {@link 164 * FieldContinueOnEvent}. 165 * 166 * @param <T> type of the field elements 167 * @param detectors the operands. Must contain at least one detector. 168 * @return a new event detector that is the logical OR of the operands. 169 * @throws NoSuchElementException if {@code detectors} is empty. 170 * @see FieldBooleanDetector 171 * @see #orCombine(Collection) 172 * @see #andCombine(FieldEventDetector...) 173 * @see #notCombine(FieldEventDetector) 174 */ 175 @SafeVarargs 176 public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> orCombine(final FieldEventDetector<T>... detectors) { 177 return orCombine(Arrays.asList(detectors)); 178 } 179 180 /** 181 * Create a new event detector that is the logical OR of the given event detectors. 182 * 183 * <p> The created event detector's g function is positive if and only if at least 184 * one of g functions of the event detectors in {@code detectors} is positive. 185 * 186 * <p> The starting interval, threshold, and iteration count are set to the most 187 * stringent (minimum) of the {@code detectors}. The event handlers of the 188 * underlying EventDetectors are not used, instead the default handler is {@link 189 * FieldContinueOnEvent}. 190 * 191 * @param <T> type of the field elements 192 * @param detectors the operands. Must contain at least one detector. 193 * @return a new event detector that is the logical OR of the operands. 194 * @throws NoSuchElementException if {@code detectors} is empty. 195 * @see FieldBooleanDetector 196 * @see #orCombine(FieldEventDetector...) 197 * @see #andCombine(Collection) 198 * @see #notCombine(FieldEventDetector) 199 */ 200 public static <T extends CalculusFieldElement<T>> FieldBooleanDetector<T> orCombine(final Collection<? extends FieldEventDetector<T>> detectors) { 201 202 return new FieldBooleanDetector<>(new ArrayList<>(detectors), // copy for immutability 203 Operator.OR, 204 s -> { 205 double minInterval = Double.POSITIVE_INFINITY; 206 for (final FieldEventDetector<T> detector : detectors) { 207 minInterval = FastMath.min(minInterval, detector.getMaxCheckInterval().currentInterval(s)); 208 } 209 return minInterval; 210 }, 211 detectors.stream().map(FieldEventDetector::getThreshold).min(new FieldComparator<>()).get(), 212 detectors.stream().map(FieldEventDetector::getMaxIterationCount).min(Integer::compareTo).get(), 213 new FieldContinueOnEvent<>()); 214 } 215 216 /** 217 * Create a new event detector that negates the g function of another detector. 218 * 219 * <p> This detector will be initialized with the same {@link 220 * FieldEventDetector#getMaxCheckInterval()}, {@link FieldEventDetector#getThreshold()}, and 221 * {@link FieldEventDetector#getMaxIterationCount()} as {@code detector}. The event handler 222 * of the underlying detector is not used, instead the default handler is {@link 223 * FieldContinueOnEvent}. 224 * 225 * @param <T> type of the field elements 226 * @param detector to negate. 227 * @return an new event detector whose g function is the same magnitude but opposite 228 * sign of {@code detector}. 229 * @see #andCombine(Collection) 230 * @see #orCombine(Collection) 231 * @see FieldBooleanDetector 232 */ 233 public static <T extends CalculusFieldElement<T>> FieldNegateDetector<T> notCombine(final FieldEventDetector<T> detector) { 234 return new FieldNegateDetector<>(detector); 235 } 236 237 @Override 238 public T g(final FieldSpacecraftState<T> s) { 239 // can't use stream/lambda here because g(s) throws a checked exception 240 // so write out and combine the map and reduce loops 241 T ret = s.getDate().getField().getZero().newInstance(Double.NaN); // return value 242 boolean first = true; 243 for (final FieldEventDetector<T> detector : detectors) { 244 if (first) { 245 ret = detector.g(s); 246 first = false; 247 } else { 248 ret = operator.combine(ret, detector.g(s)); 249 } 250 } 251 // return the result of applying the operator to all operands 252 return ret; 253 } 254 255 @Override 256 protected FieldBooleanDetector<T> create(final FieldAdaptableInterval<T> newMaxCheck, 257 final T newThreshold, 258 final int newMaxIter, 259 final FieldEventHandler<T> newHandler) { 260 return new FieldBooleanDetector<>(detectors, operator, newMaxCheck, newThreshold, 261 newMaxIter, newHandler); 262 } 263 264 @Override 265 public void init(final FieldSpacecraftState<T> s0, 266 final FieldAbsoluteDate<T> t) { 267 super.init(s0, t); 268 for (final FieldEventDetector<T> detector : detectors) { 269 detector.init(s0, t); 270 } 271 } 272 273 /** 274 * Get the list of original detectors. 275 * @return the list of original detectors 276 */ 277 public List<FieldEventDetector<T>> getDetectors() { 278 return new ArrayList<>(detectors); 279 } 280 281 /** Local class for operator. */ 282 private enum Operator { 283 284 /** And operator. */ 285 AND() { 286 287 @Override 288 /** {@inheritDoc} */ 289 public <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) { 290 return FastMath.min(g1, g2); 291 } 292 293 }, 294 295 /** Or operator. */ 296 OR() { 297 298 @Override 299 /** {@inheritDoc} */ 300 public <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) { 301 return FastMath.max(g1, g2); 302 } 303 304 }; 305 306 /** Combine two g functions evaluations. 307 * @param <T> type of the field elements 308 * @param g1 first evaluation 309 * @param g2 second evaluation 310 * @return combined evaluation 311 */ 312 public abstract <T extends CalculusFieldElement<T>> T combine(T g1, T g2); 313 314 }; 315 316 /** Comparator for field elements. 317 * @param <T> type of the field elements 318 */ 319 private static class FieldComparator<T extends CalculusFieldElement<T>> implements Comparator<T> { 320 public int compare(final T t1, final T t2) { 321 return Double.compare(t1.getReal(), t2.getReal()); 322 } 323 } 324 325 }