FieldBrouwerLyddanePropagator.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 java.util.Collections;
  19. import java.util.List;

  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.Field;
  22. import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1;
  23. import org.hipparchus.util.CombinatoricsUtils;
  24. import org.hipparchus.util.FastMath;
  25. import org.hipparchus.util.FieldSinCos;
  26. import org.hipparchus.util.MathUtils;
  27. import org.orekit.attitudes.AttitudeProvider;
  28. import org.orekit.attitudes.FrameAlignedProvider;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  32. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  33. import org.orekit.orbits.FieldKeplerianAnomalyUtility;
  34. import org.orekit.orbits.FieldKeplerianOrbit;
  35. import org.orekit.orbits.FieldOrbit;
  36. import org.orekit.orbits.OrbitType;
  37. import org.orekit.orbits.PositionAngleType;
  38. import org.orekit.propagation.FieldSpacecraftState;
  39. import org.orekit.propagation.PropagationType;
  40. import org.orekit.propagation.analytical.tle.FieldTLE;
  41. import org.orekit.propagation.conversion.osc2mean.BrouwerLyddaneTheory;
  42. import org.orekit.propagation.conversion.osc2mean.FixedPointConverter;
  43. import org.orekit.propagation.conversion.osc2mean.MeanTheory;
  44. import org.orekit.propagation.conversion.osc2mean.OsculatingToMeanConverter;
  45. import org.orekit.time.AbsoluteDate;
  46. import org.orekit.time.FieldAbsoluteDate;
  47. import org.orekit.utils.FieldTimeSpanMap;
  48. import org.orekit.utils.ParameterDriver;

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

  94.     /** Parameters scaling factor.
  95.      * <p>
  96.      * We use a power of 2 to avoid numeric noise introduction
  97.      * in the multiplications/divisions sequences.
  98.      * </p>
  99.      */
  100.     private static final double SCALE = FastMath.scalb(1.0, -32);

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

  103.     /** Max value for the eccentricity. */
  104.     private static final double MAX_ECC = 0.999999;

  105.     /** Initial Brouwer-Lyddane model. */
  106.     private FieldBLModel<T> initialModel;

  107.     /** All models. */
  108.     private transient FieldTimeSpanMap<FieldBLModel<T>, T> models;

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

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

  113.     /** Un-normalized zonal coefficients. */
  114.     private double[] ck0;

  115.     /** Empirical coefficient used in the drag modeling. */
  116.     private final ParameterDriver M2Driver;

  117.     /** Build a propagator from orbit and potential provider.
  118.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  119.      *
  120.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  121.      *
  122.      * @param initialOrbit initial orbit
  123.      * @param provider for un-normalized zonal coefficients
  124.      * @param m2Value value of empirical drag coefficient in rad/s².
  125.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  126.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, UnnormalizedSphericalHarmonicsProvider, PropagationType, double)
  127.      */
  128.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  129.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  130.                                          final double m2Value) {
  131.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  132.              initialOrbit.getMu().newInstance(DEFAULT_MASS), provider,
  133.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), m2Value);
  134.     }

  135.     /**
  136.      * Private helper constructor.
  137.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  138.      * @param initialOrbit initial orbit
  139.      * @param attitude attitude provider
  140.      * @param mass spacecraft mass
  141.      * @param provider for un-normalized zonal coefficients
  142.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  143.      * @param m2Value value of empirical drag coefficient in rad/s².
  144.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  145.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement,
  146.      * UnnormalizedSphericalHarmonicsProvider, UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics, PropagationType, double)
  147.      */
  148.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  149.                                          final AttitudeProvider attitude,
  150.                                          final T mass,
  151.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  152.                                          final UnnormalizedSphericalHarmonics harmonics,
  153.                                          final double m2Value) {
  154.         this(initialOrbit, attitude,  mass, provider.getAe(), initialOrbit.getMu().newInstance(provider.getMu()),
  155.              harmonics.getUnnormalizedCnm(2, 0),
  156.              harmonics.getUnnormalizedCnm(3, 0),
  157.              harmonics.getUnnormalizedCnm(4, 0),
  158.              harmonics.getUnnormalizedCnm(5, 0),
  159.              m2Value);
  160.     }

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

  199.     /** Build a propagator from orbit, mass and potential provider.
  200.      * <p>Attitude law is set to an unspecified non-null arbitrary value.</p>
  201.      *
  202.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  203.      *
  204.      * @param initialOrbit initial orbit
  205.      * @param mass spacecraft mass
  206.      * @param provider for un-normalized zonal coefficients
  207.      * @param m2Value value of empirical drag coefficient in rad/s².
  208.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  209.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement, UnnormalizedSphericalHarmonicsProvider, double)
  210.      */
  211.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  212.                                          final T mass,
  213.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  214.                                          final double m2Value) {
  215.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  216.              mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), m2Value);
  217.     }

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

  251.     /** Build a propagator from orbit, attitude provider and potential provider.
  252.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  253.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  254.      * @param initialOrbit initial orbit
  255.      * @param attitudeProv attitude provider
  256.      * @param provider for un-normalized zonal coefficients
  257.      * @param m2Value value of empirical drag coefficient in rad/s².
  258.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  259.      */
  260.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  261.                                          final AttitudeProvider attitudeProv,
  262.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  263.                                          final double m2Value) {
  264.         this(initialOrbit, attitudeProv, initialOrbit.getMu().newInstance(DEFAULT_MASS), provider,
  265.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), m2Value);
  266.     }

  267.     /** Build a propagator from orbit, attitude provider and potential.
  268.      * <p>Mass is set to an unspecified non-null arbitrary value.</p>
  269.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  270.      * are related to both the normalized coefficients
  271.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  272.      *  and the J<sub>n</sub> one as follows:</p>
  273.      *
  274.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  275.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  276.      *
  277.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  278.      *
  279.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  280.      *
  281.      * @param initialOrbit initial orbit
  282.      * @param attitudeProv attitude provider
  283.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  284.      * @param mu central attraction coefficient (m³/s²)
  285.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  286.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  287.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  288.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  289.      * @param m2Value value of empirical drag coefficient in rad/s².
  290.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  291.      */
  292.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  293.                                          final AttitudeProvider attitudeProv,
  294.                                          final double referenceRadius, final T mu,
  295.                                          final double c20, final double c30, final double c40,
  296.                                          final double c50, final double m2Value) {
  297.         this(initialOrbit, attitudeProv, initialOrbit.getMu().newInstance(DEFAULT_MASS),
  298.              referenceRadius, mu, c20, c30, c40, c50, m2Value);
  299.     }

  300.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  301.      * <p>Using this constructor, an initial osculating orbit is considered.</p>
  302.      * @param initialOrbit initial orbit
  303.      * @param attitudeProv attitude provider
  304.      * @param mass spacecraft mass
  305.      * @param provider for un-normalized zonal coefficients
  306.      * @param m2Value value of empirical drag coefficient in rad/s².
  307.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  308.      * @see #FieldBrouwerLyddanePropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement, UnnormalizedSphericalHarmonicsProvider, PropagationType, double)
  309.      */
  310.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  311.                                          final AttitudeProvider attitudeProv,
  312.                                          final T mass,
  313.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  314.                                          final double m2Value) {
  315.         this(initialOrbit, attitudeProv, mass, provider, provider.onDate(initialOrbit.getDate().toAbsoluteDate()), m2Value);
  316.     }

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


  351.     /** Build a propagator from orbit and potential provider.
  352.      * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
  353.      *
  354.      * <p>Using this constructor, it is possible to define the initial orbit as
  355.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  356.      *
  357.      * @param initialOrbit initial orbit
  358.      * @param provider for un-normalized zonal coefficients
  359.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  360.      * @param m2Value value of empirical drag coefficient in rad/s².
  361.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  362.      */
  363.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  364.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  365.                                          final PropagationType initialType,
  366.                                          final double m2Value) {
  367.         this(initialOrbit, FrameAlignedProvider.of(initialOrbit.getFrame()),
  368.              initialOrbit.getMu().newInstance(DEFAULT_MASS), provider,
  369.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, m2Value);
  370.     }

  371.     /** Build a propagator from orbit, attitude provider, mass and potential provider.
  372.      * <p>Using this constructor, it is possible to define the initial orbit as
  373.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  374.      * @param initialOrbit initial orbit
  375.      * @param attitudeProv attitude provider
  376.      * @param mass spacecraft mass
  377.      * @param provider for un-normalized zonal coefficients
  378.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  379.      * @param m2Value value of empirical drag coefficient in rad/s².
  380.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  381.      */
  382.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  383.                                          final AttitudeProvider attitudeProv,
  384.                                          final T mass,
  385.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  386.                                          final PropagationType initialType,
  387.                                          final double m2Value) {
  388.         this(initialOrbit, attitudeProv, mass, provider,
  389.              provider.onDate(initialOrbit.getDate().toAbsoluteDate()), initialType, m2Value);
  390.     }

  391.     /**
  392.      * Private helper constructor.
  393.      * <p>Using this constructor, it is possible to define the initial orbit as
  394.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  395.      * @param initialOrbit initial orbit
  396.      * @param attitude attitude provider
  397.      * @param mass spacecraft mass
  398.      * @param provider for un-normalized zonal coefficients
  399.      * @param harmonics {@code provider.onDate(initialOrbit.getDate())}
  400.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  401.      * @param m2Value value of empirical drag coefficient in rad/s².
  402.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  403.      */
  404.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  405.                                          final AttitudeProvider attitude,
  406.                                          final T mass,
  407.                                          final UnnormalizedSphericalHarmonicsProvider provider,
  408.                                          final UnnormalizedSphericalHarmonics harmonics,
  409.                                          final PropagationType initialType,
  410.                                          final double m2Value) {
  411.         this(initialOrbit, attitude, mass, provider.getAe(), initialOrbit.getMu().newInstance(provider.getMu()),
  412.              harmonics.getUnnormalizedCnm(2, 0),
  413.              harmonics.getUnnormalizedCnm(3, 0),
  414.              harmonics.getUnnormalizedCnm(4, 0),
  415.              harmonics.getUnnormalizedCnm(5, 0),
  416.              initialType, m2Value);
  417.     }

  418.     /** Build a propagator from orbit, attitude provider, mass and potential.
  419.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  420.      * are related to both the normalized coefficients
  421.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  422.      *  and the J<sub>n</sub> one as follows:</p>
  423.      *
  424.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  425.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  426.      *
  427.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  428.      *
  429.      * <p>Using this constructor, it is possible to define the initial orbit as
  430.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  431.      *
  432.      * @param initialOrbit initial orbit
  433.      * @param attitudeProv attitude provider
  434.      * @param mass spacecraft mass
  435.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  436.      * @param mu central attraction coefficient (m³/s²)
  437.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  438.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  439.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  440.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  441.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  442.      * @param m2Value value of empirical drag coefficient in rad/s².
  443.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  444.      */
  445.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  446.                                          final AttitudeProvider attitudeProv,
  447.                                          final T mass,
  448.                                          final double referenceRadius, final T mu,
  449.                                          final double c20, final double c30, final double c40,
  450.                                          final double c50,
  451.                                          final PropagationType initialType,
  452.                                          final double m2Value) {
  453.         this(initialOrbit, attitudeProv, mass, referenceRadius, mu,
  454.              c20, c30, c40, c50, initialType, m2Value,
  455.              new FixedPointConverter(BrouwerLyddanePropagator.EPSILON_DEFAULT,
  456.                                      BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT,
  457.                                      FixedPointConverter.DEFAULT_DAMPING));
  458.     }

  459.     /** Build a propagator from orbit, attitude provider, mass and potential.
  460.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  461.      * are related to both the normalized coefficients
  462.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  463.      *  and the J<sub>n</sub> one as follows:</p>
  464.      *
  465.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  466.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  467.      *
  468.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  469.      *
  470.      * <p>Using this constructor, it is possible to define the initial orbit as
  471.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  472.      *
  473.      * @param initialOrbit initial orbit
  474.      * @param attitudeProv attitude provider
  475.      * @param mass spacecraft mass
  476.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  477.      * @param mu central attraction coefficient (m³/s²)
  478.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  479.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  480.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  481.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  482.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  483.      * @param m2Value value of empirical drag coefficient in rad/s².
  484.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  485.      * @param epsilon convergence threshold for mean parameters conversion
  486.      * @param maxIterations maximum iterations for mean parameters conversion
  487.      * @since 11.2
  488.      */
  489.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  490.                                          final AttitudeProvider attitudeProv,
  491.                                          final T mass,
  492.                                          final double referenceRadius,
  493.                                          final T mu,
  494.                                          final double c20,
  495.                                          final double c30,
  496.                                          final double c40,
  497.                                          final double c50,
  498.                                          final PropagationType initialType,
  499.                                          final double m2Value,
  500.                                          final double epsilon,
  501.                                          final int maxIterations) {
  502.         this(initialOrbit, attitudeProv, mass, referenceRadius, mu, c20, c30, c40, c50,
  503.              initialType, m2Value, new FixedPointConverter(epsilon, maxIterations,
  504.                                                            FixedPointConverter.DEFAULT_DAMPING));
  505.     }

  506.     /** Build a propagator from orbit, attitude provider, mass and potential.
  507.      * <p>The C<sub>n,0</sub> coefficients are the denormalized zonal coefficients, they
  508.      * are related to both the normalized coefficients
  509.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  510.      *  and the J<sub>n</sub> one as follows:</p>
  511.      *
  512.      * <p> C<sub>n,0</sub> = [(2-δ<sub>0,m</sub>)(2n+1)(n-m)!/(n+m)!]<sup>½</sup>
  513.      * <span style="text-decoration: overline">C</span><sub>n,0</sub>
  514.      *
  515.      * <p> C<sub>n,0</sub> = -J<sub>n</sub>
  516.      *
  517.      * <p>Using this constructor, it is possible to define the initial orbit as
  518.      * a mean Brouwer-Lyddane orbit or an osculating one.</p>
  519.      *
  520.      * @param initialOrbit initial orbit
  521.      * @param attitudeProv attitude provider
  522.      * @param mass spacecraft mass
  523.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  524.      * @param mu central attraction coefficient (m³/s²)
  525.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  526.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  527.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  528.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  529.      * @param initialType initial orbit type (mean Brouwer-Lyddane orbit or osculating orbit)
  530.      * @param m2Value value of empirical drag coefficient in rad/s².
  531.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  532.      * @param converter osculating to mean orbit converter
  533.      * @since 13.0
  534.      */
  535.     public FieldBrouwerLyddanePropagator(final FieldOrbit<T> initialOrbit,
  536.                                          final AttitudeProvider attitudeProv,
  537.                                          final T mass,
  538.                                          final double referenceRadius,
  539.                                          final T mu,
  540.                                          final double c20,
  541.                                          final double c30,
  542.                                          final double c40,
  543.                                          final double c50,
  544.                                          final PropagationType initialType,
  545.                                          final double m2Value,
  546.                                          final OsculatingToMeanConverter converter) {

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

  548.         // store model coefficients
  549.         this.referenceRadius = referenceRadius;
  550.         this.mu  = mu;
  551.         this.ck0 = new double[] {0.0, 0.0, c20, c30, c40, c50};

  552.         // initialize M2 driver
  553.         this.M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, m2Value, SCALE,
  554.                                             Double.NEGATIVE_INFINITY,
  555.                                             Double.POSITIVE_INFINITY);

  556.         // compute mean parameters if needed
  557.         resetInitialState(new FieldSpacecraftState<>(initialOrbit,
  558.                                                      attitudeProv.getAttitude(initialOrbit,
  559.                                                                               initialOrbit.getDate(),
  560.                                                                               initialOrbit.getFrame())).withMass(mass),
  561.                           initialType, converter);

  562.     }

  563.     /** Conversion from osculating to mean orbit.
  564.      * <p>
  565.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  566.      * osculating SpacecraftState in input.
  567.      * </p>
  568.      * <p>
  569.      * Since the osculating orbit is obtained with the computation of
  570.      * short-periodic variation, the resulting output will depend on
  571.      * both the gravity field parameterized in input and the
  572.      * atmospheric drag represented by the {@code m2} parameter.
  573.      * </p>
  574.      * <p>
  575.      * The computation is done through a fixed-point iteration process.
  576.      * </p>
  577.      * @param <T> type of the filed elements
  578.      * @param osculating osculating orbit to convert
  579.      * @param provider for un-normalized zonal coefficients
  580.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  581.      * @param m2Value value of empirical drag coefficient in rad/s².
  582.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  583.      * @return mean orbit in a Brouwer-Lyddane sense
  584.      * @since 11.2
  585.      */
  586.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  587.                                                                                               final UnnormalizedSphericalHarmonicsProvider provider,
  588.                                                                                               final UnnormalizedSphericalHarmonics harmonics,
  589.                                                                                               final double m2Value) {
  590.         return computeMeanOrbit(osculating, provider, harmonics, m2Value,
  591.                                 BrouwerLyddanePropagator.EPSILON_DEFAULT,
  592.                                 BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT);
  593.     }

  594.     /** Conversion from osculating to mean orbit.
  595.      * <p>
  596.      * Compute mean orbit <b>in a Brouwer-Lyddane 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.      * both the gravity field parameterized in input and the
  603.      * atmospheric drag represented by the {@code m2} parameter.
  604.      * </p>
  605.      * <p>
  606.      * The computation is done through a fixed-point iteration process.
  607.      * </p>
  608.      * @param <T> type of the filed elements
  609.      * @param osculating osculating orbit to convert
  610.      * @param provider for un-normalized zonal coefficients
  611.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  612.      * @param m2Value value of empirical drag coefficient in rad/s².
  613.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  614.      * @param epsilon convergence threshold for mean parameters conversion
  615.      * @param maxIterations maximum iterations for mean parameters conversion
  616.      * @return mean orbit in a Brouwer-Lyddane sense
  617.      * @since 11.2
  618.      */
  619.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  620.                                                                                               final UnnormalizedSphericalHarmonicsProvider provider,
  621.                                                                                               final UnnormalizedSphericalHarmonics harmonics,
  622.                                                                                               final double m2Value,
  623.                                                                                               final double epsilon,
  624.                                                                                               final int maxIterations) {
  625.         return computeMeanOrbit(osculating,
  626.                                 provider.getAe(), provider.getMu(),
  627.                                 harmonics.getUnnormalizedCnm(2, 0),
  628.                                 harmonics.getUnnormalizedCnm(3, 0),
  629.                                 harmonics.getUnnormalizedCnm(4, 0),
  630.                                 harmonics.getUnnormalizedCnm(5, 0),
  631.                                 m2Value, epsilon, maxIterations);
  632.     }

  633.     /** Conversion from osculating to mean orbit.
  634.      * <p>
  635.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  636.      * osculating SpacecraftState in input.
  637.      * </p>
  638.      * <p>
  639.      * Since the osculating orbit is obtained with the computation of
  640.      * short-periodic variation, the resulting output will depend on
  641.      * both the gravity field parameterized in input and the
  642.      * atmospheric drag represented by the {@code m2} parameter.
  643.      * </p>
  644.      * <p>
  645.      * The computation is done through a fixed-point iteration process.
  646.      * </p>
  647.      * @param <T> type of the filed elements
  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 m2Value value of empirical drag coefficient in rad/s².
  656.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  657.      * @param epsilon convergence threshold for mean parameters conversion
  658.      * @param maxIterations maximum iterations for mean parameters conversion
  659.      * @return mean orbit in a Brouwer-Lyddane sense
  660.      * @since 11.2
  661.      */
  662.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  663.                                                                                               final double referenceRadius,
  664.                                                                                               final double mu,
  665.                                                                                               final double c20,
  666.                                                                                               final double c30,
  667.                                                                                               final double c40,
  668.                                                                                               final double c50,
  669.                                                                                               final double m2Value,
  670.                                                                                               final double epsilon,
  671.                                                                                               final int maxIterations) {
  672.         // Build a fixed-point converter
  673.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  674.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  675.         return computeMeanOrbit(osculating, referenceRadius, mu, c20, c30, c40, c50, m2Value, converter);
  676.     }

  677.     /** Conversion from osculating to mean orbit.
  678.      * <p>
  679.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  680.      * osculating SpacecraftState in input.
  681.      * </p>
  682.      * <p>
  683.      * Since the osculating orbit is obtained with the computation of
  684.      * short-periodic variation, the resulting output will depend on
  685.      * both the gravity field parameterized in input and the
  686.      * atmospheric drag represented by the {@code m2} parameter.
  687.      * </p>
  688.      * <p>
  689.      * The computation is done through the given osculating to mean orbit converter.
  690.      * </p>
  691.      * @param <T> type of the filed elements
  692.      * @param osculating osculating orbit to convert
  693.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  694.      * @param mu central attraction coefficient (m³/s²)
  695.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  696.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  697.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  698.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  699.      * @param m2Value value of empirical drag coefficient in rad/s².
  700.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  701.      * @param converter osculating to mean orbit converter
  702.      * @return mean orbit in a Brouwer-Lyddane sense
  703.      * @since 13.0
  704.      */
  705.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  706.                                                                                               final double referenceRadius,
  707.                                                                                               final double mu,
  708.                                                                                               final double c20,
  709.                                                                                               final double c30,
  710.                                                                                               final double c40,
  711.                                                                                               final double c50,
  712.                                                                                               final double m2Value,
  713.                                                                                               final OsculatingToMeanConverter converter) {
  714.         // Set BL as the mean theory for converting
  715.         final MeanTheory theory = new BrouwerLyddaneTheory(referenceRadius, mu, c20, c30, c40, c50, m2Value);
  716.         converter.setMeanTheory(theory);
  717.         return (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(converter.convertToMean(osculating));
  718.     }

  719.     /** {@inheritDoc}
  720.      * <p>The new initial state to consider
  721.      * must be defined with an osculating orbit.</p>
  722.      * @see #resetInitialState(FieldSpacecraftState, PropagationType)
  723.      */
  724.     @Override
  725.     public void resetInitialState(final FieldSpacecraftState<T> state) {
  726.         resetInitialState(state, PropagationType.OSCULATING);
  727.     }

  728.     /** Reset the propagator initial state.
  729.      * @param state new initial state to consider
  730.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  731.      */
  732.     public void resetInitialState(final FieldSpacecraftState<T> state,
  733.                                   final PropagationType stateType) {
  734.         final OsculatingToMeanConverter converter = new FixedPointConverter(BrouwerLyddanePropagator.EPSILON_DEFAULT,
  735.                                                                             BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT,
  736.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  737.         resetInitialState(state, stateType, converter);
  738.     }

  739.     /** Reset the propagator initial state.
  740.      * @param state new initial state to consider
  741.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  742.      * @param epsilon convergence threshold for mean parameters conversion
  743.      * @param maxIterations maximum iterations for mean parameters conversion
  744.      * @since 11.2
  745.      */
  746.     public void resetInitialState(final FieldSpacecraftState<T> state,
  747.                                   final PropagationType stateType,
  748.                                   final double epsilon,
  749.                                   final int maxIterations) {
  750.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  751.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  752.         resetInitialState(state, stateType, converter);
  753.     }

  754.     /** Reset the propagator initial state.
  755.      * @param state     new initial state to consider
  756.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  757.      * @param converter osculating to mean orbit converter
  758.      * @since 13.0
  759.      */
  760.     public void resetInitialState(final FieldSpacecraftState<T> state,
  761.                                   final PropagationType stateType,
  762.                                   final OsculatingToMeanConverter converter) {
  763.         super.resetInitialState(state);
  764.         FieldKeplerianOrbit<T> keplerian = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(state.getOrbit());
  765.         if (stateType == PropagationType.OSCULATING) {
  766.             final MeanTheory theory = new BrouwerLyddaneTheory(referenceRadius, mu.getReal(),
  767.                                                                ck0[2], ck0[3], ck0[4], ck0[5],
  768.                                                                getM2());
  769.             converter.setMeanTheory(theory);
  770.             keplerian = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(converter.convertToMean(keplerian));
  771.         }
  772.         this.initialModel = new FieldBLModel<>(keplerian, state.getMass(), referenceRadius, mu, ck0);
  773.         this.models = new FieldTimeSpanMap<>(initialModel, state.getMass().getField());
  774.     }

  775.     /** {@inheritDoc} */
  776.     @Override
  777.     protected void resetIntermediateState(final FieldSpacecraftState<T> state,
  778.                                           final boolean forward) {
  779.         final OsculatingToMeanConverter converter = new FixedPointConverter(BrouwerLyddanePropagator.EPSILON_DEFAULT,
  780.                                                                             BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT,
  781.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  782.         resetIntermediateState(state, forward, converter);
  783.     }

  784.     /** Reset an intermediate state.
  785.      * @param state new intermediate state to consider
  786.      * @param forward if true, the intermediate state is valid for
  787.      *                propagations after itself
  788.      * @param epsilon convergence threshold for mean parameters conversion
  789.      * @param maxIterations maximum iterations for mean parameters conversion
  790.      * @since 11.2
  791.      */
  792.     protected void resetIntermediateState(final FieldSpacecraftState<T> state,
  793.                                           final boolean forward,
  794.                                           final double epsilon,
  795.                                           final int maxIterations) {
  796.         final OsculatingToMeanConverter converter = new FixedPointConverter(epsilon, maxIterations,
  797.                                                                             FixedPointConverter.DEFAULT_DAMPING);
  798.         resetIntermediateState(state, forward, converter);
  799.     }

  800.     /** Reset an intermediate state.
  801.      * @param state     new intermediate state to consider
  802.      * @param forward   if true, the intermediate state is valid for
  803.      *                  propagations after itself
  804.      * @param converter osculating to mean orbit converter
  805.      * @since 13.0
  806.      */
  807.     protected void resetIntermediateState(final FieldSpacecraftState<T> state,
  808.                                           final boolean forward,
  809.                                           final OsculatingToMeanConverter converter) {
  810.         final MeanTheory theory = new BrouwerLyddaneTheory(referenceRadius, mu.getReal(),
  811.                                                            ck0[2], ck0[3], ck0[4], ck0[5],
  812.                                                            getM2());
  813.         converter.setMeanTheory(theory);
  814.         final FieldKeplerianOrbit<T> mean = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(converter.convertToMean(state.getOrbit()));
  815.         final FieldBLModel<T> newModel = new FieldBLModel<>(mean, state.getMass(), referenceRadius, mu, ck0);
  816.         if (forward) {
  817.             models.addValidAfter(newModel, state.getDate());
  818.         } else {
  819.             models.addValidBefore(newModel, state.getDate());
  820.         }
  821.         stateChanged(state);
  822.     }

  823.     /** {@inheritDoc} */
  824.     public FieldKeplerianOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
  825.         // compute Cartesian parameters, taking derivatives into account
  826.         final FieldBLModel<T> current = models.get(date);
  827.         return current.propagateParameters(date, parameters);
  828.     }

  829.     /**
  830.      * Get the value of the M2 drag parameter.
  831.      * @return the value of the M2 drag parameter
  832.      */
  833.     public double getM2() {
  834.         return M2Driver.getValue();
  835.     }

  836.     /**
  837.      * Get the value of the M2 drag parameter.
  838.      * @param date date at which the model parameters want to be known
  839.      * @return the value of the M2 drag parameter
  840.      */
  841.     public double getM2(final AbsoluteDate date) {
  842.         return M2Driver.getValue(date);
  843.     }

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

  846.         /** Constant mass. */
  847.         private final T mass;

  848.         /** Central attraction coefficient. */
  849.         private final T mu;

  850.         /** Brouwer-Lyddane mean orbit. */
  851.         private final FieldKeplerianOrbit<T> mean;

  852.         // Preprocessed values

  853.         /** Mean mean motion: n0 = √(μ/a")/a". */
  854.         private final T n0;

  855.         /** η = √(1 - e"²). */
  856.         private final T n;
  857.         /** η². */
  858.         private final T n2;
  859.         /** η³. */
  860.         private final T n3;
  861.         /** η + 1 / (1 + η). */
  862.         private final T t8;

  863.         /** Secular correction for mean anomaly l: &delta;<sub>s</sub>l. */
  864.         private final T dsl;
  865.         /** Secular correction for perigee argument g: &delta;<sub>s</sub>g. */
  866.         private final T dsg;
  867.         /** Secular correction for raan h: &delta;<sub>s</sub>h. */
  868.         private final T dsh;

  869.         /** Secular rate of change of semi-major axis due to drag. */
  870.         private final T aRate;
  871.         /** Secular rate of change of eccentricity due to drag. */
  872.         private final T eRate;

  873.         // CHECKSTYLE: stop JavadocVariable check

  874.         // Storage for speed-up
  875.         private final T yp2;
  876.         private final T ci;
  877.         private final T si;
  878.         private final T oneMci2;
  879.         private final T ci2X3M1;

  880.         // Long periodic corrections factors
  881.         private final T vle1;
  882.         private final T vle2;
  883.         private final T vle3;
  884.         private final T vli1;
  885.         private final T vli2;
  886.         private final T vli3;
  887.         private final T vll2;
  888.         private final T vlh1I;
  889.         private final T vlh2I;
  890.         private final T vlh3I;
  891.         private final T vls1;
  892.         private final T vls2;
  893.         private final T vls3;

  894.         // CHECKSTYLE: resume JavadocVariable check

  895.         /** Create a model for specified mean orbit.
  896.          * @param mean mean Fieldorbit
  897.          * @param mass constant mass
  898.          * @param referenceRadius reference radius of the central body attraction model (m)
  899.          * @param mu central attraction coefficient (m³/s²)
  900.          * @param ck0 un-normalized zonal coefficients
  901.          */
  902.         FieldBLModel(final FieldKeplerianOrbit<T> mean, final T mass,
  903.                      final double referenceRadius, final T mu, final double[] ck0) {

  904.             this.mass = mass;
  905.             this.mu   = mu;

  906.             // mean orbit
  907.             this.mean = mean;

  908.             final T one = mass.getField().getOne();

  909.             // mean eccentricity e"
  910.             final T epp = mean.getE();
  911.             if (epp.getReal() >= 1) {
  912.                 // Only for elliptical (e < 1) orbits
  913.                 throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL,
  914.                                           epp.getReal());
  915.             }
  916.             final T epp2 = epp.square();

  917.             // η
  918.             n2 = one.subtract(epp2);
  919.             n  = n2.sqrt();
  920.             n3 = n2.multiply(n);
  921.             t8 = n.add(one.add(n).reciprocal());

  922.             // mean semi-major axis a"
  923.             final T app = mean.getA();

  924.             // mean mean motion
  925.             n0 = mu.divide(app).sqrt().divide(app);

  926.             // ae/a"
  927.             final T q = app.divide(referenceRadius).reciprocal();

  928.             // γ2'
  929.             T ql = q.square();
  930.             T nl = n2.square();
  931.             yp2 = ql.multiply(-0.5 * ck0[2]).divide(nl);
  932.             final T yp22 = yp2.square();

  933.             // γ3'
  934.             ql = ql.multiply(q);
  935.             nl = nl.multiply(n2);
  936.             final T yp3 = ql.multiply(ck0[3]).divide(nl);

  937.             // γ4'
  938.             ql = ql.multiply(q);
  939.             nl = nl.multiply(n2);
  940.             final T yp4 = ql.multiply(0.375 * ck0[4]).divide(nl);

  941.             // γ5'
  942.             ql = ql.multiply(q);
  943.             nl = nl.multiply(n2);
  944.             final T yp5 = ql.multiply(ck0[5]).divide(nl);

  945.             // mean inclination I" sin & cos
  946.             final FieldSinCos<T> sc = FastMath.sinCos(mean.getI());
  947.             si = sc.sin();
  948.             ci = sc.cos();
  949.             final T ci2 = ci.square();
  950.             oneMci2 = one.subtract(ci2);
  951.             ci2X3M1 = ci2.multiply(3.).subtract(one);
  952.             final T ci2X5M1 = ci2.multiply(5.).subtract(one);

  953.             // secular corrections
  954.             // true anomaly
  955.             final T dsl1  = yp2.multiply(n).multiply(1.5);
  956.             final T dsl2a = n.multiply(n.multiply(25.).add(16.)).subtract(15.);
  957.             final T dsl2b = n.multiply(n.multiply(90.).add(96.)).negate().add(30.);
  958.             final T dsl2c = n.multiply(n.multiply(25.).add(144.)).add(105.);
  959.             final T dsl21 = dsl2a.add(ci2.multiply(dsl2b.add(ci2.multiply(dsl2c))));
  960.             final T dsl2  = ci2X3M1.add(yp2.multiply(0.0625).multiply(dsl21));
  961.             final T dsl3  = yp4.multiply(n).multiply(epp2).multiply(0.9375).
  962.                                 multiply(ci2.multiply(35.0).subtract(30.0).multiply(ci2).add(3.));
  963.             dsl = dsl1.multiply(dsl2).add(dsl3);

  964.             // perigee argument
  965.             final T dsg1  = yp2.multiply(1.5).multiply(ci2X5M1);
  966.             final T dsg2a = n.multiply(25.).add(24.).multiply(n).add(-35.);
  967.             final T dsg2b = n.multiply(126.).add(192.).multiply(n).negate().add(90.);
  968.             final T dsg2c = n.multiply(45.).add(360.).multiply(n).add(385.);
  969.             final T dsg21 = dsg2a.add(ci2.multiply(dsg2b.add(ci2.multiply(dsg2c))));
  970.             final T dsg2  = yp22.multiply(0.09375).multiply(dsg21);
  971.             final T dsg3a = n2.multiply(-9.).add(21.);
  972.             final T dsg3b = n2.multiply(126.).add(-270.);
  973.             final T dsg3c = n2.multiply(-189.).add(385.);
  974.             final T dsg31 = dsg3a.add(ci2.multiply(dsg3b.add(ci2.multiply(dsg3c))));
  975.             final T dsg3  = yp4.multiply(0.3125).multiply(dsg31);
  976.             dsg = dsg1.add(dsg2).add(dsg3);

  977.             // right ascension of ascending node
  978.             final T dsh1  = yp2.multiply(-3.);
  979.             final T dsh2a = n.multiply(9.).add(12.).multiply(n).add(-5.);
  980.             final T dsh2b = n.multiply(5.).add(36.).multiply(n).add(35.);
  981.             final T dsh21 = dsh2a.subtract(ci2.multiply(dsh2b));
  982.             final T dsh2  = yp22.multiply(0.375).multiply(dsh21);
  983.             final T dsh31 = n2.multiply(3.).subtract(5.);
  984.             final T dsh32 = ci2.multiply(7.).subtract(3.);
  985.             final T dsh3  = yp4.multiply(1.25).multiply(dsh31).multiply(dsh32);
  986.             dsh = ci.multiply(dsh1.add(dsh2).add(dsh3));

  987.             // secular rates of change due to drag
  988.             // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis
  989.             final T coef = n0.multiply(one.add(dsl)).multiply(3.).reciprocal().multiply(-4);
  990.             aRate = coef.multiply(app);
  991.             eRate = coef.multiply(epp).multiply(n2);

  992.             // singular term 1/(1 - 5 * cos²(I")) replaced by T2 function
  993.             final T t2 = T2(ci);

  994.             // factors for long periodic corrections
  995.             final T fs12 = yp3.divide(yp2);
  996.             final T fs13 = yp4.multiply(10).divide(yp2.multiply(3));
  997.             final T fs14 = yp5.divide(yp2);

  998.             final T ci2Xt2 = ci2.multiply(t2);
  999.             final T cA = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(11.)));
  1000.             final T cB = one.subtract(ci2.multiply(ci2Xt2.multiply(8.)  .add(3.)));
  1001.             final T cC = one.subtract(ci2.multiply(ci2Xt2.multiply(24.) .add(9.)));
  1002.             final T cD = one.subtract(ci2.multiply(ci2Xt2.multiply(16.) .add(5.)));
  1003.             final T cE = one.subtract(ci2.multiply(ci2Xt2.multiply(200.).add(33.)));
  1004.             final T cF = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(9.)));

  1005.             final T p5p   = one.add(ci2Xt2.multiply(ci2Xt2.multiply(20.).add(8.)));
  1006.             final T p5p2  = one.add(p5p.multiply(2.));
  1007.             final T p5p4  = one.add(p5p.multiply(4.));
  1008.             final T p5p10 = one.add(p5p.multiply(10.));

  1009.             final T e2X3P4  = epp2.multiply(3.).add(4.);
  1010.             final T ciO1Pci = ci.divide(one.add(ci));
  1011.             final T oneMci  = one.subtract(ci);

  1012.             final T q1 = (yp2.multiply(cA).subtract(fs13.multiply(cB))).
  1013.                             multiply(0.125);
  1014.             final T q2 = (yp2.multiply(p5p10).subtract(fs13.multiply(p5p2))).
  1015.                             multiply(epp2).multiply(ci).multiply(0.125);
  1016.             final T q5 = (fs12.add(e2X3P4.multiply(fs14).multiply(cC).multiply(0.3125))).
  1017.                             multiply(0.25);
  1018.             final T p2 = p5p2.multiply(epp).multiply(ci).multiply(si).multiply(e2X3P4).multiply(fs14).
  1019.                             multiply(0.46875);
  1020.             final T p3 = epp.multiply(si).multiply(fs14).multiply(cC).
  1021.                             multiply(0.15625);
  1022.             final double kf = 35. / 1152.;
  1023.             final T p4 = epp.multiply(fs14).multiply(cD).
  1024.                             multiply(kf);
  1025.             final T p5 = epp.multiply(epp2).multiply(ci).multiply(si).multiply(fs14).multiply(p5p4).
  1026.                             multiply(2. * kf);

  1027.             vle1 = epp.multiply(n2).multiply(q1);
  1028.             vle2 = n2.multiply(si).multiply(q5);
  1029.             vle3 = epp.multiply(n2).multiply(si).multiply(p4).multiply(-3.0);

  1030.             vli1 = epp.multiply(q1).divide(si).negate();
  1031.             vli2 = epp.multiply(ci).multiply(q5).negate();
  1032.             vli3 = epp2.multiply(ci).multiply(p4).multiply(-3.0);

  1033.             vll2 = vle2.add(epp.multiply(n2).multiply(p3).multiply(3.0));

  1034.             vlh1I = si.multiply(q2).negate();
  1035.             vlh2I = epp.multiply(ci).multiply(q5).add(si.multiply(p2));
  1036.             vlh3I = (epp2.multiply(ci).multiply(p4).add(si.multiply(p5))).negate();

  1037.             vls1 = q1.multiply(n3.subtract(one)).
  1038.                    subtract(q2).
  1039.                    add(epp2.multiply(ci2).multiply(ci2Xt2).multiply(ci2Xt2).
  1040.                        multiply(yp2.subtract(fs13.multiply(0.2))).multiply(25.0)).
  1041.                    subtract(epp2.multiply(yp2.multiply(cE).subtract(fs13.multiply(cF))).multiply(0.0625));

  1042.             vls2 = epp.multiply(si).multiply(t8.add(ciO1Pci)).multiply(q5).
  1043.                    add((epp2.subtract(n3).multiply(3.).add(11.)).multiply(p3)).
  1044.                    add(oneMci.multiply(p2));

  1045.             vls3 = si.multiply(p4).multiply(n3.subtract(one).multiply(3.).
  1046.                                             subtract(epp2.multiply(ciO1Pci.add(2.)))).
  1047.                    subtract(oneMci.multiply(p5));
  1048.         }

  1049.         /**
  1050.          * Get true anomaly from mean anomaly.
  1051.          * @param lM  the mean anomaly (rad)
  1052.          * @param ecc the eccentricity
  1053.          * @return the true anomaly (rad)
  1054.          */
  1055.         private FieldUnivariateDerivative1<T> getTrueAnomaly(final FieldUnivariateDerivative1<T> lM,
  1056.                                                              final FieldUnivariateDerivative1<T> ecc) {

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

  1058.             // reduce M to [-PI PI] interval
  1059.             final FieldUnivariateDerivative1<T> reducedM = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(lM.getValue(), zero),
  1060.                                                                                             lM.getFirstDerivative());

  1061.             // compute the true anomaly
  1062.             FieldUnivariateDerivative1<T> lV = FieldKeplerianAnomalyUtility.ellipticMeanToTrue(ecc, lM);

  1063.             // expand the result back to original range
  1064.             lV = lV.add(lM.getValue().subtract(reducedM.getValue()));

  1065.             // Returns the true anomaly
  1066.             return lV;
  1067.         }

  1068.         /**
  1069.          * This method is used in Brouwer-Lyddane model to avoid singularity at the
  1070.          * critical inclination (i = 63.4°).
  1071.          * <p>
  1072.          * This method, based on Warren Phipps's 1992 thesis (Eq. 2.47 and 2.48),
  1073.          * approximate the factor (1.0 - 5.0 * cos²(i))<sup>-1</sup> (causing the singularity)
  1074.          * by a function, named T2 in the thesis.
  1075.          * </p>
  1076.          * @param cosI cosine of the mean inclination
  1077.          * @return an approximation of (1.0 - 5.0 * cos²(i))<sup>-1</sup> term
  1078.          */
  1079.         private T T2(final T cosI) {

  1080.             // X = (1.0 - 5.0 * cos²(i))
  1081.             final T x  = cosI.square().multiply(-5.0).add(1.0);
  1082.             final T x2 = x.square();
  1083.             final T xb = x2.multiply(BETA);

  1084.             // Eq. 2.48
  1085.             T sum = x.getField().getZero();
  1086.             for (int i = 0; i <= 12; i++) {
  1087.                 final double sign = i % 2 == 0 ? +1.0 : -1.0;
  1088.                 sum = sum.add(FastMath.pow(x2, i).
  1089.                               multiply(FastMath.pow(BETA, i)).
  1090.                               multiply(sign).
  1091.                               divide(CombinatoricsUtils.factorialDouble(i + 1)));
  1092.             }

  1093.             // Right term of equation 2.47
  1094.             final T one = x.getField().getOne();
  1095.             T product = one;
  1096.             for (int i = 0; i <= 10; i++) {
  1097.                 product = product.multiply(one.add(FastMath.exp(xb.multiply(FastMath.scalb(-1.0, i)))));
  1098.             }

  1099.             // Return (Eq. 2.47)
  1100.             return x.multiply(BETA).multiply(sum).multiply(product);
  1101.         }

  1102.         /** Extrapolate an orbit up to a specific target date.
  1103.          * @param date target date for the orbit
  1104.          * @param parameters model parameters
  1105.          * @return propagated parameters
  1106.          */
  1107.         public FieldKeplerianOrbit<T> propagateParameters(final FieldAbsoluteDate<T> date, final T[] parameters) {

  1108.             // Field
  1109.             final Field<T> field = date.getField();
  1110.             final T one  = field.getOne();
  1111.             final T zero = field.getZero();

  1112.             // Empirical drag coefficient M2
  1113.             final T m2 = parameters[0];

  1114.             // Keplerian evolution
  1115.             final FieldUnivariateDerivative1<T> dt  = new FieldUnivariateDerivative1<>(date.durationFrom(mean.getDate()), one);
  1116.             final FieldUnivariateDerivative1<T> not = dt.multiply(n0);

  1117.             final FieldUnivariateDerivative1<T> dtM2  = dt.multiply(m2);
  1118.             final FieldUnivariateDerivative1<T> dt2M2 = dt.multiply(dtM2);

  1119.             // Secular corrections
  1120.             // -------------------

  1121.             // semi-major axis (with drag Eq. 2.41 of Phipps' 1992 thesis)
  1122.             final FieldUnivariateDerivative1<T> app = dtM2.multiply(aRate).add(mean.getA());

  1123.             // eccentricity  (with drag Eq. 2.45 of Phipps' 1992 thesis) reduced to [0, 1[
  1124.             final FieldUnivariateDerivative1<T> tmp = dtM2.multiply(eRate).add(mean.getE());
  1125.             final FieldUnivariateDerivative1<T> epp = FastMath.max(FastMath.min(tmp, MAX_ECC), 0.);

  1126.             // mean argument of perigee
  1127.             final T gp0 = MathUtils.normalizeAngle(mean.getPerigeeArgument().add(dsg.multiply(not.getValue())), zero);
  1128.             final T gp1 = dsg.multiply(n0);
  1129.             final FieldUnivariateDerivative1<T> gpp = new FieldUnivariateDerivative1<>(gp0, gp1);

  1130.             // mean longitude of ascending node
  1131.             final T hp0 = MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(dsh.multiply(not.getValue())), zero);
  1132.             final T hp1 = dsh.multiply(n0);
  1133.             final FieldUnivariateDerivative1<T> hpp = new FieldUnivariateDerivative1<>(hp0, hp1);

  1134.             // mean anomaly (with drag Eq. 2.38 of Phipps' 1992 thesis)
  1135.             final T lp0 = MathUtils.normalizeAngle(mean.getMeanAnomaly().add(dsl.add(one).multiply(not.getValue())).add(dt2M2.getValue()), zero);
  1136.             final T lp1 = dsl.add(one).multiply(n0).add(dtM2.multiply(2.0).getValue());
  1137.             final FieldUnivariateDerivative1<T> lpp = new FieldUnivariateDerivative1<>(lp0, lp1);

  1138.             // Long period corrections
  1139.             //------------------------
  1140.             final FieldSinCos<FieldUnivariateDerivative1<T>> scgpp = gpp.sinCos();
  1141.             final FieldUnivariateDerivative1<T> cgpp = scgpp.cos();
  1142.             final FieldUnivariateDerivative1<T> sgpp = scgpp.sin();
  1143.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2gpp = gpp.multiply(2).sinCos();
  1144.             final FieldUnivariateDerivative1<T> c2gpp  = sc2gpp.cos();
  1145.             final FieldUnivariateDerivative1<T> s2gpp  = sc2gpp.sin();
  1146.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc3gpp = gpp.multiply(3).sinCos();
  1147.             final FieldUnivariateDerivative1<T> c3gpp  = sc3gpp.cos();
  1148.             final FieldUnivariateDerivative1<T> s3gpp  = sc3gpp.sin();

  1149.             // δ1e
  1150.             final FieldUnivariateDerivative1<T> d1e = c2gpp.multiply(vle1).
  1151.                                                       add(sgpp.multiply(vle2)).
  1152.                                                       add(s3gpp.multiply(vle3));

  1153.             // δ1I
  1154.             FieldUnivariateDerivative1<T> d1I = sgpp.multiply(vli2).
  1155.                                                 add(s3gpp.multiply(vli3));
  1156.             // Pseudo singular term, not to add if I" is zero
  1157.             if (Double.isFinite(vli1.getReal())) {
  1158.                 d1I = d1I.add(c2gpp.multiply(vli1));
  1159.             }

  1160.             // e"δ1l
  1161.             final FieldUnivariateDerivative1<T> eppd1l = s2gpp.multiply(vle1).
  1162.                                                          subtract(cgpp.multiply(vll2)).
  1163.                                                          subtract(c3gpp.multiply(vle3)).
  1164.                                                          multiply(n);

  1165.             // sinI"δ1h
  1166.             final FieldUnivariateDerivative1<T> sIppd1h = s2gpp.multiply(vlh1I).
  1167.                                                           add(cgpp.multiply(vlh2I)).
  1168.                                                           add(c3gpp.multiply(vlh3I));

  1169.             // δ1z = δ1l + δ1g + δ1h
  1170.             final FieldUnivariateDerivative1<T> d1z = s2gpp.multiply(vls1).
  1171.                                                       add(cgpp.multiply(vls2)).
  1172.                                                       add(c3gpp.multiply(vls3));

  1173.             // Short period corrections
  1174.             // ------------------------

  1175.             // true anomaly
  1176.             final FieldUnivariateDerivative1<T> fpp = getTrueAnomaly(lpp, epp);
  1177.             final FieldSinCos<FieldUnivariateDerivative1<T>> scfpp = fpp.sinCos();
  1178.             final FieldUnivariateDerivative1<T> cfpp = scfpp.cos();
  1179.             final FieldUnivariateDerivative1<T> sfpp = scfpp.sin();

  1180.             // e"sin(f')
  1181.             final FieldUnivariateDerivative1<T> eppsfpp = epp.multiply(sfpp);
  1182.             // e"cos(f')
  1183.             final FieldUnivariateDerivative1<T> eppcfpp = epp.multiply(cfpp);
  1184.             // 1 + e"cos(f')
  1185.             final FieldUnivariateDerivative1<T> eppcfppP1 = eppcfpp.add(1);
  1186.             // 2 + e"cos(f')
  1187.             final FieldUnivariateDerivative1<T> eppcfppP2 = eppcfpp.add(2);
  1188.             // 3 + e"cos(f')
  1189.             final FieldUnivariateDerivative1<T> eppcfppP3 = eppcfpp.add(3);
  1190.             // (1 + e"cos(f'))³
  1191.             final FieldUnivariateDerivative1<T> eppcfppP1_3 = eppcfppP1.square().multiply(eppcfppP1);

  1192.             // 2g"
  1193.             final FieldUnivariateDerivative1<T> g2 = gpp.multiply(2);

  1194.             // 2g" + f"
  1195.             final FieldUnivariateDerivative1<T> g2f = g2.add(fpp);
  1196.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2gf = g2f.sinCos();
  1197.             final FieldUnivariateDerivative1<T> c2gf = sc2gf.cos();
  1198.             final FieldUnivariateDerivative1<T> s2gf = sc2gf.sin();
  1199.             final FieldUnivariateDerivative1<T> eppc2gf = epp.multiply(c2gf);
  1200.             final FieldUnivariateDerivative1<T> epps2gf = epp.multiply(s2gf);

  1201.             // 2g" + 2f"
  1202.             final FieldUnivariateDerivative1<T> g2f2 = g2.add(fpp.multiply(2));
  1203.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2g2f = g2f2.sinCos();
  1204.             final FieldUnivariateDerivative1<T> c2g2f = sc2g2f.cos();
  1205.             final FieldUnivariateDerivative1<T> s2g2f = sc2g2f.sin();

  1206.             // 2g" + 3f"
  1207.             final FieldUnivariateDerivative1<T> g2f3 = g2.add(fpp.multiply(3));
  1208.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2g3f = g2f3.sinCos();
  1209.             final FieldUnivariateDerivative1<T> c2g3f = sc2g3f.cos();
  1210.             final FieldUnivariateDerivative1<T> s2g3f = sc2g3f.sin();

  1211.             // e"cos(2g" + 3f")
  1212.             final FieldUnivariateDerivative1<T> eppc2g3f = epp.multiply(c2g3f);
  1213.             // e"sin(2g" + 3f")
  1214.             final FieldUnivariateDerivative1<T> epps2g3f = epp.multiply(s2g3f);

  1215.             // f" + e"sin(f") - l"
  1216.             final FieldUnivariateDerivative1<T> w17 = fpp.add(eppsfpp).subtract(lpp);

  1217.             // ((e"cos(f") + 3)e"cos(f") + 3)cos(f")
  1218.             final FieldUnivariateDerivative1<T> w20 = cfpp.multiply(eppcfppP3.multiply(eppcfpp).add(3.));

  1219.             // 3sin(2g" + 2f") + 3e"sin(2g" + f") + e"sin(2g" + f")
  1220.             final FieldUnivariateDerivative1<T> w21 = s2g2f.add(epps2gf).multiply(3).add(epps2g3f);

  1221.             // (1 + e"cos(f"))(2 + e"cos(f"))/η²
  1222.             final FieldUnivariateDerivative1<T> w22 = eppcfppP1.multiply(eppcfppP2).divide(n2);

  1223.             // sinCos(I"/2)
  1224.             final FieldSinCos<T> sci = FastMath.sinCos(mean.getI().divide(2.));
  1225.             final T siO2 = sci.sin();
  1226.             final T ciO2 = sci.cos();

  1227.             // δ2a
  1228.             final FieldUnivariateDerivative1<T> d2a = app.multiply(yp2).divide(n2).
  1229.                                                       multiply(eppcfppP1_3.subtract(n3).multiply(ci2X3M1).
  1230.                                                                add(c2g2f.multiply(eppcfppP1_3).multiply(oneMci2).multiply(3.)));

  1231.             // δ2e
  1232.             final FieldUnivariateDerivative1<T> d2e = (w20.add(epp.multiply(t8))).multiply(ci2X3M1).
  1233.                                                        add((w20.add(epp.multiply(c2g2f))).multiply(oneMci2.multiply(3))).
  1234.                                                        subtract((eppc2gf.multiply(3).add(eppc2g3f)).multiply(oneMci2.multiply(n2))).
  1235.                                                       multiply(yp2.multiply(0.5));

  1236.             // δ2I
  1237.             final FieldUnivariateDerivative1<T> d2I = ((c2g2f.add(eppc2gf)).multiply(3).add(eppc2g3f)).
  1238.                                                        multiply(yp2.divide(2.).multiply(ci).multiply(si));

  1239.             // e"δ2l
  1240.             final FieldUnivariateDerivative1<T> eppd2l = (w22.add(1).multiply(sfpp).multiply(oneMci2).multiply(2.).
  1241.                                                          add((w22.subtract(1).negate().multiply(s2gf)).
  1242.                                                               add(w22.add(1. / 3.).multiply(s2g3f)).
  1243.                                                              multiply(oneMci2.multiply(3.)))).
  1244.                                                         multiply(yp2.divide(4.).multiply(n3)).negate();

  1245.             // sinI"δ2h
  1246.             final FieldUnivariateDerivative1<T> sIppd2h = (w21.subtract(w17.multiply(6))).
  1247.                                                            multiply(yp2).multiply(ci).multiply(si).divide(2.);

  1248.             // δ2z = δ2l + δ2g + δ2h
  1249.             final T ttt = one.add(ci.multiply(ci.multiply(-5).add(2.)));
  1250.             final FieldUnivariateDerivative1<T> d2z = (epp.multiply(eppd2l).multiply(t8.subtract(one)).divide(n3).
  1251.                                                        add(w17.multiply(ttt).multiply(6).subtract(w21.multiply(ttt.add(2.))).
  1252.                                                            multiply(yp2.divide(4.)))).
  1253.                                                        negate();

  1254.             // Assembling elements
  1255.             // -------------------

  1256.             // e" + δe
  1257.             final FieldUnivariateDerivative1<T> de = epp.add(d1e).add(d2e);

  1258.             // e"δl
  1259.             final FieldUnivariateDerivative1<T> dl = eppd1l.add(eppd2l);

  1260.             // sin(I"/2)δh = sin(I")δh / cos(I"/2) (singular for I" = π, very unlikely)
  1261.             final FieldUnivariateDerivative1<T> dh = sIppd1h.add(sIppd2h).divide(ciO2.multiply(2.));

  1262.             // δI
  1263.             final FieldUnivariateDerivative1<T> di = d1I.add(d2I).multiply(ciO2).divide(2.).add(siO2);

  1264.             // z = l" + g" + h" + δ1z + δ2z
  1265.             final FieldUnivariateDerivative1<T> z = lpp.add(gpp).add(hpp).add(d1z).add(d2z);

  1266.             // Osculating elements
  1267.             // -------------------

  1268.             // Semi-major axis
  1269.             final FieldUnivariateDerivative1<T> a = app.add(d2a);

  1270.             // Eccentricity
  1271.             final FieldUnivariateDerivative1<T> e = FastMath.sqrt(de.square().add(dl.square()));

  1272.             // Mean anomaly
  1273.             final FieldSinCos<FieldUnivariateDerivative1<T>> sclpp = lpp.sinCos();
  1274.             final FieldUnivariateDerivative1<T> clpp = sclpp.cos();
  1275.             final FieldUnivariateDerivative1<T> slpp = sclpp.sin();
  1276.             final FieldUnivariateDerivative1<T> l = FastMath.atan2(de.multiply(slpp).add(dl.multiply(clpp)),
  1277.                                                                    de.multiply(clpp).subtract(dl.multiply(slpp)));

  1278.             // Inclination
  1279.             final FieldUnivariateDerivative1<T> i = FastMath.acos(di.square().add(dh.square()).multiply(2).negate().add(1.));

  1280.             // Longitude of ascending node
  1281.             final FieldSinCos<FieldUnivariateDerivative1<T>> schpp = hpp.sinCos();
  1282.             final FieldUnivariateDerivative1<T> chpp = schpp.cos();
  1283.             final FieldUnivariateDerivative1<T> shpp = schpp.sin();
  1284.             final FieldUnivariateDerivative1<T> h = FastMath.atan2(di.multiply(shpp).add(dh.multiply(chpp)),
  1285.                                                                    di.multiply(chpp).subtract(dh.multiply(shpp)));

  1286.             // Argument of perigee
  1287.             final FieldUnivariateDerivative1<T> g = z.subtract(l).subtract(h);

  1288.             // Return a Keplerian orbit
  1289.             return new FieldKeplerianOrbit<>(a.getValue(), e.getValue(), i.getValue(),
  1290.                                              g.getValue(), h.getValue(), l.getValue(),
  1291.                                              a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(),
  1292.                                              g.getFirstDerivative(), h.getFirstDerivative(), l.getFirstDerivative(),
  1293.                                              PositionAngleType.MEAN, mean.getFrame(), date, this.mu);
  1294.         }
  1295.     }

  1296.     /** {@inheritDoc} */
  1297.     @Override
  1298.     protected T getMass(final FieldAbsoluteDate<T> date) {
  1299.         return models.get(date).mass;
  1300.     }

  1301.     /** {@inheritDoc} */
  1302.     @Override
  1303.     public List<ParameterDriver> getParametersDrivers() {
  1304.         return Collections.singletonList(M2Driver);
  1305.     }

  1306. }