EcksteinHechlerPropagator.java

  1. /* Copyright 2002-2016 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.propagation.analytical;

  18. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.SortedSet;

  23. import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
  24. import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
  25. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  26. import org.apache.commons.math3.util.FastMath;
  27. import org.apache.commons.math3.util.MathUtils;
  28. import org.orekit.attitudes.AttitudeProvider;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitInternalError;
  31. import org.orekit.errors.OrekitMessages;
  32. import org.orekit.errors.PropagationException;
  33. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  34. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  35. import org.orekit.orbits.CartesianOrbit;
  36. import org.orekit.orbits.CircularOrbit;
  37. import org.orekit.orbits.Orbit;
  38. import org.orekit.orbits.OrbitType;
  39. import org.orekit.orbits.PositionAngle;
  40. import org.orekit.propagation.AdditionalStateProvider;
  41. import org.orekit.propagation.SpacecraftState;
  42. import org.orekit.time.AbsoluteDate;
  43. import org.orekit.utils.TimeSpanMap;
  44. import org.orekit.utils.TimeStampedPVCoordinates;

  45. /** This class propagates a {@link org.orekit.propagation.SpacecraftState}
  46.  *  using the analytical Eckstein-Hechler model.
  47.  * <p>The Eckstein-Hechler model is suited for near circular orbits
  48.  * (e < 0.1, with poor accuracy between 0.005 and 0.1) and inclination
  49.  * neither equatorial (direct or retrograde) nor critical (direct or
  50.  * retrograde).</p>
  51.  * <p>
  52.  * Note that before version 7.0, there was a large inconsistency in the generated
  53.  * orbits, and it was fixed as of version 7.0 of Orekit, with a visible side effect.
  54.  * The problems is that if the circular parameters produced by the Eckstein-Hechler
  55.  * model are used to build an orbit considered to be osculating, the velocity deduced
  56.  * from this orbit was <em>inconsistent with the position evolution</em>! The reason is
  57.  * that the model includes non-Keplerian effects but it does not include a corresponding
  58.  * circular/Cartesian conversion. As a consequence, all subsequent computation involving
  59.  * velocity were wrong. This includes attitude modes like yaw compensation and Doppler
  60.  * effect. As this effect was considered serious enough and as accurate velocities were
  61.  * considered important, the propagator now generates {@link CartesianOrbit Cartesian
  62.  * orbits} which are built in a special way to ensure consistency throughout propagation.
  63.  * A side effect is that if circular parameters are rebuilt by user from these propagated
  64.  * Cartesian orbit, the circular parameters will generally <em>not</em> match the initial
  65.  * orbit (differences in semi-major axis can exceed 120 m). The position however <em>will</em>
  66.  * match to sub-micrometer level, and this position will be identical to the positions
  67.  * that were generated by previous versions (in other words, the internals of the models
  68.  * have not been changed, only the output parameters have been changed). The correctness
  69.  * of the initialization has been assessed and is good, as it allows the subsequent orbit
  70.  * to remain close to a numerical reference orbit.
  71.  * </p>
  72.  * <p>
  73.  * If users need a more definitive initialization of an Eckstein-Hechler propagator, they
  74.  * should consider using a {@link org.orekit.propagation.conversion.PropagatorConverter
  75.  * propagator converter} to initialize their Eckstein-Hechler propagator using a complete
  76.  * sample instead of just a single initial orbit.
  77.  * </p>
  78.  * @see Orbit
  79.  * @author Guylaine Prat
  80.  */
  81. public class EcksteinHechlerPropagator extends AbstractAnalyticalPropagator implements Serializable {

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

  84.     /** Initial Eckstein-Hechler model. */
  85.     private EHModel initialModel;

  86.     /** All models. */
  87.     private transient TimeSpanMap<EHModel> models;

  88.     /** Reference radius of the central body attraction model (m). */
  89.     private double referenceRadius;

  90.     /** Central attraction coefficient (m³/s²). */
  91.     private double mu;

  92.     /** Un-normalized zonal coefficients. */
  93.     private double[] ck0;

  94.     /** Build a propagator from orbit and potential provider.
  95.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  96.      * @param initialOrbit initial orbit
  97.      * @param provider for un-normalized zonal coefficients
  98.      * @exception OrekitException if the zonal coefficients cannot be retrieved
  99.      * @exception PropagationException if the mean parameters cannot be computed
  100.      */
  101.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  102.                                      final UnnormalizedSphericalHarmonicsProvider provider)
  103.         throws PropagationException, OrekitException {
  104.         this(initialOrbit, DEFAULT_LAW, DEFAULT_MASS, provider,
  105.              provider.onDate(initialOrbit.getDate()));
  106.     }

  107.     /**
  108.      * Private helper constructor.
  109.      * @param initialOrbit initial orbit
  110.      * @param attitude attitude provider
  111.      * @param mass spacecraft mass
  112.      * @param provider for un-normalized zonal coefficients
  113.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  114.      * @exception OrekitException if the zonal coefficients cannot be retrieved
  115.      */
  116.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  117.                                      final AttitudeProvider attitude,
  118.                                      final double mass,
  119.                                      final UnnormalizedSphericalHarmonicsProvider provider,
  120.                                      final UnnormalizedSphericalHarmonics harmonics)
  121.         throws OrekitException {
  122.         this(initialOrbit, attitude, mass, provider.getAe(), provider.getMu(),
  123.              harmonics.getUnnormalizedCnm(2, 0),
  124.              harmonics.getUnnormalizedCnm(3, 0),
  125.              harmonics.getUnnormalizedCnm(4, 0),
  126.              harmonics.getUnnormalizedCnm(5, 0),
  127.              harmonics.getUnnormalizedCnm(6, 0));
  128.     }

  129.     /** Build a propagator from orbit and potential.
  130.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  131.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  132.      * are related to both the normalized coefficients
  133.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  134.      *  and the J<sub>n</sub> one as follows:</p>
  135.      * <pre>
  136.      *   C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  137.      *                      <span style="text-decoration: overline">C</span><sub>n,0</sub>
  138.      *   C<sub>n,0</sub> = -J<sub>n</sub>
  139.      * </pre>
  140.      * @param initialOrbit initial orbit
  141.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  142.      * @param mu central attraction coefficient (m³/s²)
  143.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  144.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  145.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  146.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  147.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  148.      * @exception PropagationException if the mean parameters cannot be computed
  149.      * @see org.orekit.utils.Constants
  150.      */
  151.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  152.                                      final double referenceRadius, final double mu,
  153.                                      final double c20, final double c30, final double c40,
  154.                                      final double c50, final double c60)
  155.         throws PropagationException {
  156.         this(initialOrbit, DEFAULT_LAW, DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60);
  157.     }

  158.     /** Build a propagator from orbit, mass and potential provider.
  159.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  160.      * @param initialOrbit initial orbit
  161.      * @param mass spacecraft mass
  162.      * @param provider for un-normalized zonal coefficients
  163.      * @exception OrekitException if the zonal coefficients cannot be retrieved
  164.      * @exception PropagationException if the mean parameters cannot be computed
  165.      */
  166.     public EcksteinHechlerPropagator(final Orbit initialOrbit, final double mass,
  167.                                      final UnnormalizedSphericalHarmonicsProvider provider)
  168.         throws PropagationException, OrekitException {
  169.         this(initialOrbit, DEFAULT_LAW, mass, provider, provider.onDate(initialOrbit.getDate()));
  170.     }

  171.     /** Build a propagator from orbit, mass and potential.
  172.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  173.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  174.      * are related to both the normalized coefficients
  175.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  176.      *  and the J<sub>n</sub> one as follows:</p>
  177.      * <pre>
  178.      *   C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  179.      *                      <span style="text-decoration: overline">C</span><sub>n,0</sub>
  180.      *   C<sub>n,0</sub> = -J<sub>n</sub>
  181.      * </pre>
  182.      * @param initialOrbit initial orbit
  183.      * @param mass spacecraft mass
  184.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  185.      * @param mu central attraction coefficient (m³/s²)
  186.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  187.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  188.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  189.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  190.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  191.      * @exception PropagationException if the mean parameters cannot be computed
  192.      */
  193.     public EcksteinHechlerPropagator(final Orbit initialOrbit, final double mass,
  194.                                      final double referenceRadius, final double mu,
  195.                                      final double c20, final double c30, final double c40,
  196.                                      final double c50, final double c60)
  197.         throws PropagationException {
  198.         this(initialOrbit, DEFAULT_LAW, mass, referenceRadius, mu, c20, c30, c40, c50, c60);
  199.     }

  200.     /** Build a propagator from orbit, attitude provider and potential provider.
  201.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  202.      * @param initialOrbit initial orbit
  203.      * @param attitudeProv attitude provider
  204.      * @param provider for un-normalized zonal coefficients
  205.      * @exception OrekitException if the zonal coefficients cannot be retrieved
  206.      * @exception PropagationException if the mean parameters cannot be computed
  207.      */
  208.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  209.                                      final AttitudeProvider attitudeProv,
  210.                                      final UnnormalizedSphericalHarmonicsProvider provider)
  211.         throws PropagationException, OrekitException {
  212.         this(initialOrbit, attitudeProv, DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()));
  213.     }

  214.     /** Build a propagator from orbit, attitude provider and potential.
  215.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  216.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  217.      * are related to both the normalized coefficients
  218.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  219.      *  and the J<sub>n</sub> one as follows:</p>
  220.      * <pre>
  221.      *   C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  222.      *                     <span style="text-decoration: overline">C</span><sub>n,0</sub>
  223.      *   C<sub>n,0</sub> = -J<sub>n</sub>
  224.      * </pre>
  225.      * @param initialOrbit initial orbit
  226.      * @param attitudeProv attitude provider
  227.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  228.      * @param mu central attraction coefficient (m³/s²)
  229.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  230.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  231.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  232.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  233.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  234.      * @exception PropagationException if the mean parameters cannot be computed
  235.      */
  236.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  237.                                      final AttitudeProvider attitudeProv,
  238.                                      final double referenceRadius, final double mu,
  239.                                      final double c20, final double c30, final double c40,
  240.                                      final double c50, final double c60)
  241.         throws PropagationException {
  242.         this(initialOrbit, attitudeProv, DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60);
  243.     }

  244.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  245.      * @param initialOrbit initial orbit
  246.      * @param attitudeProv attitude provider
  247.      * @param mass spacecraft mass
  248.      * @param provider for un-normalized zonal coefficients
  249.      * @exception OrekitException if the zonal coefficients cannot be retrieved
  250.      * @exception PropagationException if the mean parameters cannot be computed
  251.      */
  252.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  253.                                      final AttitudeProvider attitudeProv,
  254.                                      final double mass,
  255.                                      final UnnormalizedSphericalHarmonicsProvider provider)
  256.         throws PropagationException, OrekitException {
  257.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate()));
  258.     }

  259.     /** Build a propagator from orbit, attitude provider, mass and potential.
  260.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  261.      * are related to both the normalized coefficients
  262.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  263.      *  and the J<sub>n</sub> one as follows:</p>
  264.      * <pre>
  265.      *   C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  266.      *                      <span style="text-decoration: overline">C</span><sub>n,0</sub>
  267.      *   C<sub>n,0</sub> = -J<sub>n</sub>
  268.      * </pre>
  269.      * @param initialOrbit initial orbit
  270.      * @param attitudeProv attitude provider
  271.      * @param mass spacecraft mass
  272.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  273.      * @param mu central attraction coefficient (m³/s²)
  274.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  275.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  276.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  277.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  278.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  279.      * @exception PropagationException if the mean parameters cannot be computed
  280.      */
  281.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  282.                                      final AttitudeProvider attitudeProv,
  283.                                      final double mass,
  284.                                      final double referenceRadius, final double mu,
  285.                                      final double c20, final double c30, final double c40,
  286.                                      final double c50, final double c60)
  287.         throws PropagationException {

  288.         super(attitudeProv);

  289.         try {

  290.             // store model coefficients
  291.             this.referenceRadius = referenceRadius;
  292.             this.mu  = mu;
  293.             this.ck0 = new double[] {
  294.                 0.0, 0.0, c20, c30, c40, c50, c60
  295.             };

  296.             // compute mean parameters
  297.             // transform into circular adapted parameters used by the Eckstein-Hechler model
  298.             resetInitialState(new SpacecraftState(initialOrbit,
  299.                                                   attitudeProv.getAttitude(initialOrbit,
  300.                                                                            initialOrbit.getDate(),
  301.                                                                            initialOrbit.getFrame()),
  302.                                                   mass));

  303.         } catch (OrekitException oe) {
  304.             throw new PropagationException(oe);
  305.         }
  306.     }

  307.     /** {@inheritDoc} */
  308.     public void resetInitialState(final SpacecraftState state)
  309.         throws PropagationException {
  310.         super.resetInitialState(state);
  311.         this.initialModel = computeMeanParameters((CircularOrbit) OrbitType.CIRCULAR.convertType(state.getOrbit()),
  312.                                                   state.getMass());
  313.         this.models       = new TimeSpanMap<EHModel>(initialModel);
  314.     }

  315.     /** {@inheritDoc} */
  316.     protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
  317.         throws PropagationException {
  318.         super.resetIntermediateState(state, forward);
  319.         final EHModel newModel = computeMeanParameters((CircularOrbit) OrbitType.CIRCULAR.convertType(state.getOrbit()),
  320.                                                        state.getMass());
  321.         if (forward) {
  322.             models.addValidAfter(newModel, state.getDate());
  323.         } else {
  324.             models.addValidBefore(newModel, state.getDate());
  325.         }
  326.     }

  327.     /** Compute mean parameters according to the Eckstein-Hechler analytical model.
  328.      * @param osculating osculating orbit
  329.      * @param mass constant mass
  330.      * @return Eckstein-Hechler mean model
  331.      * @exception PropagationException if orbit goes outside of supported range
  332.      * (trajectory inside the Brillouin sphere, too eccentric, equatorial, critical
  333.      * inclination) or if convergence cannot be reached
  334.      */
  335.     private EHModel computeMeanParameters(final CircularOrbit osculating, final double mass)
  336.         throws PropagationException {

  337.         // sanity check
  338.         if (osculating.getA() < referenceRadius) {
  339.             throw new PropagationException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE,
  340.                                            osculating.getA());
  341.         }

  342.         // rough initialization of the mean parameters
  343.         EHModel current = new EHModel(osculating, mass, referenceRadius, mu, ck0);

  344.         // threshold for each parameter
  345.         final double epsilon         = 1.0e-13;
  346.         final double thresholdA      = epsilon * (1 + FastMath.abs(current.mean.getA()));
  347.         final double thresholdE      = epsilon * (1 + current.mean.getE());
  348.         final double thresholdAngles = epsilon * FastMath.PI;

  349.         int i = 0;
  350.         while (i++ < 100) {

  351.             // recompute the osculating parameters from the current mean parameters
  352.             final DerivativeStructure[] parameters = current.propagateParameters(current.mean.getDate());

  353.             // adapted parameters residuals
  354.             final double deltaA      = osculating.getA()          - parameters[0].getValue();
  355.             final double deltaEx     = osculating.getCircularEx() - parameters[1].getValue();
  356.             final double deltaEy     = osculating.getCircularEy() - parameters[2].getValue();
  357.             final double deltaI      = osculating.getI()          - parameters[3].getValue();
  358.             final double deltaRAAN   = MathUtils.normalizeAngle(osculating.getRightAscensionOfAscendingNode() -
  359.                                                                 parameters[4].getValue(),
  360.                                                                 0.0);
  361.             final double deltaAlphaM = MathUtils.normalizeAngle(osculating.getAlphaM() - parameters[5].getValue(), 0.0);

  362.             // update mean parameters
  363.             current = new EHModel(new CircularOrbit(current.mean.getA()          + deltaA,
  364.                                                     current.mean.getCircularEx() + deltaEx,
  365.                                                     current.mean.getCircularEy() + deltaEy,
  366.                                                     current.mean.getI()          + deltaI,
  367.                                                     current.mean.getRightAscensionOfAscendingNode() + deltaRAAN,
  368.                                                     current.mean.getAlphaM()     + deltaAlphaM,
  369.                                                     PositionAngle.MEAN,
  370.                                                     current.mean.getFrame(),
  371.                                                     current.mean.getDate(), mu),
  372.                                   mass, referenceRadius, mu, ck0);

  373.             // check convergence
  374.             if ((FastMath.abs(deltaA)      < thresholdA) &&
  375.                 (FastMath.abs(deltaEx)     < thresholdE) &&
  376.                 (FastMath.abs(deltaEy)     < thresholdE) &&
  377.                 (FastMath.abs(deltaI)      < thresholdAngles) &&
  378.                 (FastMath.abs(deltaRAAN)   < thresholdAngles) &&
  379.                 (FastMath.abs(deltaAlphaM) < thresholdAngles)) {
  380.                 return current;
  381.             }

  382.         }

  383.         throw new PropagationException(OrekitMessages.UNABLE_TO_COMPUTE_ECKSTEIN_HECHLER_MEAN_PARAMETERS, i);

  384.     }

  385.     /** {@inheritDoc} */
  386.     public CartesianOrbit propagateOrbit(final AbsoluteDate date)
  387.         throws PropagationException {
  388.         // compute Cartesian parameters, taking derivatives into account
  389.         // to make sure velocity and acceleration are consistent
  390.         final EHModel current = models.get(date);
  391.         return new CartesianOrbit(toCartesian(date, current.propagateParameters(date)),
  392.                                   current.mean.getFrame(), mu);
  393.     }

  394.     /** Local class for Eckstein-Hechler model, with fixed mean parameters. */
  395.     private static class EHModel implements Serializable {

  396.         /** Serializable UID. */
  397.         private static final long serialVersionUID = 20160115L;

  398.         /** Mean orbit. */
  399.         private final CircularOrbit mean;

  400.         /** Constant mass. */
  401.         private final double mass;

  402.         // CHECKSTYLE: stop JavadocVariable check

  403.         // preprocessed values
  404.         private final double xnotDot;
  405.         private final double rdpom;
  406.         private final double rdpomp;
  407.         private final double eps1;
  408.         private final double eps2;
  409.         private final double xim;
  410.         private final double ommD;
  411.         private final double rdl;
  412.         private final double aMD;

  413.         private final double kh;
  414.         private final double kl;

  415.         private final double ax1;
  416.         private final double ay1;
  417.         private final double as1;
  418.         private final double ac2;
  419.         private final double axy3;
  420.         private final double as3;
  421.         private final double ac4;
  422.         private final double as5;
  423.         private final double ac6;

  424.         private final double ex1;
  425.         private final double exx2;
  426.         private final double exy2;
  427.         private final double ex3;
  428.         private final double ex4;

  429.         private final double ey1;
  430.         private final double eyx2;
  431.         private final double eyy2;
  432.         private final double ey3;
  433.         private final double ey4;

  434.         private final double rx1;
  435.         private final double ry1;
  436.         private final double r2;
  437.         private final double r3;
  438.         private final double rl;

  439.         private final double iy1;
  440.         private final double ix1;
  441.         private final double i2;
  442.         private final double i3;
  443.         private final double ih;

  444.         private final double lx1;
  445.         private final double ly1;
  446.         private final double l2;
  447.         private final double l3;
  448.         private final double ll;

  449.         // CHECKSTYLE: resume JavadocVariable check

  450.         /** Create a model for specified mean orbit.
  451.          * @param mean mean orbit
  452.          * @param mass constant mass
  453.          * @param referenceRadius reference radius of the central body attraction model (m)
  454.          * @param mu central attraction coefficient (m³/s²)
  455.          * @param ck0 un-normalized zonal coefficients
  456.          * @exception PropagationException if mean orbit is not within model supported domain
  457.          */
  458.         EHModel(final CircularOrbit mean, final double mass,
  459.                 final double referenceRadius, final double mu, final double[] ck0)
  460.             throws PropagationException {

  461.             this.mean            = mean;
  462.             this.mass            = mass;

  463.             // preliminary processing
  464.             double q = referenceRadius / mean.getA();
  465.             double ql = q * q;
  466.             final double g2 = ck0[2] * ql;
  467.             ql *= q;
  468.             final double g3 = ck0[3] * ql;
  469.             ql *= q;
  470.             final double g4 = ck0[4] * ql;
  471.             ql *= q;
  472.             final double g5 = ck0[5] * ql;
  473.             ql *= q;
  474.             final double g6 = ck0[6] * ql;

  475.             final double cosI1 = FastMath.cos(mean.getI());
  476.             final double sinI1 = FastMath.sin(mean.getI());
  477.             final double sinI2 = sinI1 * sinI1;
  478.             final double sinI4 = sinI2 * sinI2;
  479.             final double sinI6 = sinI2 * sinI4;

  480.             if (sinI2 < 1.0e-10) {
  481.                 throw new PropagationException(OrekitMessages.ALMOST_EQUATORIAL_ORBIT,
  482.                                                FastMath.toDegrees(mean.getI()));
  483.             }

  484.             if (FastMath.abs(sinI2 - 4.0 / 5.0) < 1.0e-3) {
  485.                 throw new PropagationException(OrekitMessages.ALMOST_CRITICALLY_INCLINED_ORBIT,
  486.                                                FastMath.toDegrees(mean.getI()));
  487.             }

  488.             if (mean.getE() > 0.1) {
  489.                 // if 0.005 < e < 0.1 no error is triggered, but accuracy is poor
  490.                 throw new PropagationException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL,
  491.                                                mean.getE());
  492.             }

  493.             xnotDot = FastMath.sqrt(mu / mean.getA()) / mean.getA();

  494.             rdpom = -0.75 * g2 * (4.0 - 5.0 * sinI2);
  495.             rdpomp = 7.5 * g4 * (1.0 - 31.0 / 8.0 * sinI2 + 49.0 / 16.0 * sinI4) -
  496.                     13.125 * g6 * (1.0 - 8.0 * sinI2 + 129.0 / 8.0 * sinI4 - 297.0 / 32.0 * sinI6);

  497.             q = 3.0 / (32.0 * rdpom);
  498.             eps1 = q * g4 * sinI2 * (30.0 - 35.0 * sinI2) -
  499.                     175.0 * q * g6 * sinI2 * (1.0 - 3.0 * sinI2 + 2.0625 * sinI4);
  500.             q = 3.0 * sinI1 / (8.0 * rdpom);
  501.             eps2 = q * g3 * (4.0 - 5.0 * sinI2) - q * g5 * (10.0 - 35.0 * sinI2 + 26.25 * sinI4);

  502.             xim = mean.getI();
  503.             ommD = cosI1 * (1.50    * g2 - 2.25 * g2 * g2 * (2.5 - 19.0 / 6.0 * sinI2) +
  504.                             0.9375  * g4 * (7.0 * sinI2 - 4.0) +
  505.                             3.28125 * g6 * (2.0 - 9.0 * sinI2 + 8.25 * sinI4));

  506.             rdl = 1.0 - 1.50 * g2 * (3.0 - 4.0 * sinI2);
  507.             aMD = rdl +
  508.                     2.25 * g2 * g2 * (9.0 - 263.0 / 12.0 * sinI2 + 341.0 / 24.0 * sinI4) +
  509.                     15.0 / 16.0 * g4 * (8.0 - 31.0 * sinI2 + 24.5 * sinI4) +
  510.                     105.0 / 32.0 * g6 * (-10.0 / 3.0 + 25.0 * sinI2 - 48.75 * sinI4 + 27.5 * sinI6);

  511.             final double qq = -1.5 * g2 / rdl;
  512.             final double qA   = 0.75 * g2 * g2 * sinI2;
  513.             final double qB   = 0.25 * g4 * sinI2;
  514.             final double qC   = 105.0 / 16.0 * g6 * sinI2;
  515.             final double qD   = -0.75 * g3 * sinI1;
  516.             final double qE   = 3.75 * g5 * sinI1;
  517.             kh = 0.375 / rdpom;
  518.             kl = kh / sinI1;

  519.             ax1 = qq * (2.0 - 3.5 * sinI2);
  520.             ay1 = qq * (2.0 - 2.5 * sinI2);
  521.             as1 = qD * (4.0 - 5.0 * sinI2) +
  522.                   qE * (2.625 * sinI4 - 3.5 * sinI2 + 1.0);
  523.             ac2 = qq * sinI2 +
  524.                   qA * 7.0 * (2.0 - 3.0 * sinI2) +
  525.                   qB * (15.0 - 17.5 * sinI2) +
  526.                   qC * (3.0 * sinI2 - 1.0 - 33.0 / 16.0 * sinI4);
  527.             axy3 = qq * 3.5 * sinI2;
  528.             as3 = qD * 5.0 / 3.0 * sinI2 +
  529.                   qE * 7.0 / 6.0 * sinI2 * (1.0 - 1.125 * sinI2);
  530.             ac4 = qA * sinI2 +
  531.                   qB * 4.375 * sinI2 +
  532.                   qC * 0.75 * (1.1 * sinI4 - sinI2);

  533.             as5 = qE * 21.0 / 80.0 * sinI4;

  534.             ac6 = qC * -11.0 / 80.0 * sinI4;

  535.             ex1 = qq * (1.0 - 1.25 * sinI2);
  536.             exx2 = qq * 0.5 * (3.0 - 5.0 * sinI2);
  537.             exy2 = qq * (2.0 - 1.5 * sinI2);
  538.             ex3 = qq * 7.0 / 12.0 * sinI2;
  539.             ex4 = qq * 17.0 / 8.0 * sinI2;

  540.             ey1 = qq * (1.0 - 1.75 * sinI2);
  541.             eyx2 = qq * (1.0 - 3.0 * sinI2);
  542.             eyy2 = qq * (2.0 * sinI2 - 1.5);
  543.             ey3 = qq * 7.0 / 12.0 * sinI2;
  544.             ey4 = qq * 17.0 / 8.0 * sinI2;

  545.             q  = -qq * cosI1;
  546.             rx1 =  3.5 * q;
  547.             ry1 = -2.5 * q;
  548.             r2 = -0.5 * q;
  549.             r3 =  7.0 / 6.0 * q;
  550.             rl = g3 * cosI1 * (4.0 - 15.0 * sinI2) -
  551.                  2.5 * g5 * cosI1 * (4.0 - 42.0 * sinI2 + 52.5 * sinI4);

  552.             q = 0.5 * qq * sinI1 * cosI1;
  553.             iy1 =  q;
  554.             ix1 = -q;
  555.             i2 =  q;
  556.             i3 =  q * 7.0 / 3.0;
  557.             ih = -g3 * cosI1 * (4.0 - 5.0 * sinI2) +
  558.                  2.5 * g5 * cosI1 * (4.0 - 14.0 * sinI2 + 10.5 * sinI4);

  559.             lx1 = qq * (7.0 - 77.0 / 8.0 * sinI2);
  560.             ly1 = qq * (55.0 / 8.0 * sinI2 - 7.50);
  561.             l2 = qq * (1.25 * sinI2 - 0.5);
  562.             l3 = qq * (77.0 / 24.0 * sinI2 - 7.0 / 6.0);
  563.             ll = g3 * (53.0 * sinI2 - 4.0 - 57.5 * sinI4) +
  564.                  2.5 * g5 * (4.0 - 96.0 * sinI2 + 269.5 * sinI4 - 183.75 * sinI6);

  565.         }

  566.         /** Extrapolate an orbit up to a specific target date.
  567.          * @param date target date for the orbit
  568.          * @return propagated parameters
  569.          * @exception PropagationException if some parameters are out of bounds
  570.          */
  571.         public DerivativeStructure[] propagateParameters(final AbsoluteDate date)
  572.             throws PropagationException {

  573.             // keplerian evolution
  574.             final DerivativeStructure dt =
  575.                     new DerivativeStructure(1, 2, 0, date.durationFrom(mean.getDate()));
  576.             final DerivativeStructure xnot = dt.multiply(xnotDot);

  577.             // secular effects

  578.             // eccentricity
  579.             final DerivativeStructure x   = xnot.multiply(rdpom + rdpomp);
  580.             final DerivativeStructure cx  = x.cos();
  581.             final DerivativeStructure sx  = x.sin();
  582.             final DerivativeStructure exm = cx.multiply(mean.getCircularEx()).
  583.                                             add(sx.multiply(eps2 - (1.0 - eps1) * mean.getCircularEy()));
  584.             final DerivativeStructure eym = sx.multiply((1.0 + eps1) * mean.getCircularEx()).
  585.                                             add(cx.multiply(mean.getCircularEy() - eps2)).
  586.                                             add(eps2);

  587.             // no secular effect on inclination

  588.             // right ascension of ascending node
  589.             final DerivativeStructure omm =
  590.                     new DerivativeStructure(1, 2,
  591.                                             MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode() + ommD * xnot.getValue(),
  592.                                                                      FastMath.PI),
  593.                                             ommD * xnotDot,
  594.                                             0.0);

  595.             // latitude argument
  596.             final DerivativeStructure xlm =
  597.                     new DerivativeStructure(1, 2,
  598.                                             MathUtils.normalizeAngle(mean.getAlphaM() + aMD * xnot.getValue(), FastMath.PI),
  599.                                             aMD * xnotDot,
  600.                                             0.0);

  601.             // periodical terms
  602.             final DerivativeStructure cl1 = xlm.cos();
  603.             final DerivativeStructure sl1 = xlm.sin();
  604.             final DerivativeStructure cl2 = cl1.multiply(cl1).subtract(sl1.multiply(sl1));
  605.             final DerivativeStructure sl2 = cl1.multiply(sl1).add(sl1.multiply(cl1));
  606.             final DerivativeStructure cl3 = cl2.multiply(cl1).subtract(sl2.multiply(sl1));
  607.             final DerivativeStructure sl3 = cl2.multiply(sl1).add(sl2.multiply(cl1));
  608.             final DerivativeStructure cl4 = cl3.multiply(cl1).subtract(sl3.multiply(sl1));
  609.             final DerivativeStructure sl4 = cl3.multiply(sl1).add(sl3.multiply(cl1));
  610.             final DerivativeStructure cl5 = cl4.multiply(cl1).subtract(sl4.multiply(sl1));
  611.             final DerivativeStructure sl5 = cl4.multiply(sl1).add(sl4.multiply(cl1));
  612.             final DerivativeStructure cl6 = cl5.multiply(cl1).subtract(sl5.multiply(sl1));

  613.             final DerivativeStructure qh  = eym.subtract(eps2).multiply(kh);
  614.             final DerivativeStructure ql  = exm.multiply(kl);

  615.             final DerivativeStructure exmCl1 = exm.multiply(cl1);
  616.             final DerivativeStructure exmSl1 = exm.multiply(sl1);
  617.             final DerivativeStructure eymCl1 = eym.multiply(cl1);
  618.             final DerivativeStructure eymSl1 = eym.multiply(sl1);
  619.             final DerivativeStructure exmCl2 = exm.multiply(cl2);
  620.             final DerivativeStructure exmSl2 = exm.multiply(sl2);
  621.             final DerivativeStructure eymCl2 = eym.multiply(cl2);
  622.             final DerivativeStructure eymSl2 = eym.multiply(sl2);
  623.             final DerivativeStructure exmCl3 = exm.multiply(cl3);
  624.             final DerivativeStructure exmSl3 = exm.multiply(sl3);
  625.             final DerivativeStructure eymCl3 = eym.multiply(cl3);
  626.             final DerivativeStructure eymSl3 = eym.multiply(sl3);
  627.             final DerivativeStructure exmCl4 = exm.multiply(cl4);
  628.             final DerivativeStructure exmSl4 = exm.multiply(sl4);
  629.             final DerivativeStructure eymCl4 = eym.multiply(cl4);
  630.             final DerivativeStructure eymSl4 = eym.multiply(sl4);

  631.             // semi major axis
  632.             final DerivativeStructure rda = exmCl1.multiply(ax1).
  633.                                             add(eymSl1.multiply(ay1)).
  634.                                             add(sl1.multiply(as1)).
  635.                                             add(cl2.multiply(ac2)).
  636.                                             add(exmCl3.add(eymSl3).multiply(axy3)).
  637.                                             add(sl3.multiply(as3)).
  638.                                             add(cl4.multiply(ac4)).
  639.                                             add(sl5.multiply(as5)).
  640.                                             add(cl6.multiply(ac6));

  641.             // eccentricity
  642.             final DerivativeStructure rdex = cl1.multiply(ex1).
  643.                                              add(exmCl2.multiply(exx2)).
  644.                                              add(eymSl2.multiply(exy2)).
  645.                                              add(cl3.multiply(ex3)).
  646.                                              add(exmCl4.add(eymSl4).multiply(ex4));
  647.             final DerivativeStructure rdey = sl1.multiply(ey1).
  648.                                              add(exmSl2.multiply(eyx2)).
  649.                                              add(eymCl2.multiply(eyy2)).
  650.                                              add(sl3.multiply(ey3)).
  651.                                              add(exmSl4.subtract(eymCl4).multiply(ey4));

  652.             // ascending node
  653.             final DerivativeStructure rdom = exmSl1.multiply(rx1).
  654.                                              add(eymCl1.multiply(ry1)).
  655.                                              add(sl2.multiply(r2)).
  656.                                              add(eymCl3.subtract(exmSl3).multiply(r3)).
  657.                                              add(ql.multiply(rl));

  658.             // inclination
  659.             final DerivativeStructure rdxi = eymSl1.multiply(iy1).
  660.                                              add(exmCl1.multiply(ix1)).
  661.                                              add(cl2.multiply(i2)).
  662.                                              add(exmCl3.add(eymSl3).multiply(i3)).
  663.                                              add(qh.multiply(ih));

  664.             // latitude argument
  665.             final DerivativeStructure rdxl = exmSl1.multiply(lx1).
  666.                                              add(eymCl1.multiply(ly1)).
  667.                                              add(sl2.multiply(l2)).
  668.                                              add(exmSl3.subtract(eymCl3).multiply(l3)).
  669.                                              add(ql.multiply(ll));

  670.             // osculating parameters
  671.             return new DerivativeStructure[] {
  672.                 rda.add(1.0).multiply(mean.getA()),
  673.                 rdex.add(exm),
  674.                 rdey.add(eym),
  675.                 rdxi.add(xim),
  676.                 rdom.add(omm),
  677.                 rdxl.add(xlm)
  678.             };

  679.         }

  680.     }

  681.     /** Convert circular parameters <em>with derivatives</em> to Cartesian coordinates.
  682.      * @param date date of the orbital parameters
  683.      * @param parameters circular parameters (a, ex, ey, i, raan, alphaM)
  684.      * @return Cartesian coordinates consistent with values and derivatives
  685.      */
  686.     private TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final DerivativeStructure[] parameters) {

  687.         // evaluate coordinates in the orbit canonical reference frame
  688.         final DerivativeStructure cosOmega = parameters[4].cos();
  689.         final DerivativeStructure sinOmega = parameters[4].sin();
  690.         final DerivativeStructure cosI     = parameters[3].cos();
  691.         final DerivativeStructure sinI     = parameters[3].sin();
  692.         final DerivativeStructure alphaE   = meanToEccentric(parameters[5], parameters[1], parameters[2]);
  693.         final DerivativeStructure cosAE    = alphaE.cos();
  694.         final DerivativeStructure sinAE    = alphaE.sin();
  695.         final DerivativeStructure ex2      = parameters[1].multiply(parameters[1]);
  696.         final DerivativeStructure ey2      = parameters[2].multiply(parameters[2]);
  697.         final DerivativeStructure exy      = parameters[1].multiply(parameters[2]);
  698.         final DerivativeStructure q        = ex2.add(ey2).subtract(1).negate().sqrt();
  699.         final DerivativeStructure beta     = q.add(1).reciprocal();
  700.         final DerivativeStructure bx2      = beta.multiply(ex2);
  701.         final DerivativeStructure by2      = beta.multiply(ey2);
  702.         final DerivativeStructure bxy      = beta.multiply(exy);
  703.         final DerivativeStructure u        = bxy.multiply(sinAE).subtract(parameters[1].add(by2.subtract(1).multiply(cosAE)));
  704.         final DerivativeStructure v        = bxy.multiply(cosAE).subtract(parameters[2].add(bx2.subtract(1).multiply(sinAE)));
  705.         final DerivativeStructure x        = parameters[0].multiply(u);
  706.         final DerivativeStructure y        = parameters[0].multiply(v);

  707.         // canonical orbit reference frame
  708.         final FieldVector3D<DerivativeStructure> p =
  709.                 new FieldVector3D<DerivativeStructure>(x.multiply(cosOmega).subtract(y.multiply(cosI.multiply(sinOmega))),
  710.                                                        x.multiply(sinOmega).add(y.multiply(cosI.multiply(cosOmega))),
  711.                                                        y.multiply(sinI));

  712.         // dispatch derivatives
  713.         final Vector3D p0 = new Vector3D(p.getX().getValue(),
  714.                                          p.getY().getValue(),
  715.                                          p.getZ().getValue());
  716.         final Vector3D p1 = new Vector3D(p.getX().getPartialDerivative(1),
  717.                                          p.getY().getPartialDerivative(1),
  718.                                          p.getZ().getPartialDerivative(1));
  719.         final Vector3D p2 = new Vector3D(p.getX().getPartialDerivative(2),
  720.                                          p.getY().getPartialDerivative(2),
  721.                                          p.getZ().getPartialDerivative(2));
  722.         return new TimeStampedPVCoordinates(date, p0, p1, p2);

  723.     }

  724.     /** Computes the eccentric latitude argument from the mean latitude argument.
  725.      * @param alphaM = M + Ω mean latitude argument (rad)
  726.      * @param ex e cos(Ω), first component of circular eccentricity vector
  727.      * @param ey e sin(Ω), second component of circular eccentricity vector
  728.      * @return the eccentric latitude argument.
  729.      */
  730.     private DerivativeStructure meanToEccentric(final DerivativeStructure alphaM,
  731.                                                 final DerivativeStructure ex,
  732.                                                 final DerivativeStructure ey) {
  733.         // Generalization of Kepler equation to circular parameters
  734.         // with alphaE = PA + E and
  735.         //      alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE)
  736.         DerivativeStructure alphaE        = alphaM;
  737.         DerivativeStructure shift         = alphaM.getField().getZero();
  738.         DerivativeStructure alphaEMalphaM = alphaM.getField().getZero();
  739.         DerivativeStructure cosAlphaE     = alphaE.cos();
  740.         DerivativeStructure sinAlphaE     = alphaE.sin();
  741.         int                 iter          = 0;
  742.         do {
  743.             final DerivativeStructure f2 = ex.multiply(sinAlphaE).subtract(ey.multiply(cosAlphaE));
  744.             final DerivativeStructure f1 = alphaM.getField().getOne().subtract(ex.multiply(cosAlphaE)).subtract(ey.multiply(sinAlphaE));
  745.             final DerivativeStructure f0 = alphaEMalphaM.subtract(f2);

  746.             final DerivativeStructure f12 = f1.multiply(2);
  747.             shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2)));

  748.             alphaEMalphaM  = alphaEMalphaM.subtract(shift);
  749.             alphaE         = alphaM.add(alphaEMalphaM);
  750.             cosAlphaE      = alphaE.cos();
  751.             sinAlphaE      = alphaE.sin();

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

  753.         return alphaE;

  754.     }

  755.     /** {@inheritDoc} */
  756.     protected double getMass(final AbsoluteDate date) {
  757.         return models.get(date).mass;
  758.     }

  759.     /** Replace the instance with a data transfer object for serialization.
  760.      * @return data transfer object that will be serialized
  761.      * @exception NotSerializableException if an additional state provider is not serializable
  762.      */
  763.     private Object writeReplace() throws NotSerializableException {
  764.         try {
  765.             // managed states providers
  766.             final List<AdditionalStateProvider> serializableProviders = new ArrayList<AdditionalStateProvider>();
  767.             for (final AdditionalStateProvider provider : getAdditionalStateProviders()) {
  768.                 if (provider instanceof Serializable) {
  769.                     serializableProviders.add(provider);
  770.                 } else {
  771.                     throw new NotSerializableException(provider.getClass().getName());
  772.                 }
  773.             }

  774.             // states transitions
  775.             final AbsoluteDate[]  transitionDates;
  776.             final CircularOrbit[] allOrbits;
  777.             final double[]        allMasses;
  778.             final SortedSet<TimeSpanMap.Transition<EHModel>> transitions = models.getTransitions();
  779.             if (transitions.size() == 1  && transitions.first().getBefore() == transitions.first().getAfter()) {
  780.                 // the single entry is a dummy one, without a real transition
  781.                 // we ignore it completely
  782.                 transitionDates = null;
  783.                 allOrbits       = null;
  784.                 allMasses       = null;
  785.             } else {
  786.                 transitionDates = new AbsoluteDate[transitions.size()];
  787.                 allOrbits       = new CircularOrbit[transitions.size() + 1];
  788.                 allMasses       = new double[transitions.size() + 1];
  789.                 int i = 0;
  790.                 for (final TimeSpanMap.Transition<EHModel> transition : transitions) {
  791.                     if (i == 0) {
  792.                         // model before the first transition
  793.                         allOrbits[i] = transition.getBefore().mean;
  794.                         allMasses[i] = transition.getBefore().mass;
  795.                     }
  796.                     transitionDates[i] = transition.getDate();
  797.                     allOrbits[++i]     = transition.getAfter().mean;
  798.                     allMasses[i]       = transition.getAfter().mass;
  799.                 }
  800.             }

  801.             return new DataTransferObject(getInitialState().getOrbit(), initialModel.mass,
  802.                                           referenceRadius, mu, ck0, getAttitudeProvider(),
  803.                                           transitionDates, allOrbits, allMasses,
  804.                                           serializableProviders.toArray(new AdditionalStateProvider[serializableProviders.size()]));
  805.         } catch (OrekitException orekitException) {
  806.             // this should never happen
  807.             throw new OrekitInternalError(null);
  808.         }

  809.     }

  810.     /** Internal class used only for serialization. */
  811.     private static class DataTransferObject implements Serializable {

  812.         /** Serializable UID. */
  813.         private static final long serialVersionUID = 20151202L;

  814.         /** Initial orbit. */
  815.         private final Orbit orbit;

  816.         /** Attitude provider. */
  817.         private final AttitudeProvider attitudeProvider;

  818.         /** Mass and gravity field. */
  819.         private double[] g;

  820.         /** Transition dates (may be null). */
  821.         private final AbsoluteDate[] transitionDates;

  822.         /** Orbits before and after transitions (may be null). */
  823.         private final CircularOrbit[] allOrbits;

  824.         /** Masses before and after transitions (may be null). */
  825.         private final double[] allMasses;

  826.         /** Providers for additional states. */
  827.         private final AdditionalStateProvider[] providers;

  828.         /** Simple constructor.
  829.          * @param orbit initial orbit
  830.          * @param mass spacecraft mass
  831.          * @param referenceRadius reference radius of the Earth for the potential model (m)
  832.          * @param mu central attraction coefficient (m³/s²)
  833.          * @param ck0 un-normalized zonal coefficients
  834.          * @param attitudeProvider attitude provider
  835.          * @param transitionDates transition dates (may be null)
  836.          * @param allOrbits orbits before and after transitions (may be null)
  837.          * @param allMasses masses before and after transitions (may be null)
  838.          * @param providers providers for additional states
  839.          */
  840.         DataTransferObject(final Orbit orbit, final double mass,
  841.                            final double referenceRadius, final double mu,
  842.                            final double[] ck0,
  843.                            final AttitudeProvider attitudeProvider,
  844.                            final AbsoluteDate[] transitionDates,
  845.                            final CircularOrbit[] allOrbits,
  846.                            final double[] allMasses,
  847.                            final AdditionalStateProvider[] providers) {
  848.             this.orbit            = orbit;
  849.             this.attitudeProvider = attitudeProvider;
  850.             this.g = new double[] {
  851.                 mass, referenceRadius, mu,
  852.                 ck0[2], ck0[3], ck0[4], ck0[5], ck0[6] // ck0[0] and ck0[1] are both zero so not serialized
  853.             };
  854.             this.transitionDates  = transitionDates;
  855.             this.allOrbits        = allOrbits;
  856.             this.allMasses        = allMasses;
  857.             this.providers        = providers;
  858.         }

  859.         /** Replace the deserialized data transfer object with a {@link EcksteinHechlerPropagator}.
  860.          * @return replacement {@link EcksteinHechlerPropagator}
  861.          */
  862.         private Object readResolve() {
  863.             try {
  864.                 final EcksteinHechlerPropagator propagator =
  865.                                 new EcksteinHechlerPropagator(orbit, attitudeProvider,
  866.                                                               g[0], g[1], g[2],              // mass, referenceRadius, mu
  867.                                                               g[3], g[4], g[5], g[6], g[7]); // c20, c30, c40, c50, c60
  868.                 for (final AdditionalStateProvider provider : providers) {
  869.                     propagator.addAdditionalStateProvider(provider);

  870.                 }
  871.                 if (transitionDates != null) {
  872.                     // override the state transitions
  873.                     final double[] ck0 = new double[] {
  874.                         0, 0, g[3], g[4], g[5], g[6], g[7]
  875.                     };
  876.                     propagator.models = new TimeSpanMap<EHModel>(new EHModel(allOrbits[0], allMasses[0],
  877.                                                                              g[1], g[2], ck0));
  878.                     for (int i = 0; i < transitionDates.length; ++i) {
  879.                         propagator.models.addValidAfter(new EHModel(allOrbits[i + 1], allMasses[i + 1],
  880.                                                                     g[1], g[2], ck0),
  881.                                                         transitionDates[i]);
  882.                     }
  883.                 }

  884.                 return propagator;
  885.             } catch (OrekitException oe) {
  886.                 throw new OrekitInternalError(oe);
  887.             }
  888.         }

  889.     }

  890. }