IERSConventions.java

  1. /* Copyright 2002-2017 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.PoissonSeriesParser;
  37. import org.orekit.data.PolynomialNutation;
  38. import org.orekit.data.PolynomialParser;
  39. import org.orekit.data.PolynomialParser.Unit;
  40. import org.orekit.data.SimpleTimeStampedTableParser;
  41. import org.orekit.errors.OrekitException;
  42. import org.orekit.errors.OrekitInternalError;
  43. import org.orekit.errors.OrekitMessages;
  44. import org.orekit.errors.TimeStampedCacheException;
  45. import org.orekit.frames.EOPHistory;
  46. import org.orekit.frames.FieldPoleCorrection;
  47. import org.orekit.frames.PoleCorrection;
  48. import org.orekit.time.AbsoluteDate;
  49. import org.orekit.time.DateComponents;
  50. import org.orekit.time.FieldAbsoluteDate;
  51. import org.orekit.time.TimeComponents;
  52. import org.orekit.time.TimeScalarFunction;
  53. import org.orekit.time.TimeScale;
  54. import org.orekit.time.TimeScalesFactory;
  55. import org.orekit.time.TimeStamped;
  56. import org.orekit.time.TimeVectorFunction;


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

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

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

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

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

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

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

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

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

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

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

  82.         /** {@inheritDoc} */
  83.         @Override
  84.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  85.             throws OrekitException {
  86.             return new FundamentalNutationArguments(this, timeScale,
  87.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  88.         }

  89.         /** {@inheritDoc} */
  90.         @Override
  91.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  92.             // value from chapter 5, page 22
  93.             final PolynomialNutation epsilonA =
  94.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  95.                                              -46.8150   * Constants.ARC_SECONDS_TO_RADIANS,
  96.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  97.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  98.             return new TimeScalarFunction() {

  99.                 /** {@inheritDoc} */
  100.                 @Override
  101.                 public double value(final AbsoluteDate date) {
  102.                     return epsilonA.value(evaluateTC(date));
  103.                 }

  104.                 /** {@inheritDoc} */
  105.                 @Override
  106.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  107.                     return epsilonA.value(evaluateTC(date));
  108.                 }

  109.             };

  110.         }

  111.         /** {@inheritDoc} */
  112.         @Override
  113.         public TimeVectorFunction getXYSpXY2Function()
  114.             throws OrekitException {

  115.             // set up nutation arguments
  116.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

  126.             final double fXCosOm    = 0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  127.             final double fXSinOm    = 0.00204 * Constants.ARC_SECONDS_TO_RADIANS;
  128.             final double fXSin2FDOm = 0.00016 * Constants.ARC_SECONDS_TO_RADIANS;
  129.             final double sinEps0   = FastMath.sin(getMeanObliquityFunction().value(getNutationReferenceEpoch()));

  130.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  131.             final PoissonSeriesParser baseParser =
  132.                     new PoissonSeriesParser(12).withFirstDelaunay(1);

  133.             final PoissonSeriesParser xParser =
  134.                     baseParser.
  135.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  136.                     withSinCos(1, 8, deciMilliAS,  9, deciMilliAS);
  137.             final PoissonSeries xSum = xParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

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

  147.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  148.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

  149.             final PoissonSeriesParser yParser =
  150.                     baseParser.
  151.                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
  152.                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
  153.             final PoissonSeries ySum = yParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

  154.             final PoissonSeries.CompiledSeries xySum =
  155.                     PoissonSeries.compile(xSum, ySum);

  156.             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
  157.             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
  158.             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
  159.             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
  160.             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
  161.             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  162.             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
  163.             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;

  164.             return new TimeVectorFunction() {

  165.                 /** {@inheritDoc} */
  166.                 @Override
  167.                 public double[] value(final AbsoluteDate date) {

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

  170.                     final double omega     = elements.getOmega();
  171.                     final double f         = elements.getF();
  172.                     final double d         = elements.getD();
  173.                     final double t         = elements.getTC();

  174.                     final double cosOmega  = FastMath.cos(omega);
  175.                     final double sinOmega  = FastMath.sin(omega);
  176.                     final double sin2Omega = FastMath.sin(2 * omega);
  177.                     final double cos2FDOm  = FastMath.cos(2 * (f - d + omega));
  178.                     final double sin2FDOm  = FastMath.sin(2 * (f - d + omega));

  179.                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
  180.                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
  181.                     final double y = yPolynomial.value(t) + xy[1] +
  182.                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
  183.                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
  184.                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));

  185.                     return new double[] {
  186.                         x, y, sPxy2
  187.                     };

  188.                 }

  189.                 /** {@inheritDoc} */
  190.                 @Override
  191.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

  194.                     final T omega     = elements.getOmega();
  195.                     final T f         = elements.getF();
  196.                     final T d         = elements.getD();
  197.                     final T t         = elements.getTC();
  198.                     final T t2        = t.multiply(t);

  199.                     final T cosOmega  = omega.cos();
  200.                     final T sinOmega  = omega.sin();
  201.                     final T sin2Omega = omega.multiply(2).sin();
  202.                     final T fMDPO2 = f.subtract(d).add(omega).multiply(2);
  203.                     final T cos2FDOm  = fMDPO2.cos();
  204.                     final T sin2FDOm  = fMDPO2.sin();

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

  214.                     final T[] a = MathArrays.buildArray(date.getField(), 3);
  215.                     a[0] = x;
  216.                     a[1] = y;
  217.                     a[2] = sPxy2;
  218.                     return a;

  219.                 }

  220.             };

  221.         }

  222.         /** {@inheritDoc} */
  223.         @Override
  224.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

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

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

  246.         }

  247.         /** {@inheritDoc} */
  248.         @Override
  249.         public TimeVectorFunction getNutationFunction()
  250.             throws OrekitException {

  251.             // set up nutation arguments
  252.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  253.             // set up Poisson series
  254.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  255.             final PoissonSeriesParser baseParser =
  256.                     new PoissonSeriesParser(10).withFirstDelaunay(1);

  257.             final PoissonSeriesParser psiParser =
  258.                     baseParser.
  259.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  260.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  261.             final PoissonSeries psiSeries = psiParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  262.             final PoissonSeriesParser epsilonParser =
  263.                     baseParser.
  264.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  265.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  266.             final PoissonSeries epsilonSeries = epsilonParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  267.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  268.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  269.             return new TimeVectorFunction() {

  270.                 /** {@inheritDoc} */
  271.                 @Override
  272.                 public double[] value(final AbsoluteDate date) {
  273.                     final BodiesElements elements = arguments.evaluateAll(date);
  274.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  275.                     return new double[] {
  276.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  277.                     };
  278.                 }

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

  290.             };

  291.         }

  292.         /** {@inheritDoc} */
  293.         @Override
  294.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1)
  295.             throws OrekitException {

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

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

  306.             return new TimeScalarFunction() {

  307.                 /** {@inheritDoc} */
  308.                 @Override
  309.                 public double value(final AbsoluteDate date) {

  310.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  311.                     final double dtai = date.durationFrom(gmstReference);
  312.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  313.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

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

  319.                 }

  320.                 /** {@inheritDoc} */
  321.                 @Override
  322.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  323.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  324.                     final T dtai = date.durationFrom(gmstReference);
  325.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  326.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

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

  332.                 }

  333.             };

  334.         }

  335.         /** {@inheritDoc} */
  336.         @Override
  337.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1)
  338.             throws OrekitException {

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

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

  348.             return new TimeScalarFunction() {

  349.                 /** {@inheritDoc} */
  350.                 @Override
  351.                 public double value(final AbsoluteDate date) {

  352.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  353.                     final double dtai = date.durationFrom(gmstReference);
  354.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  355.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

  358.                 }

  359.                 /** {@inheritDoc} */
  360.                 @Override
  361.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  362.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  363.                     final T dtai = date.durationFrom(gmstReference);
  364.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  365.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

  368.                 }

  369.             };

  370.         }

  371.         /** {@inheritDoc} */
  372.         @Override
  373.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  374.             throws OrekitException {

  375.             // obliquity
  376.             final TimeScalarFunction epsilonA = getMeanObliquityFunction();

  377.             // GMST function
  378.             final TimeScalarFunction gmst = getGMSTFunction(ut1);

  379.             // nutation function
  380.             final TimeVectorFunction nutation = getNutationFunction();

  381.             return new TimeScalarFunction() {

  382.                 /** {@inheritDoc} */
  383.                 @Override
  384.                 public double value(final AbsoluteDate date) {

  385.                     // compute equation of equinoxes
  386.                     final double[] angles = nutation.value(date);
  387.                     double deltaPsi = angles[0];
  388.                     if (eopHistory != null) {
  389.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  390.                     }
  391.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

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

  394.                 }

  395.                 /** {@inheritDoc} */
  396.                 @Override
  397.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  398.                     // compute equation of equinoxes
  399.                     final T[] angles = nutation.value(date);
  400.                     T deltaPsi = angles[0];
  401.                     if (eopHistory != null) {
  402.                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
  403.                     }
  404.                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);

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

  407.                 }

  408.             };

  409.         }

  410.         /** {@inheritDoc} */
  411.         @Override
  412.         public TimeVectorFunction getEOPTidalCorrection()
  413.             throws OrekitException {

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

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

  435.             final double deciMilliS = 1.0e-4;
  436.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
  437.                     withOptionalColumn(1).
  438.                     withGamma(7).
  439.                     withFirstDelaunay(2).
  440.                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
  441.             final PoissonSeries ut1Series =
  442.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

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

  444.         }

  445.         /** {@inheritDoc} */
  446.         public LoveNumbers getLoveNumbers() throws OrekitException {
  447.             return loadLoveNumbers(LOVE_NUMBERS);
  448.         }

  449.         /** {@inheritDoc} */
  450.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  451.             throws OrekitException {

  452.             // set up nutation arguments
  453.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  454.             // set up Poisson series
  455.             final PoissonSeriesParser k20Parser =
  456.                     new PoissonSeriesParser(18).
  457.                         withOptionalColumn(1).
  458.                         withDoodson(4, 2).
  459.                         withFirstDelaunay(10);
  460.             final PoissonSeriesParser k21Parser =
  461.                     new PoissonSeriesParser(18).
  462.                         withOptionalColumn(1).
  463.                         withDoodson(4, 3).
  464.                         withFirstDelaunay(10);
  465.             final PoissonSeriesParser k22Parser =
  466.                     new PoissonSeriesParser(16).
  467.                         withOptionalColumn(1).
  468.                         withDoodson(4, 2).
  469.                         withFirstDelaunay(10);

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

  491.             return new TideFrequencyDependenceFunction(arguments,
  492.                                                        c20Series,
  493.                                                        c21Series, s21Series,
  494.                                                        c22Series, s22Series);

  495.         }

  496.         /** {@inheritDoc} */
  497.         @Override
  498.         public double getPermanentTide() throws OrekitException {
  499.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  500.         }

  501.         /** {@inheritDoc} */
  502.         @Override
  503.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  504.             // constants from IERS 1996 page 47
  505.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  506.             final double coupling     =  0.00112;

  507.             return new TimeVectorFunction() {

  508.                 /** {@inheritDoc} */
  509.                 @Override
  510.                 public double[] value(final AbsoluteDate date) {
  511.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  512.                     return new double[] {
  513.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  514.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  515.                     };
  516.                 }

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

  526.             };
  527.         }

  528.         /** {@inheritDoc} */
  529.         @Override
  530.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  531.             throws OrekitException {

  532.             return new TimeVectorFunction() {

  533.                 /** {@inheritDoc} */
  534.                 @Override
  535.                 public double[] value(final AbsoluteDate date) {
  536.                     // there are no model for ocean pole tide prior to conventions 2010
  537.                     return new double[] {
  538.                         0.0, 0.0
  539.                     };
  540.                 }

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

  547.             };
  548.         }

  549.     },

  550.     /** Constant for IERS 2003 conventions. */
  551.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  580.         /** {@inheritDoc} */
  581.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  582.             throws OrekitException {
  583.             return new FundamentalNutationArguments(this, timeScale,
  584.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  585.         }

  586.         /** {@inheritDoc} */
  587.         @Override
  588.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  589.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  590.             final PolynomialNutation epsilonA =
  591.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  592.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  593.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  594.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  595.             return new TimeScalarFunction() {

  596.                 /** {@inheritDoc} */
  597.                 @Override
  598.                 public double value(final AbsoluteDate date) {
  599.                     return epsilonA.value(evaluateTC(date));
  600.                 }

  601.                 /** {@inheritDoc} */
  602.                 @Override
  603.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  604.                     return epsilonA.value(evaluateTC(date));
  605.                 }

  606.             };

  607.         }

  608.         /** {@inheritDoc} */
  609.         @Override
  610.         public TimeVectorFunction getXYSpXY2Function()
  611.             throws OrekitException {

  612.             // set up nutation arguments
  613.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  614.             // set up Poisson series
  615.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  616.             final PoissonSeriesParser parser =
  617.                     new PoissonSeriesParser(17).
  618.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  619.                         withFirstDelaunay(4).
  620.                         withFirstPlanetary(9).
  621.                         withSinCos(0, 2, microAS, 3, microAS);

  622.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  623.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  624.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  625.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  626.             // create a function evaluating the series
  627.             return new TimeVectorFunction() {

  628.                 /** {@inheritDoc} */
  629.                 @Override
  630.                 public double[] value(final AbsoluteDate date) {
  631.                     return xys.value(arguments.evaluateAll(date));
  632.                 }

  633.                 /** {@inheritDoc} */
  634.                 @Override
  635.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  636.                     return xys.value(arguments.evaluateAll(date));
  637.                 }

  638.             };

  639.         }


  640.         /** {@inheritDoc} */
  641.         @Override
  642.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

  643.             // set up the conventional polynomials
  644.             // the following values are from equation 32 in IERS 2003 conventions
  645.             final PolynomialNutation psiA =
  646.                     new PolynomialNutation(    0.0,
  647.                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  648.                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  649.                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  650.             final PolynomialNutation omegaA =
  651.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  652.                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  653.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  654.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  655.             final PolynomialNutation chiA =
  656.                     new PolynomialNutation( 0.0,
  657.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  658.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  659.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

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

  661.         }

  662.         /** {@inheritDoc} */
  663.         @Override
  664.         public TimeVectorFunction getNutationFunction()
  665.             throws OrekitException {

  666.             // set up nutation arguments
  667.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  668.             // set up Poisson series
  669.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  670.             final PoissonSeriesParser luniSolarParser =
  671.                     new PoissonSeriesParser(14).withFirstDelaunay(1);
  672.             final PoissonSeriesParser luniSolarPsiParser =
  673.                     luniSolarParser.
  674.                     withSinCos(0, 7, milliAS, 11, milliAS).
  675.                     withSinCos(1, 8, milliAS, 12, milliAS);
  676.             final PoissonSeries psiLuniSolarSeries =
  677.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
  678.             final PoissonSeriesParser luniSolarEpsilonParser =
  679.                     luniSolarParser.
  680.                     withSinCos(0, 13, milliAS, 9, milliAS).
  681.                     withSinCos(1, 14, milliAS, 10, milliAS);
  682.             final PoissonSeries epsilonLuniSolarSeries =
  683.                     luniSolarEpsilonParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  684.             final PoissonSeriesParser planetaryParser =
  685.                     new PoissonSeriesParser(21).
  686.                         withFirstDelaunay(2).
  687.                         withFirstPlanetary(7);
  688.             final PoissonSeriesParser planetaryPsiParser =
  689.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  690.             final PoissonSeries psiPlanetarySeries =
  691.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
  692.             final PoissonSeriesParser planetaryEpsilonParser =
  693.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  694.             final PoissonSeries epsilonPlanetarySeries =
  695.                     planetaryEpsilonParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  696.             final PoissonSeries.CompiledSeries luniSolarSeries =
  697.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  698.             final PoissonSeries.CompiledSeries planetarySeries =
  699.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  700.             return new TimeVectorFunction() {

  701.                 /** {@inheritDoc} */
  702.                 @Override
  703.                 public double[] value(final AbsoluteDate date) {
  704.                     final BodiesElements elements = arguments.evaluateAll(date);
  705.                     final double[] luniSolar = luniSolarSeries.value(elements);
  706.                     final double[] planetary = planetarySeries.value(elements);
  707.                     return new double[] {
  708.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  709.                         IAU1994ResolutionC7.value(elements)
  710.                     };
  711.                 }

  712.                 /** {@inheritDoc} */
  713.                 @Override
  714.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  715.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  716.                     final T[] luniSolar = luniSolarSeries.value(elements);
  717.                     final T[] planetary = planetarySeries.value(elements);
  718.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  719.                     result[0] = luniSolar[0].add(planetary[0]);
  720.                     result[1] = luniSolar[1].add(planetary[1]);
  721.                     result[2] = IAU1994ResolutionC7.value(elements);
  722.                     return result;
  723.                 }

  724.             };

  725.         }

  726.         /** {@inheritDoc} */
  727.         @Override
  728.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1)
  729.             throws OrekitException {

  730.             // Earth Rotation Angle
  731.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  732.             // Polynomial part of the apparent sidereal time series
  733.             // which is the opposite of Equation of Origins (EO)
  734.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  735.             final PoissonSeriesParser parser =
  736.                     new PoissonSeriesParser(17).
  737.                         withFirstDelaunay(4).
  738.                         withFirstPlanetary(9).
  739.                         withSinCos(0, 2, microAS, 3, microAS).
  740.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  741.             final PolynomialNutation minusEO =
  742.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  743.             // create a function evaluating the series
  744.             return new TimeScalarFunction() {

  745.                 /** {@inheritDoc} */
  746.                 @Override
  747.                 public double value(final AbsoluteDate date) {
  748.                     return era.value(date) + minusEO.value(evaluateTC(date));
  749.                 }

  750.                 /** {@inheritDoc} */
  751.                 @Override
  752.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  753.                     return era.value(date).add(minusEO.value(evaluateTC(date)));
  754.                 }

  755.             };

  756.         }

  757.         /** {@inheritDoc} */
  758.         @Override
  759.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1)
  760.             throws OrekitException {

  761.             // Earth Rotation Angle
  762.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  763.             // Polynomial part of the apparent sidereal time series
  764.             // which is the opposite of Equation of Origins (EO)
  765.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  766.             final PoissonSeriesParser parser =
  767.                     new PoissonSeriesParser(17).
  768.                         withFirstDelaunay(4).
  769.                         withFirstPlanetary(9).
  770.                         withSinCos(0, 2, microAS, 3, microAS).
  771.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  772.             final PolynomialNutation minusEO =
  773.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  774.             // create a function evaluating the series
  775.             return new TimeScalarFunction() {

  776.                 /** {@inheritDoc} */
  777.                 @Override
  778.                 public double value(final AbsoluteDate date) {
  779.                     return era.getRate() + minusEO.derivative(evaluateTC(date));
  780.                 }

  781.                 /** {@inheritDoc} */
  782.                 @Override
  783.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  784.                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
  785.                 }

  786.             };

  787.         }

  788.         /** {@inheritDoc} */
  789.         @Override
  790.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  791.             throws OrekitException {

  792.             // set up nutation arguments
  793.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  794.             // mean obliquity function
  795.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  796.             // set up Poisson series
  797.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  798.             final PoissonSeriesParser luniSolarPsiParser =
  799.                     new PoissonSeriesParser(14).
  800.                         withFirstDelaunay(1).
  801.                         withSinCos(0, 7, milliAS, 11, milliAS).
  802.                         withSinCos(1, 8, milliAS, 12, milliAS);
  803.             final PoissonSeries psiLuniSolarSeries =
  804.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  805.             final PoissonSeriesParser planetaryPsiParser =
  806.                     new PoissonSeriesParser(21).
  807.                         withFirstDelaunay(2).
  808.                         withFirstPlanetary(7).
  809.                         withSinCos(0, 17, milliAS, 18, milliAS);
  810.             final PoissonSeries psiPlanetarySeries =
  811.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  812.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  813.             final PoissonSeriesParser gstParser =
  814.                     new PoissonSeriesParser(17).
  815.                         withFirstDelaunay(4).
  816.                         withFirstPlanetary(9).
  817.                         withSinCos(0, 2, microAS, 3, microAS).
  818.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  819.             final PoissonSeries gstSeries = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  820.             final PoissonSeries.CompiledSeries psiGstSeries =
  821.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  822.             // ERA function
  823.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  824.             return new TimeScalarFunction() {

  825.                 /** {@inheritDoc} */
  826.                 @Override
  827.                 public double value(final AbsoluteDate date) {

  828.                     // evaluate equation of origins
  829.                     final BodiesElements elements = arguments.evaluateAll(date);
  830.                     final double[] angles = psiGstSeries.value(elements);
  831.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  832.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  833.                     final double epsilonA = epsilon.value(date);

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

  837.                 }

  838.                 /** {@inheritDoc} */
  839.                 @Override
  840.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  841.                     // evaluate equation of origins
  842.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  843.                     final T[] angles = psiGstSeries.value(elements);
  844.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  845.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  846.                     final T epsilonA = epsilon.value(date);

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

  850.                 }

  851.             };

  852.         }

  853.         /** {@inheritDoc} */
  854.         @Override
  855.         public TimeVectorFunction getEOPTidalCorrection()
  856.             throws OrekitException {

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

  863.             // set up Poisson series
  864.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  865.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  866.                     withOptionalColumn(1).
  867.                     withGamma(2).
  868.                     withFirstDelaunay(3);
  869.             final PoissonSeries xSeries =
  870.                     xyParser.
  871.                     withSinCos(0, 10, microAS, 11, microAS).
  872.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  873.             final PoissonSeries ySeries =
  874.                     xyParser.
  875.                     withSinCos(0, 12, microAS, 13, microAS).
  876.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  877.             final double microS = 1.0e-6;
  878.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  879.                     withOptionalColumn(1).
  880.                     withGamma(2).
  881.                     withFirstDelaunay(3).
  882.                     withSinCos(0, 10, microS, 11, microS);
  883.             final PoissonSeries ut1Series =
  884.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

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

  886.         }

  887.         /** {@inheritDoc} */
  888.         public LoveNumbers getLoveNumbers() throws OrekitException {
  889.             return loadLoveNumbers(LOVE_NUMBERS);
  890.         }

  891.         /** {@inheritDoc} */
  892.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  893.             throws OrekitException {

  894.             // set up nutation arguments
  895.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  896.             // set up Poisson series
  897.             final PoissonSeriesParser k20Parser =
  898.                     new PoissonSeriesParser(18).
  899.                         withOptionalColumn(1).
  900.                         withDoodson(4, 2).
  901.                         withFirstDelaunay(10);
  902.             final PoissonSeriesParser k21Parser =
  903.                     new PoissonSeriesParser(18).
  904.                         withOptionalColumn(1).
  905.                         withDoodson(4, 3).
  906.                         withFirstDelaunay(10);
  907.             final PoissonSeriesParser k22Parser =
  908.                     new PoissonSeriesParser(16).
  909.                         withOptionalColumn(1).
  910.                         withDoodson(4, 2).
  911.                         withFirstDelaunay(10);

  912.             final double pico = 1.0e-12;
  913.             final PoissonSeries c20Series =
  914.                     k20Parser.
  915.                   withSinCos(0, 18, -pico, 16, pico).
  916.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  917.             final PoissonSeries c21Series =
  918.                     k21Parser.
  919.                     withSinCos(0, 17, pico, 18, pico).
  920.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  921.             final PoissonSeries s21Series =
  922.                     k21Parser.
  923.                     withSinCos(0, 18, -pico, 17, pico).
  924.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  925.             final PoissonSeries c22Series =
  926.                     k22Parser.
  927.                     withSinCos(0, -1, pico, 16, pico).
  928.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  929.             final PoissonSeries s22Series =
  930.                     k22Parser.
  931.                     withSinCos(0, 16, -pico, -1, pico).
  932.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  933.             return new TideFrequencyDependenceFunction(arguments,
  934.                                                        c20Series,
  935.                                                        c21Series, s21Series,
  936.                                                        c22Series, s22Series);

  937.         }

  938.         /** {@inheritDoc} */
  939.         @Override
  940.         public double getPermanentTide() throws OrekitException {
  941.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  942.         }

  943.         /** {@inheritDoc} */
  944.         @Override
  945.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory)
  946.             throws OrekitException {

  947.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  948.             final TimeScale utc = TimeScalesFactory.getUTC();
  949.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  950.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  951.                     /** {@inheritDoc} */
  952.                     @Override
  953.                     public MeanPole convert(final double[] rawFields) throws OrekitException {
  954.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  955.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  956.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  957.                     }
  958.                 };
  959.             final SimpleTimeStampedTableParser<MeanPole> parser =
  960.                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
  961.             final List<MeanPole> annualPoleList = parser.parse(getStream(ANNUAL_POLE), ANNUAL_POLE);
  962.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  963.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  964.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  965.                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);

  966.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  967.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  968.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  969.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  970.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  971.             // constants from IERS 2003, section 6.2
  972.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  973.             final double ratio        =  0.00115;

  974.             return new TimeVectorFunction() {

  975.                 /** {@inheritDoc} */
  976.                 @Override
  977.                 public double[] value(final AbsoluteDate date) {

  978.                     // we can't compute anything before the range covered by the annual pole file
  979.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  980.                         return new double[] {
  981.                             0.0, 0.0
  982.                         };
  983.                     }

  984.                     // evaluate mean pole
  985.                     double meanPoleX = 0;
  986.                     double meanPoleY = 0;
  987.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  988.                         // we are within the range covered by the annual pole file,
  989.                         // we interpolate within it
  990.                         try {
  991.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  992.                             annualCache.getNeighbors(date).forEach(neighbor ->
  993.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  994.                                                             new double[] {
  995.                                                                 neighbor.getX(), neighbor.getY()
  996.                                                             }));
  997.                             final double[] interpolated = interpolator.value(0);
  998.                             meanPoleX = interpolated[0];
  999.                             meanPoleY = interpolated[1];
  1000.                         } catch (TimeStampedCacheException tsce) {
  1001.                             // this should never happen
  1002.                             throw new OrekitInternalError(tsce);
  1003.                         }
  1004.                     } else {

  1005.                         // we are after the range covered by the annual pole file,
  1006.                         // we use the polynomial extension
  1007.                         final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1008.                         meanPoleX = xp0 + t * xp0Dot;
  1009.                         meanPoleY = yp0 + t * yp0Dot;

  1010.                     }

  1011.                     // evaluate wobble variables
  1012.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1013.                     final double m1 = correction.getXp() - meanPoleX;
  1014.                     final double m2 = meanPoleY - correction.getYp();

  1015.                     return new double[] {
  1016.                         // the following correspond to the equations published in IERS 2003 conventions,
  1017.                         // section 6.2 page 65. In the publication, the equations read:
  1018.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1019.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1020.                         // However, it seems there are sign errors in these equations, which have
  1021.                         // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1022.                         // publication, the equations read:
  1023.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1024.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1025.                         // the newer equations seem more consistent with the premises as the
  1026.                         // deformation due to the centrifugal potential has the form:
  1027.                         // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1028.                         // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1029.                         // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1030.                         // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1031.                         // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1032.                         // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1033.                         // and Im(k₂)/Re(k₂) is very close to +0.00115
  1034.                         // As the equation as written in the IERS 2003 conventions are used in
  1035.                         // legacy systems, we have reproduced this alleged error here (and fixed it in
  1036.                         // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1037.                         // using the IERS 2003 conventions for solid pole tide computation other than
  1038.                         // for validation or reproducibility of legacy applications behavior.
  1039.                         // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1040.                         // the effect is quite small. A test case on a propagated orbit showed a position change
  1041.                         // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1042.                         globalFactor * (m1 - ratio * m2),
  1043.                         globalFactor * (m2 + ratio * m1),
  1044.                     };

  1045.                 }

  1046.                 /** {@inheritDoc} */
  1047.                 @Override
  1048.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1054.                     // evaluate mean pole
  1055.                     T meanPoleX = date.getField().getZero();
  1056.                     T meanPoleY = date.getField().getZero();
  1057.                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
  1058.                         // we are within the range covered by the annual pole file,
  1059.                         // we interpolate within it
  1060.                         try {
  1061.                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
  1062.                             final T[] y = MathArrays.buildArray(date.getField(), 2);
  1063.                             final T zero = date.getField().getZero();
  1064.                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
  1065.                                                                                                        // for example removing derivatives
  1066.                                                                                                        // if T was DerivativeStructure
  1067.                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
  1068.                                 y[0] = zero.add(neighbor.getX());
  1069.                                 y[1] = zero.add(neighbor.getY());
  1070.                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
  1071.                             });
  1072.                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
  1073.                             meanPoleX = interpolated[0];
  1074.                             meanPoleY = interpolated[1];
  1075.                         } catch (TimeStampedCacheException tsce) {
  1076.                             // this should never happen
  1077.                             throw new OrekitInternalError(tsce);
  1078.                         }
  1079.                     } else {

  1080.                         // we are after the range covered by the annual pole file,
  1081.                         // we use the polynomial extension
  1082.                         final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1083.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1084.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1085.                     }

  1086.                     // evaluate wobble variables
  1087.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1088.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1089.                     final T m2 = meanPoleY.subtract(correction.getYp());

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

  1091.                     // the following correspond to the equations published in IERS 2003 conventions,
  1092.                     // section 6.2 page 65. In the publication, the equations read:
  1093.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1094.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1095.                     // However, it seems there are sign errors in these equations, which have
  1096.                     // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1097.                     // publication, the equations read:
  1098.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1099.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1100.                     // the newer equations seem more consistent with the premises as the
  1101.                     // deformation due to the centrifugal potential has the form:
  1102.                     // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1103.                     // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1104.                     // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1105.                     // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1106.                     // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1107.                     // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1108.                     // and Im(k₂)/Re(k₂) is very close to +0.00115
  1109.                     // As the equation as written in the IERS 2003 conventions are used in
  1110.                     // legacy systems, we have reproduced this alleged error here (and fixed it in
  1111.                     // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1112.                     // using the IERS 2003 conventions for solid pole tide computation other than
  1113.                     // for validation or reproducibility of legacy applications behavior.
  1114.                     // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1115.                     // the effect is quite small. A test case on a propagated orbit showed a position change
  1116.                     // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1117.                     a[0] = m1.add(m2.multiply(-ratio)).multiply(globalFactor);
  1118.                     a[1] = m2.add(m1.multiply( ratio)).multiply(globalFactor);

  1119.                     return a;

  1120.                 }

  1121.             };

  1122.         }

  1123.         /** {@inheritDoc} */
  1124.         @Override
  1125.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  1126.             throws OrekitException {

  1127.             return new TimeVectorFunction() {

  1128.                 /** {@inheritDoc} */
  1129.                 @Override
  1130.                 public double[] value(final AbsoluteDate date) {
  1131.                     // there are no model for ocean pole tide prior to conventions 2010
  1132.                     return new double[] {
  1133.                         0.0, 0.0
  1134.                     };
  1135.                 }

  1136.                 /** {@inheritDoc} */
  1137.                 @Override
  1138.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1139.                     // there are no model for ocean pole tide prior to conventions 2010
  1140.                     return MathArrays.buildArray(date.getField(), 2);
  1141.                 }

  1142.             };
  1143.         }

  1144.     },

  1145.     /** Constant for IERS 2010 conventions. */
  1146.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

  1173.         /** {@inheritDoc} */
  1174.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  1175.             throws OrekitException {
  1176.             return new FundamentalNutationArguments(this, timeScale,
  1177.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  1178.         }

  1179.         /** {@inheritDoc} */
  1180.         @Override
  1181.         public TimeScalarFunction getMeanObliquityFunction() throws OrekitException {

  1182.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1183.             final PolynomialNutation epsilonA =
  1184.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1185.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1186.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1187.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1188.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1189.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1190.             return new TimeScalarFunction() {

  1191.                 /** {@inheritDoc} */
  1192.                 @Override
  1193.                 public double value(final AbsoluteDate date) {
  1194.                     return epsilonA.value(evaluateTC(date));
  1195.                 }

  1196.                 /** {@inheritDoc} */
  1197.                 @Override
  1198.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1199.                     return epsilonA.value(evaluateTC(date));
  1200.                 }

  1201.             };

  1202.         }

  1203.         /** {@inheritDoc} */
  1204.         @Override
  1205.         public TimeVectorFunction getXYSpXY2Function() throws OrekitException {

  1206.             // set up nutation arguments
  1207.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1208.             // set up Poisson series
  1209.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1210.             final PoissonSeriesParser parser =
  1211.                     new PoissonSeriesParser(17).
  1212.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1213.                         withFirstDelaunay(4).
  1214.                         withFirstPlanetary(9).
  1215.                         withSinCos(0, 2, microAS, 3, microAS);
  1216.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  1217.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  1218.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  1219.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  1220.             // create a function evaluating the series
  1221.             return new TimeVectorFunction() {

  1222.                 /** {@inheritDoc} */
  1223.                 @Override
  1224.                 public double[] value(final AbsoluteDate date) {
  1225.                     return xys.value(arguments.evaluateAll(date));
  1226.                 }

  1227.                 /** {@inheritDoc} */
  1228.                 @Override
  1229.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1230.                     return xys.value(arguments.evaluateAll(date));
  1231.                 }

  1232.             };

  1233.         }

  1234.         /** {@inheritDoc} */
  1235.         public LoveNumbers getLoveNumbers() throws OrekitException {
  1236.             return loadLoveNumbers(LOVE_NUMBERS);
  1237.         }

  1238.         /** {@inheritDoc} */
  1239.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1)
  1240.             throws OrekitException {

  1241.             // set up nutation arguments
  1242.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  1243.             // set up Poisson series
  1244.             final PoissonSeriesParser k20Parser =
  1245.                     new PoissonSeriesParser(18).
  1246.                         withOptionalColumn(1).
  1247.                         withDoodson(4, 2).
  1248.                         withFirstDelaunay(10);
  1249.             final PoissonSeriesParser k21Parser =
  1250.                     new PoissonSeriesParser(18).
  1251.                         withOptionalColumn(1).
  1252.                         withDoodson(4, 3).
  1253.                         withFirstDelaunay(10);
  1254.             final PoissonSeriesParser k22Parser =
  1255.                     new PoissonSeriesParser(16).
  1256.                         withOptionalColumn(1).
  1257.                         withDoodson(4, 2).
  1258.                         withFirstDelaunay(10);

  1259.             final double pico = 1.0e-12;
  1260.             final PoissonSeries c20Series =
  1261.                     k20Parser.
  1262.                     withSinCos(0, 18, -pico, 16, pico).
  1263.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  1264.             final PoissonSeries c21Series =
  1265.                     k21Parser.
  1266.                     withSinCos(0, 17, pico, 18, pico).
  1267.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1268.             final PoissonSeries s21Series =
  1269.                     k21Parser.
  1270.                     withSinCos(0, 18, -pico, 17, pico).
  1271.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1272.             final PoissonSeries c22Series =
  1273.                     k22Parser.
  1274.                     withSinCos(0, -1, pico, 16, pico).
  1275.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  1276.             final PoissonSeries s22Series =
  1277.                     k22Parser.
  1278.                     withSinCos(0, 16, -pico, -1, pico).
  1279.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  1280.             return new TideFrequencyDependenceFunction(arguments,
  1281.                                                        c20Series,
  1282.                                                        c21Series, s21Series,
  1283.                                                        c22Series, s22Series);

  1284.         }

  1285.         /** {@inheritDoc} */
  1286.         @Override
  1287.         public double getPermanentTide() throws OrekitException {
  1288.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1289.         }

  1290.         /** Compute pole wobble variables m₁ and m₂.
  1291.          * @param date current date
  1292.          * @param eopHistory EOP history
  1293.          * @return array containing m₁ and m₂
  1294.          */
  1295.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1296.             // polynomial model from IERS 2010, table 7.7
  1297.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1298.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1299.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1300.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1301.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1302.             // evaluate mean pole
  1303.             final double[] xPolynomial;
  1304.             final double[] yPolynomial;
  1305.             if (date.compareTo(changeDate) <= 0) {
  1306.                 xPolynomial = new double[] {
  1307.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1308.                 };
  1309.                 yPolynomial = new double[] {
  1310.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1311.                 };
  1312.             } else {
  1313.                 xPolynomial = new double[] {
  1314.                     23.513 * f0, 7.6141 * f1
  1315.                 };
  1316.                 yPolynomial = new double[] {
  1317.                     358.891 * f0,  -0.6287 * f1
  1318.                 };
  1319.             }
  1320.             double meanPoleX = 0;
  1321.             double meanPoleY = 0;
  1322.             final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1323.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1324.                 meanPoleX = meanPoleX * t + xPolynomial[i];
  1325.             }
  1326.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1327.                 meanPoleY = meanPoleY * t + yPolynomial[i];
  1328.             }

  1329.             // evaluate wobble variables
  1330.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1331.             final double m1 = correction.getXp() - meanPoleX;
  1332.             final double m2 = meanPoleY - correction.getYp();

  1333.             return new double[] {
  1334.                 m1, m2
  1335.             };

  1336.         }

  1337.         /** Compute pole wobble variables m₁ and m₂.
  1338.          * @param date current date
  1339.          * @param <T> type of the field elements
  1340.          * @param eopHistory EOP history
  1341.          * @return array containing m₁ and m₂
  1342.          */
  1343.         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1344.             // polynomial model from IERS 2010, table 7.7
  1345.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1346.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1347.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1348.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1349.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1350.             // evaluate mean pole
  1351.             final double[] xPolynomial;
  1352.             final double[] yPolynomial;
  1353.             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
  1354.                 xPolynomial = new double[] {
  1355.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1356.                 };
  1357.                 yPolynomial = new double[] {
  1358.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1359.                 };
  1360.             } else {
  1361.                 xPolynomial = new double[] {
  1362.                     23.513 * f0, 7.6141 * f1
  1363.                 };
  1364.                 yPolynomial = new double[] {
  1365.                     358.891 * f0,  -0.6287 * f1
  1366.                 };
  1367.             }
  1368.             T meanPoleX = date.getField().getZero();
  1369.             T meanPoleY = date.getField().getZero();
  1370.             final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1371.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1372.                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
  1373.             }
  1374.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1375.                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
  1376.             }

  1377.             // evaluate wobble variables
  1378.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1379.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1380.             m[0] = correction.getXp().subtract(meanPoleX);
  1381.             m[1] = meanPoleY.subtract(correction.getYp());

  1382.             return m;

  1383.         }

  1384.         /** {@inheritDoc} */
  1385.         @Override
  1386.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory)
  1387.             throws OrekitException {

  1388.             // constants from IERS 2010, section 6.4
  1389.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1390.             final double ratio        =  0.00115;

  1391.             return new TimeVectorFunction() {

  1392.                 /** {@inheritDoc} */
  1393.                 @Override
  1394.                 public double[] value(final AbsoluteDate date) {

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

  1397.                     return new double[] {
  1398.                         // the following correspond to the equations published in IERS 2010 conventions,
  1399.                         // section 6.4 page 94. The equations read:
  1400.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1401.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1402.                         // These equations seem to fix what was probably a sign error in IERS 2003
  1403.                         // conventions section 6.2 page 65. In this older publication, the equations read:
  1404.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1405.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1406.                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
  1407.                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
  1408.                     };

  1409.                 }

  1410.                 /** {@inheritDoc} */
  1411.                 @Override
  1412.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1416.                     // the following correspond to the equations published in IERS 2010 conventions,
  1417.                     // section 6.4 page 94. The equations read:
  1418.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1419.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1420.                     // These equations seem to fix what was probably a sign error in IERS 2003
  1421.                     // conventions section 6.2 page 65. In this older publication, the equations read:
  1422.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1423.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1424.                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
  1425.                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);

  1426.                     return a;

  1427.                 }

  1428.             };

  1429.         }

  1430.         /** {@inheritDoc} */
  1431.         @Override
  1432.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory)
  1433.             throws OrekitException {

  1434.             return new TimeVectorFunction() {

  1435.                 /** {@inheritDoc} */
  1436.                 @Override
  1437.                 public double[] value(final AbsoluteDate date) {

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

  1440.                     return new double[] {
  1441.                         // the following correspond to the equations published in IERS 2010 conventions,
  1442.                         // section 6.4 page 94 equation 6.24:
  1443.                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1444.                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1445.                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
  1446.                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
  1447.                     };

  1448.                 }

  1449.                 /** {@inheritDoc} */
  1450.                 @Override
  1451.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1461.                     return v;

  1462.                 }

  1463.             };

  1464.         }

  1465.         /** {@inheritDoc} */
  1466.         @Override
  1467.         public TimeVectorFunction getPrecessionFunction() throws OrekitException {

  1468.             // set up the conventional polynomials
  1469.             // the following values are from equation 5.40 in IERS 2010 conventions
  1470.             final PolynomialNutation psiA =
  1471.                     new PolynomialNutation(   0.0,
  1472.                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1473.                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1474.                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1475.                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1476.                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1477.             final PolynomialNutation omegaA =
  1478.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  1479.                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1480.                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1481.                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1482.                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1483.                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1484.             final PolynomialNutation chiA =
  1485.                     new PolynomialNutation( 0.0,
  1486.                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1487.                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1488.                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1489.                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1490.                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

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

  1492.         }

  1493.          /** {@inheritDoc} */
  1494.         @Override
  1495.         public TimeVectorFunction getNutationFunction()
  1496.             throws OrekitException {

  1497.             // set up nutation arguments
  1498.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1499.             // set up Poisson series
  1500.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1501.             final PoissonSeriesParser parser =
  1502.                     new PoissonSeriesParser(17).
  1503.                         withFirstDelaunay(4).
  1504.                         withFirstPlanetary(9).
  1505.                         withSinCos(0, 2, microAS, 3, microAS);
  1506.             final PoissonSeries psiSeries     = parser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1507.             final PoissonSeries epsilonSeries = parser.parse(getStream(EPSILON_SERIES), EPSILON_SERIES);
  1508.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  1509.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  1510.             return new TimeVectorFunction() {

  1511.                 /** {@inheritDoc} */
  1512.                 @Override
  1513.                 public double[] value(final AbsoluteDate date) {
  1514.                     final BodiesElements elements = arguments.evaluateAll(date);
  1515.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1516.                     return new double[] {
  1517.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  1518.                     };
  1519.                 }

  1520.                 /** {@inheritDoc} */
  1521.                 @Override
  1522.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1523.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1524.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1525.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1526.                     result[0] = psiEpsilon[0];
  1527.                     result[1] = psiEpsilon[1];
  1528.                     result[2] = IAU1994ResolutionC7.value(elements);
  1529.                     return result;
  1530.                 }

  1531.             };

  1532.         }

  1533.         /** {@inheritDoc} */
  1534.         @Override
  1535.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) throws OrekitException {

  1536.             // Earth Rotation Angle
  1537.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1538.             // Polynomial part of the apparent sidereal time series
  1539.             // which is the opposite of Equation of Origins (EO)
  1540.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1541.             final PoissonSeriesParser parser =
  1542.                     new PoissonSeriesParser(17).
  1543.                         withFirstDelaunay(4).
  1544.                         withFirstPlanetary(9).
  1545.                         withSinCos(0, 2, microAS, 3, microAS).
  1546.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1547.             final PolynomialNutation minusEO =
  1548.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1549.             // create a function evaluating the series
  1550.             return new TimeScalarFunction() {

  1551.                 /** {@inheritDoc} */
  1552.                 @Override
  1553.                 public double value(final AbsoluteDate date) {
  1554.                     return era.value(date) + minusEO.value(evaluateTC(date));
  1555.                 }

  1556.                 /** {@inheritDoc} */
  1557.                 @Override
  1558.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1559.                     return era.value(date).add(minusEO.value(evaluateTC(date)));
  1560.                 }

  1561.             };

  1562.         }

  1563.         /** {@inheritDoc} */
  1564.         @Override
  1565.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) throws OrekitException {

  1566.             // Earth Rotation Angle
  1567.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1568.             // Polynomial part of the apparent sidereal time series
  1569.             // which is the opposite of Equation of Origins (EO)
  1570.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1571.             final PoissonSeriesParser parser =
  1572.                     new PoissonSeriesParser(17).
  1573.                         withFirstDelaunay(4).
  1574.                         withFirstPlanetary(9).
  1575.                         withSinCos(0, 2, microAS, 3, microAS).
  1576.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1577.             final PolynomialNutation minusEO =
  1578.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1579.             // create a function evaluating the series
  1580.             return new TimeScalarFunction() {

  1581.                 /** {@inheritDoc} */
  1582.                 @Override
  1583.                 public double value(final AbsoluteDate date) {
  1584.                     return era.getRate() + minusEO.derivative(evaluateTC(date));
  1585.                 }

  1586.                 /** {@inheritDoc} */
  1587.                 @Override
  1588.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1589.                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
  1590.                 }

  1591.             };

  1592.         }

  1593.         /** {@inheritDoc} */
  1594.         @Override
  1595.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory)
  1596.             throws OrekitException {

  1597.             // set up nutation arguments
  1598.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1599.             // mean obliquity function
  1600.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  1601.             // set up Poisson series
  1602.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1603.             final PoissonSeriesParser baseParser =
  1604.                     new PoissonSeriesParser(17).
  1605.                         withFirstDelaunay(4).
  1606.                         withFirstPlanetary(9).
  1607.                         withSinCos(0, 2, microAS, 3, microAS);
  1608.             final PoissonSeriesParser gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1609.             final PoissonSeries psiSeries        = baseParser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1610.             final PoissonSeries gstSeries        = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  1611.             final PoissonSeries.CompiledSeries psiGstSeries =
  1612.                     PoissonSeries.compile(psiSeries, gstSeries);

  1613.             // ERA function
  1614.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  1615.             return new TimeScalarFunction() {

  1616.                 /** {@inheritDoc} */
  1617.                 @Override
  1618.                 public double value(final AbsoluteDate date) {

  1619.                     // evaluate equation of origins
  1620.                     final BodiesElements elements = arguments.evaluateAll(date);
  1621.                     final double[] angles = psiGstSeries.value(elements);
  1622.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1623.                     final double deltaPsi = angles[0] + ddPsi;
  1624.                     final double epsilonA = epsilon.value(date);

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

  1628.                 }

  1629.                 /** {@inheritDoc} */
  1630.                 @Override
  1631.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1632.                     // evaluate equation of origins
  1633.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1634.                     final T[] angles = psiGstSeries.value(elements);
  1635.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1636.                     final T deltaPsi = angles[0].add(ddPsi);
  1637.                     final T epsilonA = epsilon.value(date);

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

  1641.                 }

  1642.             };

  1643.         }

  1644.         /** {@inheritDoc} */
  1645.         @Override
  1646.         public TimeVectorFunction getEOPTidalCorrection()
  1647.             throws OrekitException {

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

  1654.             // set up Poisson series
  1655.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1656.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  1657.                     withOptionalColumn(1).
  1658.                     withGamma(2).
  1659.                     withFirstDelaunay(3);
  1660.             final PoissonSeries xSeries =
  1661.                     xyParser.
  1662.                     withSinCos(0, 10, microAS, 11, microAS).
  1663.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  1664.             final PoissonSeries ySeries =
  1665.                     xyParser.
  1666.                     withSinCos(0, 12, microAS, 13, microAS).
  1667.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  1668.             final double microS = 1.0e-6;
  1669.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1670.                     withOptionalColumn(1).
  1671.                     withGamma(2).
  1672.                     withFirstDelaunay(3).
  1673.                     withSinCos(0, 10, microS, 11, microS);
  1674.             final PoissonSeries ut1Series =
  1675.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

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

  1677.         }

  1678.     };

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

  1681.     /** Get the reference epoch for fundamental nutation arguments.
  1682.      * @return reference epoch for fundamental nutation arguments
  1683.      * @since 6.1
  1684.      */
  1685.     public AbsoluteDate getNutationReferenceEpoch() {
  1686.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1687.         return AbsoluteDate.J2000_EPOCH;
  1688.     }

  1689.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1690.      * @param date current date
  1691.      * @return date offset in Julian centuries
  1692.      * @since 6.1
  1693.      */
  1694.     public double evaluateTC(final AbsoluteDate date) {
  1695.         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
  1696.     }

  1697.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1698.      * @param date current date
  1699.      * @param <T> type of the field elements
  1700.      * @return date offset in Julian centuries
  1701.      * @since 9.0
  1702.      */
  1703.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
  1704.         return date.durationFrom(getNutationReferenceEpoch()).divide(Constants.JULIAN_CENTURY);
  1705.     }

  1706.     /** Get the fundamental nutation arguments.
  1707.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  1708.      * (typically {@link TimeScalesFactory#getUT1(IERSConventions, boolean) UT1})
  1709.      * @return fundamental nutation arguments
  1710.      * @exception OrekitException if fundamental nutation arguments cannot be loaded
  1711.      * @since 6.1
  1712.      */
  1713.     public abstract FundamentalNutationArguments getNutationArguments(TimeScale timeScale)
  1714.         throws OrekitException;

  1715.     /** Get the function computing mean obliquity of the ecliptic.
  1716.      * @return function computing mean obliquity of the ecliptic
  1717.      * @exception OrekitException if table cannot be loaded
  1718.      * @since 6.1
  1719.      */
  1720.     public abstract TimeScalarFunction getMeanObliquityFunction() throws OrekitException;

  1721.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  1722.      * <p>
  1723.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  1724.      * </p>
  1725.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  1726.      * @exception OrekitException if table cannot be loaded
  1727.      * @since 6.1
  1728.      */
  1729.     public abstract TimeVectorFunction getXYSpXY2Function()
  1730.         throws OrekitException;

  1731.     /** Get the function computing the raw Earth Orientation Angle.
  1732.      * <p>
  1733.      * The raw angle does not contain any correction. If for example dTU1 correction
  1734.      * due to tidal effect is desired, it must be added afterward by the caller.
  1735.      * The returned value contain the angle as the value and the angular rate as
  1736.      * the first derivative.
  1737.      * </p>
  1738.      * @param ut1 UT1 time scale
  1739.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  1740.      * @since 6.1
  1741.      */
  1742.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
  1743.         return new StellarAngleCapitaine(ut1);
  1744.     }


  1745.     /** Get the function computing the precession angles.
  1746.      * <p>
  1747.      * The function returned computes the three precession angles
  1748.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  1749.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  1750.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  1751.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  1752.      * #getNutationReferenceEpoch() nutation reference epoch}.
  1753.      * </p>
  1754.      * @return function computing the precession angle
  1755.      * @exception OrekitException if table cannot be loaded
  1756.      * @since 6.1
  1757.      */
  1758.     public abstract TimeVectorFunction getPrecessionFunction() throws OrekitException;

  1759.     /** Get the function computing the nutation angles.
  1760.      * <p>
  1761.      * The function returned computes the two classical angles ΔΨ and Δε,
  1762.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  1763.      * resolution C7 (the correction is forced to 0 before this date)
  1764.      * </p>
  1765.      * @return function computing the nutation in longitude ΔΨ and Δε
  1766.      * and the correction of equation of equinoxes
  1767.      * @exception OrekitException if table cannot be loaded
  1768.      * @since 6.1
  1769.      */
  1770.     public abstract TimeVectorFunction getNutationFunction()
  1771.         throws OrekitException;

  1772.     /** Get the function computing Greenwich mean sidereal time, in radians.
  1773.      * @param ut1 UT1 time scale
  1774.      * @return function computing Greenwich mean sidereal time
  1775.      * @exception OrekitException if table cannot be loaded
  1776.      * @since 6.1
  1777.      */
  1778.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1)
  1779.         throws OrekitException;

  1780.     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
  1781.      * @param ut1 UT1 time scale
  1782.      * @return function computing Greenwich mean sidereal time rate
  1783.      * @exception OrekitException if table cannot be loaded
  1784.      * @since 9.0
  1785.      */
  1786.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1)
  1787.         throws OrekitException;

  1788.     /** Get the function computing Greenwich apparent sidereal time, in radians.
  1789.      * @param ut1 UT1 time scale
  1790.      * @param eopHistory EOP history
  1791.      * @return function computing Greenwich apparent sidereal time
  1792.      * @exception OrekitException if table cannot be loaded
  1793.      * @since 6.1
  1794.      */
  1795.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1, EOPHistory eopHistory)
  1796.         throws OrekitException;

  1797.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  1798.      * @return function computing tidal corrections for Earth Orientation Parameters,
  1799.      * for xp, yp, ut1 and lod respectively
  1800.      * @exception OrekitException if table cannot be loaded
  1801.      * @since 6.1
  1802.      */
  1803.     public abstract TimeVectorFunction getEOPTidalCorrection()
  1804.         throws OrekitException;

  1805.     /** Get the Love numbers.
  1806.      * @return Love numbers
  1807.      * @exception OrekitException if table cannot be loaded
  1808.      * @since 6.1
  1809.      */
  1810.     public abstract LoveNumbers getLoveNumbers()
  1811.         throws OrekitException;

  1812.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1813.      * @param ut1 UT1 time scale
  1814.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1815.      * @exception OrekitException if table cannot be loaded
  1816.      * @since 6.1
  1817.      */
  1818.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(TimeScale ut1)
  1819.         throws OrekitException;

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

  1825.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  1826.      * @param eopHistory EOP history
  1827.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1828.      * @exception OrekitException if table cannot be loaded
  1829.      * @since 6.1
  1830.      */
  1831.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory)
  1832.         throws OrekitException;

  1833.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  1834.      * @param eopHistory EOP history
  1835.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1836.      * @exception OrekitException if table cannot be loaded
  1837.      * @since 6.1
  1838.      */
  1839.     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory)
  1840.         throws OrekitException;

  1841.     /** Interface for functions converting nutation corrections between
  1842.      * δΔψ/δΔε to δX/δY.
  1843.      * <ul>
  1844.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  1845.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1846.      * </ul>
  1847.      * @since 6.1
  1848.      */
  1849.     public interface NutationCorrectionConverter {

  1850.         /** Convert nutation corrections.
  1851.          * @param date current date
  1852.          * @param ddPsi δΔψ part of the nutation correction
  1853.          * @param ddEpsilon δΔε part of the nutation correction
  1854.          * @return array containing δX and δY
  1855.          * @exception OrekitException if correction cannot be converted
  1856.          */
  1857.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon)
  1858.             throws OrekitException;

  1859.         /** Convert nutation corrections.
  1860.          * @param date current date
  1861.          * @param dX δX part of the nutation correction
  1862.          * @param dY δY part of the nutation correction
  1863.          * @return array containing δΔψ and δΔε
  1864.          * @exception OrekitException if correction cannot be converted
  1865.          */
  1866.         double[] toEquinox(AbsoluteDate date, double dX, double dY)
  1867.             throws OrekitException;

  1868.     }

  1869.     /** Create a function converting nutation corrections between
  1870.      * δX/δY and δΔψ/δΔε.
  1871.      * <ul>
  1872.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1873.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  1874.      * </ul>
  1875.      * @return a new converter
  1876.      * @exception OrekitException if some convention table cannot be loaded
  1877.      * @since 6.1
  1878.      */
  1879.     public NutationCorrectionConverter getNutationCorrectionConverter()
  1880.         throws OrekitException {

  1881.         // get models parameters
  1882.         final TimeVectorFunction precessionFunction = getPrecessionFunction();
  1883.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction();
  1884.         final AbsoluteDate date0 = getNutationReferenceEpoch();
  1885.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  1886.         return new NutationCorrectionConverter() {

  1887.             /** {@inheritDoc} */
  1888.             @Override
  1889.             public double[] toNonRotating(final AbsoluteDate date,
  1890.                                           final double ddPsi, final double ddEpsilon)
  1891.                 throws OrekitException {
  1892.                 // compute precession angles psiA, omegaA and chiA
  1893.                 final double[] angles = precessionFunction.value(date);

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

  1897.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  1898.                 return new double[] {
  1899.                     sinEA * ddPsi + c * ddEpsilon,
  1900.                     ddEpsilon - c * sinEA * ddPsi
  1901.                 };

  1902.             }

  1903.             /** {@inheritDoc} */
  1904.             @Override
  1905.             public double[] toEquinox(final AbsoluteDate date,
  1906.                                       final double dX, final double dY)
  1907.                 throws OrekitException {
  1908.                 // compute precession angles psiA, omegaA and chiA
  1909.                 final double[] angles   = precessionFunction.value(date);

  1910.                 // conversion coefficients
  1911.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  1912.                 final double c     = angles[0] * cosE0 - angles[2];
  1913.                 final double opC2  = 1 + c * c;

  1914.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  1915.                 return new double[] {
  1916.                     (dX - c * dY) / (sinEA * opC2),
  1917.                     (dY + c * dX) / opC2
  1918.                 };
  1919.             }

  1920.         };

  1921.     }

  1922.     /** Load the Love numbers.
  1923.      * @param nameLove name of the Love number resource
  1924.      * @return Love numbers
  1925.      * @exception OrekitException if the Love numbers embedded in the
  1926.      * library cannot be read
  1927.      */
  1928.     protected LoveNumbers loadLoveNumbers(final String nameLove) throws OrekitException {
  1929.         try {

  1930.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  1931.             final double[][] real      = new double[4][];
  1932.             final double[][] imaginary = new double[4][];
  1933.             final double[][] plus      = new double[4][];
  1934.             for (int i = 0; i < real.length; ++i) {
  1935.                 real[i]      = new double[i + 1];
  1936.                 imaginary[i] = new double[i + 1];
  1937.                 plus[i]      = new double[i + 1];
  1938.             }

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

  1940.                 if (stream == null) {
  1941.                     // this should never happen with files embedded within Orekit
  1942.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  1943.                 }

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

  1946.                     String line = reader.readLine();
  1947.                     int lineNumber = 1;

  1948.                     // look for the Love numbers
  1949.                     while (line != null) {

  1950.                         line = line.trim();
  1951.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  1952.                             final String[] fields = line.split("\\p{Space}+");
  1953.                             if (fields.length != 5) {
  1954.                                 // this should never happen with files embedded within Orekit
  1955.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  1956.                                                           lineNumber, nameLove, line);
  1957.                             }
  1958.                             final int n = Integer.parseInt(fields[0]);
  1959.                             final int m = Integer.parseInt(fields[1]);
  1960.                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  1961.                                 // this should never happen with files embedded within Orekit
  1962.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  1963.                                                           lineNumber, nameLove, line);

  1964.                             }
  1965.                             real[n][m]      = Double.parseDouble(fields[2]);
  1966.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  1967.                             plus[n][m]      = Double.parseDouble(fields[4]);
  1968.                         }

  1969.                         // next line
  1970.                         lineNumber++;
  1971.                         line = reader.readLine();

  1972.                     }
  1973.                 }
  1974.             }

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

  1976.         } catch (IOException ioe) {
  1977.             // this should never happen with files embedded within Orekit
  1978.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  1979.         }
  1980.     }

  1981.     /** Get a data stream.
  1982.      * @param name file name of the resource stream
  1983.      * @return stream
  1984.      */
  1985.     private static InputStream getStream(final String name) {
  1986.         return IERSConventions.class.getResourceAsStream(name);
  1987.     }

  1988.     /** Correction to equation of equinoxes.
  1989.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  1990.      * taking effect since 1997-02-27 for continuity
  1991.      * </p>
  1992.      */
  1993.     private static class IAU1994ResolutionC7 {

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

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

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

  2003.         /** Evaluate the correction.
  2004.          * @param arguments Delaunay for nutation
  2005.          * @return correction value (0 before 1997-02-27)
  2006.          */
  2007.         public static double value(final DelaunayArguments arguments) {
  2008.             if (arguments.getDate().compareTo(MODEL_START) >= 0) {

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

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

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

  2015.             } else {
  2016.                 return 0.0;
  2017.             }
  2018.         }

  2019.         /** Evaluate the correction.
  2020.          * @param arguments Delaunay for nutation
  2021.          * @param <T> type of the field elements
  2022.          * @return correction value (0 before 1997-02-27)
  2023.          */
  2024.         public static <T extends RealFieldElement<T>> T value(final FieldDelaunayArguments<T> arguments) {
  2025.             if (arguments.getDate().toAbsoluteDate().compareTo(MODEL_START) >= 0) {

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

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

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

  2032.             } else {
  2033.                 return arguments.getDate().getField().getZero();
  2034.             }
  2035.         }

  2036.     };

  2037.     /** Stellar angle model.
  2038.      * <p>
  2039.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  2040.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  2041.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  2042.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  2043.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  2044.      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
  2045.      * </p>
  2046.      * <p>
  2047.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  2048.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  2049.      * IERS conventions 2003 and 2010.
  2050.      * </p>
  2051.      */
  2052.     private static class StellarAngleCapitaine implements TimeScalarFunction {

  2053.         /** Reference date of Capitaine's Earth Rotation Angle model. */
  2054.         private static final AbsoluteDate REFERENCE_DATE = new AbsoluteDate(DateComponents.J2000_EPOCH,
  2055.                                                                             TimeComponents.H12,
  2056.                                                                             TimeScalesFactory.getTAI());

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

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

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

  2065.         /** UT1 time scale. */
  2066.         private final TimeScale ut1;

  2067.         /** Simple constructor.
  2068.          * @param ut1 UT1 time scale
  2069.          */
  2070.         StellarAngleCapitaine(final TimeScale ut1) {
  2071.             this.ut1 = ut1;
  2072.         }

  2073.         /** Get the rotation rate.
  2074.          * @return rotation rate
  2075.          */
  2076.         public double getRate() {
  2077.             return ERA_1A + ERA_1B;
  2078.         }

  2079.         /** {@inheritDoc} */
  2080.         @Override
  2081.         public double value(final AbsoluteDate date) {

  2082.             // split the date offset as a full number of days plus a smaller part
  2083.             final int secondsInDay = 86400;
  2084.             final double dt  = date.durationFrom(REFERENCE_DATE);
  2085.             final long days  = ((long) dt) / secondsInDay;
  2086.             final double dtA = secondsInDay * days;
  2087.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

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

  2089.         }

  2090.         /** {@inheritDoc} */
  2091.         @Override
  2092.         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2093.             // split the date offset as a full number of days plus a smaller part
  2094.             final int secondsInDay = 86400;
  2095.             final T dt  = date.durationFrom(REFERENCE_DATE);
  2096.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2097.             final double dtA = secondsInDay * days;
  2098.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));

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

  2100.         }

  2101.     }

  2102.     /** Mean pole. */
  2103.     private static class MeanPole implements TimeStamped, Serializable {

  2104.         /** Serializable UID. */
  2105.         private static final long serialVersionUID = 20131028l;

  2106.         /** Date. */
  2107.         private final AbsoluteDate date;

  2108.         /** X coordinate. */
  2109.         private double x;

  2110.         /** Y coordinate. */
  2111.         private double y;

  2112.         /** Simple constructor.
  2113.          * @param date date
  2114.          * @param x x coordinate
  2115.          * @param y y coordinate
  2116.          */
  2117.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2118.             this.date = date;
  2119.             this.x    = x;
  2120.             this.y    = y;
  2121.         }

  2122.         /** {@inheritDoc} */
  2123.         @Override
  2124.         public AbsoluteDate getDate() {
  2125.             return date;
  2126.         }

  2127.         /** Get x coordinate.
  2128.          * @return x coordinate
  2129.          */
  2130.         public double getX() {
  2131.             return x;
  2132.         }

  2133.         /** Get y coordinate.
  2134.          * @return y coordinate
  2135.          */
  2136.         public double getY() {
  2137.             return y;
  2138.         }

  2139.     }

  2140.     /** Local class for precession function. */
  2141.     private class PrecessionFunction implements TimeVectorFunction {

  2142.         /** Polynomial nutation for psiA. */
  2143.         private final PolynomialNutation psiA;

  2144.         /** Polynomial nutation for omegaA. */
  2145.         private final PolynomialNutation omegaA;

  2146.         /** Polynomial nutation for chiA. */
  2147.         private final PolynomialNutation chiA;

  2148.         /** Simple constructor.
  2149.          * @param psiA polynomial nutation for psiA
  2150.          * @param omegaA polynomial nutation for omegaA
  2151.          * @param chiA polynomial nutation for chiA
  2152.          */
  2153.         PrecessionFunction(final PolynomialNutation psiA,
  2154.                            final PolynomialNutation omegaA,
  2155.                            final PolynomialNutation chiA) {
  2156.             this.psiA   = psiA;
  2157.             this.omegaA = omegaA;
  2158.             this.chiA   = chiA;
  2159.         }


  2160.         /** {@inheritDoc} */
  2161.         @Override
  2162.         public double[] value(final AbsoluteDate date) {
  2163.             final double tc = evaluateTC(date);
  2164.             return new double[] {
  2165.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2166.             };
  2167.         }

  2168.         /** {@inheritDoc} */
  2169.         @Override
  2170.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2171.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2172.             final T tc = evaluateTC(date);
  2173.             a[0] = psiA.value(tc);
  2174.             a[1] = omegaA.value(tc);
  2175.             a[2] = chiA.value(tc);
  2176.             return a;
  2177.         }

  2178.     }

  2179.     /** Local class for tides frequency function. */
  2180.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2181.         /** Nutation arguments. */
  2182.         private final FundamentalNutationArguments arguments;

  2183.         /** Correction series. */
  2184.         private final PoissonSeries.CompiledSeries kSeries;

  2185.         /** Simple constructor.
  2186.          * @param arguments nutation arguments
  2187.          * @param c20Series correction series for the C20 term
  2188.          * @param c21Series correction series for the C21 term
  2189.          * @param s21Series correction series for the S21 term
  2190.          * @param c22Series correction series for the C22 term
  2191.          * @param s22Series correction series for the S22 term
  2192.          */
  2193.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2194.                                         final PoissonSeries c20Series,
  2195.                                         final PoissonSeries c21Series,
  2196.                                         final PoissonSeries s21Series,
  2197.                                         final PoissonSeries c22Series,
  2198.                                         final PoissonSeries s22Series) {
  2199.             this.arguments = arguments;
  2200.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2201.         }


  2202.         /** {@inheritDoc} */
  2203.         @Override
  2204.         public double[] value(final AbsoluteDate date) {
  2205.             return kSeries.value(arguments.evaluateAll(date));
  2206.         }

  2207.         /** {@inheritDoc} */
  2208.         @Override
  2209.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2210.             return kSeries.value(arguments.evaluateAll(date));
  2211.         }

  2212.     }

  2213.     /** Local class for EOP tidal corrections. */
  2214.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2215.         /** Nutation arguments. */
  2216.         private final FundamentalNutationArguments arguments;

  2217.         /** Correction series. */
  2218.         private final PoissonSeries.CompiledSeries correctionSeries;

  2219.         /** Simple constructor.
  2220.          * @param arguments nutation arguments
  2221.          * @param xSeries correction series for the x coordinate
  2222.          * @param ySeries correction series for the y coordinate
  2223.          * @param ut1Series correction series for the UT1
  2224.          */
  2225.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2226.                            final PoissonSeries xSeries,
  2227.                            final PoissonSeries ySeries,
  2228.                            final PoissonSeries ut1Series) {
  2229.             this.arguments        = arguments;
  2230.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2231.         }

  2232.         /** {@inheritDoc} */
  2233.         @Override
  2234.         public double[] value(final AbsoluteDate date) {
  2235.             final BodiesElements elements = arguments.evaluateAll(date);
  2236.             final double[] correction    = correctionSeries.value(elements);
  2237.             final double[] correctionDot = correctionSeries.derivative(elements);
  2238.             return new double[] {
  2239.                 correction[0],
  2240.                 correction[1],
  2241.                 correction[2],
  2242.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2243.             };
  2244.         }

  2245.         /** {@inheritDoc} */
  2246.         @Override
  2247.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2248.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2249.             final T[] correction    = correctionSeries.value(elements);
  2250.             final T[] correctionDot = correctionSeries.derivative(elements);
  2251.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2252.             a[0] = correction[0];
  2253.             a[1] = correction[1];
  2254.             a[2] = correction[2];
  2255.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2256.             return a;
  2257.         }

  2258.     }

  2259. }