CdmMetadata.java

/* Copyright 2002-2023 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.files.ccsds.ndm.cdm;

import java.util.List;

import org.orekit.annotation.DefaultDataContext;
import org.orekit.bodies.CelestialBody;
import org.orekit.bodies.CelestialBodyFactory;
import org.orekit.data.DataContext;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.definitions.YesNoUnknown;
import org.orekit.files.ccsds.definitions.BodyFacade;
import org.orekit.files.ccsds.definitions.FrameFacade;
import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
import org.orekit.files.ccsds.definitions.ModifiedFrame;
import org.orekit.files.ccsds.definitions.TimeSystem;
import org.orekit.files.ccsds.ndm.odm.ocm.ObjectType;
import org.orekit.files.ccsds.section.Metadata;
import org.orekit.frames.Frame;

/**
 * This class gathers the meta-data present in the Conjunction Data Message (CDM).
 * @author Melina Vanel
 * @since 11.2
 */
public class CdmMetadata extends Metadata {

    /** CDM relative metadata. */
    private CdmRelativeMetadata relativeMetadata;

    /** Refering to object 1 or 2. */
    private String object;

    /** Unique satellite identification designator for the object. */
    private String objectDesignator;

    /** Specification of satellite catalog source. */
    private String catalogName;

    /** Object name. */
    private String objectName;

    /** International designator for the object as assigned by the UN Committee
     * on Space Research (COSPAR) and the US National Space Science Data Center (NSSDC). */
    private String internationalDesignator;

    /** Type of object. */
    private ObjectType objectType;

    /** Operator contact position for the space object. */
    private String operatorContact;

    /** Operator organization for the space object. */
    private String operatorOrganization;

    /** Operator phone for the space object. */
    private String operatorPhone;

    /** Operator email for the space object. */
    private String operatorEmail;

    /** Unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message. */
    private String odmMsgLink;

    /** Unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message. */
    private String admMsgLink;

    /** Unique name of the external ephemeris file used for the object or NONE. */
    private String ephemName;

    /** Flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
     * specified by CONJUNCTION_ID. */
    private YesNoUnknown obsBeforeNextMessage;

    /** Operator email for the space object. */
    private CovarianceMethod covarianceMethod;

    /** Maneuver capacity. */
    private Maneuvrable maneuverable;

    /** Central body around which Object1 and 2 are orbiting. */
    private BodyFacade orbitCenter;

    /** Reference frame in which state vector data are given. */
    private FrameFacade refFrame;

    /** Gravity model name. */
    private String gravityModel;

    /** Degree of the gravity model. */
    private int gravityDegree;

    /** Order of the gravity model. */
    private int gravityOrder;

    /** Name of atmospheric model. */
    private String atmosphericModel;

    /** N-body perturbation bodies. */
    private List<BodyFacade> nBodyPerturbations;

    /** Is solar radiation pressure taken into account or not ? STANDARD CCSDS saying YES/NO choice and optional */
    private YesNoUnknown isSolarRadPressure;

    /** Is solid Earth and ocean tides taken into account or not. STANDARD CCSDS saying YES/NO choice and optional */
    private YesNoUnknown isEarthTides;

    /** Is in-track thrust modelling used or not. STANDARD CCSDS saying YES/NO choice and optional */
    private YesNoUnknown isIntrackThrustModeled;

    /** The source from which the covariance data used in the report for both Object 1 and Object 2 originates. */
    private String covarianceSource;

    /** Flag indicating the type of alternate covariance information provided. */
    private AltCovarianceType altCovType;

    /** Reference frame in which the alternate covariance data are given. */
    private FrameFacade altCovRefFrame;

    /** Simple constructor.
     */
    @DefaultDataContext
    public CdmMetadata() {
        super(null);
        orbitCenter = new BodyFacade(CelestialBodyFactory.EARTH.toUpperCase(), CelestialBodyFactory.getEarth());
    }

    /** Simple constructor.
     *
     * @param dataContext data context
     */
    public CdmMetadata(final DataContext dataContext) {
        super(null);
        final CelestialBody earth = dataContext.getCelestialBodies().getEarth();
        orbitCenter = new BodyFacade(earth.getName().toUpperCase(), earth);
    }

