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