1 /* Copyright 2002-2022 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.forces.maneuvers;
18
19 import org.hipparchus.geometry.euclidean.threed.Vector3D;
20 import org.hipparchus.ode.events.Action;
21 import org.hipparchus.util.FastMath;
22 import org.orekit.attitudes.Attitude;
23 import org.orekit.attitudes.AttitudeProvider;
24 import org.orekit.orbits.CartesianOrbit;
25 import org.orekit.propagation.SpacecraftState;
26 import org.orekit.propagation.events.AbstractDetector;
27 import org.orekit.propagation.events.EventDetector;
28 import org.orekit.propagation.events.handlers.EventHandler;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.utils.Constants;
31 import org.orekit.utils.DoubleArrayDictionary;
32 import org.orekit.utils.PVCoordinates;
33
34 /** Impulse maneuver model.
35 * <p>This class implements an impulse maneuver as a discrete event
36 * that can be provided to any {@link org.orekit.propagation.Propagator
37 * Propagator}.</p>
38 * <p>The maneuver is triggered when an underlying event generates a
39 * {@link Action#STOP STOP} event, in which case this class will generate a {@link
40 * Action#RESET_STATE RESET_STATE}
41 * event (the stop event from the underlying object is therefore filtered out).
42 * In the simple cases, the underlying event detector may be a basic
43 * {@link org.orekit.propagation.events.DateDetector date event}, but it
44 * can also be a more elaborate {@link
45 * org.orekit.propagation.events.ApsideDetector apside event} for apogee
46 * maneuvers for example.</p>
47 * <p>The maneuver is defined by a single velocity increment.
48 * If no AttitudeProvider is given, the current attitude of the spacecraft,
49 * defined by the current spacecraft state, will be used as the
50 * {@link AttitudeProvider} so the velocity increment should be given in
51 * the same pseudoinertial frame as the {@link SpacecraftState} used to
52 * construct the propagator that will handle the maneuver.
53 * If an AttitudeProvider is given, the velocity increment given should be
54 * defined appropriately in consideration of that provider. So, a typical
55 * case for tangential maneuvers is to provide a {@link org.orekit.attitudes.LofOffset LOF aligned}
56 * attitude provider along with a velocity increment defined in accordance with
57 * that LOF aligned attitude provider; e.g. if the LOF aligned attitude provider
58 * was constructed using LOFType.VNC the velocity increment should be
59 * provided in VNC coordinates.</p>
60 * <p>Beware that the triggering event detector must behave properly both
61 * before and after maneuver. If for example a node detector is used to trigger
62 * an inclination maneuver and the maneuver change the orbit to an equatorial one,
63 * the node detector will fail just after the maneuver, being unable to find a
64 * node on an equatorial orbit! This is a real case that has been encountered
65 * during validation ...</p>
66 * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
67 * @param <T> class type for the generic version
68 * @author Luc Maisonobe
69 */
70 public class ImpulseManeuver<T extends EventDetector> extends AbstractDetector<ImpulseManeuver<T>> {
71
72 /** The attitude to override during the maneuver, if set. */
73 private final AttitudeProvider attitudeOverride;
74
75 /** Triggering event. */
76 private final T trigger;
77
78 /** Velocity increment in satellite frame. */
79 private final Vector3D deltaVSat;
80
81 /** Specific impulse. */
82 private final double isp;
83
84 /** Engine exhaust velocity. */
85 private final double vExhaust;
86
87 /** Indicator for forward propagation. */
88 private boolean forward;
89
90 /** Build a new instance.
91 * @param trigger triggering event
92 * @param deltaVSat velocity increment in satellite frame
93 * @param isp engine specific impulse (s)
94 */
95 public ImpulseManeuver(final T trigger, final Vector3D deltaVSat, final double isp) {
96 this(trigger.getMaxCheckInterval(), trigger.getThreshold(),
97 trigger.getMaxIterationCount(), new Handler<T>(),
98 trigger, null, deltaVSat, isp);
99 }
100
101
102 /** Build a new instance.
103 * @param trigger triggering event
104 * @param attitudeOverride the attitude provider to use for the maneuver
105 * @param deltaVSat velocity increment in satellite frame
106 * @param isp engine specific impulse (s)
107 */
108 public ImpulseManeuver(final T trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat, final double isp) {
109 this(trigger.getMaxCheckInterval(), trigger.getThreshold(),
110 trigger.getMaxIterationCount(), new Handler<T>(),
111 trigger, attitudeOverride, deltaVSat, isp);
112 }
113
114 /** Private constructor with full parameters.
115 * <p>
116 * This constructor is private as users are expected to use the builder
117 * API with the various {@code withXxx()} methods to set up the instance
118 * in a readable manner without using a huge amount of parameters.
119 * </p>
120 * @param maxCheck maximum checking interval (s)
121 * @param threshold convergence threshold (s)
122 * @param maxIter maximum number of iterations in the event time search
123 * @param handler event handler to call at event occurrences
124 * @param trigger triggering event
125 * @param attitudeOverride the attitude provider to use for the maneuver
126 * @param deltaVSat velocity increment in satellite frame
127 * @param isp engine specific impulse (s)
128 * @since 6.1
129 */
130 private ImpulseManeuver(final double maxCheck, final double threshold,
131 final int maxIter, final EventHandler<? super ImpulseManeuver<T>> handler,
132 final T trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat,
133 final double isp) {
134 super(maxCheck, threshold, maxIter, handler);
135 this.attitudeOverride = attitudeOverride;
136 this.trigger = trigger;
137 this.deltaVSat = deltaVSat;
138 this.isp = isp;
139 this.vExhaust = Constants.G0_STANDARD_GRAVITY * isp;
140 }
141
142 /** {@inheritDoc} */
143 @Override
144 protected ImpulseManeuver<T> create(final double newMaxCheck, final double newThreshold,
145 final int newMaxIter, final EventHandler<? super ImpulseManeuver<T>> newHandler) {
146 return new ImpulseManeuver<T>(newMaxCheck, newThreshold, newMaxIter, newHandler,
147 trigger, attitudeOverride, deltaVSat, isp);
148 }
149
150 /** {@inheritDoc} */
151 public void init(final SpacecraftState s0, final AbsoluteDate t) {
152 forward = t.durationFrom(s0.getDate()) >= 0;
153 // Initialize the triggering event
154 trigger.init(s0, t);
155 }
156
157 /** {@inheritDoc} */
158 public double g(final SpacecraftState s) {
159 return trigger.g(s);
160 }
161
162 /**
163 * Get the Attitude Provider to use during maneuver.
164 * @return the attitude provider
165 */
166 public AttitudeProvider getAttitudeOverride() {
167 return attitudeOverride;
168 }
169
170 /** Get the triggering event.
171 * @return triggering event
172 */
173 public T getTrigger() {
174 return trigger;
175 }
176
177 /** Get the velocity increment in satellite frame.
178 * @return velocity increment in satellite frame
179 */
180 public Vector3D getDeltaVSat() {
181 return deltaVSat;
182 }
183
184 /** Get the specific impulse.
185 * @return specific impulse
186 */
187 public double getIsp() {
188 return isp;
189 }
190
191 /** Local handler.
192 * @param <T> class type for the generic version
193 */
194 private static class Handler<T extends EventDetector> implements EventHandler<ImpulseManeuver<T>> {
195
196 /** {@inheritDoc} */
197 public Action eventOccurred(final SpacecraftState s, final ImpulseManeuver<T> im,
198 final boolean increasing) {
199
200 // filter underlying event
201 final Action underlyingAction = im.trigger.eventOccurred(s, increasing);
202
203 return (underlyingAction == Action.STOP) ? Action.RESET_STATE : Action.CONTINUE;
204
205 }
206
207 /** {@inheritDoc} */
208 @Override
209 public SpacecraftState resetState(final ImpulseManeuver<T> im, final SpacecraftState oldState) {
210
211 final AbsoluteDate date = oldState.getDate();
212 final AttitudeProvider override = im.getAttitudeOverride();
213 final Attitude attitude;
214
215 if (override == null) {
216 attitude = oldState.getAttitude();
217 } else {
218 attitude = override.getAttitude(oldState.getOrbit(), date, oldState.getFrame());
219 }
220
221 // convert velocity increment in inertial frame
222 final Vector3D deltaV = attitude.getRotation().applyInverseTo(im.deltaVSat);
223 final double sign = im.forward ? +1 : -1;
224
225 // apply increment to position/velocity
226 final PVCoordinates oldPV = oldState.getPVCoordinates();
227 final PVCoordinates newPV =
228 new PVCoordinates(oldPV.getPosition(),
229 new Vector3D(1, oldPV.getVelocity(), sign, deltaV));
230 final CartesianOrbit newOrbit =
231 new CartesianOrbit(newPV, oldState.getFrame(), date, oldState.getMu());
232
233 // compute new mass
234 final double newMass = oldState.getMass() * FastMath.exp(-sign * deltaV.getNorm() / im.vExhaust);
235
236 // pack everything in a new state
237 SpacecraftState newState = new SpacecraftState(oldState.getOrbit().getType().normalize(newOrbit, oldState.getOrbit()),
238 attitude, newMass);
239 for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesValues().getData()) {
240 newState = newState.addAdditionalState(entry.getKey(), entry.getValue());
241 }
242 for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesDerivatives().getData()) {
243 newState = newState.addAdditionalStateDerivative(entry.getKey(), entry.getValue());
244 }
245 return newState;
246
247
248 }
249
250 }
251
252 }