/* 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.events; import java.lang.reflect.Array; import org.hipparchus.Field; import org.hipparchus.RealFieldElement; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaFieldIntegrator; import org.hipparchus.util.Decimal64Field; import org.hipparchus.util.FastMath; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.orekit.Utils; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.orbits.FieldCircularOrbit; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngle; import org.orekit.propagation.FieldPropagator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.analytical.FieldKeplerianPropagator; import org.orekit.propagation.events.handlers.FieldContinueOnEvent; import org.orekit.propagation.events.handlers.FieldEventHandler; import org.orekit.propagation.events.handlers.FieldEventHandler.Action; import org.orekit.propagation.events.handlers.FieldStopOnEvent; import org.orekit.propagation.numerical.FieldNumericalPropagator; import org.orekit.propagation.sampling.FieldOrekitFixedStepHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.FieldPVCoordinatesProvider; public class FieldEventDetectorTest { private double mu; @Test public void testBasicScheduling() throws OrekitException { doTestBasicScheduling(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestBasicScheduling(Field<T> field) throws OrekitException { final T zero = field.getZero(); final TimeScale utc = TimeScalesFactory.getUTC(); final FieldVector3D<T> position = new FieldVector3D<>(zero.add(-6142438.668), zero.add(3492467.56), zero.add(-25767.257)); final FieldVector3D<T> velocity = new FieldVector3D<>(zero.add(505.848), zero.add(942.781), zero.add(7435.922)); final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); final FieldOrbit<T> orbit = new FieldCircularOrbit<>(new FieldPVCoordinates<>(position, velocity), FramesFactory.getEME2000(), date, mu); FieldPropagator<T> propagator = new FieldKeplerianPropagator<>(orbit); T stepSize = zero.add(60.0); OutOfOrderChecker<T> checker = new OutOfOrderChecker<>(stepSize); propagator.addEventDetector(new FieldDateDetector<>(date.shiftedBy(stepSize.multiply(5.25))).withHandler(checker)); propagator.setMasterMode(stepSize, checker); propagator.propagate(date.shiftedBy(stepSize.multiply(10))); Assert.assertTrue(checker.outOfOrderCallDetected()); } private static class OutOfOrderChecker<T extends RealFieldElement<T>> implements FieldEventHandler<FieldDateDetector<T>, T>, FieldOrekitFixedStepHandler<T> { private FieldAbsoluteDate<T> triggerDate; private boolean outOfOrderCallDetected; private T stepSize; public OutOfOrderChecker(final T stepSize) { triggerDate = null; outOfOrderCallDetected = false; this.stepSize = stepSize; } public Action eventOccurred(FieldSpacecraftState<T> s, FieldDateDetector<T> detector, boolean increasing) { triggerDate = s.getDate(); return Action.CONTINUE; } public void handleStep(FieldSpacecraftState<T> currentState, boolean isLast) { // step handling and event occurrences may be out of order up to one step // with variable steps, and two steps with fixed steps (due to the delay // induced by StepNormalizer) if (triggerDate != null) { double dt = currentState.getDate().durationFrom(triggerDate).getReal(); if (dt < 0) { outOfOrderCallDetected = true; Assert.assertTrue(FastMath.abs(dt) < (2 * stepSize.getReal())); } } } public boolean outOfOrderCallDetected() { return outOfOrderCallDetected; } } @Test public void testIssue108Numerical() throws OrekitException { doTestIssue108Numerical(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestIssue108Numerical(Field<T> field) throws OrekitException { final T zero = field.getZero(); final TimeScale utc = TimeScalesFactory.getUTC(); final FieldVector3D<T> position = new FieldVector3D<>(zero.add(-6142438.668), zero.add(3492467.56), zero.add(-25767.257)); final FieldVector3D<T> velocity = new FieldVector3D<>(zero.add(505.848), zero.add(942.781), zero.add(7435.922)); final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); final FieldOrbit<T> orbit = new FieldCircularOrbit<>(new FieldPVCoordinates<>(position, velocity), FramesFactory.getEME2000(), date, mu); final T step = zero.add(60.0); final int n = 100; FieldNumericalPropagator<T> propagator = new FieldNumericalPropagator<>(field, new ClassicalRungeKuttaFieldIntegrator<T>(field, step)); propagator.setOrbitType(OrbitType.EQUINOCTIAL); propagator.resetInitialState(new FieldSpacecraftState<>(orbit)); GCallsCounter<T> counter = new GCallsCounter<>(zero.add(100000.0), zero.add(1.0e-6), 20, new FieldStopOnEvent<GCallsCounter<T>, T>()); propagator.addEventDetector(counter); propagator.propagate(date.shiftedBy(step.multiply(n))); Assert.assertEquals(n + 1, counter.getCount()); } @Test public void testIssue108Analytical() throws OrekitException { doTestIssue108Analytical(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestIssue108Analytical(Field<T> field) throws OrekitException { final T zero = field.getZero(); final TimeScale utc = TimeScalesFactory.getUTC(); final FieldVector3D<T> position = new FieldVector3D<>(zero.add(-6142438.668), zero.add(3492467.56), zero.add(-25767.257)); final FieldVector3D<T> velocity = new FieldVector3D<>(zero.add(505.848), zero.add(942.781), zero.add(7435.922)); final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); final FieldOrbit<T> orbit = new FieldCircularOrbit<>(new FieldPVCoordinates<>(position, velocity), FramesFactory.getEME2000(), date, mu); final T step = zero.add(60.0); final int n = 100; FieldKeplerianPropagator<T> propagator = new FieldKeplerianPropagator<>(orbit); GCallsCounter<T> counter = new GCallsCounter<>(zero.add(100000.0), zero.add(1.0e-6), 20, new FieldStopOnEvent<GCallsCounter<T>, T>()); propagator.addEventDetector(counter); propagator.setMasterMode(step, new FieldOrekitFixedStepHandler<T>() { public void handleStep(FieldSpacecraftState<T> currentState, boolean isLast) { } }); propagator.propagate(date.shiftedBy(step.multiply(n))); Assert.assertEquals(n + 1, counter.getCount()); } private static class GCallsCounter<T extends RealFieldElement<T>> extends FieldAbstractDetector<GCallsCounter<T>, T> { private int count; public GCallsCounter(final T maxCheck, final T threshold, final int maxIter, final FieldEventHandler<? super GCallsCounter<T>, T> handler) { super(maxCheck, threshold, maxIter, handler); count = 0; } protected GCallsCounter<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler<? super GCallsCounter<T>, T> newHandler) { return new GCallsCounter<>(newMaxCheck, newThreshold, newMaxIter, newHandler); } public int getCount() { return count; } public T g(FieldSpacecraftState<T> s) { count++; return s.getMass().getField().getZero().add(1.0); } } @Test public void testNoisyGFunction() throws OrekitException { doTestNoisyGFunction(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestNoisyGFunction(Field<T> field) throws OrekitException { final T zero = field.getZero(); // initial conditions Frame eme2000 = FramesFactory.getEME2000(); TimeScale utc = TimeScalesFactory.getUTC(); FieldAbsoluteDate<T> initialDate = new FieldAbsoluteDate<>(field, 2011, 5, 11, utc); FieldAbsoluteDate<T> startDate = new FieldAbsoluteDate<>(field, 2032, 10, 17, utc); @SuppressWarnings("unchecked") FieldAbsoluteDate<T>[] interruptDates = ( FieldAbsoluteDate<T>[]) Array.newInstance(FieldAbsoluteDate.class, 1); interruptDates[0] = new FieldAbsoluteDate<>(field, 2032, 10, 18, utc); FieldAbsoluteDate<T> targetDate = new FieldAbsoluteDate<>(field, 2211, 5, 11, utc); FieldKeplerianPropagator<T> k1 = new FieldKeplerianPropagator<>(new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(new FieldVector3D<>(zero.add(4008462.4706055815), zero.add(-3155502.5373837613), zero.add(-5044275.9880020910)), new FieldVector3D<>(zero.add(-5012.9298276860990), zero.add(1920.3567095973078), zero.add(-5172.7403501801580))), eme2000, initialDate, Constants.WGS84_EARTH_MU)); FieldKeplerianPropagator<T> k2 = new FieldKeplerianPropagator<>(new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(new FieldVector3D<>(zero.add(4008912.4039522274), zero.add(-3155453.3125615157), zero.add(-5044297.6484738905)), new FieldVector3D<>(zero.add(-5012.5883854112530), zero.add(1920.6332221785074), zero.add(-5172.2177085540500))), eme2000, initialDate, Constants.WGS84_EARTH_MU)); k2.addEventDetector(new FieldCloseApproachDetector<>(zero.add(2015.243454166727), zero.add(0.0001), 100, new FieldContinueOnEvent<FieldCloseApproachDetector<T>, T>(), k1)); k2.addEventDetector(new FieldDateDetector<>(zero.add(Constants.JULIAN_DAY), zero.add(1.0e-6), interruptDates)); FieldSpacecraftState<T> s = k2.propagate(startDate, targetDate); Assert.assertEquals(0.0, interruptDates[0].durationFrom(s.getDate()).getReal(), 1.1e-6); } private static class FieldCloseApproachDetector<T extends RealFieldElement<T>> extends FieldAbstractDetector<FieldCloseApproachDetector<T>, T> { private final FieldPVCoordinatesProvider<T> provider; public FieldCloseApproachDetector(T maxCheck, T threshold, final int maxIter, final FieldEventHandler<? super FieldCloseApproachDetector<T>, T> handler, FieldPVCoordinatesProvider<T> provider) { super(maxCheck, threshold, maxIter, handler); this.provider = provider; } public T g(final FieldSpacecraftState<T> s) throws OrekitException { FieldPVCoordinates<T> pv1 = provider.getPVCoordinates(s.getDate(), s.getFrame()); FieldPVCoordinates<T> pv2 = s.getPVCoordinates(); FieldVector3D<T> deltaP = pv1.getPosition().subtract(pv2.getPosition()); FieldVector3D<T> deltaV = pv1.getVelocity().subtract(pv2.getVelocity()); T radialVelocity = FieldVector3D.dotProduct(deltaP.normalize(), deltaV); return radialVelocity; } protected FieldCloseApproachDetector<T> create(final T newMaxCheck, final T newThreshold, final int newMaxIter, final FieldEventHandler<? super FieldCloseApproachDetector<T>, T> newHandler) { return new FieldCloseApproachDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, provider); } } @Test public void testWrappedException() throws OrekitException { doTestWrappedException(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestWrappedException(Field<T> field) throws OrekitException { final T zero = field.getZero(); final Throwable dummyCause = new RuntimeException(); try { // initial conditions Frame eme2000 = FramesFactory.getEME2000(); TimeScale utc = TimeScalesFactory.getUTC(); final FieldAbsoluteDate<T> initialDate = new FieldAbsoluteDate<>(field, 2011, 5, 11, utc); final FieldAbsoluteDate<T> exceptionDate = initialDate.shiftedBy(3600.0); FieldKeplerianPropagator<T> k = new FieldKeplerianPropagator<>(new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(new FieldVector3D<>(zero.add(4008462.4706055815), zero.add(-3155502.5373837613), zero.add(-5044275.9880020910)), new FieldVector3D<>(zero.add(-5012.9298276860990), zero.add(1920.3567095973078), zero.add(-5172.7403501801580))), eme2000, initialDate, Constants.WGS84_EARTH_MU)); k.addEventDetector(new FieldDateDetector<T>(initialDate.shiftedBy(Constants.JULIAN_DAY)) { @Override public T g(final FieldSpacecraftState<T> s) throws OrekitException { final T dt = s.getDate().durationFrom(exceptionDate); if (dt.abs().getReal() < 1.0) { throw new OrekitException(dummyCause, LocalizedCoreFormats.SIMPLE_MESSAGE, "dummy"); } return dt; } }); k.propagate(initialDate.shiftedBy(Constants.JULIAN_YEAR)); Assert.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assert.assertSame(dummyCause, oe.getCause()); } } @Test public void testDefaultMethods() throws OrekitException { doTestDefaultMethods(Decimal64Field.getInstance()); } private <T extends RealFieldElement<T>> void doTestDefaultMethods(final Field<T> field) throws OrekitException { FieldEventDetector<T> dummyDetector = new FieldEventDetector<T>() { @Override public T getThreshold() { return field.getZero().add(1.0e-10); } @Override public int getMaxIterationCount() { return 100; } @Override public T getMaxCheckInterval() { return field.getZero().add(60); } @Override public T g(FieldSpacecraftState<T> s) { return s.getDate().durationFrom(AbsoluteDate.J2000_EPOCH); } @Override public Action eventOccurred(FieldSpacecraftState<T> s, boolean increasing) { return Action.RESET_STATE; } }; // by default, this method does nothing, so this should pass without exception dummyDetector.init(null, null); // by default, this method returns its argument FieldSpacecraftState<T> s = new FieldSpacecraftState<>(new FieldKeplerianOrbit<>(field.getZero().add(7e6), field.getZero().add(0.01), field.getZero().add(0.3), field.getZero().add(0), field.getZero().add(0), field.getZero().add(0), PositionAngle.TRUE, FramesFactory.getEME2000(), FieldAbsoluteDate.getJ2000Epoch(field), Constants.EIGEN5C_EARTH_MU)); Assert.assertSame(s, dummyDetector.resetState(s)); } @Before public void setUp() { Utils.setDataRoot("regular-data"); mu = Constants.EIGEN5C_EARTH_MU; } }