1   /* Copyright 2002-2022 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 java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.concurrent.Callable;
27  import java.util.concurrent.ExecutionException;
28  import java.util.concurrent.Executors;
29  import java.util.concurrent.Future;
30  
31  import org.hipparchus.geometry.euclidean.threed.Rotation;
32  import org.hipparchus.geometry.euclidean.threed.Vector3D;
33  import org.hipparchus.util.FastMath;
34  import org.hipparchus.util.Precision;
35  import org.junit.Assert;
36  import org.junit.Before;
37  import org.junit.Test;
38  import org.orekit.Utils;
39  import org.orekit.data.DataContext;
40  import org.orekit.time.AbsoluteDate;
41  import org.orekit.time.DateComponents;
42  import org.orekit.time.TimeComponents;
43  import org.orekit.time.TimeScale;
44  import org.orekit.time.TimeScalesFactory;
45  import org.orekit.utils.AngularCoordinates;
46  import org.orekit.utils.IERSConventions;
47  import org.orekit.utils.PVCoordinates;
48  
49  /**
50   * Unit tests for {@link TIRFProvider}.
51   *
52   * @author Evan Ward
53   */
54  public class TIRFProviderTest {
55  
56      @Test
57      public void testAASReferenceLEO() {
58  
59          // this reference test has been extracted from the following paper:
60          // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
61          // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
62          // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
63          // Reference position & velocity from : "Fundamentals of Astrodynamics and Applications", Third edition, David A. Vallado
64          Utils.setLoaders(IERSConventions.IERS_2010,
65                           Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
66                               { 53098, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
67                               { 53099, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
68                               { 53100, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
69                               { 53101, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
70                               { 53102, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
71                               { 53103, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
72                               { 53104, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 },
73                               { 53105, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 }
74                           }));
75          AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 04, 06),
76                                             new TimeComponents(07, 51, 28.386009),
77                                             TimeScalesFactory.getUTC());
78  
79          // Positions LEO
80          Frame itrfA = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
81          PVCoordinates pvITRF =
82              new PVCoordinates(new Vector3D(-1033479.3830, 7901295.2754, 6380356.5958),
83                                new Vector3D(-3225.636520, -2872.451450, 5531.924446));
84  
85          // Reference coordinates
86          Frame tirf = FramesFactory.getTIRF(IERSConventions.IERS_2010);
87          PVCoordinates pvTIRF =
88              new PVCoordinates(new Vector3D(-1033475.0312, 7901305.5856, 6380344.5328),
89                                new Vector3D(-3225.632747, -2872.442511, 5531.931288));
90          checkPV(pvTIRF,
91                  itrfA.getTransformTo(tirf, t0).transformPVCoordinates(pvITRF),
92                  6.379e-5, 3.78e-7);
93  
94          Frame cirf = FramesFactory.getCIRF(IERSConventions.IERS_2010, true);
95          PVCoordinates pvCIRF =
96              new PVCoordinates(new Vector3D(5100018.4047, 6122786.3648, 6380344.5328),
97                                new Vector3D(-4745.380330, 790.341453, 5531.931288));
98          checkPV(pvCIRF,
99                  tirf.getTransformTo(cirf, t0).transformPVCoordinates(pvTIRF),
100                 8.59e-3, 4.65e-6);
101 
102     }
103 
104     @Test
105     public void testAASReferenceGEO() {
106 
107         // this reference test has been extracted from the following paper:
108         // Implementation Issues Surrounding the New IAU Reference Systems for Astrodynamics
109         // David A. Vallado, John H. Seago, P. Kenneth Seidelmann
110         // http://www.centerforspace.com/downloads/files/pubs/AAS-06-134.pdf
111         Utils.setLoaders(IERSConventions.IERS_2010,
112                          Utils.buildEOPList(IERSConventions.IERS_2010, ITRFVersion.ITRF_2008, new double[][] {
113                              { 53153, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
114                              { 53154, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
115                              { 53155, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
116                              { 53156, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
117                              { 53157, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
118                              { 53158, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
119                              { 53159, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 },
120                              { 53160, -0.4709050,  0.0000000, -0.083853,  0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 }
121                          }));
122 
123         AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01),
124                                            TimeComponents.H00,
125                                            TimeScalesFactory.getUTC());
126 
127         //  Positions GEO
128         Frame itrfA = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
129         PVCoordinates pvITRF =
130             new PVCoordinates(new Vector3D(24796919.2915, -34115870.9234, 10226.0621),
131                               new Vector3D(-0.979178, -1.476538, -0.928776));
132 
133         Frame tirf = FramesFactory.getTIRF(IERSConventions.IERS_2010);
134         PVCoordinates pvTIRF =
135             new PVCoordinates(new Vector3D(24796919.2953, -34115870.9004, 10293.2583),
136                               new Vector3D(-0.979178, -1.476540, -0.928772));
137         checkPV(pvTIRF,
138                 itrfA.getTransformTo(tirf, t0).transformPVCoordinates(pvITRF),
139                 5.697e-5, 4.69e-7);
140 
141         Frame cirf = FramesFactory.getCIRF(IERSConventions.IERS_2010, true);
142         PVCoordinates pvCIRF =
143             new PVCoordinates(new Vector3D(-40588158.1236, -11462167.0709, 10293.2583),
144                               new Vector3D(834.787843, -2958.305669, -0.928772));
145         checkPV(pvCIRF,
146                 tirf.getTransformTo(cirf, t0).transformPVCoordinates(pvTIRF),
147                 0.0505, 3.60e-6);
148 
149     }
150 
151     /** Issue #636 */
152     @Test
153     public void testContinuousDuringLeap() {
154         // setup
155         Utils.setDataRoot("regular-data");
156         Frame tirf = FramesFactory.getTIRF(IERSConventions.IERS_2010, true);
157         Frame cirf = FramesFactory.getCIRF(IERSConventions.IERS_2010, true);
158         TimeScale utc = TimeScalesFactory.getUTC();
159         AbsoluteDate dateA = new AbsoluteDate(2005, 12, 31, 23, 59, 0.0, utc);
160         AbsoluteDate dateB = new AbsoluteDate(2006, 1, 1, 0, 1, 0.0, utc);
161         double dt = 0.5;
162         double expected = dt * 2 * FastMath.PI / (23 * 3600 + 56 * 60 + 4.1);
163         double tol = expected * 1e-6;
164         Rotation previous = cirf.getTransformTo(tirf, dateA.shiftedBy(-dt)).getRotation();
165 
166         // action + verify
167         for (AbsoluteDate d = dateA; d.compareTo(dateB) < 0; d = d.shiftedBy(dt)) {
168             Rotation actual = cirf.getTransformTo(tirf, d).getRotation();
169             Assert.assertEquals("At " + d.toString(utc),
170                     expected,
171                     Rotation.distance(previous, actual),
172                     tol);
173             previous = actual;
174         }
175     }
176 
177     @Test
178     public void testSerialization() throws IOException, ClassNotFoundException {
179         EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true);
180         TimeScale ut1 = DataContext.getDefault().getTimeScales().getUT1(eopHistory);
181         TIRFProvider provider = new TIRFProvider(eopHistory, ut1);
182 
183         ByteArrayOutputStream bos = new ByteArrayOutputStream();
184         ObjectOutputStream    oos = new ObjectOutputStream(bos);
185         oos.writeObject(provider);
186 
187         Assert.assertTrue(bos.size() > 295000);
188         Assert.assertTrue(bos.size() < 300000);
189 
190         ByteArrayInputStream  bis = new ByteArrayInputStream(bos.toByteArray());
191         ObjectInputStream     ois = new ObjectInputStream(bis);
192         TIRFProvider deserialized  = (TIRFProvider) ois.readObject();
193         for (int i = 0; i < FastMath.min(100, provider.getEOPHistory().getEntries().size()); ++i) {
194             AbsoluteDate date = provider.getEOPHistory().getEntries().get(i).getDate();
195             Transform expectedIdentity = new Transform(date,
196                                                        provider.getTransform(date).getInverse(),
197                                                        deserialized.getTransform(date));
198             Assert.assertEquals(0.0, expectedIdentity.getTranslation().getNorm(), 1.0e-15);
199             Assert.assertEquals(0.0, expectedIdentity.getRotation().getAngle(),   1.0e-15);
200         }
201 
202     }
203 
204     /**
205      * Checks that {@link TIRFProvider#getTransform(AbsoluteDate)} is thread safe.
206      */
207     @Test
208     public void testConcurrentGetTransform()
209         throws InterruptedException, ExecutionException {
210 
211         // subject under test
212         final EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, false);
213         final TimeScale ut1 = DataContext.getDefault().getTimeScales().getUT1(eopHistory);
214         final TIRFProvider tirf = new TIRFProvider(eopHistory, ut1);
215         // arbitrary date
216         final AbsoluteDate start = new AbsoluteDate("2009-09-19T23:59:45.000", TimeScalesFactory.getUTC());
217         // in seconds = 15min
218         final double timeStep = 300;
219         // the number of possible concurrent threads
220         final int nJobs = 24;
221         // the number of calculations per thread
222         final int nPerJob = 1000;
223         // tolerance of comparisons
224         final double absTol = Precision.EPSILON;
225         // the expected result
226         final List<Transform> expecteds = new ArrayList<Transform>();
227         for (int j = 0; j < nPerJob; j++) {
228             final AbsoluteDate date = start.shiftedBy(timeStep * j);
229             // action
230             final Transform expected = tirf.getTransform(date);
231             // verify
232             expecteds.add(expected);
233         }
234 
235         // build jobs for concurrent execution
236         final List<Callable<Boolean>> jobs = new ArrayList<Callable<Boolean>>();
237         for (int i = 0; i < nJobs; i++) {
238             jobs.add(new Callable<Boolean>() {
239                 public Boolean call() throws Exception {
240                     for (int j = 0; j < nPerJob; j++) {
241                         final AbsoluteDate date = start.shiftedBy(timeStep * j);
242                         // action
243                         final Transform actual = tirf.getTransform(date);
244                         // verify
245                         assertTransformEquals(expecteds.get(j), actual, absTol);
246                     }
247                     return true;
248                 }
249             });
250         }
251 
252         // run the jobs
253         runConcurrentlyAndCheck(jobs, nJobs);
254     }
255 
256     /**
257      * Runs several jobs at concurrently in the given number of threads.
258      *
259      * @param jobs
260      *            the jobs to execute
261      * @param threads
262      *            the size of the thread pool
263      */
264     private static void runConcurrentlyAndCheck(List<Callable<Boolean>> jobs, int threads)
265         throws InterruptedException, ExecutionException {
266 
267         // action
268         final List<Future<Boolean>> futures = Executors.newFixedThreadPool(threads).invokeAll(jobs);
269 
270         // verify - necessary to throw AssertionErrors from the Callable
271         for (Future<Boolean> future : futures) {
272             Assert.assertEquals(true, future.get());
273         }
274 
275     }
276 
277     /**
278      * Check the two {@link Transform}s are the same to within an absolute tolerance.
279      */
280     private static void assertTransformEquals(Transform expected, Transform actual, double absTol) {
281         final AngularCoordinates expectedAngular = expected.getAngular();
282         final AngularCoordinates actualAngular = actual.getAngular();
283         final Rotation expectedRotation = expectedAngular.getRotation();
284         final Rotation actualRotation = actualAngular.getRotation();
285         final PVCoordinates expectedPV = expected.getCartesian();
286         final PVCoordinates actualPV = actual.getCartesian();
287 
288         // transform
289         Assert.assertEquals(Rotation.distance(actualRotation, expectedRotation), 0, absTol);
290         Assert.assertEquals(expectedAngular.getRotationRate(), actualAngular.getRotationRate());
291         Assert.assertEquals(expectedPV.getPosition(), actualPV.getPosition());
292         Assert.assertEquals(expectedPV.getVelocity(), actualPV.getVelocity());
293     }
294 
295     @Before
296     public void setUp() {
297         Utils.setDataRoot("compressed-data");
298     }
299 
300     private void checkPV(PVCoordinates reference, PVCoordinates result,
301                          double expectedPositionError, double expectedVelocityError) {
302 
303         Vector3D dP = result.getPosition().subtract(reference.getPosition());
304         Vector3D dV = result.getVelocity().subtract(reference.getVelocity());
305         Assert.assertEquals(expectedPositionError, dP.getNorm(), 0.01 * expectedPositionError);
306         Assert.assertEquals(expectedVelocityError, dV.getNorm(), 0.01 * expectedVelocityError);
307     }
308 
309 }