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