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 java.io.File;
20  import java.io.IOException;
21  import java.net.URISyntaxException;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  
26  import org.hipparchus.exception.LocalizedCoreFormats;
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.Test;
30  import org.orekit.KeyValueFileParser;
31  import org.orekit.Utils;
32  import org.orekit.attitudes.AttitudeProvider;
33  import org.orekit.bodies.CelestialBody;
34  import org.orekit.bodies.OneAxisEllipsoid;
35  import org.orekit.errors.OrekitException;
36  import org.orekit.estimation.common.AbstractOrbitDetermination;
37  import org.orekit.estimation.common.ParameterKey;
38  import org.orekit.estimation.common.ResultBatchLeastSquares;
39  import org.orekit.forces.drag.DragSensitive;
40  import org.orekit.forces.gravity.potential.GravityFieldFactory;
41  import org.orekit.forces.gravity.potential.ICGEMFormatReader;
42  import org.orekit.forces.radiation.RadiationSensitive;
43  import org.orekit.frames.FramesFactory;
44  import org.orekit.frames.Transform;
45  import org.orekit.models.earth.atmosphere.Atmosphere;
46  import org.orekit.orbits.Orbit;
47  import org.orekit.orbits.PositionAngleType;
48  import org.orekit.propagation.analytical.tle.TLE;
49  import org.orekit.propagation.analytical.tle.TLEConstants;
50  import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm;
51  import org.orekit.propagation.conversion.ODEIntegratorBuilder;
52  import org.orekit.propagation.conversion.TLEPropagatorBuilder;
53  import org.orekit.utils.IERSConventions;
54  import org.orekit.utils.PVCoordinates;
55  import org.orekit.utils.ParameterDriver;
56  import org.orekit.utils.TimeStampedPVCoordinates;
57  
58  public class TLEOrbitDeterminationTest extends AbstractOrbitDetermination<TLEPropagatorBuilder> {
59  
60      /** Initial TLE. */
61      public TLE templateTLE;
62  
63      /** {@inheritDoc} */
64      @Override
65      protected void createGravityField(final KeyValueFileParser<ParameterKey> parser)
66          throws NoSuchElementException {
67          // TLE OD does not need gravity field
68      }
69  
70      /** {@inheritDoc} */
71      @Override
72      protected double getMu() {
73          return TLEConstants.MU;
74      }
75  
76      /** {@inheritDoc} */
77      @Override
78      protected TLEPropagatorBuilder createPropagatorBuilder(final Orbit referenceOrbit,
79                                                             final ODEIntegratorBuilder builder,
80                                                             final double positionScale) {
81          return new TLEPropagatorBuilder(templateTLE, PositionAngleType.MEAN, positionScale,
82                                          new FixedPointTleGenerationAlgorithm());
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      protected void setMass(final TLEPropagatorBuilder propagatorBuilder,
88                             final double mass) {
89          // TLE OD does not need to set mass
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      protected List<ParameterDriver> setGravity(final TLEPropagatorBuilder propagatorBuilder,
95                                                 final OneAxisEllipsoid body) {
96          return Collections.emptyList();
97  
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     protected List<ParameterDriver> setOceanTides(final TLEPropagatorBuilder propagatorBuilder,
103                                                   final IERSConventions conventions,
104                                                   final OneAxisEllipsoid body,
105                                                   final int degree, final int order) {
106         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
107                         "Ocean tides not implemented in TLE Propagator");
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     protected List<ParameterDriver> setSolidTides(final TLEPropagatorBuilder propagatorBuilder,
113                                                   final IERSConventions conventions,
114                                                   final OneAxisEllipsoid body,
115                                                   final CelestialBody[] solidTidesBodies) {
116         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
117                                   "Solid tides not implemented in TLE Propagator");
118     }
119 
120     /** {@inheritDoc} */
121     @Override
122     protected List<ParameterDriver> setThirdBody(final TLEPropagatorBuilder propagatorBuilder,
123                                                  final CelestialBody thirdBody) {
124         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
125                         "Third body not implemented in TLE Propagator");
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     protected List<ParameterDriver> setDrag(final TLEPropagatorBuilder propagatorBuilder,
131                                             final Atmosphere atmosphere, final DragSensitive spacecraft) {
132         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
133                         "Drag not implemented in TLE Propagator");
134     }
135 
136     /** {@inheritDoc} */
137     @Override
138     protected List<ParameterDriver> setSolarRadiationPressure(final TLEPropagatorBuilder propagatorBuilder, final CelestialBody sun,
139                                                               final OneAxisEllipsoid body, final RadiationSensitive spacecraft) {
140         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
141                         "SRP not implemented in TLE Propagator");
142     }
143 
144     /** {@inheritDoc} */
145     @Override
146     protected List<ParameterDriver> setAlbedoInfrared(final TLEPropagatorBuilder propagatorBuilder,
147                                                       final CelestialBody sun, final double equatorialRadius,
148                                                       final double angularResolution,
149                                                       final RadiationSensitive spacecraft) {
150         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
151                         "Albedo and infrared not implemented in TLE Propagator");
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     protected List<ParameterDriver> setRelativity(final TLEPropagatorBuilder propagatorBuilder) {
157         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
158                         "Relativity not implemented in TLE Propagator");
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     protected List<ParameterDriver> setPolynomialAcceleration(final TLEPropagatorBuilder propagatorBuilder,
164                                                               final String name, final Vector3D direction, final int degree) {
165         throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE,
166                         "Polynomial acceleration not implemented in TLE Propagator");
167     }
168 
169     /** {@inheritDoc} */
170     @Override
171     protected void setAttitudeProvider(final TLEPropagatorBuilder propagatorBuilder,
172                                        final AttitudeProvider attitudeProvider) {
173         propagatorBuilder.setAttitudeProvider(attitudeProvider);
174     }
175 
176     /** Orbit determination for GNSS satellite based on range measurements */
177     @Test
178     public void testGNSS()
179         throws URISyntaxException, IllegalArgumentException, IOException, OrekitException {
180 
181         // input in resources directory
182         final String inputPath = TLEOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/analytical/tle_od_test_GPS07.in").toURI().getPath();
183         final File input  = new File(inputPath);
184 
185         // configure Orekit data access
186         Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
187         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
188 
189         // initiate TLE (from Celestrak)
190         final String line1 = "1 32711U 08012A   16044.40566026 -.00000039  00000-0  00000+0 0  9991";
191         final String line2 = "2 32711  55.4362 301.3402 0091577 207.7302 151.8353  2.00563580 58013";
192         templateTLE = new TLE(line1, line2);
193         templateTLE.getParametersDrivers().get(0).setSelected(false);
194 
195         //orbit determination run.
196         ResultBatchLeastSquares odGNSS = runBLS(input, false);
197 
198         //test
199 
200         //definition of the accuracy for the test
201         final double distanceAccuracy = 113.46;
202 
203         //test on the convergence
204         final int numberOfIte  = 2;
205         final int numberOfEval = 3;
206         Assertions.assertEquals(numberOfIte, odGNSS.getNumberOfIteration());
207         Assertions.assertEquals(numberOfEval, odGNSS.getNumberOfEvaluation());
208 
209         //test on the estimated position (reference from file esa18836.sp3)
210         TimeStampedPVCoordinates odPV = odGNSS.getEstimatedPV();
211         final Transform transform = FramesFactory.getTEME().getTransformTo(FramesFactory.getEME2000(), odPV.getDate());
212         odPV = transform.transformPVCoordinates(odPV);
213         final Vector3D estimatedPos = odPV.getPosition();
214 
215         // Reference position from GPS ephemeris (esa18836.sp3)
216         final Vector3D refPos = new Vector3D(13848957.285213307, -22916266.10257542, -23458.8341713716);
217 
218         //test on the estimated position
219         Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), distanceAccuracy);
220 
221         //test on statistic for the range residuals
222         final long nbRange = 8211;
223         final double[] RefStatRange = { -14.448, 18.706, 0.132, 6.322 };
224         Assertions.assertEquals(nbRange, odGNSS.getRangeStat().getN());
225         Assertions.assertEquals(RefStatRange[0], odGNSS.getRangeStat().getMin(),               1.0e-3);
226         Assertions.assertEquals(RefStatRange[1], odGNSS.getRangeStat().getMax(),               1.0e-3);
227         Assertions.assertEquals(RefStatRange[2], odGNSS.getRangeStat().getMean(),              1.0e-3);
228         Assertions.assertEquals(RefStatRange[3], odGNSS.getRangeStat().getStandardDeviation(), 1.0e-3);
229 
230     }
231 
232     @Test
233     public void testLageos2()
234         throws URISyntaxException, IllegalArgumentException, IOException, OrekitException {
235 
236         // input in resources directory
237         final String inputPath = TLEOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/tle_od_test_Lageos2.in").toURI().getPath();
238         final File input  = new File(inputPath);
239 
240         // configure Orekit data access
241         Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format");
242         GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true));
243 
244         // initiate TLE
245         final String line1 = "1 22195U 92070B   16045.51027931 -.00000009  00000-0  00000+0 0  9990";
246         final String line2 = "2 22195  52.6508 132.9147 0137738 336.2706   1.6348  6.47294052551192";
247         templateTLE = new TLE(line1, line2);
248         templateTLE.getParametersDrivers().get(0).setSelected(false);
249 
250         //orbit determination run.
251         ResultBatchLeastSquares odLageos2 = runBLS(input, false);
252 
253         //test
254         //definition of the accuracy for the test
255         final double distanceAccuracy = 212.82;
256         final double velocityAccuracy = 6.17e-2;
257 
258         //test on the convergence
259         final int numberOfIte  = 4;
260         final int numberOfEval = 4;
261 
262         Assertions.assertEquals(numberOfIte, odLageos2.getNumberOfIteration());
263         Assertions.assertEquals(numberOfEval, odLageos2.getNumberOfEvaluation());
264 
265         //test on the estimated position and velocity
266         PVCoordinates odPV = odLageos2.getEstimatedPV();
267         final Transform transform = FramesFactory.getTEME().getTransformTo(FramesFactory.getEME2000(), templateTLE.getDate());
268         odPV = transform.transformPVCoordinates(odPV);
269         final Vector3D estimatedPos = odPV.getPosition();
270         final Vector3D estimatedVel = odPV.getVelocity();
271 
272         final Vector3D refPos = new Vector3D(-5532131.956902, 10025696.592156, -3578940.040009);
273         final Vector3D refVel = new Vector3D(-3871.275109, -607.880985, 4280.972530);
274         Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), distanceAccuracy);
275         Assertions.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), velocityAccuracy);
276 
277         //test on statistic for the range residuals
278         final long nbRange = 95;
279         final double[] RefStatRange = { -67.331, 79.823, 6.668E-8, 32.296 };
280         Assertions.assertEquals(nbRange, odLageos2.getRangeStat().getN());
281         Assertions.assertEquals(RefStatRange[0], odLageos2.getRangeStat().getMin(),               1.0e-3);
282         Assertions.assertEquals(RefStatRange[1], odLageos2.getRangeStat().getMax(),               1.0e-3);
283         Assertions.assertEquals(RefStatRange[2], odLageos2.getRangeStat().getMean(),              1.0e-3);
284         Assertions.assertEquals(RefStatRange[3], odLageos2.getRangeStat().getStandardDeviation(), 1.0e-3);
285 
286     }
287 
288 }
289