AEMFile.java

/* Copyright 2002-2020 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;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.general.AttitudeEphemerisFile;
import org.orekit.frames.Frame;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.utils.AngularDerivativesFilter;
import org.orekit.utils.TimeStampedAngularCoordinates;

/**
 * This class stocks all the information of the Attitude Ephemeris Message (AEM) File parsed
 * by AEMParser. It contains the header and a list of Attitude Ephemerides Blocks each
 * containing metadata and a list of attitude ephemerides data lines.
 * @author Bryan Cazabonne
 * @since 10.2
 */
public class AEMFile extends ADMFile implements AttitudeEphemerisFile {

    /** List of ephemeris blocks. */
    private List<AttitudeEphemeridesBlock> attitudeBlocks;

    /**
     * AEMFile constructor.
     */
    public AEMFile() {
        attitudeBlocks = new ArrayList<AttitudeEphemeridesBlock>();
    }

    /**
     * Add a block to the list of ephemeris blocks.
     */
    public void addAttitudeBlock() {
        attitudeBlocks.add(new AttitudeEphemeridesBlock());
    }

    /**
     * Get the list of attitude ephemerides blocks as an unmodifiable list.
     * @return the list of attitude ephemerides blocks
     */
    public List<AttitudeEphemeridesBlock> getAttitudeBlocks() {
        return Collections.unmodifiableList(attitudeBlocks);
    }

