1   /* Copyright 2002-2024 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.propagation.conversion.osc2mean;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.hipparchus.optim.nonlinear.vector.leastsquares.GaussNewtonOptimizer;
23  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
24  import org.hipparchus.util.Binary64Field;
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.data.DataContext;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
32  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
33  import org.orekit.frames.FramesFactory;
34  import org.orekit.orbits.FieldOrbit;
35  import org.orekit.orbits.KeplerianOrbit;
36  import org.orekit.orbits.Orbit;
37  import org.orekit.orbits.OrbitType;
38  import org.orekit.orbits.PositionAngleType;
39  import org.orekit.propagation.analytical.BrouwerLyddanePropagator;
40  import org.orekit.propagation.analytical.tle.FieldTLE;
41  import org.orekit.propagation.analytical.tle.TLE;
42  import org.orekit.propagation.analytical.tle.TLEConstants;
43  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
44  import org.orekit.propagation.semianalytical.dsst.forces.DSSTZonal;
45  import org.orekit.time.AbsoluteDate;
46  import org.orekit.utils.Constants;
47  
48  class OsculatingToMeanConverterTest {
49  
50      private Orbit osculating;
51  
52      private LeastSquaresOptimizer optimizer;
53  
54      private Binary64Field field;
55  
56      private boolean doPrint;
57  
58      @BeforeEach
59      void setUp() {
60          Utils.setDataRoot("regular-data:potential/icgem-format");
61          osculating = getOsculatingOrbit();
62          optimizer  = new GaussNewtonOptimizer();
63          field      = Binary64Field.getInstance();
64          doPrint    = false;
65      }
66  
67      @Test
68      void testBrouwerLyddane() {
69          // GIVEN
70          // Mean theory
71          final BrouwerLyddaneTheory theory = new BrouwerLyddaneTheory(getProvider(5),
72                                                                       BrouwerLyddanePropagator.M2);
73          // THEN
74          compareAlgorithms(theory, 1e-6, 1e-13, 1e-12, 1e-13, 1e-14, 1e-12);
75          compareFieldVersions(theory, 1e-8, 1e-15, 1e-16, 1e-15, 1e-15, 1e-15);
76          miscellaneous(theory);
77      }
78  
79      @Test
80      void testEcksteinHechler() {
81          // GIVEN
82          // Mean theory
83          final UnnormalizedSphericalHarmonicsProvider provider = getProvider(6);
84          final UnnormalizedSphericalHarmonics harmonics = provider.onDate(getOsculatingOrbit().getDate());
85          final EcksteinHechlerTheory theory = new EcksteinHechlerTheory(provider.getAe(), provider.getMu(),
86                                                                         harmonics.getUnnormalizedCnm(2, 0),
87                                                                         harmonics.getUnnormalizedCnm(3, 0),
88                                                                         harmonics.getUnnormalizedCnm(4, 0),
89                                                                         harmonics.getUnnormalizedCnm(5, 0),
90                                                                         harmonics.getUnnormalizedCnm(6, 0));
91          // THEN
92          compareAlgorithms(theory, 1e-5, 1e-12, 1e-13, 1e-12, 1e-12, 1e-12);
93          compareFieldVersions(theory, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15);
94          miscellaneous(theory);
95      }
96  
97      @Test
98      void testDSST() {
99          // GIVEN
100         // Mean theory
101         final DSSTTheory theory0 = new DSSTTheory(createDSSTForces());
102         final DSSTTheory theory1 = new DSSTTheory(createDSSTForces());
103         final DSSTTheory theory2 = new DSSTTheory(createDSSTForces());
104         // THEN
105         compareAlgorithms(theory0, 1e-6, 1e-13, 1e-13, 1e-14, 1e-14, 1e-15);
106         compareFieldVersions(theory1, 1e-8, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15);
107         miscellaneous(theory2);
108     }
109 
110     @Test
111     void testTLE() {
112         // GIVEN
113         // Mean theory
114         final TLETheory theory = new TLETheory();
115         final TLETheory fieldTheory = new TLETheory(new FieldTLE<>(field, TLETheory.TMP_L1, TLETheory.TMP_L2));
116         // THEN
117         compareAlgorithms(theory, 1e-5, 1e-12, 1e-12, 1e-12, 1e-13, 1e-12);
118         compareFieldVersions(fieldTheory, 1e-8, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15);
119     }
120 
121     @Test
122     void testTLEMisc() {
123         // GIVEN
124         final String  l1 = "1 00000U 00000A   00001.00000000  .00000000  00000+0  00000+0 0    02";
125         final String  l2 = "2 00000   0.0000   0.0000 0000000   0.0000   0.0000  0.00000000    02";
126         final TLE tmpTle = new TLE(l1, l2);
127         // Mean theory
128         final TLETheory theory = new TLETheory(tmpTle);
129         // Mean orbit converters
130         final FixedPointConverter   fpConverter = new FixedPointConverter(theory);
131         final LeastSquaresConverter lsConverter = new LeastSquaresConverter(theory, optimizer);
132         // WHEN
133         final Orbit fpMean = fpConverter.convertToMean(osculating);
134         final Orbit lsMean = lsConverter.convertToMean(osculating);
135         // THEN
136         Assertions.assertEquals(fpConverter.getMeanTheory().getTheoryName(),
137                                 lsConverter.getMeanTheory().getTheoryName());
138         Assertions.assertEquals(FixedPointConverter.DEFAULT_DAMPING,          fpConverter.getDamping(), 0.);
139         Assertions.assertEquals(FixedPointConverter.DEFAULT_MAX_ITERATIONS,   fpConverter.getMaxIterations(), 0);
140         Assertions.assertEquals(FixedPointConverter.DEFAULT_THRESHOLD,        fpConverter.getThreshold(), 0.);
141         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_MAX_ITERATIONS, lsConverter.getMaxIterations(), 0);
142         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_THRESHOLD,      lsConverter.getThreshold(), 0.);
143         Assertions.assertEquals(fpMean.getType(),  OrbitType.KEPLERIAN);
144         Assertions.assertEquals(fpMean.getDate(),  osculating.getDate());
145         Assertions.assertEquals(fpMean.getFrame(), FramesFactory.getTEME());
146         Assertions.assertEquals(fpMean.getMu(),    TLEConstants.MU);
147         Assertions.assertEquals(lsMean.getType(),  OrbitType.KEPLERIAN);
148         Assertions.assertEquals(lsMean.getDate(),  osculating.getDate());
149         Assertions.assertEquals(lsMean.getFrame(), FramesFactory.getTEME());
150         Assertions.assertEquals(lsMean.getMu(),    TLEConstants.MU);
151     }
152 
153     @Test
154     void testErrors() {
155         // GIVEN
156         final Orbit orbit = new KeplerianOrbit(1e4, 0.1, 1., 1., 2., -3.,
157                                                PositionAngleType.MEAN,
158                                                FramesFactory.getEME2000(),
159                                                AbsoluteDate.J2000_EPOCH,
160                                                Constants.EGM96_EARTH_MU);
161         // Field orbit
162         final FieldOrbit<?> fieldOrbit = orbit.getType().convertToFieldOrbit(field, orbit);
163         // Fixed-point converter
164         final FixedPointConverter fpConvert = new FixedPointConverter(new TLETheory());
165         // THEN
166         // Try converting simple orbit
167         Assertions.assertThrows(OrekitException.class, () -> {
168             fpConvert.convertToMean(orbit);
169         });
170         // Try converting field orbit
171         Assertions.assertThrows(OrekitException.class, () -> {
172             fpConvert.convertToMean(fieldOrbit);
173         });
174     }
175 
176     /**
177      * Compares fixed-point and lest-squares algorithms for a given theory.
178      * @param theory the theory to consider
179      * @param dA  expected deviation on the semi-major axis
180      * @param dEx expected deviation on Ex
181      * @param dEy expected deviation on Ey
182      * @param dHx expected deviation on Hx
183      * @param dHy expected deviation on Hy
184      * @param dLv expected deviation on true latitude argument
185      */
186     private void compareAlgorithms(final MeanTheory theory,
187                                    final double dA, final double dEx, final double dEy,
188                                    final double dHx, final double dHy, final double dLv) {
189         // GIVEN
190         // Mean orbit converters
191         final FixedPointConverter   fpConvert = new FixedPointConverter(theory);
192         final LeastSquaresConverter lsConvert = new LeastSquaresConverter(theory, optimizer);
193         // WHEN
194         final Orbit fpMean = fpConvert.convertToMean(osculating);
195         final Orbit lsMean = lsConvert.convertToMean(osculating);
196         if (doPrint) {
197             System.out.println(fpConvert.getIterationsNb() + " " + lsConvert.getIterationsNb());
198             System.out.println();
199         }
200         // THEN
201         compareOrbits(fpMean, lsMean, dA, dEx, dEy, dHx, dHy, dLv);
202     }
203 
204     /**
205      * Compares field versions of fixed-point and lest-squares algorithms for a given theory.
206      * @param theory the theory to consider
207      * @param dA  expected deviation on the semi-major axis
208      * @param dEx expected deviation on Ex
209      * @param dEy expected deviation on Ey
210      * @param dHx expected deviation on Hx
211      * @param dHy expected deviation on Hy
212      * @param dLv expected deviation on true latitude argument
213      */
214     private void compareFieldVersions(final MeanTheory theory,
215                                       final double dA, final double dEx, final double dEy,
216                                       final double dHx, final double dHy, final double dLv) {
217         // GIVEN
218         // Field orbit
219         final FieldOrbit<?> fieldOrbit = osculating.getType().convertToFieldOrbit(field, osculating);
220         // Mean orbit converters
221         final FixedPointConverter   fpConvert = new FixedPointConverter(theory);
222         final LeastSquaresConverter lsConvert = new LeastSquaresConverter(theory, optimizer);
223         // THEN
224         // Try conversion using fixed point algorithm
225         Assertions.assertDoesNotThrow(() -> {
226             final FieldOrbit<?> fpMean = fpConvert.convertToMean(fieldOrbit);
227             compareOrbits(fpMean.toOrbit(), fpConvert.convertToMean(osculating),
228                           dA, dEx, dEy, dHx, dHy, dLv);
229         });
230         // Try conversion using least-squares algorithm
231         Assertions.assertThrows(UnsupportedOperationException.class, () -> {
232             lsConvert.convertToMean(fieldOrbit);
233         });
234     }
235 
236     /**
237      * Miscellaneous coverage tests.
238      * @param theory the mean theory to consider
239      */
240     private void miscellaneous(final MeanTheory theory) {
241         // Check for FixedPointConverter
242         fixedPointMisc(theory);
243         // Check for LeastSquaresConverter
244         leastSquaresMisc(theory);
245     }
246 
247     /**
248      * Miscellaneous coverage tests for FixedPointConverter.
249      * @param theory the theory to consider
250      */
251     private void fixedPointMisc(final MeanTheory theory) {
252         FixedPointConverter fpConvert = new FixedPointConverter(1.e-10, 1000, 0.75);
253         Assertions.assertNull(fpConvert.getMeanTheory());
254         Assertions.assertEquals(1.e-10, fpConvert.getThreshold());
255         Assertions.assertEquals(1000, fpConvert.getMaxIterations());
256         Assertions.assertEquals(0.75, fpConvert.getDamping());
257 
258         fpConvert = new FixedPointConverter();
259         Assertions.assertNull(fpConvert.getMeanTheory());
260         Assertions.assertEquals(FixedPointConverter.DEFAULT_THRESHOLD, fpConvert.getThreshold());
261         Assertions.assertEquals(FixedPointConverter.DEFAULT_MAX_ITERATIONS, fpConvert.getMaxIterations());
262         Assertions.assertEquals(FixedPointConverter.DEFAULT_DAMPING, fpConvert.getDamping());
263 
264         fpConvert.setMeanTheory(theory);
265         Assertions.assertEquals(theory.getTheoryName(), fpConvert.getMeanTheory().getTheoryName());
266 
267         Assertions.assertEquals(0, fpConvert.getIterationsNb());
268         // WHEN
269         final Orbit fpMean = fpConvert.convertToMean(osculating);
270         // THEN
271         Assertions.assertNotEquals(0, fpConvert.getIterationsNb());
272         if (theory.getTheoryName().contentEquals(EcksteinHechlerTheory.THEORY)) {
273             Assertions.assertEquals(OrbitType.CIRCULAR, fpMean.getType());
274         } else {
275             Assertions.assertEquals(fpMean.getType(),  osculating.getType());
276         }
277         Assertions.assertEquals(fpMean.getDate(),  osculating.getDate());
278         Assertions.assertEquals(fpMean.getMu(),    osculating.getMu());
279         Assertions.assertEquals(fpMean.getFrame(), osculating.getFrame());
280     }
281 
282     /**
283      * Miscellaneous coverage tests for LeastSquaresConverter.
284      * @param theory the theory to consider
285      */
286     private void leastSquaresMisc(final MeanTheory theory) {
287         LeastSquaresConverter lsConvert = new LeastSquaresConverter(1.e-3, 100);
288         Assertions.assertNull(lsConvert.getMeanTheory());
289         Assertions.assertNull(lsConvert.getOptimizer());
290         Assertions.assertEquals(1.e-3, lsConvert.getThreshold());
291         Assertions.assertEquals(100, lsConvert.getMaxIterations());
292 
293         lsConvert = new LeastSquaresConverter();
294         Assertions.assertNull(lsConvert.getMeanTheory());
295         Assertions.assertNull(lsConvert.getOptimizer());
296         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_THRESHOLD, lsConvert.getThreshold());
297         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_MAX_ITERATIONS, lsConvert.getMaxIterations());
298 
299         lsConvert = new LeastSquaresConverter(theory);
300         Assertions.assertEquals(theory.getTheoryName(), lsConvert.getMeanTheory().getTheoryName());
301         Assertions.assertNull(lsConvert.getOptimizer());
302         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_THRESHOLD, lsConvert.getThreshold());
303         Assertions.assertEquals(LeastSquaresConverter.DEFAULT_MAX_ITERATIONS, lsConvert.getMaxIterations());
304         lsConvert.setOptimizer(optimizer);
305         Assertions.assertEquals(optimizer, lsConvert.getOptimizer());
306 
307         Assertions.assertEquals(0, lsConvert.getIterationsNb());
308         Assertions.assertEquals(0, lsConvert.getRMS());
309         // WHEN
310         final Orbit lsMean = lsConvert.convertToMean(osculating);
311         // THEN
312         Assertions.assertNotEquals(0, lsConvert.getIterationsNb());
313         Assertions.assertNotEquals(0, lsConvert.getRMS());
314         if (theory.getTheoryName().contentEquals(EcksteinHechlerTheory.THEORY)) {
315             Assertions.assertEquals(OrbitType.CIRCULAR, lsMean.getType());
316         } else {
317             Assertions.assertEquals(lsMean.getType(),  osculating.getType());
318         }
319         Assertions.assertEquals(lsMean.getDate(),  osculating.getDate());
320         Assertions.assertEquals(lsMean.getMu(),    osculating.getMu());
321         Assertions.assertEquals(lsMean.getFrame(), osculating.getFrame());
322     }
323 
324     /**
325      * Compares 2 orbits.
326      * @param orbit1 the 1st orbit to consider
327      * @param orbit2 the 2nd orbit to consider
328      * @param dA  expected deviation on the semi-major axis
329      * @param dEx expected deviation on Ex
330      * @param dEy expected deviation on Ey
331      * @param dHx expected deviation on Hx
332      * @param dHy expected deviation on Hy
333      * @param dLv expected deviation on true latitude argument
334      */
335     private void compareOrbits(final Orbit orbit1, final Orbit orbit2,
336                                final double dA, final double dEx, final double dEy,
337                                final double dHx, final double dHy, final double dLv) {
338         if (doPrint) {
339             System.out.println(orbit1.getA() - orbit2.getA());
340             System.out.println(orbit1.getEquinoctialEx() - orbit2.getEquinoctialEx());
341             System.out.println(orbit1.getEquinoctialEy() - orbit2.getEquinoctialEy());
342             System.out.println(orbit1.getHx() - orbit2.getHx());
343             System.out.println(orbit1.getHy() - orbit2.getHy());
344             System.out.println(orbit1.getLv() - orbit2.getLv());
345             System.out.println();
346         }
347         Assertions.assertEquals(orbit1.getA(), orbit2.getA(), dA);
348         Assertions.assertEquals(orbit1.getEquinoctialEx(), orbit2.getEquinoctialEx(), dEx);
349         Assertions.assertEquals(orbit1.getEquinoctialEy(), orbit2.getEquinoctialEy(), dEy);
350         Assertions.assertEquals(orbit1.getHx(), orbit2.getHx(), dHx);
351         Assertions.assertEquals(orbit1.getHy(), orbit2.getHy(), dHy);
352         Assertions.assertEquals(orbit1.getLv(), orbit2.getLv(), dLv);
353     }
354 
355     /**
356      * Builds an orbit.
357      * @return the orbit
358      */
359     private Orbit getOsculatingOrbit() {
360         return new KeplerianOrbit(1e7, 0.1, 1., 1., 2., -3.,
361                                   PositionAngleType.MEAN,
362                                   FramesFactory.getEME2000(),
363                                   AbsoluteDate.J2000_EPOCH,
364                                   Constants.EGM96_EARTH_MU);
365     }
366 
367     /**
368      * Gets an harmonic provider.
369      * @param maxDegree the max degree to set (order is set to 0)
370      * @return harmonic provider
371      */
372     private UnnormalizedSphericalHarmonicsProvider getProvider(final int maxDegree) {
373         return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(maxDegree, 0);
374     }
375 
376     /**
377      * Create list of first 6 zonal DSST forces.
378      * @return six first zonal forces
379      */
380     private List<DSSTForceModel> createDSSTForces() {
381         final List<DSSTForceModel> forceModels = new ArrayList<>();
382         final DSSTZonal zonal = new DSSTZonal(getProvider(6));
383         forceModels.add(zonal);
384         return forceModels;
385     }
386 
387 }