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     /** Is solar radiation pressure taken into account or not ? STANDARD CCSDS saying YES/NO choice and optional */
133     @Nullable
134     private YesNoUnknown isSolarRadPressure;
135 
136     /** Is solid Earth and ocean tides taken into account or not. STANDARD CCSDS saying YES/NO choice and optional */
137     @Nullable
138     private YesNoUnknown isEarthTides;
139 
140     /** Is in-track thrust modelling used or not. STANDARD CCSDS saying YES/NO choice and optional */
141     @Nullable
142     private YesNoUnknown isIntrackThrustModeled;
143 
144     /** The source from which the covariance data used in the report for both Object 1 and Object 2 originates. */
145     @Nullable
146     private String covarianceSource;
147 
148     /** Flag indicating the type of alternate covariance information provided. */
149     @Nullable
150     private AltCovarianceType altCovType;
151 
152     /** Reference frame in which the alternate covariance data are given. */
153     @Nullable
154     private FrameFacade altCovRefFrame;
155 
156     /** Simple constructor.
157      *
158      * @param dataContext data context
159      * @param frameMapper for creating an Orekit {@link Frame}.
160      * @since 13.1.5
161      */
162     public CdmMetadata(final DataContext dataContext,
163                        final CcsdsFrameMapper frameMapper) {
164         super(null, frameMapper);
165         this.nBodyPerturbations = new ArrayList<>();
166         final CelestialBody earth = dataContext.getCelestialBodies().getEarth();
167         orbitCenter = new BodyFacade(earth.getName().toUpperCase(), earth);
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public void validate(final double version) {
173         // We only check values that are mandatory in a cdm file
174         checkNotNull(object,                  CdmMetadataKey.OBJECT.name());
175         checkNotNull(objectDesignator,        CdmMetadataKey.OBJECT_DESIGNATOR.name());
176         checkNotNull(catalogName,             CdmMetadataKey.CATALOG_NAME.name());
177         checkNotNull(objectName,              CdmMetadataKey.OBJECT_NAME.name());
178         checkNotNull(internationalDesignator, CdmMetadataKey.INTERNATIONAL_DESIGNATOR.name());
179         checkNotNull(ephemName,               CdmMetadataKey.EPHEMERIS_NAME.name());
180         checkNotNull(covarianceMethod,        CdmMetadataKey.COVARIANCE_METHOD.name());
181         checkNotNull(maneuverable,            CdmMetadataKey.MANEUVERABLE.name());
182         checkNotNull(refFrame,                CdmMetadataKey.REF_FRAME.name());
183     }
184 
185     /**
186      * Get the relative metadata following header, they are the common metadata for the CDM.
187      * @return relativeMetadata relative metadata
188      */
189     public CdmRelativeMetadata getRelativeMetadata() {
190         return relativeMetadata;
191     }
192 
193     /**
194      * Set the relative metadata following header, they are the common metadata for the CDM.
195      * @param relativeMetadata relative metadata
196      */
197     public void setRelativeMetadata(final CdmRelativeMetadata relativeMetadata) {
198         this.relativeMetadata = relativeMetadata;
199     }
200 
201     /**
202      * Get the object name for which metadata are given.
203      * @return the object name
204      */
205     public String getObject() {
206         return object;
207     }
208 
209     /**
210      * Set the object name for which metadata are given.
211      * @param object = object 1 or 2 to be set
212      */
213     public void setObject(final String object) {
214         this.setTimeSystem(TimeSystem.UTC);
215         refuseFurtherComments();
216         this.object = object;
217     }
218 
219     /**
220      * Get the object satellite catalog designator for which metadata are given.
221      * @return the satellite catalog designator for the object
222      */
223     public String getObjectDesignator() {
224         return objectDesignator;
225     }
226 
227     /**
228      * Set the satellite designator for the object for which metadata are given.
229      * @param objectDesignator for the spacecraft to be set
230      */
231     public void setObjectDesignator(final String objectDesignator) {
232         refuseFurtherComments();
233         this.objectDesignator = objectDesignator;
234     }
235 
236     /**
237      * Get the satellite catalog used for the object.
238      * @return the catalog name
239      */
240     public String getCatalogName() {
241         return catalogName;
242     }
243 
244     /**
245      * Set the satellite catalog name used for object.
246      * @param catalogName for the spacecraft to be set
247      */
248     public void setCatalogName(final String catalogName) {
249         refuseFurtherComments();
250         this.catalogName = catalogName;
251     }
252 
253     /**
254      * Get the spacecraft name for the object.
255      * @return the spacecraft name
256      */
257     public String getObjectName() {
258         return objectName;
259     }
260 
261     /**
262      * Set the spacecraft name used for object.
263      * @param objectName for the spacecraft to be set
264      */
265     public void setObjectName(final String objectName) {
266         refuseFurtherComments();
267         this.objectName = objectName;
268     }
269 
270     /**
271      * Get the international designator for the object.
272      * @return the international designator
273      */
274     public String getInternationalDes() {
275         return internationalDesignator;
276     }
277 
278     /**
279      * Set the international designator used for object.
280      * @param internationalDes for the object to be set
281      */
282     public void setInternationalDes(final String internationalDes) {
283         refuseFurtherComments();
284         this.internationalDesignator = internationalDes;
285     }
286 
287     /**
288      * Get the type of object.
289      * @return the object type
290      */
291     public Optional<ObjectType> getObjectType() {
292         return Optional.ofNullable(objectType);
293     }
294 
295     /**
296      * Set the type of object.
297      * @param objectType type of object
298      */
299     public void setObjectType(final ObjectType objectType) {
300         refuseFurtherComments();
301         this.objectType = objectType;
302     }
303 
304     /**
305      * Get the contact position of the owner / operator of the object.
306      * @return the contact position
307      */
308     public Optional<String> getOperatorContactPosition() {
309         return Optional.ofNullable(operatorContact);
310     }
311 
312     /**
313      * Set the contact position for the object owner / operator.
314      * @param opContact for the object to be set
315      */
316     public void setOperatorContactPosition(final String opContact) {
317         refuseFurtherComments();
318         this.operatorContact = opContact;
319     }
320 
321     /**
322      * Get the contact organisation of the object.
323      * @return the contact organisation
324      */
325     public Optional<String> getOperatorOrganization() {
326         return Optional.ofNullable(operatorOrganization);
327     }
328 
329     /**
330      * Set the contact organisation of the object.
331      * @param operatorOrganization contact organisation for the object to be set
332      */
333     public void setOperatorOrganization(final String operatorOrganization) {
334         refuseFurtherComments();
335         this.operatorOrganization = operatorOrganization;
336     }
337 
338     /**
339      * Get the contact phone of the operator of the object.
340      * @return the operator phone
341      */
342     public Optional<String> getOperatorPhone() {
343         return Optional.ofNullable(operatorPhone);
344     }
345 
346     /**
347      * Set the operator phone of the object.
348      * @param operatorPhone contact phone for the object to be set
349      */
350     public void setOperatorPhone(final String operatorPhone) {
351         refuseFurtherComments();
352         this.operatorPhone = operatorPhone;
353     }
354 
355     /**
356      * Get the email of the operator of the object.
357      * @return the operator email
358      */
359     public Optional<String> getOperatorEmail() {
360         return Optional.ofNullable(operatorEmail);
361     }
362 
363     /**
364      * Set the object operator email.
365      * @param operatorEmail operator email for the object to be set
366      */
367     public void setOperatorEmail(final String operatorEmail) {
368         refuseFurtherComments();
369         this.operatorEmail = operatorEmail;
370     }
371 
372     /**
373      * Get the unique name of the external ephemeris used for OD.
374      * @return the name of ephemeris used
375      */
376     public String getEphemName() {
377         return ephemName;
378     }
379 
380     /**
381      * Set the name of external ephemeris used for OD.
382      * @param ephemName me of external ephemeris used
383      */
384     public void setEphemName(final String ephemName) {
385         refuseFurtherComments();
386         this.ephemName = ephemName;
387     }
388 
389     /**
390      * Get the method name used to calculate covariance during OD.
391      * @return the name of covariance calculation method
392      */
393     public CovarianceMethod getCovarianceMethod() {
394         return covarianceMethod;
395     }
396 
397     /**
398      * Set the method name used to calculate covariance during OD.
399      * @param covarianceMethod method name for covariance calculation
400      */
401     public void setCovarianceMethod(final CovarianceMethod covarianceMethod) {
402         refuseFurtherComments();
403         this.covarianceMethod = covarianceMethod;
404     }
405 
406     /**
407      * Get the ability of object to maneuver or not.
408      * @return the ability to maneuver
409      */
410     public Maneuvrable getManeuverable() {
411         return maneuverable;
412     }
413 
414     /**
415      * Set the object maneuver ability.
416      * @param maneuverable ability to maneuver
417      */
418     public void setManeuverable(final Maneuvrable maneuverable) {
419         refuseFurtherComments();
420         this.maneuverable = maneuverable;
421     }
422 
423     /**
424      * Get the central body for object 1 and 2.
425      * @return the name of the central body
426      */
427     public Optional<BodyFacade> getOrbitCenter() {
428         return Optional.ofNullable(orbitCenter);
429     }
430 
431     /**
432      * Set the central body name for object 1 and 2.
433      * @param orbitCenter name of the central body
434      */
435     public void setOrbitCenter(final BodyFacade orbitCenter) {
436         refuseFurtherComments();
437         this.orbitCenter = orbitCenter;
438     }
439 
440     /**
441      * Get the reference frame in which data are given: used for state vector and
442      * Keplerian elements data (and for the covariance reference frame if none is given).
443      *
444      * @return the reference frame
445      */
446     public Frame getFrame() {
447         // CDM format does not allow specifying frame epoch
448         return getFrameMapper().buildCcsdsFrame(orbitCenter, refFrame, null);
449     }
450 
451     /**
452      * Get the value of {@code REF_FRAME} as an Orekit {@link Frame}. The {@code
453      * ORBIT_CENTER} key word has not been applied yet, so the returned frame may not
454      * correspond to the reference frame of the data in the file.
455      * @return the reference frame
456      */
457     public FrameFacade getRefFrame() {
458         return refFrame;
459     }
460 
461     /**
462      * Set the name of the reference frame in which the state vector data are given.
463      * @param refFrame reference frame
464      */
465     public void setRefFrame(final FrameFacade refFrame) {
466         refuseFurtherComments();
467         this.refFrame = refFrame;
468     }
469 
470     /** Get gravity model name.
471      * @return gravity model name
472      */
473     public Optional<String> getGravityModel() {
474         return Optional.ofNullable(gravityModel);
475     }
476 
477     /** Get degree of the gravity model.
478      * @return degree of the gravity model
479      */
480     public Optional<Integer> getGravityDegree() {
481         return Optional.ofNullable(gravityDegree);
482     }
483 
484     /** Get order of the gravity model.
485      * @return order of the gravity model
486      */
487     public Optional<Integer> getGravityOrder() {
488         return Optional.ofNullable(gravityOrder);
489     }
490 
491     /** Set gravity model.
492      * @param name name of the model
493      * @param degree degree of the model
494      * @param order order of the model
495      */
496     public void setGravityModel(final String name, final int degree, final int order) {
497         refuseFurtherComments();
498         this.gravityModel  = name;
499         this.gravityDegree = degree;
500         this.gravityOrder  = order;
501     }
502 
503     /** Get name of atmospheric model.
504      * @return name of atmospheric model
505      */
506     public Optional<String> getAtmosphericModel() {
507         return Optional.ofNullable(atmosphericModel);
508     }
509 
510     /** Set name of atmospheric model.
511      * @param atmosphericModel name of atmospheric model
512      */
513     public void setAtmosphericModel(final String atmosphericModel) {
514         refuseFurtherComments();
515         this.atmosphericModel = atmosphericModel;
516     }
517 
518     /** Get n-body perturbation bodies.
519      * @return n-body perturbation bodies
520      */
521     public List<BodyFacade> getNBodyPerturbations() {
522         return nBodyPerturbations;
523     }
524 
525     /** Set n-body perturbation bodies.
526      * @param nBody n-body perturbation bodies
527      */
528     public void setNBodyPerturbations(final List<BodyFacade> nBody) {
529         refuseFurtherComments();
530         this.nBodyPerturbations = nBody;
531     }
532 
533     /**
534      * Get Enum YesNoUnknown that indicates if Solar Radiation Pressure is taken into account or not.
535      * @return isSolarRadPressure YesNoUnknown
536      */
537     public Optional<YesNoUnknown> getSolarRadiationPressure() {
538         return Optional.ofNullable(isSolarRadPressure);
539     }
540 
541     /**
542      * Set Enum that indicates if Solar Radiation Pressure is taken into account or not.
543      * @param isSolRadPressure YesNoUnknown
544      */
545     public void setSolarRadiationPressure(final YesNoUnknown isSolRadPressure) {
546         refuseFurtherComments();
547         this.isSolarRadPressure = isSolRadPressure;
548     }
549 
550     /**
551      * Get Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
552      * @return isEarthTides YesNoUnknown
553      */
554     public Optional<YesNoUnknown> getEarthTides() {
555         return Optional.ofNullable(isEarthTides);
556     }
557 
558     /**
559      * Set Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
560      * @param EarthTides YesNoUnknown
561      */
562     public void setEarthTides(final YesNoUnknown EarthTides) {
563         refuseFurtherComments();
564         this.isEarthTides = EarthTides;
565     }
566 
567     /**
568      * Get Enum YesNoUnknown that indicates if intrack thrust modeling was into account or not.
569      * @return isEarthTides YesNoUnknown
570      */
571     public Optional<YesNoUnknown> getIntrackThrust() {
572         return Optional.ofNullable(isIntrackThrustModeled);
573     }
574 
575     /**
576      * Set boolean that indicates if intrack thrust modeling was into account or not.
577      * @param IntrackThrustModeled YesNoUnknown
578      */
579     public void setIntrackThrust(final YesNoUnknown IntrackThrustModeled) {
580         refuseFurtherComments();
581         this.isIntrackThrustModeled = IntrackThrustModeled;
582     }
583 
584     /** Get the source of the covariance data.
585      * @return the covarianceSource
586      */
587     public Optional<String> getCovarianceSource() {
588         return Optional.ofNullable(covarianceSource);
589     }
590 
591     /** Set the source of the covariance data.
592      * @param covarianceSource the covarianceSource to set
593      */
594     public void setCovarianceSource(final String covarianceSource) {
595         refuseFurtherComments();
596         this.covarianceSource = covarianceSource;
597     }
598 
599     /** Get the flag indicating the type of alternate covariance information provided.
600      * @return the altCovType
601      */
602     public Optional<AltCovarianceType> getAltCovType() {
603         return Optional.ofNullable(altCovType);
604     }
605 
606     /** Set the flag indicating the type of alternate covariance information provided.
607      * @param altCovType the altCovType to set
608      */
609     public void setAltCovType(final AltCovarianceType altCovType) {
610         refuseFurtherComments();
611         this.altCovType = altCovType;
612     }
613 
614      /**
615      * Get the value of {@code ALT_COV_REF_FRAME} as an Orekit {@link Frame}.
616      * @return the reference frame
617      */
618     public Optional<FrameFacade> getAltCovRefFrame() {
619         return Optional.ofNullable(altCovRefFrame);
620     }
621 
622     /**
623      * Set the name of the reference frame in which the alternate covariance data are given.
624      * @param altCovRefFrame alternate covariance reference frame
625      */
626     public void setAltCovRefFrame(final FrameFacade altCovRefFrame) {
627         refuseFurtherComments();
628 
629         getAltCovType().
630             orElseThrow(() -> new OrekitException(OrekitMessages.CCSDS_MISSING_KEYWORD,
631                                                   CdmMetadataKey.ALT_COV_TYPE));
632 
633         altCovRefFrame.
634             asFrame().
635             orElseThrow(() -> new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME,
636                                                   altCovRefFrame.getName()));
637 
638         // Only set the frame if within the allowed options: GCRF, EME2000, ITRF
639         final CelestialBodyFrame celestialBodyFrame = altCovRefFrame.asCelestialBodyFrame().orElseThrow();
640         if ( celestialBodyFrame == CelestialBodyFrame.GCRF ||
641                  celestialBodyFrame == CelestialBodyFrame.EME2000 ||
642                      celestialBodyFrame.name().contains("ITRF") ) {
643             this.altCovRefFrame = altCovRefFrame;
644         } else {
645             throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, altCovRefFrame.getName());
646         }
647     }
648 
649     /**
650      * Get the reference frame in which the alternative covariance data is
651      * given.
652      * <p>
653      * Can be empty if {@link #getAltCovType()} is empty.
654      * </p>
655      * @return alternative covariance reference frame.
656      * @see #getAltCovType()
657      * @see #getAltCovRefFrame()
658      * @since 13.1.5
659      */
660     public Optional<Frame> getAltCovFrame() {
661         // Epoch of AltCovRefFrame can't be specified.
662         return getAltCovRefFrame().isEmpty() ?
663                Optional.empty() :
664                Optional.of(getFrameMapper().buildCcsdsFrame(getAltCovRefFrame().get(), null));
665     }
666 
667     /** Get the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
668      * @return the odmMsgLink
669      */
670     public Optional<String> getOdmMsgLink() {
671         return Optional.ofNullable(odmMsgLink);
672     }
673 
674     /** Set the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
675      * @param odmMsgLink the odmMsgLink to set
676      */
677     public void setOdmMsgLink(final String odmMsgLink) {
678         refuseFurtherComments();
679         this.odmMsgLink = odmMsgLink;
680     }
681 
682     /** Get the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
683      * @return the admMsgLink
684      */
685     public Optional<String> getAdmMsgLink() {
686         return Optional.ofNullable(admMsgLink);
687     }
688 
689     /** Set the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
690      * @param admMsgLink the admMsgLink to set
691      */
692     public void setAdmMsgLink(final String admMsgLink) {
693         refuseFurtherComments();
694         this.admMsgLink = admMsgLink;
695     }
696 
697     /** Get the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
698      * specified by CONJUNCTION_ID.
699      * @return the obsBeforeNextMessage
700      */
701     public Optional<YesNoUnknown> getObsBeforeNextMessage() {
702         return Optional.ofNullable(obsBeforeNextMessage);
703     }
704 
705     /** Set the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
706      * specified by CONJUNCTION_ID.
707      * @param obsBeforeNextMessage the obsBeforeNextMessage to set
708      */
709     public void setObsBeforeNextMessage(final YesNoUnknown obsBeforeNextMessage) {
710         refuseFurtherComments();
711         this.obsBeforeNextMessage = obsBeforeNextMessage;
712     }
713 }