Java tutorial
/* Copyright 2002-2015 CS Systmes d'Information * Licensed to CS Systmes 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.propagation.analytical; import java.io.Serializable; import java.util.List; import java.util.Set; import org.apache.commons.math3.exception.MathIllegalArgumentException; import org.apache.commons.math3.exception.OutOfRangeException; import org.apache.commons.math3.exception.util.LocalizedFormats; import org.apache.commons.math3.util.FastMath; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitInternalError; import org.orekit.errors.OrekitMessages; import org.orekit.errors.PropagationException; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ImmutableTimeStampedCache; import org.orekit.utils.TimeStampedPVCoordinates; import org.orekit.utils.PVCoordinatesProvider; /** This class is designed to accept and handle tabulated orbital entries. * Tabulated entries are classified and then extrapolated in way to obtain * continuous output, with accuracy and computation methods configured by the user. * * @author Fabien Maussion * @author Véronique Pommier-Maurussane * @author Luc Maisonobe */ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPropagator { /** First date in range. */ private final AbsoluteDate minDate; /** Last date in range. */ private final AbsoluteDate maxDate; /** Reference frame. */ private final Frame frame; /** Names of the additional states. */ private final String[] additional; /** Local PV Provider used for computing attitude. **/ private LocalPVProvider pvProvider; /** Thread-safe cache. */ private final transient ImmutableTimeStampedCache<SpacecraftState> cache; /** Constructor with tabulated states. * @param states tabulates states * @param interpolationPoints number of points to use in interpolation * @exception OrekitException if some states have incompatible additional states * @exception MathIllegalArgumentException if the number of states is smaller than * the number of points to use in interpolation */ public Ephemeris(final List<SpacecraftState> states, final int interpolationPoints) throws OrekitException, MathIllegalArgumentException { super(DEFAULT_LAW); if (states.size() < interpolationPoints) { throw new MathIllegalArgumentException(LocalizedFormats.INSUFFICIENT_DIMENSION, states.size(), interpolationPoints); } final SpacecraftState s0 = states.get(0); minDate = s0.getDate(); maxDate = states.get(states.size() - 1).getDate(); frame = s0.getFrame(); final Set<String> names0 = s0.getAdditionalStates().keySet(); additional = names0.toArray(new String[names0.size()]); // check all states handle the same additional states for (final SpacecraftState state : states) { s0.ensureCompatibleAdditionalStates(state); } pvProvider = new LocalPVProvider(); //User needs to explicitly set attitude provider if they want to use one this.setAttitudeProvider(null); // set up cache cache = new ImmutableTimeStampedCache<SpacecraftState>(interpolationPoints, states); } /** Get the first date of the range. * @return the first date of the range */ public AbsoluteDate getMinDate() { return minDate; } /** Get the last date of the range. * @return the last date of the range */ public AbsoluteDate getMaxDate() { return maxDate; } @Override public Frame getFrame() { return this.frame; } @Override /** {@inheritDoc} */ public SpacecraftState basicPropagate(final AbsoluteDate date) throws PropagationException { try { final List<SpacecraftState> neighbors = cache.getNeighbors(date); final SpacecraftState interpolatedState = neighbors.get(0).interpolate(date, neighbors); final AttitudeProvider attitudeProvider = this.getAttitudeProvider(); if (attitudeProvider == null) { return interpolatedState; } else { pvProvider.setCurrentState(interpolatedState); final Attitude calculatedAttitude = attitudeProvider.getAttitude(pvProvider, date, interpolatedState.getFrame()); return new SpacecraftState(interpolatedState.getOrbit(), calculatedAttitude, interpolatedState.getMass()); } } catch (OrekitException tce) { throw new PropagationException(tce); } } /** {@inheritDoc} */ protected Orbit propagateOrbit(final AbsoluteDate date) throws PropagationException { return basicPropagate(date).getOrbit(); } /** {@inheritDoc} */ protected double getMass(final AbsoluteDate date) throws PropagationException { return basicPropagate(date).getMass(); } /** {@inheritDoc} */ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f) throws OrekitException { return propagate(date).getPVCoordinates(f); } /** Try (and fail) to reset the initial state. * <p> * This method always throws an exception, as ephemerides cannot be reset. * </p> * @param state new initial state to consider * @exception PropagationException always thrown as ephemerides cannot be reset */ public void resetInitialState(final SpacecraftState state) throws PropagationException { throw new PropagationException(OrekitMessages.NON_RESETABLE_STATE); } /** {@inheritDoc} */ public SpacecraftState getInitialState() throws PropagationException { return basicPropagate(getMinDate()); } /** {@inheritDoc} */ @Override public boolean isAdditionalStateManaged(final String name) { // the additional state may be managed by a specific provider in the base class if (super.isAdditionalStateManaged(name)) { return true; } // the additional state may be managed in the states sample for (final String a : additional) { if (a.equals(name)) { return true; } } return false; } /** {@inheritDoc} */ @Override public String[] getManagedAdditionalStates() { final String[] upperManaged = super.getManagedAdditionalStates(); final String[] managed = new String[upperManaged.length + additional.length]; System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length); System.arraycopy(additional, 0, managed, upperManaged.length, additional.length); return managed; } /** Replace the instance with a data transfer object for serialization. * <p> * This intermediate class serializes only the data needed for generation, * but does <em>not</em> serializes the cache itself (in fact the cache is * not serializable). * </p> * @return data transfer object that will be serialized */ private Object writeReplace() { return new DataTransferObject(cache.getAll(), cache.getNeighborsSize()); } /** Internal class used only for serialization. */ private static class DataTransferObject implements Serializable { /** Serializable UID. */ private static final long serialVersionUID = -8479036196711159270L; /** Tabulates states. */ private final List<SpacecraftState> states; /** Number of points to use in interpolation. */ private final int interpolationPoints; /** Simple constructor. * @param states tabulates states * @param interpolationPoints number of points to use in interpolation */ private DataTransferObject(final List<SpacecraftState> states, final int interpolationPoints) { this.states = states; this.interpolationPoints = interpolationPoints; } /** Replace the deserialized data transfer object with a {@link Ephemeris}. * @return replacement {@link Ephemeris} */ private Object readResolve() { try { // build a new provider, with an empty cache return new Ephemeris(states, interpolationPoints); } catch (OrekitException oe) { // this should never happen throw new OrekitInternalError(oe); } } } /** Internal PVCoordinatesProvider for attitude computation. */ private static class LocalPVProvider implements PVCoordinatesProvider { /** Current state. */ private SpacecraftState currentState; /** Get the current state. * @return current state */ public SpacecraftState getCurrentState() { return currentState; } /** Set the current state. * @param state state to set */ public void setCurrentState(final SpacecraftState state) { this.currentState = state; } /** {@inheritDoc} */ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame f) throws OrekitException { final double dt = this.getCurrentState().getDate().durationFrom(date); final double closeEnoughTimeInSec = 1e-9; if (FastMath.abs(dt) > closeEnoughTimeInSec) { throw new OrekitException( new OutOfRangeException(new Double(FastMath.abs(dt)), 0.0, closeEnoughTimeInSec)); } return this.getCurrentState().getPVCoordinates(f); } } }