1   /* Copyright 2022-2025 Romain Serra
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  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.ode.events.Action;
21  import org.hipparchus.util.FastMath;
22  import org.orekit.propagation.FieldSpacecraftState;
23  import org.orekit.propagation.SpacecraftState;
24  import org.orekit.propagation.events.handlers.EventHandler;
25  import org.orekit.propagation.events.handlers.FieldEventHandler;
26  
27  /** Wrapper shifting events occurrences times.
28   * <p>This class wraps an {@link FieldEventDetector event detector} to slightly
29   * shift the events occurrences times. A typical use case is for handling
30   * operational delays before or after some physical event really occurs.</p>
31   * <p>For example, the satellite attitude mode may be switched from sun pointed
32   * to spin-stabilized a few minutes before eclipse entry, and switched back
33   * to sun pointed a few minutes after eclipse exit. This behavior is handled
34   * by wrapping an {@link FieldEclipseDetector eclipse detector} into an instance
35   * of this class with a positive times shift for increasing events (eclipse exit)
36   * and a negative times shift for decreasing events (eclipse entry).</p>
37   * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector)
38   * @see FieldEventDetector
39   * @see EventShifter
40   * @author Luc Maisonobe
41   * @author Romain Serra
42   * @since 13.0
43   */
44  public class FieldEventShifter<T extends CalculusFieldElement<T>> implements FieldDetectorModifier<T> {
45  
46      /** Event detector for the raw unshifted event. */
47      private final FieldEventDetector<T> detector;
48  
49      /** Indicator for using shifted or unshifted states at event occurrence. */
50      private final boolean useShiftedStates;
51  
52      /** Offset to apply to find increasing events. */
53      private final T increasingOffset;
54  
55      /** Offset to apply to find decreasing events. */
56      private final T decreasingOffset;
57  
58      /** Specialized event handler. */
59      private final LocalHandler<T> handler;
60  
61      /** Event detection settings. */
62      private final FieldEventDetectionSettings<T> detectionSettings;
63  
64      /** Build a new instance.
65       * <p>The {@link #getMaxCheckInterval() max check interval}, the
66       * {@link #getThreshold() convergence threshold} of the raw unshifted
67       * events will be used for the shifted event. When an event occurs,
68       * the {@link EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred}
69       * method of the raw unshifted events will be called (with spacecraft
70       * state at either the shifted or the unshifted event date depending
71       * on the <code>useShiftedStates</code> parameter).</p>
72       * @param detector event detector for the raw unshifted event
73       * @param useShiftedStates if true, the state provided to {@link
74       * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of
75       * the associated {@code handler} will remain shifted, otherwise it will
76       * be <i>unshifted</i> to correspond to the underlying raw event.
77       * @param increasingTimeShift increasing events time shift.
78       * @param decreasingTimeShift decreasing events time shift.
79       */
80      public FieldEventShifter(final FieldEventDetector<T> detector, final boolean useShiftedStates,
81                               final T increasingTimeShift, final T decreasingTimeShift) {
82          this(detector.getDetectionSettings(), detector, useShiftedStates, increasingTimeShift, decreasingTimeShift);
83      }
84  
85      /** Constructor with full parameters.
86       * @param detectionSettings event detection settings
87       * @param detector event detector for the raw unshifted event
88       * @param useShiftedStates if true, the state provided to {@link
89       * EventHandler#eventOccurred(SpacecraftState, EventDetector, boolean) eventOccurred} method of
90       * the <code>detector</code> will remain shifted, otherwise it will
91       * be <i>unshifted</i> to correspond to the underlying raw event.
92       * @param increasingTimeShift increasing events time shift.
93       * @param decreasingTimeShift decreasing events time shift.
94       * @since 13.0
95       */
96      public FieldEventShifter(final FieldEventDetectionSettings<T> detectionSettings,
97                               final FieldEventDetector<T> detector, final boolean useShiftedStates,
98                               final T increasingTimeShift, final T decreasingTimeShift) {
99          this.detectionSettings = detectionSettings;
100         this.handler          = new LocalHandler<>();
101         this.detector         = detector;
102         this.useShiftedStates = useShiftedStates;
103         this.increasingOffset = increasingTimeShift.negate();
104         this.decreasingOffset = decreasingTimeShift.negate();
105     }
106 
107     @Override
108     public FieldEventHandler<T> getHandler() {
109         return handler;
110     }
111 
112     @Override
113     public FieldEventDetectionSettings<T> getDetectionSettings() {
114         return detectionSettings;
115     }
116 
117     /**
118      * Get the detector for the raw unshifted event.
119      * @return the detector for the raw unshifted event
120      */
121     public FieldEventDetector<T> getDetector() {
122         return detector;
123     }
124 
125     /** Get the increasing events time shift.
126      * @return increasing events time shift
127      */
128     public T getIncreasingTimeShift() {
129         return increasingOffset.negate();
130     }
131 
132     /** Get the decreasing events time shift.
133      * @return decreasing events time shift
134      */
135     public T getDecreasingTimeShift() {
136         return decreasingOffset.negate();
137     }
138 
139     /**
140      * Builds a new instance from the input detection settings.
141      * @param settings event detection settings to be used
142      * @return a new detector
143      */
144     public FieldEventShifter<T> withDetectionSettings(final FieldEventDetectionSettings<T> settings) {
145         return new FieldEventShifter<>(settings, detector, useShiftedStates, getIncreasingTimeShift(), getDecreasingTimeShift());
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public T g(final FieldSpacecraftState<T> s) {
151         final T incShiftedG = detector.g(s.shiftedBy(increasingOffset));
152         final T decShiftedG = detector.g(s.shiftedBy(decreasingOffset));
153         return (increasingOffset.getReal() >= decreasingOffset.getReal()) ?
154                FastMath.max(incShiftedG, decShiftedG) : FastMath.min(incShiftedG, decShiftedG);
155     }
156 
157     /** Local class for handling events. */
158     private static class LocalHandler<W extends CalculusFieldElement<W>> implements FieldEventHandler<W> {
159 
160         /** Shifted state at even occurrence. */
161         private FieldSpacecraftState<W> shiftedState;
162 
163         /** {@inheritDoc} */
164         public Action eventOccurred(final FieldSpacecraftState<W> s, final FieldEventDetector<W> detector,
165                                     final boolean increasing) {
166 
167             final FieldEventShifter<W> shifter = (FieldEventShifter<W>) detector;
168             if (shifter.useShiftedStates) {
169                 // the state provided by the caller already includes the time shift
170                 shiftedState = s;
171             } else {
172                 // we need to "unshift" the state
173                 final W offset = increasing ? shifter.increasingOffset : shifter.decreasingOffset;
174                 shiftedState = s.shiftedBy(offset);
175             }
176 
177             return shifter.detector.getHandler().eventOccurred(shiftedState, shifter.detector, increasing);
178 
179         }
180 
181         /** {@inheritDoc} */
182         @Override
183         public FieldSpacecraftState<W> resetState(final FieldEventDetector<W> detector,
184                                                   final FieldSpacecraftState<W> oldState) {
185             final FieldEventShifter<W> shifter = (FieldEventShifter<W>) detector;
186             return shifter.detector.getHandler().resetState(shifter.detector, shiftedState);
187         }
188 
189     }
190 
191 }