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