Ephemeris.java
/* Copyright 2002-2016 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.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, Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20151022L;
/** 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} */
protected void resetIntermediateState(final SpacecraftState state, final boolean forward)
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, Serializable {
/** Serializable UID. */
private static final long serialVersionUID = 20160115L;
/** 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);
}
}
}