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.bodies;
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.Arrays;
26  import java.util.List;
27  import java.util.concurrent.ExecutorService;
28  import java.util.concurrent.Executors;
29  import java.util.concurrent.Future;
30  import java.util.concurrent.TimeUnit;
31  import java.util.concurrent.atomic.AtomicReference;
32  
33  import org.hipparchus.geometry.euclidean.threed.Rotation;
34  import org.hipparchus.geometry.euclidean.threed.Vector3D;
35  import org.junit.Assert;
36  import org.junit.Test;
37  import org.orekit.Utils;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.frames.Frame;
40  import org.orekit.frames.FramesFactory;
41  import org.orekit.time.AbsoluteDate;
42  import org.orekit.time.TimeScalesFactory;
43  import org.orekit.utils.Constants;
44  import org.orekit.utils.IERSConventions;
45  import org.orekit.utils.TimeStampedPVCoordinates;
46  
47  public class CelestialBodyFactoryTest {
48  
49      @Test
50      public void getSun() {
51          Utils.setDataRoot("regular-data");
52  
53          CelestialBody sun = CelestialBodyFactory.getSun();
54          Assert.assertNotNull(sun);
55      }
56  
57      @Test
58      public void clearCache() {
59          Utils.setDataRoot("regular-data");
60  
61          CelestialBody sun = CelestialBodyFactory.getSun();
62          Assert.assertNotNull(sun);
63          CelestialBodyFactory.clearCelestialBodyCache();
64          CelestialBody sun2 = CelestialBodyFactory.getSun();
65          Assert.assertNotNull(sun2);
66          Assert.assertNotSame(sun, sun2);
67      }
68  
69      @Test
70      public void clearLoaders() {
71          Utils.setDataRoot("regular-data");
72  
73          CelestialBody sun = CelestialBodyFactory.getSun();
74          Assert.assertNotNull(sun);
75          CelestialBodyFactory.clearCelestialBodyLoaders();
76          CelestialBody sun2 = CelestialBodyFactory.getSun();
77          Assert.assertNotNull(sun2);
78          Assert.assertNotSame(sun, sun2);
79          CelestialBodyFactory.clearCelestialBodyLoaders(CelestialBodyFactory.SUN);
80          CelestialBodyFactory.clearCelestialBodyCache(CelestialBodyFactory.SUN);
81          CelestialBodyFactory.addDefaultCelestialBodyLoader(JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES);
82          CelestialBody sun3 = CelestialBodyFactory.getSun();
83          Assert.assertNotNull(sun3);
84          Assert.assertNotSame(sun,  sun3);
85          Assert.assertNotSame(sun2, sun3);
86      }
87  
88      @Test
89      public void testHorizon() {
90  
91          // The following data are an excerpt from a telnet session with JPL Horizon system
92          // note that in Horizon we selected Jupiter barycenter rather than Jupiter body center
93          // this seems to match better the content of the DE-431 ephemeris
94  
95          //  *******************************************************************************
96          //  Ephemeris / PORT_LOGIN Mon Oct 26 04:53:43 2015 Pasadena, USA    / Horizons
97          //  *******************************************************************************
98          //  Target body name: Jupiter Barycenter (5)          {source: DE-0431LE-0431}
99          //  Center body name: Solar System Barycenter (0)     {source: DE-0431LE-0431}
100         //  Center-site name: BODY CENTER
101         //  *******************************************************************************
102         //  Start time      : A.D. 2000-Jan-01 00:00:00.0000 CT
103         //  Stop  time      : A.D. 2003-Dec-31 23:59:00.0000 CT
104         //  Step-size       : 1440 minutes
105         //  *******************************************************************************
106         //  Center geodetic : 0.00000000,0.00000000,0.0000000 {E-lon(deg),Lat(deg),Alt(km)}
107         //  Center cylindric: 0.00000000,0.00000000,0.0000000 {E-lon(deg),Dxy(km),Dz(km)}
108         //  Center radii    : (undefined)
109         //  Output units    : KM-S
110         //  Output format   : 02
111         //  Reference frame : ICRF/J2000.0
112         //  Output type     : GEOMETRIC cartesian states
113         //  Coordinate systm: Earth Mean Equator and Equinox of Reference Epoch
114         //  *******************************************************************************
115         //  JDCT
116         //     X     Y     Z
117         //     VX    VY    VZ
118         //  *******************************************************************************
119         //  $$SOE
120         //  2451544.500000000 = A.D. 2000-Jan-01 00:00:00.0000 (TDB)
121         //   X = 5.978411018921824E+08 Y = 4.085508359611598E+08 Z = 1.605595308103096E+08
122         //   VX=-7.892151874487445E+00 VY= 1.017751699703826E+01 VZ= 4.554715748011852E+00
123         //  2451545.500000000 = A.D. 2000-Jan-02 00:00:00.0000 (TDB)
124         //   X = 5.971584965869523E+08 Y = 4.094296790808872E+08 Z = 1.609528639632485E+08
125         //   VX=-7.908893450088906E+00 VY= 1.016606978596496E+01 VZ= 4.550216570971850E+00
126         //  2451546.500000000 = A.D. 2000-Jan-03 00:00:00.0000 (TDB)
127         //   X = 5.964744456934582E+08 Y = 4.103075321378759E+08 Z = 1.613458079269412E+08
128         //   VX=-7.925614558638352E+00 VY= 1.015459888397081E+01 VZ= 4.545706740033853E+00
129         //  2451547.500000000 = A.D. 2000-Jan-04 00:00:00.0000 (TDB)
130         //   X = 5.957889509819047E+08 Y = 4.111843930867567E+08 Z = 1.617383617815004E+08
131         //   VX=-7.942315157290042E+00 VY= 1.014310432640078E+01 VZ= 4.541186269306714E+00
132         //  2451548.500000000 = A.D. 2000-Jan-05 00:00:00.0000 (TDB)
133         //   X = 5.951020142261952E+08 Y = 4.120602598852173E+08 Z = 1.621305246082588E+08
134         //   VX=-7.958995203281466E+00 VY= 1.013158614867071E+01 VZ= 4.536655172931710E+00
135         //  2451549.500000000 = A.D. 2000-Jan-06 00:00:00.0000 (TDB)
136         //   X = 5.944136372039236E+08 Y = 4.129351304940084E+08 Z = 1.625222954897725E+08
137         //   VX=-7.975654653933506E+00 VY= 1.012004438626713E+01 VZ= 4.532113465082445E+00
138         final TimeStampedPVCoordinates[] refPV = new TimeStampedPVCoordinates[] {
139             createPV(2000, 1, 1,
140                      5.978411018921824E+08, 4.085508359611598E+08, 1.605595308103096E+08,
141                      -7.892151874487445E+00, 1.017751699703826E+01, 4.554715748011852E+00),
142             createPV(2000, 1, 2,
143                      5.971584965869523E+08, 4.094296790808872E+08, 1.609528639632485E+08,
144              -7.908893450088906E+00, 1.016606978596496E+01, 4.550216570971850E+00),
145             createPV(2000, 1, 3,
146                      5.964744456934582E+08, 4.103075321378759E+08, 1.613458079269412E+08,
147              -7.925614558638352E+00, 1.015459888397081E+01, 4.545706740033853E+00),
148             createPV(2000, 1, 4,
149                      5.957889509819047E+08, 4.111843930867567E+08, 1.617383617815004E+08,
150                      -7.942315157290042E+00, 1.014310432640078E+01, 4.541186269306714E+00),
151             createPV(2000, 1, 5,
152                      5.951020142261952E+08, 4.120602598852173E+08, 1.621305246082588E+08,
153                      -7.958995203281466E+00, 1.013158614867071E+01, 4.536655172931710E+00),
154             createPV(2000, 1, 6,
155                      5.944136372039236E+08, 4.129351304940084E+08, 1.625222954897725E+08,
156                      -7.975654653933506E+00, 1.012004438626713E+01, 4.532113465082445E+00)
157         };
158 
159         Utils.setDataRoot("regular-data");
160         final CelestialBody jupiter = CelestialBodyFactory.getJupiter();
161         for (final TimeStampedPVCoordinates ref : refPV) {
162             TimeStampedPVCoordinates testPV = jupiter.getPVCoordinates(ref.getDate(),
163                                                                        FramesFactory.getICRF());
164             Assert.assertEquals(0.0,
165                                 Vector3D.distance(ref.getPosition(), testPV.getPosition()),
166                                 4.0e-4);
167             Assert.assertEquals(0.0,
168                                 Vector3D.distance(ref.getVelocity(), testPV.getVelocity()),
169                                 1.0e-11);
170         }
171 
172    }
173 
174     private TimeStampedPVCoordinates createPV(int year, int month, int day,
175                                               double xKm, double yKm, double zKM,
176                                               double vxKmS, double vyKms, double vzKms) {
177         return new TimeStampedPVCoordinates(new AbsoluteDate(year, month, day, TimeScalesFactory.getTDB()),
178                                             new Vector3D(  xKm * 1000,   yKm * 1000,   zKM * 1000),
179                                             new Vector3D(vxKmS * 1000, vyKms * 1000, vzKms * 1000));
180     }
181 
182     @Test
183     public void testSerialization()
184             throws IOException, ClassNotFoundException {
185         Utils.setDataRoot("regular-data");
186         for (String name : new String[] {
187             CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER, CelestialBodyFactory.SUN, CelestialBodyFactory.MERCURY,
188             CelestialBodyFactory.VENUS, CelestialBodyFactory.EARTH_MOON, CelestialBodyFactory.EARTH,
189             CelestialBodyFactory.MOON, CelestialBodyFactory.MARS, CelestialBodyFactory.JUPITER,
190             CelestialBodyFactory.SATURN, CelestialBodyFactory.URANUS, CelestialBodyFactory.NEPTUNE, CelestialBodyFactory.PLUTO
191         }) {
192 
193             CelestialBody original = CelestialBodyFactory.getBody(name);
194 
195             ByteArrayOutputStream bosBody = new ByteArrayOutputStream();
196             ObjectOutputStream    oosBody = new ObjectOutputStream(bosBody);
197             oosBody.writeObject(original);
198             Assert.assertTrue(bosBody.size() > 400);
199             Assert.assertTrue(bosBody.size() < 460);
200 
201             ByteArrayInputStream  bisBody = new ByteArrayInputStream(bosBody.toByteArray());
202             ObjectInputStream     oisBody = new ObjectInputStream(bisBody);
203             CelestialBody deserializedBody  = (CelestialBody) oisBody.readObject();
204             Assert.assertTrue(original == deserializedBody);
205 
206             ByteArrayOutputStream bosInertialFrame = new ByteArrayOutputStream();
207             ObjectOutputStream    oosInertialFrame = new ObjectOutputStream(bosInertialFrame);
208             oosInertialFrame.writeObject(original.getInertiallyOrientedFrame());
209             Assert.assertTrue(bosInertialFrame.size() > 400);
210             Assert.assertTrue(bosInertialFrame.size() < 460);
211 
212             ByteArrayInputStream  bisInertialFrame = new ByteArrayInputStream(bosInertialFrame.toByteArray());
213             ObjectInputStream     oisInertialFrame = new ObjectInputStream(bisInertialFrame);
214             Frame deserializedInertialFrame  = (Frame) oisInertialFrame.readObject();
215             Assert.assertTrue(original.getInertiallyOrientedFrame() == deserializedInertialFrame);
216 
217             ByteArrayOutputStream bosBodyFrame = new ByteArrayOutputStream();
218             ObjectOutputStream    oosBodyFrame = new ObjectOutputStream(bosBodyFrame);
219             oosBodyFrame.writeObject(original.getBodyOrientedFrame());
220             Assert.assertTrue(bosBodyFrame.size() > 400);
221             Assert.assertTrue(bosBodyFrame.size() < 460);
222 
223             ByteArrayInputStream  bisBodyFrame = new ByteArrayInputStream(bosBodyFrame.toByteArray());
224             ObjectInputStream     oisBodyFrame = new ObjectInputStream(bisBodyFrame);
225             Frame deserializedBodyFrame  = (Frame) oisBodyFrame.readObject();
226             Assert.assertTrue(original.getBodyOrientedFrame() == deserializedBodyFrame);
227 
228         }
229     }
230 
231     @Test
232     public void multithreadTest() {
233         Utils.setDataRoot("regular-data");
234         checkMultiThread(10, 100);
235     }
236 
237     private void checkMultiThread(final int threads, final int runs) {
238 
239         final AtomicReference<OrekitException> caught = new AtomicReference<OrekitException>();
240         ExecutorService executorService = Executors.newFixedThreadPool(threads);
241 
242         List<Future<?>> results = new ArrayList<Future<?>>();
243         for (int i = 0; i < threads; i++) {
244             Future<?> result = executorService.submit(new Runnable() {
245                 public void run() {
246                     try {
247                         for (int run = 0; run < runs; run++) {
248                             CelestialBody mars = CelestialBodyFactory.getBody(CelestialBodyFactory.MARS);
249                             Assert.assertNotNull(mars);
250                             CelestialBodyFactory.clearCelestialBodyLoaders();
251                         }
252                     } catch (OrekitException oe) {
253                         caught.set(oe);
254                     }
255                 }
256             });
257             results.add(result);
258         }
259 
260         try {
261             executorService.shutdown();
262             executorService.awaitTermination(5, TimeUnit.SECONDS);
263         } catch (InterruptedException ie) {
264             Assert.fail(ie.getLocalizedMessage());
265         }
266 
267         for (Future<?> result : results) {
268             Assert.assertTrue("Not all threads finished -> possible deadlock", result.isDone());
269         }
270 
271         if (caught.get() != null) {
272             throw caught.get();
273         }
274     }
275 
276     @Test
277     public void testEarthMoonBarycenter() {
278         Utils.setDataRoot("regular-data/de405-ephemerides");
279         CelestialBody sun = CelestialBodyFactory.getSun();
280         CelestialBody mars = CelestialBodyFactory.getMars();
281         CelestialBody earth = CelestialBodyFactory.getEarth();
282         CelestialBody earthMoonBarycenter = CelestialBodyFactory.getEarthMoonBarycenter();
283         List<Frame> frames = Arrays.asList(FramesFactory.getEME2000(),
284                                            FramesFactory.getGCRF(),
285                                            sun.getInertiallyOrientedFrame(),
286                                            mars.getInertiallyOrientedFrame(),
287                                            earth.getInertiallyOrientedFrame());
288 
289         AbsoluteDate date = new AbsoluteDate(1969, 7, 23, TimeScalesFactory.getTT());
290         final double refDistance = bodyDistance(sun, earthMoonBarycenter, date, frames.get(0));
291         for (Frame frame : frames) {
292             Assert.assertEquals(frame.toString(), refDistance,
293                                 bodyDistance(sun, earthMoonBarycenter, date, frame),
294                                 1.0e-14 * refDistance);
295         }
296     }
297 
298     @Test
299     public void testICRFAndGCRFAlignment() {
300         Utils.setDataRoot("regular-data");
301         final CelestialBody earthMoonBarycenter   = CelestialBodyFactory.getEarthMoonBarycenter();
302         final CelestialBody solarSystemBarycenter = CelestialBodyFactory.getSolarSystemBarycenter();
303         final List<Frame> frames = Arrays.asList(earthMoonBarycenter.getInertiallyOrientedFrame(),
304                                                  earthMoonBarycenter.getBodyOrientedFrame(),
305                                                  solarSystemBarycenter.getInertiallyOrientedFrame(),
306                                                  solarSystemBarycenter.getBodyOrientedFrame());
307         final Frame icrf = FramesFactory.getICRF();
308         final Frame gcrf = FramesFactory.getGCRF();
309         for (double dt = 0; dt < Constants.JULIAN_DAY; dt += 60) {
310             final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
311             for (final Frame frame : frames) {
312                 Assert.assertEquals(0.0, frame.getTransformTo(icrf, date).getRotation().getAngle(), 1.0e-15);
313                 Assert.assertEquals(0.0, frame.getTransformTo(gcrf, date).getRotation().getAngle(), 1.0e-15);
314             }
315         }
316     }
317 
318     @Test
319     public void testEarthInertialFrameAroundJ2000() {
320         Utils.setDataRoot("regular-data");
321         final Frame earthFrame = CelestialBodyFactory.getEarth().getInertiallyOrientedFrame();
322         final Frame base       = FramesFactory.getGCRF();
323         final Rotation reference = new Rotation(Vector3D.PLUS_K, Vector3D.PLUS_J,
324                                                 Vector3D.PLUS_K, Vector3D.PLUS_I);
325          for (double dt = -60; dt <= 60; dt += 1.0) {
326              final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
327              Rotation rotation = base.getTransformTo(earthFrame, date).getRotation();
328              Assert.assertEquals(0.0, Rotation.distance(reference, rotation), 3.0e-10);
329          }
330     }
331 
332     @Test
333     public void testEarthBodyOrientedFrameAroundJ2000() {
334         Utils.setDataRoot("regular-data");
335         final Frame earthFrame = CelestialBodyFactory.getEarth().getBodyOrientedFrame();
336         final Frame base       = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
337          for (double dt = -60; dt <= 60; dt += 1.0) {
338              final AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt);
339              Rotation rotation = base.getTransformTo(earthFrame, date).getRotation();
340              Assert.assertEquals(7.9426e-4, Rotation.distance(Rotation.IDENTITY, rotation), 1.0e-8);
341          }
342     }
343 
344     private double bodyDistance(CelestialBody body1, CelestialBody body2, AbsoluteDate date, Frame frame)
345         {
346         Vector3D body1Position = body1.getPVCoordinates(date, frame).getPosition();
347         Vector3D body2Position = body2.getPVCoordinates(date, frame).getPosition();
348         Vector3D bodyPositionDifference = body1Position.subtract(body2Position);
349         return bodyPositionDifference.getNorm();
350     }
351 
352 }