1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.data;
18  
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.InputStream;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  
25  import org.hipparchus.analysis.UnivariateFunction;
26  import org.hipparchus.analysis.differentiation.DSFactory;
27  import org.hipparchus.analysis.differentiation.DerivativeStructure;
28  import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator;
29  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
30  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableVectorFunction;
31  import org.hipparchus.util.Decimal64;
32  import org.hipparchus.util.Decimal64Field;
33  import org.hipparchus.util.FastMath;
34  import org.junit.Assert;
35  import org.junit.Test;
36  import org.orekit.Utils;
37  import org.orekit.errors.OrekitException;
38  import org.orekit.errors.OrekitMessages;
39  import org.orekit.frames.FramesFactory;
40  import org.orekit.time.AbsoluteDate;
41  import org.orekit.time.FieldAbsoluteDate;
42  import org.orekit.time.TimeScale;
43  import org.orekit.time.TimeScalesFactory;
44  import org.orekit.utils.Constants;
45  import org.orekit.utils.IERSConventions;
46  
47  
48  public class PoissonSeriesParserTest {
49  
50      @Test(expected=OrekitException.class)
51      public void testEmptyData() {
52          buildData("");
53      }
54  
55      @Test(expected=OrekitException.class)
56      public void testNoCoeffData() {
57          buildData("this is NOT an IERS nutation model file\n");
58      }
59  
60      @Test(expected=OrekitException.class)
61      public void testEmptyArrayData() {
62          buildData("  0.0 + 0.0 t - 0.0 t^2 - 0.0 t^3 - 0.0 t^4 + 0.0 t^5\n");
63      }
64  
65      @Test(expected=OrekitException.class)
66      public void testMissingTermData() {
67          buildData("  0.0 + 0.0 t - 0.0 t^2 - 0.0 t^3 - 0.0 t^4 + 0.0 t^5\n"
68                    + "j = 0  Nb of terms = 1\n");
69      }
70  
71      private PoissonSeries buildData(String data) {
72          return new PoissonSeriesParser(0).
73                  withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
74                  parse(new ByteArrayInputStream(data.getBytes()),
75                        "<file-content>" + data + "</file-content>");
76      }
77  
78      @Test(expected=OrekitException.class)
79      public void testNoFile() {
80          InputStream stream =
81                  PoissonSeriesParserTest.class.getResourceAsStream("/org/orekit/resources/missing");
82          new PoissonSeriesParser(17).
83              withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
84              withFirstDelaunay(4).
85              withFirstPlanetary(9).
86              withSinCos(0, 2, 1.0, 3, 1.0).
87              parse(stream, "missing");
88      }
89  
90      @Test
91      public void testMissingSeries() {
92          try {
93              String data =
94                      "  0.0 + 0.0 x - 0.0 x^2 - 0.0 x^3 - 0.0 x^4 + 0.0 x^5\n"
95                      + "j = 0  Nb of terms = 1\n"
96                      + "1 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n"
97                      + "j = 1  Nb of terms = 1\n"
98                      + "2 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n"
99                      + "j = 3  Nb of terms = 1\n"
100                     + "3 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n";
101             new PoissonSeriesParser(17).
102                 withPolynomialPart('x', PolynomialParser.Unit.NO_UNITS).
103                 withFirstDelaunay(4).
104                 withFirstPlanetary(9).
105                 withSinCos(0, 2, 1.0, 3, 1.0).
106                 parse(new ByteArrayInputStream(data.getBytes()), "");
107             Assert.fail("an exception should have been thrown");
108         } catch (OrekitException oe) {
109             Assert.assertEquals(OrekitMessages.MISSING_SERIE_J_IN_FILE, oe.getSpecifier());
110             Assert.assertEquals(2, oe.getParts()[0]);
111             Assert.assertEquals(6, oe.getParts()[2]);
112         }
113     }
114 
115     @Test
116     public void testMissingTerms() {
117         try {
118             String data =
119                     "  0.0 + 0.0 x - 0.0 x^2 - 0.0 x^3 - 0.0 x^4 + 0.0 x^5\n"
120                     + "j = 0  Nb of terms = 1\n"
121                     + "1 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n"
122                     + "j = 1  Nb of terms = 3\n"
123                     + "2 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n"
124                     + "3 1.0 0.0 0 0 0 0 0 2 0 0 0 0 0 0 0 0\n"
125                     + "j = 2  Nb of terms = 1\n"
126                     + "4 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n";
127             new PoissonSeriesParser(17).
128                 withPolynomialPart('x', PolynomialParser.Unit.NO_UNITS).
129                 withFirstDelaunay(4).
130                 withFirstPlanetary(9).
131                 withSinCos(0, 2, 1.0, 3, 1.0).
132                 parse(new ByteArrayInputStream(data.getBytes()), "");
133             Assert.fail("an exception should have been thrown");
134         } catch (OrekitException oe) {
135             Assert.assertEquals(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, oe.getSpecifier());
136         }
137     }
138 
139     @Test
140     public void testSmall() {
141         String data =
142             "  0.0 + 0.0 x - 0.0 x^2 - 0.0 x^3 - 0.0 x^4 + 0.0 x^5\n"
143             + "j = 0  Nb of terms = 1\n"
144             + "1 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n";
145         PoissonSeries nd = new PoissonSeriesParser(17).
146                                withPolynomialPart('x', PolynomialParser.Unit.NO_UNITS).
147                                withFirstDelaunay(4).
148                                withFirstPlanetary(9).
149                                withSinCos(0, 2, 1.0, 3, 1.0).
150                                parse(new ByteArrayInputStream(data.getBytes()), "");
151         Assert.assertEquals(1, nd.getNonPolynomialSize());
152     }
153 
154     @Test
155     public void testSecondsMarkers() {
156         String data =
157             "  0''.0 + 0''.0 t - 0''.0 t^2 - 0''.0 t^3 - 0''.0 t^4 + 0''.0 t^5\n"
158             + "j = 0  Nb of terms = 1\n"
159             + "1 1.0 0.0 0 0 0 0 1 0 0 0 0 0 0 0 0 0\n";
160         PoissonSeries nd = new PoissonSeriesParser(17).
161                                withFirstPlanetary(9).
162                                withSinCos(0, 2, 1.0, 3, 1.0).
163                                withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
164                                withFirstDelaunay(4).
165                                parse(new ByteArrayInputStream(data.getBytes()), "");
166         Assert.assertEquals(1, nd.getNonPolynomialSize());
167     }
168 
169     @Test
170     public void testExtract() {
171         String data =
172             "Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A\n"
173             + "precession-nutation model\n"
174             + "\n"
175             + "\n"
176             + "----------------------------------------------------------------------\n"
177             + "\n"
178             + "X = polynomial part + non-polynomial part\n"
179             + "\n"
180             + "----------------------------------------------------------------------\n"
181             + "\n"
182             + "Polynomial part (unit microarcsecond)\n"
183             + "\n"
184             + "  -16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
185             + "\n"
186             + "----------------------------------------------------------------------\n"
187             + "\n"
188             + "Non-polynomial part (unit microarcsecond)\n"
189             + "(ARG being for various combination of the fundamental arguments of the nutation theory)\n"
190             + "\n"
191             + "  Sum_i[a_{s,0})_i * sin(ARG) + a_{c,0})_i * cos(ARG)] \n"
192             + "\n"
193             + "+ Sum_i)j=1,4 [a_{s,j})_i * t^j * sin(ARG) + a_{c,j})_i * cos(ARG)] * t^j]\n"
194             + "\n"
195             + "The Table below provides the values for a_{s,j})_i and a_{c,j})_i\n"
196             + "\n"
197             + "The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) \n"
198             + "and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000\n"
199             + "\n"
200             + "----------------------------------------------------------------------\n"
201             + "\n"
202             + "    i    a_{s,j})_i      a_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A\n"
203             + "\n"
204             + "----------------------------------------------------------------------\n"
205             + "-16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
206             + "j = 0  Nb of terms = 2\n"
207             + "\n"
208             + "   1    -6844318.44        1328.67    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
209             + "   2           0.11           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0\n"
210             + "\n"
211             + "j = 1  Nb of terms = 2\n"
212             + "\n"
213             + "   3       -3328.48      205833.15    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
214             + "   4           0.00          -0.10    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0\n"
215             + "\n"
216             + " j = 2  Nb of terms = 2\n"
217             + "\n"
218             + "   5        2038.00          82.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
219             + "   6          -0.12           0.00    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0\n"
220             + "  \n"
221             + " j = 3  Nb of terms = 2\n"
222             + "\n"
223             + "   7           1.76         -20.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
224             + "   8           0.00           0.20    0    0    0    0    2    0    0    0    0    0    0    0    0    0\n"
225             + "\n"
226             + " j = 4  Nb of terms = 1\n"
227             + "       \n"
228             + "   9          -0.10          -0.02    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n";
229         // despite there are 9 data lines above, there are only 5 different terms,
230         // as some terms share the same Delaunay and planetary coefficients and are
231         // therefore grouped together. The Delaunay arguments for the 5 terms are:
232         // Ω, 4(F-D+Ω), l-l'-2(F+D)-Ω, l-2(F+D)-Ω and 2Ω
233         Assert.assertEquals(5,
234                             new PoissonSeriesParser(17).
235                              withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
236                              withFirstDelaunay(4).
237                              withFirstPlanetary(9).
238                              withSinCos(0, 2, 1.0, 3, 1.0).
239                              parse(new ByteArrayInputStream(data.getBytes()), "dummy").getNonPolynomialSize());
240     }
241 
242     @Test
243     public void testWrongIndex() {
244         String data =
245             "Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A\n"
246             + "precession-nutation model\n"
247             + "\n"
248             + "\n"
249             + "----------------------------------------------------------------------\n"
250             + "\n"
251             + "X = polynomial part + non-polynomial part\n"
252             + "\n"
253             + "----------------------------------------------------------------------\n"
254             + "\n"
255             + "Polynomial part (unit microarcsecond)\n"
256             + "\n"
257             + "  -16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
258             + "\n"
259             + "----------------------------------------------------------------------\n"
260             + "\n"
261             + "Non-polynomial part (unit microarcsecond)\n"
262             + "(ARG being for various combination of the fundamental arguments of the nutation theory)\n"
263             + "\n"
264             + "  Sum_i[a_{s,0})_i * sin(ARG) + a_{c,0})_i * cos(ARG)] \n"
265             + "\n"
266             + "+ Sum_i)j=1,4 [a_{s,j})_i * t^j * sin(ARG) + a_{c,j})_i * cos(ARG)] * t^j]\n"
267             + "\n"
268             + "The Table below provides the values for a_{s,j})_i and a_{c,j})_i\n"
269             + "\n"
270             + "The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) \n"
271             + "and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000\n"
272             + "\n"
273             + "----------------------------------------------------------------------\n"
274             + "\n"
275             + "    i    a_{s,j})_i      a_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A\n"
276             + "\n"
277             + "----------------------------------------------------------------------\n"
278             + "-16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
279             + "j = 0  Nb of terms = 2\n"
280             + "\n"
281             + "   1    -6844318.44        1328.67    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
282             + "   2           0.11           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0\n"
283             + "\n"
284             + "j = 1  Nb of terms = 2\n"
285             + "\n"
286             + "   3       -3328.48      205833.15    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
287             + "   4           0.00          -0.10    1   -1   -2   -2   -1    0    0    0    0    0    0    0    0    0\n"
288             + "\n"
289             + " j = 2  Nb of terms = 2\n"
290             + "\n"
291             + "   5        2038.00          82.26    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
292             + "   6          -0.12           0.00    1    0   -2   -2   -1    0    0    0    0    0    0    0    0    0\n"
293             + "  \n"
294             + " j = 3  Nb of terms = 2\n"
295             + "\n"
296             + "   7           1.76         -20.39    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
297             + " 999           0.00           0.20    0    0    0    0    2    0    0    0    0    0    0    0    0    0\n"
298             + "\n"
299             + " j = 4  Nb of terms = 1\n"
300             + "       \n"
301             + "   9          -0.10          -0.02    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n";
302         try {
303             new PoissonSeriesParser(17).
304                 withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
305                 withFirstDelaunay(4).
306                 withFirstPlanetary(9).
307                 withSinCos(0, 2, 1.0, 3, 1.0).
308                 parse(new ByteArrayInputStream(data.getBytes()), "dummy");
309             Assert.fail("an exception should have been thrown");
310         } catch (OrekitException oe) {
311             Assert.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
312             Assert.assertEquals(53, oe.getParts()[0]);
313             Assert.assertTrue(((String) oe.getParts()[2]).startsWith(" 999           0.00"));
314         }
315     }
316 
317     @Test
318     public void testTruncated() {
319         String data =
320             "Expression for the X coordinate of the CIP in the GCRS based on the IAU2000A\n"
321             + "precession-nutation model\n"
322             + "\n"
323             + "\n"
324             + "----------------------------------------------------------------------\n"
325             + "\n"
326             + "X = polynomial part + non-polynomial part\n"
327             + "\n"
328             + "----------------------------------------------------------------------\n"
329             + "\n"
330             + "Polynomial part (unit microarcsecond)\n"
331             + "\n"
332             + "  -16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
333             + "\n"
334             + "----------------------------------------------------------------------\n"
335             + "\n"
336             + "Non-polynomial part (unit microarcsecond)\n"
337             + "(ARG being for various combination of the fundamental arguments of the nutation theory)\n"
338             + "\n"
339             + "  Sum_i[a_{s,0})_i * sin(ARG) + a_{c,0})_i * cos(ARG)] \n"
340             + "\n"
341             + "+ Sum_i)j=1,4 [a_{s,j})_i * t^j * sin(ARG) + a_{c,j})_i * cos(ARG)] * t^j]\n"
342             + "\n"
343             + "The Table below provides the values for a_{s,j})_i and a_{c,j})_i\n"
344             + "\n"
345             + "The expressions for the fundamental arguments appearing in columns 4 to 8 (luni-solar part) \n"
346             + "and in columns 6 to 17 (planetary part) are those of the IERS Conventions 2000\n"
347             + "\n"
348             + "----------------------------------------------------------------------\n"
349             + "\n"
350             + "    i    a_{s,j})_i      a_{c,j})_i    l    l'   F    D   Om L_Me L_Ve  L_E L_Ma  L_J L_Sa  L_U L_Ne  p_A\n"
351             + "\n"
352             + "----------------------------------------------------------------------\n"
353             + "-16616.99 + 2004191742.88 t - 427219.05 t^2 - 198620.54 t^3 - 46.05 t^4 + 5.98 t^5\n"
354             + "j = 0  Nb of terms = 2\n"
355             + "\n"
356             + "   1    -6844318.44        1328.67    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n"
357             + "   2           0.11           0.00    0    0    4   -4    4    0    0    0    0    0    0    0    0    0\n"
358             + "\n"
359             + "j = 1  Nb of terms = 2\n"
360             + "\n"
361             + "   3       -3328.48      205833.15    0    0    0    0    1    0    0    0    0    0    0    0    0    0\n";
362         try {
363             new PoissonSeriesParser(17).
364                 withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
365                 withFirstDelaunay(4).
366                 withFirstPlanetary(9).
367                 withSinCos(0, 2, 1.0, 3, 1.0).
368                 parse(new ByteArrayInputStream(data.getBytes()), "dummy");
369             Assert.fail("an exception should have been thrown");
370         } catch (OrekitException oe) {
371             Assert.assertEquals(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, oe.getSpecifier());
372         }
373     }
374 
375     @Test
376     public void testTrue1996Files() {
377         String directory = "/assets/org/orekit/IERS-conventions/";
378         PoissonSeriesParser parser =
379                 new PoissonSeriesParser(10).
380                     withFirstDelaunay(1).
381                     withSinCos(0, 7, 1.0, -1, 1.0).
382                     withSinCos(1, 8, 1.0, -1, 1.0);
383         InputStream psiStream =
384             getClass().getResourceAsStream(directory + "1996/tab5.1.txt");
385         Assert.assertEquals(106,
386                             parser.parse(psiStream, "1996/tab5.1.txt").getNonPolynomialSize());
387         parser = parser.withSinCos(0, -1, 1.0, 9, 1.0).withSinCos(1, -1, 1.0, 10, 1.0);
388         InputStream epsilonStream =
389             getClass().getResourceAsStream(directory + "1996/tab5.1.txt");
390         Assert.assertNotNull(parser.parse(epsilonStream, "1996/tab5.1.txt"));
391     }
392 
393     @Test
394     public void testTrue2003Files() {
395         String directory = "/assets/org/orekit/IERS-conventions/";
396         PoissonSeriesParser parser =
397                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
398                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
399         InputStream xStream =
400             getClass().getResourceAsStream(directory + "2003/tab5.2a.txt");
401         Assert.assertNotNull(parser.parse(xStream, "2003/tab5.2a.txt"));
402         InputStream yStream =
403             getClass().getResourceAsStream(directory + "2003/tab5.2b.txt");
404         Assert.assertNotNull(parser.parse(yStream, "2003/tab5.2b.txt"));
405         InputStream zStream =
406             getClass().getResourceAsStream(directory + "2003/tab5.2c.txt");
407         Assert.assertNotNull(parser.parse(zStream, "2003/tab5.2c.txt"));
408     }
409 
410     @Test
411     public void testTrue2010Files() {
412         String directory = "/assets/org/orekit/IERS-conventions/";
413         PoissonSeriesParser parser =
414                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
415                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
416         InputStream xStream =
417             getClass().getResourceAsStream(directory + "2010/tab5.2a.txt");
418         Assert.assertNotNull(parser.parse(xStream, "2010/tab5.2a.txt"));
419         InputStream yStream =
420             getClass().getResourceAsStream(directory + "2010/tab5.2b.txt");
421         Assert.assertNotNull(parser.parse(yStream, "2010/tab5.2b.txt"));
422         InputStream zStream =
423                 getClass().getResourceAsStream(directory + "2010/tab5.2d.txt");
424         Assert.assertNotNull(parser.parse(zStream, "2010/tab5.2d.txt"));
425 
426         PoissonSeriesParser correctionParser =
427                 new PoissonSeriesParser(14).withFirstDelaunay(4).withSinCos(0, 11, 1.0, 12, 1.0);
428         InputStream xCorrectionStream =
429                 getClass().getResourceAsStream(directory + "2010/tab5.1a.txt");
430         Assert.assertNotNull(correctionParser.parse(xCorrectionStream, "2010/tab5.1a.txt"));
431         correctionParser = correctionParser.withSinCos(0, 13, 1.0, 14, 1.0);
432         InputStream yCorrectionStream =
433                 getClass().getResourceAsStream(directory + "2010/tab5.1a.txt");
434         Assert.assertNotNull(correctionParser.parse(yCorrectionStream, "2010/tab5.1a.txt"));
435 
436 
437     }
438 
439     @Test
440     public void testCorruptedLDelaunayMultiplier() {
441         checkCorrupted("/tides/tab6.5a-corrupted-l-Delaunay-multiplier.txt", "σ₁");
442     }
443 
444     @Test
445     public void testCorruptedLPrimeDelaunayMultiplier() {
446         checkCorrupted("/tides/tab6.5a-corrupted-lPrime-Delaunay-multiplier.txt", "Q₁");
447     }
448 
449     @Test
450     public void testCorruptedFDelaunayMultiplier() {
451         checkCorrupted("/tides/tab6.5a-corrupted-F-Delaunay-multiplier.txt", "Nτ₁");
452     }
453 
454     @Test
455     public void testCorruptedDDelaunayMultiplier() {
456         checkCorrupted("/tides/tab6.5a-corrupted-D-Delaunay-multiplier.txt", "2Q₁");
457     }
458 
459     @Test
460     public void testCorruptedOmegaDelaunayMultiplier() {
461         checkCorrupted("/tides/tab6.5a-corrupted-Omega-Delaunay-multiplier.txt", "τ₁");
462     }
463 
464     @Test
465     public void testCorruptedDoodsonMultiplier() {
466         checkCorrupted("/tides/tab6.5a-corrupted-Doodson-multiplier.txt", "Lk₁");
467     }
468 
469     @Test
470     public void testCorruptedDoodsonNumber() {
471         checkCorrupted("/tides/tab6.5a-corrupted-Doodson-number.txt", "No₁");
472     }
473 
474     private void checkCorrupted(String resourceName, String lineStart) {
475         try {
476             PoissonSeriesParser parser =
477                     new PoissonSeriesParser(18).
478                     withOptionalColumn(1).
479                     withDoodson(4, 3).
480                     withFirstDelaunay(10).
481                     withSinCos(0, 18, 1.0e-12, 17, 1.0e-12);
482             parser.parse(getClass().getResourceAsStream(resourceName), resourceName);
483             Assert.fail("an exception should have been thrown");
484         } catch (OrekitException oe) {
485             if (lineStart == null) {
486                 Assert.assertEquals(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, oe.getSpecifier());
487             } else {
488                 Assert.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
489                 Assert.assertTrue(((String) oe.getParts()[2]).trim().startsWith(lineStart));
490             }
491         } catch (Exception e) {
492             Assert.fail("wrong exception caught: " + e);
493         }
494     }
495 
496     @Test
497     public void testGammaTauForbidden() {
498         try {
499             new PoissonSeriesParser(18).withGamma(4).withDoodson(4, 3);
500             Assert.fail("an exception should have been thrown");
501         } catch (OrekitException oe) {
502             Assert.assertEquals(OrekitMessages.CANNOT_PARSE_BOTH_TAU_AND_GAMMA, oe.getSpecifier());
503         }
504     }
505 
506     @Test
507     public void testTauGammaForbidden() {
508         try {
509             new PoissonSeriesParser(18).withDoodson(4, 3).withGamma(4);
510             Assert.fail("an exception should have been thrown");
511         } catch (OrekitException oe) {
512             Assert.assertEquals(OrekitMessages.CANNOT_PARSE_BOTH_TAU_AND_GAMMA, oe.getSpecifier());
513         }
514     }
515 
516     @Test
517     public void testCompile() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
518         String directory = "/assets/org/orekit/IERS-conventions/";
519         PoissonSeriesParser parser =
520                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
521                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
522         InputStream xStream =
523             getClass().getResourceAsStream(directory + "2010/tab5.2a.txt");
524         PoissonSeries xSeries = parser.parse(xStream, "2010/tab5.2a.txt");
525         InputStream yStream =
526             getClass().getResourceAsStream(directory + "2010/tab5.2b.txt");
527         PoissonSeries ySeries = parser.parse(yStream, "2010/tab5.2b.txt");
528         InputStream zStream =
529             getClass().getResourceAsStream(directory + "2010/tab5.2d.txt");
530         PoissonSeries sSeries = parser.parse(zStream, "2010/tab5.2d.txt");
531         PoissonSeries.CompiledSeries xysSeries =
532                 PoissonSeries.compile(xSeries, ySeries, sSeries);
533 
534         Method m = IERSConventions.class.getDeclaredMethod("getNutationArguments", TimeScale.class);
535         m.setAccessible(true);
536         FundamentalNutationArguments arguments =
537                 (FundamentalNutationArguments) m.invoke(IERSConventions.IERS_2010, (TimeScale) null);
538 
539         for (double dt = 0; dt < Constants.JULIAN_YEAR; dt += Constants.JULIAN_DAY) {
540             AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
541             BodiesElements elements = arguments.evaluateAll(date);
542             double x     = xSeries.value(elements);
543             double y     = ySeries.value(elements);
544             double s     = sSeries.value(elements);
545             double[] xys = xysSeries.value(elements);
546             Assert.assertEquals(x, xys[0], 1.0e-15 * FastMath.abs(x));
547             Assert.assertEquals(y, xys[1], 1.0e-15 * FastMath.abs(y));
548             Assert.assertEquals(s, xys[2], 1.0e-15 * FastMath.abs(s));
549         }
550 
551     }
552 
553     @Test
554     public void testDerivativesAsField() {
555 
556         Utils.setDataRoot("regular-data");
557         String directory = "/assets/org/orekit/IERS-conventions/";
558         PoissonSeriesParser parser =
559                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
560                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
561         PoissonSeries xSeries =
562                         parser.parse(getClass().getResourceAsStream(directory + "2010/tab5.2a.txt"), "2010/tab5.2a.txt");
563         PoissonSeries ySeries =
564                         parser.parse(getClass().getResourceAsStream(directory + "2010/tab5.2b.txt"), "2010/tab5.2b.txt");
565         PoissonSeries zSeries =
566                         parser.parse(getClass().getResourceAsStream(directory + "2010/tab5.2d.txt"), "2010/tab5.2d.txt");
567 
568         TimeScale ut1 = TimeScalesFactory.getUT1(FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true));
569         FundamentalNutationArguments arguments = IERSConventions.IERS_2010.getNutationArguments(ut1);
570 
571         Coordinate xCoordinate              = new Coordinate(xSeries, arguments);
572         Coordinate yCoordinate              = new Coordinate(ySeries, arguments);
573         Coordinate zCoordinate              = new Coordinate(zSeries, arguments);
574         UnivariateDifferentiableFunction dx = new FiniteDifferencesDifferentiator(4, 0.4).differentiate(xCoordinate);
575         UnivariateDifferentiableFunction dy = new FiniteDifferencesDifferentiator(4, 0.4).differentiate(yCoordinate);
576         UnivariateDifferentiableFunction dz = new FiniteDifferencesDifferentiator(4, 0.4).differentiate(zCoordinate);
577 
578         DSFactory factory = new DSFactory(1, 1);
579         FieldAbsoluteDate<DerivativeStructure> ds2000 = FieldAbsoluteDate.getJ2000Epoch(factory.getDerivativeField());
580         for (double t = 0; t < Constants.JULIAN_DAY; t += 120) {
581 
582             final FieldAbsoluteDate<DerivativeStructure> date = ds2000.shiftedBy(factory.variable(0, t));
583 
584             // direct computation of derivatives
585             FieldBodiesElements<DerivativeStructure> elements = arguments.evaluateAll(date);
586             Assert.assertEquals(0.0, elements.getDate().durationFrom(date).getValue(), 1.0e-15);
587             DerivativeStructure xDirect = xSeries.value(elements);
588             DerivativeStructure yDirect = ySeries.value(elements);
589             DerivativeStructure zDirect = zSeries.value(elements);
590 
591             // finite differences computation of derivatives
592             DerivativeStructure zero = factory.variable(0, 0.0);
593             xCoordinate.setDate(date.toAbsoluteDate());
594             DerivativeStructure xFinite = dx.value(zero);
595             yCoordinate.setDate(date.toAbsoluteDate());
596             DerivativeStructure yFinite = dy.value(zero);
597             zCoordinate.setDate(date.toAbsoluteDate());
598             DerivativeStructure zFinite = dz.value(zero);
599 
600             Assert.assertEquals(xFinite.getValue(),              xDirect.getValue(),              FastMath.abs(7.0e-15 * xFinite.getValue()));
601             Assert.assertEquals(xFinite.getPartialDerivative(1), xDirect.getPartialDerivative(1), FastMath.abs(2.0e-07 * xFinite.getPartialDerivative(1)));
602             Assert.assertEquals(yFinite.getValue(),              yDirect.getValue(),              FastMath.abs(7.0e-15 * yFinite.getValue()));
603             Assert.assertEquals(yFinite.getPartialDerivative(1), yDirect.getPartialDerivative(1), FastMath.abs(2.0e-07 * yFinite.getPartialDerivative(1)));
604             Assert.assertEquals(zFinite.getValue(),              zDirect.getValue(),              FastMath.abs(7.0e-15 * zFinite.getValue()));
605             Assert.assertEquals(zFinite.getPartialDerivative(1), zDirect.getPartialDerivative(1), FastMath.abs(2.0e-07 * zFinite.getPartialDerivative(1)));
606 
607         }
608 
609     }
610 
611     @Test
612     public void testDerivativesFromDoubleAPI() {
613         Utils.setDataRoot("regular-data");
614         String directory = "/assets/org/orekit/IERS-conventions/";
615         PoissonSeriesParser parser =
616                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
617                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
618         InputStream xStream =
619             getClass().getResourceAsStream(directory + "2010/tab5.2a.txt");
620         PoissonSeries xSeries = parser.parse(xStream, "2010/tab5.2a.txt");
621         InputStream yStream =
622             getClass().getResourceAsStream(directory + "2010/tab5.2b.txt");
623         PoissonSeries ySeries = parser.parse(yStream, "2010/tab5.2b.txt");
624         InputStream zStream =
625                 getClass().getResourceAsStream(directory + "2010/tab5.2d.txt");
626         PoissonSeries zSeries = parser.parse(zStream, "2010/tab5.2d.txt");
627 
628         final PoissonSeries.CompiledSeries compiled =
629                         PoissonSeries.compile(xSeries, ySeries, zSeries);
630 
631         TimeScale ut1 = TimeScalesFactory.getUT1(FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true));
632         final FundamentalNutationArguments arguments = IERSConventions.IERS_2010.getNutationArguments(ut1);
633 
634         UnivariateDifferentiableVectorFunction finite = new FiniteDifferencesDifferentiator(4, 0.4).differentiate((double t) ->
635             compiled.value(arguments.evaluateAll(AbsoluteDate.J2000_EPOCH.shiftedBy(t))));
636 
637         DSFactory factory = new DSFactory(1, 1);
638         for (double t = 0; t < Constants.JULIAN_DAY; t += 120) {
639 
640             // computation of derivatives from API
641             double[] dAPI = compiled.derivative(arguments.evaluateAll(AbsoluteDate.J2000_EPOCH.shiftedBy(t)));
642 
643             // finite differences computation of derivatives
644             DerivativeStructure[] d = finite.value(factory.variable(0, t));
645 
646             Assert.assertEquals(d.length, dAPI.length);
647             for (int i = 0; i < d.length; ++i) {
648                 Assert.assertEquals(d[i].getPartialDerivative(1), dAPI[i], FastMath.abs(2.0e-7 * d[i].getPartialDerivative(1)));
649             }
650 
651         }
652 
653     }
654 
655     @Test
656     public void testDerivativesFromFieldAPI() {
657         Utils.setDataRoot("regular-data");
658         String directory = "/assets/org/orekit/IERS-conventions/";
659         PoissonSeriesParser parser =
660                 new PoissonSeriesParser(17).withPolynomialPart('t', PolynomialParser.Unit.NO_UNITS).
661                     withFirstDelaunay(4).withFirstPlanetary(9).withSinCos(0, 2, 1.0, 3, 1.0);
662         InputStream xStream =
663             getClass().getResourceAsStream(directory + "2010/tab5.2a.txt");
664         PoissonSeries xSeries = parser.parse(xStream, "2010/tab5.2a.txt");
665         InputStream yStream =
666             getClass().getResourceAsStream(directory + "2010/tab5.2b.txt");
667         PoissonSeries ySeries = parser.parse(yStream, "2010/tab5.2b.txt");
668         InputStream zStream =
669                 getClass().getResourceAsStream(directory + "2010/tab5.2d.txt");
670         PoissonSeries zSeries = parser.parse(zStream, "2010/tab5.2d.txt");
671 
672         final PoissonSeries.CompiledSeries compiled =
673                         PoissonSeries.compile(xSeries, ySeries, zSeries);
674 
675         TimeScale ut1 = TimeScalesFactory.getUT1(FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true));
676         final FundamentalNutationArguments arguments = IERSConventions.IERS_2010.getNutationArguments(ut1);
677 
678         UnivariateDifferentiableVectorFunction finite = new FiniteDifferencesDifferentiator(4, 0.4).differentiate((double t) ->
679             compiled.value(arguments.evaluateAll(AbsoluteDate.J2000_EPOCH.shiftedBy(t))));
680 
681         DSFactory factory = new DSFactory(1, 1);
682         for (double t = 0; t < Constants.JULIAN_DAY; t += 120) {
683 
684             // computation of derivatives from API
685             Decimal64[] dAPI = compiled.derivative(arguments.evaluateAll(FieldAbsoluteDate.getJ2000Epoch(Decimal64Field.getInstance()).shiftedBy(t)));
686 
687             // finite differences computation of derivatives
688             DerivativeStructure[] d = finite.value(factory.variable(0, t));
689 
690             Assert.assertEquals(d.length, dAPI.length);
691             for (int i = 0; i < d.length; ++i) {
692                 Assert.assertEquals(d[i].getPartialDerivative(1), dAPI[i].getReal(), FastMath.abs(2.0e-7 * d[i].getPartialDerivative(1)));
693             }
694 
695         }
696 
697     }
698 
699     private static class Coordinate implements UnivariateFunction {
700         private final PoissonSeries series;
701         private final FundamentalNutationArguments arguments;
702         private AbsoluteDate date;
703         Coordinate(PoissonSeries series, FundamentalNutationArguments arguments) {
704             this.series    = series;
705             this.arguments = arguments;
706             this.date      = AbsoluteDate.J2000_EPOCH;
707         }
708         void setDate(AbsoluteDate date) {
709             this.date = date;
710         }
711         public double value(double x) {
712             return series.value(arguments.evaluateAll(date.shiftedBy(x)));
713         }
714     }
715 
716 }