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 }