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 StringBuilder buffer = new StringBuilder();
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(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 StringBuilder buffer = new StringBuilder();
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(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
823 for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) {
824 if (templateDrivers.isSelected()) {
825
826 current.getParameterDriver(templateDrivers.getName()).setSelected(true);
827 }
828 }
829
830
831 return current;
832 }
833
834
835 sma += deltaSma;
836 ex += deltaEx;
837 ey += deltaEy;
838 hx += deltaHx;
839 hy += deltaHy;
840 lv += deltaLv;
841 final EquinoctialOrbit newEquiOrbit =
842 new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngle.TRUE,
843 equiOrbit.getFrame(), equiOrbit.getDate(), equiOrbit.getMu());
844 final KeplerianOrbit newKeplOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquiOrbit);
845
846
847 current = newTLE(newKeplOrbit, templateTLE, utc);
848 }
849
850 throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);
851 }
852
853
854
855
856
857
858
859
860 private static EquinoctialOrbit convert(final Orbit orbitIn, final Frame teme) {
861 return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu());
862 }
863
864
865
866
867
868
869
870
871 private static TLE newTLE(final KeplerianOrbit keplerianOrbit, final TLE templateTLE,
872 final TimeScale utc) {
873
874 final double meanMotion = keplerianOrbit.getKeplerianMeanMotion();
875 final double e = keplerianOrbit.getE();
876 final double i = keplerianOrbit.getI();
877 final double raan = keplerianOrbit.getRightAscensionOfAscendingNode();
878 final double pa = keplerianOrbit.getPerigeeArgument();
879 final double meanAnomaly = keplerianOrbit.getMeanAnomaly();
880
881 final AbsoluteDate epoch = keplerianOrbit.getDate();
882
883 final int satelliteNumber = templateTLE.getSatelliteNumber();
884 final char classification = templateTLE.getClassification();
885 final int launchYear = templateTLE.getLaunchYear();
886 final int launchNumber = templateTLE.getLaunchNumber();
887 final String launchPiece = templateTLE.getLaunchPiece();
888 final int ephemerisType = templateTLE.getEphemerisType();
889 final int elementNumber = templateTLE.getElementNumber();
890
891 final int revolutionNumberAtEpoch = templateTLE.getRevolutionNumberAtEpoch();
892 final double dt = epoch.durationFrom(templateTLE.getDate());
893 final int newRevolutionNumberAtEpoch = (int) (revolutionNumberAtEpoch + FastMath.floor((MathUtils.normalizeAngle(meanAnomaly, FastMath.PI) + dt * meanMotion) / (2 * FastMath.PI)));
894
895 final double bStar = templateTLE.getBStar();
896
897 final double meanMotionFirstDerivative = templateTLE.getMeanMotionFirstDerivative();
898 final double meanMotionSecondDerivative = templateTLE.getMeanMotionSecondDerivative();
899
900 return new TLE(satelliteNumber, classification, launchYear, launchNumber, launchPiece, ephemerisType,
901 elementNumber, epoch, meanMotion, meanMotionFirstDerivative, meanMotionSecondDerivative,
902 e, i, pa, raan, meanAnomaly, newRevolutionNumberAtEpoch, bStar, utc);
903 }
904
905
906
907
908
909
910
911 public static boolean isFormatOK(final String line1, final String line2) {
912
913 if (line1 == null || line1.length() != 69 ||
914 line2 == null || line2.length() != 69) {
915 return false;
916 }
917
918 if (!(LINE_1_PATTERN.matcher(line1).matches() &&
919 LINE_2_PATTERN.matcher(line2).matches())) {
920 return false;
921 }
922
923
924 final int checksum1 = checksum(line1);
925 if (Integer.parseInt(line1.substring(68)) != (checksum1 % 10)) {
926 throw new OrekitException(OrekitMessages.TLE_CHECKSUM_ERROR,
927 1, Integer.toString(checksum1 % 10), line1.substring(68), line1);
928 }
929
930 final int checksum2 = checksum(line2);
931 if (Integer.parseInt(line2.substring(68)) != (checksum2 % 10)) {
932 throw new OrekitException(OrekitMessages.TLE_CHECKSUM_ERROR,
933 2, Integer.toString(checksum2 % 10), line2.substring(68), line2);
934 }
935
936 return true;
937
938 }
939
940
941
942
943
944 private static int checksum(final CharSequence line) {
945 int sum = 0;
946 for (int j = 0; j < 68; j++) {
947 final char c = line.charAt(j);
948 if (Character.isDigit(c)) {
949 sum += Character.digit(c, 10);
950 } else if (c == '-') {
951 ++sum;
952 }
953 }
954 return sum % 10;
955 }
956
957
958
959
960
961
962
963
964
965 @Override
966 public boolean equals(final Object o) {
967 if (o == this) {
968 return true;
969 }
970 if (!(o instanceof TLE)) {
971 return false;
972 }
973 final TLE tle = (TLE) o;
974 return satelliteNumber == tle.satelliteNumber &&
975 classification == tle.classification &&
976 launchYear == tle.launchYear &&
977 launchNumber == tle.launchNumber &&
978 Objects.equals(launchPiece, tle.launchPiece) &&
979 ephemerisType == tle.ephemerisType &&
980 elementNumber == tle.elementNumber &&
981 Objects.equals(epoch, tle.epoch) &&
982 meanMotion == tle.meanMotion &&
983 meanMotionFirstDerivative == tle.meanMotionFirstDerivative &&
984 meanMotionSecondDerivative == tle.meanMotionSecondDerivative &&
985 eccentricity == tle.eccentricity &&
986 inclination == tle.inclination &&
987 pa == tle.pa &&
988 raan == tle.raan &&
989 meanAnomaly == tle.meanAnomaly &&
990 revolutionNumberAtEpoch == tle.revolutionNumberAtEpoch &&
991 getBStar() == tle.getBStar();
992 }
993
994
995
996
997 @Override
998 public int hashCode() {
999 return Objects.hash(satelliteNumber,
1000 classification,
1001 launchYear,
1002 launchNumber,
1003 launchPiece,
1004 ephemerisType,
1005 elementNumber,
1006 epoch,
1007 meanMotion,
1008 meanMotionFirstDerivative,
1009 meanMotionSecondDerivative,
1010 eccentricity,
1011 inclination,
1012 pa,
1013 raan,
1014 meanAnomaly,
1015 revolutionNumberAtEpoch,
1016 getBStar());
1017 }
1018
1019
1020
1021
1022 public List<ParameterDriver> getParametersDrivers() {
1023 return Collections.singletonList(bStarParameterDriver);
1024 }
1025
1026
1027
1028
1029
1030
1031 public ParameterDriver getParameterDriver(final String name) {
1032
1033 for (final ParameterDriver driver : getParametersDrivers()) {
1034 if (name.equals(driver.getName())) {
1035
1036 return driver;
1037 }
1038 }
1039
1040
1041 final StringBuilder sBuilder = new StringBuilder();
1042 for (final ParameterDriver driver : getParametersDrivers()) {
1043 if (sBuilder.length() > 0) {
1044 sBuilder.append(", ");
1045 }
1046 sBuilder.append(driver.getName());
1047 }
1048 throw new OrekitException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
1049 name, sBuilder.toString());
1050
1051 }
1052
1053
1054
1055
1056 private Object writeReplace() {
1057 return new DataTransferObject(line1, line2, utc);
1058 }
1059
1060
1061 private static class DataTransferObject implements Serializable {
1062
1063
1064 private static final long serialVersionUID = -1596648022319057689L;
1065
1066
1067 private String line1;
1068
1069
1070 private String line2;
1071
1072
1073 private final TimeScale utc;
1074
1075
1076
1077
1078
1079
1080 DataTransferObject(final String line1, final String line2, final TimeScale utc) {
1081 this.line1 = line1;
1082 this.line2 = line2;
1083 this.utc = utc;
1084 }
1085
1086
1087
1088
1089 private Object readResolve() {
1090 return new TLE(line1, line2, utc);
1091 }
1092
1093 }
1094
1095 }