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