IERSConventions.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.utils;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.Serializable;
  23. import java.util.List;

  24. import org.hipparchus.RealFieldElement;
  25. import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
  26. import org.hipparchus.analysis.interpolation.HermiteInterpolator;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.MathArrays;
  29. import org.hipparchus.util.MathUtils;
  30. import org.orekit.data.BodiesElements;
  31. import org.orekit.data.DelaunayArguments;
  32. import org.orekit.data.FieldBodiesElements;
  33. import org.orekit.data.FieldDelaunayArguments;
  34. import org.orekit.data.FundamentalNutationArguments;
  35. import org.orekit.data.PoissonSeries;
  36. import org.orekit.data.PoissonSeries.CompiledSeries;
  37. import org.orekit.data.PoissonSeriesParser;
  38. import org.orekit.data.PolynomialNutation;
  39. import org.orekit.data.PolynomialParser;
  40. import org.orekit.data.PolynomialParser.Unit;
  41. import org.orekit.data.SimpleTimeStampedTableParser;
  42. import org.orekit.errors.OrekitException;
  43. import org.orekit.errors.OrekitInternalError;
  44. import org.orekit.errors.OrekitMessages;
  45. import org.orekit.errors.TimeStampedCacheException;
  46. import org.orekit.frames.EOPHistory;
  47. import org.orekit.frames.FieldPoleCorrection;
  48. import org.orekit.frames.PoleCorrection;
  49. import org.orekit.time.AbsoluteDate;
  50. import org.orekit.time.DateComponents;
  51. import org.orekit.time.FieldAbsoluteDate;
  52. import org.orekit.time.TimeComponents;
  53. import org.orekit.time.TimeScalarFunction;
  54. import org.orekit.time.TimeScale;
  55. import org.orekit.time.TimeScalesFactory;
  56. import org.orekit.time.TimeStamped;
  57. import org.orekit.time.TimeVectorFunction;


  58. /** Supported IERS conventions.
  59.  * @since 6.0
  60.  * @author Luc Maisonobe
  61.  */
  62. public enum IERSConventions {

  63.     /** Constant for IERS 1996 conventions. */
  64.     IERS_1996 {

  65.         /** Nutation arguments resources. */
  66.         private static final String NUTATION_ARGUMENTS = IERS_BASE + "1996/nutation-arguments.txt";

  67.         /** X series resources. */
  68.         private static final String X_Y_SERIES         = IERS_BASE + "1996/tab5.4.txt";

  69.         /** Psi series resources. */
  70.         private static final String PSI_EPSILON_SERIES = IERS_BASE + "1996/tab5.1.txt";

  71.         /** Tidal correction for xp, yp series resources. */
  72.         private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "1996/tab8.4.txt";

  73.         /** Tidal correction for UT1 resources. */
  74.         private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "1996/tab8.3.txt";

  75.         /** Love numbers resources. */
  76.         private static final String LOVE_NUMBERS = IERS_BASE + "1996/tab6.1.txt";

  77.         /** Frequency dependence model for k₂₀. */
  78.         private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2b.txt";

  79.         /** Frequency dependence model for k₂₁. */
  80.         private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2a.txt";

  81.         /** Frequency dependence model for k₂₂. */
  82.         private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2c.txt";

  83.         /** Tidal displacement frequency correction for diurnal tides. */
  84.         private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "1996/tab7.3a.txt";

  85.         /** Tidal displacement frequency correction for zonal tides. */
  86.         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "1996/tab7.3b.txt";

  87.         /** {@inheritDoc} */
  88.         @Override
  89.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  90.             throws OrekitException {
  91.             return new FundamentalNutationArguments(this, timeScale,
  92.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  93.         }

  94.         /** {@inheritDoc} */
  95.         @Override
  96.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  97.             // value from chapter 5, page 22
  98.             final PolynomialNutation epsilonA =
  99.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  100.                                              -46.8150   * Constants.ARC_SECONDS_TO_RADIANS,
  101.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  102.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  103.             return new TimeScalarFunction() {

  104.                 /** {@inheritDoc} */
  105.                 @Override
  106.                 public double value(final AbsoluteDate date) {
  107.                     return epsilonA.value(evaluateTC(date));
  108.                 }

  109.                 /** {@inheritDoc} */
  110.                 @Override
  111.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  112.                     return epsilonA.value(evaluateTC(date));
  113.                 }

  114.             };

  115.         }

  116.         /** {@inheritDoc} */
  117.         @Override
  118.         public TimeVectorFunction getXYSpXY2Function()
  119.             throws OrekitException {

  120.             // set up nutation arguments
  121.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  122.             // X = 2004.3109″t - 0.42665″t² - 0.198656″t³ + 0.0000140″t⁴
  123.             //     + 0.00006″t² cos Ω + sin ε0 { Σ [(Ai + Ai' t) sin(ARGUMENT) + Ai'' t cos(ARGUMENT)]}
  124.             //     + 0.00204″t² sin Ω + 0.00016″t² sin 2(F - D + Ω),
  125.             final PolynomialNutation xPolynomial =
  126.                     new PolynomialNutation(0,
  127.                                            2004.3109 * Constants.ARC_SECONDS_TO_RADIANS,
  128.                                            -0.42665  * Constants.ARC_SECONDS_TO_RADIANS,
  129.                                            -0.198656 * Constants.ARC_SECONDS_TO_RADIANS,
  130.                                            0.0000140 * Constants.ARC_SECONDS_TO_RADIANS);

  131.             final double fXCosOm    = 0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  132.             final double fXSinOm    = 0.00204 * Constants.ARC_SECONDS_TO_RADIANS;
  133.             final double fXSin2FDOm = 0.00016 * Constants.ARC_SECONDS_TO_RADIANS;
  134.             final double sinEps0   = FastMath.sin(getMeanObliquityFunction().value(getNutationReferenceEpoch()));

  135.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  136.             final PoissonSeriesParser baseParser =
  137.                     new PoissonSeriesParser(12).withFirstDelaunay(1);

  138.             final PoissonSeriesParser xParser =
  139.                     baseParser.
  140.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  141.                     withSinCos(1, 8, deciMilliAS,  9, deciMilliAS);
  142.             final PoissonSeries xSum = xParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

  143.             // Y = -0.00013″ - 22.40992″t² + 0.001836″t³ + 0.0011130″t⁴
  144.             //     + Σ [(Bi + Bi' t) cos(ARGUMENT) + Bi'' t sin(ARGUMENT)]
  145.             //    - 0.00231″t² cos Ω − 0.00014″t² cos 2(F - D + Ω)
  146.             final PolynomialNutation yPolynomial =
  147.                     new PolynomialNutation(-0.00013  * Constants.ARC_SECONDS_TO_RADIANS,
  148.                                            0.0,
  149.                                            -22.40992 * Constants.ARC_SECONDS_TO_RADIANS,
  150.                                            0.001836  * Constants.ARC_SECONDS_TO_RADIANS,
  151.                                            0.0011130 * Constants.ARC_SECONDS_TO_RADIANS);

  152.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  153.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

  154.             final PoissonSeriesParser yParser =
  155.                     baseParser.
  156.                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
  157.                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
  158.             final PoissonSeries ySum = yParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

  159.             final PoissonSeries.CompiledSeries xySum =
  160.                     PoissonSeries.compile(xSum, ySum);

  161.             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
  162.             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
  163.             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
  164.             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
  165.             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
  166.             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  167.             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
  168.             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;

  169.             return new TimeVectorFunction() {

  170.                 /** {@inheritDoc} */
  171.                 @Override
  172.                 public double[] value(final AbsoluteDate date) {

  173.                     final BodiesElements elements = arguments.evaluateAll(date);
  174.                     final double[] xy             = xySum.value(elements);

  175.                     final double omega     = elements.getOmega();
  176.                     final double f         = elements.getF();
  177.                     final double d         = elements.getD();
  178.                     final double t         = elements.getTC();

  179.                     final double cosOmega  = FastMath.cos(omega);
  180.                     final double sinOmega  = FastMath.sin(omega);
  181.                     final double sin2Omega = FastMath.sin(2 * omega);
  182.                     final double cos2FDOm  = FastMath.cos(2 * (f - d + omega));
  183.                     final double sin2FDOm  = FastMath.sin(2 * (f - d + omega));

  184.                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
  185.                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
  186.                     final double y = yPolynomial.value(t) + xy[1] +
  187.                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
  188.                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
  189.                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));

  190.                     return new double[] {
  191.                         x, y, sPxy2
  192.                     };

  193.                 }

  194.                 /** {@inheritDoc} */
  195.                 @Override
  196.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  197.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  198.                     final T[] xy             = xySum.value(elements);

  199.                     final T omega     = elements.getOmega();
  200.                     final T f         = elements.getF();
  201.                     final T d         = elements.getD();
  202.                     final T t         = elements.getTC();
  203.                     final T t2        = t.multiply(t);

  204.                     final T cosOmega  = omega.cos();
  205.                     final T sinOmega  = omega.sin();
  206.                     final T sin2Omega = omega.multiply(2).sin();
  207.                     final T fMDPO2 = f.subtract(d).add(omega).multiply(2);
  208.                     final T cos2FDOm  = fMDPO2.cos();
  209.                     final T sin2FDOm  = fMDPO2.sin();

  210.                     final T x = xPolynomial.value(t).
  211.                                 add(xy[0].multiply(sinEps0)).
  212.                                 add(t2.multiply(cosOmega.multiply(fXCosOm).add(sinOmega.multiply(fXSinOm)).add(cos2FDOm.multiply(fXSin2FDOm))));
  213.                     final T y = yPolynomial.value(t).
  214.                                 add(xy[1]).
  215.                                 add(t2.multiply(cosOmega.multiply(fYCosOm).add(cos2FDOm.multiply(fYCos2FDOm))));
  216.                     final T sPxy2 = sinOmega.multiply(fSSinOm).
  217.                                     add(sin2Omega.multiply(fSSin2Om)).
  218.                                     add(t.multiply(fST3).add(sinOmega.multiply(fST2SinOm)).add(sin2FDOm.multiply(fST2Sin2FDOm)).multiply(t).add(fST).multiply(t));

  219.                     final T[] a = MathArrays.buildArray(date.getField(), 3);
  220.                     a[0] = x;
  221.                     a[1] = y;
  222.                     a[2] = sPxy2;
  223.                     return a;

  224.                 }

  225.             };

  226.         }

  227.         /** {@inheritDoc} */
  228.         @Override
  229.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

  230.             // set up the conventional polynomials
  231.             // the following values are from Lieske et al. paper:
  232.             // Expressions for the precession quantities based upon the IAU(1976) system of astronomical constants
  233.             // http://articles.adsabs.harvard.edu/full/1977A%26A....58....1L
  234.             // also available as equation 30 in IERS 2003 conventions
  235.             final PolynomialNutation psiA =
  236.                     new PolynomialNutation(   0.0,
  237.                                            5038.7784   * Constants.ARC_SECONDS_TO_RADIANS,
  238.                                              -1.07259  * Constants.ARC_SECONDS_TO_RADIANS,
  239.                                              -0.001147 * Constants.ARC_SECONDS_TO_RADIANS);
  240.             final PolynomialNutation omegaA =
  241.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  242.                                             0.0,
  243.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  244.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  245.             final PolynomialNutation chiA =
  246.                     new PolynomialNutation( 0.0,
  247.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  248.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  249.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  250.             return new PrecessionFunction(psiA, omegaA, chiA);

  251.         }

  252.         /** {@inheritDoc} */
  253.         @Override
  254.         public TimeVectorFunction getNutationFunction()
  255.             throws OrekitException {

  256.             // set up nutation arguments
  257.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  258.             // set up Poisson series
  259.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  260.             final PoissonSeriesParser baseParser =
  261.                     new PoissonSeriesParser(10).withFirstDelaunay(1);

  262.             final PoissonSeriesParser psiParser =
  263.                     baseParser.
  264.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  265.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  266.             final PoissonSeries psiSeries = psiParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  267.             final PoissonSeriesParser epsilonParser =
  268.                     baseParser.
  269.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  270.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  271.             final PoissonSeries epsilonSeries = epsilonParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  272.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  273.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  274.             return new TimeVectorFunction() {

  275.                 /** {@inheritDoc} */
  276.                 @Override
  277.                 public double[] value(final AbsoluteDate date) {
  278.                     final BodiesElements elements = arguments.evaluateAll(date);
  279.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  280.                     return new double[] {
  281.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  282.                     };
  283.                 }

  284.                 /** {@inheritDoc} */
  285.                 @Override
  286.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  287.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  288.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  289.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  290.                     result[0] = psiEpsilon[0];
  291.                     result[1] = psiEpsilon[1];
  292.                     result[2] = IAU1994ResolutionC7.value(elements);
  293.                     return result;
  294.                 }

  295.             };

  296.         }

  297.         /** {@inheritDoc} */
  298.         @Override
  299.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1)
  300.             throws OrekitException {

  301.             // Radians per second of time
  302.             final double radiansPerSecond = MathUtils.TWO_PI / Constants.JULIAN_DAY;

  303.             // constants from IERS 1996 page 21
  304.             // the underlying model is IAU 1982 GMST-UT1
  305.             final AbsoluteDate gmstReference =
  306.                 new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
  307.             final double gmst0 = 24110.54841;
  308.             final double gmst1 = 8640184.812866;
  309.             final double gmst2 = 0.093104;
  310.             final double gmst3 = -6.2e-6;

  311.             return new TimeScalarFunction() {

  312.                 /** {@inheritDoc} */
  313.                 @Override
  314.                 public double value(final AbsoluteDate date) {

  315.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  316.                     final double dtai = date.durationFrom(gmstReference);
  317.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  318.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

  319.                     // Seconds in the day, adjusted by 12 hours because the
  320.                     // UT1 is supplied as a Julian date beginning at noon.
  321.                     final double sd = FastMath.IEEEremainder(tut1 + Constants.JULIAN_DAY / 2, Constants.JULIAN_DAY);

  322.                     // compute Greenwich mean sidereal time, in radians
  323.                     return ((((((tt * gmst3 + gmst2) * tt) + gmst1) * tt) + gmst0) + sd) * radiansPerSecond;

  324.                 }

  325.                 /** {@inheritDoc} */
  326.                 @Override
  327.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  328.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  329.                     final T dtai = date.durationFrom(gmstReference);
  330.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  331.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

  332.                     // Seconds in the day, adjusted by 12 hours because the
  333.                     // UT1 is supplied as a Julian date beginning at noon.
  334.                     final T sd = tut1.add(Constants.JULIAN_DAY / 2).remainder(Constants.JULIAN_DAY);

  335.                     // compute Greenwich mean sidereal time, in radians
  336.                     return tt.multiply(gmst3).add(gmst2).multiply(tt).add(gmst1).multiply(tt).add(gmst0).add(sd).multiply(radiansPerSecond);

  337.                 }

  338.             };

  339.         }

  340.         /** {@inheritDoc} */
  341.         @Override
  342.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1)
  343.             throws OrekitException {

  344.             // Radians per second of time
  345.             final double radiansPerSecond = MathUtils.TWO_PI / Constants.JULIAN_DAY;

  346.             // constants from IERS 1996 page 21
  347.             // the underlying model is IAU 1982 GMST-UT1
  348.             final AbsoluteDate gmstReference =
  349.                 new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
  350.             final double gmst1 = 8640184.812866;
  351.             final double gmst2 = 0.093104;
  352.             final double gmst3 = -6.2e-6;

  353.             return new TimeScalarFunction() {

  354.                 /** {@inheritDoc} */
  355.                 @Override
  356.                 public double value(final AbsoluteDate date) {

  357.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  358.                     final double dtai = date.durationFrom(gmstReference);
  359.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  360.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

  361.                     // compute Greenwich mean sidereal time rate, in radians per second
  362.                     return ((((tt * 3 * gmst3 + 2 * gmst2) * tt) + gmst1) / Constants.JULIAN_CENTURY + 1) * radiansPerSecond;

  363.                 }

  364.                 /** {@inheritDoc} */
  365.                 @Override
  366.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  367.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  368.                     final T dtai = date.durationFrom(gmstReference);
  369.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  370.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

  371.                     // compute Greenwich mean sidereal time, in radians
  372.                     return tt.multiply(3 * gmst3).add(2 * gmst2).multiply(tt).add(gmst1).divide(Constants.JULIAN_CENTURY).add(1).multiply(radiansPerSecond);

  373.                 }

  374.             };

  375.         }

  376.         /** {@inheritDoc} */
  377.         @Override
  378.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  379.             throws OrekitException {

  380.             // obliquity
  381.             final TimeScalarFunction epsilonA = getMeanObliquityFunction();

  382.             // GMST function
  383.             final TimeScalarFunction gmst = getGMSTFunction(ut1);

  384.             // nutation function
  385.             final TimeVectorFunction nutation = getNutationFunction();

  386.             return new TimeScalarFunction() {

  387.                 /** {@inheritDoc} */
  388.                 @Override
  389.                 public double value(final AbsoluteDate date) {

  390.                     // compute equation of equinoxes
  391.                     final double[] angles = nutation.value(date);
  392.                     double deltaPsi = angles[0];
  393.                     if (eopHistory != null) {
  394.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  395.                     }
  396.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

  397.                     // add mean sidereal time and equation of equinoxes
  398.                     return gmst.value(date) + eqe;

  399.                 }

  400.                 /** {@inheritDoc} */
  401.                 @Override
  402.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  403.                     // compute equation of equinoxes
  404.                     final T[] angles = nutation.value(date);
  405.                     T deltaPsi = angles[0];
  406.                     if (eopHistory != null) {
  407.                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
  408.                     }
  409.                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);

  410.                     // add mean sidereal time and equation of equinoxes
  411.                     return gmst.value(date).add(eqe);

  412.                 }

  413.             };

  414.         }

  415.         /** {@inheritDoc} */
  416.         @Override
  417.         public TimeVectorFunction getEOPTidalCorrection()
  418.             throws OrekitException {

  419.             // set up nutation arguments
  420.             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
  421.             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
  422.             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
  423.             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
  424.             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());

  425.             // set up Poisson series
  426.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  427.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(17).
  428.                     withOptionalColumn(1).
  429.                     withGamma(7).
  430.                     withFirstDelaunay(2);
  431.             final PoissonSeries xSeries =
  432.                     xyParser.
  433.                     withSinCos(0, 14, milliAS, 15, milliAS).
  434.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  435.             final PoissonSeries ySeries =
  436.                     xyParser.
  437.                     withSinCos(0, 16, milliAS, 17, milliAS).
  438.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES),
  439.                                                          TIDAL_CORRECTION_XP_YP_SERIES);

  440.             final double deciMilliS = 1.0e-4;
  441.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
  442.                     withOptionalColumn(1).
  443.                     withGamma(7).
  444.                     withFirstDelaunay(2).
  445.                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
  446.             final PoissonSeries ut1Series =
  447.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  448.             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);

  449.         }

  450.         /** {@inheritDoc} */
  451.         public LoveNumbers getLoveNumbers() throws OrekitException {
  452.             return loadLoveNumbers(LOVE_NUMBERS);
  453.         }

  454.         /** {@inheritDoc} */
  455.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  456.             throws OrekitException {

  457.             // set up nutation arguments
  458.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  459.             // set up Poisson series
  460.             final PoissonSeriesParser k20Parser =
  461.                     new PoissonSeriesParser(18).
  462.                         withOptionalColumn(1).
  463.                         withDoodson(4, 2).
  464.                         withFirstDelaunay(10);
  465.             final PoissonSeriesParser k21Parser =
  466.                     new PoissonSeriesParser(18).
  467.                         withOptionalColumn(1).
  468.                         withDoodson(4, 3).
  469.                         withFirstDelaunay(10);
  470.             final PoissonSeriesParser k22Parser =
  471.                     new PoissonSeriesParser(16).
  472.                         withOptionalColumn(1).
  473.                         withDoodson(4, 2).
  474.                         withFirstDelaunay(10);

  475.             final double pico = 1.0e-12;
  476.             final PoissonSeries c20Series =
  477.                     k20Parser.
  478.                   withSinCos(0, 18, -pico, 16, pico).
  479.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  480.             final PoissonSeries c21Series =
  481.                     k21Parser.
  482.                     withSinCos(0, 17, pico, 18, pico).
  483.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  484.             final PoissonSeries s21Series =
  485.                     k21Parser.
  486.                     withSinCos(0, 18, -pico, 17, pico).
  487.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  488.             final PoissonSeries c22Series =
  489.                     k22Parser.
  490.                     withSinCos(0, -1, pico, 16, pico).
  491.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  492.             final PoissonSeries s22Series =
  493.                     k22Parser.
  494.                     withSinCos(0, 16, -pico, -1, pico).
  495.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  496.             return new TideFrequencyDependenceFunction(arguments,
  497.                                                        c20Series,
  498.                                                        c21Series, s21Series,
  499.                                                        c22Series, s22Series);

  500.         }

  501.         /** {@inheritDoc} */
  502.         @Override
  503.         public double getPermanentTide() throws OrekitException {
  504.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  505.         }

  506.         /** {@inheritDoc} */
  507.         @Override
  508.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  509.             // constants from IERS 1996 page 47
  510.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  511.             final double coupling     =  0.00112;

  512.             return new TimeVectorFunction() {

  513.                 /** {@inheritDoc} */
  514.                 @Override
  515.                 public double[] value(final AbsoluteDate date) {
  516.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  517.                     return new double[] {
  518.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  519.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  520.                     };
  521.                 }

  522.                 /** {@inheritDoc} */
  523.                 @Override
  524.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  525.                     final FieldPoleCorrection<T> pole = eopHistory.getPoleCorrection(date);
  526.                     final T[] a = MathArrays.buildArray(date.getField(), 2);
  527.                     a[0] = pole.getXp().add(pole.getYp().multiply(coupling)).multiply(globalFactor);
  528.                     a[1] = pole.getXp().multiply(coupling).subtract(pole.getYp()).multiply(globalFactor);
  529.                     return a;
  530.                 }

  531.             };
  532.         }

  533.         /** {@inheritDoc} */
  534.         @Override
  535.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  536.             throws OrekitException {

  537.             return new TimeVectorFunction() {

  538.                 /** {@inheritDoc} */
  539.                 @Override
  540.                 public double[] value(final AbsoluteDate date) {
  541.                     // there are no model for ocean pole tide prior to conventions 2010
  542.                     return new double[] {
  543.                         0.0, 0.0
  544.                     };
  545.                 }

  546.                 /** {@inheritDoc} */
  547.                 @Override
  548.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  549.                     // there are no model for ocean pole tide prior to conventions 2010
  550.                     return MathArrays.buildArray(date.getField(), 2);
  551.                 }

  552.             };
  553.         }

  554.         /** {@inheritDoc} */
  555.         @Override
  556.         public double[] getNominalTidalDisplacement() {

  557.             //  // elastic Earth values
  558.             //  return new double[] {
  559.             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  560.             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
  561.             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  562.             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  563.             //      // H₀
  564.             //      -0.31460
  565.             //  };

  566.             // anelastic Earth values
  567.             return new double[] {
  568.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  569.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  570.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  571.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  572.                 // H₀
  573.                 -0.31460
  574.             };

  575.         }

  576.         /** {@inheritDoc} */
  577.         @Override
  578.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal()
  579.             throws OrekitException {
  580.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  581.                                                                   18, 17, -1, 18, -1);
  582.         }

  583.         /** {@inheritDoc} */
  584.         @Override
  585.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal()
  586.             throws OrekitException {
  587.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  588.                                                                 20, 17, 19, 18, 20);
  589.         }

  590.     },

  591.     /** Constant for IERS 2003 conventions. */
  592.     IERS_2003 {

  593.         /** Nutation arguments resources. */
  594.         private static final String NUTATION_ARGUMENTS = IERS_BASE + "2003/nutation-arguments.txt";

  595.         /** X series resources. */
  596.         private static final String X_SERIES           = IERS_BASE + "2003/tab5.2a.txt";

  597.         /** Y series resources. */
  598.         private static final String Y_SERIES           = IERS_BASE + "2003/tab5.2b.txt";

  599.         /** S series resources. */
  600.         private static final String S_SERIES           = IERS_BASE + "2003/tab5.2c.txt";

  601.         /** Luni-solar series resources. */
  602.         private static final String LUNI_SOLAR_SERIES  = IERS_BASE + "2003/tab5.3a-first-table.txt";

  603.         /** Planetary series resources. */
  604.         private static final String PLANETARY_SERIES   = IERS_BASE + "2003/tab5.3b.txt";

  605.         /** Greenwhich sidereal time series resources. */
  606.         private static final String GST_SERIES         = IERS_BASE + "2003/tab5.4.txt";

  607.         /** Tidal correction for xp, yp series resources. */
  608.         private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "2003/tab8.2ab.txt";

  609.         /** Tidal correction for UT1 resources. */
  610.         private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "2003/tab8.3ab.txt";

  611.         /** Love numbers resources. */
  612.         private static final String LOVE_NUMBERS = IERS_BASE + "2003/tab6.1.txt";

  613.         /** Frequency dependence model for k₂₀. */
  614.         private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3b.txt";

  615.         /** Frequency dependence model for k₂₁. */
  616.         private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3a.txt";

  617.         /** Frequency dependence model for k₂₂. */
  618.         private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3c.txt";

  619.         /** Annual pole table. */
  620.         private static final String ANNUAL_POLE = IERS_BASE + "2003/annual.pole";

  621.         /** Tidal displacement frequency correction for diurnal tides. */
  622.         private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "2003/tab7.5a.txt";

  623.         /** Tidal displacement frequency correction for zonal tides. */
  624.         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "2003/tab7.5b.txt";

  625.         /** {@inheritDoc} */
  626.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  627.             throws OrekitException {
  628.             return new FundamentalNutationArguments(this, timeScale,
  629.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  630.         }

  631.         /** {@inheritDoc} */
  632.         @Override
  633.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  634.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  635.             final PolynomialNutation epsilonA =
  636.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  637.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  638.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  639.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  640.             return new TimeScalarFunction() {

  641.                 /** {@inheritDoc} */
  642.                 @Override
  643.                 public double value(final AbsoluteDate date) {
  644.                     return epsilonA.value(evaluateTC(date));
  645.                 }

  646.                 /** {@inheritDoc} */
  647.                 @Override
  648.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  649.                     return epsilonA.value(evaluateTC(date));
  650.                 }

  651.             };

  652.         }

  653.         /** {@inheritDoc} */
  654.         @Override
  655.         public TimeVectorFunction getXYSpXY2Function()
  656.             throws OrekitException {

  657.             // set up nutation arguments
  658.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  659.             // set up Poisson series
  660.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  661.             final PoissonSeriesParser parser =
  662.                     new PoissonSeriesParser(17).
  663.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  664.                         withFirstDelaunay(4).
  665.                         withFirstPlanetary(9).
  666.                         withSinCos(0, 2, microAS, 3, microAS);

  667.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  668.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  669.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  670.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  671.             // create a function evaluating the series
  672.             return new TimeVectorFunction() {

  673.                 /** {@inheritDoc} */
  674.                 @Override
  675.                 public double[] value(final AbsoluteDate date) {
  676.                     return xys.value(arguments.evaluateAll(date));
  677.                 }

  678.                 /** {@inheritDoc} */
  679.                 @Override
  680.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  681.                     return xys.value(arguments.evaluateAll(date));
  682.                 }

  683.             };

  684.         }


  685.         /** {@inheritDoc} */
  686.         @Override
  687.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

  688.             // set up the conventional polynomials
  689.             // the following values are from equation 32 in IERS 2003 conventions
  690.             final PolynomialNutation psiA =
  691.                     new PolynomialNutation(    0.0,
  692.                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  693.                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  694.                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  695.             final PolynomialNutation omegaA =
  696.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  697.                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  698.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  699.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  700.             final PolynomialNutation chiA =
  701.                     new PolynomialNutation( 0.0,
  702.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  703.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  704.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  705.             return new PrecessionFunction(psiA, omegaA, chiA);

  706.         }

  707.         /** {@inheritDoc} */
  708.         @Override
  709.         public TimeVectorFunction getNutationFunction()
  710.             throws OrekitException {

  711.             // set up nutation arguments
  712.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  713.             // set up Poisson series
  714.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  715.             final PoissonSeriesParser luniSolarParser =
  716.                     new PoissonSeriesParser(14).withFirstDelaunay(1);
  717.             final PoissonSeriesParser luniSolarPsiParser =
  718.                     luniSolarParser.
  719.                     withSinCos(0, 7, milliAS, 11, milliAS).
  720.                     withSinCos(1, 8, milliAS, 12, milliAS);
  721.             final PoissonSeries psiLuniSolarSeries =
  722.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
  723.             final PoissonSeriesParser luniSolarEpsilonParser =
  724.                     luniSolarParser.
  725.                     withSinCos(0, 13, milliAS, 9, milliAS).
  726.                     withSinCos(1, 14, milliAS, 10, milliAS);
  727.             final PoissonSeries epsilonLuniSolarSeries =
  728.                     luniSolarEpsilonParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  729.             final PoissonSeriesParser planetaryParser =
  730.                     new PoissonSeriesParser(21).
  731.                         withFirstDelaunay(2).
  732.                         withFirstPlanetary(7);
  733.             final PoissonSeriesParser planetaryPsiParser =
  734.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  735.             final PoissonSeries psiPlanetarySeries =
  736.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
  737.             final PoissonSeriesParser planetaryEpsilonParser =
  738.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  739.             final PoissonSeries epsilonPlanetarySeries =
  740.                     planetaryEpsilonParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  741.             final PoissonSeries.CompiledSeries luniSolarSeries =
  742.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  743.             final PoissonSeries.CompiledSeries planetarySeries =
  744.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  745.             return new TimeVectorFunction() {

  746.                 /** {@inheritDoc} */
  747.                 @Override
  748.                 public double[] value(final AbsoluteDate date) {
  749.                     final BodiesElements elements = arguments.evaluateAll(date);
  750.                     final double[] luniSolar = luniSolarSeries.value(elements);
  751.                     final double[] planetary = planetarySeries.value(elements);
  752.                     return new double[] {
  753.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  754.                         IAU1994ResolutionC7.value(elements)
  755.                     };
  756.                 }

  757.                 /** {@inheritDoc} */
  758.                 @Override
  759.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  760.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  761.                     final T[] luniSolar = luniSolarSeries.value(elements);
  762.                     final T[] planetary = planetarySeries.value(elements);
  763.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  764.                     result[0] = luniSolar[0].add(planetary[0]);
  765.                     result[1] = luniSolar[1].add(planetary[1]);
  766.                     result[2] = IAU1994ResolutionC7.value(elements);
  767.                     return result;
  768.                 }

  769.             };

  770.         }

  771.         /** {@inheritDoc} */
  772.         @Override
  773.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1)
  774.             throws OrekitException {

  775.             // Earth Rotation Angle
  776.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  777.             // Polynomial part of the apparent sidereal time series
  778.             // which is the opposite of Equation of Origins (EO)
  779.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  780.             final PoissonSeriesParser parser =
  781.                     new PoissonSeriesParser(17).
  782.                         withFirstDelaunay(4).
  783.                         withFirstPlanetary(9).
  784.                         withSinCos(0, 2, microAS, 3, microAS).
  785.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  786.             final PolynomialNutation minusEO =
  787.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  788.             // create a function evaluating the series
  789.             return new TimeScalarFunction() {

  790.                 /** {@inheritDoc} */
  791.                 @Override
  792.                 public double value(final AbsoluteDate date) {
  793.                     return era.value(date) + minusEO.value(evaluateTC(date));
  794.                 }

  795.                 /** {@inheritDoc} */
  796.                 @Override
  797.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  798.                     return era.value(date).add(minusEO.value(evaluateTC(date)));
  799.                 }

  800.             };

  801.         }

  802.         /** {@inheritDoc} */
  803.         @Override
  804.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1)
  805.             throws OrekitException {

  806.             // Earth Rotation Angle
  807.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  808.             // Polynomial part of the apparent sidereal time series
  809.             // which is the opposite of Equation of Origins (EO)
  810.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  811.             final PoissonSeriesParser parser =
  812.                     new PoissonSeriesParser(17).
  813.                         withFirstDelaunay(4).
  814.                         withFirstPlanetary(9).
  815.                         withSinCos(0, 2, microAS, 3, microAS).
  816.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  817.             final PolynomialNutation minusEO =
  818.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  819.             // create a function evaluating the series
  820.             return new TimeScalarFunction() {

  821.                 /** {@inheritDoc} */
  822.                 @Override
  823.                 public double value(final AbsoluteDate date) {
  824.                     return era.getRate() + minusEO.derivative(evaluateTC(date));
  825.                 }

  826.                 /** {@inheritDoc} */
  827.                 @Override
  828.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  829.                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
  830.                 }

  831.             };

  832.         }

  833.         /** {@inheritDoc} */
  834.         @Override
  835.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  836.             throws OrekitException {

  837.             // set up nutation arguments
  838.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  839.             // mean obliquity function
  840.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  841.             // set up Poisson series
  842.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  843.             final PoissonSeriesParser luniSolarPsiParser =
  844.                     new PoissonSeriesParser(14).
  845.                         withFirstDelaunay(1).
  846.                         withSinCos(0, 7, milliAS, 11, milliAS).
  847.                         withSinCos(1, 8, milliAS, 12, milliAS);
  848.             final PoissonSeries psiLuniSolarSeries =
  849.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  850.             final PoissonSeriesParser planetaryPsiParser =
  851.                     new PoissonSeriesParser(21).
  852.                         withFirstDelaunay(2).
  853.                         withFirstPlanetary(7).
  854.                         withSinCos(0, 17, milliAS, 18, milliAS);
  855.             final PoissonSeries psiPlanetarySeries =
  856.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  857.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  858.             final PoissonSeriesParser gstParser =
  859.                     new PoissonSeriesParser(17).
  860.                         withFirstDelaunay(4).
  861.                         withFirstPlanetary(9).
  862.                         withSinCos(0, 2, microAS, 3, microAS).
  863.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  864.             final PoissonSeries gstSeries = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  865.             final PoissonSeries.CompiledSeries psiGstSeries =
  866.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  867.             // ERA function
  868.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  869.             return new TimeScalarFunction() {

  870.                 /** {@inheritDoc} */
  871.                 @Override
  872.                 public double value(final AbsoluteDate date) {

  873.                     // evaluate equation of origins
  874.                     final BodiesElements elements = arguments.evaluateAll(date);
  875.                     final double[] angles = psiGstSeries.value(elements);
  876.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  877.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  878.                     final double epsilonA = epsilon.value(date);

  879.                     // subtract equation of origin from EA
  880.                     // (hence add the series above which have the sign included)
  881.                     return era.value(date) + deltaPsi * FastMath.cos(epsilonA) + angles[2];

  882.                 }

  883.                 /** {@inheritDoc} */
  884.                 @Override
  885.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  886.                     // evaluate equation of origins
  887.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  888.                     final T[] angles = psiGstSeries.value(elements);
  889.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  890.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  891.                     final T epsilonA = epsilon.value(date);

  892.                     // subtract equation of origin from EA
  893.                     // (hence add the series above which have the sign included)
  894.                     return era.value(date).add(deltaPsi.multiply(epsilonA.cos())).add(angles[2]);

  895.                 }

  896.             };

  897.         }

  898.         /** {@inheritDoc} */
  899.         @Override
  900.         public TimeVectorFunction getEOPTidalCorrection()
  901.             throws OrekitException {

  902.             // set up nutation arguments
  903.             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
  904.             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
  905.             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
  906.             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
  907.             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());

  908.             // set up Poisson series
  909.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  910.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  911.                     withOptionalColumn(1).
  912.                     withGamma(2).
  913.                     withFirstDelaunay(3);
  914.             final PoissonSeries xSeries =
  915.                     xyParser.
  916.                     withSinCos(0, 10, microAS, 11, microAS).
  917.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  918.             final PoissonSeries ySeries =
  919.                     xyParser.
  920.                     withSinCos(0, 12, microAS, 13, microAS).
  921.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  922.             final double microS = 1.0e-6;
  923.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  924.                     withOptionalColumn(1).
  925.                     withGamma(2).
  926.                     withFirstDelaunay(3).
  927.                     withSinCos(0, 10, microS, 11, microS);
  928.             final PoissonSeries ut1Series =
  929.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  930.             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);

  931.         }

  932.         /** {@inheritDoc} */
  933.         public LoveNumbers getLoveNumbers() throws OrekitException {
  934.             return loadLoveNumbers(LOVE_NUMBERS);
  935.         }

  936.         /** {@inheritDoc} */
  937.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  938.             throws OrekitException {

  939.             // set up nutation arguments
  940.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  941.             // set up Poisson series
  942.             final PoissonSeriesParser k20Parser =
  943.                     new PoissonSeriesParser(18).
  944.                         withOptionalColumn(1).
  945.                         withDoodson(4, 2).
  946.                         withFirstDelaunay(10);
  947.             final PoissonSeriesParser k21Parser =
  948.                     new PoissonSeriesParser(18).
  949.                         withOptionalColumn(1).
  950.                         withDoodson(4, 3).
  951.                         withFirstDelaunay(10);
  952.             final PoissonSeriesParser k22Parser =
  953.                     new PoissonSeriesParser(16).
  954.                         withOptionalColumn(1).
  955.                         withDoodson(4, 2).
  956.                         withFirstDelaunay(10);

  957.             final double pico = 1.0e-12;
  958.             final PoissonSeries c20Series =
  959.                     k20Parser.
  960.                   withSinCos(0, 18, -pico, 16, pico).
  961.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  962.             final PoissonSeries c21Series =
  963.                     k21Parser.
  964.                     withSinCos(0, 17, pico, 18, pico).
  965.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  966.             final PoissonSeries s21Series =
  967.                     k21Parser.
  968.                     withSinCos(0, 18, -pico, 17, pico).
  969.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  970.             final PoissonSeries c22Series =
  971.                     k22Parser.
  972.                     withSinCos(0, -1, pico, 16, pico).
  973.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  974.             final PoissonSeries s22Series =
  975.                     k22Parser.
  976.                     withSinCos(0, 16, -pico, -1, pico).
  977.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  978.             return new TideFrequencyDependenceFunction(arguments,
  979.                                                        c20Series,
  980.                                                        c21Series, s21Series,
  981.                                                        c22Series, s22Series);

  982.         }

  983.         /** {@inheritDoc} */
  984.         @Override
  985.         public double getPermanentTide() throws OrekitException {
  986.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  987.         }

  988.         /** {@inheritDoc} */
  989.         @Override
  990.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory)
  991.             throws OrekitException {

  992.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  993.             final TimeScale utc = TimeScalesFactory.getUTC();
  994.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  995.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  996.                     /** {@inheritDoc} */
  997.                     @Override
  998.                     public MeanPole convert(final double[] rawFields) throws OrekitException {
  999.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  1000.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  1001.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  1002.                     }
  1003.                 };
  1004.             final SimpleTimeStampedTableParser<MeanPole> parser =
  1005.                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
  1006.             final List<MeanPole> annualPoleList = parser.parse(getStream(ANNUAL_POLE), ANNUAL_POLE);
  1007.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  1008.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  1009.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  1010.                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);

  1011.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  1012.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  1013.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  1014.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  1015.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  1016.             // constants from IERS 2003, section 6.2
  1017.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1018.             final double ratio        =  0.00115;

  1019.             return new TimeVectorFunction() {

  1020.                 /** {@inheritDoc} */
  1021.                 @Override
  1022.                 public double[] value(final AbsoluteDate date) {

  1023.                     // we can't compute anything before the range covered by the annual pole file
  1024.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  1025.                         return new double[] {
  1026.                             0.0, 0.0
  1027.                         };
  1028.                     }

  1029.                     // evaluate mean pole
  1030.                     double meanPoleX = 0;
  1031.                     double meanPoleY = 0;
  1032.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  1033.                         // we are within the range covered by the annual pole file,
  1034.                         // we interpolate within it
  1035.                         try {
  1036.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  1037.                             annualCache.getNeighbors(date).forEach(neighbor ->
  1038.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  1039.                                                             new double[] {
  1040.                                                                 neighbor.getX(), neighbor.getY()
  1041.                                                             }));
  1042.                             final double[] interpolated = interpolator.value(0);
  1043.                             meanPoleX = interpolated[0];
  1044.                             meanPoleY = interpolated[1];
  1045.                         } catch (TimeStampedCacheException tsce) {
  1046.                             // this should never happen
  1047.                             throw new OrekitInternalError(tsce);
  1048.                         }
  1049.                     } else {

  1050.                         // we are after the range covered by the annual pole file,
  1051.                         // we use the polynomial extension
  1052.                         final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1053.                         meanPoleX = xp0 + t * xp0Dot;
  1054.                         meanPoleY = yp0 + t * yp0Dot;

  1055.                     }

  1056.                     // evaluate wobble variables
  1057.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1058.                     final double m1 = correction.getXp() - meanPoleX;
  1059.                     final double m2 = meanPoleY - correction.getYp();

  1060.                     return new double[] {
  1061.                         // the following correspond to the equations published in IERS 2003 conventions,
  1062.                         // section 6.2 page 65. In the publication, the equations read:
  1063.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1064.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1065.                         // However, it seems there are sign errors in these equations, which have
  1066.                         // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1067.                         // publication, the equations read:
  1068.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1069.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1070.                         // the newer equations seem more consistent with the premises as the
  1071.                         // deformation due to the centrifugal potential has the form:
  1072.                         // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1073.                         // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1074.                         // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1075.                         // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1076.                         // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1077.                         // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1078.                         // and Im(k₂)/Re(k₂) is very close to +0.00115
  1079.                         // As the equation as written in the IERS 2003 conventions are used in
  1080.                         // legacy systems, we have reproduced this alleged error here (and fixed it in
  1081.                         // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1082.                         // using the IERS 2003 conventions for solid pole tide computation other than
  1083.                         // for validation or reproducibility of legacy applications behavior.
  1084.                         // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1085.                         // the effect is quite small. A test case on a propagated orbit showed a position change
  1086.                         // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1087.                         globalFactor * (m1 - ratio * m2),
  1088.                         globalFactor * (m2 + ratio * m1),
  1089.                     };

  1090.                 }

  1091.                 /** {@inheritDoc} */
  1092.                 @Override
  1093.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  1094.                     final AbsoluteDate aDate = date.toAbsoluteDate();

  1095.                     // we can't compute anything before the range covered by the annual pole file
  1096.                     if (aDate.compareTo(firstAnnualPoleDate) <= 0) {
  1097.                         return MathArrays.buildArray(date.getField(), 2);
  1098.                     }

  1099.                     // evaluate mean pole
  1100.                     T meanPoleX = date.getField().getZero();
  1101.                     T meanPoleY = date.getField().getZero();
  1102.                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
  1103.                         // we are within the range covered by the annual pole file,
  1104.                         // we interpolate within it
  1105.                         try {
  1106.                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
  1107.                             final T[] y = MathArrays.buildArray(date.getField(), 2);
  1108.                             final T zero = date.getField().getZero();
  1109.                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
  1110.                                                                                                        // for example removing derivatives
  1111.                                                                                                        // if T was DerivativeStructure
  1112.                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
  1113.                                 y[0] = zero.add(neighbor.getX());
  1114.                                 y[1] = zero.add(neighbor.getY());
  1115.                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
  1116.                             });
  1117.                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
  1118.                             meanPoleX = interpolated[0];
  1119.                             meanPoleY = interpolated[1];
  1120.                         } catch (TimeStampedCacheException tsce) {
  1121.                             // this should never happen
  1122.                             throw new OrekitInternalError(tsce);
  1123.                         }
  1124.                     } else {

  1125.                         // we are after the range covered by the annual pole file,
  1126.                         // we use the polynomial extension
  1127.                         final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1128.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1129.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1130.                     }

  1131.                     // evaluate wobble variables
  1132.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1133.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1134.                     final T m2 = meanPoleY.subtract(correction.getYp());

  1135.                     final T[] a = MathArrays.buildArray(date.getField(), 2);

  1136.                     // the following correspond to the equations published in IERS 2003 conventions,
  1137.                     // section 6.2 page 65. In the publication, the equations read:
  1138.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1139.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1140.                     // However, it seems there are sign errors in these equations, which have
  1141.                     // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1142.                     // publication, the equations read:
  1143.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1144.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1145.                     // the newer equations seem more consistent with the premises as the
  1146.                     // deformation due to the centrifugal potential has the form:
  1147.                     // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1148.                     // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1149.                     // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1150.                     // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1151.                     // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1152.                     // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1153.                     // and Im(k₂)/Re(k₂) is very close to +0.00115
  1154.                     // As the equation as written in the IERS 2003 conventions are used in
  1155.                     // legacy systems, we have reproduced this alleged error here (and fixed it in
  1156.                     // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1157.                     // using the IERS 2003 conventions for solid pole tide computation other than
  1158.                     // for validation or reproducibility of legacy applications behavior.
  1159.                     // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1160.                     // the effect is quite small. A test case on a propagated orbit showed a position change
  1161.                     // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1162.                     a[0] = m1.add(m2.multiply(-ratio)).multiply(globalFactor);
  1163.                     a[1] = m2.add(m1.multiply( ratio)).multiply(globalFactor);

  1164.                     return a;

  1165.                 }

  1166.             };

  1167.         }

  1168.         /** {@inheritDoc} */
  1169.         @Override
  1170.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  1171.             throws OrekitException {

  1172.             return new TimeVectorFunction() {

  1173.                 /** {@inheritDoc} */
  1174.                 @Override
  1175.                 public double[] value(final AbsoluteDate date) {
  1176.                     // there are no model for ocean pole tide prior to conventions 2010
  1177.                     return new double[] {
  1178.                         0.0, 0.0
  1179.                     };
  1180.                 }

  1181.                 /** {@inheritDoc} */
  1182.                 @Override
  1183.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1184.                     // there are no model for ocean pole tide prior to conventions 2010
  1185.                     return MathArrays.buildArray(date.getField(), 2);
  1186.                 }

  1187.             };
  1188.         }

  1189.         /** {@inheritDoc} */
  1190.         @Override
  1191.         public double[] getNominalTidalDisplacement() {
  1192.             return new double[] {
  1193.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1194.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1195.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1196.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1197.                 // H₀
  1198.                 -0.31460
  1199.             };
  1200.         }

  1201.         /** {@inheritDoc} */
  1202.         @Override
  1203.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal()
  1204.             throws OrekitException {
  1205.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1206.                                                                   18, 15, 16, 17, 18);
  1207.         }

  1208.         /** {@inheritDoc} */
  1209.         @Override
  1210.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal()
  1211.             throws OrekitException {
  1212.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1213.                                                                 18, 15, 16, 17, 18);
  1214.         }

  1215.     },

  1216.     /** Constant for IERS 2010 conventions. */
  1217.     IERS_2010 {

  1218.         /** Nutation arguments resources. */
  1219.         private static final String NUTATION_ARGUMENTS = IERS_BASE + "2010/nutation-arguments.txt";

  1220.         /** X series resources. */
  1221.         private static final String X_SERIES           = IERS_BASE + "2010/tab5.2a.txt";

  1222.         /** Y series resources. */
  1223.         private static final String Y_SERIES           = IERS_BASE + "2010/tab5.2b.txt";

  1224.         /** S series resources. */
  1225.         private static final String S_SERIES           = IERS_BASE + "2010/tab5.2d.txt";

  1226.         /** Psi series resources. */
  1227.         private static final String PSI_SERIES         = IERS_BASE + "2010/tab5.3a.txt";

  1228.         /** Epsilon series resources. */
  1229.         private static final String EPSILON_SERIES     = IERS_BASE + "2010/tab5.3b.txt";

  1230.         /** Greenwhich sidereal time series resources. */
  1231.         private static final String GST_SERIES         = IERS_BASE + "2010/tab5.2e.txt";

  1232.         /** Tidal correction for xp, yp series resources. */
  1233.         private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "2010/tab8.2ab.txt";

  1234.         /** Tidal correction for UT1 resources. */
  1235.         private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "2010/tab8.3ab.txt";

  1236.         /** Love numbers resources. */
  1237.         private static final String LOVE_NUMBERS = IERS_BASE + "2010/tab6.3.txt";

  1238.         /** Frequency dependence model for k₂₀. */
  1239.         private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5b.txt";

  1240.         /** Frequency dependence model for k₂₁. */
  1241.         private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5a.txt";

  1242.         /** Frequency dependence model for k₂₂. */
  1243.         private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5c.txt";

  1244.         /** Tidal displacement frequency correction for diurnal tides. */
  1245.         private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "2010/tab7.3a.txt";

  1246.         /** Tidal displacement frequency correction for zonal tides. */
  1247.         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "2010/tab7.3b.txt";

  1248.         /** {@inheritDoc} */
  1249.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  1250.             throws OrekitException {
  1251.             return new FundamentalNutationArguments(this, timeScale,
  1252.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  1253.         }

  1254.         /** {@inheritDoc} */
  1255.         @Override
  1256.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  1257.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1258.             final PolynomialNutation epsilonA =
  1259.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1260.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1261.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1262.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1263.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1264.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1265.             return new TimeScalarFunction() {

  1266.                 /** {@inheritDoc} */
  1267.                 @Override
  1268.                 public double value(final AbsoluteDate date) {
  1269.                     return epsilonA.value(evaluateTC(date));
  1270.                 }

  1271.                 /** {@inheritDoc} */
  1272.                 @Override
  1273.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1274.                     return epsilonA.value(evaluateTC(date));
  1275.                 }

  1276.             };

  1277.         }

  1278.         /** {@inheritDoc} */
  1279.         @Override
  1280.         public TimeVectorFunction getXYSpXY2Function() throws OrekitException {

  1281.             // set up nutation arguments
  1282.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1283.             // set up Poisson series
  1284.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1285.             final PoissonSeriesParser parser =
  1286.                     new PoissonSeriesParser(17).
  1287.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1288.                         withFirstDelaunay(4).
  1289.                         withFirstPlanetary(9).
  1290.                         withSinCos(0, 2, microAS, 3, microAS);
  1291.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  1292.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  1293.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  1294.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  1295.             // create a function evaluating the series
  1296.             return new TimeVectorFunction() {

  1297.                 /** {@inheritDoc} */
  1298.                 @Override
  1299.                 public double[] value(final AbsoluteDate date) {
  1300.                     return xys.value(arguments.evaluateAll(date));
  1301.                 }

  1302.                 /** {@inheritDoc} */
  1303.                 @Override
  1304.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1305.                     return xys.value(arguments.evaluateAll(date));
  1306.                 }

  1307.             };

  1308.         }

  1309.         /** {@inheritDoc} */
  1310.         public LoveNumbers getLoveNumbers() throws OrekitException {
  1311.             return loadLoveNumbers(LOVE_NUMBERS);
  1312.         }

  1313.         /** {@inheritDoc} */
  1314.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  1315.             throws OrekitException {

  1316.             // set up nutation arguments
  1317.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  1318.             // set up Poisson series
  1319.             final PoissonSeriesParser k20Parser =
  1320.                     new PoissonSeriesParser(18).
  1321.                         withOptionalColumn(1).
  1322.                         withDoodson(4, 2).
  1323.                         withFirstDelaunay(10);
  1324.             final PoissonSeriesParser k21Parser =
  1325.                     new PoissonSeriesParser(18).
  1326.                         withOptionalColumn(1).
  1327.                         withDoodson(4, 3).
  1328.                         withFirstDelaunay(10);
  1329.             final PoissonSeriesParser k22Parser =
  1330.                     new PoissonSeriesParser(16).
  1331.                         withOptionalColumn(1).
  1332.                         withDoodson(4, 2).
  1333.                         withFirstDelaunay(10);

  1334.             final double pico = 1.0e-12;
  1335.             final PoissonSeries c20Series =
  1336.                     k20Parser.
  1337.                     withSinCos(0, 18, -pico, 16, pico).
  1338.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  1339.             final PoissonSeries c21Series =
  1340.                     k21Parser.
  1341.                     withSinCos(0, 17, pico, 18, pico).
  1342.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1343.             final PoissonSeries s21Series =
  1344.                     k21Parser.
  1345.                     withSinCos(0, 18, -pico, 17, pico).
  1346.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1347.             final PoissonSeries c22Series =
  1348.                     k22Parser.
  1349.                     withSinCos(0, -1, pico, 16, pico).
  1350.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  1351.             final PoissonSeries s22Series =
  1352.                     k22Parser.
  1353.                     withSinCos(0, 16, -pico, -1, pico).
  1354.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  1355.             return new TideFrequencyDependenceFunction(arguments,
  1356.                                                        c20Series,
  1357.                                                        c21Series, s21Series,
  1358.                                                        c22Series, s22Series);

  1359.         }

  1360.         /** {@inheritDoc} */
  1361.         @Override
  1362.         public double getPermanentTide() throws OrekitException {
  1363.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1364.         }

  1365.         /** Compute pole wobble variables m₁ and m₂.
  1366.          * @param date current date
  1367.          * @param eopHistory EOP history
  1368.          * @return array containing m₁ and m₂
  1369.          */
  1370.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1371.             // polynomial model from IERS 2010, table 7.7
  1372.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1373.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1374.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1375.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1376.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1377.             // evaluate mean pole
  1378.             final double[] xPolynomial;
  1379.             final double[] yPolynomial;
  1380.             if (date.compareTo(changeDate) <= 0) {
  1381.                 xPolynomial = new double[] {
  1382.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1383.                 };
  1384.                 yPolynomial = new double[] {
  1385.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1386.                 };
  1387.             } else {
  1388.                 xPolynomial = new double[] {
  1389.                     23.513 * f0, 7.6141 * f1
  1390.                 };
  1391.                 yPolynomial = new double[] {
  1392.                     358.891 * f0,  -0.6287 * f1
  1393.                 };
  1394.             }
  1395.             double meanPoleX = 0;
  1396.             double meanPoleY = 0;
  1397.             final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1398.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1399.                 meanPoleX = meanPoleX * t + xPolynomial[i];
  1400.             }
  1401.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1402.                 meanPoleY = meanPoleY * t + yPolynomial[i];
  1403.             }

  1404.             // evaluate wobble variables
  1405.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1406.             final double m1 = correction.getXp() - meanPoleX;
  1407.             final double m2 = meanPoleY - correction.getYp();

  1408.             return new double[] {
  1409.                 m1, m2
  1410.             };

  1411.         }

  1412.         /** Compute pole wobble variables m₁ and m₂.
  1413.          * @param date current date
  1414.          * @param <T> type of the field elements
  1415.          * @param eopHistory EOP history
  1416.          * @return array containing m₁ and m₂
  1417.          */
  1418.         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1419.             // polynomial model from IERS 2010, table 7.7
  1420.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1421.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1422.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1423.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1424.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1425.             // evaluate mean pole
  1426.             final double[] xPolynomial;
  1427.             final double[] yPolynomial;
  1428.             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
  1429.                 xPolynomial = new double[] {
  1430.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1431.                 };
  1432.                 yPolynomial = new double[] {
  1433.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1434.                 };
  1435.             } else {
  1436.                 xPolynomial = new double[] {
  1437.                     23.513 * f0, 7.6141 * f1
  1438.                 };
  1439.                 yPolynomial = new double[] {
  1440.                     358.891 * f0,  -0.6287 * f1
  1441.                 };
  1442.             }
  1443.             T meanPoleX = date.getField().getZero();
  1444.             T meanPoleY = date.getField().getZero();
  1445.             final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1446.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1447.                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
  1448.             }
  1449.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1450.                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
  1451.             }

  1452.             // evaluate wobble variables
  1453.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1454.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1455.             m[0] = correction.getXp().subtract(meanPoleX);
  1456.             m[1] = meanPoleY.subtract(correction.getYp());

  1457.             return m;

  1458.         }

  1459.         /** {@inheritDoc} */
  1460.         @Override
  1461.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory)
  1462.             throws OrekitException {

  1463.             // constants from IERS 2010, section 6.4
  1464.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1465.             final double ratio        =  0.00115;

  1466.             return new TimeVectorFunction() {

  1467.                 /** {@inheritDoc} */
  1468.                 @Override
  1469.                 public double[] value(final AbsoluteDate date) {

  1470.                     // evaluate wobble variables
  1471.                     final double[] wobbleM = computePoleWobble(date, eopHistory);

  1472.                     return new double[] {
  1473.                         // the following correspond to the equations published in IERS 2010 conventions,
  1474.                         // section 6.4 page 94. The equations read:
  1475.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1476.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1477.                         // These equations seem to fix what was probably a sign error in IERS 2003
  1478.                         // conventions section 6.2 page 65. In this older publication, the equations read:
  1479.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1480.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1481.                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
  1482.                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
  1483.                     };

  1484.                 }

  1485.                 /** {@inheritDoc} */
  1486.                 @Override
  1487.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  1488.                     // evaluate wobble variables
  1489.                     final T[] wobbleM = computePoleWobble(date, eopHistory);

  1490.                     final T[] a = MathArrays.buildArray(date.getField(), 2);

  1491.                     // the following correspond to the equations published in IERS 2010 conventions,
  1492.                     // section 6.4 page 94. The equations read:
  1493.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1494.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1495.                     // These equations seem to fix what was probably a sign error in IERS 2003
  1496.                     // conventions section 6.2 page 65. In this older publication, the equations read:
  1497.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1498.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1499.                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
  1500.                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);

  1501.                     return a;

  1502.                 }

  1503.             };

  1504.         }

  1505.         /** {@inheritDoc} */
  1506.         @Override
  1507.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  1508.             throws OrekitException {

  1509.             return new TimeVectorFunction() {

  1510.                 /** {@inheritDoc} */
  1511.                 @Override
  1512.                 public double[] value(final AbsoluteDate date) {

  1513.                     // evaluate wobble variables
  1514.                     final double[] wobbleM = computePoleWobble(date, eopHistory);

  1515.                     return new double[] {
  1516.                         // the following correspond to the equations published in IERS 2010 conventions,
  1517.                         // section 6.4 page 94 equation 6.24:
  1518.                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1519.                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1520.                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
  1521.                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
  1522.                     };

  1523.                 }

  1524.                 /** {@inheritDoc} */
  1525.                 @Override
  1526.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  1527.                     final T[] v = MathArrays.buildArray(date.getField(), 2);

  1528.                     // evaluate wobble variables
  1529.                     final T[] wobbleM = computePoleWobble(date, eopHistory);

  1530.                     // the following correspond to the equations published in IERS 2010 conventions,
  1531.                     // section 6.4 page 94 equation 6.24:
  1532.                     // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1533.                     // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1534.                     v[0] = wobbleM[0].subtract(wobbleM[1].multiply(0.01724)).multiply(-2.1778e-10 / Constants.ARC_SECONDS_TO_RADIANS);
  1535.                     v[1] = wobbleM[1].subtract(wobbleM[0].multiply(0.03365)).multiply(-1.7232e-10 / Constants.ARC_SECONDS_TO_RADIANS);

  1536.                     return v;

  1537.                 }

  1538.             };

  1539.         }

  1540.         /** {@inheritDoc} */
  1541.         @Override
  1542.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

  1543.             // set up the conventional polynomials
  1544.             // the following values are from equation 5.40 in IERS 2010 conventions
  1545.             final PolynomialNutation psiA =
  1546.                     new PolynomialNutation(   0.0,
  1547.                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1548.                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1549.                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1550.                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1551.                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1552.             final PolynomialNutation omegaA =
  1553.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  1554.                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1555.                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1556.                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1557.                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1558.                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1559.             final PolynomialNutation chiA =
  1560.                     new PolynomialNutation( 0.0,
  1561.                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1562.                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1563.                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1564.                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1565.                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

  1566.             return new PrecessionFunction(psiA, omegaA, chiA);

  1567.         }

  1568.          /** {@inheritDoc} */
  1569.         @Override
  1570.         public TimeVectorFunction getNutationFunction()
  1571.             throws OrekitException {

  1572.             // set up nutation arguments
  1573.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1574.             // set up Poisson series
  1575.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1576.             final PoissonSeriesParser parser =
  1577.                     new PoissonSeriesParser(17).
  1578.                         withFirstDelaunay(4).
  1579.                         withFirstPlanetary(9).
  1580.                         withSinCos(0, 2, microAS, 3, microAS);
  1581.             final PoissonSeries psiSeries     = parser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1582.             final PoissonSeries epsilonSeries = parser.parse(getStream(EPSILON_SERIES), EPSILON_SERIES);
  1583.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  1584.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  1585.             return new TimeVectorFunction() {

  1586.                 /** {@inheritDoc} */
  1587.                 @Override
  1588.                 public double[] value(final AbsoluteDate date) {
  1589.                     final BodiesElements elements = arguments.evaluateAll(date);
  1590.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1591.                     return new double[] {
  1592.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  1593.                     };
  1594.                 }

  1595.                 /** {@inheritDoc} */
  1596.                 @Override
  1597.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1598.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1599.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1600.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1601.                     result[0] = psiEpsilon[0];
  1602.                     result[1] = psiEpsilon[1];
  1603.                     result[2] = IAU1994ResolutionC7.value(elements);
  1604.                     return result;
  1605.                 }

  1606.             };

  1607.         }

  1608.         /** {@inheritDoc} */
  1609.         @Override
  1610.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) throws OrekitException {

  1611.             // Earth Rotation Angle
  1612.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1613.             // Polynomial part of the apparent sidereal time series
  1614.             // which is the opposite of Equation of Origins (EO)
  1615.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1616.             final PoissonSeriesParser parser =
  1617.                     new PoissonSeriesParser(17).
  1618.                         withFirstDelaunay(4).
  1619.                         withFirstPlanetary(9).
  1620.                         withSinCos(0, 2, microAS, 3, microAS).
  1621.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1622.             final PolynomialNutation minusEO =
  1623.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1624.             // create a function evaluating the series
  1625.             return new TimeScalarFunction() {

  1626.                 /** {@inheritDoc} */
  1627.                 @Override
  1628.                 public double value(final AbsoluteDate date) {
  1629.                     return era.value(date) + minusEO.value(evaluateTC(date));
  1630.                 }

  1631.                 /** {@inheritDoc} */
  1632.                 @Override
  1633.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1634.                     return era.value(date).add(minusEO.value(evaluateTC(date)));
  1635.                 }

  1636.             };

  1637.         }

  1638.         /** {@inheritDoc} */
  1639.         @Override
  1640.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) throws OrekitException {

  1641.             // Earth Rotation Angle
  1642.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1643.             // Polynomial part of the apparent sidereal time series
  1644.             // which is the opposite of Equation of Origins (EO)
  1645.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1646.             final PoissonSeriesParser parser =
  1647.                     new PoissonSeriesParser(17).
  1648.                         withFirstDelaunay(4).
  1649.                         withFirstPlanetary(9).
  1650.                         withSinCos(0, 2, microAS, 3, microAS).
  1651.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1652.             final PolynomialNutation minusEO =
  1653.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1654.             // create a function evaluating the series
  1655.             return new TimeScalarFunction() {

  1656.                 /** {@inheritDoc} */
  1657.                 @Override
  1658.                 public double value(final AbsoluteDate date) {
  1659.                     return era.getRate() + minusEO.derivative(evaluateTC(date));
  1660.                 }

  1661.                 /** {@inheritDoc} */
  1662.                 @Override
  1663.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1664.                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
  1665.                 }

  1666.             };

  1667.         }

  1668.         /** {@inheritDoc} */
  1669.         @Override
  1670.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  1671.             throws OrekitException {

  1672.             // set up nutation arguments
  1673.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1674.             // mean obliquity function
  1675.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  1676.             // set up Poisson series
  1677.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1678.             final PoissonSeriesParser baseParser =
  1679.                     new PoissonSeriesParser(17).
  1680.                         withFirstDelaunay(4).
  1681.                         withFirstPlanetary(9).
  1682.                         withSinCos(0, 2, microAS, 3, microAS);
  1683.             final PoissonSeriesParser gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1684.             final PoissonSeries psiSeries        = baseParser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1685.             final PoissonSeries gstSeries        = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  1686.             final PoissonSeries.CompiledSeries psiGstSeries =
  1687.                     PoissonSeries.compile(psiSeries, gstSeries);

  1688.             // ERA function
  1689.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  1690.             return new TimeScalarFunction() {

  1691.                 /** {@inheritDoc} */
  1692.                 @Override
  1693.                 public double value(final AbsoluteDate date) {

  1694.                     // evaluate equation of origins
  1695.                     final BodiesElements elements = arguments.evaluateAll(date);
  1696.                     final double[] angles = psiGstSeries.value(elements);
  1697.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1698.                     final double deltaPsi = angles[0] + ddPsi;
  1699.                     final double epsilonA = epsilon.value(date);

  1700.                     // subtract equation of origin from EA
  1701.                     // (hence add the series above which have the sign included)
  1702.                     return era.value(date) + deltaPsi * FastMath.cos(epsilonA) + angles[1];

  1703.                 }

  1704.                 /** {@inheritDoc} */
  1705.                 @Override
  1706.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1707.                     // evaluate equation of origins
  1708.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1709.                     final T[] angles = psiGstSeries.value(elements);
  1710.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1711.                     final T deltaPsi = angles[0].add(ddPsi);
  1712.                     final T epsilonA = epsilon.value(date);

  1713.                     // subtract equation of origin from EA
  1714.                     // (hence add the series above which have the sign included)
  1715.                     return era.value(date).add(deltaPsi.multiply(epsilonA.cos())).add(angles[1]);

  1716.                 }

  1717.             };

  1718.         }

  1719.         /** {@inheritDoc} */
  1720.         @Override
  1721.         public TimeVectorFunction getEOPTidalCorrection()
  1722.             throws OrekitException {

  1723.             // set up nutation arguments
  1724.             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
  1725.             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
  1726.             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
  1727.             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
  1728.             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());

  1729.             // set up Poisson series
  1730.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1731.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  1732.                     withOptionalColumn(1).
  1733.                     withGamma(2).
  1734.                     withFirstDelaunay(3);
  1735.             final PoissonSeries xSeries =
  1736.                     xyParser.
  1737.                     withSinCos(0, 10, microAS, 11, microAS).
  1738.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  1739.             final PoissonSeries ySeries =
  1740.                     xyParser.
  1741.                     withSinCos(0, 12, microAS, 13, microAS).
  1742.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  1743.             final double microS = 1.0e-6;
  1744.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1745.                     withOptionalColumn(1).
  1746.                     withGamma(2).
  1747.                     withFirstDelaunay(3).
  1748.                     withSinCos(0, 10, microS, 11, microS);
  1749.             final PoissonSeries ut1Series =
  1750.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  1751.             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);

  1752.         }

  1753.         /** {@inheritDoc} */
  1754.         @Override
  1755.         public double[] getNominalTidalDisplacement() {
  1756.             return new double[] {
  1757.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1758.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1759.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1760.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1761.                 // H₀
  1762.                 -0.31460
  1763.             };
  1764.         }

  1765.         /** {@inheritDoc} */
  1766.         @Override
  1767.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal()
  1768.             throws OrekitException {
  1769.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1770.                                                                   18, 15, 16, 17, 18);
  1771.         }

  1772.         /** {@inheritDoc} */
  1773.         @Override
  1774.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal()
  1775.             throws OrekitException {
  1776.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1777.                                                                 18, 15, 16, 17, 18);
  1778.         }

  1779.     };

  1780.     /** IERS conventions resources base directory. */
  1781.     private static final String IERS_BASE = "/assets/org/orekit/IERS-conventions/";

  1782.     /** Get the reference epoch for fundamental nutation arguments.
  1783.      * @return reference epoch for fundamental nutation arguments
  1784.      * @since 6.1
  1785.      */
  1786.     public AbsoluteDate getNutationReferenceEpoch() {
  1787.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1788.         return AbsoluteDate.J2000_EPOCH;
  1789.     }

  1790.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1791.      * @param date current date
  1792.      * @return date offset in Julian centuries
  1793.      * @since 6.1
  1794.      */
  1795.     public double evaluateTC(final AbsoluteDate date) {
  1796.         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
  1797.     }

  1798.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1799.      * @param date current date
  1800.      * @param <T> type of the field elements
  1801.      * @return date offset in Julian centuries
  1802.      * @since 9.0
  1803.      */
  1804.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
  1805.         return date.durationFrom(getNutationReferenceEpoch()).divide(Constants.JULIAN_CENTURY);
  1806.     }

  1807.     /** Get the fundamental nutation arguments.
  1808.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  1809.      * (typically {@link TimeScalesFactory#getUT1(IERSConventions, boolean) UT1})
  1810.      * @return fundamental nutation arguments
  1811.      * @exception OrekitException if fundamental nutation arguments cannot be loaded
  1812.      * @since 6.1
  1813.      */
  1814.     public abstract FundamentalNutationArguments getNutationArguments(TimeScale timeScale)
  1815.         throws OrekitException;

  1816.     /** Get the function computing mean obliquity of the ecliptic.
  1817.      * @return function computing mean obliquity of the ecliptic
  1818.      * @exception OrekitException if table cannot be loaded
  1819.      * @since 6.1
  1820.      */
  1821.     public abstract TimeScalarFunction getMeanObliquityFunction() throws OrekitException;

  1822.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  1823.      * <p>
  1824.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  1825.      * </p>
  1826.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  1827.      * @exception OrekitException if table cannot be loaded
  1828.      * @since 6.1
  1829.      */
  1830.     public abstract TimeVectorFunction getXYSpXY2Function()
  1831.         throws OrekitException;

  1832.     /** Get the function computing the raw Earth Orientation Angle.
  1833.      * <p>
  1834.      * The raw angle does not contain any correction. If for example dTU1 correction
  1835.      * due to tidal effect is desired, it must be added afterward by the caller.
  1836.      * The returned value contain the angle as the value and the angular rate as
  1837.      * the first derivative.
  1838.      * </p>
  1839.      * @param ut1 UT1 time scale
  1840.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  1841.      * @since 6.1
  1842.      */
  1843.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
  1844.         return new StellarAngleCapitaine(ut1);
  1845.     }


  1846.     /** Get the function computing the precession angles.
  1847.      * <p>
  1848.      * The function returned computes the three precession angles
  1849.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  1850.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  1851.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  1852.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  1853.      * #getNutationReferenceEpoch() nutation reference epoch}.
  1854.      * </p>
  1855.      * @return function computing the precession angle
  1856.      * @exception OrekitException if table cannot be loaded
  1857.      * @since 6.1
  1858.      */
  1859.     public abstract TimeVectorFunction getPrecessionFunction() throws OrekitException;

  1860.     /** Get the function computing the nutation angles.
  1861.      * <p>
  1862.      * The function returned computes the two classical angles ΔΨ and Δε,
  1863.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  1864.      * resolution C7 (the correction is forced to 0 before this date)
  1865.      * </p>
  1866.      * @return function computing the nutation in longitude ΔΨ and Δε
  1867.      * and the correction of equation of equinoxes
  1868.      * @exception OrekitException if table cannot be loaded
  1869.      * @since 6.1
  1870.      */
  1871.     public abstract TimeVectorFunction getNutationFunction()
  1872.         throws OrekitException;

  1873.     /** Get the function computing Greenwich mean sidereal time, in radians.
  1874.      * @param ut1 UT1 time scale
  1875.      * @return function computing Greenwich mean sidereal time
  1876.      * @exception OrekitException if table cannot be loaded
  1877.      * @since 6.1
  1878.      */
  1879.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1)
  1880.         throws OrekitException;

  1881.     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
  1882.      * @param ut1 UT1 time scale
  1883.      * @return function computing Greenwich mean sidereal time rate
  1884.      * @exception OrekitException if table cannot be loaded
  1885.      * @since 9.0
  1886.      */
  1887.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1)
  1888.         throws OrekitException;

  1889.     /** Get the function computing Greenwich apparent sidereal time, in radians.
  1890.      * @param ut1 UT1 time scale
  1891.      * @param eopHistory EOP history
  1892.      * @return function computing Greenwich apparent sidereal time
  1893.      * @exception OrekitException if table cannot be loaded
  1894.      * @since 6.1
  1895.      */
  1896.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1, EOPHistory eopHistory)
  1897.         throws OrekitException;

  1898.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  1899.      * @return function computing tidal corrections for Earth Orientation Parameters,
  1900.      * for xp, yp, ut1 and lod respectively
  1901.      * @exception OrekitException if table cannot be loaded
  1902.      * @since 6.1
  1903.      */
  1904.     public abstract TimeVectorFunction getEOPTidalCorrection()
  1905.         throws OrekitException;

  1906.     /** Get the Love numbers.
  1907.      * @return Love numbers
  1908.      * @exception OrekitException if table cannot be loaded
  1909.      * @since 6.1
  1910.      */
  1911.     public abstract LoveNumbers getLoveNumbers()
  1912.         throws OrekitException;

  1913.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1914.      * @param ut1 UT1 time scale
  1915.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1916.      * @exception OrekitException if table cannot be loaded
  1917.      * @since 6.1
  1918.      */
  1919.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(TimeScale ut1)
  1920.         throws OrekitException;

  1921.     /** Get the permanent tide to be <em>removed</em> from ΔC₂₀ when zero-tide potentials are used.
  1922.      * @return permanent tide to remove
  1923.      * @exception OrekitException if table cannot be loaded
  1924.      */
  1925.     public abstract double getPermanentTide() throws OrekitException;

  1926.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  1927.      * @param eopHistory EOP history
  1928.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1929.      * @exception OrekitException if table cannot be loaded
  1930.      * @since 6.1
  1931.      */
  1932.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory)
  1933.         throws OrekitException;

  1934.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  1935.      * @param eopHistory EOP history
  1936.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1937.      * @exception OrekitException if table cannot be loaded
  1938.      * @since 6.1
  1939.      */
  1940.     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory)
  1941.         throws OrekitException;

  1942.     /** Get the nominal values of the displacement numbers.
  1943.      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
  1944.      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
  1945.      * H₀ permanent deformation amplitude
  1946.      * @since 9.1
  1947.      */
  1948.     public abstract double[] getNominalTidalDisplacement();

  1949.     /** Get the correction function for tidal displacement for diurnal tides.
  1950.      * <ul>
  1951.      *  <li>f[0]: radial correction, longitude cosine part</li>
  1952.      *  <li>f[1]: radial correction, longitude sine part</li>
  1953.      *  <li>f[2]: North correction, longitude cosine part</li>
  1954.      *  <li>f[3]: North correction, longitude sine part</li>
  1955.      *  <li>f[4]: East correction, longitude cosine part</li>
  1956.      *  <li>f[5]: East correction, longitude sine part</li>
  1957.      * </ul>
  1958.      * @return correction function for tidal displacement
  1959.      * @throws OrekitException if Poisson series cannot be loaded
  1960.      * @since 9.1
  1961.      */
  1962.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal()
  1963.         throws OrekitException;

  1964.     /** Get the correction function for tidal displacement for diurnal tides.
  1965.      * <ul>
  1966.      *  <li>f[0]: radial correction, longitude cosine part</li>
  1967.      *  <li>f[1]: radial correction, longitude sine part</li>
  1968.      *  <li>f[2]: North correction, longitude cosine part</li>
  1969.      *  <li>f[3]: North correction, longitude sine part</li>
  1970.      *  <li>f[4]: East correction, longitude cosine part</li>
  1971.      *  <li>f[5]: East correction, longitude sine part</li>
  1972.      * </ul>
  1973.      * @param tableName name for the diurnal tides table
  1974.      * @param cols total number of columns of the diurnal tides table
  1975.      * @param rIp column holding ∆Rf(ip) in the diurnal tides table, counting from 1
  1976.      * @param rOp column holding ∆Rf(op) in the diurnal tides table, counting from 1
  1977.      * @param tIp column holding ∆Tf(ip) in the diurnal tides table, counting from 1
  1978.      * @param tOp column holding ∆Tf(op) in the diurnal tides table, counting from 1
  1979.      * @return correction function for tidal displacement for diurnal tides
  1980.      * @exception OrekitException if Poisson series cannot be loaded
  1981.      * @since 9.1
  1982.      */
  1983.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal(final String tableName, final int cols,
  1984.                                                                                    final int rIp, final int rOp,
  1985.                                                                                    final int tIp, final int tOp)
  1986.         throws OrekitException {

  1987.         // radial component, missing the sin 2φ factor; this corresponds to:
  1988.         //  - equation 15a in IERS conventions 1996, chapter 7
  1989.         //  - equation 16a in IERS conventions 2003, chapter 7
  1990.         //  - equation 7.12a in IERS conventions 2010, chapter 7
  1991.         final PoissonSeries drCos = new PoissonSeriesParser(cols).
  1992.                                     withOptionalColumn(1).
  1993.                                     withDoodson(4, 3).
  1994.                                     withFirstDelaunay(10).
  1995.                                     withSinCos(0, rIp, +1.0e-3, rOp, +1.0e-3).
  1996.                                     parse(getStream(tableName), tableName);
  1997.         final PoissonSeries drSin = new PoissonSeriesParser(cols).
  1998.                                     withOptionalColumn(1).
  1999.                                     withDoodson(4, 3).
  2000.                                     withFirstDelaunay(10).
  2001.                                     withSinCos(0, rOp, -1.0e-3, rIp, +1.0e-3).
  2002.                                     parse(getStream(tableName), tableName);

  2003.         // North component, missing the cos 2φ factor; this corresponds to:
  2004.         //  - equation 15b in IERS conventions 1996, chapter 7
  2005.         //  - equation 16b in IERS conventions 2003, chapter 7
  2006.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  2007.         final PoissonSeries dnCos = new PoissonSeriesParser(cols).
  2008.                                     withOptionalColumn(1).
  2009.                                     withDoodson(4, 3).
  2010.                                     withFirstDelaunay(10).
  2011.                                     withSinCos(0, tIp, +1.0e-3, tOp, +1.0e-3).
  2012.                                     parse(getStream(tableName), tableName);
  2013.         final PoissonSeries dnSin = new PoissonSeriesParser(cols).
  2014.                                     withOptionalColumn(1).
  2015.                                     withDoodson(4, 3).
  2016.                                     withFirstDelaunay(10).
  2017.                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2018.                                     parse(getStream(tableName), tableName);

  2019.         // East component, missing the sin φ factor; this corresponds to:
  2020.         //  - equation 15b in IERS conventions 1996, chapter 7
  2021.         //  - equation 16b in IERS conventions 2003, chapter 7
  2022.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  2023.         final PoissonSeries deCos = new PoissonSeriesParser(cols).
  2024.                                     withOptionalColumn(1).
  2025.                                     withDoodson(4, 3).
  2026.                                     withFirstDelaunay(10).
  2027.                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2028.                                     parse(getStream(tableName), tableName);
  2029.         final PoissonSeries deSin = new PoissonSeriesParser(cols).
  2030.                                     withOptionalColumn(1).
  2031.                                     withDoodson(4, 3).
  2032.                                     withFirstDelaunay(10).
  2033.                                     withSinCos(0, tIp, -1.0e-3, tOp, -1.0e-3).
  2034.                                     parse(getStream(tableName), tableName);

  2035.         return PoissonSeries.compile(drCos, drSin,
  2036.                                      dnCos, dnSin,
  2037.                                      deCos, deSin);

  2038.     }

  2039.     /** Get the correction function for tidal displacement for zonal tides.
  2040.      * <ul>
  2041.      *  <li>f[0]: radial correction</li>
  2042.      *  <li>f[1]: North correction</li>
  2043.      * </ul>
  2044.      * @return correction function for tidal displacement
  2045.      * @throws OrekitException if Poisson series cannot be loaded
  2046.      * @since 9.1
  2047.      */
  2048.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal()
  2049.         throws OrekitException;

  2050.     /** Get the correction function for tidal displacement for zonal tides.
  2051.      * <ul>
  2052.      *  <li>f[0]: radial correction</li>
  2053.      *  <li>f[1]: North correction</li>
  2054.      * </ul>
  2055.      * @param tableName name for the zonal tides table
  2056.      * @param cols total number of columns of the table
  2057.      * @param rIp column holding ∆Rf(ip) in the table, counting from 1
  2058.      * @param rOp column holding ∆Rf(op) in the table, counting from 1
  2059.      * @param tIp column holding ∆Tf(ip) in the table, counting from 1
  2060.      * @param tOp column holding ∆Tf(op) in the table, counting from 1
  2061.      * @return correction function for tidal displacement for zonal tides
  2062.      * @exception OrekitException if Poisson series cannot be loaded
  2063.      * @since 9.1
  2064.      */
  2065.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionZonal(final String tableName, final int cols,
  2066.                                                                                  final int rIp, final int rOp,
  2067.                                                                                  final int tIp, final int tOp)
  2068.         throws OrekitException {

  2069.         // radial component, missing the 3⁄2 sin² φ - 1⁄2 factor; this corresponds to:
  2070.         //  - equation 16a in IERS conventions 1996, chapter 7
  2071.         //  - equation 17a in IERS conventions 2003, chapter 7
  2072.         //  - equation 7.13a in IERS conventions 2010, chapter 7
  2073.         final PoissonSeries dr = new PoissonSeriesParser(cols).
  2074.                                  withOptionalColumn(1).
  2075.                                  withDoodson(4, 3).
  2076.                                  withFirstDelaunay(10).
  2077.                                  withSinCos(0, rOp, +1.0e-3, rIp, +1.0e-3).
  2078.                                  parse(getStream(tableName), tableName);

  2079.         // North component, missing the sin 2φ factor; this corresponds to:
  2080.         //  - equation 16b in IERS conventions 1996, chapter 7
  2081.         //  - equation 17b in IERS conventions 2003, chapter 7
  2082.         //  - equation 7.13b in IERS conventions 2010, chapter 7
  2083.         final PoissonSeries dn = new PoissonSeriesParser(cols).
  2084.                                  withOptionalColumn(1).
  2085.                                  withDoodson(4, 3).
  2086.                                  withFirstDelaunay(10).
  2087.                                  withSinCos(0, tOp, +1.0e-3, tIp, +1.0e-3).
  2088.                                  parse(getStream(tableName), tableName);

  2089.         return PoissonSeries.compile(dr, dn);

  2090.     }

  2091.     /** Interface for functions converting nutation corrections between
  2092.      * δΔψ/δΔε to δX/δY.
  2093.      * <ul>
  2094.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2095.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2096.      * </ul>
  2097.      * @since 6.1
  2098.      */
  2099.     public interface NutationCorrectionConverter {

  2100.         /** Convert nutation corrections.
  2101.          * @param date current date
  2102.          * @param ddPsi δΔψ part of the nutation correction
  2103.          * @param ddEpsilon δΔε part of the nutation correction
  2104.          * @return array containing δX and δY
  2105.          * @exception OrekitException if correction cannot be converted
  2106.          */
  2107.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon)
  2108.             throws OrekitException;

  2109.         /** Convert nutation corrections.
  2110.          * @param date current date
  2111.          * @param dX δX part of the nutation correction
  2112.          * @param dY δY part of the nutation correction
  2113.          * @return array containing δΔψ and δΔε
  2114.          * @exception OrekitException if correction cannot be converted
  2115.          */
  2116.         double[] toEquinox(AbsoluteDate date, double dX, double dY)
  2117.             throws OrekitException;

  2118.     }

  2119.     /** Create a function converting nutation corrections between
  2120.      * δX/δY and δΔψ/δΔε.
  2121.      * <ul>
  2122.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2123.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2124.      * </ul>
  2125.      * @return a new converter
  2126.      * @exception OrekitException if some convention table cannot be loaded
  2127.      * @since 6.1
  2128.      */
  2129.     public NutationCorrectionConverter getNutationCorrectionConverter()
  2130.         throws OrekitException {

  2131.         // get models parameters
  2132.         final TimeVectorFunction precessionFunction = getPrecessionFunction();
  2133.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction();
  2134.         final AbsoluteDate date0 = getNutationReferenceEpoch();
  2135.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  2136.         return new NutationCorrectionConverter() {

  2137.             /** {@inheritDoc} */
  2138.             @Override
  2139.             public double[] toNonRotating(final AbsoluteDate date,
  2140.                                           final double ddPsi, final double ddEpsilon)
  2141.                 throws OrekitException {
  2142.                 // compute precession angles psiA, omegaA and chiA
  2143.                 final double[] angles = precessionFunction.value(date);

  2144.                 // conversion coefficients
  2145.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2146.                 final double c     = angles[0] * cosE0 - angles[2];

  2147.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  2148.                 return new double[] {
  2149.                     sinEA * ddPsi + c * ddEpsilon,
  2150.                     ddEpsilon - c * sinEA * ddPsi
  2151.                 };

  2152.             }

  2153.             /** {@inheritDoc} */
  2154.             @Override
  2155.             public double[] toEquinox(final AbsoluteDate date,
  2156.                                       final double dX, final double dY)
  2157.                 throws OrekitException {
  2158.                 // compute precession angles psiA, omegaA and chiA
  2159.                 final double[] angles   = precessionFunction.value(date);

  2160.                 // conversion coefficients
  2161.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2162.                 final double c     = angles[0] * cosE0 - angles[2];
  2163.                 final double opC2  = 1 + c * c;

  2164.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  2165.                 return new double[] {
  2166.                     (dX - c * dY) / (sinEA * opC2),
  2167.                     (dY + c * dX) / opC2
  2168.                 };
  2169.             }

  2170.         };

  2171.     }

  2172.     /** Load the Love numbers.
  2173.      * @param nameLove name of the Love number resource
  2174.      * @return Love numbers
  2175.      * @exception OrekitException if the Love numbers embedded in the
  2176.      * library cannot be read
  2177.      */
  2178.     protected LoveNumbers loadLoveNumbers(final String nameLove) throws OrekitException {
  2179.         try {

  2180.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  2181.             final double[][] real      = new double[4][];
  2182.             final double[][] imaginary = new double[4][];
  2183.             final double[][] plus      = new double[4][];
  2184.             for (int i = 0; i < real.length; ++i) {
  2185.                 real[i]      = new double[i + 1];
  2186.                 imaginary[i] = new double[i + 1];
  2187.                 plus[i]      = new double[i + 1];
  2188.             }

  2189.             try (InputStream stream = IERSConventions.class.getResourceAsStream(nameLove)) {

  2190.                 if (stream == null) {
  2191.                     // this should never happen with files embedded within Orekit
  2192.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  2193.                 }

  2194.                 // setup the reader
  2195.                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"))) {

  2196.                     String line = reader.readLine();
  2197.                     int lineNumber = 1;

  2198.                     // look for the Love numbers
  2199.                     while (line != null) {

  2200.                         line = line.trim();
  2201.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  2202.                             final String[] fields = line.split("\\p{Space}+");
  2203.                             if (fields.length != 5) {
  2204.                                 // this should never happen with files embedded within Orekit
  2205.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2206.                                                           lineNumber, nameLove, line);
  2207.                             }
  2208.                             final int n = Integer.parseInt(fields[0]);
  2209.                             final int m = Integer.parseInt(fields[1]);
  2210.                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  2211.                                 // this should never happen with files embedded within Orekit
  2212.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2213.                                                           lineNumber, nameLove, line);

  2214.                             }
  2215.                             real[n][m]      = Double.parseDouble(fields[2]);
  2216.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  2217.                             plus[n][m]      = Double.parseDouble(fields[4]);
  2218.                         }

  2219.                         // next line
  2220.                         lineNumber++;
  2221.                         line = reader.readLine();

  2222.                     }
  2223.                 }
  2224.             }

  2225.             return new LoveNumbers(real, imaginary, plus);

  2226.         } catch (IOException ioe) {
  2227.             // this should never happen with files embedded within Orekit
  2228.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  2229.         }
  2230.     }

  2231.     /** Get a data stream.
  2232.      * @param name file name of the resource stream
  2233.      * @return stream
  2234.      */
  2235.     private static InputStream getStream(final String name) {
  2236.         return IERSConventions.class.getResourceAsStream(name);
  2237.     }

  2238.     /** Correction to equation of equinoxes.
  2239.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2240.      * taking effect since 1997-02-27 for continuity
  2241.      * </p>
  2242.      */
  2243.     private static class IAU1994ResolutionC7 {

  2244.         /** First Moon correction term for the Equation of the Equinoxes. */
  2245.         private static final double EQE1 =     0.00264  * Constants.ARC_SECONDS_TO_RADIANS;

  2246.         /** Second Moon correction term for the Equation of the Equinoxes. */
  2247.         private static final double EQE2 =     0.000063 * Constants.ARC_SECONDS_TO_RADIANS;

  2248.         /** Start date for applying Moon corrections to the equation of the equinoxes.
  2249.          * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
  2250.          */
  2251.         private static final AbsoluteDate MODEL_START =
  2252.             new AbsoluteDate(1997, 2, 27, 0, 0, 30, TimeScalesFactory.getTAI());

  2253.         /** Evaluate the correction.
  2254.          * @param arguments Delaunay for nutation
  2255.          * @return correction value (0 before 1997-02-27)
  2256.          */
  2257.         public static double value(final DelaunayArguments arguments) {
  2258.             if (arguments.getDate().compareTo(MODEL_START) >= 0) {

  2259.                 // IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2260.                 // taking effect since 1997-02-27 for continuity

  2261.                 // Mean longitude of the ascending node of the Moon
  2262.                 final double om = arguments.getOmega();

  2263.                 // add the two correction terms
  2264.                 return EQE1 * FastMath.sin(om) + EQE2 * FastMath.sin(om + om);

  2265.             } else {
  2266.                 return 0.0;
  2267.             }
  2268.         }

  2269.         /** Evaluate the correction.
  2270.          * @param arguments Delaunay for nutation
  2271.          * @param <T> type of the field elements
  2272.          * @return correction value (0 before 1997-02-27)
  2273.          */
  2274.         public static <T extends RealFieldElement<T>> T value(final FieldDelaunayArguments<T> arguments) {
  2275.             if (arguments.getDate().toAbsoluteDate().compareTo(MODEL_START) >= 0) {

  2276.                 // IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2277.                 // taking effect since 1997-02-27 for continuity

  2278.                 // Mean longitude of the ascending node of the Moon
  2279.                 final T om = arguments.getOmega();

  2280.                 // add the two correction terms
  2281.                 return om.sin().multiply(EQE1).add(om.add(om).sin().multiply(EQE2));

  2282.             } else {
  2283.                 return arguments.getDate().getField().getZero();
  2284.             }
  2285.         }

  2286.     };

  2287.     /** Stellar angle model.
  2288.      * <p>
  2289.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  2290.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  2291.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  2292.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  2293.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  2294.      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
  2295.      * </p>
  2296.      * <p>
  2297.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  2298.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  2299.      * IERS conventions 2003 and 2010.
  2300.      * </p>
  2301.      */
  2302.     private static class StellarAngleCapitaine implements TimeScalarFunction {

  2303.         /** Reference date of Capitaine's Earth Rotation Angle model. */
  2304.         private static final AbsoluteDate REFERENCE_DATE = new AbsoluteDate(DateComponents.J2000_EPOCH,
  2305.                                                                             TimeComponents.H12,
  2306.                                                                             TimeScalesFactory.getTAI());

  2307.         /** Constant term of Capitaine's Earth Rotation Angle model. */
  2308.         private static final double ERA_0   = MathUtils.TWO_PI * 0.7790572732640;

  2309.         /** Rate term of Capitaine's Earth Rotation Angle model.
  2310.          * (radians per day, main part) */
  2311.         private static final double ERA_1A  = MathUtils.TWO_PI / Constants.JULIAN_DAY;

  2312.         /** Rate term of Capitaine's Earth Rotation Angle model.
  2313.          * (radians per day, fractional part) */
  2314.         private static final double ERA_1B  = ERA_1A * 0.00273781191135448;

  2315.         /** UT1 time scale. */
  2316.         private final TimeScale ut1;

  2317.         /** Simple constructor.
  2318.          * @param ut1 UT1 time scale
  2319.          */
  2320.         StellarAngleCapitaine(final TimeScale ut1) {
  2321.             this.ut1 = ut1;
  2322.         }

  2323.         /** Get the rotation rate.
  2324.          * @return rotation rate
  2325.          */
  2326.         public double getRate() {
  2327.             return ERA_1A + ERA_1B;
  2328.         }

  2329.         /** {@inheritDoc} */
  2330.         @Override
  2331.         public double value(final AbsoluteDate date) {

  2332.             // split the date offset as a full number of days plus a smaller part
  2333.             final int secondsInDay = 86400;
  2334.             final double dt  = date.durationFrom(REFERENCE_DATE);
  2335.             final long days  = ((long) dt) / secondsInDay;
  2336.             final double dtA = secondsInDay * days;
  2337.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

  2338.             return ERA_0 + ERA_1A * dtB + ERA_1B * (dtA + dtB);

  2339.         }

  2340.         /** {@inheritDoc} */
  2341.         @Override
  2342.         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2343.             // split the date offset as a full number of days plus a smaller part
  2344.             final int secondsInDay = 86400;
  2345.             final T dt  = date.durationFrom(REFERENCE_DATE);
  2346.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2347.             final double dtA = secondsInDay * days;
  2348.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));

  2349.             return dtB.add(dtA).multiply(ERA_1B).add(dtB.multiply(ERA_1A)).add(ERA_0);

  2350.         }

  2351.     }

  2352.     /** Mean pole. */
  2353.     private static class MeanPole implements TimeStamped, Serializable {

  2354.         /** Serializable UID. */
  2355.         private static final long serialVersionUID = 20131028l;

  2356.         /** Date. */
  2357.         private final AbsoluteDate date;

  2358.         /** X coordinate. */
  2359.         private double x;

  2360.         /** Y coordinate. */
  2361.         private double y;

  2362.         /** Simple constructor.
  2363.          * @param date date
  2364.          * @param x x coordinate
  2365.          * @param y y coordinate
  2366.          */
  2367.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2368.             this.date = date;
  2369.             this.x    = x;
  2370.             this.y    = y;
  2371.         }

  2372.         /** {@inheritDoc} */
  2373.         @Override
  2374.         public AbsoluteDate getDate() {
  2375.             return date;
  2376.         }

  2377.         /** Get x coordinate.
  2378.          * @return x coordinate
  2379.          */
  2380.         public double getX() {
  2381.             return x;
  2382.         }

  2383.         /** Get y coordinate.
  2384.          * @return y coordinate
  2385.          */
  2386.         public double getY() {
  2387.             return y;
  2388.         }

  2389.     }

  2390.     /** Local class for precession function. */
  2391.     private class PrecessionFunction implements TimeVectorFunction {

  2392.         /** Polynomial nutation for psiA. */
  2393.         private final PolynomialNutation psiA;

  2394.         /** Polynomial nutation for omegaA. */
  2395.         private final PolynomialNutation omegaA;

  2396.         /** Polynomial nutation for chiA. */
  2397.         private final PolynomialNutation chiA;

  2398.         /** Simple constructor.
  2399.          * @param psiA polynomial nutation for psiA
  2400.          * @param omegaA polynomial nutation for omegaA
  2401.          * @param chiA polynomial nutation for chiA
  2402.          */
  2403.         PrecessionFunction(final PolynomialNutation psiA,
  2404.                            final PolynomialNutation omegaA,
  2405.                            final PolynomialNutation chiA) {
  2406.             this.psiA   = psiA;
  2407.             this.omegaA = omegaA;
  2408.             this.chiA   = chiA;
  2409.         }


  2410.         /** {@inheritDoc} */
  2411.         @Override
  2412.         public double[] value(final AbsoluteDate date) {
  2413.             final double tc = evaluateTC(date);
  2414.             return new double[] {
  2415.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2416.             };
  2417.         }

  2418.         /** {@inheritDoc} */
  2419.         @Override
  2420.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2421.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2422.             final T tc = evaluateTC(date);
  2423.             a[0] = psiA.value(tc);
  2424.             a[1] = omegaA.value(tc);
  2425.             a[2] = chiA.value(tc);
  2426.             return a;
  2427.         }

  2428.     }

  2429.     /** Local class for tides frequency function. */
  2430.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2431.         /** Nutation arguments. */
  2432.         private final FundamentalNutationArguments arguments;

  2433.         /** Correction series. */
  2434.         private final PoissonSeries.CompiledSeries kSeries;

  2435.         /** Simple constructor.
  2436.          * @param arguments nutation arguments
  2437.          * @param c20Series correction series for the C20 term
  2438.          * @param c21Series correction series for the C21 term
  2439.          * @param s21Series correction series for the S21 term
  2440.          * @param c22Series correction series for the C22 term
  2441.          * @param s22Series correction series for the S22 term
  2442.          */
  2443.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2444.                                         final PoissonSeries c20Series,
  2445.                                         final PoissonSeries c21Series,
  2446.                                         final PoissonSeries s21Series,
  2447.                                         final PoissonSeries c22Series,
  2448.                                         final PoissonSeries s22Series) {
  2449.             this.arguments = arguments;
  2450.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2451.         }


  2452.         /** {@inheritDoc} */
  2453.         @Override
  2454.         public double[] value(final AbsoluteDate date) {
  2455.             return kSeries.value(arguments.evaluateAll(date));
  2456.         }

  2457.         /** {@inheritDoc} */
  2458.         @Override
  2459.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2460.             return kSeries.value(arguments.evaluateAll(date));
  2461.         }

  2462.     }

  2463.     /** Local class for EOP tidal corrections. */
  2464.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2465.         /** Nutation arguments. */
  2466.         private final FundamentalNutationArguments arguments;

  2467.         /** Correction series. */
  2468.         private final PoissonSeries.CompiledSeries correctionSeries;

  2469.         /** Simple constructor.
  2470.          * @param arguments nutation arguments
  2471.          * @param xSeries correction series for the x coordinate
  2472.          * @param ySeries correction series for the y coordinate
  2473.          * @param ut1Series correction series for the UT1
  2474.          */
  2475.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2476.                            final PoissonSeries xSeries,
  2477.                            final PoissonSeries ySeries,
  2478.                            final PoissonSeries ut1Series) {
  2479.             this.arguments        = arguments;
  2480.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2481.         }

  2482.         /** {@inheritDoc} */
  2483.         @Override
  2484.         public double[] value(final AbsoluteDate date) {
  2485.             final BodiesElements elements = arguments.evaluateAll(date);
  2486.             final double[] correction    = correctionSeries.value(elements);
  2487.             final double[] correctionDot = correctionSeries.derivative(elements);
  2488.             return new double[] {
  2489.                 correction[0],
  2490.                 correction[1],
  2491.                 correction[2],
  2492.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2493.             };
  2494.         }

  2495.         /** {@inheritDoc} */
  2496.         @Override
  2497.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2498.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2499.             final T[] correction    = correctionSeries.value(elements);
  2500.             final T[] correctionDot = correctionSeries.derivative(elements);
  2501.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2502.             a[0] = correction[0];
  2503.             a[1] = correction[1];
  2504.             a[2] = correction[2];
  2505.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2506.             return a;
  2507.         }

  2508.     }

  2509. }