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