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.MathArrays;
  31. import org.hipparchus.util.MathUtils;
  32. import org.orekit.annotation.DefaultDataContext;
  33. import org.orekit.data.BodiesElements;
  34. import org.orekit.data.DataContext;
  35. import org.orekit.data.DelaunayArguments;
  36. import org.orekit.data.FieldBodiesElements;
  37. import org.orekit.data.FieldDelaunayArguments;
  38. import org.orekit.data.FundamentalNutationArguments;
  39. import org.orekit.data.PoissonSeries;
  40. import org.orekit.data.PoissonSeries.CompiledSeries;
  41. import org.orekit.data.PoissonSeriesParser;
  42. import org.orekit.data.PolynomialNutation;
  43. import org.orekit.data.PolynomialParser;
  44. import org.orekit.data.PolynomialParser.Unit;
  45. import org.orekit.data.SimpleTimeStampedTableParser;
  46. import org.orekit.errors.OrekitException;
  47. import org.orekit.errors.OrekitInternalError;
  48. import org.orekit.errors.OrekitMessages;
  49. import org.orekit.errors.TimeStampedCacheException;
  50. import org.orekit.frames.EOPHistory;
  51. import org.orekit.frames.FieldPoleCorrection;
  52. import org.orekit.frames.PoleCorrection;
  53. import org.orekit.time.AbsoluteDate;
  54. import org.orekit.time.DateComponents;
  55. import org.orekit.time.FieldAbsoluteDate;
  56. import org.orekit.time.TimeComponents;
  57. import org.orekit.time.TimeScalarFunction;
  58. import org.orekit.time.TimeScale;
  59. import org.orekit.time.TimeScales;
  60. import org.orekit.time.TimeStamped;
  61. import org.orekit.time.TimeVectorFunction;


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

  67.     /** Constant for IERS 1996 conventions. */
  68.     IERS_1996 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  111.             return new TimeScalarFunction() {

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

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

  122.             };

  123.         }

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

  127.             // set up nutation arguments
  128.             final FundamentalNutationArguments arguments =
  129.                     getNutationArguments(timeScales);

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

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

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

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

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

  166.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  167.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

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

  178.             final PoissonSeries.CompiledSeries xySum =
  179.                     PoissonSeries.compile(xSum, ySum);

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

  188.             return new TimeVectorFunction() {

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

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

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

  198.                     final double cosOmega  = FastMath.cos(omega);
  199.                     final double sinOmega  = FastMath.sin(omega);
  200.                     final double sin2Omega = FastMath.sin(2 * omega);
  201.                     final double cos2FDOm  = FastMath.cos(2 * (f - d + omega));
  202.                     final double sin2FDOm  = FastMath.sin(2 * (f - d + omega));

  203.                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
  204.                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
  205.                     final double y = yPolynomial.value(t) + xy[1] +
  206.                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
  207.                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
  208.                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));

  209.                     return new double[] {
  210.                         x, y, sPxy2
  211.                     };

  212.                 }

  213.                 /** {@inheritDoc} */
  214.                 @Override
  215.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

  218.                     final T omega     = elements.getOmega();
  219.                     final T f         = elements.getF();
  220.                     final T d         = elements.getD();
  221.                     final T t         = elements.getTC();
  222.                     final T t2        = t.multiply(t);

  223.                     final T cosOmega  = omega.cos();
  224.                     final T sinOmega  = omega.sin();
  225.                     final T sin2Omega = omega.multiply(2).sin();
  226.                     final T fMDPO2 = f.subtract(d).add(omega).multiply(2);
  227.                     final T cos2FDOm  = fMDPO2.cos();
  228.                     final T sin2FDOm  = fMDPO2.sin();

  229.                     final T x = xPolynomial.value(t).
  230.                                 add(xy[0].multiply(sinEps0)).
  231.                                 add(t2.multiply(cosOmega.multiply(fXCosOm).add(sinOmega.multiply(fXSinOm)).add(cos2FDOm.multiply(fXSin2FDOm))));
  232.                     final T y = yPolynomial.value(t).
  233.                                 add(xy[1]).
  234.                                 add(t2.multiply(cosOmega.multiply(fYCosOm).add(cos2FDOm.multiply(fYCos2FDOm))));
  235.                     final T sPxy2 = sinOmega.multiply(fSSinOm).
  236.                                     add(sin2Omega.multiply(fSSin2Om)).
  237.                                     add(t.multiply(fST3).add(sinOmega.multiply(fST2SinOm)).add(sin2FDOm.multiply(fST2Sin2FDOm)).multiply(t).add(fST).multiply(t));

  238.                     final T[] a = MathArrays.buildArray(date.getField(), 3);
  239.                     a[0] = x;
  240.                     a[1] = y;
  241.                     a[2] = sPxy2;
  242.                     return a;

  243.                 }

  244.             };

  245.         }

  246.         /** {@inheritDoc} */
  247.         @Override
  248.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

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

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

  271.         }

  272.         /** {@inheritDoc} */
  273.         @Override
  274.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  275.             // set up nutation arguments
  276.             final FundamentalNutationArguments arguments =
  277.                     getNutationArguments(timeScales);

  278.             // set up Poisson series
  279.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  280.             final PoissonSeriesParser baseParser =
  281.                     new PoissonSeriesParser(10).withFirstDelaunay(1);

  282.             final PoissonSeriesParser psiParser =
  283.                     baseParser.
  284.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  285.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  286.             final PoissonSeries psiSeries;
  287.             try (InputStream in = getStream(PSI_EPSILON_SERIES)) {
  288.                 psiSeries = psiParser.parse(in, PSI_EPSILON_SERIES);
  289.             } catch (IOException e) {
  290.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  291.             }

  292.             final PoissonSeriesParser epsilonParser =
  293.                     baseParser.
  294.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  295.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  296.             final PoissonSeries epsilonSeries;
  297.             try (InputStream in = getStream(PSI_EPSILON_SERIES)) {
  298.                 epsilonSeries = epsilonParser.parse(in, PSI_EPSILON_SERIES);
  299.             } catch (IOException e) {
  300.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  301.             }

  302.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  303.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  304.             return new TimeVectorFunction() {

  305.                 /** {@inheritDoc} */
  306.                 @Override
  307.                 public double[] value(final AbsoluteDate date) {
  308.                     final BodiesElements elements = arguments.evaluateAll(date);
  309.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  310.                     return new double[] {
  311.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  312.                     };
  313.                 }

  314.                 /** {@inheritDoc} */
  315.                 @Override
  316.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  317.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  318.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  319.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  320.                     result[0] = psiEpsilon[0];
  321.                     result[1] = psiEpsilon[1];
  322.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  323.                     return result;
  324.                 }

  325.             };

  326.         }

  327.         /** {@inheritDoc} */
  328.         @Override
  329.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  330.                                                   final TimeScales timeScales) {

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

  333.             // constants from IERS 1996 page 21
  334.             // the underlying model is IAU 1982 GMST-UT1
  335.             final AbsoluteDate gmstReference = new AbsoluteDate(
  336.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  337.             final double gmst0 = 24110.54841;
  338.             final double gmst1 = 8640184.812866;
  339.             final double gmst2 = 0.093104;
  340.             final double gmst3 = -6.2e-6;

  341.             return new TimeScalarFunction() {

  342.                 /** {@inheritDoc} */
  343.                 @Override
  344.                 public double value(final AbsoluteDate date) {

  345.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  346.                     final double dtai = date.durationFrom(gmstReference);
  347.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  348.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

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

  354.                 }

  355.                 /** {@inheritDoc} */
  356.                 @Override
  357.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  358.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  359.                     final T dtai = date.durationFrom(gmstReference);
  360.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  361.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

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

  367.                 }

  368.             };

  369.         }

  370.         /** {@inheritDoc} */
  371.         @Override
  372.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  373.                                                       final TimeScales timeScales) {

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

  376.             // constants from IERS 1996 page 21
  377.             // the underlying model is IAU 1982 GMST-UT1
  378.             final AbsoluteDate gmstReference = new AbsoluteDate(
  379.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  380.             final double gmst1 = 8640184.812866;
  381.             final double gmst2 = 0.093104;
  382.             final double gmst3 = -6.2e-6;

  383.             return new TimeScalarFunction() {

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

  387.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  388.                     final double dtai = date.durationFrom(gmstReference);
  389.                     final double tut1 = dtai + ut1.offsetFromTAI(date);
  390.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

  393.                 }

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

  397.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  398.                     final T dtai = date.durationFrom(gmstReference);
  399.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
  400.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

  403.                 }

  404.             };

  405.         }

  406.         /** {@inheritDoc} */
  407.         @Override
  408.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  409.                                                   final EOPHistory eopHistory,
  410.                                                   final TimeScales timeScales) {

  411.             // obliquity
  412.             final TimeScalarFunction epsilonA = getMeanObliquityFunction(timeScales);

  413.             // GMST function
  414.             final TimeScalarFunction gmst = getGMSTFunction(ut1, timeScales);

  415.             // nutation function
  416.             final TimeVectorFunction nutation = getNutationFunction(timeScales);

  417.             return new TimeScalarFunction() {

  418.                 /** {@inheritDoc} */
  419.                 @Override
  420.                 public double value(final AbsoluteDate date) {

  421.                     // compute equation of equinoxes
  422.                     final double[] angles = nutation.value(date);
  423.                     double deltaPsi = angles[0];
  424.                     if (eopHistory != null) {
  425.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  426.                     }
  427.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

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

  430.                 }

  431.                 /** {@inheritDoc} */
  432.                 @Override
  433.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  434.                     // compute equation of equinoxes
  435.                     final T[] angles = nutation.value(date);
  436.                     T deltaPsi = angles[0];
  437.                     if (eopHistory != null) {
  438.                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
  439.                     }
  440.                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);

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

  443.                 }

  444.             };

  445.         }

  446.         /** {@inheritDoc} */
  447.         @Override
  448.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

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

  478.             final double deciMilliS = 1.0e-4;
  479.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
  480.                     withOptionalColumn(1).
  481.                     withGamma(7).
  482.                     withFirstDelaunay(2).
  483.                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
  484.             final PoissonSeries ut1Series;
  485.             try (InputStream in = getStream(TIDAL_CORRECTION_UT1_SERIES)) {
  486.                 ut1Series = ut1Parser.parse(in, TIDAL_CORRECTION_UT1_SERIES);
  487.             } catch (IOException e) {
  488.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  489.             }

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

  491.         }

  492.         /** {@inheritDoc} */
  493.         public LoveNumbers getLoveNumbers() {
  494.             return loadLoveNumbers(LOVE_NUMBERS);
  495.         }

  496.         /** {@inheritDoc} */
  497.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  498.                                                                      final TimeScales timeScales) {

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

  501.             // set up Poisson series
  502.             final PoissonSeriesParser k20Parser =
  503.                     new PoissonSeriesParser(18).
  504.                         withOptionalColumn(1).
  505.                         withDoodson(4, 2).
  506.                         withFirstDelaunay(10);
  507.             final PoissonSeriesParser k21Parser =
  508.                     new PoissonSeriesParser(18).
  509.                         withOptionalColumn(1).
  510.                         withDoodson(4, 3).
  511.                         withFirstDelaunay(10);
  512.             final PoissonSeriesParser k22Parser =
  513.                     new PoissonSeriesParser(16).
  514.                         withOptionalColumn(1).
  515.                         withDoodson(4, 2).
  516.                         withFirstDelaunay(10);

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

  543.                 return new TideFrequencyDependenceFunction(arguments,
  544.                                                            c20Series,
  545.                                                            c21Series, s21Series,
  546.                                                            c22Series, s22Series);
  547.             } catch (IOException e) {
  548.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  549.             }

  550.         }

  551.         /** {@inheritDoc} */
  552.         @Override
  553.         public double getPermanentTide() {
  554.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  555.         }

  556.         /** {@inheritDoc} */
  557.         @Override
  558.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  559.             // constants from IERS 1996 page 47
  560.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  561.             final double coupling     =  0.00112;

  562.             return new TimeVectorFunction() {

  563.                 /** {@inheritDoc} */
  564.                 @Override
  565.                 public double[] value(final AbsoluteDate date) {
  566.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  567.                     return new double[] {
  568.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  569.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  570.                     };
  571.                 }

  572.                 /** {@inheritDoc} */
  573.                 @Override
  574.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  575.                     final FieldPoleCorrection<T> pole = eopHistory.getPoleCorrection(date);
  576.                     final T[] a = MathArrays.buildArray(date.getField(), 2);
  577.                     a[0] = pole.getXp().add(pole.getYp().multiply(coupling)).multiply(globalFactor);
  578.                     a[1] = pole.getXp().multiply(coupling).subtract(pole.getYp()).multiply(globalFactor);
  579.                     return a;
  580.                 }

  581.             };
  582.         }

  583.         /** {@inheritDoc} */
  584.         @Override
  585.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  586.             return new TimeVectorFunction() {

  587.                 /** {@inheritDoc} */
  588.                 @Override
  589.                 public double[] value(final AbsoluteDate date) {
  590.                     // there are no model for ocean pole tide prior to conventions 2010
  591.                     return new double[] {
  592.                         0.0, 0.0
  593.                     };
  594.                 }

  595.                 /** {@inheritDoc} */
  596.                 @Override
  597.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  598.                     // there are no model for ocean pole tide prior to conventions 2010
  599.                     return MathArrays.buildArray(date.getField(), 2);
  600.                 }

  601.             };
  602.         }

  603.         /** {@inheritDoc} */
  604.         @Override
  605.         public double[] getNominalTidalDisplacement() {

  606.             //  // elastic Earth values
  607.             //  return new double[] {
  608.             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  609.             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
  610.             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  611.             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  612.             //      // H₀
  613.             //      -0.31460
  614.             //  };

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

  624.         }

  625.         /** {@inheritDoc} */
  626.         @Override
  627.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  628.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  629.                                                                   18, 17, -1, 18, -1);
  630.         }

  631.         /** {@inheritDoc} */
  632.         @Override
  633.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  634.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  635.                                                                 20, 17, 19, 18, 20);
  636.         }

  637.     },

  638.     /** Constant for IERS 2003 conventions. */
  639.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  672.         /** {@inheritDoc} */
  673.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  674.                                                                  final TimeScales timeScales) {
  675.             try (InputStream in = getStream(NUTATION_ARGUMENTS)) {
  676.                 return new FundamentalNutationArguments(this, timeScale,
  677.                         in, NUTATION_ARGUMENTS, timeScales);
  678.             } catch (IOException e) {
  679.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  680.             }
  681.         }

  682.         /** {@inheritDoc} */
  683.         @Override
  684.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  685.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  686.             final PolynomialNutation epsilonA =
  687.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  688.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  689.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  690.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  691.             return new TimeScalarFunction() {

  692.                 /** {@inheritDoc} */
  693.                 @Override
  694.                 public double value(final AbsoluteDate date) {
  695.                     return epsilonA.value(evaluateTC(date, timeScales));
  696.                 }

  697.                 /** {@inheritDoc} */
  698.                 @Override
  699.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  700.                     return epsilonA.value(evaluateTC(date, timeScales));
  701.                 }

  702.             };

  703.         }

  704.         /** {@inheritDoc} */
  705.         @Override
  706.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  707.             // set up nutation arguments
  708.             final FundamentalNutationArguments arguments =
  709.                     getNutationArguments(timeScales);

  710.             // set up Poisson series
  711.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  712.             final PoissonSeriesParser parser =
  713.                     new PoissonSeriesParser(17).
  714.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  715.                         withFirstDelaunay(4).
  716.                         withFirstPlanetary(9).
  717.                         withSinCos(0, 2, microAS, 3, microAS);

  718.             final PoissonSeries.CompiledSeries xys;
  719.             try (InputStream xIn = getStream(X_SERIES);
  720.                  InputStream yIn = getStream(Y_SERIES);
  721.                  InputStream sIn = getStream(S_SERIES)) {
  722.                 final PoissonSeries xSeries = parser.parse(xIn, X_SERIES);
  723.                 final PoissonSeries ySeries = parser.parse(yIn, Y_SERIES);
  724.                 final PoissonSeries sSeries = parser.parse(sIn, S_SERIES);
  725.                 xys = PoissonSeries.compile(xSeries, ySeries, sSeries);
  726.             } catch (IOException e) {
  727.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  728.             }

  729.             // create a function evaluating the series
  730.             return new TimeVectorFunction() {

  731.                 /** {@inheritDoc} */
  732.                 @Override
  733.                 public double[] value(final AbsoluteDate date) {
  734.                     return xys.value(arguments.evaluateAll(date));
  735.                 }

  736.                 /** {@inheritDoc} */
  737.                 @Override
  738.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  739.                     return xys.value(arguments.evaluateAll(date));
  740.                 }

  741.             };

  742.         }


  743.         /** {@inheritDoc} */
  744.         @Override
  745.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

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

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

  765.         }

  766.         /** {@inheritDoc} */
  767.         @Override
  768.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  769.             // set up nutation arguments
  770.             final FundamentalNutationArguments arguments =
  771.                     getNutationArguments(timeScales);

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

  796.             final PoissonSeriesParser planetaryParser =
  797.                     new PoissonSeriesParser(21).
  798.                         withFirstDelaunay(2).
  799.                         withFirstPlanetary(7);
  800.             final PoissonSeriesParser planetaryPsiParser =
  801.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  802.             final PoissonSeries psiPlanetarySeries;
  803.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  804.                 psiPlanetarySeries = planetaryPsiParser.parse(in, PLANETARY_SERIES);
  805.             } catch (IOException e) {
  806.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  807.             }
  808.             final PoissonSeriesParser planetaryEpsilonParser =
  809.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  810.             final PoissonSeries epsilonPlanetarySeries;
  811.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  812.                 epsilonPlanetarySeries = planetaryEpsilonParser.parse(in, PLANETARY_SERIES);
  813.             } catch (IOException e) {
  814.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  815.             }

  816.             final PoissonSeries.CompiledSeries luniSolarSeries =
  817.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  818.             final PoissonSeries.CompiledSeries planetarySeries =
  819.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  820.             return new TimeVectorFunction() {

  821.                 /** {@inheritDoc} */
  822.                 @Override
  823.                 public double[] value(final AbsoluteDate date) {
  824.                     final BodiesElements elements = arguments.evaluateAll(date);
  825.                     final double[] luniSolar = luniSolarSeries.value(elements);
  826.                     final double[] planetary = planetarySeries.value(elements);
  827.                     return new double[] {
  828.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  829.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  830.                     };
  831.                 }

  832.                 /** {@inheritDoc} */
  833.                 @Override
  834.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  835.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  836.                     final T[] luniSolar = luniSolarSeries.value(elements);
  837.                     final T[] planetary = planetarySeries.value(elements);
  838.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  839.                     result[0] = luniSolar[0].add(planetary[0]);
  840.                     result[1] = luniSolar[1].add(planetary[1]);
  841.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  842.                     return result;
  843.                 }

  844.             };

  845.         }

  846.         /** {@inheritDoc} */
  847.         @Override
  848.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  849.                                                   final TimeScales timeScales) {

  850.             // Earth Rotation Angle
  851.             final StellarAngleCapitaine era =
  852.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

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

  868.             // create a function evaluating the series
  869.             return new TimeScalarFunction() {

  870.                 /** {@inheritDoc} */
  871.                 @Override
  872.                 public double value(final AbsoluteDate date) {
  873.                     return era.value(date) + minusEO.value(evaluateTC(date, timeScales));
  874.                 }

  875.                 /** {@inheritDoc} */
  876.                 @Override
  877.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  878.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  879.                 }

  880.             };

  881.         }

  882.         /** {@inheritDoc} */
  883.         @Override
  884.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  885.                                                       final TimeScales timeScales) {

  886.             // Earth Rotation Angle
  887.             final StellarAngleCapitaine era =
  888.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

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

  904.             // create a function evaluating the series
  905.             return new TimeScalarFunction() {

  906.                 /** {@inheritDoc} */
  907.                 @Override
  908.                 public double value(final AbsoluteDate date) {
  909.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  910.                 }

  911.                 /** {@inheritDoc} */
  912.                 @Override
  913.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  914.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  915.                 }

  916.             };

  917.         }

  918.         /** {@inheritDoc} */
  919.         @Override
  920.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  921.                                                   final EOPHistory eopHistory,
  922.                                                   final TimeScales timeScales) {

  923.             // set up nutation arguments
  924.             final FundamentalNutationArguments arguments =
  925.                     getNutationArguments(timeScales);

  926.             // mean obliquity function
  927.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

  928.             // set up Poisson series
  929.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  930.             final PoissonSeriesParser luniSolarPsiParser =
  931.                     new PoissonSeriesParser(14).
  932.                         withFirstDelaunay(1).
  933.                         withSinCos(0, 7, milliAS, 11, milliAS).
  934.                         withSinCos(1, 8, milliAS, 12, milliAS);
  935.             final PoissonSeries psiLuniSolarSeries;
  936.             try (InputStream in = getStream(LUNI_SOLAR_SERIES)) {
  937.                 psiLuniSolarSeries = luniSolarPsiParser.parse(in, LUNI_SOLAR_SERIES);
  938.             } catch (IOException e) {
  939.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  940.             }

  941.             final PoissonSeriesParser planetaryPsiParser =
  942.                     new PoissonSeriesParser(21).
  943.                         withFirstDelaunay(2).
  944.                         withFirstPlanetary(7).
  945.                         withSinCos(0, 17, milliAS, 18, milliAS);
  946.             final PoissonSeries psiPlanetarySeries;
  947.             try (InputStream in = getStream(PLANETARY_SERIES)) {
  948.                 psiPlanetarySeries = planetaryPsiParser.parse(in, PLANETARY_SERIES);
  949.             } catch (IOException e) {
  950.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  951.             }


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

  967.             // ERA function
  968.             final TimeScalarFunction era =
  969.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  970.             return new TimeScalarFunction() {

  971.                 /** {@inheritDoc} */
  972.                 @Override
  973.                 public double value(final AbsoluteDate date) {

  974.                     // evaluate equation of origins
  975.                     final BodiesElements elements = arguments.evaluateAll(date);
  976.                     final double[] angles = psiGstSeries.value(elements);
  977.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  978.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  979.                     final double epsilonA = epsilon.value(date);

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

  983.                 }

  984.                 /** {@inheritDoc} */
  985.                 @Override
  986.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  987.                     // evaluate equation of origins
  988.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  989.                     final T[] angles = psiGstSeries.value(elements);
  990.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  991.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  992.                     final T epsilonA = epsilon.value(date);

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

  996.                 }

  997.             };

  998.         }

  999.         /** {@inheritDoc} */
  1000.         @Override
  1001.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

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

  1026.                 final double microS = 1.0e-6;
  1027.                 final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1028.                         withOptionalColumn(1).
  1029.                         withGamma(2).
  1030.                         withFirstDelaunay(3).
  1031.                         withSinCos(0, 10, microS, 11, microS);
  1032.                 final PoissonSeries ut1Series =
  1033.                         ut1Parser.parse(tidalUt1, TIDAL_CORRECTION_UT1_SERIES);

  1034.                 return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
  1035.             } catch (IOException e) {
  1036.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1037.             }

  1038.         }

  1039.         /** {@inheritDoc} */
  1040.         public LoveNumbers getLoveNumbers() {
  1041.             return loadLoveNumbers(LOVE_NUMBERS);
  1042.         }

  1043.         /** {@inheritDoc} */
  1044.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  1045.                                                                      final TimeScales timeScales) {

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

  1048.             // set up Poisson series
  1049.             final PoissonSeriesParser k20Parser =
  1050.                     new PoissonSeriesParser(18).
  1051.                         withOptionalColumn(1).
  1052.                         withDoodson(4, 2).
  1053.                         withFirstDelaunay(10);
  1054.             final PoissonSeriesParser k21Parser =
  1055.                     new PoissonSeriesParser(18).
  1056.                         withOptionalColumn(1).
  1057.                         withDoodson(4, 3).
  1058.                         withFirstDelaunay(10);
  1059.             final PoissonSeriesParser k22Parser =
  1060.                     new PoissonSeriesParser(16).
  1061.                         withOptionalColumn(1).
  1062.                         withDoodson(4, 2).
  1063.                         withFirstDelaunay(10);

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

  1090.                 return new TideFrequencyDependenceFunction(arguments,
  1091.                                                            c20Series,
  1092.                                                            c21Series, s21Series,
  1093.                                                            c22Series, s22Series);
  1094.             } catch (IOException e) {
  1095.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1096.             }

  1097.         }

  1098.         /** {@inheritDoc} */
  1099.         @Override
  1100.         public double getPermanentTide() {
  1101.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1102.         }

  1103.         /** {@inheritDoc} */
  1104.         @Override
  1105.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

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

  1130.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  1131.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  1132.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  1133.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  1134.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  1135.             // constants from IERS 2003, section 6.2
  1136.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1137.             final double ratio        =  0.00115;

  1138.             return new TimeVectorFunction() {

  1139.                 /** {@inheritDoc} */
  1140.                 @Override
  1141.                 public double[] value(final AbsoluteDate date) {

  1142.                     // we can't compute anything before the range covered by the annual pole file
  1143.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  1144.                         return new double[] {
  1145.                             0.0, 0.0
  1146.                         };
  1147.                     }

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

  1169.                         // we are after the range covered by the annual pole file,
  1170.                         // we use the polynomial extension
  1171.                         final double t = date.durationFrom(
  1172.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1173.                         meanPoleX = xp0 + t * xp0Dot;
  1174.                         meanPoleY = yp0 + t * yp0Dot;

  1175.                     }

  1176.                     // evaluate wobble variables
  1177.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1178.                     final double m1 = correction.getXp() - meanPoleX;
  1179.                     final double m2 = meanPoleY - correction.getYp();

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

  1210.                 }

  1211.                 /** {@inheritDoc} */
  1212.                 @Override
  1213.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1245.                         // we are after the range covered by the annual pole file,
  1246.                         // we use the polynomial extension
  1247.                         final T t = date.durationFrom(
  1248.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1249.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1250.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1251.                     }

  1252.                     // evaluate wobble variables
  1253.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1254.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1255.                     final T m2 = meanPoleY.subtract(correction.getYp());

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

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

  1285.                     return a;

  1286.                 }

  1287.             };

  1288.         }

  1289.         /** {@inheritDoc} */
  1290.         @Override
  1291.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1292.             return new TimeVectorFunction() {

  1293.                 /** {@inheritDoc} */
  1294.                 @Override
  1295.                 public double[] value(final AbsoluteDate date) {
  1296.                     // there are no model for ocean pole tide prior to conventions 2010
  1297.                     return new double[] {
  1298.                         0.0, 0.0
  1299.                     };
  1300.                 }

  1301.                 /** {@inheritDoc} */
  1302.                 @Override
  1303.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1304.                     // there are no model for ocean pole tide prior to conventions 2010
  1305.                     return MathArrays.buildArray(date.getField(), 2);
  1306.                 }

  1307.             };
  1308.         }

  1309.         /** {@inheritDoc} */
  1310.         @Override
  1311.         public double[] getNominalTidalDisplacement() {
  1312.             return new double[] {
  1313.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1314.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1315.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1316.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1317.                 // H₀
  1318.                 -0.31460
  1319.             };
  1320.         }

  1321.         /** {@inheritDoc} */
  1322.         @Override
  1323.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1324.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1325.                                                                   18, 15, 16, 17, 18);
  1326.         }

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

  1333.     },

  1334.     /** Constant for IERS 2010 conventions. */
  1335.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1366.         /** {@inheritDoc} */
  1367.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  1368.                                                                  final TimeScales timeScales) {
  1369.             try (InputStream in = getStream(NUTATION_ARGUMENTS)) {
  1370.                 return new FundamentalNutationArguments(this, timeScale,
  1371.                         in, NUTATION_ARGUMENTS, timeScales);
  1372.             } catch (IOException e) {
  1373.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1374.             }
  1375.         }

  1376.         /** {@inheritDoc} */
  1377.         @Override
  1378.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  1379.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1380.             final PolynomialNutation epsilonA =
  1381.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1382.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1383.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1384.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1385.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1386.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1387.             return new TimeScalarFunction() {

  1388.                 /** {@inheritDoc} */
  1389.                 @Override
  1390.                 public double value(final AbsoluteDate date) {
  1391.                     return epsilonA.value(evaluateTC(date, timeScales));
  1392.                 }

  1393.                 /** {@inheritDoc} */
  1394.                 @Override
  1395.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1396.                     return epsilonA.value(evaluateTC(date, timeScales));
  1397.                 }

  1398.             };

  1399.         }

  1400.         /** {@inheritDoc} */
  1401.         @Override
  1402.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  1403.             // set up nutation arguments
  1404.             final FundamentalNutationArguments arguments =
  1405.                     getNutationArguments(timeScales);

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

  1425.             // create a function evaluating the series
  1426.             return new TimeVectorFunction() {

  1427.                 /** {@inheritDoc} */
  1428.                 @Override
  1429.                 public double[] value(final AbsoluteDate date) {
  1430.                     return xys.value(arguments.evaluateAll(date));
  1431.                 }

  1432.                 /** {@inheritDoc} */
  1433.                 @Override
  1434.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1435.                     return xys.value(arguments.evaluateAll(date));
  1436.                 }

  1437.             };

  1438.         }

  1439.         /** {@inheritDoc} */
  1440.         public LoveNumbers getLoveNumbers() {
  1441.             return loadLoveNumbers(LOVE_NUMBERS);
  1442.         }

  1443.         /** {@inheritDoc} */
  1444.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  1445.                                                                      final TimeScales timeScales) {

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

  1448.             // set up Poisson series
  1449.             final PoissonSeriesParser k20Parser =
  1450.                     new PoissonSeriesParser(18).
  1451.                         withOptionalColumn(1).
  1452.                         withDoodson(4, 2).
  1453.                         withFirstDelaunay(10);
  1454.             final PoissonSeriesParser k21Parser =
  1455.                     new PoissonSeriesParser(18).
  1456.                         withOptionalColumn(1).
  1457.                         withDoodson(4, 3).
  1458.                         withFirstDelaunay(10);
  1459.             final PoissonSeriesParser k22Parser =
  1460.                     new PoissonSeriesParser(16).
  1461.                         withOptionalColumn(1).
  1462.                         withDoodson(4, 2).
  1463.                         withFirstDelaunay(10);

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

  1490.                 return new TideFrequencyDependenceFunction(arguments,
  1491.                                                            c20Series,
  1492.                                                            c21Series, s21Series,
  1493.                                                            c22Series, s22Series);
  1494.             } catch (IOException e) {
  1495.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1496.             }

  1497.         }

  1498.         /** {@inheritDoc} */
  1499.         @Override
  1500.         public double getPermanentTide() {
  1501.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1502.         }

  1503.         /** Compute pole wobble variables m₁ and m₂.
  1504.          * @param date current date
  1505.          * @param eopHistory EOP history
  1506.          * @return array containing m₁ and m₂
  1507.          */
  1508.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1509.             // polynomial model from IERS 2010, table 7.7
  1510.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1511.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1512.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1513.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1514.             final AbsoluteDate changeDate =
  1515.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

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

  1544.             // evaluate wobble variables
  1545.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1546.             final double m1 = correction.getXp() - meanPoleX;
  1547.             final double m2 = meanPoleY - correction.getYp();

  1548.             return new double[] {
  1549.                 m1, m2
  1550.             };

  1551.         }

  1552.         /** Compute pole wobble variables m₁ and m₂.
  1553.          * @param date current date
  1554.          * @param <T> type of the field elements
  1555.          * @param eopHistory EOP history
  1556.          * @return array containing m₁ and m₂
  1557.          */
  1558.         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1559.             // polynomial model from IERS 2010, table 7.7
  1560.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1561.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1562.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1563.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1564.             final AbsoluteDate changeDate =
  1565.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

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

  1594.             // evaluate wobble variables
  1595.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1596.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1597.             m[0] = correction.getXp().subtract(meanPoleX);
  1598.             m[1] = meanPoleY.subtract(correction.getYp());

  1599.             return m;

  1600.         }

  1601.         /** {@inheritDoc} */
  1602.         @Override
  1603.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1604.             // constants from IERS 2010, section 6.4
  1605.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1606.             final double ratio        =  0.00115;

  1607.             return new TimeVectorFunction() {

  1608.                 /** {@inheritDoc} */
  1609.                 @Override
  1610.                 public double[] value(final AbsoluteDate date) {

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

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

  1625.                 }

  1626.                 /** {@inheritDoc} */
  1627.                 @Override
  1628.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1642.                     return a;

  1643.                 }

  1644.             };

  1645.         }

  1646.         /** {@inheritDoc} */
  1647.         @Override
  1648.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1649.             return new TimeVectorFunction() {

  1650.                 /** {@inheritDoc} */
  1651.                 @Override
  1652.                 public double[] value(final AbsoluteDate date) {

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

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

  1663.                 }

  1664.                 /** {@inheritDoc} */
  1665.                 @Override
  1666.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1676.                     return v;

  1677.                 }

  1678.             };

  1679.         }

  1680.         /** {@inheritDoc} */
  1681.         @Override
  1682.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

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

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

  1708.         }

  1709.         /** {@inheritDoc} */
  1710.         @Override
  1711.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  1712.             // set up nutation arguments
  1713.             final FundamentalNutationArguments arguments =
  1714.                     getNutationArguments(timeScales);

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

  1731.             return new TimeVectorFunction() {

  1732.                 /** {@inheritDoc} */
  1733.                 @Override
  1734.                 public double[] value(final AbsoluteDate date) {
  1735.                     final BodiesElements elements = arguments.evaluateAll(date);
  1736.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1737.                     return new double[] {
  1738.                         psiEpsilon[0], psiEpsilon[1],
  1739.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  1740.                     };
  1741.                 }

  1742.                 /** {@inheritDoc} */
  1743.                 @Override
  1744.                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1745.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1746.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1747.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1748.                     result[0] = psiEpsilon[0];
  1749.                     result[1] = psiEpsilon[1];
  1750.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  1751.                     return result;
  1752.                 }

  1753.             };

  1754.         }

  1755.         /** {@inheritDoc} */
  1756.         @Override
  1757.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  1758.                                                   final TimeScales timeScales) {

  1759.             // Earth Rotation Angle
  1760.             final StellarAngleCapitaine era =
  1761.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

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

  1777.             // create a function evaluating the series
  1778.             return new TimeScalarFunction() {

  1779.                 /** {@inheritDoc} */
  1780.                 @Override
  1781.                 public double value(final AbsoluteDate date) {
  1782.                     return era.value(date) + minusEO.value(evaluateTC(date, timeScales));
  1783.                 }

  1784.                 /** {@inheritDoc} */
  1785.                 @Override
  1786.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1787.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  1788.                 }

  1789.             };

  1790.         }

  1791.         /** {@inheritDoc} */
  1792.         @Override
  1793.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  1794.                                                       final TimeScales timeScales) {

  1795.             // Earth Rotation Angle
  1796.             final StellarAngleCapitaine era =
  1797.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

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

  1813.             // create a function evaluating the series
  1814.             return new TimeScalarFunction() {

  1815.                 /** {@inheritDoc} */
  1816.                 @Override
  1817.                 public double value(final AbsoluteDate date) {
  1818.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  1819.                 }

  1820.                 /** {@inheritDoc} */
  1821.                 @Override
  1822.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1823.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  1824.                 }

  1825.             };

  1826.         }

  1827.         /** {@inheritDoc} */
  1828.         @Override
  1829.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  1830.                                                   final EOPHistory eopHistory,
  1831.                                                   final TimeScales timeScales) {

  1832.             // set up nutation arguments
  1833.             final FundamentalNutationArguments arguments =
  1834.                     getNutationArguments(timeScales);

  1835.             // mean obliquity function
  1836.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

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

  1854.             // ERA function
  1855.             final TimeScalarFunction era =
  1856.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  1857.             return new TimeScalarFunction() {

  1858.                 /** {@inheritDoc} */
  1859.                 @Override
  1860.                 public double value(final AbsoluteDate date) {

  1861.                     // evaluate equation of origins
  1862.                     final BodiesElements elements = arguments.evaluateAll(date);
  1863.                     final double[] angles = psiGstSeries.value(elements);
  1864.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1865.                     final double deltaPsi = angles[0] + ddPsi;
  1866.                     final double epsilonA = epsilon.value(date);

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

  1870.                 }

  1871.                 /** {@inheritDoc} */
  1872.                 @Override
  1873.                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1874.                     // evaluate equation of origins
  1875.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1876.                     final T[] angles = psiGstSeries.value(elements);
  1877.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1878.                     final T deltaPsi = angles[0].add(ddPsi);
  1879.                     final T epsilonA = epsilon.value(date);

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

  1883.                 }

  1884.             };

  1885.         }

  1886.         /** {@inheritDoc} */
  1887.         @Override
  1888.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

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

  1913.                 final double microS = 1.0e-6;
  1914.                 final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1915.                         withOptionalColumn(1).
  1916.                         withGamma(2).
  1917.                         withFirstDelaunay(3).
  1918.                         withSinCos(0, 10, microS, 11, microS);
  1919.                 final PoissonSeries ut1Series =
  1920.                         ut1Parser.parse(ut1In, TIDAL_CORRECTION_UT1_SERIES);

  1921.                 return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
  1922.             } catch (IOException e) {
  1923.                 throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  1924.             }

  1925.         }

  1926.         /** {@inheritDoc} */
  1927.         @Override
  1928.         public double[] getNominalTidalDisplacement() {
  1929.             return new double[] {
  1930.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1931.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1932.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1933.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1934.                 // H₀
  1935.                 -0.31460
  1936.             };
  1937.         }

  1938.         /** {@inheritDoc} */
  1939.         @Override
  1940.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1941.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1942.                                                                   18, 15, 16, 17, 18);
  1943.         }

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

  1950.     };

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

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

  1955.     /** Get the reference epoch for fundamental nutation arguments.
  1956.      *
  1957.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1958.      *
  1959.      * @return reference epoch for fundamental nutation arguments
  1960.      * @since 6.1
  1961.      * @see #getNutationReferenceEpoch(TimeScales)
  1962.      */
  1963.     @DefaultDataContext
  1964.     public AbsoluteDate getNutationReferenceEpoch() {
  1965.         return getNutationReferenceEpoch(DataContext.getDefault().getTimeScales());
  1966.     }

  1967.     /**
  1968.      * Get the reference epoch for fundamental nutation arguments.
  1969.      *
  1970.      * @param timeScales to use for the reference epoch.
  1971.      * @return reference epoch for fundamental nutation arguments
  1972.      * @since 10.1
  1973.      */
  1974.     public AbsoluteDate getNutationReferenceEpoch(final TimeScales timeScales) {
  1975.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1976.         return timeScales.getJ2000Epoch();
  1977.     }

  1978.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1979.      *
  1980.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1981.      *
  1982.      * @param date current date
  1983.      * @return date offset in Julian centuries
  1984.      * @since 6.1
  1985.      * @see #evaluateTC(AbsoluteDate, TimeScales)
  1986.      */
  1987.     @DefaultDataContext
  1988.     public double evaluateTC(final AbsoluteDate date) {
  1989.         return evaluateTC(date, DataContext.getDefault().getTimeScales());
  1990.     }

  1991.     /**
  1992.      * Evaluate the date offset between the current date and the {@link
  1993.      * #getNutationReferenceEpoch() reference date}.
  1994.      *
  1995.      * @param date       current date
  1996.      * @param timeScales used in the evaluation.
  1997.      * @return date offset in Julian centuries
  1998.      * @since 10.1
  1999.      */
  2000.     public double evaluateTC(final AbsoluteDate date, final TimeScales timeScales) {
  2001.         return date.durationFrom(getNutationReferenceEpoch(timeScales)) /
  2002.                 Constants.JULIAN_CENTURY;
  2003.     }

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

  2018.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  2019.      * @param <T> type of the field elements
  2020.      * @param date current date
  2021.      * @param timeScales used in the evaluation.
  2022.      * @return date offset in Julian centuries
  2023.      * @since 10.1
  2024.      */
  2025.     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date,
  2026.                                                         final TimeScales timeScales) {
  2027.         return date.durationFrom(getNutationReferenceEpoch(timeScales))
  2028.                 .divide(Constants.JULIAN_CENTURY);
  2029.     }

  2030.     /**
  2031.      * Get the fundamental nutation arguments. Does not compute GMST based values: gamma,
  2032.      * gammaDot.
  2033.      *
  2034.      * @param timeScales other time scales used in the computation including TAI and TT.
  2035.      * @return fundamental nutation arguments
  2036.      * @see #getNutationArguments(TimeScale, TimeScales)
  2037.      * @since 10.1
  2038.      */
  2039.     protected FundamentalNutationArguments getNutationArguments(
  2040.             final TimeScales timeScales) {

  2041.         return getNutationArguments(null, timeScales);
  2042.     }

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

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

  2070.     /** Get the function computing mean obliquity of the ecliptic.
  2071.      *
  2072.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2073.      *
  2074.      * @return function computing mean obliquity of the ecliptic
  2075.      * @since 6.1
  2076.      * @see #getMeanObliquityFunction(TimeScales)
  2077.      */
  2078.     @DefaultDataContext
  2079.     public TimeScalarFunction getMeanObliquityFunction() {
  2080.         return getMeanObliquityFunction(DataContext.getDefault().getTimeScales());
  2081.     }

  2082.     /**
  2083.      * Get the function computing mean obliquity of the ecliptic.
  2084.      *
  2085.      * @param timeScales used in computing the function.
  2086.      * @return function computing mean obliquity of the ecliptic
  2087.      * @since 10.1
  2088.      */
  2089.     public abstract TimeScalarFunction getMeanObliquityFunction(TimeScales timeScales);

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

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

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

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

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

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

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

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

  2219.     /** Get the function computing Greenwich mean sidereal time, in radians.
  2220.      *
  2221.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2222.      *
  2223.      * @param ut1 UT1 time scale
  2224.      * @return function computing Greenwich mean sidereal time
  2225.      * @since 6.1
  2226.      * @see #getGMSTFunction(TimeScale, TimeScales)
  2227.      */
  2228.     @DefaultDataContext
  2229.     public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
  2230.         return getGMSTFunction(ut1, DataContext.getDefault().getTimeScales());
  2231.     }

  2232.     /**
  2233.      * Get the function computing Greenwich mean sidereal time, in radians.
  2234.      *
  2235.      * @param ut1 UT1 time scale
  2236.      * @param timeScales other time scales used in the computation including TAI and TT.
  2237.      * @return function computing Greenwich mean sidereal time
  2238.      * @since 10.1
  2239.      */
  2240.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1,
  2241.                                                        TimeScales timeScales);

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

  2256.     /**
  2257.      * Get the function computing Greenwich mean sidereal time rate, in radians per
  2258.      * second.
  2259.      *
  2260.      * @param ut1 UT1 time scale
  2261.      * @param timeScales other time scales used in the computation including TAI and TT.
  2262.      * @return function computing Greenwich mean sidereal time rate
  2263.      * @since 10.1
  2264.      */
  2265.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1,
  2266.                                                            TimeScales timeScales);

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

  2288.     /**
  2289.      * Get the function computing Greenwich apparent sidereal time, in radians.
  2290.      *
  2291.      * @param ut1        UT1 time scale
  2292.      * @param eopHistory EOP history. If {@code null} then no nutation correction is
  2293.      *                   applied for EOP.
  2294.      * @param timeScales        TAI time scale.
  2295.      * @return function computing Greenwich apparent sidereal time
  2296.      * @since 10.1
  2297.      */
  2298.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1,
  2299.                                                        EOPHistory eopHistory,
  2300.                                                        TimeScales timeScales);

  2301.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  2302.      *
  2303.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2304.      *
  2305.      * @return function computing tidal corrections for Earth Orientation Parameters,
  2306.      * for xp, yp, ut1 and lod respectively
  2307.      * @since 6.1
  2308.      * @see #getEOPTidalCorrection(TimeScales)
  2309.      */
  2310.     @DefaultDataContext
  2311.     public TimeVectorFunction getEOPTidalCorrection() {
  2312.         return getEOPTidalCorrection(DataContext.getDefault().getTimeScales());
  2313.     }

  2314.     /**
  2315.      * Get the function computing tidal corrections for Earth Orientation Parameters.
  2316.      *
  2317.      * @param timeScales used in the computation. The TT and TAI scales are used.
  2318.      * @return function computing tidal corrections for Earth Orientation Parameters, for
  2319.      * xp, yp, ut1 and lod respectively
  2320.      * @since 10.1
  2321.      */
  2322.     public abstract TimeVectorFunction getEOPTidalCorrection(TimeScales timeScales);

  2323.     /** Get the Love numbers.
  2324.      * @return Love numbers
  2325.           * @since 6.1
  2326.      */
  2327.     public abstract LoveNumbers getLoveNumbers();

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

  2342.     /**
  2343.      * Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2344.      * ΔS₂₂).
  2345.      *
  2346.      * @param ut1 UT1 time scale
  2347.      * @param timeScales other time scales used in the computation including TAI and TT.
  2348.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2349.      * ΔS₂₂).
  2350.      * @since 10.1
  2351.      */
  2352.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(
  2353.             TimeScale ut1,
  2354.             TimeScales timeScales);

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

  2359.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  2360.      * @param eopHistory EOP history
  2361.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2362.           * @since 6.1
  2363.      */
  2364.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory);

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

  2371.     /** Get the nominal values of the displacement numbers.
  2372.      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
  2373.      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
  2374.      * H₀ permanent deformation amplitude
  2375.      * @since 9.1
  2376.      */
  2377.     public abstract double[] getNominalTidalDisplacement();

  2378.     /** Get the correction function for tidal displacement for diurnal tides.
  2379.      * <ul>
  2380.      *  <li>f[0]: radial correction, longitude cosine part</li>
  2381.      *  <li>f[1]: radial correction, longitude sine part</li>
  2382.      *  <li>f[2]: North correction, longitude cosine part</li>
  2383.      *  <li>f[3]: North correction, longitude sine part</li>
  2384.      *  <li>f[4]: East correction, longitude cosine part</li>
  2385.      *  <li>f[5]: East correction, longitude sine part</li>
  2386.      * </ul>
  2387.      * @return correction function for tidal displacement
  2388.      * @since 9.1
  2389.      */
  2390.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal();

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

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

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

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

  2466.             return PoissonSeries.compile(drCos, drSin,
  2467.                                          dnCos, dnSin,
  2468.                                          deCos, deSin);
  2469.         } catch (IOException e) {
  2470.             throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  2471.         }

  2472.     }

  2473.     /** Get the correction function for tidal displacement for zonal tides.
  2474.      * <ul>
  2475.      *  <li>f[0]: radial correction</li>
  2476.      *  <li>f[1]: North correction</li>
  2477.      * </ul>
  2478.      * @return correction function for tidal displacement
  2479.      * @since 9.1
  2480.      */
  2481.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal();

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

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

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

  2521.             return PoissonSeries.compile(dr, dn);
  2522.         } catch (IOException e) {
  2523.             throw new OrekitException(OrekitMessages.INTERNAL_ERROR, e);
  2524.         }

  2525.     }

  2526.     /** Interface for functions converting nutation corrections between
  2527.      * δΔψ/δΔε to δX/δY.
  2528.      * <ul>
  2529.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2530.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2531.      * </ul>
  2532.      * @since 6.1
  2533.      */
  2534.     public interface NutationCorrectionConverter {

  2535.         /** Convert nutation corrections.
  2536.          * @param date current date
  2537.          * @param ddPsi δΔψ part of the nutation correction
  2538.          * @param ddEpsilon δΔε part of the nutation correction
  2539.          * @return array containing δX and δY
  2540.          */
  2541.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon);

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

  2549.     }

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

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

  2579.         // get models parameters
  2580.         final TimeVectorFunction precessionFunction = getPrecessionFunction(timeScales);
  2581.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction(timeScales);
  2582.         final AbsoluteDate date0 = getNutationReferenceEpoch(timeScales);
  2583.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  2584.         return new NutationCorrectionConverter() {

  2585.             /** {@inheritDoc} */
  2586.             @Override
  2587.             public double[] toNonRotating(final AbsoluteDate date,
  2588.                                           final double ddPsi, final double ddEpsilon) {
  2589.                 // compute precession angles psiA, omegaA and chiA
  2590.                 final double[] angles = precessionFunction.value(date);

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

  2594.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  2595.                 return new double[] {
  2596.                     sinEA * ddPsi + c * ddEpsilon,
  2597.                     ddEpsilon - c * sinEA * ddPsi
  2598.                 };

  2599.             }

  2600.             /** {@inheritDoc} */
  2601.             @Override
  2602.             public double[] toEquinox(final AbsoluteDate date,
  2603.                                       final double dX, final double dY) {
  2604.                 // compute precession angles psiA, omegaA and chiA
  2605.                 final double[] angles   = precessionFunction.value(date);

  2606.                 // conversion coefficients
  2607.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2608.                 final double c     = angles[0] * cosE0 - angles[2];
  2609.                 final double opC2  = 1 + c * c;

  2610.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  2611.                 return new double[] {
  2612.                     (dX - c * dY) / (sinEA * opC2),
  2613.                     (dY + c * dX) / opC2
  2614.                 };
  2615.             }

  2616.         };

  2617.     }

  2618.     /** Load the Love numbers.
  2619.      * @param nameLove name of the Love number resource
  2620.      * @return Love numbers
  2621.      */
  2622.     protected LoveNumbers loadLoveNumbers(final String nameLove) {
  2623.         try {

  2624.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  2625.             final double[][] real      = new double[4][];
  2626.             final double[][] imaginary = new double[4][];
  2627.             final double[][] plus      = new double[4][];
  2628.             for (int i = 0; i < real.length; ++i) {
  2629.                 real[i]      = new double[i + 1];
  2630.                 imaginary[i] = new double[i + 1];
  2631.                 plus[i]      = new double[i + 1];
  2632.             }

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

  2634.                 if (stream == null) {
  2635.                     // this should never happen with files embedded within Orekit
  2636.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  2637.                 }

  2638.                 int lineNumber = 1;
  2639.                 String line = null;
  2640.                 // setup the reader
  2641.                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {

  2642.                     line = reader.readLine();

  2643.                     // look for the Love numbers
  2644.                     while (line != null) {

  2645.                         line = line.trim();
  2646.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  2647.                             final String[] fields = SEPARATOR.split(line);
  2648.                             if (fields.length != 5) {
  2649.                                 // this should never happen with files embedded within Orekit
  2650.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2651.                                                           lineNumber, nameLove, line);
  2652.                             }
  2653.                             final int n = Integer.parseInt(fields[0]);
  2654.                             final int m = Integer.parseInt(fields[1]);
  2655.                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  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.                             real[n][m]      = Double.parseDouble(fields[2]);
  2661.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  2662.                             plus[n][m]      = Double.parseDouble(fields[4]);
  2663.                         }

  2664.                         // next line
  2665.                         lineNumber++;
  2666.                         line = reader.readLine();

  2667.                     }
  2668.                 } catch (NumberFormatException nfe) {
  2669.                     // this should never happen with files embedded within Orekit
  2670.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2671.                                               lineNumber, nameLove, line);
  2672.                 }
  2673.             }

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

  2675.         } catch (IOException ioe) {
  2676.             // this should never happen with files embedded within Orekit
  2677.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  2678.         }
  2679.     }

  2680.     /** Get a data stream.
  2681.      * @param name file name of the resource stream
  2682.      * @return stream
  2683.      */
  2684.     private static InputStream getStream(final String name) {
  2685.         return IERSConventions.class.getResourceAsStream(name);
  2686.     }

  2687.     /** Correction to equation of equinoxes.
  2688.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2689.      * taking effect since 1997-02-27 for continuity
  2690.      * </p>
  2691.      */
  2692.     private static class IAU1994ResolutionC7 {

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

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


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

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

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

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

  2715.             } else {
  2716.                 return 0.0;
  2717.             }
  2718.         }

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

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

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

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

  2739.             } else {
  2740.                 return arguments.getDate().getField().getZero();
  2741.             }
  2742.         }

  2743.     };

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

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

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

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

  2768.         /** UT1 time scale. */
  2769.         private final TimeScale ut1;

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

  2772.         /** Simple constructor.
  2773.          * @param ut1 UT1 time scale
  2774.          * @param tai TAI time scale
  2775.          */
  2776.         StellarAngleCapitaine(final TimeScale ut1, final TimeScale tai) {
  2777.             this.ut1 = ut1;
  2778.             referenceDate = new AbsoluteDate(
  2779.                     DateComponents.J2000_EPOCH,
  2780.                     TimeComponents.H12,
  2781.                     tai);
  2782.         }

  2783.         /** Get the rotation rate.
  2784.          * @return rotation rate
  2785.          */
  2786.         public double getRate() {
  2787.             return ERA_1A + ERA_1B;
  2788.         }

  2789.         /** {@inheritDoc} */
  2790.         @Override
  2791.         public double value(final AbsoluteDate date) {

  2792.             // split the date offset as a full number of days plus a smaller part
  2793.             final int secondsInDay = 86400;
  2794.             final double dt  = date.durationFrom(referenceDate);
  2795.             final long days  = ((long) dt) / secondsInDay;
  2796.             final double dtA = secondsInDay * days;
  2797.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

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

  2799.         }

  2800.         /** {@inheritDoc} */
  2801.         @Override
  2802.         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2803.             // split the date offset as a full number of days plus a smaller part
  2804.             final int secondsInDay = 86400;
  2805.             final T dt  = date.durationFrom(referenceDate);
  2806.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2807.             final double dtA = secondsInDay * days;
  2808.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));

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

  2810.         }

  2811.     }

  2812.     /** Mean pole. */
  2813.     private static class MeanPole implements TimeStamped, Serializable {

  2814.         /** Serializable UID. */
  2815.         private static final long serialVersionUID = 20131028l;

  2816.         /** Date. */
  2817.         private final AbsoluteDate date;

  2818.         /** X coordinate. */
  2819.         private double x;

  2820.         /** Y coordinate. */
  2821.         private double y;

  2822.         /** Simple constructor.
  2823.          * @param date date
  2824.          * @param x x coordinate
  2825.          * @param y y coordinate
  2826.          */
  2827.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2828.             this.date = date;
  2829.             this.x    = x;
  2830.             this.y    = y;
  2831.         }

  2832.         /** {@inheritDoc} */
  2833.         @Override
  2834.         public AbsoluteDate getDate() {
  2835.             return date;
  2836.         }

  2837.         /** Get x coordinate.
  2838.          * @return x coordinate
  2839.          */
  2840.         public double getX() {
  2841.             return x;
  2842.         }

  2843.         /** Get y coordinate.
  2844.          * @return y coordinate
  2845.          */
  2846.         public double getY() {
  2847.             return y;
  2848.         }

  2849.     }

  2850.     /** Local class for precession function. */
  2851.     private class PrecessionFunction implements TimeVectorFunction {

  2852.         /** Polynomial nutation for psiA. */
  2853.         private final PolynomialNutation psiA;

  2854.         /** Polynomial nutation for omegaA. */
  2855.         private final PolynomialNutation omegaA;

  2856.         /** Polynomial nutation for chiA. */
  2857.         private final PolynomialNutation chiA;

  2858.         /** Time scales to use. */
  2859.         private final TimeScales timeScales;

  2860.         /** Simple constructor.
  2861.          * @param psiA polynomial nutation for psiA
  2862.          * @param omegaA polynomial nutation for omegaA
  2863.          * @param chiA polynomial nutation for chiA
  2864.          * @param timeScales used in the computation.
  2865.          */
  2866.         PrecessionFunction(final PolynomialNutation psiA,
  2867.                            final PolynomialNutation omegaA,
  2868.                            final PolynomialNutation chiA,
  2869.                            final TimeScales timeScales) {
  2870.             this.psiA   = psiA;
  2871.             this.omegaA = omegaA;
  2872.             this.chiA   = chiA;
  2873.             this.timeScales = timeScales;
  2874.         }


  2875.         /** {@inheritDoc} */
  2876.         @Override
  2877.         public double[] value(final AbsoluteDate date) {
  2878.             final double tc = evaluateTC(date, timeScales);
  2879.             return new double[] {
  2880.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2881.             };
  2882.         }

  2883.         /** {@inheritDoc} */
  2884.         @Override
  2885.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2886.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2887.             final T tc = evaluateTC(date, timeScales);
  2888.             a[0] = psiA.value(tc);
  2889.             a[1] = omegaA.value(tc);
  2890.             a[2] = chiA.value(tc);
  2891.             return a;
  2892.         }

  2893.     }

  2894.     /** Local class for tides frequency function. */
  2895.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2896.         /** Nutation arguments. */
  2897.         private final FundamentalNutationArguments arguments;

  2898.         /** Correction series. */
  2899.         private final PoissonSeries.CompiledSeries kSeries;

  2900.         /** Simple constructor.
  2901.          * @param arguments nutation arguments
  2902.          * @param c20Series correction series for the C20 term
  2903.          * @param c21Series correction series for the C21 term
  2904.          * @param s21Series correction series for the S21 term
  2905.          * @param c22Series correction series for the C22 term
  2906.          * @param s22Series correction series for the S22 term
  2907.          */
  2908.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2909.                                         final PoissonSeries c20Series,
  2910.                                         final PoissonSeries c21Series,
  2911.                                         final PoissonSeries s21Series,
  2912.                                         final PoissonSeries c22Series,
  2913.                                         final PoissonSeries s22Series) {
  2914.             this.arguments = arguments;
  2915.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2916.         }


  2917.         /** {@inheritDoc} */
  2918.         @Override
  2919.         public double[] value(final AbsoluteDate date) {
  2920.             return kSeries.value(arguments.evaluateAll(date));
  2921.         }

  2922.         /** {@inheritDoc} */
  2923.         @Override
  2924.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2925.             return kSeries.value(arguments.evaluateAll(date));
  2926.         }

  2927.     }

  2928.     /** Local class for EOP tidal corrections. */
  2929.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2930.         /** Nutation arguments. */
  2931.         private final FundamentalNutationArguments arguments;

  2932.         /** Correction series. */
  2933.         private final PoissonSeries.CompiledSeries correctionSeries;

  2934.         /** Simple constructor.
  2935.          * @param arguments nutation arguments
  2936.          * @param xSeries correction series for the x coordinate
  2937.          * @param ySeries correction series for the y coordinate
  2938.          * @param ut1Series correction series for the UT1
  2939.          */
  2940.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2941.                            final PoissonSeries xSeries,
  2942.                            final PoissonSeries ySeries,
  2943.                            final PoissonSeries ut1Series) {
  2944.             this.arguments        = arguments;
  2945.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2946.         }

  2947.         /** {@inheritDoc} */
  2948.         @Override
  2949.         public double[] value(final AbsoluteDate date) {
  2950.             final BodiesElements elements = arguments.evaluateAll(date);
  2951.             final double[] correction    = correctionSeries.value(elements);
  2952.             final double[] correctionDot = correctionSeries.derivative(elements);
  2953.             return new double[] {
  2954.                 correction[0],
  2955.                 correction[1],
  2956.                 correction[2],
  2957.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2958.             };
  2959.         }

  2960.         /** {@inheritDoc} */
  2961.         @Override
  2962.         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2963.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2964.             final T[] correction    = correctionSeries.value(elements);
  2965.             final T[] correctionDot = correctionSeries.derivative(elements);
  2966.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2967.             a[0] = correction[0];
  2968.             a[1] = correction[1];
  2969.             a[2] = correction[2];
  2970.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2971.             return a;
  2972.         }

  2973.     }

  2974. }