DSSTZonal.java

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

  18. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.SortedSet;
  26. import java.util.TreeMap;

  27. import org.hipparchus.exception.LocalizedCoreFormats;
  28. import org.hipparchus.util.FastMath;
  29. import org.orekit.attitudes.AttitudeProvider;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  32. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  33. import org.orekit.orbits.Orbit;
  34. import org.orekit.propagation.SpacecraftState;
  35. import org.orekit.propagation.events.EventDetector;
  36. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  37. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  38. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  39. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
  40. import org.orekit.propagation.semianalytical.dsst.utilities.GHIJjsPolynomials;
  41. import org.orekit.propagation.semianalytical.dsst.utilities.LnsCoefficients;
  42. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  43. import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
  44. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenZonalLinear;
  45. import org.orekit.time.AbsoluteDate;
  46. import org.orekit.utils.TimeSpanMap;

  47. /** Zonal contribution to the central body gravitational perturbation.
  48.  *
  49.  *   @author Romain Di Costanzo
  50.  *   @author Pascal Parraud
  51.  */
  52. public class DSSTZonal implements DSSTForceModel {

  53.     /** Truncation tolerance. */
  54.     private static final double TRUNCATION_TOLERANCE = 1e-4;

  55.     /** Number of points for interpolation. */
  56.     private static final int INTERPOLATION_POINTS = 3;

  57.     /** Retrograde factor I.
  58.      *  <p>
  59.      *  DSST model needs equinoctial orbit as internal representation.
  60.      *  Classical equinoctial elements have discontinuities when inclination
  61.      *  is close to zero. In this representation, I = +1. <br>
  62.      *  To avoid this discontinuity, another representation exists and equinoctial
  63.      *  elements can be expressed in a different way, called "retrograde" orbit.
  64.      *  This implies I = -1. <br>
  65.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  66.      *  But for the sake of consistency with the theory, the retrograde factor
  67.      *  has been kept in the formulas.
  68.      *  </p>
  69.      */
  70.     private static final int I = 1;

  71.     /** Provider for spherical harmonics. */
  72.     private final UnnormalizedSphericalHarmonicsProvider provider;

  73.     /** Maximal degree to consider for harmonics potential. */
  74.     private final int maxDegree;

  75.     /** Maximal degree to consider for harmonics potential. */
  76.     private final int maxDegreeShortPeriodics;

  77.     /** Maximal degree to consider for harmonics potential in short periodic computations. */
  78.     private final int maxOrder;

  79.     /** Factorial. */
  80.     private final double[] fact;

  81.     /** Coefficient used to define the mean disturbing function V<sub>ns</sub> coefficient. */
  82.     private final TreeMap<NSKey, Double> Vns;

  83.     /** Highest power of the eccentricity to be used in mean elements computations. */
  84.     private int maxEccPowMeanElements;

  85.     /** Highest power of the eccentricity to be used in short periodic computations. */
  86.     private final int maxEccPowShortPeriodics;

  87.     /** Maximum frequency in true longitude for short periodic computations. */
  88.     private final int maxFrequencyShortPeriodics;

  89.     /** Short period terms. */
  90.     private ZonalShortPeriodicCoefficients zonalSPCoefs;

  91.     // Equinoctial elements (according to DSST notation)
  92.     /** a. */
  93.     private double a;
  94.     /** ex. */
  95.     private double k;
  96.     /** ey. */
  97.     private double h;
  98.     /** hx. */
  99.     private double q;
  100.     /** hy. */
  101.     private double p;

  102.     /** Eccentricity. */
  103.     private double ecc;

  104.     /** Direction cosine &alpha. */
  105.     private double alpha;
  106.     /** Direction cosine &beta. */
  107.     private double beta;
  108.     /** Direction cosine &gamma. */
  109.     private double gamma;

  110.     // Common factors for potential computation
  111.     /** &Chi; = 1 / sqrt(1 - e²) = 1 / B. */
  112.     private double X;
  113.     /** &Chi;². */
  114.     private double XX;
  115.     /** &Chi;³. */
  116.     private double XXX;
  117.     /** 1 / (A * B) .*/
  118.     private double ooAB;
  119.     /** B / A .*/
  120.     private double BoA;
  121.     /** B / A(1 + B) .*/
  122.     private double BoABpo;
  123.     /** -C / (2 * A * B) .*/
  124.     private double mCo2AB;
  125.     /** -2 * a / A .*/
  126.     private double m2aoA;
  127.     /** μ / a .*/
  128.     private double muoa;
  129.     /** R / a .*/
  130.     private double roa;

  131.     /** An array that contains the objects needed to build the Hansen coefficients. <br/>
  132.      * The index is s*/
  133.     private HansenZonalLinear[] hansenObjects;

  134.     /** The current value of the U function. <br/>
  135.      * Needed when computed the short periodic contribution */
  136.     private double U;

  137.     /** A = sqrt( μ * a ) = n * a². */
  138.     private double A;
  139.     /** B = sqrt( 1 - k² - h² ). */
  140.     private double B;
  141.     /** C = 1 + p² + Q². */
  142.     private double C;

  143.     /** The mean motion (n). */
  144.     private double meanMotion;

  145.     /** h * k. */
  146.     private double hk;
  147.     /** k² - h². */
  148.     private double k2mh2;
  149.     /** (k² - h²) / 2. */
  150.     private double k2mh2o2;
  151.     /** 1 / (n² * a²). */
  152.     private double oon2a2;
  153.     /** 1 / (n² * a) . */
  154.     private double oon2a;
  155.     /** χ³ / (n² * a). */
  156.     private double x3on2a;
  157.     /** χ / (n² * a²). */
  158.     private double xon2a2;
  159.     /** (C * χ) / ( 2 * n² * a² ). */
  160.     private double cxo2n2a2;
  161.     /** (χ²) / (n² * a² * (χ + 1 ) ). */
  162.     private double x2on2a2xp1;
  163.     /** B * B.*/
  164.     private double BB;

  165.     /** Simple constructor.
  166.      * @param provider provider for spherical harmonics
  167.      * @param maxDegreeShortPeriodics maximum degree to consider for short periodics zonal harmonics potential
  168.      * (must be between 2 and {@code provider.getMaxDegree()})
  169.      * @param maxEccPowShortPeriodics maximum power of the eccentricity to be used in short periodic computations
  170.      * (must be between 0 and {@code maxDegreeShortPeriodics - 1}, but should typically not exceed 4 as higher
  171.      * values will exceed computer capacity)
  172.      * @param maxFrequencyShortPeriodics maximum frequency in true longitude for short periodic computations
  173.      * (must be between 1 and {@code 2 * maxDegreeShortPeriodics + 1})
  174.      * @exception OrekitException if degrees or powers are out of range
  175.      * @since 7.2
  176.      */
  177.     public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider,
  178.                      final int maxDegreeShortPeriodics,
  179.                      final int maxEccPowShortPeriodics,
  180.                      final int maxFrequencyShortPeriodics)
  181.         throws OrekitException {

  182.         this.provider  = provider;
  183.         this.maxDegree = provider.getMaxDegree();
  184.         this.maxOrder  = provider.getMaxOrder();

  185.         checkIndexRange(maxDegreeShortPeriodics, 2, maxDegree);
  186.         this.maxDegreeShortPeriodics = maxDegreeShortPeriodics;

  187.         checkIndexRange(maxEccPowShortPeriodics, 0, maxDegreeShortPeriodics - 1);
  188.         this.maxEccPowShortPeriodics = maxEccPowShortPeriodics;

  189.         checkIndexRange(maxFrequencyShortPeriodics, 1, 2 * maxDegreeShortPeriodics + 1);
  190.         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;

  191.         // Vns coefficients
  192.         this.Vns = CoefficientsFactory.computeVns(maxDegree + 1);

  193.         // Factorials computation
  194.         final int maxFact = 2 * maxDegree + 1;
  195.         this.fact = new double[maxFact];
  196.         fact[0] = 1.;
  197.         for (int i = 1; i < maxFact; i++) {
  198.             fact[i] = i * fact[i - 1];
  199.         }

  200.         // Initialize default values
  201.         this.maxEccPowMeanElements = (maxDegree == 2) ? 0 : Integer.MIN_VALUE;

  202.     }

  203.     /** Check an index range.
  204.      * @param index index value
  205.      * @param min minimum value for index
  206.      * @param max maximum value for index
  207.      * @exception OrekitException if index is out of range
  208.      */
  209.     private void checkIndexRange(final int index, final int min, final int max)
  210.         throws OrekitException {
  211.         if (index < min || index > max) {
  212.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
  213.         }
  214.     }

  215.     /** Get the spherical harmonics provider.
  216.      *  @return the spherical harmonics provider
  217.      */
  218.     public UnnormalizedSphericalHarmonicsProvider getProvider() {
  219.         return provider;
  220.     }

  221.     /** {@inheritDoc}
  222.      *  <p>
  223.      *  Computes the highest power of the eccentricity to appear in the truncated
  224.      *  analytical power series expansion.
  225.      *  </p>
  226.      *  <p>
  227.      *  This method computes the upper value for the central body potential and
  228.      *  determines the maximal power for the eccentricity producing potential
  229.      *  terms bigger than a defined tolerance.
  230.      *  </p>
  231.      */
  232.     @Override
  233.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements aux, final boolean meanOnly)
  234.         throws OrekitException {

  235.         computeMeanElementsTruncations(aux);

  236.         final int maxEccPow;
  237.         if (!meanOnly) {
  238.             maxEccPow = FastMath.max(maxEccPowMeanElements, maxEccPowShortPeriodics);
  239.         } else {
  240.             maxEccPow = maxEccPowMeanElements;
  241.         }

  242.         //Initialize the HansenCoefficient generator
  243.         this.hansenObjects = new HansenZonalLinear[maxEccPow + 1];

  244.         for (int s = 0; s <= maxEccPow; s++) {
  245.             this.hansenObjects[s] = new HansenZonalLinear(maxDegree, s);
  246.         }

  247.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  248.         zonalSPCoefs = new ZonalShortPeriodicCoefficients(maxFrequencyShortPeriodics,
  249.                                                           INTERPOLATION_POINTS,
  250.                                                           new TimeSpanMap<Slot>(new Slot(maxFrequencyShortPeriodics,
  251.                                                                                          INTERPOLATION_POINTS)));
  252.         list.add(zonalSPCoefs);
  253.         return list;

  254.     }

  255.     /** Compute indices truncations for mean elements computations.
  256.      * @param aux auxiliary elements
  257.      * @throws OrekitException if an error occurs
  258.      */
  259.     private void computeMeanElementsTruncations(final AuxiliaryElements aux) throws OrekitException {

  260.         //Compute the max eccentricity power for the mean element rate expansion
  261.         if (maxDegree == 2) {
  262.             maxEccPowMeanElements = 0;
  263.         } else {
  264.             // Initializes specific parameters.
  265.             initializeStep(aux);
  266.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(aux.getDate());

  267.             // Utilities for truncation
  268.             final double ax2or = 2. * a / provider.getAe();
  269.             double xmuran = provider.getMu() / a;
  270.             // Set a lower bound for eccentricity
  271.             final double eo2  = FastMath.max(0.0025, ecc / 2.);
  272.             final double x2o2 = XX / 2.;
  273.             final double[] eccPwr = new double[maxDegree + 1];
  274.             final double[] chiPwr = new double[maxDegree + 1];
  275.             final double[] hafPwr = new double[maxDegree + 1];
  276.             eccPwr[0] = 1.;
  277.             chiPwr[0] = X;
  278.             hafPwr[0] = 1.;
  279.             for (int i = 1; i <= maxDegree; i++) {
  280.                 eccPwr[i] = eccPwr[i - 1] * eo2;
  281.                 chiPwr[i] = chiPwr[i - 1] * x2o2;
  282.                 hafPwr[i] = hafPwr[i - 1] * 0.5;
  283.                 xmuran  /= ax2or;
  284.             }

  285.             // Set highest power of e and degree of current spherical harmonic.
  286.             maxEccPowMeanElements = 0;
  287.             int n = maxDegree;
  288.             // Loop over n
  289.             do {
  290.                 // Set order of current spherical harmonic.
  291.                 int m = 0;
  292.                 // Loop over m
  293.                 do {
  294.                     // Compute magnitude of current spherical harmonic coefficient.
  295.                     final double cnm = harmonics.getUnnormalizedCnm(n, m);
  296.                     final double snm = harmonics.getUnnormalizedSnm(n, m);
  297.                     final double csnm = FastMath.hypot(cnm, snm);
  298.                     if (csnm == 0.) break;
  299.                     // Set magnitude of last spherical harmonic term.
  300.                     double lastTerm = 0.;
  301.                     // Set current power of e and related indices.
  302.                     int nsld2 = (n - maxEccPowMeanElements - 1) / 2;
  303.                     int l = n - 2 * nsld2;
  304.                     // Loop over l
  305.                     double term = 0.;
  306.                     do {
  307.                         // Compute magnitude of current spherical harmonic term.
  308.                         if (m < l) {
  309.                             term = csnm * xmuran *
  310.                                     (fact[n - l] / (fact[n - m])) *
  311.                                     (fact[n + l] / (fact[nsld2] * fact[nsld2 + l])) *
  312.                                     eccPwr[l] * UpperBounds.getDnl(XX, chiPwr[l], n, l) *
  313.                                     (UpperBounds.getRnml(gamma, n, l, m, 1, I) + UpperBounds.getRnml(gamma, n, l, m, -1, I));
  314.                         } else {
  315.                             term = csnm * xmuran *
  316.                                     (fact[n + m] / (fact[nsld2] * fact[nsld2 + l])) *
  317.                                     eccPwr[l] * hafPwr[m - l] * UpperBounds.getDnl(XX, chiPwr[l], n, l) *
  318.                                     (UpperBounds.getRnml(gamma, n, m, l, 1, I) + UpperBounds.getRnml(gamma, n, m, l, -1, I));
  319.                         }
  320.                         // Is the current spherical harmonic term bigger than the truncation tolerance ?
  321.                         if (term >= TRUNCATION_TOLERANCE) {
  322.                             maxEccPowMeanElements = l;
  323.                         } else {
  324.                             // Is the current term smaller than the last term ?
  325.                             if (term < lastTerm) {
  326.                                 break;
  327.                             }
  328.                         }
  329.                         // Proceed to next power of e.
  330.                         lastTerm = term;
  331.                         l += 2;
  332.                         nsld2--;
  333.                     } while (l < n);
  334.                     // Is the current spherical harmonic term bigger than the truncation tolerance ?
  335.                     if (term >= TRUNCATION_TOLERANCE) {
  336.                         maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  337.                         return;
  338.                     }
  339.                     // Proceed to next order.
  340.                     m++;
  341.                 } while (m <= FastMath.min(n, maxOrder));
  342.                 // Proceed to next degree.
  343.                 xmuran *= ax2or;
  344.                 n--;
  345.             } while (n > maxEccPowMeanElements + 2);

  346.             maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  347.         }
  348.     }

  349.     /** {@inheritDoc} */
  350.     @Override
  351.     public void initializeStep(final AuxiliaryElements aux) throws OrekitException {

  352.         // Equinoctial elements
  353.         a = aux.getSma();
  354.         k = aux.getK();
  355.         h = aux.getH();
  356.         q = aux.getQ();
  357.         p = aux.getP();

  358.         // Eccentricity
  359.         ecc = aux.getEcc();

  360.         // Direction cosines
  361.         alpha = aux.getAlpha();
  362.         beta  = aux.getBeta();
  363.         gamma = aux.getGamma();

  364.         // Equinoctial coefficients
  365.         A = aux.getA();
  366.         B = aux.getB();
  367.         C = aux.getC();

  368.         // &Chi; = 1 / B
  369.         X = 1. / B;
  370.         XX = X * X;
  371.         XXX = X * XX;
  372.         // 1 / AB
  373.         ooAB = 1. / (A * B);
  374.         // B / A
  375.         BoA = B / A;
  376.         // -C / 2AB
  377.         mCo2AB = -C * ooAB / 2.;
  378.         // B / A(1 + B)
  379.         BoABpo = BoA / (1. + B);
  380.         // -2 * a / A
  381.         m2aoA = -2 * a / A;
  382.         // μ / a
  383.         muoa = provider.getMu() / a;
  384.         // R / a
  385.         roa = provider.getAe() / a;

  386.         // Mean motion
  387.         meanMotion = aux.getMeanMotion();
  388.     }

  389.     /** {@inheritDoc} */
  390.     @Override
  391.     public double[] getMeanElementRate(final SpacecraftState spacecraftState) throws OrekitException {
  392.         return computeMeanElementRates(spacecraftState.getDate());
  393.     }

  394.     /** {@inheritDoc} */
  395.     @Override
  396.     public EventDetector[] getEventsDetectors() {
  397.         return null;
  398.     }

  399.     /** Compute the mean element rates.
  400.      * @param date current date
  401.      * @return the mean element rates
  402.      * @throws OrekitException if an error occurs in hansen computation
  403.      */
  404.     private double[] computeMeanElementRates(final AbsoluteDate date) throws OrekitException {
  405.         // Compute potential derivative
  406.         final double[] dU  = computeUDerivatives(date);
  407.         final double dUda  = dU[0];
  408.         final double dUdk  = dU[1];
  409.         final double dUdh  = dU[2];
  410.         final double dUdAl = dU[3];
  411.         final double dUdBe = dU[4];
  412.         final double dUdGa = dU[5];

  413.         // Compute cross derivatives [Eq. 2.2-(8)]
  414.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  415.         final double UAlphaGamma   = alpha * dUdGa - gamma * dUdAl;
  416.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  417.         final double UBetaGamma    =  beta * dUdGa - gamma * dUdBe;
  418.         // Common factor
  419.         final double pUAGmIqUBGoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;

  420.         // Compute mean elements rates [Eq. 3.1-(1)]
  421.         final double da =  0.;
  422.         final double dh =  BoA * dUdk + k * pUAGmIqUBGoAB;
  423.         final double dk = -BoA * dUdh - h * pUAGmIqUBGoAB;
  424.         final double dp =  mCo2AB * UBetaGamma;
  425.         final double dq =  mCo2AB * UAlphaGamma * I;
  426.         final double dM =  m2aoA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUAGmIqUBGoAB;

  427.         return new double[] {da, dk, dh, dq, dp, dM};
  428.     }

  429.     /** Compute the derivatives of the gravitational potential U [Eq. 3.1-(6)].
  430.      *  <p>
  431.      *  The result is the array
  432.      *  [dU/da, dU/dk, dU/dh, dU/dα, dU/dβ, dU/dγ]
  433.      *  </p>
  434.      *  @param date current date
  435.      *  @return potential derivatives
  436.      *  @throws OrekitException if an error occurs in hansen computation
  437.      */
  438.     private double[] computeUDerivatives(final AbsoluteDate date) throws OrekitException {

  439.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);

  440.         //Reset U
  441.         U = 0.;

  442.         // Gs and Hs coefficients
  443.         final double[][] GsHs = CoefficientsFactory.computeGsHs(k, h, alpha, beta, maxEccPowMeanElements);
  444.         // Qns coefficients
  445.         final double[][] Qns  = CoefficientsFactory.computeQns(gamma, maxDegree, maxEccPowMeanElements);

  446.         final double[] roaPow = new double[maxDegree + 1];
  447.         roaPow[0] = 1.;
  448.         for (int i = 1; i <= maxDegree; i++) {
  449.             roaPow[i] = roa * roaPow[i - 1];
  450.         }

  451.         // Potential derivatives
  452.         double dUda  = 0.;
  453.         double dUdk  = 0.;
  454.         double dUdh  = 0.;
  455.         double dUdAl = 0.;
  456.         double dUdBe = 0.;
  457.         double dUdGa = 0.;

  458.         for (int s = 0; s <= maxEccPowMeanElements; s++) {
  459.             //Initialize the Hansen roots
  460.             this.hansenObjects[s].computeInitValues(X);

  461.             // Get the current Gs coefficient
  462.             final double gs = GsHs[0][s];

  463.             // Compute Gs partial derivatives from 3.1-(9)
  464.             double dGsdh  = 0.;
  465.             double dGsdk  = 0.;
  466.             double dGsdAl = 0.;
  467.             double dGsdBe = 0.;
  468.             if (s > 0) {
  469.                 // First get the G(s-1) and the H(s-1) coefficients
  470.                 final double sxgsm1 = s * GsHs[0][s - 1];
  471.                 final double sxhsm1 = s * GsHs[1][s - 1];
  472.                 // Then compute derivatives
  473.                 dGsdh  = beta  * sxgsm1 - alpha * sxhsm1;
  474.                 dGsdk  = alpha * sxgsm1 + beta  * sxhsm1;
  475.                 dGsdAl = k * sxgsm1 - h * sxhsm1;
  476.                 dGsdBe = h * sxgsm1 + k * sxhsm1;
  477.             }

  478.             // Kronecker symbol (2 - delta(0,s))
  479.             final double d0s = (s == 0) ? 1 : 2;

  480.             for (int n = s + 2; n <= maxDegree; n++) {
  481.                 // (n - s) must be even
  482.                 if ((n - s) % 2 == 0) {

  483.                     //Extract data from previous computation :
  484.                     final double kns   = this.hansenObjects[s].getValue(-n - 1, X);
  485.                     final double dkns  = this.hansenObjects[s].getDerivative(-n - 1, X);

  486.                     final double vns   = Vns.get(new NSKey(n, s));
  487.                     final double coef0 = d0s * roaPow[n] * vns * -harmonics.getUnnormalizedCnm(n, 0);
  488.                     final double coef1 = coef0 * Qns[n][s];
  489.                     final double coef2 = coef1 * kns;
  490.                     final double coef3 = coef2 * gs;
  491.                     // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  492.                     final double dqns  = Qns[n][s + 1];

  493.                     // Compute U
  494.                     U += coef3;
  495.                     // Compute dU / da :
  496.                     dUda += coef3 * (n + 1);
  497.                     // Compute dU / dEx
  498.                     dUdk += coef1 * (kns * dGsdk + k * XXX * gs * dkns);
  499.                     // Compute dU / dEy
  500.                     dUdh += coef1 * (kns * dGsdh + h * XXX * gs * dkns);
  501.                     // Compute dU / dAlpha
  502.                     dUdAl += coef2 * dGsdAl;
  503.                     // Compute dU / dBeta
  504.                     dUdBe += coef2 * dGsdBe;
  505.                     // Compute dU / dGamma
  506.                     dUdGa += coef0 * kns * dqns * gs;

  507.                 }
  508.             }
  509.         }

  510.         // Multiply by -(μ / a)
  511.         U *= -muoa;

  512.         return new double[] {
  513.             dUda  *  muoa / a,
  514.             dUdk  * -muoa,
  515.             dUdh  * -muoa,
  516.             dUdAl * -muoa,
  517.             dUdBe * -muoa,
  518.             dUdGa * -muoa
  519.         };
  520.     }

  521.     /** {@inheritDoc} */
  522.     @Override
  523.     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
  524.         //nothing is done since this contribution is not sensitive to attitude
  525.     }

  526.     /** Check if an index is within the accepted interval.
  527.      *
  528.      * @param index the index to check
  529.      * @param lowerBound the lower bound of the interval
  530.      * @param upperBound the upper bound of the interval
  531.      * @return true if the index is between the lower and upper bounds, false otherwise
  532.      */
  533.     private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
  534.         return index >= lowerBound && index <= upperBound;
  535.     }

  536.     /** {@inheritDoc} */
  537.     @Override
  538.     public void updateShortPeriodTerms(final SpacecraftState... meanStates)
  539.         throws OrekitException {

  540.         final Slot slot = zonalSPCoefs.createSlot(meanStates);
  541.         for (final SpacecraftState meanState : meanStates) {

  542.             initializeStep(new AuxiliaryElements(meanState.getOrbit(), I));

  543.             // h * k.
  544.             this.hk = h * k;
  545.             // k² - h².
  546.             this.k2mh2 = k * k - h * h;
  547.             // (k² - h²) / 2.
  548.             this.k2mh2o2 = k2mh2 / 2.;
  549.             // 1 / (n² * a²) = 1 / (n * A)
  550.             this.oon2a2 = 1 / (A * meanMotion);
  551.             // 1 / (n² * a) = a / (n * A)
  552.             this.oon2a = a * oon2a2;
  553.             // χ³ / (n² * a)
  554.             this.x3on2a = XXX * oon2a;
  555.             // χ / (n² * a²)
  556.             this.xon2a2 = X * oon2a2;
  557.             // (C * χ) / ( 2 * n² * a² )
  558.             this.cxo2n2a2 = xon2a2 * C / 2;
  559.             // (χ²) / (n² * a² * (χ + 1 ) )
  560.             this.x2on2a2xp1 = xon2a2 * X / (X + 1);
  561.             // B * B
  562.             this.BB = B * B;

  563.             // Compute rhoj and sigmaj
  564.             final double[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot);

  565.             // Compute Di
  566.             computeDiCoefficients(meanState.getDate(), slot);

  567.             // generate the Cij and Sij coefficients
  568.             final FourierCjSjCoefficients cjsj = new FourierCjSjCoefficients(meanState.getDate(),
  569.                                                                              maxDegreeShortPeriodics,
  570.                                                                              maxEccPowShortPeriodics,
  571.                                                                              maxFrequencyShortPeriodics);
  572.             computeCijSijCoefficients(meanState.getDate(), slot, cjsj, rhoSigma);
  573.         }

  574.     }

  575.     /** Generate the values for the D<sub>i</sub> coefficients.
  576.      * @param date target date
  577.      * @param slot slot to which the coefficients belong
  578.      * @throws OrekitException if an error occurs during the coefficient computation
  579.      */
  580.     private void computeDiCoefficients(final AbsoluteDate date, final Slot slot)
  581.         throws OrekitException {
  582.         final double[] meanElementRates = computeMeanElementRates(date);
  583.         final double[] currentDi = new double[6];

  584.         // Add the coefficients to the interpolation grid
  585.         for (int i = 0; i < 6; i++) {
  586.             currentDi[i] = meanElementRates[i] / meanMotion;

  587.             if (i == 5) {
  588.                 currentDi[i] += -1.5 * 2 * U * oon2a2;
  589.             }

  590.         }

  591.         slot.di.addGridPoint(date, currentDi);

  592.     }

  593.     /**
  594.      * Generate the values for the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
  595.      * @param date date of computation
  596.      * @param slot slot to which the coefficients belong
  597.      * @param cjsj Fourier coefficients
  598.      * @param rhoSigma ρ<sub>j</sub> and σ<sub>j</sub>
  599.      */
  600.     private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot,
  601.                                            final FourierCjSjCoefficients cjsj,
  602.                                            final double[][] rhoSigma) {

  603.         final int nMax = maxDegreeShortPeriodics;

  604.         // The C<sub>i</sub>⁰ coefficients
  605.         final double[] currentCi0 = new double[] {0., 0., 0., 0., 0., 0.};
  606.         for (int j = 1; j < slot.cij.length; j++) {

  607.             // Create local arrays
  608.             final double[] currentCij = new double[] {0., 0., 0., 0., 0., 0.};
  609.             final double[] currentSij = new double[] {0., 0., 0., 0., 0., 0.};

  610.             // j == 1
  611.             if (j == 1) {
  612.                 final double coef1 = 4 * k * U - hk * cjsj.getCj(1) + k2mh2o2 * cjsj.getSj(1);
  613.                 final double coef2 = 4 * h * U + k2mh2o2 * cjsj.getCj(1) + hk * cjsj.getSj(1);
  614.                 final double coef3 = (k * cjsj.getCj(1) + h * cjsj.getSj(1)) / 4.;
  615.                 final double coef4 = (8 * U - h * cjsj.getCj(1) + k * cjsj.getSj(1)) / 4.;

  616.                 //Coefficients for a
  617.                 currentCij[0] += coef1;
  618.                 currentSij[0] += coef2;

  619.                 //Coefficients for k
  620.                 currentCij[1] += coef4;
  621.                 currentSij[1] += coef3;

  622.                 //Coefficients for h
  623.                 currentCij[2] -= coef3;
  624.                 currentSij[2] += coef4;

  625.                 //Coefficients for λ
  626.                 currentCij[5] -= coef2 / 2;
  627.                 currentSij[5] += coef1 / 2;
  628.             }

  629.             // j == 2
  630.             if (j == 2) {
  631.                 final double coef1 = k2mh2 * U;
  632.                 final double coef2 = 2 * hk * U;
  633.                 final double coef3 = h * U / 2;
  634.                 final double coef4 = k * U / 2;

  635.                 //Coefficients for a
  636.                 currentCij[0] += coef1;
  637.                 currentSij[0] += coef2;

  638.                 //Coefficients for k
  639.                 currentCij[1] += coef4;
  640.                 currentSij[1] += coef3;

  641.                 //Coefficients for h
  642.                 currentCij[2] -= coef3;
  643.                 currentSij[2] += coef4;

  644.                 //Coefficients for λ
  645.                 currentCij[5] -= coef2 / 2;
  646.                 currentSij[5] += coef1 / 2;
  647.             }

  648.             // j between 1 and 2N-3
  649.             if (isBetween(j, 1, 2 * nMax - 3) && j + 2 < cjsj.jMax) {
  650.                 final double coef1 = ( j + 2 ) * (-hk * cjsj.getCj(j + 2) + k2mh2o2 * cjsj.getSj(j + 2));
  651.                 final double coef2 = ( j + 2 ) * (k2mh2o2 * cjsj.getCj(j + 2) + hk * cjsj.getSj(j + 2));
  652.                 final double coef3 = ( j + 2 ) * (k * cjsj.getCj(j + 2) + h * cjsj.getSj(j + 2)) / 4;
  653.                 final double coef4 = ( j + 2 ) * (h * cjsj.getCj(j + 2) - k * cjsj.getSj(j + 2)) / 4;

  654.                 //Coefficients for a
  655.                 currentCij[0] += coef1;
  656.                 currentSij[0] -= coef2;

  657.                 //Coefficients for k
  658.                 currentCij[1] += -coef4;
  659.                 currentSij[1] -= coef3;

  660.                 //Coefficients for h
  661.                 currentCij[2] -= coef3;
  662.                 currentSij[2] += coef4;

  663.                 //Coefficients for λ
  664.                 currentCij[5] -= coef2 / 2;
  665.                 currentSij[5] += -coef1 / 2;
  666.             }

  667.             // j between 1 and 2N-2
  668.             if (isBetween(j, 1, 2 * nMax - 2) && j + 1 < cjsj.jMax) {
  669.                 final double coef1 = 2 * ( j + 1 ) * (-h * cjsj.getCj(j + 1) + k * cjsj.getSj(j + 1));
  670.                 final double coef2 = 2 * ( j + 1 ) * (k * cjsj.getCj(j + 1) + h * cjsj.getSj(j + 1));
  671.                 final double coef3 = ( j + 1 ) * cjsj.getCj(j + 1);
  672.                 final double coef4 = ( j + 1 ) * cjsj.getSj(j + 1);

  673.                 //Coefficients for a
  674.                 currentCij[0] += coef1;
  675.                 currentSij[0] -= coef2;

  676.                 //Coefficients for k
  677.                 currentCij[1] += coef4;
  678.                 currentSij[1] -= coef3;

  679.                 //Coefficients for h
  680.                 currentCij[2] -= coef3;
  681.                 currentSij[2] -= coef4;

  682.                 //Coefficients for λ
  683.                 currentCij[5] -= coef2 / 2;
  684.                 currentSij[5] += -coef1 / 2;
  685.             }

  686.             // j between 2 and 2N
  687.             if (isBetween(j, 2, 2 * nMax) && j - 1 < cjsj.jMax) {
  688.                 final double coef1 = 2 * ( j - 1 ) * (h * cjsj.getCj(j - 1) + k * cjsj.getSj(j - 1));
  689.                 final double coef2 = 2 * ( j - 1 ) * (k * cjsj.getCj(j - 1) - h * cjsj.getSj(j - 1));
  690.                 final double coef3 = ( j - 1 ) * cjsj.getCj(j - 1);
  691.                 final double coef4 = ( j - 1 ) * cjsj.getSj(j - 1);

  692.                 //Coefficients for a
  693.                 currentCij[0] += coef1;
  694.                 currentSij[0] -= coef2;

  695.                 //Coefficients for k
  696.                 currentCij[1] += coef4;
  697.                 currentSij[1] -= coef3;

  698.                 //Coefficients for h
  699.                 currentCij[2] += coef3;
  700.                 currentSij[2] += coef4;

  701.                 //Coefficients for λ
  702.                 currentCij[5] += coef2 / 2;
  703.                 currentSij[5] += coef1 / 2;
  704.             }

  705.             // j between 3 and 2N + 1
  706.             if (isBetween(j, 3, 2 * nMax + 1) && j - 2 < cjsj.jMax) {
  707.                 final double coef1 = ( j - 2 ) * (hk * cjsj.getCj(j - 2) + k2mh2o2 * cjsj.getSj(j - 2));
  708.                 final double coef2 = ( j - 2 ) * (-k2mh2o2 * cjsj.getCj(j - 2) + hk * cjsj.getSj(j - 2));
  709.                 final double coef3 = ( j - 2 ) * (k * cjsj.getCj(j - 2) - h * cjsj.getSj(j - 2)) / 4;
  710.                 final double coef4 = ( j - 2 ) * (h * cjsj.getCj(j - 2) + k * cjsj.getSj(j - 2)) / 4;
  711.                 final double coef5 = ( j - 2 ) * (k2mh2o2 * cjsj.getCj(j - 2) - hk * cjsj.getSj(j - 2));

  712.                 //Coefficients for a
  713.                 currentCij[0] += coef1;
  714.                 currentSij[0] += coef2;

  715.                 //Coefficients for k
  716.                 currentCij[1] += coef4;
  717.                 currentSij[1] += -coef3;

  718.                 //Coefficients for h
  719.                 currentCij[2] += coef3;
  720.                 currentSij[2] += coef4;

  721.                 //Coefficients for λ
  722.                 currentCij[5] += coef5 / 2;
  723.                 currentSij[5] += coef1 / 2;
  724.             }

  725.             //multiply by the common factor
  726.             //for a (i == 0) -> χ³ / (n² * a)
  727.             currentCij[0] *= this.x3on2a;
  728.             currentSij[0] *= this.x3on2a;
  729.             //for k (i == 1) -> χ / (n² * a²)
  730.             currentCij[1] *= this.xon2a2;
  731.             currentSij[1] *= this.xon2a2;
  732.             //for h (i == 2) -> χ / (n² * a²)
  733.             currentCij[2] *= this.xon2a2;
  734.             currentSij[2] *= this.xon2a2;
  735.             //for λ (i == 5) -> (χ²) / (n² * a² * (χ + 1 ) )
  736.             currentCij[5] *= this.x2on2a2xp1;
  737.             currentSij[5] *= this.x2on2a2xp1;

  738.             // j is between 1 and 2 * N - 1
  739.             if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) {
  740.                 // Compute cross derivatives
  741.                 // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha
  742.                 final double CjAlphaGamma   = alpha * cjsj.getdCjdGamma(j) - gamma * cjsj.getdCjdAlpha(j);
  743.                 // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha
  744.                 final double CjAlphaBeta   = alpha * cjsj.getdCjdBeta(j) - beta * cjsj.getdCjdAlpha(j);
  745.                 // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta
  746.                 final double CjBetaGamma    =  beta * cjsj.getdCjdGamma(j) - gamma * cjsj.getdCjdBeta(j);
  747.                 // Cj(h,k) = h * dC/dk - k * dC/dh
  748.                 final double CjHK   = h * cjsj.getdCjdK(j) - k * cjsj.getdCjdH(j);
  749.                 // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha
  750.                 final double SjAlphaGamma   = alpha * cjsj.getdSjdGamma(j) - gamma * cjsj.getdSjdAlpha(j);
  751.                 // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha
  752.                 final double SjAlphaBeta   = alpha * cjsj.getdSjdBeta(j) - beta * cjsj.getdSjdAlpha(j);
  753.                 // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta
  754.                 final double SjBetaGamma    =  beta * cjsj.getdSjdGamma(j) - gamma * cjsj.getdSjdBeta(j);
  755.                 // Sj(h,k) = h * dS/dk - k * dS/dh
  756.                 final double SjHK   = h * cjsj.getdSjdK(j) - k * cjsj.getdSjdH(j);

  757.                 //Coefficients for a
  758.                 final double coef1 = this.x3on2a * (3 - BB) * j;
  759.                 currentCij[0] += coef1 * cjsj.getSj(j);
  760.                 currentSij[0] -= coef1 * cjsj.getCj(j);

  761.                 //Coefficients for k and h
  762.                 final double coef2 = p * CjAlphaGamma - I * q * CjBetaGamma;
  763.                 final double coef3 = p * SjAlphaGamma - I * q * SjBetaGamma;
  764.                 currentCij[1] -= this.xon2a2 * (h * coef2 + BB * cjsj.getdCjdH(j) - 1.5 * k * j * cjsj.getSj(j));
  765.                 currentSij[1] -= this.xon2a2 * (h * coef3 + BB * cjsj.getdSjdH(j) + 1.5 * k * j * cjsj.getCj(j));
  766.                 currentCij[2] += this.xon2a2 * (k * coef2 + BB * cjsj.getdCjdK(j) + 1.5 * h * j * cjsj.getSj(j));
  767.                 currentSij[2] += this.xon2a2 * (k * coef3 + BB * cjsj.getdSjdK(j) - 1.5 * h * j * cjsj.getCj(j));

  768.                 //Coefficients for q and p
  769.                 final double coef4 = CjHK - CjAlphaBeta - j * cjsj.getSj(j);
  770.                 final double coef5 = SjHK - SjAlphaBeta + j * cjsj.getCj(j);
  771.                 currentCij[3] = this.cxo2n2a2 * (-I * CjAlphaGamma + q * coef4);
  772.                 currentSij[3] = this.cxo2n2a2 * (-I * SjAlphaGamma + q * coef5);
  773.                 currentCij[4] = this.cxo2n2a2 * (-CjBetaGamma + p * coef4);
  774.                 currentSij[4] = this.cxo2n2a2 * (-SjBetaGamma + p * coef5);

  775.                 //Coefficients for λ
  776.                 final double coef6 = h * cjsj.getdCjdH(j) + k * cjsj.getdCjdK(j);
  777.                 final double coef7 = h * cjsj.getdSjdH(j) + k * cjsj.getdSjdK(j);
  778.                 currentCij[5] += this.oon2a2 * (-2 * a * cjsj.getdCjdA(j) + coef6 / (X + 1) + X * coef2 - 3 * cjsj.getCj(j));
  779.                 currentSij[5] += this.oon2a2 * (-2 * a * cjsj.getdSjdA(j) + coef7 / (X + 1) + X * coef3 - 3 * cjsj.getSj(j));
  780.             }

  781.             for (int i = 0; i < 6; i++) {
  782.                 //Add the current coefficients contribution to C<sub>i</sub>⁰
  783.                 currentCi0[i] -= currentCij[i] * rhoSigma[j][0] + currentSij[i] * rhoSigma[j][1];
  784.             }

  785.             // Add the coefficients to the interpolation grid
  786.             slot.cij[j].addGridPoint(date, currentCij);
  787.             slot.sij[j].addGridPoint(date, currentSij);

  788.         }

  789.         //Add C<sub>i</sub>⁰ to the interpolation grid
  790.         slot.cij[0].addGridPoint(date, currentCi0);

  791.     }

  792.     /**
  793.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  794.      * <p>
  795.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  796.      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  797.      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  798.      * </p>
  799.      * @param date target date
  800.      * @param slot slot to which the coefficients belong
  801.      * @return array containing ρ<sub>j</sub> and σ<sub>j</sub>
  802.      */
  803.     private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, final Slot slot) {
  804.         final CjSjCoefficient cjsjKH = new CjSjCoefficient(k, h);
  805.         final double b = 1. / (1 + B);

  806.         // (-b)<sup>j</sup>
  807.         double mbtj = 1;

  808.         final double[][] rhoSigma = new double[slot.cij.length][2];
  809.         for (int j = 1; j < rhoSigma.length; j++) {

  810.             //Compute current rho and sigma;
  811.             mbtj *= -b;
  812.             final double coef  = (1 + j * B) * mbtj;
  813.             final double rho   = coef * cjsjKH.getCj(j);
  814.             final double sigma = coef * cjsjKH.getSj(j);

  815.             // Add the coefficients to the interpolation grid
  816.             rhoSigma[j][0] = rho;
  817.             rhoSigma[j][1] = sigma;
  818.         }

  819.         return rhoSigma;

  820.     }

  821.     /** The coefficients used to compute the short-periodic zonal contribution.
  822.      *
  823.      * <p>
  824.      * Those coefficients are given in Danielson paper by expressions 4.1-(20) to 4.1.-(25)
  825.      * </p>
  826.      * <p>
  827.      * The coefficients are: <br>
  828.      * - C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> <br>
  829.      * - ρ<sub>j</sub> and σ<sub>j</sub> <br>
  830.      * - C<sub>i</sub>⁰
  831.      * </p>
  832.      *
  833.      * @author Lucian Barbulescu
  834.      */
  835.     private static class ZonalShortPeriodicCoefficients implements ShortPeriodTerms {

  836.         /** Serializable UID. */
  837.         private static final long serialVersionUID = 20151118L;

  838.         /** Maximum value for j index. */
  839.         private final int maxFrequencyShortPeriodics;

  840.         /** Number of points used in the interpolation process. */
  841.         private final int interpolationPoints;

  842.         /** All coefficients slots. */
  843.         private final transient TimeSpanMap<Slot> slots;

  844.         /** Constructor.
  845.          * @param maxFrequencyShortPeriodics maximum value for j index
  846.          * @param interpolationPoints number of points used in the interpolation process
  847.          * @param slots all coefficients slots
  848.          */
  849.         ZonalShortPeriodicCoefficients(final int maxFrequencyShortPeriodics, final int interpolationPoints,
  850.                                        final TimeSpanMap<Slot> slots) {

  851.             // Save parameters
  852.             this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
  853.             this.interpolationPoints        = interpolationPoints;
  854.             this.slots                      = slots;

  855.         }

  856.         /** Get the slot valid for some date.
  857.          * @param meanStates mean states defining the slot
  858.          * @return slot valid at the specified date
  859.          */
  860.         public Slot createSlot(final SpacecraftState... meanStates) {
  861.             final Slot         slot  = new Slot(maxFrequencyShortPeriodics, interpolationPoints);
  862.             final AbsoluteDate first = meanStates[0].getDate();
  863.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  864.             if (first.compareTo(last) <= 0) {
  865.                 slots.addValidAfter(slot, first);
  866.             } else {
  867.                 slots.addValidBefore(slot, first);
  868.             }
  869.             return slot;
  870.         }

  871.         /** {@inheritDoc} */
  872.         @Override
  873.         public double[] value(final Orbit meanOrbit) {

  874.             // select the coefficients slot
  875.             final Slot slot = slots.get(meanOrbit.getDate());

  876.             // Get the True longitude L
  877.             final double L = meanOrbit.getLv();

  878.             // Compute the center
  879.             final double center = L - meanOrbit.getLM();

  880.             // Initialize short periodic variations
  881.             final double[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  882.             final double[] d = slot.di.value(meanOrbit.getDate());
  883.             for (int i = 0; i < 6; i++) {
  884.                 shortPeriodicVariation[i] +=  center * d[i];
  885.             }

  886.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  887.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  888.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  889.                 final double cos = FastMath.cos(j * L);
  890.                 final double sin = FastMath.sin(j * L);
  891.                 for (int i = 0; i < 6; i++) {
  892.                     // add corresponding term to the short periodic variation
  893.                     shortPeriodicVariation[i] += c[i] * cos;
  894.                     shortPeriodicVariation[i] += s[i] * sin;
  895.                 }
  896.             }

  897.             return shortPeriodicVariation;
  898.         }

  899.         /** {@inheritDoc} */
  900.         @Override
  901.         public String getCoefficientsKeyPrefix() {
  902.             return "DSST-central-body-zonal-";
  903.         }

  904.         /** {@inheritDoc}
  905.          * <p>
  906.          * For zonal terms contributions,there are maxJ cj coefficients,
  907.          * maxJ sj coefficients and 2 dj coefficients, where maxJ depends
  908.          * on the orbit. The j index is the integer multiplier for the true
  909.          * longitude argument in the cj and sj coefficients and the degree
  910.          * in the polynomial dj coefficients.
  911.          * </p>
  912.          */
  913.         @Override
  914.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected)
  915.                 throws OrekitException {

  916.             // select the coefficients slot
  917.             final Slot slot = slots.get(date);

  918.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFrequencyShortPeriodics + 2);
  919.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  920.             storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1);
  921.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  922.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  923.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  924.             }
  925.             return coefficients;

  926.         }

  927.         /** Put a coefficient in a map if selected.
  928.          * @param map map to populate
  929.          * @param selected set of coefficients that should be put in the map
  930.          * (empty set means all coefficients are selected)
  931.          * @param value coefficient value
  932.          * @param id coefficient identifier
  933.          * @param indices list of coefficient indices
  934.          */
  935.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  936.                                      final double[] value, final String id, final int... indices) {
  937.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  938.             keyBuilder.append(id);
  939.             for (int index : indices) {
  940.                 keyBuilder.append('[').append(index).append(']');
  941.             }
  942.             final String key = keyBuilder.toString();
  943.             if (selected.isEmpty() || selected.contains(key)) {
  944.                 map.put(key, value);
  945.             }
  946.         }

  947.         /** Replace the instance with a data transfer object for serialization.
  948.          * @return data transfer object that will be serialized
  949.          * @exception NotSerializableException if an additional state provider is not serializable
  950.          */
  951.         private Object writeReplace() throws NotSerializableException {

  952.             // slots transitions
  953.             final SortedSet<TimeSpanMap.Transition<Slot>> transitions     = slots.getTransitions();
  954.             final AbsoluteDate[]                          transitionDates = new AbsoluteDate[transitions.size()];
  955.             final Slot[]                                  allSlots        = new Slot[transitions.size() + 1];
  956.             int i = 0;
  957.             for (final TimeSpanMap.Transition<Slot> transition : transitions) {
  958.                 if (i == 0) {
  959.                     // slot before the first transition
  960.                     allSlots[i] = transition.getBefore();
  961.                 }
  962.                 if (i < transitionDates.length) {
  963.                     transitionDates[i] = transition.getDate();
  964.                     allSlots[++i]      = transition.getAfter();
  965.                 }
  966.             }

  967.             return new DataTransferObject(maxFrequencyShortPeriodics, interpolationPoints,
  968.                                           transitionDates, allSlots);

  969.         }


  970.         /** Internal class used only for serialization. */
  971.         private static class DataTransferObject implements Serializable {

  972.             /** Serializable UID. */
  973.             private static final long serialVersionUID = 20170420L;

  974.             /** Maximum value for j index. */
  975.             private final int maxFrequencyShortPeriodics;

  976.             /** Number of points used in the interpolation process. */
  977.             private final int interpolationPoints;

  978.             /** Transitions dates. */
  979.             private final AbsoluteDate[] transitionDates;

  980.             /** All slots. */
  981.             private final Slot[] allSlots;

  982.             /** Simple constructor.
  983.              * @param maxFrequencyShortPeriodics maximum value for j index
  984.              * @param interpolationPoints number of points used in the interpolation process
  985.              * @param transitionDates transitions dates
  986.              * @param allSlots all slots
  987.              */
  988.             DataTransferObject(final int maxFrequencyShortPeriodics, final int interpolationPoints,
  989.                                final AbsoluteDate[] transitionDates, final Slot[] allSlots) {
  990.                 this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
  991.                 this.interpolationPoints        = interpolationPoints;
  992.                 this.transitionDates            = transitionDates;
  993.                 this.allSlots                   = allSlots;
  994.             }

  995.             /** Replace the deserialized data transfer object with a {@link ZonalShortPeriodicCoefficients}.
  996.              * @return replacement {@link ZonalShortPeriodicCoefficients}
  997.              */
  998.             private Object readResolve() {

  999.                 final TimeSpanMap<Slot> slots = new TimeSpanMap<Slot>(allSlots[0]);
  1000.                 for (int i = 0; i < transitionDates.length; ++i) {
  1001.                     slots.addValidAfter(allSlots[i + 1], transitionDates[i]);
  1002.                 }

  1003.                 return new ZonalShortPeriodicCoefficients(maxFrequencyShortPeriodics,
  1004.                                                           interpolationPoints,
  1005.                                                           slots);

  1006.             }

  1007.         }

  1008.     }

  1009.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1010.      *  <p>
  1011.      *  Those coefficients are given in Danielson paper by expressions 4.1-(13) to 4.1.-(16b)
  1012.      *  </p>
  1013.      */
  1014.     private class FourierCjSjCoefficients {

  1015.         /** The G<sub>js</sub>, H<sub>js</sub>, I<sub>js</sub> and J<sub>js</sub> polynomials. */
  1016.         private final GHIJjsPolynomials ghijCoef;

  1017.         /** L<sub>n</sub><sup>s</sup>(γ). */
  1018.         private final LnsCoefficients lnsCoef;

  1019.         /** Maximum possible value for n. */
  1020.         private final int nMax;

  1021.         /** Maximum possible value for s. */
  1022.         private final int sMax;

  1023.         /** Maximum possible value for j. */
  1024.         private final int jMax;

  1025.         /** The C<sup>j</sup> coefficients and their derivatives.
  1026.          * <p>
  1027.          * Each column of the matrix contains the following values: <br/>
  1028.          * - C<sup>j</sup> <br/>
  1029.          * - dC<sup>j</sup> / da <br/>
  1030.          * - dC<sup>j</sup> / dh <br/>
  1031.          * - dC<sup>j</sup> / dk <br/>
  1032.          * - dC<sup>j</sup> / dα <br/>
  1033.          * - dC<sup>j</sup> / dβ <br/>
  1034.          * - dC<sup>j</sup> / dγ <br/>
  1035.          * </p>
  1036.          */
  1037.         private final double[][] cCoef;

  1038.         /** The S<sup>j</sup> coefficients and their derivatives.
  1039.          * <p>
  1040.          * Each column of the matrix contains the following values: <br/>
  1041.          * - S<sup>j</sup> <br/>
  1042.          * - dS<sup>j</sup> / da <br/>
  1043.          * - dS<sup>j</sup> / dh <br/>
  1044.          * - dS<sup>j</sup> / dk <br/>
  1045.          * - dS<sup>j</sup> / dα <br/>
  1046.          * - dS<sup>j</sup> / dβ <br/>
  1047.          * - dS<sup>j</sup> / dγ <br/>
  1048.          * </p>
  1049.          */
  1050.         private final double[][] sCoef;

  1051.         /** h * &Chi;³. */
  1052.         private final double hXXX;
  1053.         /** k * &Chi;³. */
  1054.         private final double kXXX;

  1055.         /** Create a set of C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1056.          *  @param date the current date
  1057.          *  @param nMax maximum possible value for n
  1058.          *  @param sMax maximum possible value for s
  1059.          *  @param jMax maximum possible value for j
  1060.          * @throws OrekitException if an error occurs while generating the coefficients
  1061.          */
  1062.         FourierCjSjCoefficients(final AbsoluteDate date,
  1063.                                 final int nMax, final int sMax, final int jMax)
  1064.                 throws OrekitException {
  1065.             this.ghijCoef = new GHIJjsPolynomials(k, h, alpha, beta);
  1066.             // Qns coefficients
  1067.             final double[][] Qns  = CoefficientsFactory.computeQns(gamma, nMax, nMax);

  1068.             this.lnsCoef = new LnsCoefficients(nMax, nMax, Qns, Vns, roa);
  1069.             this.nMax = nMax;
  1070.             this.sMax = sMax;
  1071.             this.jMax = jMax;

  1072.             // compute the common factors that depends on the mean elements
  1073.             this.hXXX = h * XXX;
  1074.             this.kXXX = k * XXX;

  1075.             this.cCoef = new double[7][jMax + 1];
  1076.             this.sCoef = new double[7][jMax + 1];

  1077.             for (int s = 0; s <= sMax; s++) {
  1078.                 //Initialise the Hansen roots
  1079.                 hansenObjects[s].computeInitValues(X);
  1080.             }
  1081.             generateCoefficients(date);
  1082.         }

  1083.         /** Generate all coefficients.
  1084.          * @param date the current date
  1085.          * @throws OrekitException if an error occurs while generating the coefficients
  1086.          */
  1087.         private void generateCoefficients(final AbsoluteDate date) throws OrekitException {
  1088.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
  1089.             for (int j = 1; j <= jMax; j++) {

  1090.                 //init arrays
  1091.                 for (int i = 0; i <= 6; i++) {
  1092.                     cCoef[i][j] = 0.;
  1093.                     sCoef[i][j] = 0.;
  1094.                 }

  1095.                 if (isBetween(j, 1, nMax - 1)) {

  1096.                     //compute first double sum where s: j -> N-1 and n: s+1 -> N
  1097.                     for (int s = j; s <= FastMath.min(nMax - 1, sMax); s++) {
  1098.                         // j - s
  1099.                         final int jms = j - s;
  1100.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1101.                         final int d0smj = (s == j) ? 1 : 2;

  1102.                         for (int n = s + 1; n <= nMax; n++) {
  1103.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1104.                             if ((n + jms) % 2 == 0) {
  1105.                                 // (2 - delta(0,s-j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1106.                                 final double lns = lnsCoef.getLns(n, -jms);
  1107.                                 final double dlns = lnsCoef.getdLnsdGamma(n, -jms);

  1108.                                 final double hjs = ghijCoef.getHjs(s, -jms);
  1109.                                 final double dHjsdh = ghijCoef.getdHjsdh(s, -jms);
  1110.                                 final double dHjsdk = ghijCoef.getdHjsdk(s, -jms);
  1111.                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, -jms);
  1112.                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, -jms);

  1113.                                 final double gjs = ghijCoef.getGjs(s, -jms);
  1114.                                 final double dGjsdh = ghijCoef.getdGjsdh(s, -jms);
  1115.                                 final double dGjsdk = ghijCoef.getdGjsdk(s, -jms);
  1116.                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, -jms);
  1117.                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, -jms);

  1118.                                 // J<sub>n</sub>
  1119.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1120.                                 // K₀<sup>-n-1,s</sup>
  1121.                                 final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1122.                                 final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1123.                                 final double coef0 = d0smj * jn;
  1124.                                 final double coef1 = coef0 * lns;
  1125.                                 final double coef2 = coef1 * kns;
  1126.                                 final double coef3 = coef2 * hjs;
  1127.                                 final double coef4 = coef2 * gjs;

  1128.                                 // Add the term to the coefficients
  1129.                                 cCoef[0][j] += coef3;
  1130.                                 cCoef[1][j] += coef3 * (n + 1);
  1131.                                 cCoef[2][j] += coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1132.                                 cCoef[3][j] += coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1133.                                 cCoef[4][j] += coef2 * dHjsdAlpha;
  1134.                                 cCoef[5][j] += coef2 * dHjsdBeta;
  1135.                                 cCoef[6][j] += coef0 * dlns * kns * hjs;

  1136.                                 sCoef[0][j] += coef4;
  1137.                                 sCoef[1][j] += coef4 * (n + 1);
  1138.                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1139.                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1140.                                 sCoef[4][j] += coef2 * dGjsdAlpha;
  1141.                                 sCoef[5][j] += coef2 * dGjsdBeta;
  1142.                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
  1143.                             }
  1144.                         }
  1145.                     }

  1146.                     //compute second double sum where s: 0 -> N-j and n: max(j+s, j+1) -> N
  1147.                     for (int s = 0; s <= FastMath.min(nMax - j, sMax); s++) {
  1148.                         // j + s
  1149.                         final int jps = j + s;
  1150.                         // Kronecker symbols (2 - delta(0,j+s))
  1151.                         final double d0spj = (s == -j) ? 1 : 2;

  1152.                         for (int n = FastMath.max(j + s, j + 1); n <= nMax; n++) {
  1153.                             // if n + (j+s) is odd, then the term is equal to zero due to the factor Vn,s+j
  1154.                             if ((n + jps) % 2 == 0) {
  1155.                                 // (2 - delta(0,s+j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j+s</sup>
  1156.                                 final double lns = lnsCoef.getLns(n, jps);
  1157.                                 final double dlns = lnsCoef.getdLnsdGamma(n, jps);

  1158.                                 final double hjs = ghijCoef.getHjs(s, jps);
  1159.                                 final double dHjsdh = ghijCoef.getdHjsdh(s, jps);
  1160.                                 final double dHjsdk = ghijCoef.getdHjsdk(s, jps);
  1161.                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, jps);
  1162.                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, jps);

  1163.                                 final double gjs = ghijCoef.getGjs(s, jps);
  1164.                                 final double dGjsdh = ghijCoef.getdGjsdh(s, jps);
  1165.                                 final double dGjsdk = ghijCoef.getdGjsdk(s, jps);
  1166.                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, jps);
  1167.                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, jps);

  1168.                                 // J<sub>n</sub>
  1169.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1170.                                 // K₀<sup>-n-1,s</sup>
  1171.                                 final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1172.                                 final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1173.                                 final double coef0 = d0spj * jn;
  1174.                                 final double coef1 = coef0 * lns;
  1175.                                 final double coef2 = coef1 * kns;

  1176.                                 final double coef3 = coef2 * hjs;
  1177.                                 final double coef4 = coef2 * gjs;

  1178.                                 // Add the term to the coefficients
  1179.                                 cCoef[0][j] -= coef3;
  1180.                                 cCoef[1][j] -= coef3 * (n + 1);
  1181.                                 cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1182.                                 cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1183.                                 cCoef[4][j] -= coef2 * dHjsdAlpha;
  1184.                                 cCoef[5][j] -= coef2 * dHjsdBeta;
  1185.                                 cCoef[6][j] -= coef0 * dlns * kns * hjs;

  1186.                                 sCoef[0][j] += coef4;
  1187.                                 sCoef[1][j] += coef4 * (n + 1);
  1188.                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1189.                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1190.                                 sCoef[4][j] += coef2 * dGjsdAlpha;
  1191.                                 sCoef[5][j] += coef2 * dGjsdBeta;
  1192.                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
  1193.                             }
  1194.                         }
  1195.                     }

  1196.                     //compute third double sum where s: 1 -> j and  n: j+1 -> N
  1197.                     for (int s = 1; s <= FastMath.min(j, sMax); s++) {
  1198.                         // j - s
  1199.                         final int jms = j - s;
  1200.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1201.                         final int d0smj = (s == j) ? 1 : 2;

  1202.                         for (int n = j + 1; n <= nMax; n++) {
  1203.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1204.                             if ((n + jms) % 2 == 0) {
  1205.                                 // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1206.                                 final double lns = lnsCoef.getLns(n, jms);
  1207.                                 final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1208.                                 final double ijs = ghijCoef.getIjs(s, jms);
  1209.                                 final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1210.                                 final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1211.                                 final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1212.                                 final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1213.                                 final double jjs = ghijCoef.getJjs(s, jms);
  1214.                                 final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1215.                                 final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1216.                                 final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1217.                                 final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1218.                                 // J<sub>n</sub>
  1219.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1220.                                 // K₀<sup>-n-1,s</sup>
  1221.                                 final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1222.                                 final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1223.                                 final double coef0 = d0smj * jn;
  1224.                                 final double coef1 = coef0 * lns;
  1225.                                 final double coef2 = coef1 * kns;

  1226.                                 final double coef3 = coef2 * ijs;
  1227.                                 final double coef4 = coef2 * jjs;

  1228.                                 // Add the term to the coefficients
  1229.                                 cCoef[0][j] -= coef3;
  1230.                                 cCoef[1][j] -= coef3 * (n + 1);
  1231.                                 cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1232.                                 cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1233.                                 cCoef[4][j] -= coef2 * dIjsdAlpha;
  1234.                                 cCoef[5][j] -= coef2 * dIjsdBeta;
  1235.                                 cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1236.                                 sCoef[0][j] += coef4;
  1237.                                 sCoef[1][j] += coef4 * (n + 1);
  1238.                                 sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1239.                                 sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1240.                                 sCoef[4][j] += coef2 * dJjsdAlpha;
  1241.                                 sCoef[5][j] += coef2 * dJjsdBeta;
  1242.                                 sCoef[6][j] += coef0 * dlns * kns * jjs;
  1243.                             }
  1244.                         }
  1245.                     }
  1246.                 }

  1247.                 if (isBetween(j, 2, nMax)) {
  1248.                     //add first term
  1249.                     // J<sub>j</sub>
  1250.                     final double jj = -harmonics.getUnnormalizedCnm(j, 0);
  1251.                     double kns = hansenObjects[0].getValue(-j - 1, X);
  1252.                     double dkns = hansenObjects[0].getDerivative(-j - 1, X);

  1253.                     double lns = lnsCoef.getLns(j, j);
  1254.                     //dlns is 0 because n == s == j

  1255.                     final double hjs = ghijCoef.getHjs(0, j);
  1256.                     final double dHjsdh = ghijCoef.getdHjsdh(0, j);
  1257.                     final double dHjsdk = ghijCoef.getdHjsdk(0, j);
  1258.                     final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(0, j);
  1259.                     final double dHjsdBeta = ghijCoef.getdHjsdBeta(0, j);

  1260.                     final double gjs = ghijCoef.getGjs(0, j);
  1261.                     final double dGjsdh = ghijCoef.getdGjsdh(0, j);
  1262.                     final double dGjsdk = ghijCoef.getdGjsdk(0, j);
  1263.                     final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(0, j);
  1264.                     final double dGjsdBeta = ghijCoef.getdGjsdBeta(0, j);

  1265.                     // 2 * J<sub>j</sub> * K₀<sup>-j-1,0</sup> * L<sub>j</sub><sup>j</sup>
  1266.                     double coef0 = 2 * jj;
  1267.                     double coef1 = coef0 * lns;
  1268.                     double coef2 = coef1 * kns;

  1269.                     double coef3 = coef2 * hjs;
  1270.                     double coef4 = coef2 * gjs;

  1271.                     // Add the term to the coefficients
  1272.                     cCoef[0][j] -= coef3;
  1273.                     cCoef[1][j] -= coef3 * (j + 1);
  1274.                     cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1275.                     cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1276.                     cCoef[4][j] -= coef2 * dHjsdAlpha;
  1277.                     cCoef[5][j] -= coef2 * dHjsdBeta;
  1278.                     //no contribution to cCoef[6][j] because dlns is 0

  1279.                     sCoef[0][j] += coef4;
  1280.                     sCoef[1][j] += coef4 * (j + 1);
  1281.                     sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1282.                     sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1283.                     sCoef[4][j] += coef2 * dGjsdAlpha;
  1284.                     sCoef[5][j] += coef2 * dGjsdBeta;
  1285.                     //no contribution to sCoef[6][j] because dlns is 0

  1286.                     //compute simple sum where s: 1 -> j-1
  1287.                     for (int s = 1; s <= FastMath.min(j - 1, sMax); s++) {
  1288.                         // j - s
  1289.                         final int jms = j - s;
  1290.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1291.                         final int d0smj = (s == j) ? 1 : 2;

  1292.                         // if s is odd, then the term is equal to zero due to the factor Vj,s-j
  1293.                         if (s % 2 == 0) {
  1294.                             // (2 - delta(0,j-s)) * J<sub>j</sub> * K₀<sup>-j-1,s</sup> * L<sub>j</sub><sup>j-s</sup>
  1295.                             kns   = hansenObjects[s].getValue(-j - 1, X);
  1296.                             dkns  = hansenObjects[s].getDerivative(-j - 1, X);

  1297.                             lns = lnsCoef.getLns(j, jms);
  1298.                             final double dlns = lnsCoef.getdLnsdGamma(j, jms);

  1299.                             final double ijs = ghijCoef.getIjs(s, jms);
  1300.                             final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1301.                             final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1302.                             final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1303.                             final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1304.                             final double jjs = ghijCoef.getJjs(s, jms);
  1305.                             final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1306.                             final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1307.                             final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1308.                             final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1309.                             coef0 = d0smj * jj;
  1310.                             coef1 = coef0 * lns;
  1311.                             coef2 = coef1 * kns;

  1312.                             coef3 = coef2 * ijs;
  1313.                             coef4 = coef2 * jjs;

  1314.                             // Add the term to the coefficients
  1315.                             cCoef[0][j] -= coef3;
  1316.                             cCoef[1][j] -= coef3 * (j + 1);
  1317.                             cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1318.                             cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1319.                             cCoef[4][j] -= coef2 * dIjsdAlpha;
  1320.                             cCoef[5][j] -= coef2 * dIjsdBeta;
  1321.                             cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1322.                             sCoef[0][j] += coef4;
  1323.                             sCoef[1][j] += coef4 * (j + 1);
  1324.                             sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1325.                             sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1326.                             sCoef[4][j] += coef2 * dJjsdAlpha;
  1327.                             sCoef[5][j] += coef2 * dJjsdBeta;
  1328.                             sCoef[6][j] += coef0 * dlns * kns * jjs;
  1329.                         }
  1330.                     }
  1331.                 }

  1332.                 if (isBetween(j, 3, 2 * nMax - 1)) {
  1333.                     //compute uppercase sigma expressions

  1334.                     //min(j-1,N)
  1335.                     final int minjm1on = FastMath.min(j - 1, nMax);

  1336.                     //if j is even
  1337.                     if (j % 2 == 0) {
  1338.                         //compute first double sum where s: j-min(j-1,N) -> j/2-1 and n: j-s -> min(j-1,N)
  1339.                         for (int s = j - minjm1on; s <= FastMath.min(j / 2 - 1, sMax); s++) {
  1340.                             // j - s
  1341.                             final int jms = j - s;
  1342.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1343.                             final int d0smj = (s == j) ? 1 : 2;

  1344.                             for (int n = j - s; n <= minjm1on; n++) {
  1345.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1346.                                 if ((n + jms) % 2 == 0) {
  1347.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1348.                                     final double lns = lnsCoef.getLns(n, jms);
  1349.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1350.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1351.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1352.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1353.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1354.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1355.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1356.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1357.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1358.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1359.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1360.                                     // J<sub>n</sub>
  1361.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1362.                                     // K₀<sup>-n-1,s</sup>
  1363.                                     final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1364.                                     final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1365.                                     final double coef0 = d0smj * jn;
  1366.                                     final double coef1 = coef0 * lns;
  1367.                                     final double coef2 = coef1 * kns;

  1368.                                     final double coef3 = coef2 * ijs;
  1369.                                     final double coef4 = coef2 * jjs;

  1370.                                     // Add the term to the coefficients
  1371.                                     cCoef[0][j] -= coef3;
  1372.                                     cCoef[1][j] -= coef3 * (n + 1);
  1373.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1374.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1375.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1376.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1377.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1378.                                     sCoef[0][j] += coef4;
  1379.                                     sCoef[1][j] += coef4 * (n + 1);
  1380.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1381.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1382.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1383.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1384.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1385.                                 }
  1386.                             }
  1387.                         }

  1388.                         //compute second double sum where s: j/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  1389.                         for (int s = j / 2; s <=  FastMath.min(minjm1on - 1, sMax); s++) {
  1390.                             // j - s
  1391.                             final int jms = j - s;
  1392.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1393.                             final int d0smj = (s == j) ? 1 : 2;

  1394.                             for (int n = s + 1; n <= minjm1on; n++) {
  1395.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1396.                                 if ((n + jms) % 2 == 0) {
  1397.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1398.                                     final double lns = lnsCoef.getLns(n, jms);
  1399.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1400.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1401.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1402.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1403.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1404.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1405.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1406.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1407.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1408.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1409.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1410.                                     // J<sub>n</sub>
  1411.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1412.                                     // K₀<sup>-n-1,s</sup>
  1413.                                     final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1414.                                     final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1415.                                     final double coef0 = d0smj * jn;
  1416.                                     final double coef1 = coef0 * lns;
  1417.                                     final double coef2 = coef1 * kns;

  1418.                                     final double coef3 = coef2 * ijs;
  1419.                                     final double coef4 = coef2 * jjs;

  1420.                                     // Add the term to the coefficients
  1421.                                     cCoef[0][j] -= coef3;
  1422.                                     cCoef[1][j] -= coef3 * (n + 1);
  1423.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1424.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1425.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1426.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1427.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1428.                                     sCoef[0][j] += coef4;
  1429.                                     sCoef[1][j] += coef4 * (n + 1);
  1430.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1431.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1432.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1433.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1434.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1435.                                 }
  1436.                             }
  1437.                         }
  1438.                     }

  1439.                     //if j is odd
  1440.                     else {
  1441.                         //compute first double sum where s: (j-1)/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  1442.                         for (int s = (j - 1) / 2; s <= FastMath.min(minjm1on - 1, sMax); s++) {
  1443.                             // j - s
  1444.                             final int jms = j - s;
  1445.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1446.                             final int d0smj = (s == j) ? 1 : 2;

  1447.                             for (int n = s + 1; n <= minjm1on; n++) {
  1448.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1449.                                 if ((n + jms) % 2 == 0) {
  1450.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1451.                                     final double lns = lnsCoef.getLns(n, jms);
  1452.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1453.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1454.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1455.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1456.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1457.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1458.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1459.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1460.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1461.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1462.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1463.                                     // J<sub>n</sub>
  1464.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1465.                                     // K₀<sup>-n-1,s</sup>

  1466.                                     final double kns = hansenObjects[s].getValue(-n - 1, X);
  1467.                                     final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1468.                                     final double coef0 = d0smj * jn;
  1469.                                     final double coef1 = coef0 * lns;
  1470.                                     final double coef2 = coef1 * kns;

  1471.                                     final double coef3 = coef2 * ijs;
  1472.                                     final double coef4 = coef2 * jjs;

  1473.                                     // Add the term to the coefficients
  1474.                                     cCoef[0][j] -= coef3;
  1475.                                     cCoef[1][j] -= coef3 * (n + 1);
  1476.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1477.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1478.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1479.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1480.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1481.                                     sCoef[0][j] += coef4;
  1482.                                     sCoef[1][j] += coef4 * (n + 1);
  1483.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1484.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1485.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1486.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1487.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1488.                                 }
  1489.                             }
  1490.                         }

  1491.                         //the second double sum is added only if N >= 4 and j between 5 and 2*N-3
  1492.                         if (nMax >= 4 && isBetween(j, 5, 2 * nMax - 3)) {
  1493.                             //compute second double sum where s: j-min(j-1,N) -> (j-3)/2 and n: j-s -> min(j-1,N)
  1494.                             for (int s = j - minjm1on; s <= FastMath.min((j - 3) / 2, sMax); s++) {
  1495.                                 // j - s
  1496.                                 final int jms = j - s;
  1497.                                 // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1498.                                 final int d0smj = (s == j) ? 1 : 2;

  1499.                                 for (int n = j - s; n <= minjm1on; n++) {
  1500.                                     // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1501.                                     if ((n + jms) % 2 == 0) {
  1502.                                         // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1503.                                         final double lns = lnsCoef.getLns(n, jms);
  1504.                                         final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1505.                                         final double ijs = ghijCoef.getIjs(s, jms);
  1506.                                         final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1507.                                         final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1508.                                         final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1509.                                         final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1510.                                         final double jjs = ghijCoef.getJjs(s, jms);
  1511.                                         final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1512.                                         final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1513.                                         final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1514.                                         final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1515.                                         // J<sub>n</sub>
  1516.                                         final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1517.                                         // K₀<sup>-n-1,s</sup>
  1518.                                         final double kns   = hansenObjects[s].getValue(-n - 1, X);
  1519.                                         final double dkns  = hansenObjects[s].getDerivative(-n - 1, X);

  1520.                                         final double coef0 = d0smj * jn;
  1521.                                         final double coef1 = coef0 * lns;
  1522.                                         final double coef2 = coef1 * kns;

  1523.                                         final double coef3 = coef2 * ijs;
  1524.                                         final double coef4 = coef2 * jjs;

  1525.                                         // Add the term to the coefficients
  1526.                                         cCoef[0][j] -= coef3;
  1527.                                         cCoef[1][j] -= coef3 * (n + 1);
  1528.                                         cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1529.                                         cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1530.                                         cCoef[4][j] -= coef2 * dIjsdAlpha;
  1531.                                         cCoef[5][j] -= coef2 * dIjsdBeta;
  1532.                                         cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1533.                                         sCoef[0][j] += coef4;
  1534.                                         sCoef[1][j] += coef4 * (n + 1);
  1535.                                         sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1536.                                         sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1537.                                         sCoef[4][j] += coef2 * dJjsdAlpha;
  1538.                                         sCoef[5][j] += coef2 * dJjsdBeta;
  1539.                                         sCoef[6][j] += coef0 * dlns * kns * jjs;
  1540.                                     }
  1541.                                 }
  1542.                             }
  1543.                         }
  1544.                     }
  1545.                 }

  1546.                 cCoef[0][j] *= -muoa / j;
  1547.                 cCoef[1][j] *=  muoa / ( j * a );
  1548.                 cCoef[2][j] *= -muoa / j;
  1549.                 cCoef[3][j] *= -muoa / j;
  1550.                 cCoef[4][j] *= -muoa / j;
  1551.                 cCoef[5][j] *= -muoa / j;
  1552.                 cCoef[6][j] *= -muoa / j;

  1553.                 sCoef[0][j] *= -muoa / j;
  1554.                 sCoef[1][j] *=  muoa / ( j * a );
  1555.                 sCoef[2][j] *= -muoa / j;
  1556.                 sCoef[3][j] *= -muoa / j;
  1557.                 sCoef[4][j] *= -muoa / j;
  1558.                 sCoef[5][j] *= -muoa / j;
  1559.                 sCoef[6][j] *= -muoa / j;

  1560.             }
  1561.         }

  1562.         /** Check if an index is within the accepted interval.
  1563.          *
  1564.          * @param index the index to check
  1565.          * @param lowerBound the lower bound of the interval
  1566.          * @param upperBound the upper bound of the interval
  1567.          * @return true if the index is between the lower and upper bounds, false otherwise
  1568.          */
  1569.         private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
  1570.             return index >= lowerBound && index <= upperBound;
  1571.         }

  1572.         /**Get the value of C<sup>j</sup>.
  1573.          *
  1574.          * @param j j index
  1575.          * @return C<sup>j</sup>
  1576.          */
  1577.         public double getCj(final int j) {
  1578.             return cCoef[0][j];
  1579.         }

  1580.         /**Get the value of dC<sup>j</sup> / da.
  1581.          *
  1582.          * @param j j index
  1583.          * @return dC<sup>j</sup> / da
  1584.          */
  1585.         public double getdCjdA(final int j) {
  1586.             return cCoef[1][j];
  1587.         }

  1588.         /**Get the value of dC<sup>j</sup> / dh.
  1589.          *
  1590.          * @param j j index
  1591.          * @return dC<sup>j</sup> / dh
  1592.          */
  1593.         public double getdCjdH(final int j) {
  1594.             return cCoef[2][j];
  1595.         }

  1596.         /**Get the value of dC<sup>j</sup> / dk.
  1597.          *
  1598.          * @param j j index
  1599.          * @return dC<sup>j</sup> / dk
  1600.          */
  1601.         public double getdCjdK(final int j) {
  1602.             return cCoef[3][j];
  1603.         }

  1604.         /**Get the value of dC<sup>j</sup> / dα.
  1605.          *
  1606.          * @param j j index
  1607.          * @return dC<sup>j</sup> / dα
  1608.          */
  1609.         public double getdCjdAlpha(final int j) {
  1610.             return cCoef[4][j];
  1611.         }

  1612.         /**Get the value of dC<sup>j</sup> / dβ.
  1613.          *
  1614.          * @param j j index
  1615.          * @return dC<sup>j</sup> / dβ
  1616.          */
  1617.         public double getdCjdBeta(final int j) {
  1618.             return cCoef[5][j];
  1619.         }

  1620.         /**Get the value of dC<sup>j</sup> / dγ.
  1621.          *
  1622.          * @param j j index
  1623.          * @return dC<sup>j</sup> / dγ
  1624.          */
  1625.         public double getdCjdGamma(final int j) {
  1626.             return cCoef[6][j];
  1627.         }

  1628.         /**Get the value of S<sup>j</sup>.
  1629.          *
  1630.          * @param j j index
  1631.          * @return S<sup>j</sup>
  1632.          */
  1633.         public double getSj(final int j) {
  1634.             return sCoef[0][j];
  1635.         }

  1636.         /**Get the value of dS<sup>j</sup> / da.
  1637.          *
  1638.          * @param j j index
  1639.          * @return dS<sup>j</sup> / da
  1640.          */
  1641.         public double getdSjdA(final int j) {
  1642.             return sCoef[1][j];
  1643.         }

  1644.         /**Get the value of dS<sup>j</sup> / dh.
  1645.          *
  1646.          * @param j j index
  1647.          * @return dS<sup>j</sup> / dh
  1648.          */
  1649.         public double getdSjdH(final int j) {
  1650.             return sCoef[2][j];
  1651.         }

  1652.         /**Get the value of dS<sup>j</sup> / dk.
  1653.          *
  1654.          * @param j j index
  1655.          * @return dS<sup>j</sup> / dk
  1656.          */
  1657.         public double getdSjdK(final int j) {
  1658.             return sCoef[3][j];
  1659.         }

  1660.         /**Get the value of dS<sup>j</sup> / dα.
  1661.          *
  1662.          * @param j j index
  1663.          * @return dS<sup>j</sup> / dα
  1664.          */
  1665.         public double getdSjdAlpha(final int j) {
  1666.             return sCoef[4][j];
  1667.         }

  1668.         /**Get the value of dS<sup>j</sup> / dβ.
  1669.          *
  1670.          * @param j j index
  1671.          * @return dS<sup>j</sup> / dβ
  1672.          */
  1673.         public double getdSjdBeta(final int j) {
  1674.             return sCoef[5][j];
  1675.         }

  1676.         /**Get the value of dS<sup>j</sup> /  dγ.
  1677.          *
  1678.          * @param j j index
  1679.          * @return dS<sup>j</sup> /  dγ
  1680.          */
  1681.         public double getdSjdGamma(final int j) {
  1682.             return sCoef[6][j];
  1683.         }
  1684.     }

  1685.     /** Coefficients valid for one time slot. */
  1686.     private static class Slot implements Serializable {

  1687.         /** Serializable UID. */
  1688.         private static final long serialVersionUID = 20160319L;

  1689.         /**The coefficients D<sub>i</sub>.
  1690.          * <p>
  1691.          * i corresponds to the equinoctial element, as follows:
  1692.          * - i=0 for a <br/>
  1693.          * - i=1 for k <br/>
  1694.          * - i=2 for h <br/>
  1695.          * - i=3 for q <br/>
  1696.          * - i=4 for p <br/>
  1697.          * - i=5 for λ <br/>
  1698.          * </p>
  1699.          */
  1700.         private final ShortPeriodicsInterpolatedCoefficient di;

  1701.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  1702.          * <p>
  1703.          * The constant term C<sub>i</sub>⁰ is also stored in this variable at index j = 0 <br>
  1704.          * The index order is cij[j][i] <br/>
  1705.          * i corresponds to the equinoctial element, as follows: <br/>
  1706.          * - i=0 for a <br/>
  1707.          * - i=1 for k <br/>
  1708.          * - i=2 for h <br/>
  1709.          * - i=3 for q <br/>
  1710.          * - i=4 for p <br/>
  1711.          * - i=5 for λ <br/>
  1712.          * </p>
  1713.          */
  1714.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  1715.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  1716.          * <p>
  1717.          * The index order is sij[j][i] <br/>
  1718.          * i corresponds to the equinoctial element, as follows: <br/>
  1719.          * - i=0 for a <br/>
  1720.          * - i=1 for k <br/>
  1721.          * - i=2 for h <br/>
  1722.          * - i=3 for q <br/>
  1723.          * - i=4 for p <br/>
  1724.          * - i=5 for λ <br/>
  1725.          * </p>
  1726.          */
  1727.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  1728.         /** Simple constructor.
  1729.          *  @param maxFrequencyShortPeriodics maximum value for j index
  1730.          *  @param interpolationPoints number of points used in the interpolation process
  1731.          */
  1732.         Slot(final int maxFrequencyShortPeriodics, final int interpolationPoints) {

  1733.             final int rows = maxFrequencyShortPeriodics + 1;
  1734.             di  = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1735.             cij = new ShortPeriodicsInterpolatedCoefficient[rows];
  1736.             sij = new ShortPeriodicsInterpolatedCoefficient[rows];

  1737.             //Initialize the arrays
  1738.             for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  1739.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1740.                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1741.             }

  1742.         }

  1743.     }

  1744. }