IERSConventions.java

  1. /* Copyright 2002-2025 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.function.Function;
  26. import java.util.regex.Pattern;

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


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

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

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

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

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

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

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

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

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

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

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

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

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

  94.         /** {@inheritDoc} */
  95.         @Override
  96.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  97.                                                                  final TimeScales timeScales) {
  98.             return load(NUTATION_ARGUMENTS, in -> new FundamentalNutationArguments(this, timeScale,
  99.                                                                                             in, NUTATION_ARGUMENTS,
  100.                                                                                             timeScales));
  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 CalculusFieldElement<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 = load(X_Y_SERIES, in -> xParser.parse(in, X_Y_SERIES));

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

  161.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  162.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

  163.             final PoissonSeriesParser yParser =
  164.                     baseParser.
  165.                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
  166.                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
  167.             final PoissonSeries ySum = load(X_Y_SERIES, in -> yParser.parse(in, X_Y_SERIES));

  168.             final PoissonSeries.CompiledSeries xySum =
  169.                     PoissonSeries.compile(xSum, ySum);

  170.             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
  171.             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
  172.             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
  173.             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
  174.             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
  175.             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  176.             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
  177.             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;

  178.             return new TimeVectorFunction() {

  179.                 /** {@inheritDoc} */
  180.                 @Override
  181.                 public double[] value(final AbsoluteDate date) {

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

  184.                     final double omega     = elements.getOmega();
  185.                     final double f         = elements.getF();
  186.                     final double d         = elements.getD();
  187.                     final double t         = elements.getTC();

  188.                     final SinCos scOmega   = FastMath.sinCos(omega);
  189.                     final SinCos sc2omega  = SinCos.sum(scOmega, scOmega);
  190.                     final SinCos sc2FD0m   = FastMath.sinCos(2 * (f - d + omega));
  191.                     final double cosOmega  = scOmega.cos();
  192.                     final double sinOmega  = scOmega.sin();
  193.                     final double sin2Omega = sc2omega.sin();
  194.                     final double cos2FDOm  = sc2FD0m.cos();
  195.                     final double sin2FDOm  = sc2FD0m.sin();

  196.                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
  197.                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
  198.                     final double y = yPolynomial.value(t) + xy[1] +
  199.                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
  200.                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
  201.                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));

  202.                     return new double[] {
  203.                         x, y, sPxy2
  204.                     };

  205.                 }

  206.                 /** {@inheritDoc} */
  207.                 @Override
  208.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

  211.                     final T omega     = elements.getOmega();
  212.                     final T f         = elements.getF();
  213.                     final T d         = elements.getD();
  214.                     final T t         = elements.getTC();
  215.                     final T t2        = t.square();

  216.                     final FieldSinCos<T> scOmega  = FastMath.sinCos(omega);
  217.                     final FieldSinCos<T> sc2omega = FieldSinCos.sum(scOmega, scOmega);
  218.                     final FieldSinCos<T> sc2FD0m  = FastMath.sinCos(f.subtract(d).add(omega).multiply(2));
  219.                     final T cosOmega  = scOmega.cos();
  220.                     final T sinOmega  = scOmega.sin();
  221.                     final T sin2Omega = sc2omega.sin();
  222.                     final T cos2FDOm  = sc2FD0m.cos();
  223.                     final T sin2FDOm  = sc2FD0m.sin();

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

  233.                     final T[] a = MathArrays.buildArray(date.getField(), 3);
  234.                     a[0] = x;
  235.                     a[1] = y;
  236.                     a[2] = sPxy2;
  237.                     return a;

  238.                 }

  239.             };

  240.         }

  241.         /** {@inheritDoc} */
  242.         @Override
  243.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

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

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

  266.         }

  267.         /** {@inheritDoc} */
  268.         @Override
  269.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  270.             // set up nutation arguments
  271.             final FundamentalNutationArguments arguments =
  272.                     getNutationArguments(timeScales);

  273.             // set up Poisson series
  274.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  275.             final PoissonSeriesParser baseParser =
  276.                     new PoissonSeriesParser(10).withFirstDelaunay(1);

  277.             final PoissonSeriesParser psiParser =
  278.                     baseParser.
  279.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  280.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  281.             final PoissonSeries psiSeries = load(PSI_EPSILON_SERIES, in -> psiParser.parse(in, PSI_EPSILON_SERIES));

  282.             final PoissonSeriesParser epsilonParser =
  283.                     baseParser.
  284.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  285.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  286.             final PoissonSeries epsilonSeries = load(PSI_EPSILON_SERIES, in -> epsilonParser.parse(in, PSI_EPSILON_SERIES));

  287.             final PoissonSeries.CompiledSeries psiEpsilonSeries =
  288.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  289.             return new TimeVectorFunction() {

  290.                 /** {@inheritDoc} */
  291.                 @Override
  292.                 public double[] value(final AbsoluteDate date) {
  293.                     final BodiesElements elements = arguments.evaluateAll(date);
  294.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  295.                     return new double[] {
  296.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  297.                     };
  298.                 }

  299.                 /** {@inheritDoc} */
  300.                 @Override
  301.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  302.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  303.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  304.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  305.                     result[0] = psiEpsilon[0];
  306.                     result[1] = psiEpsilon[1];
  307.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  308.                     return result;
  309.                 }

  310.             };

  311.         }

  312.         /** {@inheritDoc} */
  313.         @Override
  314.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  315.                                                   final TimeScales timeScales) {

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

  318.             // constants from IERS 1996 page 21
  319.             // the underlying model is IAU 1982 GMST-UT1
  320.             final AbsoluteDate gmstReference = new AbsoluteDate(
  321.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  322.             final double gmst0 = 24110.54841;
  323.             final double gmst1 = 8640184.812866;
  324.             final double gmst2 = 0.093104;
  325.             final double gmst3 = -6.2e-6;

  326.             return new TimeScalarFunction() {

  327.                 /** {@inheritDoc} */
  328.                 @Override
  329.                 public double value(final AbsoluteDate date) {

  330.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  331.                     final double dtai = date.durationFrom(gmstReference);
  332.                     final double tut1 = dtai + ut1.offsetFromTAI(date).toDouble();
  333.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

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

  339.                 }

  340.                 /** {@inheritDoc} */
  341.                 @Override
  342.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  343.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  344.                     final T dtai = date.durationFrom(gmstReference);
  345.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()).toDouble());
  346.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

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

  352.                 }

  353.             };

  354.         }

  355.         /** {@inheritDoc} */
  356.         @Override
  357.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  358.                                                       final TimeScales timeScales) {

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

  361.             // constants from IERS 1996 page 21
  362.             // the underlying model is IAU 1982 GMST-UT1
  363.             final AbsoluteDate gmstReference = new AbsoluteDate(
  364.                     DateComponents.J2000_EPOCH, TimeComponents.H12, timeScales.getTAI());
  365.             final double gmst1 = 8640184.812866;
  366.             final double gmst2 = 0.093104;
  367.             final double gmst3 = -6.2e-6;

  368.             return new TimeScalarFunction() {

  369.                 /** {@inheritDoc} */
  370.                 @Override
  371.                 public double value(final AbsoluteDate date) {

  372.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  373.                     final double dtai = date.durationFrom(gmstReference);
  374.                     final double tut1 = dtai + ut1.offsetFromTAI(date).toDouble();
  375.                     final double tt   = tut1 / Constants.JULIAN_CENTURY;

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

  378.                 }

  379.                 /** {@inheritDoc} */
  380.                 @Override
  381.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  382.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  383.                     final T dtai = date.durationFrom(gmstReference);
  384.                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()).toDouble());
  385.                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);

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

  388.                 }

  389.             };

  390.         }

  391.         /** {@inheritDoc} */
  392.         @Override
  393.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  394.                                                   final EOPHistory eopHistory,
  395.                                                   final TimeScales timeScales) {

  396.             // obliquity
  397.             final TimeScalarFunction epsilonA = getMeanObliquityFunction(timeScales);

  398.             // GMST function
  399.             final TimeScalarFunction gmst = getGMSTFunction(ut1, timeScales);

  400.             // nutation function
  401.             final TimeVectorFunction nutation = getNutationFunction(timeScales);

  402.             return new TimeScalarFunction() {

  403.                 /** {@inheritDoc} */
  404.                 @Override
  405.                 public double value(final AbsoluteDate date) {

  406.                     // compute equation of equinoxes
  407.                     final double[] angles = nutation.value(date);
  408.                     double deltaPsi = angles[0];
  409.                     if (eopHistory != null) {
  410.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  411.                     }
  412.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

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

  415.                 }

  416.                 /** {@inheritDoc} */
  417.                 @Override
  418.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  419.                     // compute equation of equinoxes
  420.                     final T[] angles = nutation.value(date);
  421.                     T deltaPsi = angles[0];
  422.                     if (eopHistory != null) {
  423.                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
  424.                     }
  425.                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);

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

  428.                 }

  429.             };

  430.         }

  431.         /** {@inheritDoc} */
  432.         @Override
  433.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  441.             // set up Poisson series
  442.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  443.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(17).
  444.                     withOptionalColumn(1).
  445.                     withGamma(7).
  446.                     withFirstDelaunay(2);
  447.             final PoissonSeries xSeries =
  448.                             load(TIDAL_CORRECTION_XP_YP_SERIES, in -> xyParser.
  449.                                                                       withSinCos(0, 14, milliAS, 15, milliAS).
  450.                                                                       parse(in, TIDAL_CORRECTION_XP_YP_SERIES));
  451.             final PoissonSeries ySeries =
  452.                             load(TIDAL_CORRECTION_XP_YP_SERIES, in -> xyParser.
  453.                                                                       withSinCos(0, 16, milliAS, 17, milliAS).
  454.                                                                       parse(in, TIDAL_CORRECTION_XP_YP_SERIES));

  455.             final double deciMilliS = 1.0e-4;
  456.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
  457.                     withOptionalColumn(1).
  458.                     withGamma(7).
  459.                     withFirstDelaunay(2);
  460.             final PoissonSeries ut1Series =
  461.                             load(TIDAL_CORRECTION_UT1_SERIES, in -> ut1Parser.
  462.                                                                     withSinCos(0, 16, deciMilliS, 17, deciMilliS).
  463.                                                                     parse(in, TIDAL_CORRECTION_UT1_SERIES));

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

  465.         }

  466.         /** {@inheritDoc} */
  467.         public LoveNumbers getLoveNumbers() {
  468.             return loadLoveNumbers(LOVE_NUMBERS);
  469.         }

  470.         /** {@inheritDoc} */
  471.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  472.                                                                      final TimeScales timeScales) {

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

  475.             // set up Poisson series
  476.             final PoissonSeriesParser k20Parser =
  477.                     new PoissonSeriesParser(18).
  478.                         withOptionalColumn(1).
  479.                         withDoodson(4, 2).
  480.                         withFirstDelaunay(10);
  481.             final PoissonSeriesParser k21Parser =
  482.                     new PoissonSeriesParser(18).
  483.                         withOptionalColumn(1).
  484.                         withDoodson(4, 3).
  485.                         withFirstDelaunay(10);
  486.             final PoissonSeriesParser k22Parser =
  487.                     new PoissonSeriesParser(16).
  488.                         withOptionalColumn(1).
  489.                         withDoodson(4, 2).
  490.                         withFirstDelaunay(10);

  491.             final double pico = 1.0e-12;
  492.             final PoissonSeries c20Series =
  493.                             load(K20_FREQUENCY_DEPENDENCE, in -> k20Parser.
  494.                                                                  withSinCos(0, 18, -pico, 16, pico).
  495.                                                                  parse(in, K20_FREQUENCY_DEPENDENCE));
  496.             final PoissonSeries c21Series =
  497.                             load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  498.                                                                  withSinCos(0, 17, pico, 18, pico).
  499.                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  500.             final PoissonSeries s21Series =
  501.                             load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  502.                                                                  withSinCos(0, 18, -pico, 17, pico).
  503.                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  504.             final PoissonSeries c22Series =
  505.                             load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  506.                                                                  withSinCos(0, -1, pico, 16, pico).
  507.                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));
  508.             final PoissonSeries s22Series =
  509.                             load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  510.                                                                  withSinCos(0, 16, -pico, -1, pico).
  511.                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));

  512.             return new TideFrequencyDependenceFunction(arguments,
  513.                                                        c20Series,
  514.                                                        c21Series, s21Series,
  515.                                                        c22Series, s22Series);

  516.         }

  517.         /** {@inheritDoc} */
  518.         @Override
  519.         public double getPermanentTide() {
  520.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  521.         }

  522.         /** {@inheritDoc} */
  523.         @Override
  524.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  525.             // constants from IERS 1996 page 47
  526.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  527.             final double coupling     =  0.0112;

  528.             return new TimeVectorFunction() {

  529.                 /** {@inheritDoc} */
  530.                 @Override
  531.                 public double[] value(final AbsoluteDate date) {
  532.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  533.                     return new double[] {
  534.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  535.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  536.                     };
  537.                 }

  538.                 /** {@inheritDoc} */
  539.                 @Override
  540.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  541.                     final FieldPoleCorrection<T> pole = eopHistory.getPoleCorrection(date);
  542.                     final T[] a = MathArrays.buildArray(date.getField(), 2);
  543.                     a[0] = pole.getXp().add(pole.getYp().multiply(coupling)).multiply(globalFactor);
  544.                     a[1] = pole.getXp().multiply(coupling).subtract(pole.getYp()).multiply(globalFactor);
  545.                     return a;
  546.                 }

  547.             };
  548.         }

  549.         /** {@inheritDoc} */
  550.         @Override
  551.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  552.             return new TimeVectorFunction() {

  553.                 /** {@inheritDoc} */
  554.                 @Override
  555.                 public double[] value(final AbsoluteDate date) {
  556.                     // there are no model for ocean pole tide prior to conventions 2010
  557.                     return new double[] {
  558.                         0.0, 0.0
  559.                     };
  560.                 }

  561.                 /** {@inheritDoc} */
  562.                 @Override
  563.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  564.                     // there are no model for ocean pole tide prior to conventions 2010
  565.                     return MathArrays.buildArray(date.getField(), 2);
  566.                 }

  567.             };
  568.         }

  569.         /** {@inheritDoc} */
  570.         @Override
  571.         public double[] getNominalTidalDisplacement() {

  572.             //  // elastic Earth values
  573.             //  return new double[] {
  574.             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  575.             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
  576.             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  577.             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  578.             //      // H₀
  579.             //      -0.31460
  580.             //  };

  581.             // anelastic Earth values
  582.             return new double[] {
  583.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  584.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  585.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  586.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  587.                 // H₀
  588.                 -0.31460
  589.             };

  590.         }

  591.         /** {@inheritDoc} */
  592.         @Override
  593.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  594.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  595.                                                                   18, 17, -1, 18, -1);
  596.         }

  597.         /** {@inheritDoc} */
  598.         @Override
  599.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  600.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  601.                                                                 20, 17, 19, 18, 20);
  602.         }

  603.     },

  604.     /** Constant for IERS 2003 conventions. */
  605.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  638.         /** {@inheritDoc} */
  639.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  640.                                                                  final TimeScales timeScales) {
  641.             return load(NUTATION_ARGUMENTS, in -> new FundamentalNutationArguments(this, timeScale,
  642.                                                                                    in, NUTATION_ARGUMENTS,
  643.                                                                                    timeScales));
  644.         }

  645.         /** {@inheritDoc} */
  646.         @Override
  647.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  648.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  649.             final PolynomialNutation epsilonA =
  650.                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  651.                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  652.                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  653.                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  654.             return new TimeScalarFunction() {

  655.                 /** {@inheritDoc} */
  656.                 @Override
  657.                 public double value(final AbsoluteDate date) {
  658.                     return epsilonA.value(evaluateTC(date, timeScales));
  659.                 }

  660.                 /** {@inheritDoc} */
  661.                 @Override
  662.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  663.                     return epsilonA.value(evaluateTC(date, timeScales));
  664.                 }

  665.             };

  666.         }

  667.         /** {@inheritDoc} */
  668.         @Override
  669.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  670.             // set up nutation arguments
  671.             final FundamentalNutationArguments arguments =
  672.                     getNutationArguments(timeScales);

  673.             // set up Poisson series
  674.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  675.             final PoissonSeriesParser parser =
  676.                     new PoissonSeriesParser(17).
  677.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  678.                         withFirstDelaunay(4).
  679.                         withFirstPlanetary(9).
  680.                         withSinCos(0, 2, microAS, 3, microAS);

  681.             final PoissonSeries xSeries = load(X_SERIES, in -> parser.parse(in, X_SERIES));
  682.             final PoissonSeries ySeries = load(Y_SERIES, in -> parser.parse(in, Y_SERIES));
  683.             final PoissonSeries sSeries = load(S_SERIES, in -> parser.parse(in, S_SERIES));
  684.             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  685.             // create a function evaluating the series
  686.             return new TimeVectorFunction() {

  687.                 /** {@inheritDoc} */
  688.                 @Override
  689.                 public double[] value(final AbsoluteDate date) {
  690.                     return xys.value(arguments.evaluateAll(date));
  691.                 }

  692.                 /** {@inheritDoc} */
  693.                 @Override
  694.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  695.                     return xys.value(arguments.evaluateAll(date));
  696.                 }

  697.             };

  698.         }


  699.         /** {@inheritDoc} */
  700.         @Override
  701.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

  702.             // set up the conventional polynomials
  703.             // the following values are from equation 32 in IERS 2003 conventions
  704.             final PolynomialNutation psiA =
  705.                     new PolynomialNutation(    0.0,
  706.                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  707.                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  708.                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  709.             final PolynomialNutation omegaA =
  710.                     new PolynomialNutation(getMeanObliquityFunction(timeScales)
  711.                             .value(getNutationReferenceEpoch(timeScales)),
  712.                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  713.                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  714.                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  715.             final PolynomialNutation chiA =
  716.                     new PolynomialNutation( 0.0,
  717.                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  718.                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  719.                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

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

  721.         }

  722.         /** {@inheritDoc} */
  723.         @Override
  724.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  725.             // set up nutation arguments
  726.             final FundamentalNutationArguments arguments =
  727.                     getNutationArguments(timeScales);

  728.             // set up Poisson series
  729.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  730.             final PoissonSeriesParser luniSolarParser =
  731.                     new PoissonSeriesParser(14).withFirstDelaunay(1);
  732.             final PoissonSeriesParser luniSolarPsiParser =
  733.                     luniSolarParser.
  734.                     withSinCos(0, 7, milliAS, 11, milliAS).
  735.                     withSinCos(1, 8, milliAS, 12, milliAS);
  736.             final PoissonSeries psiLuniSolarSeries =
  737.                             load(LUNI_SOLAR_SERIES, in -> luniSolarPsiParser.parse(in, LUNI_SOLAR_SERIES));
  738.             final PoissonSeriesParser luniSolarEpsilonParser = luniSolarParser.
  739.                                                                withSinCos(0, 13, milliAS, 9, milliAS).
  740.                                                                withSinCos(1, 14, milliAS, 10, milliAS);
  741.             final PoissonSeries epsilonLuniSolarSeries =
  742.                             load(LUNI_SOLAR_SERIES, in -> luniSolarEpsilonParser.parse(in, LUNI_SOLAR_SERIES));

  743.             final PoissonSeriesParser planetaryParser =
  744.                     new PoissonSeriesParser(21).
  745.                         withFirstDelaunay(2).
  746.                         withFirstPlanetary(7);
  747.             final PoissonSeriesParser planetaryPsiParser =
  748.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  749.             final PoissonSeries psiPlanetarySeries =
  750.                             load(PLANETARY_SERIES, in -> planetaryPsiParser.parse(in, PLANETARY_SERIES));
  751.             final PoissonSeriesParser planetaryEpsilonParser =
  752.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  753.             final PoissonSeries epsilonPlanetarySeries =
  754.                             load(PLANETARY_SERIES, in -> planetaryEpsilonParser.parse(in, PLANETARY_SERIES));

  755.             final PoissonSeries.CompiledSeries luniSolarSeries =
  756.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  757.             final PoissonSeries.CompiledSeries planetarySeries =
  758.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  759.             return new TimeVectorFunction() {

  760.                 /** {@inheritDoc} */
  761.                 @Override
  762.                 public double[] value(final AbsoluteDate date) {
  763.                     final BodiesElements elements = arguments.evaluateAll(date);
  764.                     final double[] luniSolar = luniSolarSeries.value(elements);
  765.                     final double[] planetary = planetarySeries.value(elements);
  766.                     return new double[] {
  767.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  768.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  769.                     };
  770.                 }

  771.                 /** {@inheritDoc} */
  772.                 @Override
  773.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  774.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  775.                     final T[] luniSolar = luniSolarSeries.value(elements);
  776.                     final T[] planetary = planetarySeries.value(elements);
  777.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  778.                     result[0] = luniSolar[0].add(planetary[0]);
  779.                     result[1] = luniSolar[1].add(planetary[1]);
  780.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  781.                     return result;
  782.                 }

  783.             };

  784.         }

  785.         /** {@inheritDoc} */
  786.         @Override
  787.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  788.                                                   final TimeScales timeScales) {

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

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

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

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

  809.                 /** {@inheritDoc} */
  810.                 @Override
  811.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  812.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  813.                 }

  814.             };

  815.         }

  816.         /** {@inheritDoc} */
  817.         @Override
  818.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  819.                                                       final TimeScales timeScales) {

  820.             // Earth Rotation Angle
  821.             final StellarAngleCapitaine era =
  822.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  823.             // Polynomial part of the apparent sidereal time series
  824.             // which is the opposite of Equation of Origins (EO)
  825.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  826.             final PoissonSeriesParser parser =
  827.                     new PoissonSeriesParser(17).
  828.                         withFirstDelaunay(4).
  829.                         withFirstPlanetary(9).
  830.                         withSinCos(0, 2, microAS, 3, microAS).
  831.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  832.             final PolynomialNutation minusEO = load(GST_SERIES, in -> parser.parse(in, GST_SERIES).getPolynomial());

  833.             // create a function evaluating the series
  834.             return new TimeScalarFunction() {

  835.                 /** {@inheritDoc} */
  836.                 @Override
  837.                 public double value(final AbsoluteDate date) {
  838.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  839.                 }

  840.                 /** {@inheritDoc} */
  841.                 @Override
  842.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  843.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  844.                 }

  845.             };

  846.         }

  847.         /** {@inheritDoc} */
  848.         @Override
  849.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  850.                                                   final EOPHistory eopHistory,
  851.                                                   final TimeScales timeScales) {

  852.             // set up nutation arguments
  853.             final FundamentalNutationArguments arguments =
  854.                     getNutationArguments(timeScales);

  855.             // mean obliquity function
  856.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

  857.             // set up Poisson series
  858.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  859.             final PoissonSeriesParser luniSolarPsiParser =
  860.                     new PoissonSeriesParser(14).
  861.                         withFirstDelaunay(1).
  862.                         withSinCos(0, 7, milliAS, 11, milliAS).
  863.                         withSinCos(1, 8, milliAS, 12, milliAS);
  864.             final PoissonSeries psiLuniSolarSeries =
  865.                             load(LUNI_SOLAR_SERIES, in -> luniSolarPsiParser.parse(in, LUNI_SOLAR_SERIES));

  866.             final PoissonSeriesParser planetaryPsiParser =
  867.                     new PoissonSeriesParser(21).
  868.                         withFirstDelaunay(2).
  869.                         withFirstPlanetary(7).
  870.                         withSinCos(0, 17, milliAS, 18, milliAS);
  871.             final PoissonSeries psiPlanetarySeries =
  872.                             load(PLANETARY_SERIES, in -> planetaryPsiParser.parse(in, PLANETARY_SERIES));


  873.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  874.             final PoissonSeriesParser gstParser =
  875.                     new PoissonSeriesParser(17).
  876.                         withFirstDelaunay(4).
  877.                         withFirstPlanetary(9).
  878.                         withSinCos(0, 2, microAS, 3, microAS).
  879.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  880.             final PoissonSeries gstSeries = load(GST_SERIES, in -> gstParser.parse(in, GST_SERIES));
  881.             final PoissonSeries.CompiledSeries psiGstSeries =
  882.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  883.             // ERA function
  884.             final TimeScalarFunction era =
  885.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  886.             return new TimeScalarFunction() {

  887.                 /** {@inheritDoc} */
  888.                 @Override
  889.                 public double value(final AbsoluteDate date) {

  890.                     // evaluate equation of origins
  891.                     final BodiesElements elements = arguments.evaluateAll(date);
  892.                     final double[] angles = psiGstSeries.value(elements);
  893.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  894.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  895.                     final double epsilonA = epsilon.value(date);

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

  899.                 }

  900.                 /** {@inheritDoc} */
  901.                 @Override
  902.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  903.                     // evaluate equation of origins
  904.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  905.                     final T[] angles = psiGstSeries.value(elements);
  906.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  907.                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
  908.                     final T epsilonA = epsilon.value(date);

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

  912.                 }

  913.             };

  914.         }

  915.         /** {@inheritDoc} */
  916.         @Override
  917.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  925.             // set up Poisson series
  926.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  927.             final PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
  928.                     withOptionalColumn(1).
  929.                     withGamma(2).
  930.                     withFirstDelaunay(3);
  931.             final double microS = 1.0e-6;
  932.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  933.                             withOptionalColumn(1).
  934.                             withGamma(2).
  935.                             withFirstDelaunay(3);
  936.             final PoissonSeries xSeries =
  937.                             load(TIDAL_CORRECTION_XP_YP_SERIES, in -> xyParser.
  938.                                                                       withSinCos(0, 10, microAS, 11, microAS).
  939.                                                                       parse(in, TIDAL_CORRECTION_XP_YP_SERIES));
  940.             final PoissonSeries ySeries =
  941.                             load(TIDAL_CORRECTION_XP_YP_SERIES, in -> xyParser.
  942.                                                                       withSinCos(0, 12, microAS, 13, microAS).
  943.                                                                       parse(in, TIDAL_CORRECTION_XP_YP_SERIES));

  944.             final PoissonSeries ut1Series =
  945.                             load(TIDAL_CORRECTION_UT1_SERIES, in -> ut1Parser.
  946.                                                                     withSinCos(0, 10, microS, 11, microS).
  947.                                                                     parse(in, TIDAL_CORRECTION_UT1_SERIES));

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

  949.         }

  950.         /** {@inheritDoc} */
  951.         public LoveNumbers getLoveNumbers() {
  952.             return loadLoveNumbers(LOVE_NUMBERS);
  953.         }

  954.         /** {@inheritDoc} */
  955.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  956.                                                                      final TimeScales timeScales) {

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

  959.             // set up Poisson series
  960.             final PoissonSeriesParser k20Parser =
  961.                     new PoissonSeriesParser(18).
  962.                         withOptionalColumn(1).
  963.                         withDoodson(4, 2).
  964.                         withFirstDelaunay(10);
  965.             final PoissonSeriesParser k21Parser =
  966.                     new PoissonSeriesParser(18).
  967.                         withOptionalColumn(1).
  968.                         withDoodson(4, 3).
  969.                         withFirstDelaunay(10);
  970.             final PoissonSeriesParser k22Parser =
  971.                     new PoissonSeriesParser(16).
  972.                         withOptionalColumn(1).
  973.                         withDoodson(4, 2).
  974.                         withFirstDelaunay(10);

  975.             final double pico = 1.0e-12;
  976.             final PoissonSeries c20Series =
  977.                             load(K20_FREQUENCY_DEPENDENCE, in -> k20Parser.
  978.                                                                  withSinCos(0, 18, -pico, 16, pico).
  979.                                                                  parse(in, K20_FREQUENCY_DEPENDENCE));
  980.             final PoissonSeries c21Series =
  981.                             load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  982.                                                                  withSinCos(0, 17, pico, 18, pico).
  983.                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  984.             final PoissonSeries s21Series =
  985.                             load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  986.                                                                  withSinCos(0, 18, -pico, 17, pico).
  987.                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  988.             final PoissonSeries c22Series =
  989.                             load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  990.                                                                  withSinCos(0, -1, pico, 16, pico).
  991.                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));
  992.             final PoissonSeries s22Series =
  993.                             load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  994.                                                                  withSinCos(0, 16, -pico, -1, pico).
  995.                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));

  996.             return new TideFrequencyDependenceFunction(arguments,
  997.                                                        c20Series,
  998.                                                        c21Series, s21Series,
  999.                                                        c22Series, s22Series);

  1000.         }

  1001.         /** {@inheritDoc} */
  1002.         @Override
  1003.         public double getPermanentTide() {
  1004.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1005.         }

  1006.         /** {@inheritDoc} */
  1007.         @Override
  1008.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1009.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  1010.             final TimeScale utc = eopHistory.getTimeScales().getUTC();
  1011.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  1012.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  1013.                     /** {@inheritDoc} */
  1014.                     @Override
  1015.                     public MeanPole convert(final double[] rawFields) {
  1016.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  1017.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  1018.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  1019.                     }
  1020.                 };
  1021.             final SimpleTimeStampedTableParser<MeanPole> parser =
  1022.                     new SimpleTimeStampedTableParser<>(3, converter);
  1023.             final List<MeanPole> annualPoleList = load(ANNUAL_POLE, in -> parser.parse(in, ANNUAL_POLE));
  1024.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  1025.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  1026.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  1027.                     new ImmutableTimeStampedCache<>(2, annualPoleList);

  1028.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  1029.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  1030.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  1031.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  1032.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  1033.             // constants from IERS 2003, section 6.2
  1034.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1035.             final double ratio        =  0.0115;

  1036.             return new TimeVectorFunction() {

  1037.                 /** {@inheritDoc} */
  1038.                 @Override
  1039.                 public double[] value(final AbsoluteDate date) {

  1040.                     // we can't compute anything before the range covered by the annual pole file
  1041.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  1042.                         return new double[] {
  1043.                             0.0, 0.0
  1044.                         };
  1045.                     }

  1046.                     // evaluate mean pole
  1047.                     double meanPoleX = 0;
  1048.                     double meanPoleY = 0;
  1049.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  1050.                         // we are within the range covered by the annual pole file,
  1051.                         // we interpolate within it
  1052.                         try {
  1053.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  1054.                             annualCache.getNeighbors(date).forEach(neighbor ->
  1055.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  1056.                                                             new double[] {
  1057.                                                                 neighbor.getX(), neighbor.getY()
  1058.                                                             }));
  1059.                             final double[] interpolated = interpolator.value(0);
  1060.                             meanPoleX = interpolated[0];
  1061.                             meanPoleY = interpolated[1];
  1062.                         } catch (TimeStampedCacheException tsce) {
  1063.                             // this should never happen
  1064.                             throw new OrekitInternalError(tsce);
  1065.                         }
  1066.                     } else {

  1067.                         // we are after the range covered by the annual pole file,
  1068.                         // we use the polynomial extension
  1069.                         final double t = date.durationFrom(
  1070.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1071.                         meanPoleX = xp0 + t * xp0Dot;
  1072.                         meanPoleY = yp0 + t * yp0Dot;

  1073.                     }

  1074.                     // evaluate wobble variables
  1075.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1076.                     final double m1 = correction.getXp() - meanPoleX;
  1077.                     final double m2 = meanPoleY - correction.getYp();

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

  1108.                 }

  1109.                 /** {@inheritDoc} */
  1110.                 @Override
  1111.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1117.                     // evaluate mean pole
  1118.                     T meanPoleX = date.getField().getZero();
  1119.                     T meanPoleY = date.getField().getZero();
  1120.                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
  1121.                         // we are within the range covered by the annual pole file,
  1122.                         // we interpolate within it
  1123.                         try {
  1124.                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
  1125.                             final T[] y = MathArrays.buildArray(date.getField(), 2);
  1126.                             final T zero = date.getField().getZero();
  1127.                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
  1128.                                                                                                        // for example removing derivatives
  1129.                                                                                                        // if T was DerivativeStructure
  1130.                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
  1131.                                 y[0] = zero.newInstance(neighbor.getX());
  1132.                                 y[1] = zero.newInstance(neighbor.getY());
  1133.                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
  1134.                             });
  1135.                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
  1136.                             meanPoleX = interpolated[0];
  1137.                             meanPoleY = interpolated[1];
  1138.                         } catch (TimeStampedCacheException tsce) {
  1139.                             // this should never happen
  1140.                             throw new OrekitInternalError(tsce);
  1141.                         }
  1142.                     } else {

  1143.                         // we are after the range covered by the annual pole file,
  1144.                         // we use the polynomial extension
  1145.                         final T t = date.durationFrom(
  1146.                                 eopHistory.getTimeScales().getJ2000Epoch());
  1147.                         meanPoleX = t.multiply(xp0Dot).add(xp0);
  1148.                         meanPoleY = t.multiply(yp0Dot).add(yp0);

  1149.                     }

  1150.                     // evaluate wobble variables
  1151.                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1152.                     final T m1 = correction.getXp().subtract(meanPoleX);
  1153.                     final T m2 = meanPoleY.subtract(correction.getYp());

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

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

  1183.                     return a;

  1184.                 }

  1185.             };

  1186.         }

  1187.         /** {@inheritDoc} */
  1188.         @Override
  1189.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1190.             return new TimeVectorFunction() {

  1191.                 /** {@inheritDoc} */
  1192.                 @Override
  1193.                 public double[] value(final AbsoluteDate date) {
  1194.                     // there are no model for ocean pole tide prior to conventions 2010
  1195.                     return new double[] {
  1196.                         0.0, 0.0
  1197.                     };
  1198.                 }

  1199.                 /** {@inheritDoc} */
  1200.                 @Override
  1201.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1202.                     // there are no model for ocean pole tide prior to conventions 2010
  1203.                     return MathArrays.buildArray(date.getField(), 2);
  1204.                 }

  1205.             };
  1206.         }

  1207.         /** {@inheritDoc} */
  1208.         @Override
  1209.         public double[] getNominalTidalDisplacement() {
  1210.             return new double[] {
  1211.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1212.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1213.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1214.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1215.                 // H₀
  1216.                 -0.31460
  1217.             };
  1218.         }

  1219.         /** {@inheritDoc} */
  1220.         @Override
  1221.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1222.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1223.                                                                   18, 15, 16, 17, 18);
  1224.         }

  1225.         /** {@inheritDoc} */
  1226.         @Override
  1227.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1228.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1229.                                                                 18, 15, 16, 17, 18);
  1230.         }

  1231.     },

  1232.     /** Constant for IERS 2010 conventions. */
  1233.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1264.         /** {@inheritDoc} */
  1265.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale,
  1266.                                                                  final TimeScales timeScales) {
  1267.             return load(NUTATION_ARGUMENTS, in -> new FundamentalNutationArguments(this, timeScale,
  1268.                                                                                             in, NUTATION_ARGUMENTS,
  1269.                                                                                             timeScales));
  1270.         }

  1271.         /** {@inheritDoc} */
  1272.         @Override
  1273.         public TimeScalarFunction getMeanObliquityFunction(final TimeScales timeScales) {

  1274.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  1275.             final PolynomialNutation epsilonA =
  1276.                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  1277.                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  1278.                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  1279.                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  1280.                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  1281.                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  1282.             return new TimeScalarFunction() {

  1283.                 /** {@inheritDoc} */
  1284.                 @Override
  1285.                 public double value(final AbsoluteDate date) {
  1286.                     return epsilonA.value(evaluateTC(date, timeScales));
  1287.                 }

  1288.                 /** {@inheritDoc} */
  1289.                 @Override
  1290.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1291.                     return epsilonA.value(evaluateTC(date, timeScales));
  1292.                 }

  1293.             };

  1294.         }

  1295.         /** {@inheritDoc} */
  1296.         @Override
  1297.         public TimeVectorFunction getXYSpXY2Function(final TimeScales timeScales) {

  1298.             // set up nutation arguments
  1299.             final FundamentalNutationArguments arguments =
  1300.                     getNutationArguments(timeScales);

  1301.             // set up Poisson series
  1302.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1303.             final PoissonSeriesParser parser =
  1304.                     new PoissonSeriesParser(17).
  1305.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1306.                         withFirstDelaunay(4).
  1307.                         withFirstPlanetary(9).
  1308.                         withSinCos(0, 2, microAS, 3, microAS);
  1309.             final PoissonSeries                xSeries = load(X_SERIES, in -> parser.parse(in, X_SERIES));
  1310.             final PoissonSeries                ySeries = load(Y_SERIES, in -> parser.parse(in, Y_SERIES));
  1311.             final PoissonSeries                sSeries = load(S_SERIES, in -> parser.parse(in, S_SERIES));
  1312.             final PoissonSeries.CompiledSeries xys     = PoissonSeries.compile(xSeries, ySeries, sSeries);

  1313.             // create a function evaluating the series
  1314.             return new TimeVectorFunction() {

  1315.                 /** {@inheritDoc} */
  1316.                 @Override
  1317.                 public double[] value(final AbsoluteDate date) {
  1318.                     return xys.value(arguments.evaluateAll(date));
  1319.                 }

  1320.                 /** {@inheritDoc} */
  1321.                 @Override
  1322.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1323.                     return xys.value(arguments.evaluateAll(date));
  1324.                 }

  1325.             };

  1326.         }

  1327.         /** {@inheritDoc} */
  1328.         public LoveNumbers getLoveNumbers() {
  1329.             return loadLoveNumbers(LOVE_NUMBERS);
  1330.         }

  1331.         /** {@inheritDoc} */
  1332.         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1,
  1333.                                                                      final TimeScales timeScales) {

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

  1336.             // set up Poisson series
  1337.             final PoissonSeriesParser k20Parser =
  1338.                     new PoissonSeriesParser(18).
  1339.                         withOptionalColumn(1).
  1340.                         withDoodson(4, 2).
  1341.                         withFirstDelaunay(10);
  1342.             final PoissonSeriesParser k21Parser =
  1343.                     new PoissonSeriesParser(18).
  1344.                         withOptionalColumn(1).
  1345.                         withDoodson(4, 3).
  1346.                         withFirstDelaunay(10);
  1347.             final PoissonSeriesParser k22Parser =
  1348.                     new PoissonSeriesParser(16).
  1349.                         withOptionalColumn(1).
  1350.                         withDoodson(4, 2).
  1351.                         withFirstDelaunay(10);

  1352.             final double pico = 1.0e-12;
  1353.             final PoissonSeries c20Series = load(K20_FREQUENCY_DEPENDENCE, in -> k20Parser.
  1354.                                                                                  withSinCos(0, 18, -pico, 16, pico).
  1355.                                                                                  parse(in, K20_FREQUENCY_DEPENDENCE));
  1356.             final PoissonSeries c21Series = load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  1357.                                                                                  withSinCos(0, 17, pico, 18, pico).
  1358.                                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  1359.             final PoissonSeries s21Series = load(K21_FREQUENCY_DEPENDENCE, in -> k21Parser.
  1360.                                                                                  withSinCos(0, 18, -pico, 17, pico).
  1361.                                                                                  parse(in, K21_FREQUENCY_DEPENDENCE));
  1362.             final PoissonSeries c22Series = load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  1363.                                                                                  withSinCos(0, -1, pico, 16, pico).
  1364.                                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));
  1365.             final PoissonSeries s22Series = load(K22_FREQUENCY_DEPENDENCE, in -> k22Parser.
  1366.                                                                                  withSinCos(0, 16, -pico, -1, pico).
  1367.                                                                                  parse(in, K22_FREQUENCY_DEPENDENCE));

  1368.             return new TideFrequencyDependenceFunction(arguments,
  1369.                                                        c20Series,
  1370.                                                        c21Series, s21Series,
  1371.                                                        c22Series, s22Series);

  1372.         }

  1373.         /** {@inheritDoc} */
  1374.         @Override
  1375.         public double getPermanentTide() {
  1376.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1377.         }

  1378.         /** Compute pole wobble variables m₁ and m₂.
  1379.          * @param date current date
  1380.          * @param eopHistory EOP history
  1381.          * @return array containing m₁ and m₂
  1382.          */
  1383.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1384.             // polynomial model from IERS 2010, table 7.7
  1385.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1386.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1387.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1388.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1389.             final AbsoluteDate changeDate =
  1390.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

  1391.             // evaluate mean pole
  1392.             final double[] xPolynomial;
  1393.             final double[] yPolynomial;
  1394.             if (date.compareTo(changeDate) <= 0) {
  1395.                 xPolynomial = new double[] {
  1396.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1397.                 };
  1398.                 yPolynomial = new double[] {
  1399.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1400.                 };
  1401.             } else {
  1402.                 xPolynomial = new double[] {
  1403.                     23.513 * f0, 7.6141 * f1
  1404.                 };
  1405.                 yPolynomial = new double[] {
  1406.                     358.891 * f0,  -0.6287 * f1
  1407.                 };
  1408.             }
  1409.             double meanPoleX = 0;
  1410.             double meanPoleY = 0;
  1411.             final double t = date.durationFrom(
  1412.                     eopHistory.getTimeScales().getJ2000Epoch());
  1413.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1414.                 meanPoleX = meanPoleX * t + xPolynomial[i];
  1415.             }
  1416.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1417.                 meanPoleY = meanPoleY * t + yPolynomial[i];
  1418.             }

  1419.             // evaluate wobble variables
  1420.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1421.             final double m1 = correction.getXp() - meanPoleX;
  1422.             final double m2 = meanPoleY - correction.getYp();

  1423.             return new double[] {
  1424.                 m1, m2
  1425.             };

  1426.         }

  1427.         /** Compute pole wobble variables m₁ and m₂.
  1428.          * @param date current date
  1429.          * @param <T> type of the field elements
  1430.          * @param eopHistory EOP history
  1431.          * @return array containing m₁ and m₂
  1432.          */
  1433.         private <T extends CalculusFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {

  1434.             // polynomial model from IERS 2010, table 7.7
  1435.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1436.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1437.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1438.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1439.             final AbsoluteDate changeDate =
  1440.                     new AbsoluteDate(2010, 1, 1, eopHistory.getTimeScales().getTT());

  1441.             // evaluate mean pole
  1442.             final double[] xPolynomial;
  1443.             final double[] yPolynomial;
  1444.             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
  1445.                 xPolynomial = new double[] {
  1446.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1447.                 };
  1448.                 yPolynomial = new double[] {
  1449.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1450.                 };
  1451.             } else {
  1452.                 xPolynomial = new double[] {
  1453.                     23.513 * f0, 7.6141 * f1
  1454.                 };
  1455.                 yPolynomial = new double[] {
  1456.                     358.891 * f0,  -0.6287 * f1
  1457.                 };
  1458.             }
  1459.             T meanPoleX = date.getField().getZero();
  1460.             T meanPoleY = date.getField().getZero();
  1461.             final T t = date.durationFrom(
  1462.                     eopHistory.getTimeScales().getJ2000Epoch());
  1463.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1464.                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
  1465.             }
  1466.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1467.                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
  1468.             }

  1469.             // evaluate wobble variables
  1470.             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
  1471.             final T[] m = MathArrays.buildArray(date.getField(), 2);
  1472.             m[0] = correction.getXp().subtract(meanPoleX);
  1473.             m[1] = meanPoleY.subtract(correction.getYp());

  1474.             return m;

  1475.         }

  1476.         /** {@inheritDoc} */
  1477.         @Override
  1478.         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {

  1479.             // constants from IERS 2010, section 6.4
  1480.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1481.             final double ratio        =  0.0115;

  1482.             return new TimeVectorFunction() {

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

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

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

  1500.                 }

  1501.                 /** {@inheritDoc} */
  1502.                 @Override
  1503.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

  1507.                     // the following correspond to the equations published in IERS 2010 conventions,
  1508.                     // section 6.4 page 94. The equations read:
  1509.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1510.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1511.                     // These equations seem to fix what was probably a sign error in IERS 2003
  1512.                     // conventions section 6.2 page 65. In this older publication, the equations read:
  1513.                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1514.                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1515.                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
  1516.                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);

  1517.                     return a;

  1518.                 }

  1519.             };

  1520.         }

  1521.         /** {@inheritDoc} */
  1522.         @Override
  1523.         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {

  1524.             return new TimeVectorFunction() {

  1525.                 /** {@inheritDoc} */
  1526.                 @Override
  1527.                 public double[] value(final AbsoluteDate date) {

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

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

  1538.                 }

  1539.                 /** {@inheritDoc} */
  1540.                 @Override
  1541.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

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

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

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

  1551.                     return v;

  1552.                 }

  1553.             };

  1554.         }

  1555.         /** {@inheritDoc} */
  1556.         @Override
  1557.         public TimeVectorFunction getPrecessionFunction(final TimeScales timeScales) {

  1558.             // set up the conventional polynomials
  1559.             // the following values are from equation 5.40 in IERS 2010 conventions
  1560.             final PolynomialNutation psiA =
  1561.                     new PolynomialNutation(   0.0,
  1562.                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1563.                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1564.                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1565.                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1566.                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1567.             final PolynomialNutation omegaA =
  1568.                     new PolynomialNutation(getMeanObliquityFunction(timeScales)
  1569.                             .value(getNutationReferenceEpoch(timeScales)),
  1570.                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1571.                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1572.                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1573.                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1574.                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1575.             final PolynomialNutation chiA =
  1576.                     new PolynomialNutation( 0.0,
  1577.                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1578.                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1579.                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1580.                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1581.                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

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

  1583.         }

  1584.         /** {@inheritDoc} */
  1585.         @Override
  1586.         public TimeVectorFunction getNutationFunction(final TimeScales timeScales) {

  1587.             // set up nutation arguments
  1588.             final FundamentalNutationArguments arguments =
  1589.                     getNutationArguments(timeScales);

  1590.             // set up Poisson series
  1591.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1592.             final PoissonSeriesParser parser =
  1593.                     new PoissonSeriesParser(17).
  1594.                         withFirstDelaunay(4).
  1595.                         withFirstPlanetary(9).
  1596.                         withSinCos(0, 2, microAS, 3, microAS);
  1597.             final PoissonSeries psiSeries     = load(PSI_SERIES,     in -> parser.parse(in, PSI_SERIES));
  1598.             final PoissonSeries epsilonSeries = load(EPSILON_SERIES, in -> parser.parse(in, EPSILON_SERIES));
  1599.             final PoissonSeries.CompiledSeries psiEpsilonSeries = PoissonSeries.compile(psiSeries, epsilonSeries);

  1600.             return new TimeVectorFunction() {

  1601.                 /** {@inheritDoc} */
  1602.                 @Override
  1603.                 public double[] value(final AbsoluteDate date) {
  1604.                     final BodiesElements elements = arguments.evaluateAll(date);
  1605.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1606.                     return new double[] {
  1607.                         psiEpsilon[0], psiEpsilon[1],
  1608.                         IAU1994ResolutionC7.value(elements, timeScales.getTAI())
  1609.                     };
  1610.                 }

  1611.                 /** {@inheritDoc} */
  1612.                 @Override
  1613.                 public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  1614.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1615.                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
  1616.                     final T[] result = MathArrays.buildArray(date.getField(), 3);
  1617.                     result[0] = psiEpsilon[0];
  1618.                     result[1] = psiEpsilon[1];
  1619.                     result[2] = IAU1994ResolutionC7.value(elements, timeScales.getTAI());
  1620.                     return result;
  1621.                 }

  1622.             };

  1623.         }

  1624.         /** {@inheritDoc} */
  1625.         @Override
  1626.         public TimeScalarFunction getGMSTFunction(final TimeScale ut1,
  1627.                                                   final TimeScales timeScales) {

  1628.             // Earth Rotation Angle
  1629.             final StellarAngleCapitaine era =
  1630.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  1631.             // Polynomial part of the apparent sidereal time series
  1632.             // which is the opposite of Equation of Origins (EO)
  1633.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1634.             final PoissonSeriesParser parser =
  1635.                     new PoissonSeriesParser(17).
  1636.                         withFirstDelaunay(4).
  1637.                         withFirstPlanetary(9).
  1638.                         withSinCos(0, 2, microAS, 3, microAS).
  1639.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1640.             final PolynomialNutation minusEO = load(GST_SERIES, in ->  parser.parse(in, GST_SERIES).getPolynomial());

  1641.             // create a function evaluating the series
  1642.             return new TimeScalarFunction() {

  1643.                 /** {@inheritDoc} */
  1644.                 @Override
  1645.                 public double value(final AbsoluteDate date) {
  1646.                     return era.value(date) + minusEO.value(evaluateTC(date, timeScales));
  1647.                 }

  1648.                 /** {@inheritDoc} */
  1649.                 @Override
  1650.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1651.                     return era.value(date).add(minusEO.value(evaluateTC(date, timeScales)));
  1652.                 }

  1653.             };

  1654.         }

  1655.         /** {@inheritDoc} */
  1656.         @Override
  1657.         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1,
  1658.                                                       final TimeScales timeScales) {

  1659.             // Earth Rotation Angle
  1660.             final StellarAngleCapitaine era =
  1661.                     new StellarAngleCapitaine(ut1, timeScales.getTAI());

  1662.             // Polynomial part of the apparent sidereal time series
  1663.             // which is the opposite of Equation of Origins (EO)
  1664.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1665.             final PoissonSeriesParser parser =
  1666.                     new PoissonSeriesParser(17).
  1667.                         withFirstDelaunay(4).
  1668.                         withFirstPlanetary(9).
  1669.                         withSinCos(0, 2, microAS, 3, microAS).
  1670.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1671.             final PolynomialNutation minusEO = load(GST_SERIES, in -> parser.parse(in, GST_SERIES).getPolynomial());

  1672.             // create a function evaluating the series
  1673.             return new TimeScalarFunction() {

  1674.                 /** {@inheritDoc} */
  1675.                 @Override
  1676.                 public double value(final AbsoluteDate date) {
  1677.                     return era.getRate() + minusEO.derivative(evaluateTC(date, timeScales));
  1678.                 }

  1679.                 /** {@inheritDoc} */
  1680.                 @Override
  1681.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
  1682.                     return minusEO.derivative(evaluateTC(date, timeScales)).add(era.getRate());
  1683.                 }

  1684.             };

  1685.         }

  1686.         /** {@inheritDoc} */
  1687.         @Override
  1688.         public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  1689.                                                   final EOPHistory eopHistory,
  1690.                                                   final TimeScales timeScales) {

  1691.             // set up nutation arguments
  1692.             final FundamentalNutationArguments arguments =
  1693.                     getNutationArguments(timeScales);

  1694.             // mean obliquity function
  1695.             final TimeScalarFunction epsilon = getMeanObliquityFunction(timeScales);

  1696.             // set up Poisson series
  1697.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1698.             final PoissonSeriesParser baseParser =
  1699.                     new PoissonSeriesParser(17).
  1700.                         withFirstDelaunay(4).
  1701.                         withFirstPlanetary(9).
  1702.                         withSinCos(0, 2, microAS, 3, microAS);
  1703.             final PoissonSeriesParser          gstParser    = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1704.             final PoissonSeries                psiSeries    = load(PSI_SERIES, in -> baseParser.parse(in, PSI_SERIES));
  1705.             final PoissonSeries                gstSeries    = load(GST_SERIES, in -> gstParser.parse(in, GST_SERIES));
  1706.             final PoissonSeries.CompiledSeries psiGstSeries = PoissonSeries.compile(psiSeries, gstSeries);

  1707.             // ERA function
  1708.             final TimeScalarFunction era =
  1709.                     getEarthOrientationAngleFunction(ut1, timeScales.getTAI());

  1710.             return new TimeScalarFunction() {

  1711.                 /** {@inheritDoc} */
  1712.                 @Override
  1713.                 public double value(final AbsoluteDate date) {

  1714.                     // evaluate equation of origins
  1715.                     final BodiesElements elements = arguments.evaluateAll(date);
  1716.                     final double[] angles = psiGstSeries.value(elements);
  1717.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1718.                     final double deltaPsi = angles[0] + ddPsi;
  1719.                     final double epsilonA = epsilon.value(date);

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

  1723.                 }

  1724.                 /** {@inheritDoc} */
  1725.                 @Override
  1726.                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  1727.                     // evaluate equation of origins
  1728.                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  1729.                     final T[] angles = psiGstSeries.value(elements);
  1730.                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
  1731.                     final T deltaPsi = angles[0].add(ddPsi);
  1732.                     final T epsilonA = epsilon.value(date);

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

  1736.                 }

  1737.             };

  1738.         }

  1739.         /** {@inheritDoc} */
  1740.         @Override
  1741.         public TimeVectorFunction getEOPTidalCorrection(final TimeScales timeScales) {

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

  1749.             // set up Poisson series
  1750.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1751.             final PoissonSeriesParser xyParser  = new PoissonSeriesParser(13).
  1752.                                                   withOptionalColumn(1).
  1753.                                                   withGamma(2).
  1754.                                                   withFirstDelaunay(3);
  1755.             final double microS = 1.0e-6;
  1756.             final PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
  1757.                                                   withOptionalColumn(1).
  1758.                                                   withGamma(2).
  1759.                                                   withFirstDelaunay(3);
  1760.             final PoissonSeries xSeries =
  1761.                             load(TIDAL_CORRECTION_XP_YP_SERIES, xpIn -> xyParser.
  1762.                                                                         withSinCos(0, 10, microAS, 11, microAS).
  1763.                                                                         parse(xpIn, TIDAL_CORRECTION_XP_YP_SERIES));
  1764.             final PoissonSeries ySeries =
  1765.                             load(TIDAL_CORRECTION_XP_YP_SERIES, ypIn -> xyParser.
  1766.                                                                         withSinCos(0, 12, microAS, 13, microAS).
  1767.                                                                         parse(ypIn, TIDAL_CORRECTION_XP_YP_SERIES));
  1768.             final PoissonSeries ut1Series =
  1769.                             load(TIDAL_CORRECTION_UT1_SERIES, ut1In -> ut1Parser.
  1770.                                                                        withSinCos(0, 10, microS, 11, microS).
  1771.                                                                        parse(ut1In, TIDAL_CORRECTION_UT1_SERIES));

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

  1773.         }

  1774.         /** {@inheritDoc} */
  1775.         @Override
  1776.         public double[] getNominalTidalDisplacement() {
  1777.             return new double[] {
  1778.                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
  1779.                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
  1780.                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
  1781.                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
  1782.                 // H₀
  1783.                 -0.31460
  1784.             };
  1785.         }

  1786.         /** {@inheritDoc} */
  1787.         @Override
  1788.         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
  1789.             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
  1790.                                                                   18, 15, 16, 17, 18);
  1791.         }

  1792.         /** {@inheritDoc} */
  1793.         @Override
  1794.         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
  1795.             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
  1796.                                                                 18, 15, 16, 17, 18);
  1797.         }

  1798.     };

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

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

  1803.     /** Get the reference epoch for fundamental nutation arguments.
  1804.      *
  1805.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1806.      *
  1807.      * @return reference epoch for fundamental nutation arguments
  1808.      * @since 6.1
  1809.      * @see #getNutationReferenceEpoch(TimeScales)
  1810.      */
  1811.     @DefaultDataContext
  1812.     public AbsoluteDate getNutationReferenceEpoch() {
  1813.         return getNutationReferenceEpoch(DataContext.getDefault().getTimeScales());
  1814.     }

  1815.     /**
  1816.      * Get the reference epoch for fundamental nutation arguments.
  1817.      *
  1818.      * @param timeScales to use for the reference epoch.
  1819.      * @return reference epoch for fundamental nutation arguments
  1820.      * @since 10.1
  1821.      */
  1822.     public AbsoluteDate getNutationReferenceEpoch(final TimeScales timeScales) {
  1823.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1824.         return timeScales.getJ2000Epoch();
  1825.     }

  1826.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1827.      *
  1828.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1829.      *
  1830.      * @param date current date
  1831.      * @return date offset in Julian centuries
  1832.      * @since 6.1
  1833.      * @see #evaluateTC(AbsoluteDate, TimeScales)
  1834.      */
  1835.     @DefaultDataContext
  1836.     public double evaluateTC(final AbsoluteDate date) {
  1837.         return evaluateTC(date, DataContext.getDefault().getTimeScales());
  1838.     }

  1839.     /**
  1840.      * Evaluate the date offset between the current date and the {@link
  1841.      * #getNutationReferenceEpoch() reference date}.
  1842.      *
  1843.      * @param date       current date
  1844.      * @param timeScales used in the evaluation.
  1845.      * @return date offset in Julian centuries
  1846.      * @since 10.1
  1847.      */
  1848.     public double evaluateTC(final AbsoluteDate date, final TimeScales timeScales) {
  1849.         return date.durationFrom(getNutationReferenceEpoch(timeScales)) /
  1850.                 Constants.JULIAN_CENTURY;
  1851.     }

  1852.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1853.      *
  1854.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1855.      *
  1856.      * @param date current date
  1857.      * @param <T> type of the field elements
  1858.      * @return date offset in Julian centuries
  1859.      * @since 9.0
  1860.      * @see #evaluateTC(FieldAbsoluteDate, TimeScales)
  1861.      */
  1862.     @DefaultDataContext
  1863.     public <T extends CalculusFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
  1864.         return evaluateTC(date, DataContext.getDefault().getTimeScales());
  1865.     }

  1866.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1867.      * @param <T> type of the field elements
  1868.      * @param date current date
  1869.      * @param timeScales used in the evaluation.
  1870.      * @return date offset in Julian centuries
  1871.      * @since 10.1
  1872.      */
  1873.     public <T extends CalculusFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date,
  1874.                                                         final TimeScales timeScales) {
  1875.         return date.durationFrom(getNutationReferenceEpoch(timeScales))
  1876.                 .divide(Constants.JULIAN_CENTURY);
  1877.     }

  1878.     /**
  1879.      * Get the fundamental nutation arguments. Does not compute GMST based values: gamma,
  1880.      * gammaDot.
  1881.      *
  1882.      * @param timeScales other time scales used in the computation including TAI and TT.
  1883.      * @return fundamental nutation arguments
  1884.      * @see #getNutationArguments(TimeScale, TimeScales)
  1885.      * @since 10.1
  1886.      */
  1887.     protected FundamentalNutationArguments getNutationArguments(
  1888.             final TimeScales timeScales) {

  1889.         return getNutationArguments(null, timeScales);
  1890.     }

  1891.     /** Get the fundamental nutation arguments.
  1892.      *
  1893.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1894.      *
  1895.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  1896.      * (typically {@link TimeScales#getUT1(IERSConventions, boolean) UT1})
  1897.      * @return fundamental nutation arguments
  1898.      * @since 6.1
  1899.      * @see #getNutationArguments(TimeScale, TimeScales)
  1900.      * @see #getNutationArguments(TimeScales)
  1901.      */
  1902.     @DefaultDataContext
  1903.     public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
  1904.         return getNutationArguments(timeScale, DataContext.getDefault().getTimeScales());
  1905.     }

  1906.     /**
  1907.      * Get the fundamental nutation arguments.
  1908.      *
  1909.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time (typically
  1910.      *                  {@link TimeScales#getUT1(IERSConventions, boolean) UT1})
  1911.      * @param timeScales other time scales used in the computation including TAI and TT.
  1912.      * @return fundamental nutation arguments
  1913.      * @since 10.1
  1914.      */
  1915.     public abstract FundamentalNutationArguments getNutationArguments(
  1916.             TimeScale timeScale,
  1917.             TimeScales timeScales);

  1918.     /** Get the function computing mean obliquity of the ecliptic.
  1919.      *
  1920.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1921.      *
  1922.      * @return function computing mean obliquity of the ecliptic
  1923.      * @since 6.1
  1924.      * @see #getMeanObliquityFunction(TimeScales)
  1925.      */
  1926.     @DefaultDataContext
  1927.     public TimeScalarFunction getMeanObliquityFunction() {
  1928.         return getMeanObliquityFunction(DataContext.getDefault().getTimeScales());
  1929.     }

  1930.     /**
  1931.      * Get the function computing mean obliquity of the ecliptic.
  1932.      *
  1933.      * @param timeScales used in computing the function.
  1934.      * @return function computing mean obliquity of the ecliptic
  1935.      * @since 10.1
  1936.      */
  1937.     public abstract TimeScalarFunction getMeanObliquityFunction(TimeScales timeScales);

  1938.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  1939.      * <p>
  1940.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  1941.      * </p>
  1942.      *
  1943.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1944.      *
  1945.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  1946.      * @since 6.1
  1947.      * @see #getXYSpXY2Function(TimeScales)
  1948.      */
  1949.     @DefaultDataContext
  1950.     public TimeVectorFunction getXYSpXY2Function() {
  1951.         return getXYSpXY2Function(DataContext.getDefault().getTimeScales());
  1952.     }

  1953.     /**
  1954.      * Get the function computing the Celestial Intermediate Pole and Celestial
  1955.      * Intermediate Origin components.
  1956.      * <p>
  1957.      * The returned function computes the two X, Y components of CIP and the S+XY/2
  1958.      * component of the non-rotating CIO.
  1959.      * </p>
  1960.      *
  1961.      * @param timeScales used to define the function.
  1962.      * @return function computing the Celestial Intermediate Pole and Celestial
  1963.      * Intermediate Origin components
  1964.      * @since 10.1
  1965.      */
  1966.     public abstract TimeVectorFunction getXYSpXY2Function(TimeScales timeScales);

  1967.     /** Get the function computing the raw Earth Orientation Angle.
  1968.      *
  1969.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1970.      *
  1971.      * <p>
  1972.      * The raw angle does not contain any correction. If for example dTU1 correction
  1973.      * due to tidal effect is desired, it must be added afterward by the caller.
  1974.      * The returned value contain the angle as the value and the angular rate as
  1975.      * the first derivative.
  1976.      * </p>
  1977.      * @param ut1 UT1 time scale
  1978.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  1979.      * @since 6.1
  1980.      * @see #getEarthOrientationAngleFunction(TimeScale, TimeScale)
  1981.      */
  1982.     @DefaultDataContext
  1983.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
  1984.         return getEarthOrientationAngleFunction(ut1,
  1985.                 DataContext.getDefault().getTimeScales().getTAI());
  1986.     }

  1987.     /** Get the function computing the raw Earth Orientation Angle.
  1988.      * <p>
  1989.      * The raw angle does not contain any correction. If for example dTU1 correction
  1990.      * due to tidal effect is desired, it must be added afterward by the caller.
  1991.      * The returned value contain the angle as the value and the angular rate as
  1992.      * the first derivative.
  1993.      * </p>
  1994.      * @param ut1 UT1 time scale
  1995.      * @param tai TAI time scale
  1996.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
  1997.      * @since 10.1
  1998.      */
  1999.     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1,
  2000.                                                                final TimeScale tai) {
  2001.         return new StellarAngleCapitaine(ut1, tai);
  2002.     }

  2003.     /** Get the function computing the precession angles.
  2004.      * <p>
  2005.      * The function returned computes the three precession angles
  2006.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  2007.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  2008.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  2009.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  2010.      * #getNutationReferenceEpoch() nutation reference epoch}.
  2011.      * </p>
  2012.      *
  2013.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2014.      *
  2015.      * @return function computing the precession angle
  2016.      * @since 6.1
  2017.      * @see #getPrecessionFunction(TimeScales)
  2018.      */
  2019.     @DefaultDataContext
  2020.     public TimeVectorFunction getPrecessionFunction()
  2021.     {
  2022.         return getPrecessionFunction(DataContext.getDefault().getTimeScales());
  2023.     }

  2024.     /** Get the function computing the precession angles.
  2025.      * <p>
  2026.      * The function returned computes the three precession angles
  2027.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  2028.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  2029.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  2030.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  2031.      * #getNutationReferenceEpoch() nutation reference epoch}.
  2032.      * </p>
  2033.      * @return function computing the precession angle
  2034.      * @since 10.1
  2035.      * @param timeScales used to define the function.
  2036.      */
  2037.     public abstract TimeVectorFunction getPrecessionFunction(TimeScales timeScales);

  2038.     /** Get the function computing the nutation angles.
  2039.      *
  2040.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2041.      *
  2042.      * <p>
  2043.      * The function returned computes the two classical angles ΔΨ and Δε,
  2044.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  2045.      * resolution C7 (the correction is forced to 0 before this date)
  2046.      * </p>
  2047.      * @return function computing the nutation in longitude ΔΨ and Δε
  2048.      * and the correction of equation of equinoxes
  2049.      * @since 6.1
  2050.      */
  2051.     @DefaultDataContext
  2052.     public TimeVectorFunction getNutationFunction() {
  2053.         return getNutationFunction(DataContext.getDefault().getTimeScales());
  2054.     }

  2055.     /** Get the function computing the nutation angles.
  2056.      * <p>
  2057.      * The function returned computes the two classical angles ΔΨ and Δε,
  2058.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  2059.      * resolution C7 (the correction is forced to 0 before this date)
  2060.      * </p>
  2061.      * @return function computing the nutation in longitude ΔΨ and Δε
  2062.      * and the correction of equation of equinoxes
  2063.      * @param timeScales used in the computation including TAI and TT.
  2064.      * @since 10.1
  2065.      */
  2066.     public abstract TimeVectorFunction getNutationFunction(TimeScales timeScales);

  2067.     /** Get the function computing Greenwich mean sidereal time, in radians.
  2068.      *
  2069.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2070.      *
  2071.      * @param ut1 UT1 time scale
  2072.      * @return function computing Greenwich mean sidereal time
  2073.      * @since 6.1
  2074.      * @see #getGMSTFunction(TimeScale, TimeScales)
  2075.      */
  2076.     @DefaultDataContext
  2077.     public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
  2078.         return getGMSTFunction(ut1, DataContext.getDefault().getTimeScales());
  2079.     }

  2080.     /**
  2081.      * Get the function computing Greenwich mean sidereal time, in radians.
  2082.      *
  2083.      * @param ut1 UT1 time scale
  2084.      * @param timeScales other time scales used in the computation including TAI and TT.
  2085.      * @return function computing Greenwich mean sidereal time
  2086.      * @since 10.1
  2087.      */
  2088.     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1,
  2089.                                                        TimeScales timeScales);

  2090.     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
  2091.      *
  2092.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2093.      *
  2094.      * @param ut1 UT1 time scale
  2095.      * @return function computing Greenwich mean sidereal time rate
  2096.      * @since 9.0
  2097.      * @see #getGMSTRateFunction(TimeScale, TimeScales)
  2098.      */
  2099.     @DefaultDataContext
  2100.     public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {
  2101.         return getGMSTRateFunction(ut1,
  2102.                 DataContext.getDefault().getTimeScales());
  2103.     }

  2104.     /**
  2105.      * Get the function computing Greenwich mean sidereal time rate, in radians per
  2106.      * second.
  2107.      *
  2108.      * @param ut1 UT1 time scale
  2109.      * @param timeScales other time scales used in the computation including TAI and TT.
  2110.      * @return function computing Greenwich mean sidereal time rate
  2111.      * @since 10.1
  2112.      */
  2113.     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1,
  2114.                                                            TimeScales timeScales);

  2115.     /**
  2116.      * Get the function computing Greenwich apparent sidereal time, in radians.
  2117.      *
  2118.      * <p>This method uses the {@link DataContext#getDefault() default data context} if
  2119.      * {@code eopHistory == null}.
  2120.      *
  2121.      * @param ut1        UT1 time scale
  2122.      * @param eopHistory EOP history. If {@code null} then no nutation correction is
  2123.      *                   applied for EOP.
  2124.      * @return function computing Greenwich apparent sidereal time
  2125.      * @since 6.1
  2126.      * @see #getGASTFunction(TimeScale, EOPHistory, TimeScales)
  2127.      */
  2128.     @DefaultDataContext
  2129.     public TimeScalarFunction getGASTFunction(final TimeScale ut1,
  2130.                                               final EOPHistory eopHistory) {
  2131.         final TimeScales timeScales = eopHistory != null ?
  2132.                 eopHistory.getTimeScales() :
  2133.                 DataContext.getDefault().getTimeScales();
  2134.         return getGASTFunction(ut1, eopHistory, timeScales);
  2135.     }

  2136.     /**
  2137.      * Get the function computing Greenwich apparent sidereal time, in radians.
  2138.      *
  2139.      * @param ut1        UT1 time scale
  2140.      * @param eopHistory EOP history. If {@code null} then no nutation correction is
  2141.      *                   applied for EOP.
  2142.      * @param timeScales        TAI time scale.
  2143.      * @return function computing Greenwich apparent sidereal time
  2144.      * @since 10.1
  2145.      */
  2146.     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1,
  2147.                                                        EOPHistory eopHistory,
  2148.                                                        TimeScales timeScales);

  2149.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  2150.      *
  2151.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2152.      *
  2153.      * @return function computing tidal corrections for Earth Orientation Parameters,
  2154.      * for xp, yp, ut1 and lod respectively
  2155.      * @since 6.1
  2156.      * @see #getEOPTidalCorrection(TimeScales)
  2157.      */
  2158.     @DefaultDataContext
  2159.     public TimeVectorFunction getEOPTidalCorrection() {
  2160.         return getEOPTidalCorrection(DataContext.getDefault().getTimeScales());
  2161.     }

  2162.     /**
  2163.      * Get the function computing tidal corrections for Earth Orientation Parameters.
  2164.      *
  2165.      * @param timeScales used in the computation. The TT and TAI scales are used.
  2166.      * @return function computing tidal corrections for Earth Orientation Parameters, for
  2167.      * xp, yp, ut1 and lod respectively
  2168.      * @since 10.1
  2169.      */
  2170.     public abstract TimeVectorFunction getEOPTidalCorrection(TimeScales timeScales);

  2171.     /** Get the Love numbers.
  2172.      * @return Love numbers
  2173.           * @since 6.1
  2174.      */
  2175.     public abstract LoveNumbers getLoveNumbers();

  2176.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2177.      *
  2178.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2179.      *
  2180.      * @param ut1 UT1 time scale
  2181.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2182.      * @since 6.1
  2183.      * @see #getTideFrequencyDependenceFunction(TimeScale, TimeScales)
  2184.      */
  2185.     @DefaultDataContext
  2186.     public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {
  2187.         return getTideFrequencyDependenceFunction(ut1,
  2188.                 DataContext.getDefault().getTimeScales());
  2189.     }

  2190.     /**
  2191.      * Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2192.      * ΔS₂₂).
  2193.      *
  2194.      * @param ut1 UT1 time scale
  2195.      * @param timeScales other time scales used in the computation including TAI and TT.
  2196.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂,
  2197.      * ΔS₂₂).
  2198.      * @since 10.1
  2199.      */
  2200.     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(
  2201.             TimeScale ut1,
  2202.             TimeScales timeScales);

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

  2207.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  2208.      * @param eopHistory EOP history
  2209.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2210.           * @since 6.1
  2211.      */
  2212.     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory);

  2213.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  2214.      * @param eopHistory EOP history
  2215.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  2216.           * @since 6.1
  2217.      */
  2218.     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory);

  2219.     /** Get the nominal values of the displacement numbers.
  2220.      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
  2221.      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
  2222.      * H₀ permanent deformation amplitude
  2223.      * @since 9.1
  2224.      */
  2225.     public abstract double[] getNominalTidalDisplacement();

  2226.     /** Get the correction function for tidal displacement for diurnal tides.
  2227.      * <ul>
  2228.      *  <li>f[0]: radial correction, longitude cosine part</li>
  2229.      *  <li>f[1]: radial correction, longitude sine part</li>
  2230.      *  <li>f[2]: North correction, longitude cosine part</li>
  2231.      *  <li>f[3]: North correction, longitude sine part</li>
  2232.      *  <li>f[4]: East correction, longitude cosine part</li>
  2233.      *  <li>f[5]: East correction, longitude sine part</li>
  2234.      * </ul>
  2235.      * @return correction function for tidal displacement
  2236.      * @since 9.1
  2237.      */
  2238.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal();

  2239.     /** Get the correction function for tidal displacement for diurnal tides.
  2240.      * <ul>
  2241.      *  <li>f[0]: radial correction, longitude cosine part</li>
  2242.      *  <li>f[1]: radial correction, longitude sine part</li>
  2243.      *  <li>f[2]: North correction, longitude cosine part</li>
  2244.      *  <li>f[3]: North correction, longitude sine part</li>
  2245.      *  <li>f[4]: East correction, longitude cosine part</li>
  2246.      *  <li>f[5]: East correction, longitude sine part</li>
  2247.      * </ul>
  2248.      * @param tableName name for the diurnal tides table
  2249.      * @param cols total number of columns of the diurnal tides table
  2250.      * @param rIp column holding ∆Rf(ip) in the diurnal tides table, counting from 1
  2251.      * @param rOp column holding ∆Rf(op) in the diurnal tides table, counting from 1
  2252.      * @param tIp column holding ∆Tf(ip) in the diurnal tides table, counting from 1
  2253.      * @param tOp column holding ∆Tf(op) in the diurnal tides table, counting from 1
  2254.      * @return correction function for tidal displacement for diurnal tides
  2255.           * @since 9.1
  2256.      */
  2257.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal(final String tableName, final int cols,
  2258.                                                                                    final int rIp, final int rOp,
  2259.                                                                                    final int tIp, final int tOp) {

  2260.         // radial component, missing the sin 2φ factor; this corresponds to:
  2261.         //  - equation 15a in IERS conventions 1996, chapter 7
  2262.         //  - equation 16a in IERS conventions 2003, chapter 7
  2263.         //  - equation 7.12a in IERS conventions 2010, chapter 7
  2264.         final PoissonSeries drCos = load(tableName, in -> new PoissonSeriesParser(cols).
  2265.                                                           withOptionalColumn(1).
  2266.                                                           withDoodson(4, 3).
  2267.                                                           withFirstDelaunay(10).
  2268.                                                           withSinCos(0, rIp, +1.0e-3, rOp, +1.0e-3).
  2269.                                                           parse(in, tableName));
  2270.         final PoissonSeries drSin = load(tableName, in -> new PoissonSeriesParser(cols).
  2271.                                                           withOptionalColumn(1).
  2272.                                                           withDoodson(4, 3).
  2273.                                                           withFirstDelaunay(10).
  2274.                                                           withSinCos(0, rOp, -1.0e-3, rIp, +1.0e-3).
  2275.                                                           parse(in, tableName));

  2276.         // North component, missing the cos 2φ factor; this corresponds to:
  2277.         //  - equation 15b in IERS conventions 1996, chapter 7
  2278.         //  - equation 16b in IERS conventions 2003, chapter 7
  2279.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  2280.         final PoissonSeries dnCos = load(tableName, in -> new PoissonSeriesParser(cols).
  2281.                                                           withOptionalColumn(1).
  2282.                                                           withDoodson(4, 3).
  2283.                                                           withFirstDelaunay(10).
  2284.                                                           withSinCos(0, tIp, +1.0e-3, tOp, +1.0e-3).
  2285.                                                           parse(in, tableName));
  2286.         final PoissonSeries dnSin = load(tableName, in -> new PoissonSeriesParser(cols).
  2287.                                                           withOptionalColumn(1).
  2288.                                                           withDoodson(4, 3).
  2289.                                                           withFirstDelaunay(10).
  2290.                                                           withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2291.                                                           parse(in, tableName));

  2292.         // East component, missing the sin φ factor; this corresponds to:
  2293.         //  - equation 15b in IERS conventions 1996, chapter 7
  2294.         //  - equation 16b in IERS conventions 2003, chapter 7
  2295.         //  - equation 7.12b in IERS conventions 2010, chapter 7
  2296.         final PoissonSeries deCos = load(tableName, in -> new PoissonSeriesParser(cols).
  2297.                                                           withOptionalColumn(1).
  2298.                                                           withDoodson(4, 3).
  2299.                                                           withFirstDelaunay(10).
  2300.                                                           withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
  2301.                                                           parse(in, tableName));
  2302.         final PoissonSeries deSin = load(tableName, in -> new PoissonSeriesParser(cols).
  2303.                                                           withOptionalColumn(1).
  2304.                                                           withDoodson(4, 3).
  2305.                                                           withFirstDelaunay(10).
  2306.                                                           withSinCos(0, tIp, -1.0e-3, tOp, -1.0e-3).
  2307.                                                           parse(in, tableName));

  2308.         return PoissonSeries.compile(drCos, drSin, dnCos, dnSin, deCos, deSin);

  2309.     }

  2310.     /** Get the correction function for tidal displacement for zonal tides.
  2311.      * <ul>
  2312.      *  <li>f[0]: radial correction</li>
  2313.      *  <li>f[1]: North correction</li>
  2314.      * </ul>
  2315.      * @return correction function for tidal displacement
  2316.      * @since 9.1
  2317.      */
  2318.     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal();

  2319.     /** Get the correction function for tidal displacement for zonal tides.
  2320.      * <ul>
  2321.      *  <li>f[0]: radial correction</li>
  2322.      *  <li>f[1]: North correction</li>
  2323.      * </ul>
  2324.      * @param tableName name for the zonal tides table
  2325.      * @param cols total number of columns of the table
  2326.      * @param rIp column holding ∆Rf(ip) in the table, counting from 1
  2327.      * @param rOp column holding ∆Rf(op) in the table, counting from 1
  2328.      * @param tIp column holding ∆Tf(ip) in the table, counting from 1
  2329.      * @param tOp column holding ∆Tf(op) in the table, counting from 1
  2330.      * @return correction function for tidal displacement for zonal tides
  2331.           * @since 9.1
  2332.      */
  2333.     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionZonal(final String tableName, final int cols,
  2334.                                                                                  final int rIp, final int rOp,
  2335.                                                                                  final int tIp, final int tOp) {

  2336.         // radial component, missing the 3⁄2 sin² φ - 1⁄2 factor; this corresponds to:
  2337.         //  - equation 16a in IERS conventions 1996, chapter 7
  2338.         //  - equation 17a in IERS conventions 2003, chapter 7
  2339.         //  - equation 7.13a in IERS conventions 2010, chapter 7
  2340.         final PoissonSeries dr = load(tableName, in -> new PoissonSeriesParser(cols).
  2341.                                                        withOptionalColumn(1).
  2342.                                                        withDoodson(4, 3).
  2343.                                                        withFirstDelaunay(10).
  2344.                                                        withSinCos(0, rOp, +1.0e-3, rIp, +1.0e-3).
  2345.                                                        parse(in, tableName));

  2346.         // North component, missing the sin 2φ factor; this corresponds to:
  2347.         //  - equation 16b in IERS conventions 1996, chapter 7
  2348.         //  - equation 17b in IERS conventions 2003, chapter 7
  2349.         //  - equation 7.13b in IERS conventions 2010, chapter 7
  2350.         final PoissonSeries dn = load(tableName, in -> new PoissonSeriesParser(cols).
  2351.                                                        withOptionalColumn(1).
  2352.                                                        withDoodson(4, 3).
  2353.                                                        withFirstDelaunay(10).
  2354.                                                        withSinCos(0, tOp, +1.0e-3, tIp, +1.0e-3).
  2355.                                                        parse(in, tableName));

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

  2357.     }

  2358.     /** Interface for functions converting nutation corrections between
  2359.      * δΔψ/δΔε to δX/δY.
  2360.      * <ul>
  2361.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2362.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2363.      * </ul>
  2364.      * @since 6.1
  2365.      */
  2366.     public interface NutationCorrectionConverter {

  2367.         /** Convert nutation corrections.
  2368.          * @param date current date
  2369.          * @param ddPsi δΔψ part of the nutation correction
  2370.          * @param ddEpsilon δΔε part of the nutation correction
  2371.          * @return array containing δX and δY
  2372.          */
  2373.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon);

  2374.         /** Convert nutation corrections.
  2375.          * @param date current date
  2376.          * @param dX δX part of the nutation correction
  2377.          * @param dY δY part of the nutation correction
  2378.          * @return array containing δΔψ and δΔε
  2379.          */
  2380.         double[] toEquinox(AbsoluteDate date, double dX, double dY);

  2381.     }

  2382.     /** Create a function converting nutation corrections between
  2383.      * δX/δY and δΔψ/δΔε.
  2384.      * <ul>
  2385.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2386.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2387.      * </ul>
  2388.      *
  2389.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  2390.      *
  2391.      * @return a new converter
  2392.      * @since 6.1
  2393.      * @see #getNutationCorrectionConverter(TimeScales)
  2394.      */
  2395.     @DefaultDataContext
  2396.     public NutationCorrectionConverter getNutationCorrectionConverter() {
  2397.         return getNutationCorrectionConverter(DataContext.getDefault().getTimeScales());
  2398.     }

  2399.     /** Create a function converting nutation corrections between
  2400.      * δX/δY and δΔψ/δΔε.
  2401.      * <ul>
  2402.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  2403.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  2404.      * </ul>
  2405.      * @return a new converter
  2406.      * @since 10.1
  2407.      * @param timeScales used to define the conversion.
  2408.      */
  2409.     public NutationCorrectionConverter getNutationCorrectionConverter(
  2410.             final TimeScales timeScales) {

  2411.         // get models parameters
  2412.         final TimeVectorFunction precessionFunction = getPrecessionFunction(timeScales);
  2413.         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction(timeScales);
  2414.         final AbsoluteDate date0 = getNutationReferenceEpoch(timeScales);
  2415.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  2416.         return new NutationCorrectionConverter() {

  2417.             /** {@inheritDoc} */
  2418.             @Override
  2419.             public double[] toNonRotating(final AbsoluteDate date,
  2420.                                           final double ddPsi, final double ddEpsilon) {
  2421.                 // compute precession angles psiA, omegaA and chiA
  2422.                 final double[] angles = precessionFunction.value(date);

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

  2426.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  2427.                 return new double[] {
  2428.                     sinEA * ddPsi + c * ddEpsilon,
  2429.                     ddEpsilon - c * sinEA * ddPsi
  2430.                 };

  2431.             }

  2432.             /** {@inheritDoc} */
  2433.             @Override
  2434.             public double[] toEquinox(final AbsoluteDate date,
  2435.                                       final double dX, final double dY) {
  2436.                 // compute precession angles psiA, omegaA and chiA
  2437.                 final double[] angles   = precessionFunction.value(date);

  2438.                 // conversion coefficients
  2439.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  2440.                 final double c     = angles[0] * cosE0 - angles[2];
  2441.                 final double opC2  = 1 + c * c;

  2442.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  2443.                 return new double[] {
  2444.                     (dX - c * dY) / (sinEA * opC2),
  2445.                     (dY + c * dX) / opC2
  2446.                 };
  2447.             }

  2448.         };

  2449.     }

  2450.     /** Load the Love numbers.
  2451.      * @param nameLove name of the Love number resource
  2452.      * @return Love numbers
  2453.      */
  2454.     protected LoveNumbers loadLoveNumbers(final String nameLove) {
  2455.         try {

  2456.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  2457.             final double[][] real      = new double[4][];
  2458.             final double[][] imaginary = new double[4][];
  2459.             final double[][] plus      = new double[4][];
  2460.             for (int i = 0; i < real.length; ++i) {
  2461.                 real[i]      = new double[i + 1];
  2462.                 imaginary[i] = new double[i + 1];
  2463.                 plus[i]      = new double[i + 1];
  2464.             }

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

  2466.                 if (stream == null) {
  2467.                     // this should never happen with files embedded within Orekit
  2468.                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  2469.                 }

  2470.                 int lineNumber = 1;
  2471.                 String line = null;
  2472.                 // setup the reader
  2473.                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {

  2474.                     line = reader.readLine();

  2475.                     // look for the Love numbers
  2476.                     while (line != null) {

  2477.                         line = line.trim();
  2478.                         if (!(line.isEmpty() || line.startsWith("#"))) {
  2479.                             final String[] fields = SEPARATOR.split(line);
  2480.                             if (fields.length != 5) {
  2481.                                 // this should never happen with files embedded within Orekit
  2482.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2483.                                                           lineNumber, nameLove, line);
  2484.                             }
  2485.                             final int n = Integer.parseInt(fields[0]);
  2486.                             final int m = Integer.parseInt(fields[1]);
  2487.                             if (n < 2 || n > 3 || m < 0 || m > n) {
  2488.                                 // this should never happen with files embedded within Orekit
  2489.                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2490.                                                           lineNumber, nameLove, line);

  2491.                             }
  2492.                             real[n][m]      = Double.parseDouble(fields[2]);
  2493.                             imaginary[n][m] = Double.parseDouble(fields[3]);
  2494.                             plus[n][m]      = Double.parseDouble(fields[4]);
  2495.                         }

  2496.                         // next line
  2497.                         lineNumber++;
  2498.                         line = reader.readLine();

  2499.                     }
  2500.                 } catch (NumberFormatException nfe) {
  2501.                     // this should never happen with files embedded within Orekit
  2502.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  2503.                                               lineNumber, nameLove, line);
  2504.                 }
  2505.             }

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

  2507.         } catch (IOException ioe) {
  2508.             // this should never happen with files embedded within Orekit
  2509.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  2510.         }
  2511.     }

  2512.     /** Load resources.
  2513.      * @param name name of the resource
  2514.      * @param loader loaader for the resource
  2515.      * @param <T> type of the processed data
  2516.      * @return processed data
  2517.      */
  2518.     private static <T> T load(final String name, final Function<InputStream, T> loader) {
  2519.         try (InputStream is = IERSConventions.class.getResourceAsStream(name)) {
  2520.             return loader.apply(is);
  2521.         } catch (IOException ioe) {
  2522.             // this should never happen with internal streams
  2523.             throw new OrekitException(OrekitMessages.INTERNAL_ERROR, ioe);
  2524.         }
  2525.     }

  2526.     /** Correction to equation of equinoxes.
  2527.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  2528.      * taking effect since 1997-02-27 for continuity
  2529.      * </p>
  2530.      */
  2531.     private static class IAU1994ResolutionC7 {

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

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


  2536.         /** Evaluate the correction.
  2537.          * @param arguments Delaunay for nutation
  2538.          * @param tai TAI time scale.
  2539.          * @return correction value (0 before 1997-02-27)
  2540.          */
  2541.         public static double value(final DelaunayArguments arguments,
  2542.                                    final TimeScale tai) {
  2543.             /* Start date for applying Moon corrections to the equation of the equinoxes.
  2544.              * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
  2545.              */
  2546.             final AbsoluteDate modelStart = new AbsoluteDate(1997, 2, 27, 0, 0, 30, tai);
  2547.             if (arguments.getDate().compareTo(modelStart) >= 0) {

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

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

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

  2554.             } else {
  2555.                 return 0.0;
  2556.             }
  2557.         }

  2558.         /** Evaluate the correction.
  2559.          * @param arguments Delaunay for nutation
  2560.          * @param tai TAI time scale.
  2561.          * @param <T> type of the field elements
  2562.          * @return correction value (0 before 1997-02-27)
  2563.          */
  2564.         public static <T extends CalculusFieldElement<T>> T value(
  2565.                 final FieldDelaunayArguments<T> arguments,
  2566.                 final TimeScale tai) {
  2567.             /* Start date for applying Moon corrections to the equation of the equinoxes.
  2568.              * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
  2569.              */
  2570.             final AbsoluteDate modelStart = new AbsoluteDate(1997, 2, 27, 0, 0, 30, tai);
  2571.             if (arguments.getDate().toAbsoluteDate().compareTo(modelStart) >= 0) {

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

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

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

  2578.             } else {
  2579.                 return arguments.getDate().getField().getZero();
  2580.             }
  2581.         }

  2582.     }

  2583.     /** Stellar angle model.
  2584.      * <p>
  2585.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  2586.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  2587.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  2588.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  2589.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  2590.      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
  2591.      * </p>
  2592.      * <p>
  2593.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  2594.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  2595.      * IERS conventions 2003 and 2010.
  2596.      * </p>
  2597.      */
  2598.     private static class StellarAngleCapitaine implements TimeScalarFunction {

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

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

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

  2607.         /** UT1 time scale. */
  2608.         private final TimeScale ut1;

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

  2611.         /** Simple constructor.
  2612.          * @param ut1 UT1 time scale
  2613.          * @param tai TAI time scale
  2614.          */
  2615.         StellarAngleCapitaine(final TimeScale ut1, final TimeScale tai) {
  2616.             this.ut1 = ut1;
  2617.             referenceDate = new AbsoluteDate(
  2618.                     DateComponents.J2000_EPOCH,
  2619.                     TimeComponents.H12,
  2620.                     tai);
  2621.         }

  2622.         /** Get the rotation rate.
  2623.          * @return rotation rate
  2624.          */
  2625.         public double getRate() {
  2626.             return ERA_1A + ERA_1B;
  2627.         }

  2628.         /** {@inheritDoc} */
  2629.         @Override
  2630.         public double value(final AbsoluteDate date) {

  2631.             // split the date offset as a full number of days plus a smaller part
  2632.             final int secondsInDay = 86400;
  2633.             final double dt  = date.durationFrom(referenceDate);
  2634.             final long days  = ((long) dt) / secondsInDay;
  2635.             final double dtA = secondsInDay * days;
  2636.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date).toDouble();

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

  2638.         }

  2639.         /** {@inheritDoc} */
  2640.         @Override
  2641.         public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {

  2642.             // split the date offset as a full number of days plus a smaller part
  2643.             final int secondsInDay = 86400;
  2644.             final T dt  = date.durationFrom(referenceDate);
  2645.             final long days  = ((long) dt.getReal()) / secondsInDay;
  2646.             final double dtA = secondsInDay * days;
  2647.             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()).toDouble());

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

  2649.         }

  2650.     }

  2651.     /** Mean pole. */
  2652.     private static class MeanPole implements TimeStamped, Serializable {

  2653.         /** Serializable UID. */
  2654.         private static final long serialVersionUID = 20131028L;

  2655.         /** Date. */
  2656.         private final AbsoluteDate date;

  2657.         /** X coordinate. */
  2658.         private final double x;

  2659.         /** Y coordinate. */
  2660.         private final double y;

  2661.         /** Simple constructor.
  2662.          * @param date date
  2663.          * @param x x coordinate
  2664.          * @param y y coordinate
  2665.          */
  2666.         MeanPole(final AbsoluteDate date, final double x, final double y) {
  2667.             this.date = date;
  2668.             this.x    = x;
  2669.             this.y    = y;
  2670.         }

  2671.         /** {@inheritDoc} */
  2672.         @Override
  2673.         public AbsoluteDate getDate() {
  2674.             return date;
  2675.         }

  2676.         /** Get x coordinate.
  2677.          * @return x coordinate
  2678.          */
  2679.         public double getX() {
  2680.             return x;
  2681.         }

  2682.         /** Get y coordinate.
  2683.          * @return y coordinate
  2684.          */
  2685.         public double getY() {
  2686.             return y;
  2687.         }

  2688.     }

  2689.     /** Local class for precession function. */
  2690.     private class PrecessionFunction implements TimeVectorFunction {

  2691.         /** Polynomial nutation for psiA. */
  2692.         private final PolynomialNutation psiA;

  2693.         /** Polynomial nutation for omegaA. */
  2694.         private final PolynomialNutation omegaA;

  2695.         /** Polynomial nutation for chiA. */
  2696.         private final PolynomialNutation chiA;

  2697.         /** Time scales to use. */
  2698.         private final TimeScales timeScales;

  2699.         /** Simple constructor.
  2700.          * @param psiA polynomial nutation for psiA
  2701.          * @param omegaA polynomial nutation for omegaA
  2702.          * @param chiA polynomial nutation for chiA
  2703.          * @param timeScales used in the computation.
  2704.          */
  2705.         PrecessionFunction(final PolynomialNutation psiA,
  2706.                            final PolynomialNutation omegaA,
  2707.                            final PolynomialNutation chiA,
  2708.                            final TimeScales timeScales) {
  2709.             this.psiA   = psiA;
  2710.             this.omegaA = omegaA;
  2711.             this.chiA   = chiA;
  2712.             this.timeScales = timeScales;
  2713.         }


  2714.         /** {@inheritDoc} */
  2715.         @Override
  2716.         public double[] value(final AbsoluteDate date) {
  2717.             final double tc = evaluateTC(date, timeScales);
  2718.             return new double[] {
  2719.                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  2720.             };
  2721.         }

  2722.         /** {@inheritDoc} */
  2723.         @Override
  2724.         public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2725.             final T[] a = MathArrays.buildArray(date.getField(), 3);
  2726.             final T tc = evaluateTC(date, timeScales);
  2727.             a[0] = psiA.value(tc);
  2728.             a[1] = omegaA.value(tc);
  2729.             a[2] = chiA.value(tc);
  2730.             return a;
  2731.         }

  2732.     }

  2733.     /** Local class for tides frequency function. */
  2734.     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {

  2735.         /** Nutation arguments. */
  2736.         private final FundamentalNutationArguments arguments;

  2737.         /** Correction series. */
  2738.         private final PoissonSeries.CompiledSeries kSeries;

  2739.         /** Simple constructor.
  2740.          * @param arguments nutation arguments
  2741.          * @param c20Series correction series for the C20 term
  2742.          * @param c21Series correction series for the C21 term
  2743.          * @param s21Series correction series for the S21 term
  2744.          * @param c22Series correction series for the C22 term
  2745.          * @param s22Series correction series for the S22 term
  2746.          */
  2747.         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
  2748.                                         final PoissonSeries c20Series,
  2749.                                         final PoissonSeries c21Series,
  2750.                                         final PoissonSeries s21Series,
  2751.                                         final PoissonSeries c22Series,
  2752.                                         final PoissonSeries s22Series) {
  2753.             this.arguments = arguments;
  2754.             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
  2755.         }


  2756.         /** {@inheritDoc} */
  2757.         @Override
  2758.         public double[] value(final AbsoluteDate date) {
  2759.             return kSeries.value(arguments.evaluateAll(date));
  2760.         }

  2761.         /** {@inheritDoc} */
  2762.         @Override
  2763.         public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
  2764.             return kSeries.value(arguments.evaluateAll(date));
  2765.         }

  2766.     }

  2767.     /** Local class for EOP tidal corrections. */
  2768.     private static class EOPTidalCorrection implements TimeVectorFunction {

  2769.         /** Nutation arguments. */
  2770.         private final FundamentalNutationArguments arguments;

  2771.         /** Correction series. */
  2772.         private final PoissonSeries.CompiledSeries correctionSeries;

  2773.         /** Simple constructor.
  2774.          * @param arguments nutation arguments
  2775.          * @param xSeries correction series for the x coordinate
  2776.          * @param ySeries correction series for the y coordinate
  2777.          * @param ut1Series correction series for the UT1
  2778.          */
  2779.         EOPTidalCorrection(final FundamentalNutationArguments arguments,
  2780.                            final PoissonSeries xSeries,
  2781.                            final PoissonSeries ySeries,
  2782.                            final PoissonSeries ut1Series) {
  2783.             this.arguments        = arguments;
  2784.             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
  2785.         }

  2786.         /** {@inheritDoc} */
  2787.         @Override
  2788.         public double[] value(final AbsoluteDate date) {
  2789.             final BodiesElements elements = arguments.evaluateAll(date);
  2790.             final double[] correction    = correctionSeries.value(elements);
  2791.             final double[] correctionDot = correctionSeries.derivative(elements);
  2792.             return new double[] {
  2793.                 correction[0],
  2794.                 correction[1],
  2795.                 correction[2],
  2796.                 correctionDot[2] * (-Constants.JULIAN_DAY)
  2797.             };
  2798.         }

  2799.         /** {@inheritDoc} */
  2800.         @Override
  2801.         public <T extends CalculusFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {

  2802.             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
  2803.             final T[] correction    = correctionSeries.value(elements);
  2804.             final T[] correctionDot = correctionSeries.derivative(elements);
  2805.             final T[] a = MathArrays.buildArray(date.getField(), 4);
  2806.             a[0] = correction[0];
  2807.             a[1] = correction[1];
  2808.             a[2] = correction[2];
  2809.             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
  2810.             return a;
  2811.         }

  2812.     }

  2813. }