    /** {@inheritDoc} */
    @Override
    public void validate(final double version) {
        // We only check values that are mandatory in a cdm file
        checkNotNull(object,                  CdmMetadataKey.OBJECT.name());
        checkNotNull(objectDesignator,        CdmMetadataKey.OBJECT_DESIGNATOR.name());
        checkNotNull(catalogName,             CdmMetadataKey.CATALOG_NAME.name());
        checkNotNull(objectName,              CdmMetadataKey.OBJECT_NAME.name());
        checkNotNull(internationalDesignator, CdmMetadataKey.INTERNATIONAL_DESIGNATOR.name());
        checkNotNull(ephemName,               CdmMetadataKey.EPHEMERIS_NAME.name());
        checkNotNull(covarianceMethod,        CdmMetadataKey.COVARIANCE_METHOD.name());
        checkNotNull(maneuverable,            CdmMetadataKey.MANEUVERABLE.name());
        checkNotNull(refFrame,                CdmMetadataKey.REF_FRAME.name());
    }

    /**
     * Get the relative metadata following header, they are the common metadata for the CDM.
     * @return relativeMetadata relative metadata
     */
    public CdmRelativeMetadata getRelativeMetadata() {
        return relativeMetadata;
    }

    /**
     * Set the relative metadata following header, they are the common metadata for the CDM.
     * @param relativeMetadata relative metadata
     */
    public void setRelativeMetadata(final CdmRelativeMetadata relativeMetadata) {
        this.relativeMetadata = relativeMetadata;
    }

    /**
     * Get the object name for which metadata are given.
     * @return the object name
     */
    public String getObject() {
        return object;
    }

    /**
     * Set the object name for which metadata are given.
     * @param object = object 1 or 2 to be set
     */
    public void setObject(final String object) {
        this.setTimeSystem(TimeSystem.UTC);
        refuseFurtherComments();
        this.object = object;
    }

    /**
     * Get the object satellite catalog designator for which metadata are given.
     * @return the satellite catalog designator for the object
     */
    public String getObjectDesignator() {
        return objectDesignator;
    }

    /**
     * Set the satellite designator for the object for which metadata are given.
     * @param objectDesignator for the spacecraft to be set
     */
    public void setObjectDesignator(final String objectDesignator) {
        refuseFurtherComments();
        this.objectDesignator = objectDesignator;
    }

    /**
     * Get the satellite catalog used for the object.
     * @return the catalog name
     */
    public String getCatalogName() {
        return catalogName;
    }

    /**
     * Set the satellite catalog name used for object.
     * @param catalogName for the spacecraft to be set
     */
    public void setCatalogName(final String catalogName) {
        refuseFurtherComments();
        this.catalogName = catalogName;
    }

    /**
     * Get the spacecraft name for the object.
     * @return the spacecraft name
     */
    public String getObjectName() {
        return objectName;
    }

    /**
     * Set the spacecraft name used for object.
     * @param objectName for the spacecraft to be set
     */
    public void setObjectName(final String objectName) {
        refuseFurtherComments();
        this.objectName = objectName;
    }

    /**
     * Get the international designator for the object.
     * @return the international designator
     */
    public String getInternationalDes() {
        return internationalDesignator;
    }

    /**
     * Set the international designator used for object.
     * @param internationalDes for the object to be set
     */
    public void setInternationalDes(final String internationalDes) {
        refuseFurtherComments();
        this.internationalDesignator = internationalDes;
    }

    /**
     * Get the type of object.
     * @return the object type
     */
    public ObjectType getObjectType() {
        return objectType;
    }

    /**
     * Set the type of object.
     * @param objectType type of object
     */
    public void setObjectType(final ObjectType objectType) {
        refuseFurtherComments();
        this.objectType = objectType;
    }

    /**
     * Get the contact position of the owner / operator of the object.
     * @return the contact position
     */
    public String getOperatorContactPosition() {
        return operatorContact;
    }

