ImpulseManeuver.java

  1. /* Copyright 2002-2025 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. import org.hipparchus.geometry.euclidean.threed.Rotation;
  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.AttitudeProvider;
  23. import org.orekit.forces.maneuvers.propulsion.ThrustPropulsionModel;
  24. import org.orekit.orbits.CartesianOrbit;
  25. import org.orekit.propagation.SpacecraftState;
  26. import org.orekit.propagation.events.DetectorModifier;
  27. import org.orekit.propagation.events.EventDetectionSettings;
  28. import org.orekit.propagation.events.EventDetector;
  29. import org.orekit.propagation.events.handlers.EventHandler;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.utils.AbsolutePVCoordinates;
  32. import org.orekit.utils.PVCoordinates;

  33. /** Impulse maneuver model.
  34.  * <p>This class implements an impulse maneuver as a discrete event
  35.  * that can be provided to any {@link org.orekit.propagation.Propagator
  36.  * Propagator}.</p>
  37.  * <p>The maneuver is executed when an underlying is triggered, in which case this class will generate a {@link
  38.  * Action#RESET_STATE RESET_STATE} event. By default, the detection settings are those of the trigger.
  39.  * In the simple cases, the underlying event detector may be a basic
  40.  * {@link org.orekit.propagation.events.DateDetector date event}, but it
  41.  * can also be a more elaborate {@link
  42.  * org.orekit.propagation.events.ApsideDetector apside event} for apogee
  43.  * maneuvers for example.</p>
  44.  * <p>The maneuver velocity increment is defined via {@link ImpulseProvider}.
  45.  * If no AttitudeProvider is given, the current attitude of the spacecraft,
  46.  * defined by the current spacecraft state, will be used as the
  47.  * {@link AttitudeProvider} so the velocity increment should be given in
  48.  * the same pseudoinertial frame as the {@link SpacecraftState} used to
  49.  * construct the propagator that will handle the maneuver.
  50.  * If an AttitudeProvider is given, the velocity increment given should be
  51.  * defined appropriately in consideration of that provider. So, a typical
  52.  * case for tangential maneuvers is to provide a {@link org.orekit.attitudes.LofOffset LOF aligned}
  53.  * attitude provider along with a velocity increment defined in accordance with
  54.  * that LOF aligned attitude provider; e.g. if the LOF aligned attitude provider
  55.  * was constructed using LOFType.VNC the velocity increment should be
  56.  * provided in VNC coordinates.</p>
  57.  * <p>The norm through which the delta-V maps to the mass consumption is chosen via the
  58.  * enum {@link Control3DVectorCostType}. Default is Euclidean. </p>
  59.  * <p>Beware that the triggering event detector must behave properly both
  60.  * before and after maneuver. If for example a node detector is used to trigger
  61.  * an inclination maneuver and the maneuver change the orbit to an equatorial one,
  62.  * the node detector will fail just after the maneuver, being unable to find a
  63.  * node on an equatorial orbit! This is a real case that has been encountered
  64.  * during validation ...</p>
  65.  * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector)
  66.  * @author Luc Maisonobe
  67.  */
  68. public class ImpulseManeuver extends AbstractImpulseManeuver implements DetectorModifier {

  69.     /** Triggering event. */
  70.     private final EventDetector trigger;

  71.     /** Specific impulse. */
  72.     private final double isp;

  73.     /** Engine exhaust velocity. */
  74.     private final double vExhaust;

  75.     /** Trigger's detection settings. */
  76.     private final EventDetectionSettings detectionSettings;

  77.     /** Specific event handler. */
  78.     private final Handler handler;

  79.     /** Impulse provider. */
  80.     private final ImpulseProvider impulseProvider;

  81.     /** Indicator for forward propagation. */
  82.     private boolean forward;

  83.     /** Build a new instance.
  84.      * @param trigger triggering event
  85.      * @param deltaVSat velocity increment in satellite frame
  86.      * @param isp engine specific impulse (s)
  87.      */
  88.     public ImpulseManeuver(final EventDetector trigger, final Vector3D deltaVSat, final double isp) {
  89.         this(trigger, null, deltaVSat, isp);
  90.     }

  91.     /** Build a new instance.
  92.      * @param trigger triggering event
  93.      * @param attitudeOverride the attitude provider to use for the maneuver
  94.      * @param deltaVSat velocity increment in satellite frame
  95.      * @param isp engine specific impulse (s)
  96.      */
  97.     public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride,
  98.                            final Vector3D deltaVSat, final double isp) {
  99.         this(trigger, attitudeOverride, ImpulseProvider.of(deltaVSat), isp, Control3DVectorCostType.TWO_NORM);
  100.     }

  101.     /** Build a new instance.
  102.      * @param trigger triggering event
  103.      * @param attitudeOverride the attitude provider to use for the maneuver
  104.      * @param deltaVSat velocity increment in satellite frame
  105.      * @param isp engine specific impulse (s)
  106.      * @param control3DVectorCostType increment's norm for mass consumption
  107.      * @deprecated since 13.0
  108.      */
  109.     @Deprecated
  110.     public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride,
  111.                            final Vector3D deltaVSat, final double isp, final Control3DVectorCostType control3DVectorCostType) {
  112.         this(trigger, trigger.getDetectionSettings(), attitudeOverride, ImpulseProvider.of(deltaVSat), isp, control3DVectorCostType);
  113.     }

  114.     /** Build a new instance.
  115.      * @param trigger triggering event
  116.      * @param attitudeOverride the attitude provider to use for the maneuver
  117.      * @param impulseProvider impulse provider
  118.      * @param isp engine specific impulse (s)
  119.      * @param control3DVectorCostType increment's norm for mass consumption
  120.      * @since 13.0
  121.      */
  122.     public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride,
  123.                            final ImpulseProvider impulseProvider, final double isp, final Control3DVectorCostType control3DVectorCostType) {
  124.         this(trigger, trigger.getDetectionSettings(), attitudeOverride, impulseProvider, isp, control3DVectorCostType);
  125.     }

  126.     /** Private constructor.
  127.      * @param trigger triggering event
  128.      * @param detectionSettings event detection settings
  129.      * @param attitudeOverride the attitude provider to use for the maneuver
  130.      * @param impulseProvider impulse provider
  131.      * @param isp engine specific impulse (s)
  132.      * @param control3DVectorCostType increment's norm for mass consumption
  133.      * @since 13.0
  134.      */
  135.     private ImpulseManeuver(final EventDetector trigger, final EventDetectionSettings detectionSettings,
  136.                             final AttitudeProvider attitudeOverride, final ImpulseProvider impulseProvider,
  137.                             final double isp, final Control3DVectorCostType control3DVectorCostType) {
  138.         super(attitudeOverride, control3DVectorCostType);
  139.         this.trigger   = trigger;
  140.         this.detectionSettings = detectionSettings;
  141.         this.impulseProvider = impulseProvider;
  142.         this.isp       = isp;
  143.         this.vExhaust  = ThrustPropulsionModel.getExhaustVelocity(isp);
  144.         this.handler = new Handler();
  145.     }

  146.     /**
  147.      * Creates a copy with different event detection settings.
  148.      * @param eventDetectionSettings new detection settings
  149.      * @return a new detector with same properties except for the detection settings
  150.      */
  151.     public ImpulseManeuver withDetectionSettings(final EventDetectionSettings eventDetectionSettings) {
  152.         return new ImpulseManeuver(trigger, eventDetectionSettings, getAttitudeOverride(), impulseProvider, isp,
  153.                 getControl3DVectorCostType());
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  158.         DetectorModifier.super.init(s0, t);
  159.         forward = t.durationFrom(s0.getDate()) >= 0;
  160.         impulseProvider.init(s0, t);
  161.     }

  162.     /** {@inheritDoc} */
  163.     @Override
  164.     public void finish(final SpacecraftState state) {
  165.         DetectorModifier.super.finish(state);
  166.         impulseProvider.finish(state);
  167.     }

  168.     /** {@inheritDoc} */
  169.     @Override
  170.     public EventDetector getDetector() {
  171.         return getTrigger();
  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public EventHandler getHandler() {
  176.         return handler;
  177.     }

  178.     /** {@inheritDoc} */
  179.     @Override
  180.     public EventDetectionSettings getDetectionSettings() {
  181.         return detectionSettings;
  182.     }

  183.     /** Get the triggering event.
  184.      * @return triggering event
  185.      */
  186.     public EventDetector getTrigger() {
  187.         return trigger;
  188.     }

  189.     /**
  190.      * Getter for the impulse provider.
  191.      * @return impulse provider
  192.      * @since 13.0
  193.      */
  194.     public ImpulseProvider getImpulseProvider() {
  195.         return impulseProvider;
  196.     }

  197.     /** Get the specific impulse.
  198.     * @return specific impulse
  199.     */
  200.     public double getIsp() {
  201.         return isp;
  202.     }

  203.     /** Local handler. */
  204.     private static class Handler implements EventHandler {

  205.         /** {@inheritDoc} */
  206.         public Action eventOccurred(final SpacecraftState s, final EventDetector detector,
  207.                                     final boolean increasing) {
  208.             final ImpulseManeuver im = (ImpulseManeuver) detector;
  209.             im.trigger.getHandler().eventOccurred(s, im.trigger, increasing); // Action is ignored but method still called
  210.             return Action.RESET_STATE;
  211.         }

  212.         /** {@inheritDoc} */
  213.         @Override
  214.         public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {

  215.             final ImpulseManeuver im = (ImpulseManeuver) detector;
  216.             final AbsoluteDate date = oldState.getDate();
  217.             final AttitudeProvider override = im.getAttitudeOverride();
  218.             final boolean isStateOrbitDefined = oldState.isOrbitDefined();

  219.             final Rotation rotation;
  220.             if (override == null) {
  221.                 rotation = oldState.getAttitude().getRotation();
  222.             } else {
  223.                 rotation = override.getAttitudeRotation(isStateOrbitDefined ? oldState.getOrbit() : oldState.getAbsPVA(),
  224.                         date, oldState.getFrame());
  225.             }

  226.             // convert velocity increment in inertial frame
  227.             final Vector3D deltaVSat = im.impulseProvider.getImpulse(oldState, im.forward);
  228.             final Vector3D deltaV = rotation.applyInverseTo(deltaVSat);

  229.             // apply increment to position/velocity
  230.             final PVCoordinates oldPV = oldState.getPVCoordinates();
  231.             final Vector3D newVelocity = oldPV.getVelocity().add(deltaV);
  232.             final PVCoordinates newPV = new PVCoordinates(oldPV.getPosition(), newVelocity);

  233.             // compute new mass
  234.             final double normDeltaV = im.getControl3DVectorCostType().evaluate(deltaVSat);
  235.             final double sign     = im.forward ? +1 : -1;
  236.             final double newMass = oldState.getMass() * FastMath.exp(-sign * normDeltaV / im.vExhaust);

  237.             // pack everything in a new state
  238.             if (oldState.isOrbitDefined()) {
  239.                 final CartesianOrbit newOrbit = new CartesianOrbit(newPV, oldState.getFrame(), oldState.getDate(),
  240.                         oldState.getOrbit().getMu());
  241.                 return new SpacecraftState(oldState.getOrbit().getType().normalize(newOrbit, oldState.getOrbit()),
  242.                         oldState.getAttitude(), newMass, oldState.getAdditionalDataValues(), oldState.getAdditionalStatesDerivatives());
  243.             } else {
  244.                 final AbsolutePVCoordinates newAPV = new AbsolutePVCoordinates(oldState.getFrame(), oldState.getDate(),
  245.                         newPV);
  246.                 return new SpacecraftState(newAPV, oldState.getAttitude(), newMass,
  247.                         oldState.getAdditionalDataValues(), oldState.getAdditionalStatesDerivatives());
  248.             }
  249.         }

  250.     }

  251. }