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.frames;
18  
19  import org.hipparchus.geometry.euclidean.threed.Rotation;
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.MathUtils;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.Utils;
27  import org.orekit.time.AbsoluteDate;
28  import org.orekit.time.DateComponents;
29  import org.orekit.time.TTScale;
30  import org.orekit.time.TimeComponents;
31  import org.orekit.time.TimeScale;
32  import org.orekit.time.TimeScalesFactory;
33  import org.orekit.time.UT1Scale;
34  import org.orekit.utils.Constants;
35  import org.orekit.utils.IERSConventions;
36  import org.orekit.utils.PVCoordinates;
37  
38  public class ITRFProviderTest {
39  
40      @Test
41      public void testTidalEffects() {
42  
43          final Frame itrfWith    = FramesFactory.getITRF(IERSConventions.IERS_2010, false);
44          final Frame itrfWithout = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
45          final AbsoluteDate date0 = new AbsoluteDate(2007, 10, 20, TimeScalesFactory.getUTC());
46  
47          double minCorrection = Double.POSITIVE_INFINITY;
48          double maxCorrection = Double.NEGATIVE_INFINITY;
49          for (double dt = 0; dt < 3 * Constants.JULIAN_DAY; dt += 60) {
50              final AbsoluteDate date = date0.shiftedBy(dt);
51              final Transform t = itrfWith.getTransformTo(itrfWithout, date);
52              Assertions.assertEquals(0, t.getTranslation().getNorm(), 1.0e-15);
53              final double milliarcSeconds = FastMath.toDegrees(t.getRotation().getAngle()) * 3600000.0;
54              minCorrection = FastMath.min(minCorrection, milliarcSeconds);
55              maxCorrection = FastMath.max(maxCorrection, milliarcSeconds);
56          }
57  
58          Assertions.assertEquals(0.064, minCorrection, 0.001);
59          Assertions.assertEquals(0.613, maxCorrection, 0.001);
60  
61      }
62  
63      @Test
64      public void testAASReferenceLEO() {
65  
66          // this reference test has been extracted from the following paper:
67          // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
68          // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
69          // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
70          // Reference position & velocity from : "Fundamentals of Astrodynamics and Applications", Third edition, David A. Vallado
71          Utils.setLoaders(IERSConventions.IERS_2010,
72                           Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
73                               { 53098, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
74                               { 53099, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
75                               { 53100, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
76                               { 53101, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
77                               { 53102, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
78                               { 53103, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
79                               { 53104, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
80                               { 53105, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 }
81                           }));
82          AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 04, 06),
83                                             new TimeComponents(07, 51, 28.386009),
84                                             TimeScalesFactory.getUTC());
85  
86          // Positions LEO
87          Frame itrfA = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
88          PVCoordinates pvITRF =
89              new PVCoordinates(new Vector3D(-1033479.3830, 7901295.2754, 6380356.5958),
90                                new Vector3D(-3225.636520, -2872.451450, 5531.924446));
91  
92          // Reference coordinates
93          PVCoordinates pvGcrfIau2000A =
94              new PVCoordinates(new Vector3D(5102508.9579, 6123011.4038, 6378136.9252),
95                                new Vector3D(-4743.220156, 790.536497, 5533.755728));
96          checkPV(pvGcrfIau2000A,
97                  itrfA.getTransformTo(FramesFactory.getGCRF(), t0).transformPVCoordinates(pvITRF),
98                  0.0192, 2.15e-5);
99  
100         PVCoordinates pvEME2000EqA =
101             new PVCoordinates(new Vector3D(5102509.0383, 6123011.9758, 6378136.3118),
102                               new Vector3D(-4743.219766, 790.536344, 5533.756084));
103         checkPV(pvEME2000EqA,
104                 itrfA.getTransformTo(FramesFactory.getEME2000(), t0).transformPVCoordinates(pvITRF),
105                 0.0191, 2.13e-5);
106 
107     }
108 
109     @Test
110     public void testAASReferenceGEO() {
111 
112         // this reference test has been extracted from the following paper:
113         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
114         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
115         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
116         Utils.setLoaders(IERSConventions.IERS_2010,
117                          Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
118                              { 53153, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
119                              { 53154, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
120                              { 53155, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
121                              { 53156, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
122                              { 53157, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
123                              { 53158, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
124                              { 53159, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
125                              { 53160, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 }
126                          }));
127 
128         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01),
129                                            TimeComponents.H00,
130                                            TimeScalesFactory.getUTC());
131 
132         //  Positions GEO
133         Frame itrfA = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
134         PVCoordinates pvITRF =
135             new PVCoordinates(new Vector3D(24796919.2915, -34115870.9234, 10226.0621),
136                               new Vector3D(-0.979178, -1.476538, -0.928776));
137 
138         PVCoordinates pvGCRFiau2000A =
139             new PVCoordinates(new Vector3D(-40588150.3617, -11462167.0397, 27143.1974),
140                               new Vector3D(834.787458, -2958.305691, -1.172993));
141         checkPV(pvGCRFiau2000A,
142                 itrfA.getTransformTo(FramesFactory.getGCRF(), t0).transformPVCoordinates(pvITRF),
143                 0.0806, 1.03e-4);
144 
145         PVCoordinates pvEME2000EqA =
146             new PVCoordinates(new Vector3D(-40588149.5482, -11462169.9118, 27146.8462),
147                               new Vector3D(834.787667, -2958.305632, -1.172963));
148         checkPV(pvEME2000EqA,
149                 itrfA.getTransformTo(FramesFactory.getEME2000(), t0).transformPVCoordinates(pvITRF),
150                 0.0806, 1.04e-4);
151 
152     }
153 
154     @Test
155     public void testAASReferenceGEODX0DY0() {
156 
157         // this reference test has been extracted from the following paper:
158         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
159         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
160         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
161         Utils.setLoaders(IERSConventions.IERS_2010,
162                          Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
163                              { 53153, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
164                              { 53154, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
165                              { 53155, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
166                              { 53156, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
167                              { 53157, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
168                              { 53158, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
169                              { 53159, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 },
170                              { 53160, -0.4709050,  0.0000000, -0.083853,  0.467217, 0.0, 0.0, 0.0, 0.0 }
171                          }));
172 
173         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01),
174                                            TimeComponents.H00,
175                                            TimeScalesFactory.getUTC());
176 
177         //  Positions GEO
178         Frame itrfA = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
179         PVCoordinates pvITRF =
180             new PVCoordinates(new Vector3D(24796919.2915, -34115870.9234, 10226.0621),
181                               new Vector3D(-0.979178, -1.476538, -0.928776));
182 
183         PVCoordinates pvGCRFdx0dy0 =
184             new PVCoordinates(new Vector3D(-40588150.3643, -11462167.0302, 27143.1979),
185                               new Vector3D(834.787457, -2958.305691, -1.172993));
186         checkPV(pvGCRFdx0dy0,
187                 itrfA.getTransformTo(FramesFactory.getGCRF(), t0).transformPVCoordinates(pvITRF),
188                 0.0505, 1.06e-4);
189 
190         PVCoordinates pvEME2000EqA =
191             new PVCoordinates(new Vector3D(-40588149.5482, -11462169.9118, 27146.8462),
192                               new Vector3D(834.787667, -2958.305632, -1.172963));
193         checkPV(pvEME2000EqA,
194                 itrfA.getTransformTo(FramesFactory.getEME2000(), t0).transformPVCoordinates(pvITRF),
195                 0.0603, 1.07e-4);
196 
197     }
198 
199     @Test
200     public void testSofaCookbook() {
201 
202         // SOFA cookbook test case:
203         //     date       2007 April 05, 12h00m00s.0 UTC
204         //     xp         +0′′.0349282
205         //     yp         +0′′.4833163
206         //     UT1 − UTC  -0s.072073685
207         //     dψ 1980    -0′′.0550655
208         //     dε 1980    -0′′.0063580
209         //     dX 2000    +0′′.0001725
210         //     dY 2000    -0′′.0002650
211         //     dX 2006    +0′′.0001750
212         //     dY 2006    -0′′.0002259
213 
214         Utils.setLoaders(IERSConventions.IERS_2010,
215                          Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
216                              { 54192, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
217                              { 54193, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
218                              { 54194, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
219                              { 54195, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
220                              { 54196, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
221                              { 54197, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
222                              { 54198, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 },
223                              { 54199, -0.072073685,  1.4020, 0.0349282, 0.4833163, -Double.NaN, Double.NaN, 0.0001750, -0.0002259 }
224                          }));
225 
226         EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true);
227 
228         TimeScale utc = TimeScalesFactory.getUTC();
229         TTScale tt    = TimeScalesFactory.getTT();
230         UT1Scale ut1  = TimeScalesFactory.getUT1(eopHistory);
231         Frame gcrf    = FramesFactory.getGCRF();
232         Frame itrf    = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
233         Frame gtod    = itrf.getParent();
234         Frame tod     = gtod.getParent();
235 
236         // time scales checks
237         AbsoluteDate date = new AbsoluteDate(new DateComponents(2007, 4, 5), TimeComponents.H12, utc);
238         Assertions.assertEquals(0.50075444444444,
239                             date.getComponents(tt).getTime().getSecondsInUTCDay() / Constants.JULIAN_DAY,
240                             5.0e-15);
241         Assertions.assertEquals(0.499999165813831,
242                             date.getComponents(ut1).getTime().getSecondsInUTCDay() / Constants.JULIAN_DAY,
243                             1.0e-15);
244 
245         // sidereal time check
246         double era = IERSConventions.IERS_2010.getEarthOrientationAngleFunction(ut1).value(date);
247         Assertions.assertEquals(13.318492966097 * 3600 * 1.0e6,
248                             radToMicroAS(MathUtils.normalizeAngle(era, 0)),
249                             0.0022);
250 
251         // nutation/precession/bias matrix check
252         Rotation refNPB = new Rotation(new double[][] {
253             { +0.999999746339445, -0.000000005138721, -0.000712264730182 },
254             { -0.000000026475329, +0.999999999014975, -0.000044385242666 },
255             { +0.000712264729708, +0.000044385250265, +0.999999745354420 }
256         }, 1.0e-13);
257         Rotation npb = gcrf.getTransformTo(tod, date).getRotation();
258         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refNPB, npb)), 0.31);
259 
260         // celestial to terrestrial frames matrix, without polar motion
261         Rotation refWithoutPolarMotion = new Rotation(new double[][] {
262             { +0.973104317573104, +0.230363826247808, -0.000703332818915 },
263             { -0.230363798804281, +0.973104570735550, +0.000120888549767 },
264             { +0.000712264729708, +0.000044385250265, +0.999999745354420 }
265         }, 1.0e-13);
266         Rotation withoutPM = gcrf.getTransformTo(gtod, date).getRotation();
267         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refWithoutPolarMotion, withoutPM)), 0.31);
268 
269         // celestial to terrestrial frames matrix, with polar motion
270         Rotation refWithPolarMotion = new Rotation(new double[][] {
271             { +0.973104317697512, +0.230363826239227, -0.000703163482268 },
272             { -0.230363800456136, +0.973104570632777, +0.000118545366806 },
273             { +0.000711560162777, +0.000046626403835, +0.999999745754024 }
274         }, 1.0e-13);
275         Rotation withPM = gcrf.getTransformTo(itrf, date).getRotation();
276         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refWithPolarMotion, withPM)), 0.31);
277 
278     }
279 
280     @BeforeEach
281     public void setUp() {
282         Utils.setDataRoot("compressed-data");
283     }
284 
285     private void checkPV(PVCoordinates reference, PVCoordinates result,
286                          double expectedPositionError, double expectedVelocityError) {
287 
288         Vector3D dP = result.getPosition().subtract(reference.getPosition());
289         Vector3D dV = result.getVelocity().subtract(reference.getVelocity());
290         Assertions.assertEquals(expectedPositionError, dP.getNorm(), 0.01 * expectedPositionError);
291         Assertions.assertEquals(expectedVelocityError, dV.getNorm(), 0.01 * expectedVelocityError);
292     }
293 
294     double radToMicroAS(double deltaRad) {
295         return deltaRad * 1.0e6 / Constants.ARC_SECONDS_TO_RADIANS;
296     }
297 
298 }