1   /* Copyright 2002-2019 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  
19  import java.io.BufferedReader;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.InputStreamReader;
23  import java.io.Serializable;
24  import java.util.List;
25  
26  import org.hipparchus.RealFieldElement;
27  import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
28  import org.hipparchus.analysis.interpolation.HermiteInterpolator;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.MathArrays;
31  import org.hipparchus.util.MathUtils;
32  import org.orekit.data.BodiesElements;
33  import org.orekit.data.DelaunayArguments;
34  import org.orekit.data.FieldBodiesElements;
35  import org.orekit.data.FieldDelaunayArguments;
36  import org.orekit.data.FundamentalNutationArguments;
37  import org.orekit.data.PoissonSeries;
38  import org.orekit.data.PoissonSeries.CompiledSeries;
39  import org.orekit.data.PoissonSeriesParser;
40  import org.orekit.data.PolynomialNutation;
41  import org.orekit.data.PolynomialParser;
42  import org.orekit.data.PolynomialParser.Unit;
43  import org.orekit.data.SimpleTimeStampedTableParser;
44  import org.orekit.errors.OrekitException;
45  import org.orekit.errors.OrekitInternalError;
46  import org.orekit.errors.OrekitMessages;
47  import org.orekit.errors.TimeStampedCacheException;
48  import org.orekit.frames.EOPHistory;
49  import org.orekit.frames.FieldPoleCorrection;
50  import org.orekit.frames.PoleCorrection;
51  import org.orekit.time.AbsoluteDate;
52  import org.orekit.time.DateComponents;
53  import org.orekit.time.FieldAbsoluteDate;
54  import org.orekit.time.TimeComponents;
55  import org.orekit.time.TimeScalarFunction;
56  import org.orekit.time.TimeScale;
57  import org.orekit.time.TimeScalesFactory;
58  import org.orekit.time.TimeStamped;
59  import org.orekit.time.TimeVectorFunction;
60  
61  
62  /** Supported IERS conventions.
63   * @since 6.0
64   * @author Luc Maisonobe
65   */
66  public enum IERSConventions {
67  
68      /** Constant for IERS 1996 conventions. */
69      IERS_1996 {
70  
71          /** Nutation arguments resources. */
72          private static final String NUTATION_ARGUMENTS = IERS_BASE + "1996/nutation-arguments.txt";
73  
74          /** X series resources. */
75          private static final String X_Y_SERIES         = IERS_BASE + "1996/tab5.4.txt";
76  
77          /** Psi series resources. */
78          private static final String PSI_EPSILON_SERIES = IERS_BASE + "1996/tab5.1.txt";
79  
80          /** Tidal correction for xp, yp series resources. */
81          private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "1996/tab8.4.txt";
82  
83          /** Tidal correction for UT1 resources. */
84          private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "1996/tab8.3.txt";
85  
86          /** Love numbers resources. */
87          private static final String LOVE_NUMBERS = IERS_BASE + "1996/tab6.1.txt";
88  
89          /** Frequency dependence model for k₂₀. */
90          private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2b.txt";
91  
92          /** Frequency dependence model for k₂₁. */
93          private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2a.txt";
94  
95          /** Frequency dependence model for k₂₂. */
96          private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "1996/tab6.2c.txt";
97  
98          /** Tidal displacement frequency correction for diurnal tides. */
99          private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "1996/tab7.3a.txt";
100 
101         /** Tidal displacement frequency correction for zonal tides. */
102         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "1996/tab7.3b.txt";
103 
104         /** {@inheritDoc} */
105         @Override
106         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
107             return new FundamentalNutationArguments(this, timeScale,
108                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
109         }
110 
111         /** {@inheritDoc} */
112         @Override
113         public TimeScalarFunction getMeanObliquityFunction() {
114 
115             // value from chapter 5, page 22
116             final PolynomialNutation epsilonA =
117                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
118                                              -46.8150   * Constants.ARC_SECONDS_TO_RADIANS,
119                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
120                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);
121 
122             return new TimeScalarFunction() {
123 
124                 /** {@inheritDoc} */
125                 @Override
126                 public double value(final AbsoluteDate date) {
127                     return epsilonA.value(evaluateTC(date));
128                 }
129 
130                 /** {@inheritDoc} */
131                 @Override
132                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
133                     return epsilonA.value(evaluateTC(date));
134                 }
135 
136             };
137 
138         }
139 
140         /** {@inheritDoc} */
141         @Override
142         public TimeVectorFunction getXYSpXY2Function() {
143 
144             // set up nutation arguments
145             final FundamentalNutationArguments arguments = getNutationArguments(null);
146 
147             // X = 2004.3109″t - 0.42665″t² - 0.198656″t³ + 0.0000140″t⁴
148             //     + 0.00006″t² cos Ω + sin ε0 { Σ [(Ai + Ai' t) sin(ARGUMENT) + Ai'' t cos(ARGUMENT)]}
149             //     + 0.00204″t² sin Ω + 0.00016″t² sin 2(F - D + Ω),
150             final PolynomialNutation xPolynomial =
151                     new PolynomialNutation(0,
152                                            2004.3109 * Constants.ARC_SECONDS_TO_RADIANS,
153                                            -0.42665  * Constants.ARC_SECONDS_TO_RADIANS,
154                                            -0.198656 * Constants.ARC_SECONDS_TO_RADIANS,
155                                            0.0000140 * Constants.ARC_SECONDS_TO_RADIANS);
156 
157             final double fXCosOm    = 0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
158             final double fXSinOm    = 0.00204 * Constants.ARC_SECONDS_TO_RADIANS;
159             final double fXSin2FDOm = 0.00016 * Constants.ARC_SECONDS_TO_RADIANS;
160             final double sinEps0   = FastMath.sin(getMeanObliquityFunction().value(getNutationReferenceEpoch()));
161 
162             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
163             final PoissonSeriesParser baseParser =
164                     new PoissonSeriesParser(12).withFirstDelaunay(1);
165 
166             final PoissonSeriesParser xParser =
167                     baseParser.
168                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
169                     withSinCos(1, 8, deciMilliAS,  9, deciMilliAS);
170             final PoissonSeries xSum = xParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);
171 
172             // Y = -0.00013″ - 22.40992″t² + 0.001836″t³ + 0.0011130″t⁴
173             //     + Σ [(Bi + Bi' t) cos(ARGUMENT) + Bi'' t sin(ARGUMENT)]
174             //    - 0.00231″t² cos Ω − 0.00014″t² cos 2(F - D + Ω)
175             final PolynomialNutation yPolynomial =
176                     new PolynomialNutation(-0.00013  * Constants.ARC_SECONDS_TO_RADIANS,
177                                            0.0,
178                                            -22.40992 * Constants.ARC_SECONDS_TO_RADIANS,
179                                            0.001836  * Constants.ARC_SECONDS_TO_RADIANS,
180                                            0.0011130 * Constants.ARC_SECONDS_TO_RADIANS);
181 
182             final double fYCosOm    = -0.00231 * Constants.ARC_SECONDS_TO_RADIANS;
183             final double fYCos2FDOm = -0.00014 * Constants.ARC_SECONDS_TO_RADIANS;
184 
185             final PoissonSeriesParser yParser =
186                     baseParser.
187                     withSinCos(0, -1, deciMilliAS, 10, deciMilliAS).
188                     withSinCos(1, 12, deciMilliAS, 11, deciMilliAS);
189             final PoissonSeries ySum = yParser.parse(getStream(X_Y_SERIES), X_Y_SERIES);
190 
191             final PoissonSeries.CompiledSeries xySum =
192                     PoissonSeries.compile(xSum, ySum);
193 
194             // s = -XY/2 + 0.00385″t - 0.07259″t³ - 0.00264″ sin Ω - 0.00006″ sin 2Ω
195             //     + 0.00074″t² sin Ω + 0.00006″t² sin 2(F - D + Ω)
196             final double fST          =  0.00385 * Constants.ARC_SECONDS_TO_RADIANS;
197             final double fST3         = -0.07259 * Constants.ARC_SECONDS_TO_RADIANS;
198             final double fSSinOm      = -0.00264 * Constants.ARC_SECONDS_TO_RADIANS;
199             final double fSSin2Om     = -0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
200             final double fST2SinOm    =  0.00074 * Constants.ARC_SECONDS_TO_RADIANS;
201             final double fST2Sin2FDOm =  0.00006 * Constants.ARC_SECONDS_TO_RADIANS;
202 
203             return new TimeVectorFunction() {
204 
205                 /** {@inheritDoc} */
206                 @Override
207                 public double[] value(final AbsoluteDate date) {
208 
209                     final BodiesElements elements = arguments.evaluateAll(date);
210                     final double[] xy             = xySum.value(elements);
211 
212                     final double omega     = elements.getOmega();
213                     final double f         = elements.getF();
214                     final double d         = elements.getD();
215                     final double t         = elements.getTC();
216 
217                     final double cosOmega  = FastMath.cos(omega);
218                     final double sinOmega  = FastMath.sin(omega);
219                     final double sin2Omega = FastMath.sin(2 * omega);
220                     final double cos2FDOm  = FastMath.cos(2 * (f - d + omega));
221                     final double sin2FDOm  = FastMath.sin(2 * (f - d + omega));
222 
223                     final double x = xPolynomial.value(t) + sinEps0 * xy[0] +
224                             t * t * (fXCosOm * cosOmega + fXSinOm * sinOmega + fXSin2FDOm * cos2FDOm);
225                     final double y = yPolynomial.value(t) + xy[1] +
226                             t * t * (fYCosOm * cosOmega + fYCos2FDOm * cos2FDOm);
227                     final double sPxy2 = fSSinOm * sinOmega + fSSin2Om * sin2Omega +
228                             t * (fST + t * (fST2SinOm * sinOmega + fST2Sin2FDOm * sin2FDOm + t * fST3));
229 
230                     return new double[] {
231                         x, y, sPxy2
232                     };
233 
234                 }
235 
236                 /** {@inheritDoc} */
237                 @Override
238                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
239 
240                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
241                     final T[] xy             = xySum.value(elements);
242 
243                     final T omega     = elements.getOmega();
244                     final T f         = elements.getF();
245                     final T d         = elements.getD();
246                     final T t         = elements.getTC();
247                     final T t2        = t.multiply(t);
248 
249                     final T cosOmega  = omega.cos();
250                     final T sinOmega  = omega.sin();
251                     final T sin2Omega = omega.multiply(2).sin();
252                     final T fMDPO2 = f.subtract(d).add(omega).multiply(2);
253                     final T cos2FDOm  = fMDPO2.cos();
254                     final T sin2FDOm  = fMDPO2.sin();
255 
256                     final T x = xPolynomial.value(t).
257                                 add(xy[0].multiply(sinEps0)).
258                                 add(t2.multiply(cosOmega.multiply(fXCosOm).add(sinOmega.multiply(fXSinOm)).add(cos2FDOm.multiply(fXSin2FDOm))));
259                     final T y = yPolynomial.value(t).
260                                 add(xy[1]).
261                                 add(t2.multiply(cosOmega.multiply(fYCosOm).add(cos2FDOm.multiply(fYCos2FDOm))));
262                     final T sPxy2 = sinOmega.multiply(fSSinOm).
263                                     add(sin2Omega.multiply(fSSin2Om)).
264                                     add(t.multiply(fST3).add(sinOmega.multiply(fST2SinOm)).add(sin2FDOm.multiply(fST2Sin2FDOm)).multiply(t).add(fST).multiply(t));
265 
266                     final T[] a = MathArrays.buildArray(date.getField(), 3);
267                     a[0] = x;
268                     a[1] = y;
269                     a[2] = sPxy2;
270                     return a;
271 
272                 }
273 
274             };
275 
276         }
277 
278         /** {@inheritDoc} */
279         @Override
280         public TimeVectorFunction getPrecessionFunction() {
281 
282             // set up the conventional polynomials
283             // the following values are from Lieske et al. paper:
284             // Expressions for the precession quantities based upon the IAU(1976) system of astronomical constants
285             // http://articles.adsabs.harvard.edu/full/1977A%26A....58....1L
286             // also available as equation 30 in IERS 2003 conventions
287             final PolynomialNutation psiA =
288                     new PolynomialNutation(   0.0,
289                                            5038.7784   * Constants.ARC_SECONDS_TO_RADIANS,
290                                              -1.07259  * Constants.ARC_SECONDS_TO_RADIANS,
291                                              -0.001147 * Constants.ARC_SECONDS_TO_RADIANS);
292             final PolynomialNutation omegaA =
293                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
294                                             0.0,
295                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
296                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
297             final PolynomialNutation chiA =
298                     new PolynomialNutation( 0.0,
299                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
300                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
301                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);
302 
303             return new PrecessionFunction(psiA, omegaA, chiA);
304 
305         }
306 
307         /** {@inheritDoc} */
308         @Override
309         public TimeVectorFunction getNutationFunction() {
310 
311             // set up nutation arguments
312             final FundamentalNutationArguments arguments = getNutationArguments(null);
313 
314             // set up Poisson series
315             final double deciMilliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-4;
316             final PoissonSeriesParser baseParser =
317                     new PoissonSeriesParser(10).withFirstDelaunay(1);
318 
319             final PoissonSeriesParser psiParser =
320                     baseParser.
321                     withSinCos(0, 7, deciMilliAS, -1, deciMilliAS).
322                     withSinCos(1, 8, deciMilliAS, -1, deciMilliAS);
323             final PoissonSeries psiSeries = psiParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);
324 
325             final PoissonSeriesParser epsilonParser =
326                     baseParser.
327                     withSinCos(0, -1, deciMilliAS, 9, deciMilliAS).
328                     withSinCos(1, -1, deciMilliAS, 10, deciMilliAS);
329             final PoissonSeries epsilonSeries = epsilonParser.parse(getStream(PSI_EPSILON_SERIES), PSI_EPSILON_SERIES);
330 
331             final PoissonSeries.CompiledSeries psiEpsilonSeries =
332                     PoissonSeries.compile(psiSeries, epsilonSeries);
333 
334             return new TimeVectorFunction() {
335 
336                 /** {@inheritDoc} */
337                 @Override
338                 public double[] value(final AbsoluteDate date) {
339                     final BodiesElements elements = arguments.evaluateAll(date);
340                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
341                     return new double[] {
342                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
343                     };
344                 }
345 
346                 /** {@inheritDoc} */
347                 @Override
348                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
349                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
350                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
351                     final T[] result = MathArrays.buildArray(date.getField(), 3);
352                     result[0] = psiEpsilon[0];
353                     result[1] = psiEpsilon[1];
354                     result[2] = IAU1994ResolutionC7.value(elements);
355                     return result;
356                 }
357 
358             };
359 
360         }
361 
362         /** {@inheritDoc} */
363         @Override
364         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
365 
366             // Radians per second of time
367             final double radiansPerSecond = MathUtils.TWO_PI / Constants.JULIAN_DAY;
368 
369             // constants from IERS 1996 page 21
370             // the underlying model is IAU 1982 GMST-UT1
371             final AbsoluteDate gmstReference =
372                 new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
373             final double gmst0 = 24110.54841;
374             final double gmst1 = 8640184.812866;
375             final double gmst2 = 0.093104;
376             final double gmst3 = -6.2e-6;
377 
378             return new TimeScalarFunction() {
379 
380                 /** {@inheritDoc} */
381                 @Override
382                 public double value(final AbsoluteDate date) {
383 
384                     // offset in Julian centuries from J2000 epoch (UT1 scale)
385                     final double dtai = date.durationFrom(gmstReference);
386                     final double tut1 = dtai + ut1.offsetFromTAI(date);
387                     final double tt   = tut1 / Constants.JULIAN_CENTURY;
388 
389                     // Seconds in the day, adjusted by 12 hours because the
390                     // UT1 is supplied as a Julian date beginning at noon.
391                     final double sd = FastMath.IEEEremainder(tut1 + Constants.JULIAN_DAY / 2, Constants.JULIAN_DAY);
392 
393                     // compute Greenwich mean sidereal time, in radians
394                     return ((((((tt * gmst3 + gmst2) * tt) + gmst1) * tt) + gmst0) + sd) * radiansPerSecond;
395 
396                 }
397 
398                 /** {@inheritDoc} */
399                 @Override
400                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
401 
402                     // offset in Julian centuries from J2000 epoch (UT1 scale)
403                     final T dtai = date.durationFrom(gmstReference);
404                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
405                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);
406 
407                     // Seconds in the day, adjusted by 12 hours because the
408                     // UT1 is supplied as a Julian date beginning at noon.
409                     final T sd = tut1.add(Constants.JULIAN_DAY / 2).remainder(Constants.JULIAN_DAY);
410 
411                     // compute Greenwich mean sidereal time, in radians
412                     return tt.multiply(gmst3).add(gmst2).multiply(tt).add(gmst1).multiply(tt).add(gmst0).add(sd).multiply(radiansPerSecond);
413 
414                 }
415 
416             };
417 
418         }
419 
420         /** {@inheritDoc} */
421         @Override
422         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {
423 
424             // Radians per second of time
425             final double radiansPerSecond = MathUtils.TWO_PI / Constants.JULIAN_DAY;
426 
427             // constants from IERS 1996 page 21
428             // the underlying model is IAU 1982 GMST-UT1
429             final AbsoluteDate gmstReference =
430                 new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, TimeScalesFactory.getTAI());
431             final double gmst1 = 8640184.812866;
432             final double gmst2 = 0.093104;
433             final double gmst3 = -6.2e-6;
434 
435             return new TimeScalarFunction() {
436 
437                 /** {@inheritDoc} */
438                 @Override
439                 public double value(final AbsoluteDate date) {
440 
441                     // offset in Julian centuries from J2000 epoch (UT1 scale)
442                     final double dtai = date.durationFrom(gmstReference);
443                     final double tut1 = dtai + ut1.offsetFromTAI(date);
444                     final double tt   = tut1 / Constants.JULIAN_CENTURY;
445 
446                     // compute Greenwich mean sidereal time rate, in radians per second
447                     return ((((tt * 3 * gmst3 + 2 * gmst2) * tt) + gmst1) / Constants.JULIAN_CENTURY + 1) * radiansPerSecond;
448 
449                 }
450 
451                 /** {@inheritDoc} */
452                 @Override
453                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
454 
455                     // offset in Julian centuries from J2000 epoch (UT1 scale)
456                     final T dtai = date.durationFrom(gmstReference);
457                     final T tut1 = dtai.add(ut1.offsetFromTAI(date.toAbsoluteDate()));
458                     final T tt   = tut1.divide(Constants.JULIAN_CENTURY);
459 
460                     // compute Greenwich mean sidereal time, in radians
461                     return tt.multiply(3 * gmst3).add(2 * gmst2).multiply(tt).add(gmst1).divide(Constants.JULIAN_CENTURY).add(1).multiply(radiansPerSecond);
462 
463                 }
464 
465             };
466 
467         }
468 
469         /** {@inheritDoc} */
470         @Override
471         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory) {
472 
473             // obliquity
474             final TimeScalarFunction epsilonA = getMeanObliquityFunction();
475 
476             // GMST function
477             final TimeScalarFunction gmst = getGMSTFunction(ut1);
478 
479             // nutation function
480             final TimeVectorFunction nutation = getNutationFunction();
481 
482             return new TimeScalarFunction() {
483 
484                 /** {@inheritDoc} */
485                 @Override
486                 public double value(final AbsoluteDate date) {
487 
488                     // compute equation of equinoxes
489                     final double[] angles = nutation.value(date);
490                     double deltaPsi = angles[0];
491                     if (eopHistory != null) {
492                         deltaPsi += eopHistory.getEquinoxNutationCorrection(date)[0];
493                     }
494                     final double eqe = deltaPsi  * FastMath.cos(epsilonA.value(date)) + angles[2];
495 
496                     // add mean sidereal time and equation of equinoxes
497                     return gmst.value(date) + eqe;
498 
499                 }
500 
501                 /** {@inheritDoc} */
502                 @Override
503                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
504 
505                     // compute equation of equinoxes
506                     final T[] angles = nutation.value(date);
507                     T deltaPsi = angles[0];
508                     if (eopHistory != null) {
509                         deltaPsi = deltaPsi.add(eopHistory.getEquinoxNutationCorrection(date)[0]);
510                     }
511                     final T eqe = deltaPsi.multiply(epsilonA.value(date).cos()).add(angles[2]);
512 
513                     // add mean sidereal time and equation of equinoxes
514                     return gmst.value(date).add(eqe);
515 
516                 }
517 
518             };
519 
520         }
521 
522         /** {@inheritDoc} */
523         @Override
524         public TimeVectorFunction getEOPTidalCorrection() {
525 
526             // set up nutation arguments
527             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
528             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
529             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
530             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
531             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());
532 
533             // set up Poisson series
534             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
535             final PoissonSeriesParseronSeriesParser">PoissonSeriesParser xyParser = new PoissonSeriesParser(17).
536                     withOptionalColumn(1).
537                     withGamma(7).
538                     withFirstDelaunay(2);
539             final PoissonSeries xSeries =
540                     xyParser.
541                     withSinCos(0, 14, milliAS, 15, milliAS).
542                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
543             final PoissonSeries ySeries =
544                     xyParser.
545                     withSinCos(0, 16, milliAS, 17, milliAS).
546                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES),
547                                                          TIDAL_CORRECTION_XP_YP_SERIES);
548 
549             final double deciMilliS = 1.0e-4;
550             final PoissonSeriesParsernSeriesParser">PoissonSeriesParser ut1Parser = new PoissonSeriesParser(17).
551                     withOptionalColumn(1).
552                     withGamma(7).
553                     withFirstDelaunay(2).
554                     withSinCos(0, 16, deciMilliS, 17, deciMilliS);
555             final PoissonSeries ut1Series =
556                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);
557 
558             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
559 
560         }
561 
562         /** {@inheritDoc} */
563         public LoveNumbers getLoveNumbers() {
564             return loadLoveNumbers(LOVE_NUMBERS);
565         }
566 
567         /** {@inheritDoc} */
568         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {
569 
570             // set up nutation arguments
571             final FundamentalNutationArguments arguments = getNutationArguments(ut1);
572 
573             // set up Poisson series
574             final PoissonSeriesParser k20Parser =
575                     new PoissonSeriesParser(18).
576                         withOptionalColumn(1).
577                         withDoodson(4, 2).
578                         withFirstDelaunay(10);
579             final PoissonSeriesParser k21Parser =
580                     new PoissonSeriesParser(18).
581                         withOptionalColumn(1).
582                         withDoodson(4, 3).
583                         withFirstDelaunay(10);
584             final PoissonSeriesParser k22Parser =
585                     new PoissonSeriesParser(16).
586                         withOptionalColumn(1).
587                         withDoodson(4, 2).
588                         withFirstDelaunay(10);
589 
590             final double pico = 1.0e-12;
591             final PoissonSeries c20Series =
592                     k20Parser.
593                   withSinCos(0, 18, -pico, 16, pico).
594                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
595             final PoissonSeries c21Series =
596                     k21Parser.
597                     withSinCos(0, 17, pico, 18, pico).
598                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
599             final PoissonSeries s21Series =
600                     k21Parser.
601                     withSinCos(0, 18, -pico, 17, pico).
602                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
603             final PoissonSeries c22Series =
604                     k22Parser.
605                     withSinCos(0, -1, pico, 16, pico).
606                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
607             final PoissonSeries s22Series =
608                     k22Parser.
609                     withSinCos(0, 16, -pico, -1, pico).
610                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
611 
612             return new TideFrequencyDependenceFunction(arguments,
613                                                        c20Series,
614                                                        c21Series, s21Series,
615                                                        c22Series, s22Series);
616 
617         }
618 
619         /** {@inheritDoc} */
620         @Override
621         public double getPermanentTide() {
622             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
623         }
624 
625         /** {@inheritDoc} */
626         @Override
627         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {
628 
629             // constants from IERS 1996 page 47
630             final double globalFactor = -1.348e-9 / Constants.ARC_SECONDS_TO_RADIANS;
631             final double coupling     =  0.00112;
632 
633             return new TimeVectorFunction() {
634 
635                 /** {@inheritDoc} */
636                 @Override
637                 public double[] value(final AbsoluteDate date) {
638                     final PoleCorrection pole = eopHistory.getPoleCorrection(date);
639                     return new double[] {
640                         globalFactor * (pole.getXp() + coupling * pole.getYp()),
641                         globalFactor * (coupling * pole.getXp() - pole.getYp()),
642                     };
643                 }
644 
645                 /** {@inheritDoc} */
646                 @Override
647                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
648                     final FieldPoleCorrection<T> pole = eopHistory.getPoleCorrection(date);
649                     final T[] a = MathArrays.buildArray(date.getField(), 2);
650                     a[0] = pole.getXp().add(pole.getYp().multiply(coupling)).multiply(globalFactor);
651                     a[1] = pole.getXp().multiply(coupling).subtract(pole.getYp()).multiply(globalFactor);
652                     return a;
653                 }
654 
655             };
656         }
657 
658         /** {@inheritDoc} */
659         @Override
660         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {
661 
662             return new TimeVectorFunction() {
663 
664                 /** {@inheritDoc} */
665                 @Override
666                 public double[] value(final AbsoluteDate date) {
667                     // there are no model for ocean pole tide prior to conventions 2010
668                     return new double[] {
669                         0.0, 0.0
670                     };
671                 }
672 
673                 /** {@inheritDoc} */
674                 @Override
675                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
676                     // there are no model for ocean pole tide prior to conventions 2010
677                     return MathArrays.buildArray(date.getField(), 2);
678                 }
679 
680             };
681         }
682 
683         /** {@inheritDoc} */
684         @Override
685         public double[] getNominalTidalDisplacement() {
686 
687             //  // elastic Earth values
688             //  return new double[] {
689             //      // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
690             //      0.6026,                                  -0.0006, 0.292, -0.0025, -0.0022,
691             //      // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
692             //      0.0831,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
693             //      // H₀
694             //      -0.31460
695             //  };
696 
697             // anelastic Earth values
698             return new double[] {
699                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
700                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
701                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
702                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
703                 // H₀
704                 -0.31460
705             };
706 
707         }
708 
709         /** {@inheritDoc} */
710         @Override
711         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
712             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
713                                                                   18, 17, -1, 18, -1);
714         }
715 
716         /** {@inheritDoc} */
717         @Override
718         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
719             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
720                                                                 20, 17, 19, 18, 20);
721         }
722 
723     },
724 
725     /** Constant for IERS 2003 conventions. */
726     IERS_2003 {
727 
728         /** Nutation arguments resources. */
729         private static final String NUTATION_ARGUMENTS = IERS_BASE + "2003/nutation-arguments.txt";
730 
731         /** X series resources. */
732         private static final String X_SERIES           = IERS_BASE + "2003/tab5.2a.txt";
733 
734         /** Y series resources. */
735         private static final String Y_SERIES           = IERS_BASE + "2003/tab5.2b.txt";
736 
737         /** S series resources. */
738         private static final String S_SERIES           = IERS_BASE + "2003/tab5.2c.txt";
739 
740         /** Luni-solar series resources. */
741         private static final String LUNI_SOLAR_SERIES  = IERS_BASE + "2003/tab5.3a-first-table.txt";
742 
743         /** Planetary series resources. */
744         private static final String PLANETARY_SERIES   = IERS_BASE + "2003/tab5.3b.txt";
745 
746         /** Greenwhich sidereal time series resources. */
747         private static final String GST_SERIES         = IERS_BASE + "2003/tab5.4.txt";
748 
749         /** Tidal correction for xp, yp series resources. */
750         private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "2003/tab8.2ab.txt";
751 
752         /** Tidal correction for UT1 resources. */
753         private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "2003/tab8.3ab.txt";
754 
755         /** Love numbers resources. */
756         private static final String LOVE_NUMBERS = IERS_BASE + "2003/tab6.1.txt";
757 
758         /** Frequency dependence model for k₂₀. */
759         private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3b.txt";
760 
761         /** Frequency dependence model for k₂₁. */
762         private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3a.txt";
763 
764         /** Frequency dependence model for k₂₂. */
765         private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "2003/tab6.3c.txt";
766 
767         /** Annual pole table. */
768         private static final String ANNUAL_POLE = IERS_BASE + "2003/annual.pole";
769 
770         /** Tidal displacement frequency correction for diurnal tides. */
771         private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "2003/tab7.5a.txt";
772 
773         /** Tidal displacement frequency correction for zonal tides. */
774         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "2003/tab7.5b.txt";
775 
776         /** {@inheritDoc} */
777         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
778             return new FundamentalNutationArguments(this, timeScale,
779                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
780         }
781 
782         /** {@inheritDoc} */
783         @Override
784         public TimeScalarFunction getMeanObliquityFunction() {
785 
786             // epsilon 0 value from chapter 5, page 41, other terms from equation 32 page 45
787             final PolynomialNutation epsilonA =
788                     new PolynomialNutation(84381.448    * Constants.ARC_SECONDS_TO_RADIANS,
789                                              -46.84024  * Constants.ARC_SECONDS_TO_RADIANS,
790                                               -0.00059  * Constants.ARC_SECONDS_TO_RADIANS,
791                                                0.001813 * Constants.ARC_SECONDS_TO_RADIANS);
792 
793             return new TimeScalarFunction() {
794 
795                 /** {@inheritDoc} */
796                 @Override
797                 public double value(final AbsoluteDate date) {
798                     return epsilonA.value(evaluateTC(date));
799                 }
800 
801                 /** {@inheritDoc} */
802                 @Override
803                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
804                     return epsilonA.value(evaluateTC(date));
805                 }
806 
807             };
808 
809         }
810 
811         /** {@inheritDoc} */
812         @Override
813         public TimeVectorFunction getXYSpXY2Function() {
814 
815             // set up nutation arguments
816             final FundamentalNutationArguments arguments = getNutationArguments(null);
817 
818             // set up Poisson series
819             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
820             final PoissonSeriesParser parser =
821                     new PoissonSeriesParser(17).
822                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
823                         withFirstDelaunay(4).
824                         withFirstPlanetary(9).
825                         withSinCos(0, 2, microAS, 3, microAS);
826 
827             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
828             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
829             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
830             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);
831 
832             // create a function evaluating the series
833             return new TimeVectorFunction() {
834 
835                 /** {@inheritDoc} */
836                 @Override
837                 public double[] value(final AbsoluteDate date) {
838                     return xys.value(arguments.evaluateAll(date));
839                 }
840 
841                 /** {@inheritDoc} */
842                 @Override
843                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
844                     return xys.value(arguments.evaluateAll(date));
845                 }
846 
847             };
848 
849         }
850 
851 
852         /** {@inheritDoc} */
853         @Override
854         public TimeVectorFunction getPrecessionFunction() {
855 
856             // set up the conventional polynomials
857             // the following values are from equation 32 in IERS 2003 conventions
858             final PolynomialNutation psiA =
859                     new PolynomialNutation(    0.0,
860                                             5038.47875   * Constants.ARC_SECONDS_TO_RADIANS,
861                                               -1.07259   * Constants.ARC_SECONDS_TO_RADIANS,
862                                               -0.001147  * Constants.ARC_SECONDS_TO_RADIANS);
863             final PolynomialNutation omegaA =
864                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
865                                            -0.02524   * Constants.ARC_SECONDS_TO_RADIANS,
866                                             0.05127   * Constants.ARC_SECONDS_TO_RADIANS,
867                                            -0.007726  * Constants.ARC_SECONDS_TO_RADIANS);
868             final PolynomialNutation chiA =
869                     new PolynomialNutation( 0.0,
870                                            10.5526   * Constants.ARC_SECONDS_TO_RADIANS,
871                                            -2.38064  * Constants.ARC_SECONDS_TO_RADIANS,
872                                            -0.001125 * Constants.ARC_SECONDS_TO_RADIANS);
873 
874             return new PrecessionFunction(psiA, omegaA, chiA);
875 
876         }
877 
878         /** {@inheritDoc} */
879         @Override
880         public TimeVectorFunction getNutationFunction() {
881 
882             // set up nutation arguments
883             final FundamentalNutationArguments arguments = getNutationArguments(null);
884 
885             // set up Poisson series
886             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
887             final PoissonSeriesParser luniSolarParser =
888                     new PoissonSeriesParser(14).withFirstDelaunay(1);
889             final PoissonSeriesParser luniSolarPsiParser =
890                     luniSolarParser.
891                     withSinCos(0, 7, milliAS, 11, milliAS).
892                     withSinCos(1, 8, milliAS, 12, milliAS);
893             final PoissonSeries psiLuniSolarSeries =
894                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
895             final PoissonSeriesParser luniSolarEpsilonParser =
896                     luniSolarParser.
897                     withSinCos(0, 13, milliAS, 9, milliAS).
898                     withSinCos(1, 14, milliAS, 10, milliAS);
899             final PoissonSeries epsilonLuniSolarSeries =
900                     luniSolarEpsilonParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
901 
902             final PoissonSeriesParser planetaryParser =
903                     new PoissonSeriesParser(21).
904                         withFirstDelaunay(2).
905                         withFirstPlanetary(7);
906             final PoissonSeriesParser planetaryPsiParser =
907                     planetaryParser.withSinCos(0, 17, milliAS, 18, milliAS);
908             final PoissonSeries psiPlanetarySeries =
909                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
910             final PoissonSeriesParser planetaryEpsilonParser =
911                     planetaryParser.withSinCos(0, 19, milliAS, 20, milliAS);
912             final PoissonSeries epsilonPlanetarySeries =
913                     planetaryEpsilonParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
914 
915             final PoissonSeries.CompiledSeries luniSolarSeries =
916                     PoissonSeries.compile(psiLuniSolarSeries, epsilonLuniSolarSeries);
917             final PoissonSeries.CompiledSeries planetarySeries =
918                     PoissonSeries.compile(psiPlanetarySeries, epsilonPlanetarySeries);
919 
920             return new TimeVectorFunction() {
921 
922                 /** {@inheritDoc} */
923                 @Override
924                 public double[] value(final AbsoluteDate date) {
925                     final BodiesElements elements = arguments.evaluateAll(date);
926                     final double[] luniSolar = luniSolarSeries.value(elements);
927                     final double[] planetary = planetarySeries.value(elements);
928                     return new double[] {
929                         luniSolar[0] + planetary[0], luniSolar[1] + planetary[1],
930                         IAU1994ResolutionC7.value(elements)
931                     };
932                 }
933 
934                 /** {@inheritDoc} */
935                 @Override
936                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
937                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
938                     final T[] luniSolar = luniSolarSeries.value(elements);
939                     final T[] planetary = planetarySeries.value(elements);
940                     final T[] result = MathArrays.buildArray(date.getField(), 3);
941                     result[0] = luniSolar[0].add(planetary[0]);
942                     result[1] = luniSolar[1].add(planetary[1]);
943                     result[2] = IAU1994ResolutionC7.value(elements);
944                     return result;
945                 }
946 
947             };
948 
949         }
950 
951         /** {@inheritDoc} */
952         @Override
953         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
954 
955             // Earth Rotation Angle
956             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);
957 
958             // Polynomial part of the apparent sidereal time series
959             // which is the opposite of Equation of Origins (EO)
960             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
961             final PoissonSeriesParser parser =
962                     new PoissonSeriesParser(17).
963                         withFirstDelaunay(4).
964                         withFirstPlanetary(9).
965                         withSinCos(0, 2, microAS, 3, microAS).
966                         withPolynomialPart('t', Unit.ARC_SECONDS);
967             final PolynomialNutation minusEO =
968                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();
969 
970             // create a function evaluating the series
971             return new TimeScalarFunction() {
972 
973                 /** {@inheritDoc} */
974                 @Override
975                 public double value(final AbsoluteDate date) {
976                     return era.value(date) + minusEO.value(evaluateTC(date));
977                 }
978 
979                 /** {@inheritDoc} */
980                 @Override
981                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
982                     return era.value(date).add(minusEO.value(evaluateTC(date)));
983                 }
984 
985             };
986 
987         }
988 
989         /** {@inheritDoc} */
990         @Override
991         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {
992 
993             // Earth Rotation Angle
994             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);
995 
996             // Polynomial part of the apparent sidereal time series
997             // which is the opposite of Equation of Origins (EO)
998             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
999             final PoissonSeriesParser parser =
1000                     new PoissonSeriesParser(17).
1001                         withFirstDelaunay(4).
1002                         withFirstPlanetary(9).
1003                         withSinCos(0, 2, microAS, 3, microAS).
1004                         withPolynomialPart('t', Unit.ARC_SECONDS);
1005             final PolynomialNutation minusEO =
1006                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();
1007 
1008             // create a function evaluating the series
1009             return new TimeScalarFunction() {
1010 
1011                 /** {@inheritDoc} */
1012                 @Override
1013                 public double value(final AbsoluteDate date) {
1014                     return era.getRate() + minusEO.derivative(evaluateTC(date));
1015                 }
1016 
1017                 /** {@inheritDoc} */
1018                 @Override
1019                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
1020                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
1021                 }
1022 
1023             };
1024 
1025         }
1026 
1027         /** {@inheritDoc} */
1028         @Override
1029         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory) {
1030 
1031             // set up nutation arguments
1032             final FundamentalNutationArguments arguments = getNutationArguments(null);
1033 
1034             // mean obliquity function
1035             final TimeScalarFunction epsilon = getMeanObliquityFunction();
1036 
1037             // set up Poisson series
1038             final double milliAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-3;
1039             final PoissonSeriesParser luniSolarPsiParser =
1040                     new PoissonSeriesParser(14).
1041                         withFirstDelaunay(1).
1042                         withSinCos(0, 7, milliAS, 11, milliAS).
1043                         withSinCos(1, 8, milliAS, 12, milliAS);
1044             final PoissonSeries psiLuniSolarSeries =
1045                     luniSolarPsiParser.parse(getStream(LUNI_SOLAR_SERIES), LUNI_SOLAR_SERIES);
1046 
1047             final PoissonSeriesParser planetaryPsiParser =
1048                     new PoissonSeriesParser(21).
1049                         withFirstDelaunay(2).
1050                         withFirstPlanetary(7).
1051                         withSinCos(0, 17, milliAS, 18, milliAS);
1052             final PoissonSeries psiPlanetarySeries =
1053                     planetaryPsiParser.parse(getStream(PLANETARY_SERIES), PLANETARY_SERIES);
1054 
1055             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1056             final PoissonSeriesParser gstParser =
1057                     new PoissonSeriesParser(17).
1058                         withFirstDelaunay(4).
1059                         withFirstPlanetary(9).
1060                         withSinCos(0, 2, microAS, 3, microAS).
1061                         withPolynomialPart('t', Unit.ARC_SECONDS);
1062             final PoissonSeries gstSeries = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
1063             final PoissonSeries.CompiledSeries psiGstSeries =
1064                     PoissonSeries.compile(psiLuniSolarSeries, psiPlanetarySeries, gstSeries);
1065 
1066             // ERA function
1067             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);
1068 
1069             return new TimeScalarFunction() {
1070 
1071                 /** {@inheritDoc} */
1072                 @Override
1073                 public double value(final AbsoluteDate date) {
1074 
1075                     // evaluate equation of origins
1076                     final BodiesElements elements = arguments.evaluateAll(date);
1077                     final double[] angles = psiGstSeries.value(elements);
1078                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
1079                     final double deltaPsi = angles[0] + angles[1] + ddPsi;
1080                     final double epsilonA = epsilon.value(date);
1081 
1082                     // subtract equation of origin from EA
1083                     // (hence add the series above which have the sign included)
1084                     return era.value(date) + deltaPsi * FastMath.cos(epsilonA) + angles[2];
1085 
1086                 }
1087 
1088                 /** {@inheritDoc} */
1089                 @Override
1090                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
1091 
1092                     // evaluate equation of origins
1093                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
1094                     final T[] angles = psiGstSeries.value(elements);
1095                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
1096                     final T deltaPsi = angles[0].add(angles[1]).add(ddPsi);
1097                     final T epsilonA = epsilon.value(date);
1098 
1099                     // subtract equation of origin from EA
1100                     // (hence add the series above which have the sign included)
1101                     return era.value(date).add(deltaPsi.multiply(epsilonA.cos())).add(angles[2]);
1102 
1103                 }
1104 
1105             };
1106 
1107         }
1108 
1109         /** {@inheritDoc} */
1110         @Override
1111         public TimeVectorFunction getEOPTidalCorrection() {
1112 
1113             // set up nutation arguments
1114             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
1115             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
1116             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
1117             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
1118             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());
1119 
1120             // set up Poisson series
1121             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1122             final PoissonSeriesParseronSeriesParser">PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
1123                     withOptionalColumn(1).
1124                     withGamma(2).
1125                     withFirstDelaunay(3);
1126             final PoissonSeries xSeries =
1127                     xyParser.
1128                     withSinCos(0, 10, microAS, 11, microAS).
1129                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
1130             final PoissonSeries ySeries =
1131                     xyParser.
1132                     withSinCos(0, 12, microAS, 13, microAS).
1133                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
1134 
1135             final double microS = 1.0e-6;
1136             final PoissonSeriesParsernSeriesParser">PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
1137                     withOptionalColumn(1).
1138                     withGamma(2).
1139                     withFirstDelaunay(3).
1140                     withSinCos(0, 10, microS, 11, microS);
1141             final PoissonSeries ut1Series =
1142                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);
1143 
1144             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
1145 
1146         }
1147 
1148         /** {@inheritDoc} */
1149         public LoveNumbers getLoveNumbers() {
1150             return loadLoveNumbers(LOVE_NUMBERS);
1151         }
1152 
1153         /** {@inheritDoc} */
1154         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {
1155 
1156             // set up nutation arguments
1157             final FundamentalNutationArguments arguments = getNutationArguments(ut1);
1158 
1159             // set up Poisson series
1160             final PoissonSeriesParser k20Parser =
1161                     new PoissonSeriesParser(18).
1162                         withOptionalColumn(1).
1163                         withDoodson(4, 2).
1164                         withFirstDelaunay(10);
1165             final PoissonSeriesParser k21Parser =
1166                     new PoissonSeriesParser(18).
1167                         withOptionalColumn(1).
1168                         withDoodson(4, 3).
1169                         withFirstDelaunay(10);
1170             final PoissonSeriesParser k22Parser =
1171                     new PoissonSeriesParser(16).
1172                         withOptionalColumn(1).
1173                         withDoodson(4, 2).
1174                         withFirstDelaunay(10);
1175 
1176             final double pico = 1.0e-12;
1177             final PoissonSeries c20Series =
1178                     k20Parser.
1179                   withSinCos(0, 18, -pico, 16, pico).
1180                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
1181             final PoissonSeries c21Series =
1182                     k21Parser.
1183                     withSinCos(0, 17, pico, 18, pico).
1184                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
1185             final PoissonSeries s21Series =
1186                     k21Parser.
1187                     withSinCos(0, 18, -pico, 17, pico).
1188                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
1189             final PoissonSeries c22Series =
1190                     k22Parser.
1191                     withSinCos(0, -1, pico, 16, pico).
1192                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
1193             final PoissonSeries s22Series =
1194                     k22Parser.
1195                     withSinCos(0, 16, -pico, -1, pico).
1196                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
1197 
1198             return new TideFrequencyDependenceFunction(arguments,
1199                                                        c20Series,
1200                                                        c21Series, s21Series,
1201                                                        c22Series, s22Series);
1202 
1203         }
1204 
1205         /** {@inheritDoc} */
1206         @Override
1207         public double getPermanentTide() {
1208             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
1209         }
1210 
1211         /** {@inheritDoc} */
1212         @Override
1213         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {
1214 
1215             // annual pole from ftp://tai.bipm.org/iers/conv2003/chapter7/annual.pole
1216             final TimeScale utc = TimeScalesFactory.getUTC();
1217             final SimpleTimeStampedTableParser.RowConverter<MeanPole> converter =
1218                 new SimpleTimeStampedTableParser.RowConverter<MeanPole>() {
1219                     /** {@inheritDoc} */
1220                     @Override
1221                     public MeanPole convert(final double[] rawFields) {
1222                         return new MeanPole(new AbsoluteDate((int) rawFields[0], 1, 1, utc),
1223                                             rawFields[1] * Constants.ARC_SECONDS_TO_RADIANS,
1224                                             rawFields[2] * Constants.ARC_SECONDS_TO_RADIANS);
1225                     }
1226                 };
1227             final SimpleTimeStampedTableParser<MeanPole> parser =
1228                     new SimpleTimeStampedTableParser<MeanPole>(3, converter);
1229             final List<MeanPole> annualPoleList = parser.parse(getStream(ANNUAL_POLE), ANNUAL_POLE);
1230             final AbsoluteDate firstAnnualPoleDate = annualPoleList.get(0).getDate();
1231             final AbsoluteDate lastAnnualPoleDate  = annualPoleList.get(annualPoleList.size() - 1).getDate();
1232             final ImmutableTimeStampedCache<MeanPole> annualCache =
1233                     new ImmutableTimeStampedCache<MeanPole>(2, annualPoleList);
1234 
1235             // polynomial extension from IERS 2003, section 7.1.4, equations 23a and 23b
1236             final double xp0    = 0.054   * Constants.ARC_SECONDS_TO_RADIANS;
1237             final double xp0Dot = 0.00083 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
1238             final double yp0    = 0.357   * Constants.ARC_SECONDS_TO_RADIANS;
1239             final double yp0Dot = 0.00395 * Constants.ARC_SECONDS_TO_RADIANS / Constants.JULIAN_YEAR;
1240 
1241             // constants from IERS 2003, section 6.2
1242             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
1243             final double ratio        =  0.00115;
1244 
1245             return new TimeVectorFunction() {
1246 
1247                 /** {@inheritDoc} */
1248                 @Override
1249                 public double[] value(final AbsoluteDate date) {
1250 
1251                     // we can't compute anything before the range covered by the annual pole file
1252                     if (date.compareTo(firstAnnualPoleDate) <= 0) {
1253                         return new double[] {
1254                             0.0, 0.0
1255                         };
1256                     }
1257 
1258                     // evaluate mean pole
1259                     double meanPoleX = 0;
1260                     double meanPoleY = 0;
1261                     if (date.compareTo(lastAnnualPoleDate) <= 0) {
1262                         // we are within the range covered by the annual pole file,
1263                         // we interpolate within it
1264                         try {
1265                             final HermiteInterpolator interpolator = new HermiteInterpolator();
1266                             annualCache.getNeighbors(date).forEach(neighbor ->
1267                                 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
1268                                                             new double[] {
1269                                                                 neighbor.getX(), neighbor.getY()
1270                                                             }));
1271                             final double[] interpolated = interpolator.value(0);
1272                             meanPoleX = interpolated[0];
1273                             meanPoleY = interpolated[1];
1274                         } catch (TimeStampedCacheException tsce) {
1275                             // this should never happen
1276                             throw new OrekitInternalError(tsce);
1277                         }
1278                     } else {
1279 
1280                         // we are after the range covered by the annual pole file,
1281                         // we use the polynomial extension
1282                         final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
1283                         meanPoleX = xp0 + t * xp0Dot;
1284                         meanPoleY = yp0 + t * yp0Dot;
1285 
1286                     }
1287 
1288                     // evaluate wobble variables
1289                     final PoleCorrection correction = eopHistory.getPoleCorrection(date);
1290                     final double m1 = correction.getXp() - meanPoleX;
1291                     final double m2 = meanPoleY - correction.getYp();
1292 
1293                     return new double[] {
1294                         // the following correspond to the equations published in IERS 2003 conventions,
1295                         // section 6.2 page 65. In the publication, the equations read:
1296                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
1297                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
1298                         // However, it seems there are sign errors in these equations, which have
1299                         // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
1300                         // publication, the equations read:
1301                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
1302                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
1303                         // the newer equations seem more consistent with the premises as the
1304                         // deformation due to the centrifugal potential has the form:
1305                         // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
1306                         // number 0.3077 + 0.0036i, so the real part in the previous equation is:
1307                         // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
1308                         // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
1309                         // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
1310                         // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
1311                         // and Im(k₂)/Re(k₂) is very close to +0.00115
1312                         // As the equation as written in the IERS 2003 conventions are used in
1313                         // legacy systems, we have reproduced this alleged error here (and fixed it in
1314                         // the IERS 2010 conventions below) for validation purposes. We don't recommend
1315                         // using the IERS 2003 conventions for solid pole tide computation other than
1316                         // for validation or reproducibility of legacy applications behavior.
1317                         // As solid pole tide is small and as the sign change is on the smallest coefficient,
1318                         // the effect is quite small. A test case on a propagated orbit showed a position change
1319                         // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
1320                         globalFactor * (m1 - ratio * m2),
1321                         globalFactor * (m2 + ratio * m1),
1322                     };
1323 
1324                 }
1325 
1326                 /** {@inheritDoc} */
1327                 @Override
1328                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1329 
1330                     final AbsoluteDate aDate = date.toAbsoluteDate();
1331 
1332                     // we can't compute anything before the range covered by the annual pole file
1333                     if (aDate.compareTo(firstAnnualPoleDate) <= 0) {
1334                         return MathArrays.buildArray(date.getField(), 2);
1335                     }
1336 
1337                     // evaluate mean pole
1338                     T meanPoleX = date.getField().getZero();
1339                     T meanPoleY = date.getField().getZero();
1340                     if (aDate.compareTo(lastAnnualPoleDate) <= 0) {
1341                         // we are within the range covered by the annual pole file,
1342                         // we interpolate within it
1343                         try {
1344                             final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
1345                             final T[] y = MathArrays.buildArray(date.getField(), 2);
1346                             final T zero = date.getField().getZero();
1347                             final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero); // here, we attempt to get a constant date,
1348                                                                                                        // for example removing derivatives
1349                                                                                                        // if T was DerivativeStructure
1350                             annualCache.getNeighbors(aDate).forEach(neighbor -> {
1351                                 y[0] = zero.add(neighbor.getX());
1352                                 y[1] = zero.add(neighbor.getY());
1353                                 interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y);
1354                             });
1355                             final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case)
1356                             meanPoleX = interpolated[0];
1357                             meanPoleY = interpolated[1];
1358                         } catch (TimeStampedCacheException tsce) {
1359                             // this should never happen
1360                             throw new OrekitInternalError(tsce);
1361                         }
1362                     } else {
1363 
1364                         // we are after the range covered by the annual pole file,
1365                         // we use the polynomial extension
1366                         final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
1367                         meanPoleX = t.multiply(xp0Dot).add(xp0);
1368                         meanPoleY = t.multiply(yp0Dot).add(yp0);
1369 
1370                     }
1371 
1372                     // evaluate wobble variables
1373                     final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
1374                     final T m1 = correction.getXp().subtract(meanPoleX);
1375                     final T m2 = meanPoleY.subtract(correction.getYp());
1376 
1377                     final T[] a = MathArrays.buildArray(date.getField(), 2);
1378 
1379                     // the following correspond to the equations published in IERS 2003 conventions,
1380                     // section 6.2 page 65. In the publication, the equations read:
1381                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
1382                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
1383                     // However, it seems there are sign errors in these equations, which have
1384                     // been fixed in IERS 2010 conventions, section 6.4 page 94. In these newer
1385                     // publication, the equations read:
1386                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
1387                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
1388                     // the newer equations seem more consistent with the premises as the
1389                     // deformation due to the centrifugal potential has the form:
1390                     // −Ω²r²/2 sin 2θ Re [k₂(m₁ − im₂) exp(iλ)] where k₂ is the complex
1391                     // number 0.3077 + 0.0036i, so the real part in the previous equation is:
1392                     // A[Re(k₂) m₁ + Im(k₂) m₂)] cos λ + A[Re(k₂) m₂ - Im(k₂) m₁] sin λ
1393                     // identifying this with ∆C₂₁ cos λ + ∆S₂₁ sin λ we get:
1394                     // ∆C₂₁ = A Re(k₂) [m₁ + Im(k₂)/Re(k₂) m₂)]
1395                     // ∆S₂₁ = A Re(k₂) [m₂ - Im(k₂)/Re(k₂) m₁)]
1396                     // and Im(k₂)/Re(k₂) is very close to +0.00115
1397                     // As the equation as written in the IERS 2003 conventions are used in
1398                     // legacy systems, we have reproduced this alleged error here (and fixed it in
1399                     // the IERS 2010 conventions below) for validation purposes. We don't recommend
1400                     // using the IERS 2003 conventions for solid pole tide computation other than
1401                     // for validation or reproducibility of legacy applications behavior.
1402                     // As solid pole tide is small and as the sign change is on the smallest coefficient,
1403                     // the effect is quite small. A test case on a propagated orbit showed a position change
1404                     // slightly below 0.4m after a 30 days propagation on a Low Earth Orbit
1405                     a[0] = m1.add(m2.multiply(-ratio)).multiply(globalFactor);
1406                     a[1] = m2.add(m1.multiply( ratio)).multiply(globalFactor);
1407 
1408                     return a;
1409 
1410                 }
1411 
1412             };
1413 
1414         }
1415 
1416         /** {@inheritDoc} */
1417         @Override
1418         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {
1419 
1420             return new TimeVectorFunction() {
1421 
1422                 /** {@inheritDoc} */
1423                 @Override
1424                 public double[] value(final AbsoluteDate date) {
1425                     // there are no model for ocean pole tide prior to conventions 2010
1426                     return new double[] {
1427                         0.0, 0.0
1428                     };
1429                 }
1430 
1431                 /** {@inheritDoc} */
1432                 @Override
1433                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1434                     // there are no model for ocean pole tide prior to conventions 2010
1435                     return MathArrays.buildArray(date.getField(), 2);
1436                 }
1437 
1438             };
1439         }
1440 
1441         /** {@inheritDoc} */
1442         @Override
1443         public double[] getNominalTidalDisplacement() {
1444             return new double[] {
1445                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
1446                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
1447                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
1448                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
1449                 // H₀
1450                 -0.31460
1451             };
1452         }
1453 
1454         /** {@inheritDoc} */
1455         @Override
1456         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
1457             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
1458                                                                   18, 15, 16, 17, 18);
1459         }
1460 
1461         /** {@inheritDoc} */
1462         @Override
1463         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
1464             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
1465                                                                 18, 15, 16, 17, 18);
1466         }
1467 
1468     },
1469 
1470     /** Constant for IERS 2010 conventions. */
1471     IERS_2010 {
1472 
1473         /** Nutation arguments resources. */
1474         private static final String NUTATION_ARGUMENTS = IERS_BASE + "2010/nutation-arguments.txt";
1475 
1476         /** X series resources. */
1477         private static final String X_SERIES           = IERS_BASE + "2010/tab5.2a.txt";
1478 
1479         /** Y series resources. */
1480         private static final String Y_SERIES           = IERS_BASE + "2010/tab5.2b.txt";
1481 
1482         /** S series resources. */
1483         private static final String S_SERIES           = IERS_BASE + "2010/tab5.2d.txt";
1484 
1485         /** Psi series resources. */
1486         private static final String PSI_SERIES         = IERS_BASE + "2010/tab5.3a.txt";
1487 
1488         /** Epsilon series resources. */
1489         private static final String EPSILON_SERIES     = IERS_BASE + "2010/tab5.3b.txt";
1490 
1491         /** Greenwhich sidereal time series resources. */
1492         private static final String GST_SERIES         = IERS_BASE + "2010/tab5.2e.txt";
1493 
1494         /** Tidal correction for xp, yp series resources. */
1495         private static final String TIDAL_CORRECTION_XP_YP_SERIES = IERS_BASE + "2010/tab8.2ab.txt";
1496 
1497         /** Tidal correction for UT1 resources. */
1498         private static final String TIDAL_CORRECTION_UT1_SERIES = IERS_BASE + "2010/tab8.3ab.txt";
1499 
1500         /** Love numbers resources. */
1501         private static final String LOVE_NUMBERS = IERS_BASE + "2010/tab6.3.txt";
1502 
1503         /** Frequency dependence model for k₂₀. */
1504         private static final String K20_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5b.txt";
1505 
1506         /** Frequency dependence model for k₂₁. */
1507         private static final String K21_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5a.txt";
1508 
1509         /** Frequency dependence model for k₂₂. */
1510         private static final String K22_FREQUENCY_DEPENDENCE = IERS_BASE + "2010/tab6.5c.txt";
1511 
1512         /** Tidal displacement frequency correction for diurnal tides. */
1513         private static final String TIDAL_DISPLACEMENT_CORRECTION_DIURNAL = IERS_BASE + "2010/tab7.3a.txt";
1514 
1515         /** Tidal displacement frequency correction for zonal tides. */
1516         private static final String TIDAL_DISPLACEMENT_CORRECTION_ZONAL = IERS_BASE + "2010/tab7.3b.txt";
1517 
1518         /** {@inheritDoc} */
1519         public FundamentalNutationArguments getNutationArguments(final TimeScale timeScale) {
1520             return new FundamentalNutationArguments(this, timeScale,
1521                                                     getStream(NUTATION_ARGUMENTS), NUTATION_ARGUMENTS);
1522         }
1523 
1524         /** {@inheritDoc} */
1525         @Override
1526         public TimeScalarFunction getMeanObliquityFunction() {
1527 
1528             // epsilon 0 value from chapter 5, page 56, other terms from equation 5.40 page 65
1529             final PolynomialNutation epsilonA =
1530                     new PolynomialNutation(84381.406        * Constants.ARC_SECONDS_TO_RADIANS,
1531                                              -46.836769     * Constants.ARC_SECONDS_TO_RADIANS,
1532                                               -0.0001831    * Constants.ARC_SECONDS_TO_RADIANS,
1533                                                0.00200340   * Constants.ARC_SECONDS_TO_RADIANS,
1534                                               -0.000000576  * Constants.ARC_SECONDS_TO_RADIANS,
1535                                               -0.0000000434 * Constants.ARC_SECONDS_TO_RADIANS);
1536 
1537             return new TimeScalarFunction() {
1538 
1539                 /** {@inheritDoc} */
1540                 @Override
1541                 public double value(final AbsoluteDate date) {
1542                     return epsilonA.value(evaluateTC(date));
1543                 }
1544 
1545                 /** {@inheritDoc} */
1546                 @Override
1547                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
1548                     return epsilonA.value(evaluateTC(date));
1549                 }
1550 
1551             };
1552 
1553         }
1554 
1555         /** {@inheritDoc} */
1556         @Override
1557         public TimeVectorFunction getXYSpXY2Function() {
1558 
1559             // set up nutation arguments
1560             final FundamentalNutationArguments arguments = getNutationArguments(null);
1561 
1562             // set up Poisson series
1563             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1564             final PoissonSeriesParser parser =
1565                     new PoissonSeriesParser(17).
1566                         withPolynomialPart('t', PolynomialParser.Unit.MICRO_ARC_SECONDS).
1567                         withFirstDelaunay(4).
1568                         withFirstPlanetary(9).
1569                         withSinCos(0, 2, microAS, 3, microAS);
1570             final PoissonSeries xSeries = parser.parse(getStream(X_SERIES), X_SERIES);
1571             final PoissonSeries ySeries = parser.parse(getStream(Y_SERIES), Y_SERIES);
1572             final PoissonSeries sSeries = parser.parse(getStream(S_SERIES), S_SERIES);
1573             final PoissonSeries.CompiledSeries xys = PoissonSeries.compile(xSeries, ySeries, sSeries);
1574 
1575             // create a function evaluating the series
1576             return new TimeVectorFunction() {
1577 
1578                 /** {@inheritDoc} */
1579                 @Override
1580                 public double[] value(final AbsoluteDate date) {
1581                     return xys.value(arguments.evaluateAll(date));
1582                 }
1583 
1584                 /** {@inheritDoc} */
1585                 @Override
1586                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1587                     return xys.value(arguments.evaluateAll(date));
1588                 }
1589 
1590             };
1591 
1592         }
1593 
1594         /** {@inheritDoc} */
1595         public LoveNumbers getLoveNumbers() {
1596             return loadLoveNumbers(LOVE_NUMBERS);
1597         }
1598 
1599         /** {@inheritDoc} */
1600         public TimeVectorFunction getTideFrequencyDependenceFunction(final TimeScale ut1) {
1601 
1602             // set up nutation arguments
1603             final FundamentalNutationArguments arguments = getNutationArguments(ut1);
1604 
1605             // set up Poisson series
1606             final PoissonSeriesParser k20Parser =
1607                     new PoissonSeriesParser(18).
1608                         withOptionalColumn(1).
1609                         withDoodson(4, 2).
1610                         withFirstDelaunay(10);
1611             final PoissonSeriesParser k21Parser =
1612                     new PoissonSeriesParser(18).
1613                         withOptionalColumn(1).
1614                         withDoodson(4, 3).
1615                         withFirstDelaunay(10);
1616             final PoissonSeriesParser k22Parser =
1617                     new PoissonSeriesParser(16).
1618                         withOptionalColumn(1).
1619                         withDoodson(4, 2).
1620                         withFirstDelaunay(10);
1621 
1622             final double pico = 1.0e-12;
1623             final PoissonSeries c20Series =
1624                     k20Parser.
1625                     withSinCos(0, 18, -pico, 16, pico).
1626                     parse(getStream(K20_FREQUENCY_DEPENDENCE), K20_FREQUENCY_DEPENDENCE);
1627             final PoissonSeries c21Series =
1628                     k21Parser.
1629                     withSinCos(0, 17, pico, 18, pico).
1630                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
1631             final PoissonSeries s21Series =
1632                     k21Parser.
1633                     withSinCos(0, 18, -pico, 17, pico).
1634                     parse(getStream(K21_FREQUENCY_DEPENDENCE), K21_FREQUENCY_DEPENDENCE);
1635             final PoissonSeries c22Series =
1636                     k22Parser.
1637                     withSinCos(0, -1, pico, 16, pico).
1638                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
1639             final PoissonSeries s22Series =
1640                     k22Parser.
1641                     withSinCos(0, 16, -pico, -1, pico).
1642                     parse(getStream(K22_FREQUENCY_DEPENDENCE), K22_FREQUENCY_DEPENDENCE);
1643 
1644             return new TideFrequencyDependenceFunction(arguments,
1645                                                        c20Series,
1646                                                        c21Series, s21Series,
1647                                                        c22Series, s22Series);
1648 
1649         }
1650 
1651         /** {@inheritDoc} */
1652         @Override
1653         public double getPermanentTide() {
1654             return 4.4228e-8 * -0.31460 * getLoveNumbers().getReal(2, 0);
1655         }
1656 
1657         /** Compute pole wobble variables m₁ and m₂.
1658          * @param date current date
1659          * @param eopHistory EOP history
1660          * @return array containing m₁ and m₂
1661          */
1662         private double[] computePoleWobble(final AbsoluteDate date, final EOPHistory eopHistory) {
1663 
1664             // polynomial model from IERS 2010, table 7.7
1665             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
1666             final double f1 = f0 / Constants.JULIAN_YEAR;
1667             final double f2 = f1 / Constants.JULIAN_YEAR;
1668             final double f3 = f2 / Constants.JULIAN_YEAR;
1669             final AbsoluteDateeDate">AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());
1670 
1671             // evaluate mean pole
1672             final double[] xPolynomial;
1673             final double[] yPolynomial;
1674             if (date.compareTo(changeDate) <= 0) {
1675                 xPolynomial = new double[] {
1676                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
1677                 };
1678                 yPolynomial = new double[] {
1679                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
1680                 };
1681             } else {
1682                 xPolynomial = new double[] {
1683                     23.513 * f0, 7.6141 * f1
1684                 };
1685                 yPolynomial = new double[] {
1686                     358.891 * f0,  -0.6287 * f1
1687                 };
1688             }
1689             double meanPoleX = 0;
1690             double meanPoleY = 0;
1691             final double t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
1692             for (int i = xPolynomial.length - 1; i >= 0; --i) {
1693                 meanPoleX = meanPoleX * t + xPolynomial[i];
1694             }
1695             for (int i = yPolynomial.length - 1; i >= 0; --i) {
1696                 meanPoleY = meanPoleY * t + yPolynomial[i];
1697             }
1698 
1699             // evaluate wobble variables
1700             final PoleCorrection correction = eopHistory.getPoleCorrection(date);
1701             final double m1 = correction.getXp() - meanPoleX;
1702             final double m2 = meanPoleY - correction.getYp();
1703 
1704             return new double[] {
1705                 m1, m2
1706             };
1707 
1708         }
1709 
1710         /** Compute pole wobble variables m₁ and m₂.
1711          * @param date current date
1712          * @param <T> type of the field elements
1713          * @param eopHistory EOP history
1714          * @return array containing m₁ and m₂
1715          */
1716         private <T extends RealFieldElement<T>> T[] computePoleWobble(final FieldAbsoluteDate<T> date, final EOPHistory eopHistory) {
1717 
1718             // polynomial model from IERS 2010, table 7.7
1719             final double f0 = Constants.ARC_SECONDS_TO_RADIANS / 1000.0;
1720             final double f1 = f0 / Constants.JULIAN_YEAR;
1721             final double f2 = f1 / Constants.JULIAN_YEAR;
1722             final double f3 = f2 / Constants.JULIAN_YEAR;
1723             final AbsoluteDateeDate">AbsoluteDate changeDate = new AbsoluteDate(2010, 1, 1, TimeScalesFactory.getTT());
1724 
1725             // evaluate mean pole
1726             final double[] xPolynomial;
1727             final double[] yPolynomial;
1728             if (date.toAbsoluteDate().compareTo(changeDate) <= 0) {
1729                 xPolynomial = new double[] {
1730                     55.974 * f0, 1.8243 * f1, 0.18413 * f2, 0.007024 * f3
1731                 };
1732                 yPolynomial = new double[] {
1733                     346.346 * f0, 1.7896 * f1, -0.10729 * f2, -0.000908 * f3
1734                 };
1735             } else {
1736                 xPolynomial = new double[] {
1737                     23.513 * f0, 7.6141 * f1
1738                 };
1739                 yPolynomial = new double[] {
1740                     358.891 * f0,  -0.6287 * f1
1741                 };
1742             }
1743             T meanPoleX = date.getField().getZero();
1744             T meanPoleY = date.getField().getZero();
1745             final T t = date.durationFrom(AbsoluteDate.J2000_EPOCH);
1746             for (int i = xPolynomial.length - 1; i >= 0; --i) {
1747                 meanPoleX = meanPoleX.multiply(t).add(xPolynomial[i]);
1748             }
1749             for (int i = yPolynomial.length - 1; i >= 0; --i) {
1750                 meanPoleY = meanPoleY.multiply(t).add(yPolynomial[i]);
1751             }
1752 
1753             // evaluate wobble variables
1754             final FieldPoleCorrection<T> correction = eopHistory.getPoleCorrection(date);
1755             final T[] m = MathArrays.buildArray(date.getField(), 2);
1756             m[0] = correction.getXp().subtract(meanPoleX);
1757             m[1] = meanPoleY.subtract(correction.getYp());
1758 
1759             return m;
1760 
1761         }
1762 
1763         /** {@inheritDoc} */
1764         @Override
1765         public TimeVectorFunction getSolidPoleTide(final EOPHistory eopHistory) {
1766 
1767             // constants from IERS 2010, section 6.4
1768             final double globalFactor = -1.333e-9 / Constants.ARC_SECONDS_TO_RADIANS;
1769             final double ratio        =  0.00115;
1770 
1771             return new TimeVectorFunction() {
1772 
1773                 /** {@inheritDoc} */
1774                 @Override
1775                 public double[] value(final AbsoluteDate date) {
1776 
1777                     // evaluate wobble variables
1778                     final double[] wobbleM = computePoleWobble(date, eopHistory);
1779 
1780                     return new double[] {
1781                         // the following correspond to the equations published in IERS 2010 conventions,
1782                         // section 6.4 page 94. The equations read:
1783                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
1784                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
1785                         // These equations seem to fix what was probably a sign error in IERS 2003
1786                         // conventions section 6.2 page 65. In this older publication, the equations read:
1787                         // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
1788                         // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
1789                         globalFactor * (wobbleM[0] + ratio * wobbleM[1]),
1790                         globalFactor * (wobbleM[1] - ratio * wobbleM[0])
1791                     };
1792 
1793                 }
1794 
1795                 /** {@inheritDoc} */
1796                 @Override
1797                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1798 
1799                     // evaluate wobble variables
1800                     final T[] wobbleM = computePoleWobble(date, eopHistory);
1801 
1802                     final T[] a = MathArrays.buildArray(date.getField(), 2);
1803 
1804                     // the following correspond to the equations published in IERS 2010 conventions,
1805                     // section 6.4 page 94. The equations read:
1806                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ + 0.0115m₂)
1807                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ − 0.0115m₁)
1808                     // These equations seem to fix what was probably a sign error in IERS 2003
1809                     // conventions section 6.2 page 65. In this older publication, the equations read:
1810                     // ∆C₂₁ = −1.333 × 10⁻⁹ (m₁ − 0.0115m₂)
1811                     // ∆S₂₁ = −1.333 × 10⁻⁹ (m₂ + 0.0115m₁)
1812                     a[0] = wobbleM[0].add(wobbleM[1].multiply( ratio)).multiply(globalFactor);
1813                     a[1] = wobbleM[1].add(wobbleM[0].multiply(-ratio)).multiply(globalFactor);
1814 
1815                     return a;
1816 
1817                 }
1818 
1819             };
1820 
1821         }
1822 
1823         /** {@inheritDoc} */
1824         @Override
1825         public TimeVectorFunction getOceanPoleTide(final EOPHistory eopHistory) {
1826 
1827             return new TimeVectorFunction() {
1828 
1829                 /** {@inheritDoc} */
1830                 @Override
1831                 public double[] value(final AbsoluteDate date) {
1832 
1833                     // evaluate wobble variables
1834                     final double[] wobbleM = computePoleWobble(date, eopHistory);
1835 
1836                     return new double[] {
1837                         // the following correspond to the equations published in IERS 2010 conventions,
1838                         // section 6.4 page 94 equation 6.24:
1839                         // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
1840                         // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
1841                         -2.1778e-10 * (wobbleM[0] - 0.01724 * wobbleM[1]) / Constants.ARC_SECONDS_TO_RADIANS,
1842                         -1.7232e-10 * (wobbleM[1] - 0.03365 * wobbleM[0]) / Constants.ARC_SECONDS_TO_RADIANS
1843                     };
1844 
1845                 }
1846 
1847                 /** {@inheritDoc} */
1848                 @Override
1849                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1850 
1851                     final T[] v = MathArrays.buildArray(date.getField(), 2);
1852 
1853                     // evaluate wobble variables
1854                     final T[] wobbleM = computePoleWobble(date, eopHistory);
1855 
1856                     // the following correspond to the equations published in IERS 2010 conventions,
1857                     // section 6.4 page 94 equation 6.24:
1858                     // ∆C₂₁ = −2.1778 × 10⁻¹⁰ (m₁ − 0.01724m₂)
1859                     // ∆S₂₁ = −1.7232 × 10⁻¹⁰ (m₂ − 0.03365m₁)
1860                     v[0] = wobbleM[0].subtract(wobbleM[1].multiply(0.01724)).multiply(-2.1778e-10 / Constants.ARC_SECONDS_TO_RADIANS);
1861                     v[1] = wobbleM[1].subtract(wobbleM[0].multiply(0.03365)).multiply(-1.7232e-10 / Constants.ARC_SECONDS_TO_RADIANS);
1862 
1863                     return v;
1864 
1865                 }
1866 
1867             };
1868 
1869         }
1870 
1871         /** {@inheritDoc} */
1872         @Override
1873         public TimeVectorFunction getPrecessionFunction() {
1874 
1875             // set up the conventional polynomials
1876             // the following values are from equation 5.40 in IERS 2010 conventions
1877             final PolynomialNutation psiA =
1878                     new PolynomialNutation(   0.0,
1879                                            5038.481507     * Constants.ARC_SECONDS_TO_RADIANS,
1880                                              -1.0790069    * Constants.ARC_SECONDS_TO_RADIANS,
1881                                              -0.00114045   * Constants.ARC_SECONDS_TO_RADIANS,
1882                                               0.000132851  * Constants.ARC_SECONDS_TO_RADIANS,
1883                                              -0.0000000951 * Constants.ARC_SECONDS_TO_RADIANS);
1884             final PolynomialNutation omegaA =
1885                     new PolynomialNutation(getMeanObliquityFunction().value(getNutationReferenceEpoch()),
1886                                            -0.025754     * Constants.ARC_SECONDS_TO_RADIANS,
1887                                             0.0512623    * Constants.ARC_SECONDS_TO_RADIANS,
1888                                            -0.00772503   * Constants.ARC_SECONDS_TO_RADIANS,
1889                                            -0.000000467  * Constants.ARC_SECONDS_TO_RADIANS,
1890                                             0.0000003337 * Constants.ARC_SECONDS_TO_RADIANS);
1891             final PolynomialNutation chiA =
1892                     new PolynomialNutation( 0.0,
1893                                            10.556403     * Constants.ARC_SECONDS_TO_RADIANS,
1894                                            -2.3814292    * Constants.ARC_SECONDS_TO_RADIANS,
1895                                            -0.00121197   * Constants.ARC_SECONDS_TO_RADIANS,
1896                                             0.000170663  * Constants.ARC_SECONDS_TO_RADIANS,
1897                                            -0.0000000560 * Constants.ARC_SECONDS_TO_RADIANS);
1898 
1899             return new PrecessionFunction(psiA, omegaA, chiA);
1900 
1901         }
1902 
1903          /** {@inheritDoc} */
1904         @Override
1905         public TimeVectorFunction getNutationFunction() {
1906 
1907             // set up nutation arguments
1908             final FundamentalNutationArguments arguments = getNutationArguments(null);
1909 
1910             // set up Poisson series
1911             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1912             final PoissonSeriesParser parser =
1913                     new PoissonSeriesParser(17).
1914                         withFirstDelaunay(4).
1915                         withFirstPlanetary(9).
1916                         withSinCos(0, 2, microAS, 3, microAS);
1917             final PoissonSeries psiSeries     = parser.parse(getStream(PSI_SERIES), PSI_SERIES);
1918             final PoissonSeries epsilonSeries = parser.parse(getStream(EPSILON_SERIES), EPSILON_SERIES);
1919             final PoissonSeries.CompiledSeries psiEpsilonSeries =
1920                     PoissonSeries.compile(psiSeries, epsilonSeries);
1921 
1922             return new TimeVectorFunction() {
1923 
1924                 /** {@inheritDoc} */
1925                 @Override
1926                 public double[] value(final AbsoluteDate date) {
1927                     final BodiesElements elements = arguments.evaluateAll(date);
1928                     final double[] psiEpsilon = psiEpsilonSeries.value(elements);
1929                     return new double[] {
1930                         psiEpsilon[0], psiEpsilon[1], IAU1994ResolutionC7.value(elements)
1931                     };
1932                 }
1933 
1934                 /** {@inheritDoc} */
1935                 @Override
1936                 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
1937                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
1938                     final T[] psiEpsilon = psiEpsilonSeries.value(elements);
1939                     final T[] result = MathArrays.buildArray(date.getField(), 3);
1940                     result[0] = psiEpsilon[0];
1941                     result[1] = psiEpsilon[1];
1942                     result[2] = IAU1994ResolutionC7.value(elements);
1943                     return result;
1944                 }
1945 
1946             };
1947 
1948         }
1949 
1950         /** {@inheritDoc} */
1951         @Override
1952         public TimeScalarFunction getGMSTFunction(final TimeScale ut1) {
1953 
1954             // Earth Rotation Angle
1955             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);
1956 
1957             // Polynomial part of the apparent sidereal time series
1958             // which is the opposite of Equation of Origins (EO)
1959             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1960             final PoissonSeriesParser parser =
1961                     new PoissonSeriesParser(17).
1962                         withFirstDelaunay(4).
1963                         withFirstPlanetary(9).
1964                         withSinCos(0, 2, microAS, 3, microAS).
1965                         withPolynomialPart('t', Unit.ARC_SECONDS);
1966             final PolynomialNutation minusEO =
1967                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();
1968 
1969             // create a function evaluating the series
1970             return new TimeScalarFunction() {
1971 
1972                 /** {@inheritDoc} */
1973                 @Override
1974                 public double value(final AbsoluteDate date) {
1975                     return era.value(date) + minusEO.value(evaluateTC(date));
1976                 }
1977 
1978                 /** {@inheritDoc} */
1979                 @Override
1980                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
1981                     return era.value(date).add(minusEO.value(evaluateTC(date)));
1982                 }
1983 
1984             };
1985 
1986         }
1987 
1988         /** {@inheritDoc} */
1989         @Override
1990         public TimeScalarFunction getGMSTRateFunction(final TimeScale ut1) {
1991 
1992             // Earth Rotation Angle
1993             final StellarAngleCapitaine era = new StellarAngleCapitaine(ut1);
1994 
1995             // Polynomial part of the apparent sidereal time series
1996             // which is the opposite of Equation of Origins (EO)
1997             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
1998             final PoissonSeriesParser parser =
1999                     new PoissonSeriesParser(17).
2000                         withFirstDelaunay(4).
2001                         withFirstPlanetary(9).
2002                         withSinCos(0, 2, microAS, 3, microAS).
2003                         withPolynomialPart('t', Unit.ARC_SECONDS);
2004             final PolynomialNutation minusEO =
2005                     parser.parse(getStream(GST_SERIES), GST_SERIES).getPolynomial();
2006 
2007             // create a function evaluating the series
2008             return new TimeScalarFunction() {
2009 
2010                 /** {@inheritDoc} */
2011                 @Override
2012                 public double value(final AbsoluteDate date) {
2013                     return era.getRate() + minusEO.derivative(evaluateTC(date));
2014                 }
2015 
2016                 /** {@inheritDoc} */
2017                 @Override
2018                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
2019                     return minusEO.derivative(evaluateTC(date)).add(era.getRate());
2020                 }
2021 
2022             };
2023 
2024         }
2025 
2026         /** {@inheritDoc} */
2027         @Override
2028         public TimeScalarFunction getGASTFunction(final TimeScale ut1, final EOPHistory eopHistory) {
2029 
2030             // set up nutation arguments
2031             final FundamentalNutationArguments arguments = getNutationArguments(null);
2032 
2033             // mean obliquity function
2034             final TimeScalarFunction epsilon = getMeanObliquityFunction();
2035 
2036             // set up Poisson series
2037             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
2038             final PoissonSeriesParser baseParser =
2039                     new PoissonSeriesParser(17).
2040                         withFirstDelaunay(4).
2041                         withFirstPlanetary(9).
2042                         withSinCos(0, 2, microAS, 3, microAS);
2043             final PoissonSeriesParser gstParser  = baseParser.withPolynomialPart('t', Unit.ARC_SECONDS);
2044             final PoissonSeries psiSeries        = baseParser.parse(getStream(PSI_SERIES), PSI_SERIES);
2045             final PoissonSeries gstSeries        = gstParser.parse(getStream(GST_SERIES), GST_SERIES);
2046             final PoissonSeries.CompiledSeries psiGstSeries =
2047                     PoissonSeries.compile(psiSeries, gstSeries);
2048 
2049             // ERA function
2050             final TimeScalarFunction era = getEarthOrientationAngleFunction(ut1);
2051 
2052             return new TimeScalarFunction() {
2053 
2054                 /** {@inheritDoc} */
2055                 @Override
2056                 public double value(final AbsoluteDate date) {
2057 
2058                     // evaluate equation of origins
2059                     final BodiesElements elements = arguments.evaluateAll(date);
2060                     final double[] angles = psiGstSeries.value(elements);
2061                     final double ddPsi    = (eopHistory == null) ? 0 : eopHistory.getEquinoxNutationCorrection(date)[0];
2062                     final double deltaPsi = angles[0] + ddPsi;
2063                     final double epsilonA = epsilon.value(date);
2064 
2065                     // subtract equation of origin from EA
2066                     // (hence add the series above which have the sign included)
2067                     return era.value(date) + deltaPsi * FastMath.cos(epsilonA) + angles[1];
2068 
2069                 }
2070 
2071                 /** {@inheritDoc} */
2072                 @Override
2073                 public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
2074 
2075                     // evaluate equation of origins
2076                     final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
2077                     final T[] angles = psiGstSeries.value(elements);
2078                     final T ddPsi    = (eopHistory == null) ? date.getField().getZero() : eopHistory.getEquinoxNutationCorrection(date)[0];
2079                     final T deltaPsi = angles[0].add(ddPsi);
2080                     final T epsilonA = epsilon.value(date);
2081 
2082                     // subtract equation of origin from EA
2083                     // (hence add the series above which have the sign included)
2084                     return era.value(date).add(deltaPsi.multiply(epsilonA.cos())).add(angles[1]);
2085 
2086                 }
2087 
2088             };
2089 
2090         }
2091 
2092         /** {@inheritDoc} */
2093         @Override
2094         public TimeVectorFunction getEOPTidalCorrection() {
2095 
2096             // set up nutation arguments
2097             // BEWARE! Using TT as the time scale here and not UT1 is intentional!
2098             // as this correction is used to compute UT1 itself, it is not surprising we cannot use UT1 yet,
2099             // however, using the close UTC as would seem logical make the comparison with interp.f from IERS fail
2100             // looking in the interp.f code, the same TT scale is used for both Delaunay and gamma argument
2101             final FundamentalNutationArguments arguments = getNutationArguments(TimeScalesFactory.getTT());
2102 
2103             // set up Poisson series
2104             final double microAS = Constants.ARC_SECONDS_TO_RADIANS * 1.0e-6;
2105             final PoissonSeriesParseronSeriesParser">PoissonSeriesParser xyParser = new PoissonSeriesParser(13).
2106                     withOptionalColumn(1).
2107                     withGamma(2).
2108                     withFirstDelaunay(3);
2109             final PoissonSeries xSeries =
2110                     xyParser.
2111                     withSinCos(0, 10, microAS, 11, microAS).
2112                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
2113             final PoissonSeries ySeries =
2114                     xyParser.
2115                     withSinCos(0, 12, microAS, 13, microAS).
2116                     parse(getStream(TIDAL_CORRECTION_XP_YP_SERIES), TIDAL_CORRECTION_XP_YP_SERIES);
2117 
2118             final double microS = 1.0e-6;
2119             final PoissonSeriesParsernSeriesParser">PoissonSeriesParser ut1Parser = new PoissonSeriesParser(11).
2120                     withOptionalColumn(1).
2121                     withGamma(2).
2122                     withFirstDelaunay(3).
2123                     withSinCos(0, 10, microS, 11, microS);
2124             final PoissonSeries ut1Series =
2125                     ut1Parser.parse(getStream(TIDAL_CORRECTION_UT1_SERIES), TIDAL_CORRECTION_UT1_SERIES);
2126 
2127             return new EOPTidalCorrection(arguments, xSeries, ySeries, ut1Series);
2128 
2129         }
2130 
2131         /** {@inheritDoc} */
2132         @Override
2133         public double[] getNominalTidalDisplacement() {
2134             return new double[] {
2135                 // h⁽⁰⁾,                                  h⁽²⁾,   h₃,     hI diurnal, hI semi-diurnal,
2136                 0.6078,                                  -0.0006, 0.292, -0.0025,    -0.0022,
2137                 // l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾,   l₃,     lI diurnal, lI semi-diurnal
2138                 0.0847,  0.0012,       0.0024,            0.0002, 0.015, -0.0007,    -0.0007,
2139                 // H₀
2140                 -0.31460
2141             };
2142         }
2143 
2144         /** {@inheritDoc} */
2145         @Override
2146         public CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal() {
2147             return getTidalDisplacementFrequencyCorrectionDiurnal(TIDAL_DISPLACEMENT_CORRECTION_DIURNAL,
2148                                                                   18, 15, 16, 17, 18);
2149         }
2150 
2151         /** {@inheritDoc} */
2152         @Override
2153         public CompiledSeries getTidalDisplacementFrequencyCorrectionZonal() {
2154             return getTidalDisplacementFrequencyCorrectionZonal(TIDAL_DISPLACEMENT_CORRECTION_ZONAL,
2155                                                                 18, 15, 16, 17, 18);
2156         }
2157 
2158     };
2159 
2160     /** IERS conventions resources base directory. */
2161     private static final String IERS_BASE = "/assets/org/orekit/IERS-conventions/";
2162 
2163     /** Get the reference epoch for fundamental nutation arguments.
2164      * @return reference epoch for fundamental nutation arguments
2165      * @since 6.1
2166      */
2167     public AbsoluteDate getNutationReferenceEpoch() {
2168         // IERS 1996, IERS 2003 and IERS 2010 use the same J2000.0 reference date
2169         return AbsoluteDate.J2000_EPOCH;
2170     }
2171 
2172     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
2173      * @param date current date
2174      * @return date offset in Julian centuries
2175      * @since 6.1
2176      */
2177     public double evaluateTC(final AbsoluteDate date) {
2178         return date.durationFrom(getNutationReferenceEpoch()) / Constants.JULIAN_CENTURY;
2179     }
2180 
2181     /** Evaluate the date offset between the current date and the {@link #getNutationReferenceEpoch() reference date}.
2182      * @param date current date
2183      * @param <T> type of the field elements
2184      * @return date offset in Julian centuries
2185      * @since 9.0
2186      */
2187     public <T extends RealFieldElement<T>> T evaluateTC(final FieldAbsoluteDate<T> date) {
2188         return date.durationFrom(getNutationReferenceEpoch()).divide(Constants.JULIAN_CENTURY);
2189     }
2190 
2191     /** Get the fundamental nutation arguments.
2192      * @param timeScale time scale for computing Greenwich Mean Sidereal Time
2193      * (typically {@link TimeScalesFactory#getUT1(IERSConventions, boolean) UT1})
2194      * @return fundamental nutation arguments
2195           * @since 6.1
2196      */
2197     public abstract FundamentalNutationArguments getNutationArguments(TimeScale timeScale);
2198 
2199     /** Get the function computing mean obliquity of the ecliptic.
2200      * @return function computing mean obliquity of the ecliptic
2201           * @since 6.1
2202      */
2203     public abstract TimeScalarFunction getMeanObliquityFunction();
2204 
2205     /** Get the function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components.
2206      * <p>
2207      * The returned function computes the two X, Y components of CIP and the S+XY/2 component of the non-rotating CIO.
2208      * </p>
2209      * @return function computing the Celestial Intermediate Pole and Celestial Intermediate Origin components
2210           * @since 6.1
2211      */
2212     public abstract TimeVectorFunction getXYSpXY2Function();
2213 
2214     /** Get the function computing the raw Earth Orientation Angle.
2215      * <p>
2216      * The raw angle does not contain any correction. If for example dTU1 correction
2217      * due to tidal effect is desired, it must be added afterward by the caller.
2218      * The returned value contain the angle as the value and the angular rate as
2219      * the first derivative.
2220      * </p>
2221      * @param ut1 UT1 time scale
2222      * @return function computing the rawEarth Orientation Angle, in the non-rotating origin paradigm
2223      * @since 6.1
2224      */
2225     public TimeScalarFunction getEarthOrientationAngleFunction(final TimeScale ut1) {
2226         return new StellarAngleCapitaine(ut1);
2227     }
2228 
2229 
2230     /** Get the function computing the precession angles.
2231      * <p>
2232      * The function returned computes the three precession angles
2233      * ψ<sub>A</sub> (around Z axis), ω<sub>A</sub> (around X axis)
2234      * and χ<sub>A</sub> (around Z axis). The constant angle ε₀
2235      * for the fourth rotation (around X axis) can be retrieved by evaluating the
2236      * function returned by {@link #getMeanObliquityFunction()} at {@link
2237      * #getNutationReferenceEpoch() nutation reference epoch}.
2238      * </p>
2239      * @return function computing the precession angle
2240           * @since 6.1
2241      */
2242     public abstract TimeVectorFunction getPrecessionFunction();
2243 
2244     /** Get the function computing the nutation angles.
2245      * <p>
2246      * The function returned computes the two classical angles ΔΨ and Δε,
2247      * and the correction to the equation of equinoxes introduced since 1997-02-27 by IAU 1994
2248      * resolution C7 (the correction is forced to 0 before this date)
2249      * </p>
2250      * @return function computing the nutation in longitude ΔΨ and Δε
2251      * and the correction of equation of equinoxes
2252           * @since 6.1
2253      */
2254     public abstract TimeVectorFunction getNutationFunction();
2255 
2256     /** Get the function computing Greenwich mean sidereal time, in radians.
2257      * @param ut1 UT1 time scale
2258      * @return function computing Greenwich mean sidereal time
2259           * @since 6.1
2260      */
2261     public abstract TimeScalarFunction getGMSTFunction(TimeScale ut1);
2262 
2263     /** Get the function computing Greenwich mean sidereal time rate, in radians per second.
2264      * @param ut1 UT1 time scale
2265      * @return function computing Greenwich mean sidereal time rate
2266           * @since 9.0
2267      */
2268     public abstract TimeScalarFunction getGMSTRateFunction(TimeScale ut1);
2269 
2270     /** Get the function computing Greenwich apparent sidereal time, in radians.
2271      * @param ut1 UT1 time scale
2272      * @param eopHistory EOP history
2273      * @return function computing Greenwich apparent sidereal time
2274           * @since 6.1
2275      */
2276     public abstract TimeScalarFunction getGASTFunction(TimeScale ut1, EOPHistory eopHistory);
2277 
2278     /** Get the function computing tidal corrections for Earth Orientation Parameters.
2279      * @return function computing tidal corrections for Earth Orientation Parameters,
2280      * for xp, yp, ut1 and lod respectively
2281           * @since 6.1
2282      */
2283     public abstract TimeVectorFunction getEOPTidalCorrection();
2284 
2285     /** Get the Love numbers.
2286      * @return Love numbers
2287           * @since 6.1
2288      */
2289     public abstract LoveNumbers getLoveNumbers();
2290 
2291     /** Get the function computing frequency dependent terms (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
2292      * @param ut1 UT1 time scale
2293      * @return frequency dependence model for tides computation (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
2294           * @since 6.1
2295      */
2296     public abstract TimeVectorFunction getTideFrequencyDependenceFunction(TimeScale ut1);
2297 
2298     /** Get the permanent tide to be <em>removed</em> from ΔC₂₀ when zero-tide potentials are used.
2299      * @return permanent tide to remove
2300      */
2301     public abstract double getPermanentTide();
2302 
2303     /** Get the function computing solid pole tide (ΔC₂₁, ΔS₂₁).
2304      * @param eopHistory EOP history
2305      * @return model for solid pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
2306           * @since 6.1
2307      */
2308     public abstract TimeVectorFunction getSolidPoleTide(EOPHistory eopHistory);
2309 
2310     /** Get the function computing ocean pole tide (ΔC₂₁, ΔS₂₁).
2311      * @param eopHistory EOP history
2312      * @return model for ocean pole tide (ΔC₂₀, ΔC₂₁, ΔS₂₁, ΔC₂₂, ΔS₂₂).
2313           * @since 6.1
2314      */
2315     public abstract TimeVectorFunction getOceanPoleTide(EOPHistory eopHistory);
2316 
2317     /** Get the nominal values of the displacement numbers.
2318      * @return an array containing h⁽⁰⁾, h⁽²⁾, h₃, hI diurnal, hI semi-diurnal,
2319      * l⁽⁰⁾, l⁽¹⁾ diurnal, l⁽¹⁾ semi-diurnal, l⁽²⁾, l₃, lI diurnal, lI semi-diurnal,
2320      * H₀ permanent deformation amplitude
2321      * @since 9.1
2322      */
2323     public abstract double[] getNominalTidalDisplacement();
2324 
2325     /** Get the correction function for tidal displacement for diurnal tides.
2326      * <ul>
2327      *  <li>f[0]: radial correction, longitude cosine part</li>
2328      *  <li>f[1]: radial correction, longitude sine part</li>
2329      *  <li>f[2]: North correction, longitude cosine part</li>
2330      *  <li>f[3]: North correction, longitude sine part</li>
2331      *  <li>f[4]: East correction, longitude cosine part</li>
2332      *  <li>f[5]: East correction, longitude sine part</li>
2333      * </ul>
2334      * @return correction function for tidal displacement
2335      * @since 9.1
2336      */
2337     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal();
2338 
2339     /** Get the correction function for tidal displacement for diurnal tides.
2340      * <ul>
2341      *  <li>f[0]: radial correction, longitude cosine part</li>
2342      *  <li>f[1]: radial correction, longitude sine part</li>
2343      *  <li>f[2]: North correction, longitude cosine part</li>
2344      *  <li>f[3]: North correction, longitude sine part</li>
2345      *  <li>f[4]: East correction, longitude cosine part</li>
2346      *  <li>f[5]: East correction, longitude sine part</li>
2347      * </ul>
2348      * @param tableName name for the diurnal tides table
2349      * @param cols total number of columns of the diurnal tides table
2350      * @param rIp column holding ∆Rf(ip) in the diurnal tides table, counting from 1
2351      * @param rOp column holding ∆Rf(op) in the diurnal tides table, counting from 1
2352      * @param tIp column holding ∆Tf(ip) in the diurnal tides table, counting from 1
2353      * @param tOp column holding ∆Tf(op) in the diurnal tides table, counting from 1
2354      * @return correction function for tidal displacement for diurnal tides
2355           * @since 9.1
2356      */
2357     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionDiurnal(final String tableName, final int cols,
2358                                                                                    final int rIp, final int rOp,
2359                                                                                    final int tIp, final int tOp) {
2360 
2361         // radial component, missing the sin 2φ factor; this corresponds to:
2362         //  - equation 15a in IERS conventions 1996, chapter 7
2363         //  - equation 16a in IERS conventions 2003, chapter 7
2364         //  - equation 7.12a in IERS conventions 2010, chapter 7
2365         final PoissonSeries drCos = new PoissonSeriesParser(cols).
2366                                     withOptionalColumn(1).
2367                                     withDoodson(4, 3).
2368                                     withFirstDelaunay(10).
2369                                     withSinCos(0, rIp, +1.0e-3, rOp, +1.0e-3).
2370                                     parse(getStream(tableName), tableName);
2371         final PoissonSeries drSin = new PoissonSeriesParser(cols).
2372                                     withOptionalColumn(1).
2373                                     withDoodson(4, 3).
2374                                     withFirstDelaunay(10).
2375                                     withSinCos(0, rOp, -1.0e-3, rIp, +1.0e-3).
2376                                     parse(getStream(tableName), tableName);
2377 
2378         // North component, missing the cos 2φ factor; this corresponds to:
2379         //  - equation 15b in IERS conventions 1996, chapter 7
2380         //  - equation 16b in IERS conventions 2003, chapter 7
2381         //  - equation 7.12b in IERS conventions 2010, chapter 7
2382         final PoissonSeries dnCos = new PoissonSeriesParser(cols).
2383                                     withOptionalColumn(1).
2384                                     withDoodson(4, 3).
2385                                     withFirstDelaunay(10).
2386                                     withSinCos(0, tIp, +1.0e-3, tOp, +1.0e-3).
2387                                     parse(getStream(tableName), tableName);
2388         final PoissonSeries dnSin = new PoissonSeriesParser(cols).
2389                                     withOptionalColumn(1).
2390                                     withDoodson(4, 3).
2391                                     withFirstDelaunay(10).
2392                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
2393                                     parse(getStream(tableName), tableName);
2394 
2395         // East component, missing the sin φ factor; this corresponds to:
2396         //  - equation 15b in IERS conventions 1996, chapter 7
2397         //  - equation 16b in IERS conventions 2003, chapter 7
2398         //  - equation 7.12b in IERS conventions 2010, chapter 7
2399         final PoissonSeries deCos = new PoissonSeriesParser(cols).
2400                                     withOptionalColumn(1).
2401                                     withDoodson(4, 3).
2402                                     withFirstDelaunay(10).
2403                                     withSinCos(0, tOp, -1.0e-3, tIp, +1.0e-3).
2404                                     parse(getStream(tableName), tableName);
2405         final PoissonSeries deSin = new PoissonSeriesParser(cols).
2406                                     withOptionalColumn(1).
2407                                     withDoodson(4, 3).
2408                                     withFirstDelaunay(10).
2409                                     withSinCos(0, tIp, -1.0e-3, tOp, -1.0e-3).
2410                                     parse(getStream(tableName), tableName);
2411 
2412         return PoissonSeries.compile(drCos, drSin,
2413                                      dnCos, dnSin,
2414                                      deCos, deSin);
2415 
2416     }
2417 
2418     /** Get the correction function for tidal displacement for zonal tides.
2419      * <ul>
2420      *  <li>f[0]: radial correction</li>
2421      *  <li>f[1]: North correction</li>
2422      * </ul>
2423      * @return correction function for tidal displacement
2424      * @since 9.1
2425      */
2426     public abstract CompiledSeries getTidalDisplacementFrequencyCorrectionZonal();
2427 
2428     /** Get the correction function for tidal displacement for zonal tides.
2429      * <ul>
2430      *  <li>f[0]: radial correction</li>
2431      *  <li>f[1]: North correction</li>
2432      * </ul>
2433      * @param tableName name for the zonal tides table
2434      * @param cols total number of columns of the table
2435      * @param rIp column holding ∆Rf(ip) in the table, counting from 1
2436      * @param rOp column holding ∆Rf(op) in the table, counting from 1
2437      * @param tIp column holding ∆Tf(ip) in the table, counting from 1
2438      * @param tOp column holding ∆Tf(op) in the table, counting from 1
2439      * @return correction function for tidal displacement for zonal tides
2440           * @since 9.1
2441      */
2442     protected static CompiledSeries getTidalDisplacementFrequencyCorrectionZonal(final String tableName, final int cols,
2443                                                                                  final int rIp, final int rOp,
2444                                                                                  final int tIp, final int tOp) {
2445 
2446         // radial component, missing the 3⁄2 sin² φ - 1⁄2 factor; this corresponds to:
2447         //  - equation 16a in IERS conventions 1996, chapter 7
2448         //  - equation 17a in IERS conventions 2003, chapter 7
2449         //  - equation 7.13a in IERS conventions 2010, chapter 7
2450         final PoissonSeries dr = new PoissonSeriesParser(cols).
2451                                  withOptionalColumn(1).
2452                                  withDoodson(4, 3).
2453                                  withFirstDelaunay(10).
2454                                  withSinCos(0, rOp, +1.0e-3, rIp, +1.0e-3).
2455                                  parse(getStream(tableName), tableName);
2456 
2457         // North component, missing the sin 2φ factor; this corresponds to:
2458         //  - equation 16b in IERS conventions 1996, chapter 7
2459         //  - equation 17b in IERS conventions 2003, chapter 7
2460         //  - equation 7.13b in IERS conventions 2010, chapter 7
2461         final PoissonSeries dn = new PoissonSeriesParser(cols).
2462                                  withOptionalColumn(1).
2463                                  withDoodson(4, 3).
2464                                  withFirstDelaunay(10).
2465                                  withSinCos(0, tOp, +1.0e-3, tIp, +1.0e-3).
2466                                  parse(getStream(tableName), tableName);
2467 
2468         return PoissonSeries.compile(dr, dn);
2469 
2470     }
2471 
2472     /** Interface for functions converting nutation corrections between
2473      * δΔψ/δΔε to δX/δY.
2474      * <ul>
2475      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
2476      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
2477      * </ul>
2478      * @since 6.1
2479      */
2480     public interface NutationCorrectionConverter {
2481 
2482         /** Convert nutation corrections.
2483          * @param date current date
2484          * @param ddPsi δΔψ part of the nutation correction
2485          * @param ddEpsilon δΔε part of the nutation correction
2486          * @return array containing δX and δY
2487          */
2488         double[] toNonRotating(AbsoluteDate date, double ddPsi, double ddEpsilon);
2489 
2490         /** Convert nutation corrections.
2491          * @param date current date
2492          * @param dX δX part of the nutation correction
2493          * @param dY δY part of the nutation correction
2494          * @return array containing δΔψ and δΔε
2495          */
2496         double[] toEquinox(AbsoluteDate date, double dX, double dY);
2497 
2498     }
2499 
2500     /** Create a function converting nutation corrections between
2501      * δX/δY and δΔψ/δΔε.
2502      * <ul>
2503      * <li>δX/δY nutation corrections are used with the Non-Rotating Origin paradigm.</li>
2504      * <li>δΔψ/δΔε nutation corrections are used with the equinox-based paradigm.</li>
2505      * </ul>
2506      * @return a new converter
2507           * @since 6.1
2508      */
2509     public NutationCorrectionConverter getNutationCorrectionConverter() {
2510 
2511         // get models parameters
2512         final TimeVectorFunction precessionFunction = getPrecessionFunction();
2513         final TimeScalarFunction epsilonAFunction = getMeanObliquityFunction();
2514         final AbsoluteDate date0 = getNutationReferenceEpoch();
2515         final double cosE0 = FastMath.cos(epsilonAFunction.value(date0));
2516 
2517         return new NutationCorrectionConverter() {
2518 
2519             /** {@inheritDoc} */
2520             @Override
2521             public double[] toNonRotating(final AbsoluteDate date,
2522                                           final double ddPsi, final double ddEpsilon) {
2523                 // compute precession angles psiA, omegaA and chiA
2524                 final double[] angles = precessionFunction.value(date);
2525 
2526                 // conversion coefficients
2527                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
2528                 final double c     = angles[0] * cosE0 - angles[2];
2529 
2530                 // convert nutation corrections (equation 23/IERS-2003 or 5.25/IERS-2010)
2531                 return new double[] {
2532                     sinEA * ddPsi + c * ddEpsilon,
2533                     ddEpsilon - c * sinEA * ddPsi
2534                 };
2535 
2536             }
2537 
2538             /** {@inheritDoc} */
2539             @Override
2540             public double[] toEquinox(final AbsoluteDate date,
2541                                       final double dX, final double dY) {
2542                 // compute precession angles psiA, omegaA and chiA
2543                 final double[] angles   = precessionFunction.value(date);
2544 
2545                 // conversion coefficients
2546                 final double sinEA = FastMath.sin(epsilonAFunction.value(date));
2547                 final double c     = angles[0] * cosE0 - angles[2];
2548                 final double opC2  = 1 + c * c;
2549 
2550                 // convert nutation corrections (inverse of equation 23/IERS-2003 or 5.25/IERS-2010)
2551                 return new double[] {
2552                     (dX - c * dY) / (sinEA * opC2),
2553                     (dY + c * dX) / opC2
2554                 };
2555             }
2556 
2557         };
2558 
2559     }
2560 
2561     /** Load the Love numbers.
2562      * @param nameLove name of the Love number resource
2563      * @return Love numbers
2564      */
2565     protected LoveNumbers loadLoveNumbers(final String nameLove) {
2566         try {
2567 
2568             // allocate the three triangular arrays for real, imaginary and time-dependent numbers
2569             final double[][] real      = new double[4][];
2570             final double[][] imaginary = new double[4][];
2571             final double[][] plus      = new double[4][];
2572             for (int i = 0; i < real.length; ++i) {
2573                 real[i]      = new double[i + 1];
2574                 imaginary[i] = new double[i + 1];
2575                 plus[i]      = new double[i + 1];
2576             }
2577 
2578             try (InputStream stream = IERSConventions.class.getResourceAsStream(nameLove)) {
2579 
2580                 if (stream == null) {
2581                     // this should never happen with files embedded within Orekit
2582                     throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, nameLove);
2583                 }
2584 
2585                 // setup the reader
2586                 try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"))) {
2587 
2588                     String line = reader.readLine();
2589                     int lineNumber = 1;
2590 
2591                     // look for the Love numbers
2592                     while (line != null) {
2593 
2594                         line = line.trim();
2595                         if (!(line.isEmpty() || line.startsWith("#"))) {
2596                             final String[] fields = line.split("\\p{Space}+");
2597                             if (fields.length != 5) {
2598                                 // this should never happen with files embedded within Orekit
2599                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
2600                                                           lineNumber, nameLove, line);
2601                             }
2602                             final int n = Integer.parseInt(fields[0]);
2603                             final int m = Integer.parseInt(fields[1]);
2604                             if ((n < 2) || (n > 3) || (m < 0) || (m > n)) {
2605                                 // this should never happen with files embedded within Orekit
2606                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
2607                                                           lineNumber, nameLove, line);
2608 
2609                             }
2610                             real[n][m]      = Double.parseDouble(fields[2]);
2611                             imaginary[n][m] = Double.parseDouble(fields[3]);
2612                             plus[n][m]      = Double.parseDouble(fields[4]);
2613                         }
2614 
2615                         // next line
2616                         lineNumber++;
2617                         line = reader.readLine();
2618 
2619                     }
2620                 }
2621             }
2622 
2623             return new LoveNumbers(real, imaginary, plus);
2624 
2625         } catch (IOException ioe) {
2626             // this should never happen with files embedded within Orekit
2627             throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, nameLove);
2628         }
2629     }
2630 
2631     /** Get a data stream.
2632      * @param name file name of the resource stream
2633      * @return stream
2634      */
2635     private static InputStream getStream(final String name) {
2636         return IERSConventions.class.getResourceAsStream(name);
2637     }
2638 
2639     /** Correction to equation of equinoxes.
2640      * <p>IAU 1994 resolution C7 added two terms to the equation of equinoxes
2641      * taking effect since 1997-02-27 for continuity
2642      * </p>
2643      */
2644     private static class IAU1994ResolutionC7 {
2645 
2646         /** First Moon correction term for the Equation of the Equinoxes. */
2647         private static final double EQE1 =     0.00264  * Constants.ARC_SECONDS_TO_RADIANS;
2648 
2649         /** Second Moon correction term for the Equation of the Equinoxes. */
2650         private static final double EQE2 =     0.000063 * Constants.ARC_SECONDS_TO_RADIANS;
2651 
2652         /** Start date for applying Moon corrections to the equation of the equinoxes.
2653          * This date corresponds to 1997-02-27T00:00:00 UTC, hence the 30s offset from TAI.
2654          */
2655         private static final AbsoluteDate MODEL_START =
2656             new AbsoluteDate(1997, 2, 27, 0, 0, 30, TimeScalesFactory.getTAI());
2657 
2658         /** Evaluate the correction.
2659          * @param arguments Delaunay for nutation
2660          * @return correction value (0 before 1997-02-27)
2661          */
2662         public static double value(final DelaunayArguments arguments) {
2663             if (arguments.getDate().compareTo(MODEL_START) >= 0) {
2664 
2665                 // IAU 1994 resolution C7 added two terms to the equation of equinoxes
2666                 // taking effect since 1997-02-27 for continuity
2667 
2668                 // Mean longitude of the ascending node of the Moon
2669                 final double om = arguments.getOmega();
2670 
2671                 // add the two correction terms
2672                 return EQE1 * FastMath.sin(om) + EQE2 * FastMath.sin(om + om);
2673 
2674             } else {
2675                 return 0.0;
2676             }
2677         }
2678 
2679         /** Evaluate the correction.
2680          * @param arguments Delaunay for nutation
2681          * @param <T> type of the field elements
2682          * @return correction value (0 before 1997-02-27)
2683          */
2684         public static <T extends RealFieldElement<T>> T value(final FieldDelaunayArguments<T> arguments) {
2685             if (arguments.getDate().toAbsoluteDate().compareTo(MODEL_START) >= 0) {
2686 
2687                 // IAU 1994 resolution C7 added two terms to the equation of equinoxes
2688                 // taking effect since 1997-02-27 for continuity
2689 
2690                 // Mean longitude of the ascending node of the Moon
2691                 final T om = arguments.getOmega();
2692 
2693                 // add the two correction terms
2694                 return om.sin().multiply(EQE1).add(om.add(om).sin().multiply(EQE2));
2695 
2696             } else {
2697                 return arguments.getDate().getField().getZero();
2698             }
2699         }
2700 
2701     };
2702 
2703     /** Stellar angle model.
2704      * <p>
2705      * The stellar angle computed here has been defined in the paper "A non-rotating origin on the
2706      * instantaneous equator: Definition, properties and use", N. Capitaine, Guinot B., and Souchay J.,
2707      * Celestial Mechanics, Volume 39, Issue 3, pp 283-307. It has been proposed as a conventional
2708      * conventional relationship between UT1 and Earth rotation in the paper "Definition of the Celestial
2709      * Ephemeris origin and of UT1 in the International Celestial Reference Frame", Capitaine, N.,
2710      * Guinot, B., and McCarthy, D. D., 2000, Astronomy and Astrophysics, 355(1), pp. 398–405.
2711      * </p>
2712      * <p>
2713      * It is presented simply as stellar angle in IERS conventions 1996 but as since been adopted as
2714      * the conventional relationship defining UT1 from ICRF and is called Earth Rotation Angle in
2715      * IERS conventions 2003 and 2010.
2716      * </p>
2717      */
2718     private static class StellarAngleCapitaine implements TimeScalarFunction {
2719 
2720         /** Reference date of Capitaine's Earth Rotation Angle model. */
2721         private static final AbsoluteDatee">AbsoluteDate REFERENCE_DATE = new AbsoluteDate(DateComponents.J2000_EPOCH,
2722                                                                             TimeComponents.H12,
2723                                                                             TimeScalesFactory.getTAI());
2724 
2725         /** Constant term of Capitaine's Earth Rotation Angle model. */
2726         private static final double ERA_0   = MathUtils.TWO_PI * 0.7790572732640;
2727 
2728         /** Rate term of Capitaine's Earth Rotation Angle model.
2729          * (radians per day, main part) */
2730         private static final double ERA_1A  = MathUtils.TWO_PI / Constants.JULIAN_DAY;
2731 
2732         /** Rate term of Capitaine's Earth Rotation Angle model.
2733          * (radians per day, fractional part) */
2734         private static final double ERA_1B  = ERA_1A * 0.00273781191135448;
2735 
2736         /** UT1 time scale. */
2737         private final TimeScale ut1;
2738 
2739         /** Simple constructor.
2740          * @param ut1 UT1 time scale
2741          */
2742         StellarAngleCapitaine(final TimeScale ut1) {
2743             this.ut1 = ut1;
2744         }
2745 
2746         /** Get the rotation rate.
2747          * @return rotation rate
2748          */
2749         public double getRate() {
2750             return ERA_1A + ERA_1B;
2751         }
2752 
2753         /** {@inheritDoc} */
2754         @Override
2755         public double value(final AbsoluteDate date) {
2756 
2757             // split the date offset as a full number of days plus a smaller part
2758             final int secondsInDay = 86400;
2759             final double dt  = date.durationFrom(REFERENCE_DATE);
2760             final long days  = ((long) dt) / secondsInDay;
2761             final double dtA = secondsInDay * days;
2762             final double dtB = (dt - dtA) + ut1.offsetFromTAI(date);
2763 
2764             return ERA_0 + ERA_1A * dtB + ERA_1B * (dtA + dtB);
2765 
2766         }
2767 
2768         /** {@inheritDoc} */
2769         @Override
2770         public <T extends RealFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
2771 
2772             // split the date offset as a full number of days plus a smaller part
2773             final int secondsInDay = 86400;
2774             final T dt  = date.durationFrom(REFERENCE_DATE);
2775             final long days  = ((long) dt.getReal()) / secondsInDay;
2776             final double dtA = secondsInDay * days;
2777             final T dtB = dt.subtract(dtA).add(ut1.offsetFromTAI(date.toAbsoluteDate()));
2778 
2779             return dtB.add(dtA).multiply(ERA_1B).add(dtB.multiply(ERA_1A)).add(ERA_0);
2780 
2781         }
2782 
2783     }
2784 
2785     /** Mean pole. */
2786     private static class MeanPole implements TimeStamped, Serializable {
2787 
2788         /** Serializable UID. */
2789         private static final long serialVersionUID = 20131028l;
2790 
2791         /** Date. */
2792         private final AbsoluteDate date;
2793 
2794         /** X coordinate. */
2795         private double x;
2796 
2797         /** Y coordinate. */
2798         private double y;
2799 
2800         /** Simple constructor.
2801          * @param date date
2802          * @param x x coordinate
2803          * @param y y coordinate
2804          */
2805         MeanPole(final AbsoluteDate date, final double x, final double y) {
2806             this.date = date;
2807             this.x    = x;
2808             this.y    = y;
2809         }
2810 
2811         /** {@inheritDoc} */
2812         @Override
2813         public AbsoluteDate getDate() {
2814             return date;
2815         }
2816 
2817         /** Get x coordinate.
2818          * @return x coordinate
2819          */
2820         public double getX() {
2821             return x;
2822         }
2823 
2824         /** Get y coordinate.
2825          * @return y coordinate
2826          */
2827         public double getY() {
2828             return y;
2829         }
2830 
2831     }
2832 
2833     /** Local class for precession function. */
2834     private class PrecessionFunction implements TimeVectorFunction {
2835 
2836         /** Polynomial nutation for psiA. */
2837         private final PolynomialNutation psiA;
2838 
2839         /** Polynomial nutation for omegaA. */
2840         private final PolynomialNutation omegaA;
2841 
2842         /** Polynomial nutation for chiA. */
2843         private final PolynomialNutation chiA;
2844 
2845         /** Simple constructor.
2846          * @param psiA polynomial nutation for psiA
2847          * @param omegaA polynomial nutation for omegaA
2848          * @param chiA polynomial nutation for chiA
2849          */
2850         PrecessionFunction(final PolynomialNutation psiA,
2851                            final PolynomialNutation omegaA,
2852                            final PolynomialNutation chiA) {
2853             this.psiA   = psiA;
2854             this.omegaA = omegaA;
2855             this.chiA   = chiA;
2856         }
2857 
2858 
2859         /** {@inheritDoc} */
2860         @Override
2861         public double[] value(final AbsoluteDate date) {
2862             final double tc = evaluateTC(date);
2863             return new double[] {
2864                 psiA.value(tc), omegaA.value(tc), chiA.value(tc)
2865             };
2866         }
2867 
2868         /** {@inheritDoc} */
2869         @Override
2870         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
2871             final T[] a = MathArrays.buildArray(date.getField(), 3);
2872             final T tc = evaluateTC(date);
2873             a[0] = psiA.value(tc);
2874             a[1] = omegaA.value(tc);
2875             a[2] = chiA.value(tc);
2876             return a;
2877         }
2878 
2879     }
2880 
2881     /** Local class for tides frequency function. */
2882     private static class TideFrequencyDependenceFunction implements TimeVectorFunction {
2883 
2884         /** Nutation arguments. */
2885         private final FundamentalNutationArguments arguments;
2886 
2887         /** Correction series. */
2888         private final PoissonSeries.CompiledSeries kSeries;
2889 
2890         /** Simple constructor.
2891          * @param arguments nutation arguments
2892          * @param c20Series correction series for the C20 term
2893          * @param c21Series correction series for the C21 term
2894          * @param s21Series correction series for the S21 term
2895          * @param c22Series correction series for the C22 term
2896          * @param s22Series correction series for the S22 term
2897          */
2898         TideFrequencyDependenceFunction(final FundamentalNutationArguments arguments,
2899                                         final PoissonSeries c20Series,
2900                                         final PoissonSeries c21Series,
2901                                         final PoissonSeries s21Series,
2902                                         final PoissonSeries c22Series,
2903                                         final PoissonSeries s22Series) {
2904             this.arguments = arguments;
2905             this.kSeries   = PoissonSeries.compile(c20Series, c21Series, s21Series, c22Series, s22Series);
2906         }
2907 
2908 
2909         /** {@inheritDoc} */
2910         @Override
2911         public double[] value(final AbsoluteDate date) {
2912             return kSeries.value(arguments.evaluateAll(date));
2913         }
2914 
2915         /** {@inheritDoc} */
2916         @Override
2917         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
2918             return kSeries.value(arguments.evaluateAll(date));
2919         }
2920 
2921     }
2922 
2923     /** Local class for EOP tidal corrections. */
2924     private static class EOPTidalCorrection implements TimeVectorFunction {
2925 
2926         /** Nutation arguments. */
2927         private final FundamentalNutationArguments arguments;
2928 
2929         /** Correction series. */
2930         private final PoissonSeries.CompiledSeries correctionSeries;
2931 
2932         /** Simple constructor.
2933          * @param arguments nutation arguments
2934          * @param xSeries correction series for the x coordinate
2935          * @param ySeries correction series for the y coordinate
2936          * @param ut1Series correction series for the UT1
2937          */
2938         EOPTidalCorrection(final FundamentalNutationArguments arguments,
2939                            final PoissonSeries xSeries,
2940                            final PoissonSeries ySeries,
2941                            final PoissonSeries ut1Series) {
2942             this.arguments        = arguments;
2943             this.correctionSeries = PoissonSeries.compile(xSeries, ySeries, ut1Series);
2944         }
2945 
2946         /** {@inheritDoc} */
2947         @Override
2948         public double[] value(final AbsoluteDate date) {
2949             final BodiesElements elements = arguments.evaluateAll(date);
2950             final double[] correction    = correctionSeries.value(elements);
2951             final double[] correctionDot = correctionSeries.derivative(elements);
2952             return new double[] {
2953                 correction[0],
2954                 correction[1],
2955                 correction[2],
2956                 correctionDot[2] * (-Constants.JULIAN_DAY)
2957             };
2958         }
2959 
2960         /** {@inheritDoc} */
2961         @Override
2962         public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
2963 
2964             final FieldBodiesElements<T> elements = arguments.evaluateAll(date);
2965             final T[] correction    = correctionSeries.value(elements);
2966             final T[] correctionDot = correctionSeries.derivative(elements);
2967             final T[] a = MathArrays.buildArray(date.getField(), 4);
2968             a[0] = correction[0];
2969             a[1] = correction[1];
2970             a[2] = correction[2];
2971             a[3] = correctionDot[2].multiply(-Constants.JULIAN_DAY);
2972             return a;
2973         }
2974 
2975     }
2976 
2977 }