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.estimation.leastsquares;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.Test;
23  import org.orekit.KeyValueFileParser;
24  import org.orekit.Utils;
25  import org.orekit.attitudes.AttitudeProvider;
26  import org.orekit.bodies.CelestialBody;
27  import org.orekit.bodies.OneAxisEllipsoid;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.estimation.common.AbstractOrbitDetermination;
30  import org.orekit.estimation.common.ParameterKey;
31  import org.orekit.estimation.common.ResultSequentialBatchLeastSquares;
32  import org.orekit.forces.ForceModel;
33  import org.orekit.forces.drag.DragForce;
34  import org.orekit.forces.drag.DragSensitive;
35  import org.orekit.forces.empirical.AccelerationModel;
36  import org.orekit.forces.empirical.ParametricAcceleration;
37  import org.orekit.forces.empirical.PolynomialAccelerationModel;
38  import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
39  import org.orekit.forces.gravity.OceanTides;
40  import org.orekit.forces.gravity.Relativity;
41  import org.orekit.forces.gravity.SolidTides;
42  import org.orekit.forces.gravity.ThirdBodyAttraction;
43  import org.orekit.forces.gravity.potential.GravityFieldFactory;
44  import org.orekit.forces.gravity.potential.ICGEMFormatReader;
45  import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
46  import org.orekit.forces.radiation.KnockeRediffusedForceModel;
47  import org.orekit.forces.radiation.RadiationSensitive;
48  import org.orekit.forces.radiation.SolarRadiationPressure;
49  import org.orekit.models.earth.atmosphere.Atmosphere;
50  import org.orekit.orbits.Orbit;
51  import org.orekit.orbits.PositionAngleType;
52  import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
53  import org.orekit.propagation.conversion.ODEIntegratorBuilder;
54  import org.orekit.time.TimeScalesFactory;
55  import org.orekit.utils.IERSConventions;
56  import org.orekit.utils.ParameterDriver;
57  
58  import java.io.File;
59  import java.io.IOException;
60  import java.net.URISyntaxException;
61  import java.text.ParseException;
62  import java.util.List;
63  import java.util.NoSuchElementException;
64  
65  public class NumericalSequentialOrbitDeterminationTest extends AbstractOrbitDetermination<NumericalPropagatorBuilder> {
66  
67      /** Gravity field. */
68      private NormalizedSphericalHarmonicsProvider gravityField;
69  
70      /** {@inheritDoc} */
71      @Override
72      protected void createGravityField(final KeyValueFileParser<ParameterKey> parser)
73          throws NoSuchElementException {
74  
75          final int degree = parser.getInt(ParameterKey.CENTRAL_BODY_DEGREE);
76          final int order  = FastMath.min(degree, parser.getInt(ParameterKey.CENTRAL_BODY_ORDER));
77          gravityField = GravityFieldFactory.getNormalizedProvider(degree, order);
78  
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      protected double getMu() {
84          return gravityField.getMu();
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      protected NumericalPropagatorBuilder createPropagatorBuilder(final Orbit referenceOrbit,
90                                                                   final ODEIntegratorBuilder builder,
91                                                                   final double positionScale) {
92          return new NumericalPropagatorBuilder(referenceOrbit, builder, PositionAngleType.MEAN,
93                                                positionScale);
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      protected void setMass(final NumericalPropagatorBuilder propagatorBuilder,
99                                  final double mass) {
100         propagatorBuilder.setMass(mass);
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     protected List<ParameterDriver> setGravity(final NumericalPropagatorBuilder propagatorBuilder,
106                                                final OneAxisEllipsoid body) {
107         final ForceModel gravityModel = new HolmesFeatherstoneAttractionModel(body.getBodyFrame(), gravityField);
108         propagatorBuilder.addForceModel(gravityModel);
109         return gravityModel.getParametersDrivers();
110     }
111 
112     /** {@inheritDoc} */
113     @Override
114     protected List<ParameterDriver> setOceanTides(final NumericalPropagatorBuilder propagatorBuilder,
115                                                   final IERSConventions conventions,
116                                                   final OneAxisEllipsoid body,
117                                                   final int degree, final int order) {
118         final ForceModel tidesModel = new OceanTides(body.getBodyFrame(),
119                                                      gravityField.getAe(), gravityField.getMu(),
120                                                      degree, order, conventions,
121                                                      TimeScalesFactory.getUT1(conventions, true));
122         propagatorBuilder.addForceModel(tidesModel);
123         return tidesModel.getParametersDrivers();
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     protected List<ParameterDriver> setSolidTides(final NumericalPropagatorBuilder propagatorBuilder,
129                                                   final IERSConventions conventions,
130                                                   final OneAxisEllipsoid body,
131                                                   final CelestialBody[] solidTidesBodies) {
132         final ForceModel tidesModel = new SolidTides(body.getBodyFrame(),
133                                                      gravityField.getAe(), gravityField.getMu(),
134                                                      gravityField.getTideSystem(), conventions,
135                                                      TimeScalesFactory.getUT1(conventions, true),
136                                                      solidTidesBodies);
137         propagatorBuilder.addForceModel(tidesModel);
138         return tidesModel.getParametersDrivers();
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     protected List<ParameterDriver> setThirdBody(final NumericalPropagatorBuilder propagatorBuilder,
144                                                  final CelestialBody thirdBody) {
145         final ForceModel thirdBodyModel = new ThirdBodyAttraction(thirdBody);
146         propagatorBuilder.addForceModel(thirdBodyModel);
147         return thirdBodyModel.getParametersDrivers();
148     }
149 
150     /** {@inheritDoc} */
151     @Override
152     protected List<ParameterDriver> setDrag(final NumericalPropagatorBuilder propagatorBuilder,
153                                             final Atmosphere atmosphere, final DragSensitive spacecraft) {
154         final ForceModel dragModel = new DragForce(atmosphere, spacecraft);
155         propagatorBuilder.addForceModel(dragModel);
156         return dragModel.getParametersDrivers();
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     protected List<ParameterDriver> setSolarRadiationPressure(final NumericalPropagatorBuilder propagatorBuilder, final CelestialBody sun,
162                                                               final OneAxisEllipsoid body, final RadiationSensitive spacecraft) {
163         final ForceModel srpModel = new SolarRadiationPressure(sun, body, spacecraft);
164         propagatorBuilder.addForceModel(srpModel);
165         return srpModel.getParametersDrivers();
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     protected List<ParameterDriver> setAlbedoInfrared(final NumericalPropagatorBuilder propagatorBuilder,
171                                                       final CelestialBody sun, final double equatorialRadius,
172                                                       final double angularResolution,
173                                                       final RadiationSensitive spacecraft) {
174         final ForceModel albedoIR = new KnockeRediffusedForceModel(sun, spacecraft, equatorialRadius, angularResolution);
175         propagatorBuilder.addForceModel(albedoIR);
176         return albedoIR.getParametersDrivers();
177     }
178 
179     /** {@inheritDoc} */
180     @Override
181     protected List<ParameterDriver> setRelativity(final NumericalPropagatorBuilder propagatorBuilder) {
182         final ForceModel relativityModel = new Relativity(gravityField.getMu());
183         propagatorBuilder.addForceModel(relativityModel);
184         return relativityModel.getParametersDrivers();
185     }
186 
187     /** {@inheritDoc} */
188     @Override
189     protected List<ParameterDriver> setPolynomialAcceleration(final NumericalPropagatorBuilder propagatorBuilder,
190                                                              final String name, final Vector3D direction, final int degree) {
191         final AccelerationModel accModel = new PolynomialAccelerationModel(name, null, degree);
192         final ForceModel polynomialModel = new ParametricAcceleration(direction, true, accModel);
193         propagatorBuilder.addForceModel(polynomialModel);
194         return polynomialModel.getParametersDrivers();
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     protected void setAttitudeProvider(final NumericalPropagatorBuilder propagatorBuilder,
200                                        final AttitudeProvider attitudeProvider) {
201         propagatorBuilder.setAttitudeProvider(attitudeProvider);
202     }
203 
204     @Test
205     // Orbit determination for Lageos2 based on position measurements
206     public void testLageos2()
207         throws URISyntaxException, IllegalArgumentException, IOException,
208                OrekitException, ParseException {
209 
210         // input in resources directory
211         final String inputPathModel = NumericalSequentialOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/sequential_od_test_Lageos.in").toURI().getPath();
212         final File inputModel  = new File(inputPathModel);
213 
214         // configure Orekit data access
215         Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
216         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
217 
218 
219         //orbit determination run.
220         final boolean print = false;
221         ResultSequentialBatchLeastSquares odLageos2 = runSequentialBLS(inputModel, print);
222 
223         //test
224         //definition of the accuracy for the test
225         final double distanceAccuracyBLS = 0.13;
226         final double distanceAccuracySBLS = 0.06;
227 
228         //test on the convergence BLS
229         final int numberOfIte  = 4;
230         final int numberOfEval = 4;
231 
232         Assertions.assertEquals(numberOfIte, odLageos2.getNumberOfIteration());
233         Assertions.assertEquals(numberOfEval, odLageos2.getNumberOfEvaluation());
234 
235         //test on the convergence SBLS
236         final int numberOfIteS  = 5;
237         final int numberOfEvalS = 5;
238 
239         Assertions.assertEquals(numberOfIteS, odLageos2.getNumberOfIterationSequential());
240         Assertions.assertEquals(numberOfEvalS, odLageos2.getNumberOfEvaluationSequential());
241 
242         //test on the estimated position
243         final Vector3D estimatedPos = odLageos2.getEstimatedPV().getPosition();
244         final Vector3D estimatedPosSequential = odLageos2.getEstimatedPVSequential().getPosition();
245 
246         // Ref position from "sequential_least_squares_lageos2_cpf_160213_5441.sgf"
247         // Position in the file is given in ITRF. It is converted in EME2000 here
248         final Vector3D refPos = new Vector3D(-8470598.591629019, -656367.1112940479, 8683152.425956512);
249 
250         Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), distanceAccuracyBLS);
251         Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPosSequential), distanceAccuracySBLS);
252 
253        }
254 }