IERSConventions.java

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

  93.         /** {@inheritDoc} */
  94.         @Override
  95.         public TimeScalarFunction getMeanObliquityFunction() {

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

  102.             return new TimeScalarFunction() {

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

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

  113.             };

  114.         }

  115.         /** {@inheritDoc} */
  116.         @Override
  117.         public TimeVectorFunction getXYSpXY2Function() {

  118.             // set up nutation arguments
  119.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

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

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

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

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

  150.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  151.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

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

  157.             final PoissonSeries.CompiledSeries xySum =
  158.                     PoissonSeries.compile(xSum, ySum);

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

  167.             return new TimeVectorFunction() {

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

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

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

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

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

  188.                     return new double[] {
  189.                         x, y, sPxy2
  190.                     };

  191.                 }

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

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

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

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

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

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

  222.                 }

  223.             };

  224.         }

  225.         /** {@inheritDoc} */
  226.         @Override
  227.         public TimeVectorFunction getPrecessionFunction() {

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

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

  249.         }

  250.         /** {@inheritDoc} */
  251.         @Override
  252.         public TimeVectorFunction getNutationFunction() {

  253.             // set up nutation arguments
  254.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

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

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

  269.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  270.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  271.             return new TimeVectorFunction() {

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

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

  292.             };

  293.         }

  294.         /** {@inheritDoc} */
  295.         @Override
  296.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {

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

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

  307.             return new TimeScalarFunction() {

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

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

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

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

  320.                 }

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

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

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

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

  333.                 }

  334.             };

  335.         }

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

  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.             // obliquity
  375.             final TimeScalarFunction epsilonA = getMeanObliquityFunction();

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

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

  380.             return new TimeScalarFunction() {

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

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

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

  393.                 }

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

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

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

  406.                 }

  407.             };

  408.         }

  409.         /** {@inheritDoc} */
  410.         @Override
  411.         public TimeVectorFunction getEOPTidalCorrection() {

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

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

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

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

  442.         }

  443.         /** {@inheritDoc} */
  444.         public LoveNumbers getLoveNumbers() {
  445.             return loadLoveNumbers(LOVE_NUMBERS);
  446.         }

  447.         /** {@inheritDoc} */
  448.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {

  449.             // set up nutation arguments
  450.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

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

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

  488.             return new TideFrequencyDependenceFunction(arguments,
  489.                                                        c20Series,
  490.                                                        c21Series, s21Series,
  491.                                                        c22Series, s22Series);

  492.         }

  493.         /** {@inheritDoc} */
  494.         @Override
  495.         public double getPermanentTide() {
  496.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  497.         }

  498.         /** {@inheritDoc} */
  499.         @Override
  500.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  501.             // constants from IERS 1996 page 47
  502.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  503.             final double coupling     =  0.00112;

  504.             return new TimeVectorFunction() {

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

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

  523.             };
  524.         }

  525.         /** {@inheritDoc} */
  526.         @Override
  527.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  528.             return new TimeVectorFunction() {

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

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

  543.             };
  544.         }

  545.         /** {@inheritDoc} */
  546.         @Override
  547.         public double[] getNominalTidalDisplacement() {

  548.             //  // elastic Earth values
  549.             //  return new double[] {
  550.             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  551.             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
  552.             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  553.             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  554.             //      // H₀
  555.             //      -0.31460
  556.             //  };

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

  566.         }

  567.         /** {@inheritDoc} */
  568.         @Override
  569.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  570.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  571.                                                                   18, 17, -1, 18, -1);
  572.         }

  573.         /** {@inheritDoc} */
  574.         @Override
  575.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  576.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  577.                                                                 20, 17, 19, 18, 20);
  578.         }

  579.     },

  580.     /** Constant for IERS 2003 conventions. */
  581.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  614.         /** {@inheritDoc} */
  615.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
  616.             return new FundamentalNutationArguments(this, timeScale,
  617.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  618.         }

  619.         /** {@inheritDoc} */
  620.         @Override
  621.         public TimeScalarFunction getMeanObliquityFunction() {

  622.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  623.             final PolynomialNutation epsilonA =
  624.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  625.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  626.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  627.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  628.             return new TimeScalarFunction() {

  629.                 /** {@inheritDoc} */
  630.                 @Override
  631.                 public double value(final AbsoluteDate date) {
  632.                     return epsilonA.value(evaluateTC(date));
  633.                 }

  634.                 /** {@inheritDoc} */
  635.                 @Override
  636.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  637.                     return epsilonA.value(evaluateTC(date));
  638.                 }

  639.             };

  640.         }

  641.         /** {@inheritDoc} */
  642.         @Override
  643.         public TimeVectorFunction getXYSpXY2Function() {

  644.             // set up nutation arguments
  645.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  646.             // set up Poisson series
  647.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  648.             final PoissonSeriesParser parser =
  649.                     new PoissonSeriesParser(17).
  650.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  651.                         withFirstDelaunay(4).
  652.                         withFirstPlanetary(9).
  653.                         withSinCos(0, 2, microAS, 3, microAS);

  654.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  655.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  656.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  657.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  658.             // create a function evaluating the series
  659.             return new TimeVectorFunction() {

  660.                 /** {@inheritDoc} */
  661.                 @Override
  662.                 public double[] value(final AbsoluteDate date) {
  663.                     return xys.value(arguments.evaluateAll(date));
  664.                 }

  665.                 /** {@inheritDoc} */
  666.                 @Override
  667.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  668.                     return xys.value(arguments.evaluateAll(date));
  669.                 }

  670.             };

  671.         }


  672.         /** {@inheritDoc} */
  673.         @Override
  674.         public TimeVectorFunction getPrecessionFunction() {

  675.             // set up the conventional polynomials
  676.             // the following values are from equation 32 in IERS 2003 conventions
  677.             final PolynomialNutation psiA =
  678.                     new PolynomialNutation(    0.0,
  679.                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  680.                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  681.                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  682.             final PolynomialNutation omegaA =
  683.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  684.                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  685.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  686.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  687.             final PolynomialNutation chiA =
  688.                     new PolynomialNutation( 0.0,
  689.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  690.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  691.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

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

  693.         }

  694.         /** {@inheritDoc} */
  695.         @Override
  696.         public TimeVectorFunction getNutationFunction() {

  697.             // set up nutation arguments
  698.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  699.             // set up Poisson series
  700.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  701.             final PoissonSeriesParser luniSolarParser =
  702.                     new PoissonSeriesParser(14).withFirstDelaunay(1);
  703.             final PoissonSeriesParser luniSolarPsiParser =
  704.                     luniSolarParser.
  705.                     withSinCos(0, 7, milliAS, 11, milliAS).
  706.                     withSinCos(1, 8, milliAS, 12, milliAS);
  707.             final PoissonSeries psiLuniSolarSeries =
  708.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
  709.             final PoissonSeriesParser luniSolarEpsilonParser =
  710.                     luniSolarParser.
  711.                     withSinCos(0, 13, milliAS, 9, milliAS).
  712.                     withSinCos(1, 14, milliAS, 10, milliAS);
  713.             final PoissonSeries epsilonLuniSolarSeries =
  714.                     luniSolarEpsilonParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  715.             final PoissonSeriesParser planetaryParser =
  716.                     new PoissonSeriesParser(21).
  717.                         withFirstDelaunay(2).
  718.                         withFirstPlanetary(7);
  719.             final PoissonSeriesParser planetaryPsiParser =
  720.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  721.             final PoissonSeries psiPlanetarySeries =
  722.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
  723.             final PoissonSeriesParser planetaryEpsilonParser =
  724.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  725.             final PoissonSeries epsilonPlanetarySeries =
  726.                     planetaryEpsilonParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  727.             final PoissonSeries.CompiledSeries luniSolarSeries =
  728.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  729.             final PoissonSeries.CompiledSeries planetarySeries =
  730.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  731.             return new TimeVectorFunction() {

  732.                 /** {@inheritDoc} */
  733.                 @Override
  734.                 public double[] value(final AbsoluteDate date) {
  735.                     final BodiesElements elements = arguments.evaluateAll(date);
  736.                     final double[] luniSolar = luniSolarSeries.value(elements);
  737.                     final double[] planetary = planetarySeries.value(elements);
  738.                     return new double[] {
  739.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  740.                         IAU1994ResolutionC7.value(elements)
  741.                     };
  742.                 }

  743.                 /** {@inheritDoc} */
  744.                 @Override
  745.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  746.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  747.                     final T[] luniSolar = luniSolarSeries.value(elements);
  748.                     final T[] planetary = planetarySeries.value(elements);
  749.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  750.                     result[0] = luniSolar[0].add(planetary[0]);
  751.                     result[1] = luniSolar[1].add(planetary[1]);
  752.                     result[2] = IAU1994ResolutionC7.value(elements);
  753.                     return result;
  754.                 }

  755.             };

  756.         }

  757.         /** {@inheritDoc} */
  758.         @Override
  759.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {

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

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

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

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

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

  785.             };

  786.         }

  787.         /** {@inheritDoc} */
  788.         @Override
  789.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {

  790.             // Earth Rotation Angle
  791.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  792.             // Polynomial part of the apparent sidereal time series
  793.             // which is the opposite of Equation of Origins (EO)
  794.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  795.             final PoissonSeriesParser parser =
  796.                     new PoissonSeriesParser(17).
  797.                         withFirstDelaunay(4).
  798.                         withFirstPlanetary(9).
  799.                         withSinCos(0, 2, microAS, 3, microAS).
  800.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  801.             final PolynomialNutation minusEO =
  802.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  803.             // create a function evaluating the series
  804.             return new TimeScalarFunction() {

  805.                 /** {@inheritDoc} */
  806.                 @Override
  807.                 public double value(final AbsoluteDate date) {
  808.                     return era.getRate() + minusEO.derivative(evaluateTC(date));
  809.                 }

  810.                 /** {@inheritDoc} */
  811.                 @Override
  812.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  813.                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
  814.                 }

  815.             };

  816.         }

  817.         /** {@inheritDoc} */
  818.         @Override
  819.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory) {

  820.             // set up nutation arguments
  821.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  822.             // mean obliquity function
  823.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  824.             // set up Poisson series
  825.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  826.             final PoissonSeriesParser luniSolarPsiParser =
  827.                     new PoissonSeriesParser(14).
  828.                         withFirstDelaunay(1).
  829.                         withSinCos(0, 7, milliAS, 11, milliAS).
  830.                         withSinCos(1, 8, milliAS, 12, milliAS);
  831.             final PoissonSeries psiLuniSolarSeries =
  832.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  833.             final PoissonSeriesParser planetaryPsiParser =
  834.                     new PoissonSeriesParser(21).
  835.                         withFirstDelaunay(2).
  836.                         withFirstPlanetary(7).
  837.                         withSinCos(0, 17, milliAS, 18, milliAS);
  838.             final PoissonSeries psiPlanetarySeries =
  839.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  840.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  841.             final PoissonSeriesParser gstParser =
  842.                     new PoissonSeriesParser(17).
  843.                         withFirstDelaunay(4).
  844.                         withFirstPlanetary(9).
  845.                         withSinCos(0, 2, microAS, 3, microAS).
  846.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  847.             final PoissonSeries gstSeries = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  848.             final PoissonSeries.CompiledSeries psiGstSeries =
  849.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  850.             // ERA function
  851.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  852.             return new TimeScalarFunction() {

  853.                 /** {@inheritDoc} */
  854.                 @Override
  855.                 public double value(final AbsoluteDate date) {

  856.                     // evaluate equation of origins
  857.                     final BodiesElements elements = arguments.evaluateAll(date);
  858.                     final double[] angles = psiGstSeries.value(elements);
  859.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  860.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  861.                     final double epsilonA = epsilon.value(date);

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

  865.                 }

  866.                 /** {@inheritDoc} */
  867.                 @Override
  868.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  869.                     // evaluate equation of origins
  870.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  871.                     final T[] angles = psiGstSeries.value(elements);
  872.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  873.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  874.                     final T epsilonA = epsilon.value(date);

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

  878.                 }

  879.             };

  880.         }

  881.         /** {@inheritDoc} */
  882.         @Override
  883.         public TimeVectorFunction getEOPTidalCorrection() {

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

  890.             // set up Poisson series
  891.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  892.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  893.                     withOptionalColumn(1).
  894.                     withGamma(2).
  895.                     withFirstDelaunay(3);
  896.             final PoissonSeries xSeries =
  897.                     xyParser.
  898.                     withSinCos(0, 10, microAS, 11, microAS).
  899.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  900.             final PoissonSeries ySeries =
  901.                     xyParser.
  902.                     withSinCos(0, 12, microAS, 13, microAS).
  903.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  904.             final double microS = 1.0e-6;
  905.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  906.                     withOptionalColumn(1).
  907.                     withGamma(2).
  908.                     withFirstDelaunay(3).
  909.                     withSinCos(0, 10, microS, 11, microS);
  910.             final PoissonSeries ut1Series =
  911.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

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

  913.         }

  914.         /** {@inheritDoc} */
  915.         public LoveNumbers getLoveNumbers() {
  916.             return loadLoveNumbers(LOVE_NUMBERS);
  917.         }

  918.         /** {@inheritDoc} */
  919.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {

  920.             // set up nutation arguments
  921.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  922.             // set up Poisson series
  923.             final PoissonSeriesParser k20Parser =
  924.                     new PoissonSeriesParser(18).
  925.                         withOptionalColumn(1).
  926.                         withDoodson(4, 2).
  927.                         withFirstDelaunay(10);
  928.             final PoissonSeriesParser k21Parser =
  929.                     new PoissonSeriesParser(18).
  930.                         withOptionalColumn(1).
  931.                         withDoodson(4, 3).
  932.                         withFirstDelaunay(10);
  933.             final PoissonSeriesParser k22Parser =
  934.                     new PoissonSeriesParser(16).
  935.                         withOptionalColumn(1).
  936.                         withDoodson(4, 2).
  937.                         withFirstDelaunay(10);

  938.             final double pico = 1.0e-12;
  939.             final PoissonSeries c20Series =
  940.                     k20Parser.
  941.                   withSinCos(0, 18, -pico, 16, pico).
  942.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  943.             final PoissonSeries c21Series =
  944.                     k21Parser.
  945.                     withSinCos(0, 17, pico, 18, pico).
  946.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  947.             final PoissonSeries s21Series =
  948.                     k21Parser.
  949.                     withSinCos(0, 18, -pico, 17, pico).
  950.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  951.             final PoissonSeries c22Series =
  952.                     k22Parser.
  953.                     withSinCos(0, -1, pico, 16, pico).
  954.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  955.             final PoissonSeries s22Series =
  956.                     k22Parser.
  957.                     withSinCos(0, 16, -pico, -1, pico).
  958.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  959.             return new TideFrequencyDependenceFunction(arguments,
  960.                                                        c20Series,
  961.                                                        c21Series, s21Series,
  962.                                                        c22Series, s22Series);

  963.         }

  964.         /** {@inheritDoc} */
  965.         @Override
  966.         public double getPermanentTide() {
  967.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  968.         }

  969.         /** {@inheritDoc} */
  970.         @Override
  971.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  972.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  973.             final TimeScale utc = TimeScalesFactory.getUTC();
  974.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  975.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  976.                     /** {@inheritDoc} */
  977.                     @Override
  978.                     public MeanPole convert(final double[] rawFields) {
  979.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  980.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  981.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  982.                     }
  983.                 };
  984.             final SimpleTimeStampedTableParser<MeanPole> parser =
  985.                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
  986.             final List<MeanPole> annualPoleList = parser.parse(getStream(ANNUAL_POLE), ANNUAL_POLE);
  987.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  988.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  989.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  990.                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);

  991.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  992.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  993.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  994.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  995.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  996.             // constants from IERS 2003, section 6.2
  997.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  998.             final double ratio        =  0.00115;

  999.             return new TimeVectorFunction() {

  1000.                 /** {@inheritDoc} */
  1001.                 @Override
  1002.                 public double[] value(final AbsoluteDate date) {

  1003.                     // we can't compute anything before the range covered by the annual pole file
  1004.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  1005.                         return new double[] {
  1006.                             0.0, 0.0
  1007.                         };
  1008.                     }

  1009.                     // evaluate mean pole
  1010.                     double meanPoleX = 0;
  1011.                     double meanPoleY = 0;
  1012.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  1013.                         // we are within the range covered by the annual pole file,
  1014.                         // we interpolate within it
  1015.                         try {
  1016.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  1017.                             annualCache.getNeighbors(date).forEach(neighbor ->
  1018.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  1019.                                                             new double[] {
  1020.                                                                 neighbor.getX(), neighbor.getY()
  1021.                                                             }));
  1022.                             final double[] interpolated = interpolator.value(0);
  1023.                             meanPoleX = interpolated[0];
  1024.                             meanPoleY = interpolated[1];
  1025.                         } catch (TimeStampedCacheException tsce) {
  1026.                             // this should never happen
  1027.                             throw new OrekitInternalError(tsce);
  1028.                         }
  1029.                     } else {

  1030.                         // we are after the range covered by the annual pole file,
  1031.                         // we use the polynomial extension
  1032.                         final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1033.                         meanPoleX = xp0 + t * xp0Dot;
  1034.                         meanPoleY = yp0 + t * yp0Dot;

  1035.                     }

  1036.                     // evaluate wobble variables
  1037.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1038.                     final double m1 = correction.getXp() - meanPoleX;
  1039.                     final double m2 = meanPoleY - correction.getYp();

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

  1070.                 }

  1071.                 /** {@inheritDoc} */
  1072.                 @Override
  1073.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1079.                     // evaluate mean pole
  1080.                     T meanPoleX = date.getField().getZero();
  1081.                     T meanPoleY = date.getField().getZero();
  1082.                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
  1083.                         // we are within the range covered by the annual pole file,
  1084.                         // we interpolate within it
  1085.                         try {
  1086.                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
  1087.                             final T[] y = MathArrays.buildArray(date.getField(), 2);
  1088.                             final T zero = date.getField().getZero();
  1089.                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
  1090.                                                                                                        // for example removing derivatives
  1091.                                                                                                        // if T was DerivativeStructure
  1092.                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
  1093.                                 y[0] = zero.add(neighbor.getX());
  1094.                                 y[1] = zero.add(neighbor.getY());
  1095.                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
  1096.                             });
  1097.                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
  1098.                             meanPoleX = interpolated[0];
  1099.                             meanPoleY = interpolated[1];
  1100.                         } catch (TimeStampedCacheException tsce) {
  1101.                             // this should never happen
  1102.                             throw new OrekitInternalError(tsce);
  1103.                         }
  1104.                     } else {

  1105.                         // we are after the range covered by the annual pole file,
  1106.                         // we use the polynomial extension
  1107.                         final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1108.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1109.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1110.                     }

  1111.                     // evaluate wobble variables
  1112.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1113.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1114.                     final T m2 = meanPoleY.subtract(correction.getYp());

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

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

  1144.                     return a;

  1145.                 }

  1146.             };

  1147.         }

  1148.         /** {@inheritDoc} */
  1149.         @Override
  1150.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1151.             return new TimeVectorFunction() {

  1152.                 /** {@inheritDoc} */
  1153.                 @Override
  1154.                 public double[] value(final AbsoluteDate date) {
  1155.                     // there are no model for ocean pole tide prior to conventions 2010
  1156.                     return new double[] {
  1157.                         0.0, 0.0
  1158.                     };
  1159.                 }

  1160.                 /** {@inheritDoc} */
  1161.                 @Override
  1162.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1163.                     // there are no model for ocean pole tide prior to conventions 2010
  1164.                     return MathArrays.buildArray(date.getField(), 2);
  1165.                 }

  1166.             };
  1167.         }

  1168.         /** {@inheritDoc} */
  1169.         @Override
  1170.         public double[] getNominalTidalDisplacement() {
  1171.             return new double[] {
  1172.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1173.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1174.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1175.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1176.                 // H₀
  1177.                 -0.31460
  1178.             };
  1179.         }

  1180.         /** {@inheritDoc} */
  1181.         @Override
  1182.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1183.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1184.                                                                   18, 15, 16, 17, 18);
  1185.         }

  1186.         /** {@inheritDoc} */
  1187.         @Override
  1188.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1189.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1190.                                                                 18, 15, 16, 17, 18);
  1191.         }

  1192.     },

  1193.     /** Constant for IERS 2010 conventions. */
  1194.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1225.         /** {@inheritDoc} */
  1226.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
  1227.             return new FundamentalNutationArguments(this, timeScale,
  1228.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  1229.         }

  1230.         /** {@inheritDoc} */
  1231.         @Override
  1232.         public TimeScalarFunction getMeanObliquityFunction() {

  1233.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1234.             final PolynomialNutation epsilonA =
  1235.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1236.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1237.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1238.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1239.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1240.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1241.             return new TimeScalarFunction() {

  1242.                 /** {@inheritDoc} */
  1243.                 @Override
  1244.                 public double value(final AbsoluteDate date) {
  1245.                     return epsilonA.value(evaluateTC(date));
  1246.                 }

  1247.                 /** {@inheritDoc} */
  1248.                 @Override
  1249.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1250.                     return epsilonA.value(evaluateTC(date));
  1251.                 }

  1252.             };

  1253.         }

  1254.         /** {@inheritDoc} */
  1255.         @Override
  1256.         public TimeVectorFunction getXYSpXY2Function() {

  1257.             // set up nutation arguments
  1258.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1259.             // set up Poisson series
  1260.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1261.             final PoissonSeriesParser parser =
  1262.                     new PoissonSeriesParser(17).
  1263.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1264.                         withFirstDelaunay(4).
  1265.                         withFirstPlanetary(9).
  1266.                         withSinCos(0, 2, microAS, 3, microAS);
  1267.             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  1268.             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  1269.             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  1270.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  1271.             // create a function evaluating the series
  1272.             return new TimeVectorFunction() {

  1273.                 /** {@inheritDoc} */
  1274.                 @Override
  1275.                 public double[] value(final AbsoluteDate date) {
  1276.                     return xys.value(arguments.evaluateAll(date));
  1277.                 }

  1278.                 /** {@inheritDoc} */
  1279.                 @Override
  1280.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1281.                     return xys.value(arguments.evaluateAll(date));
  1282.                 }

  1283.             };

  1284.         }

  1285.         /** {@inheritDoc} */
  1286.         public LoveNumbers getLoveNumbers() {
  1287.             return loadLoveNumbers(LOVE_NUMBERS);
  1288.         }

  1289.         /** {@inheritDoc} */
  1290.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {

  1291.             // set up nutation arguments
  1292.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  1293.             // set up Poisson series
  1294.             final PoissonSeriesParser k20Parser =
  1295.                     new PoissonSeriesParser(18).
  1296.                         withOptionalColumn(1).
  1297.                         withDoodson(4, 2).
  1298.                         withFirstDelaunay(10);
  1299.             final PoissonSeriesParser k21Parser =
  1300.                     new PoissonSeriesParser(18).
  1301.                         withOptionalColumn(1).
  1302.                         withDoodson(4, 3).
  1303.                         withFirstDelaunay(10);
  1304.             final PoissonSeriesParser k22Parser =
  1305.                     new PoissonSeriesParser(16).
  1306.                         withOptionalColumn(1).
  1307.                         withDoodson(4, 2).
  1308.                         withFirstDelaunay(10);

  1309.             final double pico = 1.0e-12;
  1310.             final PoissonSeries c20Series =
  1311.                     k20Parser.
  1312.                     withSinCos(0, 18, -pico, 16, pico).
  1313.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  1314.             final PoissonSeries c21Series =
  1315.                     k21Parser.
  1316.                     withSinCos(0, 17, pico, 18, pico).
  1317.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1318.             final PoissonSeries s21Series =
  1319.                     k21Parser.
  1320.                     withSinCos(0, 18, -pico, 17, pico).
  1321.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1322.             final PoissonSeries c22Series =
  1323.                     k22Parser.
  1324.                     withSinCos(0, -1, pico, 16, pico).
  1325.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  1326.             final PoissonSeries s22Series =
  1327.                     k22Parser.
  1328.                     withSinCos(0, 16, -pico, -1, pico).
  1329.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  1330.             return new TideFrequencyDependenceFunction(arguments,
  1331.                                                        c20Series,
  1332.                                                        c21Series, s21Series,
  1333.                                                        c22Series, s22Series);

  1334.         }

  1335.         /** {@inheritDoc} */
  1336.         @Override
  1337.         public double getPermanentTide() {
  1338.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1339.         }

  1340.         /** Compute pole wobble variables m₁ and m₂.
  1341.          * @param date current date
  1342.          * @param eopHistory EOP history
  1343.          * @return array containing m₁ and m₂
  1344.          */
  1345.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

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

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

  1379.             // evaluate wobble variables
  1380.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1381.             final double m1 = correction.getXp() - meanPoleX;
  1382.             final double m2 = meanPoleY - correction.getYp();

  1383.             return new double[] {
  1384.                 m1, m2
  1385.             };

  1386.         }

  1387.         /** Compute pole wobble variables m₁ and m₂.
  1388.          * @param date current date
  1389.          * @param <T> type of the field elements
  1390.          * @param eopHistory EOP history
  1391.          * @return array containing m₁ and m₂
  1392.          */
  1393.         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1394.             // polynomial model from IERS 2010, table 7.7
  1395.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1396.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1397.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1398.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1399.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1400.             // evaluate mean pole
  1401.             final double[] xPolynomial;
  1402.             final double[] yPolynomial;
  1403.             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
  1404.                 xPolynomial = new double[] {
  1405.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1406.                 };
  1407.                 yPolynomial = new double[] {
  1408.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1409.                 };
  1410.             } else {
  1411.                 xPolynomial = new double[] {
  1412.                     23.513 * f0, 7.6141 * f1
  1413.                 };
  1414.                 yPolynomial = new double[] {
  1415.                     358.891 * f0,  -0.6287 * f1
  1416.                 };
  1417.             }
  1418.             T meanPoleX = date.getField().getZero();
  1419.             T meanPoleY = date.getField().getZero();
  1420.             final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1421.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1422.                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
  1423.             }
  1424.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1425.                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
  1426.             }

  1427.             // evaluate wobble variables
  1428.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1429.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1430.             m[0] = correction.getXp().subtract(meanPoleX);
  1431.             m[1] = meanPoleY.subtract(correction.getYp());

  1432.             return m;

  1433.         }

  1434.         /** {@inheritDoc} */
  1435.         @Override
  1436.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1437.             // constants from IERS 2010, section 6.4
  1438.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1439.             final double ratio        =  0.00115;

  1440.             return new TimeVectorFunction() {

  1441.                 /** {@inheritDoc} */
  1442.                 @Override
  1443.                 public double[] value(final AbsoluteDate date) {

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

  1446.                     return new double[] {
  1447.                         // the following correspond to the equations published in IERS 2010 conventions,
  1448.                         // section 6.4 page 94. The equations read:
  1449.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1450.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1451.                         // These equations seem to fix what was probably a sign error in IERS 2003
  1452.                         // conventions section 6.2 page 65. In this older publication, the equations read:
  1453.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1454.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1455.                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
  1456.                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
  1457.                     };

  1458.                 }

  1459.                 /** {@inheritDoc} */
  1460.                 @Override
  1461.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1465.                     // the following correspond to the equations published in IERS 2010 conventions,
  1466.                     // section 6.4 page 94. The equations read:
  1467.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1468.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1469.                     // These equations seem to fix what was probably a sign error in IERS 2003
  1470.                     // conventions section 6.2 page 65. In this older publication, the equations read:
  1471.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1472.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1473.                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
  1474.                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);

  1475.                     return a;

  1476.                 }

  1477.             };

  1478.         }

  1479.         /** {@inheritDoc} */
  1480.         @Override
  1481.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1482.             return new TimeVectorFunction() {

  1483.                 /** {@inheritDoc} */
  1484.                 @Override
  1485.                 public double[] value(final AbsoluteDate date) {

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

  1488.                     return new double[] {
  1489.                         // the following correspond to the equations published in IERS 2010 conventions,
  1490.                         // section 6.4 page 94 equation 6.24:
  1491.                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1492.                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1493.                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
  1494.                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
  1495.                     };

  1496.                 }

  1497.                 /** {@inheritDoc} */
  1498.                 @Override
  1499.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1509.                     return v;

  1510.                 }

  1511.             };

  1512.         }

  1513.         /** {@inheritDoc} */
  1514.         @Override
  1515.         public TimeVectorFunction getPrecessionFunction() {

  1516.             // set up the conventional polynomials
  1517.             // the following values are from equation 5.40 in IERS 2010 conventions
  1518.             final PolynomialNutation psiA =
  1519.                     new PolynomialNutation(   0.0,
  1520.                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1521.                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1522.                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1523.                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1524.                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1525.             final PolynomialNutation omegaA =
  1526.                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  1527.                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1528.                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1529.                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1530.                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1531.                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1532.             final PolynomialNutation chiA =
  1533.                     new PolynomialNutation( 0.0,
  1534.                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1535.                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1536.                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1537.                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1538.                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

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

  1540.         }

  1541.          /** {@inheritDoc} */
  1542.         @Override
  1543.         public TimeVectorFunction getNutationFunction() {

  1544.             // set up nutation arguments
  1545.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1546.             // set up Poisson series
  1547.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1548.             final PoissonSeriesParser parser =
  1549.                     new PoissonSeriesParser(17).
  1550.                         withFirstDelaunay(4).
  1551.                         withFirstPlanetary(9).
  1552.                         withSinCos(0, 2, microAS, 3, microAS);
  1553.             final PoissonSeries psiSeries     = parser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1554.             final PoissonSeries epsilonSeries = parser.parse(getStream(EPSILON_SERIES), EPSILON_SERIES);
  1555.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  1556.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  1557.             return new TimeVectorFunction() {

  1558.                 /** {@inheritDoc} */
  1559.                 @Override
  1560.                 public double[] value(final AbsoluteDate date) {
  1561.                     final BodiesElements elements = arguments.evaluateAll(date);
  1562.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1563.                     return new double[] {
  1564.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  1565.                     };
  1566.                 }

  1567.                 /** {@inheritDoc} */
  1568.                 @Override
  1569.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1570.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1571.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1572.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1573.                     result[0] = psiEpsilon[0];
  1574.                     result[1] = psiEpsilon[1];
  1575.                     result[2] = IAU1994ResolutionC7.value(elements);
  1576.                     return result;
  1577.                 }

  1578.             };

  1579.         }

  1580.         /** {@inheritDoc} */
  1581.         @Override
  1582.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {

  1583.             // Earth Rotation Angle
  1584.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1585.             // Polynomial part of the apparent sidereal time series
  1586.             // which is the opposite of Equation of Origins (EO)
  1587.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1588.             final PoissonSeriesParser parser =
  1589.                     new PoissonSeriesParser(17).
  1590.                         withFirstDelaunay(4).
  1591.                         withFirstPlanetary(9).
  1592.                         withSinCos(0, 2, microAS, 3, microAS).
  1593.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1594.             final PolynomialNutation minusEO =
  1595.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1596.             // create a function evaluating the series
  1597.             return new TimeScalarFunction() {

  1598.                 /** {@inheritDoc} */
  1599.                 @Override
  1600.                 public double value(final AbsoluteDate date) {
  1601.                     return era.value(date) + minusEO.value(evaluateTC(date));
  1602.                 }

  1603.                 /** {@inheritDoc} */
  1604.                 @Override
  1605.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1606.                     return era.value(date).add(minusEO.value(evaluateTC(date)));
  1607.                 }

  1608.             };

  1609.         }

  1610.         /** {@inheritDoc} */
  1611.         @Override
  1612.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {

  1613.             // Earth Rotation Angle
  1614.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

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

  1626.             // create a function evaluating the series
  1627.             return new TimeScalarFunction() {

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

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

  1638.             };

  1639.         }

  1640.         /** {@inheritDoc} */
  1641.         @Override
  1642.         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory) {

  1643.             // set up nutation arguments
  1644.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1645.             // mean obliquity function
  1646.             final TimeScalarFunction epsilon = getMeanObliquityFunction();

  1647.             // set up Poisson series
  1648.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1649.             final PoissonSeriesParser baseParser =
  1650.                     new PoissonSeriesParser(17).
  1651.                         withFirstDelaunay(4).
  1652.                         withFirstPlanetary(9).
  1653.                         withSinCos(0, 2, microAS, 3, microAS);
  1654.             final PoissonSeriesParser gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1655.             final PoissonSeries psiSeries        = baseParser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1656.             final PoissonSeries gstSeries        = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  1657.             final PoissonSeries.CompiledSeries psiGstSeries =
  1658.                     PoissonSeries.compile(psiSeries, gstSeries);

  1659.             // ERA function
  1660.             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);

  1661.             return new TimeScalarFunction() {

  1662.                 /** {@inheritDoc} */
  1663.                 @Override
  1664.                 public double value(final AbsoluteDate date) {

  1665.                     // evaluate equation of origins
  1666.                     final BodiesElements elements = arguments.evaluateAll(date);
  1667.                     final double[] angles = psiGstSeries.value(elements);
  1668.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1669.                     final double deltaPsi = angles[0] + ddPsi;
  1670.                     final double epsilonA = epsilon.value(date);

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

  1674.                 }

  1675.                 /** {@inheritDoc} */
  1676.                 @Override
  1677.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1678.                     // evaluate equation of origins
  1679.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1680.                     final T[] angles = psiGstSeries.value(elements);
  1681.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1682.                     final T deltaPsi = angles[0].add(ddPsi);
  1683.                     final T epsilonA = epsilon.value(date);

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

  1687.                 }

  1688.             };

  1689.         }

  1690.         /** {@inheritDoc} */
  1691.         @Override
  1692.         public TimeVectorFunction getEOPTidalCorrection() {

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

  1699.             // set up Poisson series
  1700.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1701.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  1702.                     withOptionalColumn(1).
  1703.                     withGamma(2).
  1704.                     withFirstDelaunay(3);
  1705.             final PoissonSeries xSeries =
  1706.                     xyParser.
  1707.                     withSinCos(0, 10, microAS, 11, microAS).
  1708.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  1709.             final PoissonSeries ySeries =
  1710.                     xyParser.
  1711.                     withSinCos(0, 12, microAS, 13, microAS).
  1712.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  1713.             final double microS = 1.0e-6;
  1714.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1715.                     withOptionalColumn(1).
  1716.                     withGamma(2).
  1717.                     withFirstDelaunay(3).
  1718.                     withSinCos(0, 10, microS, 11, microS);
  1719.             final PoissonSeries ut1Series =
  1720.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

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

  1722.         }

  1723.         /** {@inheritDoc} */
  1724.         @Override
  1725.         public double[] getNominalTidalDisplacement() {
  1726.             return new double[] {
  1727.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1728.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1729.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1730.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1731.                 // H₀
  1732.                 -0.31460
  1733.             };
  1734.         }

  1735.         /** {@inheritDoc} */
  1736.         @Override
  1737.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1738.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1739.                                                                   18, 15, 16, 17, 18);
  1740.         }

  1741.         /** {@inheritDoc} */
  1742.         @Override
  1743.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1744.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1745.                                                                 18, 15, 16, 17, 18);
  1746.         }

  1747.     };

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

  1750.     /** Get the reference epoch for fundamental nutation arguments.
  1751.      * @return reference epoch for fundamental nutation arguments
  1752.      * @since 6.1
  1753.      */
  1754.     public AbsoluteDate getNutationReferenceEpoch() {
  1755.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1756.         return AbsoluteDate.J2000_EPOCH;
  1757.     }

  1758.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1759.      * @param date current date
  1760.      * @return date offset in Julian centuries
  1761.      * @since 6.1
  1762.      */
  1763.     public double evaluateTC(final AbsoluteDate date) {
  1764.         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
  1765.     }

  1766.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1767.      * @param date current date
  1768.      * @param <T> type of the field elements
  1769.      * @return date offset in Julian centuries
  1770.      * @since 9.0
  1771.      */
  1772.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
  1773.         return date.durationFrom(getNutationReferenceEpoch()).divide(Constants.JULIAN_CENTURY);
  1774.     }

  1775.     /** Get the fundamental nutation arguments.
  1776.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  1777.      * (typically {@link TimeScalesFactory#getUT1(IERSConventions, boolean) UT1})
  1778.      * @return fundamental nutation arguments
  1779.           * @since 6.1
  1780.      */
  1781.     public abstract FundamentalNutationArguments getNutationArguments(TimeScale timeScale);

  1782.     /** Get the function computing mean obliquity of the ecliptic.
  1783.      * @return function computing mean obliquity of the ecliptic
  1784.           * @since 6.1
  1785.      */
  1786.     public abstract TimeScalarFunction getMeanObliquityFunction();

  1787.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  1788.      * <p>
  1789.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  1790.      * </p>
  1791.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  1792.           * @since 6.1
  1793.      */
  1794.     public abstract TimeVectorFunction getXYSpXY2Function();

  1795.     /** Get the function computing the raw Earth Orientation Angle.
  1796.      * <p>
  1797.      * The raw angle does not contain any correction. If for example dTU1 correction
  1798.      * due to tidal effect is desired, it must be added afterward by the caller.
  1799.      * The returned value contain the angle as the value and the angular rate as
  1800.      * the first derivative.
  1801.      * </p>
  1802.      * @param ut1 UT1 time scale
  1803.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  1804.      * @since 6.1
  1805.      */
  1806.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
  1807.         return new StellarAngleCapitaine(ut1);
  1808.     }


  1809.     /** Get the function computing the precession angles.
  1810.      * <p>
  1811.      * The function returned computes the three precession angles
  1812.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  1813.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  1814.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  1815.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  1816.      * #getNutationReferenceEpoch() nutation reference epoch}.
  1817.      * </p>
  1818.      * @return function computing the precession angle
  1819.           * @since 6.1
  1820.      */
  1821.     public abstract TimeVectorFunction getPrecessionFunction();

  1822.     /** Get the function computing the nutation angles.
  1823.      * <p>
  1824.      * The function returned computes the two classical angles ΔΨ and Δε,
  1825.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  1826.      * resolution C7 (the correction is forced to 0 before this date)
  1827.      * </p>
  1828.      * @return function computing the nutation in longitude ΔΨ and Δε
  1829.      * and the correction of equation of equinoxes
  1830.           * @since 6.1
  1831.      */
  1832.     public abstract TimeVectorFunction getNutationFunction();

  1833.     /** Get the function computing Greenwich mean sidereal time, in radians.
  1834.      * @param ut1 UT1 time scale
  1835.      * @return function computing Greenwich mean sidereal time
  1836.           * @since 6.1
  1837.      */
  1838.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1);

  1839.     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
  1840.      * @param ut1 UT1 time scale
  1841.      * @return function computing Greenwich mean sidereal time rate
  1842.           * @since 9.0
  1843.      */
  1844.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1);

  1845.     /** Get the function computing Greenwich apparent sidereal time, in radians.
  1846.      * @param ut1 UT1 time scale
  1847.      * @param eopHistory EOP history
  1848.      * @return function computing Greenwich apparent sidereal time
  1849.           * @since 6.1
  1850.      */
  1851.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1, EOPHistory eopHistory);

  1852.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  1853.      * @return function computing tidal corrections for Earth Orientation Parameters,
  1854.      * for xp, yp, ut1 and lod respectively
  1855.           * @since 6.1
  1856.      */
  1857.     public abstract TimeVectorFunction getEOPTidalCorrection();

  1858.     /** Get the Love numbers.
  1859.      * @return Love numbers
  1860.           * @since 6.1
  1861.      */
  1862.     public abstract LoveNumbers getLoveNumbers();

  1863.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1864.      * @param ut1 UT1 time scale
  1865.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1866.           * @since 6.1
  1867.      */
  1868.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(TimeScale ut1);

  1869.     /** Get the permanent tide to be <em>removed</em> from ΔC₂₀ when zero-tide potentials are used.
  1870.      * @return permanent tide to remove
  1871.      */
  1872.     public abstract double getPermanentTide();

  1873.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  1874.      * @param eopHistory EOP history
  1875.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1876.           * @since 6.1
  1877.      */
  1878.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory);

  1879.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  1880.      * @param eopHistory EOP history
  1881.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1882.           * @since 6.1
  1883.      */
  1884.     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory);

  1885.     /** Get the nominal values of the displacement numbers.
  1886.      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
  1887.      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
  1888.      * H₀ permanent deformation amplitude
  1889.      * @since 9.1
  1890.      */
  1891.     public abstract double[] getNominalTidalDisplacement();

  1892.     /** Get the correction function for tidal displacement for diurnal tides.
  1893.      * <ul>
  1894.      *  <li>f[0]: radial correction, longitude cosine part</li>
  1895.      *  <li>f[1]: radial correction, longitude sine part</li>
  1896.      *  <li>f[2]: North correction, longitude cosine part</li>
  1897.      *  <li>f[3]: North correction, longitude sine part</li>
  1898.      *  <li>f[4]: East correction, longitude cosine part</li>
  1899.      *  <li>f[5]: East correction, longitude sine part</li>
  1900.      * </ul>
  1901.      * @return correction function for tidal displacement
  1902.      * @since 9.1
  1903.      */
  1904.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal();

  1905.     /** Get the correction function for tidal displacement for diurnal tides.
  1906.      * <ul>
  1907.      *  <li>f[0]: radial correction, longitude cosine part</li>
  1908.      *  <li>f[1]: radial correction, longitude sine part</li>
  1909.      *  <li>f[2]: North correction, longitude cosine part</li>
  1910.      *  <li>f[3]: North correction, longitude sine part</li>
  1911.      *  <li>f[4]: East correction, longitude cosine part</li>
  1912.      *  <li>f[5]: East correction, longitude sine part</li>
  1913.      * </ul>
  1914.      * @param tableName name for the diurnal tides table
  1915.      * @param cols total number of columns of the diurnal tides table
  1916.      * @param rIp column holding ∆Rf(ip) in the diurnal tides table, counting from 1
  1917.      * @param rOp column holding ∆Rf(op) in the diurnal tides table, counting from 1
  1918.      * @param tIp column holding ∆Tf(ip) in the diurnal tides table, counting from 1
  1919.      * @param tOp column holding ∆Tf(op) in the diurnal tides table, counting from 1
  1920.      * @return correction function for tidal displacement for diurnal tides
  1921.           * @since 9.1
  1922.      */
  1923.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal(final String tableName, final int cols,
  1924.                                                                                    final int rIp, final int rOp,
  1925.                                                                                    final int tIp, final int tOp) {

  1926.         // radial component, missing the sin 2φ factor; this corresponds to:
  1927.         //  - equation 15a in IERS conventions 1996, chapter 7
  1928.         //  - equation 16a in IERS conventions 2003, chapter 7
  1929.         //  - equation 7.12a in IERS conventions 2010, chapter 7
  1930.         final PoissonSeries drCos = new PoissonSeriesParser(cols).
  1931.                                     withOptionalColumn(1).
  1932.                                     withDoodson(4, 3).
  1933.                                     withFirstDelaunay(10).
  1934.                                     withSinCos(0, rIp, +1.0e-3, rOp, +1.0e-3).
  1935.                                     parse(getStream(tableName), tableName);
  1936.         final PoissonSeries drSin = new PoissonSeriesParser(cols).
  1937.                                     withOptionalColumn(1).
  1938.                                     withDoodson(4, 3).
  1939.                                     withFirstDelaunay(10).
  1940.                                     withSinCos(0, rOp, -1.0e-3, rIp, +1.0e-3).
  1941.                                     parse(getStream(tableName), tableName);

  1942.         // North component, missing the cos 2φ factor; this corresponds to:
  1943.         //  - equation 15b in IERS conventions 1996, chapter 7
  1944.         //  - equation 16b in IERS conventions 2003, chapter 7
  1945.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  1946.         final PoissonSeries dnCos = new PoissonSeriesParser(cols).
  1947.                                     withOptionalColumn(1).
  1948.                                     withDoodson(4, 3).
  1949.                                     withFirstDelaunay(10).
  1950.                                     withSinCos(0, tIp, +1.0e-3, tOp, +1.0e-3).
  1951.                                     parse(getStream(tableName), tableName);
  1952.         final PoissonSeries dnSin = new PoissonSeriesParser(cols).
  1953.                                     withOptionalColumn(1).
  1954.                                     withDoodson(4, 3).
  1955.                                     withFirstDelaunay(10).
  1956.                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  1957.                                     parse(getStream(tableName), tableName);

  1958.         // East component, missing the sin φ factor; this corresponds to:
  1959.         //  - equation 15b in IERS conventions 1996, chapter 7
  1960.         //  - equation 16b in IERS conventions 2003, chapter 7
  1961.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  1962.         final PoissonSeries deCos = new PoissonSeriesParser(cols).
  1963.                                     withOptionalColumn(1).
  1964.                                     withDoodson(4, 3).
  1965.                                     withFirstDelaunay(10).
  1966.                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  1967.                                     parse(getStream(tableName), tableName);
  1968.         final PoissonSeries deSin = new PoissonSeriesParser(cols).
  1969.                                     withOptionalColumn(1).
  1970.                                     withDoodson(4, 3).
  1971.                                     withFirstDelaunay(10).
  1972.                                     withSinCos(0, tIp, -1.0e-3, tOp, -1.0e-3).
  1973.                                     parse(getStream(tableName), tableName);

  1974.         return PoissonSeries.compile(drCos, drSin,
  1975.                                      dnCos, dnSin,
  1976.                                      deCos, deSin);

  1977.     }

  1978.     /** Get the correction function for tidal displacement for zonal tides.
  1979.      * <ul>
  1980.      *  <li>f[0]: radial correction</li>
  1981.      *  <li>f[1]: North correction</li>
  1982.      * </ul>
  1983.      * @return correction function for tidal displacement
  1984.      * @since 9.1
  1985.      */
  1986.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal();

  1987.     /** Get the correction function for tidal displacement for zonal tides.
  1988.      * <ul>
  1989.      *  <li>f[0]: radial correction</li>
  1990.      *  <li>f[1]: North correction</li>
  1991.      * </ul>
  1992.      * @param tableName name for the zonal tides table
  1993.      * @param cols total number of columns of the table
  1994.      * @param rIp column holding ∆Rf(ip) in the table, counting from 1
  1995.      * @param rOp column holding ∆Rf(op) in the table, counting from 1
  1996.      * @param tIp column holding ∆Tf(ip) in the table, counting from 1
  1997.      * @param tOp column holding ∆Tf(op) in the table, counting from 1
  1998.      * @return correction function for tidal displacement for zonal tides
  1999.           * @since 9.1
  2000.      */
  2001.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionZonal(final String tableName, final int cols,
  2002.                                                                                  final int rIp, final int rOp,
  2003.                                                                                  final int tIp, final int tOp) {

  2004.         // radial component, missing the 3⁄2 sin² φ - 1⁄2 factor; this corresponds to:
  2005.         //  - equation 16a in IERS conventions 1996, chapter 7
  2006.         //  - equation 17a in IERS conventions 2003, chapter 7
  2007.         //  - equation 7.13a in IERS conventions 2010, chapter 7
  2008.         final PoissonSeries dr = new PoissonSeriesParser(cols).
  2009.                                  withOptionalColumn(1).
  2010.                                  withDoodson(4, 3).
  2011.                                  withFirstDelaunay(10).
  2012.                                  withSinCos(0, rOp, +1.0e-3, rIp, +1.0e-3).
  2013.                                  parse(getStream(tableName), tableName);

  2014.         // North component, missing the sin 2φ factor; this corresponds to:
  2015.         //  - equation 16b in IERS conventions 1996, chapter 7
  2016.         //  - equation 17b in IERS conventions 2003, chapter 7
  2017.         //  - equation 7.13b in IERS conventions 2010, chapter 7
  2018.         final PoissonSeries dn = new PoissonSeriesParser(cols).
  2019.                                  withOptionalColumn(1).
  2020.                                  withDoodson(4, 3).
  2021.                                  withFirstDelaunay(10).
  2022.                                  withSinCos(0, tOp, +1.0e-3, tIp, +1.0e-3).
  2023.                                  parse(getStream(tableName), tableName);

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

  2025.     }

  2026.     /** Interface for functions converting nutation corrections between
  2027.      * δΔψ/δΔε to δX/δY.
  2028.      * <ul>
  2029.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2030.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2031.      * </ul>
  2032.      * @since 6.1
  2033.      */
  2034.     public interface NutationCorrectionConverter {

  2035.         /** Convert nutation corrections.
  2036.          * @param date current date
  2037.          * @param ddPsi δΔψ part of the nutation correction
  2038.          * @param ddEpsilon δΔε part of the nutation correction
  2039.          * @return array containing δX and δY
  2040.          */
  2041.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon);

  2042.         /** Convert nutation corrections.
  2043.          * @param date current date
  2044.          * @param dX δX part of the nutation correction
  2045.          * @param dY δY part of the nutation correction
  2046.          * @return array containing δΔψ and δΔε
  2047.          */
  2048.         double[] toEquinox(AbsoluteDate date, double dX, double dY);

  2049.     }

  2050.     /** Create a function converting nutation corrections between
  2051.      * δX/δY and δΔψ/δΔε.
  2052.      * <ul>
  2053.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2054.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2055.      * </ul>
  2056.      * @return a new converter
  2057.           * @since 6.1
  2058.      */
  2059.     public NutationCorrectionConverter getNutationCorrectionConverter() {

  2060.         // get models parameters
  2061.         final TimeVectorFunction precessionFunction = getPrecessionFunction();
  2062.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction();
  2063.         final AbsoluteDate date0 = getNutationReferenceEpoch();
  2064.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  2065.         return new NutationCorrectionConverter() {

  2066.             /** {@inheritDoc} */
  2067.             @Override
  2068.             public double[] toNonRotating(final AbsoluteDate date,
  2069.                                           final double ddPsi, final double ddEpsilon) {
  2070.                 // compute precession angles psiA, omegaA and chiA
  2071.                 final double[] angles = precessionFunction.value(date);

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

  2075.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  2076.                 return new double[] {
  2077.                     sinEA * ddPsi + c * ddEpsilon,
  2078.                     ddEpsilon - c * sinEA * ddPsi
  2079.                 };

  2080.             }

  2081.             /** {@inheritDoc} */
  2082.             @Override
  2083.             public double[] toEquinox(final AbsoluteDate date,
  2084.                                       final double dX, final double dY) {
  2085.                 // compute precession angles psiA, omegaA and chiA
  2086.                 final double[] angles   = precessionFunction.value(date);

  2087.                 // conversion coefficients
  2088.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2089.                 final double c     = angles[0] * cosE0 - angles[2];
  2090.                 final double opC2  = 1 + c * c;

  2091.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  2092.                 return new double[] {
  2093.                     (dX - c * dY) / (sinEA * opC2),
  2094.                     (dY + c * dX) / opC2
  2095.                 };
  2096.             }

  2097.         };

  2098.     }

  2099.     /** Load the Love numbers.
  2100.      * @param nameLove name of the Love number resource
  2101.      * @return Love numbers
  2102.      */
  2103.     protected LoveNumbers loadLoveNumbers(final String nameLove) {
  2104.         try {

  2105.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  2106.             final double[][] real      = new double[4][];
  2107.             final double[][] imaginary = new double[4][];
  2108.             final double[][] plus      = new double[4][];
  2109.             for (int i = 0; i < real.length; ++i) {
  2110.                 real[i]      = new double[i + 1];
  2111.                 imaginary[i] = new double[i + 1];
  2112.                 plus[i]      = new double[i + 1];
  2113.             }

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

  2115.                 if (stream == null) {
  2116.                     // this should never happen with files embedded within Orekit
  2117.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  2118.                 }

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

  2121.                     String line = reader.readLine();
  2122.                     int lineNumber = 1;

  2123.                     // look for the Love numbers
  2124.                     while (line != null) {

  2125.                         line = line.trim();
  2126.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  2127.                             final String[] fields = line.split("\\p{Space}+");
  2128.                             if (fields.length != 5) {
  2129.                                 // this should never happen with files embedded within Orekit
  2130.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2131.                                                           lineNumber, nameLove, line);
  2132.                             }
  2133.                             final int n = Integer.parseInt(fields[0]);
  2134.                             final int m = Integer.parseInt(fields[1]);
  2135.                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  2136.                                 // this should never happen with files embedded within Orekit
  2137.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2138.                                                           lineNumber, nameLove, line);

  2139.                             }
  2140.                             real[n][m]      = Double.parseDouble(fields[2]);
  2141.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  2142.                             plus[n][m]      = Double.parseDouble(fields[4]);
  2143.                         }

  2144.                         // next line
  2145.                         lineNumber++;
  2146.                         line = reader.readLine();

  2147.                     }
  2148.                 }
  2149.             }

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

  2151.         } catch (IOException ioe) {
  2152.             // this should never happen with files embedded within Orekit
  2153.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  2154.         }
  2155.     }

  2156.     /** Get a data stream.
  2157.      * @param name file name of the resource stream
  2158.      * @return stream
  2159.      */
  2160.     private static InputStream getStream(final String name) {
  2161.         return IERSConventions.class.getResourceAsStream(name);
  2162.     }

  2163.     /** Correction to equation of equinoxes.
  2164.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2165.      * taking effect since 1997-02-27 for continuity
  2166.      * </p>
  2167.      */
  2168.     private static class IAU1994ResolutionC7 {

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

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

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

  2178.         /** Evaluate the correction.
  2179.          * @param arguments Delaunay for nutation
  2180.          * @return correction value (0 before 1997-02-27)
  2181.          */
  2182.         public static double value(final DelaunayArguments arguments) {
  2183.             if (arguments.getDate().compareTo(MODEL_START) >= 0) {

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

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

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

  2190.             } else {
  2191.                 return 0.0;
  2192.             }
  2193.         }

  2194.         /** Evaluate the correction.
  2195.          * @param arguments Delaunay for nutation
  2196.          * @param <T> type of the field elements
  2197.          * @return correction value (0 before 1997-02-27)
  2198.          */
  2199.         public static <T extends RealFieldElement<T>> T value(final FieldDelaunayArguments<T> arguments) {
  2200.             if (arguments.getDate().toAbsoluteDate().compareTo(MODEL_START) >= 0) {

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

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

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

  2207.             } else {
  2208.                 return arguments.getDate().getField().getZero();
  2209.             }
  2210.         }

  2211.     };

  2212.     /** Stellar angle model.
  2213.      * <p>
  2214.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  2215.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  2216.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  2217.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  2218.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  2219.      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
  2220.      * </p>
  2221.      * <p>
  2222.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  2223.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  2224.      * IERS conventions 2003 and 2010.
  2225.      * </p>
  2226.      */
  2227.     private static class StellarAngleCapitaine implements TimeScalarFunction {

  2228.         /** Reference date of Capitaine's Earth Rotation Angle model. */
  2229.         private static final AbsoluteDate REFERENCE_DATE = new AbsoluteDate(DateComponents.J2000_EPOCH,
  2230.                                                                             TimeComponents.H12,
  2231.                                                                             TimeScalesFactory.getTAI());

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

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

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

  2240.         /** UT1 time scale. */
  2241.         private final TimeScale ut1;

  2242.         /** Simple constructor.
  2243.          * @param ut1 UT1 time scale
  2244.          */
  2245.         StellarAngleCapitaine(final TimeScale ut1) {
  2246.             this.ut1 = ut1;
  2247.         }

  2248.         /** Get the rotation rate.
  2249.          * @return rotation rate
  2250.          */
  2251.         public double getRate() {
  2252.             return ERA_1A + ERA_1B;
  2253.         }

  2254.         /** {@inheritDoc} */
  2255.         @Override
  2256.         public double value(final AbsoluteDate date) {

  2257.             // split the date offset as a full number of days plus a smaller part
  2258.             final int secondsInDay = 86400;
  2259.             final double dt  = date.durationFrom(REFERENCE_DATE);
  2260.             final long days  = ((long) dt) / secondsInDay;
  2261.             final double dtA = secondsInDay * days;
  2262.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

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

  2264.         }

  2265.         /** {@inheritDoc} */
  2266.         @Override
  2267.         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2268.             // split the date offset as a full number of days plus a smaller part
  2269.             final int secondsInDay = 86400;
  2270.             final T dt  = date.durationFrom(REFERENCE_DATE);
  2271.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2272.             final double dtA = secondsInDay * days;
  2273.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));

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

  2275.         }

  2276.     }

  2277.     /** Mean pole. */
  2278.     private static class MeanPole implements TimeStamped, Serializable {

  2279.         /** Serializable UID. */
  2280.         private static final long serialVersionUID = 20131028l;

  2281.         /** Date. */
  2282.         private final AbsoluteDate date;

  2283.         /** X coordinate. */
  2284.         private double x;

  2285.         /** Y coordinate. */
  2286.         private double y;

  2287.         /** Simple constructor.
  2288.          * @param date date
  2289.          * @param x x coordinate
  2290.          * @param y y coordinate
  2291.          */
  2292.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2293.             this.date = date;
  2294.             this.x    = x;
  2295.             this.y    = y;
  2296.         }

  2297.         /** {@inheritDoc} */
  2298.         @Override
  2299.         public AbsoluteDate getDate() {
  2300.             return date;
  2301.         }

  2302.         /** Get x coordinate.
  2303.          * @return x coordinate
  2304.          */
  2305.         public double getX() {
  2306.             return x;
  2307.         }

  2308.         /** Get y coordinate.
  2309.          * @return y coordinate
  2310.          */
  2311.         public double getY() {
  2312.             return y;
  2313.         }

  2314.     }

  2315.     /** Local class for precession function. */
  2316.     private class PrecessionFunction implements TimeVectorFunction {

  2317.         /** Polynomial nutation for psiA. */
  2318.         private final PolynomialNutation psiA;

  2319.         /** Polynomial nutation for omegaA. */
  2320.         private final PolynomialNutation omegaA;

  2321.         /** Polynomial nutation for chiA. */
  2322.         private final PolynomialNutation chiA;

  2323.         /** Simple constructor.
  2324.          * @param psiA polynomial nutation for psiA
  2325.          * @param omegaA polynomial nutation for omegaA
  2326.          * @param chiA polynomial nutation for chiA
  2327.          */
  2328.         PrecessionFunction(final PolynomialNutation psiA,
  2329.                            final PolynomialNutation omegaA,
  2330.                            final PolynomialNutation chiA) {
  2331.             this.psiA   = psiA;
  2332.             this.omegaA = omegaA;
  2333.             this.chiA   = chiA;
  2334.         }


  2335.         /** {@inheritDoc} */
  2336.         @Override
  2337.         public double[] value(final AbsoluteDate date) {
  2338.             final double tc = evaluateTC(date);
  2339.             return new double[] {
  2340.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2341.             };
  2342.         }

  2343.         /** {@inheritDoc} */
  2344.         @Override
  2345.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2346.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2347.             final T tc = evaluateTC(date);
  2348.             a[0] = psiA.value(tc);
  2349.             a[1] = omegaA.value(tc);
  2350.             a[2] = chiA.value(tc);
  2351.             return a;
  2352.         }

  2353.     }

  2354.     /** Local class for tides frequency function. */
  2355.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2356.         /** Nutation arguments. */
  2357.         private final FundamentalNutationArguments arguments;

  2358.         /** Correction series. */
  2359.         private final PoissonSeries.CompiledSeries kSeries;

  2360.         /** Simple constructor.
  2361.          * @param arguments nutation arguments
  2362.          * @param c20Series correction series for the C20 term
  2363.          * @param c21Series correction series for the C21 term
  2364.          * @param s21Series correction series for the S21 term
  2365.          * @param c22Series correction series for the C22 term
  2366.          * @param s22Series correction series for the S22 term
  2367.          */
  2368.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2369.                                         final PoissonSeries c20Series,
  2370.                                         final PoissonSeries c21Series,
  2371.                                         final PoissonSeries s21Series,
  2372.                                         final PoissonSeries c22Series,
  2373.                                         final PoissonSeries s22Series) {
  2374.             this.arguments = arguments;
  2375.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2376.         }


  2377.         /** {@inheritDoc} */
  2378.         @Override
  2379.         public double[] value(final AbsoluteDate date) {
  2380.             return kSeries.value(arguments.evaluateAll(date));
  2381.         }

  2382.         /** {@inheritDoc} */
  2383.         @Override
  2384.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2385.             return kSeries.value(arguments.evaluateAll(date));
  2386.         }

  2387.     }

  2388.     /** Local class for EOP tidal corrections. */
  2389.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2390.         /** Nutation arguments. */
  2391.         private final FundamentalNutationArguments arguments;

  2392.         /** Correction series. */
  2393.         private final PoissonSeries.CompiledSeries correctionSeries;

  2394.         /** Simple constructor.
  2395.          * @param arguments nutation arguments
  2396.          * @param xSeries correction series for the x coordinate
  2397.          * @param ySeries correction series for the y coordinate
  2398.          * @param ut1Series correction series for the UT1
  2399.          */
  2400.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2401.                            final PoissonSeries xSeries,
  2402.                            final PoissonSeries ySeries,
  2403.                            final PoissonSeries ut1Series) {
  2404.             this.arguments        = arguments;
  2405.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2406.         }

  2407.         /** {@inheritDoc} */
  2408.         @Override
  2409.         public double[] value(final AbsoluteDate date) {
  2410.             final BodiesElements elements = arguments.evaluateAll(date);
  2411.             final double[] correction    = correctionSeries.value(elements);
  2412.             final double[] correctionDot = correctionSeries.derivative(elements);
  2413.             return new double[] {
  2414.                 correction[0],
  2415.                 correction[1],
  2416.                 correction[2],
  2417.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2418.             };
  2419.         }

  2420.         /** {@inheritDoc} */
  2421.         @Override
  2422.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2423.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2424.             final T[] correction    = correctionSeries.value(elements);
  2425.             final T[] correctionDot = correctionSeries.derivative(elements);
  2426.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2427.             a[0] = correction[0];
  2428.             a[1] = correction[1];
  2429.             a[2] = correction[2];
  2430.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2431.             return a;
  2432.         }

  2433.     }

  2434. }