    /**
     * Set the contact position for the object owner / operator.
     * @param opContact for the object to be set
     */
    public void setOperatorContactPosition(final String opContact) {
        refuseFurtherComments();
        this.operatorContact = opContact;
    }

    /**
     * Get the contact organisation of the object.
     * @return the contact organisation
     */
    public String getOperatorOrganization() {
        return operatorOrganization;
    }

    /**
     * Set the contact organisation of the object.
     * @param operatorOrganization contact organisation for the object to be set
     */
    public void setOperatorOrganization(final String operatorOrganization) {
        refuseFurtherComments();
        this.operatorOrganization = operatorOrganization;
    }

    /**
     * Get the contact phone of the operator of the object.
     * @return the operator phone
     */
    public String getOperatorPhone() {
        return operatorPhone;
    }

    /**
     * Set the operator phone of the object.
     * @param operatorPhone contact phone for the object to be set
     */
    public void setOperatorPhone(final String operatorPhone) {
        refuseFurtherComments();
        this.operatorPhone = operatorPhone;
    }

    /**
     * Get the email of the operator of the object.
     * @return the operator email
     */
    public String getOperatorEmail() {
        return operatorEmail;
    }

    /**
     * Set the object operator email.
     * @param operatorEmail operator email for the object to be set
     */
    public void setOperatorEmail(final String operatorEmail) {
        refuseFurtherComments();
        this.operatorEmail = operatorEmail;
    }

    /**
     * Get the unique name of the external ephemeris used for OD.
     * @return the name of ephemeris used
     */
    public String getEphemName() {
        return ephemName;
    }

    /**
     * Set the name of external ephemeris used for OD.
     * @param ephemName me of external ephemeris used
     */
    public void setEphemName(final String ephemName) {
        refuseFurtherComments();
        this.ephemName = ephemName;
    }

    /**
     * Get the method name used to calculate covariance during OD.
     * @return the name of covariance calculation method
     */
    public CovarianceMethod getCovarianceMethod() {
        return covarianceMethod;
    }

    /**
     * Set the method name used to calculate covariance during OD.
     * @param covarianceMethod method name for covariance calculation
     */
    public void setCovarianceMethod(final CovarianceMethod covarianceMethod) {
        refuseFurtherComments();
        this.covarianceMethod = covarianceMethod;
    }

    /**
     * Get the ability of object to maneuver or not.
     * @return the ability to maneuver
     */
    public Maneuvrable getManeuverable() {
        return maneuverable;
    }

    /**
     * Set the object maneuver ability.
     * @param maneuverable ability to maneuver
     */
    public void setManeuverable(final Maneuvrable maneuverable) {
        refuseFurtherComments();
        this.maneuverable = maneuverable;
    }

    /**
     * Get the central body for object 1 and 2.
     * @return the name of the central body
     */
    public BodyFacade getOrbitCenter() {
        return orbitCenter;
    }

    /**
     * Set the central body name for object 1 and 2.
     * @param orbitCenter name of the central body
     */
    public void setOrbitCenter(final BodyFacade orbitCenter) {
        refuseFurtherComments();
        this.orbitCenter = orbitCenter;
    }

