IERSConventions.java

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

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


  64. /** Supported IERS conventions.
  65.  * @since 6.0
  66.  * @author Luc Maisonobe
  67.  */
  68. public enum IERSConventions {

  69.     /** Constant for IERS 1996 conventions. */
  70.     IERS_1996 {

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

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

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

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

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

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

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

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

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

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

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

  93.         /** {@inheritDoc} */
  94.         @Override
  95.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  96.                                                                  final TimeScales timeScales) {
  97.             try (InputStream in = getStream(NUTATION_ARGUMENTS)) {
  98.                 return new FundamentalNutationArguments(this, timeScale,
  99.                         in, NUTATION_ARGUMENTS, timeScales);
  100.             } catch (IOException e) {
  101.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  102.             }
  103.         }

  104.         /** {@inheritDoc} */
  105.         @Override
  106.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  107.             // value from chapter 5, page 22
  108.             final PolynomialNutation epsilonA =
  109.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  110.                                              -46.8150   * Constants.ARC_SECONDS_TO_RADIANS,
  111.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  112.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  113.             return new TimeScalarFunction() {

  114.                 /** {@inheritDoc} */
  115.                 @Override
  116.                 public double value(final AbsoluteDate date) {
  117.                     return epsilonA.value(evaluateTC(date, timeScales));
  118.                 }

  119.                 /** {@inheritDoc} */
  120.                 @Override
  121.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  122.                     return epsilonA.value(evaluateTC(date, timeScales));
  123.                 }

  124.             };

  125.         }

  126.         /** {@inheritDoc} */
  127.         @Override
  128.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  129.             // set up nutation arguments
  130.             final FundamentalNutationArguments arguments =
  131.                     getNutationArguments(timeScales);

  132.             // X = 2004.3109″t - 0.42665″t² - 0.198656″t³ + 0.0000140″t⁴
  133.             //     + 0.00006″t² cos Ω + sin ε0 { Σ [(Ai + Ai' t) sin(ARGUMENT) + Ai'' t cos(ARGUMENT)]}
  134.             //     + 0.00204″t² sin Ω + 0.00016″t² sin 2(F - D + Ω),
  135.             final PolynomialNutation xPolynomial =
  136.                     new PolynomialNutation(0,
  137.                                            2004.3109 * Constants.ARC_SECONDS_TO_RADIANS,
  138.                                            -0.42665  * Constants.ARC_SECONDS_TO_RADIANS,
  139.                                            -0.198656 * Constants.ARC_SECONDS_TO_RADIANS,
  140.                                            0.0000140 * Constants.ARC_SECONDS_TO_RADIANS);

  141.             final double fXCosOm    = 0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  142.             final double fXSinOm    = 0.00204 * Constants.ARC_SECONDS_TO_RADIANS;
  143.             final double fXSin2FDOm = 0.00016 * Constants.ARC_SECONDS_TO_RADIANS;
  144.             final double sinEps0   = FastMath.sin(getMeanObliquityFunction(timeScales)
  145.                     .value(getNutationReferenceEpoch(timeScales)));

  146.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  147.             final PoissonSeriesParser baseParser =
  148.                     new PoissonSeriesParser(12).withFirstDelaunay(1);

  149.             final PoissonSeriesParser xParser =
  150.                     baseParser.
  151.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  152.                     withSinCos(1, 8, deciMilliAS,  9, deciMilliAS);
  153.             final PoissonSeries xSum;
  154.             try (InputStream in = getStream(X_Y_SERIES)) {
  155.                 xSum = xParser.parse(in, X_Y_SERIES);
  156.             } catch (IOException e) {
  157.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  158.             }

  159.             // Y = -0.00013″ - 22.40992″t² + 0.001836″t³ + 0.0011130″t⁴
  160.             //     + Σ [(Bi + Bi' t) cos(ARGUMENT) + Bi'' t sin(ARGUMENT)]
  161.             //    - 0.00231″t² cos Ω − 0.00014″t² cos 2(F - D + Ω)
  162.             final PolynomialNutation yPolynomial =
  163.                     new PolynomialNutation(-0.00013  * Constants.ARC_SECONDS_TO_RADIANS,
  164.                                            0.0,
  165.                                            -22.40992 * Constants.ARC_SECONDS_TO_RADIANS,
  166.                                            0.001836  * Constants.ARC_SECONDS_TO_RADIANS,
  167.                                            0.0011130 * Constants.ARC_SECONDS_TO_RADIANS);

  168.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  169.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

  170.             final PoissonSeriesParser yParser =
  171.                     baseParser.
  172.                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
  173.                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
  174.             final PoissonSeries ySum;
  175.             try (InputStream in = getStream(X_Y_SERIES)) {
  176.                 ySum = yParser.parse(in, X_Y_SERIES);
  177.             } catch (IOException e) {
  178.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  179.             }

  180.             final PoissonSeries.CompiledSeries xySum =
  181.                     PoissonSeries.compile(xSum, ySum);

  182.             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
  183.             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
  184.             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
  185.             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
  186.             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
  187.             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  188.             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
  189.             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;

  190.             return new TimeVectorFunction() {

  191.                 /** {@inheritDoc} */
  192.                 @Override
  193.                 public double[] value(final AbsoluteDate date) {

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

  196.                     final double omega     = elements.getOmega();
  197.                     final double f         = elements.getF();
  198.                     final double d         = elements.getD();
  199.                     final double t         = elements.getTC();

  200.                     final SinCos scOmega   = FastMath.sinCos(omega);
  201.                     final SinCos sc2omega  = SinCos.sum(scOmega, scOmega);
  202.                     final SinCos sc2FD0m   = FastMath.sinCos(2 * (f - d + omega));
  203.                     final double cosOmega  = scOmega.cos();
  204.                     final double sinOmega  = scOmega.sin();
  205.                     final double sin2Omega = sc2omega.sin();
  206.                     final double cos2FDOm  = sc2FD0m.cos();
  207.                     final double sin2FDOm  = sc2FD0m.sin();

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

  214.                     return new double[] {
  215.                         x, y, sPxy2
  216.                     };

  217.                 }

  218.                 /** {@inheritDoc} */
  219.                 @Override
  220.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

  223.                     final T omega     = elements.getOmega();
  224.                     final T f         = elements.getF();
  225.                     final T d         = elements.getD();
  226.                     final T t         = elements.getTC();
  227.                     final T t2        = t.multiply(t);

  228.                     final FieldSinCos<T> scOmega  = FastMath.sinCos(omega);
  229.                     final FieldSinCos<T> sc2omega = FieldSinCos.sum(scOmega, scOmega);
  230.                     final FieldSinCos<T> sc2FD0m  = FastMath.sinCos(f.subtract(d).add(omega).multiply(2));
  231.                     final T cosOmega  = scOmega.cos();
  232.                     final T sinOmega  = scOmega.sin();
  233.                     final T sin2Omega = sc2omega.sin();
  234.                     final T cos2FDOm  = sc2FD0m.cos();
  235.                     final T sin2FDOm  = sc2FD0m.sin();

  236.                     final T x = xPolynomial.value(t).
  237.                                 add(xy[0].multiply(sinEps0)).
  238.                                 add(t2.multiply(cosOmega.multiply(fXCosOm).add(sinOmega.multiply(fXSinOm)).add(cos2FDOm.multiply(fXSin2FDOm))));
  239.                     final T y = yPolynomial.value(t).
  240.                                 add(xy[1]).
  241.                                 add(t2.multiply(cosOmega.multiply(fYCosOm).add(cos2FDOm.multiply(fYCos2FDOm))));
  242.                     final T sPxy2 = sinOmega.multiply(fSSinOm).
  243.                                     add(sin2Omega.multiply(fSSin2Om)).
  244.                                     add(t.multiply(fST3).add(sinOmega.multiply(fST2SinOm)).add(sin2FDOm.multiply(fST2Sin2FDOm)).multiply(t).add(fST).multiply(t));

  245.                     final T[] a = MathArrays.buildArray(date.getField(), 3);
  246.                     a[0] = x;
  247.                     a[1] = y;
  248.                     a[2] = sPxy2;
  249.                     return a;

  250.                 }

  251.             };

  252.         }

  253.         /** {@inheritDoc} */
  254.         @Override
  255.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

  256.             // set up the conventional polynomials
  257.             // the following values are from Lieske et al. paper:
  258.             // Expressions for the precession quantities based upon the IAU(1976) system of astronomical constants
  259.             // http://articles.adsabs.harvard.edu/full/1977A%26A....58....1L
  260.             // also available as equation 30 in IERS 2003 conventions
  261.             final PolynomialNutation psiA =
  262.                     new PolynomialNutation(   0.0,
  263.                                            5038.7784   * Constants.ARC_SECONDS_TO_RADIANS,
  264.                                              -1.07259  * Constants.ARC_SECONDS_TO_RADIANS,
  265.                                              -0.001147 * Constants.ARC_SECONDS_TO_RADIANS);
  266.             final PolynomialNutation omegaA =
  267.                     new PolynomialNutation(getMeanObliquityFunction(timeScales)
  268.                             .value(getNutationReferenceEpoch(timeScales)),
  269.                                             0.0,
  270.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  271.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  272.             final PolynomialNutation chiA =
  273.                     new PolynomialNutation( 0.0,
  274.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  275.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  276.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  277.             return new PrecessionFunction(psiA, omegaA, chiA, timeScales);

  278.         }

  279.         /** {@inheritDoc} */
  280.         @Override
  281.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  282.             // set up nutation arguments
  283.             final FundamentalNutationArguments arguments =
  284.                     getNutationArguments(timeScales);

  285.             // set up Poisson series
  286.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  287.             final PoissonSeriesParser baseParser =
  288.                     new PoissonSeriesParser(10).withFirstDelaunay(1);

  289.             final PoissonSeriesParser psiParser =
  290.                     baseParser.
  291.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  292.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  293.             final PoissonSeries psiSeries;
  294.             try (InputStream in = getStream(PSI_EPSILON_SERIES)) {
  295.                 psiSeries = psiParser.parse(in, PSI_EPSILON_SERIES);
  296.             } catch (IOException e) {
  297.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  298.             }

  299.             final PoissonSeriesParser epsilonParser =
  300.                     baseParser.
  301.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  302.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  303.             final PoissonSeries epsilonSeries;
  304.             try (InputStream in = getStream(PSI_EPSILON_SERIES)) {
  305.                 epsilonSeries = epsilonParser.parse(in, PSI_EPSILON_SERIES);
  306.             } catch (IOException e) {
  307.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  308.             }

  309.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  310.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  311.             return new TimeVectorFunction() {

  312.                 /** {@inheritDoc} */
  313.                 @Override
  314.                 public double[] value(final AbsoluteDate date) {
  315.                     final BodiesElements elements = arguments.evaluateAll(date);
  316.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  317.                     return new double[] {
  318.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  319.                     };
  320.                 }

  321.                 /** {@inheritDoc} */
  322.                 @Override
  323.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  324.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  325.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  326.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  327.                     result[0] = psiEpsilon[0];
  328.                     result[1] = psiEpsilon[1];
  329.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  330.                     return result;
  331.                 }

  332.             };

  333.         }

  334.         /** {@inheritDoc} */
  335.         @Override
  336.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  337.                                                   final TimeScales timeScales) {

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

  340.             // constants from IERS 1996 page 21
  341.             // the underlying model is IAU 1982 GMST-UT1
  342.             final AbsoluteDate gmstReference = new AbsoluteDate(
  343.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  344.             final double gmst0 = 24110.54841;
  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.                     // Seconds in the day, adjusted by 12 hours because the
  357.                     // UT1 is supplied as a Julian date beginning at noon.
  358.                     final double sd = FastMath.IEEEremainder(tut1 + Constants.JULIAN_DAY / 2, Constants.JULIAN_DAY);

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

  361.                 }

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

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

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

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

  374.                 }

  375.             };

  376.         }

  377.         /** {@inheritDoc} */
  378.         @Override
  379.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  380.                                                       final TimeScales timeScales) {

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

  383.             // constants from IERS 1996 page 21
  384.             // the underlying model is IAU 1982 GMST-UT1
  385.             final AbsoluteDate gmstReference = new AbsoluteDate(
  386.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  387.             final double gmst1 = 8640184.812866;
  388.             final double gmst2 = 0.093104;
  389.             final double gmst3 = -6.2e-6;

  390.             return new TimeScalarFunction() {

  391.                 /** {@inheritDoc} */
  392.                 @Override
  393.                 public double value(final AbsoluteDate date) {

  394.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  395.                     final double dtai = date.durationFrom(gmstReference);
  396.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  397.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

  400.                 }

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

  404.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  405.                     final T dtai = date.durationFrom(gmstReference);
  406.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  407.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

  410.                 }

  411.             };

  412.         }

  413.         /** {@inheritDoc} */
  414.         @Override
  415.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  416.                                                   final EOPHistory eopHistory,
  417.                                                   final TimeScales timeScales) {

  418.             // obliquity
  419.             final TimeScalarFunction epsilonA = getMeanObliquityFunction(timeScales);

  420.             // GMST function
  421.             final TimeScalarFunction gmst = getGMSTFunction(ut1, timeScales);

  422.             // nutation function
  423.             final TimeVectorFunction nutation = getNutationFunction(timeScales);

  424.             return new TimeScalarFunction() {

  425.                 /** {@inheritDoc} */
  426.                 @Override
  427.                 public double value(final AbsoluteDate date) {

  428.                     // compute equation of equinoxes
  429.                     final double[] angles = nutation.value(date);
  430.                     double deltaPsi = angles[0];
  431.                     if (eopHistory != null) {
  432.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  433.                     }
  434.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

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

  437.                 }

  438.                 /** {@inheritDoc} */
  439.                 @Override
  440.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  441.                     // compute equation of equinoxes
  442.                     final T[] angles = nutation.value(date);
  443.                     T deltaPsi = angles[0];
  444.                     if (eopHistory != null) {
  445.                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
  446.                     }
  447.                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);

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

  450.                 }

  451.             };

  452.         }

  453.         /** {@inheritDoc} */
  454.         @Override
  455.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  463.             // set up Poisson series
  464.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  465.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(17).
  466.                     withOptionalColumn(1).
  467.                     withGamma(7).
  468.                     withFirstDelaunay(2);
  469.             final PoissonSeries xSeries;
  470.             try (InputStream in = getStream(TIDAL_CORRECTION_XP_YP_SERIES)) {
  471.                 xSeries = xyParser.
  472.                         withSinCos(0, 14, milliAS, 15, milliAS).
  473.                         parse(in, TIDAL_CORRECTION_XP_YP_SERIES);
  474.             } catch (IOException e) {
  475.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  476.             }
  477.             final PoissonSeries ySeries;
  478.             try (InputStream in = getStream(TIDAL_CORRECTION_XP_YP_SERIES)) {
  479.                 ySeries = xyParser.
  480.                         withSinCos(0, 16, milliAS, 17, milliAS).
  481.                         parse(in, TIDAL_CORRECTION_XP_YP_SERIES);
  482.             } catch (IOException e) {
  483.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  484.             }

  485.             final double deciMilliS = 1.0e-4;
  486.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
  487.                     withOptionalColumn(1).
  488.                     withGamma(7).
  489.                     withFirstDelaunay(2).
  490.                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
  491.             final PoissonSeries ut1Series;
  492.             try (InputStream in = getStream(TIDAL_CORRECTION_UT1_SERIES)) {
  493.                 ut1Series = ut1Parser.parse(in, TIDAL_CORRECTION_UT1_SERIES);
  494.             } catch (IOException e) {
  495.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  496.             }

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

  498.         }

  499.         /** {@inheritDoc} */
  500.         public LoveNumbers getLoveNumbers() {
  501.             return loadLoveNumbers(LOVE_NUMBERS);
  502.         }

  503.         /** {@inheritDoc} */
  504.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  505.                                                                      final TimeScales timeScales) {

  506.             // set up nutation arguments
  507.             final FundamentalNutationArguments arguments = getNutationArguments(ut1, timeScales);

  508.             // set up Poisson series
  509.             final PoissonSeriesParser k20Parser =
  510.                     new PoissonSeriesParser(18).
  511.                         withOptionalColumn(1).
  512.                         withDoodson(4, 2).
  513.                         withFirstDelaunay(10);
  514.             final PoissonSeriesParser k21Parser =
  515.                     new PoissonSeriesParser(18).
  516.                         withOptionalColumn(1).
  517.                         withDoodson(4, 3).
  518.                         withFirstDelaunay(10);
  519.             final PoissonSeriesParser k22Parser =
  520.                     new PoissonSeriesParser(16).
  521.                         withOptionalColumn(1).
  522.                         withDoodson(4, 2).
  523.                         withFirstDelaunay(10);

  524.             final double pico = 1.0e-12;
  525.             try (InputStream k20 = getStream(K20_FREQUENCY_DEPENDENCE);
  526.                  InputStream k21In1 = getStream(K21_FREQUENCY_DEPENDENCE);
  527.                  InputStream k21In2 = getStream(K21_FREQUENCY_DEPENDENCE);
  528.                  InputStream k22In1 = getStream(K22_FREQUENCY_DEPENDENCE);
  529.                  InputStream k22In2 = getStream(K22_FREQUENCY_DEPENDENCE)) {
  530.                 final PoissonSeries c20Series =
  531.                         k20Parser.
  532.                         withSinCos(0, 18, -pico, 16, pico).
  533.                         parse(k20, K20_FREQUENCY_DEPENDENCE);
  534.                 final PoissonSeries c21Series =
  535.                         k21Parser.
  536.                         withSinCos(0, 17, pico, 18, pico).
  537.                         parse(k21In1, K21_FREQUENCY_DEPENDENCE);
  538.                 final PoissonSeries s21Series =
  539.                         k21Parser.
  540.                         withSinCos(0, 18, -pico, 17, pico).
  541.                         parse(k21In2, K21_FREQUENCY_DEPENDENCE);
  542.                 final PoissonSeries c22Series =
  543.                         k22Parser.
  544.                         withSinCos(0, -1, pico, 16, pico).
  545.                         parse(k22In1, K22_FREQUENCY_DEPENDENCE);
  546.                 final PoissonSeries s22Series =
  547.                         k22Parser.
  548.                         withSinCos(0, 16, -pico, -1, pico).
  549.                         parse(k22In2, K22_FREQUENCY_DEPENDENCE);

  550.                 return new TideFrequencyDependenceFunction(arguments,
  551.                                                            c20Series,
  552.                                                            c21Series, s21Series,
  553.                                                            c22Series, s22Series);
  554.             } catch (IOException e) {
  555.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  556.             }

  557.         }

  558.         /** {@inheritDoc} */
  559.         @Override
  560.         public double getPermanentTide() {
  561.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  562.         }

  563.         /** {@inheritDoc} */
  564.         @Override
  565.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  566.             // constants from IERS 1996 page 47
  567.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  568.             final double coupling     =  0.00112;

  569.             return new TimeVectorFunction() {

  570.                 /** {@inheritDoc} */
  571.                 @Override
  572.                 public double[] value(final AbsoluteDate date) {
  573.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  574.                     return new double[] {
  575.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  576.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  577.                     };
  578.                 }

  579.                 /** {@inheritDoc} */
  580.                 @Override
  581.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  582.                     final FieldPoleCorrection<T> pole = eopHistory.getPoleCorrection(date);
  583.                     final T[] a = MathArrays.buildArray(date.getField(), 2);
  584.                     a[0] = pole.getXp().add(pole.getYp().multiply(coupling)).multiply(globalFactor);
  585.                     a[1] = pole.getXp().multiply(coupling).subtract(pole.getYp()).multiply(globalFactor);
  586.                     return a;
  587.                 }

  588.             };
  589.         }

  590.         /** {@inheritDoc} */
  591.         @Override
  592.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  593.             return new TimeVectorFunction() {

  594.                 /** {@inheritDoc} */
  595.                 @Override
  596.                 public double[] value(final AbsoluteDate date) {
  597.                     // there are no model for ocean pole tide prior to conventions 2010
  598.                     return new double[] {
  599.                         0.0, 0.0
  600.                     };
  601.                 }

  602.                 /** {@inheritDoc} */
  603.                 @Override
  604.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  605.                     // there are no model for ocean pole tide prior to conventions 2010
  606.                     return MathArrays.buildArray(date.getField(), 2);
  607.                 }

  608.             };
  609.         }

  610.         /** {@inheritDoc} */
  611.         @Override
  612.         public double[] getNominalTidalDisplacement() {

  613.             //  // elastic Earth values
  614.             //  return new double[] {
  615.             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  616.             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
  617.             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  618.             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  619.             //      // H₀
  620.             //      -0.31460
  621.             //  };

  622.             // anelastic Earth values
  623.             return new double[] {
  624.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  625.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  626.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  627.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  628.                 // H₀
  629.                 -0.31460
  630.             };

  631.         }

  632.         /** {@inheritDoc} */
  633.         @Override
  634.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  635.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  636.                                                                   18, 17, -1, 18, -1);
  637.         }

  638.         /** {@inheritDoc} */
  639.         @Override
  640.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  641.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  642.                                                                 20, 17, 19, 18, 20);
  643.         }

  644.     },

  645.     /** Constant for IERS 2003 conventions. */
  646.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  679.         /** {@inheritDoc} */
  680.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  681.                                                                  final TimeScales timeScales) {
  682.             try (InputStream in = getStream(NUTATION_ARGUMENTS)) {
  683.                 return new FundamentalNutationArguments(this, timeScale,
  684.                         in, NUTATION_ARGUMENTS, timeScales);
  685.             } catch (IOException e) {
  686.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  687.             }
  688.         }

  689.         /** {@inheritDoc} */
  690.         @Override
  691.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  692.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  693.             final PolynomialNutation epsilonA =
  694.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  695.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  696.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  697.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  698.             return new TimeScalarFunction() {

  699.                 /** {@inheritDoc} */
  700.                 @Override
  701.                 public double value(final AbsoluteDate date) {
  702.                     return epsilonA.value(evaluateTC(date, timeScales));
  703.                 }

  704.                 /** {@inheritDoc} */
  705.                 @Override
  706.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  707.                     return epsilonA.value(evaluateTC(date, timeScales));
  708.                 }

  709.             };

  710.         }

  711.         /** {@inheritDoc} */
  712.         @Override
  713.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  714.             // set up nutation arguments
  715.             final FundamentalNutationArguments arguments =
  716.                     getNutationArguments(timeScales);

  717.             // set up Poisson series
  718.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  719.             final PoissonSeriesParser parser =
  720.                     new PoissonSeriesParser(17).
  721.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  722.                         withFirstDelaunay(4).
  723.                         withFirstPlanetary(9).
  724.                         withSinCos(0, 2, microAS, 3, microAS);

  725.             final PoissonSeries.CompiledSeries xys;
  726.             try (InputStream xIn = getStream(X_SERIES);
  727.                  InputStream yIn = getStream(Y_SERIES);
  728.                  InputStream sIn = getStream(S_SERIES)) {
  729.                 final PoissonSeries xSeries = parser.parse(xIn, X_SERIES);
  730.                 final PoissonSeries ySeries = parser.parse(yIn, Y_SERIES);
  731.                 final PoissonSeries sSeries = parser.parse(sIn, S_SERIES);
  732.                 xys = PoissonSeries.compile(xSeries, ySeries, sSeries);
  733.             } catch (IOException e) {
  734.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  735.             }

  736.             // create a function evaluating the series
  737.             return new TimeVectorFunction() {

  738.                 /** {@inheritDoc} */
  739.                 @Override
  740.                 public double[] value(final AbsoluteDate date) {
  741.                     return xys.value(arguments.evaluateAll(date));
  742.                 }

  743.                 /** {@inheritDoc} */
  744.                 @Override
  745.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  746.                     return xys.value(arguments.evaluateAll(date));
  747.                 }

  748.             };

  749.         }


  750.         /** {@inheritDoc} */
  751.         @Override
  752.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

  753.             // set up the conventional polynomials
  754.             // the following values are from equation 32 in IERS 2003 conventions
  755.             final PolynomialNutation psiA =
  756.                     new PolynomialNutation(    0.0,
  757.                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  758.                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  759.                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  760.             final PolynomialNutation omegaA =
  761.                     new PolynomialNutation(getMeanObliquityFunction(timeScales)
  762.                             .value(getNutationReferenceEpoch(timeScales)),
  763.                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  764.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  765.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  766.             final PolynomialNutation chiA =
  767.                     new PolynomialNutation( 0.0,
  768.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  769.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  770.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  771.             return new PrecessionFunction(psiA, omegaA, chiA, timeScales);

  772.         }

  773.         /** {@inheritDoc} */
  774.         @Override
  775.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  776.             // set up nutation arguments
  777.             final FundamentalNutationArguments arguments =
  778.                     getNutationArguments(timeScales);

  779.             // set up Poisson series
  780.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  781.             final PoissonSeriesParser luniSolarParser =
  782.                     new PoissonSeriesParser(14).withFirstDelaunay(1);
  783.             final PoissonSeriesParser luniSolarPsiParser =
  784.                     luniSolarParser.
  785.                     withSinCos(0, 7, milliAS, 11, milliAS).
  786.                     withSinCos(1, 8, milliAS, 12, milliAS);
  787.             final PoissonSeries psiLuniSolarSeries;
  788.             try (InputStream in = getStream(LUNI_SOLAR_SERIES)) {
  789.                 psiLuniSolarSeries = luniSolarPsiParser.parse(in, LUNI_SOLAR_SERIES);
  790.             } catch (IOException e) {
  791.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  792.             }
  793.             final PoissonSeriesParser luniSolarEpsilonParser =
  794.                     luniSolarParser.
  795.                     withSinCos(0, 13, milliAS, 9, milliAS).
  796.                     withSinCos(1, 14, milliAS, 10, milliAS);
  797.             final PoissonSeries epsilonLuniSolarSeries;
  798.             try (InputStream in = getStream(LUNI_SOLAR_SERIES)) {
  799.                 epsilonLuniSolarSeries = luniSolarEpsilonParser.parse(in, LUNI_SOLAR_SERIES);
  800.             } catch (IOException e) {
  801.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  802.             }

  803.             final PoissonSeriesParser planetaryParser =
  804.                     new PoissonSeriesParser(21).
  805.                         withFirstDelaunay(2).
  806.                         withFirstPlanetary(7);
  807.             final PoissonSeriesParser planetaryPsiParser =
  808.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  809.             final PoissonSeries psiPlanetarySeries;
  810.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  811.                 psiPlanetarySeries = planetaryPsiParser.parse(in, PLANETARY_SERIES);
  812.             } catch (IOException e) {
  813.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  814.             }
  815.             final PoissonSeriesParser planetaryEpsilonParser =
  816.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  817.             final PoissonSeries epsilonPlanetarySeries;
  818.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  819.                 epsilonPlanetarySeries = planetaryEpsilonParser.parse(in, PLANETARY_SERIES);
  820.             } catch (IOException e) {
  821.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  822.             }

  823.             final PoissonSeries.CompiledSeries luniSolarSeries =
  824.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  825.             final PoissonSeries.CompiledSeries planetarySeries =
  826.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  827.             return new TimeVectorFunction() {

  828.                 /** {@inheritDoc} */
  829.                 @Override
  830.                 public double[] value(final AbsoluteDate date) {
  831.                     final BodiesElements elements = arguments.evaluateAll(date);
  832.                     final double[] luniSolar = luniSolarSeries.value(elements);
  833.                     final double[] planetary = planetarySeries.value(elements);
  834.                     return new double[] {
  835.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  836.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  837.                     };
  838.                 }

  839.                 /** {@inheritDoc} */
  840.                 @Override
  841.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  842.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  843.                     final T[] luniSolar = luniSolarSeries.value(elements);
  844.                     final T[] planetary = planetarySeries.value(elements);
  845.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  846.                     result[0] = luniSolar[0].add(planetary[0]);
  847.                     result[1] = luniSolar[1].add(planetary[1]);
  848.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  849.                     return result;
  850.                 }

  851.             };

  852.         }

  853.         /** {@inheritDoc} */
  854.         @Override
  855.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  856.                                                   final TimeScales timeScales) {

  857.             // Earth Rotation Angle
  858.             final StellarAngleCapitaine era =
  859.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  860.             // Polynomial part of the apparent sidereal time series
  861.             // which is the opposite of Equation of Origins (EO)
  862.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  863.             final PoissonSeriesParser parser =
  864.                     new PoissonSeriesParser(17).
  865.                         withFirstDelaunay(4).
  866.                         withFirstPlanetary(9).
  867.                         withSinCos(0, 2, microAS, 3, microAS).
  868.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  869.             final PolynomialNutation minusEO;
  870.             try (InputStream in = getStream(GST_SERIES)) {
  871.                 minusEO = parser.parse(in, GST_SERIES).getPolynomial();
  872.             } catch (IOException e) {
  873.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  874.             }

  875.             // create a function evaluating the series
  876.             return new TimeScalarFunction() {

  877.                 /** {@inheritDoc} */
  878.                 @Override
  879.                 public double value(final AbsoluteDate date) {
  880.                     return era.value(date) + minusEO.value(evaluateTC(date, timeScales));
  881.                 }

  882.                 /** {@inheritDoc} */
  883.                 @Override
  884.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  885.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  886.                 }

  887.             };

  888.         }

  889.         /** {@inheritDoc} */
  890.         @Override
  891.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  892.                                                       final TimeScales timeScales) {

  893.             // Earth Rotation Angle
  894.             final StellarAngleCapitaine era =
  895.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  896.             // Polynomial part of the apparent sidereal time series
  897.             // which is the opposite of Equation of Origins (EO)
  898.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  899.             final PoissonSeriesParser parser =
  900.                     new PoissonSeriesParser(17).
  901.                         withFirstDelaunay(4).
  902.                         withFirstPlanetary(9).
  903.                         withSinCos(0, 2, microAS, 3, microAS).
  904.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  905.             final PolynomialNutation minusEO;
  906.             try (InputStream in = getStream(GST_SERIES)) {
  907.                 minusEO = parser.parse(in, GST_SERIES).getPolynomial();
  908.             } catch (IOException e) {
  909.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  910.             }

  911.             // create a function evaluating the series
  912.             return new TimeScalarFunction() {

  913.                 /** {@inheritDoc} */
  914.                 @Override
  915.                 public double value(final AbsoluteDate date) {
  916.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  917.                 }

  918.                 /** {@inheritDoc} */
  919.                 @Override
  920.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  921.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  922.                 }

  923.             };

  924.         }

  925.         /** {@inheritDoc} */
  926.         @Override
  927.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  928.                                                   final EOPHistory eopHistory,
  929.                                                   final TimeScales timeScales) {

  930.             // set up nutation arguments
  931.             final FundamentalNutationArguments arguments =
  932.                     getNutationArguments(timeScales);

  933.             // mean obliquity function
  934.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

  935.             // set up Poisson series
  936.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  937.             final PoissonSeriesParser luniSolarPsiParser =
  938.                     new PoissonSeriesParser(14).
  939.                         withFirstDelaunay(1).
  940.                         withSinCos(0, 7, milliAS, 11, milliAS).
  941.                         withSinCos(1, 8, milliAS, 12, milliAS);
  942.             final PoissonSeries psiLuniSolarSeries;
  943.             try (InputStream in = getStream(LUNI_SOLAR_SERIES)) {
  944.                 psiLuniSolarSeries = luniSolarPsiParser.parse(in, LUNI_SOLAR_SERIES);
  945.             } catch (IOException e) {
  946.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  947.             }

  948.             final PoissonSeriesParser planetaryPsiParser =
  949.                     new PoissonSeriesParser(21).
  950.                         withFirstDelaunay(2).
  951.                         withFirstPlanetary(7).
  952.                         withSinCos(0, 17, milliAS, 18, milliAS);
  953.             final PoissonSeries psiPlanetarySeries;
  954.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  955.                 psiPlanetarySeries = planetaryPsiParser.parse(in, PLANETARY_SERIES);
  956.             } catch (IOException e) {
  957.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  958.             }


  959.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  960.             final PoissonSeriesParser gstParser =
  961.                     new PoissonSeriesParser(17).
  962.                         withFirstDelaunay(4).
  963.                         withFirstPlanetary(9).
  964.                         withSinCos(0, 2, microAS, 3, microAS).
  965.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  966.             final PoissonSeries gstSeries;
  967.             try (InputStream in = getStream(GST_SERIES)) {
  968.                 gstSeries = gstParser.parse(in, GST_SERIES);
  969.             } catch (IOException e) {
  970.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  971.             }
  972.             final PoissonSeries.CompiledSeries psiGstSeries =
  973.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  974.             // ERA function
  975.             final TimeScalarFunction era =
  976.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  977.             return new TimeScalarFunction() {

  978.                 /** {@inheritDoc} */
  979.                 @Override
  980.                 public double value(final AbsoluteDate date) {

  981.                     // evaluate equation of origins
  982.                     final BodiesElements elements = arguments.evaluateAll(date);
  983.                     final double[] angles = psiGstSeries.value(elements);
  984.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  985.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  986.                     final double epsilonA = epsilon.value(date);

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

  990.                 }

  991.                 /** {@inheritDoc} */
  992.                 @Override
  993.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  994.                     // evaluate equation of origins
  995.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  996.                     final T[] angles = psiGstSeries.value(elements);
  997.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  998.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  999.                     final T epsilonA = epsilon.value(date);

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

  1003.                 }

  1004.             };

  1005.         }

  1006.         /** {@inheritDoc} */
  1007.         @Override
  1008.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  1016.             // set up Poisson series
  1017.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1018.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  1019.                     withOptionalColumn(1).
  1020.                     withGamma(2).
  1021.                     withFirstDelaunay(3);
  1022.             try (InputStream tidalIn1 = getStream(TIDAL_CORRECTION_XP_YP_SERIES);
  1023.                  InputStream tidalIn2 = getStream(TIDAL_CORRECTION_XP_YP_SERIES);
  1024.                  InputStream tidalUt1 = getStream(TIDAL_CORRECTION_UT1_SERIES)) {
  1025.                 final PoissonSeries xSeries =
  1026.                         xyParser.
  1027.                         withSinCos(0, 10, microAS, 11, microAS).
  1028.                         parse(tidalIn1, TIDAL_CORRECTION_XP_YP_SERIES);
  1029.                 final PoissonSeries ySeries =
  1030.                         xyParser.
  1031.                         withSinCos(0, 12, microAS, 13, microAS).
  1032.                         parse(tidalIn2, TIDAL_CORRECTION_XP_YP_SERIES);

  1033.                 final double microS = 1.0e-6;
  1034.                 final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1035.                         withOptionalColumn(1).
  1036.                         withGamma(2).
  1037.                         withFirstDelaunay(3).
  1038.                         withSinCos(0, 10, microS, 11, microS);
  1039.                 final PoissonSeries ut1Series =
  1040.                         ut1Parser.parse(tidalUt1, TIDAL_CORRECTION_UT1_SERIES);

  1041.                 return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
  1042.             } catch (IOException e) {
  1043.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1044.             }

  1045.         }

  1046.         /** {@inheritDoc} */
  1047.         public LoveNumbers getLoveNumbers() {
  1048.             return loadLoveNumbers(LOVE_NUMBERS);
  1049.         }

  1050.         /** {@inheritDoc} */
  1051.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  1052.                                                                      final TimeScales timeScales) {

  1053.             // set up nutation arguments
  1054.             final FundamentalNutationArguments arguments = getNutationArguments(ut1, timeScales);

  1055.             // set up Poisson series
  1056.             final PoissonSeriesParser k20Parser =
  1057.                     new PoissonSeriesParser(18).
  1058.                         withOptionalColumn(1).
  1059.                         withDoodson(4, 2).
  1060.                         withFirstDelaunay(10);
  1061.             final PoissonSeriesParser k21Parser =
  1062.                     new PoissonSeriesParser(18).
  1063.                         withOptionalColumn(1).
  1064.                         withDoodson(4, 3).
  1065.                         withFirstDelaunay(10);
  1066.             final PoissonSeriesParser k22Parser =
  1067.                     new PoissonSeriesParser(16).
  1068.                         withOptionalColumn(1).
  1069.                         withDoodson(4, 2).
  1070.                         withFirstDelaunay(10);

  1071.             final double pico = 1.0e-12;
  1072.             try (InputStream k20In = getStream(K20_FREQUENCY_DEPENDENCE);
  1073.                  InputStream k21In1 = getStream(K21_FREQUENCY_DEPENDENCE);
  1074.                  InputStream k21In2 = getStream(K21_FREQUENCY_DEPENDENCE);
  1075.                  InputStream k22In1 = getStream(K22_FREQUENCY_DEPENDENCE);
  1076.                  InputStream k22In2 = getStream(K22_FREQUENCY_DEPENDENCE)) {
  1077.                 final PoissonSeries c20Series =
  1078.                         k20Parser.
  1079.                         withSinCos(0, 18, -pico, 16, pico).
  1080.                         parse(k20In, K20_FREQUENCY_DEPENDENCE);
  1081.                 final PoissonSeries c21Series =
  1082.                         k21Parser.
  1083.                         withSinCos(0, 17, pico, 18, pico).
  1084.                         parse(k21In1, K21_FREQUENCY_DEPENDENCE);
  1085.                 final PoissonSeries s21Series =
  1086.                         k21Parser.
  1087.                         withSinCos(0, 18, -pico, 17, pico).
  1088.                         parse(k21In2, K21_FREQUENCY_DEPENDENCE);
  1089.                 final PoissonSeries c22Series =
  1090.                         k22Parser.
  1091.                         withSinCos(0, -1, pico, 16, pico).
  1092.                         parse(k22In1, K22_FREQUENCY_DEPENDENCE);
  1093.                 final PoissonSeries s22Series =
  1094.                         k22Parser.
  1095.                         withSinCos(0, 16, -pico, -1, pico).
  1096.                         parse(k22In2, K22_FREQUENCY_DEPENDENCE);

  1097.                 return new TideFrequencyDependenceFunction(arguments,
  1098.                                                            c20Series,
  1099.                                                            c21Series, s21Series,
  1100.                                                            c22Series, s22Series);
  1101.             } catch (IOException e) {
  1102.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1103.             }

  1104.         }

  1105.         /** {@inheritDoc} */
  1106.         @Override
  1107.         public double getPermanentTide() {
  1108.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1109.         }

  1110.         /** {@inheritDoc} */
  1111.         @Override
  1112.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1113.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  1114.             final TimeScale utc = eopHistory.getTimeScales().getUTC();
  1115.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  1116.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  1117.                     /** {@inheritDoc} */
  1118.                     @Override
  1119.                     public MeanPole convert(final double[] rawFields) {
  1120.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  1121.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  1122.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  1123.                     }
  1124.                 };
  1125.             final SimpleTimeStampedTableParser<MeanPole> parser =
  1126.                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
  1127.             final List<MeanPole> annualPoleList;
  1128.             try (InputStream in = getStream(ANNUAL_POLE)) {
  1129.                 annualPoleList = parser.parse(in, ANNUAL_POLE);
  1130.             } catch (IOException e) {
  1131.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1132.             }
  1133.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  1134.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  1135.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  1136.                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);

  1137.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  1138.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  1139.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  1140.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  1141.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  1142.             // constants from IERS 2003, section 6.2
  1143.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1144.             final double ratio        =  0.00115;

  1145.             return new TimeVectorFunction() {

  1146.                 /** {@inheritDoc} */
  1147.                 @Override
  1148.                 public double[] value(final AbsoluteDate date) {

  1149.                     // we can't compute anything before the range covered by the annual pole file
  1150.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  1151.                         return new double[] {
  1152.                             0.0, 0.0
  1153.                         };
  1154.                     }

  1155.                     // evaluate mean pole
  1156.                     double meanPoleX = 0;
  1157.                     double meanPoleY = 0;
  1158.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  1159.                         // we are within the range covered by the annual pole file,
  1160.                         // we interpolate within it
  1161.                         try {
  1162.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  1163.                             annualCache.getNeighbors(date).forEach(neighbor ->
  1164.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  1165.                                                             new double[] {
  1166.                                                                 neighbor.getX(), neighbor.getY()
  1167.                                                             }));
  1168.                             final double[] interpolated = interpolator.value(0);
  1169.                             meanPoleX = interpolated[0];
  1170.                             meanPoleY = interpolated[1];
  1171.                         } catch (TimeStampedCacheException tsce) {
  1172.                             // this should never happen
  1173.                             throw new OrekitInternalError(tsce);
  1174.                         }
  1175.                     } else {

  1176.                         // we are after the range covered by the annual pole file,
  1177.                         // we use the polynomial extension
  1178.                         final double t = date.durationFrom(
  1179.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1180.                         meanPoleX = xp0 + t * xp0Dot;
  1181.                         meanPoleY = yp0 + t * yp0Dot;

  1182.                     }

  1183.                     // evaluate wobble variables
  1184.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1185.                     final double m1 = correction.getXp() - meanPoleX;
  1186.                     final double m2 = meanPoleY - correction.getYp();

  1187.                     return new double[] {
  1188.                         // the following correspond to the equations published in IERS 2003 conventions,
  1189.                         // section 6.2 page 65. In the publication, the equations read:
  1190.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1191.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1192.                         // However, it seems there are sign errors in these equations, which have
  1193.                         // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1194.                         // publication, the equations read:
  1195.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1196.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1197.                         // the newer equations seem more consistent with the premises as the
  1198.                         // deformation due to the centrifugal potential has the form:
  1199.                         // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1200.                         // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1201.                         // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1202.                         // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1203.                         // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1204.                         // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1205.                         // and Im(k₂)/Re(k₂) is very close to +0.00115
  1206.                         // As the equation as written in the IERS 2003 conventions are used in
  1207.                         // legacy systems, we have reproduced this alleged error here (and fixed it in
  1208.                         // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1209.                         // using the IERS 2003 conventions for solid pole tide computation other than
  1210.                         // for validation or reproducibility of legacy applications behavior.
  1211.                         // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1212.                         // the effect is quite small. A test case on a propagated orbit showed a position change
  1213.                         // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1214.                         globalFactor * (m1 - ratio * m2),
  1215.                         globalFactor * (m2 + ratio * m1),
  1216.                     };

  1217.                 }

  1218.                 /** {@inheritDoc} */
  1219.                 @Override
  1220.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1226.                     // evaluate mean pole
  1227.                     T meanPoleX = date.getField().getZero();
  1228.                     T meanPoleY = date.getField().getZero();
  1229.                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
  1230.                         // we are within the range covered by the annual pole file,
  1231.                         // we interpolate within it
  1232.                         try {
  1233.                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
  1234.                             final T[] y = MathArrays.buildArray(date.getField(), 2);
  1235.                             final T zero = date.getField().getZero();
  1236.                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
  1237.                                                                                                        // for example removing derivatives
  1238.                                                                                                        // if T was DerivativeStructure
  1239.                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
  1240.                                 y[0] = zero.add(neighbor.getX());
  1241.                                 y[1] = zero.add(neighbor.getY());
  1242.                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
  1243.                             });
  1244.                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
  1245.                             meanPoleX = interpolated[0];
  1246.                             meanPoleY = interpolated[1];
  1247.                         } catch (TimeStampedCacheException tsce) {
  1248.                             // this should never happen
  1249.                             throw new OrekitInternalError(tsce);
  1250.                         }
  1251.                     } else {

  1252.                         // we are after the range covered by the annual pole file,
  1253.                         // we use the polynomial extension
  1254.                         final T t = date.durationFrom(
  1255.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1256.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1257.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1258.                     }

  1259.                     // evaluate wobble variables
  1260.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1261.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1262.                     final T m2 = meanPoleY.subtract(correction.getYp());

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

  1264.                     // the following correspond to the equations published in IERS 2003 conventions,
  1265.                     // section 6.2 page 65. In the publication, the equations read:
  1266.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1267.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1268.                     // However, it seems there are sign errors in these equations, which have
  1269.                     // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  1270.                     // publication, the equations read:
  1271.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1272.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1273.                     // the newer equations seem more consistent with the premises as the
  1274.                     // deformation due to the centrifugal potential has the form:
  1275.                     // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  1276.                     // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  1277.                     // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  1278.                     // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  1279.                     // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  1280.                     // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  1281.                     // and Im(k₂)/Re(k₂) is very close to +0.00115
  1282.                     // As the equation as written in the IERS 2003 conventions are used in
  1283.                     // legacy systems, we have reproduced this alleged error here (and fixed it in
  1284.                     // the IERS 2010 conventions below) for validation purposes. We don't recommend
  1285.                     // using the IERS 2003 conventions for solid pole tide computation other than
  1286.                     // for validation or reproducibility of legacy applications behavior.
  1287.                     // As solid pole tide is small and as the sign change is on the smallest coefficient,
  1288.                     // the effect is quite small. A test case on a propagated orbit showed a position change
  1289.                     // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  1290.                     a[0] = m1.add(m2.multiply(-ratio)).multiply(globalFactor);
  1291.                     a[1] = m2.add(m1.multiply( ratio)).multiply(globalFactor);

  1292.                     return a;

  1293.                 }

  1294.             };

  1295.         }

  1296.         /** {@inheritDoc} */
  1297.         @Override
  1298.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1299.             return new TimeVectorFunction() {

  1300.                 /** {@inheritDoc} */
  1301.                 @Override
  1302.                 public double[] value(final AbsoluteDate date) {
  1303.                     // there are no model for ocean pole tide prior to conventions 2010
  1304.                     return new double[] {
  1305.                         0.0, 0.0
  1306.                     };
  1307.                 }

  1308.                 /** {@inheritDoc} */
  1309.                 @Override
  1310.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1311.                     // there are no model for ocean pole tide prior to conventions 2010
  1312.                     return MathArrays.buildArray(date.getField(), 2);
  1313.                 }

  1314.             };
  1315.         }

  1316.         /** {@inheritDoc} */
  1317.         @Override
  1318.         public double[] getNominalTidalDisplacement() {
  1319.             return new double[] {
  1320.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1321.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1322.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1323.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1324.                 // H₀
  1325.                 -0.31460
  1326.             };
  1327.         }

  1328.         /** {@inheritDoc} */
  1329.         @Override
  1330.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1331.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1332.                                                                   18, 15, 16, 17, 18);
  1333.         }

  1334.         /** {@inheritDoc} */
  1335.         @Override
  1336.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1337.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1338.                                                                 18, 15, 16, 17, 18);
  1339.         }

  1340.     },

  1341.     /** Constant for IERS 2010 conventions. */
  1342.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1373.         /** {@inheritDoc} */
  1374.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  1375.                                                                  final TimeScales timeScales) {
  1376.             try (InputStream in = getStream(NUTATION_ARGUMENTS)) {
  1377.                 return new FundamentalNutationArguments(this, timeScale,
  1378.                         in, NUTATION_ARGUMENTS, timeScales);
  1379.             } catch (IOException e) {
  1380.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1381.             }
  1382.         }

  1383.         /** {@inheritDoc} */
  1384.         @Override
  1385.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  1386.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1387.             final PolynomialNutation epsilonA =
  1388.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1389.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1390.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1391.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1392.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1393.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1394.             return new TimeScalarFunction() {

  1395.                 /** {@inheritDoc} */
  1396.                 @Override
  1397.                 public double value(final AbsoluteDate date) {
  1398.                     return epsilonA.value(evaluateTC(date, timeScales));
  1399.                 }

  1400.                 /** {@inheritDoc} */
  1401.                 @Override
  1402.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1403.                     return epsilonA.value(evaluateTC(date, timeScales));
  1404.                 }

  1405.             };

  1406.         }

  1407.         /** {@inheritDoc} */
  1408.         @Override
  1409.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  1410.             // set up nutation arguments
  1411.             final FundamentalNutationArguments arguments =
  1412.                     getNutationArguments(timeScales);

  1413.             // set up Poisson series
  1414.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1415.             final PoissonSeriesParser parser =
  1416.                     new PoissonSeriesParser(17).
  1417.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1418.                         withFirstDelaunay(4).
  1419.                         withFirstPlanetary(9).
  1420.                         withSinCos(0, 2, microAS, 3, microAS);
  1421.             final PoissonSeries.CompiledSeries xys;
  1422.             try (InputStream xIn = getStream(X_SERIES);
  1423.                  InputStream yIn = getStream(Y_SERIES);
  1424.                  InputStream sIn = getStream(S_SERIES)) {
  1425.                 final PoissonSeries xSeries = parser.parse(xIn, X_SERIES);
  1426.                 final PoissonSeries ySeries = parser.parse(yIn, Y_SERIES);
  1427.                 final PoissonSeries sSeries = parser.parse(sIn, S_SERIES);
  1428.                 xys = PoissonSeries.compile(xSeries, ySeries, sSeries);
  1429.             } catch (IOException e) {
  1430.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1431.             }

  1432.             // create a function evaluating the series
  1433.             return new TimeVectorFunction() {

  1434.                 /** {@inheritDoc} */
  1435.                 @Override
  1436.                 public double[] value(final AbsoluteDate date) {
  1437.                     return xys.value(arguments.evaluateAll(date));
  1438.                 }

  1439.                 /** {@inheritDoc} */
  1440.                 @Override
  1441.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1442.                     return xys.value(arguments.evaluateAll(date));
  1443.                 }

  1444.             };

  1445.         }

  1446.         /** {@inheritDoc} */
  1447.         public LoveNumbers getLoveNumbers() {
  1448.             return loadLoveNumbers(LOVE_NUMBERS);
  1449.         }

  1450.         /** {@inheritDoc} */
  1451.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  1452.                                                                      final TimeScales timeScales) {

  1453.             // set up nutation arguments
  1454.             final FundamentalNutationArguments arguments = getNutationArguments(ut1, timeScales);

  1455.             // set up Poisson series
  1456.             final PoissonSeriesParser k20Parser =
  1457.                     new PoissonSeriesParser(18).
  1458.                         withOptionalColumn(1).
  1459.                         withDoodson(4, 2).
  1460.                         withFirstDelaunay(10);
  1461.             final PoissonSeriesParser k21Parser =
  1462.                     new PoissonSeriesParser(18).
  1463.                         withOptionalColumn(1).
  1464.                         withDoodson(4, 3).
  1465.                         withFirstDelaunay(10);
  1466.             final PoissonSeriesParser k22Parser =
  1467.                     new PoissonSeriesParser(16).
  1468.                         withOptionalColumn(1).
  1469.                         withDoodson(4, 2).
  1470.                         withFirstDelaunay(10);

  1471.             final double pico = 1.0e-12;
  1472.             try (InputStream k0In = getStream(K20_FREQUENCY_DEPENDENCE);
  1473.                  InputStream k21In1 = getStream(K21_FREQUENCY_DEPENDENCE);
  1474.                  InputStream k21In2 = getStream(K21_FREQUENCY_DEPENDENCE);
  1475.                  InputStream k22In1 = getStream(K22_FREQUENCY_DEPENDENCE);
  1476.                  InputStream k22In2 = getStream(K22_FREQUENCY_DEPENDENCE)) {
  1477.                 final PoissonSeries c20Series =
  1478.                         k20Parser.
  1479.                         withSinCos(0, 18, -pico, 16, pico).
  1480.                         parse(k0In, K20_FREQUENCY_DEPENDENCE);
  1481.                 final PoissonSeries c21Series =
  1482.                         k21Parser.
  1483.                         withSinCos(0, 17, pico, 18, pico).
  1484.                         parse(k21In1, K21_FREQUENCY_DEPENDENCE);
  1485.                 final PoissonSeries s21Series =
  1486.                         k21Parser.
  1487.                         withSinCos(0, 18, -pico, 17, pico).
  1488.                         parse(k21In2, K21_FREQUENCY_DEPENDENCE);
  1489.                 final PoissonSeries c22Series =
  1490.                         k22Parser.
  1491.                         withSinCos(0, -1, pico, 16, pico).
  1492.                         parse(k22In1, K22_FREQUENCY_DEPENDENCE);
  1493.                 final PoissonSeries s22Series =
  1494.                         k22Parser.
  1495.                         withSinCos(0, 16, -pico, -1, pico).
  1496.                         parse(k22In2, K22_FREQUENCY_DEPENDENCE);

  1497.                 return new TideFrequencyDependenceFunction(arguments,
  1498.                                                            c20Series,
  1499.                                                            c21Series, s21Series,
  1500.                                                            c22Series, s22Series);
  1501.             } catch (IOException e) {
  1502.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1503.             }

  1504.         }

  1505.         /** {@inheritDoc} */
  1506.         @Override
  1507.         public double getPermanentTide() {
  1508.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1509.         }

  1510.         /** Compute pole wobble variables m₁ and m₂.
  1511.          * @param date current date
  1512.          * @param eopHistory EOP history
  1513.          * @return array containing m₁ and m₂
  1514.          */
  1515.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1516.             // polynomial model from IERS 2010, table 7.7
  1517.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1518.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1519.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1520.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1521.             final AbsoluteDate changeDate =
  1522.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

  1523.             // evaluate mean pole
  1524.             final double[] xPolynomial;
  1525.             final double[] yPolynomial;
  1526.             if (date.compareTo(changeDate) <= 0) {
  1527.                 xPolynomial = new double[] {
  1528.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1529.                 };
  1530.                 yPolynomial = new double[] {
  1531.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1532.                 };
  1533.             } else {
  1534.                 xPolynomial = new double[] {
  1535.                     23.513 * f0, 7.6141 * f1
  1536.                 };
  1537.                 yPolynomial = new double[] {
  1538.                     358.891 * f0,  -0.6287 * f1
  1539.                 };
  1540.             }
  1541.             double meanPoleX = 0;
  1542.             double meanPoleY = 0;
  1543.             final double t = date.durationFrom(
  1544.                     eopHistory.getTimeScales().getJ2000Epoch());
  1545.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1546.                 meanPoleX = meanPoleX * t + xPolynomial[i];
  1547.             }
  1548.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1549.                 meanPoleY = meanPoleY * t + yPolynomial[i];
  1550.             }

  1551.             // evaluate wobble variables
  1552.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1553.             final double m1 = correction.getXp() - meanPoleX;
  1554.             final double m2 = meanPoleY - correction.getYp();

  1555.             return new double[] {
  1556.                 m1, m2
  1557.             };

  1558.         }

  1559.         /** Compute pole wobble variables m₁ and m₂.
  1560.          * @param date current date
  1561.          * @param <T> type of the field elements
  1562.          * @param eopHistory EOP history
  1563.          * @return array containing m₁ and m₂
  1564.          */
  1565.         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1566.             // polynomial model from IERS 2010, table 7.7
  1567.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1568.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1569.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1570.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1571.             final AbsoluteDate changeDate =
  1572.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

  1573.             // evaluate mean pole
  1574.             final double[] xPolynomial;
  1575.             final double[] yPolynomial;
  1576.             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
  1577.                 xPolynomial = new double[] {
  1578.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1579.                 };
  1580.                 yPolynomial = new double[] {
  1581.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1582.                 };
  1583.             } else {
  1584.                 xPolynomial = new double[] {
  1585.                     23.513 * f0, 7.6141 * f1
  1586.                 };
  1587.                 yPolynomial = new double[] {
  1588.                     358.891 * f0,  -0.6287 * f1
  1589.                 };
  1590.             }
  1591.             T meanPoleX = date.getField().getZero();
  1592.             T meanPoleY = date.getField().getZero();
  1593.             final T t = date.durationFrom(
  1594.                     eopHistory.getTimeScales().getJ2000Epoch());
  1595.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1596.                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
  1597.             }
  1598.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1599.                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
  1600.             }

  1601.             // evaluate wobble variables
  1602.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1603.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1604.             m[0] = correction.getXp().subtract(meanPoleX);
  1605.             m[1] = meanPoleY.subtract(correction.getYp());

  1606.             return m;

  1607.         }

  1608.         /** {@inheritDoc} */
  1609.         @Override
  1610.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1611.             // constants from IERS 2010, section 6.4
  1612.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1613.             final double ratio        =  0.00115;

  1614.             return new TimeVectorFunction() {

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

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

  1620.                     return new double[] {
  1621.                         // the following correspond to the equations published in IERS 2010 conventions,
  1622.                         // section 6.4 page 94. The equations read:
  1623.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1624.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1625.                         // These equations seem to fix what was probably a sign error in IERS 2003
  1626.                         // conventions section 6.2 page 65. In this older publication, the equations read:
  1627.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1628.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1629.                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
  1630.                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
  1631.                     };

  1632.                 }

  1633.                 /** {@inheritDoc} */
  1634.                 @Override
  1635.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1639.                     // the following correspond to the equations published in IERS 2010 conventions,
  1640.                     // section 6.4 page 94. The equations read:
  1641.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1642.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1643.                     // These equations seem to fix what was probably a sign error in IERS 2003
  1644.                     // conventions section 6.2 page 65. In this older publication, the equations read:
  1645.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1646.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1647.                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
  1648.                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);

  1649.                     return a;

  1650.                 }

  1651.             };

  1652.         }

  1653.         /** {@inheritDoc} */
  1654.         @Override
  1655.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1656.             return new TimeVectorFunction() {

  1657.                 /** {@inheritDoc} */
  1658.                 @Override
  1659.                 public double[] value(final AbsoluteDate date) {

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

  1662.                     return new double[] {
  1663.                         // the following correspond to the equations published in IERS 2010 conventions,
  1664.                         // section 6.4 page 94 equation 6.24:
  1665.                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1666.                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1667.                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
  1668.                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
  1669.                     };

  1670.                 }

  1671.                 /** {@inheritDoc} */
  1672.                 @Override
  1673.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1683.                     return v;

  1684.                 }

  1685.             };

  1686.         }

  1687.         /** {@inheritDoc} */
  1688.         @Override
  1689.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

  1690.             // set up the conventional polynomials
  1691.             // the following values are from equation 5.40 in IERS 2010 conventions
  1692.             final PolynomialNutation psiA =
  1693.                     new PolynomialNutation(   0.0,
  1694.                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1695.                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1696.                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1697.                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1698.                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1699.             final PolynomialNutation omegaA =
  1700.                     new PolynomialNutation(getMeanObliquityFunction(timeScales)
  1701.                             .value(getNutationReferenceEpoch(timeScales)),
  1702.                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1703.                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1704.                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1705.                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1706.                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1707.             final PolynomialNutation chiA =
  1708.                     new PolynomialNutation( 0.0,
  1709.                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1710.                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1711.                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1712.                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1713.                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

  1714.             return new PrecessionFunction(psiA, omegaA, chiA, timeScales);

  1715.         }

  1716.         /** {@inheritDoc} */
  1717.         @Override
  1718.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  1719.             // set up nutation arguments
  1720.             final FundamentalNutationArguments arguments =
  1721.                     getNutationArguments(timeScales);

  1722.             // set up Poisson series
  1723.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1724.             final PoissonSeriesParser parser =
  1725.                     new PoissonSeriesParser(17).
  1726.                         withFirstDelaunay(4).
  1727.                         withFirstPlanetary(9).
  1728.                         withSinCos(0, 2, microAS, 3, microAS);
  1729.             final PoissonSeries.CompiledSeries psiEpsilonSeries;
  1730.             try (InputStream psiIn = getStream(PSI_SERIES);
  1731.                  InputStream epsilonIn = getStream(EPSILON_SERIES)) {
  1732.                 final PoissonSeries psiSeries     = parser.parse(psiIn, PSI_SERIES);
  1733.                 final PoissonSeries epsilonSeries = parser.parse(epsilonIn, EPSILON_SERIES);
  1734.                 psiEpsilonSeries = PoissonSeries.compile(psiSeries, epsilonSeries);
  1735.             } catch (IOException e) {
  1736.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1737.             }

  1738.             return new TimeVectorFunction() {

  1739.                 /** {@inheritDoc} */
  1740.                 @Override
  1741.                 public double[] value(final AbsoluteDate date) {
  1742.                     final BodiesElements elements = arguments.evaluateAll(date);
  1743.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1744.                     return new double[] {
  1745.                         psiEpsilon[0], psiEpsilon[1],
  1746.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  1747.                     };
  1748.                 }

  1749.                 /** {@inheritDoc} */
  1750.                 @Override
  1751.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1752.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1753.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1754.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1755.                     result[0] = psiEpsilon[0];
  1756.                     result[1] = psiEpsilon[1];
  1757.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  1758.                     return result;
  1759.                 }

  1760.             };

  1761.         }

  1762.         /** {@inheritDoc} */
  1763.         @Override
  1764.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  1765.                                                   final TimeScales timeScales) {

  1766.             // Earth Rotation Angle
  1767.             final StellarAngleCapitaine era =
  1768.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  1769.             // Polynomial part of the apparent sidereal time series
  1770.             // which is the opposite of Equation of Origins (EO)
  1771.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1772.             final PoissonSeriesParser parser =
  1773.                     new PoissonSeriesParser(17).
  1774.                         withFirstDelaunay(4).
  1775.                         withFirstPlanetary(9).
  1776.                         withSinCos(0, 2, microAS, 3, microAS).
  1777.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1778.             final PolynomialNutation minusEO;
  1779.             try (InputStream in = getStream(GST_SERIES)) {
  1780.                 minusEO = parser.parse(in, GST_SERIES).getPolynomial();
  1781.             } catch (IOException e) {
  1782.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1783.             }

  1784.             // create a function evaluating the series
  1785.             return new TimeScalarFunction() {

  1786.                 /** {@inheritDoc} */
  1787.                 @Override
  1788.                 public double value(final AbsoluteDate date) {
  1789.                     return era.value(date) + minusEO.value(evaluateTC(date, timeScales));
  1790.                 }

  1791.                 /** {@inheritDoc} */
  1792.                 @Override
  1793.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1794.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  1795.                 }

  1796.             };

  1797.         }

  1798.         /** {@inheritDoc} */
  1799.         @Override
  1800.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  1801.                                                       final TimeScales timeScales) {

  1802.             // Earth Rotation Angle
  1803.             final StellarAngleCapitaine era =
  1804.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  1805.             // Polynomial part of the apparent sidereal time series
  1806.             // which is the opposite of Equation of Origins (EO)
  1807.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1808.             final PoissonSeriesParser parser =
  1809.                     new PoissonSeriesParser(17).
  1810.                         withFirstDelaunay(4).
  1811.                         withFirstPlanetary(9).
  1812.                         withSinCos(0, 2, microAS, 3, microAS).
  1813.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1814.             final PolynomialNutation minusEO;
  1815.             try (InputStream in = getStream(GST_SERIES)) {
  1816.                 minusEO = parser.parse(in, GST_SERIES).getPolynomial();
  1817.             } catch (IOException e) {
  1818.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1819.             }

  1820.             // create a function evaluating the series
  1821.             return new TimeScalarFunction() {

  1822.                 /** {@inheritDoc} */
  1823.                 @Override
  1824.                 public double value(final AbsoluteDate date) {
  1825.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  1826.                 }

  1827.                 /** {@inheritDoc} */
  1828.                 @Override
  1829.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1830.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  1831.                 }

  1832.             };

  1833.         }

  1834.         /** {@inheritDoc} */
  1835.         @Override
  1836.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  1837.                                                   final EOPHistory eopHistory,
  1838.                                                   final TimeScales timeScales) {

  1839.             // set up nutation arguments
  1840.             final FundamentalNutationArguments arguments =
  1841.                     getNutationArguments(timeScales);

  1842.             // mean obliquity function
  1843.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

  1844.             // set up Poisson series
  1845.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1846.             final PoissonSeriesParser baseParser =
  1847.                     new PoissonSeriesParser(17).
  1848.                         withFirstDelaunay(4).
  1849.                         withFirstPlanetary(9).
  1850.                         withSinCos(0, 2, microAS, 3, microAS);
  1851.             final PoissonSeriesParser gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1852.             final PoissonSeries.CompiledSeries psiGstSeries;
  1853.             try (InputStream psiIn = getStream(PSI_SERIES);
  1854.                  InputStream gstIn = getStream(GST_SERIES)) {
  1855.                 final PoissonSeries psiSeries        = baseParser.parse(psiIn, PSI_SERIES);
  1856.                 final PoissonSeries gstSeries        = gstParser.parse(gstIn, GST_SERIES);
  1857.                 psiGstSeries = PoissonSeries.compile(psiSeries, gstSeries);
  1858.             } catch (IOException e) {
  1859.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1860.             }

  1861.             // ERA function
  1862.             final TimeScalarFunction era =
  1863.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  1864.             return new TimeScalarFunction() {

  1865.                 /** {@inheritDoc} */
  1866.                 @Override
  1867.                 public double value(final AbsoluteDate date) {

  1868.                     // evaluate equation of origins
  1869.                     final BodiesElements elements = arguments.evaluateAll(date);
  1870.                     final double[] angles = psiGstSeries.value(elements);
  1871.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1872.                     final double deltaPsi = angles[0] + ddPsi;
  1873.                     final double epsilonA = epsilon.value(date);

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

  1877.                 }

  1878.                 /** {@inheritDoc} */
  1879.                 @Override
  1880.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1881.                     // evaluate equation of origins
  1882.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1883.                     final T[] angles = psiGstSeries.value(elements);
  1884.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1885.                     final T deltaPsi = angles[0].add(ddPsi);
  1886.                     final T epsilonA = epsilon.value(date);

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

  1890.                 }

  1891.             };

  1892.         }

  1893.         /** {@inheritDoc} */
  1894.         @Override
  1895.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  1903.             // set up Poisson series
  1904.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1905.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  1906.                     withOptionalColumn(1).
  1907.                     withGamma(2).
  1908.                     withFirstDelaunay(3);
  1909.             try (InputStream xpIn = getStream(TIDAL_CORRECTION_XP_YP_SERIES);
  1910.                  InputStream ypIn = getStream(TIDAL_CORRECTION_XP_YP_SERIES);
  1911.                  InputStream ut1In = getStream(TIDAL_CORRECTION_UT1_SERIES)) {
  1912.                 final PoissonSeries xSeries =
  1913.                         xyParser.
  1914.                         withSinCos(0, 10, microAS, 11, microAS).
  1915.                         parse(xpIn, TIDAL_CORRECTION_XP_YP_SERIES);
  1916.                 final PoissonSeries ySeries =
  1917.                         xyParser.
  1918.                         withSinCos(0, 12, microAS, 13, microAS).
  1919.                         parse(ypIn, TIDAL_CORRECTION_XP_YP_SERIES);

  1920.                 final double microS = 1.0e-6;
  1921.                 final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1922.                         withOptionalColumn(1).
  1923.                         withGamma(2).
  1924.                         withFirstDelaunay(3).
  1925.                         withSinCos(0, 10, microS, 11, microS);
  1926.                 final PoissonSeries ut1Series =
  1927.                         ut1Parser.parse(ut1In, TIDAL_CORRECTION_UT1_SERIES);

  1928.                 return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
  1929.             } catch (IOException e) {
  1930.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1931.             }

  1932.         }

  1933.         /** {@inheritDoc} */
  1934.         @Override
  1935.         public double[] getNominalTidalDisplacement() {
  1936.             return new double[] {
  1937.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1938.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1939.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1940.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1941.                 // H₀
  1942.                 -0.31460
  1943.             };
  1944.         }

  1945.         /** {@inheritDoc} */
  1946.         @Override
  1947.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1948.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1949.                                                                   18, 15, 16, 17, 18);
  1950.         }

  1951.         /** {@inheritDoc} */
  1952.         @Override
  1953.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1954.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1955.                                                                 18, 15, 16, 17, 18);
  1956.         }

  1957.     };

  1958.     /** Pattern for delimiting regular expressions. */
  1959.     private static final Pattern SEPARATOR = Pattern.compile("\\p{Space}+");

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

  1962.     /** Get the reference epoch for fundamental nutation arguments.
  1963.      *
  1964.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1965.      *
  1966.      * @return reference epoch for fundamental nutation arguments
  1967.      * @since 6.1
  1968.      * @see #getNutationReferenceEpoch(TimeScales)
  1969.      */
  1970.     @DefaultDataContext
  1971.     public AbsoluteDate getNutationReferenceEpoch() {
  1972.         return getNutationReferenceEpoch(DataContext.getDefault().getTimeScales());
  1973.     }

  1974.     /**
  1975.      * Get the reference epoch for fundamental nutation arguments.
  1976.      *
  1977.      * @param timeScales to use for the reference epoch.
  1978.      * @return reference epoch for fundamental nutation arguments
  1979.      * @since 10.1
  1980.      */
  1981.     public AbsoluteDate getNutationReferenceEpoch(final TimeScales timeScales) {
  1982.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1983.         return timeScales.getJ2000Epoch();
  1984.     }

  1985.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1986.      *
  1987.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1988.      *
  1989.      * @param date current date
  1990.      * @return date offset in Julian centuries
  1991.      * @since 6.1
  1992.      * @see #evaluateTC(AbsoluteDate, TimeScales)
  1993.      */
  1994.     @DefaultDataContext
  1995.     public double evaluateTC(final AbsoluteDate date) {
  1996.         return evaluateTC(date, DataContext.getDefault().getTimeScales());
  1997.     }

  1998.     /**
  1999.      * Evaluate the date offset between the current date and the {@link
  2000.      * #getNutationReferenceEpoch() reference date}.
  2001.      *
  2002.      * @param date       current date
  2003.      * @param timeScales used in the evaluation.
  2004.      * @return date offset in Julian centuries
  2005.      * @since 10.1
  2006.      */
  2007.     public double evaluateTC(final AbsoluteDate date, final TimeScales timeScales) {
  2008.         return date.durationFrom(getNutationReferenceEpoch(timeScales)) /
  2009.                 Constants.JULIAN_CENTURY;
  2010.     }

  2011.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  2012.      *
  2013.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2014.      *
  2015.      * @param date current date
  2016.      * @param <T> type of the field elements
  2017.      * @return date offset in Julian centuries
  2018.      * @since 9.0
  2019.      * @see #evaluateTC(FieldAbsoluteDate, TimeScales)
  2020.      */
  2021.     @DefaultDataContext
  2022.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
  2023.         return evaluateTC(date, DataContext.getDefault().getTimeScales());
  2024.     }

  2025.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  2026.      * @param <T> type of the field elements
  2027.      * @param date current date
  2028.      * @param timeScales used in the evaluation.
  2029.      * @return date offset in Julian centuries
  2030.      * @since 10.1
  2031.      */
  2032.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date,
  2033.                                                         final TimeScales timeScales) {
  2034.         return date.durationFrom(getNutationReferenceEpoch(timeScales))
  2035.                 .divide(Constants.JULIAN_CENTURY);
  2036.     }

  2037.     /**
  2038.      * Get the fundamental nutation arguments. Does not compute GMST based values: gamma,
  2039.      * gammaDot.
  2040.      *
  2041.      * @param timeScales other time scales used in the computation including TAI and TT.
  2042.      * @return fundamental nutation arguments
  2043.      * @see #getNutationArguments(TimeScale, TimeScales)
  2044.      * @since 10.1
  2045.      */
  2046.     protected FundamentalNutationArguments getNutationArguments(
  2047.             final TimeScales timeScales) {

  2048.         return getNutationArguments(null, timeScales);
  2049.     }

  2050.     /** Get the fundamental nutation arguments.
  2051.      *
  2052.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2053.      *
  2054.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  2055.      * (typically {@link TimeScales#getUT1(IERSConventions, boolean) UT1})
  2056.      * @return fundamental nutation arguments
  2057.      * @since 6.1
  2058.      * @see #getNutationArguments(TimeScale, TimeScales)
  2059.      * @see #getNutationArguments(TimeScales)
  2060.      */
  2061.     @DefaultDataContext
  2062.     public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
  2063.         return getNutationArguments(timeScale, DataContext.getDefault().getTimeScales());
  2064.     }

  2065.     /**
  2066.      * Get the fundamental nutation arguments.
  2067.      *
  2068.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time (typically
  2069.      *                  {@link TimeScales#getUT1(IERSConventions, boolean) UT1})
  2070.      * @param timeScales other time scales used in the computation including TAI and TT.
  2071.      * @return fundamental nutation arguments
  2072.      * @since 10.1
  2073.      */
  2074.     public abstract FundamentalNutationArguments getNutationArguments(
  2075.             TimeScale timeScale,
  2076.             TimeScales timeScales);

  2077.     /** Get the function computing mean obliquity of the ecliptic.
  2078.      *
  2079.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2080.      *
  2081.      * @return function computing mean obliquity of the ecliptic
  2082.      * @since 6.1
  2083.      * @see #getMeanObliquityFunction(TimeScales)
  2084.      */
  2085.     @DefaultDataContext
  2086.     public TimeScalarFunction getMeanObliquityFunction() {
  2087.         return getMeanObliquityFunction(DataContext.getDefault().getTimeScales());
  2088.     }

  2089.     /**
  2090.      * Get the function computing mean obliquity of the ecliptic.
  2091.      *
  2092.      * @param timeScales used in computing the function.
  2093.      * @return function computing mean obliquity of the ecliptic
  2094.      * @since 10.1
  2095.      */
  2096.     public abstract TimeScalarFunction getMeanObliquityFunction(TimeScales timeScales);

  2097.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  2098.      * <p>
  2099.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  2100.      * </p>
  2101.      *
  2102.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2103.      *
  2104.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  2105.      * @since 6.1
  2106.      * @see #getXYSpXY2Function(TimeScales)
  2107.      */
  2108.     @DefaultDataContext
  2109.     public TimeVectorFunction getXYSpXY2Function() {
  2110.         return getXYSpXY2Function(DataContext.getDefault().getTimeScales());
  2111.     }

  2112.     /**
  2113.      * Get the function computing the Celestial Intermediate Pole and Celestial
  2114.      * Intermediate Origin components.
  2115.      * <p>
  2116.      * The returned function computes the two X, Y components of CIP and the S+XY/2
  2117.      * component of the non-rotating CIO.
  2118.      * </p>
  2119.      *
  2120.      * @param timeScales used to define the function.
  2121.      * @return function computing the Celestial Intermediate Pole and Celestial
  2122.      * Intermediate Origin components
  2123.      * @since 10.1
  2124.      */
  2125.     public abstract TimeVectorFunction getXYSpXY2Function(TimeScales timeScales);

  2126.     /** Get the function computing the raw Earth Orientation Angle.
  2127.      *
  2128.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2129.      *
  2130.      * <p>
  2131.      * The raw angle does not contain any correction. If for example dTU1 correction
  2132.      * due to tidal effect is desired, it must be added afterward by the caller.
  2133.      * The returned value contain the angle as the value and the angular rate as
  2134.      * the first derivative.
  2135.      * </p>
  2136.      * @param ut1 UT1 time scale
  2137.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  2138.      * @since 6.1
  2139.      * @see #getEarthOrientationAngleFunction(TimeScale, TimeScale)
  2140.      */
  2141.     @DefaultDataContext
  2142.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
  2143.         return getEarthOrientationAngleFunction(ut1,
  2144.                 DataContext.getDefault().getTimeScales().getTAI());
  2145.     }

  2146.     /** Get the function computing the raw Earth Orientation Angle.
  2147.      * <p>
  2148.      * The raw angle does not contain any correction. If for example dTU1 correction
  2149.      * due to tidal effect is desired, it must be added afterward by the caller.
  2150.      * The returned value contain the angle as the value and the angular rate as
  2151.      * the first derivative.
  2152.      * </p>
  2153.      * @param ut1 UT1 time scale
  2154.      * @param tai TAI time scale
  2155.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  2156.      * @since 10.1
  2157.      */
  2158.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1,
  2159.                                                                final TimeScale tai) {
  2160.         return new StellarAngleCapitaine(ut1, tai);
  2161.     }

  2162.     /** Get the function computing the precession angles.
  2163.      * <p>
  2164.      * The function returned computes the three precession angles
  2165.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  2166.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  2167.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  2168.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  2169.      * #getNutationReferenceEpoch() nutation reference epoch}.
  2170.      * </p>
  2171.      *
  2172.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2173.      *
  2174.      * @return function computing the precession angle
  2175.      * @since 6.1
  2176.      * @see #getPrecessionFunction(TimeScales)
  2177.      */
  2178.     @DefaultDataContext
  2179.     public TimeVectorFunction getPrecessionFunction()
  2180.     {
  2181.         return getPrecessionFunction(DataContext.getDefault().getTimeScales());
  2182.     }

  2183.     /** Get the function computing the precession angles.
  2184.      * <p>
  2185.      * The function returned computes the three precession angles
  2186.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  2187.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  2188.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  2189.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  2190.      * #getNutationReferenceEpoch() nutation reference epoch}.
  2191.      * </p>
  2192.      * @return function computing the precession angle
  2193.      * @since 10.1
  2194.      * @param timeScales used to define the function.
  2195.      */
  2196.     public abstract TimeVectorFunction getPrecessionFunction(TimeScales timeScales);

  2197.     /** Get the function computing the nutation angles.
  2198.      *
  2199.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2200.      *
  2201.      * <p>
  2202.      * The function returned computes the two classical angles ΔΨ and Δε,
  2203.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  2204.      * resolution C7 (the correction is forced to 0 before this date)
  2205.      * </p>
  2206.      * @return function computing the nutation in longitude ΔΨ and Δε
  2207.      * and the correction of equation of equinoxes
  2208.      * @since 6.1
  2209.      */
  2210.     @DefaultDataContext
  2211.     public TimeVectorFunction getNutationFunction() {
  2212.         return getNutationFunction(DataContext.getDefault().getTimeScales());
  2213.     }

  2214.     /** Get the function computing the nutation angles.
  2215.      * <p>
  2216.      * The function returned computes the two classical angles ΔΨ and Δε,
  2217.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  2218.      * resolution C7 (the correction is forced to 0 before this date)
  2219.      * </p>
  2220.      * @return function computing the nutation in longitude ΔΨ and Δε
  2221.      * and the correction of equation of equinoxes
  2222.      * @param timeScales used in the computation including TAI and TT.
  2223.      * @since 10.1
  2224.      */
  2225.     public abstract TimeVectorFunction getNutationFunction(TimeScales timeScales);

  2226.     /** Get the function computing Greenwich mean sidereal time, in radians.
  2227.      *
  2228.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2229.      *
  2230.      * @param ut1 UT1 time scale
  2231.      * @return function computing Greenwich mean sidereal time
  2232.      * @since 6.1
  2233.      * @see #getGMSTFunction(TimeScale, TimeScales)
  2234.      */
  2235.     @DefaultDataContext
  2236.     public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
  2237.         return getGMSTFunction(ut1, DataContext.getDefault().getTimeScales());
  2238.     }

  2239.     /**
  2240.      * Get the function computing Greenwich mean sidereal time, in radians.
  2241.      *
  2242.      * @param ut1 UT1 time scale
  2243.      * @param timeScales other time scales used in the computation including TAI and TT.
  2244.      * @return function computing Greenwich mean sidereal time
  2245.      * @since 10.1
  2246.      */
  2247.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1,
  2248.                                                        TimeScales timeScales);

  2249.     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
  2250.      *
  2251.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2252.      *
  2253.      * @param ut1 UT1 time scale
  2254.      * @return function computing Greenwich mean sidereal time rate
  2255.      * @since 9.0
  2256.      * @see #getGMSTRateFunction(TimeScale, TimeScales)
  2257.      */
  2258.     @DefaultDataContext
  2259.     public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {
  2260.         return getGMSTRateFunction(ut1,
  2261.                 DataContext.getDefault().getTimeScales());
  2262.     }

  2263.     /**
  2264.      * Get the function computing Greenwich mean sidereal time rate, in radians per
  2265.      * second.
  2266.      *
  2267.      * @param ut1 UT1 time scale
  2268.      * @param timeScales other time scales used in the computation including TAI and TT.
  2269.      * @return function computing Greenwich mean sidereal time rate
  2270.      * @since 10.1
  2271.      */
  2272.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1,
  2273.                                                            TimeScales timeScales);

  2274.     /**
  2275.      * Get the function computing Greenwich apparent sidereal time, in radians.
  2276.      *
  2277.      * <p>This method uses the {@link DataContext#getDefault() default data context} if
  2278.      * {@code eopHistory == null}.
  2279.      *
  2280.      * @param ut1        UT1 time scale
  2281.      * @param eopHistory EOP history. If {@code null} then no nutation correction is
  2282.      *                   applied for EOP.
  2283.      * @return function computing Greenwich apparent sidereal time
  2284.      * @since 6.1
  2285.      * @see #getGASTFunction(TimeScale, EOPHistory, TimeScales)
  2286.      */
  2287.     @DefaultDataContext
  2288.     public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  2289.                                               final EOPHistory eopHistory) {
  2290.         final TimeScales timeScales = eopHistory != null ?
  2291.                 eopHistory.getTimeScales() :
  2292.                 DataContext.getDefault().getTimeScales();
  2293.         return getGASTFunction(ut1, eopHistory, timeScales);
  2294.     }

  2295.     /**
  2296.      * Get the function computing Greenwich apparent sidereal time, in radians.
  2297.      *
  2298.      * @param ut1        UT1 time scale
  2299.      * @param eopHistory EOP history. If {@code null} then no nutation correction is
  2300.      *                   applied for EOP.
  2301.      * @param timeScales        TAI time scale.
  2302.      * @return function computing Greenwich apparent sidereal time
  2303.      * @since 10.1
  2304.      */
  2305.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1,
  2306.                                                        EOPHistory eopHistory,
  2307.                                                        TimeScales timeScales);

  2308.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  2309.      *
  2310.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2311.      *
  2312.      * @return function computing tidal corrections for Earth Orientation Parameters,
  2313.      * for xp, yp, ut1 and lod respectively
  2314.      * @since 6.1
  2315.      * @see #getEOPTidalCorrection(TimeScales)
  2316.      */
  2317.     @DefaultDataContext
  2318.     public TimeVectorFunction getEOPTidalCorrection() {
  2319.         return getEOPTidalCorrection(DataContext.getDefault().getTimeScales());
  2320.     }

  2321.     /**
  2322.      * Get the function computing tidal corrections for Earth Orientation Parameters.
  2323.      *
  2324.      * @param timeScales used in the computation. The TT and TAI scales are used.
  2325.      * @return function computing tidal corrections for Earth Orientation Parameters, for
  2326.      * xp, yp, ut1 and lod respectively
  2327.      * @since 10.1
  2328.      */
  2329.     public abstract TimeVectorFunction getEOPTidalCorrection(TimeScales timeScales);

  2330.     /** Get the Love numbers.
  2331.      * @return Love numbers
  2332.           * @since 6.1
  2333.      */
  2334.     public abstract LoveNumbers getLoveNumbers();

  2335.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2336.      *
  2337.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2338.      *
  2339.      * @param ut1 UT1 time scale
  2340.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2341.      * @since 6.1
  2342.      * @see #getTideFrequencyDependenceFunction(TimeScale, TimeScales)
  2343.      */
  2344.     @DefaultDataContext
  2345.     public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {
  2346.         return getTideFrequencyDependenceFunction(ut1,
  2347.                 DataContext.getDefault().getTimeScales());
  2348.     }

  2349.     /**
  2350.      * Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2351.      * ΔS₂₂).
  2352.      *
  2353.      * @param ut1 UT1 time scale
  2354.      * @param timeScales other time scales used in the computation including TAI and TT.
  2355.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2356.      * ΔS₂₂).
  2357.      * @since 10.1
  2358.      */
  2359.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(
  2360.             TimeScale ut1,
  2361.             TimeScales timeScales);

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

  2366.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  2367.      * @param eopHistory EOP history
  2368.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2369.           * @since 6.1
  2370.      */
  2371.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory);

  2372.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  2373.      * @param eopHistory EOP history
  2374.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2375.           * @since 6.1
  2376.      */
  2377.     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory);

  2378.     /** Get the nominal values of the displacement numbers.
  2379.      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
  2380.      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
  2381.      * H₀ permanent deformation amplitude
  2382.      * @since 9.1
  2383.      */
  2384.     public abstract double[] getNominalTidalDisplacement();

  2385.     /** Get the correction function for tidal displacement for diurnal tides.
  2386.      * <ul>
  2387.      *  <li>f[0]: radial correction, longitude cosine part</li>
  2388.      *  <li>f[1]: radial correction, longitude sine part</li>
  2389.      *  <li>f[2]: North correction, longitude cosine part</li>
  2390.      *  <li>f[3]: North correction, longitude sine part</li>
  2391.      *  <li>f[4]: East correction, longitude cosine part</li>
  2392.      *  <li>f[5]: East correction, longitude sine part</li>
  2393.      * </ul>
  2394.      * @return correction function for tidal displacement
  2395.      * @since 9.1
  2396.      */
  2397.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal();

  2398.     /** Get the correction function for tidal displacement for diurnal tides.
  2399.      * <ul>
  2400.      *  <li>f[0]: radial correction, longitude cosine part</li>
  2401.      *  <li>f[1]: radial correction, longitude sine part</li>
  2402.      *  <li>f[2]: North correction, longitude cosine part</li>
  2403.      *  <li>f[3]: North correction, longitude sine part</li>
  2404.      *  <li>f[4]: East correction, longitude cosine part</li>
  2405.      *  <li>f[5]: East correction, longitude sine part</li>
  2406.      * </ul>
  2407.      * @param tableName name for the diurnal tides table
  2408.      * @param cols total number of columns of the diurnal tides table
  2409.      * @param rIp column holding ∆Rf(ip) in the diurnal tides table, counting from 1
  2410.      * @param rOp column holding ∆Rf(op) in the diurnal tides table, counting from 1
  2411.      * @param tIp column holding ∆Tf(ip) in the diurnal tides table, counting from 1
  2412.      * @param tOp column holding ∆Tf(op) in the diurnal tides table, counting from 1
  2413.      * @return correction function for tidal displacement for diurnal tides
  2414.           * @since 9.1
  2415.      */
  2416.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal(final String tableName, final int cols,
  2417.                                                                                    final int rIp, final int rOp,
  2418.                                                                                    final int tIp, final int tOp) {

  2419.         try (InputStream in1 = getStream(tableName);
  2420.              InputStream in2 = getStream(tableName);
  2421.              InputStream in3 = getStream(tableName);
  2422.              InputStream in4 = getStream(tableName);
  2423.              InputStream in5 = getStream(tableName);
  2424.              InputStream in6 = getStream(tableName)) {
  2425.             // radial component, missing the sin 2φ factor; this corresponds to:
  2426.             //  - equation 15a in IERS conventions 1996, chapter 7
  2427.             //  - equation 16a in IERS conventions 2003, chapter 7
  2428.             //  - equation 7.12a in IERS conventions 2010, chapter 7
  2429.             final PoissonSeries drCos = new PoissonSeriesParser(cols).
  2430.                                         withOptionalColumn(1).
  2431.                                         withDoodson(4, 3).
  2432.                                         withFirstDelaunay(10).
  2433.                                         withSinCos(0, rIp, +1.0e-3, rOp, +1.0e-3).
  2434.                                         parse(in1, tableName);
  2435.             final PoissonSeries drSin = new PoissonSeriesParser(cols).
  2436.                                         withOptionalColumn(1).
  2437.                                         withDoodson(4, 3).
  2438.                                         withFirstDelaunay(10).
  2439.                                         withSinCos(0, rOp, -1.0e-3, rIp, +1.0e-3).
  2440.                                         parse(in2, tableName);

  2441.             // North component, missing the cos 2φ factor; this corresponds to:
  2442.             //  - equation 15b in IERS conventions 1996, chapter 7
  2443.             //  - equation 16b in IERS conventions 2003, chapter 7
  2444.             //  - equation 7.12b in IERS conventions 2010, chapter 7
  2445.             final PoissonSeries dnCos = new PoissonSeriesParser(cols).
  2446.                                         withOptionalColumn(1).
  2447.                                         withDoodson(4, 3).
  2448.                                         withFirstDelaunay(10).
  2449.                                         withSinCos(0, tIp, +1.0e-3, tOp, +1.0e-3).
  2450.                                         parse(in3, tableName);
  2451.             final PoissonSeries dnSin = new PoissonSeriesParser(cols).
  2452.                                         withOptionalColumn(1).
  2453.                                         withDoodson(4, 3).
  2454.                                         withFirstDelaunay(10).
  2455.                                         withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2456.                                         parse(in4, tableName);

  2457.             // East component, missing the sin φ factor; this corresponds to:
  2458.             //  - equation 15b in IERS conventions 1996, chapter 7
  2459.             //  - equation 16b in IERS conventions 2003, chapter 7
  2460.             //  - equation 7.12b in IERS conventions 2010, chapter 7
  2461.             final PoissonSeries deCos = new PoissonSeriesParser(cols).
  2462.                                         withOptionalColumn(1).
  2463.                                         withDoodson(4, 3).
  2464.                                         withFirstDelaunay(10).
  2465.                                         withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2466.                                         parse(in5, tableName);
  2467.             final PoissonSeries deSin = new PoissonSeriesParser(cols).
  2468.                                         withOptionalColumn(1).
  2469.                                         withDoodson(4, 3).
  2470.                                         withFirstDelaunay(10).
  2471.                                         withSinCos(0, tIp, -1.0e-3, tOp, -1.0e-3).
  2472.                                         parse(in6, tableName);

  2473.             return PoissonSeries.compile(drCos, drSin,
  2474.                                          dnCos, dnSin,
  2475.                                          deCos, deSin);
  2476.         } catch (IOException e) {
  2477.             throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  2478.         }

  2479.     }

  2480.     /** Get the correction function for tidal displacement for zonal tides.
  2481.      * <ul>
  2482.      *  <li>f[0]: radial correction</li>
  2483.      *  <li>f[1]: North correction</li>
  2484.      * </ul>
  2485.      * @return correction function for tidal displacement
  2486.      * @since 9.1
  2487.      */
  2488.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal();

  2489.     /** Get the correction function for tidal displacement for zonal tides.
  2490.      * <ul>
  2491.      *  <li>f[0]: radial correction</li>
  2492.      *  <li>f[1]: North correction</li>
  2493.      * </ul>
  2494.      * @param tableName name for the zonal tides table
  2495.      * @param cols total number of columns of the table
  2496.      * @param rIp column holding ∆Rf(ip) in the table, counting from 1
  2497.      * @param rOp column holding ∆Rf(op) in the table, counting from 1
  2498.      * @param tIp column holding ∆Tf(ip) in the table, counting from 1
  2499.      * @param tOp column holding ∆Tf(op) in the table, counting from 1
  2500.      * @return correction function for tidal displacement for zonal tides
  2501.           * @since 9.1
  2502.      */
  2503.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionZonal(final String tableName, final int cols,
  2504.                                                                                  final int rIp, final int rOp,
  2505.                                                                                  final int tIp, final int tOp) {

  2506.         try (InputStream in1 = getStream(tableName);
  2507.              InputStream in2 = getStream(tableName)) {
  2508.             // radial component, missing the 3⁄2 sin² φ - 1⁄2 factor; this corresponds to:
  2509.             //  - equation 16a in IERS conventions 1996, chapter 7
  2510.             //  - equation 17a in IERS conventions 2003, chapter 7
  2511.             //  - equation 7.13a in IERS conventions 2010, chapter 7
  2512.             final PoissonSeries dr = new PoissonSeriesParser(cols).
  2513.                                      withOptionalColumn(1).
  2514.                                      withDoodson(4, 3).
  2515.                                      withFirstDelaunay(10).
  2516.                                      withSinCos(0, rOp, +1.0e-3, rIp, +1.0e-3).
  2517.                                      parse(in1, tableName);

  2518.             // North component, missing the sin 2φ factor; this corresponds to:
  2519.             //  - equation 16b in IERS conventions 1996, chapter 7
  2520.             //  - equation 17b in IERS conventions 2003, chapter 7
  2521.             //  - equation 7.13b in IERS conventions 2010, chapter 7
  2522.             final PoissonSeries dn = new PoissonSeriesParser(cols).
  2523.                                      withOptionalColumn(1).
  2524.                                      withDoodson(4, 3).
  2525.                                      withFirstDelaunay(10).
  2526.                                      withSinCos(0, tOp, +1.0e-3, tIp, +1.0e-3).
  2527.                                      parse(in2, tableName);

  2528.             return PoissonSeries.compile(dr, dn);
  2529.         } catch (IOException e) {
  2530.             throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  2531.         }

  2532.     }

  2533.     /** Interface for functions converting nutation corrections between
  2534.      * δΔψ/δΔε to δX/δY.
  2535.      * <ul>
  2536.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2537.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2538.      * </ul>
  2539.      * @since 6.1
  2540.      */
  2541.     public interface NutationCorrectionConverter {

  2542.         /** Convert nutation corrections.
  2543.          * @param date current date
  2544.          * @param ddPsi δΔψ part of the nutation correction
  2545.          * @param ddEpsilon δΔε part of the nutation correction
  2546.          * @return array containing δX and δY
  2547.          */
  2548.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon);

  2549.         /** Convert nutation corrections.
  2550.          * @param date current date
  2551.          * @param dX δX part of the nutation correction
  2552.          * @param dY δY part of the nutation correction
  2553.          * @return array containing δΔψ and δΔε
  2554.          */
  2555.         double[] toEquinox(AbsoluteDate date, double dX, double dY);

  2556.     }

  2557.     /** Create a function converting nutation corrections between
  2558.      * δX/δY and δΔψ/δΔε.
  2559.      * <ul>
  2560.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2561.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2562.      * </ul>
  2563.      *
  2564.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2565.      *
  2566.      * @return a new converter
  2567.      * @since 6.1
  2568.      * @see #getNutationCorrectionConverter(TimeScales)
  2569.      */
  2570.     @DefaultDataContext
  2571.     public NutationCorrectionConverter getNutationCorrectionConverter() {
  2572.         return getNutationCorrectionConverter(DataContext.getDefault().getTimeScales());
  2573.     }

  2574.     /** Create a function converting nutation corrections between
  2575.      * δX/δY and δΔψ/δΔε.
  2576.      * <ul>
  2577.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2578.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2579.      * </ul>
  2580.      * @return a new converter
  2581.      * @since 10.1
  2582.      * @param timeScales used to define the conversion.
  2583.      */
  2584.     public NutationCorrectionConverter getNutationCorrectionConverter(
  2585.             final TimeScales timeScales) {

  2586.         // get models parameters
  2587.         final TimeVectorFunction precessionFunction = getPrecessionFunction(timeScales);
  2588.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction(timeScales);
  2589.         final AbsoluteDate date0 = getNutationReferenceEpoch(timeScales);
  2590.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  2591.         return new NutationCorrectionConverter() {

  2592.             /** {@inheritDoc} */
  2593.             @Override
  2594.             public double[] toNonRotating(final AbsoluteDate date,
  2595.                                           final double ddPsi, final double ddEpsilon) {
  2596.                 // compute precession angles psiA, omegaA and chiA
  2597.                 final double[] angles = precessionFunction.value(date);

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

  2601.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  2602.                 return new double[] {
  2603.                     sinEA * ddPsi + c * ddEpsilon,
  2604.                     ddEpsilon - c * sinEA * ddPsi
  2605.                 };

  2606.             }

  2607.             /** {@inheritDoc} */
  2608.             @Override
  2609.             public double[] toEquinox(final AbsoluteDate date,
  2610.                                       final double dX, final double dY) {
  2611.                 // compute precession angles psiA, omegaA and chiA
  2612.                 final double[] angles   = precessionFunction.value(date);

  2613.                 // conversion coefficients
  2614.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2615.                 final double c     = angles[0] * cosE0 - angles[2];
  2616.                 final double opC2  = 1 + c * c;

  2617.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  2618.                 return new double[] {
  2619.                     (dX - c * dY) / (sinEA * opC2),
  2620.                     (dY + c * dX) / opC2
  2621.                 };
  2622.             }

  2623.         };

  2624.     }

  2625.     /** Load the Love numbers.
  2626.      * @param nameLove name of the Love number resource
  2627.      * @return Love numbers
  2628.      */
  2629.     protected LoveNumbers loadLoveNumbers(final String nameLove) {
  2630.         try {

  2631.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  2632.             final double[][] real      = new double[4][];
  2633.             final double[][] imaginary = new double[4][];
  2634.             final double[][] plus      = new double[4][];
  2635.             for (int i = 0; i < real.length; ++i) {
  2636.                 real[i]      = new double[i + 1];
  2637.                 imaginary[i] = new double[i + 1];
  2638.                 plus[i]      = new double[i + 1];
  2639.             }

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

  2641.                 if (stream == null) {
  2642.                     // this should never happen with files embedded within Orekit
  2643.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  2644.                 }

  2645.                 int lineNumber = 1;
  2646.                 String line = null;
  2647.                 // setup the reader
  2648.                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {

  2649.                     line = reader.readLine();

  2650.                     // look for the Love numbers
  2651.                     while (line != null) {

  2652.                         line = line.trim();
  2653.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  2654.                             final String[] fields = SEPARATOR.split(line);
  2655.                             if (fields.length != 5) {
  2656.                                 // this should never happen with files embedded within Orekit
  2657.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2658.                                                           lineNumber, nameLove, line);
  2659.                             }
  2660.                             final int n = Integer.parseInt(fields[0]);
  2661.                             final int m = Integer.parseInt(fields[1]);
  2662.                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  2663.                                 // this should never happen with files embedded within Orekit
  2664.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2665.                                                           lineNumber, nameLove, line);

  2666.                             }
  2667.                             real[n][m]      = Double.parseDouble(fields[2]);
  2668.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  2669.                             plus[n][m]      = Double.parseDouble(fields[4]);
  2670.                         }

  2671.                         // next line
  2672.                         lineNumber++;
  2673.                         line = reader.readLine();

  2674.                     }
  2675.                 } catch (NumberFormatException nfe) {
  2676.                     // this should never happen with files embedded within Orekit
  2677.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2678.                                               lineNumber, nameLove, line);
  2679.                 }
  2680.             }

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

  2682.         } catch (IOException ioe) {
  2683.             // this should never happen with files embedded within Orekit
  2684.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  2685.         }
  2686.     }

  2687.     /** Get a data stream.
  2688.      * @param name file name of the resource stream
  2689.      * @return stream
  2690.      */
  2691.     private static InputStream getStream(final String name) {
  2692.         return IERSConventions.class.getResourceAsStream(name);
  2693.     }

  2694.     /** Correction to equation of equinoxes.
  2695.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2696.      * taking effect since 1997-02-27 for continuity
  2697.      * </p>
  2698.      */
  2699.     private static class IAU1994ResolutionC7 {

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

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


  2704.         /** Evaluate the correction.
  2705.          * @param arguments Delaunay for nutation
  2706.          * @param tai TAI time scale.
  2707.          * @return correction value (0 before 1997-02-27)
  2708.          */
  2709.         public static double value(final DelaunayArguments arguments,
  2710.                                    final TimeScale tai) {
  2711.             /* Start date for applying Moon corrections to the equation of the equinoxes.
  2712.              * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
  2713.              */
  2714.             final AbsoluteDate modelStart = new AbsoluteDate(1997, 2, 27, 0, 0, 30, tai);
  2715.             if (arguments.getDate().compareTo(modelStart) >= 0) {

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

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

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

  2722.             } else {
  2723.                 return 0.0;
  2724.             }
  2725.         }

  2726.         /** Evaluate the correction.
  2727.          * @param arguments Delaunay for nutation
  2728.          * @param tai TAI time scale.
  2729.          * @param <T> type of the field elements
  2730.          * @return correction value (0 before 1997-02-27)
  2731.          */
  2732.         public static <T extends RealFieldElement<T>> T value(
  2733.                 final FieldDelaunayArguments<T> arguments,
  2734.                 final TimeScale tai) {
  2735.             /* Start date for applying Moon corrections to the equation of the equinoxes.
  2736.              * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
  2737.              */
  2738.             final AbsoluteDate modelStart = new AbsoluteDate(1997, 2, 27, 0, 0, 30, tai);
  2739.             if (arguments.getDate().toAbsoluteDate().compareTo(modelStart) >= 0) {

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

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

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

  2746.             } else {
  2747.                 return arguments.getDate().getField().getZero();
  2748.             }
  2749.         }

  2750.     };

  2751.     /** Stellar angle model.
  2752.      * <p>
  2753.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  2754.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  2755.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  2756.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  2757.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  2758.      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
  2759.      * </p>
  2760.      * <p>
  2761.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  2762.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  2763.      * IERS conventions 2003 and 2010.
  2764.      * </p>
  2765.      */
  2766.     private static class StellarAngleCapitaine implements TimeScalarFunction {

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

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

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

  2775.         /** UT1 time scale. */
  2776.         private final TimeScale ut1;

  2777.         /** Reference date of Capitaine's Earth Rotation Angle model. */
  2778.         private final AbsoluteDate referenceDate;

  2779.         /** Simple constructor.
  2780.          * @param ut1 UT1 time scale
  2781.          * @param tai TAI time scale
  2782.          */
  2783.         StellarAngleCapitaine(final TimeScale ut1, final TimeScale tai) {
  2784.             this.ut1 = ut1;
  2785.             referenceDate = new AbsoluteDate(
  2786.                     DateComponents.J2000_EPOCH,
  2787.                     TimeComponents.H12,
  2788.                     tai);
  2789.         }

  2790.         /** Get the rotation rate.
  2791.          * @return rotation rate
  2792.          */
  2793.         public double getRate() {
  2794.             return ERA_1A + ERA_1B;
  2795.         }

  2796.         /** {@inheritDoc} */
  2797.         @Override
  2798.         public double value(final AbsoluteDate date) {

  2799.             // split the date offset as a full number of days plus a smaller part
  2800.             final int secondsInDay = 86400;
  2801.             final double dt  = date.durationFrom(referenceDate);
  2802.             final long days  = ((long) dt) / secondsInDay;
  2803.             final double dtA = secondsInDay * days;
  2804.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

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

  2806.         }

  2807.         /** {@inheritDoc} */
  2808.         @Override
  2809.         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2810.             // split the date offset as a full number of days plus a smaller part
  2811.             final int secondsInDay = 86400;
  2812.             final T dt  = date.durationFrom(referenceDate);
  2813.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2814.             final double dtA = secondsInDay * days;
  2815.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));

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

  2817.         }

  2818.     }

  2819.     /** Mean pole. */
  2820.     private static class MeanPole implements TimeStamped, Serializable {

  2821.         /** Serializable UID. */
  2822.         private static final long serialVersionUID = 20131028l;

  2823.         /** Date. */
  2824.         private final AbsoluteDate date;

  2825.         /** X coordinate. */
  2826.         private double x;

  2827.         /** Y coordinate. */
  2828.         private double y;

  2829.         /** Simple constructor.
  2830.          * @param date date
  2831.          * @param x x coordinate
  2832.          * @param y y coordinate
  2833.          */
  2834.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2835.             this.date = date;
  2836.             this.x    = x;
  2837.             this.y    = y;
  2838.         }

  2839.         /** {@inheritDoc} */
  2840.         @Override
  2841.         public AbsoluteDate getDate() {
  2842.             return date;
  2843.         }

  2844.         /** Get x coordinate.
  2845.          * @return x coordinate
  2846.          */
  2847.         public double getX() {
  2848.             return x;
  2849.         }

  2850.         /** Get y coordinate.
  2851.          * @return y coordinate
  2852.          */
  2853.         public double getY() {
  2854.             return y;
  2855.         }

  2856.     }

  2857.     /** Local class for precession function. */
  2858.     private class PrecessionFunction implements TimeVectorFunction {

  2859.         /** Polynomial nutation for psiA. */
  2860.         private final PolynomialNutation psiA;

  2861.         /** Polynomial nutation for omegaA. */
  2862.         private final PolynomialNutation omegaA;

  2863.         /** Polynomial nutation for chiA. */
  2864.         private final PolynomialNutation chiA;

  2865.         /** Time scales to use. */
  2866.         private final TimeScales timeScales;

  2867.         /** Simple constructor.
  2868.          * @param psiA polynomial nutation for psiA
  2869.          * @param omegaA polynomial nutation for omegaA
  2870.          * @param chiA polynomial nutation for chiA
  2871.          * @param timeScales used in the computation.
  2872.          */
  2873.         PrecessionFunction(final PolynomialNutation psiA,
  2874.                            final PolynomialNutation omegaA,
  2875.                            final PolynomialNutation chiA,
  2876.                            final TimeScales timeScales) {
  2877.             this.psiA   = psiA;
  2878.             this.omegaA = omegaA;
  2879.             this.chiA   = chiA;
  2880.             this.timeScales = timeScales;
  2881.         }


  2882.         /** {@inheritDoc} */
  2883.         @Override
  2884.         public double[] value(final AbsoluteDate date) {
  2885.             final double tc = evaluateTC(date, timeScales);
  2886.             return new double[] {
  2887.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2888.             };
  2889.         }

  2890.         /** {@inheritDoc} */
  2891.         @Override
  2892.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2893.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2894.             final T tc = evaluateTC(date, timeScales);
  2895.             a[0] = psiA.value(tc);
  2896.             a[1] = omegaA.value(tc);
  2897.             a[2] = chiA.value(tc);
  2898.             return a;
  2899.         }

  2900.     }

  2901.     /** Local class for tides frequency function. */
  2902.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2903.         /** Nutation arguments. */
  2904.         private final FundamentalNutationArguments arguments;

  2905.         /** Correction series. */
  2906.         private final PoissonSeries.CompiledSeries kSeries;

  2907.         /** Simple constructor.
  2908.          * @param arguments nutation arguments
  2909.          * @param c20Series correction series for the C20 term
  2910.          * @param c21Series correction series for the C21 term
  2911.          * @param s21Series correction series for the S21 term
  2912.          * @param c22Series correction series for the C22 term
  2913.          * @param s22Series correction series for the S22 term
  2914.          */
  2915.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2916.                                         final PoissonSeries c20Series,
  2917.                                         final PoissonSeries c21Series,
  2918.                                         final PoissonSeries s21Series,
  2919.                                         final PoissonSeries c22Series,
  2920.                                         final PoissonSeries s22Series) {
  2921.             this.arguments = arguments;
  2922.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2923.         }


  2924.         /** {@inheritDoc} */
  2925.         @Override
  2926.         public double[] value(final AbsoluteDate date) {
  2927.             return kSeries.value(arguments.evaluateAll(date));
  2928.         }

  2929.         /** {@inheritDoc} */
  2930.         @Override
  2931.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2932.             return kSeries.value(arguments.evaluateAll(date));
  2933.         }

  2934.     }

  2935.     /** Local class for EOP tidal corrections. */
  2936.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2937.         /** Nutation arguments. */
  2938.         private final FundamentalNutationArguments arguments;

  2939.         /** Correction series. */
  2940.         private final PoissonSeries.CompiledSeries correctionSeries;

  2941.         /** Simple constructor.
  2942.          * @param arguments nutation arguments
  2943.          * @param xSeries correction series for the x coordinate
  2944.          * @param ySeries correction series for the y coordinate
  2945.          * @param ut1Series correction series for the UT1
  2946.          */
  2947.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2948.                            final PoissonSeries xSeries,
  2949.                            final PoissonSeries ySeries,
  2950.                            final PoissonSeries ut1Series) {
  2951.             this.arguments        = arguments;
  2952.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2953.         }

  2954.         /** {@inheritDoc} */
  2955.         @Override
  2956.         public double[] value(final AbsoluteDate date) {
  2957.             final BodiesElements elements = arguments.evaluateAll(date);
  2958.             final double[] correction    = correctionSeries.value(elements);
  2959.             final double[] correctionDot = correctionSeries.derivative(elements);
  2960.             return new double[] {
  2961.                 correction[0],
  2962.                 correction[1],
  2963.                 correction[2],
  2964.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2965.             };
  2966.         }

  2967.         /** {@inheritDoc} */
  2968.         @Override
  2969.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2970.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2971.             final T[] correction    = correctionSeries.value(elements);
  2972.             final T[] correctionDot = correctionSeries.derivative(elements);
  2973.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2974.             a[0] = correction[0];
  2975.             a[1] = correction[1];
  2976.             a[2] = correction[2];
  2977.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2978.             return a;
  2979.         }

  2980.     }

  2981. }