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.RotationConvention;
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.FastMath;
23  import org.hipparchus.util.MathUtils;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.BeforeEach;
26  import org.junit.jupiter.api.Test;
27  import org.orekit.Utils;
28  import org.orekit.time.AbsoluteDate;
29  import org.orekit.time.DateComponents;
30  import org.orekit.time.TTScale;
31  import org.orekit.time.TimeComponents;
32  import org.orekit.time.TimeScale;
33  import org.orekit.time.TimeScalesFactory;
34  import org.orekit.time.UT1Scale;
35  import org.orekit.utils.Constants;
36  import org.orekit.utils.IERSConventions;
37  import org.orekit.utils.PVCoordinates;
38  
39  import java.util.ArrayList;
40  
41  
42  public class ITRFEquinoxProviderTest {
43  
44      @Test
45      public void testEquinoxVersusCIO() {
46          Frame itrfEquinox  = FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true);
47          Frame itrfCIO      = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
48          AbsoluteDate start = new AbsoluteDate(2011, 4, 10, TimeScalesFactory.getUTC());
49          AbsoluteDate end   = new AbsoluteDate(2011, 7,  4, TimeScalesFactory.getUTC());
50          for (AbsoluteDate date = start; date.compareTo(end) < 0; date = date.shiftedBy(10000)) {
51              double angularOffset =
52                      itrfEquinox.getTransformTo(itrfCIO, date).getRotation().getAngle();
53              Assertions.assertEquals(0, angularOffset / Constants.ARC_SECONDS_TO_RADIANS, 0.07);
54          }
55      }
56  
57      @Test
58      public void testAASReferenceLEO() {
59  
60          // this reference test has been extracted from the following paper:
61          // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
62          // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
63          // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
64          Utils.setLoaders(IERSConventions.IERS_1996,
65                           Utils.buildEOPList(IERSConventions.IERS_1996, ITRFVersion.ITRF_2008, new double[][] {
66                               { 53098, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
67                               { 53099, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
68                               { 53100, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
69                               { 53101, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
70                               { 53102, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
71                               { 53103, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
72                               { 53104, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN },
73                               { 53105, -0.4399619, 0.0015563, -0.140682, 0.333309, -0.052195, -0.003875, Double.NaN, Double.NaN }
74                           }));
75          AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 04, 06),
76                                             new TimeComponents(07, 51, 28.386009),
77                                             TimeScalesFactory.getUTC());
78  
79          // ITRF
80          PVCoordinates pvITRF =
81             new PVCoordinates(new Vector3D(-1033479.3830, 7901295.2754, 6380356.5958),
82                               new Vector3D(-3225.636520, -2872.451450, 5531.924446));
83  
84          // GTOD
85          PVCoordinates pvGTOD =
86              new PVCoordinates(new Vector3D(-1033475.0313, 7901305.5856, 6380344.5328),
87                                new Vector3D(-3225.632747, -2872.442511, 5531.931288));
88  
89          Transform t = FramesFactory.getGTOD(IERSConventions.IERS_1996, true).
90                  getTransformTo(FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true), t0);
91          checkPV(pvITRF, t.transformPVCoordinates(pvGTOD), 8.08e-5, 3.78e-7);
92  
93      }
94  
95      @Test
96      public void testAASReferenceGEO() {
97  
98          // this reference test has been extracted from the following paper:
99          // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
100         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
101         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
102         Utils.setLoaders(IERSConventions.IERS_1996,
103                          Utils.buildEOPList(IERSConventions.IERS_1996, ITRFVersion.ITRF_2008, new double[][] {
104                              { 53153, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
105                              { 53154, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
106                              { 53155, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
107                              { 53156, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
108                              { 53157, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
109                              { 53158, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
110                              { 53159, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN },
111                              { 53160, -0.4709050,  0.0000000, -0.083853,  0.467217, -0.053614, -0.004494, Double.NaN, Double.NaN }
112                          }));
113         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01),
114                                            TimeComponents.H00,
115                                            TimeScalesFactory.getUTC());
116 
117         Transform t = FramesFactory.getGTOD(IERSConventions.IERS_1996, true).
118                 getTransformTo(FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true), t0);
119 
120         // GTOD
121         PVCoordinates pvGTOD =
122             new PVCoordinates(new Vector3D(24796919.2956, -34115870.9001, 10293.2583),
123                               new Vector3D(-0.979178, -1.476540, -0.928772));
124 
125         // ITRF
126         PVCoordinates pvITRF =
127             new PVCoordinates(new Vector3D(24796919.2915, -34115870.9234, 10226.0621),
128                               new Vector3D(-0.979178, -1.476538, -0.928776));
129 
130         checkPV(pvITRF, t.transformPVCoordinates(pvGTOD), 3.954e-4, 4.69e-7);
131 
132     }
133 
134     @Test
135     public void testSofaCookbook() {
136 
137         // SOFA cookbook test case:
138         //     date       2007 April 05, 12h00m00s.0 UTC
139         //     xp         +0′′.0349282
140         //     yp         +0′′.4833163
141         //     UT1 − UTC  -0s.072073685
142         //     dψ 1980    -0′′.0550655
143         //     dε 1980    -0′′.0063580
144         //     dX 2000    +0′′.0001725
145         //     dY 2000    -0′′.0002650
146         //     dX 2006    +0′′.0001750
147         //     dY 2006    -0′′.0002259
148 
149         Utils.setLoaders(IERSConventions.IERS_1996,
150                          Utils.buildEOPList(IERSConventions.IERS_1996, ITRFVersion.ITRF_2008, new double[][] {
151                              { 54192, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
152                              { 54193, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
153                              { 54194, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
154                              { 54195, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
155                              { 54196, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
156                              { 54197, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
157                              { 54198, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN },
158                              { 54199, -0.072073685,  1.4020, 0.0349282, 0.4833163, -0.0550666, -0.0063580, Double.NaN, Double.NaN }
159                          }));
160 
161         EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_1996, true);
162 
163         TimeScale utc = TimeScalesFactory.getUTC();
164         TTScale tt    = TimeScalesFactory.getTT();
165         UT1Scale ut1  = TimeScalesFactory.getUT1(eopHistory);
166         Frame gcrf    = FramesFactory.getGCRF();
167         Frame itrf    = FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true);
168         Frame gtod    = itrf.getParent();
169         Frame tod     = gtod.getParent();
170 
171         // time scales checks
172         AbsoluteDate date = new AbsoluteDate(new DateComponents(2007, 4, 5), TimeComponents.H12, utc);
173         Assertions.assertEquals(0.50075444444444,
174                             date.getComponents(tt).getTime().getSecondsInUTCDay() / Constants.JULIAN_DAY,
175                             5.0e-15);
176         Assertions.assertEquals(0.499999165813831,
177                             date.getComponents(ut1).getTime().getSecondsInUTCDay() / Constants.JULIAN_DAY,
178                             1.0e-15);
179 
180         // sidereal time check
181         double gast = IERSConventions.IERS_1996.getGASTFunction(ut1, eopHistory).value(date);
182         Assertions.assertEquals(13.412402380740 * 3600 * 1.0e6,
183                             radToMicroAS(MathUtils.normalizeAngle(gast, 0)),
184                             25);
185 
186         // nutation/precession/bias matrix check
187         Rotation refNPB = new Rotation(new double[][] {
188             { +0.999998403176203, -0.001639032970562, -0.000712190961847 },
189             { +0.001639000942243, +0.999998655799521, -0.000045552846624 },
190             { +0.000712264667137, +0.000044385492226, +0.999999745354454 }
191         }, 1.0e-13);
192         Rotation npb = gcrf.getTransformTo(tod, date).getRotation();
193         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refNPB, npb)), 27.0);
194 
195         // celestial to terrestrial frames matrix, without polar motion
196         Rotation refWithoutPolarMotion = new Rotation(new double[][] {
197             { +0.973104317592265, +0.230363826166883, -0.000703332813776 },
198             { -0.230363798723533, +0.973104570754697, +0.000120888299841 },
199             { +0.000712264667137, +0.000044385492226, +0.999999745354454 }
200         }, 1.0e-13);
201         Rotation withoutPM = gcrf.getTransformTo(gtod, date).getRotation();
202         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refWithoutPolarMotion, withoutPM)), 9);
203 
204         // celestial to terrestrial frames matrix, with polar motion
205         Rotation refWithPolarMotion = new Rotation(new double[][] {
206             { +0.973104317712772, +0.230363826174782, -0.000703163477127 },
207             { -0.230363800391868, +0.973104570648022, +0.000118545116892 },
208             { +0.000711560100206, +0.000046626645796, +0.999999745754058 }
209         }, 1.0e-13);
210         Rotation withPM = gcrf.getTransformTo(itrf, date).getRotation();
211         Assertions.assertEquals(0.0, radToMicroAS(Rotation.distance(refWithPolarMotion, withPM)), 10);
212 
213     }
214 
215     @Test
216     public void testNROvsEquinoxRealEOP() {
217         Utils.setDataRoot("regular-data");
218         checkFrames(FramesFactory.getITRF(IERSConventions.IERS_2010, true),
219                     FramesFactory.getITRFEquinox(IERSConventions.IERS_2010, true),
220                     1.7);
221     }
222 
223     @Test
224     public void testNROvsEquinoxNoEOP2010() {
225         Utils.setLoaders(IERSConventions.IERS_2010, new ArrayList<EOPEntry>());
226         checkFrames(FramesFactory.getITRF(IERSConventions.IERS_2010, true),
227                     FramesFactory.getITRFEquinox(IERSConventions.IERS_2010, true),
228                     1.7);
229     }
230 
231     @Test
232     public void testNROvsEquinoxNoEOP2003() {
233         Utils.setLoaders(IERSConventions.IERS_2003, new ArrayList<EOPEntry>());
234         checkFrames(FramesFactory.getITRF(IERSConventions.IERS_2003, true),
235                     FramesFactory.getITRFEquinox(IERSConventions.IERS_2003, true),
236                     1.9);
237     }
238 
239     @Test
240     public void testNROvsEquinoxNoEOP1996() {
241         Utils.setLoaders(IERSConventions.IERS_1996, new ArrayList<EOPEntry>());
242         checkFrames(FramesFactory.getITRF(IERSConventions.IERS_1996, true),
243                     FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true),
244                     100);
245     }
246 
247     private void checkFrames(Frame frame1, Frame frame2, double toleranceMicroAS)
248         {
249         AbsoluteDate t0 = new AbsoluteDate(2005, 5, 30, TimeScalesFactory.getUTC());
250         for (double dt = 0; dt < Constants.JULIAN_YEAR; dt += Constants.JULIAN_DAY / 4) {
251             AbsoluteDate date = t0.shiftedBy(dt);
252             Transform t = FramesFactory.getNonInterpolatingTransform(frame1, frame2, date);
253             Vector3D a = t.getRotation().getAxis(RotationConvention.VECTOR_OPERATOR);
254             double delta = FastMath.copySign(radToMicroAS(t.getRotation().getAngle()), a.getZ());
255             Assertions.assertEquals(0.0, delta, toleranceMicroAS);
256         }
257     }
258 
259     @BeforeEach
260     public void setUp() {
261         Utils.setDataRoot("rapid-data-columns");
262     }
263 
264     private void checkPV(PVCoordinates reference, PVCoordinates result,
265                          double expectedPositionError, double expectedVelocityError) {
266 
267         Vector3D dP = result.getPosition().subtract(reference.getPosition());
268         Vector3D dV = result.getVelocity().subtract(reference.getVelocity());
269         Assertions.assertEquals(expectedPositionError, dP.getNorm(), 0.01 * expectedPositionError);
270         Assertions.assertEquals(expectedVelocityError, dV.getNorm(), 0.01 * expectedVelocityError);
271     }
272 
273     double radToMicroAS(double deltaRad) {
274         return deltaRad * 1.0e6 / Constants.ARC_SECONDS_TO_RADIANS;
275     }
276 
277 }