1 /* Copyright 2002-2026 CS GROUP
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.orekit.files.ccsds.ndm.cdm;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Optional;
22
23 import org.orekit.annotation.Nullable;
24 import org.orekit.bodies.CelestialBody;
25 import org.orekit.data.DataContext;
26 import org.orekit.errors.OrekitException;
27 import org.orekit.errors.OrekitMessages;
28 import org.orekit.files.ccsds.definitions.BodyFacade;
29 import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
30 import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
31 import org.orekit.files.ccsds.definitions.FrameFacade;
32 import org.orekit.files.ccsds.definitions.TimeSystem;
33 import org.orekit.files.ccsds.definitions.YesNoUnknown;
34 import org.orekit.files.ccsds.ndm.odm.ocm.ObjectType;
35 import org.orekit.files.ccsds.section.Metadata;
36 import org.orekit.frames.Frame;
37
38 /**
39 * This class gathers the meta-data present in the Conjunction Data Message (CDM).
40 * @author Melina Vanel
41 * @since 11.2
42 */
43 public class CdmMetadata extends Metadata {
44
45 /** CDM relative metadata. */
46 private CdmRelativeMetadata relativeMetadata;
47
48 /** Refering to object 1 or 2. */
49 private String object;
50
51 /** Unique satellite identification designator for the object. */
52 private String objectDesignator;
53
54 /** Specification of satellite catalog source. */
55 private String catalogName;
56
57 /** Object name. */
58 private String objectName;
59
60 /** International designator for the object as assigned by the UN Committee
61 * on Space Research (COSPAR) and the US National Space Science Data Center (NSSDC). */
62 private String internationalDesignator;
63
64 /** Type of object. */
65 @Nullable
66 private ObjectType objectType;
67
68 /** Operator contact position for the space object. */
69 @Nullable
70 private String operatorContact;
71
72 /** Operator organization for the space object. */
73 @Nullable
74 private String operatorOrganization;
75
76 /** Operator phone for the space object. */
77 @Nullable
78 private String operatorPhone;
79
80 /** Operator email for the space object. */
81 @Nullable
82 private String operatorEmail;
83
84 /** Unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message. */
85 @Nullable
86 private String odmMsgLink;
87
88 /** Unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message. */
89 @Nullable
90 private String admMsgLink;
91
92 /** Unique name of the external ephemeris file used for the object or NONE. */
93 private String ephemName;
94
95 /** Flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
96 * specified by CONJUNCTION_ID. */
97 @Nullable
98 private YesNoUnknown obsBeforeNextMessage;
99
100 /** Operator email for the space object. */
101 private CovarianceMethod covarianceMethod;
102
103 /** Maneuver capacity. */
104 private Maneuvrable maneuverable;
105
106 /** Central body around which Object1 and 2 are orbiting. */
107 @Nullable
108 private BodyFacade orbitCenter;
109
110 /** Reference frame in which state vector data are given. */
111 private FrameFacade refFrame;
112
113 /** Gravity model name. */
114 @Nullable
115 private String gravityModel;
116
117 /** Degree of the gravity model. */
118 @Nullable
119 private Integer gravityDegree;
120
121 /** Order of the gravity model. */
122 @Nullable
123 private Integer gravityOrder;
124
125 /** Name of atmospheric model. */
126 @Nullable
127 private String atmosphericModel;
128
129 /** N-body perturbation bodies. */
130 private List<BodyFacade> nBodyPerturbations;
131
132 /** Solar radiation pressure taken indicator.
133 * <p>
134 * STANDARD CCSDS saying YES/NO choice and optional
135 * </p>
136 */
137 @Nullable
138 private YesNoUnknown isSolarRadPressure;
139
140 /** Is solid Earth and ocean tides taken into account or not. STANDARD CCSDS saying YES/NO choice and optional */
141 @Nullable
142 private YesNoUnknown isEarthTides;
143
144 /** Is in-track thrust modelling used or not. STANDARD CCSDS saying YES/NO choice and optional */
145 @Nullable
146 private YesNoUnknown isIntrackThrustModeled;
147
148 /** The source from which the covariance data used in the report for both Object 1 and Object 2 originates. */
149 @Nullable
150 private String covarianceSource;
151
152 /** Flag indicating the type of alternate covariance information provided. */
153 @Nullable
154 private AltCovarianceType altCovType;
155
156 /** Reference frame in which the alternate covariance data are given. */
157 @Nullable
158 private FrameFacade altCovRefFrame;
159
160 /** Simple constructor.
161 *
162 * @param dataContext data context
163 * @param frameMapper for creating an Orekit {@link Frame}.
164 * @since 13.1.5
165 */
166 public CdmMetadata(final DataContext dataContext,
167 final CcsdsFrameMapper frameMapper) {
168 super(null, frameMapper);
169 this.nBodyPerturbations = new ArrayList<>();
170 final CelestialBody earth = dataContext.getCelestialBodies().getEarth();
171 orbitCenter = new BodyFacade(earth.getName().toUpperCase(), earth);
172 }
173
174 /** {@inheritDoc} */
175 @Override
176 public void validate(final double version) {
177 // We only check values that are mandatory in a cdm file
178 checkNotNull(object, CdmMetadataKey.OBJECT.name());
179 checkNotNull(objectDesignator, CdmMetadataKey.OBJECT_DESIGNATOR.name());
180 checkNotNull(catalogName, CdmMetadataKey.CATALOG_NAME.name());
181 checkNotNull(objectName, CdmMetadataKey.OBJECT_NAME.name());
182 checkNotNull(internationalDesignator, CdmMetadataKey.INTERNATIONAL_DESIGNATOR.name());
183 checkNotNull(ephemName, CdmMetadataKey.EPHEMERIS_NAME.name());
184 checkNotNull(covarianceMethod, CdmMetadataKey.COVARIANCE_METHOD.name());
185 checkNotNull(maneuverable, CdmMetadataKey.MANEUVERABLE.name());
186 checkNotNull(refFrame, CdmMetadataKey.REF_FRAME.name());
187 }
188
189 /**
190 * Get the relative metadata following header, they are the common metadata for the CDM.
191 * @return relativeMetadata relative metadata
192 */
193 public CdmRelativeMetadata getRelativeMetadata() {
194 return relativeMetadata;
195 }
196
197 /**
198 * Set the relative metadata following header, they are the common metadata for the CDM.
199 * @param relativeMetadata relative metadata
200 */
201 public void setRelativeMetadata(final CdmRelativeMetadata relativeMetadata) {
202 this.relativeMetadata = relativeMetadata;
203 }
204
205 /**
206 * Get the object name for which metadata are given.
207 * @return the object name
208 */
209 public String getObject() {
210 return object;
211 }
212
213 /**
214 * Set the object name for which metadata are given.
215 * @param object = object 1 or 2 to be set
216 */
217 public void setObject(final String object) {
218 this.setTimeSystem(TimeSystem.UTC);
219 refuseFurtherComments();
220 this.object = object;
221 }
222
223 /**
224 * Get the object satellite catalog designator for which metadata are given.
225 * @return the satellite catalog designator for the object
226 */
227 public String getObjectDesignator() {
228 return objectDesignator;
229 }
230
231 /**
232 * Set the satellite designator for the object for which metadata are given.
233 * @param objectDesignator for the spacecraft to be set
234 */
235 public void setObjectDesignator(final String objectDesignator) {
236 refuseFurtherComments();
237 this.objectDesignator = objectDesignator;
238 }
239
240 /**
241 * Get the satellite catalog used for the object.
242 * @return the catalog name
243 */
244 public String getCatalogName() {
245 return catalogName;
246 }
247
248 /**
249 * Set the satellite catalog name used for object.
250 * @param catalogName for the spacecraft to be set
251 */
252 public void setCatalogName(final String catalogName) {
253 refuseFurtherComments();
254 this.catalogName = catalogName;
255 }
256
257 /**
258 * Get the spacecraft name for the object.
259 * @return the spacecraft name
260 */
261 public String getObjectName() {
262 return objectName;
263 }
264
265 /**
266 * Set the spacecraft name used for object.
267 * @param objectName for the spacecraft to be set
268 */
269 public void setObjectName(final String objectName) {
270 refuseFurtherComments();
271 this.objectName = objectName;
272 }
273
274 /**
275 * Get the international designator for the object.
276 * @return the international designator
277 */
278 public String getInternationalDes() {
279 return internationalDesignator;
280 }
281
282 /**
283 * Set the international designator used for object.
284 * @param internationalDes for the object to be set
285 */
286 public void setInternationalDes(final String internationalDes) {
287 refuseFurtherComments();
288 this.internationalDesignator = internationalDes;
289 }
290
291 /**
292 * Get the type of object.
293 * @return the object type
294 */
295 public Optional<ObjectType> getObjectType() {
296 return Optional.ofNullable(objectType);
297 }
298
299 /**
300 * Set the type of object.
301 * @param objectType type of object
302 */
303 public void setObjectType(final ObjectType objectType) {
304 refuseFurtherComments();
305 this.objectType = objectType;
306 }
307
308 /**
309 * Get the contact position of the owner / operator of the object.
310 * @return the contact position
311 */
312 public Optional<String> getOperatorContactPosition() {
313 return Optional.ofNullable(operatorContact);
314 }
315
316 /**
317 * Set the contact position for the object owner / operator.
318 * @param opContact for the object to be set
319 */
320 public void setOperatorContactPosition(final String opContact) {
321 refuseFurtherComments();
322 this.operatorContact = opContact;
323 }
324
325 /**
326 * Get the contact organisation of the object.
327 * @return the contact organisation
328 */
329 public Optional<String> getOperatorOrganization() {
330 return Optional.ofNullable(operatorOrganization);
331 }
332
333 /**
334 * Set the contact organisation of the object.
335 * @param operatorOrganization contact organisation for the object to be set
336 */
337 public void setOperatorOrganization(final String operatorOrganization) {
338 refuseFurtherComments();
339 this.operatorOrganization = operatorOrganization;
340 }
341
342 /**
343 * Get the contact phone of the operator of the object.
344 * @return the operator phone
345 */
346 public Optional<String> getOperatorPhone() {
347 return Optional.ofNullable(operatorPhone);
348 }
349
350 /**
351 * Set the operator phone of the object.
352 * @param operatorPhone contact phone for the object to be set
353 */
354 public void setOperatorPhone(final String operatorPhone) {
355 refuseFurtherComments();
356 this.operatorPhone = operatorPhone;
357 }
358
359 /**
360 * Get the email of the operator of the object.
361 * @return the operator email
362 */
363 public Optional<String> getOperatorEmail() {
364 return Optional.ofNullable(operatorEmail);
365 }
366
367 /**
368 * Set the object operator email.
369 * @param operatorEmail operator email for the object to be set
370 */
371 public void setOperatorEmail(final String operatorEmail) {
372 refuseFurtherComments();
373 this.operatorEmail = operatorEmail;
374 }
375
376 /**
377 * Get the unique name of the external ephemeris used for OD.
378 * @return the name of ephemeris used
379 */
380 public String getEphemName() {
381 return ephemName;
382 }
383
384 /**
385 * Set the name of external ephemeris used for OD.
386 * @param ephemName me of external ephemeris used
387 */
388 public void setEphemName(final String ephemName) {
389 refuseFurtherComments();
390 this.ephemName = ephemName;
391 }
392
393 /**
394 * Get the method name used to calculate covariance during OD.
395 * @return the name of covariance calculation method
396 */
397 public CovarianceMethod getCovarianceMethod() {
398 return covarianceMethod;
399 }
400
401 /**
402 * Set the method name used to calculate covariance during OD.
403 * @param covarianceMethod method name for covariance calculation
404 */
405 public void setCovarianceMethod(final CovarianceMethod covarianceMethod) {
406 refuseFurtherComments();
407 this.covarianceMethod = covarianceMethod;
408 }
409
410 /**
411 * Get the ability of object to maneuver or not.
412 * @return the ability to maneuver
413 */
414 public Maneuvrable getManeuverable() {
415 return maneuverable;
416 }
417
418 /**
419 * Set the object maneuver ability.
420 * @param maneuverable ability to maneuver
421 */
422 public void setManeuverable(final Maneuvrable maneuverable) {
423 refuseFurtherComments();
424 this.maneuverable = maneuverable;
425 }
426
427 /**
428 * Get the central body for object 1 and 2.
429 * @return the name of the central body
430 */
431 public Optional<BodyFacade> getOrbitCenter() {
432 return Optional.ofNullable(orbitCenter);
433 }
434
435 /**
436 * Set the central body name for object 1 and 2.
437 * @param orbitCenter name of the central body
438 */
439 public void setOrbitCenter(final BodyFacade orbitCenter) {
440 refuseFurtherComments();
441 this.orbitCenter = orbitCenter;
442 }
443
444 /**
445 * Get the reference frame in which data are given: used for state vector and
446 * Keplerian elements data (and for the covariance reference frame if none is given).
447 *
448 * @return the reference frame
449 */
450 public Frame getFrame() {
451 // CDM format does not allow specifying frame epoch
452 return getFrameMapper().buildCcsdsFrame(orbitCenter, refFrame, null);
453 }
454
455 /**
456 * Get the value of {@code REF_FRAME} as an Orekit {@link Frame}. The {@code
457 * ORBIT_CENTER} key word has not been applied yet, so the returned frame may not
458 * correspond to the reference frame of the data in the file.
459 * @return the reference frame
460 */
461 public FrameFacade getRefFrame() {
462 return refFrame;
463 }
464
465 /**
466 * Set the name of the reference frame in which the state vector data are given.
467 * @param refFrame reference frame
468 */
469 public void setRefFrame(final FrameFacade refFrame) {
470 refuseFurtherComments();
471 this.refFrame = refFrame;
472 }
473
474 /** Get gravity model name.
475 * @return gravity model name
476 */
477 public Optional<String> getGravityModel() {
478 return Optional.ofNullable(gravityModel);
479 }
480
481 /** Get degree of the gravity model.
482 * @return degree of the gravity model
483 */
484 public Optional<Integer> getGravityDegree() {
485 return Optional.ofNullable(gravityDegree);
486 }
487
488 /** Get order of the gravity model.
489 * @return order of the gravity model
490 */
491 public Optional<Integer> getGravityOrder() {
492 return Optional.ofNullable(gravityOrder);
493 }
494
495 /** Set gravity model.
496 * @param name name of the model
497 * @param degree degree of the model
498 * @param order order of the model
499 */
500 public void setGravityModel(final String name, final int degree, final int order) {
501 refuseFurtherComments();
502 this.gravityModel = name;
503 this.gravityDegree = degree;
504 this.gravityOrder = order;
505 }
506
507 /** Get name of atmospheric model.
508 * @return name of atmospheric model
509 */
510 public Optional<String> getAtmosphericModel() {
511 return Optional.ofNullable(atmosphericModel);
512 }
513
514 /** Set name of atmospheric model.
515 * @param atmosphericModel name of atmospheric model
516 */
517 public void setAtmosphericModel(final String atmosphericModel) {
518 refuseFurtherComments();
519 this.atmosphericModel = atmosphericModel;
520 }
521
522 /** Get n-body perturbation bodies.
523 * @return n-body perturbation bodies
524 */
525 public List<BodyFacade> getNBodyPerturbations() {
526 return nBodyPerturbations;
527 }
528
529 /** Set n-body perturbation bodies.
530 * @param nBody n-body perturbation bodies
531 */
532 public void setNBodyPerturbations(final List<BodyFacade> nBody) {
533 refuseFurtherComments();
534 this.nBodyPerturbations = nBody;
535 }
536
537 /**
538 * Get Enum YesNoUnknown that indicates if Solar Radiation Pressure is taken into account or not.
539 * @return isSolarRadPressure YesNoUnknown
540 */
541 public Optional<YesNoUnknown> getSolarRadiationPressure() {
542 return Optional.ofNullable(isSolarRadPressure);
543 }
544
545 /**
546 * Set Enum that indicates if Solar Radiation Pressure is taken into account or not.
547 * @param isSolRadPressure YesNoUnknown
548 */
549 public void setSolarRadiationPressure(final YesNoUnknown isSolRadPressure) {
550 refuseFurtherComments();
551 this.isSolarRadPressure = isSolRadPressure;
552 }
553
554 /**
555 * Get Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
556 * @return isEarthTides YesNoUnknown
557 */
558 public Optional<YesNoUnknown> getEarthTides() {
559 return Optional.ofNullable(isEarthTides);
560 }
561
562 /**
563 * Set Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
564 * @param EarthTides YesNoUnknown
565 */
566 public void setEarthTides(final YesNoUnknown EarthTides) {
567 refuseFurtherComments();
568 this.isEarthTides = EarthTides;
569 }
570
571 /**
572 * Get Enum YesNoUnknown that indicates if intrack thrust modeling was into account or not.
573 * @return isEarthTides YesNoUnknown
574 */
575 public Optional<YesNoUnknown> getIntrackThrust() {
576 return Optional.ofNullable(isIntrackThrustModeled);
577 }
578
579 /**
580 * Set boolean that indicates if intrack thrust modeling was into account or not.
581 * @param IntrackThrustModeled YesNoUnknown
582 */
583 public void setIntrackThrust(final YesNoUnknown IntrackThrustModeled) {
584 refuseFurtherComments();
585 this.isIntrackThrustModeled = IntrackThrustModeled;
586 }
587
588 /** Get the source of the covariance data.
589 * @return the covarianceSource
590 */
591 public Optional<String> getCovarianceSource() {
592 return Optional.ofNullable(covarianceSource);
593 }
594
595 /** Set the source of the covariance data.
596 * @param covarianceSource the covarianceSource to set
597 */
598 public void setCovarianceSource(final String covarianceSource) {
599 refuseFurtherComments();
600 this.covarianceSource = covarianceSource;
601 }
602
603 /** Get the flag indicating the type of alternate covariance information provided.
604 * @return the altCovType
605 */
606 public Optional<AltCovarianceType> getAltCovType() {
607 return Optional.ofNullable(altCovType);
608 }
609
610 /** Set the flag indicating the type of alternate covariance information provided.
611 * @param altCovType the altCovType to set
612 */
613 public void setAltCovType(final AltCovarianceType altCovType) {
614 refuseFurtherComments();
615 this.altCovType = altCovType;
616 }
617
618 /**
619 * Get the value of {@code ALT_COV_REF_FRAME} as an Orekit {@link Frame}.
620 * @return the reference frame
621 */
622 public Optional<FrameFacade> getAltCovRefFrame() {
623 return Optional.ofNullable(altCovRefFrame);
624 }
625
626 /**
627 * Set the name of the reference frame in which the alternate covariance data are given.
628 * @param altCovRefFrame alternate covariance reference frame
629 */
630 public void setAltCovRefFrame(final FrameFacade altCovRefFrame) {
631 refuseFurtherComments();
632
633 getAltCovType().
634 orElseThrow(() -> new OrekitException(OrekitMessages.CCSDS_MISSING_KEYWORD,
635 CdmMetadataKey.ALT_COV_TYPE));
636
637 altCovRefFrame.
638 asFrame().
639 orElseThrow(() -> new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME,
640 altCovRefFrame.getName()));
641
642 // Only set the frame if within the allowed options: GCRF, EME2000, ITRF
643 final CelestialBodyFrame celestialBodyFrame = altCovRefFrame.asCelestialBodyFrame().orElseThrow();
644 if ( celestialBodyFrame == CelestialBodyFrame.GCRF ||
645 celestialBodyFrame == CelestialBodyFrame.EME2000 ||
646 celestialBodyFrame.name().contains("ITRF") ) {
647 this.altCovRefFrame = altCovRefFrame;
648 } else {
649 throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, altCovRefFrame.getName());
650 }
651 }
652
653 /**
654 * Get the reference frame in which the alternative covariance data is
655 * given.
656 * <p>
657 * Can be empty if {@link #getAltCovType()} is empty.
658 * </p>
659 * @return alternative covariance reference frame.
660 * @see #getAltCovType()
661 * @see #getAltCovRefFrame()
662 * @since 13.1.5
663 */
664 public Optional<Frame> getAltCovFrame() {
665 // Epoch of AltCovRefFrame can't be specified.
666 return getAltCovRefFrame().isEmpty() ?
667 Optional.empty() :
668 Optional.of(getFrameMapper().buildCcsdsFrame(getAltCovRefFrame().get(), null));
669 }
670
671 /** Get the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
672 * @return the odmMsgLink
673 */
674 public Optional<String> getOdmMsgLink() {
675 return Optional.ofNullable(odmMsgLink);
676 }
677
678 /** Set the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
679 * @param odmMsgLink the odmMsgLink to set
680 */
681 public void setOdmMsgLink(final String odmMsgLink) {
682 refuseFurtherComments();
683 this.odmMsgLink = odmMsgLink;
684 }
685
686 /** Get the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
687 * @return the admMsgLink
688 */
689 public Optional<String> getAdmMsgLink() {
690 return Optional.ofNullable(admMsgLink);
691 }
692
693 /** Set the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
694 * @param admMsgLink the admMsgLink to set
695 */
696 public void setAdmMsgLink(final String admMsgLink) {
697 refuseFurtherComments();
698 this.admMsgLink = admMsgLink;
699 }
700
701 /** Get the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
702 * specified by CONJUNCTION_ID.
703 * @return the obsBeforeNextMessage
704 */
705 public Optional<YesNoUnknown> getObsBeforeNextMessage() {
706 return Optional.ofNullable(obsBeforeNextMessage);
707 }
708
709 /** Set the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
710 * specified by CONJUNCTION_ID.
711 * @param obsBeforeNextMessage the obsBeforeNextMessage to set
712 */
713 public void setObsBeforeNextMessage(final YesNoUnknown obsBeforeNextMessage) {
714 refuseFurtherComments();
715 this.obsBeforeNextMessage = obsBeforeNextMessage;
716 }
717 }