FieldBrouwerLyddanePropagator.java

  1. /* Copyright 2002-2024 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.FieldEquinoctialOrbit;
  34. import org.orekit.orbits.FieldKeplerianAnomalyUtility;
  35. import org.orekit.orbits.FieldKeplerianOrbit;
  36. import org.orekit.orbits.FieldOrbit;
  37. import org.orekit.orbits.OrbitType;
  38. import org.orekit.orbits.PositionAngleType;
  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.AbsoluteDate;
  43. import org.orekit.time.FieldAbsoluteDate;
  44. import org.orekit.utils.FieldTimeSpanMap;
  45. import org.orekit.utils.ParameterDriver;

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

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

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

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

  102.     /** Initial Brouwer-Lyddane model. */
  103.     private FieldBLModel<T> initialModel;

  104.     /** All models. */
  105.     private transient FieldTimeSpanMap<FieldBLModel<T>, T> models;

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

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

  110.     /** Un-normalized zonal coefficients. */
  111.     private double[] ck0;

  112.     /** Empirical coefficient used in the drag modeling. */
  113.     private final ParameterDriver M2Driver;

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

  489.         // store model coefficients
  490.         this.referenceRadius = referenceRadius;
  491.         this.mu  = mu;
  492.         this.ck0 = new double[] {0.0, 0.0, c20, c30, c40, c50};

  493.         // initialize M2 driver
  494.         this.M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, M2, SCALE,
  495.                                             Double.NEGATIVE_INFINITY,
  496.                                             Double.POSITIVE_INFINITY);

  497.         // compute mean parameters if needed
  498.         resetInitialState(new FieldSpacecraftState<>(initialOrbit,
  499.                                                      attitudeProv.getAttitude(initialOrbit,
  500.                                                                               initialOrbit.getDate(),
  501.                                                                               initialOrbit.getFrame()),
  502.                                                      mass),
  503.                                                      initialType, epsilon, maxIterations);

  504.     }

  505.     /** Conversion from osculating to mean orbit.
  506.      * <p>
  507.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  508.      * osculating SpacecraftState in input.
  509.      * </p>
  510.      * <p>
  511.      * Since the osculating orbit is obtained with the computation of
  512.      * short-periodic variation, the resulting output will depend on
  513.      * both the gravity field parameterized in input and the
  514.      * atmospheric drag represented by the {@code m2} parameter.
  515.      * </p>
  516.      * <p>
  517.      * The computation is done through a fixed-point iteration process.
  518.      * </p>
  519.      * @param <T> type of the filed elements
  520.      * @param osculating osculating orbit to convert
  521.      * @param provider for un-normalized zonal coefficients
  522.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  523.      * @param M2Value value of empirical drag coefficient in rad/s².
  524.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  525.      * @return mean orbit in a Brouwer-Lyddane sense
  526.      * @since 11.2
  527.      */
  528.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  529.                                                                                               final UnnormalizedSphericalHarmonicsProvider provider,
  530.                                                                                               final UnnormalizedSphericalHarmonics harmonics,
  531.                                                                                               final double M2Value) {
  532.         return computeMeanOrbit(osculating, provider, harmonics, M2Value,
  533.                                 BrouwerLyddanePropagator.EPSILON_DEFAULT,
  534.                                 BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT);
  535.     }

  536.     /** Conversion from osculating to mean orbit.
  537.      * <p>
  538.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  539.      * osculating SpacecraftState in input.
  540.      * </p>
  541.      * <p>
  542.      * Since the osculating orbit is obtained with the computation of
  543.      * short-periodic variation, the resulting output will depend on
  544.      * both the gravity field parameterized in input and the
  545.      * atmospheric drag represented by the {@code m2} parameter.
  546.      * </p>
  547.      * <p>
  548.      * The computation is done through a fixed-point iteration process.
  549.      * </p>
  550.      * @param <T> type of the filed elements
  551.      * @param osculating osculating orbit to convert
  552.      * @param provider for un-normalized zonal coefficients
  553.      * @param harmonics {@code provider.onDate(osculating.getDate())}
  554.      * @param M2Value value of empirical drag coefficient in rad/s².
  555.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  556.      * @param epsilon convergence threshold for mean parameters conversion
  557.      * @param maxIterations maximum iterations for mean parameters conversion
  558.      * @return mean orbit in a Brouwer-Lyddane sense
  559.      * @since 11.2
  560.      */
  561.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  562.                                                                                               final UnnormalizedSphericalHarmonicsProvider provider,
  563.                                                                                               final UnnormalizedSphericalHarmonics harmonics,
  564.                                                                                               final double M2Value,
  565.                                                                                               final double epsilon, final int maxIterations) {
  566.         return computeMeanOrbit(osculating,
  567.                                 provider.getAe(), provider.getMu(),
  568.                                 harmonics.getUnnormalizedCnm(2, 0),
  569.                                 harmonics.getUnnormalizedCnm(3, 0),
  570.                                 harmonics.getUnnormalizedCnm(4, 0),
  571.                                 harmonics.getUnnormalizedCnm(5, 0),
  572.                                 M2Value, epsilon, maxIterations);
  573.     }

  574.     /** Conversion from osculating to mean orbit.
  575.      * <p>
  576.      * Compute mean orbit <b>in a Brouwer-Lyddane sense</b>, corresponding to the
  577.      * osculating SpacecraftState in input.
  578.      * </p>
  579.      * <p>
  580.      * Since the osculating orbit is obtained with the computation of
  581.      * short-periodic variation, the resulting output will depend on
  582.      * both the gravity field parameterized in input and the
  583.      * atmospheric drag represented by the {@code m2} parameter.
  584.      * </p>
  585.      * <p>
  586.      * The computation is done through a fixed-point iteration process.
  587.      * </p>
  588.      * @param <T> type of the filed elements
  589.      * @param osculating osculating orbit to convert
  590.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  591.      * @param mu central attraction coefficient (m³/s²)
  592.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  593.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  594.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  595.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  596.      * @param M2Value value of empirical drag coefficient in rad/s².
  597.      *        If equal to {@code BrouwerLyddanePropagator.M2} drag is not considered
  598.      * @param epsilon convergence threshold for mean parameters conversion
  599.      * @param maxIterations maximum iterations for mean parameters conversion
  600.      * @return mean orbit in a Brouwer-Lyddane sense
  601.      * @since 11.2
  602.      */
  603.     public static <T extends CalculusFieldElement<T>> FieldKeplerianOrbit<T> computeMeanOrbit(final FieldOrbit<T> osculating,
  604.                                                                                               final double referenceRadius,
  605.                                                                                               final double mu,
  606.                                                                                               final double c20,
  607.                                                                                               final double c30,
  608.                                                                                               final double c40,
  609.                                                                                               final double c50,
  610.                                                                                               final double M2Value,
  611.                                                                                               final double epsilon,
  612.                                                                                               final int maxIterations) {
  613.         final FieldBrouwerLyddanePropagator<T> propagator =
  614.                         new FieldBrouwerLyddanePropagator<>(osculating,
  615.                                                             FrameAlignedProvider.of(osculating.getFrame()),
  616.                                                             osculating.getMu().newInstance(DEFAULT_MASS),
  617.                                                             referenceRadius, osculating.getMu().newInstance(mu),
  618.                                                             c20, c30, c40, c50,
  619.                                                             PropagationType.OSCULATING, M2Value,
  620.                                                             epsilon, maxIterations);
  621.         return propagator.initialModel.mean;
  622.     }

  623.     /** {@inheritDoc}
  624.      * <p>The new initial state to consider
  625.      * must be defined with an osculating orbit.</p>
  626.      * @see #resetInitialState(FieldSpacecraftState, PropagationType)
  627.      */
  628.     @Override
  629.     public void resetInitialState(final FieldSpacecraftState<T> state) {
  630.         resetInitialState(state, PropagationType.OSCULATING);
  631.     }

  632.     /** Reset the propagator initial state.
  633.      * @param state new initial state to consider
  634.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  635.      */
  636.     public void resetInitialState(final FieldSpacecraftState<T> state, final PropagationType stateType) {
  637.         resetInitialState(state, stateType,
  638.                           BrouwerLyddanePropagator.EPSILON_DEFAULT,
  639.                           BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT);
  640.     }

  641.     /** Reset the propagator initial state.
  642.      * @param state new initial state to consider
  643.      * @param stateType mean Brouwer-Lyddane orbit or osculating orbit
  644.      * @param epsilon convergence threshold for mean parameters conversion
  645.      * @param maxIterations maximum iterations for mean parameters conversion
  646.      * @since 11.2
  647.      */
  648.     public void resetInitialState(final FieldSpacecraftState<T> state, final PropagationType stateType,
  649.                                   final double epsilon, final int maxIterations) {
  650.         super.resetInitialState(state);
  651.         final FieldKeplerianOrbit<T> keplerian = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(state.getOrbit());
  652.         this.initialModel = (stateType == PropagationType.MEAN) ?
  653.                              new FieldBLModel<>(keplerian, state.getMass(), referenceRadius, mu, ck0) :
  654.                              computeMeanParameters(keplerian, state.getMass(), epsilon, maxIterations);
  655.         this.models = new FieldTimeSpanMap<>(initialModel, state.getA().getField());
  656.     }

  657.     /** {@inheritDoc} */
  658.     @Override
  659.     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
  660.         resetIntermediateState(state, forward,
  661.                                BrouwerLyddanePropagator.EPSILON_DEFAULT,
  662.                                BrouwerLyddanePropagator.MAX_ITERATIONS_DEFAULT);
  663.     }

  664.     /** Reset an intermediate state.
  665.      * @param state new intermediate state to consider
  666.      * @param forward if true, the intermediate state is valid for
  667.      * propagations after itself
  668.      * @param epsilon convergence threshold for mean parameters conversion
  669.      * @param maxIterations maximum iterations for mean parameters conversion
  670.      * @since 11.2
  671.      */
  672.     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward,
  673.                                           final double epsilon, final int maxIterations) {
  674.         final FieldBLModel<T> newModel = computeMeanParameters((FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(state.getOrbit()),
  675.                                                                state.getMass(), epsilon, maxIterations);
  676.         if (forward) {
  677.             models.addValidAfter(newModel, state.getDate());
  678.         } else {
  679.             models.addValidBefore(newModel, state.getDate());
  680.         }
  681.         stateChanged(state);
  682.     }

  683.     /** Compute mean parameters according to the Brouwer-Lyddane analytical model computation
  684.      * in order to do the propagation.
  685.      * @param osculating osculating orbit
  686.      * @param mass constant mass
  687.      * @param epsilon convergence threshold for mean parameters conversion
  688.      * @param maxIterations maximum iterations for mean parameters conversion
  689.      * @return Brouwer-Lyddane mean model
  690.      */
  691.     private FieldBLModel<T> computeMeanParameters(final FieldKeplerianOrbit<T> osculating, final T mass,
  692.                                                   final double epsilon, final int maxIterations) {

  693.         // damping factor
  694.         final double damping = BrouwerLyddanePropagator.DAMPING_DEFAULT;

  695.         // sanity check
  696.         if (osculating.getA().getReal() < referenceRadius) {
  697.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE,
  698.                                            osculating.getA());
  699.         }

  700.         final Field<T> field = osculating.getDate().getField();
  701.         final T zero = field.getZero();
  702.         final T pi   = zero.getPi();

  703.         // rough initialization of the mean parameters
  704.         FieldBLModel<T> current = new FieldBLModel<>(osculating, mass, referenceRadius, mu, ck0);

  705.         // Get equinoctial parameters
  706.         T sma = osculating.getA();
  707.         T ex  = osculating.getEquinoctialEx();
  708.         T ey  = osculating.getEquinoctialEy();
  709.         T hx  = osculating.getHx();
  710.         T hy  = osculating.getHy();
  711.         T lv  = osculating.getLv();

  712.         // threshold for each parameter
  713.         final T thresholdA  = osculating.getA().abs().add(1).multiply(epsilon);
  714.         final T thresholdE  = FastMath.hypot(ex, ey).add(1).multiply(epsilon);
  715.         final T thresholdH  = FastMath.hypot(hx, hy).add(1).multiply(epsilon);
  716.         final T thresholdLv = pi.multiply(epsilon);

  717.         int i = 0;
  718.         while (i++ < maxIterations) {

  719.             // recompute the osculating parameters from the current mean parameters
  720.             final FieldKeplerianOrbit<T> parameters = current.propagateParameters(current.mean.getDate(),
  721.                                                                                   getParameters(field,
  722.                                                                                                 current.mean.getDate()));

  723.             // adapted parameters residuals
  724.             final T deltaA  = osculating.getA().subtract(parameters.getA());
  725.             final T deltaEx = osculating.getEquinoctialEx().subtract(parameters.getEquinoctialEx());
  726.             final T deltaEy = osculating.getEquinoctialEy().subtract(parameters.getEquinoctialEy());
  727.             final T deltaHx = osculating.getHx().subtract(parameters.getHx());
  728.             final T deltaHy = osculating.getHy().subtract(parameters.getHy());
  729.             final T deltaLv = MathUtils.normalizeAngle(osculating.getLv().subtract(parameters.getLv()), zero);

  730.             // update state
  731.             sma = sma.add(deltaA.multiply(damping));
  732.             ex  = ex.add(deltaEx.multiply(damping));
  733.             ey  = ey.add(deltaEy.multiply(damping));
  734.             hx  = hx.add(deltaHx.multiply(damping));
  735.             hy  = hy.add(deltaHy.multiply(damping));
  736.             lv  = lv.add(deltaLv.multiply(damping));

  737.             // Update mean orbit
  738.             final FieldEquinoctialOrbit<T> meanEquinoctial =
  739.                             new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE,
  740.                                                         osculating.getFrame(), osculating.getDate(),
  741.                                                         osculating.getMu());
  742.             final FieldKeplerianOrbit<T> meanKeplerian =
  743.                             (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(meanEquinoctial);

  744.             // update mean parameters
  745.             current = new FieldBLModel<>(meanKeplerian, mass, referenceRadius, mu, ck0);

  746.             // check convergence
  747.             if (FastMath.abs(deltaA.getReal())  < thresholdA.getReal() &&
  748.                 FastMath.abs(deltaEx.getReal()) < thresholdE.getReal() &&
  749.                 FastMath.abs(deltaEy.getReal()) < thresholdE.getReal() &&
  750.                 FastMath.abs(deltaHx.getReal()) < thresholdH.getReal() &&
  751.                 FastMath.abs(deltaHy.getReal()) < thresholdH.getReal() &&
  752.                 FastMath.abs(deltaLv.getReal()) < thresholdLv.getReal()) {
  753.                 return current;
  754.             }
  755.         }
  756.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS, i);
  757.     }


  758.     /** {@inheritDoc} */
  759.     public FieldKeplerianOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
  760.         // compute Cartesian parameters, taking derivatives into account
  761.         final FieldBLModel<T> current = models.get(date);
  762.         return current.propagateParameters(date, parameters);
  763.     }

  764.     /**
  765.      * Get the value of the M2 drag parameter.
  766.      * @return the value of the M2 drag parameter
  767.      */
  768.     public double getM2() {
  769.         return M2Driver.getValue();
  770.     }

  771.     /**
  772.      * Get the value of the M2 drag parameter.
  773.      * @param date date at which the model parameters want to be known
  774.      * @return the value of the M2 drag parameter
  775.      */
  776.     public double getM2(final AbsoluteDate date) {
  777.         return M2Driver.getValue(date);
  778.     }

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

  781.         /** Constant mass. */
  782.         private final T mass;

  783.         /** Central attraction coefficient. */
  784.         private final T mu;

  785.         /** Brouwer-Lyddane mean orbit. */
  786.         private final FieldKeplerianOrbit<T> mean;

  787.         // Preprocessed values

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

  790.         /** η = √(1 - e"²). */
  791.         private final T n;
  792.         /** η². */
  793.         private final T n2;
  794.         /** η³. */
  795.         private final T n3;
  796.         /** η + 1 / (1 + η). */
  797.         private final T t8;

  798.         /** Secular correction for mean anomaly l: &delta;<sub>s</sub>l. */
  799.         private final T dsl;
  800.         /** Secular correction for perigee argument g: &delta;<sub>s</sub>g. */
  801.         private final T dsg;
  802.         /** Secular correction for raan h: &delta;<sub>s</sub>h. */
  803.         private final T dsh;

  804.         /** Secular rate of change of semi-major axis due to drag. */
  805.         private final T aRate;
  806.         /** Secular rate of change of eccentricity due to drag. */
  807.         private final T eRate;

  808.         // CHECKSTYLE: stop JavadocVariable check

  809.         // Storage for speed-up
  810.         private final T yp2;
  811.         private final T ci;
  812.         private final T si;
  813.         private final T oneMci2;
  814.         private final T ci2X3M1;

  815.         // Long periodic corrections factors
  816.         private final T vle1;
  817.         private final T vle2;
  818.         private final T vle3;
  819.         private final T vli1;
  820.         private final T vli2;
  821.         private final T vli3;
  822.         private final T vll2;
  823.         private final T vlh1I;
  824.         private final T vlh2I;
  825.         private final T vlh3I;
  826.         private final T vls1;
  827.         private final T vls2;
  828.         private final T vls3;

  829.         // CHECKSTYLE: resume JavadocVariable check

  830.         /** Create a model for specified mean orbit.
  831.          * @param mean mean Fieldorbit
  832.          * @param mass constant mass
  833.          * @param referenceRadius reference radius of the central body attraction model (m)
  834.          * @param mu central attraction coefficient (m³/s²)
  835.          * @param ck0 un-normalized zonal coefficients
  836.          */
  837.         FieldBLModel(final FieldKeplerianOrbit<T> mean, final T mass,
  838.                      final double referenceRadius, final T mu, final double[] ck0) {

  839.             this.mass = mass;
  840.             this.mu   = mu;

  841.             // mean orbit
  842.             this.mean = mean;

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

  844.             // mean eccentricity e"
  845.             final T epp = mean.getE();
  846.             if (epp.getReal() >= 1) {
  847.                 // Only for elliptical (e < 1) orbits
  848.                 throw new OrekitException(OrekitMessages.TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL,
  849.                                           epp.getReal());
  850.             }
  851.             final T epp2 = epp.square();

  852.             // η
  853.             n2 = one.subtract(epp2);
  854.             n  = n2.sqrt();
  855.             n3 = n2.multiply(n);
  856.             t8 = n.add(one.add(n).reciprocal());

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

  859.             // mean mean motion
  860.             n0 = mu.divide(app).sqrt().divide(app);

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

  863.             // γ2'
  864.             T ql = q.square();
  865.             T nl = n2.square();
  866.             yp2 = ql.multiply(-0.5 * ck0[2]).divide(nl);
  867.             final T yp22 = yp2.square();

  868.             // γ3'
  869.             ql = ql.multiply(q);
  870.             nl = nl.multiply(n2);
  871.             final T yp3 = ql.multiply(ck0[3]).divide(nl);

  872.             // γ4'
  873.             ql = ql.multiply(q);
  874.             nl = nl.multiply(n2);
  875.             final T yp4 = ql.multiply(0.375 * ck0[4]).divide(nl);

  876.             // γ5'
  877.             ql = ql.multiply(q);
  878.             nl = nl.multiply(n2);
  879.             final T yp5 = ql.multiply(ck0[5]).divide(nl);

  880.             // mean inclination I" sin & cos
  881.             final FieldSinCos<T> sc = FastMath.sinCos(mean.getI());
  882.             si = sc.sin();
  883.             ci = sc.cos();
  884.             final T ci2 = ci.square();
  885.             oneMci2 = one.subtract(ci2);
  886.             ci2X3M1 = ci2.multiply(3.).subtract(one);
  887.             final T ci2X5M1 = ci2.multiply(5.).subtract(one);

  888.             // secular corrections
  889.             // true anomaly
  890.             final T dsl1  = yp2.multiply(n).multiply(1.5);
  891.             final T dsl2a = n.multiply(n.multiply(25.).add(16.)).subtract(15.);
  892.             final T dsl2b = n.multiply(n.multiply(90.).add(96.)).negate().add(30.);
  893.             final T dsl2c = n.multiply(n.multiply(25.).add(144.)).add(105.);
  894.             final T dsl21 = dsl2a.add(ci2.multiply(dsl2b.add(ci2.multiply(dsl2c))));
  895.             final T dsl2  = ci2X3M1.add(yp2.multiply(0.0625).multiply(dsl21));
  896.             final T dsl3  = yp4.multiply(n).multiply(epp2).multiply(0.9375).
  897.                                 multiply(ci2.multiply(35.0).subtract(30.0).multiply(ci2).add(3.));
  898.             dsl = dsl1.multiply(dsl2).add(dsl3);

  899.             // perigee argument
  900.             final T dsg1  = yp2.multiply(1.5).multiply(ci2X5M1);
  901.             final T dsg2a = n.multiply(25.).add(24.).multiply(n).add(-35.);
  902.             final T dsg2b = n.multiply(126.).add(192.).multiply(n).negate().add(90.);
  903.             final T dsg2c = n.multiply(45.).add(360.).multiply(n).add(385.);
  904.             final T dsg21 = dsg2a.add(ci2.multiply(dsg2b.add(ci2.multiply(dsg2c))));
  905.             final T dsg2  = yp22.multiply(0.09375).multiply(dsg21);
  906.             final T dsg3a = n2.multiply(-9.).add(21.);
  907.             final T dsg3b = n2.multiply(126.).add(-270.);
  908.             final T dsg3c = n2.multiply(-189.).add(385.);
  909.             final T dsg31 = dsg3a.add(ci2.multiply(dsg3b.add(ci2.multiply(dsg3c))));
  910.             final T dsg3  = yp4.multiply(0.3125).multiply(dsg31);
  911.             dsg = dsg1.add(dsg2).add(dsg3);

  912.             // right ascension of ascending node
  913.             final T dsh1  = yp2.multiply(-3.);
  914.             final T dsh2a = n.multiply(9.).add(12.).multiply(n).add(-5.);
  915.             final T dsh2b = n.multiply(5.).add(36.).multiply(n).add(35.);
  916.             final T dsh21 = dsh2a.subtract(ci2.multiply(dsh2b));
  917.             final T dsh2  = yp22.multiply(0.375).multiply(dsh21);
  918.             final T dsh31 = n2.multiply(3.).subtract(5.);
  919.             final T dsh32 = ci2.multiply(7.).subtract(3.);
  920.             final T dsh3  = yp4.multiply(1.25).multiply(dsh31).multiply(dsh32);
  921.             dsh = ci.multiply(dsh1.add(dsh2).add(dsh3));

  922.             // secular rates of change due to drag
  923.             // Eq. 2.41 and Eq. 2.45 of Phipps' 1992 thesis
  924.             final T coef = n0.multiply(one.add(dsl)).multiply(3.).reciprocal().multiply(-4);
  925.             aRate = coef.multiply(app);
  926.             eRate = coef.multiply(epp).multiply(n2);

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

  929.             // factors for long periodic corrections
  930.             final T fs12 = yp3.divide(yp2);
  931.             final T fs13 = yp4.multiply(10).divide(yp2.multiply(3));
  932.             final T fs14 = yp5.divide(yp2);

  933.             final T ci2Xt2 = ci2.multiply(t2);
  934.             final T cA = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(11.)));
  935.             final T cB = one.subtract(ci2.multiply(ci2Xt2.multiply(8.)  .add(3.)));
  936.             final T cC = one.subtract(ci2.multiply(ci2Xt2.multiply(24.) .add(9.)));
  937.             final T cD = one.subtract(ci2.multiply(ci2Xt2.multiply(16.) .add(5.)));
  938.             final T cE = one.subtract(ci2.multiply(ci2Xt2.multiply(200.).add(33.)));
  939.             final T cF = one.subtract(ci2.multiply(ci2Xt2.multiply(40.) .add(9.)));

  940.             final T p5p   = one.add(ci2Xt2.multiply(ci2Xt2.multiply(20.).add(8.)));
  941.             final T p5p2  = one.add(p5p.multiply(2.));
  942.             final T p5p4  = one.add(p5p.multiply(4.));
  943.             final T p5p10 = one.add(p5p.multiply(10.));

  944.             final T e2X3P4  = epp2.multiply(3.).add(4.);
  945.             final T ciO1Pci = ci.divide(one.add(ci));
  946.             final T oneMci  = one.subtract(ci);

  947.             final T q1 = (yp2.multiply(cA).subtract(fs13.multiply(cB))).
  948.                             multiply(0.125);
  949.             final T q2 = (yp2.multiply(p5p10).subtract(fs13.multiply(p5p2))).
  950.                             multiply(epp2).multiply(ci).multiply(0.125);
  951.             final T q5 = (fs12.add(e2X3P4.multiply(fs14).multiply(cC).multiply(0.3125))).
  952.                             multiply(0.25);
  953.             final T p2 = p5p2.multiply(epp).multiply(ci).multiply(si).multiply(e2X3P4).multiply(fs14).
  954.                             multiply(0.46875);
  955.             final T p3 = epp.multiply(si).multiply(fs14).multiply(cC).
  956.                             multiply(0.15625);
  957.             final double kf = 35. / 1152.;
  958.             final T p4 = epp.multiply(fs14).multiply(cD).
  959.                             multiply(kf);
  960.             final T p5 = epp.multiply(epp2).multiply(ci).multiply(si).multiply(fs14).multiply(p5p4).
  961.                             multiply(2. * kf);

  962.             vle1 = epp.multiply(n2).multiply(q1);
  963.             vle2 = n2.multiply(si).multiply(q5);
  964.             vle3 = epp.multiply(n2).multiply(si).multiply(p4).multiply(-3.0);

  965.             vli1 = epp.multiply(q1).divide(si).negate();
  966.             vli2 = epp.multiply(ci).multiply(q5).negate();
  967.             vli3 = epp2.multiply(ci).multiply(p4).multiply(-3.0);

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

  969.             vlh1I = si.multiply(q2).negate();
  970.             vlh2I = epp.multiply(ci).multiply(q5).add(si.multiply(p2));
  971.             vlh3I = (epp2.multiply(ci).multiply(p4).add(si.multiply(p5))).negate();

  972.             vls1 = q1.multiply(n3.subtract(one)).
  973.                    subtract(q2).
  974.                    add(epp2.multiply(ci2).multiply(ci2Xt2).multiply(ci2Xt2).
  975.                        multiply(yp2.subtract(fs13.multiply(0.2))).multiply(25.0)).
  976.                    subtract(epp2.multiply(yp2.multiply(cE).subtract(fs13.multiply(cF))).multiply(0.0625));

  977.             vls2 = epp.multiply(si).multiply(t8.add(ciO1Pci)).multiply(q5).
  978.                    add((epp2.subtract(n3).multiply(3.).add(11.)).multiply(p3)).
  979.                    add(oneMci.multiply(p2));

  980.             vls3 = si.multiply(p4).multiply(n3.subtract(one).multiply(3.).
  981.                                             subtract(epp2.multiply(ciO1Pci.add(2.)))).
  982.                    subtract(oneMci.multiply(p5));
  983.         }

  984.         /**
  985.          * Get true anomaly from mean anomaly.
  986.          * @param lM  the mean anomaly (rad)
  987.          * @param ecc the eccentricity
  988.          * @return the true anomaly (rad)
  989.          */
  990.         private FieldUnivariateDerivative1<T> getTrueAnomaly(final FieldUnivariateDerivative1<T> lM,
  991.                                                              final FieldUnivariateDerivative1<T> ecc) {

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

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

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

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

  1000.             // Returns the true anomaly
  1001.             return lV;
  1002.         }

  1003.         /**
  1004.          * This method is used in Brouwer-Lyddane model to avoid singularity at the
  1005.          * critical inclination (i = 63.4°).
  1006.          * <p>
  1007.          * This method, based on Warren Phipps's 1992 thesis (Eq. 2.47 and 2.48),
  1008.          * approximate the factor (1.0 - 5.0 * cos²(i))<sup>-1</sup> (causing the singularity)
  1009.          * by a function, named T2 in the thesis.
  1010.          * </p>
  1011.          * @param cosI cosine of the mean inclination
  1012.          * @return an approximation of (1.0 - 5.0 * cos²(i))<sup>-1</sup> term
  1013.          */
  1014.         private T T2(final T cosI) {

  1015.             // X = (1.0 - 5.0 * cos²(i))
  1016.             final T x  = cosI.square().multiply(-5.0).add(1.0);
  1017.             final T x2 = x.square();
  1018.             final T xb = x2.multiply(BETA);

  1019.             // Eq. 2.48
  1020.             T sum = x.getField().getZero();
  1021.             for (int i = 0; i <= 12; i++) {
  1022.                 final double sign = i % 2 == 0 ? +1.0 : -1.0;
  1023.                 sum = sum.add(FastMath.pow(x2, i).
  1024.                               multiply(FastMath.pow(BETA, i)).
  1025.                               multiply(sign).
  1026.                               divide(CombinatoricsUtils.factorialDouble(i + 1)));
  1027.             }

  1028.             // Right term of equation 2.47
  1029.             final T one = x.getField().getOne();
  1030.             T product = one;
  1031.             for (int i = 0; i <= 10; i++) {
  1032.                 product = product.multiply(one.add(FastMath.exp(xb.multiply(FastMath.scalb(-1.0, i)))));
  1033.             }

  1034.             // Return (Eq. 2.47)
  1035.             return x.multiply(BETA).multiply(sum).multiply(product);
  1036.         }

  1037.         /** Extrapolate an orbit up to a specific target date.
  1038.          * @param date target date for the orbit
  1039.          * @param parameters model parameters
  1040.          * @return propagated parameters
  1041.          */
  1042.         public FieldKeplerianOrbit<T> propagateParameters(final FieldAbsoluteDate<T> date, final T[] parameters) {

  1043.             // Field
  1044.             final Field<T> field = date.getField();
  1045.             final T one  = field.getOne();
  1046.             final T zero = field.getZero();

  1047.             // Empirical drag coefficient M2
  1048.             final T m2 = parameters[0];

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

  1052.             final FieldUnivariateDerivative1<T> dtM2  = dt.multiply(m2);
  1053.             final FieldUnivariateDerivative1<T> dt2M2 = dt.multiply(dtM2);

  1054.             // Secular corrections
  1055.             // -------------------

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

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

  1061.             // mean argument of perigee
  1062.             final T gp0 = MathUtils.normalizeAngle(mean.getPerigeeArgument().add(dsg.multiply(not.getValue())), zero);
  1063.             final T gp1 = dsg.multiply(n0);
  1064.             final FieldUnivariateDerivative1<T> gpp = new FieldUnivariateDerivative1<>(gp0, gp1);

  1065.             // mean longitude of ascending node
  1066.             final T hp0 = MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(dsh.multiply(not.getValue())), zero);
  1067.             final T hp1 = dsh.multiply(n0);
  1068.             final FieldUnivariateDerivative1<T> hpp = new FieldUnivariateDerivative1<>(hp0, hp1);

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

  1073.             // Long period corrections
  1074.             //------------------------
  1075.             final FieldSinCos<FieldUnivariateDerivative1<T>> scgpp = gpp.sinCos();
  1076.             final FieldUnivariateDerivative1<T> cgpp = scgpp.cos();
  1077.             final FieldUnivariateDerivative1<T> sgpp = scgpp.sin();
  1078.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2gpp = gpp.multiply(2).sinCos();
  1079.             final FieldUnivariateDerivative1<T> c2gpp  = sc2gpp.cos();
  1080.             final FieldUnivariateDerivative1<T> s2gpp  = sc2gpp.sin();
  1081.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc3gpp = gpp.multiply(3).sinCos();
  1082.             final FieldUnivariateDerivative1<T> c3gpp  = sc3gpp.cos();
  1083.             final FieldUnivariateDerivative1<T> s3gpp  = sc3gpp.sin();

  1084.             // δ1e
  1085.             final FieldUnivariateDerivative1<T> d1e = c2gpp.multiply(vle1).
  1086.                                                       add(sgpp.multiply(vle2)).
  1087.                                                       add(s3gpp.multiply(vle3));

  1088.             // δ1I
  1089.             FieldUnivariateDerivative1<T> d1I = sgpp.multiply(vli2).
  1090.                                                 add(s3gpp.multiply(vli3));
  1091.             // Pseudo singular term, not to add if I" is zero
  1092.             if (Double.isFinite(vli1.getReal())) {
  1093.                 d1I = d1I.add(c2gpp.multiply(vli1));
  1094.             }

  1095.             // e"δ1l
  1096.             final FieldUnivariateDerivative1<T> eppd1l = s2gpp.multiply(vle1).
  1097.                                                          subtract(cgpp.multiply(vll2)).
  1098.                                                          subtract(c3gpp.multiply(vle3)).
  1099.                                                          multiply(n);

  1100.             // sinI"δ1h
  1101.             final FieldUnivariateDerivative1<T> sIppd1h = s2gpp.multiply(vlh1I).
  1102.                                                           add(cgpp.multiply(vlh2I)).
  1103.                                                           add(c3gpp.multiply(vlh3I));

  1104.             // δ1z = δ1l + δ1g + δ1h
  1105.             final FieldUnivariateDerivative1<T> d1z = s2gpp.multiply(vls1).
  1106.                                                       add(cgpp.multiply(vls2)).
  1107.                                                       add(c3gpp.multiply(vls3));

  1108.             // Short period corrections
  1109.             // ------------------------

  1110.             // true anomaly
  1111.             final FieldUnivariateDerivative1<T> fpp = getTrueAnomaly(lpp, epp);
  1112.             final FieldSinCos<FieldUnivariateDerivative1<T>> scfpp = fpp.sinCos();
  1113.             final FieldUnivariateDerivative1<T> cfpp = scfpp.cos();
  1114.             final FieldUnivariateDerivative1<T> sfpp = scfpp.sin();

  1115.             // e"sin(f')
  1116.             final FieldUnivariateDerivative1<T> eppsfpp = epp.multiply(sfpp);
  1117.             // e"cos(f')
  1118.             final FieldUnivariateDerivative1<T> eppcfpp = epp.multiply(cfpp);
  1119.             // 1 + e"cos(f')
  1120.             final FieldUnivariateDerivative1<T> eppcfppP1 = eppcfpp.add(1);
  1121.             // 2 + e"cos(f')
  1122.             final FieldUnivariateDerivative1<T> eppcfppP2 = eppcfpp.add(2);
  1123.             // 3 + e"cos(f')
  1124.             final FieldUnivariateDerivative1<T> eppcfppP3 = eppcfpp.add(3);
  1125.             // (1 + e"cos(f'))³
  1126.             final FieldUnivariateDerivative1<T> eppcfppP1_3 = eppcfppP1.square().multiply(eppcfppP1);

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

  1129.             // 2g" + f"
  1130.             final FieldUnivariateDerivative1<T> g2f = g2.add(fpp);
  1131.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2gf = g2f.sinCos();
  1132.             final FieldUnivariateDerivative1<T> c2gf = sc2gf.cos();
  1133.             final FieldUnivariateDerivative1<T> s2gf = sc2gf.sin();
  1134.             final FieldUnivariateDerivative1<T> eppc2gf = epp.multiply(c2gf);
  1135.             final FieldUnivariateDerivative1<T> epps2gf = epp.multiply(s2gf);

  1136.             // 2g" + 2f"
  1137.             final FieldUnivariateDerivative1<T> g2f2 = g2.add(fpp.multiply(2));
  1138.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2g2f = g2f2.sinCos();
  1139.             final FieldUnivariateDerivative1<T> c2g2f = sc2g2f.cos();
  1140.             final FieldUnivariateDerivative1<T> s2g2f = sc2g2f.sin();

  1141.             // 2g" + 3f"
  1142.             final FieldUnivariateDerivative1<T> g2f3 = g2.add(fpp.multiply(3));
  1143.             final FieldSinCos<FieldUnivariateDerivative1<T>> sc2g3f = g2f3.sinCos();
  1144.             final FieldUnivariateDerivative1<T> c2g3f = sc2g3f.cos();
  1145.             final FieldUnivariateDerivative1<T> s2g3f = sc2g3f.sin();

  1146.             // e"cos(2g" + 3f")
  1147.             final FieldUnivariateDerivative1<T> eppc2g3f = epp.multiply(c2g3f);
  1148.             // e"sin(2g" + 3f")
  1149.             final FieldUnivariateDerivative1<T> epps2g3f = epp.multiply(s2g3f);

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

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

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

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

  1158.             // sinCos(I"/2)
  1159.             final FieldSinCos<T> sci = FastMath.sinCos(mean.getI().divide(2.));
  1160.             final T siO2 = sci.sin();
  1161.             final T ciO2 = sci.cos();

  1162.             // δ2a
  1163.             final FieldUnivariateDerivative1<T> d2a = app.multiply(yp2).divide(n2).
  1164.                                                       multiply(eppcfppP1_3.subtract(n3).multiply(ci2X3M1).
  1165.                                                                add(c2g2f.multiply(eppcfppP1_3).multiply(oneMci2).multiply(3.)));

  1166.             // δ2e
  1167.             final FieldUnivariateDerivative1<T> d2e = (w20.add(epp.multiply(t8))).multiply(ci2X3M1).
  1168.                                                        add((w20.add(epp.multiply(c2g2f))).multiply(oneMci2.multiply(3))).
  1169.                                                        subtract((eppc2gf.multiply(3).add(eppc2g3f)).multiply(oneMci2.multiply(n2))).
  1170.                                                       multiply(yp2.multiply(0.5));

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

  1174.             // e"δ2l
  1175.             final FieldUnivariateDerivative1<T> eppd2l = (w22.add(1).multiply(sfpp).multiply(oneMci2).multiply(2.).
  1176.                                                          add((w22.subtract(1).negate().multiply(s2gf)).
  1177.                                                               add(w22.add(1. / 3.).multiply(s2g3f)).
  1178.                                                              multiply(oneMci2.multiply(3.)))).
  1179.                                                         multiply(yp2.divide(4.).multiply(n3)).negate();

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

  1183.             // δ2z = δ2l + δ2g + δ2h
  1184.             final T ttt = one.add(ci.multiply(ci.multiply(-5).add(2.)));
  1185.             final FieldUnivariateDerivative1<T> d2z = (epp.multiply(eppd2l).multiply(t8.subtract(one)).divide(n3).
  1186.                                                        add(w17.multiply(ttt).multiply(6).subtract(w21.multiply(ttt.add(2.))).
  1187.                                                            multiply(yp2.divide(4.)))).
  1188.                                                        negate();

  1189.             // Assembling elements
  1190.             // -------------------

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

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

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

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

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

  1201.             // Osculating elements
  1202.             // -------------------

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

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

  1207.             // Mean anomaly
  1208.             final FieldSinCos<FieldUnivariateDerivative1<T>> sclpp = lpp.sinCos();
  1209.             final FieldUnivariateDerivative1<T> clpp = sclpp.cos();
  1210.             final FieldUnivariateDerivative1<T> slpp = sclpp.sin();
  1211.             final FieldUnivariateDerivative1<T> l = FastMath.atan2(de.multiply(slpp).add(dl.multiply(clpp)),
  1212.                                                                    de.multiply(clpp).subtract(dl.multiply(slpp)));

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

  1215.             // Longitude of ascending node
  1216.             final FieldSinCos<FieldUnivariateDerivative1<T>> schpp = hpp.sinCos();
  1217.             final FieldUnivariateDerivative1<T> chpp = schpp.cos();
  1218.             final FieldUnivariateDerivative1<T> shpp = schpp.sin();
  1219.             final FieldUnivariateDerivative1<T> h = FastMath.atan2(di.multiply(shpp).add(dh.multiply(chpp)),
  1220.                                                                    di.multiply(chpp).subtract(dh.multiply(shpp)));

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

  1223.             // Return a Keplerian orbit
  1224.             return new FieldKeplerianOrbit<>(a.getValue(), e.getValue(), i.getValue(),
  1225.                                              g.getValue(), h.getValue(), l.getValue(),
  1226.                                              a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(),
  1227.                                              g.getFirstDerivative(), h.getFirstDerivative(), l.getFirstDerivative(),
  1228.                                              PositionAngleType.MEAN, mean.getFrame(), date, this.mu);
  1229.         }
  1230.     }

  1231.     /** {@inheritDoc} */
  1232.     @Override
  1233.     protected T getMass(final FieldAbsoluteDate<T> date) {
  1234.         return models.get(date).mass;
  1235.     }

  1236.     /** {@inheritDoc} */
  1237.     @Override
  1238.     public List<ParameterDriver> getParametersDrivers() {
  1239.         return Collections.singletonList(M2Driver);
  1240.     }

  1241. }