OEMFile.java

/* Copyright 2002-2013 CS Systèmes d'Information
 * Licensed to CS Systèmes d'Information (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.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.util.Pair;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.general.OrbitFile;
import org.orekit.files.general.SatelliteInformation;
import org.orekit.files.general.SatelliteTimeCoordinate;
import org.orekit.frames.Frame;
import org.orekit.frames.LOFType;
import org.orekit.orbits.CartesianOrbit;
import org.orekit.time.AbsoluteDate;

/** This class stocks all the information of the OEM File parsed by OEMParser. It
 * contains the header and a list of Ephemerides Blocks each containing
 * metadata, a list of ephemerides data lines and optional covariance matrices
 * (and their metadata).
 * @author sports
 * @since 6.1
 */
public class OEMFile extends ODMFile {

    /** List of ephemeris blocks. */
    private List<EphemeridesBlock> ephemeridesBlocks;

    /** OEMFile constructor. */
    public OEMFile() {
        ephemeridesBlocks = new ArrayList<EphemeridesBlock>();
    }

    /** Add a block to the list of ephemeris blocks. */
    void addEphemeridesBlock() {
        ephemeridesBlocks.add(new EphemeridesBlock());
    }

    /**Get the list of ephemerides blocks as an unmodifiable list.
     * @return the list of ephemerides blocks
     */
    public List<EphemeridesBlock> getEphemeridesBlocks() {
        return Collections.unmodifiableList(ephemeridesBlocks);
    }

    /** Check that, according to the CCSDS standard, every OEMBlock has the same time system.
     *  @exception OrekitException if some blocks do not have the same time system
     */
    void checkTimeSystems() throws OrekitException {
        final OrbitFile.TimeSystem timeSystem = getEphemeridesBlocks().get(0).getMetaData().getTimeSystem();
        for (final EphemeridesBlock block : ephemeridesBlocks) {
            if (!timeSystem.equals(block.getMetaData().getTimeSystem())) {
                throw new OrekitException(OrekitMessages.CCSDS_OEM_INCONSISTENT_TIME_SYSTEMS,
                                          timeSystem, block.getMetaData().getTimeSystem());
            }
        }
    }

    /** {@inheritDoc}
     * <p>
     * We return here only the coordinate systems of the first ephemerides block.
     * </p>
     */
    @Override
    public String getCoordinateSystem() {
        return ephemeridesBlocks.get(0).getMetaData().getFrame().toString();
    }

    /** {@inheritDoc} */
    @Override
    public OrbitFile.TimeSystem getTimeSystem() {
        return ephemeridesBlocks.get(0).getMetaData().getTimeSystem();
    }

    /** {@inheritDoc}
     * <p>
     * We return here only the start time of the first ephemerides block.
     * </p>
     */
    @Override
    public AbsoluteDate getEpoch() {
        return ephemeridesBlocks.get(0).getStartTime();
    }

    /** {@inheritDoc} */
    @Override
    public Collection<SatelliteInformation> getSatellites() {
        final Set<String> availableSatellites = getAvailableSatelliteIds();
        final List<SatelliteInformation> satellites =
                new ArrayList<SatelliteInformation>(availableSatellites.size());
        for (String satId : availableSatellites) {
            satellites.add(new SatelliteInformation(satId));
        }
        return satellites;
    }

    /** {@inheritDoc} */
    @Override
    public int getSatelliteCount() {
        return getAvailableSatelliteIds().size();
    }

    /** {@inheritDoc} */
    @Override
    public SatelliteInformation getSatellite(final String satId) {
        final Set<String> availableSatellites = getAvailableSatelliteIds();
        if (availableSatellites.contains(satId)) {
            return new SatelliteInformation(satId);
        } else {
            return null;
        }
    }

