EcksteinHechlerPropagator.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation.analytical;

  18. import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
  19. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.linear.RealMatrix;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathUtils;
  24. import org.hipparchus.util.SinCos;
  25. import org.orekit.attitudes.AttitudeProvider;
  26. import org.orekit.attitudes.FrameAlignedProvider;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  30. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  31. import org.orekit.orbits.CartesianOrbit;
  32. import org.orekit.orbits.CircularOrbit;
  33. import org.orekit.orbits.Orbit;
  34. import org.orekit.orbits.OrbitType;
  35. import org.orekit.orbits.PositionAngleType;
  36. import org.orekit.propagation.AbstractMatricesHarvester;
  37. import org.orekit.propagation.PropagationType;
  38. import org.orekit.propagation.SpacecraftState;
  39. import org.orekit.propagation.conversion.osc2mean.EcksteinHechlerTheory;
  40. import org.orekit.propagation.conversion.osc2mean.FixedPointConverter;
  41. import org.orekit.propagation.conversion.osc2mean.MeanTheory;
  42. import org.orekit.propagation.conversion.osc2mean.OsculatingToMeanConverter;
  43. import org.orekit.time.AbsoluteDate;
  44. import org.orekit.utils.DoubleArrayDictionary;
  45. import org.orekit.utils.TimeSpanMap;
  46. import org.orekit.utils.TimeStampedPVCoordinates;

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

  84.     /** Default convergence threshold for mean parameters conversion. */
  85.     private static final double EPSILON_DEFAULT = 1.0e-11;

  86.     /** Default value for maxIterations. */
  87.     private static final int MAX_ITERATIONS_DEFAULT = 100;

  88.     /** Initial Eckstein-Hechler model. */
  89.     private EHModel initialModel;

  90.     /** All models. */
  91.     private TimeSpanMap<EHModel> models;

  92.     /** Reference radius of the central body attraction model (m). */
  93.     private final double referenceRadius;

  94.     /** Central attraction coefficient (m³/s²). */
  95.     private final double mu;

  96.     /** Un-normalized zonal coefficients. */
  97.     private final double[] ck0;

  98.     /** Build a propagator from orbit and potential provider.
  99.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  100.      *
  101.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  102.      *
  103.      * @param initialOrbit initial orbit
  104.      * @param provider for un-normalized zonal coefficients
  105.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider,
  106.      * UnnormalizedSphericalHarmonicsProvider)
  107.      * @see #EcksteinHechlerPropagator(Orbit, UnnormalizedSphericalHarmonicsProvider,
  108.      *                                 PropagationType)
  109.      */
  110.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  111.                                      final UnnormalizedSphericalHarmonicsProvider provider) {
  112.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  113.              DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()));
  114.     }

  115.     /**
  116.      * Private helper constructor.
  117.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  118.      * @param initialOrbit initial orbit
  119.      * @param attitude attitude provider
  120.      * @param mass spacecraft mass
  121.      * @param provider for un-normalized zonal coefficients
  122.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  123.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double,
  124.      *                                 UnnormalizedSphericalHarmonicsProvider,
  125.      *                                 UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics,
  126.      *                                 PropagationType)
  127.      */
  128.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  129.                                      final AttitudeProvider attitude,
  130.                                      final double mass,
  131.                                      final UnnormalizedSphericalHarmonicsProvider provider,
  132.                                      final UnnormalizedSphericalHarmonics harmonics) {
  133.         this(initialOrbit, attitude, mass, provider.getAe(), provider.getMu(),
  134.              harmonics.getUnnormalizedCnm(2, 0),
  135.              harmonics.getUnnormalizedCnm(3, 0),
  136.              harmonics.getUnnormalizedCnm(4, 0),
  137.              harmonics.getUnnormalizedCnm(5, 0),
  138.              harmonics.getUnnormalizedCnm(6, 0));
  139.     }

  140.     /** Build a propagator from orbit and potential.
  141.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  142.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  143.      * are related to both the normalized coefficients
  144.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  145.      *  and the J<sub>n</sub> one as follows:</p>
  146.      *
  147.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  148.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  149.      *
  150.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  151.      *
  152.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  153.      *
  154.      * @param initialOrbit initial orbit
  155.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  156.      * @param mu central attraction coefficient (m³/s²)
  157.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  158.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  159.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  160.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  161.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  162.      * @see org.orekit.utils.Constants
  163.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double, double, double,
  164.      * double, double, double, double, double)
  165.      */
  166.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  167.                                      final double referenceRadius,
  168.                                      final double mu,
  169.                                      final double c20,
  170.                                      final double c30,
  171.                                      final double c40,
  172.                                      final double c50,
  173.                                      final double c60) {
  174.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  175.              DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60);
  176.     }

  177.     /** Build a propagator from orbit, mass and potential provider.
  178.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  179.      *
  180.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  181.      *
  182.      * @param initialOrbit initial orbit
  183.      * @param mass spacecraft mass
  184.      * @param provider for un-normalized zonal coefficients
  185.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double,
  186.      * UnnormalizedSphericalHarmonicsProvider)
  187.      */
  188.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  189.                                      final double mass,
  190.                                      final UnnormalizedSphericalHarmonicsProvider provider) {
  191.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  192.              mass, provider, provider.onDate(initialOrbit.getDate()));
  193.     }

  194.     /** Build a propagator from orbit, mass and potential.
  195.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  196.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  197.      * are related to both the normalized coefficients
  198.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  199.      *  and the J<sub>n</sub> one as follows:</p>
  200.      *
  201.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  202.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  203.      *
  204.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  205.      *
  206.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  207.      *
  208.      * @param initialOrbit initial orbit
  209.      * @param mass spacecraft mass
  210.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  211.      * @param mu central attraction coefficient (m³/s²)
  212.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  213.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  214.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  215.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  216.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  217.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double, double, double,
  218.      * double, double, double, double, double)
  219.      */
  220.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  221.                                      final double mass,
  222.                                      final double referenceRadius,
  223.                                      final double mu,
  224.                                      final double c20,
  225.                                      final double c30,
  226.                                      final double c40,
  227.                                      final double c50,
  228.                                      final double c60) {
  229.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  230.              mass, referenceRadius, mu, c20, c30, c40, c50, c60);
  231.     }

  232.     /** Build a propagator from orbit, attitude provider and potential provider.
  233.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  234.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  235.      * @param initialOrbit initial orbit
  236.      * @param attitudeProv attitude provider
  237.      * @param provider for un-normalized zonal coefficients
  238.      */
  239.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  240.                                      final AttitudeProvider attitudeProv,
  241.                                      final UnnormalizedSphericalHarmonicsProvider provider) {
  242.         this(initialOrbit, attitudeProv, DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()));
  243.     }

  244.     /** Build a propagator from orbit, attitude provider and potential.
  245.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  246.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  247.      * are related to both the normalized coefficients
  248.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  249.      *  and the J<sub>n</sub> one as follows:</p>
  250.      *
  251.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  252.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  253.      *
  254.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  255.      *
  256.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  257.      *
  258.      * @param initialOrbit initial orbit
  259.      * @param attitudeProv attitude provider
  260.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  261.      * @param mu central attraction coefficient (m³/s²)
  262.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  263.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  264.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  265.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  266.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  267.      */
  268.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  269.                                      final AttitudeProvider attitudeProv,
  270.                                      final double referenceRadius,
  271.                                      final double mu,
  272.                                      final double c20,
  273.                                      final double c30,
  274.                                      final double c40,
  275.                                      final double c50,
  276.                                      final double c60) {
  277.         this(initialOrbit, attitudeProv, DEFAULT_MASS, referenceRadius, mu, c20, c30, c40, c50, c60);
  278.     }

  279.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  280.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  281.      * @param initialOrbit initial orbit
  282.      * @param attitudeProv attitude provider
  283.      * @param mass spacecraft mass
  284.      * @param provider for un-normalized zonal coefficients
  285.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double,
  286.      *                                 UnnormalizedSphericalHarmonicsProvider, PropagationType)
  287.      */
  288.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  289.                                      final AttitudeProvider attitudeProv,
  290.                                      final double mass,
  291.                                      final UnnormalizedSphericalHarmonicsProvider provider) {
  292.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate()));
  293.     }

  294.     /** Build a propagator from orbit, attitude provider, mass and potential.
  295.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  296.      * are related to both the normalized coefficients
  297.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  298.      *  and the J<sub>n</sub> one as follows:</p>
  299.      *
  300.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  301.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  302.      *
  303.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  304.      *
  305.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  306.      *
  307.      * @param initialOrbit initial orbit
  308.      * @param attitudeProv attitude provider
  309.      * @param mass spacecraft mass
  310.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  311.      * @param mu central attraction coefficient (m³/s²)
  312.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  313.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  314.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  315.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  316.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  317.      * @see #EcksteinHechlerPropagator(Orbit, AttitudeProvider, double, double, double,
  318.      *                                 double, double, double, double, double, PropagationType)
  319.      */
  320.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  321.                                      final AttitudeProvider attitudeProv,
  322.                                      final double mass,
  323.                                      final double referenceRadius,
  324.                                      final double mu,
  325.                                      final double c20,
  326.                                      final double c30,
  327.                                      final double c40,
  328.                                      final double c50,
  329.                                      final double c60) {
  330.         this(initialOrbit, attitudeProv, mass, referenceRadius, mu, c20, c30, c40, c50, c60,
  331.              PropagationType.OSCULATING);
  332.     }


  333.     /** Build a propagator from orbit and potential provider.
  334.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  335.      *
  336.      * <p>Using this constructor, it is possible to define the initial orbit as
  337.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  338.      *
  339.      * @param initialOrbit initial orbit
  340.      * @param provider for un-normalized zonal coefficients
  341.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  342.      * @since 10.2
  343.      */
  344.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  345.                                      final UnnormalizedSphericalHarmonicsProvider provider,
  346.                                      final PropagationType initialType) {
  347.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  348.              DEFAULT_MASS, provider, provider.onDate(initialOrbit.getDate()), initialType);
  349.     }

  350.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  351.      * <p>Using this constructor, it is possible to define the initial orbit as
  352.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  353.      * @param initialOrbit initial orbit
  354.      * @param attitudeProv attitude provider
  355.      * @param mass spacecraft mass
  356.      * @param provider for un-normalized zonal coefficients
  357.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  358.      * @since 10.2
  359.      */
  360.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  361.                                      final AttitudeProvider attitudeProv,
  362.                                      final double mass,
  363.                                      final UnnormalizedSphericalHarmonicsProvider provider,
  364.                                      final PropagationType initialType) {
  365.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate()), initialType);
  366.     }

  367.     /**
  368.      * Private helper constructor.
  369.      * <p>Using this constructor, it is possible to define the initial orbit as
  370.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  371.      * @param initialOrbit initial orbit
  372.      * @param attitude attitude provider
  373.      * @param mass spacecraft mass
  374.      * @param provider for un-normalized zonal coefficients
  375.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  376.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  377.      * @since 10.2
  378.      */
  379.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  380.                                      final AttitudeProvider attitude,
  381.                                      final double mass,
  382.                                      final UnnormalizedSphericalHarmonicsProvider provider,
  383.                                      final UnnormalizedSphericalHarmonics harmonics,
  384.                                      final PropagationType initialType) {
  385.         this(initialOrbit, attitude, mass, provider.getAe(), provider.getMu(),
  386.              harmonics.getUnnormalizedCnm(2, 0),
  387.              harmonics.getUnnormalizedCnm(3, 0),
  388.              harmonics.getUnnormalizedCnm(4, 0),
  389.              harmonics.getUnnormalizedCnm(5, 0),
  390.              harmonics.getUnnormalizedCnm(6, 0),
  391.              initialType);
  392.     }

  393.     /** Build a propagator from orbit, attitude provider, mass and potential.
  394.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  395.      * are related to both the normalized coefficients
  396.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  397.      *  and the J<sub>n</sub> one as follows:</p>
  398.      *
  399.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  400.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  401.      *
  402.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  403.      *
  404.      * <p>Using this constructor, it is possible to define the initial orbit as
  405.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  406.      *
  407.      * @param initialOrbit initial orbit
  408.      * @param attitudeProv attitude provider
  409.      * @param mass spacecraft mass
  410.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  411.      * @param mu central attraction coefficient (m³/s²)
  412.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  413.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  414.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  415.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  416.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  417.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  418.      * @since 10.2
  419.      */
  420.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  421.                                      final AttitudeProvider attitudeProv,
  422.                                      final double mass,
  423.                                      final double referenceRadius,
  424.                                      final double mu,
  425.                                      final double c20,
  426.                                      final double c30,
  427.                                      final double c40,
  428.                                      final double c50,
  429.                                      final double c60,
  430.                                      final PropagationType initialType) {
  431.         this(initialOrbit, attitudeProv, mass, referenceRadius, mu,
  432.              c20, c30, c40, c50, c60, initialType, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
  433.     }

  434.     /** Build a propagator from orbit, attitude provider, mass and potential.
  435.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  436.      * are related to both the normalized coefficients
  437.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  438.      *  and the J<sub>n</sub> one as follows:</p>
  439.      *
  440.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  441.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  442.      *
  443.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  444.      *
  445.      * <p>Using this constructor, it is possible to define the initial orbit as
  446.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  447.      *
  448.      * @param initialOrbit initial orbit
  449.      * @param attitudeProv attitude provider
  450.      * @param mass spacecraft mass
  451.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  452.      * @param mu central attraction coefficient (m³/s²)
  453.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  454.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  455.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  456.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  457.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  458.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  459.      * @param epsilon convergence threshold for mean parameters conversion
  460.      * @param maxIterations maximum iterations for mean parameters conversion
  461.      * @since 11.2
  462.      */
  463.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  464.                                      final AttitudeProvider attitudeProv,
  465.                                      final double mass,
  466.                                      final double referenceRadius,
  467.                                      final double mu,
  468.                                      final double c20,
  469.                                      final double c30,
  470.                                      final double c40,
  471.                                      final double c50,
  472.                                      final double c60,
  473.                                      final PropagationType initialType,
  474.                                      final double epsilon,
  475.                                      final int maxIterations) {
  476.         this(initialOrbit, attitudeProv, mass, referenceRadius, mu,
  477.              c20, c30, c40, c50, c60, initialType,
  478.              new FixedPointConverter(epsilon, maxIterations,
  479.                                      FixedPointConverter.DEFAULT_DAMPING));
  480.     }

  481.     /** Build a propagator from orbit, attitude provider, mass and potential.
  482.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  483.      * are related to both the normalized coefficients
  484.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  485.      *  and the J<sub>n</sub> one as follows:</p>
  486.      *
  487.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  488.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  489.      *
  490.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  491.      *
  492.      * <p>Using this constructor, it is possible to define the initial orbit as
  493.      * a mean Eckstein-Hechler orbit or an osculating one.</p>
  494.      *
  495.      * @param initialOrbit initial orbit
  496.      * @param attitudeProv attitude provider
  497.      * @param mass spacecraft mass
  498.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  499.      * @param mu central attraction coefficient (m³/s²)
  500.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  501.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  502.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  503.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  504.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  505.      * @param initialType initial orbit type (mean Eckstein-Hechler orbit or osculating orbit)
  506.      * @param converter osculating to mean orbit converter
  507.      * @since 13.0
  508.      */
  509.     public EcksteinHechlerPropagator(final Orbit initialOrbit,
  510.                                      final AttitudeProvider attitudeProv,
  511.                                      final double mass,
  512.                                      final double referenceRadius,
  513.                                      final double mu,
  514.                                      final double c20,
  515.                                      final double c30,
  516.                                      final double c40,
  517.                                      final double c50,
  518.                                      final double c60,
  519.                                      final PropagationType initialType,
  520.                                      final OsculatingToMeanConverter converter) {
  521.         super(attitudeProv);

  522.         // store model coefficients
  523.         this.referenceRadius = referenceRadius;
  524.         this.mu  = mu;
  525.         this.ck0 = new double[] {
  526.             0.0, 0.0, c20, c30, c40, c50, c60
  527.         };

  528.         // compute mean parameters if needed
  529.         // transform into circular adapted parameters used by the Eckstein-Hechler model
  530.         resetInitialState(new SpacecraftState(initialOrbit,
  531.                                               attitudeProv.getAttitude(initialOrbit,
  532.                                                                        initialOrbit.getDate(),
  533.                                                                        initialOrbit.getFrame())).withMass(mass),
  534.                           initialType, converter);

  535.     }

  536.     /** Conversion from osculating to mean orbit.
  537.      * <p>
  538.      * Compute mean orbit <b>in a Eckstein-Hechler sense</b>, corresponding to the
  539.      * osculating SpacecraftState in input.
  540.      * </p>
  541.      * <p>
  542.      * Since the osculating orbit is obtained with the computation of
  543.      * short-periodic variation, the resulting output will depend on
  544.      * the gravity field parameterized in input.
  545.      * </p>
  546.      * <p>
  547.      * The computation is done through a fixed-point iteration process.
  548.      * </p>
  549.      * @param osculating osculating orbit to convert
  550.      * @param provider for un-normalized zonal coefficients
  551.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  552.      * @return mean orbit in a Eckstein-Hechler sense
  553.      * @since 11.2
  554.      */
  555.     public static CircularOrbit computeMeanOrbit(final Orbit osculating,
  556.                                                  final UnnormalizedSphericalHarmonicsProvider provider,
  557.                                                  final UnnormalizedSphericalHarmonics harmonics) {
  558.         return computeMeanOrbit(osculating, provider, harmonics, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
  559.     }

  560.     /** Conversion from osculating to mean orbit.
  561.      * <p>
  562.      * Compute mean orbit <b>in a Eckstein-Hechler sense</b>, corresponding to the
  563.      * osculating SpacecraftState in input.
  564.      * </p>
  565.      * <p>
  566.      * Since the osculating orbit is obtained with the computation of
  567.      * short-periodic variation, the resulting output will depend on
  568.      * the gravity field parameterized in input.
  569.      * </p>
  570.      * <p>
  571.      * The computation is done through a fixed-point iteration process.
  572.      * </p>
  573.      * @param osculating osculating orbit to convert
  574.      * @param provider for un-normalized zonal coefficients
  575.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  576.      * @param epsilon convergence threshold for mean parameters conversion
  577.      * @param maxIterations maximum iterations for mean parameters conversion
  578.      * @return mean orbit in a Eckstein-Hechler sense
  579.      * @since 11.2
  580.      */
  581.     public static CircularOrbit computeMeanOrbit(final Orbit osculating,
  582.                                                  final UnnormalizedSphericalHarmonicsProvider provider,
  583.                                                  final UnnormalizedSphericalHarmonics harmonics,
  584.                                                  final double epsilon, final int maxIterations) {
  585.         return computeMeanOrbit(osculating,
  586.                                 provider.getAe(), provider.getMu(),
  587.                                 harmonics.getUnnormalizedCnm(2, 0),
  588.                                 harmonics.getUnnormalizedCnm(3, 0),
  589.                                 harmonics.getUnnormalizedCnm(4, 0),
  590.                                 harmonics.getUnnormalizedCnm(5, 0),
  591.                                 harmonics.getUnnormalizedCnm(6, 0),
  592.                                 epsilon, maxIterations);
  593.     }

  594.     /** Conversion from osculating to mean orbit.
  595.      * <p>
  596.      * Compute mean orbit <b>in a Eckstein-Hechler sense</b>, corresponding to the
  597.      * osculating SpacecraftState in input.
  598.      * </p>
  599.      * <p>
  600.      * Since the osculating orbit is obtained with the computation of
  601.      * short-periodic variation, the resulting output will depend on
  602.      * the gravity field parameterized in input.
  603.      * </p>
  604.      * <p>
  605.      * The computation is done through a fixed-point iteration process.
  606.      * </p>
  607.      * @param osculating osculating orbit to convert
  608.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  609.      * @param mu central attraction coefficient (m³/s²)
  610.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  611.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  612.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  613.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  614.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  615.      * @param epsilon convergence threshold for mean parameters conversion
  616.      * @param maxIterations maximum iterations for mean parameters conversion
  617.      * @return mean orbit in a Eckstein-Hechler sense
  618.      * @since 11.2
  619.      */
  620.     public static CircularOrbit computeMeanOrbit(final Orbit osculating,
  621.                                                  final double referenceRadius,
  622.                                                  final double mu,
  623.                                                  final double c20,
  624.                                                  final double c30,
  625.                                                  final double c40,
  626.                                                  final double c50,
  627.                                                  final double c60,
  628.                                                  final double epsilon,
  629.                                                  final int maxIterations) {
  630.         // Build a fixed-point converter
  631.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  632.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  633.         return computeMeanOrbit(osculating, referenceRadius, mu, c20, c30, c40, c50, c60, converter);
  634.     }

  635.     /** Conversion from osculating to mean orbit.
  636.      * <p>
  637.      * Compute mean orbit <b>in a Eckstein-Hechler sense</b>, corresponding to the
  638.      * osculating SpacecraftState in input.
  639.      * </p>
  640.      * <p>
  641.      * Since the osculating orbit is obtained with the computation of
  642.      * short-periodic variation, the resulting output will depend on
  643.      * the gravity field parameterized in input.
  644.      * </p>
  645.      * <p>
  646.      * The computation is done through a fixed-point iteration process.
  647.      * </p>
  648.      * @param osculating osculating orbit to convert
  649.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  650.      * @param mu central attraction coefficient (m³/s²)
  651.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  652.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  653.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  654.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  655.      * @param c60 un-normalized zonal coefficient (about -5.41e-7 for Earth)
  656.      * @param converter osculating to mean orbit converter
  657.      * @return mean orbit in a Eckstein-Hechler sense
  658.      * @since 13.0
  659.      */
  660.     public static CircularOrbit computeMeanOrbit(final Orbit osculating,
  661.                                                  final double referenceRadius,
  662.                                                  final double mu,
  663.                                                  final double c20,
  664.                                                  final double c30,
  665.                                                  final double c40,
  666.                                                  final double c50,
  667.                                                  final double c60,
  668.                                                  final OsculatingToMeanConverter converter) {
  669.         // Set EH as the mean theory for converting
  670.         final MeanTheory theory = new EcksteinHechlerTheory(referenceRadius, mu,
  671.                                                             c20, c30, c40, c50, c60);
  672.         converter.setMeanTheory(theory);
  673.         return (CircularOrbit) OrbitType.CIRCULAR.convertType(converter.convertToMean(osculating));
  674.     }

  675.     /** {@inheritDoc}
  676.      * <p>The new initial state to consider
  677.      * must be defined with an osculating orbit.</p>
  678.      * @see #resetInitialState(SpacecraftState, PropagationType)
  679.      */
  680.     @Override
  681.     public void resetInitialState(final SpacecraftState state) {
  682.         resetInitialState(state, PropagationType.OSCULATING);
  683.     }

  684.     /** Reset the propagator initial state.
  685.      * @param state new initial state to consider
  686.      * @param stateType mean Eckstein-Hechler orbit or osculating orbit
  687.      * @since 10.2
  688.      */
  689.     public void resetInitialState(final SpacecraftState state,
  690.                                   final PropagationType stateType) {
  691.         resetInitialState(state, stateType, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
  692.     }

  693.     /** Reset the propagator initial state.
  694.      * @param state new initial state to consider
  695.      * @param stateType mean Eckstein-Hechler orbit or osculating orbit
  696.      * @param epsilon convergence threshold for mean parameters conversion
  697.      * @param maxIterations maximum iterations for mean parameters conversion
  698.      * @since 11.2
  699.      */
  700.     public void resetInitialState(final SpacecraftState state,
  701.                                   final PropagationType stateType,
  702.                                   final double epsilon,
  703.                                   final int maxIterations) {
  704.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  705.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  706.         resetInitialState(state, stateType, converter);
  707.     }

  708.     /** Reset the propagator initial state.
  709.      * @param state new initial state to consider
  710.      * @param stateType mean Eckstein-Hechler orbit or osculating orbit
  711.      * @param converter osculating to mean orbit converter
  712.      * @since 13.0
  713.      */
  714.     public void resetInitialState(final SpacecraftState state,
  715.                                   final PropagationType stateType,
  716.                                   final OsculatingToMeanConverter converter) {
  717.         super.resetInitialState(state);
  718.         CircularOrbit circular = (CircularOrbit) OrbitType.CIRCULAR.convertType(state.getOrbit());
  719.         if (stateType == PropagationType.OSCULATING) {
  720.             final MeanTheory theory = new EcksteinHechlerTheory(referenceRadius, mu, ck0[2],
  721.                                                                 ck0[3], ck0[4], ck0[5], ck0[6]);
  722.             converter.setMeanTheory(theory);
  723.             circular = (CircularOrbit) OrbitType.CIRCULAR.convertType(converter.convertToMean(circular));
  724.         }
  725.         this.initialModel = new EHModel(circular, state.getMass(), referenceRadius, mu, ck0);
  726.         this.models = new TimeSpanMap<>(initialModel);
  727.     }

  728.     /** {@inheritDoc} */
  729.     protected void resetIntermediateState(final SpacecraftState state,
  730.                                           final boolean forward) {
  731.         resetIntermediateState(state, forward, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
  732.     }

  733.     /** Reset an intermediate state.
  734.      * @param state new intermediate state to consider
  735.      * @param forward if true, the intermediate state is valid for
  736.      * propagations after itself
  737.      * @param epsilon convergence threshold for mean parameters conversion
  738.      * @param maxIterations maximum iterations for mean parameters conversion
  739.      * @since 11.2
  740.      */
  741.     protected void resetIntermediateState(final SpacecraftState state,
  742.                                           final boolean forward,
  743.                                           final double epsilon,
  744.                                           final int maxIterations) {
  745.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  746.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  747.         resetIntermediateState(state, forward, converter);
  748.     }

  749.     /** Reset an intermediate state.
  750.      * @param state     new intermediate state to consider
  751.      * @param forward   if true, the intermediate state is valid for
  752.      *                  propagations after itself
  753.      * @param converter osculating to mean orbit converter
  754.      * @since 13.0
  755.      */
  756.     protected void resetIntermediateState(final SpacecraftState state,
  757.                                           final boolean forward,
  758.                                           final OsculatingToMeanConverter converter) {
  759.         final MeanTheory theory = new EcksteinHechlerTheory(referenceRadius, mu, ck0[2],
  760.                                                             ck0[3], ck0[4], ck0[5], ck0[6]);
  761.         converter.setMeanTheory(theory);
  762.         final CircularOrbit mean = (CircularOrbit) OrbitType.CIRCULAR.convertType(converter.convertToMean(state.getOrbit()));
  763.         final EHModel newModel = new EHModel(mean, state.getMass(), referenceRadius, mu, ck0);
  764.         if (forward) {
  765.             models.addValidAfter(newModel, state.getDate(), false);
  766.         } else {
  767.             models.addValidBefore(newModel, state.getDate(), false);
  768.         }
  769.         stateChanged(state);
  770.     }

  771.     /** {@inheritDoc} */
  772.     public CartesianOrbit propagateOrbit(final AbsoluteDate date) {
  773.         // compute Cartesian parameters, taking derivatives into account
  774.         // to make sure velocity and acceleration are consistent
  775.         final EHModel current = models.get(date);
  776.         return new CartesianOrbit(toCartesian(date, current.propagateParameters(date)),
  777.                                   current.mean.getFrame(), mu);
  778.     }

  779.     /**
  780.      * Get the osculating circular orbit from the EH model.
  781.      * <p>This method is only relevant for the conversion from osculating to mean orbit.</p>
  782.      * @param date target date for the orbit
  783.      * @return the osculating circular orbite
  784.      */
  785.     public CircularOrbit getOsculatingCircularOrbit(final AbsoluteDate date) {
  786.         // compute circular parameters from the model
  787.         final EHModel current = models.get(date);
  788.         final UnivariateDerivative2[] parameter = current.propagateParameters(date);
  789.         return new CircularOrbit(parameter[0].getValue(),
  790.                                  parameter[1].getValue(),
  791.                                  parameter[2].getValue(),
  792.                                  parameter[3].getValue(),
  793.                                  parameter[4].getValue(),
  794.                                  parameter[5].getValue(),
  795.                                  PositionAngleType.MEAN,
  796.                                  current.mean.getFrame(),
  797.                                  date, mu);
  798.     }

  799.     /**
  800.      * Get the central attraction coefficient μ.
  801.      * @return mu central attraction coefficient (m³/s²)
  802.      * @since 11.1
  803.      */
  804.     public double getMu() {
  805.         return mu;
  806.     }

  807.     /**
  808.      * Get the un-normalized zonal coefficients.
  809.      * @return the un-normalized zonal coefficients
  810.      * @since 11.1
  811.      */
  812.     public double[] getCk0() {
  813.         return ck0.clone();
  814.     }

  815.     /**
  816.      * Get the reference radius of the central body attraction model.
  817.      * @return the reference radius in meters
  818.      * @since 11.1
  819.      */
  820.     public double getReferenceRadius() {
  821.         return referenceRadius;
  822.     }

  823.     /** {@inheritDoc} */
  824.     @Override
  825.     protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
  826.                                                         final DoubleArrayDictionary initialJacobianColumns) {
  827.         // Create the harvester
  828.         final EcksteinHechlerHarvester harvester = new EcksteinHechlerHarvester(this, stmName, initialStm, initialJacobianColumns);
  829.         // Update the list of additional state provider
  830.         addAdditionalDataProvider(harvester);
  831.         // Return the configured harvester
  832.         return harvester;
  833.     }

  834.     /** Local class for Eckstein-Hechler model, with fixed mean parameters. */
  835.     private static class EHModel {

  836.         /** Mean orbit. */
  837.         private final CircularOrbit mean;

  838.         /** Constant mass. */
  839.         private final double mass;

  840.         // CHECKSTYLE: stop JavadocVariable check

  841.         // preprocessed values
  842.         private final double xnotDot;
  843.         private final double rdpom;
  844.         private final double rdpomp;
  845.         private final double eps1;
  846.         private final double eps2;
  847.         private final double xim;
  848.         private final double ommD;
  849.         private final double rdl;
  850.         private final double aMD;

  851.         private final double kh;
  852.         private final double kl;

  853.         private final double ax1;
  854.         private final double ay1;
  855.         private final double as1;
  856.         private final double ac2;
  857.         private final double axy3;
  858.         private final double as3;
  859.         private final double ac4;
  860.         private final double as5;
  861.         private final double ac6;

  862.         private final double ex1;
  863.         private final double exx2;
  864.         private final double exy2;
  865.         private final double ex3;
  866.         private final double ex4;

  867.         private final double ey1;
  868.         private final double eyx2;
  869.         private final double eyy2;
  870.         private final double ey3;
  871.         private final double ey4;

  872.         private final double rx1;
  873.         private final double ry1;
  874.         private final double r2;
  875.         private final double r3;
  876.         private final double rl;

  877.         private final double iy1;
  878.         private final double ix1;
  879.         private final double i2;
  880.         private final double i3;
  881.         private final double ih;

  882.         private final double lx1;
  883.         private final double ly1;
  884.         private final double l2;
  885.         private final double l3;
  886.         private final double ll;

  887.         // CHECKSTYLE: resume JavadocVariable check

  888.         /** Create a model for specified mean orbit.
  889.          * @param mean mean orbit
  890.          * @param mass constant mass
  891.          * @param referenceRadius reference radius of the central body attraction model (m)
  892.          * @param mu central attraction coefficient (m³/s²)
  893.          * @param ck0 un-normalized zonal coefficients
  894.          */
  895.         EHModel(final CircularOrbit mean, final double mass,
  896.                 final double referenceRadius, final double mu, final double[] ck0) {

  897.             this.mean            = mean;
  898.             this.mass            = mass;

  899.             // preliminary processing
  900.             double q = referenceRadius / mean.getA();
  901.             double ql = q * q;
  902.             final double g2 = ck0[2] * ql;
  903.             ql *= q;
  904.             final double g3 = ck0[3] * ql;
  905.             ql *= q;
  906.             final double g4 = ck0[4] * ql;
  907.             ql *= q;
  908.             final double g5 = ck0[5] * ql;
  909.             ql *= q;
  910.             final double g6 = ck0[6] * ql;

  911.             final SinCos sc    = FastMath.sinCos(mean.getI());
  912.             final double cosI1 = sc.cos();
  913.             final double sinI1 = sc.sin();
  914.             final double sinI2 = sinI1 * sinI1;
  915.             final double sinI4 = sinI2 * sinI2;
  916.             final double sinI6 = sinI2 * sinI4;

  917.             if (sinI2 < 1.0e-10) {
  918.                 throw new OrekitException(OrekitMessages.ALMOST_EQUATORIAL_ORBIT,
  919.                                           FastMath.toDegrees(mean.getI()));
  920.             }

  921.             if (FastMath.abs(sinI2 - 4.0 / 5.0) < 1.0e-3) {
  922.                 throw new OrekitException(OrekitMessages.ALMOST_CRITICALLY_INCLINED_ORBIT,
  923.                                           FastMath.toDegrees(mean.getI()));
  924.             }

  925.             if (mean.getE() > 0.1) {
  926.                 // if 0.005 < e < 0.1 no error is triggered, but accuracy is poor
  927.                 throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL,
  928.                                           mean.getE());
  929.             }

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

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

  934.             q = 3.0 / (32.0 * rdpom);
  935.             eps1 = q * g4 * sinI2 * (30.0 - 35.0 * sinI2) -
  936.                     175.0 * q * g6 * sinI2 * (1.0 - 3.0 * sinI2 + 2.0625 * sinI4);
  937.             q = 3.0 * sinI1 / (8.0 * rdpom);
  938.             eps2 = q * g3 * (4.0 - 5.0 * sinI2) - q * g5 * (10.0 - 35.0 * sinI2 + 26.25 * sinI4);

  939.             xim = mean.getI();
  940.             ommD = cosI1 * (1.50    * g2 - 2.25 * g2 * g2 * (2.5 - 19.0 / 6.0 * sinI2) +
  941.                             0.9375  * g4 * (7.0 * sinI2 - 4.0) +
  942.                             3.28125 * g6 * (2.0 - 9.0 * sinI2 + 8.25 * sinI4));

  943.             rdl = 1.0 - 1.50 * g2 * (3.0 - 4.0 * sinI2);
  944.             aMD = rdl +
  945.                     2.25 * g2 * g2 * (9.0 - 263.0 / 12.0 * sinI2 + 341.0 / 24.0 * sinI4) +
  946.                     15.0 / 16.0 * g4 * (8.0 - 31.0 * sinI2 + 24.5 * sinI4) +
  947.                     105.0 / 32.0 * g6 * (-10.0 / 3.0 + 25.0 * sinI2 - 48.75 * sinI4 + 27.5 * sinI6);

  948.             final double qq = -1.5 * g2 / rdl;
  949.             final double qA   = 0.75 * g2 * g2 * sinI2;
  950.             final double qB   = 0.25 * g4 * sinI2;
  951.             final double qC   = 105.0 / 16.0 * g6 * sinI2;
  952.             final double qD   = -0.75 * g3 * sinI1;
  953.             final double qE   = 3.75 * g5 * sinI1;
  954.             kh = 0.375 / rdpom;
  955.             kl = kh / sinI1;

  956.             ax1 = qq * (2.0 - 3.5 * sinI2);
  957.             ay1 = qq * (2.0 - 2.5 * sinI2);
  958.             as1 = qD * (4.0 - 5.0 * sinI2) +
  959.                   qE * (2.625 * sinI4 - 3.5 * sinI2 + 1.0);
  960.             ac2 = qq * sinI2 +
  961.                   qA * 7.0 * (2.0 - 3.0 * sinI2) +
  962.                   qB * (15.0 - 17.5 * sinI2) +
  963.                   qC * (3.0 * sinI2 - 1.0 - 33.0 / 16.0 * sinI4);
  964.             axy3 = qq * 3.5 * sinI2;
  965.             as3 = qD * 5.0 / 3.0 * sinI2 +
  966.                   qE * 7.0 / 6.0 * sinI2 * (1.0 - 1.125 * sinI2);
  967.             ac4 = qA * sinI2 +
  968.                   qB * 4.375 * sinI2 +
  969.                   qC * 0.75 * (1.1 * sinI4 - sinI2);

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

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

  972.             ex1 = qq * (1.0 - 1.25 * sinI2);
  973.             exx2 = qq * 0.5 * (3.0 - 5.0 * sinI2);
  974.             exy2 = qq * (2.0 - 1.5 * sinI2);
  975.             ex3 = qq * 7.0 / 12.0 * sinI2;
  976.             ex4 = qq * 17.0 / 8.0 * sinI2;

  977.             ey1 = qq * (1.0 - 1.75 * sinI2);
  978.             eyx2 = qq * (1.0 - 3.0 * sinI2);
  979.             eyy2 = qq * (2.0 * sinI2 - 1.5);
  980.             ey3 = qq * 7.0 / 12.0 * sinI2;
  981.             ey4 = qq * 17.0 / 8.0 * sinI2;

  982.             q  = -qq * cosI1;
  983.             rx1 =  3.5 * q;
  984.             ry1 = -2.5 * q;
  985.             r2 = -0.5 * q;
  986.             r3 =  7.0 / 6.0 * q;
  987.             rl = g3 * cosI1 * (4.0 - 15.0 * sinI2) -
  988.                  2.5 * g5 * cosI1 * (4.0 - 42.0 * sinI2 + 52.5 * sinI4);

  989.             q = 0.5 * qq * sinI1 * cosI1;
  990.             iy1 =  q;
  991.             ix1 = -q;
  992.             i2 =  q;
  993.             i3 =  q * 7.0 / 3.0;
  994.             ih = -g3 * cosI1 * (4.0 - 5.0 * sinI2) +
  995.                  2.5 * g5 * cosI1 * (4.0 - 14.0 * sinI2 + 10.5 * sinI4);

  996.             lx1 = qq * (7.0 - 77.0 / 8.0 * sinI2);
  997.             ly1 = qq * (55.0 / 8.0 * sinI2 - 7.50);
  998.             l2 = qq * (1.25 * sinI2 - 0.5);
  999.             l3 = qq * (77.0 / 24.0 * sinI2 - 7.0 / 6.0);
  1000.             ll = g3 * (53.0 * sinI2 - 4.0 - 57.5 * sinI4) +
  1001.                  2.5 * g5 * (4.0 - 96.0 * sinI2 + 269.5 * sinI4 - 183.75 * sinI6);

  1002.         }

  1003.         /** Extrapolate an orbit up to a specific target date.
  1004.          * @param date target date for the orbit
  1005.          * @return propagated parameters
  1006.          */
  1007.         public UnivariateDerivative2[] propagateParameters(final AbsoluteDate date) {

  1008.             // Keplerian evolution
  1009.             final UnivariateDerivative2 dt = new UnivariateDerivative2(date.durationFrom(mean.getDate()), 1.0, 0.0);
  1010.             final UnivariateDerivative2 xnot = dt.multiply(xnotDot);

  1011.             // secular effects

  1012.             // eccentricity
  1013.             final UnivariateDerivative2 x   = xnot.multiply(rdpom + rdpomp);
  1014.             final UnivariateDerivative2 cx  = x.cos();
  1015.             final UnivariateDerivative2 sx  = x.sin();
  1016.             final UnivariateDerivative2 exm = cx.multiply(mean.getCircularEx()).
  1017.                                               add(sx.multiply(eps2 - (1.0 - eps1) * mean.getCircularEy()));
  1018.             final UnivariateDerivative2 eym = sx.multiply((1.0 + eps1) * mean.getCircularEx()).
  1019.                                               add(cx.multiply(mean.getCircularEy() - eps2)).
  1020.                                               add(eps2);

  1021.             // no secular effect on inclination

  1022.             // right ascension of ascending node
  1023.             final UnivariateDerivative2 omm = new UnivariateDerivative2(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode() + ommD * xnot.getValue(),
  1024.                                                                                                FastMath.PI),
  1025.                                                                       ommD * xnotDot,
  1026.                                                                       0.0);

  1027.             // latitude argument
  1028.             final UnivariateDerivative2 xlm = new UnivariateDerivative2(MathUtils.normalizeAngle(mean.getAlphaM() + aMD * xnot.getValue(),
  1029.                                                                                                  FastMath.PI),
  1030.                                                                         aMD * xnotDot,
  1031.                                                                         0.0);

  1032.             // periodical terms
  1033.             final UnivariateDerivative2 cl1 = xlm.cos();
  1034.             final UnivariateDerivative2 sl1 = xlm.sin();
  1035.             final UnivariateDerivative2 cl2 = cl1.multiply(cl1).subtract(sl1.multiply(sl1));
  1036.             final UnivariateDerivative2 sl2 = cl1.multiply(sl1).add(sl1.multiply(cl1));
  1037.             final UnivariateDerivative2 cl3 = cl2.multiply(cl1).subtract(sl2.multiply(sl1));
  1038.             final UnivariateDerivative2 sl3 = cl2.multiply(sl1).add(sl2.multiply(cl1));
  1039.             final UnivariateDerivative2 cl4 = cl3.multiply(cl1).subtract(sl3.multiply(sl1));
  1040.             final UnivariateDerivative2 sl4 = cl3.multiply(sl1).add(sl3.multiply(cl1));
  1041.             final UnivariateDerivative2 cl5 = cl4.multiply(cl1).subtract(sl4.multiply(sl1));
  1042.             final UnivariateDerivative2 sl5 = cl4.multiply(sl1).add(sl4.multiply(cl1));
  1043.             final UnivariateDerivative2 cl6 = cl5.multiply(cl1).subtract(sl5.multiply(sl1));

  1044.             final UnivariateDerivative2 qh  = eym.subtract(eps2).multiply(kh);
  1045.             final UnivariateDerivative2 ql  = exm.multiply(kl);

  1046.             final UnivariateDerivative2 exmCl1 = exm.multiply(cl1);
  1047.             final UnivariateDerivative2 exmSl1 = exm.multiply(sl1);
  1048.             final UnivariateDerivative2 eymCl1 = eym.multiply(cl1);
  1049.             final UnivariateDerivative2 eymSl1 = eym.multiply(sl1);
  1050.             final UnivariateDerivative2 exmCl2 = exm.multiply(cl2);
  1051.             final UnivariateDerivative2 exmSl2 = exm.multiply(sl2);
  1052.             final UnivariateDerivative2 eymCl2 = eym.multiply(cl2);
  1053.             final UnivariateDerivative2 eymSl2 = eym.multiply(sl2);
  1054.             final UnivariateDerivative2 exmCl3 = exm.multiply(cl3);
  1055.             final UnivariateDerivative2 exmSl3 = exm.multiply(sl3);
  1056.             final UnivariateDerivative2 eymCl3 = eym.multiply(cl3);
  1057.             final UnivariateDerivative2 eymSl3 = eym.multiply(sl3);
  1058.             final UnivariateDerivative2 exmCl4 = exm.multiply(cl4);
  1059.             final UnivariateDerivative2 exmSl4 = exm.multiply(sl4);
  1060.             final UnivariateDerivative2 eymCl4 = eym.multiply(cl4);
  1061.             final UnivariateDerivative2 eymSl4 = eym.multiply(sl4);

  1062.             // semi major axis
  1063.             final UnivariateDerivative2 rda = exmCl1.multiply(ax1).
  1064.                                               add(eymSl1.multiply(ay1)).
  1065.                                               add(sl1.multiply(as1)).
  1066.                                               add(cl2.multiply(ac2)).
  1067.                                               add(exmCl3.add(eymSl3).multiply(axy3)).
  1068.                                               add(sl3.multiply(as3)).
  1069.                                               add(cl4.multiply(ac4)).
  1070.                                               add(sl5.multiply(as5)).
  1071.                                               add(cl6.multiply(ac6));

  1072.             // eccentricity
  1073.             final UnivariateDerivative2 rdex = cl1.multiply(ex1).
  1074.                                                add(exmCl2.multiply(exx2)).
  1075.                                                add(eymSl2.multiply(exy2)).
  1076.                                                add(cl3.multiply(ex3)).
  1077.                                                add(exmCl4.add(eymSl4).multiply(ex4));
  1078.             final UnivariateDerivative2 rdey = sl1.multiply(ey1).
  1079.                                                add(exmSl2.multiply(eyx2)).
  1080.                                                add(eymCl2.multiply(eyy2)).
  1081.                                                add(sl3.multiply(ey3)).
  1082.                                                add(exmSl4.subtract(eymCl4).multiply(ey4));

  1083.             // ascending node
  1084.             final UnivariateDerivative2 rdom = exmSl1.multiply(rx1).
  1085.                                                add(eymCl1.multiply(ry1)).
  1086.                                                add(sl2.multiply(r2)).
  1087.                                                add(eymCl3.subtract(exmSl3).multiply(r3)).
  1088.                                                add(ql.multiply(rl));

  1089.             // inclination
  1090.             final UnivariateDerivative2 rdxi = eymSl1.multiply(iy1).
  1091.                                                add(exmCl1.multiply(ix1)).
  1092.                                                add(cl2.multiply(i2)).
  1093.                                                add(exmCl3.add(eymSl3).multiply(i3)).
  1094.                                                add(qh.multiply(ih));

  1095.             // latitude argument
  1096.             final UnivariateDerivative2 rdxl = exmSl1.multiply(lx1).
  1097.                                                add(eymCl1.multiply(ly1)).
  1098.                                                add(sl2.multiply(l2)).
  1099.                                                add(exmSl3.subtract(eymCl3).multiply(l3)).
  1100.                                                add(ql.multiply(ll));

  1101.             // osculating parameters
  1102.             return new UnivariateDerivative2[] {
  1103.                 rda.add(1.0).multiply(mean.getA()),
  1104.                 rdex.add(exm),
  1105.                 rdey.add(eym),
  1106.                 rdxi.add(xim),
  1107.                 rdom.add(omm),
  1108.                 rdxl.add(xlm)
  1109.             };

  1110.         }

  1111.     }

  1112.     /** Convert circular parameters <em>with derivatives</em> to Cartesian coordinates.
  1113.      * @param date date of the orbital parameters
  1114.      * @param parameters circular parameters (a, ex, ey, i, raan, alphaM)
  1115.      * @return Cartesian coordinates consistent with values and derivatives
  1116.      */
  1117.     private TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final UnivariateDerivative2[] parameters) {

  1118.         // evaluate coordinates in the orbit canonical reference frame
  1119.         final UnivariateDerivative2 cosOmega = parameters[4].cos();
  1120.         final UnivariateDerivative2 sinOmega = parameters[4].sin();
  1121.         final UnivariateDerivative2 cosI     = parameters[3].cos();
  1122.         final UnivariateDerivative2 sinI     = parameters[3].sin();
  1123.         final UnivariateDerivative2 alphaE   = meanToEccentric(parameters[5], parameters[1], parameters[2]);
  1124.         final UnivariateDerivative2 cosAE    = alphaE.cos();
  1125.         final UnivariateDerivative2 sinAE    = alphaE.sin();
  1126.         final UnivariateDerivative2 ex2      = parameters[1].square();
  1127.         final UnivariateDerivative2 ey2      = parameters[2].square();
  1128.         final UnivariateDerivative2 exy      = parameters[1].multiply(parameters[2]);
  1129.         final UnivariateDerivative2 q        = ex2.add(ey2).subtract(1).negate().sqrt();
  1130.         final UnivariateDerivative2 beta     = q.add(1).reciprocal();
  1131.         final UnivariateDerivative2 bx2      = beta.multiply(ex2);
  1132.         final UnivariateDerivative2 by2      = beta.multiply(ey2);
  1133.         final UnivariateDerivative2 bxy      = beta.multiply(exy);
  1134.         final UnivariateDerivative2 u        = bxy.multiply(sinAE).subtract(parameters[1].add(by2.subtract(1).multiply(cosAE)));
  1135.         final UnivariateDerivative2 v        = bxy.multiply(cosAE).subtract(parameters[2].add(bx2.subtract(1).multiply(sinAE)));
  1136.         final UnivariateDerivative2 x        = parameters[0].multiply(u);
  1137.         final UnivariateDerivative2 y        = parameters[0].multiply(v);

  1138.         // canonical orbit reference frame
  1139.         final FieldVector3D<UnivariateDerivative2> p =
  1140.                 new FieldVector3D<>(x.multiply(cosOmega).subtract(y.multiply(cosI.multiply(sinOmega))),
  1141.                                     x.multiply(sinOmega).add(y.multiply(cosI.multiply(cosOmega))),
  1142.                                     y.multiply(sinI));

  1143.         // dispatch derivatives
  1144.         final Vector3D p0 = new Vector3D(p.getX().getValue(),
  1145.                                          p.getY().getValue(),
  1146.                                          p.getZ().getValue());
  1147.         final Vector3D p1 = new Vector3D(p.getX().getFirstDerivative(),
  1148.                                          p.getY().getFirstDerivative(),
  1149.                                          p.getZ().getFirstDerivative());
  1150.         final Vector3D p2 = new Vector3D(p.getX().getSecondDerivative(),
  1151.                                          p.getY().getSecondDerivative(),
  1152.                                          p.getZ().getSecondDerivative());
  1153.         return new TimeStampedPVCoordinates(date, p0, p1, p2);

  1154.     }

  1155.     /** Computes the eccentric latitude argument from the mean latitude argument.
  1156.      * @param alphaM = M + Ω mean latitude argument (rad)
  1157.      * @param ex e cos(Ω), first component of circular eccentricity vector
  1158.      * @param ey e sin(Ω), second component of circular eccentricity vector
  1159.      * @return the eccentric latitude argument.
  1160.      */
  1161.     private UnivariateDerivative2 meanToEccentric(final UnivariateDerivative2 alphaM,
  1162.                                                   final UnivariateDerivative2 ex,
  1163.                                                   final UnivariateDerivative2 ey) {
  1164.         // Generalization of Kepler equation to circular parameters
  1165.         // with alphaE = PA + E and
  1166.         //      alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE)
  1167.         UnivariateDerivative2 alphaE        = alphaM;
  1168.         UnivariateDerivative2 shift         = alphaM.getField().getZero();
  1169.         UnivariateDerivative2 alphaEMalphaM = alphaM.getField().getZero();
  1170.         UnivariateDerivative2 cosAlphaE     = alphaE.cos();
  1171.         UnivariateDerivative2 sinAlphaE     = alphaE.sin();
  1172.         int                 iter          = 0;
  1173.         do {
  1174.             final UnivariateDerivative2 f2 = ex.multiply(sinAlphaE).subtract(ey.multiply(cosAlphaE));
  1175.             final UnivariateDerivative2 f1 = alphaM.getField().getOne().subtract(ex.multiply(cosAlphaE)).subtract(ey.multiply(sinAlphaE));
  1176.             final UnivariateDerivative2 f0 = alphaEMalphaM.subtract(f2);

  1177.             final UnivariateDerivative2 f12 = f1.multiply(2);
  1178.             shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2)));

  1179.             alphaEMalphaM  = alphaEMalphaM.subtract(shift);
  1180.             alphaE         = alphaM.add(alphaEMalphaM);
  1181.             cosAlphaE      = alphaE.cos();
  1182.             sinAlphaE      = alphaE.sin();

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

  1184.         return alphaE;

  1185.     }

  1186.     /** {@inheritDoc} */
  1187.     protected double getMass(final AbsoluteDate date) {
  1188.         return models.get(date).mass;
  1189.     }

  1190. }