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 }