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.forces.gravity.potential;
18  
19  import java.lang.reflect.Field;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.hipparchus.exception.Localizable;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.MathUtils;
26  import org.junit.jupiter.api.Assertions;
27  import org.junit.jupiter.api.Test;
28  import org.orekit.Utils;
29  import org.orekit.data.DataContext;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider.NormalizedSphericalHarmonics;
33  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.time.ChronologicalComparator;
36  import org.orekit.time.TimeScale;
37  import org.orekit.time.TimeScalesFactory;
38  import org.orekit.utils.Constants;
39  import org.orekit.utils.TimeSpanMap;
40  
41  public class ICGEMFormatReaderTest {
42  
43      @Test
44      void testReadLimits() {
45          Utils.setDataRoot("potential");
46          GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
47          UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(3, 2);
48          UnnormalizedSphericalHarmonics harmonics = provider.onDate(new AbsoluteDate(2004, 10, 1, 12, 0, 0.0, TimeScalesFactory.getTT()));
49          Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
50          try {
51              harmonics.getUnnormalizedCnm(3, 3);
52              Assertions.fail("an exception should have been thrown");
53          } catch (OrekitException oe) {
54              // expected
55          } catch (Exception e) {
56              Assertions.fail("wrong exception caught: " + e.getLocalizedMessage());
57          }
58          try {
59              harmonics.getUnnormalizedCnm(4, 2);
60              Assertions.fail("an exception should have been thrown");
61          } catch (OrekitException oe) {
62              // expected
63          } catch (Exception e) {
64              Assertions.fail("wrong exception caught: " + e.getLocalizedMessage());
65          }
66          harmonics.getUnnormalizedCnm(3, 2);
67          Assertions.assertEquals(3, provider.getMaxDegree());
68          Assertions.assertEquals(2, provider.getMaxOrder());
69      }
70  
71      @Test
72      void testRegular05cNormalized() {
73          Utils.setDataRoot("potential");
74          GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
75          NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5);
76          Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
77          AbsoluteDate refDate = new AbsoluteDate("2004-10-01T12:00:00", TimeScalesFactory.getTT());
78          AbsoluteDate date = new AbsoluteDate("2013-01-08T10:46:53", TimeScalesFactory.getTT());
79          NormalizedSphericalHarmonics harmonics = provider.onDate(date);
80  
81          double offset     = date.durationFrom(refDate);
82          double offsetYear = offset / Constants.JULIAN_YEAR;
83          Assertions.assertEquals(0.957212879862e-06 + offsetYear * 0.490000000000e-11,
84                              harmonics.getNormalizedCnm(3, 0), 1.0e-15);
85          Assertions.assertEquals( 0.174804558032e-06, harmonics.getNormalizedCnm(5, 5), 1.0e-15);
86          Assertions.assertEquals( 0.0,                harmonics.getNormalizedSnm(4, 0), 1.0e-15);
87          Assertions.assertEquals( 0.308816581016e-06, harmonics.getNormalizedSnm(4, 4), 1.0e-15);
88          Assertions.assertEquals(0.3986004415E+15, provider.getMu(), 1.0e-20);
89          Assertions.assertEquals(0.6378136460E+07, provider.getAe(), 1.0e-20);
90      }
91  
92      @Test
93      void testMoonGravityField() {
94          Utils.setDataRoot("potential");
95          GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("GrazLGM300c.truncated", false));
96          NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(12, 12);
97          Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
98          Assertions.assertEquals(4.9028010560e+12, provider.getMu(), 1.0e-20);
99          Assertions.assertEquals(1.7380000000e+06, provider.getAe(), 1.0e-20);
100     }
101 
102     @Test
103     void testVenusGravityField() {
104         Utils.setDataRoot("potential");
105         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("shgj180ua01.truncated", false));
106         NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(12, 12);
107         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
108         Assertions.assertEquals(3.248585920790e+14, provider.getMu(), 1.0e-20);
109         Assertions.assertEquals(6.0510e+06,         provider.getAe(), 1.0e-20);
110     }
111 
112     @Test
113     void testMarsGravityField() {
114         Utils.setDataRoot("potential");
115         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("jgm85f01.truncated", false));
116         NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(12, 12);
117         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
118         Assertions.assertEquals(4.28283763830e+13, provider.getMu(), 1.0e-20);
119         Assertions.assertEquals(3.39420e+06,       provider.getAe(), 1.0e-20);
120     }
121 
122     @Test
123     void testRegular05cUnnormalized() {
124         Utils.setDataRoot("potential");
125         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
126         UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 5);
127         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
128         AbsoluteDate date = new AbsoluteDate("2013-01-08T10:46:53", TimeScalesFactory.getTT());
129         UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
130         int maxUlps = 2;
131         checkValue(harmonics.getUnnormalizedCnm(3, 0),
132                    date, 3, 0, 2004, 10, 1, 0.957212879862e-06, 0.490000000000e-11, 0, 0, 0, 0,
133                    maxUlps);
134         checkValue(harmonics.getUnnormalizedCnm(5, 5),
135                    date, 5, 5, 2004, 10, 1, 0.174804558032e-06, 0, 0, 0, 0, 0,
136                    maxUlps);
137         checkValue(harmonics.getUnnormalizedSnm(4, 0),
138                    date, 4, 0, 2004, 10, 1, 0, 0, 0, 0, 0, 0,
139                    maxUlps);
140         checkValue(harmonics.getUnnormalizedSnm(4, 4),
141                    date, 4, 4, 2004, 10, 1, 0.308816581016e-06, 0, 0, 0, 0, 0,
142                    maxUlps);
143         Assertions.assertEquals(0.3986004415E+15, provider.getMu(), 1.0e-20);
144         Assertions.assertEquals(0.6378136460E+07, provider.getAe(), 1.0e-20);
145     }
146 
147     @Test
148     void testEigen06() {
149         Utils.setDataRoot("potential");
150         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", false));
151         UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 5);
152         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
153         AbsoluteDate date = new AbsoluteDate("2013-01-08T10:46:53", TimeScalesFactory.getTT());
154         UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
155         int maxUlps = 3;
156         checkValue(harmonics.getUnnormalizedCnm(3, 0),
157                    date, 3, 0, 2005, 1, 1, 9.57211326674e-07, -8.37191630994e-12,
158                    -1.76087178236e-11, 9.47617140143e-11, 1.06252954726e-11, -9.12524501214e-12,
159                    maxUlps);
160         checkValue(harmonics.getUnnormalizedCnm(5, 5),
161                    date, 5, 5, 2005, 1, 1, 1.74807033099e-07, -1.33498578664e-12,
162                    -2.76043013690e-12, -8.28591865697e-12, 1.57898939101e-12, 2.90931436419e-12,
163                    maxUlps);
164         checkValue(harmonics.getUnnormalizedSnm(4, 0),
165                    date, 4, 0, 2005, 1, 1, 0, 0, 0, 0, 0, 0,
166                    maxUlps);
167         checkValue(harmonics.getUnnormalizedSnm(4, 4),
168                    date, 4, 4, 2005, 1, 1, 3.08820169866e-07, 4.35447782358e-13,
169                    -1.21823769110e-11, 3.89722186321e-11, 7.28153817742e-12, -7.64506592459e-12,
170                    maxUlps);
171         Assertions.assertEquals(0.3986004415E+15, provider.getMu(), 1.0e-20);
172         Assertions.assertEquals(0.6378136460E+07, provider.getAe(), 1.0e-20);
173     }
174 
175     @Test
176     void testEigen06S4() throws OrekitException {
177         Utils.setDataRoot("potential");
178         TimeScale tt = DataContext.getDefault().getTimeScales().getTT();
179         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("EIGEN-6S4-v2-truncated", true, tt));
180         UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(3, 3);
181         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
182 
183         try {
184             Field rawProviderField = WrappingUnnormalizedProvider.class.getDeclaredField("rawProvider");
185             rawProviderField.setAccessible(true);
186             PiecewiseSphericalHarmonics psh = (PiecewiseSphericalHarmonics) rawProviderField.get(provider);
187             Field piecesField = PiecewiseSphericalHarmonics.class.getDeclaredField("pieces");
188             piecesField.setAccessible(true);
189             @SuppressWarnings("unchecked")
190             TimeSpanMap<PiecewisePart> pieces = (TimeSpanMap<PiecewisePart>) piecesField.get(psh);
191 
192             List<AbsoluteDate> ref = new ArrayList<>();
193             ref.add(new AbsoluteDate(1950, 1, 1, 0, 0, 0.0, tt));
194             for (int year = 1986; year <= 2014; year++) {
195                 if (year != 2005 && year != 2010 && year != 2011) {
196                     ref.add(new AbsoluteDate(year, 1, 1, 0, 0, 0.0, tt));
197                 }
198             }
199             ref.add(new AbsoluteDate(1985,  1,  9, 17, 51, 0.0, tt));
200             ref.add(new AbsoluteDate(2002,  8, 15,  8, 17, 0.0, tt));
201             ref.add(new AbsoluteDate(2004, 12, 26,  1,  0, 0.0, tt));
202             ref.add(new AbsoluteDate(2010,  2, 27,  7, 35, 0.0, tt));
203             ref.add(new AbsoluteDate(2011,  3, 11,  5, 15, 0.0, tt));
204             ref.add(new AbsoluteDate(2014,  6, 15,  9, 17, 0.0, tt));
205             ref.add(new AbsoluteDate(2050,  1,  1,  0,  0, 0.0, tt));
206             ref.sort(new ChronologicalComparator());
207             Assertions.assertEquals(35, pieces.getSpansNumber());
208             TimeSpanMap.Transition<PiecewisePart> transition = pieces.getFirstTransition();
209             for (final AbsoluteDate expected : ref) {
210                 Assertions.assertEquals(expected, transition.getDate());
211                 transition = transition.next();
212             }
213             Assertions.assertNull(transition);
214 
215         } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
216             Assertions.fail(e.getLocalizedMessage());
217         }
218 
219         AbsoluteDate date = new AbsoluteDate("2013-01-08T10:46:53", TimeScalesFactory.getTT());
220         UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
221         int maxUlps = 2;
222         checkValue(harmonics.getUnnormalizedCnm(3, 0),
223                    date, 3, 0, 2013, 1, 1, 0, 0, 0.0,
224                    9.57111813364E-07, 7.88508311993E-11,
225                    -7.48797671617E-12, 1.07165711711E-10, -1.77995489691E-11, 8.19305927733E-12,
226                    maxUlps);
227         checkValue(harmonics.getUnnormalizedCnm(3, 2),
228                    date, 3, 2, 2013, 1, 1, 0, 0, 0.0,
229                    9.04721171104E-07, 3.61186647604E-11,
230                    2.80429063599E-11, 2.70919929040E-11, -6.91647796355E-12, 2.03433003058E-11,
231                    maxUlps);
232         checkValue(harmonics.getUnnormalizedSnm(3, 2),
233                    date, 3, 2, 2013, 1, 1, 0, 0, 0.0,
234                    -6.18912728852E-07, -1.81446053580E-10,
235                    -2.59785683825E-11, -2.89784167157E-11, 1.89089403607E-11, -3.61256979359E-11,
236                    maxUlps);
237         checkValue(harmonics.getUnnormalizedSnm(3, 3),
238                    date, 3, 3, 2013, 1, 1, 0, 0, 0.0,
239                    1.41428736507E-06, 3.11677919895E-10,
240                    -8.67871982998E-11, 1.53591055907E-10, -4.45522488259E-11, 8.52872405198E-11,
241                    maxUlps);
242         Assertions.assertEquals(0.3986004415E+15, provider.getMu(), 1.0e-20);
243         Assertions.assertEquals(0.6378136460E+07, provider.getAe(), 1.0e-20);
244     }
245 
246     @Test
247     void testEigen06S4SplitBetween() throws OrekitException {
248         Utils.setDataRoot("potential");
249         TimeScale tai = DataContext.getDefault().getTimeScales().getTAI();
250         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s4-split-between", true, tai));
251         UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(1, 1);
252         Assertions.assertEquals(TideSystem.TIDE_FREE, provider.getTideSystem());
253 
254         try {
255             Field rawProviderField = WrappingUnnormalizedProvider.class.getDeclaredField("rawProvider");
256             rawProviderField.setAccessible(true);
257             PiecewiseSphericalHarmonics psh = (PiecewiseSphericalHarmonics) rawProviderField.get(provider);
258             Field piecesField = PiecewiseSphericalHarmonics.class.getDeclaredField("pieces");
259             piecesField.setAccessible(true);
260             @SuppressWarnings("unchecked")
261             TimeSpanMap<PiecewisePart> pieces = (TimeSpanMap<PiecewisePart>) piecesField.get(psh);
262 
263             List<AbsoluteDate> ref = new ArrayList<>();
264             ref.add(new AbsoluteDate(1950,  1,  1,  0,  0, 0.0, tai));
265             ref.add(new AbsoluteDate(2002,  8, 15,  8, 17, 0.0, tai));
266             ref.add(new AbsoluteDate(2003,  1,  1,  0,  0, 0.0, tai));
267             ref.add(new AbsoluteDate(2002,  1,  1,  0,  0, 0.0, tai));
268             ref.add(new AbsoluteDate(2002,  6,  1,  0,  0, 0.0, tai));
269             ref.add(new AbsoluteDate(2004,  1,  1,  0,  0, 0.0, tai));
270             ref.sort(new ChronologicalComparator());
271             Assertions.assertEquals(7, pieces.getSpansNumber());
272             TimeSpanMap.Transition<PiecewisePart> transition = pieces.getFirstTransition();
273             for (final AbsoluteDate expected : ref) {
274                 Assertions.assertEquals(expected, transition.getDate());
275                 transition = transition.next();
276             }
277             Assertions.assertNull(transition);
278 
279             AbsoluteDate previous = null;
280             for (final AbsoluteDate current : ref) {
281                 if (previous != null) {
282                     UnnormalizedSphericalHarmonics sh = provider.onDate(previous.shiftedBy(0.5 * current.durationFrom(previous)));
283                     Assertions.assertEquals(1.0, sh.getUnnormalizedCnm(0, 0), 1.0e-15);
284                     Assertions.assertEquals(0.0, sh.getUnnormalizedSnm(0, 0), 1.0e-15);
285                 }
286                 previous = current;
287             }
288         } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
289             Assertions.fail(e.getLocalizedMessage());
290         }
291 
292     }
293 
294     @Test
295     void testCorruptedFile01() {
296         doTestCorruptedFile("corrupted-01-g007_eigen_coef", 5, 5,
297                             OrekitMessages.UNEXPECTED_FILE_FORMAT_ERROR_FOR_LOADER, -1);
298     }
299 
300     @Test
301     void testCorruptedFile02() {
302         doTestCorruptedFile("corrupted-02-g007_eigen_coef", 5, 5,
303                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 45);
304     }
305 
306     @Test
307     void testCorruptedFile03() {
308         doTestCorruptedFile("corrupted-03-g007_eigen_coef", 5, 5,
309                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 46);
310     }
311 
312     @Test
313     void testCorruptedFile04() {
314         doTestCorruptedFile("corrupted-04-g007_eigen_coef", 5, 5,
315                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 51);
316     }
317 
318     @Test
319     void testCorruptedFile05() {
320         doTestCorruptedFile("corrupted-05-g007_eigen_coef", 5, 5,
321                             OrekitMessages.MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE, -1);
322     }
323 
324     @Test
325     void testCorruptedFile06() {
326         doTestCorruptedFile("corrupted-06-g007_eigen_coef", 5, 5,
327                             OrekitMessages.SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD, -1);
328     }
329 
330     @Test
331     void testCorruptedFile07() {
332         doTestCorruptedFile("corrupted-07-g007_eigen_coef", 5, 5,
333                             OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE, -1);
334     }
335 
336     @Test
337     void testCorruptedFile08() {
338         doTestCorruptedFile("corrupted-08-g007_eigen_coef", 5, 5,
339                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 107);
340     }
341 
342     @Test
343     void testCorruptedFile09() {
344         doTestCorruptedFile("corrupted-09-g007_eigen_coef", 5, 5,
345                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 113);
346     }
347 
348     @Test
349     void testCorruptedFile10() {
350         doTestCorruptedFile("corrupted-10-eigen-6s4", 3, 3,
351                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 77);
352     }
353 
354     @Test
355     void testCorruptedFile11() {
356         doTestCorruptedFile("corrupted-11-eigen-6s4", 3, 3,
357                             OrekitMessages.DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE, -1);
358     }
359 
360     @Test
361     void testCorruptedFile12() {
362         doTestCorruptedFile("corrupted-12-eigen-6s4", 3, 3,
363                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 53);
364     }
365 
366     @Test
367     void testCorruptedFile13() throws OrekitException {
368         doTestCorruptedFile("corrupted-13-eigen-6s4", 1, 1,
369                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 27);
370     }
371 
372     @Test
373     void testInvalidFormat() {
374         doTestCorruptedFile("dummy_invalid_format_icgem", 3, 3,
375                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 2);
376     }
377 
378     @Test
379     void testUnsupportedFormat() {
380         doTestCorruptedFile("dummy_unsupported_format_icgem", 3, 3,
381                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 2);
382     }
383 
384     @Test
385     void testInvalidProductType() {
386         doTestCorruptedFile("dummy_invalid_product_type_icgem", 3, 3,
387                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 3);
388     }
389 
390     @Test
391     void testInvalidError() {
392         doTestCorruptedFile("dummy_invalid_error_icgem", 3, 3,
393                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 8);
394     }
395 
396     @Test
397     void testInvalidNorm() {
398         doTestCorruptedFile("dummy_invalid_norm_icgem", 3, 3,
399                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 9);
400     }
401 
402     @Test
403     void testInvalidTide() {
404         doTestCorruptedFile("dummy_invalid_tide_icgem", 3, 3,
405                             OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, 10);
406     }
407 
408     private void doTestCorruptedFile(final String name, final int degree, final int order,
409                                      final Localizable specifier, int lineError) {
410         Utils.setDataRoot("potential");
411         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader(name, false));
412         try {
413             GravityFieldFactory.getUnnormalizedProvider(degree, order);
414             Assertions.fail("an exception should have been thrown");
415         } catch (OrekitException oe) {
416             Assertions.assertEquals(specifier, oe.getSpecifier());
417             if (specifier.equals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE)) {
418                 Assertions.assertEquals(lineError, ((Integer) oe.getParts()[0]).intValue());
419             }
420         }
421     }
422 
423 
424     @Test
425     void testZeroTide() {
426         Utils.setDataRoot("potential");
427         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("dummy_zero_tide_icgem", false));
428         Assertions.assertEquals(TideSystem.ZERO_TIDE,
429                             GravityFieldFactory.getUnnormalizedProvider(3, 3).getTideSystem());
430     }
431 
432     @Test
433     void testUnknownTide() {
434         Utils.setDataRoot("potential");
435         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("dummy_unknown_tide_icgem", false));
436         Assertions.assertEquals(TideSystem.UNKNOWN,
437                             GravityFieldFactory.getUnnormalizedProvider(3, 3).getTideSystem());
438     }
439 
440     /** Check numbers in the format 1.0d0 can be parsed. */
441     @Test
442     void testLowercaseD() {
443         Utils.setDataRoot("potential");
444         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("dummy_small_d_icgem", false));
445         Assertions.assertEquals(10.0,
446                 GravityFieldFactory
447                         .getUnnormalizedProvider(3, 3)
448                         .onDate(AbsoluteDate.J2000_EPOCH)
449                         .getUnnormalizedCnm(2, 2),
450                 0.0);
451     }
452 
453     /** check files without 1,0 and 1,1 can be parsed. */
454     @Test
455     void testMissingDegree1() {
456         Utils.setDataRoot("potential");
457         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("dummy_missing_degree_1", false));
458         UnnormalizedSphericalHarmonics harmonics = GravityFieldFactory
459                 .getUnnormalizedProvider(2, 2)
460                 .onDate(AbsoluteDate.J2000_EPOCH);
461         //check coefficients not in the file are initialized correctly
462         Assertions.assertEquals(0.0, harmonics.getUnnormalizedCnm(1, 0), 0.0);
463         Assertions.assertEquals(0.0, harmonics.getUnnormalizedCnm(1, 1), 0.0);
464         //check a coefficient is read correctly
465         Assertions.assertEquals(10.0, harmonics.getUnnormalizedCnm(2, 2), 0.0);
466     }
467 
468     private void checkValue(final double value,
469                             final AbsoluteDate date, final int n, final int m,
470                             final int refYear, final int refMonth, final int refDay,
471                             final double constant, final double trend,
472                             final double cosYear, final double sinYear,
473                             final double cosHalfYear, final double sinHalfYear,
474                             final int maxUlps) {
475         checkValue(value, date, n, m,
476                    refYear, refMonth, refDay, 12, 0, 0.0,
477                    constant, trend,
478                    cosYear, sinYear, cosHalfYear, sinHalfYear,
479                    maxUlps);
480     }
481 
482     private void checkValue(final double value,
483                             final AbsoluteDate date, final int n, final int m,
484                             final int refYear, final int refMonth, final int refDay,
485                             final int refHour, final int refMin, final double refSec,
486                             final double constant, final double trend,
487                             final double cosYear, final double sinYear,
488                             final double cosHalfYear, final double sinHalfYear,
489                             final int maxUlps) {
490         double factor = GravityFieldFactory.getUnnormalizationFactors(n, m)[n][m];
491         AbsoluteDate refDate = new AbsoluteDate(refYear, refMonth, refDay, refHour, refMin, refSec,
492                                                 TimeScalesFactory.getTT());
493         double dtYear = date.durationFrom(refDate) / Constants.JULIAN_YEAR;
494         double unNormalized = factor * (constant +
495                                         trend       * dtYear +
496                                         cosYear     * FastMath.cos(MathUtils.TWO_PI * dtYear) +
497                                         sinYear     * FastMath.sin(MathUtils.TWO_PI * dtYear) +
498                                         cosHalfYear * FastMath.cos(MathUtils.TWO_PI * dtYear * 2) +
499                                         sinHalfYear * FastMath.sin(MathUtils.TWO_PI * dtYear * 2));
500         double epsilon = maxUlps * FastMath.ulp(unNormalized);
501         Assertions.assertEquals(unNormalized, value, epsilon);
502     }
503 
504 }