    /**
     * Get the reference frame in which data are given: used for state vector and
     * Keplerian elements data (and for the covariance reference frame if none is given).
     *
     * @return the reference frame
     */
    public Frame getFrame() {
        if (orbitCenter == null || orbitCenter.getBody() == null) {
            throw new OrekitException(OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY, "No Orbit center name");
        }
        if (refFrame == null) {
            throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, "No reference frame");
        }
        else  {
            if (refFrame.asFrame() == null) {
                throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, refFrame.getName());
            }
        }
        // Just return frame if we don't need to shift the center based on CENTER_NAME
        // MCI and ICRF are the only non-Earth centered frames specified in Annex A.
        final boolean isMci  = refFrame.asCelestialBodyFrame() == CelestialBodyFrame.MCI;
        final boolean isIcrf = refFrame.asCelestialBodyFrame() == CelestialBodyFrame.ICRF;
        final boolean isSolarSystemBarycenter =
                CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER.equals(orbitCenter.getBody().getName());
        if (!(isMci || isIcrf) && CelestialBodyFactory.EARTH.equals(orbitCenter.getBody().getName()) ||
            isMci && CelestialBodyFactory.MARS.equals(orbitCenter.getBody().getName()) ||
            isIcrf && isSolarSystemBarycenter) {
            return refFrame.asFrame();
        }
        // else, translate frame to specified center.
        return new ModifiedFrame(refFrame.asFrame(), refFrame.asCelestialBodyFrame(),
                                 orbitCenter.getBody(), orbitCenter.getName());
    }

    /**
     * Get the value of {@code REF_FRAME} as an Orekit {@link Frame}. The {@code
     * ORBIT_CENTER} key word has not been applied yet, so the returned frame may not
     * correspond to the reference frame of the data in the file.
     * @return the reference frame
     */
    public FrameFacade getRefFrame() {
        return refFrame;
    }

    /**
     * Set the name of the reference frame in which the state vector data are given.
     * @param refFrame reference frame
     */
    public void setRefFrame(final FrameFacade refFrame) {
        refuseFurtherComments();
        this.refFrame = refFrame;
    }

    /** Get gravity model name.
     * @return gravity model name
     */
    public String getGravityModel() {
        return gravityModel;
    }

    /** Get degree of the gravity model.
     * @return degree of the gravity model
     */
    public int getGravityDegree() {
        return gravityDegree;
    }

    /** Get order of the gravity model.
     * @return order of the gravity model
     */
    public int getGravityOrder() {
        return gravityOrder;
    }

    /** Set gravity model.
     * @param name name of the model
     * @param degree degree of the model
     * @param order order of the model
     */
    public void setGravityModel(final String name, final int degree, final int order) {
        refuseFurtherComments();
        this.gravityModel  = name;
        this.gravityDegree = degree;
        this.gravityOrder  = order;
    }

    /** Get name of atmospheric model.
     * @return name of atmospheric model
     */
    public String getAtmosphericModel() {
        return atmosphericModel;
    }

    /** Set name of atmospheric model.
     * @param atmosphericModel name of atmospheric model
     */
    public void setAtmosphericModel(final String atmosphericModel) {
        refuseFurtherComments();
        this.atmosphericModel = atmosphericModel;
    }

    /** Get n-body perturbation bodies.
     * @return n-body perturbation bodies
     */
    public List<BodyFacade> getNBodyPerturbations() {
        return nBodyPerturbations;
    }

    /** Set n-body perturbation bodies.
     * @param nBody n-body perturbation bodies
     */
    public void setNBodyPerturbations(final List<BodyFacade> nBody) {
        refuseFurtherComments();
        this.nBodyPerturbations = nBody;
    }

    /**
     * Get Enum YesNoUnknown that indicates if Solar Radiation Pressure is taken into account or not.
     * @return isSolarRadPressure YesNoUnknown
     */
    public YesNoUnknown getSolarRadiationPressure() {
        return isSolarRadPressure;
    }

    /**
     * Set Enum that indicates if Solar Radiation Pressure is taken into account or not.
     * @param isSolRadPressure YesNoUnknown
     */
    public void setSolarRadiationPressure(final YesNoUnknown isSolRadPressure) {
        refuseFurtherComments();
        this.isSolarRadPressure = isSolRadPressure;
    }

    /**
     * Get Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
     * @return isEarthTides YesNoUnknown
     */
    public YesNoUnknown getEarthTides() {
        return isEarthTides;
    }

    /**
     * Set Enum YesNoUnknown that indicates if Earth and ocean tides are taken into account or not.
     * @param EarthTides YesNoUnknown
     */
    public void setEarthTides(final YesNoUnknown EarthTides) {
        refuseFurtherComments();
        this.isEarthTides = EarthTides;
    }

    /**
     * Get Enum YesNoUnknown that indicates if intrack thrust modeling was into account or not.
     * @return isEarthTides YesNoUnknown
     */
    public YesNoUnknown getIntrackThrust() {
        return isIntrackThrustModeled;
    }

    /**
     * Set boolean that indicates if intrack thrust modeling was into account or not.
     * @param IntrackThrustModeled YesNoUnknown
     */
    public void setIntrackThrust(final YesNoUnknown IntrackThrustModeled) {
        refuseFurtherComments();
        this.isIntrackThrustModeled = IntrackThrustModeled;
    }

    /** Get the source of the covariance data.
     * @return the covarianceSource
     */
    public String getCovarianceSource() {
        return covarianceSource;
    }

    /** Set the source of the covariance data.
     * @param covarianceSource the covarianceSource to set
     */
    public void setCovarianceSource(final String covarianceSource) {
        refuseFurtherComments();
        this.covarianceSource = covarianceSource;
    }

    /** Get the flag indicating the type of alternate covariance information provided.
     * @return the altCovType
     */
    public AltCovarianceType getAltCovType() {
        return altCovType;
    }

    /** Set the flag indicating the type of alternate covariance information provided.
     * @param altCovType the altCovType to set
     */
    public void setAltCovType(final AltCovarianceType altCovType) {
        refuseFurtherComments();
        this.altCovType = altCovType;
    }

     /**
     * Get the value of {@code ALT_COV_REF_FRAME} as an Orekit {@link Frame}.
     * @return the reference frame
     */
    public FrameFacade getAltCovRefFrame() {
        return altCovRefFrame;
    }

    /**
     * Set the name of the reference frame in which the alternate covariance data are given.
     * @param altCovRefFrame alternate covariance reference frame
     */
    public void setAltCovRefFrame(final FrameFacade altCovRefFrame) {
        refuseFurtherComments();

        if (getAltCovType() == null) {
            throw new OrekitException(OrekitMessages.CCSDS_MISSING_KEYWORD, CdmMetadataKey.ALT_COV_TYPE);
        }

        if (altCovRefFrame.asFrame() == null) {
            throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, altCovRefFrame.getName());
        }

        // Only set the frame if within the allowed options: GCRF, EME2000, ITRF
        if ( altCovRefFrame.asCelestialBodyFrame() == CelestialBodyFrame.GCRF ||
                 altCovRefFrame.asCelestialBodyFrame() == CelestialBodyFrame.EME2000 ||
                     altCovRefFrame.asCelestialBodyFrame().name().contains("ITRF") ) {
            this.altCovRefFrame = altCovRefFrame;
        } else {
            throw new OrekitException(OrekitMessages.CCSDS_INVALID_FRAME, altCovRefFrame.getName());
        }
    }

    /** Get the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
     * @return the odmMsgLink
     */
    public String getOdmMsgLink() {
        return odmMsgLink;
    }

    /** Set the unique identifier of Orbit Data Message(s) that are linked (relevant) to this Conjunction Data Message.
     * @param odmMsgLink the odmMsgLink to set
     */
    public void setOdmMsgLink(final String odmMsgLink) {
        refuseFurtherComments();
        this.odmMsgLink = odmMsgLink;
    }

    /** Get the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
     * @return the admMsgLink
     */
    public String getAdmMsgLink() {
        return admMsgLink;
    }

    /** Set the unique identifier of Attitude Data Message(s) that are linked (relevant) to this Conjunction Data Message.
     * @param admMsgLink the admMsgLink to set
     */
    public void setAdmMsgLink(final String admMsgLink) {
        refuseFurtherComments();
        this.admMsgLink = admMsgLink;
    }

    /** Get the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
     * specified by CONJUNCTION_ID.
     * @return the obsBeforeNextMessage
     */
    public YesNoUnknown getObsBeforeNextMessage() {
        return obsBeforeNextMessage;
    }

    /** Set the flag indicating whether new tracking observations are anticipated prior to the issue of the next CDM associated with the event
     * specified by CONJUNCTION_ID.
     * @param obsBeforeNextMessage the obsBeforeNextMessage to set
     */
    public void setObsBeforeNextMessage(final YesNoUnknown obsBeforeNextMessage) {
        refuseFurtherComments();
        this.obsBeforeNextMessage = obsBeforeNextMessage;
    }
}