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.propagation.conversion;
18  
19  import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy;
20  
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.Utils;
27  import org.orekit.forces.gravity.potential.GravityFieldFactory;
28  import org.orekit.forces.gravity.potential.TideSystem;
29  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
30  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
31  import org.orekit.frames.Frame;
32  import org.orekit.frames.FramesFactory;
33  import org.orekit.orbits.CartesianOrbit;
34  import org.orekit.orbits.KeplerianOrbit;
35  import org.orekit.orbits.Orbit;
36  import org.orekit.orbits.OrbitType;
37  import org.orekit.orbits.PositionAngleType;
38  import org.orekit.propagation.Propagator;
39  import org.orekit.propagation.analytical.BrouwerLyddanePropagator;
40  import org.orekit.time.AbsoluteDate;
41  import org.orekit.utils.Constants;
42  import org.orekit.utils.PVCoordinates;
43  import org.orekit.utils.ParameterDriver;
44  import org.orekit.utils.TimeStampedPVCoordinates;
45  
46  public class BrouwerLyddanePropagatorBuilderTest {
47  
48      private Orbit orbit;
49      private UnnormalizedSphericalHarmonicsProvider provider;
50  
51      @Test
52      public void doTestBuildPropagator() {
53  
54          final double eps  = 2.0e-10;
55  
56          // Define initial state and BrouwerLyddane Propagator
57          AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(583.);
58          BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(orbit, provider, BrouwerLyddanePropagator.M2);
59          // We propagate using directly the propagator of the set up
60          final Orbit orbitWithPropagator = propagator.propagate(initDate.shiftedBy(60000)).getOrbit();
61  
62          // Convert povider to normalized provider to be able to build a Brouwer Lyddane propagator
63          UnnormalizedSphericalHarmonics harmonics = provider.onDate(orbit.getDate());
64  
65          // We propagate using a build version of the propagator
66          // We shall have the same results than before
67          BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit,
68                                                                                        provider.getAe(),
69                                                                                        provider.getMu(),
70                                                                                        provider.getTideSystem(),
71                                                                                        harmonics.getUnnormalizedCnm(2, 0),
72                                                                                        harmonics.getUnnormalizedCnm(3, 0),
73                                                                                        harmonics.getUnnormalizedCnm(4, 0),
74                                                                                        harmonics.getUnnormalizedCnm(5, 0),
75                                                                                        OrbitType.KEPLERIAN,
76                                                                                        PositionAngleType.TRUE,
77                                                                                        1.0,
78                                                                                        BrouwerLyddanePropagator.M2);
79  
80          final Propagator prop = builder.buildPropagator();
81          final Orbit orbitWithBuilder = prop.propagate(initDate.shiftedBy(60000)).getOrbit();
82  
83          // Verify
84          Assertions.assertEquals(orbitWithPropagator.getA(),             orbitWithBuilder.getA(), 1.e-1);
85          Assertions.assertEquals(orbitWithPropagator.getEquinoctialEx(), orbitWithBuilder.getEquinoctialEx(), eps);
86          Assertions.assertEquals(orbitWithPropagator.getEquinoctialEy(), orbitWithBuilder.getEquinoctialEy(), eps);
87          Assertions.assertEquals(orbitWithPropagator.getHx(),            orbitWithBuilder.getHx(), eps);
88          Assertions.assertEquals(orbitWithPropagator.getHy(),            orbitWithBuilder.getHy(), eps);
89          Assertions.assertEquals(orbitWithPropagator.getLM(),            orbitWithBuilder.getLM(), 8.0e-10);
90  
91      }
92  
93      @Test
94      public void doTestBuildPropagatorWithDrag() {
95  
96          // M2
97          final double M2 = 1.0e-15;
98  
99          // Convert provider to normalized provider to be able to build a Brouwer Lyddane propagator
100         UnnormalizedSphericalHarmonics harmonics = provider.onDate(orbit.getDate());
101 
102         // Initialize propagator builder
103         BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit,
104                                                                                       provider.getAe(),
105                                                                                       provider.getMu(),
106                                                                                       provider.getTideSystem(),
107                                                                                       harmonics.getUnnormalizedCnm(2, 0),
108                                                                                       harmonics.getUnnormalizedCnm(3, 0),
109                                                                                       harmonics.getUnnormalizedCnm(4, 0),
110                                                                                       harmonics.getUnnormalizedCnm(5, 0),
111                                                                                       OrbitType.KEPLERIAN,
112                                                                                       PositionAngleType.TRUE,
113                                                                                       1.0,
114                                                                                       M2);
115 
116         // Set the M2 parameter to selected
117         for (ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
118             if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
119                 driver.setSelected(true);
120             }
121         }
122 
123         // Build the propagator
124         final BrouwerLyddanePropagator prop = builder.buildPropagator();
125 
126         // Verify
127         Assertions.assertEquals(M2, prop.getM2(), Double.MIN_VALUE);
128         Assertions.assertTrue(prop.getParametersDrivers().get(0).isSelected());
129 
130     }
131 
132     @BeforeEach
133     public void setUp() {
134         Utils.setDataRoot("potential:regular-data");
135 
136         AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(583.);
137         final Frame inertialFrame = FramesFactory.getEME2000();
138 
139         // Provider definition
140         double mu  = 3.9860047e14;
141         double ae  = 6.378137e6;
142         double[][] cnm = new double[][] {
143             { 0 }, { 0 }, { -1.08263e-3 }, { 2.54e-6 }, { 1.62e-6 }, { 2.3e-7 }
144            };
145         double[][] snm = new double[][] {
146             { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }
147            };
148         provider = GravityFieldFactory.getUnnormalizedProvider(ae, mu, TideSystem.UNKNOWN, cnm, snm);
149 
150         // Initial orbit
151         final double a = 24396159; // semi major axis in meters
152         final double e = 0.01; // eccentricity
153         final double i = FastMath.toRadians(47.); // inclination
154         final double omega = FastMath.toRadians(180); // perigee argument
155         final double raan = FastMath.toRadians(261); // right ascention of ascending node
156         final double lM = 0; // mean anomaly
157         orbit = new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.TRUE, inertialFrame, initDate, mu);
158     }
159 
160     @Test
161     void testClone() {
162 
163         // Given
164         final Orbit orbit = new CartesianOrbit(new PVCoordinates(
165                 new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0),
166                 new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(),
167                 new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU);
168         final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(5, 0);
169 
170         final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit, harmonicsProvider,
171                 PositionAngleType.MEAN, 10.0, 1.0e-8);
172         builder.getPropagationParametersDrivers().getDrivers().forEach(driver -> driver.setSelected(true));
173 
174         // When
175         final BrouwerLyddanePropagatorBuilder copyBuilder = builder.clone();
176 
177         // Then
178         assertPropagatorBuilderIsACopy(builder, copyBuilder);
179         Assertions.assertEquals(builder.getM2Value(), copyBuilder.getM2Value());
180         Assertions.assertTrue(builder.getPropagationParametersDrivers().getDrivers().get(0).isSelected());
181         Assertions.assertTrue(copyBuilder.getPropagationParametersDrivers().getDrivers().get(0).isSelected());
182     }
183 
184     /** Test for issue #1741.
185      * <p>This test checks that orbital drivers in cloned propagator builders
186      * ain't at the same physical address, i.e. that they're not linked anymore.</p>
187      */
188     @Test
189     void testIssue1741() {
190 
191         // Given
192         final Orbit orbit = new CartesianOrbit(new PVCoordinates(
193                         new Vector3D(Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS + 400000, 0, 0),
194                         new Vector3D(10, 7668.6, 3)), FramesFactory.getGCRF(),
195                                                new AbsoluteDate(), Constants.EIGEN5C_EARTH_MU);
196         final UnnormalizedSphericalHarmonicsProvider harmonicsProvider = GravityFieldFactory.getUnnormalizedProvider(5, 0);
197 
198         final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(orbit, harmonicsProvider,
199                                                                                             PositionAngleType.MEAN, 10.0, 1.0e-8);
200         builder.getPropagationParametersDrivers().getDrivers().forEach(driver -> driver.setSelected(true));
201 
202         // When
203         final BrouwerLyddanePropagatorBuilder copyBuilder = builder.clone();
204 
205         // Change orbit of the copied builder
206         final TimeStampedPVCoordinates modifiedPv = orbit.shiftedBy(3600.).getPVCoordinates();
207         copyBuilder.resetOrbit(new CartesianOrbit(modifiedPv, orbit.getFrame(), orbit.getDate(), orbit.getMu()));
208 
209         // Then
210         // Original builder should still have original orbit
211         final PVCoordinates originalPv = orbit.getPVCoordinates();
212         final PVCoordinates initialPv = builder.createInitialOrbit().getPVCoordinates();
213         final double dP = originalPv.getPosition().distance(initialPv.getPosition());
214         final double dV = originalPv.getVelocity().distance(initialPv.getVelocity());
215         final double dA = originalPv.getAcceleration().distance(initialPv.getAcceleration());
216         Assertions.assertEquals(0., dP, 0.);
217         Assertions.assertEquals(0., dV, 0.);
218         Assertions.assertEquals(0., dA, 0.);
219     }
220 }