    /**
     * Check that, according to the CCSDS standard, every AEMBlock has the same time system.
     */
    public void checkTimeSystems() {
        final CcsdsTimeScale timeSystem = getAttitudeBlocks().get(0).getMetaData().getTimeSystem();
        for (final AttitudeEphemeridesBlock block : attitudeBlocks) {
            if (!timeSystem.equals(block.getMetaData().getTimeSystem())) {
                throw new OrekitException(OrekitMessages.CCSDS_AEM_INCONSISTENT_TIME_SYSTEMS,
                                          timeSystem, block.getMetaData().getTimeSystem());
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public Map<String, AemSatelliteEphemeris> getSatellites() {
        final Map<String, List<AttitudeEphemeridesBlock>> satellites = new HashMap<>();
        for (final AttitudeEphemeridesBlock ephemeridesBlock : attitudeBlocks) {
            final String id = ephemeridesBlock.getMetaData().getObjectID();
            satellites.putIfAbsent(id, new ArrayList<>());
            satellites.get(id).add(ephemeridesBlock);
        }
        final Map<String, AemSatelliteEphemeris> ret = new HashMap<>();
        for (final Entry<String, List<AttitudeEphemeridesBlock>> entry : satellites.entrySet()) {
            final String id = entry.getKey();
            ret.put(id, new AemSatelliteEphemeris(id, entry.getValue()));
        }
        return ret;
    }


    /** AEM ephemeris blocks for a single satellite. */
    public static class AemSatelliteEphemeris implements SatelliteAttitudeEphemeris {

        /** ID of the satellite. */
        private final String id;

        /** The attitude ephemeris data for the satellite. */
        private final List<AttitudeEphemeridesBlock> blocks;

        /**
         * Create a container for the set of ephemeris blocks in the file that pertain to
         * a single satellite. The satellite's ID is set to ""
         * @param blocks containing ephemeris data for the satellite.
         * @deprecated in 10.3, replaced by AemSatelliteEphemeris(String, List)
         */
        @Deprecated
        public AemSatelliteEphemeris(final List<AttitudeEphemeridesBlock> blocks) {
            this("", blocks);
        }

        /**
         * Create a container for the set of ephemeris blocks in the file that pertain to
         * a single satellite.
         * @param id     of the satellite.
         * @param blocks containing ephemeris data for the satellite.
         * @since 10.3
         */
        public AemSatelliteEphemeris(final String id, final List<AttitudeEphemeridesBlock> blocks) {
            this.id = id;
            this.blocks = blocks;
        }

        /** {@inheritDoc} */
        @Override
        public String getId() {
            return this.id;
        }

        /** {@inheritDoc} */
        @Override
        public List<AttitudeEphemeridesBlock> getSegments() {
            return Collections.unmodifiableList(blocks);
        }

        /** {@inheritDoc} */
        @Override
        public AbsoluteDate getStart() {
            return blocks.get(0).getStart();
        }

        /** {@inheritDoc} */
        @Override
        public AbsoluteDate getStop() {
            return blocks.get(blocks.size() - 1).getStop();
        }

    }

    /**
     * The Attitude Ephemerides Blocks class contain metadata
     * and the list of attitude data lines.
     */
    public class AttitudeEphemeridesBlock implements AttitudeEphemerisSegment {

        /** Meta-data for the block. */
        private ADMMetaData metaData;

        /** The reference frame A specifier, as it appeared in the file. */
        private String refFrameAString;

        /** The reference frame B specifier, as it appeared in the file. */
        private String refFrameBString;

        /** The reference frame from which attitude is defined. */
        private Frame refFrame;

        /** Rotation direction of the attitude. */
        private String attitudeDir;

        /** Start of total time span covered by attitude data. */
        private AbsoluteDate startTime;

        /** End of total time span covered by attitude data. */
        private AbsoluteDate stopTime;

        /** Start of useable time span covered by attitude data. */
        private AbsoluteDate useableStartTime;

        /** End of useable time span covered by attitude data. */
        private AbsoluteDate useableStopTime;

        /** The format of the data lines in the message. */
        private String attitudeType;

        /** The placement of the scalar portion of the quaternion (QC) in the attitude data. */
        private boolean isFirst;

        /**
         * The rotation sequence of the Euler angles.
         * (e.g., 312, where X=1, Y=2, Z=3)
         */
        private String eulerRotSeq;

        /** The rate frame specifier, as it appeared in the file. */
        private String rateFrameString;

        /** The interpolation method to be used. */
        private String interpolationMethod;

        /** The interpolation degree. */
        private int interpolationDegree;

        /** The rotation order. */
        private RotationOrder rotationOrder;

        /** List of data lines. */
        private List<TimeStampedAngularCoordinates> attitudeDataLines;

        /** Data Lines comments. The list contains a string for each line of comment. */
        private List<String> attitudeDataLinesComment;

        /** Enumerate for selecting which derivatives to use in {@link #attitudeDataLines}. */
        private AngularDerivativesFilter angularDerivativesFilter;

        /**
         * Constructor.
         */
        public AttitudeEphemeridesBlock() {
            attitudeDataLines = new ArrayList<>();
            metaData          = new ADMMetaData(AEMFile.this);
        }

        /**
         * Get the list of attitude data lines.
         * @return a list of data
         */
        public List<TimeStampedAngularCoordinates> getAttitudeDataLines() {
            return attitudeDataLines;
        }

        /** {@inheritDoc} */
        @Override
        public List<TimeStampedAngularCoordinates> getAngularCoordinates() {
            return Collections.unmodifiableList(this.attitudeDataLines);
        }

        /** {@inheritDoc} */
        @Override
        public AngularDerivativesFilter getAvailableDerivatives() {
            return angularDerivativesFilter;
        }

        /**
         * Update the value of {@link #angularDerivativesFilter}.
         *
         * @param pointAngularDerivativesFilter enumerate for selecting which derivatives to use in
         *                                      attitude data.
         */
        void updateAngularDerivativesFilter(final AngularDerivativesFilter pointAngularDerivativesFilter) {
            this.angularDerivativesFilter = pointAngularDerivativesFilter;
        }

        /**
         * Get the meta-data for the block.
         * @return meta-data for the block
         */
        public ADMMetaData getMetaData() {
            return metaData;
        }

        /** {@inheritDoc} */
        @Override
        public String getFrameCenterString() {
            return this.getMetaData().getCenterName();
        }

        /** {@inheritDoc} */
        @Override
        public String getRefFrameAString() {
            return refFrameAString;
        }

        /**
         * Set the reference frame A name.
         * @param frame specifier as it appeared in the file.
         */
        public void setRefFrameAString(final String frame) {
            this.refFrameAString = frame;
        }

        /** {@inheritDoc} */
        @Override
        public String getRefFrameBString() {
            return this.refFrameBString;
        }

        /**
         * Set the reference frame B name.
         * @param frame specifier as it appeared in the file.
         */
        public void setRefFrameBString(final String frame) {
            this.refFrameBString = frame;
        }

        /**
         * Get the reference frame from which attitude is defined.
         * @param frame reference frame
         */
        public void setReferenceFrame(final Frame frame) {
            this.refFrame = frame;

        }

        /** {@inheritDoc} */
        @Override
        public Frame getReferenceFrame() {
            return refFrame;
        }

        /**
         * Get the rate frame specifier as it appeared in the file.
         * @return the rate frame name.
         */
        public String getRateFrameString() {
            return rateFrameString;
        }

        /**
         * Set the rate frame name.
         * @param frame specifier as it appeared in the file.
         */
        public void setRateFrameString(final String frame) {
            this.rateFrameString = frame;
        }

        /** {@inheritDoc} */
        @Override
        public String getAttitudeDirection() {
            return attitudeDir;
        }

        /**
         * Set the rotation direction of the attitude.
         * @param direction rotation direction to be set
         */
        public void setAttitudeDirection(final String direction) {
            this.attitudeDir = direction;
        }

        /** {@inheritDoc} */
        @Override
        public String getAttitudeType() {
            return attitudeType;
        }

        /**
         * Set the format of the data lines in the message.
         * @param type format to be set
         */
        public void setAttitudeType(final String type) {
            this.attitudeType = type;
        }

        /** {@inheritDoc} */
        @Override
        public boolean isFirst() {
            return isFirst;
        }

        /**
         * Set the flag for the placement of the quaternion QC in the attitude data.
         * @param isFirst true if QC is the first element in the attitude data
         */
        public void setIsFirst(final boolean isFirst) {
            this.isFirst = isFirst;
        }

        /**
         * Get the rotation sequence of the Euler angles (e.g., 312, where X=1, Y=2, Z=3).
         * @return the rotation sequence of the Euler angles
         */
        public String getEulerRotSeq() {
            return eulerRotSeq;
        }

        /**
         * Set the rotation sequence of the Euler angles (e.g., 312, where X=1, Y=2, Z=3).
         * @param eulerRotSeq rotation sequence to be set
         */
        public void setEulerRotSeq(final String eulerRotSeq) {
            this.eulerRotSeq = eulerRotSeq;
        }

        /** {@inheritDoc} */
        @Override
        public String getTimeScaleString() {
            return metaData.getTimeSystem().toString();
        }

        /** {@inheritDoc} */
        @Override
        public TimeScale getTimeScale() {
            return metaData.getTimeScale();
        }

        /**
         * Get start of total time span covered by attitude data.
         * @return the start time
         */
        public AbsoluteDate getStartTime() {
            return startTime;
        }

        /**
         * Set start of total time span covered by attitude data.
         * @param startTime the time to be set
         */
        public void setStartTime(final AbsoluteDate startTime) {
            this.startTime = startTime;
        }

        /**
         * Get end of total time span covered by attitude data.
         * @return the stop time
         */
        public AbsoluteDate getStopTime() {
            return stopTime;
        }

        /**
         * Set end of total time span covered by attitude data.
         * @param stopTime the time to be set
         */
        public void setStopTime(final AbsoluteDate stopTime) {
            this.stopTime = stopTime;
        }

        /**
         * Get start of useable time span covered by attitude data.
         * @return the useable start time
         */
        public AbsoluteDate getUseableStartTime() {
            return useableStartTime;
        }

        /**
         * Set start of useable time span covered by attitude data.
         * @param useableStartTime the time to be set
         */
        public void setUseableStartTime(final AbsoluteDate useableStartTime) {
            this.useableStartTime = useableStartTime;
        }

        /**
         * Get end of useable time span covered by ephemerides data.
         * @return the useable stop time
         */
        public AbsoluteDate getUseableStopTime() {
            return useableStopTime;
        }

        /**
         * Set end of useable time span covered by ephemerides data.
         * @param useableStopTime the time to be set
         */
        public void setUseableStopTime(final AbsoluteDate useableStopTime) {
            this.useableStopTime = useableStopTime;
        }

        /** {@inheritDoc} */
        @Override
        public AbsoluteDate getStart() {
            // usable start time overrides start time if it is set
            final AbsoluteDate start = this.getUseableStartTime();
            if (start != null) {
                return start;
            } else {
                return this.getStartTime();
            }
        }

        /** {@inheritDoc} */
        @Override
        public AbsoluteDate getStop() {
            // useable stop time overrides stop time if it is set
            final AbsoluteDate stop = this.getUseableStopTime();
            if (stop != null) {
                return stop;
            } else {
                return this.getStopTime();
            }
        }

        /** {@inheritDoc} */
        @Override
        public String getInterpolationMethod() {
            return interpolationMethod;
        }

        /**
         * Set the interpolation method to be used.
         * @param interpolationMethod the interpolation method to be set
         */
        public void setInterpolationMethod(final String interpolationMethod) {
            this.interpolationMethod = interpolationMethod;
        }

        /**
         * Get the interpolation degree.
         * @return the interpolation degree
         */
        public int getInterpolationDegree() {
            return interpolationDegree;
        }

        /**
         * Set the interpolation degree.
         * @param interpolationDegree the interpolation degree to be set
         */
        public void setInterpolationDegree(final int interpolationDegree) {
            this.interpolationDegree = interpolationDegree;
        }

        /** {@inheritDoc} */
        @Override
        public int getInterpolationSamples() {
            // From the standard it is not entirely clear how to interpret the degree.
            return getInterpolationDegree() + 1;
        }

        /** Get the attitude data lines comment.
         * @return the comment
         */
        public List<String> getAttitudeDataLinesComment() {
            return attitudeDataLinesComment;
        }

        /** Set the attitude data lines comment.
         * @param ephemeridesDataLinesComment the comment to be set
         */
        public void setAttitudeDataLinesComment(final List<String> ephemeridesDataLinesComment) {
            this.attitudeDataLinesComment = new ArrayList<String>(ephemeridesDataLinesComment);
        }

        /** {@inheritDoc} */
        @Override
        public RotationOrder getRotationOrder() {
            return rotationOrder;
        }

        /**
         * Set the rotation order for Euler angles.
         * @param order the rotation order to be set
         */
        public void setRotationOrder(final RotationOrder order) {
            this.rotationOrder = order;
        }

    }


}