Commit 1cd1de20 authored by Luc Maisonobe's avatar Luc Maisonobe

Copy user additional states when no provider/equation is known.

parent c35cbfcc
......@@ -65,7 +65,8 @@ discrete events. Here is a short list of the features offered by the library:</p
<li>attitude state and derivative</li>
<li>Jacobians</li>
<li>mass management</li>
<li>user-defined associated state</li>
<li>user-defined associated state
(for example battery status, or higher order derivatives, or anything else)</li>
</ul>
</li>
<li>Maneuvers
......
......@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.orekit.attitudes.AttitudeProvider;
import org.orekit.errors.OrekitException;
......@@ -174,12 +175,32 @@ public abstract class AbstractPropagator implements Propagator {
*/
protected SpacecraftState updateAdditionalStates(final SpacecraftState original)
throws PropagationException {
// start with original state,
// which may already contain additional states, for example in interpolated ephemerides
SpacecraftState updated = original;
if (initialState != null) {
// there is an initial state
// (null initial states occur for example in interpolated ephemerides)
// copy the additional states present in initialState but otherwise not managed
for (final Map.Entry<String, double[]> initial : initialState.getAdditionalStates().entrySet()) {
if (!isAdditionalStateManaged(initial.getKey())) {
// this additional state was in the initial state, but is unknown to the propagator
// we simply copy its initial value as is
updated = updated.addAdditionalState(initial.getKey(), initial.getValue());
}
}
}
// update the additional states managed by providers
for (final AdditionalStateProvider provider : additionalStateProviders) {
updated = updated.addAdditionalState(provider.getName(),
provider.getAdditionalState(updated));
}
return updated;
}
/** {@inheritDoc} */
......@@ -193,7 +214,7 @@ public abstract class AbstractPropagator implements Propagator {
}
/** {@inheritDoc} */
public String[] getManagedStates() {
public String[] getManagedAdditionalStates() {
final String[] managed = new String[additionalStateProviders.size()];
for (int i = 0; i < managed.length; ++i) {
managed[i] = additionalStateProviders.get(i).getName();
......
......@@ -163,6 +163,26 @@ public interface Propagator extends PVCoordinatesProvider {
List<AdditionalStateProvider> getAdditionalStateProviders();
/** Check if an additional state is managed.
* <p>
* Managed states are states for which the propagators know how to compute
* its evolution. They correspond to additional states for which an
* {@link AdditionalStateProvider additional state provider} has been registered
* by calling the {@link #addAdditionalStateProvider(AdditionalStateProvider)
* addAdditionalStateProvider} method. If the propagator is an {@link
* org.orekit.propagation.integration.AbstractIntegratedPropagator integrator-based
* propagator}, the states for which a set of {@link
* org.orekit.propagation.integration.AdditionalEquations additional equations} has
* been registered by calling the {@link
* org.orekit.propagation.integration.AbstractIntegratedPropagator#addAdditionalEquations(
* org.orekit.propagation.integration.AdditionalEquations) addAdditionalEquations}
* method are also counted as managed additional states.
* </p>
* <p>
* Additional states that are present in the {@link #getInitialState() initial state}
* but have no evolution method registered are <em>not</em> considered as managed states.
* These unmanaged additional states are not lost during propagation, though. Their
* value will simply be copied unchanged throughout propagation.
* </p>
* @param name name of the additional state
* @return true if the additional state is managed
*/
......@@ -171,7 +191,7 @@ public interface Propagator extends PVCoordinatesProvider {
/** Get all the names of all managed states.
* @return names of all managed states
*/
String[] getManagedStates();
String[] getManagedAdditionalStates();
/** Add an event detector.
* @param detector event detector to add
......
......@@ -208,7 +208,7 @@ public class SpacecraftState
* creates a new instance, which has the same orbit, attitude, mass
* and additional states as the original instance, except it also
* has the specified state. If the original instance already had an
* additional state with the same name, it will be overriden. If it
* additional state with the same name, it will be overridden. If it
* did not have any additional state with that name, the new instance
* will have one more additional state than the original instance.
* </p>
......
......@@ -19,6 +19,7 @@ package org.orekit.propagation.analytical;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitExceptionWrapper;
......@@ -127,13 +128,22 @@ public class AdapterPropagator extends AbstractAnalyticalPropagator {
try {
// compute reference state
SpacecraftState state = reference.propagate(date);
final Map<String, double[]> before = state.getAdditionalStates();
// add all the effects
for (final DifferentialEffect effect : effects) {
state = effect.apply(state);
}
// forward additional states from the reference propagator
for (final Map.Entry<String, double[]> entry : before.entrySet()) {
if (!state.hasAdditionalState(entry.getKey())) {
state = state.addAdditionalState(entry.getKey(), entry.getValue());
}
}
return state;
} catch (OrekitExceptionWrapper oew) {
if (oew.getException() instanceof PropagationException) {
throw (PropagationException) oew.getException();
......
......@@ -177,8 +177,8 @@ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPr
/** {@inheritDoc} */
@Override
public String[] getManagedStates() {
final String[] upperManaged = super.getManagedStates();
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);
......
......@@ -19,7 +19,9 @@ package org.orekit.propagation.integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.math3.exception.MathIllegalArgumentException;
import org.apache.commons.math3.exception.MathIllegalStateException;
......@@ -189,8 +191,8 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator {
/** {@inheritDoc} */
@Override
public String[] getManagedStates() {
final String[] alreadyIntegrated = super.getManagedStates();
public String[] getManagedAdditionalStates() {
final String[] alreadyIntegrated = super.getManagedAdditionalStates();
final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
for (int i = 0; i < additionalEquations.size(); ++i) {
......@@ -971,15 +973,25 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator {
maxDate = stateMapper.mapDoubleToDate(tF);
}
// store the names of additional states
// get the initial additional states that are not managed
final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
if (!isAdditionalStateManaged(initial.getKey())) {
// this additional state was in the initial state, but is unknown to the propagator
// we simply copy its initial value as is
unmanaged.put(initial.getKey(), initial.getValue());
}
}
// get the names of additional states managed by differential equations
final String[] names = new String[additionalEquations.size()];
for (int i = 0; i < names.length; ++i) {
names[i] = additionalEquations.get(i).getName();
}
// create the ephemeris
// TODO: add the providers for already integrated additional states
ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
stateMapper, model,
stateMapper, model, unmanaged,
getAdditionalStateProviders(), names);
}
......
......@@ -17,6 +17,7 @@
package org.orekit.propagation.integration;
import java.util.List;
import java.util.Map;
import org.apache.commons.math3.ode.ContinuousOutputModel;
import org.orekit.errors.OrekitException;
......@@ -83,12 +84,16 @@ public class IntegratedEphemeris
/** Underlying raw mathematical model. */
private ContinuousOutputModel model;
/** Unmanaged additional states that must be simply copied. */
private final Map<String, double[]> unmanaged;
/** Creates a new instance of IntegratedEphemeris.
* @param startDate Start date of the integration (can be minDate or maxDate)
* @param minDate first date of the range
* @param maxDate last date of the range
* @param mapper mapper between raw double components and spacecraft state
* @param model underlying raw mathematical model
* @param unmanaged unmanaged additional states that must be simply copied
* @param providers providers for pre-integrated states
* @param equations names of additional equations
* @exception OrekitException if several providers have the same name
......@@ -96,6 +101,7 @@ public class IntegratedEphemeris
public IntegratedEphemeris(final AbsoluteDate startDate,
final AbsoluteDate minDate, final AbsoluteDate maxDate,
final StateMapper mapper, final ContinuousOutputModel model,
final Map<String, double[]> unmanaged,
final List<AdditionalStateProvider> providers,
final String[] equations)
throws OrekitException {
......@@ -107,6 +113,7 @@ public class IntegratedEphemeris
this.maxDate = maxDate;
this.mapper = mapper;
this.model = model;
this.unmanaged = unmanaged;
// set up the pre-integrated providers
for (final AdditionalStateProvider provider : providers) {
......@@ -150,8 +157,12 @@ public class IntegratedEphemeris
throws PropagationException {
try {
setInterpolationDate(date);
return mapper.mapArrayToState(model.getInterpolatedTime(),
model.getInterpolatedState());
SpacecraftState state = mapper.mapArrayToState(model.getInterpolatedTime(),
model.getInterpolatedState());
for (Map.Entry<String, double[]> initial : unmanaged.entrySet()) {
state = state.addAdditionalState(initial.getKey(), initial.getValue());
}
return state;
} catch (OrekitExceptionWrapper oew) {
if (oew.getException() instanceof PropagationException) {
throw (PropagationException) oew.getException();
......@@ -211,7 +222,7 @@ public class IntegratedEphemeris
/** {@inheritDoc} */
public SpacecraftState getInitialState() throws PropagationException {
return basicPropagate(getMinDate());
return updateAdditionalStates(basicPropagate(getMinDate()));
}
/** Local provider for additional state data. */
......
......@@ -137,6 +137,31 @@ Propagation
Event occurring can be automatically logged using the <EventsLogger> class.
** Additional states
All propagators can be used to propagate user additional states on top of regular
orbit attitude and mass state. These additional states will be available throughout
propagation, i.e. they can be used in the step handlers, in the event handlers and
they will also be available in the final propagated state. There are three main cases:
* if a deterministic way to compute the additional state from the spacecraft state
is known, then the user can put this computation in an implementation of the
<AdditionalStateProvider> interface and register it to the propagator, which will
call it each time it builds a <SpacecraftState>.
* if a differential equation drives the additional state evolution, then the user
can put this equation in an implementation of the <AdditionalEquations> interface
and register it to an integrator-based propagator, which will integrated it and
provide the integrated state.
* if no evolution laws are provided to the propagator, but the additional state is
available in the initial state, then the propagator will simply copy the initial
value throughout propagation, without evolving it.
The first two cases correspond to additional states managed by the propagator, the
last case not being considered as managed. The list of states managed by the propagator
is available using the <getManagedAdditionalStates> and <isAdditionalStateManaged>.
* Available propagators
The following class diagram shows the available propagators
......@@ -198,7 +223,7 @@ Propagation
the simulation. Various force models are already available in the library and specialized
ones can be added by users easily for specific needs.
The integrators (<first order integrators>) provided by commons-math need
The integrators (<first order integrators>) provided by Apache Commons Math need
the state vector at t0, the state vector first time derivative at t0,
and then calculates the next step state vector, and asks for the next first
time derivative, etc. until it reaches the final asked date. These underlying numerical
......
......@@ -73,6 +73,7 @@ Overview
* mass management
* user-defined associated state
(for example battery status, or higher order derivatives, or anything else)
** Maneuvers
......
......@@ -78,9 +78,9 @@
<action dev="luc" type="add" >
Added a way to store user data into SpacecraftState. User data are
simply double arrays associated to a name. They are handled properly
by interpolation. Note that since SpacecraftState instances are
immutable, adding states generates a new instance, using a fluent
API principle (fixes feature request #132).
by interpolation, event handlers, ephemerides and adapter propagators.
Note that since SpacecraftState instances are immutable, adding states
generates a new instance, using a fluent API principle (fixes feature request #132).
</action>
<action dev="luc" type="add" >
Added a way to retrieve all additional states at once from a step interpolator.
......
......@@ -45,6 +45,7 @@ import org.orekit.orbits.CircularOrbit;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.AdditionalStateProvider;
import org.orekit.propagation.BoundedPropagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.numerical.NumericalPropagator;
......@@ -90,6 +91,20 @@ public class AdapterPropagatorTest {
AdapterPropagator.DifferentialEffect effect =
new SmallManeuverAnalyticalModel(adapterPropagator.propagate(t0), dV.negate(), isp);
adapterPropagator.addEffect(effect);
adapterPropagator.addAdditionalStateProvider(new AdditionalStateProvider() {
public String getName() {
return "dummy 3";
}
public double[] getAdditionalState(SpacecraftState state) {
return new double[3];
}
});
// the adapted propagators do not manage the additional states from the reference,
// they simply forward them
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 1"));
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 2"));
Assert.assertTrue(adapterPropagator.isAdditionalStateManaged("dummy 3"));
for (AbsoluteDate t = t0.shiftedBy(0.5 * dt);
t.compareTo(withoutManeuver.getMaxDate()) < 0;
......@@ -98,6 +113,9 @@ public class AdapterPropagatorTest {
PVCoordinates pvReverted = adapterPropagator.getPVCoordinates(t, leo.getFrame());
double revertError = new PVCoordinates(pvWithout, pvReverted).getPosition().getNorm();
Assert.assertEquals(0, revertError, 0.45);
Assert.assertEquals(2, adapterPropagator.propagate(t).getAdditionalState("dummy 1").length);
Assert.assertEquals(1, adapterPropagator.propagate(t).getAdditionalState("dummy 2").length);
Assert.assertEquals(3, adapterPropagator.propagate(t).getAdditionalState("dummy 3").length);
}
}
......@@ -135,6 +153,20 @@ public class AdapterPropagatorTest {
AdapterPropagator.DifferentialEffect effect =
new SmallManeuverAnalyticalModel(adapterPropagator.propagate(t0), dV.negate(), isp);
adapterPropagator.addEffect(effect);
adapterPropagator.addAdditionalStateProvider(new AdditionalStateProvider() {
public String getName() {
return "dummy 3";
}
public double[] getAdditionalState(SpacecraftState state) {
return new double[3];
}
});
// the adapted propagators do not manage the additional states from the reference,
// they simply forward them
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 1"));
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 2"));
Assert.assertTrue(adapterPropagator.isAdditionalStateManaged("dummy 3"));
for (AbsoluteDate t = t0.shiftedBy(0.5 * dt);
t.compareTo(withoutManeuver.getMaxDate()) < 0;
......@@ -143,6 +175,9 @@ public class AdapterPropagatorTest {
PVCoordinates pvReverted = adapterPropagator.getPVCoordinates(t, heo.getFrame());
double revertError = new PVCoordinates(pvWithout, pvReverted).getPosition().getNorm();
Assert.assertEquals(0, revertError, 180.0);
Assert.assertEquals(2, adapterPropagator.propagate(t).getAdditionalState("dummy 1").length);
Assert.assertEquals(1, adapterPropagator.propagate(t).getAdditionalState("dummy 2").length);
Assert.assertEquals(3, adapterPropagator.propagate(t).getAdditionalState("dummy 3").length);
}
}
......@@ -190,6 +225,20 @@ public class AdapterPropagatorTest {
GravityFieldFactory.getUnnormalizedProvider(gravityField));
adapterPropagator.addEffect(directEffect);
adapterPropagator.addEffect(derivedEffect);
adapterPropagator.addAdditionalStateProvider(new AdditionalStateProvider() {
public String getName() {
return "dummy 3";
}
public double[] getAdditionalState(SpacecraftState state) {
return new double[3];
}
});
// the adapted propagators do not manage the additional states from the reference,
// they simply forward them
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 1"));
Assert.assertFalse(adapterPropagator.isAdditionalStateManaged("dummy 2"));
Assert.assertTrue(adapterPropagator.isAdditionalStateManaged("dummy 3"));
double maxDelta = 0;
double maxNominal = 0;
......@@ -202,7 +251,10 @@ public class AdapterPropagatorTest {
double nominal = new PVCoordinates(pvWithout, pvWith).getPosition().getNorm();
double revertError = new PVCoordinates(pvWithout, pvReverted).getPosition().getNorm();
maxDelta = FastMath.max(maxDelta, revertError);
maxNominal = FastMath.max(maxNominal, nominal);
maxNominal = FastMath.max(maxNominal, nominal);
Assert.assertEquals(2, adapterPropagator.propagate(t).getAdditionalState("dummy 1").length);
Assert.assertEquals(1, adapterPropagator.propagate(t).getAdditionalState("dummy 2").length);
Assert.assertEquals(3, adapterPropagator.propagate(t).getAdditionalState("dummy 3").length);
}
Assert.assertTrue(maxDelta < 120);
Assert.assertTrue(maxNominal > 2800);
......@@ -217,9 +269,12 @@ public class AdapterPropagatorTest {
final NormalizedSphericalHarmonicsProvider gravityField)
throws OrekitException, ParseException, IOException {
final SpacecraftState initialState =
SpacecraftState initialState =
new SpacecraftState(orbit, law.getAttitude(orbit, orbit.getDate(), orbit.getFrame()), mass);
// add some dummy additional states
initialState = initialState.addAdditionalState("dummy 1", 1.25, 2.5);
initialState = initialState.addAdditionalState("dummy 2", 5.0);
// set up numerical propagator
final double dP = 1.0;
......@@ -228,6 +283,14 @@ public class AdapterPropagatorTest {
new DormandPrince853Integrator(0.001, 1000, tolerances[0], tolerances[1]);
integrator.setInitialStepSize(orbit.getKeplerianPeriod() / 100.0);
final NumericalPropagator propagator = new NumericalPropagator(integrator);
propagator.addAdditionalStateProvider(new AdditionalStateProvider() {
public String getName() {
return "dummy 2";
}
public double[] getAdditionalState(SpacecraftState state) {
return new double[] { 5.0 };
}
});
propagator.setInitialState(initialState);
propagator.setAttitudeProvider(law);
......@@ -255,7 +318,19 @@ public class AdapterPropagatorTest {
propagator.setEphemerisMode();
propagator.propagate(t0.shiftedBy(nbOrbits * orbit.getKeplerianPeriod()));
return propagator.getGeneratedEphemeris();
final BoundedPropagator ephemeris = propagator.getGeneratedEphemeris();
// both the initial propagator and generated ephemeris manage one of the two
// additional states, but they also contain unmanaged copies of the other one
Assert.assertFalse(propagator.isAdditionalStateManaged("dummy 1"));
Assert.assertTrue(propagator.isAdditionalStateManaged("dummy 2"));
Assert.assertFalse(ephemeris.isAdditionalStateManaged("dummy 1"));
Assert.assertTrue(ephemeris.isAdditionalStateManaged("dummy 2"));
Assert.assertEquals(2, ephemeris.getInitialState().getAdditionalState("dummy 1").length);
Assert.assertEquals(1, ephemeris.getInitialState().getAdditionalState("dummy 2").length);
return ephemeris;
}
......
......@@ -201,8 +201,8 @@ public class TabulatedEphemerisTest {
double threshold, boolean expectedBelow)
throws OrekitException {
Assert.assertEquals(eph1.getManagedStates().length, eph2.getManagedStates().length);
for (String name : eph1.getManagedStates()) {
Assert.assertEquals(eph1.getManagedAdditionalStates().length, eph2.getManagedAdditionalStates().length);
for (String name : eph1.getManagedAdditionalStates()) {
Assert.assertTrue(eph2.isAdditionalStateManaged(name));
}
SpacecraftState state1 = eph1.propagate(date);
......@@ -213,7 +213,7 @@ public class TabulatedEphemerisTest {
maxError = FastMath.max(maxError, FastMath.abs(state1.getHx() - state2.getHx()));
maxError = FastMath.max(maxError, FastMath.abs(state1.getHy() - state2.getHy()));
maxError = FastMath.max(maxError, FastMath.abs(state1.getLv() - state2.getLv()));
for (String name : eph1.getManagedStates()) {
for (String name : eph1.getManagedAdditionalStates()) {
double[] add1 = state1.getAdditionalState(name);
double[] add2 = state2.getAdditionalState(name);
Assert.assertEquals(add1.length, add2.length);
......
......@@ -439,7 +439,7 @@ public class NumericalPropagatorTest {
Assert.assertTrue(propagator.isAdditionalStateManaged("linear"));
Assert.assertTrue(propagator.isAdditionalStateManaged("constant"));
Assert.assertFalse(propagator.isAdditionalStateManaged("non-managed"));
Assert.assertEquals(2, propagator.getManagedStates().length);
Assert.assertEquals(2, propagator.getManagedAdditionalStates().length);
propagator.setInitialState(propagator.getInitialState().addAdditionalState("linear", 1.5));
propagator.addEventDetector(new AbstractDetector(10.0, 1.0e-8) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment