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.analytical.tle;
18  
19  import java.io.BufferedReader;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.InputStreamReader;
23  import java.text.ParseException;
24  
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.hipparchus.util.CombinatoricsUtils;
27  import org.hipparchus.util.FastMath;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.DisplayName;
31  import org.junit.jupiter.api.Test;
32  import org.orekit.Utils;
33  import org.orekit.errors.OrekitException;
34  import org.orekit.errors.OrekitMessages;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.propagation.conversion.osc2mean.FixedPointConverter;
37  import org.orekit.propagation.conversion.osc2mean.OsculatingToMeanConverter;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.DateComponents;
40  import org.orekit.time.TimeComponents;
41  import org.orekit.time.TimeOffset;
42  import org.orekit.time.TimeScalesFactory;
43  import org.orekit.utils.Constants;
44  import org.orekit.utils.PVCoordinates;
45  
46  
47  public class TLETest {
48  
49      @Test
50      public void testTLEFormat() {
51  
52          String line1 = "1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20";
53          String line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62";
54  
55          Assertions.assertTrue(TLE.isFormatOK(line1, line2));
56  
57          TLE tle = new TLE(line1, line2);
58          Assertions.assertEquals(27421, tle.getSatelliteNumber(), 0);
59          Assertions.assertEquals(2002, tle.getLaunchYear());
60          Assertions.assertEquals(21, tle.getLaunchNumber());
61          Assertions.assertEquals("A", tle.getLaunchPiece());
62          Assertions.assertEquals(-0.0089879, tle.getBStar(), 0);
63          Assertions.assertEquals(0, tle.getEphemerisType());
64          Assertions.assertEquals(98.749, FastMath.toDegrees(tle.getI()), 1e-10);
65          Assertions.assertEquals(199.5121, FastMath.toDegrees(tle.getRaan()), 1e-10);
66          Assertions.assertEquals(0.0001333, tle.getE(), 1e-10);
67          Assertions.assertEquals(133.9522, FastMath.toDegrees(tle.getPerigeeArgument()), 1e-10);
68          Assertions.assertEquals(226.1918, FastMath.toDegrees(tle.getMeanAnomaly()), 1e-10);
69          Assertions.assertEquals(14.26113993, tle.getMeanMotion() * Constants.JULIAN_DAY / (2 * FastMath.PI), 0);
70          Assertions.assertEquals(7182888.814633288, tle.computeSemiMajorAxis(), 1e-10);
71          Assertions.assertEquals(tle.getRevolutionNumberAtEpoch(), 6, 0);
72          Assertions.assertEquals(tle.getElementNumber(), 2 , 0);
73  
74          line1 = "1 T7421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    28";
75          line2 = "2 T7421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    60";
76          Assertions.assertTrue(TLE.isFormatOK(line1, line2));
77  
78          tle = new TLE(line1, line2);
79          Assertions.assertEquals(277421, tle.getSatelliteNumber(), 0);
80  
81          line1 = "1 I7421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    28";
82          line2 = "2 I7421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    60";
83          Assertions.assertFalse(TLE.isFormatOK(line1, line2));
84          try {
85              new TLE(line1, line2);
86              Assertions.fail("an exception should have been thrown");
87          } catch (NumberFormatException nfe) {
88              // expected
89          }
90  
91          line1 = "1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20";
92          line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14*26113993    62";
93          Assertions.assertFalse(TLE.isFormatOK(line1, line2));
94  
95          line1 = "1 27421 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20";
96          line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62";
97          Assertions.assertFalse(TLE.isFormatOK(line1, line2));
98  
99          line1 = "1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20";
100         line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 10006113993    62";
101         Assertions.assertFalse(TLE.isFormatOK(line1, line2));
102 
103         line1 = "1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879 2 0    20";
104         line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62";
105         Assertions.assertFalse(TLE.isFormatOK(line1, line2));
106     }
107 
108     @Test
109     public void testIssue196() {
110 
111         String line1A = "1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20";
112         String line1B = "1 27421U 02021A   02124.48976499  -.0002147  00000-0 -89879-2 0    20";
113         String line2 = "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62";
114 
115         Assertions.assertTrue(TLE.isFormatOK(line1A, line2));
116         TLE tleA = new TLE(line1A, line2);
117         Assertions.assertTrue(TLE.isFormatOK(line1B, line2));
118         TLE tleB = new TLE(line1B, line2);
119         Assertions.assertEquals(tleA.getSatelliteNumber(),         tleB.getSatelliteNumber(), 0);
120         Assertions.assertEquals(tleA.getLaunchYear(),              tleB.getLaunchYear());
121         Assertions.assertEquals(tleA.getLaunchNumber(),            tleB.getLaunchNumber());
122         Assertions.assertEquals(tleA.getLaunchPiece(),             tleB.getLaunchPiece());
123         Assertions.assertEquals(tleA.getBStar(),                   tleB.getBStar(), 0);
124         Assertions.assertEquals(tleA.getEphemerisType(),           tleB.getEphemerisType());
125         Assertions.assertEquals(tleA.getI(),                       tleB.getI(), 1e-10);
126         Assertions.assertEquals(tleA.getRaan(),                    tleB.getRaan(), 1e-10);
127         Assertions.assertEquals(tleA.getE(),                       tleB.getE(), 1e-10);
128         Assertions.assertEquals(tleA.getPerigeeArgument(),         tleB.getPerigeeArgument(), 1e-10);
129         Assertions.assertEquals(tleA.getMeanAnomaly(),             tleB.getMeanAnomaly(), 1e-10);
130         Assertions.assertEquals(tleA.getMeanMotion(),              tleB.getMeanMotion(), 0);
131         Assertions.assertEquals(tleA.getRevolutionNumberAtEpoch(), tleB.getRevolutionNumberAtEpoch(), 0);
132         Assertions.assertEquals(tleA.getElementNumber(),           tleB.getElementNumber(), 0);
133 
134     }
135 
136     @Test
137     public void testSymmetry() {
138         checkSymmetry("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
139                       "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
140         checkSymmetry("1 31928U 98067BA  08269.84884916  .00114257  17652-4  13615-3 0  4412",
141                       "2 31928  51.6257 175.4142 0001703  41.9031 318.2112 16.08175249 68368");
142         checkSymmetry("1 T7421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    28",
143                       "2 T7421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    60");
144     }
145 
146     private void checkSymmetry(String line1, String line2) {
147         TLE tleRef = new TLE(line1, line2);
148         TLE tle = new TLE(tleRef.getSatelliteNumber(), tleRef.getClassification(),
149                           tleRef.getLaunchYear(), tleRef.getLaunchNumber(), tleRef.getLaunchPiece(),
150                           tleRef.getEphemerisType(), tleRef.getElementNumber(), tleRef.getDate(),
151                           tleRef.getMeanMotion(), tleRef.getMeanMotionFirstDerivative(),
152                           tleRef.getMeanMotionSecondDerivative(), tleRef.getE(), tleRef.getI(),
153                           tleRef.getPerigeeArgument(), tleRef.getRaan(), tleRef.getMeanAnomaly(),
154                           tleRef.getRevolutionNumberAtEpoch(), tleRef.getBStar());
155         Assertions.assertEquals(line1, tle.getLine1());
156         Assertions.assertEquals(line2, tle.getLine2());
157     }
158 
159     @Test
160     public void testBug74() {
161         checkSymmetry("1 00001U 00001A   12026.45833333 2.94600864  39565-9  16165-7 1    12",
162                       "2 00001 127.0796 254.4522 0000000 224.9662   0.4817  0.00000000    11");
163     }
164 
165     @Test
166     public void testBug77() {
167         checkSymmetry("1 05555U 71086J   12026.96078249 -.00000004  00001-9  01234-9 0  9082",
168                       "2 05555  74.0161 228.9750 0075476 328.9888  30.6709 12.26882470804545");
169     }
170 
171     @Test
172     public void testDirectConstruction() {
173         TLE tleA = new TLE(5555, 'U', 1971, 86, "J", 0, 908,
174                            new AbsoluteDate(new DateComponents(2012, 26),
175                                             new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
176                                             TimeScalesFactory.getUTC()),
177                            taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
178                            0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
179                            FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
180         TLE tleB =  new TLE("1 05555U 71086J   12026.96078249 -.00000004  00001-9  01234-9 0  9082",
181                             "2 05555  74.0161 228.9750 0075476 328.9888  30.6709 12.26882470804545");
182         Assertions.assertEquals(tleA.getSatelliteNumber(),         tleB.getSatelliteNumber(), 0);
183         Assertions.assertEquals(tleA.getLaunchYear(),              tleB.getLaunchYear());
184         Assertions.assertEquals(tleA.getLaunchNumber(),            tleB.getLaunchNumber());
185         Assertions.assertEquals(tleA.getLaunchPiece(),             tleB.getLaunchPiece());
186         Assertions.assertEquals(tleA.getBStar(),                   tleB.getBStar(), 0);
187         Assertions.assertEquals(tleA.getEphemerisType(),           tleB.getEphemerisType());
188         Assertions.assertEquals(tleA.getI(),                       tleB.getI(), 1e-10);
189         Assertions.assertEquals(tleA.getRaan(),                    tleB.getRaan(), 1e-10);
190         Assertions.assertEquals(tleA.getE(),                       tleB.getE(), 1e-10);
191         Assertions.assertEquals(tleA.getPerigeeArgument(),         tleB.getPerigeeArgument(), 1e-10);
192         Assertions.assertEquals(tleA.getMeanAnomaly(),             tleB.getMeanAnomaly(), 1e-10);
193         Assertions.assertEquals(tleA.getMeanMotion(),              tleB.getMeanMotion(), 0);
194         Assertions.assertEquals(tleA.getRevolutionNumberAtEpoch(), tleB.getRevolutionNumberAtEpoch(), 0);
195         Assertions.assertEquals(tleA.getElementNumber(),           tleB.getElementNumber(), 0);
196     }
197 
198     @Test
199     public void testGenerateAlpha5() {
200         TLE tle = new TLE(339999, 'U', 1971, 86, "J", 0, 908,
201                           new AbsoluteDate(new DateComponents(2012, 26),
202                                            new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
203                                            TimeScalesFactory.getUTC()),
204                           taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
205                           0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
206                           FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
207         Assertions.assertEquals("1 Z9999U 71086J   12026.96078249 -.00000004  00001-9  01234-9 0  9088", tle.getLine1());
208         Assertions.assertEquals("2 Z9999  74.0161 228.9750 0075476 328.9888  30.6709 12.26882470804541", tle.getLine2());
209     }
210 
211     @Test
212     public void testBug77TooLargeSecondDerivative() {
213         try {
214             TLE tle = new TLE(5555, 'U', 1971, 86, "J", 0, 908,
215                               new AbsoluteDate(new DateComponents(2012, 26),
216                                                new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
217                                                TimeScalesFactory.getUTC()),
218                               taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.99999e11, 3),
219                               0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
220                               FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
221             tle.getLine1();
222             Assertions.fail("an exception should have been thrown");
223         } catch (OrekitException oe) {
224             Assertions.assertEquals(OrekitMessages.TLE_INVALID_PARAMETER, oe.getSpecifier());
225             Assertions.assertEquals(5555, ((Integer) oe.getParts()[0]).intValue());
226             Assertions.assertEquals("meanMotionSecondDerivative", oe.getParts()[1]);
227         }
228     }
229 
230     @Test
231     public void testBug77TooLargeBStar() {
232         try {
233             TLE tle = new TLE(5555, 'U', 1971, 86, "J", 0, 908,
234                               new AbsoluteDate(new DateComponents(2012, 26),
235                                                new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
236                                                TimeScalesFactory.getUTC()),
237                               taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
238                               0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
239                               FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.99999e11);
240             tle.getLine1();
241             Assertions.fail("an exception should have been thrown");
242         } catch (OrekitException oe) {
243             Assertions.assertEquals(OrekitMessages.TLE_INVALID_PARAMETER, oe.getSpecifier());
244             Assertions.assertEquals(5555, ((Integer) oe.getParts()[0]).intValue());
245             Assertions.assertEquals("B*", oe.getParts()[1]);
246         }
247     }
248 
249     @Test
250     public void testBug77TooLargeEccentricity() {
251         try {
252             TLE tle = new TLE(5555, 'U', 1971, 86, "J", 0, 908,
253                               new AbsoluteDate(new DateComponents(2012, 26),
254                                                new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
255                                                TimeScalesFactory.getUTC()),
256                               taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
257                               1.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
258                               FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
259             tle.getLine2();
260             Assertions.fail("an exception should have been thrown");
261         } catch (OrekitException oe) {
262             Assertions.assertEquals(OrekitMessages.TLE_INVALID_PARAMETER, oe.getSpecifier());
263             Assertions.assertEquals(5555, ((Integer) oe.getParts()[0]).intValue());
264             Assertions.assertEquals("eccentricity", oe.getParts()[1]);
265         }
266     }
267 
268     @Test
269     public void testBug77TooLargeSatelliteNumber1() {
270         try {
271             TLE tle = new TLE(340000, 'U', 1971, 86, "J", 0, 908,
272                               new AbsoluteDate(new DateComponents(2012, 26),
273                                                new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
274                                                TimeScalesFactory.getUTC()),
275                               taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
276                               0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
277                               FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
278             tle.getLine1();
279             Assertions.fail("an exception should have been thrown");
280         } catch (OrekitException oe) {
281             Assertions.assertEquals(OrekitMessages.TLE_INVALID_PARAMETER, oe.getSpecifier());
282             Assertions.assertEquals(340000, ((Integer) oe.getParts()[0]).intValue());
283             Assertions.assertEquals("satelliteNumber-1", oe.getParts()[1]);
284         }
285     }
286 
287     @Test
288     public void testBug77TooLargeSatelliteNumber2() {
289         try {
290             TLE tle = new TLE(1000000, 'U', 1971, 86, "J", 0, 908,
291                               new AbsoluteDate(new DateComponents(2012, 26),
292                                                new TimeComponents(0.96078249 * Constants.JULIAN_DAY),
293                                                TimeScalesFactory.getUTC()),
294                               taylorConvert(12.26882470, 1), taylorConvert(-0.00000004, 2), taylorConvert(0.00001e-9, 3),
295                               0.0075476, FastMath.toRadians(74.0161), FastMath.toRadians(328.9888),
296                               FastMath.toRadians(228.9750), FastMath.toRadians(30.6709), 80454, 0.01234e-9);
297             tle.getLine2();
298             Assertions.fail("an exception should have been thrown");
299         } catch (OrekitException oe) {
300             Assertions.assertEquals(OrekitMessages.TLE_INVALID_PARAMETER, oe.getSpecifier());
301             Assertions.assertEquals(1000000, ((Integer) oe.getParts()[0]).intValue());
302             Assertions.assertEquals("satelliteNumber-2", oe.getParts()[1]);
303         }
304     }
305 
306     final double taylorConvert(final double m, final int n) {
307         // convert one term of TLE mean motion Taylor series
308         return  m * 2 * FastMath.PI * CombinatoricsUtils.factorial(n) / FastMath.pow(Constants.JULIAN_DAY, n);
309     }
310 
311     @Test
312     public void testDifferentSatNumbers() {
313         Assertions.assertThrows(OrekitException.class, () -> {
314             new TLE("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
315                     "2 27422  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
316         });
317     }
318 
319     @Test
320     public void testChecksumOK() {
321         TLE.isFormatOK("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
322                        "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
323     }
324 
325     @Test
326     public void testWrongChecksum1() {
327         try {
328             TLE.isFormatOK("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    21",
329                            "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
330             Assertions.fail("an exception should have been thrown");
331         } catch (OrekitException oe) {
332             Assertions.assertEquals(OrekitMessages.TLE_CHECKSUM_ERROR, oe.getSpecifier());
333             Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue());
334             Assertions.assertEquals("0", oe.getParts()[1]);
335             Assertions.assertEquals("1", oe.getParts()[2]);
336             Assertions.assertEquals("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    21",
337                                 oe.getParts()[3]);
338         }
339     }
340 
341     @Test
342     public void testWrongChecksum2() {
343         try {
344             TLE.isFormatOK("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
345                            "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    61");
346             Assertions.fail("an exception should have been thrown");
347         } catch (OrekitException oe) {
348             Assertions.assertEquals(OrekitMessages.TLE_CHECKSUM_ERROR, oe.getSpecifier());
349             Assertions.assertEquals(2, ((Integer) oe.getParts()[0]).intValue());
350             Assertions.assertEquals("2", oe.getParts()[1]);
351             Assertions.assertEquals("1", oe.getParts()[2]);
352             Assertions.assertEquals("2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    61",
353                                 oe.getParts()[3]);
354         }
355     }
356 
357     @Test
358     public void testSatCodeCompliance() throws IOException, OrekitException, ParseException {
359 
360         BufferedReader rEntry = null;
361         BufferedReader rResults = null;
362 
363         InputStream inEntry =
364             TLETest.class.getResourceAsStream("/tle/extrapolationTest-data/SatCode-entry");
365         rEntry = new BufferedReader(new InputStreamReader(inEntry));
366 
367         try {
368             InputStream inResults =
369                 TLETest.class.getResourceAsStream("/tle/extrapolationTest-data/SatCode-results");
370             rResults = new BufferedReader(new InputStreamReader(inResults));
371 
372             try {
373                 double cumulated = 0; // sum of all differences between test cases and OREKIT results
374                 boolean stop = false;
375 
376                 String rline = rResults.readLine();
377 
378                 while (!stop) {
379                     if (rline == null) break;
380 
381                     String[] title = rline.split(" ");
382 
383                     if (title[0].matches("r")) {
384 
385                         String eline;
386                         int count = 0;
387                         String[] header = new String[4];
388                         for (eline = rEntry.readLine(); (eline != null) && (eline.charAt(0)=='#'); eline = rEntry.readLine()) {
389                             header[count++] = eline;
390                         }
391                         String line1 = eline;
392                         String line2 = rEntry.readLine();
393                         Assertions.assertTrue(TLE.isFormatOK(line1, line2));
394 
395                         TLE tle = new TLE(line1, line2);
396 
397                         int satNum = Integer.parseInt(title[1]);
398                         Assertions.assertEquals(satNum, tle.getSatelliteNumber());
399                         TLEPropagator ex = TLEPropagator.selectExtrapolator(tle);
400 
401                         for (rline = rResults.readLine(); (rline!=null)&&(rline.charAt(0)!='r'); rline = rResults.readLine()) {
402 
403                             String[] data = rline.split(" ");
404                             double minFromStart = Double.parseDouble(data[0]);
405                             double pX = 1000*Double.parseDouble(data[1]);
406                             double pY = 1000*Double.parseDouble(data[2]);
407                             double pZ = 1000*Double.parseDouble(data[3]);
408                             double vX = 1000*Double.parseDouble(data[4]);
409                             double vY = 1000*Double.parseDouble(data[5]);
410                             double vZ = 1000*Double.parseDouble(data[6]);
411                             Vector3D testPos = new Vector3D(pX, pY, pZ);
412                             Vector3D testVel = new Vector3D(vX, vY, vZ);
413 
414                             AbsoluteDate date = tle.getDate().shiftedBy(minFromStart * 60);
415                             PVCoordinates results = ex.getPVCoordinates(date);
416                             double normDifPos = testPos.subtract(results.getPosition()).getNorm();
417                             double normDifVel = testVel.subtract(results.getVelocity()).getNorm();
418 
419                             cumulated += normDifPos;
420                             Assertions.assertEquals(0, normDifPos, 2e-3);
421                             Assertions.assertEquals(0, normDifVel, 1e-5);
422 
423 
424                         }
425                     }
426                 }
427                 Assertions.assertEquals(0, cumulated, 0.026);
428             } finally {
429                 if (rResults != null) {
430                     rResults.close();
431                 }
432             }
433         } finally {
434             if (rEntry != null) {
435                 rEntry.close();
436             }
437         }
438     }
439 
440     @Test
441     public void testZeroInclination() {
442         TLE tle = new TLE("1 26451U 00043A   10130.13784012 -.00000276  00000-0  10000-3 0  3866",
443                           "2 26451 000.0000 266.1044 0001893 160.7642 152.5985 01.00271160 35865");
444         TLEPropagator propagator = TLEPropagator.selectExtrapolator(tle);
445         PVCoordinates pv = propagator.propagate(tle.getDate().shiftedBy(100)).getPVCoordinates();
446         Assertions.assertEquals(42171546.979560345, pv.getPosition().getNorm(), 1.0e-3);
447         Assertions.assertEquals(3074.1890089357994, pv.getVelocity().getNorm(), 1.0e-6);
448     }
449 
450     @Test
451     public void testSymmetryAfterLeapSecondIntroduction() {
452         checkSymmetry("1 34602U 09013A   12187.35117436  .00002472  18981-5  42406-5 0  9995",
453                       "2 34602  96.5991 210.0210 0006808 112.8142 247.3865 16.06008103193411");
454     }
455 
456     @Test
457     public void testOldTLE() {
458         String line1 = "1 15427U          85091.94293084 0.00000051  00000+0  32913-4 0   179";
459         String line2 = "2 15427  98.9385  46.0219 0015502 321.4354  38.5705 14.11363211 15580";
460         Assertions.assertTrue(TLE.isFormatOK(line1, line2));
461         TLE tle = new TLE(line1, line2);
462         Assertions.assertEquals(15427, tle.getSatelliteNumber());
463         Assertions.assertEquals(0.00000051,
464                             tle.getMeanMotionFirstDerivative() * Constants.JULIAN_DAY * Constants.JULIAN_DAY / (4 * FastMath.PI),
465                             1.0e-15);
466     }
467 
468     @Test
469     public void testEqualTLE() {
470         TLE tleA = new TLE("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
471                            "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
472         TLE tleB = new TLE("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
473                            "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
474         Assertions.assertEquals(tleA, tleB);
475         Assertions.assertEquals(tleA, tleA);
476     }
477 
478     @Test
479     public void testNonEqualTLE() {
480         TLE tleA = new TLE("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
481                 "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
482         TLE tleB = new TLE("1 05555U 71086J   12026.96078249 -.00000004  00001-9  01234-9 0  9082",
483                 "2 05555  74.0161 228.9750 0075476 328.9888  30.6709 12.26882470804545");
484         Assertions.assertNotEquals(tleA, tleB);
485         Assertions.assertNotEquals(tleA, tleA.getLine1());
486     }
487 
488     @Test
489     public void testIssue388() {
490         TLE tleRef = new TLE("1 27421U 02021A   02124.48976499 -.00021470  00000-0 -89879-2 0    20",
491                              "2 27421  98.7490 199.5121 0001333 133.9522 226.1918 14.26113993    62");
492         TLE tleOriginal = new TLE(27421, 'U', 2002, 21, "A", TLE.DEFAULT, 2,
493                                   new AbsoluteDate("2002-05-04T11:45:15.695", TimeScalesFactory.getUTC()),
494                                   FastMath.toRadians(14.26113993 * 360 / Constants.JULIAN_DAY),
495                                   FastMath.toRadians(-.00021470 * 360 * 2 / (Constants.JULIAN_DAY * Constants.JULIAN_DAY)),
496                                   FastMath.toRadians(0.0),
497                                   1.333E-4, FastMath.toRadians(98.7490),
498                                   FastMath.toRadians(133.9522), FastMath.toRadians(199.5121), FastMath.toRadians(226.1918),
499                                   6, -0.0089879);
500         Assertions.assertEquals(tleRef.getLine1(), tleOriginal.getLine1());
501         Assertions.assertEquals(tleRef.getLine2(), tleOriginal.getLine2());
502         TLE changedBStar = new TLE(27421, 'U', 2002, 21, "A", TLE.DEFAULT, 2,
503                                    new AbsoluteDate("2002-05-04T11:45:15.695", TimeScalesFactory.getUTC()),
504                                    FastMath.toRadians(14.26113993 * 360 / Constants.JULIAN_DAY),
505                                    FastMath.toRadians(-.00021470 * 360 * 2 / (Constants.JULIAN_DAY * Constants.JULIAN_DAY)),
506                                    FastMath.toRadians(0.0),
507                                    1.333E-4, FastMath.toRadians(98.7490),
508                                    FastMath.toRadians(133.9522), FastMath.toRadians(199.5121), FastMath.toRadians(226.1918),
509                                    6, 1.0e-4);
510         Assertions.assertEquals(tleRef.getLine1().replace("-89879-2", " 10000-3"), changedBStar.getLine1());
511         Assertions.assertEquals(tleRef.getLine2(), changedBStar.getLine2());
512         Assertions.assertEquals(1.0e-4, new TLE(changedBStar.getLine1(), changedBStar.getLine2()).getBStar(), 1.0e-15);
513     }
514 
515     @Test
516     public void testIssue664NegativeRaanPa() {
517         TLE tle = new TLE(99999, 'X', 2020, 42, "F", 0, 999,
518                 new AbsoluteDate("2020-01-01T01:00:00.000", TimeScalesFactory.getUTC()), 0.0011010400252833312, 0.0,
519                 0.0, 0.0016310523359516962, 1.6999188604164899, -3.219351286726724, -2.096689019811356,
520                 2.157567545975006, 1, 1e-05);
521         // Comparing with TLE strings generated in Orekit Python after forcing the RAAN
522         // and PA to the [0, 2*Pi] range
523         Assertions.assertEquals(tle.getLine1(), "1 99999X 20042F   20001.04166667  .00000000  00000-0  10000-4 0  9997");
524         Assertions.assertEquals(tle.getLine2(), "2 99999  97.3982 239.8686 0016311 175.5448 123.6195 15.14038717    18");
525     }
526 
527     @Test
528     public void testStateToTleISS() {
529 
530         // Initialize TLE
531         final TLE tleISS = new TLE("1 25544U 98067A   21035.14486477  .00001026  00000-0  26816-4 0  9998",
532                                    "2 25544  51.6455 280.7636 0002243 335.6496 186.1723 15.48938788267977");
533 
534         // TLE propagator
535         final TLEPropagator propagator = TLEPropagator.selectExtrapolator(tleISS);
536 
537         // State at TLE epoch
538         final SpacecraftState state = propagator.propagate(tleISS.getDate());
539 
540         // Osculating to mean orbit converter
541         final OsculatingToMeanConverter converter = new FixedPointConverter();
542 
543         // Convert to TLE
544         final TLE rebuilt = TLE.stateToTLE(state, tleISS, converter);
545 
546         // Verify
547         final double eps = 1.0e-7;
548         Assertions.assertEquals(tleISS.getSatelliteNumber(),         rebuilt.getSatelliteNumber());
549         Assertions.assertEquals(tleISS.getClassification(),          rebuilt.getClassification());
550         Assertions.assertEquals(tleISS.getLaunchYear(),              rebuilt.getLaunchYear());
551         Assertions.assertEquals(tleISS.getLaunchNumber(),            rebuilt.getLaunchNumber());
552         Assertions.assertEquals(tleISS.getLaunchPiece(),             rebuilt.getLaunchPiece());
553         Assertions.assertEquals(tleISS.getElementNumber(),           rebuilt.getElementNumber());
554         Assertions.assertEquals(tleISS.getRevolutionNumberAtEpoch(), rebuilt.getRevolutionNumberAtEpoch());
555         Assertions.assertEquals(tleISS.getMeanMotion(),              rebuilt.getMeanMotion(),      eps * tleISS.getMeanMotion());
556         Assertions.assertEquals(tleISS.getE(),                       rebuilt.getE(),               eps * tleISS.getE());
557         Assertions.assertEquals(tleISS.getI(),                       rebuilt.getI(),               eps * tleISS.getI());
558         Assertions.assertEquals(tleISS.getPerigeeArgument(),         rebuilt.getPerigeeArgument(), eps * tleISS.getPerigeeArgument());
559         Assertions.assertEquals(tleISS.getRaan(),                    rebuilt.getRaan(),            eps * tleISS.getRaan());
560         Assertions.assertEquals(tleISS.getMeanAnomaly(),             rebuilt.getMeanAnomaly(),     eps * tleISS.getMeanAnomaly());
561         Assertions.assertEquals(tleISS.getMeanAnomaly(),             rebuilt.getMeanAnomaly(),     eps * tleISS.getMeanAnomaly());
562         Assertions.assertEquals(tleISS.getBStar(),                   rebuilt.getBStar(),           eps * tleISS.getBStar());
563     }
564 
565     @Test
566     public void testUnknowParameter() {
567 
568         // Initialize TLE
569         final TLE tleISS = new TLE("1 25544U 98067A   21035.14486477  .00001026  00000-0  26816-4 0  9998",
570                                    "2 25544  51.6455 280.7636 0002243 335.6496 186.1723 15.48938788267977");
571 
572         try {
573             tleISS.getParameterDriver("MyWonderfulDriver");
574             Assertions.fail("an exception should have been thrown");
575         } catch (OrekitException oe) {
576             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_PARAMETER_NAME, oe.getSpecifier());
577         }
578 
579     }
580 
581     @Test
582     void roundToNextDayError() {
583         //Given
584         final AbsoluteDate tleDate = new AbsoluteDate("2022-01-01T23:59:59.99999999", TimeScalesFactory.getUTC());
585 
586         final TLE tle =
587                 new TLE(99999, 'U', 2022, 999, "A", 0, 1, tleDate, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 99, 11606 * 1e-4,
588                         TimeScalesFactory.getUTC());
589 
590         //When
591         final AbsoluteDate returnedDate = tle.getDate();
592 
593         //Then
594         // Assert that TLE class did not round the date to the next day
595         Assertions.assertEquals(tleDate, returnedDate);
596     }
597 
598 
599     @Test
600     @DisplayName("fix issue 1773 :  with Orekit 13 new date representation, TLE parsing from lines may introduce " +
601       "some attoseconds of error")
602     void testFixIssue1773() {
603         // Given
604         final String line1WithDateParsingError = "1 00005U 58002B   25047.63247525. .00000268  00000-0  35695-3 0  9999";
605         final String line2WithDateParsingError = "2 00005  34.2494  12.9875 1841261 109.2290 271.5928 10.85874828390491";
606 
607         final DateComponents dateComponents = new DateComponents(2025, 2, 16);
608         final TimeOffset timeOffset = new TimeOffset(45, TimeOffset.MICROSECOND.multiply(861600).getAttoSeconds());
609         final TimeComponents timeComponents = new TimeComponents(15, 10, timeOffset);
610         final AbsoluteDate reconstructedExactDate = new AbsoluteDate(dateComponents, timeComponents, TimeScalesFactory.getUTC());
611 
612         // When
613         final TLE parsedTle = new TLE(line1WithDateParsingError, line2WithDateParsingError);
614 
615         // Then
616         Assertions.assertEquals(reconstructedExactDate, parsedTle.getDate());
617     }
618 
619     @BeforeEach
620     public void setUp() {
621         Utils.setDataRoot("regular-data");
622     }
623 }