SmallManeuverAnalyticalModel.java
- /* Copyright 2002-2013 CS Systèmes d'Information
- * Licensed to CS Systèmes d'Information (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.forces.maneuvers;
- import java.util.Arrays;
- import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
- import org.apache.commons.math3.util.FastMath;
- import org.orekit.errors.OrekitException;
- import org.orekit.frames.Frame;
- import org.orekit.orbits.Orbit;
- import org.orekit.orbits.OrbitType;
- import org.orekit.orbits.PositionAngle;
- import org.orekit.propagation.SpacecraftState;
- import org.orekit.propagation.analytical.AdapterPropagator;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.utils.Constants;
- /** Analytical model for small maneuvers.
- * <p>The aim of this model is to compute quickly the effect at date t<sub>1</sub>
- * of a small maneuver performed at an earlier date t<sub>0</sub>. Both the
- * direct effect of the maneuver and the Jacobian of this effect with respect to
- * maneuver parameters are available.
- * </p>
- * <p>These effect are computed analytically using two Jacobian matrices:
- * <ol>
- * <li>J<sub>0</sub>: Jacobian of Keplerian or equinoctial elements with respect
- * to cartesian parameters at date t<sub>0</sub></li> allows to compute
- * maneuver effect as a change in orbital elements at maneuver date t<sub>0</sub>,
- * <li>J<sub>1/0</sub>: Jacobian of Keplerian or equinoctial elements
- * at date t<sub>1</sub> with respect to Keplerian or equinoctial elements
- * at date t<sub>0</sub></li> allows to propagate the change in orbital elements
- * to final date t<sub>1</sub>.
- * </ol>
- * </p>
- * <p>
- * The second Jacobian, J<sub>1/0</sub>, is computed using a simple Keplerian
- * model, i.e. it is the identity except for the mean motion row which also includes
- * an off-diagonal element due to semi-major axis change.
- * </p>
- * <p>
- * The orbital elements change at date t<sub>1</sub> can be added to orbital elements
- * extracted from state, and the final elements taking account the changes are then
- * converted back to appropriate type, which may be different from Keplerian or
- * equinoctial elements.
- * </p>
- * <p>
- * Note that this model takes <em>only</em> Keplerian effects into account. This means
- * that using only this class to compute an inclination maneuver in Low Earth Orbit will
- * <em>not</em> change ascending node drift rate despite inclination has changed (the
- * same would be true for a semi-major axis change of course). In order to take this
- * drift into account, an instance of {@link
- * org.orekit.propagation.analytical.J2DifferentialEffect J2DifferentialEffect}
- * must be used together with an instance of this class.
- * </p>
- * @author Luc Maisonobe
- */
- public class SmallManeuverAnalyticalModel
- implements AdapterPropagator.DifferentialEffect {
- /** State at maneuver date (before maneuver occurrence). */
- private final SpacecraftState state0;
- /** Inertial velocity increment. */
- private final Vector3D inertialDV;
- /** Mass change ratio. */
- private final double massRatio;
- /** Type of orbit used for internal Jacobians. */
- private final OrbitType type;
- /** Initial Keplerian (or equinoctial) Jacobian with respect to maneuver. */
- private final double[][] j0;
- /** Time derivative of the initial Keplerian (or equinoctial) Jacobian with respect to maneuver. */
- private double[][] j0Dot;
- /** Mean anomaly change factor. */
- private final double ksi;
- /** Build a maneuver defined in spacecraft frame.
- * @param state0 state at maneuver date, <em>before</em> the maneuver
- * is performed
- * @param dV velocity increment in spacecraft frame
- * @param isp engine specific impulse (s)
- * @exception OrekitException if spacecraft frame cannot be transformed
- */
- public SmallManeuverAnalyticalModel(final SpacecraftState state0,
- final Vector3D dV, final double isp)
- throws OrekitException {
- this(state0, state0.getFrame(),
- state0.getAttitude().getRotation().applyInverseTo(dV),
- isp);
- }
- /** Build a maneuver defined in user-specified frame.
- * @param state0 state at maneuver date, <em>before</em> the maneuver
- * is performed
- * @param frame frame in which velocity increment is defined
- * @param dV velocity increment in specified frame
- * @param isp engine specific impulse (s)
- * @exception OrekitException if velocity increment frame cannot be transformed
- */
- public SmallManeuverAnalyticalModel(final SpacecraftState state0, final Frame frame,
- final Vector3D dV, final double isp)
- throws OrekitException {
- this.state0 = state0;
- this.massRatio = FastMath.exp(-dV.getNorm() / (Constants.G0_STANDARD_GRAVITY * isp));
- // use equinoctial orbit type if possible, Keplerian if nearly hyperbolic orbits
- type = (state0.getE() < 0.9) ? OrbitType.EQUINOCTIAL : OrbitType.KEPLERIAN;
- // compute initial Jacobian
- final double[][] fullJacobian = new double[6][6];
- j0 = new double[6][3];
- final Orbit orbit0 = type.convertType(state0.getOrbit());
- orbit0.getJacobianWrtCartesian(PositionAngle.MEAN, fullJacobian);
- for (int i = 0; i < j0.length; ++i) {
- System.arraycopy(fullJacobian[i], 3, j0[i], 0, 3);
- }
- // use lazy evaluation for j0Dot, as it is used only when Jacobians are evaluated
- j0Dot = null;
- // compute maneuver effect on Keplerian (or equinoctial) elements
- inertialDV = frame.getTransformTo(state0.getFrame(), state0.getDate()).transformVector(dV);
- // compute mean anomaly change: dM(t1) = dM(t0) + ksi * da * (t1 - t0)
- final double mu = state0.getMu();
- final double a = state0.getA();
- ksi = -1.5 * FastMath.sqrt(mu / a) / (a * a);
- }
- /** Get the date of the maneuver.
- * @return date of the maneuver
- */
- public AbsoluteDate getDate() {
- return state0.getDate();
- }
- /** Get the inertial velocity increment of the maneuver.
- * @return velocity increment in a state-dependent inertial frame
- * @see #getInertialFrame()
- */
- public Vector3D getInertialDV() {
- return inertialDV;
- }
- /** Get the inertial frame in which the velocity increment is defined.
- * @return inertial frame in which the velocity increment is defined
- * @see #getInertialDV()
- */
- public Frame getInertialFrame() {
- return state0.getFrame();
- }
- /** Compute the effect of the maneuver on an orbit.
- * @param orbit1 original orbit at t<sub>1</sub>, without maneuver
- * @return orbit at t<sub>1</sub>, taking the maneuver
- * into account if t<sub>1</sub> > t<sub>0</sub>
- * @see #apply(SpacecraftState)
- * @see #getJacobian(Orbit, PositionAngle, double[][])
- */
- public Orbit apply(final Orbit orbit1) {
- if (orbit1.getDate().compareTo(state0.getDate()) <= 0) {
- // the maneuver has not occurred yet, don't change anything
- return orbit1;
- }
- return updateOrbit(orbit1);
- }
- /** Compute the effect of the maneuver on a spacecraft state.
- * @param state1 original spacecraft state at t<sub>1</sub>,
- * without maneuver
- * @return spacecraft state at t<sub>1</sub>, taking the maneuver
- * into account if t<sub>1</sub> > t<sub>0</sub>
- * @see #apply(Orbit)
- * @see #getJacobian(Orbit, PositionAngle, double[][])
- */
- public SpacecraftState apply(final SpacecraftState state1) {
- if (state1.getDate().compareTo(state0.getDate()) <= 0) {
- // the maneuver has not occurred yet, don't change anything
- return state1;
- }
- return new SpacecraftState(updateOrbit(state1.getOrbit()),
- state1.getAttitude(), updateMass(state1.getMass()));
- }
- /** Compute the effect of the maneuver on an orbit.
- * @param orbit1 original orbit at t<sub>1</sub>, without maneuver
- * @return orbit at t<sub>1</sub>, always taking the maneuver into account
- */
- private Orbit updateOrbit(final Orbit orbit1) {
- // compute maneuver effect
- final double dt = orbit1.getDate().durationFrom(state0.getDate());
- final double x = inertialDV.getX();
- final double y = inertialDV.getY();
- final double z = inertialDV.getZ();
- final double[] delta = new double[6];
- for (int i = 0; i < delta.length; ++i) {
- delta[i] = j0[i][0] * x + j0[i][1] * y + j0[i][2] * z;
- }
- delta[5] += ksi * delta[0] * dt;
- // convert current orbital state to Keplerian or equinoctial elements
- final double[] parameters = new double[6];
- type.mapOrbitToArray(type.convertType(orbit1), PositionAngle.MEAN, parameters);
- for (int i = 0; i < delta.length; ++i) {
- parameters[i] += delta[i];
- }
- // build updated orbit as Keplerian or equinoctial elements
- final Orbit o = type.mapArrayToOrbit(parameters, PositionAngle.MEAN,
- orbit1.getDate(), orbit1.getMu(),
- orbit1.getFrame());
- // convert to required type
- return orbit1.getType().convertType(o);
- }
- /** Compute the Jacobian of the orbit with respect to maneuver parameters.
- * <p>
- * The Jacobian matrix is a 6x4 matrix. Element jacobian[i][j] corresponds to
- * the partial derivative of orbital parameter i with respect to maneuver
- * parameter j. The rows order is the same order as used in {@link
- * Orbit#getJacobianWrtCartesian(PositionAngle, double[][]) Orbit.getJacobianWrtCartesian}
- * method. Columns (0, 1, 2) correspond to the velocity increment coordinates
- * (ΔV<sub>x</sub>, ΔV<sub>y</sub>, ΔV<sub>z</sub>) in the
- * inertial frame returned by {@link #getInertialFrame()}, and column 3
- * corresponds to the maneuver date t<sub>0</sub>.
- * </p>
- * @param orbit1 original orbit at t<sub>1</sub>, without maneuver
- * @param positionAngle type of the position angle to use
- * @param jacobian placeholder 6x4 (or larger) matrix to be filled with the Jacobian, if matrix
- * is larger than 6x4, only the 6x4 upper left corner will be modified
- * @see #apply(Orbit)
- * @exception OrekitException if time derivative of the initial Jacobian cannot be computed
- */
- public void getJacobian(final Orbit orbit1, final PositionAngle positionAngle,
- final double[][] jacobian)
- throws OrekitException {
- final double dt = orbit1.getDate().durationFrom(state0.getDate());
- if (dt < 0) {
- // the maneuver has not occurred yet, Jacobian is null
- for (int i = 0; i < 6; ++i) {
- Arrays.fill(jacobian[i], 0, 4, 0.0);
- }
- return;
- }
- // derivatives of Keplerian/equinoctial elements with respect to velocity increment
- final double x = inertialDV.getX();
- final double y = inertialDV.getY();
- final double z = inertialDV.getZ();
- for (int i = 0; i < 6; ++i) {
- System.arraycopy(j0[i], 0, jacobian[i], 0, 3);
- }
- for (int j = 0; j < 3; ++j) {
- jacobian[5][j] += ksi * dt * j0[0][j];
- }
- // derivatives of Keplerian/equinoctial elements with respect to date
- evaluateJ0Dot();
- for (int i = 0; i < 6; ++i) {
- jacobian[i][3] = j0Dot[i][0] * x + j0Dot[i][1] * y + j0Dot[i][2] * z;
- }
- final double da = j0[0][0] * x + j0[0][1] * y + j0[0][2] * z;
- jacobian[5][3] += ksi * (jacobian[0][3] * dt - da);
- if (orbit1.getType() != type || positionAngle != PositionAngle.MEAN) {
- // convert to derivatives of cartesian parameters
- final double[][] j2 = new double[6][6];
- final double[][] pvJacobian = new double[6][4];
- final Orbit updated = updateOrbit(orbit1);
- type.convertType(updated).getJacobianWrtParameters(PositionAngle.MEAN, j2);
- for (int i = 0; i < 6; ++i) {
- for (int j = 0; j < 4; ++j) {
- pvJacobian[i][j] = j2[i][0] * jacobian[0][j] + j2[i][1] * jacobian[1][j] +
- j2[i][2] * jacobian[2][j] + j2[i][3] * jacobian[3][j] +
- j2[i][4] * jacobian[4][j] + j2[i][5] * jacobian[5][j];
- }
- }
- // convert to derivatives of specified parameters
- final double[][] j3 = new double[6][6];
- updated.getJacobianWrtCartesian(positionAngle, j3);
- for (int j = 0; j < 4; ++j) {
- for (int i = 0; i < 6; ++i) {
- jacobian[i][j] = j3[i][0] * pvJacobian[0][j] + j3[i][1] * pvJacobian[1][j] +
- j3[i][2] * pvJacobian[2][j] + j3[i][3] * pvJacobian[3][j] +
- j3[i][4] * pvJacobian[4][j] + j3[i][5] * pvJacobian[5][j];
- }
- }
- }
- }
- /** Lazy evaluation of the initial Jacobian time derivative.
- * @exception OrekitException if initial orbit cannot be shifted
- */
- private void evaluateJ0Dot() throws OrekitException {
- if (j0Dot == null) {
- j0Dot = new double[6][3];
- final double dt = 1.0e-5 / state0.getKeplerianMeanMotion();
- final Orbit orbit = type.convertType(state0.getOrbit());
- // compute shifted Jacobians
- final double[][] j0m1 = new double[6][6];
- orbit.shiftedBy(-1 * dt).getJacobianWrtCartesian(PositionAngle.MEAN, j0m1);
- final double[][] j0p1 = new double[6][6];
- orbit.shiftedBy(+1 * dt).getJacobianWrtCartesian(PositionAngle.MEAN, j0p1);
- // evaluate derivative by finite differences
- for (int i = 0; i < j0Dot.length; ++i) {
- final double[] m1Row = j0m1[i];
- final double[] p1Row = j0p1[i];
- final double[] j0DotRow = j0Dot[i];
- for (int j = 0; j < 3; ++j) {
- j0DotRow[j] = (p1Row[j + 3] - m1Row[j + 3]) / (2 * dt);
- }
- }
- }
- }
- /** Update a spacecraft mass due to maneuver.
- * @param mass masse before maneuver
- * @return mass after maneuver
- */
- public double updateMass(final double mass) {
- return massRatio * mass;
- }
- }