1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.ccsds.ndm.odm.ocm;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.function.Function;
24 import java.util.regex.Pattern;
25
26 import org.orekit.bodies.OneAxisEllipsoid;
27 import org.orekit.data.DataContext;
28 import org.orekit.data.DataSource;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitIllegalArgumentException;
31 import org.orekit.errors.OrekitMessages;
32 import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
33 import org.orekit.files.ccsds.ndm.odm.OdmHeader;
34 import org.orekit.files.ccsds.ndm.odm.OdmMetadataKey;
35 import org.orekit.files.ccsds.ndm.odm.OdmParser;
36 import org.orekit.files.ccsds.ndm.odm.UserDefined;
37 import org.orekit.files.ccsds.section.HeaderProcessingState;
38 import org.orekit.files.ccsds.section.KvnStructureProcessingState;
39 import org.orekit.files.ccsds.section.MetadataKey;
40 import org.orekit.files.ccsds.section.Segment;
41 import org.orekit.files.ccsds.section.XmlStructureProcessingState;
42 import org.orekit.files.ccsds.utils.ContextBinding;
43 import org.orekit.files.ccsds.utils.FileFormat;
44 import org.orekit.files.ccsds.utils.lexical.ParseToken;
45 import org.orekit.files.ccsds.utils.lexical.TokenType;
46 import org.orekit.files.ccsds.utils.lexical.UserDefinedXmlTokenBuilder;
47 import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
48 import org.orekit.files.ccsds.utils.parsing.ProcessingState;
49 import org.orekit.files.general.EphemerisFileParser;
50 import org.orekit.time.AbsoluteDate;
51 import org.orekit.utils.IERSConventions;
52 import org.orekit.utils.units.Unit;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public class OcmParser extends OdmParser<Ocm, OcmParser> implements EphemerisFileParser<Ocm> {
68
69
70 private static final Pattern SPLIT_AT_BLANKS = Pattern.compile("\\s+");
71
72
73 private OdmHeader header;
74
75
76 private OcmMetadata metadata;
77
78
79
80
81 private final double equatorialRadius;
82
83
84
85
86 private final double flattening;
87
88
89 private ContextBinding context;
90
91
92 private List<TrajectoryStateHistory> trajectoryBlocks;
93
94
95 private TrajectoryStateHistoryMetadata currentTrajectoryStateHistoryMetadata;
96
97
98 private List<TrajectoryState> currentTrajectoryStateHistory;
99
100
101 private OrbitPhysicalProperties physicBlock;
102
103
104 private List<OrbitCovarianceHistory> covarianceBlocks;
105
106
107 private OrbitCovarianceHistoryMetadata currentCovarianceHistoryMetadata;
108
109
110 private List<OrbitCovariance> currentCovarianceHistory;
111
112
113 private List<OrbitManeuverHistory> maneuverBlocks;
114
115
116 private OrbitManeuverHistoryMetadata currentManeuverHistoryMetadata;
117
118
119 private List<OrbitManeuver> currentManeuverHistory;
120
121
122 private Perturbations perturbationsBlock;
123
124
125 private OrbitDetermination orbitDeterminationBlock;
126
127
128 private UserDefined userDefinedBlock;
129
130
131 private ProcessingState structureProcessor;
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 public OcmParser(final IERSConventions conventions,
151 final double equatorialRadius, final double flattening,
152 final boolean simpleEOP, final DataContext dataContext,
153 final double mu, final ParsedUnitsBehavior parsedUnitsBehavior,
154 final Function<ParseToken, List<ParseToken>>[] filters) {
155 super(Ocm.ROOT, Ocm.FORMAT_VERSION_KEY, conventions, simpleEOP, dataContext, null,
156 mu, parsedUnitsBehavior, filters);
157 this.equatorialRadius = equatorialRadius;
158 this.flattening = flattening;
159 }
160
161
162 @Override
163 public Map<String, XmlTokenBuilder> getSpecialXmlElementsBuilders() {
164
165 final Map<String, XmlTokenBuilder> builders = super.getSpecialXmlElementsBuilders();
166
167
168 builders.put(UserDefined.USER_DEFINED_XML_TAG, new UserDefinedXmlTokenBuilder());
169
170 return builders;
171
172 }
173
174
175 @Override
176 public Ocm parse(final DataSource source) {
177 return parseMessage(source);
178 }
179
180
181 @Override
182 public OdmHeader getHeader() {
183 return header;
184 }
185
186
187 @Override
188 public void reset(final FileFormat fileFormat) {
189 header = new OdmHeader();
190 metadata = null;
191 context = null;
192 trajectoryBlocks = null;
193 physicBlock = null;
194 covarianceBlocks = null;
195 maneuverBlocks = null;
196 perturbationsBlock = null;
197 orbitDeterminationBlock = null;
198 userDefinedBlock = null;
199 if (fileFormat == FileFormat.XML) {
200 structureProcessor = new XmlStructureProcessingState(Ocm.ROOT, this);
201 reset(fileFormat, structureProcessor);
202 } else {
203 structureProcessor = new KvnStructureProcessingState(this);
204 reset(fileFormat, new HeaderProcessingState(this));
205 }
206 }
207
208
209 @Override
210 public boolean prepareHeader() {
211 anticipateNext(new HeaderProcessingState(this));
212 return true;
213 }
214
215
216 @Override
217 public boolean inHeader() {
218 anticipateNext(structureProcessor);
219 return true;
220 }
221
222
223 @Override
224 public boolean finalizeHeader() {
225 header.validate(header.getFormatVersion());
226 return true;
227 }
228
229
230 @Override
231 public boolean prepareMetadata() {
232 if (metadata != null) {
233 return false;
234 }
235 metadata = new OcmMetadata(getDataContext());
236 context = new ContextBinding(this::getConventions, this::isSimpleEOP, this::getDataContext,
237 this::getParsedUnitsBehavior, metadata::getEpochT0, metadata::getTimeSystem,
238 metadata::getSclkOffsetAtEpoch, metadata::getSclkSecPerSISec);
239 anticipateNext(this::processMetadataToken);
240 return true;
241 }
242
243
244 @Override
245 public boolean inMetadata() {
246 anticipateNext(structureProcessor);
247 return true;
248 }
249
250
251 @Override
252 public boolean finalizeMetadata() {
253 metadata.validate(header.getFormatVersion());
254 anticipateNext(this::processDataSubStructureToken);
255 return true;
256 }
257
258
259 @Override
260 public boolean prepareData() {
261 anticipateNext(this::processDataSubStructureToken);
262 return true;
263 }
264
265
266 @Override
267 public boolean inData() {
268 return true;
269 }
270
271
272 @Override
273 public boolean finalizeData() {
274 final List<TrajectoryStateHistory> old = trajectoryBlocks;
275 if (old != null) {
276 final OneAxisEllipsoid body =
277 currentTrajectoryStateHistoryMetadata.getTrajType() == OrbitElementsType.GEODETIC ?
278 new OneAxisEllipsoid(equatorialRadius, flattening,
279 currentTrajectoryStateHistoryMetadata.getTrajReferenceFrame().asFrame()) :
280 null;
281 trajectoryBlocks = new ArrayList<>(old.size());
282 for (final TrajectoryStateHistory osh : old) {
283 trajectoryBlocks.add(new TrajectoryStateHistory(osh.getMetadata(), osh.getTrajectoryStates(),
284 body, getSelectedMu()));
285 }
286 }
287 return true;
288 }
289
290
291
292
293
294
295 boolean manageTrajectoryStateSection(final boolean starting) {
296 if (starting) {
297 if (trajectoryBlocks == null) {
298
299 trajectoryBlocks = new ArrayList<>();
300 }
301 currentTrajectoryStateHistoryMetadata = new TrajectoryStateHistoryMetadata(metadata.getEpochT0(),
302 getDataContext());
303 currentTrajectoryStateHistory = new ArrayList<>();
304 anticipateNext(this::processTrajectoryStateToken);
305 } else {
306 final OneAxisEllipsoid body =
307 currentTrajectoryStateHistoryMetadata.getTrajType() == OrbitElementsType.GEODETIC ?
308 new OneAxisEllipsoid(equatorialRadius, flattening,
309 currentTrajectoryStateHistoryMetadata.getTrajReferenceFrame().asFrame()) :
310 null;
311 anticipateNext(structureProcessor);
312 if (currentTrajectoryStateHistoryMetadata.getCenter().getBody() != null) {
313 setMuCreated(currentTrajectoryStateHistoryMetadata.getCenter().getBody().getGM());
314 }
315
316
317 trajectoryBlocks.add(new TrajectoryStateHistory(currentTrajectoryStateHistoryMetadata,
318 currentTrajectoryStateHistory,
319 body, Double.NaN));
320 }
321 return true;
322 }
323
324
325
326
327
328
329 boolean managePhysicalPropertiesSection(final boolean starting) {
330 if (starting) {
331 if (physicBlock == null) {
332
333 physicBlock = new OrbitPhysicalProperties(metadata.getEpochT0());
334 }
335 anticipateNext(this::processPhysicalPropertyToken);
336 } else {
337 anticipateNext(structureProcessor);
338 }
339 return true;
340 }
341
342
343
344
345
346
347 boolean manageCovarianceHistorySection(final boolean starting) {
348 if (starting) {
349 if (covarianceBlocks == null) {
350
351 covarianceBlocks = new ArrayList<>();
352 }
353 currentCovarianceHistoryMetadata = new OrbitCovarianceHistoryMetadata(metadata.getEpochT0());
354 currentCovarianceHistory = new ArrayList<>();
355 anticipateNext(this::processCovarianceToken);
356 } else {
357 anticipateNext(structureProcessor);
358 covarianceBlocks.add(new OrbitCovarianceHistory(currentCovarianceHistoryMetadata,
359 currentCovarianceHistory));
360 currentCovarianceHistoryMetadata = null;
361 currentCovarianceHistory = null;
362 }
363 return true;
364 }
365
366
367
368
369
370
371 boolean manageManeuversSection(final boolean starting) {
372 if (starting) {
373 if (maneuverBlocks == null) {
374
375 maneuverBlocks = new ArrayList<>();
376 }
377 currentManeuverHistoryMetadata = new OrbitManeuverHistoryMetadata(metadata.getEpochT0());
378 currentManeuverHistory = new ArrayList<>();
379 anticipateNext(this::processManeuverToken);
380 } else {
381 anticipateNext(structureProcessor);
382 maneuverBlocks.add(new OrbitManeuverHistory(currentManeuverHistoryMetadata,
383 currentManeuverHistory));
384 currentManeuverHistoryMetadata = null;
385 currentManeuverHistory = null;
386 }
387 return true;
388 }
389
390
391
392
393
394
395 boolean managePerturbationParametersSection(final boolean starting) {
396 if (starting) {
397 if (perturbationsBlock == null) {
398
399 perturbationsBlock = new Perturbations(context.getDataContext().getCelestialBodies());
400 }
401 anticipateNext(this::processPerturbationToken);
402 } else {
403 anticipateNext(structureProcessor);
404 }
405 return true;
406 }
407
408
409
410
411
412
413 boolean manageOrbitDeterminationSection(final boolean starting) {
414 if (starting) {
415 if (orbitDeterminationBlock == null) {
416
417 orbitDeterminationBlock = new OrbitDetermination();
418 }
419 anticipateNext(this::processOrbitDeterminationToken);
420 } else {
421 anticipateNext(structureProcessor);
422 }
423 return true;
424 }
425
426
427
428
429
430
431 boolean manageUserDefinedParametersSection(final boolean starting) {
432 if (starting) {
433 if (userDefinedBlock == null) {
434
435 userDefinedBlock = new UserDefined();
436 }
437 anticipateNext(this::processUserDefinedToken);
438 } else {
439 anticipateNext(structureProcessor);
440 }
441 return true;
442 }
443
444
445 @Override
446 public Ocm build() {
447
448
449
450 finalizeData();
451 if (userDefinedBlock != null && userDefinedBlock.getParameters().isEmpty()) {
452 userDefinedBlock = null;
453 }
454
455
456 final double mu;
457 if (trajectoryBlocks == null) {
458 mu = Double.NaN;
459 } else {
460 if (perturbationsBlock != null) {
461
462 setMuParsed(perturbationsBlock.getGm());
463 }
464 mu = getSelectedMu();
465 }
466
467 final OcmData data = new OcmData(trajectoryBlocks, physicBlock, covarianceBlocks,
468 maneuverBlocks, perturbationsBlock,
469 orbitDeterminationBlock, userDefinedBlock);
470 data.validate(header.getFormatVersion());
471
472 return new Ocm(header, Collections.singletonList(new Segment<>(metadata, data)),
473 getConventions(), getDataContext(), mu);
474
475 }
476
477
478
479
480
481 private boolean processMetadataToken(final ParseToken token) {
482 inMetadata();
483 try {
484 return token.getName() != null &&
485 MetadataKey.valueOf(token.getName()).process(token, context, metadata);
486 } catch (IllegalArgumentException iaeM) {
487 try {
488 return OdmMetadataKey.valueOf(token.getName()).process(token, context, metadata);
489 } catch (IllegalArgumentException iaeD) {
490 try {
491 return OcmMetadataKey.valueOf(token.getName()).process(token, context, metadata);
492 } catch (IllegalArgumentException iaeC) {
493
494 return false;
495 }
496 }
497 }
498 }
499
500
501
502
503
504 private boolean processDataSubStructureToken(final ParseToken token) {
505 try {
506 return token.getName() != null &&
507 OcmDataSubStructureKey.valueOf(token.getName()).process(token, this);
508 } catch (IllegalArgumentException iae) {
509
510 return false;
511 }
512 }
513
514
515
516
517
518 private boolean processTrajectoryStateToken(final ParseToken token) {
519 if (token.getName() != null && !token.getName().equals(Ocm.TRAJ_LINE)) {
520
521 try {
522 return TrajectoryStateHistoryMetadataKey.valueOf(token.getName()).
523 process(token, context, currentTrajectoryStateHistoryMetadata);
524 } catch (IllegalArgumentException iae) {
525
526 return false;
527 }
528 } else {
529
530 if (currentTrajectoryStateHistory.isEmpty()) {
531
532 currentTrajectoryStateHistoryMetadata.validate(header.getFormatVersion());
533 anticipateNext(this::processDataSubStructureToken);
534 }
535 if (token.getType() == TokenType.START || token.getType() == TokenType.STOP) {
536 return true;
537 }
538 try {
539 final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim());
540
541 final List<Unit> units = currentTrajectoryStateHistoryMetadata.getTrajType().getUnits();
542 if (fields.length != units.size() + 1) {
543 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
544 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
545 }
546 final AbsoluteDate epoch = context.getTimeSystem().getConverter(context).parse(fields[0]);
547 return currentTrajectoryStateHistory.add(new TrajectoryState(currentTrajectoryStateHistoryMetadata.getTrajType(),
548 epoch, fields, 1, units));
549 } catch (NumberFormatException | OrekitIllegalArgumentException e) {
550 throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
551 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
552 }
553 }
554 }
555
556
557
558
559
560 private boolean processPhysicalPropertyToken(final ParseToken token) {
561 if (physicBlock == null) {
562 physicBlock = new OrbitPhysicalProperties(metadata.getEpochT0());
563 }
564 anticipateNext(this::processDataSubStructureToken);
565 try {
566 return token.getName() != null &&
567 OrbitPhysicalPropertiesKey.valueOf(token.getName()).process(token, context, physicBlock);
568 } catch (IllegalArgumentException iae) {
569
570 return false;
571 }
572 }
573
574
575
576
577
578 private boolean processCovarianceToken(final ParseToken token) {
579 if (token.getName() != null && !token.getName().equals(Ocm.COV_LINE)) {
580
581 try {
582 return OrbitCovarianceHistoryMetadataKey.valueOf(token.getName()).
583 process(token, context, currentCovarianceHistoryMetadata);
584 } catch (IllegalArgumentException iae) {
585
586 return false;
587 }
588 } else {
589
590 if (currentCovarianceHistory.isEmpty()) {
591
592 currentCovarianceHistoryMetadata.validate(header.getFormatVersion());
593 anticipateNext(this::processDataSubStructureToken);
594 }
595 if (token.getType() == TokenType.START || token.getType() == TokenType.STOP) {
596 return true;
597 }
598 try {
599 final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim());
600 final int n = currentCovarianceHistoryMetadata.getCovType().getUnits().size();
601 if (fields.length - 1 != currentCovarianceHistoryMetadata.getCovOrdering().nbElements(n)) {
602 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
603 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
604 }
605 currentCovarianceHistory.add(new OrbitCovariance(currentCovarianceHistoryMetadata.getCovType(),
606 currentCovarianceHistoryMetadata.getCovOrdering(),
607 context.getTimeSystem().getConverter(context).parse(fields[0]),
608 fields, 1));
609 return true;
610 } catch (NumberFormatException nfe) {
611 throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
612 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
613 }
614 }
615 }
616
617
618
619
620
621 private boolean processManeuverToken(final ParseToken token) {
622 if (token.getName() != null && !token.getName().equals(Ocm.MAN_LINE)) {
623
624 try {
625 return OrbitManeuverHistoryMetadataKey.valueOf(token.getName()).
626 process(token, context, currentManeuverHistoryMetadata);
627 } catch (IllegalArgumentException iae) {
628
629 return false;
630 }
631 } else {
632
633 if (currentManeuverHistory.isEmpty()) {
634
635 currentManeuverHistoryMetadata.validate(header.getFormatVersion());
636 anticipateNext(this::processDataSubStructureToken);
637 }
638 if (token.getType() == TokenType.START || token.getType() == TokenType.STOP) {
639 return true;
640 }
641 try {
642 final String[] fields = SPLIT_AT_BLANKS.split(token.getRawContent().trim());
643 final List<ManeuverFieldType> types = currentManeuverHistoryMetadata.getManComposition();
644 if (fields.length != types.size()) {
645 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
646 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
647 }
648 final OrbitManeuver maneuver = new OrbitManeuver();
649 for (int i = 0; i < fields.length; ++i) {
650 types.get(i).process(fields[i], context, maneuver, token.getLineNumber(), token.getFileName());
651 }
652 currentManeuverHistory.add(maneuver);
653 return true;
654 } catch (NumberFormatException nfe) {
655 throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
656 token.getLineNumber(), token.getFileName(), token.getContentAsNormalizedString());
657 }
658 }
659 }
660
661
662
663
664
665 private boolean processPerturbationToken(final ParseToken token) {
666 anticipateNext(this::processDataSubStructureToken);
667 try {
668 return token.getName() != null &&
669 PerturbationsKey.valueOf(token.getName()).process(token, context, perturbationsBlock);
670 } catch (IllegalArgumentException iae) {
671
672 return false;
673 }
674 }
675
676
677
678
679
680 private boolean processOrbitDeterminationToken(final ParseToken token) {
681 if (orbitDeterminationBlock == null) {
682 orbitDeterminationBlock = new OrbitDetermination();
683 }
684 anticipateNext(this::processDataSubStructureToken);
685 try {
686 return token.getName() != null &&
687 OrbitDeterminationKey.valueOf(token.getName()).process(token, context, orbitDeterminationBlock);
688 } catch (IllegalArgumentException iae) {
689
690 return false;
691 }
692 }
693
694
695
696
697
698 private boolean processUserDefinedToken(final ParseToken token) {
699 if (userDefinedBlock == null) {
700 userDefinedBlock = new UserDefined();
701 }
702 anticipateNext(this::processDataSubStructureToken);
703 if ("COMMENT".equals(token.getName())) {
704 return token.getType() == TokenType.ENTRY ? userDefinedBlock.addComment(token.getContentAsNormalizedString()) : true;
705 } else if (token.getName().startsWith(UserDefined.USER_DEFINED_PREFIX)) {
706 if (token.getType() == TokenType.ENTRY) {
707 userDefinedBlock.addEntry(token.getName().substring(UserDefined.USER_DEFINED_PREFIX.length()),
708 token.getContentAsNormalizedString());
709 }
710 return true;
711 } else {
712
713 return false;
714 }
715 }
716
717 }