    /** {@inheritDoc} */
    @Override
    public List<SatelliteTimeCoordinate> getSatelliteCoordinates(final String satId) {
        // first we collect all available EphemeridesBlocks for this satellite
        // and return a list view of the actual EphemeridesBlocks transforming the
        // EphemeridesDataLines into SatelliteTimeCoordinates in a lazy manner.
        final List<Pair<Integer, Integer>> ephemeridesBlockMapping = new ArrayList<Pair<Integer, Integer>>();
        final ListIterator<EphemeridesBlock> it = ephemeridesBlocks.listIterator();
        int totalDataLines = 0;
        while (it.hasNext()) {
            final int index = it.nextIndex();
            final EphemeridesBlock block = it.next();

            if (block.getMetaData().getObjectID().equals(satId)) {
                final int dataLines = block.getEphemeridesDataLines().size();
                totalDataLines += dataLines;
                ephemeridesBlockMapping.add(new Pair<Integer, Integer>(index, dataLines));
            }
        }

        // the total number of coordinates for this satellite
        final int totalNumberOfCoordinates = totalDataLines;

        return new AbstractList<SatelliteTimeCoordinate>() {

            @Override
            public SatelliteTimeCoordinate get(final int index) {
                if (index < 0 || index >= size()) {
                    throw new IndexOutOfBoundsException();
                }

                // find the corresponding ephemerides block and data line
                int ephemeridesBlockIndex = -1;
                int dataLineIndex = index;
                for (Pair<Integer, Integer> pair : ephemeridesBlockMapping) {
                    if (dataLineIndex < pair.getValue()) {
                        ephemeridesBlockIndex = pair.getKey();
                        break;
                    } else {
                        dataLineIndex -= pair.getValue();
                    }
                }

                if (ephemeridesBlockIndex == -1 || dataLineIndex == -1) {
                    throw new IndexOutOfBoundsException();
                }

                final EphemeridesDataLine dataLine =
                        ephemeridesBlocks.get(ephemeridesBlockIndex).getEphemeridesDataLines().get(dataLineIndex);
                final CartesianOrbit orbit = dataLine.getOrbit();
                return new SatelliteTimeCoordinate(orbit.getDate(), orbit.getPVCoordinates());
            }

            @Override
            public int size() {
                return totalNumberOfCoordinates;
            }

        };
    }

    /** Returns a set of all available satellite Ids in this OEMFile.
     * @return a set of all available satellite Ids
     */
    private Set<String> getAvailableSatelliteIds() {
        final Set<String> availableSatellites = new LinkedHashSet<String>();
        for (EphemeridesBlock block : ephemeridesBlocks) {
            availableSatellites.add(block.getMetaData().getObjectID());
        }
        return availableSatellites;
    }

    /** The Ephemerides Blocks class contain metadata, the list of ephemerides data
     * lines and optional covariance matrices (and their metadata). The reason
     * for which the ephemerides have been separated into blocks is that the
     * ephemerides of two different blocks are not suited for interpolation.
     * @author sports
     */
    public class EphemeridesBlock {

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

        /** Start of total time span covered by ephemerides data and covariance
         * data. */
        private AbsoluteDate startTime;

        /** End of total time span covered by ephemerides data and covariance
         * data. */
        private AbsoluteDate stopTime;

        /** Start of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation. */
        private AbsoluteDate useableStartTime;

        /** End of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation. */
        private AbsoluteDate useableStopTime;

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

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

        /** List of ephemerides data lines. */
        private List<EphemeridesDataLine> ephemeridesDataLines;

        /** List of covariance matrices. */
        private List<CovarianceMatrix> covarianceMatrices;

        /** Tests whether the reference frame has an epoch associated to it. */
        private boolean hasRefFrameEpoch;

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

        /** EphemeridesBlock constructor. */
        public EphemeridesBlock() {
            metaData = new ODMMetaData(OEMFile.this);
            ephemeridesDataLines = new ArrayList<EphemeridesDataLine>();
            covarianceMatrices = new ArrayList<CovarianceMatrix>();
        }

        /** Get the list of Ephemerides data lines.
         * @return the list of Ephemerides data lines
         */
        public List<EphemeridesDataLine> getEphemeridesDataLines() {
            return ephemeridesDataLines;
        }

        /** Get the list of Covariance Matrices.
         * @return the list of Covariance Matrices
         */
        public List<CovarianceMatrix> getCovarianceMatrices() {
            return covarianceMatrices;
        }

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

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

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

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

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

        /** Get start of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation.
         * @return the useable start time
         */
        public AbsoluteDate getUseableStartTime() {
            return useableStartTime;
        }

        /** Set start of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation.
         * @param useableStartTime the time to be set
         */
        void setUseableStartTime(final AbsoluteDate useableStartTime) {
            this.useableStartTime = useableStartTime;
        }

        /** Get end of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation.
         * @return the useable stop time
         */
        public AbsoluteDate getUseableStopTime() {
            return useableStopTime;
        }

        /** Set end of useable time span covered by ephemerides data, it may be
         * necessary to allow for proper interpolation.
         * @param useableStopTime the time to be set
         */
        void setUseableStopTime(final AbsoluteDate useableStopTime) {
            this.useableStopTime = useableStopTime;
        }

