SeriesTerm.java

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

  18. import java.util.Arrays;

  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.util.FastMath;
  21. import org.hipparchus.util.FieldSinCos;
  22. import org.hipparchus.util.MathArrays;
  23. import org.hipparchus.util.SinCos;
  24. import org.orekit.errors.OrekitInternalError;
  25. import org.orekit.utils.Constants;

  26. /** Base class for nutation series terms.
  27.  * @author Luc Maisonobe
  28.  * @see PoissonSeries
  29.  */
  30. abstract class SeriesTerm {

  31.     /** Coefficients for the sine part. */
  32.     private double[][] sinCoeff;

  33.     /** Coefficients for the cosine part. */
  34.     private double[][] cosCoeff;

  35.     /** Simple constructor for the base class.
  36.      */
  37.     protected SeriesTerm() {
  38.         this.sinCoeff = new double[0][0];
  39.         this.cosCoeff = new double[0][0];
  40.     }

  41.     /** Get the degree of the function component.
  42.      * @param index index of the function component (must be less than dimension)
  43.      * @return degree of the function component
  44.      */
  45.     public int getDegree(final int index) {
  46.         return  sinCoeff[index].length - 1;
  47.     }

  48.     /** Add a pair of values to existing term coefficients.
  49.      * <p>
  50.      * Despite it would seem logical to simply set coefficients
  51.      * rather than add to them, this does not work for some IERS
  52.      * files. As an example in table 5.3a in IERS conventions 2003,
  53.      * the coefficients for luni-solar term for 2F+Ω with period
  54.      * 13.633 days appears twice with different coefficients, as
  55.      * well as term for 2(F+D+Ω)+l with period 5.643 days, term for
  56.      * 2(F+D+Ω)-l with period 9.557 days, term for 2(Ω-l') with
  57.      * period -173.318 days, term for 2D-l with period 31.812 days ...
  58.      * 35 different duplicated terms have been identified in the
  59.      * tables 5.3a and 5.3b in IERS conventions 2003.
  60.      * The coefficients read in lines duplicating a term must be
  61.      * added together.
  62.      * </p>
  63.      * @param index index of the components (will automatically
  64.      * increase dimension if needed)
  65.      * @param degree degree of the coefficients, may be negative if
  66.      * the term does not contribute to component (will automatically
  67.      * increase {@link #getDegree() degree} of the component if needed)
  68.      * @param sinID coefficient for the sine part, at index and degree
  69.      * @param cosID coefficient for the cosine part, at index and degree
  70.      */
  71.     public void add(final int index, final int degree,
  72.                     final double sinID, final double cosID) {
  73.         sinCoeff = extendArray(index, degree, sinCoeff);
  74.         cosCoeff = extendArray(index, degree, cosCoeff);
  75.         if (degree >= 0) {
  76.             sinCoeff[index][degree] += sinID;
  77.             cosCoeff[index][degree] += cosID;
  78.         }
  79.     }

  80.     /** Get a coefficient for the sine part.
  81.      * @param index index of the function component (must be less than dimension)
  82.      * @param degree degree of the coefficients
  83.      * (must be less than {@link #getDegree() degree} for the component)
  84.      * @return coefficient for the sine part, at index and degree
  85.      */
  86.     public double getSinCoeff(final int index, final int degree) {
  87.         return sinCoeff[index][degree];
  88.     }

  89.     /** Get a coefficient for the cosine part.
  90.      * @param index index of the function component (must be less than dimension)
  91.      * @param degree degree of the coefficients
  92.      * (must be less than {@link #getDegree() degree} for the component)
  93.      * @return coefficient for the cosine part, at index and degree
  94.      */
  95.     public double getCosCoeff(final int index, final int degree) {
  96.         return cosCoeff[index][degree];
  97.     }

  98.     /** Evaluate the value of the series term.
  99.      * @param elements bodies elements for nutation
  100.      * @return value of the series term
  101.      */
  102.     public double[] value(final BodiesElements elements) {

  103.         // preliminary computation
  104.         final double tc  = elements.getTC();
  105.         final double a   = argument(elements);
  106.         final SinCos sc  = FastMath.sinCos(a);

  107.         // compute each function
  108.         final double[] values = new double[sinCoeff.length];
  109.         for (int i = 0; i < values.length; ++i) {
  110.             double s = 0;
  111.             double c = 0;
  112.             for (int j = sinCoeff[i].length - 1; j >= 0; --j) {
  113.                 s = s * tc + sinCoeff[i][j];
  114.                 c = c * tc + cosCoeff[i][j];
  115.             }
  116.             values[i] = s * sc.sin() + c * sc.cos();
  117.         }

  118.         return values;

  119.     }

  120.     /** Evaluate the time derivative of the series term.
  121.      * @param elements bodies elements for nutation
  122.      * @return time derivative of the series term
  123.      */
  124.     public double[] derivative(final BodiesElements elements) {

  125.         // preliminary computation
  126.         final double tc   = elements.getTC();
  127.         final double a    = argument(elements);
  128.         final double aDot = argumentDerivative(elements);
  129.         final SinCos sc   = FastMath.sinCos(a);

  130.         // compute each function
  131.         final double[] derivatives = new double[sinCoeff.length];
  132.         for (int i = 0; i < derivatives.length; ++i) {
  133.             double s    = 0;
  134.             double c    = 0;
  135.             double sDot = 0;
  136.             double cDot = 0;
  137.             if (sinCoeff[i].length > 0) {
  138.                 for (int j = sinCoeff[i].length - 1; j > 0; --j) {
  139.                     s    = s    * tc +     sinCoeff[i][j];
  140.                     c    = c    * tc +     cosCoeff[i][j];
  141.                     sDot = sDot * tc + j * sinCoeff[i][j];
  142.                     cDot = cDot * tc + j * cosCoeff[i][j];
  143.                 }
  144.                 s     = s * tc + sinCoeff[i][0];
  145.                 c     = c * tc + cosCoeff[i][0];
  146.                 sDot /= Constants.JULIAN_CENTURY;
  147.                 cDot /= Constants.JULIAN_CENTURY;
  148.             }
  149.             derivatives[i] = (sDot - c * aDot) * sc.sin() + (cDot + s * aDot) * sc.cos();
  150.         }

  151.         return derivatives;

  152.     }

  153.     /** Compute the argument for the current date.
  154.      * @param elements luni-solar and planetary elements for the current date
  155.      * @return current value of the argument
  156.      */
  157.     protected abstract double argument(BodiesElements elements);

  158.     /** Compute the time derivative of the argument for the current date.
  159.      * @param elements luni-solar and planetary elements for the current date
  160.      * @return current time derivative of the argument
  161.      */
  162.     protected abstract double argumentDerivative(BodiesElements elements);

  163.     /** Evaluate the value of the series term.
  164.      * @param elements bodies elements for nutation
  165.      * @param <T> the type of the field elements
  166.      * @return value of the series term
  167.      */
  168.     public <T extends CalculusFieldElement<T>> T[] value(final FieldBodiesElements<T> elements) {

  169.         // preliminary computation
  170.         final T tc  = elements.getTC();
  171.         final T a   = argument(elements);
  172.         final FieldSinCos<T> sc = FastMath.sinCos(a);

  173.         // compute each function
  174.         final T[] values = MathArrays.buildArray(tc.getField(), sinCoeff.length);
  175.         for (int i = 0; i < values.length; ++i) {
  176.             T s = tc.getField().getZero();
  177.             T c = tc.getField().getZero();
  178.             for (int j = sinCoeff[i].length - 1; j >= 0; --j) {
  179.                 s = s.multiply(tc).add(sinCoeff[i][j]);
  180.                 c = c.multiply(tc).add(cosCoeff[i][j]);
  181.             }
  182.             values[i] = s.multiply(sc.sin()).add(c.multiply(sc.cos()));
  183.         }

  184.         return values;

  185.     }

  186.     /** Evaluate the time derivative of the series term.
  187.      * @param elements bodies elements for nutation
  188.      * @param <T> the type of the field elements
  189.      * @return time derivative of the series term
  190.      */
  191.     public <T extends CalculusFieldElement<T>> T[] derivative(final FieldBodiesElements<T> elements) {

  192.         // preliminary computation
  193.         final T tc   = elements.getTC();
  194.         final T a    = argument(elements);
  195.         final T aDot = argumentDerivative(elements);
  196.         final FieldSinCos<T> sc = FastMath.sinCos(a);

  197.         // compute each function
  198.         final T[] derivatives = MathArrays.buildArray(tc.getField(), sinCoeff.length);
  199.         for (int i = 0; i < derivatives.length; ++i) {
  200.             T s    = tc.getField().getZero();
  201.             T c    = tc.getField().getZero();
  202.             T sDot = tc.getField().getZero();
  203.             T cDot = tc.getField().getZero();
  204.             if (sinCoeff[i].length > 0) {
  205.                 for (int j = sinCoeff[i].length - 1; j > 0; --j) {
  206.                     s    = s.multiply(tc).add(sinCoeff[i][j]);
  207.                     c    = c.multiply(tc).add(cosCoeff[i][j]);
  208.                     sDot = sDot.multiply(tc).add(j * sinCoeff[i][j]);
  209.                     cDot = cDot.multiply(tc).add(j * cosCoeff[i][j]);
  210.                 }
  211.                 s    = s.multiply(tc).add(sinCoeff[i][0]);
  212.                 c    = c.multiply(tc).add(cosCoeff[i][0]);
  213.                 sDot = sDot.divide(Constants.JULIAN_CENTURY);
  214.                 cDot = cDot.divide(Constants.JULIAN_CENTURY);
  215.             }
  216.             derivatives[i] = sDot.subtract(c.multiply(aDot)).multiply(sc.sin()).
  217.                              add(cDot.add(s.multiply(aDot)).multiply(sc.cos()));
  218.         }

  219.         return derivatives;

  220.     }

  221.     /** Compute the argument for the current date.
  222.      * @param elements luni-solar and planetary elements for the current date
  223.      * @param <T> the type of the field elements
  224.      * @return current value of the argument
  225.      */
  226.     protected abstract <T extends CalculusFieldElement<T>> T argument(FieldBodiesElements<T> elements);

  227.     /** Compute the time derivative of the argument for the current date.
  228.      * @param elements luni-solar and planetary elements for the current date
  229.      * @param <T> the type of the field elements
  230.      * @return current time derivative of the argument
  231.      */
  232.     protected abstract <T extends CalculusFieldElement<T>> T argumentDerivative(FieldBodiesElements<T> elements);

  233.     /** Factory method for building the appropriate object.
  234.      * <p>The method checks the null coefficients and build an instance
  235.      * of an appropriate type to avoid too many unnecessary multiplications
  236.      * by zero coefficients.</p>
  237.      * @param cGamma coefficient for γ = GMST + π tide parameter
  238.      * @param cL coefficient for mean anomaly of the Moon
  239.      * @param cLPrime coefficient for mean anomaly of the Sun
  240.      * @param cF coefficient for L - Ω where L is the mean longitude of the Moon
  241.      * @param cD coefficient for mean elongation of the Moon from the Sun
  242.      * @param cOmega coefficient for mean longitude of the ascending node of the Moon
  243.      * @param cMe coefficient for mean Mercury longitude
  244.      * @param cVe coefficient for mean Venus longitude
  245.      * @param cE coefficient for mean Earth longitude
  246.      * @param cMa coefficient for mean Mars longitude
  247.      * @param cJu coefficient for mean Jupiter longitude
  248.      * @param cSa coefficient for mean Saturn longitude
  249.      * @param cUr coefficient for mean Uranus longitude
  250.      * @param cNe coefficient for mean Neptune longitude
  251.      * @param cPa coefficient for general accumulated precession in longitude
  252.      * @return a nutation serie term instance well suited for the set of coefficients
  253.      */
  254.     public static  SeriesTerm buildTerm(final int cGamma,
  255.                                         final int cL, final int cLPrime, final int cF,
  256.                                         final int cD, final int cOmega,
  257.                                         final int cMe, final int cVe, final int cE,
  258.                                         final int cMa, final int cJu, final int cSa,
  259.                                         final int cUr, final int cNe, final int cPa) {
  260.         if (cGamma == 0 && cL == 0 && cLPrime == 0 && cF == 0 && cD == 0 && cOmega == 0) {
  261.             return new PlanetaryTerm(cMe, cVe, cE, cMa, cJu, cSa, cUr, cNe, cPa);
  262.         } else if (cGamma == 0 &&
  263.                    cMe == 0 && cVe == 0 && cE == 0 && cMa == 0 && cJu == 0 &&
  264.                    cSa == 0 && cUr == 0 && cNe == 0 && cPa == 0) {
  265.             return new LuniSolarTerm(cL, cLPrime, cF, cD, cOmega);
  266.         } else if (cGamma != 0 &&
  267.                    cMe == 0 && cVe == 0 && cE == 0 && cMa == 0 && cJu == 0 &&
  268.                    cSa == 0 && cUr == 0 && cNe == 0 && cPa == 0) {
  269.             return new TideTerm(cGamma, cL, cLPrime, cF, cD, cOmega);
  270.         } else if (cGamma == 0 && cLPrime == 0 && cUr == 0 && cNe == 0 && cPa == 0) {
  271.             return new NoFarPlanetsTerm(cL, cF, cD, cOmega,
  272.                                         cMe, cVe, cE, cMa, cJu, cSa);
  273.         } else if (cGamma == 0) {
  274.             return new GeneralTerm(cL, cLPrime, cF, cD, cOmega,
  275.                                    cMe, cVe, cE, cMa, cJu, cSa, cUr, cNe, cPa);
  276.         } else {
  277.             throw new OrekitInternalError(null);
  278.         }

  279.     }

  280.     /** Extend an array to hold at least index and degree.
  281.      * @param index index of the function
  282.      * @param degree degree of the coefficients
  283.      * @param array to extend
  284.      * @return extended array
  285.      */
  286.     private static double[][] extendArray(final int index, final int degree,
  287.                                           final double[][] array) {

  288.         // extend the number of rows if needed
  289.         final double[][] extended;
  290.         if (array.length > index) {
  291.             extended = array;
  292.         } else {
  293.             final int rows =  index + 1;
  294.             extended = new double[rows][];
  295.             System.arraycopy(array, 0, extended, 0, array.length);
  296.             Arrays.fill(extended, array.length, index + 1, new double[0]);
  297.         }

  298.         // extend the number of elements in the row if needed
  299.         extended[index] = extendArray(degree, extended[index]);

  300.         return extended;

  301.     }

  302.     /** Extend an array to hold at least degree.
  303.      * @param degree degree of the coefficients
  304.      * @param array to extend
  305.      * @return extended array
  306.      */
  307.     private static double[] extendArray(final int degree, final double[] array) {
  308.         // extend the number of elements if needed
  309.         if (array.length > degree) {
  310.             return array;
  311.         } else {
  312.             final double[] extended = new double[degree + 1];
  313.             System.arraycopy(array, 0, extended, 0, array.length);
  314.             return extended;
  315.         }
  316.     }

  317. }