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.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.util.Binary64Field;
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.bodies.CelestialBody;
29  import org.orekit.bodies.CelestialBodyFactory;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  import org.orekit.time.TimeScalesFactory;
33  import org.orekit.utils.Constants;
34  import org.orekit.utils.FieldPVCoordinates;
35  
36  /**Unit tests for {@link L2TransformProvider}.
37   *
38   * @author Luc Maisonobe
39   * @author Julio Hernanz
40   */
41  public class L2TransformProviderTest {
42  
43      @Test
44      public void testTransformationOrientationForEarthMoon() {
45  
46          // Load Bodies
47          final CelestialBody earth = CelestialBodyFactory.getEarth();
48          final CelestialBody moon = CelestialBodyFactory.getMoon();
49  
50          // Set framesd
51          final Frame eme2000 = FramesFactory.getEME2000();
52          final Frame l2Frame = new L2Frame(earth, moon);
53  
54          // Time settings
55          final AbsoluteDate date = new AbsoluteDate(2000, 01, 01, 0, 0, 00.000,
56                                                     TimeScalesFactory.getUTC());
57  
58          // Compute Moon position in EME2000
59          Vector3D posMoon = moon.getPosition(date, eme2000);
60  
61          // Compute L2 position in EME2000
62          // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
63          // because the test should avoid doing wrong interpretation of the meaning and
64          // particularly on the sign of the translation)
65          Vector3D posL2   = l2Frame.getStaticTransformTo(eme2000,date).transformPosition(Vector3D.ZERO);
66  
67          // check L2 and Moon are aligned as seen from Earth
68          Assertions.assertEquals(0.0, Vector3D.angle(posMoon, posL2), 1.0e-10);
69  
70          // check L2 if at least 60 000km farther than Moon
71          Assertions.assertTrue(posL2.getNorm() > posMoon.getNorm() + 6.0e7);
72  
73      }
74  
75      @Test
76      public void testFieldTransformationOrientationForEarthMoon() {
77          doTestFieldTransformationOrientationForEarthMoon(Binary64Field.getInstance());
78      }
79  
80      private <T extends CalculusFieldElement<T>> void doTestFieldTransformationOrientationForEarthMoon(final Field<T> field) {
81  
82          // Load Bodies
83          final CelestialBody earth = CelestialBodyFactory.getEarth();
84          final CelestialBody moon = CelestialBodyFactory.getMoon();
85  
86          // Set framesd
87          final Frame eme2000 = FramesFactory.getEME2000();
88          final Frame l2Frame = new L2Frame(earth, moon);
89  
90          // Time settings
91          final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2000, 01, 01, 0, 0, 00.000,
92                                                                    TimeScalesFactory.getUTC());
93  
94          // Compute Moon position in EME2000
95          FieldPVCoordinates<T> pvMoon = moon.getPVCoordinates(date, eme2000);
96          FieldVector3D<T> posMoon = pvMoon.getPosition();
97  
98          // Compute L2 position in EME2000
99          // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
100         // because the test should avoid doing wrong interpretation of the meaning and
101         // particularly on the sign of the translation)
102         FieldVector3D<T> posL2   = l2Frame.getTransformTo(eme2000,date).transformPosition(Vector3D.ZERO);
103 
104         // check L2 and Moon are aligned as seen from Earth
105         Assertions.assertEquals(0.0, FieldVector3D.angle(posMoon, posL2).getReal(), 1.0e-10);
106 
107         // check L2 if at least 60 000km farther than Moon
108         Assertions.assertTrue(posL2.getNorm().getReal() > posMoon.getNorm().getReal() + 6.0e7);
109 
110     }
111 
112     @Test
113     public void testSunEarth() {
114 
115         // Load Bodies
116         final CelestialBody sun = CelestialBodyFactory.getSun();
117         final CelestialBody earth = CelestialBodyFactory.getEarth();
118 
119         // Set frames
120         final Frame sunFrame = sun.getInertiallyOrientedFrame();
121         final Frame l2Frame = new L2Frame(sun, earth);
122 
123         // Time settings
124         final AbsoluteDate date = new AbsoluteDate(2000, 01, 01, 0, 0, 00.000,
125                                                    TimeScalesFactory.getUTC());
126 
127         // Compute Earth position in Sun centered frame
128         Vector3D posEarth = earth.getPosition(date, sunFrame);
129 
130         // Compute L2 position in Sun centered frame
131         // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
132         // because the test should avoid doing wrong interpretation of the meaning and
133         // particularly on the sign of the translation)
134         Vector3D posL2   = l2Frame.getStaticTransformTo(sunFrame,date).transformPosition(Vector3D.ZERO);
135 
136         // check L2 and Earth are aligned as seen from Sun
137         Assertions.assertEquals(0.0, Vector3D.angle(posEarth, posL2), 1.0e-10);
138 
139         // check L2 if at least 1 000 000km farther than Earth
140         Assertions.assertTrue(posL2.getNorm() > posEarth.getNorm() + 1.0e9);
141     }
142 
143     @Test
144     public void testFieldSunEarth() {
145         doTestFieldSunEarth(Binary64Field.getInstance());
146     }
147 
148     private <T extends CalculusFieldElement<T>> void doTestFieldSunEarth(final Field<T> field) {
149 
150         // Load Bodies
151         final CelestialBody sun = CelestialBodyFactory.getSun();
152         final CelestialBody earth = CelestialBodyFactory.getEarth();
153 
154         // Set frames
155         final Frame sunFrame = sun.getInertiallyOrientedFrame();
156         final Frame l2Frame = new L2Frame(sun, earth);
157 
158         // Time settings
159         final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2000, 01, 01, 0, 0, 00.000,
160                                                    TimeScalesFactory.getUTC());
161 
162         // Compute Earth position in Sun centered frame
163         FieldPVCoordinates<T> pvEarth = earth.getPVCoordinates(date, sunFrame);
164         FieldVector3D<T> posEarth = pvEarth.getPosition();
165 
166         // Compute L2 position in Sun centered frame
167         // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
168         // because the test should avoid doing wrong interpretation of the meaning and
169         // particularly on the sign of the translation)
170         FieldVector3D<T> posL2   = l2Frame.getStaticTransformTo(sunFrame,date).transformPosition(Vector3D.ZERO);
171 
172         // check L2 and Earth are aligned as seen from Sun
173         Assertions.assertEquals(0.0, FieldVector3D.angle(posEarth, posL2).getReal(), 1.0e-10);
174 
175         // check L2 if at least 1 000 000km farther than Earth
176         Assertions.assertTrue(posL2.getNorm().getReal() > posEarth.getNorm().getReal() + 1.0e9);
177     }
178 
179     @Test
180     public void testSunJupiter() {
181 
182         // Load Bodies
183         final CelestialBody sun = CelestialBodyFactory.getSun();
184         final CelestialBody jupiter = CelestialBodyFactory.getJupiter();
185 
186         // Set frames
187         final Frame sunFrame = sun.getInertiallyOrientedFrame();
188         final Frame l2Frame = new L2Frame(sun, jupiter);
189 
190         // Time settings
191         final AbsoluteDate date = new AbsoluteDate(2000, 01, 01, 0, 0, 00.000,
192                                                    TimeScalesFactory.getUTC());
193 
194         // Compute Jupiter position in Sun centered frame
195         Vector3D posJupiter = jupiter.getPosition(date, sunFrame);
196 
197         // Compute L2 position in Sun centered frame
198         // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
199         // because the test should avoid doing wrong interpretation of the meaning and
200         // particularly on the sign of the translation)
201         Vector3D posL2   = l2Frame.getStaticTransformTo(sunFrame,date).transformPosition(Vector3D.ZERO);
202 
203         // check L2 and Jupiter are aligned as seen from Sun
204         Assertions.assertEquals(0.0, Vector3D.angle(posJupiter, posL2), 1.0e-10);
205 
206         // check L2 if at least 50 000 000km farther than Jupiter
207         Assertions.assertTrue(posL2.getNorm() > posJupiter.getNorm() + 5.0e10);
208     }
209 
210     @Test
211     public void testFieldSunJupiter() {
212         doTestFieldSunJupiter(Binary64Field.getInstance());
213     }
214 
215     private <T extends CalculusFieldElement<T>> void doTestFieldSunJupiter(final Field<T> field) {
216 
217         // Load Bodies
218         final CelestialBody sun = CelestialBodyFactory.getSun();
219         final CelestialBody jupiter = CelestialBodyFactory.getJupiter();
220 
221         // Set frames
222         final Frame sunFrame = sun.getInertiallyOrientedFrame();
223         final Frame l2Frame = new L2Frame(sun, jupiter);
224 
225         // Time settings
226         final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2000, 01, 01, 0, 0, 00.000,
227                                                                   TimeScalesFactory.getUTC());
228 
229         // Compute Jupiter position in Sun centered frame
230         FieldPVCoordinates<T> pvJupiter = jupiter.getPVCoordinates(date, sunFrame);
231         FieldVector3D<T> posJupiter = pvJupiter.getPosition();
232 
233         // Compute L2 position in Sun centered frame
234         // (it is important to use transformPosition(Vector3D.ZERO) and *not* getTranslation()
235         // because the test should avoid doing wrong interpretation of the meaning and
236         // particularly on the sign of the translation)
237         FieldVector3D<T> posL2   = l2Frame.getStaticTransformTo(sunFrame,date).transformPosition(Vector3D.ZERO);
238 
239         // check L2 and Jupiter are aligned as seen from Sun
240         Assertions.assertEquals(0.0, FieldVector3D.angle(posJupiter, posL2).getReal(), 1.0e-10);
241 
242         // check L2 if at least 50 000 000km farther than Jupiter
243         Assertions.assertTrue(posL2.getNorm().getReal() > posJupiter.getNorm().getReal() + 5.0e10);
244     }
245 
246     @Test
247     public void testL2Orientation() {
248 
249         final AbsoluteDate date0 = new AbsoluteDate(2000, 01, 1, 11, 58, 20.000,
250                                                    TimeScalesFactory.getUTC());
251         final CelestialBody sun     = CelestialBodyFactory.getSun();
252         final CelestialBody earth   = CelestialBodyFactory.getEarth();
253         final Frame         l2Frame = new L2Frame(sun, earth);
254         for (double dt = -Constants.JULIAN_DAY; dt <= Constants.JULIAN_DAY; dt += 3600.0) {
255             final AbsoluteDate date              = date0.shiftedBy(dt);
256             final Vector3D     sunPositionInL2   = sun.getPosition(date, l2Frame);
257             final Vector3D     earthPositionInL2 = earth.getPosition(date, l2Frame);
258             Assertions.assertEquals(0.0, Vector3D.angle(sunPositionInL2,   Vector3D.MINUS_I), 3.0e-14);
259             Assertions.assertEquals(0.0, Vector3D.angle(earthPositionInL2, Vector3D.MINUS_I), 3.0e-14);
260         }
261     }
262 
263     @Test
264     public void testFieldL2Orientation() {
265         doTestFieldL2Orientation(Binary64Field.getInstance());
266     }
267 
268     private <T extends CalculusFieldElement<T>> void doTestFieldL2Orientation(final Field<T> field) {
269 
270         final FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, 2000, 01, 1, 11, 58, 20.000,
271                                                                    TimeScalesFactory.getUTC());
272         final CelestialBody sun     = CelestialBodyFactory.getSun();
273         final CelestialBody earth   = CelestialBodyFactory.getEarth();
274         final Frame         l2Frame = new L2Frame(sun, earth);
275         for (double dt = -Constants.JULIAN_DAY; dt <= Constants.JULIAN_DAY; dt += 3600.0) {
276             final FieldAbsoluteDate<T> date              = date0.shiftedBy(dt);
277             final FieldVector3D<T>     sunPositionInL2   = sun.getPosition(date, l2Frame);
278             final FieldVector3D<T>     earthPositionInL2 = earth.getPosition(date, l2Frame);
279             Assertions.assertEquals(0.0, FieldVector3D.angle(sunPositionInL2,   Vector3D.MINUS_I).getReal(), 3.0e-14);
280             Assertions.assertEquals(0.0, FieldVector3D.angle(earthPositionInL2, Vector3D.MINUS_I).getReal(), 3.0e-14);
281         }
282     }
283 
284     @BeforeEach
285     public void setUp() {
286         Utils.setDataRoot("regular-data");
287     }
288 
289 }