IERSConventions.java

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

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

  24. import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
  25. import org.apache.commons.math3.analysis.interpolation.HermiteInterpolator;
  26. import org.apache.commons.math3.util.FastMath;
  27. import org.apache.commons.math3.util.MathUtils;
  28. import org.orekit.data.BodiesElements;
  29. import org.orekit.data.DelaunayArguments;
  30. import org.orekit.data.FieldBodiesElements;
  31. import org.orekit.data.FundamentalNutationArguments;
  32. import org.orekit.data.PoissonSeries;
  33. import org.orekit.data.PoissonSeriesParser;
  34. import org.orekit.data.PolynomialNutation;
  35. import org.orekit.data.PolynomialParser;
  36. import org.orekit.data.PolynomialParser.Unit;
  37. import org.orekit.data.SimpleTimeStampedTableParser;
  38. import org.orekit.errors.OrekitException;
  39. import org.orekit.errors.OrekitMessages;
  40. import org.orekit.errors.TimeStampedCacheException;
  41. import org.orekit.frames.EOPHistory;
  42. import org.orekit.frames.PoleCorrection;
  43. import org.orekit.time.AbsoluteDate;
  44. import org.orekit.time.DateComponents;
  45. import org.orekit.time.TimeComponents;
  46. import org.orekit.time.TimeFunction;
  47. import org.orekit.time.TimeScale;
  48. import org.orekit.time.TimeScalesFactory;
  49. import org.orekit.time.TimeStamped;


  50. /** Supported IERS conventions.
  51.  * @since 6.0
  52.  * @author Luc Maisonobe
  53.  */
  54. public enum IERSConventions {

  55.     /** Constant for IERS 1996 conventions. */
  56.     IERS_1996 {

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

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

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

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

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

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

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

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

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

  75.         /** {@inheritDoc} */
  76.         @Override
  77.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  78.             throws OrekitException {
  79.             return new FundamentalNutationArguments(this, timeScale,
  80.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  81.         }

  82.         /** {@inheritDoc} */
  83.         @Override
  84.         public TimeFunction<Double> getMeanObliquityFunction() throws OrekitException {

  85.             // value from chapter 5, page 22
  86.             final PolynomialNutation<DerivativeStructure> epsilonA =
  87.                     new PolynomialNutation<DerivativeStructure>(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  88.                                                                   -46.8150   * Constants.ARC_SECONDS_TO_RADIANS,
  89.                                                                    -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  90.                                                                     0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  91.             return new TimeFunction<Double>() {

  92.                 /** {@inheritDoc} */
  93.                 @Override
  94.                 public Double value(final AbsoluteDate date) {
  95.                     return epsilonA.value(evaluateTC(date));
  96.                 }

  97.             };

  98.         }

  99.         /** {@inheritDoc} */
  100.         @Override
  101.         public TimeFunction<double[]> getXYSpXY2Function()
  102.             throws OrekitException {

  103.             // set up nutation arguments
  104.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  105.             // X = 2004.3109″t - 0.42665″t² - 0.198656″t³ + 0.0000140″t⁴
  106.             //     + 0.00006″t² cos Ω + sin ε0 { Σ [(Ai + Ai' t) sin(ARGUMENT) + Ai'' t cos(ARGUMENT)]}
  107.             //     + 0.00204″t² sin Ω + 0.00016″t² sin 2(F - D + Ω),
  108.             final PolynomialNutation<DerivativeStructure> xPolynomial =
  109.                     new PolynomialNutation<DerivativeStructure>(0,
  110.                                                                 2004.3109 * Constants.ARC_SECONDS_TO_RADIANS,
  111.                                                                 -0.42665  * Constants.ARC_SECONDS_TO_RADIANS,
  112.                                                                 -0.198656 * Constants.ARC_SECONDS_TO_RADIANS,
  113.                                                                 0.0000140 * Constants.ARC_SECONDS_TO_RADIANS);

  114.             final double fXCosOm    = 0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  115.             final double fXSinOm    = 0.00204 * Constants.ARC_SECONDS_TO_RADIANS;
  116.             final double fXSin2FDOm = 0.00016 * Constants.ARC_SECONDS_TO_RADIANS;
  117.             final double sinEps0   = FastMath.sin(getMeanObliquityFunction().value(getNutationReferenceEpoch()));

  118.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  119.             final PoissonSeriesParser<DerivativeStructure> baseParser =
  120.                     new PoissonSeriesParser<DerivativeStructure>(12).withFirstDelaunay(1);

  121.             final PoissonSeriesParser<DerivativeStructure> xParser =
  122.                     baseParser.
  123.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  124.                     withSinCos(1, 8, deciMilliAS,  9, deciMilliAS);
  125.             final PoissonSeries<DerivativeStructure> xSum = xParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

  126.             // Y = -0.00013″ - 22.40992″t² + 0.001836″t³ + 0.0011130″t⁴
  127.             //     + Σ [(Bi + Bi' t) cos(ARGUMENT) + Bi'' t sin(ARGUMENT)]
  128.             //    - 0.00231″t² cos Ω − 0.00014″t² cos 2(F - D + Ω)
  129.             final PolynomialNutation<DerivativeStructure> yPolynomial =
  130.                     new PolynomialNutation<DerivativeStructure>(-0.00013  * Constants.ARC_SECONDS_TO_RADIANS,
  131.                                                                 0.0,
  132.                                                                 -22.40992 * Constants.ARC_SECONDS_TO_RADIANS,
  133.                                                                 0.001836  * Constants.ARC_SECONDS_TO_RADIANS,
  134.                                                                 0.0011130 * Constants.ARC_SECONDS_TO_RADIANS);

  135.             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
  136.             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;

  137.             final PoissonSeriesParser<DerivativeStructure> yParser =
  138.                     baseParser.
  139.                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
  140.                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
  141.             final PoissonSeries<DerivativeStructure> ySum = yParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);

  142.             @SuppressWarnings("unchecked")
  143.             final PoissonSeries.CompiledSeries<DerivativeStructure> xySum =
  144.                     PoissonSeries.compile(xSum, ySum);

  145.             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
  146.             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
  147.             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
  148.             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
  149.             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
  150.             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
  151.             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
  152.             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;

  153.             return new TimeFunction<double[]>() {

  154.                 /** {@inheritDoc} */
  155.                 @Override
  156.                 public double[] value(final AbsoluteDate date) {

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

  159.                     final double omega     = elements.getOmega();
  160.                     final double f         = elements.getF();
  161.                     final double d         = elements.getD();
  162.                     final double t         = elements.getTC();

  163.                     final double cosOmega  = FastMath.cos(omega);
  164.                     final double sinOmega  = FastMath.sin(omega);
  165.                     final double sin2Omega = FastMath.sin(2 * omega);
  166.                     final double cos2FDOm  = FastMath.cos(2 * (f - d + omega));
  167.                     final double sin2FDOm  = FastMath.sin(2 * (f - d + omega));

  168.                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
  169.                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
  170.                     final double y = yPolynomial.value(t) + xy[1] +
  171.                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
  172.                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
  173.                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));

  174.                     return new double[] {
  175.                         x, y, sPxy2
  176.                     };

  177.                 }

  178.             };

  179.         }

  180.         /** {@inheritDoc} */
  181.         @Override
  182.         public TimeFunction<double[]> getPrecessionFunction() throws OrekitException {

  183.             // set up the conventional polynomials
  184.             // the following values are from Lieske et al. paper:
  185.             // Expressions for the precession quantities based upon the IAU(1976) system of astronomical constants
  186.             // http://articles.adsabs.harvard.edu/full/1977A%26A....58....1L
  187.             // also available as equation 30 in IERS 2003 conventions
  188.             final PolynomialNutation<DerivativeStructure> psiA =
  189.                     new PolynomialNutation<DerivativeStructure>(   0.0,
  190.                                                                 5038.7784   * Constants.ARC_SECONDS_TO_RADIANS,
  191.                                                                   -1.07259  * Constants.ARC_SECONDS_TO_RADIANS,
  192.                                                                   -0.001147 * Constants.ARC_SECONDS_TO_RADIANS);
  193.             final PolynomialNutation<DerivativeStructure> omegaA =
  194.                     new PolynomialNutation<DerivativeStructure>(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  195.                                                                  0.0,
  196.                                                                  0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  197.                                                                 -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  198.             final PolynomialNutation<DerivativeStructure> chiA =
  199.                     new PolynomialNutation<DerivativeStructure>( 0.0,
  200.                                                                 10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  201.                                                                 -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  202.                                                                 -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  203.             return new TimeFunction<double[]>() {
  204.                 /** {@inheritDoc} */
  205.                 @Override
  206.                 public double[] value(final AbsoluteDate date) {
  207.                     final double tc = evaluateTC(date);
  208.                     return new double[] {
  209.                         psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  210.                     };
  211.                 }
  212.             };

  213.         }

  214.         /** {@inheritDoc} */
  215.         @Override
  216.         public TimeFunction<double[]> getNutationFunction()
  217.             throws OrekitException {

  218.             // set up nutation arguments
  219.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  220.             // set up Poisson series
  221.             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
  222.             final PoissonSeriesParser<DerivativeStructure> baseParser =
  223.                     new PoissonSeriesParser<DerivativeStructure>(10).withFirstDelaunay(1);

  224.             final PoissonSeriesParser<DerivativeStructure> psiParser =
  225.                     baseParser.
  226.                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
  227.                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
  228.             final PoissonSeries<DerivativeStructure> psiSeries = psiParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  229.             final PoissonSeriesParser<DerivativeStructure> epsilonParser =
  230.                     baseParser.
  231.                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
  232.                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
  233.             final PoissonSeries<DerivativeStructure> epsilonSeries = epsilonParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);

  234.             @SuppressWarnings("unchecked")
  235.             final PoissonSeries.CompiledSeries<DerivativeStructure> psiEpsilonSeries =
  236.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  237.             return new TimeFunction<double[]>() {
  238.                 /** {@inheritDoc} */
  239.                 @Override
  240.                 public double[] value(final AbsoluteDate date) {
  241.                     final BodiesElements elements = arguments.evaluateAll(date);
  242.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  243.                     return new double[] {
  244.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  245.                     };
  246.                 }
  247.             };

  248.         }

  249.         /** {@inheritDoc} */
  250.         @Override
  251.         public TimeFunction<DerivativeStructure> getGMSTFunction(final TimeScale ut1)
  252.             throws OrekitException {

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

  255.             // constants from IERS 1996 page 21
  256.             // the underlying model is IAU 1982 GMST-UT1
  257.             final AbsoluteDate gmstReference =
  258.                 new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
  259.             final double gmst0 = 24110.54841;
  260.             final double gmst1 = 8640184.812866;
  261.             final double gmst2 = 0.093104;
  262.             final double gmst3 = -6.2e-6;

  263.             return new TimeFunction<DerivativeStructure>() {

  264.                 /** {@inheritDoc} */
  265.                 @Override
  266.                 public DerivativeStructure value(final AbsoluteDate date) {

  267.                     // offset in Julian centuries from J2000 epoch (UT1 scale)
  268.                     final double dtai = date.durationFrom(gmstReference);
  269.                     final DerivativeStructure tut1 =
  270.                             new DerivativeStructure(1, 1, dtai + ut1.offsetFromTAI(date), 1.0);
  271.                     final DerivativeStructure tt = tut1.divide(Constants.JULIAN_CENTURY);

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

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

  277.                 }

  278.             };

  279.         }

  280.         /** {@inheritDoc} */
  281.         @Override
  282.         public TimeFunction<DerivativeStructure> getGASTFunction(final TimeScale ut1,
  283.                                                                  final EOPHistory eopHistory)
  284.             throws OrekitException {

  285.             // obliquity
  286.             final TimeFunction<Double> epsilonA = getMeanObliquityFunction();

  287.             // GMST function
  288.             final TimeFunction<DerivativeStructure> gmst = getGMSTFunction(ut1);

  289.             // nutation function
  290.             final TimeFunction<double[]> nutation = getNutationFunction();

  291.             return new TimeFunction<DerivativeStructure>() {

  292.                 /** {@inheritDoc} */
  293.                 @Override
  294.                 public DerivativeStructure value(final AbsoluteDate date) {

  295.                     // compute equation of equinoxes
  296.                     final double[] angles = nutation.value(date);
  297.                     double deltaPsi = angles[0];
  298.                     if (eopHistory != null) {
  299.                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
  300.                     }
  301.                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];

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

  304.                 }

  305.             };

  306.         }

  307.         /** {@inheritDoc} */
  308.         @Override
  309.         public TimeFunction<double[]> getEOPTidalCorrection()
  310.             throws OrekitException {

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

  317.             // set up Poisson series
  318.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  319.             final PoissonSeriesParser<DerivativeStructure> xyParser = new PoissonSeriesParser<DerivativeStructure>(17).
  320.                     withOptionalColumn(1).
  321.                     withGamma(7).
  322.                     withFirstDelaunay(2);
  323.             final PoissonSeries<DerivativeStructure> xSeries =
  324.                     xyParser.
  325.                     withSinCos(0, 14, milliAS, 15, milliAS).
  326.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  327.             final PoissonSeries<DerivativeStructure> ySeries =
  328.                     xyParser.
  329.                     withSinCos(0, 16, milliAS, 17, milliAS).
  330.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES),
  331.                                                          TIDAL_CORRECTION_XP_YP_SERIES);

  332.             final double deciMilliS = 1.0e-4;
  333.             final PoissonSeriesParser<DerivativeStructure> ut1Parser = new PoissonSeriesParser<DerivativeStructure>(17).
  334.                     withOptionalColumn(1).
  335.                     withGamma(7).
  336.                     withFirstDelaunay(2).
  337.                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
  338.             final PoissonSeries<DerivativeStructure> ut1Series =
  339.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  340.             @SuppressWarnings("unchecked")
  341.             final PoissonSeries.CompiledSeries<DerivativeStructure> correctionSeries =
  342.                 PoissonSeries.compile(xSeries, ySeries, ut1Series);

  343.             return new TimeFunction<double[]>() {
  344.                 /** {@inheritDoc} */
  345.                 @Override
  346.                 public double[] value(final AbsoluteDate date) {
  347.                     final FieldBodiesElements<DerivativeStructure> elements =
  348.                             arguments.evaluateDerivative(date);
  349.                     final DerivativeStructure[] correction = correctionSeries.value(elements);
  350.                     return new double[] {
  351.                         correction[0].getValue(),
  352.                         correction[1].getValue(),
  353.                         correction[2].getValue(),
  354.                         -correction[2].getPartialDerivative(1) * Constants.JULIAN_DAY
  355.                     };
  356.                 }
  357.             };

  358.         }

  359.         /** {@inheritDoc} */
  360.         public LoveNumbers getLoveNumbers() throws OrekitException {
  361.             return loadLoveNumbers(LOVE_NUMBERS);
  362.         }

  363.         /** {@inheritDoc} */
  364.         public TimeFunction<double[]> getTideFrequencyDependenceFunction(final TimeScale ut1)
  365.             throws OrekitException {

  366.             // set up nutation arguments
  367.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  368.             // set up Poisson series
  369.             final PoissonSeriesParser<DerivativeStructure> k20Parser =
  370.                     new PoissonSeriesParser<DerivativeStructure>(18).
  371.                         withOptionalColumn(1).
  372.                         withDoodson(4, 2).
  373.                         withFirstDelaunay(10);
  374.             final PoissonSeriesParser<DerivativeStructure> k21Parser =
  375.                     new PoissonSeriesParser<DerivativeStructure>(18).
  376.                         withOptionalColumn(1).
  377.                         withDoodson(4, 3).
  378.                         withFirstDelaunay(10);
  379.             final PoissonSeriesParser<DerivativeStructure> k22Parser =
  380.                     new PoissonSeriesParser<DerivativeStructure>(16).
  381.                         withOptionalColumn(1).
  382.                         withDoodson(4, 2).
  383.                         withFirstDelaunay(10);

  384.             final double pico = 1.0e-12;
  385.             final PoissonSeries<DerivativeStructure> c20Series =
  386.                     k20Parser.
  387.                     // TODO: check sign!
  388. //                  withSinCos(0, 18, pico, 16, pico).
  389.                   withSinCos(0, 18, -pico, 16, pico).
  390.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  391.             final PoissonSeries<DerivativeStructure> c21Series =
  392.                     k21Parser.
  393.                     withSinCos(0, 17, pico, 18, pico).
  394.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  395.             final PoissonSeries<DerivativeStructure> s21Series =
  396.                     k21Parser.
  397.                     withSinCos(0, 18, -pico, 17, pico).
  398.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  399.             final PoissonSeries<DerivativeStructure> c22Series =
  400.                     k22Parser.
  401.                     withSinCos(0, -1, pico, 16, pico).
  402.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  403.             final PoissonSeries<DerivativeStructure> s22Series =
  404.                     k22Parser.
  405.                     withSinCos(0, 16, -pico, -1, pico).
  406.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  407.             @SuppressWarnings("unchecked")
  408.             final PoissonSeries.CompiledSeries<DerivativeStructure> kSeries =
  409.                 PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);

  410.             return new TimeFunction<double[]>() {
  411.                 /** {@inheritDoc} */
  412.                 @Override
  413.                 public double[] value(final AbsoluteDate date) {
  414.                     return kSeries.value(arguments.evaluateAll(date));
  415.                 }
  416.             };

  417.         }

  418.         /** {@inheritDoc} */
  419.         @Override
  420.         public double getPermanentTide() throws OrekitException {
  421.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  422.         }

  423.         /** {@inheritDoc} */
  424.         @Override
  425.         public TimeFunction<double[]> getSolidPoleTide(final EOPHistory eopHistory) {

  426.             // constants from IERS 1996 page 47
  427.             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  428.             final double coupling     =  0.00112;

  429.             return new TimeFunction<double[]>() {
  430.                 /** {@inheritDoc} */
  431.                 @Override
  432.                 public double[] value(final AbsoluteDate date) {
  433.                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
  434.                     return new double[] {
  435.                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
  436.                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
  437.                     };
  438.                 }
  439.             };
  440.         }

  441.         /** {@inheritDoc} */
  442.         @Override
  443.         public TimeFunction<double[]> getOceanPoleTide(final EOPHistory eopHistory)
  444.             throws OrekitException {

  445.             return new TimeFunction<double[]>() {
  446.                 /** {@inheritDoc} */
  447.                 @Override
  448.                 public double[] value(final AbsoluteDate date) {
  449.                     // there are no model for ocean pole tide prior to conventions 2010
  450.                     return new double[] {
  451.                         0.0, 0.0
  452.                     };
  453.                 }
  454.             };
  455.         }

  456.     },

  457.     /** Constant for IERS 2003 conventions. */
  458.     IERS_2003 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  487.         /** {@inheritDoc} */
  488.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  489.             throws OrekitException {
  490.             return new FundamentalNutationArguments(this, timeScale,
  491.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  492.         }

  493.         /** {@inheritDoc} */
  494.         @Override
  495.         public TimeFunction<Double> getMeanObliquityFunction() throws OrekitException {

  496.             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
  497.             final PolynomialNutation<DerivativeStructure> epsilonA =
  498.                     new PolynomialNutation<DerivativeStructure>(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
  499.                                                                   -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
  500.                                                                    -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
  501.                                                                     0.001813 * Constants.ARC_SECONDS_TO_RADIANS);

  502.             return new TimeFunction<Double>() {

  503.                 /** {@inheritDoc} */
  504.                 @Override
  505.                 public Double value(final AbsoluteDate date) {
  506.                     return epsilonA.value(evaluateTC(date));
  507.                 }

  508.             };

  509.         }

  510.         /** {@inheritDoc} */
  511.         @Override
  512.         public TimeFunction<double[]> getXYSpXY2Function()
  513.             throws OrekitException {

  514.             // set up nutation arguments
  515.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  516.             // set up Poisson series
  517.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  518.             final PoissonSeriesParser<DerivativeStructure> parser =
  519.                     new PoissonSeriesParser<DerivativeStructure>(17).
  520.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  521.                         withFirstDelaunay(4).
  522.                         withFirstPlanetary(9).
  523.                         withSinCos(0, 2, microAS, 3, microAS);

  524.             final PoissonSeries<DerivativeStructure> xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  525.             final PoissonSeries<DerivativeStructure> ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  526.             final PoissonSeries<DerivativeStructure> sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  527.             @SuppressWarnings("unchecked")
  528.             final PoissonSeries.CompiledSeries<DerivativeStructure> xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  529.             // create a function evaluating the series
  530.             return new TimeFunction<double[]>() {

  531.                 /** {@inheritDoc} */
  532.                 @Override
  533.                 public double[] value(final AbsoluteDate date) {
  534.                     return xys.value(arguments.evaluateAll(date));
  535.                 }

  536.             };

  537.         }


  538.         /** {@inheritDoc} */
  539.         @Override
  540.         public TimeFunction<double[]> getPrecessionFunction() throws OrekitException {

  541.             // set up the conventional polynomials
  542.             // the following values are from equation 32 in IERS 2003 conventions
  543.             final PolynomialNutation<DerivativeStructure> psiA =
  544.                     new PolynomialNutation<DerivativeStructure>(    0.0,
  545.                                                                  5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
  546.                                                                    -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
  547.                                                                    -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
  548.             final PolynomialNutation<DerivativeStructure> omegaA =
  549.                     new PolynomialNutation<DerivativeStructure>(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  550.                                                                 -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
  551.                                                                  0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
  552.                                                                 -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
  553.             final PolynomialNutation<DerivativeStructure> chiA =
  554.                     new PolynomialNutation<DerivativeStructure>( 0.0,
  555.                                                                 10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
  556.                                                                 -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
  557.                                                                 -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);

  558.             return new TimeFunction<double[]>() {
  559.                 /** {@inheritDoc} */
  560.                 @Override
  561.                 public double[] value(final AbsoluteDate date) {
  562.                     final double tc = evaluateTC(date);
  563.                     return new double[] {
  564.                         psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  565.                     };
  566.                 }
  567.             };

  568.         }

  569.         /** {@inheritDoc} */
  570.         @Override
  571.         public TimeFunction<double[]> getNutationFunction()
  572.             throws OrekitException {

  573.             // set up nutation arguments
  574.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  575.             // set up Poisson series
  576.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  577.             final PoissonSeriesParser<DerivativeStructure> luniSolarParser =
  578.                     new PoissonSeriesParser<DerivativeStructure>(14).withFirstDelaunay(1);
  579.             final PoissonSeriesParser<DerivativeStructure> luniSolarPsiParser =
  580.                     luniSolarParser.
  581.                     withSinCos(0, 7, milliAS, 11, milliAS).
  582.                     withSinCos(1, 8, milliAS, 12, milliAS);
  583.             final PoissonSeries<DerivativeStructure> psiLuniSolarSeries =
  584.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
  585.             final PoissonSeriesParser<DerivativeStructure> luniSolarEpsilonParser =
  586.                     luniSolarParser.
  587.                     withSinCos(0, 13, milliAS, 9, milliAS).
  588.                     withSinCos(1, 14, milliAS, 10, milliAS);
  589.             final PoissonSeries<DerivativeStructure> epsilonLuniSolarSeries =
  590.                     luniSolarEpsilonParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  591.             final PoissonSeriesParser<DerivativeStructure> planetaryParser =
  592.                     new PoissonSeriesParser<DerivativeStructure>(21).
  593.                         withFirstDelaunay(2).
  594.                         withFirstPlanetary(7);
  595.             final PoissonSeriesParser<DerivativeStructure> planetaryPsiParser =
  596.                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
  597.             final PoissonSeries<DerivativeStructure> psiPlanetarySeries =
  598.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
  599.             final PoissonSeriesParser<DerivativeStructure> planetaryEpsilonParser =
  600.                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
  601.             final PoissonSeries<DerivativeStructure> epsilonPlanetarySeries =
  602.                     planetaryEpsilonParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  603.             @SuppressWarnings("unchecked")
  604.             final PoissonSeries.CompiledSeries<DerivativeStructure> luniSolarSeries =
  605.                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
  606.             @SuppressWarnings("unchecked")
  607.             final PoissonSeries.CompiledSeries<DerivativeStructure> planetarySeries =
  608.                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);

  609.             return new TimeFunction<double[]>() {
  610.                 /** {@inheritDoc} */
  611.                 @Override
  612.                 public double[] value(final AbsoluteDate date) {
  613.                     final BodiesElements elements = arguments.evaluateAll(date);
  614.                     final double[] luniSolar = luniSolarSeries.value(elements);
  615.                     final double[] planetary = planetarySeries.value(elements);
  616.                     return new double[] {
  617.                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
  618.                         IAU1994ResolutionC7.value(elements)
  619.                     };
  620.                 }
  621.             };

  622.         }

  623.         /** {@inheritDoc} */
  624.         @Override
  625.         public TimeFunction<DerivativeStructure> getGMSTFunction(final TimeScale ut1)
  626.             throws OrekitException {

  627.             // Earth Rotation Angle
  628.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  629.             // Polynomial part of the apparent sidereal time series
  630.             // which is the opposite of Equation of Origins (EO)
  631.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  632.             final PoissonSeriesParser<DerivativeStructure> parser =
  633.                     new PoissonSeriesParser<DerivativeStructure>(17).
  634.                         withFirstDelaunay(4).
  635.                         withFirstPlanetary(9).
  636.                         withSinCos(0, 2, microAS, 3, microAS).
  637.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  638.             final PolynomialNutation<DerivativeStructure> minusEO =
  639.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  640.             // create a function evaluating the series
  641.             return new TimeFunction<DerivativeStructure>() {

  642.                 /** {@inheritDoc} */
  643.                 @Override
  644.                 public DerivativeStructure value(final AbsoluteDate date) {
  645.                     return era.value(date).add(minusEO.value(dsEvaluateTC(date)));
  646.                 }

  647.             };

  648.         }

  649.         /** {@inheritDoc} */
  650.         @Override
  651.         public TimeFunction<DerivativeStructure> getGASTFunction(final TimeScale ut1,
  652.                                                                  final EOPHistory eopHistory)
  653.             throws OrekitException {

  654.             // set up nutation arguments
  655.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  656.             // mean obliquity function
  657.             final TimeFunction<Double> epsilon = getMeanObliquityFunction();

  658.             // set up Poisson series
  659.             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
  660.             final PoissonSeriesParser<DerivativeStructure> luniSolarPsiParser =
  661.                     new PoissonSeriesParser<DerivativeStructure>(14).
  662.                         withFirstDelaunay(1).
  663.                         withSinCos(0, 7, milliAS, 11, milliAS).
  664.                         withSinCos(1, 8, milliAS, 12, milliAS);
  665.             final PoissonSeries<DerivativeStructure> psiLuniSolarSeries =
  666.                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);

  667.             final PoissonSeriesParser<DerivativeStructure> planetaryPsiParser =
  668.                     new PoissonSeriesParser<DerivativeStructure>(21).
  669.                         withFirstDelaunay(2).
  670.                         withFirstPlanetary(7).
  671.                         withSinCos(0, 17, milliAS, 18, milliAS);
  672.             final PoissonSeries<DerivativeStructure> psiPlanetarySeries =
  673.                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);

  674.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  675.             final PoissonSeriesParser<DerivativeStructure> gstParser =
  676.                     new PoissonSeriesParser<DerivativeStructure>(17).
  677.                         withFirstDelaunay(4).
  678.                         withFirstPlanetary(9).
  679.                         withSinCos(0, 2, microAS, 3, microAS).
  680.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  681.             final PoissonSeries<DerivativeStructure> gstSeries = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  682.             @SuppressWarnings("unchecked")
  683.             final PoissonSeries.CompiledSeries<DerivativeStructure> psiGstSeries =
  684.                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);

  685.             // ERA function
  686.             final TimeFunction<DerivativeStructure> era = getEarthOrientationAngleFunction(ut1);

  687.             return new TimeFunction<DerivativeStructure>() {

  688.                 /** {@inheritDoc} */
  689.                 @Override
  690.                 public DerivativeStructure value(final AbsoluteDate date) {

  691.                     // evaluate equation of origins
  692.                     final BodiesElements elements = arguments.evaluateAll(date);
  693.                     final double[] angles = psiGstSeries.value(elements);
  694.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  695.                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
  696.                     final double epsilonA = epsilon.value(date);

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

  700.                 }

  701.             };

  702.         }

  703.         /** {@inheritDoc} */
  704.         @Override
  705.         public TimeFunction<double[]> getEOPTidalCorrection()
  706.             throws OrekitException {

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

  713.             // set up Poisson series
  714.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  715.             final PoissonSeriesParser<DerivativeStructure> xyParser = new PoissonSeriesParser<DerivativeStructure>(13).
  716.                     withOptionalColumn(1).
  717.                     withGamma(2).
  718.                     withFirstDelaunay(3);
  719.             final PoissonSeries<DerivativeStructure> xSeries =
  720.                     xyParser.
  721.                     withSinCos(0, 10, microAS, 11, microAS).
  722.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  723.             final PoissonSeries<DerivativeStructure> ySeries =
  724.                     xyParser.
  725.                     withSinCos(0, 12, microAS, 13, microAS).
  726.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  727.             final double microS = 1.0e-6;
  728.             final PoissonSeriesParser<DerivativeStructure> ut1Parser = new PoissonSeriesParser<DerivativeStructure>(11).
  729.                     withOptionalColumn(1).
  730.                     withGamma(2).
  731.                     withFirstDelaunay(3).
  732.                     withSinCos(0, 10, microS, 11, microS);
  733.             final PoissonSeries<DerivativeStructure> ut1Series =
  734.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  735.             @SuppressWarnings("unchecked")
  736.             final PoissonSeries.CompiledSeries<DerivativeStructure> correctionSeries =
  737.                 PoissonSeries.compile(xSeries, ySeries, ut1Series);

  738.             return new TimeFunction<double[]>() {
  739.                 /** {@inheritDoc} */
  740.                 @Override
  741.                 public double[] value(final AbsoluteDate date) {
  742.                     final FieldBodiesElements<DerivativeStructure> elements =
  743.                             arguments.evaluateDerivative(date);
  744.                     final DerivativeStructure[] correction = correctionSeries.value(elements);
  745.                     return new double[] {
  746.                         correction[0].getValue(),
  747.                         correction[1].getValue(),
  748.                         correction[2].getValue(),
  749.                         -correction[2].getPartialDerivative(1) * Constants.JULIAN_DAY
  750.                     };
  751.                 }
  752.             };

  753.         }

  754.         /** {@inheritDoc} */
  755.         public LoveNumbers getLoveNumbers() throws OrekitException {
  756.             return loadLoveNumbers(LOVE_NUMBERS);
  757.         }

  758.         /** {@inheritDoc} */
  759.         public TimeFunction<double[]> getTideFrequencyDependenceFunction(final TimeScale ut1)
  760.             throws OrekitException {

  761.             // set up nutation arguments
  762.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  763.             // set up Poisson series
  764.             final PoissonSeriesParser<DerivativeStructure> k20Parser =
  765.                     new PoissonSeriesParser<DerivativeStructure>(18).
  766.                         withOptionalColumn(1).
  767.                         withDoodson(4, 2).
  768.                         withFirstDelaunay(10);
  769.             final PoissonSeriesParser<DerivativeStructure> k21Parser =
  770.                     new PoissonSeriesParser<DerivativeStructure>(18).
  771.                         withOptionalColumn(1).
  772.                         withDoodson(4, 3).
  773.                         withFirstDelaunay(10);
  774.             final PoissonSeriesParser<DerivativeStructure> k22Parser =
  775.                     new PoissonSeriesParser<DerivativeStructure>(16).
  776.                         withOptionalColumn(1).
  777.                         withDoodson(4, 2).
  778.                         withFirstDelaunay(10);

  779.             final double pico = 1.0e-12;
  780.             final PoissonSeries<DerivativeStructure> c20Series =
  781.                     k20Parser.
  782.                     // TODO: check sign!
  783. //                  withSinCos(0, 18, pico, 16, pico).
  784.                   withSinCos(0, 18, -pico, 16, pico).
  785.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  786.             final PoissonSeries<DerivativeStructure> c21Series =
  787.                     k21Parser.
  788.                     withSinCos(0, 17, pico, 18, pico).
  789.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  790.             final PoissonSeries<DerivativeStructure> s21Series =
  791.                     k21Parser.
  792.                     withSinCos(0, 18, -pico, 17, pico).
  793.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  794.             final PoissonSeries<DerivativeStructure> c22Series =
  795.                     k22Parser.
  796.                     withSinCos(0, -1, pico, 16, pico).
  797.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  798.             final PoissonSeries<DerivativeStructure> s22Series =
  799.                     k22Parser.
  800.                     withSinCos(0, 16, -pico, -1, pico).
  801.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  802.             @SuppressWarnings("unchecked")
  803.             final PoissonSeries.CompiledSeries<DerivativeStructure> kSeries =
  804.                 PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);

  805.             return new TimeFunction<double[]>() {
  806.                 /** {@inheritDoc} */
  807.                 @Override
  808.                 public double[] value(final AbsoluteDate date) {
  809.                     return kSeries.value(arguments.evaluateAll(date));
  810.                 }
  811.             };

  812.         }

  813.         /** {@inheritDoc} */
  814.         @Override
  815.         public double getPermanentTide() throws OrekitException {
  816.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  817.         }

  818.         /** {@inheritDoc} */
  819.         @Override
  820.         public TimeFunction<double[]> getSolidPoleTide(final EOPHistory eopHistory)
  821.             throws OrekitException {

  822.             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
  823.             final TimeScale utc = TimeScalesFactory.getUTC();
  824.             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
  825.                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
  826.                     /** {@inheritDoc} */
  827.                     @Override
  828.                     public MeanPole convert(final double[] rawFields) throws OrekitException {
  829.                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
  830.                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
  831.                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
  832.                     }
  833.                 };
  834.             final SimpleTimeStampedTableParser<MeanPole> parser =
  835.                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
  836.             final List<MeanPole> annualPoleList = parser.parse(getStream(ANNUAL_POLE), ANNUAL_POLE);
  837.             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
  838.             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
  839.             final ImmutableTimeStampedCache<MeanPole> annualCache =
  840.                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);

  841.             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
  842.             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
  843.             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
  844.             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
  845.             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;

  846.             // constants from IERS 2003, section 6.2
  847.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  848.             final double ratio        =  0.00115;

  849.             return new TimeFunction<double[]>() {
  850.                 /** {@inheritDoc} */
  851.                 @Override
  852.                 public double[] value(final AbsoluteDate date) {

  853.                     // we can't compute anything before the range covered by the annual pole file
  854.                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
  855.                         return new double[] {
  856.                             0.0, 0.0
  857.                         };
  858.                     }

  859.                     // evaluate mean pole
  860.                     double meanPoleX = 0;
  861.                     double meanPoleY = 0;
  862.                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
  863.                         // we are within the range covered by the annual pole file,
  864.                         // we interpolate within it
  865.                         try {
  866.                             final List<MeanPole> neighbors = annualCache.getNeighbors(date);
  867.                             final HermiteInterpolator interpolator = new HermiteInterpolator();
  868.                             for (final MeanPole neighbor : neighbors) {
  869.                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
  870.                                                             new double[] {
  871.                                                                 neighbor.getX(), neighbor.getY()
  872.                                                             });
  873.                             }
  874.                             final double[] interpolated = interpolator.value(0);
  875.                             meanPoleX = interpolated[0];
  876.                             meanPoleY = interpolated[1];
  877.                         } catch (TimeStampedCacheException tsce) {
  878.                             // this should never happen
  879.                             throw OrekitException.createInternalError(tsce);
  880.                         }
  881.                     } else {

  882.                         // we are after the range covered by the annual pole file,
  883.                         // we use the polynomial extension
  884.                         final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  885.                         meanPoleX = xp0 + t * xp0Dot;
  886.                         meanPoleY = yp0 + t * yp0Dot;

  887.                     }

  888.                     // evaluate wobble variables
  889.                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  890.                     final double m1 = correction.getXp() - meanPoleX;
  891.                     final double m2 = meanPoleY - correction.getYp();

  892.                     return new double[] {
  893.                         // the following correspond to the equations published in IERS 2003 conventions,
  894.                         // section 6.2 page 65. In the publication, the equations read:
  895.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  896.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  897.                         // However, it seems there are sign errors in these equations, which have
  898.                         // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
  899.                         // publication, the equations read:
  900.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  901.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  902.                         // the newer equations seem more consistent with the premises as the
  903.                         // deformation due to the centrifugal potential has the form:
  904.                         // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
  905.                         // number 0.3077 + 0.0036i, so the real part in the previous equation is:
  906.                         // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
  907.                         // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
  908.                         // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
  909.                         // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
  910.                         // and Im(k₂)/Re(k₂) is very close to +0.00115
  911.                         // As the equation as written in the IERS 2003 conventions are used in
  912.                         // legacy systems, we have reproduced this alleged error here (and fixed it in
  913.                         // the IERS 2010 conventions below) for validation purposes. We don't recommend
  914.                         // using the IERS 2003 conventions for solid pole tide computation other than
  915.                         // for validation or reproducibility of legacy applications behavior.
  916.                         // As solid pole tide is small and as the sign change is on the smallest coefficient,
  917.                         // the effect is quite small. A test case on a propagated orbit showed a position change
  918.                         // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
  919.                         globalFactor * (m1 - ratio * m2),
  920.                         globalFactor * (m2 + ratio * m1),
  921.                     };

  922.                 }
  923.             };

  924.         }

  925.         /** {@inheritDoc} */
  926.         @Override
  927.         public TimeFunction<double[]> getOceanPoleTide(final EOPHistory eopHistory)
  928.             throws OrekitException {

  929.             return new TimeFunction<double[]>() {
  930.                 /** {@inheritDoc} */
  931.                 @Override
  932.                 public double[] value(final AbsoluteDate date) {
  933.                     // there are no model for ocean pole tide prior to conventions 2010
  934.                     return new double[] {
  935.                         0.0, 0.0
  936.                     };
  937.                 }
  938.             };
  939.         }

  940.     },

  941.     /** Constant for IERS 2010 conventions. */
  942.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

  969.         /** {@inheritDoc} */
  970.         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  971.             throws OrekitException {
  972.             return new FundamentalNutationArguments(this, timeScale,
  973.                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
  974.         }

  975.         /** {@inheritDoc} */
  976.         @Override
  977.         public TimeFunction<Double> getMeanObliquityFunction() throws OrekitException {

  978.             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
  979.             final PolynomialNutation<DerivativeStructure> epsilonA =
  980.                     new PolynomialNutation<DerivativeStructure>(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
  981.                                                                   -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
  982.                                                                    -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
  983.                                                                     0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
  984.                                                                    -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
  985.                                                                    -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);

  986.             return new TimeFunction<Double>() {

  987.                 /** {@inheritDoc} */
  988.                 @Override
  989.                 public Double value(final AbsoluteDate date) {
  990.                     return epsilonA.value(evaluateTC(date));
  991.                 }

  992.             };

  993.         }

  994.         /** {@inheritDoc} */
  995.         @Override
  996.         public TimeFunction<double[]> getXYSpXY2Function() throws OrekitException {

  997.             // set up nutation arguments
  998.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  999.             // set up Poisson series
  1000.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1001.             final PoissonSeriesParser<DerivativeStructure> parser =
  1002.                     new PoissonSeriesParser<DerivativeStructure>(17).
  1003.                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
  1004.                         withFirstDelaunay(4).
  1005.                         withFirstPlanetary(9).
  1006.                         withSinCos(0, 2, microAS, 3, microAS);
  1007.             final PoissonSeries<DerivativeStructure> xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
  1008.             final PoissonSeries<DerivativeStructure> ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
  1009.             final PoissonSeries<DerivativeStructure> sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
  1010.             @SuppressWarnings("unchecked")
  1011.             final PoissonSeries.CompiledSeries<DerivativeStructure> xys = PoissonSeries.compile(xSeries, ySeries, sSeries);

  1012.             // create a function evaluating the series
  1013.             return new TimeFunction<double[]>() {

  1014.                 /** {@inheritDoc} */
  1015.                 @Override
  1016.                 public double[] value(final AbsoluteDate date) {
  1017.                     return xys.value(arguments.evaluateAll(date));
  1018.                 }

  1019.             };

  1020.         }

  1021.         /** {@inheritDoc} */
  1022.         public LoveNumbers getLoveNumbers() throws OrekitException {
  1023.             return loadLoveNumbers(LOVE_NUMBERS);
  1024.         }

  1025.         /** {@inheritDoc} */
  1026.         public TimeFunction<double[]> getTideFrequencyDependenceFunction(final TimeScale ut1)
  1027.             throws OrekitException {

  1028.             // set up nutation arguments
  1029.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

  1030.             // set up Poisson series
  1031.             final PoissonSeriesParser<DerivativeStructure> k20Parser =
  1032.                     new PoissonSeriesParser<DerivativeStructure>(18).
  1033.                         withOptionalColumn(1).
  1034.                         withDoodson(4, 2).
  1035.                         withFirstDelaunay(10);
  1036.             final PoissonSeriesParser<DerivativeStructure> k21Parser =
  1037.                     new PoissonSeriesParser<DerivativeStructure>(18).
  1038.                         withOptionalColumn(1).
  1039.                         withDoodson(4, 3).
  1040.                         withFirstDelaunay(10);
  1041.             final PoissonSeriesParser<DerivativeStructure> k22Parser =
  1042.                     new PoissonSeriesParser<DerivativeStructure>(16).
  1043.                         withOptionalColumn(1).
  1044.                         withDoodson(4, 2).
  1045.                         withFirstDelaunay(10);

  1046.             final double pico = 1.0e-12;
  1047.             final PoissonSeries<DerivativeStructure> c20Series =
  1048.                     k20Parser.
  1049.                     // TODO: check sign!
  1050. //                    withSinCos(0, 18, pico, 16, pico).
  1051.                     withSinCos(0, 18, -pico, 16, pico).
  1052.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  1053.             final PoissonSeries<DerivativeStructure> c21Series =
  1054.                     k21Parser.
  1055.                     withSinCos(0, 17, pico, 18, pico).
  1056.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1057.             final PoissonSeries<DerivativeStructure> s21Series =
  1058.                     k21Parser.
  1059.                     withSinCos(0, 18, -pico, 17, pico).
  1060.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1061.             final PoissonSeries<DerivativeStructure> c22Series =
  1062.                     k22Parser.
  1063.                     withSinCos(0, -1, pico, 16, pico).
  1064.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  1065.             final PoissonSeries<DerivativeStructure> s22Series =
  1066.                     k22Parser.
  1067.                     withSinCos(0, 16, -pico, -1, pico).
  1068.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  1069.             @SuppressWarnings("unchecked")
  1070.             final PoissonSeries.CompiledSeries<DerivativeStructure> kSeries =
  1071.                 PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);

  1072.             return new TimeFunction<double[]>() {
  1073.                 /** {@inheritDoc} */
  1074.                 @Override
  1075.                 public double[] value(final AbsoluteDate date) {
  1076.                     return kSeries.value(arguments.evaluateAll(date));
  1077.                 }
  1078.             };

  1079.         }

  1080.         /** {@inheritDoc} */
  1081.         @Override
  1082.         public double getPermanentTide() throws OrekitException {
  1083.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1084.         }

  1085.         /** Compute pole wobble variables m₁ and m₂.
  1086.          * @param date current date
  1087.          * @param eopHistory EOP history
  1088.          * @return array containing m₁ and m₂
  1089.          */
  1090.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1091.             // polynomial model from IERS 2010, table 7.7
  1092.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1093.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1094.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1095.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1096.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

  1097.             // evaluate mean pole
  1098.             final double[] xPolynomial;
  1099.             final double[] yPolynomial;
  1100.             if (date.compareTo(changeDate) <= 0) {
  1101.                 xPolynomial = new double[] {
  1102.                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
  1103.                 };
  1104.                 yPolynomial = new double[] {
  1105.                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
  1106.                 };
  1107.             } else {
  1108.                 xPolynomial = new double[] {
  1109.                     23.513 * f0, 7.6141 * f1
  1110.                 };
  1111.                 yPolynomial = new double[] {
  1112.                     358.891 * f0,  -0.6287 * f1
  1113.                 };
  1114.             }
  1115.             double meanPoleX = 0;
  1116.             double meanPoleY = 0;
  1117.             final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
  1118.             for (int i = xPolynomial.length - 1; i >= 0; --i) {
  1119.                 meanPoleX = meanPoleX * t + xPolynomial[i];
  1120.             }
  1121.             for (int i = yPolynomial.length - 1; i >= 0; --i) {
  1122.                 meanPoleY = meanPoleY * t + yPolynomial[i];
  1123.             }

  1124.             // evaluate wobble variables
  1125.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1126.             final double m1 = correction.getXp() - meanPoleX;
  1127.             final double m2 = meanPoleY - correction.getYp();

  1128.             return new double[] {
  1129.                 m1, m2
  1130.             };

  1131.         }

  1132.         /** {@inheritDoc} */
  1133.         @Override
  1134.         public TimeFunction<double[]> getSolidPoleTide(final EOPHistory eopHistory)
  1135.             throws OrekitException {

  1136.             // constants from IERS 2010, section 6.4
  1137.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1138.             final double ratio        =  0.00115;

  1139.             return new TimeFunction<double[]>() {
  1140.                 /** {@inheritDoc} */
  1141.                 @Override
  1142.                 public double[] value(final AbsoluteDate date) {

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

  1145.                     return new double[] {
  1146.                         // the following correspond to the equations published in IERS 2010 conventions,
  1147.                         // section 6.4 page 94. The equations read:
  1148.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
  1149.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
  1150.                         // These equations seem to fix what was probably a sign error in IERS 2003
  1151.                         // conventions section 6.2 page 65. In this older publication, the equations read:
  1152.                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
  1153.                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
  1154.                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
  1155.                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
  1156.                     };

  1157.                 }
  1158.             };

  1159.         }

  1160.         /** {@inheritDoc} */
  1161.         @Override
  1162.         public TimeFunction<double[]> getOceanPoleTide(final EOPHistory eopHistory)
  1163.             throws OrekitException {

  1164.             return new TimeFunction<double[]>() {
  1165.                 /** {@inheritDoc} */
  1166.                 @Override
  1167.                 public double[] value(final AbsoluteDate date) {

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

  1170.                     return new double[] {
  1171.                         // the following correspond to the equations published in IERS 2010 conventions,
  1172.                         // section 6.4 page 94 equation 6.24:
  1173.                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
  1174.                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
  1175.                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
  1176.                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
  1177.                     };

  1178.                 }
  1179.             };

  1180.         }

  1181.         /** {@inheritDoc} */
  1182.         @Override
  1183.         public TimeFunction<double[]> getPrecessionFunction() throws OrekitException {

  1184.             // set up the conventional polynomials
  1185.             // the following values are from equation 5.40 in IERS 2010 conventions
  1186.             final PolynomialNutation<DerivativeStructure> psiA =
  1187.                     new PolynomialNutation<DerivativeStructure>(   0.0,
  1188.                                                                 5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
  1189.                                                                   -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
  1190.                                                                   -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
  1191.                                                                    0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
  1192.                                                                   -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
  1193.             final PolynomialNutation<DerivativeStructure> omegaA =
  1194.                     new PolynomialNutation<DerivativeStructure>(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
  1195.                                                                 -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
  1196.                                                                  0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
  1197.                                                                 -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
  1198.                                                                 -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
  1199.                                                                  0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
  1200.             final PolynomialNutation<DerivativeStructure> chiA =
  1201.                     new PolynomialNutation<DerivativeStructure>( 0.0,
  1202.                                                                 10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
  1203.                                                                 -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
  1204.                                                                 -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
  1205.                                                                  0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
  1206.                                                                 -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);

  1207.             return new TimeFunction<double[]>() {
  1208.                 /** {@inheritDoc} */
  1209.                 @Override
  1210.                 public double[] value(final AbsoluteDate date) {
  1211.                     final double tc = evaluateTC(date);
  1212.                     return new double[] {
  1213.                         psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  1214.                     };
  1215.                 }
  1216.             };

  1217.         }

  1218.          /** {@inheritDoc} */
  1219.         @Override
  1220.         public TimeFunction<double[]> getNutationFunction()
  1221.             throws OrekitException {

  1222.             // set up nutation arguments
  1223.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1224.             // set up Poisson series
  1225.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1226.             final PoissonSeriesParser<DerivativeStructure> parser =
  1227.                     new PoissonSeriesParser<DerivativeStructure>(17).
  1228.                         withFirstDelaunay(4).
  1229.                         withFirstPlanetary(9).
  1230.                         withSinCos(0, 2, microAS, 3, microAS);
  1231.             final PoissonSeries<DerivativeStructure> psiSeries     = parser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1232.             final PoissonSeries<DerivativeStructure> epsilonSeries = parser.parse(getStream(EPSILON_SERIES), EPSILON_SERIES);
  1233.             @SuppressWarnings("unchecked")
  1234.             final PoissonSeries.CompiledSeries<DerivativeStructure> psiEpsilonSeries =
  1235.                     PoissonSeries.compile(psiSeries, epsilonSeries);

  1236.             return new TimeFunction<double[]>() {
  1237.                 /** {@inheritDoc} */
  1238.                 @Override
  1239.                 public double[] value(final AbsoluteDate date) {
  1240.                     final BodiesElements elements = arguments.evaluateAll(date);
  1241.                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
  1242.                     return new double[] {
  1243.                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
  1244.                     };
  1245.                 }
  1246.             };

  1247.         }

  1248.         /** {@inheritDoc} */
  1249.         @Override
  1250.         public TimeFunction<DerivativeStructure> getGMSTFunction(final TimeScale ut1) throws OrekitException {

  1251.             // Earth Rotation Angle
  1252.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

  1253.             // Polynomial part of the apparent sidereal time series
  1254.             // which is the opposite of Equation of Origins (EO)
  1255.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1256.             final PoissonSeriesParser<DerivativeStructure> parser =
  1257.                     new PoissonSeriesParser<DerivativeStructure>(17).
  1258.                         withFirstDelaunay(4).
  1259.                         withFirstPlanetary(9).
  1260.                         withSinCos(0, 2, microAS, 3, microAS).
  1261.                         withPolynomialPart('t', Unit.ARC_SECONDS);
  1262.             final PolynomialNutation<DerivativeStructure> minusEO =
  1263.                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();

  1264.             // create a function evaluating the series
  1265.             return new TimeFunction<DerivativeStructure>() {

  1266.                 /** {@inheritDoc} */
  1267.                 @Override
  1268.                 public DerivativeStructure value(final AbsoluteDate date) {
  1269.                     return era.value(date).add(minusEO.value(dsEvaluateTC(date)));
  1270.                 }

  1271.             };

  1272.         }

  1273.         /** {@inheritDoc} */
  1274.         @Override
  1275.         public TimeFunction<DerivativeStructure> getGASTFunction(final TimeScale ut1,
  1276.                                                                  final EOPHistory eopHistory)
  1277.             throws OrekitException {

  1278.             // set up nutation arguments
  1279.             final FundamentalNutationArguments arguments = getNutationArguments(null);

  1280.             // mean obliquity function
  1281.             final TimeFunction<Double> epsilon = getMeanObliquityFunction();

  1282.             // set up Poisson series
  1283.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1284.             final PoissonSeriesParser<DerivativeStructure> baseParser =
  1285.                     new PoissonSeriesParser<DerivativeStructure>(17).
  1286.                         withFirstDelaunay(4).
  1287.                         withFirstPlanetary(9).
  1288.                         withSinCos(0, 2, microAS, 3, microAS);
  1289.             final PoissonSeriesParser<DerivativeStructure> gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
  1290.             final PoissonSeries<DerivativeStructure> psiSeries        = baseParser.parse(getStream(PSI_SERIES), PSI_SERIES);
  1291.             final PoissonSeries<DerivativeStructure> gstSeries        = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
  1292.             @SuppressWarnings("unchecked")
  1293.             final PoissonSeries.CompiledSeries<DerivativeStructure> psiGstSeries =
  1294.                     PoissonSeries.compile(psiSeries, gstSeries);

  1295.             // ERA function
  1296.             final TimeFunction<DerivativeStructure> era = getEarthOrientationAngleFunction(ut1);

  1297.             return new TimeFunction<DerivativeStructure>() {

  1298.                 /** {@inheritDoc} */
  1299.                 @Override
  1300.                 public DerivativeStructure value(final AbsoluteDate date) {

  1301.                     // evaluate equation of origins
  1302.                     final BodiesElements elements = arguments.evaluateAll(date);
  1303.                     final double[] angles = psiGstSeries.value(elements);
  1304.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1305.                     final double deltaPsi = angles[0] + ddPsi;
  1306.                     final double epsilonA = epsilon.value(date);

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

  1310.                 }

  1311.             };

  1312.         }

  1313.         /** {@inheritDoc} */
  1314.         @Override
  1315.         public TimeFunction<double[]> getEOPTidalCorrection()
  1316.             throws OrekitException {

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

  1323.             // set up Poisson series
  1324.             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
  1325.             final PoissonSeriesParser<DerivativeStructure> xyParser = new PoissonSeriesParser<DerivativeStructure>(13).
  1326.                     withOptionalColumn(1).
  1327.                     withGamma(2).
  1328.                     withFirstDelaunay(3);
  1329.             final PoissonSeries<DerivativeStructure> xSeries =
  1330.                     xyParser.
  1331.                     withSinCos(0, 10, microAS, 11, microAS).
  1332.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
  1333.             final PoissonSeries<DerivativeStructure> ySeries =
  1334.                     xyParser.
  1335.                     withSinCos(0, 12, microAS, 13, microAS).
  1336.                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);

  1337.             final double microS = 1.0e-6;
  1338.             final PoissonSeriesParser<DerivativeStructure> ut1Parser = new PoissonSeriesParser<DerivativeStructure>(11).
  1339.                     withOptionalColumn(1).
  1340.                     withGamma(2).
  1341.                     withFirstDelaunay(3).
  1342.                     withSinCos(0, 10, microS, 11, microS);
  1343.             final PoissonSeries<DerivativeStructure> ut1Series =
  1344.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  1345.             @SuppressWarnings("unchecked")
  1346.             final PoissonSeries.CompiledSeries<DerivativeStructure> correctionSeries =
  1347.                     PoissonSeries.compile(xSeries, ySeries, ut1Series);

  1348.             return new TimeFunction<double[]>() {
  1349.                 /** {@inheritDoc} */
  1350.                 @Override
  1351.                 public double[] value(final AbsoluteDate date) {
  1352.                     final FieldBodiesElements<DerivativeStructure> elements =
  1353.                             arguments.evaluateDerivative(date);
  1354.                     final DerivativeStructure[] correction = correctionSeries.value(elements);
  1355.                     return new double[] {
  1356.                         correction[0].getValue(),
  1357.                         correction[1].getValue(),
  1358.                         correction[2].getValue(),
  1359.                         -correction[2].getPartialDerivative(1) * Constants.JULIAN_DAY
  1360.                     };
  1361.                 }
  1362.             };

  1363.         }

  1364.     };

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

  1367.     /** Get the reference epoch for fundamental nutation arguments.
  1368.      * @return reference epoch for fundamental nutation arguments
  1369.      * @since 6.1
  1370.      */
  1371.     public AbsoluteDate getNutationReferenceEpoch() {
  1372.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1373.         return AbsoluteDate.J2000_EPOCH;
  1374.     }

  1375.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1376.      * @param date current date
  1377.      * @return date offset in Julian centuries
  1378.      * @since 6.1
  1379.      */
  1380.     public double evaluateTC(final AbsoluteDate date) {
  1381.         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
  1382.     }

  1383.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1384.      * @param date current date
  1385.      * @return date offset in Julian centuries
  1386.      * @since 6.1
  1387.      */
  1388.     public DerivativeStructure dsEvaluateTC(final AbsoluteDate date) {
  1389.         return new DerivativeStructure(1, 1, evaluateTC(date), 1.0 / Constants.JULIAN_CENTURY);
  1390.     }

  1391.     /** Get the fundamental nutation arguments.
  1392.      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
  1393.      * (typically {@link TimeScalesFactory#getUT1(IERSConventions, boolean) UT1})
  1394.      * @return fundamental nutation arguments
  1395.      * @exception OrekitException if fundamental nutation arguments cannot be loaded
  1396.      * @since 6.1
  1397.      */
  1398.     public abstract FundamentalNutationArguments getNutationArguments(final TimeScale timeScale)
  1399.         throws OrekitException;

  1400.     /** Get the function computing mean obliquity of the ecliptic.
  1401.      * @return function computing mean obliquity of the ecliptic
  1402.      * @exception OrekitException if table cannot be loaded
  1403.      * @since 6.1
  1404.      */
  1405.     public abstract TimeFunction<Double> getMeanObliquityFunction() throws OrekitException;

  1406.     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
  1407.      * <p>
  1408.      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
  1409.      * </p>
  1410.      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
  1411.      * @exception OrekitException if table cannot be loaded
  1412.      * @since 6.1
  1413.      */
  1414.     public abstract TimeFunction<double[]> getXYSpXY2Function()
  1415.         throws OrekitException;

  1416.     /** Get the function computing the raw Earth Orientation Angle.
  1417.      * <p>
  1418.      * The raw angle does not contain any correction. If for example dTU1 correction
  1419.      * due to tidal effect is desired, it must be added afterward by the caller.
  1420.      * The returned value contain the angle as the value and the angular rate as
  1421.      * the first derivative.
  1422.      * </p>
  1423.      * @param ut1 UT1 time scale
  1424.      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm,
  1425.      * the return value containing both the angle and its first time derivative
  1426.      * @since 6.1
  1427.      */
  1428.     public TimeFunction<DerivativeStructure> getEarthOrientationAngleFunction(final TimeScale ut1) {
  1429.         return new StellarAngleCapitaine(ut1);
  1430.     }


  1431.     /** Get the function computing the precession angles.
  1432.      * <p>
  1433.      * The function returned computes the three precession angles
  1434.      * &psi;<sub>A</sub> (around Z axis), &omega;<sub>A</sub> (around X axis)
  1435.      * and &chi;<sub>A</sub> (around Z axis). The constant angle &epsilon;<sub>0</sub>
  1436.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  1437.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  1438.      * #getNutationReferenceEpoch() nutation reference epoch}.
  1439.      * </p>
  1440.      * @return function computing the precession angle
  1441.      * @exception OrekitException if table cannot be loaded
  1442.      * @since 6.1
  1443.      */
  1444.     public abstract TimeFunction<double[]> getPrecessionFunction() throws OrekitException;

  1445.     /** Get the function computing the nutation angles.
  1446.      * <p>
  1447.      * The function returned computes the two classical angles &Delta;&Psi; and &Delta;&epsilon;,
  1448.      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
  1449.      * resolution C7 (the correction is forced to 0 before this date)
  1450.      * </p>
  1451.      * @return function computing the nutation in longitude &Delta;&Psi; and &Delta;&epsilon;
  1452.      * and the correction of equation of equinoxes
  1453.      * @exception OrekitException if table cannot be loaded
  1454.      * @since 6.1
  1455.      */
  1456.     public abstract TimeFunction<double[]> getNutationFunction()
  1457.         throws OrekitException;

  1458.     /** Get the function computing Greenwich mean sidereal time, in radians.
  1459.      * @param ut1 UT1 time scale
  1460.      * @return function computing Greenwich mean sidereal time,
  1461.      * the return value containing both the angle and its first time derivative
  1462.      * @exception OrekitException if table cannot be loaded
  1463.      * @since 6.1
  1464.      */
  1465.     public abstract TimeFunction<DerivativeStructure> getGMSTFunction(TimeScale ut1)
  1466.         throws OrekitException;

  1467.     /** Get the function computing Greenwich apparent sidereal time, in radians.
  1468.      * @param ut1 UT1 time scale
  1469.      * @param eopHistory EOP history
  1470.      * @return function computing Greenwich apparent sidereal time,
  1471.      * the return value containing both the angle and its first time derivative
  1472.      * @exception OrekitException if table cannot be loaded
  1473.      * @since 6.1
  1474.      */
  1475.     public abstract TimeFunction<DerivativeStructure> getGASTFunction(TimeScale ut1,
  1476.                                                                       EOPHistory eopHistory)
  1477.         throws OrekitException;

  1478.     /** Get the function computing tidal corrections for Earth Orientation Parameters.
  1479.      * @return function computing tidal corrections for Earth Orientation Parameters,
  1480.      * for xp, yp, ut1 and lod respectively
  1481.      * @exception OrekitException if table cannot be loaded
  1482.      * @since 6.1
  1483.      */
  1484.     public abstract TimeFunction<double[]> getEOPTidalCorrection()
  1485.         throws OrekitException;

  1486.     /** Get the Love numbers.
  1487.      * @return Love numbers
  1488.      * @exception OrekitException if table cannot be loaded
  1489.      * @since 6.1
  1490.      */
  1491.     public abstract LoveNumbers getLoveNumbers()
  1492.         throws OrekitException;

  1493.     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1494.      * @param ut1 UT1 time scale
  1495.      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1496.      * @exception OrekitException if table cannot be loaded
  1497.      * @since 6.1
  1498.      */
  1499.     public abstract TimeFunction<double[]> getTideFrequencyDependenceFunction(TimeScale ut1)
  1500.         throws OrekitException;

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

  1506.     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
  1507.      * @param eopHistory EOP history
  1508.      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1509.      * @exception OrekitException if table cannot be loaded
  1510.      * @since 6.1
  1511.      */
  1512.     public abstract TimeFunction<double[]> getSolidPoleTide(EOPHistory eopHistory)
  1513.         throws OrekitException;

  1514.     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
  1515.      * @param eopHistory EOP history
  1516.      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
  1517.      * @exception OrekitException if table cannot be loaded
  1518.      * @since 6.1
  1519.      */
  1520.     public abstract TimeFunction<double[]> getOceanPoleTide(EOPHistory eopHistory)
  1521.         throws OrekitException;

  1522.     /** Interface for functions converting nutation corrections between
  1523.      * &delta;&Delta;&psi;/&delta;&Delta;&epsilon; to &delta;X/&delta;Y.
  1524.      * <ul>
  1525.      * <li>&delta;&Delta;&psi;/&delta;&Delta;&epsilon; nutation corrections are used with the equinox-based paradigm.</li>
  1526.      * <li>&delta;X/&delta;Y nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1527.      * </ul>
  1528.      * @since 6.1
  1529.      */
  1530.     public interface NutationCorrectionConverter {

  1531.         /** Convert nutation corrections.
  1532.          * @param date current date
  1533.          * @param ddPsi &delta;&Delta;&psi; part of the nutation correction
  1534.          * @param ddEpsilon &delta;&Delta;&epsilon; part of the nutation correction
  1535.          * @return array containing &delta;X and &delta;Y
  1536.          * @exception OrekitException if correction cannot be converted
  1537.          */
  1538.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon)
  1539.             throws OrekitException;

  1540.         /** Convert nutation corrections.
  1541.          * @param date current date
  1542.          * @param dX &delta;X part of the nutation correction
  1543.          * @param dY &delta;Y part of the nutation correction
  1544.          * @return array containing &delta;&Delta;&psi; and &delta;&Delta;&epsilon;
  1545.          * @exception OrekitException if correction cannot be converted
  1546.          */
  1547.         double[] toEquinox(AbsoluteDate date, double dX, double dY)
  1548.             throws OrekitException;

  1549.     }

  1550.     /** Create a function converting nutation corrections between
  1551.      * &delta;X/&delta;Y and &delta;&Delta;&psi;/&delta;&Delta;&epsilon;.
  1552.      * <ul>
  1553.      * <li>&delta;X/&delta;Y nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1554.      * <li>&delta;&Delta;&psi;/&delta;&Delta;&epsilon; nutation corrections are used with the equinox-based paradigm.</li>
  1555.      * </ul>
  1556.      * @return a new converter
  1557.      * @exception OrekitException if some convention table cannot be loaded
  1558.      * @since 6.1
  1559.      */
  1560.     public NutationCorrectionConverter getNutationCorrectionConverter()
  1561.         throws OrekitException {

  1562.         // get models parameters
  1563.         final TimeFunction<double[]> precessionFunction = getPrecessionFunction();
  1564.         final TimeFunction<Double> epsilonAFunction = getMeanObliquityFunction();
  1565.         final AbsoluteDate date0 = getNutationReferenceEpoch();
  1566.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  1567.         return new NutationCorrectionConverter() {

  1568.             /** {@inheritDoc} */
  1569.             @Override
  1570.             public double[] toNonRotating(final AbsoluteDate date,
  1571.                                           final double ddPsi, final double ddEpsilon)
  1572.                 throws OrekitException {
  1573.                 // compute precession angles psiA, omegaA and chiA
  1574.                 final double[] angles = precessionFunction.value(date);

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

  1578.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  1579.                 return new double[] {
  1580.                     sinEA * ddPsi + c * ddEpsilon,
  1581.                     ddEpsilon - c * sinEA * ddPsi
  1582.                 };

  1583.             }

  1584.             /** {@inheritDoc} */
  1585.             @Override
  1586.             public double[] toEquinox(final AbsoluteDate date,
  1587.                                       final double dX, final double dY)
  1588.                 throws OrekitException {
  1589.                 // compute precession angles psiA, omegaA and chiA
  1590.                 final double[] angles   = precessionFunction.value(date);

  1591.                 // conversion coefficients
  1592.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  1593.                 final double c     = angles[0] * cosE0 - angles[2];
  1594.                 final double opC2  = 1 + c * c;

  1595.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  1596.                 return new double[] {
  1597.                     (dX - c * dY) / (sinEA * opC2),
  1598.                     (dY + c * dX) / opC2
  1599.                 };
  1600.             }

  1601.         };

  1602.     }

  1603.     /** Load the Love numbers.
  1604.      * @param nameLove name of the Love number resource
  1605.      * @return Love numbers
  1606.      * @exception OrekitException if the Love numbers embedded in the
  1607.      * library cannot be read
  1608.      */
  1609.     protected LoveNumbers loadLoveNumbers(final String nameLove) throws OrekitException {
  1610.         InputStream stream = null;
  1611.         BufferedReader reader = null;
  1612.         try {

  1613.             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
  1614.             final double[][] real      = new double[4][];
  1615.             final double[][] imaginary = new double[4][];
  1616.             final double[][] plus      = new double[4][];
  1617.             for (int i = 0; i < real.length; ++i) {
  1618.                 real[i]      = new double[i + 1];
  1619.                 imaginary[i] = new double[i + 1];
  1620.                 plus[i]      = new double[i + 1];
  1621.             }

  1622.             stream = IERSConventions.class.getResourceAsStream(nameLove);
  1623.             if (stream == null) {
  1624.                 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  1625.             }

  1626.             // setup the reader
  1627.             reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));

  1628.             String line = reader.readLine();
  1629.             int lineNumber = 1;

  1630.             // look for the Love numbers
  1631.             while (line != null) {

  1632.                 line = line.trim();
  1633.                 if (!(line.isEmpty() || line.startsWith("#"))) {
  1634.                     final String[] fields = line.split("\\p{Space}+");
  1635.                     if (fields.length != 5) {
  1636.                         // this should never happen with files embedded within Orekit
  1637.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  1638.                                                   lineNumber, nameLove, line);
  1639.                     }
  1640.                     final int n = Integer.parseInt(fields[0]);
  1641.                     final int m = Integer.parseInt(fields[1]);
  1642.                     if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
  1643.                         // this should never happen with files embedded within Orekit
  1644.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  1645.                                                   lineNumber, nameLove, line);

  1646.                     }
  1647.                     real[n][m]      = Double.parseDouble(fields[2]);
  1648.                     imaginary[n][m] = Double.parseDouble(fields[3]);
  1649.                     plus[n][m]      = Double.parseDouble(fields[4]);
  1650.                 }

  1651.                 // next line
  1652.                 lineNumber++;
  1653.                 line = reader.readLine();

  1654.             }

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

  1656.         } catch (IOException ioe) {
  1657.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  1658.         } finally {
  1659.             try {
  1660.                 if (stream != null) {
  1661.                     stream.close();
  1662.                 }
  1663.                 if (reader != null) {
  1664.                     reader.close();
  1665.                 }
  1666.             } catch (IOException ioe) {
  1667.                 // ignored here
  1668.             }
  1669.         }
  1670.     }

  1671.     /** Get a data stream.
  1672.      * @param name file name of the resource stream
  1673.      * @return stream
  1674.      */
  1675.     private static InputStream getStream(final String name) {
  1676.         return IERSConventions.class.getResourceAsStream(name);
  1677.     }

  1678.     /** Correction to equation of equinoxes.
  1679.      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
  1680.      * taking effect since 1997-02-27 for continuity
  1681.      * </p>
  1682.      */
  1683.     private static class IAU1994ResolutionC7 {

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

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

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

  1693.         /** Evaluate the correction.
  1694.          * @param arguments Delaunay for nutation
  1695.          * @return correction value (0 before 1997-02-27)
  1696.          */
  1697.         public static double value(final DelaunayArguments arguments) {
  1698.             if (arguments.getDate().compareTo(MODEL_START) >= 0) {

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

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

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

  1705.             } else {
  1706.                 return 0.0;
  1707.             }
  1708.         }

  1709.     };

  1710.     /** Stellar angle model.
  1711.      * <p>
  1712.      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
  1713.      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
  1714.      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
  1715.      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
  1716.      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
  1717.      * Guinot, B., and McCarthy, D. D., 2000, “,” Astronomy and Astrophysics, 355(1), pp. 398–405.
  1718.      * </p>
  1719.      * <p>
  1720.      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
  1721.      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
  1722.      * IERS conventions 2003 and 2010.
  1723.      * </p>
  1724.      */
  1725.     private static class StellarAngleCapitaine implements TimeFunction<DerivativeStructure> {

  1726.         /** Reference date of Capitaine's Earth Rotation Angle model. */
  1727.         private static final AbsoluteDate REFERENCE_DATE = new AbsoluteDate(DateComponents.J2000_EPOCH,
  1728.                                                                             TimeComponents.H12,
  1729.                                                                             TimeScalesFactory.getTAI());

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

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

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

  1738.         /** Total rate term of Capitaine's Earth Rotation Angle model. */
  1739.         private static final double ERA_1AB = ERA_1A + ERA_1B;

  1740.         /** UT1 time scale. */
  1741.         private final TimeScale ut1;

  1742.         /** Simple constructor.
  1743.          * @param ut1 UT1 time scale
  1744.          */
  1745.         public StellarAngleCapitaine(final TimeScale ut1) {
  1746.             this.ut1 = ut1;
  1747.         }

  1748.         /** {@inheritDoc} */
  1749.         @Override
  1750.         public DerivativeStructure value(final AbsoluteDate date) {

  1751.             // split the date offset as a full number of days plus a smaller part
  1752.             final int secondsInDay = 86400;
  1753.             final double dt  = date.durationFrom(REFERENCE_DATE);
  1754.             final long days  = ((long) dt) / secondsInDay;
  1755.             final double dtA = secondsInDay * days;
  1756.             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);

  1757.             return new DerivativeStructure(1, 1,
  1758.                                            ERA_0 + ERA_1A * dtB + ERA_1B * (dtA + dtB),
  1759.                                            ERA_1AB);

  1760.         }

  1761.     }

  1762.     /** Mean pole. */
  1763.     private static class MeanPole implements TimeStamped, Serializable {

  1764.         /** Serializable UID. */
  1765.         private static final long serialVersionUID = 20131028l;

  1766.         /** Date. */
  1767.         private final AbsoluteDate date;

  1768.         /** X coordinate. */
  1769.         private double x;

  1770.         /** Y coordinate. */
  1771.         private double y;

  1772.         /** Simple constructor.
  1773.          * @param date date
  1774.          * @param x x coordinate
  1775.          * @param y y coordinate
  1776.          */
  1777.         public MeanPole(final AbsoluteDate date, final double x, final double y) {
  1778.             this.date = date;
  1779.             this.x    = x;
  1780.             this.y    = y;
  1781.         }

  1782.         /** {@inheritDoc} */
  1783.         @Override
  1784.         public AbsoluteDate getDate() {
  1785.             return date;
  1786.         }

  1787.         /** Get x coordinate.
  1788.          * @return x coordinate
  1789.          */
  1790.         public double getX() {
  1791.             return x;
  1792.         }

  1793.         /** Get y coordinate.
  1794.          * @return y coordinate
  1795.          */
  1796.         public double getY() {
  1797.             return y;
  1798.         }

  1799.     }
  1800. }