FieldEventShifter.java
/* Copyright 2022-2025 Romain Serra
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.propagation.events;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.ode.events.Action;
import org.hipparchus.util.FastMath;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.propagation.events.handlers.FieldEventHandler;
/** Wrapper shifting events occurrences times.
* <p>This class wraps an {@link FieldEventDetector event detector} to slightly
* shift the events occurrences times. A typical use case is for handling
* operational delays before or after some physical event really occurs.</p>
* <p>For example, the satellite attitude mode may be switched from sun pointed
* to spin-stabilized a few minutes before eclipse entry, and switched back
* to sun pointed a few minutes after eclipse exit. This behavior is handled
* by wrapping an {@link FieldEclipseDetector eclipse detector} into an instance
* of this class with a positive times shift for increasing events (eclipse exit)
* and a negative times shift for decreasing events (eclipse entry).</p>
* @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
* @see FieldEventDetector
* @see EventShifter
* @author Luc Maisonobe
* @author Romain Serra
* @since 13.0
*/
public class FieldEventShifter<T extends CalculusFieldElement<T>> implements FieldDetectorModifier<T> {
/** Event detector for the raw unshifted event. */
private final FieldEventDetector<T> detector;
/** Indicator for using shifted or unshifted states at event occurrence. */
private final boolean useShiftedStates;
/** Offset to apply to find increasing events. */
private final T increasingOffset;
/** Offset to apply to find decreasing events. */
private final T decreasingOffset;
/** Specialized event handler. */
private final LocalHandler<T> handler;
/** Event detection settings. */
private final FieldEventDetectionSettings<T> detectionSettings;
/** Build a new instance.
* <p>The {@link #getMaxCheckInterval() max check interval}, the
* {@link #getThreshold() convergence threshold} of the raw unshifted
* events will be used for the shifted event. When an event occurs,
* the {@link EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred}
* method of the raw unshifted events will be called (with spacecraft
* state at either the shifted or the unshifted event date depending
* on the <code>useShiftedStates</code> parameter).</p>
* @param detector event detector for the raw unshifted event
* @param useShiftedStates if true, the state provided to {@link
* EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of
* the associated {@code handler} will remain shifted, otherwise it will
* be <i>unshifted</i> to correspond to the underlying raw event.
* @param increasingTimeShift increasing events time shift.
* @param decreasingTimeShift decreasing events time shift.
*/
public FieldEventShifter(final FieldEventDetector<T> detector, final boolean useShiftedStates,
final T increasingTimeShift, final T decreasingTimeShift) {
this(detector.getDetectionSettings(), detector, useShiftedStates, increasingTimeShift, decreasingTimeShift);
}
/** Constructor with full parameters.
* @param detectionSettings event detection settings
* @param detector event detector for the raw unshifted event
* @param useShiftedStates if true, the state provided to {@link
* EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of
* the <code>detector</code> will remain shifted, otherwise it will
* be <i>unshifted</i> to correspond to the underlying raw event.
* @param increasingTimeShift increasing events time shift.
* @param decreasingTimeShift decreasing events time shift.
* @since 13.0
*/
public FieldEventShifter(final FieldEventDetectionSettings<T> detectionSettings,
final FieldEventDetector<T> detector, final boolean useShiftedStates,
final T increasingTimeShift, final T decreasingTimeShift) {
this.detectionSettings = detectionSettings;
this.handler = new LocalHandler<>();
this.detector = detector;
this.useShiftedStates = useShiftedStates;
this.increasingOffset = increasingTimeShift.negate();
this.decreasingOffset = decreasingTimeShift.negate();
}
@Override
public FieldEventHandler<T> getHandler() {
return handler;
}
@Override
public FieldEventDetectionSettings<T> getDetectionSettings() {
return detectionSettings;
}
/**
* Get the detector for the raw unshifted event.
* @return the detector for the raw unshifted event
*/
public FieldEventDetector<T> getDetector() {
return detector;
}
/** Get the increasing events time shift.
* @return increasing events time shift
*/
public T getIncreasingTimeShift() {
return increasingOffset.negate();
}
/** Get the decreasing events time shift.
* @return decreasing events time shift
*/
public T getDecreasingTimeShift() {
return decreasingOffset.negate();
}
/**
* Builds a new instance from the input detection settings.
* @param settings event detection settings to be used
* @return a new detector
*/
public FieldEventShifter<T> withDetectionSettings(final FieldEventDetectionSettings<T> settings) {
return new FieldEventShifter<>(settings, detector, useShiftedStates, getIncreasingTimeShift(), getDecreasingTimeShift());
}
/** {@inheritDoc} */
@Override
public T g(final FieldSpacecraftState<T> s) {
final T incShiftedG = detector.g(s.shiftedBy(increasingOffset));
final T decShiftedG = detector.g(s.shiftedBy(decreasingOffset));
return (increasingOffset.getReal() >= decreasingOffset.getReal()) ?
FastMath.max(incShiftedG, decShiftedG) : FastMath.min(incShiftedG, decShiftedG);
}
/** Local class for handling events. */
private static class LocalHandler<W extends CalculusFieldElement<W>> implements FieldEventHandler<W> {
/** Shifted state at even occurrence. */
private FieldSpacecraftState<W> shiftedState;
/** {@inheritDoc} */
public Action eventOccurred(final FieldSpacecraftState<W> s, final FieldEventDetector<W> detector,
final boolean increasing) {
final FieldEventShifter<W> shifter = (FieldEventShifter<W>) detector;
if (shifter.useShiftedStates) {
// the state provided by the caller already includes the time shift
shiftedState = s;
} else {
// we need to "unshift" the state
final W offset = increasing ? shifter.increasingOffset : shifter.decreasingOffset;
shiftedState = s.shiftedBy(offset);
}
return shifter.detector.getHandler().eventOccurred(shiftedState, shifter.detector, increasing);
}
/** {@inheritDoc} */
@Override
public FieldSpacecraftState<W> resetState(final FieldEventDetector<W> detector,
final FieldSpacecraftState<W> oldState) {
final FieldEventShifter<W> shifter = (FieldEventShifter<W>) detector;
return shifter.detector.getHandler().resetState(shifter.detector, shiftedState);
}
}
}