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 }