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.ode.events.Action;
22 import org.orekit.propagation.SpacecraftState;
23 import org.orekit.propagation.events.EventDetector;
24 import org.orekit.propagation.events.FieldEventDetector;
25 import org.orekit.time.AbsoluteDate;
26
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.stream.Stream;
30
31 /** This classes manages a sequence of different attitude providers that are activated
32 * in turn according to switching events. Changes in attitude mode are instantaneous, so state derivatives need to be
33 * reset and the {@link Action} returned by the event handler is ignored.
34 * @author Luc Maisonobe
35 * @author Romain Serra
36 * @since 13.0
37 * @see AttitudesSequence
38 */
39 public class AttitudesSwitcher extends AbstractSwitchingAttitudeProvider {
40
41 /** Switching events list. */
42 private final List<InstantaneousSwitch> instantaneousSwitches;
43
44 /** Constructor for an initially empty sequence.
45 */
46 public AttitudesSwitcher() {
47 super();
48 instantaneousSwitches = new ArrayList<>();
49 }
50
51 /** Add a switching condition between two attitude providers.
52 * <p>
53 * The {@code past} and {@code future} attitude providers are defined with regard
54 * to the natural flow of time. This means that if the propagation is forward, the
55 * propagator will switch from {@code past} provider to {@code future} provider at
56 * event occurrence, but if the propagation is backward, the propagator will switch
57 * from {@code future} provider to {@code past} provider at event occurrence.
58 * </p>
59 * <p>
60 * An attitude provider may have several different switch events associated to
61 * it. Depending on which event is triggered, the appropriate provider is
62 * switched to.
63 * </p>
64 * <p>
65 * If the underlying detector has an event handler associated to it, this handler
66 * will be triggered (i.e. its {@link org.orekit.propagation.events.handlers.EventHandler#eventOccurred(SpacecraftState,
67 * EventDetector, boolean) eventOccurred} method will be called), <em>regardless</em>
68 * of the event really triggering an attitude switch or not. As an example, if an
69 * eclipse detector is used to switch from day to night attitude mode when entering
70 * eclipse, with {@code switchOnIncrease} set to {@code false} and {@code switchOnDecrease}
71 * set to {@code true}. Then a handler set directly at eclipse detector level would
72 * be triggered at both eclipse entry and eclipse exit, but attitude switch would
73 * occur <em>only</em> at eclipse entry.
74 * </p>
75 * @param past attitude provider applicable for times in the switch event occurrence past
76 * @param future attitude provider applicable for times in the switch event occurrence future
77 * @param switchEvent event triggering the attitude providers switch
78 * @param switchOnIncrease if true, switch is triggered on increasing event
79 * @param switchOnDecrease if true, switch is triggered on decreasing event
80 * @param switchHandler handler to call for notifying when switch occurs (may be null)
81 * @param <T> class type for the switch event
82 * @since 13.0
83 */
84 public <T extends EventDetector> void addSwitchingCondition(final AttitudeProvider past,
85 final AttitudeProvider future,
86 final T switchEvent,
87 final boolean switchOnIncrease,
88 final boolean switchOnDecrease,
89 final AttitudeSwitchHandler switchHandler) {
90
91 // if it is the first switching condition, assume first active law is the past one
92 if (getActivated() == null) {
93 resetActiveProvider(past);
94 }
95
96 // add the switching condition
97 instantaneousSwitches.add(new InstantaneousSwitch(switchEvent, switchOnIncrease, switchOnDecrease,
98 past, future, switchHandler));
99
100 }
101
102 @Override
103 public Stream<EventDetector> getEventDetectors() {
104 return Stream.concat(instantaneousSwitches.stream().map(InstantaneousSwitch.class::cast), getEventDetectors(getParametersDrivers()));
105 }
106
107 @Override
108 public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
109 final Stream<FieldEventDetector<T>> switchesStream = instantaneousSwitches.stream().map(sw -> getFieldEventDetector(field, sw));
110 return Stream.concat(switchesStream, getFieldEventDetectors(field, getParametersDrivers()));
111 }
112
113 /** Switch specification. Reset derivatives due to instantaneous change of attitude. */
114 public class InstantaneousSwitch extends AbstractAttitudeSwitch {
115
116 /** Propagation direction. */
117 private boolean forward;
118
119 /**
120 * Simple constructor.
121 *
122 * @param event event
123 * @param switchOnIncrease if true, switch is triggered on increasing event
124 * @param switchOnDecrease if true, switch is triggered on decreasing event otherwise switch is triggered on
125 * decreasing event
126 * @param past attitude provider applicable for times in the switch event occurrence past
127 * @param future attitude provider applicable for times in the switch event occurrence future
128 * @param switchHandler handler to call for notifying when switch occurs (may be null)
129 */
130 private InstantaneousSwitch(final EventDetector event, final boolean switchOnIncrease, final boolean switchOnDecrease,
131 final AttitudeProvider past, final AttitudeProvider future,
132 final AttitudeSwitchHandler switchHandler) {
133 super(event, switchOnIncrease, switchOnDecrease, past, future, switchHandler);
134 }
135
136 /** {@inheritDoc} */
137 @Override
138 public void init(final SpacecraftState s0, final AbsoluteDate t) {
139 super.init(s0, t);
140
141 // reset the transition parameters (this will be done once for each switch,
142 // despite doing it only once would have sufficient; it's not really a problem)
143 forward = t.durationFrom(s0.getDate()) >= 0.0;
144 if (getActivated().getSpansNumber() > 1) {
145 // remove switches that will be overridden during upcoming propagation (use margin to avoid erasing after creation)
146 if (forward) {
147 setActivated(getActivated().extractRange(AbsoluteDate.PAST_INFINITY, s0.getDate().shiftedBy(getDetectionSettings().getThreshold())));
148 } else {
149 setActivated(getActivated().extractRange(s0.getDate().shiftedBy(-getDetectionSettings().getThreshold()), AbsoluteDate.FUTURE_INFINITY));
150 }
151 }
152
153 }
154
155 /** {@inheritDoc} */
156 public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
157
158 final AbsoluteDate date = s.getDate();
159 if (getActivated().get(date) == (forward ? getPast() : getFuture()) &&
160 (increasing && isSwitchOnIncrease() || !increasing && isSwitchOnDecrease())) {
161
162 if (forward) {
163 // prepare future law
164 getActivated().addValidAfter(getFuture(), date, false);
165
166 // notify about the switch
167 if (getSwitchHandler() != null) {
168 getSwitchHandler().switchOccurred(getPast(), getFuture(), s);
169 }
170
171 } else {
172 // prepare past law
173 getActivated().addValidBefore(getPast(), date, false);
174
175 // notify about the switch
176 if (getSwitchHandler() != null) {
177 getSwitchHandler().switchOccurred(getFuture(), getPast(), s);
178 }
179
180 }
181
182 }
183 getDetector().getHandler().eventOccurred(s, getDetector(), increasing); // call but ignore output
184 return Action.RESET_DERIVATIVES;
185 }
186
187 }
188
189 }