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.attitudes;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
22  import org.hipparchus.geometry.euclidean.threed.Rotation;
23  import org.hipparchus.ode.events.Action;
24  import org.orekit.frames.Frame;
25  import org.orekit.propagation.FieldSpacecraftState;
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.propagation.events.DetectorModifier;
28  import org.orekit.propagation.events.EventDetector;
29  import org.orekit.propagation.events.FieldEventDetector;
30  import org.orekit.propagation.events.FieldEventDetectionSettings;
31  import org.orekit.propagation.events.handlers.EventHandler;
32  import org.orekit.propagation.events.handlers.FieldEventHandler;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.time.FieldAbsoluteDate;
35  import org.orekit.utils.FieldPVCoordinatesProvider;
36  import org.orekit.utils.PVCoordinatesProvider;
37  import org.orekit.utils.TimeSpanMap;
38  
39  /** This classes manages a sequence of different attitude providers that are activated
40   * in turn according to switching events.
41   * <p>Only one attitude provider in the sequence is in an active state. When one of
42   * the switch event associated with the active provider occurs, the active provider becomes
43   * the one specified with the event. A simple example is a provider for the sun lighted part
44   * of the orbit and another provider for the eclipse time. When the sun lighted provider is active,
45   * the eclipse entry event is checked and when it occurs the eclipse provider is activated.
46   * When the eclipse provider is active, the eclipse exit event is checked and when it occurs
47   * the sun lighted provider is activated again. This sequence is a simple loop.</p>
48   * <p>An active attitude provider may have several switch events and next provider settings, leading
49   * to different activation patterns depending on which events are triggered first. An example
50   * of this feature is handling switches to safe mode if some contingency condition is met, in
51   * addition to the nominal switches that correspond to proper operations. Another example
52   * is handling of maneuver mode.
53   * <p>
54   * Note that this attitude provider is stateful, it keeps in memory the sequence of active
55   * underlying providers with their switch dates and the transitions from one provider to
56   * the other. This implies that this provider should <em>not</em> be shared among different
57   * propagators at the same time, each propagator should use its own instance of this provider.
58   * <p>
59   * The sequence kept in memory is reset when {@link #resetActiveProvider(AttitudeProvider)}
60   * is called, and only the specify provider is kept. The sequence is also partially
61   * reset each time a propagation starts. If a new propagation is started after a first
62   * propagation has been run, all the already computed switches that occur after propagation
63   * start for forward propagation or before propagation start for backward propagation will
64   * be erased. New switches will be computed and applied properly according to the new
65   * propagation settings. The already computed switches that are not in covered are kept
66   * in memory. This implies that if a propagation is interrupted and restarted in the
67   * same direction, then attitude switches will remain in place, ensuring that even if the
68   * interruption occurred in the middle of an attitude transition the second propagation will
69   * properly complete the transition that was started by the first propagator.
70   * </p>
71   * @author Luc Maisonobe
72   * @author Romain Serra
73   * @since 13.0
74   */
75  abstract class AbstractSwitchingAttitudeProvider implements AttitudeProvider {
76  
77      /** Providers that have been activated. */
78      private TimeSpanMap<AttitudeProvider> activated;
79  
80      /** Constructor for an initially empty sequence.
81       */
82      protected AbstractSwitchingAttitudeProvider() {
83          activated = null;
84      }
85  
86      /** Reset the active provider.
87       * <p>
88       * Calling this method clears all already seen switch history,
89       * so it should <em>not</em> be used during the propagation itself,
90       * it is intended to be used only at start
91       * </p>
92       * @param provider provider to activate
93       */
94      public void resetActiveProvider(final AttitudeProvider provider) {
95          activated = new TimeSpanMap<>(provider);
96      }
97  
98      /**
99       * Setter for map of activate attitude providers.
100      * @param activated new map
101      */
102     protected void setActivated(final TimeSpanMap<AttitudeProvider> activated) {
103         this.activated = activated;
104     }
105 
106     /**
107      * Getter for map of activated attitude providers.
108      * @return map of providers
109      */
110     protected TimeSpanMap<AttitudeProvider> getActivated() {
111         return activated;
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     public Attitude getAttitude(final PVCoordinatesProvider pvProv,
117                                 final AbsoluteDate date, final Frame frame) {
118         return activated.get(date).getAttitude(pvProv, date, frame);
119     }
120 
121     /** {@inheritDoc} */
122     @Override
123     public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
124                                                                             final FieldAbsoluteDate<T> date,
125                                                                             final Frame frame) {
126         return activated.get(date.toAbsoluteDate()).getAttitude(pvProv, date, frame);
127     }
128 
129     /** {@inheritDoc} */
130     @Override
131     public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
132         return activated.get(date).getAttitudeRotation(pvProv, date, frame);
133     }
134 
135     @Override
136     public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
137                                                                                     final FieldAbsoluteDate<T> date,
138                                                                                     final Frame frame) {
139         return activated.get(date.toAbsoluteDate()).getAttitudeRotation(pvProv, date, frame);
140     }
141 
142     /**
143      * Method creating a Field attitude switch from a non-Field one.
144      * @param field field
145      * @param attitudeSwitch attitude switch
146      * @return Field detector
147      * @param <T> field type
148      */
149     protected <T extends CalculusFieldElement<T>> FieldEventDetector<T> getFieldEventDetector(final Field<T> field,
150                                                                                               final AbstractAttitudeSwitch attitudeSwitch) {
151         return new FieldEventDetector<T>() {
152 
153             /** {@inheritDoc} */
154             @Override
155             public void init(final FieldSpacecraftState<T> s0, final FieldAbsoluteDate<T> t) {
156                 attitudeSwitch.init(s0.toSpacecraftState(), t.toAbsoluteDate());
157             }
158 
159             /** {@inheritDoc} */
160             @Override
161             public T g(final FieldSpacecraftState<T> s) {
162                 return field.getZero().newInstance(attitudeSwitch.g(s.toSpacecraftState()));
163             }
164 
165             @Override
166             public FieldEventDetectionSettings<T> getDetectionSettings() {
167                 return new FieldEventDetectionSettings<>(field, attitudeSwitch.getDetectionSettings());
168             }
169 
170             /** {@inheritDoc} */
171             @Override
172             public FieldEventHandler<T> getHandler() {
173                 return new FieldEventHandler<T>() {
174                     /** {@inheritDoc} */
175                     @Override
176                     public Action eventOccurred(final FieldSpacecraftState<T> s,
177                                                 final FieldEventDetector<T> detector,
178                                                 final boolean increasing) {
179                         return attitudeSwitch.eventOccurred(s.toSpacecraftState(), attitudeSwitch, increasing);
180                     }
181 
182                     /** {@inheritDoc} */
183                     @Override
184                     public FieldSpacecraftState<T> resetState(final FieldEventDetector<T> detector,
185                                                               final FieldSpacecraftState<T> oldState) {
186                         return new FieldSpacecraftState<>(field, attitudeSwitch.resetState(attitudeSwitch, oldState.toSpacecraftState()));
187                     }
188                 };
189             }
190 
191         };
192     }
193 
194     /** Abstract class to manage attitude switches.
195      * @since 13.0
196      */
197     abstract static class AbstractAttitudeSwitch implements DetectorModifier, EventHandler {
198 
199         /**
200          * Event direction triggering the switch.
201          */
202         private final boolean switchOnIncrease;
203 
204         /**
205          * Event direction triggering the switch.
206          */
207         private final boolean switchOnDecrease;
208 
209         /**
210          * Attitude provider applicable for times in the switch event occurrence past.
211          */
212         private final AttitudeProvider past;
213 
214         /**
215          * Attitude provider applicable for times in the switch event occurrence future.
216          */
217         private final AttitudeProvider future;
218 
219         /**
220          * Handler to call for notifying when switch occurs (may be null).
221          */
222         private final AttitudeSwitchHandler switchHandler;
223 
224         /** Wrapped event detector. */
225         private final EventDetector event;
226 
227         /**
228          * Simple constructor.
229          *
230          * @param event            event
231          * @param switchOnIncrease if true, switch is triggered on increasing event
232          * @param switchOnDecrease if true, switch is triggered on decreasing event otherwise switch is triggered on
233          *                         decreasing event
234          * @param past             attitude provider applicable for times in the switch event occurrence past
235          * @param future           attitude provider applicable for times in the switch event occurrence future
236          * @param switchHandler    handler to call for notifying when switch occurs (may be null)
237          */
238         protected AbstractAttitudeSwitch(final EventDetector event, final boolean switchOnIncrease,
239                                          final boolean switchOnDecrease, final AttitudeProvider past,
240                                          final AttitudeProvider future, final AttitudeSwitchHandler switchHandler) {
241             this.event = event;
242             this.switchOnIncrease = switchOnIncrease;
243             this.switchOnDecrease = switchOnDecrease;
244             this.past = past;
245             this.future = future;
246             this.switchHandler = switchHandler;
247         }
248 
249         /** {@inheritDoc} */
250         @Override
251         public EventDetector getDetector() {
252             return event;
253         }
254 
255         /**
256          * Protected getter for switch handle.
257          * @return switch handler
258          */
259         protected AttitudeSwitchHandler getSwitchHandler() {
260             return switchHandler;
261         }
262 
263         /**
264          * Protected getter for future attitude provider.
265          * @return future provider
266          */
267         protected AttitudeProvider getFuture() {
268             return future;
269         }
270 
271         /**
272          * Protected getter for past attitude provider.
273          * @return pas provider
274          */
275         protected AttitudeProvider getPast() {
276             return past;
277         }
278 
279         /**
280          * Protected getter for switch-on-decrease flag.
281          * @return flag
282          */
283         protected boolean isSwitchOnDecrease() {
284             return switchOnDecrease;
285         }
286 
287         /**
288          * Protected getter for switch-on-increase flag.
289          * @return flag
290          */
291         protected boolean isSwitchOnIncrease() {
292             return switchOnIncrease;
293         }
294 
295         /**
296          * {@inheritDoc}
297          */
298         @Override
299         public EventHandler getHandler() {
300             return this;
301         }
302 
303         /**
304          * {@inheritDoc}
305          */
306         @Override
307         public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
308             // delegate to underlying event
309             return getDetector().getHandler().resetState(getDetector(), oldState);
310         }
311 
312     }
313 
314 }