ImpulseManeuver.java

  1. /* Copyright 2002-2024 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.orbits.CartesianOrbit;
  24. import org.orekit.propagation.SpacecraftState;
  25. import org.orekit.propagation.events.AbstractDetector;
  26. import org.orekit.propagation.events.AdaptableInterval;
  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. /** 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 triggered when an underlying event generates a
  38.  * {@link Action#STOP STOP} event, in which case this class will generate a {@link
  39.  * Action#RESET_STATE RESET_STATE}
  40.  * event (the stop event from the underlying object is therefore filtered out).
  41.  * In the simple cases, the underlying event detector may be a basic
  42.  * {@link org.orekit.propagation.events.DateDetector date event}, but it
  43.  * can also be a more elaborate {@link
  44.  * org.orekit.propagation.events.ApsideDetector apside event} for apogee
  45.  * maneuvers for example.</p>
  46.  * <p>The maneuver is defined by a single velocity increment.
  47.  * If no AttitudeProvider is given, the current attitude of the spacecraft,
  48.  * defined by the current spacecraft state, will be used as the
  49.  * {@link AttitudeProvider} so the velocity increment should be given in
  50.  * the same pseudoinertial frame as the {@link SpacecraftState} used to
  51.  * construct the propagator that will handle the maneuver.
  52.  * If an AttitudeProvider is given, the velocity increment given should be
  53.  * defined appropriately in consideration of that provider. So, a typical
  54.  * case for tangential maneuvers is to provide a {@link org.orekit.attitudes.LofOffset LOF aligned}
  55.  * attitude provider along with a velocity increment defined in accordance with
  56.  * that LOF aligned attitude provider; e.g. if the LOF aligned attitude provider
  57.  * was constructed using LOFType.VNC the velocity increment should be
  58.  * provided in VNC coordinates.</p>
  59.  * <p>The norm through which the delta-V maps to the mass consumption is chosen via the
  60.  * enum {@link Control3DVectorCostType}. Default is Euclidean. </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.  * @author Luc Maisonobe
  69.  */
  70. public class ImpulseManeuver extends AbstractDetector<ImpulseManeuver> {

  71.     /** The attitude to override during the maneuver, if set. */
  72.     private final AttitudeProvider attitudeOverride;

  73.     /** Triggering event. */
  74.     private final EventDetector trigger;

  75.     /** Velocity increment in satellite frame. */
  76.     private final Vector3D deltaVSat;

  77.     /** Specific impulse. */
  78.     private final double isp;

  79.     /** Engine exhaust velocity. */
  80.     private final double vExhaust;

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

  83.     /** Type of norm linking delta-V to mass consumption. */
  84.     private final Control3DVectorCostType control3DVectorCostType;

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


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

  105.     /** Build a new instance.
  106.      * @param trigger triggering event
  107.      * @param attitudeOverride the attitude provider to use for the maneuver
  108.      * @param deltaVSat velocity increment in satellite frame
  109.      * @param isp engine specific impulse (s)
  110.      * @param control3DVectorCostType increment's norm for mass consumption
  111.      */
  112.     public ImpulseManeuver(final EventDetector trigger, final AttitudeProvider attitudeOverride,
  113.                            final Vector3D deltaVSat, final double isp, final Control3DVectorCostType control3DVectorCostType) {
  114.         this(trigger.getMaxCheckInterval(), trigger.getThreshold(),
  115.              trigger.getMaxIterationCount(), new Handler(),
  116.              trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType);
  117.     }

  118.     /** Protected constructor with full parameters.
  119.      * <p>
  120.      * This constructor is not public as users are expected to use the builder
  121.      * API with the various {@code withXxx()} methods to set up the instance
  122.      * in a readable manner without using a huge amount of parameters.
  123.      * </p>
  124.      * @param maxCheck maximum checking interval
  125.      * @param threshold convergence threshold (s)
  126.      * @param maxIter maximum number of iterations in the event time search
  127.      * @param handler event handler to call at event occurrences
  128.      * @param trigger triggering event
  129.      * @param attitudeOverride the attitude provider to use for the maneuver
  130.      * @param deltaVSat velocity increment in satellite frame
  131.      * @param isp engine specific impulse (s)
  132.      * @param control3DVectorCostType increment's norm for mass consumption
  133.      * @since 6.1
  134.      */
  135.     protected ImpulseManeuver(final AdaptableInterval maxCheck, final double threshold,
  136.                               final int maxIter, final EventHandler handler,
  137.                               final EventDetector trigger, final AttitudeProvider attitudeOverride, final Vector3D deltaVSat,
  138.                               final double isp, final Control3DVectorCostType control3DVectorCostType) {
  139.         super(maxCheck, threshold, maxIter, handler);
  140.         this.attitudeOverride = attitudeOverride;
  141.         this.trigger   = trigger;
  142.         this.deltaVSat = deltaVSat;
  143.         this.isp       = isp;
  144.         this.vExhaust  = Constants.G0_STANDARD_GRAVITY * isp;
  145.         this.control3DVectorCostType = control3DVectorCostType;
  146.     }

  147.     /** {@inheritDoc} */
  148.     @Override
  149.     protected ImpulseManeuver create(final AdaptableInterval newMaxCheck, final double newThreshold,
  150.                                      final int newMaxIter, final EventHandler newHandler) {
  151.         return new ImpulseManeuver(newMaxCheck, newThreshold, newMaxIter, newHandler,
  152.                                    trigger, attitudeOverride, deltaVSat, isp, control3DVectorCostType);
  153.     }

  154.     /** {@inheritDoc} */
  155.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  156.         forward = t.durationFrom(s0.getDate()) >= 0;
  157.         // Initialize the triggering event
  158.         trigger.init(s0, t);
  159.     }

  160.     /** {@inheritDoc} */
  161.     public double g(final SpacecraftState s) {
  162.         return trigger.g(s);
  163.     }

  164.     /**
  165.      * Get the Attitude Provider to use during maneuver.
  166.      * @return the attitude provider
  167.      */
  168.     public AttitudeProvider getAttitudeOverride() {
  169.         return attitudeOverride;
  170.     }

  171.     /** Get the triggering event.
  172.      * @return triggering event
  173.      */
  174.     public EventDetector getTrigger() {
  175.         return trigger;
  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.     /** Get the specific impulse.
  184.     * @return specific impulse
  185.     */
  186.     public double getIsp() {
  187.         return isp;
  188.     }

  189.     /** Get the control vector's cost type.
  190.      * @return control cost type
  191.      * @since 12.0
  192.      */
  193.     public Control3DVectorCostType getControl3DVectorCostType() {
  194.         return control3DVectorCostType;
  195.     }

  196.     /** Local handler. */
  197.     private static class Handler implements EventHandler {

  198.         /** {@inheritDoc} */
  199.         public Action eventOccurred(final SpacecraftState s, final EventDetector detector,
  200.                                     final boolean increasing) {

  201.             // filter underlying event
  202.             final ImpulseManeuver im = (ImpulseManeuver) detector;
  203.             final Action underlyingAction = im.trigger.getHandler().eventOccurred(s, im.trigger, increasing);

  204.             return (underlyingAction == Action.STOP) ? Action.RESET_STATE : Action.CONTINUE;

  205.         }

  206.         /** {@inheritDoc} */
  207.         @Override
  208.         public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {

  209.             final ImpulseManeuver im = (ImpulseManeuver) detector;
  210.             final AbsoluteDate date = oldState.getDate();
  211.             final AttitudeProvider override = im.getAttitudeOverride();
  212.             final Rotation rotation;

  213.             if (override == null) {
  214.                 rotation = oldState.getAttitude().getRotation();
  215.             } else {
  216.                 rotation = override.getAttitudeRotation(oldState.getOrbit(), date, oldState.getFrame());
  217.             }

  218.             // convert velocity increment in inertial frame
  219.             final Vector3D deltaV = rotation.applyInverseTo(im.deltaVSat);
  220.             final double sign     = im.forward ? +1 : -1;

  221.             // apply increment to position/velocity
  222.             final PVCoordinates oldPV = oldState.getPVCoordinates();
  223.             final PVCoordinates newPV =
  224.                             new PVCoordinates(oldPV.getPosition(),
  225.                                               new Vector3D(1, oldPV.getVelocity(), sign, deltaV));
  226.             final CartesianOrbit newOrbit =
  227.                     new CartesianOrbit(newPV, oldState.getFrame(), date, oldState.getMu());

  228.             // compute new mass
  229.             final double normDeltaV = im.control3DVectorCostType.evaluate(im.deltaVSat);
  230.             final double newMass = oldState.getMass() * FastMath.exp(-sign * normDeltaV / im.vExhaust);

  231.             // pack everything in a new state
  232.             SpacecraftState newState = new SpacecraftState(oldState.getOrbit().getType().normalize(newOrbit, oldState.getOrbit()),
  233.                                                            oldState.getAttitude(), newMass);
  234.             for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesValues().getData()) {
  235.                 newState = newState.addAdditionalState(entry.getKey(), entry.getValue());
  236.             }
  237.             for (final DoubleArrayDictionary.Entry entry : oldState.getAdditionalStatesDerivatives().getData()) {
  238.                 newState = newState.addAdditionalStateDerivative(entry.getKey(), entry.getValue());
  239.             }
  240.             return newState;

  241.         }

  242.     }

  243. }