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