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