OrekitEphemerisFile.java

/* Copyright 2016 Applied Defense Solutions (ADS)
 * 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.
 * ADS 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.general;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.orekit.bodies.CelestialBody;
import org.orekit.bodies.CelestialBodyFactory;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitMessages;
import org.orekit.frames.Frame;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScale;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.CartesianDerivativesFilter;
import org.orekit.utils.TimeStampedPVCoordinates;

/**
 * A class for encapsulating Orekit propagators within an {@link EphemerisFile}
 * complaint object that makes for easy serialization to external ephemeris
 * formats like OEM.
 *
 * @author Hank Grabowski
 * @since 9.0
 *
 */
public class OrekitEphemerisFile implements EphemerisFile {

    /** Hashmap of satellite ephemeris. **/
    private final Map<String, OrekitSatelliteEphemeris> satellites;

    /**
     * Standard default constructor.
     */
    public OrekitEphemerisFile() {
        this.satellites = new ConcurrentHashMap<String, OrekitSatelliteEphemeris>();
    }

    @Override
    public Map<String, OrekitSatelliteEphemeris> getSatellites() {
        return Collections.unmodifiableMap(satellites);
    }

    /**
     * Adds a new satellite to this object.
     *
     * @param id
     *            ID to use for this satellite
     * @return the new satellite object
     */
    public OrekitSatelliteEphemeris addSatellite(final String id) {
        final OrekitSatelliteEphemeris newSat = new OrekitSatelliteEphemeris(id);
        this.satellites.put(id, newSat);
        return newSat;
    }

    /**
     * Inner class of {@link OrekitEphemerisFile} that defines the
     * {@link OrekitSatelliteEphemeris} corresponding object for this ephemeris type.
     *
     */
    public static class OrekitSatelliteEphemeris implements SatelliteEphemeris {
        /**
         * Defines the default interpolation sample size if it is not specified
         * on a segment.
         **/
        public static final int DEFAULT_INTERPOLATION_SIZE = 7;

        /** ID of the space object encapsulated here. **/
        private final String id;

        /** Earlist date of this file. **/
        private AbsoluteDate startDate;

        /** Latest date of this file. **/
        private AbsoluteDate stopDate;

        /** List of segements in the file. **/
        private final List<OrekitEphemerisSegment> segments;

        /**
         * Standard constructor for building the satellite Ephemeris object.
         *
         * @param id
         *            the ID of the space object for this data
         */
        public OrekitSatelliteEphemeris(final String id) {
            this.id = id;
            this.segments = new ArrayList<OrekitEphemerisSegment>();
        }

        @Override
        public String getId() {
            return id;
        }

        @Override
        public double getMu() {
            if (this.segments.size() == 0) {
                return 0;
            } else {
                return this.segments.get(0).getMu();
            }
        }

        @Override
        public List<? extends EphemerisSegment> getSegments() {
            return Collections.unmodifiableList(this.segments);
        }

        @Override
        public AbsoluteDate getStart() {
            return this.startDate;
        }

        @Override
        public AbsoluteDate getStop() {
            // TODO Auto-generated method stub
            return this.stopDate;
        }

        /**
         * Injects pre-computed satellite states into this ephemeris file
         * object, returning the generated {@link OrekitEphemerisSegment} that
         * has been stored internally. Defaults the celestial body to earth and
         * the interpolation size to the default.
         *
         * @param states
         *            a list of {@link SpacecraftState} that will comprise this
         *            new unit.
         * @return the generated {@link OrekitEphemerisSegment}
         * @throws OrekitException
         *             if there is an exception in time or frame transformations
         */
        public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states) throws OrekitException {
            return this.addNewSegment(states, CelestialBodyFactory.getEarth(), DEFAULT_INTERPOLATION_SIZE);
        }

