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() {