diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2d7d344a7af032ca81ac670062aee6ed71809b11..aa1e45d949498b44b5ef14392368539e0c29856a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -24,6 +24,10 @@
Deleted deprecated methods in EclipseDetector.
+
+ Fix the bug of attitude transition with Ephemeris propagator
+ by adding a way for the LocalPVProvider to get the attitude at the end of the transition
+
Changing AbstractGNSSAttitudeProvider from public to package-private.
diff --git a/src/main/java/org/orekit/propagation/analytical/Ephemeris.java b/src/main/java/org/orekit/propagation/analytical/Ephemeris.java
index 44a19be556c0b7c49399561bbecc95c484c57b84..0667007f2be506b2bc1dc99e174a63da3c35c0e2 100644
--- a/src/main/java/org/orekit/propagation/analytical/Ephemeris.java
+++ b/src/main/java/org/orekit/propagation/analytical/Ephemeris.java
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import org.hipparchus.exception.Localizable;
import org.hipparchus.exception.LocalizedCoreFormats;
import org.hipparchus.exception.MathIllegalArgumentException;
import org.hipparchus.util.FastMath;
@@ -123,8 +124,8 @@ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPr
s0.ensureCompatibleAdditionalStates(state);
}
- pvProvider = new LocalPVProvider();
-
+ pvProvider = new LocalPVProvider(states, interpolationPoints, extrapolationThreshold);
+
// user needs to explicitly set attitude provider if they want to use one
setAttitudeProvider(null);
@@ -264,6 +265,18 @@ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPr
/** Current state. */
private SpacecraftState currentState;
+
+ private List states;
+ private int interpolationPoints;
+ private double extrapolationThreshold;
+
+ public LocalPVProvider(final List states, final int interpolationPoints,
+ final double extrapolationThreshold) {
+
+ this.states = states;
+ this.interpolationPoints = interpolationPoints;
+ this.extrapolationThreshold = extrapolationThreshold;
+ }
/** Get the current state.
* @return current state
@@ -285,11 +298,14 @@ public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPr
final double closeEnoughTimeInSec = 1e-9;
if (FastMath.abs(dt) > closeEnoughTimeInSec) {
- throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, FastMath.abs(dt), 0.0,
- closeEnoughTimeInSec);
+
+ // used in case of attitude transition, the attitude computed is not at the current date.
+ Ephemeris ephemeris = new Ephemeris(states, interpolationPoints, extrapolationThreshold);
+ return ephemeris.getPVCoordinates(date, f);
+
}
- return getCurrentState().getPVCoordinates(f);
+ return currentState.getPVCoordinates(f);
}
diff --git a/src/test/java/org/orekit/propagation/analytical/EphemerisTest.java b/src/test/java/org/orekit/propagation/analytical/EphemerisTest.java
index b03b00bec2c8c5fa433816e540081d7b5317eb99..a0a831a41d9199136b42bce7d78928f3c721c707 100644
--- a/src/test/java/org/orekit/propagation/analytical/EphemerisTest.java
+++ b/src/test/java/org/orekit/propagation/analytical/EphemerisTest.java
@@ -16,6 +16,7 @@
*/
package org.orekit.propagation.analytical;
+import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -24,12 +25,19 @@ import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Rotation;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
+import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.FastMath;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.orekit.Utils;
+import org.orekit.attitudes.AttitudeProvider;
+import org.orekit.attitudes.AttitudesSequence;
+import org.orekit.attitudes.CelestialBodyPointed;
import org.orekit.attitudes.LofOffset;
+import org.orekit.bodies.CelestialBodyFactory;
+import org.orekit.data.DataProvidersManager;
+import org.orekit.data.DirectoryCrawler;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.errors.TimeStampedCacheException;
@@ -42,10 +50,17 @@ import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.AdditionalStateProvider;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
+import org.orekit.propagation.events.DateDetector;
+import org.orekit.propagation.events.handlers.ContinueOnEvent;
+import org.orekit.propagation.numerical.NumericalPropagator;
+import org.orekit.propagation.sampling.OrekitFixedStepHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.DateComponents;
import org.orekit.time.TimeComponents;
import org.orekit.time.TimeScalesFactory;
+import org.orekit.utils.AngularDerivativesFilter;
+import org.orekit.utils.Constants;
+import org.orekit.utils.PVCoordinates;
public class EphemerisTest {
@@ -109,6 +124,88 @@ public class EphemerisTest {
}
}
+
+ @Test
+ public void testAttitudeSequenceTransition() {
+
+ // Initialize the orbit
+ final AbsoluteDate initialDate = new AbsoluteDate(2003, 01, 01, 0, 0, 00.000, TimeScalesFactory.getUTC());
+ final Vector3D position = new Vector3D(-39098981.4866597, -15784239.3610601, 78908.2289853595);
+ final Vector3D velocity = new Vector3D(1151.00321021175, -2851.14864755189, -2.02133248357321);
+ final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
+ FramesFactory.getGCRF(), initialDate,
+ Constants.WGS84_EARTH_MU);
+ final SpacecraftState initialState = new SpacecraftState(initialOrbit);
+
+ // Define attitude laws
+ AttitudeProvider before = new CelestialBodyPointed(FramesFactory.getICRF(), CelestialBodyFactory.getSun(), Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_K);
+ AttitudeProvider after = new CelestialBodyPointed(FramesFactory.getICRF(), CelestialBodyFactory.getEarth(), Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_K);
+
+ // Define attitude sequence
+ AbsoluteDate switchDate = initialDate.shiftedBy(86400.0);
+ double transitionTime = 600;
+ DateDetector switchDetector = new DateDetector(switchDate).withHandler(new ContinueOnEvent());
+
+ AttitudesSequence attitudeSequence = new AttitudesSequence();
+ attitudeSequence.resetActiveProvider(before);
+ attitudeSequence.addSwitchingCondition(before, after, switchDetector, true, false, transitionTime, AngularDerivativesFilter.USE_RR, null);
+
+ NumericalPropagator propagator = new NumericalPropagator(new DormandPrince853Integrator(0.1, 500, 1e-9, 1e-9));
+ propagator.setInitialState(initialState);
+
+ // Propagate and build ephemeris
+ final List propagatedStates = new ArrayList<>();
+
+ propagator.setMasterMode(60, new OrekitFixedStepHandler() {
+ @Override
+ public void handleStep(SpacecraftState currentState,
+ boolean isLast)
+ throws OrekitException
+ {
+ propagatedStates.add(currentState);
+ }
+ });
+ propagator.propagate(initialDate.shiftedBy(2*86400.0));
+ final Ephemeris ephemeris = new Ephemeris(propagatedStates, 8);
+
+ // Add attitude switch event to ephemeris
+ ephemeris.setAttitudeProvider(attitudeSequence);
+ attitudeSequence.registerSwitchEvents(ephemeris);
+
+ // Propagate with a step during the transition
+ AbsoluteDate endDate = initialDate.shiftedBy(2*86400.0);
+ SpacecraftState stateBefore = ephemeris.getInitialState();
+ ephemeris.propagate(switchDate.shiftedBy(transitionTime/2));
+ SpacecraftState stateAfter = ephemeris.propagate(endDate);
+
+
+ // Check that the attitudes are correct
+ Assert.assertEquals(before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ0(),
+ stateBefore.getAttitude().getRotation().getQ0(),
+ 1.0E-16);
+ Assert.assertEquals(before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ1(),
+ stateBefore.getAttitude().getRotation().getQ1(),
+ 1.0E-16);
+ Assert.assertEquals(before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ2(),
+ stateBefore.getAttitude().getRotation().getQ2(),
+ 1.0E-16);
+ Assert.assertEquals(before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ3(),
+ stateBefore.getAttitude().getRotation().getQ3(),
+ 1.0E-16);
+
+ Assert.assertEquals(after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ0(),
+ stateAfter.getAttitude().getRotation().getQ0(),
+ 1.0E-16);
+ Assert.assertEquals(after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ1(),
+ stateAfter.getAttitude().getRotation().getQ1(),
+ 1.0E-16);
+ Assert.assertEquals(after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ2(),
+ stateAfter.getAttitude().getRotation().getQ2(),
+ 1.0E-16);
+ Assert.assertEquals(after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ3(),
+ stateAfter.getAttitude().getRotation().getQ3(),
+ 1.0E-16);
+ }
@Test
public void testNonResettableState() {