        /** Get the interpolation method to be used.
         * @return the interpolation method
         */
        public String getInterpolationMethod() {
            return interpolationMethod;
        }

        /** Set the interpolation method to be used.
         * @param interpolationMethod the interpolation method to be set
         */
        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
         */
        void setInterpolationDegree(final int interpolationDegree) {
            this.interpolationDegree = interpolationDegree;
        }

        /** Get boolean testing whether the reference frame has an epoch associated to it.
         * @return true if the reference frame has an epoch associated to it
         *         false otherwise
         */
        public boolean getHasRefFrameEpoch() {
            return hasRefFrameEpoch;
        }

        /** Set boolean testing whether the reference frame has an epoch associated to it.
         * @param hasRefFrameEpoch the boolean to be set.
         */
        void setHasRefFrameEpoch(final boolean hasRefFrameEpoch) {
            this.hasRefFrameEpoch = hasRefFrameEpoch;
        }

        /** Get the ephemerides data lines comment.
         * @return the comment
         */
        public List<String> getEphemeridesDataLinesComment() {
            return ephemeridesDataLinesComment;
        }

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

    /** The EphemeridesDataLine class represents the content of an OEM ephemerides
     * data line and consists of a cartesian orbit and an optional acceleration
     * vector.
     * @author sports
     */
    public static class EphemeridesDataLine {

        /** The cartesian orbit relative to the ephemeris. */
        private CartesianOrbit orbit;

        /** The acceleration vector. */
        private Vector3D acceleration;

        /** The EphemeridesDataLine constructor.
         * @param orbit the orbit corresponding to the ephemeris
         * @param acceleration the acceleration vector
         */
        EphemeridesDataLine(final CartesianOrbit orbit, final Vector3D acceleration) {
            this.acceleration = acceleration;
            this.orbit = orbit;
        }

        /** Get the ephemerides data line orbit.
         * @return the orbit
         */
        public CartesianOrbit getOrbit() {
            return orbit;
        }

        /** Get the ephemerides data line acceleration vector.
         * @return the acceleration vector
         */
        public Vector3D getAcceleration() {
            return acceleration;
        }

    }

    /** The CovarianceMatrix class represents a covariance matrix and its
     * metadata: epoch and frame.
     * @author sports
     */
    public static class CovarianceMatrix {

        /** Covariance matrix. */
        private RealMatrix matrix;

        /** Epoch relative to the covariance matrix. */
        private AbsoluteDate epoch;

        /** Coordinate system for covariance matrix, for Local Orbital Frames. */
        private LOFType lofType;

        /** Coordinate system for covariance matrix, for absolute frames.
         * If not given it is set equal to refFrame. */
        private Frame frame;

        /** Covariance Matrix constructor.
         * @param epoch the epoch
         * @param lofType coordinate system for covariance matrix, for Local Orbital Frames
         * @param frame coordinate system for covariance matrix, for absolute frames
         * @param lastMatrix the covariance matrix
         */
        CovarianceMatrix(final AbsoluteDate epoch,
                         final LOFType lofType, final Frame frame,
                         final RealMatrix lastMatrix) {
            this.matrix  = lastMatrix;
            this.epoch   = epoch;
            this.lofType = lofType;
            this.frame   = frame;
        }

        /** Get the covariance matrix.
         * @return the covariance matrix
         */
        public RealMatrix getMatrix() {
            return matrix;
        }

        /** Get the epoch relative to the covariance matrix.
         * @return the epoch
         */
        public AbsoluteDate getEpoch() {
            return epoch;
        }

        /** Get coordinate system for covariance matrix, for Local Orbital Frames.
         * <p>
         * The value returned is null if the covariance matrix is given in an
         * absolute frame rather than a Local Orbital Frame. In this case, the
         * method {@link #getFrame()} must be used instead.
         * </p>
         * @return the coordinate system for covariance matrix, or null if the
         * covariance matrix is given in an absolute frame rather than a Local
         * Orbital Frame
         */
        public LOFType getLofType() {
            return lofType;
        }

        /** Get coordinate system for covariance matrix, for absolute frames.
         * <p>
         * The value returned is null if the covariance matrix is given in a
         * Local Orbital Frame rather than an absolute frame. In this case, the
         * method {@link #getLofType()} must be used instead.
         * </p>
         * @return the coordinate system for covariance matrix
         */
        public Frame getFrame() {
            return frame;
        }

    }


}