1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation.analytical.tle;
18
19 import java.io.Serializable;
20 import java.text.DecimalFormat;
21 import java.text.DecimalFormatSymbols;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Objects;
26 import java.util.regex.Pattern;
27
28 import org.hipparchus.geometry.euclidean.threed.Rotation;
29 import org.hipparchus.util.ArithmeticUtils;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathUtils;
32 import org.orekit.annotation.DefaultDataContext;
33 import org.orekit.attitudes.InertialProvider;
34 import org.orekit.data.DataContext;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.errors.OrekitMessages;
37 import org.orekit.frames.Frame;
38 import org.orekit.orbits.EquinoctialOrbit;
39 import org.orekit.orbits.KeplerianOrbit;
40 import org.orekit.orbits.Orbit;
41 import org.orekit.orbits.OrbitType;
42 import org.orekit.orbits.PositionAngle;
43 import org.orekit.propagation.SpacecraftState;
44 import org.orekit.time.AbsoluteDate;
45 import org.orekit.time.DateComponents;
46 import org.orekit.time.DateTimeComponents;
47 import org.orekit.time.TimeComponents;
48 import org.orekit.time.TimeScale;
49 import org.orekit.time.TimeStamped;
50 import org.orekit.utils.ParameterDriver;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class TLE implements TimeStamped, Serializable {
69
70
71 public static final int SGP = 1;
72
73
74 public static final int SGP4 = 2;
75
76
77 public static final int SDP4 = 3;
78
79
80 public static final int SGP8 = 4;
81
82
83 public static final int SDP8 = 5;
84
85
86 public static final int DEFAULT = 0;
87
88
89 public static final String B_STAR = "BSTAR";
90
91
92 private static final double EPSILON_DEFAULT = 1.0e-10;
93
94
95 private static final int MAX_ITERATIONS_DEFAULT = 100;
96
97
98
99
100
101
102
103 private static final double B_STAR_SCALE = FastMath.scalb(1.0, -20);
104
105
106 private static final String MEAN_MOTION = "meanMotion";
107
108
109 private static final String INCLINATION = "inclination";
110
111
112 private static final String ECCENTRICITY = "eccentricity";
113
114
115 private static final Pattern LINE_1_PATTERN =
116 Pattern.compile("1 [ 0-9A-Z&&[^IO]][ 0-9]{4}[A-Z] [ 0-9]{5}[ A-Z]{3} [ 0-9]{5}[.][ 0-9]{8} (?:(?:[ 0+-][.][ 0-9]{8})|(?: [ +-][.][ 0-9]{7})) " +
117 "[ +-][ 0-9]{5}[+-][ 0-9] [ +-][ 0-9]{5}[+-][ 0-9] [ 0-9] [ 0-9]{4}[ 0-9]");
118
119
120 private static final Pattern LINE_2_PATTERN =
121 Pattern.compile("2 [ 0-9A-Z&&[^IO]][ 0-9]{4} [ 0-9]{3}[.][ 0-9]{4} [ 0-9]{3}[.][ 0-9]{4} [ 0-9]{7} " +
122 "[ 0-9]{3}[.][ 0-9]{4} [ 0-9]{3}[.][ 0-9]{4} [ 0-9]{2}[.][ 0-9]{13}[ 0-9]");
123
124
125 private static final DecimalFormatSymbols SYMBOLS =
126 new DecimalFormatSymbols(Locale.US);
127
128
129 private static final long serialVersionUID = -1596648022319057689L;
130
131
132 private final int satelliteNumber;
133
134
135 private final char classification;
136
137
138 private final int launchYear;
139
140
141 private final int launchNumber;
142
143
144 private final String launchPiece;
145
146
147 private final int ephemerisType;
148
149
150 private final int elementNumber;
151
152
153 private final AbsoluteDate epoch;
154
155
156 private final double meanMotion;
157
158
159 private final double meanMotionFirstDerivative;
160
161
162 private final double meanMotionSecondDerivative;
163
164
165 private final double eccentricity;
166
167
168 private final double inclination;
169
170
171 private final double pa;
172
173
174 private final double raan;
175
176
177 private final double meanAnomaly;
178
179
180 private final int revolutionNumberAtEpoch;
181
182
183 private String line1;
184
185
186 private String line2;
187
188
189 private final TimeScale utc;
190
191
192 private final transient ParameterDriver bStarParameterDriver;
193
194
195
196
197
198
199
200
201
202
203
204 @DefaultDataContext
205 public TLE(final String line1, final String line2) {
206 this(line1, line2, DataContext.getDefault().getTimeScales().getUTC());
207 }
208
209
210
211
212
213
214
215
216
217
218 public TLE(final String line1, final String line2, final TimeScale utc) {
219
220
221 satelliteNumber = ParseUtils.parseSatelliteNumber(line1, 2, 5);
222 final int satNum2 = ParseUtils.parseSatelliteNumber(line2, 2, 5);
223 if (satelliteNumber != satNum2) {
224 throw new OrekitException(OrekitMessages.TLE_LINES_DO_NOT_REFER_TO_SAME_OBJECT,
225 line1, line2);
226 }
227 classification = line1.charAt(7);
228 launchYear = ParseUtils.parseYear(line1, 9);
229 launchNumber = ParseUtils.parseInteger(line1, 11, 3);
230 launchPiece = line1.substring(14, 17).trim();
231 ephemerisType = ParseUtils.parseInteger(line1, 62, 1);
232 elementNumber = ParseUtils.parseInteger(line1, 64, 4);
233
234
235 final int year = ParseUtils.parseYear(line1, 18);
236 final int dayInYear = ParseUtils.parseInteger(line1, 20, 3);
237 final long df = 27l * ParseUtils.parseInteger(line1, 24, 8);
238 final int secondsA = (int) (df / 31250l);
239 final double secondsB = (df % 31250l) / 31250.0;
240 epoch = new AbsoluteDate(new DateComponents(year, dayInYear),
241 new TimeComponents(secondsA, secondsB),
242 utc);
243
244
245
246 meanMotion = ParseUtils.parseDouble(line2, 52, 11) * FastMath.PI / 43200.0;
247 meanMotionFirstDerivative = ParseUtils.parseDouble(line1, 33, 10) * FastMath.PI / 1.86624e9;
248 meanMotionSecondDerivative = Double.parseDouble((line1.substring(44, 45) + '.' +
249 line1.substring(45, 50) + 'e' +
250 line1.substring(50, 52)).replace(' ', '0')) *
251 FastMath.PI / 5.3747712e13;
252
253 eccentricity = Double.parseDouble("." + line2.substring(26, 33).replace(' ', '0'));
254 inclination = FastMath.toRadians(ParseUtils.parseDouble(line2, 8, 8));
255 pa = FastMath.toRadians(ParseUtils.parseDouble(line2, 34, 8));
256 raan = FastMath.toRadians(Double.parseDouble(line2.substring(17, 25).replace(' ', '0')));
257 meanAnomaly = FastMath.toRadians(ParseUtils.parseDouble(line2, 43, 8));
258
259 revolutionNumberAtEpoch = ParseUtils.parseInteger(line2, 63, 5);
260 final double bStarValue = Double.parseDouble((line1.substring(53, 54) + '.' +
261 line1.substring(54, 59) + 'e' +
262 line1.substring(59, 61)).replace(' ', '0'));
263
264
265 this.line1 = line1;
266 this.line2 = line2;
267 this.utc = utc;
268
269
270 this.bStarParameterDriver = new ParameterDriver(B_STAR, bStarValue, B_STAR_SCALE,
271 Double.NEGATIVE_INFINITY,
272 Double.POSITIVE_INFINITY);
273
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 @DefaultDataContext
318 public TLE(final int satelliteNumber, final char classification,
319 final int launchYear, final int launchNumber, final String launchPiece,
320 final int ephemerisType, final int elementNumber, final AbsoluteDate epoch,
321 final double meanMotion, final double meanMotionFirstDerivative,
322 final double meanMotionSecondDerivative, final double e, final double i,
323 final double pa, final double raan, final double meanAnomaly,
324 final int revolutionNumberAtEpoch, final double bStar) {
325 this(satelliteNumber, classification, launchYear, launchNumber, launchPiece,
326 ephemerisType, elementNumber, epoch, meanMotion,
327 meanMotionFirstDerivative, meanMotionSecondDerivative, e, i, pa, raan,
328 meanAnomaly, revolutionNumberAtEpoch, bStar,
329 DataContext.getDefault().getTimeScales().getUTC());
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 public TLE(final int satelliteNumber, final char classification,
374 final int launchYear, final int launchNumber, final String launchPiece,
375 final int ephemerisType, final int elementNumber, final AbsoluteDate epoch,
376 final double meanMotion, final double meanMotionFirstDerivative,
377 final double meanMotionSecondDerivative, final double e, final double i,
378 final double pa, final double raan, final double meanAnomaly,
379 final int revolutionNumberAtEpoch, final double bStar,
380 final TimeScale utc) {
381
382
383 this.satelliteNumber = satelliteNumber;
384 this.classification = classification;
385 this.launchYear = launchYear;
386 this.launchNumber = launchNumber;
387 this.launchPiece = launchPiece;
388 this.ephemerisType = ephemerisType;
389 this.elementNumber = elementNumber;
390
391
392 this.epoch = epoch;
393
394 this.meanMotion = meanMotion;
395 this.meanMotionFirstDerivative = meanMotionFirstDerivative;
396 this.meanMotionSecondDerivative = meanMotionSecondDerivative;
397
398
399 this.inclination = i;
400
401
402 this.raan = MathUtils.normalizeAngle(raan, FastMath.PI);
403
404
405 this.eccentricity = e;
406
407
408 this.pa = MathUtils.normalizeAngle(pa, FastMath.PI);
409
410
411 this.meanAnomaly = MathUtils.normalizeAngle(meanAnomaly, FastMath.PI);
412
413 this.revolutionNumberAtEpoch = revolutionNumberAtEpoch;
414
415
416
417 this.line1 = null;
418 this.line2 = null;
419 this.utc = utc;
420
421
422 this.bStarParameterDriver = new ParameterDriver(B_STAR, bStar, B_STAR_SCALE,
423 Double.NEGATIVE_INFINITY,
424 Double.POSITIVE_INFINITY);
425
426 }
427
428
429
430
431
432
433 public TimeScale getUtc() {
434 return utc;
435 }
436
437
438
439
440 public String getLine1() {
441 if (line1 == null) {
442 buildLine1();
443 }
444 return line1;
445 }
446
447
448
449
450 public String getLine2() {
451 if (line2 == null) {
452 buildLine2();
453 }
454 return line2;
455 }
456
457
458
459 private void buildLine1() {
460
461 final StringBuffer buffer = new StringBuffer();
462
463 buffer.append('1');
464
465 buffer.append(' ');
466 buffer.append(ParseUtils.buildSatelliteNumber(satelliteNumber, "satelliteNumber-1"));
467 buffer.append(classification);
468
469 buffer.append(' ');
470 buffer.append(ParseUtils.addPadding("launchYear", launchYear % 100, '0', 2, true, satelliteNumber));
471 buffer.append(ParseUtils.addPadding("launchNumber", launchNumber, '0', 3, true, satelliteNumber));
472 buffer.append(ParseUtils.addPadding("launchPiece", launchPiece, ' ', 3, false, satelliteNumber));
473
474 buffer.append(' ');
475 final DateTimeComponents dtc = epoch.getComponents(utc);
476 buffer.append(ParseUtils.addPadding("year", dtc.getDate().getYear() % 100, '0', 2, true, satelliteNumber));
477 buffer.append(ParseUtils.addPadding("day", dtc.getDate().getDayOfYear(), '0', 3, true, satelliteNumber));
478 buffer.append('.');
479
480 final int fraction = (int) FastMath.rint(31250 * dtc.getTime().getSecondsInUTCDay() / 27.0);
481 buffer.append(ParseUtils.addPadding("fraction", fraction, '0', 8, true, satelliteNumber));
482
483 buffer.append(' ');
484 final double n1 = meanMotionFirstDerivative * 1.86624e9 / FastMath.PI;
485 final String sn1 = ParseUtils.addPadding("meanMotionFirstDerivative",
486 new DecimalFormat(".00000000", SYMBOLS).format(n1),
487 ' ', 10, true, satelliteNumber);
488 buffer.append(sn1);
489
490 buffer.append(' ');
491 final double n2 = meanMotionSecondDerivative * 5.3747712e13 / FastMath.PI;
492 buffer.append(formatExponentMarkerFree("meanMotionSecondDerivative", n2, 5, ' ', 8, true));
493
494 buffer.append(' ');
495 buffer.append(formatExponentMarkerFree("B*", getBStar(), 5, ' ', 8, true));
496
497 buffer.append(' ');
498 buffer.append(ephemerisType);
499
500 buffer.append(' ');
501 buffer.append(ParseUtils.addPadding("elementNumber", elementNumber, ' ', 4, true, satelliteNumber));
502
503 buffer.append(Integer.toString(checksum(buffer)));
504
505 line1 = buffer.toString();
506
507 }
508
509
510
511
512
513
514
515
516
517
518
519 private String formatExponentMarkerFree(final String name, final double d, final int mantissaSize,
520 final char c, final int size, final boolean rightJustified) {
521 final double dAbs = FastMath.abs(d);
522 int exponent = (dAbs < 1.0e-9) ? -9 : (int) FastMath.ceil(FastMath.log10(dAbs));
523 long mantissa = FastMath.round(dAbs * FastMath.pow(10.0, mantissaSize - exponent));
524 if (mantissa == 0) {
525 exponent = 0;
526 } else if (mantissa > (ArithmeticUtils.pow(10, mantissaSize) - 1)) {
527
528
529
530 exponent++;
531 mantissa = FastMath.round(dAbs * FastMath.pow(10.0, mantissaSize - exponent));
532 }
533 final String sMantissa = ParseUtils.addPadding(name, (int) mantissa, '0', mantissaSize, true, satelliteNumber);
534 final String sExponent = Integer.toString(FastMath.abs(exponent));
535 final String formatted = (d < 0 ? '-' : ' ') + sMantissa + (exponent <= 0 ? '-' : '+') + sExponent;
536
537 return ParseUtils.addPadding(name, formatted, c, size, rightJustified, satelliteNumber);
538
539 }
540
541
542
543 private void buildLine2() {
544
545 final StringBuffer buffer = new StringBuffer();
546 final DecimalFormat f34 = new DecimalFormat("##0.0000", SYMBOLS);
547 final DecimalFormat f211 = new DecimalFormat("#0.00000000", SYMBOLS);
548
549 buffer.append('2');
550
551 buffer.append(' ');
552 buffer.append(ParseUtils.buildSatelliteNumber(satelliteNumber, "satelliteNumber-2"));
553
554 buffer.append(' ');
555 buffer.append(ParseUtils.addPadding(INCLINATION, f34.format(FastMath.toDegrees(inclination)), ' ', 8, true, satelliteNumber));
556 buffer.append(' ');
557 buffer.append(ParseUtils.addPadding("raan", f34.format(FastMath.toDegrees(raan)), ' ', 8, true, satelliteNumber));
558 buffer.append(' ');
559 buffer.append(ParseUtils.addPadding(ECCENTRICITY, (int) FastMath.rint(eccentricity * 1.0e7), '0', 7, true, satelliteNumber));
560 buffer.append(' ');
561 buffer.append(ParseUtils.addPadding("pa", f34.format(FastMath.toDegrees(pa)), ' ', 8, true, satelliteNumber));
562 buffer.append(' ');
563 buffer.append(ParseUtils.addPadding("meanAnomaly", f34.format(FastMath.toDegrees(meanAnomaly)), ' ', 8, true, satelliteNumber));
564
565 buffer.append(' ');
566 buffer.append(ParseUtils.addPadding(MEAN_MOTION, f211.format(meanMotion * 43200.0 / FastMath.PI), ' ', 11, true, satelliteNumber));
567 buffer.append(ParseUtils.addPadding("revolutionNumberAtEpoch", revolutionNumberAtEpoch, ' ', 5, true, satelliteNumber));
568
569 buffer.append(Integer.toString(checksum(buffer)));
570
571 line2 = buffer.toString();
572
573 }
574
575
576
577
578 public int getSatelliteNumber() {
579 return satelliteNumber;
580 }
581
582
583
584
585 public char getClassification() {
586 return classification;
587 }
588
589
590
591
592 public int getLaunchYear() {
593 return launchYear;
594 }
595
596
597
598
599 public int getLaunchNumber() {
600 return launchNumber;
601 }
602
603
604
605
606 public String getLaunchPiece() {
607 return launchPiece;
608 }
609
610
611
612
613
614 public int getEphemerisType() {
615 return ephemerisType;
616 }
617
618
619
620
621 public int getElementNumber() {
622 return elementNumber;
623 }
624
625
626
627
628 public AbsoluteDate getDate() {
629 return epoch;
630 }
631
632
633
634
635 public double getMeanMotion() {
636 return meanMotion;
637 }
638
639
640
641
642 public double getMeanMotionFirstDerivative() {
643 return meanMotionFirstDerivative;
644 }
645
646
647
648
649 public double getMeanMotionSecondDerivative() {
650 return meanMotionSecondDerivative;
651 }
652
653
654
655
656 public double getE() {
657 return eccentricity;
658 }
659
660
661
662
663 public double getI() {
664 return inclination;
665 }
666
667
668
669
670 public double getPerigeeArgument() {
671 return pa;
672 }
673
674
675
676
677 public double getRaan() {
678 return raan;
679 }
680
681
682
683
684 public double getMeanAnomaly() {
685 return meanAnomaly;
686 }
687
688
689
690
691 public int getRevolutionNumberAtEpoch() {
692 return revolutionNumberAtEpoch;
693 }
694
695
696
697
698 public double getBStar() {
699 return bStarParameterDriver.getValue();
700 }
701
702
703
704
705
706
707 public String toString() {
708 return getLine1() + System.getProperty("line.separator") + getLine2();
709 }
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729 @DefaultDataContext
730 public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE) {
731 return stateToTLE(state, templateTLE,
732 DataContext.getDefault().getTimeScales().getUTC(),
733 DataContext.getDefault().getFrames().getTEME());
734 }
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755 public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE,
756 final TimeScale utc, final Frame teme) {
757 return stateToTLE(state, templateTLE, utc, teme, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775 public static TLE stateToTLE(final SpacecraftState state, final TLE templateTLE,
776 final TimeScale utc, final Frame teme,
777 final double epsilon, final int maxIterations) {
778
779
780 final EquinoctialOrbit equiOrbit = convert(state.getOrbit(), teme);
781 double sma = equiOrbit.getA();
782 double ex = equiOrbit.getEquinoctialEx();
783 double ey = equiOrbit.getEquinoctialEy();
784 double hx = equiOrbit.getHx();
785 double hy = equiOrbit.getHy();
786 double lv = equiOrbit.getLv();
787
788
789 final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(equiOrbit);
790 TLE current = newTLE(keplerianOrbit, templateTLE, utc);
791
792
793 final double thrA = epsilon * (1 + sma);
794 final double thrE = epsilon * (1 + FastMath.hypot(ex, ey));
795 final double thrH = epsilon * (1 + FastMath.hypot(hx, hy));
796 final double thrV = epsilon * FastMath.PI;
797
798 int k = 0;
799 while (k++ < maxIterations) {
800
801
802 final TLEPropagator propagator = TLEPropagator.selectExtrapolator(current, new InertialProvider(Rotation.IDENTITY, teme), state.getMass(), teme);
803 final Orbit recovOrbit = propagator.getInitialState().getOrbit();
804 final EquinoctialOrbit recovEquiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recovOrbit);
805
806
807 final double deltaSma = equiOrbit.getA() - recovEquiOrbit.getA();
808 final double deltaEx = equiOrbit.getEquinoctialEx() - recovEquiOrbit.getEquinoctialEx();
809 final double deltaEy = equiOrbit.getEquinoctialEy() - recovEquiOrbit.getEquinoctialEy();
810 final double deltaHx = equiOrbit.getHx() - recovEquiOrbit.getHx();
811 final double deltaHy = equiOrbit.getHy() - recovEquiOrbit.getHy();
812 final double deltaLv = MathUtils.normalizeAngle(equiOrbit.getLv() - recovEquiOrbit.getLv(), 0.0);
813
814
815 if (FastMath.abs(deltaSma) < thrA &&
816 FastMath.abs(deltaEx) < thrE &&
817 FastMath.abs(deltaEy) < thrE &&
818 FastMath.abs(deltaHx) < thrH &&
819 FastMath.abs(deltaHy) < thrH &&
820 FastMath.abs(deltaLv) < thrV) {
821
822 return current;
823 }
824
825
826 sma += deltaSma;
827 ex += deltaEx;
828 ey += deltaEy;
829 hx += deltaHx;
830 hy += deltaHy;
831 lv += deltaLv;
832 final EquinoctialOrbit newEquiOrbit =
833 new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngle.TRUE,
834 equiOrbit.getFrame(), equiOrbit.getDate(), equiOrbit.getMu());
835 final KeplerianOrbit newKeplOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquiOrbit);
836
837
838 current = newTLE(newKeplOrbit, templateTLE, utc);
839 }
840
841 throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);
842 }
843
844
845
846
847
848
849
850
851 private static EquinoctialOrbit convert(final Orbit orbitIn, final Frame teme) {
852 return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu());
853 }
854
855
856
857
858
859
860
861
862 private static TLE newTLE(final KeplerianOrbit keplerianOrbit, final TLE templateTLE,
863 final TimeScale utc) {
864
865 final double meanMotion = keplerianOrbit.getKeplerianMeanMotion();
866 final double e = keplerianOrbit.getE();
867 final double i = keplerianOrbit.getI();
868 final double raan = keplerianOrbit.getRightAscensionOfAscendingNode();
869 final double pa = keplerianOrbit.getPerigeeArgument();
870 final double meanAnomaly = keplerianOrbit.getMeanAnomaly();
871
872 final AbsoluteDate epoch = keplerianOrbit.getDate();
873
874 final int satelliteNumber = templateTLE.getSatelliteNumber();
875 final char classification = templateTLE.getClassification();
876 final int launchYear = templateTLE.getLaunchYear();
877 final int launchNumber = templateTLE.getLaunchNumber();
878 final String launchPiece = templateTLE.getLaunchPiece();
879 final int ephemerisType = templateTLE.getEphemerisType();
880 final int elementNumber = templateTLE.getElementNumber();
881
882 final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch();
883 final double dt = epoch.durationFrom(templateTLE.getDate());
884 final int newRevolutionNumberAtEpoch = (int) ((int) revolutionNumberAtEpoch + FastMath.floor((MathUtils.normalizeAngle(meanAnomaly, FastMath.PI) + dt * meanMotion) / (2 * FastMath.PI)));
885
886 final double bStar = templateTLE.getBStar();
887
888 final double meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative();
889 final double meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative();
890
891 return new TLE(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType,
892 elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative,
893 e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar, utc);
894 }
895
896
897
898
899
900
901
902 public static boolean isFormatOK(final String line1, final String line2) {
903
904 if (line1 == null || line1.length() != 69 ||
905 line2 == null || line2.length() != 69) {
906 return false;
907 }
908
909 if (!(LINE_1_PATTERN.matcher(line1).matches() &&
910 LINE_2_PATTERN.matcher(line2).matches())) {
911 return false;
912 }
913
914
915 final int checksum1 = checksum(line1);
916 if (Integer.parseInt(line1.substring(68)) != (checksum1 % 10)) {
917 throw new OrekitException(OrekitMessages.TLE_CHECKSUM_ERROR,
918 1, Integer.toString(checksum1 % 10), line1.substring(68), line1);
919 }
920
921 final int checksum2 = checksum(line2);
922 if (Integer.parseInt(line2.substring(68)) != (checksum2 % 10)) {
923 throw new OrekitException(OrekitMessages.TLE_CHECKSUM_ERROR,
924 2, Integer.toString(checksum2 % 10), line2.substring(68), line2);
925 }
926
927 return true;
928
929 }
930
931
932
933
934
935 private static int checksum(final CharSequence line) {
936 int sum = 0;
937 for (int j = 0; j < 68; j++) {
938 final char c = line.charAt(j);
939 if (Character.isDigit(c)) {
940 sum += Character.digit(c, 10);
941 } else if (c == '-') {
942 ++sum;
943 }
944 }
945 return sum % 10;
946 }
947
948
949
950
951
952
953
954
955
956 @Override
957 public boolean equals(final Object o) {
958 if (o == this) {
959 return true;
960 }
961 if (!(o instanceof TLE)) {
962 return false;
963 }
964 final TLE tle = (TLE) o;
965 return satelliteNumber == tle.satelliteNumber &&
966 classification == tle.classification &&
967 launchYear == tle.launchYear &&
968 launchNumber == tle.launchNumber &&
969 Objects.equals(launchPiece, tle.launchPiece) &&
970 ephemerisType == tle.ephemerisType &&
971 elementNumber == tle.elementNumber &&
972 Objects.equals(epoch, tle.epoch) &&
973 meanMotion == tle.meanMotion &&
974 meanMotionFirstDerivative == tle.meanMotionFirstDerivative &&
975 meanMotionSecondDerivative == tle.meanMotionSecondDerivative &&
976 eccentricity == tle.eccentricity &&
977 inclination == tle.inclination &&
978 pa == tle.pa &&
979 raan == tle.raan &&
980 meanAnomaly == tle.meanAnomaly &&
981 revolutionNumberAtEpoch == tle.revolutionNumberAtEpoch &&
982 getBStar() == tle.getBStar();
983 }
984
985
986
987
988 @Override
989 public int hashCode() {
990 return Objects.hash(satelliteNumber,
991 classification,
992 launchYear,
993 launchNumber,
994 launchPiece,
995 ephemerisType,
996 elementNumber,
997 epoch,
998 meanMotion,
999 meanMotionFirstDerivative,
1000 meanMotionSecondDerivative,
1001 eccentricity,
1002 inclination,
1003 pa,
1004 raan,
1005 meanAnomaly,
1006 revolutionNumberAtEpoch,
1007 getBStar());
1008 }
1009
1010
1011
1012
1013 public List<ParameterDriver> getParametersDrivers() {
1014 return Collections.singletonList(bStarParameterDriver);
1015 }
1016
1017
1018
1019
1020 private Object writeReplace() {
1021 return new DataTransferObject(line1, line2, utc);
1022 }
1023
1024
1025 private static class DataTransferObject implements Serializable {
1026
1027
1028 private static final long serialVersionUID = -1596648022319057689L;
1029
1030
1031 private String line1;
1032
1033
1034 private String line2;
1035
1036
1037 private final TimeScale utc;
1038
1039
1040
1041
1042
1043
1044 DataTransferObject(final String line1, final String line2, final TimeScale utc) {
1045 this.line1 = line1;
1046 this.line2 = line2;
1047 this.utc = utc;
1048 }
1049
1050
1051
1052
1053 private Object readResolve() {
1054 return new TLE(line1, line2, utc);
1055 }
1056
1057 }
1058
1059 }