/* Copyright 2002-2017 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.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import org.hipparchus.exception.DummyLocalizable; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.orekit.Utils; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.LofOffset; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.TideSystem; import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.forces.maneuvers.ImpulseManeuver; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; import org.orekit.frames.TopocentricFrame; import org.orekit.frames.Transform; import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngle; import org.orekit.propagation.AdditionalStateProvider; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.EcksteinHechlerPropagatorBuilder; import org.orekit.propagation.conversion.FiniteDifferencePropagatorConverter; import org.orekit.propagation.conversion.PropagatorConverter; import org.orekit.propagation.events.ApsideDetector; import org.orekit.propagation.events.DateDetector; import org.orekit.propagation.events.ElevationDetector; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.events.NodeDetector; 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.utils.CartesianDerivativesFilter; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedPVCoordinates; public class EcksteinHechlerPropagatorTest { @Test public void sameDateCartesian() throws OrekitException { // Definition of initial conditions with position and velocity // ------------------------------------------------------------ // with e around e = 1.4e-4 and i = 1.7 rad Vector3D position = new Vector3D(3220103., 69623., 6449822.); Vector3D velocity = new Vector3D(6414.7, -2006., -3180.); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, provider); // Extrapolation at the initial date // --------------------------------- SpacecraftState finalOrbit = extrapolator.propagate(initDate); // positions match perfectly Assert.assertEquals(0.0, Vector3D.distance(initialOrbit.getPVCoordinates().getPosition(), finalOrbit.getPVCoordinates().getPosition()), 1.0e-8); // velocity and circular parameters do *not* match, this is EXPECTED! // the reason is that we ensure position/velocity are consistent with the // evolution of the orbit, and this includes the non-Keplerian effects, // whereas the initial orbit is Keplerian only. The implementation of the // model is such that rather than having a perfect match at initial point // (either in velocity or in circular parameters), we have a propagated orbit // that remains close to a numerical reference throughout the orbit. // This is shown in the testInitializationCorrectness() where a numerical // fit is used to check initialization Assert.assertEquals(0.137, Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), finalOrbit.getPVCoordinates().getVelocity()), 1.0e-3); Assert.assertEquals(125.2, finalOrbit.getA() - initialOrbit.getA(), 0.1); } @Test public void sameDateKeplerian() throws OrekitException { // Definition of initial conditions with keplerian parameters // ----------------------------------------------------------- AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); Orbit initialOrbit = new KeplerianOrbit(7209668.0, 0.5e-4, 1.7, 2.1, 2.9, 6.2, PositionAngle.TRUE, FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, Propagator.DEFAULT_MASS, provider); // Extrapolation at the initial date // --------------------------------- SpacecraftState finalOrbit = extrapolator.propagate(initDate); // positions match perfectly Assert.assertEquals(0.0, Vector3D.distance(initialOrbit.getPVCoordinates().getPosition(), finalOrbit.getPVCoordinates().getPosition()), 3.0e-8); // velocity and circular parameters do *not* match, this is EXPECTED! // the reason is that we ensure position/velocity are consistent with the // evolution of the orbit, and this includes the non-Keplerian effects, // whereas the initial orbit is Keplerian only. The implementation of the // model is such that rather than having a perfect match at initial point // (either in velocity or in circular parameters), we have a propagated orbit // that remains close to a numerical reference throughout the orbit. // This is shown in the testInitializationCorrectness() where a numerical // fit is used to check initialization Assert.assertEquals(0.137, Vector3D.distance(initialOrbit.getPVCoordinates().getVelocity(), finalOrbit.getPVCoordinates().getVelocity()), 1.0e-3); Assert.assertEquals(126.8, finalOrbit.getA() - initialOrbit.getA(), 0.1); } @Test public void almostSphericalBody() throws OrekitException { // Definition of initial conditions // --------------------------------- // with e around e = 1.4e-4 and i = 1.7 rad Vector3D position = new Vector3D(3220103., 69623., 6449822.); Vector3D velocity = new Vector3D(6414.7, -2006., -3180.); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), initDate, provider.getMu()); // Initialisation to simulate a keplerian extrapolation // To be noticed: in order to simulate a keplerian extrapolation with the // analytical // extrapolator, one should put the zonal coefficients to 0. But due to // numerical pbs // one must put a non 0 value. UnnormalizedSphericalHarmonicsProvider kepProvider = GravityFieldFactory.getUnnormalizedProvider(6.378137e6, 3.9860047e14, TideSystem.UNKNOWN, new double[][] { { 0 }, { 0 }, { 0.1e-10 }, { 0.1e-13 }, { 0.1e-13 }, { 0.1e-14 }, { 0.1e-14 } }, new double[][] { { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }); // Extrapolators definitions // ------------------------- EcksteinHechlerPropagator extrapolatorAna = new EcksteinHechlerPropagator(initialOrbit, 1000.0, kepProvider); KeplerianPropagator extrapolatorKep = new KeplerianPropagator(initialOrbit); // Extrapolation at a final date different from initial date // --------------------------------------------------------- double delta_t = 100.0; // extrapolation duration in seconds AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); SpacecraftState finalOrbitAna = extrapolatorAna.propagate(extrapDate); SpacecraftState finalOrbitKep = extrapolatorKep.propagate(extrapDate); Assert.assertEquals(finalOrbitAna.getDate().durationFrom(extrapDate), 0.0, Utils.epsilonTest); // comparison of each orbital parameters Assert.assertEquals(finalOrbitAna.getA(), finalOrbitKep.getA(), 10 * Utils.epsilonTest * finalOrbitKep.getA()); Assert.assertEquals(finalOrbitAna.getEquinoctialEx(), finalOrbitKep.getEquinoctialEx(), Utils.epsilonE * finalOrbitKep.getE()); Assert.assertEquals(finalOrbitAna.getEquinoctialEy(), finalOrbitKep.getEquinoctialEy(), Utils.epsilonE * finalOrbitKep.getE()); Assert.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getHx(), finalOrbitKep.getHx()), finalOrbitKep.getHx(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getI())); Assert.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getHy(), finalOrbitKep.getHy()), finalOrbitKep.getHy(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getI())); Assert.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getLv(), finalOrbitKep.getLv()), finalOrbitKep.getLv(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getLv())); Assert.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getLE(), finalOrbitKep.getLE()), finalOrbitKep.getLE(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getLE())); Assert.assertEquals(MathUtils.normalizeAngle(finalOrbitAna.getLM(), finalOrbitKep.getLM()), finalOrbitKep.getLM(), Utils.epsilonAngle * FastMath.abs(finalOrbitKep.getLM())); } @Test public void propagatedCartesian() throws OrekitException { // Definition of initial conditions with position and velocity // ------------------------------------------------------------ // with e around e = 1.4e-4 and i = 1.7 rad Vector3D position = new Vector3D(3220103., 69623., 6449822.); Vector3D velocity = new Vector3D(6414.7, -2006., -3180.); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, new LofOffset(initialOrbit.getFrame(), LOFType.VNC, RotationOrder.XYZ, 0, 0, 0), provider); // Extrapolation at a final date different from initial date // --------------------------------------------------------- double delta_t = 100000.0; // extrapolation duration in seconds AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); SpacecraftState finalOrbit = extrapolator.propagate(extrapDate); Assert.assertEquals(0.0, finalOrbit.getDate().durationFrom(extrapDate), 1.0e-9); // computation of M final orbit double LM = finalOrbit.getLE() - finalOrbit.getEquinoctialEx() * FastMath.sin(finalOrbit.getLE()) + finalOrbit.getEquinoctialEy() * FastMath.cos(finalOrbit.getLE()); Assert.assertEquals(LM, finalOrbit.getLM(), Utils.epsilonAngle * FastMath.abs(finalOrbit.getLM())); // test of tan ((LE - Lv)/2) : Assert.assertEquals(FastMath.tan((finalOrbit.getLE() - finalOrbit.getLv()) / 2.), tangLEmLv(finalOrbit.getLv(), finalOrbit.getEquinoctialEx(), finalOrbit .getEquinoctialEy()), Utils.epsilonAngle); // test of evolution of M vs E: LM = LE - ex*sin(LE) + ey*cos(LE) double deltaM = finalOrbit.getLM() - initialOrbit.getLM(); double deltaE = finalOrbit.getLE() - initialOrbit.getLE(); double delta = finalOrbit.getEquinoctialEx() * FastMath.sin(finalOrbit.getLE()) - initialOrbit.getEquinoctialEx() * FastMath.sin(initialOrbit.getLE()) - finalOrbit.getEquinoctialEy() * FastMath.cos(finalOrbit.getLE()) + initialOrbit.getEquinoctialEy() * FastMath.cos(initialOrbit.getLE()); Assert.assertEquals(deltaM, deltaE - delta, Utils.epsilonAngle * FastMath.abs(deltaE - delta)); // for final orbit double ex = finalOrbit.getEquinoctialEx(); double ey = finalOrbit.getEquinoctialEy(); double hx = finalOrbit.getHx(); double hy = finalOrbit.getHy(); double LE = finalOrbit.getLE(); double ex2 = ex * ex; double ey2 = ey * ey; double hx2 = hx * hx; double hy2 = hy * hy; double h2p1 = 1. + hx2 + hy2; double beta = 1. / (1. + FastMath.sqrt(1. - ex2 - ey2)); double x3 = -ex + (1. - beta * ey2) * FastMath.cos(LE) + beta * ex * ey * FastMath.sin(LE); double y3 = -ey + (1. - beta * ex2) * FastMath.sin(LE) + beta * ex * ey * FastMath.cos(LE); Vector3D U = new Vector3D((1. + hx2 - hy2) / h2p1, (2. * hx * hy) / h2p1, (-2. * hy) / h2p1); Vector3D V = new Vector3D((2. * hx * hy) / h2p1, (1. - hx2 + hy2) / h2p1, (2. * hx) / h2p1); Vector3D r = new Vector3D(finalOrbit.getA(), (new Vector3D(x3, U, y3, V))); Assert.assertEquals(finalOrbit.getPVCoordinates().getPosition().getNorm(), r.getNorm(), Utils.epsilonTest * r.getNorm()); } @Test public void propagatedKeplerian() throws OrekitException { // Definition of initial conditions with keplerian parameters // ----------------------------------------------------------- AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH.shiftedBy(584.); Orbit initialOrbit = new KeplerianOrbit(7209668.0, 0.5e-4, 1.7, 2.1, 2.9, 6.2, PositionAngle.TRUE, FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, new LofOffset(initialOrbit.getFrame(), LOFType.VNC, RotationOrder.XYZ, 0, 0, 0), 2000.0, provider); // Extrapolation at a final date different from initial date // --------------------------------------------------------- double delta_t = 100000.0; // extrapolation duration in seconds AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); SpacecraftState finalOrbit = extrapolator.propagate(extrapDate); Assert.assertEquals(0.0, finalOrbit.getDate().durationFrom(extrapDate), 1.0e-9); // computation of M final orbit double LM = finalOrbit.getLE() - finalOrbit.getEquinoctialEx() * FastMath.sin(finalOrbit.getLE()) + finalOrbit.getEquinoctialEy() * FastMath.cos(finalOrbit.getLE()); Assert.assertEquals(LM, finalOrbit.getLM(), Utils.epsilonAngle); // test of tan((LE - Lv)/2) : Assert.assertEquals(FastMath.tan((finalOrbit.getLE() - finalOrbit.getLv()) / 2.), tangLEmLv(finalOrbit.getLv(), finalOrbit.getEquinoctialEx(), finalOrbit .getEquinoctialEy()), Utils.epsilonAngle); // test of evolution of M vs E: LM = LE - ex*sin(LE) + ey*cos(LE) // with ex and ey the same for initial and final orbit double deltaM = finalOrbit.getLM() - initialOrbit.getLM(); double deltaE = finalOrbit.getLE() - initialOrbit.getLE(); double delta = finalOrbit.getEquinoctialEx() * FastMath.sin(finalOrbit.getLE()) - initialOrbit.getEquinoctialEx() * FastMath.sin(initialOrbit.getLE()) - finalOrbit.getEquinoctialEy() * FastMath.cos(finalOrbit.getLE()) + initialOrbit.getEquinoctialEy() * FastMath.cos(initialOrbit.getLE()); Assert.assertEquals(deltaM, deltaE - delta, Utils.epsilonAngle * FastMath.abs(deltaE - delta)); // for final orbit double ex = finalOrbit.getEquinoctialEx(); double ey = finalOrbit.getEquinoctialEy(); double hx = finalOrbit.getHx(); double hy = finalOrbit.getHy(); double LE = finalOrbit.getLE(); double ex2 = ex * ex; double ey2 = ey * ey; double hx2 = hx * hx; double hy2 = hy * hy; double h2p1 = 1. + hx2 + hy2; double beta = 1. / (1. + FastMath.sqrt(1. - ex2 - ey2)); double x3 = -ex + (1. - beta * ey2) * FastMath.cos(LE) + beta * ex * ey * FastMath.sin(LE); double y3 = -ey + (1. - beta * ex2) * FastMath.sin(LE) + beta * ex * ey * FastMath.cos(LE); Vector3D U = new Vector3D((1. + hx2 - hy2) / h2p1, (2. * hx * hy) / h2p1, (-2. * hy) / h2p1); Vector3D V = new Vector3D((2. * hx * hy) / h2p1, (1. - hx2 + hy2) / h2p1, (2. * hx) / h2p1); Vector3D r = new Vector3D(finalOrbit.getA(), (new Vector3D(x3, U, y3, V))); Assert.assertEquals(finalOrbit.getPVCoordinates().getPosition().getNorm(), r.getNorm(), Utils.epsilonTest * r.getNorm()); } @Test(expected = OrekitException.class) public void undergroundOrbit() throws OrekitException { // for a semi major axis < equatorial radius Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); Vector3D velocity = new Vector3D(-500.0, 800.0, 100.0); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, provider); // Extrapolation at the initial date // --------------------------------- double delta_t = 0.0; AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); extrapolator.propagate(extrapDate); } @Test(expected = OrekitException.class) public void equatorialOrbit() throws OrekitException { AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; Orbit initialOrbit = new CircularOrbit(7000000, 1.0e-4, -1.5e-4, 0.0, 1.2, 2.3, PositionAngle.MEAN, FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, provider); // Extrapolation at the initial date // --------------------------------- double delta_t = 0.0; AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); extrapolator.propagate(extrapDate); } @Test(expected = OrekitException.class) public void criticalInclination() throws OrekitException { AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; Orbit initialOrbit = new CircularOrbit(new PVCoordinates(new Vector3D(-3862363.8474653554, -3521533.9758022362, 4647637.852558916), new Vector3D(65.36170817232278, -6056.563439401233, -4511.1247889782757)), FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, provider); // Extrapolation at the initial date // --------------------------------- double delta_t = 0.0; AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); extrapolator.propagate(extrapDate); } @Test(expected = OrekitException.class) public void tooEllipticalOrbit() throws OrekitException { // for an eccentricity too big for the model Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); Vector3D velocity = new Vector3D(-500.0, 8000.0, 1000.0); AbsoluteDate initDate = AbsoluteDate.J2000_EPOCH; Orbit initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), initDate, provider.getMu()); // Extrapolator definition // ----------------------- EcksteinHechlerPropagator extrapolator = new EcksteinHechlerPropagator(initialOrbit, provider); // Extrapolation at the initial date // --------------------------------- double delta_t = 0.0; AbsoluteDate extrapDate = initDate.shiftedBy(delta_t); extrapolator.propagate(extrapDate); } @Test(expected = OrekitException.class) public void hyperbolic() throws OrekitException { KeplerianOrbit hyperbolic = new KeplerianOrbit(-1.0e10, 2, 0, 0, 0, 0, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(hyperbolic, provider); propagator.propagate(AbsoluteDate.J2000_EPOCH.shiftedBy(10.0)); } @Test(expected = OrekitException.class) public void wrongAttitude() throws OrekitException { KeplerianOrbit orbit = new KeplerianOrbit(1.0e10, 1.0e-4, 1.0e-2, 0, 0, 0, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); AttitudeProvider wrongLaw = new AttitudeProvider() { private static final long serialVersionUID = 5918362126173997016L; public Attitude getAttitude(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) throws OrekitException { throw new OrekitException(new DummyLocalizable("gasp"), new RuntimeException()); } }; EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, wrongLaw, provider); propagator.propagate(AbsoluteDate.J2000_EPOCH.shiftedBy(10.0)); } @Test public void testAcceleration() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); AbsoluteDate target = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); List<TimeStampedPVCoordinates> sample = new ArrayList<TimeStampedPVCoordinates>(); for (double dt : Arrays.asList(-0.5, 0.0, 0.5)) { sample.add(propagator.propagate(target.shiftedBy(dt)).getPVCoordinates()); } TimeStampedPVCoordinates interpolated = TimeStampedPVCoordinates.interpolate(target, CartesianDerivativesFilter.USE_P, sample); Vector3D computedP = sample.get(1).getPosition(); Vector3D computedV = sample.get(1).getVelocity(); Vector3D referenceP = interpolated.getPosition(); Vector3D referenceV = interpolated.getVelocity(); Vector3D computedA = sample.get(1).getAcceleration(); Vector3D referenceA = interpolated.getAcceleration(); final CircularOrbit propagated = (CircularOrbit) OrbitType.CIRCULAR.convertType(propagator.propagateOrbit(target)); final CircularOrbit keplerian = new CircularOrbit(propagated.getA(), propagated.getCircularEx(), propagated.getCircularEy(), propagated.getI(), propagated.getRightAscensionOfAscendingNode(), propagated.getAlphaM(), PositionAngle.MEAN, propagated.getFrame(), propagated.getDate(), propagated.getMu()); Vector3D keplerianP = keplerian.getPVCoordinates().getPosition(); Vector3D keplerianV = keplerian.getPVCoordinates().getVelocity(); Vector3D keplerianA = keplerian.getPVCoordinates().getAcceleration(); // perturbed orbit position should be similar to Keplerian orbit position Assert.assertEquals(0.0, Vector3D.distance(referenceP, computedP), 1.0e-15); Assert.assertEquals(0.0, Vector3D.distance(referenceP, keplerianP), 4.0e-9); // perturbed orbit velocity should be equal to Keplerian orbit because // it was in fact reconstructed from Cartesian coordinates double computationErrorV = Vector3D.distance(referenceV, computedV); double nonKeplerianEffectV = Vector3D.distance(referenceV, keplerianV); Assert.assertEquals(nonKeplerianEffectV, computationErrorV, 9.0e-13); Assert.assertEquals(2.2e-4, computationErrorV, 3.0e-6); // perturbed orbit acceleration should be different from Keplerian orbit because // Keplerian orbit doesn't take orbit shape changes into account // perturbed orbit acceleration should be consistent with position evolution double computationErrorA = Vector3D.distance(referenceA, computedA); double nonKeplerianEffectA = Vector3D.distance(referenceA, keplerianA); Assert.assertEquals(1.0e-7, computationErrorA, 6.0e-9); Assert.assertEquals(6.37e-3, nonKeplerianEffectA, 7.0e-6); Assert.assertTrue(computationErrorA < nonKeplerianEffectA / 60000); } @Test public void ascendingNode() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); NodeDetector detector = new NodeDetector(orbit, FramesFactory.getITRF(IERSConventions.IERS_2010, true)); Assert.assertTrue(FramesFactory.getITRF(IERSConventions.IERS_2010, true) == detector.getFrame()); propagator.addEventDetector(detector); AbsoluteDate farTarget = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); SpacecraftState propagated = propagator.propagate(farTarget); PVCoordinates pv = propagated.getPVCoordinates(FramesFactory.getITRF(IERSConventions.IERS_2010, true)); Assert.assertTrue(farTarget.durationFrom(propagated.getDate()) > 3500.0); Assert.assertTrue(farTarget.durationFrom(propagated.getDate()) < 4000.0); Assert.assertEquals(0, pv.getPosition().getZ(), 1.0e-6); Assert.assertTrue(pv.getVelocity().getZ() > 0); Collection<EventDetector> detectors = propagator.getEventsDetectors(); Assert.assertEquals(1, detectors.size()); propagator.clearEventsDetectors(); Assert.assertEquals(0, propagator.getEventsDetectors().size()); } @Test public void stopAtTargetDate() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); propagator.addEventDetector(new NodeDetector(orbit, itrf).withHandler(new ContinueOnEvent<NodeDetector>())); AbsoluteDate farTarget = orbit.getDate().shiftedBy(10000.0); SpacecraftState propagated = propagator.propagate(farTarget); Assert.assertEquals(0.0, FastMath.abs(farTarget.durationFrom(propagated.getDate())), 1.0e-3); } @Test public void perigee() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); propagator.addEventDetector(new ApsideDetector(orbit)); AbsoluteDate farTarget = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); SpacecraftState propagated = propagator.propagate(farTarget); PVCoordinates pv = propagated.getPVCoordinates(FramesFactory.getITRF(IERSConventions.IERS_2010, true)); Assert.assertTrue(farTarget.durationFrom(propagated.getDate()) > 3000.0); Assert.assertTrue(farTarget.durationFrom(propagated.getDate()) < 3500.0); Assert.assertEquals(orbit.getA() * (1.0 - orbit.getE()), pv.getPosition().getNorm(), 410); } @Test public void date() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); final AbsoluteDate stopDate = AbsoluteDate.J2000_EPOCH.shiftedBy(500.0); propagator.addEventDetector(new DateDetector(stopDate)); AbsoluteDate farTarget = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); SpacecraftState propagated = propagator.propagate(farTarget); Assert.assertEquals(0, stopDate.durationFrom(propagated.getDate()), 1.0e-10); } @Test public void fixedStep() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); final double step = 100.0; propagator.setMasterMode(step, new OrekitFixedStepHandler() { private AbsoluteDate previous; public void handleStep(SpacecraftState currentState, boolean isLast) throws OrekitException { if (previous != null) { Assert.assertEquals(step, currentState.getDate().durationFrom(previous), 1.0e-10); } previous = currentState.getDate(); } }); AbsoluteDate farTarget = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); propagator.propagate(farTarget); } @Test public void setting() throws OrekitException { final KeplerianOrbit orbit = new KeplerianOrbit(7.8e6, 0.032, 0.4, 0.1, 0.2, 0.3, PositionAngle.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, 3.986004415e14); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(orbit, provider); final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final TopocentricFrame topo = new TopocentricFrame(earthShape, new GeodeticPoint(0.389, -2.962, 0), null); ElevationDetector detector = new ElevationDetector(60, 1.0e-9, topo).withConstantElevation(0.09); Assert.assertEquals(0.09, detector.getMinElevation(), 1.0e-12); Assert.assertTrue(topo == detector.getTopocentricFrame()); propagator.addEventDetector(detector); AbsoluteDate farTarget = AbsoluteDate.J2000_EPOCH.shiftedBy(10000.0); SpacecraftState propagated = propagator.propagate(farTarget); final double elevation = topo.getElevation(propagated.getPVCoordinates().getPosition(), propagated.getFrame(), propagated.getDate()); final double zVelocity = propagated.getPVCoordinates(topo).getVelocity().getZ(); Assert.assertTrue(farTarget.durationFrom(propagated.getDate()) > 7800.0); Assert.assertTrue("Incorrect value " + farTarget.durationFrom(propagated.getDate()) + " !< 7900",farTarget.durationFrom(propagated.getDate()) < 7900.0); Assert.assertEquals(0.09, elevation, 1.0e-11); Assert.assertTrue(zVelocity < 0); } @Test public void testInitializationCorrectness() throws OrekitException, IOException { // Definition of initial conditions AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(154.); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); Frame eme2000 = FramesFactory.getEME2000(); Vector3D pole = itrf.getTransformTo(eme2000, date).transformVector(Vector3D.PLUS_K); Frame poleAligned = new Frame(FramesFactory.getEME2000(), new Transform(date, new Rotation(pole, Vector3D.PLUS_K)), "pole aligned", true); CircularOrbit initial = new CircularOrbit(7208669.8179538045, 1.3740461966386876E-4, -3.2364250248363356E-5, FastMath.toRadians(97.40236024565775), FastMath.toRadians(166.15873160992115), FastMath.toRadians(90.1282370098961), PositionAngle.MEAN, poleAligned, date, provider.getMu()); // find the default Eckstein-Hechler propagator initialized from the initial orbit EcksteinHechlerPropagator defaultEH = new EcksteinHechlerPropagator(initial, provider); // the osculating parameters recomputed by the default Eckstein-Hechler propagator are quite different // from initial orbit CircularOrbit defaultOrbit = (CircularOrbit) OrbitType.CIRCULAR.convertType(defaultEH.propagateOrbit(initial.getDate())); Assert.assertEquals(267.4, defaultOrbit.getA() - initial.getA(), 0.1); // the position on the other hand match perfectly Assert.assertEquals(0.0, Vector3D.distance(defaultOrbit.getPVCoordinates().getPosition(), initial.getPVCoordinates().getPosition()), 1.0e-8); // set up a reference numerical propagator starting for the specified start orbit // using the same force models (i.e. the first few zonal terms) double[][] tol = NumericalPropagator.tolerances(0.1, initial, OrbitType.CIRCULAR); AdaptiveStepsizeIntegrator integrator = new DormandPrince853Integrator(0.001, 1000, tol[0], tol[1]); integrator.setInitialStepSize(60); NumericalPropagator num = new NumericalPropagator(integrator); num.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, GravityFieldFactory.getNormalizedProvider(provider))); num.setInitialState(new SpacecraftState(initial)); num.setOrbitType(OrbitType.CIRCULAR); // find the best Eckstein-Hechler propagator that match the orbit evolution PropagatorConverter converter = new FiniteDifferencePropagatorConverter(new EcksteinHechlerPropagatorBuilder(initial, provider, PositionAngle.TRUE, 1.0), 1.0e-6, 100); EcksteinHechlerPropagator fittedEH = (EcksteinHechlerPropagator) converter.convert(num, 3 * initial.getKeplerianPeriod(), 300); // the default Eckstein-Hechler propagator did however quite a good job, as it found // an orbit close to the best fitting CircularOrbit fittedOrbit = (CircularOrbit) OrbitType.CIRCULAR.convertType(fittedEH.propagateOrbit(initial.getDate())); Assert.assertEquals(0.623, defaultOrbit.getA() - fittedOrbit.getA(), 0.1); // the position on the other hand are slightly different // because the fitted orbit minimizes the residuals over a complete time span, // not on a single point Assert.assertEquals(58.0, Vector3D.distance(defaultOrbit.getPVCoordinates().getPosition(), fittedOrbit.getPVCoordinates().getPosition()), 0.1); } @Test public void testNonSerializableStateProvider() throws OrekitException, IOException { AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(154.); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); Frame eme2000 = FramesFactory.getEME2000(); Vector3D pole = itrf.getTransformTo(eme2000, date).transformVector(Vector3D.PLUS_K); Frame poleAligned = new Frame(FramesFactory.getEME2000(), new Transform(date, new Rotation(pole, Vector3D.PLUS_K)), "pole aligned", true); CircularOrbit initial = new CircularOrbit(7208669.8179538045, 1.3740461966386876E-4, -3.2364250248363356E-5, FastMath.toRadians(97.40236024565775), FastMath.toRadians(166.15873160992115), FastMath.toRadians(90.1282370098961), PositionAngle.MEAN, poleAligned, date, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(initial, provider); // this serialization should work new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(propagator); propagator.addAdditionalStateProvider(new AdditionalStateProvider() { public String getName() { return "not serializable"; } public double[] getAdditionalState(SpacecraftState state) { return new double[] { 0 }; } }); try { // this serialization should not work new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(propagator); Assert.fail("an exception should have been thrown"); } catch (NotSerializableException nse) { // expected } } @Test public void testIssue223() throws OrekitException, IOException, ClassNotFoundException { // Definition of initial conditions AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(154.); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); Frame eme2000 = FramesFactory.getEME2000(); Vector3D pole = itrf.getTransformTo(eme2000, date).transformVector(Vector3D.PLUS_K); Frame poleAligned = new Frame(FramesFactory.getEME2000(), new Transform(date, new Rotation(pole, Vector3D.PLUS_K)), "pole aligned", true); CircularOrbit initial = new CircularOrbit(7208669.8179538045, 1.3740461966386876E-4, -3.2364250248363356E-5, FastMath.toRadians(97.40236024565775), FastMath.toRadians(166.15873160992115), FastMath.toRadians(90.1282370098961), PositionAngle.MEAN, poleAligned, date, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(initial, provider); propagator.addAdditionalStateProvider(new SevenProvider()); propagator.setEphemerisMode(); propagator.propagate(initial.getDate().shiftedBy(40000)); BoundedPropagator ephemeris = propagator.getGeneratedEphemeris(); Assert.assertSame(poleAligned, ephemeris.getFrame()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(ephemeris); Assert.assertTrue(bos.size() > 2450); Assert.assertTrue(bos.size() < 2550); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); BoundedPropagator deserialized = (BoundedPropagator) ois.readObject(); Assert.assertEquals(initial.getA(), deserialized.getInitialState().getA(), 1.0e-10); Assert.assertEquals(initial.getEquinoctialEx(), deserialized.getInitialState().getEquinoctialEx(), 1.0e-10); SpacecraftState s = deserialized.propagate(initial.getDate().shiftedBy(20000)); Map<String, double[]> additional = s.getAdditionalStates(); Assert.assertEquals(1, additional.size()); Assert.assertEquals(1, additional.get("seven").length); Assert.assertEquals(7, additional.get("seven")[0], 1.0e-15); } @Test public void testIssue224Forward() throws OrekitException, IOException, ClassNotFoundException { AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(154.); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); Frame eme2000 = FramesFactory.getEME2000(); Vector3D pole = itrf.getTransformTo(eme2000, date).transformVector(Vector3D.PLUS_K); Frame poleAligned = new Frame(FramesFactory.getEME2000(), new Transform(date, new Rotation(pole, Vector3D.PLUS_K)), "pole aligned", true); CircularOrbit initial = new CircularOrbit(7208669.8179538045, 1.3740461966386876E-4, -3.2364250248363356E-5, FastMath.toRadians(97.40236024565775), FastMath.toRadians(166.15873160992115), FastMath.toRadians(90.1282370098961), PositionAngle.MEAN, poleAligned, date, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(initial, new LofOffset(poleAligned, LOFType.VVLH), 1000.0, provider); propagator.addAdditionalStateProvider(new SevenProvider()); propagator.setEphemerisMode(); // Impulsive burns final AbsoluteDate burn1Date = initial.getDate().shiftedBy(200); ImpulseManeuver<DateDetector> impulsiveBurn1 = new ImpulseManeuver<DateDetector>(new DateDetector(burn1Date), new Vector3D(0.0, 500.0, 0.0), 320); propagator.addEventDetector(impulsiveBurn1); final AbsoluteDate burn2Date = initial.getDate().shiftedBy(300); ImpulseManeuver<DateDetector> impulsiveBurn2 = new ImpulseManeuver<DateDetector>(new DateDetector(burn2Date), new Vector3D(0.0, 500.0, 0.0), 320); propagator.addEventDetector(impulsiveBurn2); propagator.propagate(initial.getDate().shiftedBy(400)); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(propagator.getGeneratedEphemeris()); Assert.assertTrue(bos.size() > 2950); Assert.assertTrue(bos.size() < 3050); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); BoundedPropagator ephemeris = (BoundedPropagator) ois.readObject(); ephemeris.setMasterMode(10, new OrekitFixedStepHandler() { public void handleStep(SpacecraftState currentState, boolean isLast) { if (currentState.getDate().durationFrom(burn1Date) < -0.001) { Assert.assertEquals(97.402, FastMath.toDegrees(currentState.getI()), 1.0e-3); } else if (currentState.getDate().durationFrom(burn1Date) > 0.001 && currentState.getDate().durationFrom(burn2Date) < -0.001) { Assert.assertEquals(98.183, FastMath.toDegrees(currentState.getI()), 1.0e-3); } else if (currentState.getDate().durationFrom(burn2Date) > 0.001) { Assert.assertEquals(99.310, FastMath.toDegrees(currentState.getI()), 1.0e-3); } } }); ephemeris.propagate(ephemeris.getMaxDate()); } @Test public void testIssue224Backward() throws OrekitException, IOException, ClassNotFoundException { AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(154.); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); Frame eme2000 = FramesFactory.getEME2000(); Vector3D pole = itrf.getTransformTo(eme2000, date).transformVector(Vector3D.PLUS_K); Frame poleAligned = new Frame(FramesFactory.getEME2000(), new Transform(date, new Rotation(pole, Vector3D.PLUS_K)), "pole aligned", true); CircularOrbit initial = new CircularOrbit(7208669.8179538045, 1.3740461966386876E-4, -3.2364250248363356E-5, FastMath.toRadians(97.40236024565775), FastMath.toRadians(166.15873160992115), FastMath.toRadians(90.1282370098961), PositionAngle.MEAN, poleAligned, date, provider.getMu()); EcksteinHechlerPropagator propagator = new EcksteinHechlerPropagator(initial, new LofOffset(poleAligned, LOFType.VVLH), 1000.0, provider); propagator.addAdditionalStateProvider(new SevenProvider()); propagator.setEphemerisMode(); // Impulsive burns final AbsoluteDate burn1Date = initial.getDate().shiftedBy(-200); ImpulseManeuver<DateDetector> impulsiveBurn1 = new ImpulseManeuver<DateDetector>(new DateDetector(burn1Date), new Vector3D(0.0, 500.0, 0.0), 320); propagator.addEventDetector(impulsiveBurn1); final AbsoluteDate burn2Date = initial.getDate().shiftedBy(-300); ImpulseManeuver<DateDetector> impulsiveBurn2 = new ImpulseManeuver<DateDetector>(new DateDetector(burn2Date), new Vector3D(0.0, 500.0, 0.0), 320); propagator.addEventDetector(impulsiveBurn2); propagator.propagate(initial.getDate().shiftedBy(-400)); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(propagator.getGeneratedEphemeris()); Assert.assertTrue(bos.size() > 2950); Assert.assertTrue(bos.size() < 3050); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); BoundedPropagator ephemeris = (BoundedPropagator) ois.readObject(); ephemeris.setMasterMode(10, new OrekitFixedStepHandler() { public void handleStep(SpacecraftState currentState, boolean isLast) { if (currentState.getDate().durationFrom(burn1Date) > 0.001) { Assert.assertEquals(97.402, FastMath.toDegrees(currentState.getI()), 1.0e-3); } else if (currentState.getDate().durationFrom(burn1Date) < -0.001 && currentState.getDate().durationFrom(burn2Date) > 0.001) { Assert.assertEquals(98.164, FastMath.toDegrees(currentState.getI()), 1.0e-3); } else if (currentState.getDate().durationFrom(burn2Date) < -0.001) { Assert.assertEquals(99.273, FastMath.toDegrees(currentState.getI()), 1.0e-3); } } }); ephemeris.propagate(ephemeris.getMinDate()); } private static class SevenProvider implements AdditionalStateProvider, Serializable { private static final long serialVersionUID = 1L; public String getName() { return "seven"; } public double[] getAdditionalState(final SpacecraftState state) { return new double[] { 7 }; } } private static double tangLEmLv(double Lv, double ex, double ey) { // tan ((LE - Lv) /2)) = return (ey * FastMath.cos(Lv) - ex * FastMath.sin(Lv)) / (1 + ex * FastMath.cos(Lv) + ey * FastMath.sin(Lv) + FastMath.sqrt(1 - ex * ex - ey * ey)); } @Before public void setUp() { Utils.setDataRoot("regular-data"); double mu = 3.9860047e14; double ae = 6.378137e6; double[][] cnm = new double[][] { { 0 }, { 0 }, { -1.08263e-3 }, { 2.54e-6 }, { 1.62e-6 }, { 2.3e-7 }, { -5.5e-7 } }; double[][] snm = new double[][] { { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; provider = GravityFieldFactory.getUnnormalizedProvider(ae, mu, TideSystem.UNKNOWN, cnm, snm); } @After public void tearDown() { provider = null; } private UnnormalizedSphericalHarmonicsProvider provider; }