IERSConventions.java

  1. /* Copyright 2002-2016 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.OrekitInternalError;
  40. import org.orekit.errors.OrekitMessages;
  41. import org.orekit.errors.TimeStampedCacheException;
  42. import org.orekit.frames.EOPHistory;
  43. import org.orekit.frames.PoleCorrection;
  44. import org.orekit.time.AbsoluteDate;
  45. import org.orekit.time.DateComponents;
  46. import org.orekit.time.TimeComponents;
  47. import org.orekit.time.TimeFunction;
  48. import org.orekit.time.TimeScale;
  49. import org.orekit.time.TimeScalesFactory;
  50. import org.orekit.time.TimeStamped;


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

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

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

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

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

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

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

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

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

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

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

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

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

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

  92.             return new TimeFunction<Double>() {

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

  98.             };

  99.         }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  178.                 }

  179.             };

  180.         }

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

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

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

  214.         }

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

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

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

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

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

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

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

  249.         }

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

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

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

  264.             return new TimeFunction<DerivativeStructure>() {

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

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

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

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

  278.                 }

  279.             };

  280.         }

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

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

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

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

  292.             return new TimeFunction<DerivativeStructure>() {

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

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

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

  305.                 }

  306.             };

  307.         }

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

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

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

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

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

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

  359.         }

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

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

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

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

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

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

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

  416.         }

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

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

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

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

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

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

  455.     },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  501.             return new TimeFunction<Double>() {

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

  507.             };

  508.         }

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

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

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

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

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

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

  535.             };

  536.         }


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

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

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

  567.         }

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

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

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

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

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

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

  621.         }

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

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

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

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

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

  646.             };

  647.         }

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

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

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

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

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

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

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

  686.             return new TimeFunction<DerivativeStructure>() {

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

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

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

  699.                 }

  700.             };

  701.         }

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

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

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

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

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

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

  752.         }

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

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

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

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

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

  799.             @SuppressWarnings("unchecked")
  800.             final PoissonSeries.CompiledSeries<DerivativeStructure> kSeries =
  801.                 PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);

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

  809.         }

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

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

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

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

  843.             // constants from IERS 2003, section 6.2
  844.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  845.             final double ratio        =  0.00115;

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

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

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

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

  884.                     }

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

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

  919.                 }
  920.             };

  921.         }

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

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

  937.     },

  938.     /** Constant for IERS 2010 conventions. */
  939.     IERS_2010 {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  972.         /** {@inheritDoc} */
  973.         @Override
  974.         public TimeFunction<Double> getMeanObliquityFunction() throws OrekitException {

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

  983.             return new TimeFunction<Double>() {

  984.                 /** {@inheritDoc} */
  985.                 @Override
  986.                 public Double value(final AbsoluteDate date) {
  987.                     return epsilonA.value(evaluateTC(date));
  988.                 }

  989.             };

  990.         }

  991.         /** {@inheritDoc} */
  992.         @Override
  993.         public TimeFunction<double[]> getXYSpXY2Function() throws OrekitException {

  994.             // set up nutation arguments
  995.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

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

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

  1016.             };

  1017.         }

  1018.         /** {@inheritDoc} */
  1019.         public LoveNumbers getLoveNumbers() throws OrekitException {
  1020.             return loadLoveNumbers(LOVE_NUMBERS);
  1021.         }

  1022.         /** {@inheritDoc} */
  1023.         public TimeFunction<double[]> getTideFrequencyDependenceFunction(final TimeScale ut1)
  1024.             throws OrekitException {

  1025.             // set up nutation arguments
  1026.             final FundamentalNutationArguments arguments = getNutationArguments(ut1);

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

  1043.             final double pico = 1.0e-12;
  1044.             final PoissonSeries<DerivativeStructure> c20Series =
  1045.                     k20Parser.
  1046.                     withSinCos(0, 18, -pico, 16, pico).
  1047.                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
  1048.             final PoissonSeries<DerivativeStructure> c21Series =
  1049.                     k21Parser.
  1050.                     withSinCos(0, 17, pico, 18, pico).
  1051.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1052.             final PoissonSeries<DerivativeStructure> s21Series =
  1053.                     k21Parser.
  1054.                     withSinCos(0, 18, -pico, 17, pico).
  1055.                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
  1056.             final PoissonSeries<DerivativeStructure> c22Series =
  1057.                     k22Parser.
  1058.                     withSinCos(0, -1, pico, 16, pico).
  1059.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
  1060.             final PoissonSeries<DerivativeStructure> s22Series =
  1061.                     k22Parser.
  1062.                     withSinCos(0, 16, -pico, -1, pico).
  1063.                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);

  1064.             @SuppressWarnings("unchecked")
  1065.             final PoissonSeries.CompiledSeries<DerivativeStructure> kSeries =
  1066.                 PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);

  1067.             return new TimeFunction<double[]>() {
  1068.                 /** {@inheritDoc} */
  1069.                 @Override
  1070.                 public double[] value(final AbsoluteDate date) {
  1071.                     return kSeries.value(arguments.evaluateAll(date));
  1072.                 }
  1073.             };

  1074.         }

  1075.         /** {@inheritDoc} */
  1076.         @Override
  1077.         public double getPermanentTide() throws OrekitException {
  1078.             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
  1079.         }

  1080.         /** Compute pole wobble variables m₁ and m₂.
  1081.          * @param date current date
  1082.          * @param eopHistory EOP history
  1083.          * @return array containing m₁ and m₂
  1084.          */
  1085.         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {

  1086.             // polynomial model from IERS 2010, table 7.7
  1087.             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
  1088.             final double f1 = f0 / Constants.JULIAN_YEAR;
  1089.             final double f2 = f1 / Constants.JULIAN_YEAR;
  1090.             final double f3 = f2 / Constants.JULIAN_YEAR;
  1091.             final AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());

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

  1119.             // evaluate wobble variables
  1120.             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
  1121.             final double m1 = correction.getXp() - meanPoleX;
  1122.             final double m2 = meanPoleY - correction.getYp();

  1123.             return new double[] {
  1124.                 m1, m2
  1125.             };

  1126.         }

  1127.         /** {@inheritDoc} */
  1128.         @Override
  1129.         public TimeFunction<double[]> getSolidPoleTide(final EOPHistory eopHistory)
  1130.             throws OrekitException {

  1131.             // constants from IERS 2010, section 6.4
  1132.             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
  1133.             final double ratio        =  0.00115;

  1134.             return new TimeFunction<double[]>() {
  1135.                 /** {@inheritDoc} */
  1136.                 @Override
  1137.                 public double[] value(final AbsoluteDate date) {

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

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

  1152.                 }
  1153.             };

  1154.         }

  1155.         /** {@inheritDoc} */
  1156.         @Override
  1157.         public TimeFunction<double[]> getOceanPoleTide(final EOPHistory eopHistory)
  1158.             throws OrekitException {

  1159.             return new TimeFunction<double[]>() {
  1160.                 /** {@inheritDoc} */
  1161.                 @Override
  1162.                 public double[] value(final AbsoluteDate date) {

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

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

  1173.                 }
  1174.             };

  1175.         }

  1176.         /** {@inheritDoc} */
  1177.         @Override
  1178.         public TimeFunction<double[]> getPrecessionFunction() throws OrekitException {

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

  1202.             return new TimeFunction<double[]>() {
  1203.                 /** {@inheritDoc} */
  1204.                 @Override
  1205.                 public double[] value(final AbsoluteDate date) {
  1206.                     final double tc = evaluateTC(date);
  1207.                     return new double[] {
  1208.                         psiA.value(tc), omegaA.value(tc), chiA.value(tc)
  1209.                     };
  1210.                 }
  1211.             };

  1212.         }

  1213.          /** {@inheritDoc} */
  1214.         @Override
  1215.         public TimeFunction<double[]> getNutationFunction()
  1216.             throws OrekitException {

  1217.             // set up nutation arguments
  1218.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

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

  1242.         }

  1243.         /** {@inheritDoc} */
  1244.         @Override
  1245.         public TimeFunction<DerivativeStructure> getGMSTFunction(final TimeScale ut1) throws OrekitException {

  1246.             // Earth Rotation Angle
  1247.             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);

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

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

  1261.                 /** {@inheritDoc} */
  1262.                 @Override
  1263.                 public DerivativeStructure value(final AbsoluteDate date) {
  1264.                     return era.value(date).add(minusEO.value(dsEvaluateTC(date)));
  1265.                 }

  1266.             };

  1267.         }

  1268.         /** {@inheritDoc} */
  1269.         @Override
  1270.         public TimeFunction<DerivativeStructure> getGASTFunction(final TimeScale ut1,
  1271.                                                                  final EOPHistory eopHistory)
  1272.             throws OrekitException {

  1273.             // set up nutation arguments
  1274.             final FundamentalNutationArguments arguments = getNutationArguments(null);

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

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

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

  1292.             return new TimeFunction<DerivativeStructure>() {

  1293.                 /** {@inheritDoc} */
  1294.                 @Override
  1295.                 public DerivativeStructure value(final AbsoluteDate date) {

  1296.                     // evaluate equation of origins
  1297.                     final BodiesElements elements = arguments.evaluateAll(date);
  1298.                     final double[] angles = psiGstSeries.value(elements);
  1299.                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
  1300.                     final double deltaPsi = angles[0] + ddPsi;
  1301.                     final double epsilonA = epsilon.value(date);

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

  1305.                 }

  1306.             };

  1307.         }

  1308.         /** {@inheritDoc} */
  1309.         @Override
  1310.         public TimeFunction<double[]> getEOPTidalCorrection()
  1311.             throws OrekitException {

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

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

  1332.             final double microS = 1.0e-6;
  1333.             final PoissonSeriesParser<DerivativeStructure> ut1Parser = new PoissonSeriesParser<DerivativeStructure>(11).
  1334.                     withOptionalColumn(1).
  1335.                     withGamma(2).
  1336.                     withFirstDelaunay(3).
  1337.                     withSinCos(0, 10, microS, 11, microS);
  1338.             final PoissonSeries<DerivativeStructure> ut1Series =
  1339.                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);

  1340.             @SuppressWarnings("unchecked")
  1341.             final PoissonSeries.CompiledSeries<DerivativeStructure> correctionSeries =
  1342.                     PoissonSeries.compile(xSeries, ySeries, ut1Series);

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

  1358.         }

  1359.     };

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

  1362.     /** Get the reference epoch for fundamental nutation arguments.
  1363.      * @return reference epoch for fundamental nutation arguments
  1364.      * @since 6.1
  1365.      */
  1366.     public AbsoluteDate getNutationReferenceEpoch() {
  1367.         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
  1368.         return AbsoluteDate.J2000_EPOCH;
  1369.     }

  1370.     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
  1371.      * @param date current date
  1372.      * @return date offset in Julian centuries
  1373.      * @since 6.1
  1374.      */
  1375.     public double evaluateTC(final AbsoluteDate date) {
  1376.         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
  1377.     }

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

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

  1395.     /** Get the function computing mean obliquity of the ecliptic.
  1396.      * @return function computing mean obliquity of the ecliptic
  1397.      * @exception OrekitException if table cannot be loaded
  1398.      * @since 6.1
  1399.      */
  1400.     public abstract TimeFunction<Double> getMeanObliquityFunction() throws OrekitException;

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

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


  1426.     /** Get the function computing the precession angles.
  1427.      * <p>
  1428.      * The function returned computes the three precession angles
  1429.      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
  1430.      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
  1431.      * for the fourth rotation (around X axis) can be retrieved by evaluating the
  1432.      * function returned by {@link #getMeanObliquityFunction()} at {@link
  1433.      * #getNutationReferenceEpoch() nutation reference epoch}.
  1434.      * </p>
  1435.      * @return function computing the precession angle
  1436.      * @exception OrekitException if table cannot be loaded
  1437.      * @since 6.1
  1438.      */
  1439.     public abstract TimeFunction<double[]> getPrecessionFunction() throws OrekitException;

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

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

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

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

  1481.     /** Get the Love numbers.
  1482.      * @return Love numbers
  1483.      * @exception OrekitException if table cannot be loaded
  1484.      * @since 6.1
  1485.      */
  1486.     public abstract LoveNumbers getLoveNumbers()
  1487.         throws OrekitException;

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

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

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

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

  1517.     /** Interface for functions converting nutation corrections between
  1518.      * δΔψ/δΔε to δX/δY.
  1519.      * <ul>
  1520.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  1521.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1522.      * </ul>
  1523.      * @since 6.1
  1524.      */
  1525.     public interface NutationCorrectionConverter {

  1526.         /** Convert nutation corrections.
  1527.          * @param date current date
  1528.          * @param ddPsi δΔψ part of the nutation correction
  1529.          * @param ddEpsilon δΔε part of the nutation correction
  1530.          * @return array containing δX and δY
  1531.          * @exception OrekitException if correction cannot be converted
  1532.          */
  1533.         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon)
  1534.             throws OrekitException;

  1535.         /** Convert nutation corrections.
  1536.          * @param date current date
  1537.          * @param dX δX part of the nutation correction
  1538.          * @param dY δY part of the nutation correction
  1539.          * @return array containing δΔψ and δΔε
  1540.          * @exception OrekitException if correction cannot be converted
  1541.          */
  1542.         double[] toEquinox(AbsoluteDate date, double dX, double dY)
  1543.             throws OrekitException;

  1544.     }

  1545.     /** Create a function converting nutation corrections between
  1546.      * δX/δY and δΔψ/δΔε.
  1547.      * <ul>
  1548.      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
  1549.      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
  1550.      * </ul>
  1551.      * @return a new converter
  1552.      * @exception OrekitException if some convention table cannot be loaded
  1553.      * @since 6.1
  1554.      */
  1555.     public NutationCorrectionConverter getNutationCorrectionConverter()
  1556.         throws OrekitException {

  1557.         // get models parameters
  1558.         final TimeFunction<double[]> precessionFunction = getPrecessionFunction();
  1559.         final TimeFunction<Double> epsilonAFunction = getMeanObliquityFunction();
  1560.         final AbsoluteDate date0 = getNutationReferenceEpoch();
  1561.         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));

  1562.         return new NutationCorrectionConverter() {

  1563.             /** {@inheritDoc} */
  1564.             @Override
  1565.             public double[] toNonRotating(final AbsoluteDate date,
  1566.                                           final double ddPsi, final double ddEpsilon)
  1567.                 throws OrekitException {
  1568.                 // compute precession angles psiA, omegaA and chiA
  1569.                 final double[] angles = precessionFunction.value(date);

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

  1573.                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
  1574.                 return new double[] {
  1575.                     sinEA * ddPsi + c * ddEpsilon,
  1576.                     ddEpsilon - c * sinEA * ddPsi
  1577.                 };

  1578.             }

  1579.             /** {@inheritDoc} */
  1580.             @Override
  1581.             public double[] toEquinox(final AbsoluteDate date,
  1582.                                       final double dX, final double dY)
  1583.                 throws OrekitException {
  1584.                 // compute precession angles psiA, omegaA and chiA
  1585.                 final double[] angles   = precessionFunction.value(date);

  1586.                 // conversion coefficients
  1587.                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
  1588.                 final double c     = angles[0] * cosE0 - angles[2];
  1589.                 final double opC2  = 1 + c * c;

  1590.                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
  1591.                 return new double[] {
  1592.                     (dX - c * dY) / (sinEA * opC2),
  1593.                     (dY + c * dX) / opC2
  1594.                 };
  1595.             }

  1596.         };

  1597.     }

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

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

  1617.             stream = IERSConventions.class.getResourceAsStream(nameLove);
  1618.             if (stream == null) {
  1619.                 // this should never happen with files embedded within Orekit
  1620.                 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
  1621.             }

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

  1624.             String line = reader.readLine();
  1625.             int lineNumber = 1;

  1626.             // look for the Love numbers
  1627.             while (line != null) {

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

  1642.                     }
  1643.                     real[n][m]      = Double.parseDouble(fields[2]);
  1644.                     imaginary[n][m] = Double.parseDouble(fields[3]);
  1645.                     plus[n][m]      = Double.parseDouble(fields[4]);
  1646.                 }

  1647.                 // next line
  1648.                 lineNumber++;
  1649.                 line = reader.readLine();

  1650.             }

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

  1652.         } catch (IOException ioe) {
  1653.             // this should never happen with files embedded within Orekit
  1654.             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
  1655.         } finally {
  1656.             try {
  1657.                 if (stream != null) {
  1658.                     stream.close();
  1659.                 }
  1660.                 if (reader != null) {
  1661.                     reader.close();
  1662.                 }
  1663.             } catch (IOException ioe) {
  1664.                 // ignored here
  1665.             }
  1666.         }
  1667.     }

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

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

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

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

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

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

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

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

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

  1702.             } else {
  1703.                 return 0.0;
  1704.             }
  1705.         }

  1706.     };

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

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

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

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

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

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

  1737.         /** UT1 time scale. */
  1738.         private final TimeScale ut1;

  1739.         /** Simple constructor.
  1740.          * @param ut1 UT1 time scale
  1741.          */
  1742.         StellarAngleCapitaine(final TimeScale ut1) {
  1743.             this.ut1 = ut1;
  1744.         }

  1745.         /** {@inheritDoc} */
  1746.         @Override
  1747.         public DerivativeStructure value(final AbsoluteDate date) {

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

  1754.             return new DerivativeStructure(1, 1,
  1755.                                            ERA_0 + ERA_1A * dtB + ERA_1B * (dtA + dtB),
  1756.                                            ERA_1AB);

  1757.         }

  1758.     }

  1759.     /** Mean pole. */
  1760.     private static class MeanPole implements TimeStamped, Serializable {

  1761.         /** Serializable UID. */
  1762.         private static final long serialVersionUID = 20131028l;

  1763.         /** Date. */
  1764.         private final AbsoluteDate date;

  1765.         /** X coordinate. */
  1766.         private double x;

  1767.         /** Y coordinate. */
  1768.         private double y;

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

  1779.         /** {@inheritDoc} */
  1780.         @Override
  1781.         public AbsoluteDate getDate() {
  1782.             return date;
  1783.         }

  1784.         /** Get x coordinate.
  1785.          * @return x coordinate
  1786.          */
  1787.         public double getX() {
  1788.             return x;
  1789.         }

  1790.         /** Get y coordinate.
  1791.          * @return y coordinate
  1792.          */
  1793.         public double getY() {
  1794.             return y;
  1795.         }

  1796.     }
  1797. }