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