1   /* Copyright 2022-2025 Romain Serra
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  
18  package org.orekit.bodies;
19  
20  import org.hipparchus.CalculusFieldElement;
21  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
22  import org.hipparchus.analysis.differentiation.UnivariateDerivative1Field;
23  import org.hipparchus.complex.Complex;
24  import org.hipparchus.complex.ComplexField;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.hipparchus.geometry.euclidean.threed.Vector3D;
27  import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.Test;
31  import org.junit.jupiter.params.ParameterizedTest;
32  import org.junit.jupiter.params.provider.ValueSource;
33  import org.orekit.Utils;
34  import org.orekit.data.DataContext;
35  import org.orekit.forces.gravity.ThirdBodyAttraction;
36  import org.orekit.frames.Frame;
37  import org.orekit.frames.Frames;
38  import org.orekit.frames.FramesFactory;
39  import org.orekit.orbits.EquinoctialOrbit;
40  import org.orekit.orbits.PositionAngleType;
41  import org.orekit.propagation.SpacecraftState;
42  import org.orekit.propagation.numerical.NumericalPropagator;
43  import org.orekit.time.AbsoluteDate;
44  import org.orekit.time.DateTimeComponents;
45  import org.orekit.time.FieldAbsoluteDate;
46  import org.orekit.time.TimeScalesFactory;
47  import org.orekit.utils.Constants;
48  import org.orekit.utils.ExtendedPositionProvider;
49  
50  class AnalyticalSolarPositionProviderTest {
51  
52      @BeforeEach
53      void setUp() {
54          Utils.setDataRoot("regular-data");
55      }
56  
57      @ParameterizedTest
58      @ValueSource(ints = {1, 2, 3, 4, 5})
59      void testGetPosition(final int month) {
60          // GIVEN
61          final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2000, month, 1, 0, 0, 0),
62                  TimeScalesFactory.getUTC());
63          final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider();
64          final Frame frame = FramesFactory.getGCRF();
65          // WHEN
66          final Vector3D actualPosition = solarPositionProvider.getPosition(date, frame);
67          // THEN
68          final CelestialBody celestialBody = CelestialBodyFactory.getSun();
69          final Vector3D expectedPosition = celestialBody.getPosition(date, frame);
70          Assertions.assertEquals(0., Vector3D.angle(expectedPosition, actualPosition), 5e-5);
71          Assertions.assertEquals(0., expectedPosition.subtract(actualPosition).getNorm(), 1e7);
72      }
73  
74      @Test
75      void testGetPositionFieldVersusNonField() {
76          // GIVEN
77          final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2025, 1, 1, 0, 0, 0),
78                  TimeScalesFactory.getUTC());
79          final DataContext dataContext = DataContext.getDefault();
80          final Frames frames = dataContext.getFrames();
81          final Frame frame = frames.getEME2000();
82          final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider(dataContext);
83          final FieldAbsoluteDate<Complex> fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), date);
84          // WHEN
85          final FieldVector3D<Complex> fieldPosition = solarPositionProvider.getPosition(fieldDate, frame);
86          // THEN
87          final Vector3D expectedPosition = solarPositionProvider.getPosition(date, frame);
88          Assertions.assertEquals(expectedPosition, fieldPosition.toVector3D());
89      }
90  
91      @Test
92      void testGetPositionFieldDate() {
93          // GIVEN
94          final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2025, 1, 1, 0, 0, 0),
95                  TimeScalesFactory.getUTC());
96          final DataContext dataContext = DataContext.getDefault();
97          final Frames frames = dataContext.getFrames();
98          final Frame frame = frames.getGCRF();
99          final AnalyticalSolarPositionProvider solarPositionProvider = new AnalyticalSolarPositionProvider(dataContext);
100         final FieldAbsoluteDate<UnivariateDerivative1> fieldDate = new FieldAbsoluteDate<>(UnivariateDerivative1Field.getInstance(),
101                 date).shiftedBy(new UnivariateDerivative1(0., 1.));
102         // WHEN
103         final FieldVector3D<UnivariateDerivative1> fieldPosition = solarPositionProvider.getPosition(fieldDate, frame);
104         // THEN
105         Assertions.assertNotEquals(0., fieldPosition.getX().getFirstDerivative());
106     }
107 
108     @Test
109     void testPropagation() {
110         // GIVEN
111         final CelestialBody sun = CelestialBodyFactory.getSun();
112         final ExtendedPositionProvider positionProvider = new AnalyticalSolarPositionProvider();
113         final CelestialBody body = createCelestialBody(sun.getGM(), positionProvider);
114         // WHEN
115         final NumericalPropagator propagator = createPropagator(body);
116         final AbsoluteDate terminalDate = propagator.getInitialState().getDate().shiftedBy(1e6);
117         final SpacecraftState actualState = propagator.propagate(terminalDate);
118         // THEN
119         final NumericalPropagator propagator2 = createPropagator(sun);
120         final SpacecraftState expectedState = propagator2.propagate(terminalDate);
121         Assertions.assertEquals(0., expectedState.getPosition().subtract(actualState.getPosition()).getNorm(), 1e-1);
122     }
123 
124     private static CelestialBody createCelestialBody(final double mu, final ExtendedPositionProvider positionProvider) {
125         return new CelestialBody() {
126             @Override
127             public Frame getInertiallyOrientedFrame() {
128                 return null;
129             }
130 
131             @Override
132             public Frame getBodyOrientedFrame() {
133                 return null;
134             }
135 
136             @Override
137             public String getName() {
138                 return "";
139             }
140 
141             @Override
142             public double getGM() {
143                 return mu;
144             }
145 
146             @Override
147             public Vector3D getPosition(AbsoluteDate date, Frame frame) {
148                 return positionProvider.getPosition(date, frame);
149             }
150 
151             @Override
152             public <T extends CalculusFieldElement<T>> FieldVector3D<T> getPosition(FieldAbsoluteDate<T> date, Frame frame) {
153                 return null;
154             }
155         };
156     }
157 
158     private static NumericalPropagator createPropagator(final CelestialBody celestialBody) {
159         final NumericalPropagator propagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(100));
160         final AbsoluteDate date = new AbsoluteDate(new DateTimeComponents(2000, 1, 1, 0, 0, 0),
161                 TimeScalesFactory.getUTC());
162         final EquinoctialOrbit orbit = new EquinoctialOrbit(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 1000.e3,
163                 0.1, 0.2, 0.3, 0.4, 5., PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), date, Constants.EGM96_EARTH_MU);
164         propagator.setInitialState(new SpacecraftState(orbit));
165         propagator.addForceModel(new ThirdBodyAttraction(celestialBody));
166         return propagator;
167     }
168  }