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