        /**
         * Injects pre-computed satellite states into this ephemeris file
         * object, returning the generated {@link OrekitEphemerisSegment} that
         * has been stored internally. Defaults the Celestial Body to be Earths
         *
         * @param states
         *            a list of {@link SpacecraftState} that will comprise this
         *            new unit.
         * @param interpolationSampleSize
         *            the number of interpolation samples that should be used
         *            when processed by another system
         * @return the generated {@link OrekitEphemerisSegment}
         * @throws OrekitException
         *             if there is an exception in time or frame transformations
         */
        public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states,
                final int interpolationSampleSize) throws OrekitException {
            return this.addNewSegment(states, CelestialBodyFactory.getEarth(), interpolationSampleSize);
        }

        /**
         * Injects pre-computed satellite states into this ephemeris file
         * object, returning the generated {@link OrekitEphemerisSegment} that
         * has been stored internally.
         *
         * @param states
         *            a list of {@link SpacecraftState} that will comprise this
         *            new unit.
         * @param body
         *            the celestial body the state's frames are with respect to
         * @param interpolationSampleSize
         *            the number of interpolation samples that should be used
         *            when processed by another system
         * @return the generated {@link OrekitEphemerisSegment}
         * @throws OrekitException
         *             if there is an exception in time or frame transformations
         */
        public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states, final CelestialBody body,
                final int interpolationSampleSize) throws OrekitException {
            final int minimumSampleSize = 2;
            if (states == null || states.size() == 0) {
                throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "states");
            }

            if (interpolationSampleSize < minimumSampleSize) {
                throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION,
                        interpolationSampleSize);
            }

            final AbsoluteDate start = states.get(0).getDate();
            final AbsoluteDate stop = states.get(states.size() - 1).getDate();

            if (this.startDate == null || start.compareTo(this.startDate) < 0) {
                this.startDate = start;
            }

            if (this.stopDate == null || stop.compareTo(this.stopDate) > 0) {
                this.stopDate = stop;
            }

            final List<TimeStampedPVCoordinates> coordinates = new ArrayList<TimeStampedPVCoordinates>();
            for (SpacecraftState state : states) {
                coordinates.add(state.getPVCoordinates());
            }

            final Frame frame = states.get(0).getFrame();

            final OrekitEphemerisSegment newSeg = new OrekitEphemerisSegment(coordinates, frame, body.getName(),
                    body.getGM(), TimeScalesFactory.getUTC(), interpolationSampleSize);
            this.segments.add(newSeg);

            return newSeg;
        }
    }

    public static class OrekitEphemerisSegment implements EphemerisSegment {
        /** **/
        private final List<TimeStampedPVCoordinates> coordinates;

        /** **/
        private final Frame frame;

        /** **/
        private final String frameCenterString;

        /** **/
        private final double mu;

        /** **/
        private final String timeScaleString;

        /** **/
        private final TimeScale timeScale;

        /** **/
        private final int samples;

        /**
         * constructor for OrekitEphemerisSegment.
         *
         * @param coordinates
         *            coordinates making up the ephemeris for this segment
         * @param frame
         *            the frame the coordinates are in
         * @param frameCenterString
         *            the name of celestial body the frame is attached to
         * @param mu
         *            the gravitional constant used in force model evaluations
         * @param timeScale
         *            the time scale of these ephemeris points
         * @param samples
         *            the number of samples to use during interpolation
         */
        public OrekitEphemerisSegment(final List<TimeStampedPVCoordinates> coordinates, final Frame frame,
                final String frameCenterString, final double mu, final TimeScale timeScale, final int samples) {
            super();
            this.coordinates = coordinates;
            this.frame = frame;
            this.frameCenterString = frameCenterString;
            this.mu = mu;
            this.timeScale = timeScale;
            this.timeScaleString = timeScale.getName();
            this.samples = samples;
        }

        @Override
        public double getMu() {
            return mu;
        }

        @Override
        public String getFrameCenterString() {
            return frameCenterString;
        }

        @Override
        public String getFrameString() {
            return frame.getName();
        }

        @Override
        public Frame getFrame() throws OrekitException {
            return frame;
        }

        @Override
        public String getTimeScaleString() {
            return timeScaleString;
        }

        @Override
        public TimeScale getTimeScale() throws OrekitException {
            return timeScale;
        }

        @Override
        public int getInterpolationSamples() {
            return samples;
        }

        @Override
        public CartesianDerivativesFilter getAvailableDerivatives() {
            return CartesianDerivativesFilter.USE_PV;
        }

        @Override
        public List<TimeStampedPVCoordinates> getCoordinates() {
            return Collections.unmodifiableList(coordinates);
        }

        @Override
        public AbsoluteDate getStart() {
            return coordinates.get(0).getDate();
        }

        @Override
        public AbsoluteDate getStop() {
            return coordinates.get(coordinates.size() - 1).getDate();
        }

    }
}