1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.stk;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.Reader;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.EnumMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.SortedSet;
30 import java.util.TreeSet;
31 import java.util.regex.Pattern;
32 import java.util.stream.Stream;
33
34 import org.hipparchus.exception.LocalizedCoreFormats;
35 import org.hipparchus.geometry.euclidean.threed.Vector3D;
36 import org.orekit.data.DataSource;
37 import org.orekit.errors.OrekitException;
38 import org.orekit.errors.OrekitMessages;
39 import org.orekit.files.general.EphemerisFileParser;
40 import org.orekit.files.stk.STKEphemerisFile.STKCoordinateSystem;
41 import org.orekit.files.stk.STKEphemerisFile.STKEphemeris;
42 import org.orekit.files.stk.STKEphemerisFile.STKEphemerisSegment;
43 import org.orekit.frames.Frame;
44 import org.orekit.time.AbsoluteDate;
45 import org.orekit.time.DateTimeComponents;
46 import org.orekit.time.Month;
47 import org.orekit.time.UTCScale;
48 import org.orekit.utils.CartesianDerivativesFilter;
49 import org.orekit.utils.PVCoordinates;
50 import org.orekit.utils.TimeStampedPVCoordinates;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public class STKEphemerisFileParser implements EphemerisFileParser<STKEphemerisFile> {
144
145
146 private static final Pattern SEPARATOR = Pattern.compile("\\s+");
147
148
149 private static final Pattern IGNORABLE_LINE = Pattern.compile("^\\s*(#.*)?");
150
151
152 private static final String MATCH_ANY_REGEX = ".*";
153
154
155 private static final List<LineParser> KEYWORDS = Arrays.asList(
156 LineParser.NUMBER_OF_EPHEMERIS_POINTS,
157 LineParser.SCENARIO_EPOCH,
158 LineParser.INTERPOLATION_METHOD,
159 LineParser.INTERPOLATION_SAMPLESM1,
160 LineParser.CENTRAL_BODY,
161 LineParser.COORDINATE_SYSTEM,
162 LineParser.BEGIN_SEGMENT_BOUNDARY_TIMES,
163 LineParser.EPHEMERIS_TIME_POS,
164 LineParser.EPHEMERIS_TIME_POS_VEL,
165 LineParser.EPHEMERIS_TIME_POS_VEL_ACC
166 );
167
168
169 private final String satelliteId;
170
171
172 private final double mu;
173
174
175 private final UTCScale utc;
176
177
178 private final Map<STKCoordinateSystem, Frame> frameMapping;
179
180
181
182
183
184
185
186
187 public STKEphemerisFileParser(final String satelliteId, final double mu, final UTCScale utc,
188 final Map<STKCoordinateSystem, Frame> frameMapping) {
189 this.satelliteId = Objects.requireNonNull(satelliteId);
190 this.mu = mu;
191 this.utc = Objects.requireNonNull(utc);
192 this.frameMapping = Collections.unmodifiableMap(new EnumMap<>(frameMapping));
193 }
194
195 @Override
196 public STKEphemerisFile parse(final DataSource source) {
197
198 try (Reader reader = source.getOpener().openReaderOnce();
199 BufferedReader br = (reader == null) ? null : new BufferedReader(reader)) {
200
201 if (br == null) {
202 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, source.getName());
203 }
204
205
206 final ParseInfo pi = new ParseInfo();
207
208 int lineNumber = 0;
209 Iterable<LineParser> parsers = Collections.singleton(LineParser.VERSION);
210 nextLine:
211 for (String line = br.readLine(); line != null; line = br.readLine()) {
212 ++lineNumber;
213 if (pi.file != null) {
214 break;
215 } else if (IGNORABLE_LINE.matcher(line).matches()) {
216 continue;
217 }
218 for (final LineParser candidate : parsers) {
219 if (candidate.canHandle(line)) {
220 try {
221 candidate.parse(line, pi);
222 parsers = candidate.allowedNext();
223 continue nextLine;
224 } catch (StringIndexOutOfBoundsException | IllegalArgumentException e) {
225 throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber,
226 source.getName(), line);
227 }
228 }
229 }
230
231
232 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, source.getName(),
233 line);
234
235 }
236
237 if (pi.file != null) {
238 return pi.file;
239 } else {
240 throw new OrekitException(OrekitMessages.STK_UNEXPECTED_END_OF_FILE, lineNumber);
241 }
242
243 } catch (IOException ioe) {
244 throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
245 }
246 }
247
248
249
250
251
252
253
254
255
256 private final class ParseInfo {
257
258
259 private String stkVersion;
260
261
262 private AbsoluteDate scenarioEpoch;
263
264
265 private Integer numberOfEphemerisPoints;
266
267
268 private int interpolationSamplesM1;
269
270
271 private CartesianDerivativesFilter cartesianDerivativesFilter;
272
273
274 private STKCoordinateSystem coordinateSystem;
275
276
277 private STKDistanceUnit distanceUnit;
278
279
280 private int numberOfEphemerisPointsRead;
281
282
283 private SortedSet<Double> segmentBoundaryTimes;
284
285
286 private List<STKEphemerisSegment> ephemerisSegments;
287
288
289 private TimeStampedPVCoordinates lastSavedEphemeris;
290
291
292 private List<TimeStampedPVCoordinates> segmentEphemeris;
293
294
295 private STKEphemerisFile file;
296
297
298
299
300 private ParseInfo() {
301
302 this.distanceUnit = STKDistanceUnit.METERS;
303 this.interpolationSamplesM1 = 5;
304 this.coordinateSystem = STKCoordinateSystem.FIXED;
305
306
307 this.ephemerisSegments = new ArrayList<>();
308 this.segmentBoundaryTimes = new TreeSet<>();
309 this.segmentEphemeris = new ArrayList<>();
310 }
311
312
313
314
315
316 private UTCScale getUTCScale() {
317 return utc;
318 }
319
320
321
322
323
324
325 private void addEphemeris(final double time, final PVCoordinates pvCoordinates) {
326 if (numberOfEphemerisPoints != null && numberOfEphemerisPointsRead == numberOfEphemerisPoints) {
327 return;
328 }
329 final AbsoluteDate date = scenarioEpoch.shiftedBy(time);
330 final TimeStampedPVCoordinates timeStampedPVCoordinates = new TimeStampedPVCoordinates(date, pvCoordinates);
331 if (segmentBoundaryTimes.contains(time) && numberOfEphemerisPointsRead > 0) {
332 if (segmentEphemeris.isEmpty()) {
333 if (!date.equals(lastSavedEphemeris.getDate())) {
334 segmentEphemeris.add(lastSavedEphemeris);
335 }
336 segmentEphemeris.add(timeStampedPVCoordinates);
337 } else {
338 segmentEphemeris.add(timeStampedPVCoordinates);
339 ephemerisSegments.add(new STKEphemerisSegment(mu, getFrame(), 1 + interpolationSamplesM1,
340 cartesianDerivativesFilter, segmentEphemeris));
341 segmentEphemeris = new ArrayList<>();
342 }
343 } else {
344 segmentEphemeris.add(timeStampedPVCoordinates);
345 }
346 lastSavedEphemeris = timeStampedPVCoordinates;
347 ++numberOfEphemerisPointsRead;
348 }
349
350
351
352
353
354 private Frame getFrame() {
355 final STKCoordinateSystem stkCoordinateSystem = coordinateSystem == null ? STKCoordinateSystem.FIXED :
356 coordinateSystem;
357 final Frame frame = frameMapping.get(stkCoordinateSystem);
358 if (frame == null) {
359 throw new OrekitException(OrekitMessages.STK_UNMAPPED_COORDINATE_SYSTEM, stkCoordinateSystem);
360 }
361 return frame;
362 }
363
364
365
366
367 private void complete() {
368 if (!segmentEphemeris.isEmpty()) {
369 ephemerisSegments.add(new STKEphemerisSegment(mu, getFrame(), 1 + interpolationSamplesM1,
370 cartesianDerivativesFilter, segmentEphemeris));
371 }
372 final STKEphemeris ephemeris = new STKEphemeris(satelliteId, mu, ephemerisSegments);
373 file = new STKEphemerisFile(stkVersion, satelliteId, ephemeris);
374 }
375
376 }
377
378
379 private enum LineParser {
380
381
382 VERSION("^stk\\.v\\.\\d+\\.\\d+$") {
383
384 @Override
385 public void parse(final String line, final ParseInfo pi) {
386 pi.stkVersion = line;
387 }
388
389 @Override
390 public Iterable<LineParser> allowedNext() {
391 return Collections.singleton(BEGIN_EPHEMERIS);
392 }
393
394 },
395
396
397 BEGIN_EPHEMERIS("^\\s*BEGIN Ephemeris\\s*(#.*)?$") {
398
399 @Override
400 public void parse(final String line, final ParseInfo pi) {
401
402 }
403
404 @Override
405 public Iterable<LineParser> allowedNext() {
406 return KEYWORDS;
407 }
408
409 },
410
411
412 NUMBER_OF_EPHEMERIS_POINTS("^\\s*NumberOfEphemerisPoints\\s*\\d+\\s*(#.*)?$") {
413
414 @Override
415 public void parse(final String line, final ParseInfo pi) {
416 pi.numberOfEphemerisPoints = Integer.parseInt(SEPARATOR.split(line.trim())[1]);
417 }
418
419 @Override
420 public Iterable<LineParser> allowedNext() {
421 return KEYWORDS;
422 }
423
424 },
425
426
427 SCENARIO_EPOCH("^\\s*ScenarioEpoch\\s* \\d{2} [a-zA-Z]{3} \\d{4} \\d{2}:\\d{2}:\\d{2}(\\.\\d*)?\\s*(#.*)?$") {
428
429 @Override
430 public void parse(final String line, final ParseInfo pi) {
431 final String[] tokens = SEPARATOR.split(line.trim());
432 final int dayOfMonth = Integer.parseInt(tokens[1]);
433 final Month month = Month.parseMonth(tokens[2]);
434 final int year = Integer.parseInt(tokens[3]);
435 final int hour = Integer.parseInt(tokens[4].substring(0, 2));
436 final int minute = Integer.parseInt(tokens[4].substring(3, 5));
437 final double seconds = Double.parseDouble(tokens[4].substring(6));
438 final DateTimeComponents dateTimeComponents = new DateTimeComponents(year, month, dayOfMonth, hour, minute, seconds);
439 pi.scenarioEpoch = new AbsoluteDate(dateTimeComponents, pi.getUTCScale());
440 }
441
442 @Override
443 public Iterable<LineParser> allowedNext() {
444 return KEYWORDS;
445 }
446
447 },
448
449
450 INTERPOLATION_METHOD("^\\s*InterpolationMethod\\s+[a-zA-Z]+\\s*(#.*)?$") {
451
452 @Override
453 public void parse(final String line, final ParseInfo pi) {
454
455 }
456
457 @Override
458 public Iterable<LineParser> allowedNext() {
459 return KEYWORDS;
460 }
461
462 },
463
464
465 INTERPOLATION_SAMPLESM1("^\\s*InterpolationSamplesM1\\s+\\d+\\s*(#.*)?$") {
466
467 @Override
468 public void parse(final String line, final ParseInfo pi) {
469 pi.interpolationSamplesM1 = Integer.parseInt(SEPARATOR.split(line.trim())[1]);
470 }
471
472 @Override
473 public Iterable<LineParser> allowedNext() {
474 return KEYWORDS;
475 }
476
477 },
478
479
480 CENTRAL_BODY("^\\s*CentralBody\\s+[a-zA-Z]+\\s*(#.*)?$") {
481
482 @Override
483 public void parse(final String line, final ParseInfo pi) {
484
485
486 }
487
488 @Override
489 public Iterable<LineParser> allowedNext() {
490 return KEYWORDS;
491 }
492
493 },
494
495
496 COORDINATE_SYSTEM("^\\s*CoordinateSystem\\s+[a-zA-Z0-9]+\\s*(#.*)?$") {
497
498 @Override
499 public void parse(final String line, final ParseInfo pi) {
500 pi.coordinateSystem = STKCoordinateSystem.parse(SEPARATOR.split(line.trim())[1]);
501 }
502
503 @Override
504 public Iterable<LineParser> allowedNext() {
505 return KEYWORDS;
506 }
507
508 },
509
510
511 DISTANCE_UNIT("^\\s*DistanceUnit\\s+[a-zA-Z0-9]+\\s*(#.*)?$") {
512
513 @Override
514 public void parse(final String line, final ParseInfo pi) {
515 pi.distanceUnit = STKDistanceUnit.valueOf(SEPARATOR.split(line.trim())[1].toUpperCase());
516 }
517
518 @Override
519 public Iterable<LineParser> allowedNext() {
520 return KEYWORDS;
521 }
522
523 },
524
525
526 BEGIN_SEGMENT_BOUNDARY_TIMES("^\\s*BEGIN SegmentBoundaryTimes\\s*(#.*)?$") {
527
528 @Override
529 public void parse(final String line, final ParseInfo pi) {
530
531 }
532
533 @Override
534 public Iterable<LineParser> allowedNext() {
535 return Collections.singleton(SEGMENT_BOUNDARY_TIME);
536 }
537
538 },
539
540
541 SEGMENT_BOUNDARY_TIME(MATCH_ANY_REGEX) {
542
543 @Override
544 public void parse(final String line, final ParseInfo pi) {
545 pi.segmentBoundaryTimes.add(Double.parseDouble(SEPARATOR.split(line.trim())[0]));
546 }
547
548 @Override
549 public Iterable<LineParser> allowedNext() {
550 return Arrays.asList(END_SEGMENT_BOUNDARY_TIMES, SEGMENT_BOUNDARY_TIME);
551 }
552
553 },
554
555
556 END_SEGMENT_BOUNDARY_TIMES("^\\s*END SegmentBoundaryTimes\\s*(#.*)?$") {
557
558 @Override
559 public void parse(final String line, final ParseInfo pi) {
560
561 }
562
563 @Override
564 public Iterable<LineParser> allowedNext() {
565 return KEYWORDS;
566 }
567
568 },
569
570
571 EPHEMERIS_TIME_POS("^\\s*EphemerisTimePos\\s*(#.*)?$") {
572
573 @Override
574 public void parse(final String line, final ParseInfo pi) {
575 pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_P;
576 }
577
578 @Override
579 public Iterable<LineParser> allowedNext() {
580 return Collections.singleton(EPHEMERIS_TIME_POS_DATUM);
581 }
582
583 },
584
585
586 EPHEMERIS_TIME_POS_DATUM(MATCH_ANY_REGEX) {
587
588 @Override
589 public void parse(final String line, final ParseInfo pi) {
590 final String[] tokens = SEPARATOR.split(line.trim());
591 final double time = Double.parseDouble(tokens[0]);
592 final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor;
593 final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor;
594 final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor;
595
596 final Vector3D position = new Vector3D(px, py, pz);
597 final Vector3D velocity = Vector3D.ZERO;
598
599 pi.addEphemeris(time, new PVCoordinates(position, velocity));
600 }
601
602 @Override
603 public Iterable<LineParser> allowedNext() {
604 return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_DATUM);
605 }
606
607 },
608
609
610 EPHEMERIS_TIME_POS_VEL("^\\s*EphemerisTimePosVel\\s*(#.*)?$") {
611
612 @Override
613 public void parse(final String line, final ParseInfo pi) {
614 pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_PV;
615 }
616
617 @Override
618 public Iterable<LineParser> allowedNext() {
619 return Collections.singleton(EPHEMERIS_TIME_POS_VEL_DATUM);
620 }
621
622 },
623
624
625 EPHEMERIS_TIME_POS_VEL_DATUM(MATCH_ANY_REGEX) {
626
627 @Override
628 public void parse(final String line, final ParseInfo pi) {
629 final String[] tokens = SEPARATOR.split(line.trim());
630 final double time = Double.parseDouble(tokens[0]);
631 final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor;
632 final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor;
633 final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor;
634 final double vx = Double.parseDouble(tokens[4]) * pi.distanceUnit.conversionToMetersFactor;
635 final double vy = Double.parseDouble(tokens[5]) * pi.distanceUnit.conversionToMetersFactor;
636 final double vz = Double.parseDouble(tokens[6]) * pi.distanceUnit.conversionToMetersFactor;
637
638 final Vector3D position = new Vector3D(px, py, pz);
639 final Vector3D velocity = new Vector3D(vx, vy, vz);
640
641 pi.addEphemeris(time, new PVCoordinates(position, velocity));
642 }
643
644 @Override
645 public Iterable<LineParser> allowedNext() {
646 return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_VEL_DATUM);
647 }
648
649 },
650
651
652 EPHEMERIS_TIME_POS_VEL_ACC("^\\s*EphemerisTimePosVelAcc\\s*(#.*)?$") {
653
654 @Override
655 public void parse(final String line, final ParseInfo pi) {
656 pi.cartesianDerivativesFilter = CartesianDerivativesFilter.USE_PVA;
657 }
658
659 @Override
660 public Iterable<LineParser> allowedNext() {
661 return Collections.singleton(EPHEMERIS_TIME_POS_VEL_ACC_DATUM);
662 }
663
664 },
665
666
667 EPHEMERIS_TIME_POS_VEL_ACC_DATUM(MATCH_ANY_REGEX) {
668
669 @Override
670 public void parse(final String line, final ParseInfo pi) {
671 final String[] tokens = SEPARATOR.split(line.trim());
672 final double time = Double.parseDouble(tokens[0]);
673 final double px = Double.parseDouble(tokens[1]) * pi.distanceUnit.conversionToMetersFactor;
674 final double py = Double.parseDouble(tokens[2]) * pi.distanceUnit.conversionToMetersFactor;
675 final double pz = Double.parseDouble(tokens[3]) * pi.distanceUnit.conversionToMetersFactor;
676 final double vx = Double.parseDouble(tokens[4]) * pi.distanceUnit.conversionToMetersFactor;
677 final double vy = Double.parseDouble(tokens[5]) * pi.distanceUnit.conversionToMetersFactor;
678 final double vz = Double.parseDouble(tokens[6]) * pi.distanceUnit.conversionToMetersFactor;
679 final double ax = Double.parseDouble(tokens[7]) * pi.distanceUnit.conversionToMetersFactor;
680 final double ay = Double.parseDouble(tokens[8]) * pi.distanceUnit.conversionToMetersFactor;
681 final double az = Double.parseDouble(tokens[9]) * pi.distanceUnit.conversionToMetersFactor;
682
683 final Vector3D position = new Vector3D(px, py, pz);
684 final Vector3D velocity = new Vector3D(vx, vy, vz);
685 final Vector3D acceleration = new Vector3D(ax, ay, az);
686
687 pi.addEphemeris(time, new PVCoordinates(position, velocity, acceleration));
688 }
689
690 @Override
691 public Iterable<LineParser> allowedNext() {
692 return Arrays.asList(END_EPHEMERIS, EPHEMERIS_TIME_POS_VEL_ACC_DATUM);
693 }
694
695 },
696
697
698 END_EPHEMERIS("\\s*END Ephemeris\\s*(#.*)?") {
699
700 @Override
701 public void parse(final String line, final ParseInfo pi) {
702 pi.complete();
703 }
704
705 @Override
706 public Iterable<LineParser> allowedNext() {
707 return Collections.emptyList();
708 }
709
710 };
711
712
713 private final Pattern pattern;
714
715
716
717
718
719 LineParser(final String regex) {
720 pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
721 }
722
723
724
725
726
727
728 public abstract void parse(String line, ParseInfo pi);
729
730
731
732
733
734 public abstract Iterable<LineParser> allowedNext();
735
736
737
738
739
740
741 public boolean canHandle(final String line) {
742 return pattern.matcher(line).matches();
743 }
744
745 }
746
747
748 private enum STKDistanceUnit {
749
750
751 KILOMETERS(1000.0),
752
753
754 METERS(1.0);
755
756
757 private final double conversionToMetersFactor;
758
759
760
761
762
763
764 STKDistanceUnit(final double conversionToMetersFactor) {
765 this.conversionToMetersFactor = conversionToMetersFactor;
766 }
767
768 }
769
770 }