CircularOrbit.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.orbits;

  18. import java.util.Collection;

  19. import org.apache.commons.math3.analysis.interpolation.HermiteInterpolator;
  20. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  21. import org.apache.commons.math3.util.FastMath;
  22. import org.apache.commons.math3.util.MathUtils;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.frames.Frame;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.utils.PVCoordinates;


  28. /**
  29.  * This class handles circular orbital parameters.

  30.  * <p>
  31.  * The parameters used internally are the circular elements which can be
  32.  * related to keplerian elements as follows:
  33.  *   <ul>
  34.  *     <li>a</li>
  35.  *     <li>e<sub>x</sub> = e cos(&omega;)</li>
  36.  *     <li>e<sub>y</sub> = e sin(&omega;)</li>
  37.  *     <li>i</li>
  38.  *     <li>&Omega;</li>
  39.  *     <li>&alpha;<sub>v</sub> = v + &omega;</li>
  40.  *   </ul>
  41.  * where &Omega; stands for the Right Ascension of the Ascending Node and
  42.  * &alpha;<sub>v</sub> stands for the true latitude argument
  43.  * </p>
  44.  * <p>
  45.  * The conversion equations from and to keplerian elements given above hold only
  46.  * when both sides are unambiguously defined, i.e. when orbit is neither equatorial
  47.  * nor circular. When orbit is circular (but not equatorial), the circular
  48.  * parameters are still unambiguously defined whereas some keplerian elements
  49.  * (more precisely &omega; and &Omega;) become ambiguous. When orbit is equatorial,
  50.  * neither the keplerian nor the circular parameters can be defined unambiguously.
  51.  * {@link EquinoctialOrbit equinoctial orbits} is the recommended way to represent
  52.  * orbits.
  53.  * </p>
  54.  * <p>
  55.  * The instance <code>CircularOrbit</code> is guaranteed to be immutable.
  56.  * </p>
  57.  * @see    Orbit
  58.  * @see    KeplerianOrbit
  59.  * @see    CartesianOrbit
  60.  * @see    EquinoctialOrbit
  61.  * @author Luc Maisonobe
  62.  * @author Fabien Maussion
  63.  * @author V&eacute;ronique Pommier-Maurussane
  64.  */

  65. public class CircularOrbit
  66.     extends Orbit {

  67.     /** Identifier for mean latitude argument.
  68.      * @deprecated as of 6.0 replaced by {@link PositionAngle}
  69.      */
  70.     @Deprecated
  71.     public static final int MEAN_LONGITUDE_ARGUMENT = 0;

  72.     /** Identifier for eccentric latitude argument.
  73.      * @deprecated as of 6.0 replaced by {@link PositionAngle}
  74.      */
  75.     @Deprecated
  76.     public static final int ECCENTRIC_LONGITUDE_ARGUMENT = 1;

  77.     /** Identifier for true latitude argument.
  78.      * @deprecated as of 6.0 replaced by {@link PositionAngle}
  79.      */
  80.     @Deprecated
  81.     public static final int TRUE_LONGITUDE_ARGUMENT = 2;

  82.     /** Serializable UID. */
  83.     private static final long serialVersionUID = 5565190329070485158L;

  84.     /** Semi-major axis (m). */
  85.     private final double a;

  86.     /** First component of the circular eccentricity vector. */
  87.     private final double ex;

  88.     /** Second component of the circular eccentricity vector. */
  89.     private final double ey;

  90.     /** Inclination (rad). */
  91.     private final double i;

  92.     /** Right Ascension of Ascending Node (rad). */
  93.     private final double raan;

  94.     /** True latitude argument (rad). */
  95.     private final double alphaV;

  96.     /** Creates a new instance.
  97.      * @param a  semi-major axis (m)
  98.      * @param ex e cos(&omega;), first component of circular eccentricity vector
  99.      * @param ey e sin(&omega;), second component of circular eccentricity vector
  100.      * @param i inclination (rad)
  101.      * @param raan right ascension of ascending node (&Omega;, rad)
  102.      * @param alpha  an + &omega;, mean, eccentric or true latitude argument (rad)
  103.      * @param type type of latitude argument
  104.      * @param frame the frame in which are defined the parameters
  105.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  106.      * @param date date of the orbital parameters
  107.      * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  108.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  109.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  110.      */
  111.     public CircularOrbit(final double a, final double ex, final double ey,
  112.                          final double i, final double raan,
  113.                          final double alpha, final PositionAngle type,
  114.                          final Frame frame, final AbsoluteDate date, final double mu)
  115.         throws IllegalArgumentException {
  116.         super(frame, date, mu);
  117.         if (ex * ex + ey * ey >= 1.0) {
  118.             throw OrekitException.createIllegalArgumentException(
  119.                   OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName());
  120.         }
  121.         this.a    =  a;
  122.         this.ex   = ex;
  123.         this.ey   = ey;
  124.         this.i    = i;
  125.         this.raan = raan;

  126.         switch (type) {
  127.         case MEAN :
  128.             this.alphaV = eccentricToTrue(meanToEccentric(alpha));
  129.             break;
  130.         case ECCENTRIC :
  131.             this.alphaV = eccentricToTrue(alpha);
  132.             break;
  133.         case TRUE :
  134.             this.alphaV = alpha;
  135.             break;
  136.         default :
  137.             throw OrekitException.createInternalError(null);
  138.         }

  139.     }

  140.     /** Creates a new instance.
  141.      * @param a  semi-major axis (m)
  142.      * @param ex e cos(&omega;), first component of circular eccentricity vector
  143.      * @param ey e sin(&omega;), second component of circular eccentricity vector
  144.      * @param i inclination (rad)
  145.      * @param raan right ascension of ascending node (&Omega;, rad)
  146.      * @param alpha  an + &omega;, mean, eccentric or true latitude argument (rad)
  147.      * @param type type of latitude argument, must be one of {@link #MEAN_LONGITUDE_ARGUMENT},
  148.      * {@link #ECCENTRIC_LONGITUDE_ARGUMENT} or  {@link #TRUE_LONGITUDE_ARGUMENT}
  149.      * @param frame the frame in which are defined the parameters
  150.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  151.      * @param date date of the orbital parameters
  152.      * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  153.      * @exception IllegalArgumentException if the latitude argument type is not
  154.      * one of {@link #MEAN_LONGITUDE_ARGUMENT}, {@link #ECCENTRIC_LONGITUDE_ARGUMENT}
  155.      * or {@link #TRUE_LONGITUDE_ARGUMENT} or if frame is not a {@link
  156.      * Frame#isPseudoInertial pseudo-inertial frame}
  157.      * @see #MEAN_LONGITUDE_ARGUMENT
  158.      * @see #ECCENTRIC_LONGITUDE_ARGUMENT
  159.      * @see #TRUE_LONGITUDE_ARGUMENT
  160.      * @deprecated as of 6.0 replaced by {@link #CircularOrbit(double, double, double,
  161.      * double, double, double, PositionAngle, Frame, AbsoluteDate, double)}
  162.      * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or
  163.      * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame}
  164.      */
  165.     @Deprecated
  166.     public CircularOrbit(final double a, final double ex, final double ey,
  167.                          final double i, final double raan,
  168.                          final double alpha, final int type,
  169.                          final Frame frame, final AbsoluteDate date, final double mu)
  170.         throws IllegalArgumentException {
  171.         super(frame, date, mu);
  172.         if (ex * ex + ey * ey >= 1.0) {
  173.             throw OrekitException.createIllegalArgumentException(
  174.                   OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName());
  175.         }
  176.         this.a    =  a;
  177.         this.ex   = ex;
  178.         this.ey   = ey;
  179.         this.i    = i;
  180.         this.raan = raan;

  181.         switch (type) {
  182.         case MEAN_LONGITUDE_ARGUMENT :
  183.             this.alphaV = eccentricToTrue(meanToEccentric(alpha));
  184.             break;
  185.         case ECCENTRIC_LONGITUDE_ARGUMENT :
  186.             this.alphaV = eccentricToTrue(alpha);
  187.             break;
  188.         case TRUE_LONGITUDE_ARGUMENT :
  189.             this.alphaV = alpha;
  190.             break;
  191.         default :
  192.             this.alphaV = Double.NaN;
  193.             throw OrekitException.createIllegalArgumentException(
  194.                   OrekitMessages.ANGLE_TYPE_NOT_SUPPORTED,
  195.                   "MEAN_LONGITUDE_ARGUMENT", "ECCENTRIC_LONGITUDE_ARGUMENT",
  196.                   "TRUE_LONGITUDE_ARGUMENT");
  197.         }

  198.     }

  199.     /** Constructor from cartesian parameters.
  200.      * @param pvCoordinates the {@link PVCoordinates} in inertial frame
  201.      * @param frame the frame in which are defined the {@link PVCoordinates}
  202.      * (<em>must</em> be a {@link Frame#isPseudoInertial pseudo-inertial frame})
  203.      * @param date date of the orbital parameters
  204.      * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  205.      * @exception IllegalArgumentException if frame is not a {@link
  206.      * Frame#isPseudoInertial pseudo-inertial frame}
  207.      */
  208.     public CircularOrbit(final PVCoordinates pvCoordinates, final Frame frame,
  209.                               final AbsoluteDate date, final double mu)
  210.         throws IllegalArgumentException {
  211.         super(pvCoordinates, frame, date, mu);

  212.         // compute semi-major axis
  213.         final Vector3D pvP = pvCoordinates.getPosition();
  214.         final Vector3D pvV = pvCoordinates.getVelocity();
  215.         final double r  = pvP.getNorm();
  216.         final double V2 = pvV.getNormSq();
  217.         final double rV2OnMu = r * V2 / mu;

  218.         if (rV2OnMu > 2) {
  219.             throw OrekitException.createIllegalArgumentException(
  220.                   OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName());
  221.         }

  222.         a = r / (2 - rV2OnMu);

  223.         // compute inclination
  224.         final Vector3D momentum = pvCoordinates.getMomentum();
  225.         i = Vector3D.angle(momentum, Vector3D.PLUS_K);

  226.         // compute right ascension of ascending node
  227.         final Vector3D node  = Vector3D.crossProduct(Vector3D.PLUS_K, momentum);
  228.         raan = FastMath.atan2(node.getY(), node.getX());

  229.         // 2D-coordinates in the canonical frame
  230.         final double cosRaan = FastMath.cos(raan);
  231.         final double sinRaan = FastMath.sin(raan);
  232.         final double cosI    = FastMath.cos(i);
  233.         final double sinI    = FastMath.sin(i);
  234.         final double xP      = pvP.getX();
  235.         final double yP      = pvP.getY();
  236.         final double zP      = pvP.getZ();
  237.         final double x2      = (xP * cosRaan + yP * sinRaan) / a;
  238.         final double y2      = ((yP * cosRaan - xP * sinRaan) * cosI + zP * sinI) / a;

  239.         // compute eccentricity vector
  240.         final double eSE    = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(mu * a);
  241.         final double eCE    = rV2OnMu - 1;
  242.         final double e2     = eCE * eCE + eSE * eSE;
  243.         final double f      = eCE - e2;
  244.         final double g      = FastMath.sqrt(1 - e2) * eSE;
  245.         final double aOnR   = a / r;
  246.         final double a2OnR2 = aOnR * aOnR;
  247.         ex = a2OnR2 * (f * x2 + g * y2);
  248.         ey = a2OnR2 * (f * y2 - g * x2);

  249.         // compute latitude argument
  250.         final double beta = 1 / (1 + FastMath.sqrt(1 - ex * ex - ey * ey));
  251.         alphaV = eccentricToTrue(FastMath.atan2(y2 + ey + eSE * beta * ex, x2 + ex - eSE * beta * ey));
  252.     }

  253.     /** Constructor from any kind of orbital parameters.
  254.      * @param op orbital parameters to copy
  255.      */
  256.     public CircularOrbit(final Orbit op) {
  257.         super(op.getFrame(), op.getDate(), op.getMu());
  258.         a    = op.getA();
  259.         i    = op.getI();
  260.         raan = FastMath.atan2(op.getHy(), op.getHx());
  261.         final double cosRaan = FastMath.cos(raan);
  262.         final double sinRaan = FastMath.sin(raan);
  263.         final double equiEx = op.getEquinoctialEx();
  264.         final double equiEy = op.getEquinoctialEy();
  265.         ex   = equiEx * cosRaan + equiEy * sinRaan;
  266.         ey   = equiEy * cosRaan - equiEx * sinRaan;
  267.         this.alphaV = op.getLv() - raan;
  268.     }

  269.     /** {@inheritDoc} */
  270.     public OrbitType getType() {
  271.         return OrbitType.CIRCULAR;
  272.     }

  273.     /** {@inheritDoc} */
  274.     public double getA() {
  275.         return a;
  276.     }

  277.     /** {@inheritDoc} */
  278.     public double getEquinoctialEx() {
  279.         return ex * FastMath.cos(raan) - ey * FastMath.sin(raan);
  280.     }

  281.     /** {@inheritDoc} */
  282.     public double getEquinoctialEy() {
  283.         return ey * FastMath.cos(raan) + ex * FastMath.sin(raan);
  284.     }

  285.     /** Get the first component of the circular eccentricity vector.
  286.      * @return ex = e cos(&omega;), first component of the circular eccentricity vector
  287.      */
  288.     public double getCircularEx() {
  289.         return ex;
  290.     }

  291.     /** Get the second component of the circular eccentricity vector.
  292.      * @return ey = e sin(&omega;), second component of the circular eccentricity vector
  293.      */
  294.     public double getCircularEy() {
  295.         return ey;
  296.     }

  297.     /** {@inheritDoc} */
  298.     public double getHx() {
  299.         return  FastMath.cos(raan) * FastMath.tan(i / 2);
  300.     }

  301.     /** {@inheritDoc} */
  302.     public double getHy() {
  303.         return  FastMath.sin(raan) * FastMath.tan(i / 2);
  304.     }

  305.     /** Get the true latitude argument.
  306.      * @return v + &omega; true latitude argument (rad)
  307.      */
  308.     public double getAlphaV() {
  309.         return alphaV;
  310.     }

  311.     /** Get the latitude argument.
  312.      * @param type type of the angle
  313.      * @return latitude argument (rad)
  314.      */
  315.     public double getAlpha(final PositionAngle type) {
  316.         return (type == PositionAngle.MEAN) ? getAlphaM() :
  317.                                               ((type == PositionAngle.ECCENTRIC) ? getAlphaE() :
  318.                                                                                    getAlphaV());
  319.     }

  320.     /** Get the eccentric latitude argument.
  321.      * @return E + &omega; eccentric latitude argument (rad)
  322.      */
  323.     public double getAlphaE() {
  324.         final double epsilon   = FastMath.sqrt(1 - ex * ex - ey * ey);
  325.         final double cosAlphaV = FastMath.cos(alphaV);
  326.         final double sinAlphaV = FastMath.sin(alphaV);
  327.         return alphaV + 2 * FastMath.atan((ey * cosAlphaV - ex * sinAlphaV) /
  328.                                       (epsilon + 1 + ex * cosAlphaV + ey * sinAlphaV));
  329.     }

  330.     /** Computes the true latitude argument from the eccentric latitude argument.
  331.      * @param alphaE = E + &omega; eccentric latitude argument (rad)
  332.      * @return the true latitude argument.
  333.      */
  334.     private double eccentricToTrue(final double alphaE) {
  335.         final double epsilon   = FastMath.sqrt(1 - ex * ex - ey * ey);
  336.         final double cosAlphaE = FastMath.cos(alphaE);
  337.         final double sinAlphaE = FastMath.sin(alphaE);
  338.         return alphaE + 2 * FastMath.atan((ex * sinAlphaE - ey * cosAlphaE) /
  339.                                       (epsilon + 1 - ex * cosAlphaE - ey * sinAlphaE));
  340.     }

  341.     /** Get the mean latitude argument.
  342.      * @return M + &omega; mean latitude argument (rad)
  343.      */
  344.     public double getAlphaM() {
  345.         final double alphaE = getAlphaE();
  346.         return alphaE - ex * FastMath.sin(alphaE) + ey * FastMath.cos(alphaE);
  347.     }

  348.     /** Computes the eccentric latitude argument from the mean latitude argument.
  349.      * @param alphaM = M + &omega;  mean latitude argument (rad)
  350.      * @return the eccentric latitude argument.
  351.      */
  352.     private double meanToEccentric(final double alphaM) {
  353.         // Generalization of Kepler equation to circular parameters
  354.         // with alphaE = PA + E and
  355.         //      alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE)
  356.         double alphaE        = alphaM;
  357.         double shift         = 0.0;
  358.         double alphaEMalphaM = 0.0;
  359.         double cosAlphaE     = FastMath.cos(alphaE);
  360.         double sinAlphaE     = FastMath.sin(alphaE);
  361.         int    iter          = 0;
  362.         do {
  363.             final double f2 = ex * sinAlphaE - ey * cosAlphaE;
  364.             final double f1 = 1.0 - ex * cosAlphaE - ey * sinAlphaE;
  365.             final double f0 = alphaEMalphaM - f2;

  366.             final double f12 = 2.0 * f1;
  367.             shift = f0 * f12 / (f1 * f12 - f0 * f2);

  368.             alphaEMalphaM -= shift;
  369.             alphaE         = alphaM + alphaEMalphaM;
  370.             cosAlphaE      = FastMath.cos(alphaE);
  371.             sinAlphaE      = FastMath.sin(alphaE);

  372.         } while ((++iter < 50) && (FastMath.abs(shift) > 1.0e-12));

  373.         return alphaE;

  374.     }

  375.     /** {@inheritDoc} */
  376.     public double getE() {
  377.         return FastMath.sqrt(ex * ex + ey * ey);
  378.     }

  379.     /** {@inheritDoc} */
  380.     public double getI() {
  381.         return i;
  382.     }

  383.     /** Get the right ascension of the ascending node.
  384.      * @return right ascension of the ascending node (rad)
  385.      */
  386.     public double getRightAscensionOfAscendingNode() {
  387.         return raan;
  388.     }

  389.     /** {@inheritDoc} */
  390.     public double getLv() {
  391.         return alphaV + raan;
  392.     }

  393.     /** {@inheritDoc} */
  394.     public double getLE() {
  395.         return getAlphaE() + raan;
  396.     }

  397.     /** {@inheritDoc} */
  398.     public double getLM() {
  399.         return getAlphaM() + raan;
  400.     }

  401.     /** {@inheritDoc} */
  402.     protected PVCoordinates initPVCoordinates() {

  403.         // get equinoctial parameters
  404.         final double equEx = getEquinoctialEx();
  405.         final double equEy = getEquinoctialEy();
  406.         final double hx = getHx();
  407.         final double hy = getHy();
  408.         final double lE = getLE();

  409.         // inclination-related intermediate parameters
  410.         final double hx2   = hx * hx;
  411.         final double hy2   = hy * hy;
  412.         final double factH = 1. / (1 + hx2 + hy2);

  413.         // reference axes defining the orbital plane
  414.         final double ux = (1 + hx2 - hy2) * factH;
  415.         final double uy =  2 * hx * hy * factH;
  416.         final double uz = -2 * hy * factH;

  417.         final double vx = uy;
  418.         final double vy = (1 - hx2 + hy2) * factH;
  419.         final double vz =  2 * hx * factH;

  420.         // eccentricity-related intermediate parameters
  421.         final double exey = equEx * equEy;
  422.         final double ex2  = equEx * equEx;
  423.         final double ey2  = equEy * equEy;
  424.         final double e2   = ex2 + ey2;
  425.         final double eta  = 1 + FastMath.sqrt(1 - e2);
  426.         final double beta = 1. / eta;

  427.         // eccentric latitude argument
  428.         final double cLe    = FastMath.cos(lE);
  429.         final double sLe    = FastMath.sin(lE);
  430.         final double exCeyS = equEx * cLe + equEy * sLe;

  431.         // coordinates of position and velocity in the orbital plane
  432.         final double x      = a * ((1 - beta * ey2) * cLe + beta * exey * sLe - equEx);
  433.         final double y      = a * ((1 - beta * ex2) * sLe + beta * exey * cLe - equEy);

  434.         final double factor = FastMath.sqrt(getMu() / a) / (1 - exCeyS);
  435.         final double xdot   = factor * (-sLe + beta * equEy * exCeyS);
  436.         final double ydot   = factor * ( cLe - beta * equEx * exCeyS);

  437.         final Vector3D position =
  438.             new Vector3D(x * ux + y * vx, x * uy + y * vy, x * uz + y * vz);
  439.         final Vector3D velocity =
  440.             new Vector3D(xdot * ux + ydot * vx, xdot * uy + ydot * vy, xdot * uz + ydot * vz);
  441.         return new PVCoordinates(position, velocity);

  442.     }

  443.     /** {@inheritDoc} */
  444.     public CircularOrbit shiftedBy(final double dt) {
  445.         return new CircularOrbit(a, ex, ey, i, raan,
  446.                                  getAlphaM() + getKeplerianMeanMotion() * dt,
  447.                                  PositionAngle.MEAN, getFrame(),
  448.                                  getDate().shiftedBy(dt), getMu());
  449.     }

  450.     /** {@inheritDoc}
  451.      * <p>
  452.      * The interpolated instance is created by polynomial Hermite interpolation
  453.      * on circular elements, without derivatives (which means the interpolation
  454.      * falls back to Lagrange interpolation only).
  455.      * </p>
  456.      * <p>
  457.      * As this implementation of interpolation is polynomial, it should be used only
  458.      * with small samples (about 10-20 points) in order to avoid <a
  459.      * href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
  460.      * and numerical problems (including NaN appearing).
  461.      * </p>
  462.      * <p>
  463.      * If orbit interpolation on large samples is needed, using the {@link
  464.      * org.orekit.propagation.analytical.Ephemeris} class is a better way than using this
  465.      * low-level interpolation. The Ephemeris class automatically handles selection of
  466.      * a neighboring sub-sample with a predefined number of point from a large global sample
  467.      * in a thread-safe way.
  468.      * </p>
  469.      */
  470.     public CircularOrbit interpolate(final AbsoluteDate date, final Collection<Orbit> sample) {

  471.         // set up an interpolator
  472.         final HermiteInterpolator interpolator = new HermiteInterpolator();

  473.         // add sample points
  474.         AbsoluteDate previousDate = null;
  475.         double previousRAAN   = Double.NaN;
  476.         double previousAlphaM = Double.NaN;
  477.         for (final Orbit orbit : sample) {
  478.             final CircularOrbit circ = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit);
  479.             final double continuousRAAN;
  480.             final double continuousAlphaM;
  481.             if (previousDate == null) {
  482.                 continuousRAAN   = circ.getRightAscensionOfAscendingNode();
  483.                 continuousAlphaM = circ.getAlphaM();
  484.             } else {
  485.                 final double dt       = circ.getDate().durationFrom(previousDate);
  486.                 final double keplerAM = previousAlphaM + circ.getKeplerianMeanMotion() * dt;
  487.                 continuousRAAN   = MathUtils.normalizeAngle(circ.getRightAscensionOfAscendingNode(), previousRAAN);
  488.                 continuousAlphaM = MathUtils.normalizeAngle(circ.getAlphaM(), keplerAM);
  489.             }
  490.             previousDate   = circ.getDate();
  491.             previousRAAN   = continuousRAAN;
  492.             previousAlphaM = continuousAlphaM;
  493.             interpolator.addSamplePoint(circ.getDate().durationFrom(date),
  494.                                         new double[] {
  495.                                             circ.getA(),
  496.                                             circ.getCircularEx(),
  497.                                             circ.getCircularEy(),
  498.                                             circ.getI(),
  499.                                             continuousRAAN,
  500.                                             continuousAlphaM
  501.                                         });
  502.         }

  503.         // interpolate
  504.         final double[] interpolated = interpolator.value(0);

  505.         // build a new interpolated instance
  506.         return new CircularOrbit(interpolated[0], interpolated[1], interpolated[2],
  507.                                  interpolated[3], interpolated[4], interpolated[5],
  508.                                  PositionAngle.MEAN, getFrame(), date, getMu());

  509.     }

  510.     /** {@inheritDoc} */
  511.     protected double[][] computeJacobianMeanWrtCartesian() {

  512.         final double[][] jacobian = new double[6][6];

  513.         // compute various intermediate parameters
  514.         final PVCoordinates pvc = getPVCoordinates();
  515.         final Vector3D position = pvc.getPosition();
  516.         final Vector3D velocity = pvc.getVelocity();

  517.         final double x          = position.getX();
  518.         final double y          = position.getY();
  519.         final double z          = position.getZ();
  520.         final double vx         = velocity.getX();
  521.         final double vy         = velocity.getY();
  522.         final double vz         = velocity.getZ();
  523.         final double pv         = Vector3D.dotProduct(position, velocity);
  524.         final double r2         = position.getNormSq();
  525.         final double r          = FastMath.sqrt(r2);
  526.         final double v2         = velocity.getNormSq();

  527.         final double mu         = getMu();
  528.         final double oOsqrtMuA  = 1 / FastMath.sqrt(mu * a);
  529.         final double rOa        = r / a;
  530.         final double aOr        = a / r;
  531.         final double aOr2       = a / r2;
  532.         final double a2         = a * a;

  533.         final double ex2        = ex * ex;
  534.         final double ey2        = ey * ey;
  535.         final double e2         = ex2 + ey2;
  536.         final double epsilon    = FastMath.sqrt(1 - e2);
  537.         final double beta       = 1 / (1 + epsilon);

  538.         final double eCosE      = 1 - rOa;
  539.         final double eSinE      = pv * oOsqrtMuA;

  540.         final double cosI       = FastMath.cos(i);
  541.         final double sinI       = FastMath.sin(i);
  542.         final double cosRaan    = FastMath.cos(raan);
  543.         final double sinRaan    = FastMath.sin(raan);

  544.         // da
  545.         fillHalfRow(2 * aOr * aOr2, position, jacobian[0], 0);
  546.         fillHalfRow(2 * a2 / mu, velocity, jacobian[0], 3);

  547.         // differentials of the normalized momentum
  548.         final Vector3D danP = new Vector3D(v2, position, -pv, velocity);
  549.         final Vector3D danV = new Vector3D(r2, velocity, -pv, position);
  550.         final double recip  = 1 / pvc.getMomentum().getNorm();
  551.         final double recip2 = recip * recip;
  552.         final Vector3D dwXP = new Vector3D(recip, new Vector3D(  0,  vz, -vy), -recip2 * sinRaan * sinI, danP);
  553.         final Vector3D dwYP = new Vector3D(recip, new Vector3D(-vz,   0,  vx),  recip2 * cosRaan * sinI, danP);
  554.         final Vector3D dwZP = new Vector3D(recip, new Vector3D( vy, -vx,   0), -recip2 * cosI,           danP);
  555.         final Vector3D dwXV = new Vector3D(recip, new Vector3D(  0,  -z,   y), -recip2 * sinRaan * sinI, danV);
  556.         final Vector3D dwYV = new Vector3D(recip, new Vector3D(  z,   0,  -x),  recip2 * cosRaan * sinI, danV);
  557.         final Vector3D dwZV = new Vector3D(recip, new Vector3D( -y,   x,   0), -recip2 * cosI,           danV);

  558.         // di
  559.         fillHalfRow(sinRaan * cosI, dwXP, -cosRaan * cosI, dwYP, -sinI, dwZP, jacobian[3], 0);
  560.         fillHalfRow(sinRaan * cosI, dwXV, -cosRaan * cosI, dwYV, -sinI, dwZV, jacobian[3], 3);

  561.         // dRaan
  562.         fillHalfRow(sinRaan / sinI, dwYP, cosRaan / sinI, dwXP, jacobian[4], 0);
  563.         fillHalfRow(sinRaan / sinI, dwYV, cosRaan / sinI, dwXV, jacobian[4], 3);

  564.         // orbital frame: (p, q, w) p along ascending node, w along momentum
  565.         // the coordinates of the spacecraft in this frame are: (u, v, 0)
  566.         final double u     =  x * cosRaan + y * sinRaan;
  567.         final double cv    = -x * sinRaan + y * cosRaan;
  568.         final double v     = cv * cosI + z * sinI;

  569.         // du
  570.         final Vector3D duP = new Vector3D(cv * cosRaan / sinI, dwXP,
  571.                                           cv * sinRaan / sinI, dwYP,
  572.                                           1, new Vector3D(cosRaan, sinRaan, 0));
  573.         final Vector3D duV = new Vector3D(cv * cosRaan / sinI, dwXV,
  574.                                           cv * sinRaan / sinI, dwYV);

  575.         // dv
  576.         final Vector3D dvP = new Vector3D(-u * cosRaan * cosI / sinI + sinRaan * z, dwXP,
  577.                                           -u * sinRaan * cosI / sinI - cosRaan * z, dwYP,
  578.                                           cv, dwZP,
  579.                                           1, new Vector3D(-sinRaan * cosI, cosRaan * cosI, sinI));
  580.         final Vector3D dvV = new Vector3D(-u * cosRaan * cosI / sinI + sinRaan * z, dwXV,
  581.                                           -u * sinRaan * cosI / sinI - cosRaan * z, dwYV,
  582.                                           cv, dwZV);

  583.         final Vector3D dc1P = new Vector3D(aOr2 * (2 * eSinE * eSinE + 1 - eCosE) / r2, position,
  584.                                             -2 * aOr2 * eSinE * oOsqrtMuA, velocity);
  585.         final Vector3D dc1V = new Vector3D(-2 * aOr2 * eSinE * oOsqrtMuA, position,
  586.                                             2 / mu, velocity);
  587.         final Vector3D dc2P = new Vector3D(aOr2 * eSinE * (eSinE * eSinE - (1 - e2)) / (r2 * epsilon), position,
  588.                                             aOr2 * (1 - e2 - eSinE * eSinE) * oOsqrtMuA / epsilon, velocity);
  589.         final Vector3D dc2V = new Vector3D(aOr2 * (1 - e2 - eSinE * eSinE) * oOsqrtMuA / epsilon, position,
  590.                                             eSinE / (mu * epsilon), velocity);

  591.         final double cof1   = aOr2 * (eCosE - e2);
  592.         final double cof2   = aOr2 * epsilon * eSinE;
  593.         final Vector3D dexP = new Vector3D(u, dc1P,  v, dc2P, cof1, duP,  cof2, dvP);
  594.         final Vector3D dexV = new Vector3D(u, dc1V,  v, dc2V, cof1, duV,  cof2, dvV);
  595.         final Vector3D deyP = new Vector3D(v, dc1P, -u, dc2P, cof1, dvP, -cof2, duP);
  596.         final Vector3D deyV = new Vector3D(v, dc1V, -u, dc2V, cof1, dvV, -cof2, duV);
  597.         fillHalfRow(1, dexP, jacobian[1], 0);
  598.         fillHalfRow(1, dexV, jacobian[1], 3);
  599.         fillHalfRow(1, deyP, jacobian[2], 0);
  600.         fillHalfRow(1, deyV, jacobian[2], 3);

  601.         final double cle = u / a + ex - eSinE * beta * ey;
  602.         final double sle = v / a + ey + eSinE * beta * ex;
  603.         final double m1  = beta * eCosE;
  604.         final double m2  = 1 - m1 * eCosE;
  605.         final double m3  = (u * ey - v * ex) + eSinE * beta * (u * ex + v * ey);
  606.         final double m4  = -sle + cle * eSinE * beta;
  607.         final double m5  = cle + sle * eSinE * beta;
  608.         fillHalfRow((2 * m3 / r + aOr * eSinE + m1 * eSinE * (1 + m1 - (1 + aOr) * m2) / epsilon) / r2, position,
  609.                     (m1 * m2 / epsilon - 1) * oOsqrtMuA, velocity,
  610.                     m4, dexP, m5, deyP, -sle / a, duP, cle / a, dvP,
  611.                     jacobian[5], 0);
  612.         fillHalfRow((m1 * m2 / epsilon - 1) * oOsqrtMuA, position,
  613.                     (2 * m3 + eSinE * a + m1 * eSinE * r * (eCosE * beta * 2 - aOr * m2) / epsilon) / mu, velocity,
  614.                     m4, dexV, m5, deyV, -sle / a, duV, cle / a, dvV,
  615.                     jacobian[5], 3);

  616.         return jacobian;

  617.     }

  618.     /** {@inheritDoc} */
  619.     protected double[][] computeJacobianEccentricWrtCartesian() {

  620.         // start by computing the Jacobian with mean angle
  621.         final double[][] jacobian = computeJacobianMeanWrtCartesian();

  622.         // Differentiating the Kepler equation aM = aE - ex sin aE + ey cos aE leads to:
  623.         // daM = (1 - ex cos aE - ey sin aE) dE - sin aE dex + cos aE dey
  624.         // which is inverted and rewritten as:
  625.         // daE = a/r daM + sin aE a/r dex - cos aE a/r dey
  626.         final double alphaE = getAlphaE();
  627.         final double cosAe  = FastMath.cos(alphaE);
  628.         final double sinAe  = FastMath.sin(alphaE);
  629.         final double aOr    = 1 / (1 - ex * cosAe - ey * sinAe);

  630.         // update longitude row
  631.         final double[] rowEx = jacobian[1];
  632.         final double[] rowEy = jacobian[2];
  633.         final double[] rowL  = jacobian[5];
  634.         for (int j = 0; j < 6; ++j) {
  635.             rowL[j] = aOr * (rowL[j] + sinAe * rowEx[j] - cosAe * rowEy[j]);
  636.         }

  637.         return jacobian;

  638.     }

  639.     /** {@inheritDoc} */
  640.     protected double[][] computeJacobianTrueWrtCartesian() {

  641.         // start by computing the Jacobian with eccentric angle
  642.         final double[][] jacobian = computeJacobianEccentricWrtCartesian();

  643.         // Differentiating the eccentric latitude equation
  644.         // tan((aV - aE)/2) = [ex sin aE - ey cos aE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos aE - ey sin aE]
  645.         // leads to
  646.         // cT (daV - daE) = cE daE + cX dex + cY dey
  647.         // with
  648.         // cT = [d^2 + (ex sin aE - ey cos aE)^2] / 2
  649.         // d  = 1 + sqrt(1-ex^2-ey^2) - ex cos aE - ey sin aE
  650.         // cE = (ex cos aE + ey sin aE) (sqrt(1-ex^2-ey^2) + 1) - ex^2 - ey^2
  651.         // cX =  sin aE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  652.         // cY = -cos aE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin aE - ey cos aE) / sqrt(1-ex^2-ey^2)
  653.         // which can be solved to find the differential of the true latitude
  654.         // daV = (cT + cE) / cT daE + cX / cT deX + cY / cT deX
  655.         final double alphaE    = getAlphaE();
  656.         final double cosAe     = FastMath.cos(alphaE);
  657.         final double sinAe     = FastMath.sin(alphaE);
  658.         final double eSinE     = ex * sinAe - ey * cosAe;
  659.         final double ecosE     = ex * cosAe + ey * sinAe;
  660.         final double e2        = ex * ex + ey * ey;
  661.         final double epsilon   = FastMath.sqrt(1 - e2);
  662.         final double onePeps   = 1 + epsilon;
  663.         final double d         = onePeps - ecosE;
  664.         final double cT        = (d * d + eSinE * eSinE) / 2;
  665.         final double cE        = ecosE * onePeps - e2;
  666.         final double cX        = ex * eSinE / epsilon - ey + sinAe * onePeps;
  667.         final double cY        = ey * eSinE / epsilon + ex - cosAe * onePeps;
  668.         final double factorLe  = (cT + cE) / cT;
  669.         final double factorEx  = cX / cT;
  670.         final double factorEy  = cY / cT;

  671.         // update latitude row
  672.         final double[] rowEx = jacobian[1];
  673.         final double[] rowEy = jacobian[2];
  674.         final double[] rowA = jacobian[5];
  675.         for (int j = 0; j < 6; ++j) {
  676.             rowA[j] = factorLe * rowA[j] + factorEx * rowEx[j] + factorEy * rowEy[j];
  677.         }

  678.         return jacobian;

  679.     }

  680.     /** {@inheritDoc} */
  681.     public void addKeplerContribution(final PositionAngle type, final double gm,
  682.                                       final double[] pDot) {
  683.         final double oMe2;
  684.         final double ksi;
  685.         final double n = FastMath.sqrt(gm / a) / a;
  686.         switch (type) {
  687.         case MEAN :
  688.             pDot[5] += n;
  689.             break;
  690.         case ECCENTRIC :
  691.             oMe2  = 1 - ex * ex - ey * ey;
  692.             ksi   = 1 + ex * FastMath.cos(alphaV) + ey * FastMath.sin(alphaV);
  693.             pDot[5] += n * ksi / oMe2;
  694.             break;
  695.         case TRUE :
  696.             oMe2  = 1 - ex * ex - ey * ey;
  697.             ksi   = 1 + ex * FastMath.cos(alphaV) + ey * FastMath.sin(alphaV);
  698.             pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2));
  699.             break;
  700.         default :
  701.             throw OrekitException.createInternalError(null);
  702.         }
  703.     }

  704.     /**  Returns a string representation of this Orbit object.
  705.      * @return a string representation of this object
  706.      */
  707.     public String toString() {
  708.         return new StringBuffer().append("circular parameters: ").append('{').
  709.                                   append("a: ").append(a).
  710.                                   append(", ex: ").append(ex).append(", ey: ").append(ey).
  711.                                   append(", i: ").append(FastMath.toDegrees(i)).
  712.                                   append(", raan: ").append(FastMath.toDegrees(raan)).
  713.                                   append(", alphaV: ").append(FastMath.toDegrees(alphaV)).
  714.                                   append(";}").toString();
  715.     }

  716. }