1   /* Copyright 2011-2012 Space Applications Services
2    * Licensed to CS Communication & Systèmes (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.models.earth.displacement;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.util.FastMath;
21  import org.hipparchus.util.MathUtils;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.BeforeEach;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.Utils;
26  import org.orekit.data.BodiesElements;
27  import org.orekit.data.FundamentalNutationArguments;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.FieldAbsoluteDate;
30  import org.orekit.time.TimeScalarFunction;
31  import org.orekit.time.TimeScale;
32  import org.orekit.time.TimeScalesFactory;
33  import org.orekit.utils.Constants;
34  import org.orekit.utils.IERSConventions;
35  
36  import java.lang.reflect.Field;
37  import java.lang.reflect.InvocationTargetException;
38  import java.lang.reflect.Method;
39  
40  public class TideTest {
41  
42      @Test
43      public void testDoodsonMultipliers() {
44          Assertions.assertArrayEquals(new int[] { 2,  0,  0,  0,  0,  0 }, Tide.M2.getDoodsonMultipliers());
45          Assertions.assertArrayEquals(new int[] { 2,  2, -2,  0,  0,  0 }, Tide.S2.getDoodsonMultipliers());
46          Assertions.assertArrayEquals(new int[] { 2, -1,  0,  1,  0,  0 }, Tide.N2.getDoodsonMultipliers());
47          Assertions.assertArrayEquals(new int[] { 2,  2,  0,  0,  0,  0 }, Tide.K2.getDoodsonMultipliers());
48          Assertions.assertArrayEquals(new int[] { 1,  1,  0,  0,  0,  0 }, Tide.K1.getDoodsonMultipliers());
49          Assertions.assertArrayEquals(new int[] { 1, -1,  0,  0,  0,  0 }, Tide.O1.getDoodsonMultipliers());
50          Assertions.assertArrayEquals(new int[] { 1,  1, -2,  0,  0,  0 }, Tide.P1.getDoodsonMultipliers());
51          Assertions.assertArrayEquals(new int[] { 1, -2,  0,  1,  0,  0 }, Tide.Q1.getDoodsonMultipliers());
52          Assertions.assertArrayEquals(new int[] { 0,  2,  0,  0,  0,  0 }, Tide.MF.getDoodsonMultipliers());
53          Assertions.assertArrayEquals(new int[] { 0,  1,  0, -1,  0,  0 }, Tide.MM.getDoodsonMultipliers());
54          Assertions.assertArrayEquals(new int[] { 0,  0,  2,  0,  0,  0 }, Tide.SSA.getDoodsonMultipliers());
55      }
56  
57      @Test
58      public void testDelaunayMultipliers() {
59          Assertions.assertArrayEquals(new int[] {  0,  0,  2,  0,  2 }, Tide.M2.getDelaunayMultipliers());
60          Assertions.assertArrayEquals(new int[] {  0,  0,  2, -2,  2 }, Tide.S2.getDelaunayMultipliers());
61          Assertions.assertArrayEquals(new int[] {  1,  0,  2,  0,  2 }, Tide.N2.getDelaunayMultipliers());
62          Assertions.assertArrayEquals(new int[] {  0,  0,  0,  0,  0 }, Tide.K2.getDelaunayMultipliers());
63          Assertions.assertArrayEquals(new int[] {  0,  0,  0,  0,  0 }, Tide.K1.getDelaunayMultipliers());
64          Assertions.assertArrayEquals(new int[] {  0,  0,  2,  0,  2 }, Tide.O1.getDelaunayMultipliers());
65          Assertions.assertArrayEquals(new int[] {  0,  0,  2, -2,  2 }, Tide.P1.getDelaunayMultipliers());
66          Assertions.assertArrayEquals(new int[] {  1,  0,  2,  0,  2 }, Tide.Q1.getDelaunayMultipliers());
67          Assertions.assertArrayEquals(new int[] {  0,  0, -2,  0, -2 }, Tide.MF.getDelaunayMultipliers());
68          Assertions.assertArrayEquals(new int[] { -1,  0,  0,  0,  0 }, Tide.MM.getDelaunayMultipliers());
69          Assertions.assertArrayEquals(new int[] {  0,  0, -2,  2, -2 }, Tide.SSA.getDelaunayMultipliers());
70      }
71  
72      @Test
73      public void testTauMultiplier() {
74          Assertions.assertEquals(2, Tide.M2.getTauMultiplier());
75          Assertions.assertEquals(2, Tide.S2.getTauMultiplier());
76          Assertions.assertEquals(2, Tide.N2.getTauMultiplier());
77          Assertions.assertEquals(2, Tide.K2.getTauMultiplier());
78          Assertions.assertEquals(1, Tide.K1.getTauMultiplier());
79          Assertions.assertEquals(1, Tide.O1.getTauMultiplier());
80          Assertions.assertEquals(1, Tide.P1.getTauMultiplier());
81          Assertions.assertEquals(1, Tide.Q1.getTauMultiplier());
82          Assertions.assertEquals(0, Tide.MF.getTauMultiplier());
83          Assertions.assertEquals(0, Tide.MM.getTauMultiplier());
84          Assertions.assertEquals(0, Tide.SSA.getTauMultiplier());
85      }
86  
87      @Test
88      public void testInherited() {
89          Assertions.assertEquals(new Tide(135655), Tide.Q1);
90          Assertions.assertNotEquals(new Tide(163545), new Tide(163544));
91          Assertions.assertNotEquals(new Tide(163545), null);
92          Assertions.assertEquals(163545, new Tide(163545).hashCode());
93      }
94  
95      @Test
96      public void testPhase() {
97          TimeScale                    ut1      = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
98          FundamentalNutationArguments fna      = IERSConventions.IERS_2010.getNutationArguments(ut1);
99          BodiesElements               elements = fna.evaluateAll(new AbsoluteDate("2003-05-17T13:41:54",
100                                                                                  TimeScalesFactory.getUTC()));
101         Assertions.assertEquals(-514.950946, Tide.M2.getPhase(elements),  1.0e-6);
102         Assertions.assertEquals(  19.738518, Tide.S2.getPhase(elements),  1.0e-6);
103         Assertions.assertEquals(-798.252248, Tide.N2.getPhase(elements),  1.0e-6);
104         Assertions.assertEquals(  59.352413, Tide.K2.getPhase(elements),  1.0e-6);
105         Assertions.assertEquals(  29.676206, Tide.K1.getPhase(elements),  1.0e-6);
106         Assertions.assertEquals(-544.627152, Tide.O1.getPhase(elements),  1.0e-6);
107         Assertions.assertEquals(  -9.937688, Tide.P1.getPhase(elements),  1.0e-6);
108         Assertions.assertEquals(-827.928455, Tide.Q1.getPhase(elements),  1.0e-6);
109         Assertions.assertEquals( 574.303358, Tide.MF.getPhase(elements),  1.0e-6);
110         Assertions.assertEquals( 283.301303, Tide.MM.getPhase(elements),  1.0e-6);
111         Assertions.assertEquals(  39.613894, Tide.SSA.getPhase(elements), 1.0e-6);
112     }
113 
114     @Test
115     public void testSortedRatesSemiLongPeriod() {
116         doTestSort(0, Tide.SSA, Tide.MM, Tide.MF);
117     }
118 
119     @Test
120     public void testSortedRatesDiurnal() {
121         doTestSort(1, Tide.Q1, Tide.O1, Tide.P1, Tide.K1);
122     }
123 
124     @Test
125     public void testSortedRatesSemiDiurnal() {
126         doTestSort(2, Tide.N2, Tide.M2, Tide.S2, Tide.K2 );
127     }
128 
129     private void doTestSort(int tauMultiplier, Tide... tides) {
130         // this test checks that tides sort order is date-independent for a large time range
131         // (almost 180000 years long)
132         // tides sort-order is based on rate, but the rates varies slightly with dates
133         // (because Delaunay nutation arguments are polynomials)
134         // The variations are however small and we want to make sure
135         // that if rate(tideA) < rate(tideB) at time t0, this order remains
136         // the same for t1 a few millenia around t0
137         TimeScale                    ut1      = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
138         FundamentalNutationArguments fna      = IERSConventions.IERS_2010.getNutationArguments(ut1);
139         for (double dt = -122000; dt < 54000; dt += 100) {
140             final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt * Constants.JULIAN_YEAR);
141             final BodiesElements el = fna.evaluateAll(date);
142             for (int i = 1; i < tides.length; ++i) {
143                 Assertions.assertTrue(tides[i - 1].getRate(el) < tides[i].getRate(el));
144             }
145         }
146 
147         // check we are in the expected species
148         for (final Tide tide : tides) {
149             Assertions.assertEquals(tauMultiplier, tide.getTauMultiplier());
150         }
151 
152     }
153 
154     @Test
155     public void testTable65a() {
156         doTestTable(new double[][] {
157             { 12.85429,  125755, 1, -3,  0,  2,   0,  0,   2,  0,  2,  0,  2  },
158             { 12.92714,  127555, 1, -3,  2,  0,   0,  0,   0,  0,  2,  2,  2  },
159             { 13.39645,  135645, 1, -2,  0,  1,  -1,  0,   1,  0,  2,  0,  1  },
160             { 13.39866,  135655, 1, -2,  0,  1,   0,  0,   1,  0,  2,  0,  2  },
161             { 13.47151,  137455, 1, -2,  2, -1,   0,  0,  -1,  0,  2,  2,  2  },
162             { 13.94083,  145545, 1, -1,  0,  0,  -1,  0,   0,  0,  2,  0,  1  },
163             { 13.94303,  145555, 1, -1,  0,  0,   0,  0,   0,  0,  2,  0,  2  },
164             { 14.02517,  147555, 1, -1,  2,  0,   0,  0,   0,  0,  0,  2,  0  },
165             { 14.41456,  153655, 1,  0, -2,  1,   0,  0,   1,  0,  2, -2,  2  },
166             { 14.48520,  155445, 1,  0,  0, -1,  -1,  0,  -1,  0,  2,  0,  1  },
167             { 14.48741,  155455, 1,  0,  0, -1,   0,  0,  -1,  0,  2,  0,  2  },
168             { 14.49669,  155655, 1,  0,  0,  1,   0,  0,   1,  0,  0,  0,  0  },
169             { 14.49890,  155665, 1,  0,  0,  1,   1,  0,   1,  0,  0,  0,  1  },
170             { 14.56955,  157455, 1,  0,  2, -1,   0,  0,  -1,  0,  0,  2,  0  },
171             { 14.57176,  157465, 1,  0,  2, -1,   1,  0,  -1,  0,  0,  2,  1  },
172             { 14.91787,  162556, 1,  1, -3,  0,   0,  1,   0,  1,  2, -2,  2  },
173             { 14.95673,  163545, 1,  1, -2,  0,  -1,  0,   0,  0,  2, -2,  1  },
174             { 14.95893,  163555, 1,  1, -2,  0,   0,  0,   0,  0,  2, -2,  2  },
175             { 15.00000,  164554, 1,  1, -1,  0,   0, -1,   0, -1,  2, -2,  2  },
176             { 15.00000,  164556, 1,  1, -1,  0,   0,  1,   0,  1,  0,  0,  0  },
177             { 15.02958,  165345, 1,  1,  0, -2,  -1,  0,  -2,  0,  2,  0,  1  },
178             { 15.03665,  165535, 1,  1,  0,  0,  -2,  0,   0,  0,  0,  0, -2  },
179             { 15.03886,  165545, 1,  1,  0,  0,  -1,  0,   0,  0,  0,  0, -1  },
180             { 15.04107,  165555, 1,  1,  0,  0,   0,  0,   0,  0,  0,  0,  0  },
181             { 15.04328,  165565, 1,  1,  0,  0,   1,  0,   0,  0,  0,  0,  1  },
182             { 15.04548,  165575, 1,  1,  0,  0,   2,  0,   0,  0,  0,  0,  2  },
183             { 15.07749,  166455, 1,  1,  1, -1,   0,  0,  -1,  0,  0,  1,  0  },
184             { 15.07993,  166544, 1,  1,  1,  0,  -1, -1,   0, -1,  0,  0, -1  },
185             { 15.08214,  166554, 1,  1,  1,  0,   0, -1,   0, -1,  0,  0,  0  },
186             { 15.08214,  166556, 1,  1,  1,  0,   0,  1,   0,  1, -2,  2, -2  },
187             { 15.08434,  166564, 1,  1,  1,  0,   1, -1,   0, -1,  0,  0,  1  },
188             { 15.11392,  167355, 1,  1,  2, -2,   0,  0,  -2,  0,  0,  2,  0  },
189             { 15.11613,  167365, 1,  1,  2, -2,   1,  0,  -2,  0,  0,  2,  1  },
190             { 15.12321,  167555, 1,  1,  2,  0,   0,  0,   0,  0, -2,  2, -2  },
191             { 15.12542,  167565, 1,  1,  2,  0,   1,  0,   0,  0, -2,  2, -1  },
192             { 15.16427,  168554, 1,  1,  3,  0,   0, -1,   0, -1, -2,  2, -2  },
193             { 15.51259,  173655, 1,  2, -2,  1,   0,  0,   1,  0,  0, -2,  0  },
194             { 15.51480,  173665, 1,  2, -2,  1,   1,  0,   1,  0,  0, -2,  1  },
195             { 15.58323,  175445, 1,  2,  0, -1,  -1,  0,  -1,  0,  0,  0, -1  },
196             { 15.58545,  175455, 1,  2,  0, -1,   0,  0,  -1,  0,  0,  0,  0  },
197             { 15.58765,  175465, 1,  2,  0, -1,   1,  0,  -1,  0,  0,  0,  1  },
198             { 16.05697,  183555, 1,  3, -2,  0,   0,  0,   0,  0,  0, -2,  0  },
199             { 16.12989,  185355, 1,  3,  0, -2,   0,  0,  -2,  0,  0,  0,  0  },
200             { 16.13911,  185555, 1,  3,  0,  0,   0,  0,   0,  0, -2,  0, -2  },
201             { 16.14131,  185565, 1,  3,  0,  0,   1,  0,   0,  0, -2,  0, -1  },
202             { 16.14352,  185575, 1,  3,  0,  0,   2,  0,   0,  0, -2,  0,  0  },
203             { 16.68348,  195455, 1,  4,  0, -1,   0,  0,  -1,  0, -2,  0, -2  },
204             { 16.68569,  195465, 1,  4,  0, -1,   1,  0,  -1,  0, -2,  0, -1  }
205         });
206     }
207 
208     @Test
209     public void testTable65b() {
210         doTestTable(new double[][] {
211             {  0.00221, 55565,  0, 0,  0,  0,  1,  0,  0,  0,  0,  0,  1 },
212             {  0.00441, 55575,  0, 0,  0,  0,  2,  0,  0,  0,  0,  0,  2 },
213             {  0.04107, 56554,  0, 0,  1,  0,  0, -1,  0, -1,  0,  0,  0 },
214             {  0.08214, 57555,  0, 0,  2,  0,  0,  0,  0,  0, -2,  2, -2 },
215             {  0.08434, 57565,  0, 0,  2,  0,  1,  0,  0,  0, -2,  2, -1 },
216             {  0.12320, 58554,  0, 0,  3,  0,  0, -1,  0, -1, -2,  2, -2 },
217             {  0.47152, 63655,  0, 1, -2,  1,  0,  0,  1,  0,  0, -2,  0 },
218             {  0.54217, 65445,  0, 1,  0, -1, -1,  0, -1,  0,  0,  0, -1 },
219             {  0.54438, 65455,  0, 1,  0, -1,  0,  0, -1,  0,  0,  0,  0 },
220             {  0.54658, 65465,  0, 1,  0, -1,  1,  0, -1,  0,  0,  0,  1 },
221             {  0.55366, 65655,  0, 1,  0,  1,  0,  0,  1,  0, -2,  0, -2 },
222             {  1.01590, 73555,  0, 2, -2,  0,  0,  0,  0,  0,  0, -2,  0 },
223             {  1.08875, 75355,  0, 2,  0, -2,  0,  0, -2,  0,  0,  0,  0 },
224             {  1.09804, 75555,  0, 2,  0,  0,  0,  0,  0,  0, -2,  0, -2 },
225             {  1.10024, 75565,  0, 2,  0,  0,  1,  0,  0,  0, -2,  0, -1 },
226             {  1.10245, 75575,  0, 2,  0,  0,  2,  0,  0,  0, -2,  0,  0 },
227             {  1.56956, 83655,  0, 3, -2,  1,  0,  0,  1,  0, -2, -2, -2 },
228             {  1.64241, 85455,  0, 3,  0, -1,  0,  0, -1,  0, -2,  0, -2 },
229             {  1.64462, 85465,  0, 3,  0, -1,  1,  0, -1,  0, -2,  0, -1 },
230             {  2.11394, 93555,  0, 4, -2,  0,  0,  0,  0,  0, -2, -2, -2 },
231             {  2.18679, 95355,  0, 4,  0, -2,  0,  0, -2,  0, -2,  0, -2 }
232         });
233     }
234 
235     @Test
236     public void testTable65c() {
237         doTestTable(new double[][] {
238             { 28.43973, 245655,  2, -1, 0, 1, 0,   0, 1, 0,  2, 0, 2 },
239             { 28.98410, 255555,  2,  0, 0, 0, 0,   0, 0, 0,  2, 0, 2 }
240         });
241     }
242 
243     @Test
244     public void testTDFRPH1OriginalEarthRotation() {
245         // this is test 1 from the TDFRPH subroutine in IERS HARDISP program
246         // using the reference routine hard coded simplified model for Earth rotation
247         doTestTDFRPH(true,
248                      2009, 6, 25, 0, 0, 0.0, Tide.M2,
249                      1.9322736160568788, 303.343338720297,
250                      5.6e-11, 1.3e-9);
251     }
252 
253     @Test
254     public void testTDFRPH1IERSEarthRotation() {
255         // this is test 1 from the TDFRPH subroutine in IERS HARDISP program
256         // but using regular IERS model for Earth rotation
257         doTestTDFRPH(false,
258                      2009, 6, 25, 0, 0, 0.0, Tide.M2,
259                      1.9322736160568788, 303.343338720297,
260                      5.6e-11, 0.014);
261     }
262 
263     @Test
264     public void testTDFRPH2OriginalEarthRotation() {
265         doTestTDFRPH(true,
266                      2009, 6, 25, 12, 1, 45.0, Tide.M2,
267                      1.9322736160568872, 291.997959322689,
268                      5.6e-11, 3.0e-9);
269     }
270 
271     @Test
272     public void testTDFRPH2IERSEarthRotation() {
273         // this is test 2 from the TDFRPH subroutine in IERS HARDISP program
274         // but using regular IERS model for Earth rotation
275         doTestTDFRPH(false,
276                      2009, 6, 25, 12, 1, 45.0, Tide.M2,
277                      1.9322736160568872, 291.997959322689,
278                      5.6e-11, 0.014);
279     }
280 
281     @Test
282     public void testTDFRPHAdditional1OriginalEarthRotation() {
283         // additional tests, for tides other than M2
284         // reference values were obtained running the TDFRPH subroutine
285         doTestTDFRPH(true,
286                      2009, 6, 25, 12, 1, 45.0, Tide.Q1,
287                      0.89324405952415054, 358.73529357509688,
288                      2.3e-11, 3.4e-9);
289         doTestTDFRPH(true,
290                      2009, 6, 25, 12, 1, 45.0, Tide.O1,
291                      0.92953570674740593, 17.795253026255523,
292                      1.5e-11, 2.5e-9);
293         doTestTDFRPH(true,
294                      2009, 6, 25, 12, 1, 45.0, Tide.SSA,
295                      0.0054758186189623609, 187.53041259286692,
296                      4.6e-11, 1.1e-9);
297         doTestTDFRPH(true,
298                      2009, 6, 25, 12, 1, 45.0, Tide.MM,
299                      0.036291647223255376, 19.059959451136820,
300                      7.5e-12, 9.5e-10);
301     }
302 
303     @Test
304     public void testTDFRPHAdditional1IERSEarthRotation() {
305         // additional tests, for tides other than M2
306         // reference values were obtained running the TDFRPH subroutine
307         doTestTDFRPH(false,
308                      2009, 6, 25, 12, 1, 45.0, Tide.Q1,
309                      0.89324405952415054, 358.73529357509688,
310                      2.3e-11, 0.0066);
311         doTestTDFRPH(false,
312                      2009, 6, 25, 12, 1, 45.0, Tide.O1,
313                      0.92953570674740593, 17.795253026255523,
314                      1.5e-11, 0.0066);
315         doTestTDFRPH(false,
316                      2009, 6, 25, 12, 1, 45.0, Tide.SSA,
317                      0.0054758186189623609, 187.53041259286692,
318                      4.6e-11, 1.1e-9);
319         doTestTDFRPH(false,
320                      2009, 6, 25, 12, 1, 45.0, Tide.MM,
321                      0.036291647223255376, 19.059959451136820,
322                      7.5e-12, 9.5e-10);
323     }
324 
325     @Test
326     public void testTDFRPHAdditional2OriginalEarthRotation() {
327         // additional tests, for tides other than M2
328         // reference values were obtained running the TDFRPH subroutine
329         doTestTDFRPH(true,
330                      2009, 6, 25, 1, 10, 45.0, Tide.Q1,
331                      0.89324405952416042, 213.35982288935338,
332                      2.3e-11, 5.8e-9);
333         doTestTDFRPH(true,
334                      2009, 6, 25, 1, 10, 45.0, Tide.O1,
335                      0.92953570674739971, 226.51331675532856,
336                      1.5e-11, 4.1e-9);
337         doTestTDFRPH(true,
338                      2009, 6, 25, 1, 10, 45.0, Tide.SSA,
339                      0.0054758186189598906, 186.63922310518683,
340                      4.6e-11, 9.4e-10);
341         doTestTDFRPH(true,
342                      2009, 6, 25, 1, 10, 45.0, Tide.MM,
343                      0.036291647223239284, 13.153493865960627,
344                      7.5e-12, 1.8e-9);
345     }
346 
347     @Test
348     public void testTDFRPHAdditional2IERSEarthRotation() {
349         // additional tests, for tides other than M2
350         // reference values were obtained running the TDFRPH subroutine
351         doTestTDFRPH(false,
352                      2009, 6, 25, 1, 10, 45.0, Tide.Q1,
353                      0.89324405952416042, 213.35982288935338,
354                      2.3e-11, 0.0066);
355         doTestTDFRPH(false,
356                      2009, 6, 25, 1, 10, 45.0, Tide.O1,
357                      0.92953570674739971, 226.51331675532856,
358                      1.5e-11, 0.0066);
359         doTestTDFRPH(false,
360                      2009, 6, 25, 1, 10, 45.0, Tide.SSA,
361                      0.0054758186189598906, 186.63922310518683,
362                      4.6e-11, 9.4e-10);
363         doTestTDFRPH(false,
364                      2009, 6, 25, 1, 10, 45.0, Tide.MM,
365                      0.036291647223239284, 13.153493865960627,
366                      7.5e-12, 1.8e-9);
367     }
368 
369     private void doTestTDFRPH(boolean patchEarthRotation,
370                               int year, int month, int day, int hour, int minute, double second,
371                               Tide tide, double refRate, double refPhase,
372                               double toleranceRate, double tolerancePhase)
373         {
374         TimeScale                    ut1      = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
375         FundamentalNutationArguments fna      = IERSConventions.IERS_2010.getNutationArguments(ut1);
376         if (patchEarthRotation) {
377             patchEarthRotationModel(fna, ut1);
378         }
379         AbsoluteDate   date     = new AbsoluteDate(year, month, day, hour, minute, second, ut1);
380         BodiesElements elements = fna.evaluateAll(date);
381         Assertions.assertEquals(refRate,
382                             tide.getRate(elements) * Constants.JULIAN_DAY/ (2 * FastMath.PI),
383                             toleranceRate);
384         Assertions.assertEquals(refPhase,
385                             FastMath.toDegrees(MathUtils.normalizeAngle(tide.getPhase(elements), FastMath.PI)),
386                             tolerancePhase);
387     }
388 
389     /** Patch FundamentalNutationArguments to use a simplified Earth rotation angle.
390      * <p>
391      * The angle is simply computed such that Sun is at the anti-meridian at 00h00 UT1,
392      * and Earth rotates at 15°/hour between 00h00 UT1 and the current date.
393      * </p>
394      * @param fna FundamentalNutationArguments to patch
395      * @param ut1 UT1 time scale to use for computing date components
396      */
397     public static void patchEarthRotationModel(FundamentalNutationArguments fna, final TimeScale ut1) {
398         try {
399             final Field conventionsField = FundamentalNutationArguments.class.getDeclaredField("conventions");
400             conventionsField.setAccessible(true);
401             final IERSConventions conventions = (IERSConventions) conventionsField.get(fna);
402             final Field fCoeffField = FundamentalNutationArguments.class.getDeclaredField("fCoefficients");
403             fCoeffField.setAccessible(true);
404             final double[] fCoefficients = (double[]) fCoeffField.get(fna);
405             final Field dCoeffField = FundamentalNutationArguments.class.getDeclaredField("dCoefficients");
406             dCoeffField.setAccessible(true);
407             final double[] dCoefficients = (double[]) dCoeffField.get(fna);
408             final Field oCoeffField = FundamentalNutationArguments.class.getDeclaredField("omegaCoefficients");
409             oCoeffField.setAccessible(true);
410             final double[] oCoefficients = (double[]) oCoeffField.get(fna);
411             final Method valueMethod = FundamentalNutationArguments.class.getDeclaredMethod("value", Double.TYPE, double[].class);
412             valueMethod.setAccessible(true);
413             final Field gmstFunctionField = FundamentalNutationArguments.class.getDeclaredField("gmstFunction");
414             gmstFunctionField.setAccessible(true);
415             final TimeScalarFunction old = (TimeScalarFunction) gmstFunctionField.get(fna);
416             gmstFunctionField.set(fna, new TimeScalarFunction() {
417 
418                 private double sunAngle(AbsoluteDate date) {
419                     try {
420                        double tc = conventions.evaluateTC(date);
421                         return ((Double) valueMethod.invoke(fna, tc, fCoefficients)).doubleValue() +
422                                ((Double) valueMethod.invoke(fna, tc, oCoefficients)).doubleValue() -
423                                ((Double) valueMethod.invoke(fna, tc, dCoefficients)).doubleValue();
424                     } catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
425                         return Double.NaN;
426                     }
427                 }
428 
429                 @Override
430                 public double value(AbsoluteDate date) {
431                     double dayFraction = date.getComponents(ut1).getTime().getSecondsInUTCDay() / Constants.JULIAN_DAY;
432                     double v = 2 * FastMath.PI * dayFraction + sunAngle(date) + FastMath.PI;
433                     // the patched value is about 24 arc seconds from the IERS value (almost independent on date)
434                     double deltaArcSeconds = 3600.0 * FastMath.toDegrees(MathUtils.normalizeAngle(old.value(date) - v, 0.0));
435                     Assertions.assertEquals(0.0, deltaArcSeconds, 23.7);
436                     return v;
437                 }
438 
439                 @Override
440                 public <T extends CalculusFieldElement<T>> T value(final FieldAbsoluteDate<T> date) {
441                     return null;
442                 }
443 
444             });
445         } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException |
446                  NoSuchMethodException | SecurityException e) {
447             Assertions.fail(e.getLocalizedMessage());
448         }
449     }
450 
451     private void doTestTable(final double[][] table) {
452         TimeScale                    ut1      = TimeScalesFactory.getUT1(IERSConventions.IERS_2010, true);
453         FundamentalNutationArguments fna      = IERSConventions.IERS_2010.getNutationArguments(ut1);
454         BodiesElements               elements = fna.evaluateAll(AbsoluteDate.J2000_EPOCH);
455         for (double[] r : table) {
456             int   doodsonNumber       = (int) r[1];
457             int[] dooodsonMultipliers = new int[] { (int) r[2], (int) r[3], (int) r[4], (int) r[5], (int) r[6], (int) r[7] };
458             int[] delaunayMultipliers = new int[] { (int) r[8], (int) r[9], (int) r[10], (int) r[11], (int) r[12] };
459             final Tide tdN = new Tide(doodsonNumber);
460             final Tide tdM = new Tide((int) r[2], (int) r[3], (int) r[4], (int) r[5], (int) r[6], (int) r[7]);
461             Assertions.assertEquals(r[0],                     FastMath.toDegrees(tdN.getRate(elements)) * 3600, 7.2e-5);
462             Assertions.assertEquals(doodsonNumber,            tdM.getDoodsonNumber());
463             Assertions.assertArrayEquals(dooodsonMultipliers, tdN.getDoodsonMultipliers());
464             Assertions.assertArrayEquals(delaunayMultipliers, tdN.getDelaunayMultipliers());
465             Assertions.assertArrayEquals(delaunayMultipliers, tdM.getDelaunayMultipliers());
466         }
467     }
468 
469     @BeforeEach
470     public void setUp() throws Exception {
471         Utils.setDataRoot("regular-data");
472     }
473 
474 }