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 }