FieldBrouwerLyddanePropagator.java

  1. /* Copyright 2002-2022 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 java.util.Collections;
  19. import java.util.List;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.Field;
  22. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2;
  23. import org.hipparchus.util.CombinatoricsUtils;
  24. import org.hipparchus.util.FastMath;
  25. import org.hipparchus.util.FieldSinCos;
  26. import org.hipparchus.util.MathArrays;
  27. import org.hipparchus.util.MathUtils;
  28. import org.hipparchus.util.Precision;
  29. import org.orekit.attitudes.AttitudeProvider;
  30. import org.orekit.attitudes.InertialProvider;
  31. import org.orekit.errors.OrekitException;
  32. import org.orekit.errors.OrekitMessages;
  33. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  34. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  35. import org.orekit.orbits.FieldKeplerianOrbit;
  36. import org.orekit.orbits.FieldOrbit;
  37. import org.orekit.orbits.OrbitType;
  38. import org.orekit.orbits.PositionAngle;
  39. import org.orekit.propagation.FieldSpacecraftState;
  40. import org.orekit.propagation.PropagationType;
  41. import org.orekit.propagation.analytical.tle.FieldTLE;
  42. import org.orekit.time.FieldAbsoluteDate;
  43. import org.orekit.utils.FieldTimeSpanMap;
  44. import org.orekit.utils.ParameterDriver;

  45. /** This class propagates a {@link org.orekit.propagation.FieldSpacecraftState}
  46.  *  using the analytical Brouwer-Lyddane model (from J2 to J5 zonal harmonics).
  47.  * <p>
  48.  * At the opposite of the {@link FieldEcksteinHechlerPropagator}, the Brouwer-Lyddane model is
  49.  * suited for elliptical orbits, there is no problem having a rather small eccentricity or inclination
  50.  * (Lyddane helped to solve this issue with the Brouwer model). Singularity for the critical
  51.  * inclination i = 63.4° is avoided using the method developed in Warren Phipps' 1992 thesis.
  52.  * <p>
  53.  * By default, Brouwer-Lyddane model considers only the perturbations due to zonal harmonics.
  54.  * However, for low Earth orbits, the magnitude of the perturbative acceleration due to
  55.  * atmospheric drag can be significant. Warren Phipps' 1992 thesis considered the atmospheric
  56.  * drag by time derivatives of the <i>mean</i> mean anomaly using the catch-all coefficient
  57.  * {@link #M2Driver}.
  58.  *
  59.  * Usually, M2 is adjusted during an orbit determination process and it represents the
  60.  * combination of all unmodeled secular along-track effects (i.e. not just the atmospheric drag).
  61.  * The behavior of M2 is close to the {@link FieldTLE#getBStar()} parameter for the TLE.
  62.  *
  63.  * If the value of M2 is equal to {@link BrouwerLyddanePropagator#M2 0.0}, the along-track secular
  64.  * effects are not considered in the dynamical model. Typical values for M2 are not known.
  65.  * It depends on the orbit type. However, the value of M2 must be very small (e.g. between 1.0e-14 and 1.0e-15).
  66.  * The unit of M2 is rad/s².
  67.  *
  68.  * The along-track effects, represented by the secular rates of the mean semi-major axis
  69.  * and eccentricity, are computed following Eq. 2.38, 2.41, and 2.45 of Warren Phipps' thesis.
  70.  *
  71.  * @see "Brouwer, Dirk. Solution of the problem of artificial satellite theory without drag.
  72.  *       YALE UNIV NEW HAVEN CT NEW HAVEN United States, 1959."
  73.  *
  74.  * @see "Lyddane, R. H. Small eccentricities or inclinations in the Brouwer theory of the
  75.  *       artificial satellite. The Astronomical Journal 68 (1963): 555."
  76.  *
  77.  * @see "Phipps Jr, Warren E. Parallelization of the Navy Space Surveillance Center
  78.  *       (NAVSPASUR) Satellite Model. NAVAL POSTGRADUATE SCHOOL MONTEREY CA, 1992."
  79.  *
  80.  * @author Melina Vanel
  81.  * @author Bryan Cazabonne
  82.  * @since 11.1
  83.  */
  84. public class FieldBrouwerLyddanePropagator<T extends CalculusFieldElement<T>> extends FieldAbstractAnalyticalPropagator<T>  {

  85.     /** Parameters scaling factor.
  86.      * <p>
  87.      * We use a power of 2 to avoid numeric noise introduction
  88.      * in the multiplications/divisions sequences.
  89.      * </p>
  90.      */
  91.     private static final double SCALE = FastMath.scalb(1.0, -20);

  92.     /** Beta constant used by T2 function. */
  93.     private static final double BETA = FastMath.scalb(100, -11);

  94.     /** Initial Brouwer-Lyddane model. */
  95.     private FieldBLModel<T> initialModel;

  96.     /** All models. */
  97.     private transient FieldTimeSpanMap<FieldBLModel<T>, T> models;

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

  100.     /** Central attraction coefficient (m³/s²). */
  101.     private T mu;

  102.     /** Un-normalized zonal coefficients. */
  103.     private double[] ck0;

  104.     /** Empirical coefficient used in the drag modeling. */
  105.     private final ParameterDriver M2Driver;

  106.     /** Build a propagator from orbit and potential provider.
  107.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  108.      *
  109.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  110.      *
  111.      * @param initialOrbit initial orbit
  112.      * @param provider for un-normalized zonal coefficients
  113.      * @param M2 value of empirical drag coefficient in rad/s².
  114.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  115.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, UnnormalizedSphericalHarmonicsProvider, PropagationType, double)
  116.      */
  117.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  118.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  119.                                          final double M2) {
  120.         this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()),
  121.              initialOrbit.getA().getField().getZero().add(DEFAULT_MASS), provider,
  122.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2);
  123.     }

  124.     /**
  125.      * Private helper constructor.
  126.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  127.      * @param initialOrbit initial orbit
  128.      * @param attitude attitude provider
  129.      * @param mass spacecraft mass
  130.      * @param provider for un-normalized zonal coefficients
  131.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  132.      * @param M2 value of empirical drag coefficient in rad/s².
  133.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  134.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement,
  135.      * UnnormalizedSphericalHarmonicsProvider, UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics, PropagationType, double)
  136.      */
  137.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  138.                                          final AttitudeProvider attitude,
  139.                                          final T mass,
  140.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  141.                                          final UnnormalizedSphericalHarmonics harmonics,
  142.                                          final double M2) {
  143.         this(initialOrbit, attitude,  mass, provider.getAe(), initialOrbit.getA().getField().getZero().add(provider.getMu()),
  144.              harmonics.getUnnormalizedCnm(2, 0),
  145.              harmonics.getUnnormalizedCnm(3, 0),
  146.              harmonics.getUnnormalizedCnm(4, 0),
  147.              harmonics.getUnnormalizedCnm(5, 0),
  148.              M2);
  149.     }

  150.     /** Build a propagator from orbit and potential.
  151.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  152.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  153.      * are related to both the normalized coefficients
  154.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  155.      *  and the J<sub>n</sub> one as follows:</p>
  156.      *
  157.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  158.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  159.      *
  160.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  161.      *
  162.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  163.      *
  164.      * @param initialOrbit initial orbit
  165.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  166.      * @param mu central attraction coefficient (m³/s²)
  167.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  168.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  169.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  170.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  171.      * @param M2 value of empirical drag coefficient in rad/s².
  172.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  173.      * @see org.orekit.utils.Constants
  174.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, double, CalculusFieldElement, double, double, double, double, double)
  175.      */
  176.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  177.                                          final double referenceRadius, final T mu,
  178.                                          final double c20, final double c30, final double c40,
  179.                                          final double c50, final double M2) {
  180.         this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()),
  181.              initialOrbit.getDate().getField().getZero().add(DEFAULT_MASS),
  182.              referenceRadius, mu, c20, c30, c40, c50, M2);
  183.     }

  184.     /** Build a propagator from orbit, mass and potential provider.
  185.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  186.      *
  187.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  188.      *
  189.      * @param initialOrbit initial orbit
  190.      * @param mass spacecraft mass
  191.      * @param provider for un-normalized zonal coefficients
  192.      * @param M2 value of empirical drag coefficient in rad/s².
  193.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  194.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement, UnnormalizedSphericalHarmonicsProvider, double)
  195.      */
  196.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit, final T mass,
  197.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  198.                                          final double M2) {
  199.         this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()),
  200.              mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2);
  201.     }

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

  235.     /** Build a propagator from orbit, attitude provider and potential provider.
  236.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  237.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  238.      * @param initialOrbit initial orbit
  239.      * @param attitudeProv attitude provider
  240.      * @param provider for un-normalized zonal coefficients
  241.      * @param M2 value of empirical drag coefficient in rad/s².
  242.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  243.      */
  244.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  245.                                          final AttitudeProvider attitudeProv,
  246.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  247.                                          final double M2) {
  248.         this(initialOrbit, attitudeProv, initialOrbit.getA().getField().getZero().add(DEFAULT_MASS), provider,
  249.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2);
  250.     }

  251.     /** Build a propagator from orbit, attitude provider and potential.
  252.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  253.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  254.      * are related to both the normalized coefficients
  255.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  256.      *  and the J<sub>n</sub> one as follows:</p>
  257.      *
  258.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  259.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  260.      *
  261.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  262.      *
  263.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  264.      *
  265.      * @param initialOrbit initial orbit
  266.      * @param attitudeProv attitude provider
  267.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  268.      * @param mu central attraction coefficient (m³/s²)
  269.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  270.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  271.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  272.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  273.      * @param M2 value of empirical drag coefficient in rad/s².
  274.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  275.      */
  276.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  277.                                          final AttitudeProvider attitudeProv,
  278.                                          final double referenceRadius, final T mu,
  279.                                          final double c20, final double c30, final double c40,
  280.                                          final double c50, final double M2) {
  281.         this(initialOrbit, attitudeProv, initialOrbit.getDate().getField().getZero().add(DEFAULT_MASS),
  282.              referenceRadius, mu, c20, c30, c40, c50, M2);
  283.     }

  284.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  285.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  286.      * @param initialOrbit initial orbit
  287.      * @param attitudeProv attitude provider
  288.      * @param mass spacecraft mass
  289.      * @param provider for un-normalized zonal coefficients
  290.      * @param M2 value of empirical drag coefficient in rad/s².
  291.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  292.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement, UnnormalizedSphericalHarmonicsProvider, PropagationType, double)
  293.      */
  294.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  295.                                          final AttitudeProvider attitudeProv,
  296.                                          final T mass,
  297.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  298.                                          final double M2) {
  299.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), M2);
  300.     }

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


  335.     /** Build a propagator from orbit and potential provider.
  336.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  337.      *
  338.      * <p>Using this constructor, it is possible to define the initial orbit as
  339.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  340.      *
  341.      * @param initialOrbit initial orbit
  342.      * @param provider for un-normalized zonal coefficients
  343.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  344.      * @param M2 value of empirical drag coefficient in rad/s².
  345.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  346.      */
  347.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  348.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  349.                                          final PropagationType initialType,
  350.                                          final double M2) {
  351.         this(initialOrbit, InertialProvider.of(initialOrbit.getFrame()),
  352.              initialOrbit.getA().getField().getZero().add(DEFAULT_MASS), provider,
  353.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, M2);
  354.     }

  355.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  356.      * <p>Using this constructor, it is possible to define the initial orbit as
  357.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  358.      * @param initialOrbit initial orbit
  359.      * @param attitudeProv attitude provider
  360.      * @param mass spacecraft mass
  361.      * @param provider for un-normalized zonal coefficients
  362.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  363.      * @param M2 value of empirical drag coefficient in rad/s².
  364.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  365.      */
  366.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  367.                                          final AttitudeProvider attitudeProv,
  368.                                          final T mass,
  369.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  370.                                          final PropagationType initialType,
  371.                                          final double M2) {
  372.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, M2);
  373.     }

  374.     /**
  375.      * Private helper constructor.
  376.      * <p>Using this constructor, it is possible to define the initial orbit as
  377.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  378.      * @param initialOrbit initial orbit
  379.      * @param attitude attitude provider
  380.      * @param mass spacecraft mass
  381.      * @param provider for un-normalized zonal coefficients
  382.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  383.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  384.      * @param M2 value of empirical drag coefficient in rad/s².
  385.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  386.      */
  387.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  388.                                          final AttitudeProvider attitude,
  389.                                          final T mass,
  390.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  391.                                          final UnnormalizedSphericalHarmonics harmonics,
  392.                                          final PropagationType initialType,
  393.                                          final double M2) {
  394.         this(initialOrbit, attitude, mass, provider.getAe(), initialOrbit.getA().getField().getZero().add(provider.getMu()),
  395.              harmonics.getUnnormalizedCnm(2, 0),
  396.              harmonics.getUnnormalizedCnm(3, 0),
  397.              harmonics.getUnnormalizedCnm(4, 0),
  398.              harmonics.getUnnormalizedCnm(5, 0),
  399.              initialType, M2);
  400.     }

  401.     /** Build a propagator from orbit, attitude provider, mass and potential.
  402.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  403.      * are related to both the normalized coefficients
  404.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  405.      *  and the J<sub>n</sub> one as follows:</p>
  406.      *
  407.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  408.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  409.      *
  410.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  411.      *
  412.      * <p>Using this constructor, it is possible to define the initial orbit as
  413.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  414.      *
  415.      * @param initialOrbit initial orbit
  416.      * @param attitudeProv attitude provider
  417.      * @param mass spacecraft mass
  418.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  419.      * @param mu central attraction coefficient (m³/s²)
  420.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  421.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  422.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  423.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  424.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  425.      * @param M2 value of empirical drag coefficient in rad/s².
  426.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  427.      */
  428.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  429.                                          final AttitudeProvider attitudeProv,
  430.                                          final T mass,
  431.                                          final double referenceRadius, final T mu,
  432.                                          final double c20, final double c30, final double c40,
  433.                                          final double c50,
  434.                                          final PropagationType initialType,
  435.                                          final double M2) {

  436.         super(mass.getField(), attitudeProv);

  437.         // store model coefficients
  438.         this.referenceRadius = referenceRadius;
  439.         this.mu  = mu;
  440.         this.ck0 = new double[] {0.0, 0.0, c20, c30, c40, c50};

  441.         // initialize M2 driver
  442.         this.M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, M2, SCALE,
  443.                                             Double.NEGATIVE_INFINITY,
  444.                                             Double.POSITIVE_INFINITY);

  445.         // compute mean parameters if needed
  446.         resetInitialState(new FieldSpacecraftState<>(initialOrbit,
  447.                                                  attitudeProv.getAttitude(initialOrbit,
  448.                                                                           initialOrbit.getDate(),
  449.                                                                           initialOrbit.getFrame()),
  450.                                                  mass),
  451.                                                  initialType);

  452.     }


  453.     /** {@inheritDoc}
  454.      * <p>The new initial state to consider
  455.      * must be defined with an osculating orbit.</p>
  456.      * @see #resetInitialState(FieldSpacecraftState, PropagationType)
  457.      */
  458.     @Override
  459.     public void resetInitialState(final FieldSpacecraftState<T> state) {
  460.         resetInitialState(state, PropagationType.OSCULATING);
  461.     }

  462.     /** Reset the propagator initial state.
  463.      * @param state new initial state to consider
  464.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  465.      * @since 10.2
  466.      */
  467.     public void resetInitialState(final FieldSpacecraftState<T> state, final PropagationType stateType) {
  468.         super.resetInitialState(state);
  469.         final FieldKeplerianOrbit<T> keplerian = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(state.getOrbit());
  470.         this.initialModel = (stateType == PropagationType.MEAN) ?
  471.                              new FieldBLModel<>(keplerian, state.getMass(), referenceRadius, mu, ck0) :
  472.                              computeMeanParameters(keplerian, state.getMass());
  473.         this.models = new FieldTimeSpanMap<FieldBLModel<T>, T>(initialModel, state.getA().getField());
  474.     }

  475.     /** {@inheritDoc} */
  476.     @Override
  477.     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
  478.         final FieldBLModel<T> newModel = computeMeanParameters((FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(state.getOrbit()),
  479.                                                        state.getMass());
  480.         if (forward) {
  481.             models.addValidAfter(newModel, state.getDate());
  482.         } else {
  483.             models.addValidBefore(newModel, state.getDate());
  484.         }
  485.         stateChanged(state);
  486.     }

  487.     /** Compute mean parameters according to the Brouwer-Lyddane analytical model computation
  488.      * in order to do the propagation.
  489.      * @param osculating osculating orbit
  490.      * @param mass constant mass
  491.      * @return Brouwer-Lyddane mean model
  492.      */
  493.     private FieldBLModel<T> computeMeanParameters(final FieldKeplerianOrbit<T> osculating, final T mass) {

  494.         // sanity check
  495.         if (osculating.getA().getReal() < referenceRadius) {
  496.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE,
  497.                                            osculating.getA());
  498.         }

  499.         final Field<T> field = mass.getField();
  500.         final T one = field.getOne();
  501.         final T zero = field.getZero();
  502.         // rough initialization of the mean parameters
  503.         FieldBLModel<T> current = new FieldBLModel<T>(osculating, mass, referenceRadius, mu, ck0);

  504.         // threshold for each parameter
  505.         final T epsilon         = one.multiply(1.0e-13);
  506.         final T thresholdA      = epsilon.multiply(current.mean.getA().abs().add(1.0));
  507.         final T thresholdE      = epsilon.multiply(current.mean.getE().add(1.0));
  508.         final T thresholdAngles = epsilon.multiply(one.getPi());

  509.         int i = 0;
  510.         while (i++ < 200) {

  511.             // recompute the osculating parameters from the current mean parameters
  512.             final FieldUnivariateDerivative2<T>[] parameters = current.propagateParameters(current.mean.getDate(), getParameters(mass.getField()));

  513.             // adapted parameters residuals
  514.             final T deltaA     = osculating.getA()  .subtract(parameters[0].getValue());
  515.             final T deltaE     = osculating.getE()  .subtract(parameters[1].getValue());
  516.             final T deltaI     = osculating.getI()  .subtract(parameters[2].getValue());
  517.             final T deltaOmega = MathUtils.normalizeAngle(osculating.getPerigeeArgument().subtract(parameters[3].getValue()), zero);
  518.             final T deltaRAAN  = MathUtils.normalizeAngle(osculating.getRightAscensionOfAscendingNode().subtract(parameters[4].getValue()), zero);
  519.             final T deltaAnom  = MathUtils.normalizeAngle(osculating.getMeanAnomaly().subtract(parameters[5].getValue()), zero);


  520.             // update mean parameters
  521.             current = new FieldBLModel<T>(new FieldKeplerianOrbit<T>(current.mean.getA()            .add(deltaA),
  522.                                                      current.mean.getE()                            .add(deltaE),
  523.                                                      current.mean.getI()                            .add(deltaI),
  524.                                                      current.mean.getPerigeeArgument()              .add(deltaOmega),
  525.                                                      current.mean.getRightAscensionOfAscendingNode().add(deltaRAAN),
  526.                                                      current.mean.getMeanAnomaly()                  .add(deltaAnom),
  527.                                                      PositionAngle.MEAN,
  528.                                                      current.mean.getFrame(),
  529.                                                      current.mean.getDate(), mu),
  530.                                   mass, referenceRadius, mu, ck0);
  531.             // check convergence
  532.             if (FastMath.abs(deltaA.getReal())     < thresholdA.getReal() &&
  533.                 FastMath.abs(deltaE.getReal())     < thresholdE.getReal() &&
  534.                 FastMath.abs(deltaI.getReal())     < thresholdAngles.getReal() &&
  535.                 FastMath.abs(deltaOmega.getReal()) < thresholdAngles.getReal() &&
  536.                 FastMath.abs(deltaRAAN.getReal())  < thresholdAngles.getReal() &&
  537.                 FastMath.abs(deltaAnom.getReal())  < thresholdAngles.getReal()) {
  538.                 return current;
  539.             }
  540.         }
  541.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS, i);
  542.     }


  543.     /** {@inheritDoc} */
  544.     public FieldKeplerianOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
  545.         // compute Cartesian parameters, taking derivatives into account
  546.         // to make sure velocity and acceleration are consistent
  547.         final FieldBLModel<T> current = models.get(date);
  548.         final FieldUnivariateDerivative2<T>[] propOrb_parameters = current.propagateParameters(date, parameters);
  549.         return new FieldKeplerianOrbit<T>(propOrb_parameters[0].getValue(), propOrb_parameters[1].getValue(),
  550.                                           propOrb_parameters[2].getValue(), propOrb_parameters[3].getValue(),
  551.                                           propOrb_parameters[4].getValue(), propOrb_parameters[5].getValue(),
  552.                                           PositionAngle.MEAN, current.mean.getFrame(), date, mu);
  553.     }

  554.     /**
  555.      * Get the value of the M2 drag parameter.
  556.      * @return the value of the M2 drag parameter
  557.      */
  558.     public double getM2() {
  559.         return M2Driver.getValue();
  560.     }

  561.     /** Local class for Brouwer-Lyddane model. */
  562.     private static class FieldBLModel<T extends CalculusFieldElement<T>> {

  563.         /** Mean orbit. */
  564.         private final FieldKeplerianOrbit<T> mean;

  565.         /** Constant mass. */
  566.         private final T mass;

  567.         // CHECKSTYLE: stop JavadocVariable check

  568.         // preprocessed values

  569.         // Constant for secular terms l'', g'', h''
  570.         // l standing for true anomaly, g for perigee argument and h for raan
  571.         private final T xnotDot;
  572.         private final T n;
  573.         private final T lt;
  574.         private final T gt;
  575.         private final T ht;


  576.         // Long periodT
  577.         private final T dei3sg;
  578.         private final T de2sg;
  579.         private final T deisg;
  580.         private final T de;


  581.         private final T dlgs2g;
  582.         private final T dlgc3g;
  583.         private final T dlgcg;


  584.         private final T dh2sgcg;
  585.         private final T dhsgcg;
  586.         private final T dhcg;


  587.         // Short perioTs
  588.         private final T aC;
  589.         private final T aCbis;
  590.         private final T ac2g2f;


  591.         private final T eC;
  592.         private final T ecf;
  593.         private final T e2cf;
  594.         private final T e3cf;
  595.         private final T ec2f2g;
  596.         private final T ecfc2f2g;
  597.         private final T e2cfc2f2g;
  598.         private final T e3cfc2f2g;
  599.         private final T ec2gf;
  600.         private final T ec2g3f;


  601.         private final T ide;
  602.         private final T isfs2f2g;
  603.         private final T icfc2f2g;
  604.         private final T ic2f2g;


  605.         private final T glf;
  606.         private final T gll;
  607.         private final T glsf;
  608.         private final T glosf;
  609.         private final T gls2f2g;
  610.         private final T gls2gf;
  611.         private final T glos2gf;
  612.         private final T gls2g3f;
  613.         private final T glos2g3f;


  614.         private final T hf;
  615.         private final T hl;
  616.         private final T hsf;
  617.         private final T hcfs2g2f;
  618.         private final T hs2g2f;
  619.         private final T hsfc2g2f;


  620.         private final T edls2g;
  621.         private final T edlcg;
  622.         private final T edlc3g;
  623.         private final T edlsf;
  624.         private final T edls2gf;
  625.         private final T edls2g3f;

  626.         // Drag terms
  627.         private final T aRate;
  628.         private final T eRate;

  629.         // CHECKSTYLE: resume JavadocVariable check

  630.         /** Create a model for specified mean orbit.
  631.          * @param mean mean Fieldorbit
  632.          * @param mass constant mass
  633.          * @param referenceRadius reference radius of the central body attraction model (m)
  634.          * @param mu central attraction coefficient (m³/s²)
  635.          * @param ck0 un-normalized zonal coefficients
  636.          */
  637.         FieldBLModel(final FieldKeplerianOrbit<T> mean, final T mass,
  638.                 final double referenceRadius, final T mu, final double[] ck0) {

  639.             this.mean = mean;
  640.             this.mass = mass;
  641.             final T one  = mass.getField().getOne();

  642.             final T app = mean.getA();
  643.             xnotDot = mu.divide(app).sqrt().divide(app);

  644.             // preliminary processing
  645.             final T q = app.divide(referenceRadius).reciprocal();
  646.             T ql = q.multiply(q);
  647.             final T y2 = ql.multiply(-0.5 * ck0[2]);

  648.             n = ((mean.getE().multiply(mean.getE()).negate()).add(1.0)).sqrt();
  649.             final T n2 = n.multiply(n);
  650.             final T n3 = n2.multiply(n);
  651.             final T n4 = n2.multiply(n2);
  652.             final T n6 = n4.multiply(n2);
  653.             final T n8 = n4.multiply(n4);
  654.             final T n10 = n8.multiply(n2);

  655.             final T yp2 = y2.divide(n4);
  656.             ql = ql.multiply(q);
  657.             final T yp3 = ql.multiply(ck0[3]).divide(n6);
  658.             ql = ql.multiply(q);
  659.             final T yp4 = ql.multiply(0.375 * ck0[4]).divide(n8);
  660.             ql = ql.multiply(q);
  661.             final T yp5 = ql.multiply(ck0[5]).divide(n10);


  662.             final FieldSinCos<T> sc = FastMath.sinCos(mean.getI());
  663.             final T sinI1 = sc.sin();
  664.             final T sinI2 = sinI1.multiply(sinI1);
  665.             final T cosI1 = sc.cos();
  666.             final T cosI2 = cosI1.multiply(cosI1);
  667.             final T cosI3 = cosI2.multiply(cosI1);
  668.             final T cosI4 = cosI2.multiply(cosI2);
  669.             final T cosI6 = cosI4.multiply(cosI2);
  670.             final T C5c2 = T2(cosI1).reciprocal();
  671.             final T C3c2 = cosI2.multiply(3.0).subtract(1.0);

  672.             final T epp = mean.getE();
  673.             final T epp2 = epp.multiply(epp);
  674.             final T epp3 = epp2.multiply(epp);
  675.             final T epp4 = epp2.multiply(epp2);

  676.             if (epp.getReal() >= 1) {
  677.                 // Only for elliptical (e < 1) orbits
  678.                 throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL,
  679.                                           mean.getE().getReal());
  680.             }

  681.             // secular multiplicative
  682.             lt = one.add(yp2.multiply(n).multiply(C3c2).multiply(1.5)).
  683.                      add(yp2.multiply(0.09375).multiply(yp2).multiply(n).multiply(n2.multiply(25.0).add(n.multiply(16.0)).add(-15.0).
  684.                              add(cosI2.multiply(n2.multiply(-90.0).add(n.multiply(-96.0)).add(30.0))).
  685.                              add(cosI4.multiply(n2.multiply(25.0).add(n.multiply(144.0)).add(105.0))))).
  686.                      add(yp4.multiply(0.9375).multiply(n).multiply(epp2).multiply(cosI4.multiply(35.0).add(cosI2.multiply(-30.0)).add(3.0)));

  687.             gt = yp2.multiply(-1.5).multiply(C5c2).
  688.                      add(yp2.multiply(0.09375).multiply(yp2).multiply(n2.multiply(25.0).add(n.multiply(24.0)).add(-35.0).
  689.                             add(cosI2.multiply(n2.multiply(-126.0).add(n.multiply(-192.0)).add(90.0))).
  690.                             add(cosI4.multiply(n2.multiply(45.0).add(n.multiply(360.0)).add(385.0))))).
  691.                      add(yp4.multiply(0.3125).multiply(n2.multiply(-9.0).add(21.0).
  692.                             add(cosI2.multiply(n2.multiply(126.0).add(-270.0))).
  693.                             add(cosI4.multiply(n2.multiply(-189.0).add(385.0)))));

  694.             ht = yp2.multiply(-3.0).multiply(cosI1).
  695.                      add(yp2.multiply(0.375).multiply(yp2).multiply(cosI1.multiply(n2.multiply(9.0).add(n.multiply(12.0)).add(-5.0)).
  696.                                                                     add(cosI3.multiply(n2.multiply(-5.0).add(n.multiply(-36.0)).add(-35.0))))).
  697.                      add(yp4.multiply(1.25).multiply(cosI1).multiply(n2.multiply(-3.0).add(5.0)).multiply(cosI2.multiply(-7.0).add(3.0)));

  698.             final T cA = one.subtract(cosI2.multiply(11.0)).subtract(cosI4.multiply(40.0).divide(C5c2));
  699.             final T cB = one.subtract(cosI2.multiply(3.0)).subtract(cosI4.multiply(8.0).divide(C5c2));
  700.             final T cC = one.subtract(cosI2.multiply(9.0)).subtract(cosI4.multiply(24.0).divide(C5c2));
  701.             final T cD = one.subtract(cosI2.multiply(5.0)).subtract(cosI4.multiply(16.0).divide(C5c2));

  702.             final T qyp2_4 = yp2.multiply(3.0).multiply(yp2).multiply(cA).
  703.                              subtract(yp4.multiply(10.0).multiply(cB));
  704.             final T qyp52 = cosI1.multiply(epp3).multiply(cD.multiply(0.5).divide(sinI1).
  705.                                                           add(sinI1.multiply(cosI4.divide(C5c2).divide(C5c2).multiply(80.0).
  706.                                                                              add(cosI2.divide(C5c2).multiply(32.0).
  707.                                                                              add(5.0)))));
  708.             final T qyp22 = epp2.add(2.0).subtract(epp2.multiply(3.0).add(2.0).multiply(11.0).multiply(cosI2)).
  709.                             subtract(epp2.multiply(5.0).add(2.0).multiply(40.0).multiply(cosI4.divide(C5c2))).
  710.                             subtract(epp2.multiply(400.0).multiply(cosI6).divide(C5c2).divide(C5c2));
  711.             final T qyp42 = one.divide(5.0).multiply(qyp22.
  712.                                                      add(one.multiply(4.0).multiply(epp2.
  713.                                                                                     add(2.0).
  714.                                                                                     subtract(cosI2.multiply(epp2.multiply(3.0).add(2.0))))));
  715.             final T qyp52bis = cosI1.multiply(sinI1).multiply(epp).multiply(epp2.multiply(3.0).add(4.0)).
  716.                                                                    multiply(cosI4.divide(C5c2).divide(C5c2).multiply(40.0).
  717.                                                                             add(cosI2.divide(C5c2).multiply(16.0)).
  718.                                                                             add(3.0));
  719.            // long periodic multiplicative
  720.             dei3sg =  yp5.divide(yp2).multiply(35.0 / 96.0).multiply(epp2).multiply(n2).multiply(cD).multiply(sinI1);
  721.             de2sg = qyp2_4.divide(yp2).multiply(epp).multiply(n2).multiply(-1.0 / 12.0);
  722.             deisg = sinI1.multiply(yp5.multiply(-35.0 / 128.0).divide(yp2).multiply(epp2).multiply(n2).multiply(cD).
  723.                             add(n2.multiply(0.25).divide(yp2).multiply(yp3.add(yp5.multiply(5.0 / 16.0).multiply(epp2.multiply(3.0).add(4.0)).multiply(cC)))));
  724.             de = epp2.multiply(n2).multiply(qyp2_4).divide(24.0).divide(yp2);

  725.             final T qyp52quotient = epp.multiply(epp4.multiply(81.0).add(-32.0)).divide(n.multiply(epp2.multiply(9.0).add(4.0)).add(epp2.multiply(3.0)).add(4.0));
  726.             dlgs2g = yp2.multiply(48.0).reciprocal().multiply(yp2.multiply(-3.0).multiply(yp2).multiply(qyp22).
  727.                             add(yp4.multiply(10.0).multiply(qyp42))).
  728.                             add(n3.divide(yp2).multiply(qyp2_4).divide(24.0));
  729.             dlgc3g =  yp5.multiply(35.0 / 384.0).divide(yp2).multiply(n3).multiply(epp).multiply(cD).multiply(sinI1).
  730.                             add(yp5.multiply(35.0 / 1152.0).divide(yp2).multiply(qyp52.multiply(2.0).multiply(cosI1).subtract(epp.multiply(cD).multiply(sinI1).multiply(epp2.multiply(2.0).add(3.0)))));
  731.             dlgcg = yp3.negate().multiply(cosI2).multiply(epp).divide(yp2.multiply(sinI1).multiply(4.0)).
  732.                     add(yp5.divide(yp2).multiply(0.078125).multiply(cC).multiply(cosI2.divide(sinI1).multiply(epp.negate()).multiply(epp2.multiply(3.0).add(4.0)).
  733.                                                                     add(sinI1.multiply(epp2).multiply(epp2.multiply(9.0).add(26.0)))).
  734.                     subtract(yp5.divide(yp2).multiply(0.46875).multiply(qyp52bis).multiply(cosI1)).
  735.                     add(yp3.divide(yp2).multiply(0.25).multiply(sinI1).multiply(epp).divide(n3.add(1.0)).multiply(epp2.multiply(epp2.subtract(3.0)).add(3.0))).
  736.                     add(yp5.divide(yp2).multiply(0.078125).multiply(n2).multiply(cC).multiply(qyp52quotient).multiply(sinI1)));
  737.             final T qyp24 = yp2.multiply(yp2).multiply(3.0).multiply(cosI4.divide(sinI2).multiply(200.0).add(cosI2.divide(sinI1).multiply(80.0)).add(11.0)).
  738.                             subtract(yp4.multiply(10.0).multiply(cosI4.divide(sinI2).multiply(40.0).add(cosI2.divide(sinI1).multiply(16.0)).add(3.0)));
  739.             dh2sgcg = yp5.divide(yp2).multiply(qyp52).multiply(35.0 / 144.0);
  740.             dhsgcg = qyp24.multiply(cosI1).multiply(epp2.negate()).divide(yp2.multiply(12.0));
  741.             dhcg = yp5.divide(yp2).multiply(qyp52).multiply(-35.0 / 576.0).
  742.                    add(cosI1.multiply(epp).divide(yp2.multiply(sinI1).multiply(4.0)).multiply(yp3.add(yp5.multiply(0.3125).multiply(cC).multiply(epp2.multiply(3.0).add(4.0))))).
  743.                    add(yp5.multiply(qyp52bis).multiply(1.875).divide(yp2.multiply(4.0)));

  744.             // short periodic multiplicative
  745.             aC = yp2.negate().multiply(C3c2).multiply(app).divide(n3);
  746.             aCbis = y2.multiply(app).multiply(C3c2);
  747.             ac2g2f = y2.multiply(app).multiply(sinI2).multiply(3.0);

  748.             T qe = y2.multiply(C3c2).multiply(0.5).multiply(n2).divide(n6);
  749.             eC = qe.multiply(epp).divide(n3.add(1.0)).multiply(epp2.multiply(epp2.subtract(3.0)).add(3.0));
  750.             ecf = qe.multiply(3.0);
  751.             e2cf = qe.multiply(3.0).multiply(epp);
  752.             e3cf = qe.multiply(epp2);
  753.             qe = y2.multiply(0.5).multiply(n2).multiply(3.0).multiply(cosI2.negate().add(1.0)).divide(n6);
  754.             ec2f2g = qe.multiply(epp);
  755.             ecfc2f2g = qe.multiply(3.0);
  756.             e2cfc2f2g = qe.multiply(3.0).multiply(epp);
  757.             e3cfc2f2g = qe.multiply(epp2);
  758.             qe = yp2.multiply(-0.5).multiply(n2).multiply(cosI2.negate().add(1.0));
  759.             ec2gf = qe.multiply(3.0);
  760.             ec2g3f = qe;

  761.             T qi = yp2.multiply(epp).multiply(cosI1).multiply(sinI1);
  762.             ide = cosI1.multiply(epp.negate()).divide(sinI1.multiply(n2));
  763.             isfs2f2g = qi;
  764.             icfc2f2g = qi.multiply(2.0);
  765.             qi = yp2.multiply(cosI1).multiply(sinI1);
  766.             ic2f2g = qi.multiply(1.5);

  767.             T qgl1 = yp2.multiply(0.25);
  768.             T qgl2 =  yp2.multiply(epp).multiply(n2).multiply(0.25).divide(n.add(1.0));
  769.             glf = qgl1.multiply(C5c2).multiply(-6.0);
  770.             gll = qgl1.multiply(C5c2).multiply(6.0);
  771.             glsf = qgl1.multiply(C5c2).multiply(-6.0).multiply(epp).
  772.                    add(qgl2.multiply(C3c2).multiply(2.0));
  773.             glosf = qgl2.multiply(C3c2).multiply(2.0);
  774.             qgl1 = qgl1.multiply(cosI2.multiply(-5.0).add(3.0));
  775.             qgl2 = qgl2.multiply(3.0).multiply(cosI2.negate().add(1.0));
  776.             gls2f2g = qgl1.multiply(3.0);
  777.             gls2gf = qgl1.multiply(3.0).multiply(epp).
  778.                      add(qgl2);
  779.             glos2gf = qgl2.negate();
  780.             gls2g3f = qgl1.multiply(epp).
  781.                       add(qgl2.multiply(1.0 / 3.0));
  782.             glos2g3f = qgl2;

  783.             final T qh = yp2.multiply(cosI1).multiply(3.0);
  784.             hf = qh.negate();
  785.             hl = qh;
  786.             hsf = qh.multiply(epp).negate();
  787.             hcfs2g2f = yp2.multiply(cosI1).multiply(epp).multiply(2.0);
  788.             hs2g2f = yp2.multiply(cosI1).multiply(1.5);
  789.             hsfc2g2f = yp2.multiply(cosI1).multiply(epp).negate();

  790.             final T qedl = yp2.multiply(n3).multiply(-0.25);
  791.             edls2g = qyp2_4.multiply(1.0 / 24.0).multiply(epp).multiply(n3).divide(yp2);
  792.             edlcg = yp3.divide(yp2).multiply(-0.25).multiply(n3).multiply(sinI1).
  793.                     subtract(yp5.divide(yp2).multiply(0.078125).multiply(n3).multiply(sinI1).multiply(cC).multiply(epp2.multiply(9.0).add(4.0)));
  794.             edlc3g = yp5.divide(yp2).multiply(n3).multiply(epp2).multiply(cD).multiply(sinI1).multiply(35.0 / 384.0);
  795.             edlsf = qedl.multiply(C3c2).multiply(2.0);
  796.             edls2gf = qedl.multiply(3.0).multiply(cosI2.negate().add(1.0));
  797.             edls2g3f = qedl.multiply(1.0 / 3.0);

  798.             // secular rates of the mean semi-major axis and eccentricity
  799.             // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis
  800.             aRate = app.multiply(-4.0).divide(xnotDot.multiply(3.0));
  801.             eRate = epp.multiply(n).multiply(n).multiply(-4.0).divide(xnotDot.multiply(3.0));

  802.         }

  803.         /**
  804.          * Accurate computation of E - e sin(E).
  805.          *
  806.          * @param E eccentric anomaly
  807.          * @return E - e sin(E)
  808.          */
  809.         private FieldUnivariateDerivative2<T> eMeSinE(final FieldUnivariateDerivative2<T> E) {
  810.             FieldUnivariateDerivative2<T> x = E.sin().multiply(mean.getE().negate().add(1.0));
  811.             final FieldUnivariateDerivative2<T> mE2 = E.negate().multiply(E);
  812.             FieldUnivariateDerivative2<T> term = E;
  813.             FieldUnivariateDerivative2<T> d    = E.getField().getZero();
  814.             // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance
  815.             for (FieldUnivariateDerivative2<T> x0 = d.add(Double.NaN); !Double.valueOf(x.getValue().getReal()).equals(Double.valueOf(x0.getValue().getReal()));) {
  816.                 d = d.add(2);
  817.                 term = term.multiply(mE2.divide(d.multiply(d.add(1))));
  818.                 x0 = x;
  819.                 x = x.subtract(term);
  820.             }
  821.             return x;
  822.         }

  823.         /**
  824.          * Gets eccentric anomaly from mean anomaly.
  825.          * <p>The algorithm used to solve the Kepler equation has been published in:
  826.          * "Procedures for  solving Kepler's Equation", A. W. Odell and R. H. Gooding,
  827.          * Celestial Mechanics 38 (1986) 307-334</p>
  828.          * <p>It has been copied from the OREKIT library (KeplerianOrbit class).</p>
  829.          *
  830.          * @param mk the mean anomaly (rad)
  831.          * @return the eccentric anomaly (rad)
  832.          */
  833.         private FieldUnivariateDerivative2<T> getEccentricAnomaly(final FieldUnivariateDerivative2<T> mk) {
  834.             final double k1 = 3 * FastMath.PI + 2;
  835.             final double k2 = FastMath.PI - 1;
  836.             final double k3 = 6 * FastMath.PI - 1;
  837.             final double A  = 3.0 * k2 * k2 / k1;
  838.             final double B  = k3 * k3 / (6.0 * k1);

  839.             final T zero = mean.getE().getField().getZero();

  840.             // reduce M to [-PI PI] interval
  841.             final FieldUnivariateDerivative2<T> reducedM = new FieldUnivariateDerivative2<T>(MathUtils.normalizeAngle(mk.getValue(), zero ),
  842.                                                                              mk.getFirstDerivative(),
  843.                                                                              mk.getSecondDerivative());

  844.             // compute start value according to A. W. Odell and R. H. Gooding S12 starter
  845.             FieldUnivariateDerivative2<T> ek;
  846.             if (reducedM.getValue().abs().getReal() < 1.0 / 6.0) {
  847.                 if (reducedM.getValue().abs().getReal() < Precision.SAFE_MIN) {
  848.                     // this is an Orekit change to the S12 starter.
  849.                     // If reducedM is 0.0, the derivative of cbrt is infinite which induces NaN appearing later in
  850.                     // the computation. As in this case E and M are almost equal, we initialize ek with reducedM
  851.                     ek = reducedM;
  852.                 } else {
  853.                     // this is the standard S12 starter
  854.                     ek = reducedM.add(reducedM.multiply(6).cbrt().subtract(reducedM).multiply(mean.getE()));
  855.                 }
  856.             } else {
  857.                 if (reducedM.getValue().getReal() < 0) {
  858.                     final FieldUnivariateDerivative2<T> w = reducedM.add(FastMath.PI);
  859.                     ek = reducedM.add(w.multiply(-A).divide(w.subtract(B)).subtract(FastMath.PI).subtract(reducedM).multiply(mean.getE()));
  860.                 } else {
  861.                     final FieldUnivariateDerivative2<T> minusW = reducedM.subtract(FastMath.PI);
  862.                     ek = reducedM.add(minusW.multiply(A).divide(minusW.add(B)).add(FastMath.PI).subtract(reducedM).multiply(mean.getE()));
  863.                 }
  864.             }

  865.             final T e1 = mean.getE().negate().add(1.0);
  866.             final boolean noCancellationRisk = (e1.add(ek.getValue().multiply(ek.getValue())).getReal() / 6) >= 0.1;

  867.             // perform two iterations, each consisting of one Halley step and one Newton-Raphson step
  868.             for (int j = 0; j < 2; ++j) {
  869.                 final FieldUnivariateDerivative2<T> f;
  870.                 FieldUnivariateDerivative2<T> fd;
  871.                 final FieldUnivariateDerivative2<T> fdd  = ek.sin().multiply(mean.getE());
  872.                 final FieldUnivariateDerivative2<T> fddd = ek.cos().multiply(mean.getE());
  873.                 if (noCancellationRisk) {
  874.                     f  = ek.subtract(fdd).subtract(reducedM);
  875.                     fd = fddd.subtract(1).negate();
  876.                 } else {
  877.                     f  = eMeSinE(ek).subtract(reducedM);
  878.                     final FieldUnivariateDerivative2<T> s = ek.multiply(0.5).sin();
  879.                     fd = s.multiply(s).multiply(mean.getE().multiply(2.0)).add(e1);
  880.                 }
  881.                 final FieldUnivariateDerivative2<T> dee = f.multiply(fd).divide(f.multiply(0.5).multiply(fdd).subtract(fd.multiply(fd)));

  882.                 // update eccentric anomaly, using expressions that limit underflow problems
  883.                 final FieldUnivariateDerivative2<T> w = fd.add(dee.multiply(0.5).multiply(fdd.add(dee.multiply(fdd).divide(3))));
  884.                 fd = fd.add(dee.multiply(fdd.add(dee.multiply(0.5).multiply(fdd))));
  885.                 ek = ek.subtract(f.subtract(dee.multiply(fd.subtract(w))).divide(fd));
  886.             }

  887.             // expand the result back to original range
  888.             ek = ek.add(mk.getValue().subtract(reducedM.getValue()));

  889.             // Returns the eccentric anomaly
  890.             return ek;
  891.         }

  892.         /**
  893.          * This method is used in Brouwer-Lyddane model to avoid singularity at the
  894.          * critical inclination (i = 63.4°).
  895.          * <p>
  896.          * This method, based on Warren Phipps's 1992 thesis (Eq. 2.47 and 2.48),
  897.          * approximate the factor (1.0 - 5.0 * cos²(inc))^-1 (causing the singularity)
  898.          * by a function, named T2 in the thesis.
  899.          * </p>
  900.          * @param cosInc cosine of the mean inclination
  901.          * @return an approximation of (1.0 - 5.0 * cos²(inc))^-1 term
  902.          */
  903.         private T T2(final T cosInc) {

  904.             // X = (1.0 - 5.0 * cos²(inc))
  905.             final T x  = cosInc.multiply(cosInc).multiply(-5.0).add(1.0);
  906.             final T x2 = x.multiply(x);

  907.             // Eq. 2.48
  908.             T sum = x.getField().getZero();
  909.             for (int i = 0; i <= 12; i++) {
  910.                 final double sign = i % 2 == 0 ? +1.0 : -1.0;
  911.                 sum = sum.add(FastMath.pow(x2, i).multiply(FastMath.pow(BETA, i)).multiply(sign).divide(CombinatoricsUtils.factorialDouble(i + 1)));
  912.             }

  913.             // Right term of equation 2.47
  914.             T product = x.getField().getOne();
  915.             for (int i = 0; i <= 10; i++) {
  916.                 product = product.multiply(FastMath.exp(x2.multiply(BETA).multiply(FastMath.scalb(-1.0, i))).add(1.0));
  917.             }

  918.             // Return (Eq. 2.47)
  919.             return x.multiply(BETA).multiply(sum).multiply(product);

  920.         }

  921.         /** Extrapolate an orbit up to a specific target date.
  922.          * @param date target date for the orbit
  923.          * @param parameters model parameters
  924.          * @return propagated parameters
  925.          */
  926.         public FieldUnivariateDerivative2<T>[] propagateParameters(final FieldAbsoluteDate<T> date, final T[] parameters) {

  927.             // Field
  928.             final Field<T> field = date.getField();
  929.             final T one  = field.getOne();
  930.             final T zero = field.getZero();

  931.             // Empirical drag coefficient M2
  932.             final T m2 = parameters[0];

  933.             // Keplerian evolution
  934.             final FieldUnivariateDerivative2<T> dt = new FieldUnivariateDerivative2<>(date.durationFrom(mean.getDate()), one, zero);
  935.             final FieldUnivariateDerivative2<T> xnot = dt.multiply(xnotDot);

  936.             //____________________________________
  937.             // secular effects

  938.             // mean mean anomaly
  939.             final FieldUnivariateDerivative2<T> dtM2  = dt.multiply(m2);
  940.             final FieldUnivariateDerivative2<T> dt2M2 = dt.multiply(dtM2);
  941.             final FieldUnivariateDerivative2<T> lpp = new FieldUnivariateDerivative2<T>(MathUtils.normalizeAngle(mean.getMeanAnomaly().add(lt.multiply(xnot.getValue())).add(dt2M2.getValue()),
  942.                                                                                         one.getPi()),
  943.                                                                                         lt.multiply(xnotDot).add(dtM2.multiply(2.0).getValue()),
  944.                                                                                         m2.multiply(2.0));
  945.             // mean argument of perigee
  946.             final FieldUnivariateDerivative2<T> gpp = new FieldUnivariateDerivative2<T>(MathUtils.normalizeAngle(mean.getPerigeeArgument().add(gt.multiply(xnot.getValue())),
  947.                                                                                         one.getPi()),
  948.                                                                                         gt.multiply(xnotDot),
  949.                                                                                         zero);
  950.             // mean longitude of ascending node
  951.             final FieldUnivariateDerivative2<T> hpp = new FieldUnivariateDerivative2<T>(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(ht.multiply(xnot.getValue())),
  952.                                                                                         one.getPi()),
  953.                                                                                         ht.multiply(xnotDot),
  954.                                                                                         zero);

  955.             // ________________________________________________
  956.             // secular rates of the mean semi-major axis and eccentricity

  957.             // semi-major axis
  958.             final FieldUnivariateDerivative2<T> appDrag = dt.multiply(aRate.multiply(m2));

  959.             // eccentricity
  960.             final FieldUnivariateDerivative2<T> eppDrag = dt.multiply(eRate.multiply(m2));

  961.             //____________________________________
  962.             // Long periodical terms
  963.             final FieldUnivariateDerivative2<T> cg1 = gpp.cos();
  964.             final FieldUnivariateDerivative2<T> sg1 = gpp.sin();
  965.             final FieldUnivariateDerivative2<T> c2g = cg1.multiply(cg1).subtract(sg1.multiply(sg1));
  966.             final FieldUnivariateDerivative2<T> s2g = cg1.multiply(sg1).add(sg1.multiply(cg1));
  967.             final FieldUnivariateDerivative2<T> c3g = c2g.multiply(cg1).subtract(s2g.multiply(sg1));
  968.             final FieldUnivariateDerivative2<T> sg2 = sg1.multiply(sg1);
  969.             final FieldUnivariateDerivative2<T> sg3 = sg1.multiply(sg2);


  970.             // de eccentricity
  971.             final FieldUnivariateDerivative2<T> d1e = sg3.multiply(dei3sg).
  972.                                                add(sg1.multiply(deisg)).
  973.                                                add(sg2.multiply(de2sg)).
  974.                                                add(de);

  975.             // l' + g'
  976.             final FieldUnivariateDerivative2<T> lp_p_gp = s2g.multiply(dlgs2g).
  977.                                                add(c3g.multiply(dlgc3g)).
  978.                                                add(cg1.multiply(dlgcg)).
  979.                                                add(lpp).
  980.                                                add(gpp);

  981.             // h'
  982.             final FieldUnivariateDerivative2<T> hp = sg2.multiply(cg1).multiply(dh2sgcg).
  983.                                                add(sg1.multiply(cg1).multiply(dhsgcg)).
  984.                                                add(cg1.multiply(dhcg)).
  985.                                                add(hpp);

  986.             // Short periodical terms
  987.             // eccentric anomaly
  988.             final FieldUnivariateDerivative2<T> Ep = getEccentricAnomaly(lpp);
  989.             final FieldUnivariateDerivative2<T> cf1 = (Ep.cos().subtract(mean.getE())).divide(Ep.cos().multiply(mean.getE().negate()).add(1.0));
  990.             final FieldUnivariateDerivative2<T> sf1 = (Ep.sin().multiply(n)).divide(Ep.cos().multiply(mean.getE().negate()).add(1.0));
  991.             final FieldUnivariateDerivative2<T> f = FastMath.atan2(sf1, cf1);

  992.             final FieldUnivariateDerivative2<T> c2f = cf1.multiply(cf1).subtract(sf1.multiply(sf1));
  993.             final FieldUnivariateDerivative2<T> s2f = cf1.multiply(sf1).add(sf1.multiply(cf1));
  994.             final FieldUnivariateDerivative2<T> c3f = c2f.multiply(cf1).subtract(s2f.multiply(sf1));
  995.             final FieldUnivariateDerivative2<T> s3f = c2f.multiply(sf1).add(s2f.multiply(cf1));
  996.             final FieldUnivariateDerivative2<T> cf2 = cf1.multiply(cf1);
  997.             final FieldUnivariateDerivative2<T> cf3 = cf1.multiply(cf2);

  998.             final FieldUnivariateDerivative2<T> c2g1f = cf1.multiply(c2g).subtract(sf1.multiply(s2g));
  999.             final FieldUnivariateDerivative2<T> c2g2f = c2f.multiply(c2g).subtract(s2f.multiply(s2g));
  1000.             final FieldUnivariateDerivative2<T> c2g3f = c3f.multiply(c2g).subtract(s3f.multiply(s2g));
  1001.             final FieldUnivariateDerivative2<T> s2g1f = cf1.multiply(s2g).add(c2g.multiply(sf1));
  1002.             final FieldUnivariateDerivative2<T> s2g2f = c2f.multiply(s2g).add(c2g.multiply(s2f));
  1003.             final FieldUnivariateDerivative2<T> s2g3f = c3f.multiply(s2g).add(c2g.multiply(s3f));

  1004.             final FieldUnivariateDerivative2<T> eE = (Ep.cos().multiply(mean.getE().negate()).add(1.0)).reciprocal();
  1005.             final FieldUnivariateDerivative2<T> eE3 = eE.multiply(eE).multiply(eE);
  1006.             final FieldUnivariateDerivative2<T> sigma = eE.multiply(n.multiply(n)).multiply(eE).add(eE);

  1007.             // Semi-major axis
  1008.             final FieldUnivariateDerivative2<T> a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())).
  1009.                                             add(aC).
  1010.                                             add(eE3.multiply(c2g2f).multiply(ac2g2f));

  1011.             // Eccentricity
  1012.             final FieldUnivariateDerivative2<T> e = d1e.add(eppDrag.add(mean.getE())).
  1013.                                             add(eC).
  1014.                                             add(cf1.multiply(ecf)).
  1015.                                             add(cf2.multiply(e2cf)).
  1016.                                             add(cf3.multiply(e3cf)).
  1017.                                             add(c2g2f.multiply(ec2f2g)).
  1018.                                             add(c2g2f.multiply(cf1).multiply(ecfc2f2g)).
  1019.                                             add(c2g2f.multiply(cf2).multiply(e2cfc2f2g)).
  1020.                                             add(c2g2f.multiply(cf3).multiply(e3cfc2f2g)).
  1021.                                             add(c2g1f.multiply(ec2gf)).
  1022.                                             add(c2g3f.multiply(ec2g3f));

  1023.             // Inclination
  1024.             final FieldUnivariateDerivative2<T> i = d1e.multiply(ide).
  1025.                                             add(mean.getI()).
  1026.                                             add(sf1.multiply(s2g2f.multiply(isfs2f2g))).
  1027.                                             add(cf1.multiply(c2g2f.multiply(icfc2f2g))).
  1028.                                             add(c2g2f.multiply(ic2f2g));

  1029.             // Argument of perigee + True anomaly
  1030.             final FieldUnivariateDerivative2<T> g_p_l = lp_p_gp.add(f.multiply(glf)).
  1031.                                              add(lpp.multiply(gll)).
  1032.                                              add(sf1.multiply(glsf)).
  1033.                                              add(sigma.multiply(sf1).multiply(glosf)).
  1034.                                              add(s2g2f.multiply(gls2f2g)).
  1035.                                              add(s2g1f.multiply(gls2gf)).
  1036.                                              add(sigma.multiply(s2g1f).multiply(glos2gf)).
  1037.                                              add(s2g3f.multiply(gls2g3f)).
  1038.                                              add(sigma.multiply(s2g3f).multiply(glos2g3f));


  1039.             // Longitude of ascending node
  1040.             final FieldUnivariateDerivative2<T> h = hp.add(f.multiply(hf)).
  1041.                                             add(lpp.multiply(hl)).
  1042.                                             add(sf1.multiply(hsf)).
  1043.                                             add(cf1.multiply(s2g2f).multiply(hcfs2g2f)).
  1044.                                             add(s2g2f.multiply(hs2g2f)).
  1045.                                             add(c2g2f.multiply(sf1).multiply(hsfc2g2f));

  1046.             final FieldUnivariateDerivative2<T> edl = s2g.multiply(edls2g).
  1047.                                             add(cg1.multiply(edlcg)).
  1048.                                             add(c3g.multiply(edlc3g)).
  1049.                                             add(sf1.multiply(edlsf)).
  1050.                                             add(s2g1f.multiply(edls2gf)).
  1051.                                             add(s2g3f.multiply(edls2g3f)).
  1052.                                             add(sf1.multiply(sigma).multiply(edlsf)).
  1053.                                             add(s2g1f.multiply(sigma).multiply(edls2gf.negate())).
  1054.                                             add(s2g3f.multiply(sigma).multiply(edls2g3f.multiply(3.0)));

  1055.             final FieldUnivariateDerivative2<T> A = e.multiply(lpp.cos()).subtract(edl.multiply(lpp.sin()));
  1056.             final FieldUnivariateDerivative2<T> B = e.multiply(lpp.sin()).add(edl.multiply(lpp.cos()));

  1057.             // True anomaly
  1058.             final FieldUnivariateDerivative2<T> l = FastMath.atan2(B, A);

  1059.             // Argument of perigee
  1060.             final FieldUnivariateDerivative2<T> g = g_p_l.subtract(l);

  1061.             final FieldUnivariateDerivative2<T>[] FTD = MathArrays.buildArray(g.getField(), 6);
  1062.             FTD[0] = a;
  1063.             FTD[1] = e;
  1064.             FTD[2] = i;
  1065.             FTD[3] = g;
  1066.             FTD[4] = h;
  1067.             FTD[5] = l;
  1068.             return FTD;
  1069.         }
  1070.     }

  1071.     /** {@inheritDoc} */
  1072.     @Override
  1073.     protected T getMass(final FieldAbsoluteDate<T> date) {
  1074.         return models.get(date).mass;
  1075.     }

  1076.     /** {@inheritDoc} */
  1077.     @Override
  1078.     protected List<ParameterDriver> getParametersDrivers() {
  1079.         return Collections.singletonList(M2Driver);
  1080.     }

  1081. }