/* 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.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.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.orekit.Utils; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.EcksteinHechlerPropagator; import org.orekit.propagation.analytical.KeplerianPropagator; import org.orekit.propagation.events.EventsLogger.LoggedEvent; import org.orekit.propagation.events.handlers.ContinueOnEvent; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; public class EventEnablingPredicateFilterTest { private OneAxisEllipsoid earth; private GeodeticPoint gp; private Orbit orbit; @Test public void testForward0Degrees() throws OrekitException { doElevationTest(FastMath.toRadians(0.0), orbit.getDate(), orbit.getDate().shiftedBy(Constants.JULIAN_DAY), 8, true); } @Test public void testForward5Degrees() throws OrekitException { doElevationTest(FastMath.toRadians(5.0), orbit.getDate(), orbit.getDate().shiftedBy(Constants.JULIAN_DAY), 6, false); } @Test public void testForward5DegreesStartEnabled() throws OrekitException { doElevationTest(FastMath.toRadians(5.0), orbit.getDate().shiftedBy(12614.0), orbit.getDate().shiftedBy(Constants.JULIAN_DAY), 6, false); } @Test public void testBackward0Degrees() throws OrekitException { doElevationTest(FastMath.toRadians(0.0), orbit.getDate().shiftedBy(Constants.JULIAN_DAY), orbit.getDate(), 8, true); } @Test public void testBackward5Degrees() throws OrekitException { doElevationTest(FastMath.toRadians(5.0), orbit.getDate().shiftedBy(Constants.JULIAN_DAY), orbit.getDate(), 6, false); } @Test public void testBackward5DegreesStartEnabled() throws OrekitException { doElevationTest(FastMath.toRadians(5.0), orbit.getDate().shiftedBy(73112.0), orbit.getDate(), 6, true); } private void doElevationTest(final double minElevation, final AbsoluteDate start, final AbsoluteDate end, final int expectedEvents, final boolean sameSign) throws OrekitException { final ElevationExtremumDetector raw = new ElevationExtremumDetector(0.001, 1.e-6, new TopocentricFrame(earth, gp, "test")). withHandler(new ContinueOnEvent<ElevationExtremumDetector>()); final EventEnablingPredicateFilter<ElevationExtremumDetector> aboveGroundElevationDetector = new EventEnablingPredicateFilter<ElevationExtremumDetector>(raw, new EnablingPredicate<ElevationExtremumDetector>() { public boolean eventIsEnabled(final SpacecraftState state, final ElevationExtremumDetector eventDetector, final double g) throws OrekitException { return eventDetector.getElevation(state) > minElevation; } }).withMaxCheck(60.0); Assert.assertEquals(0.001, raw.getMaxCheckInterval(), 1.0e-15); Assert.assertEquals(60.0, aboveGroundElevationDetector.getMaxCheckInterval(), 1.0e-15); Assert.assertEquals(1.0e-6, aboveGroundElevationDetector.getThreshold(), 1.0e-15); Assert.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, aboveGroundElevationDetector.getMaxIterationCount()); Propagator propagator = new EcksteinHechlerPropagator(orbit, Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20, Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40, Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60); EventsLogger logger = new EventsLogger(); propagator.addEventDetector(logger.monitorDetector(aboveGroundElevationDetector)); propagator.propagate(start, end); for (LoggedEvent e : logger.getLoggedEvents()) { final double eMinus = raw.getElevation(e.getState().shiftedBy(-10.0)); final double e0 = raw.getElevation(e.getState()); final double ePlus = raw.getElevation(e.getState().shiftedBy(+10.0)); Assert.assertTrue(e0 > eMinus); Assert.assertTrue(e0 > ePlus); Assert.assertTrue(e0 > minElevation); } Assert.assertEquals(expectedEvents, logger.getLoggedEvents().size()); propagator.clearEventsDetectors(); double g1Raw = raw.g(propagator.propagate(orbit.getDate().shiftedBy(18540.0))); double g2Raw = raw.g(propagator.propagate(orbit.getDate().shiftedBy(18624.0))); double g1 = aboveGroundElevationDetector.g(propagator.propagate(orbit.getDate().shiftedBy(18540.0))); double g2 = aboveGroundElevationDetector.g(propagator.propagate(orbit.getDate().shiftedBy(18624.0))); Assert.assertTrue(g1Raw > 0); Assert.assertTrue(g2Raw < 0); if (sameSign) { Assert.assertTrue(g1 > 0); Assert.assertTrue(g2 < 0); } else { Assert.assertTrue(g1 < 0); Assert.assertTrue(g2 > 0); } } @Test public void testResetState() throws OrekitException { final List<AbsoluteDate> reset = new ArrayList<AbsoluteDate>(); DateDetector raw = new DateDetector(orbit.getDate().shiftedBy(3600.0)). withMaxCheck(1000.0). withHandler(new EventHandler<DateDetector>() { public SpacecraftState resetState(DateDetector detector, SpacecraftState oldState) { reset.add(oldState.getDate()); return oldState; } public Action eventOccurred(SpacecraftState s, DateDetector detector, boolean increasing) { return Action.RESET_STATE; } }); for (int i = 2; i < 10; ++i) { raw.addEventDate(orbit.getDate().shiftedBy(i * 3600.0)); } EventEnablingPredicateFilter<DateDetector> filtered = new EventEnablingPredicateFilter<DateDetector>(raw, new EnablingPredicate<DateDetector>() { public boolean eventIsEnabled(SpacecraftState state, DateDetector eventDetector, double g) { return state.getDate().durationFrom(orbit.getDate()) > 20000.0; } }); Propagator propagator = new KeplerianPropagator(orbit); EventsLogger logger = new EventsLogger(); propagator.addEventDetector(logger.monitorDetector(filtered)); propagator.propagate(orbit.getDate().shiftedBy(Constants.JULIAN_DAY)); List<LoggedEvent> events = logger.getLoggedEvents(); Assert.assertEquals(4, events.size()); Assert.assertEquals(6 * 3600, events.get(0).getState().getDate().durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(7 * 3600, events.get(1).getState().getDate().durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(8 * 3600, events.get(2).getState().getDate().durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(9 * 3600, events.get(3).getState().getDate().durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(4, reset.size()); Assert.assertEquals(6 * 3600, reset.get(0).durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(7 * 3600, reset.get(1).durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(8 * 3600, reset.get(2).durationFrom(orbit.getDate()), 1.0e-6); Assert.assertEquals(9 * 3600, reset.get(3).durationFrom(orbit.getDate()), 1.0e-6); } @Test public void testExceedHistoryForward() throws OrekitException, IOException { final double period = 900.0; // the raw detector should trigger one event at each 900s period final DateDetector raw = new DateDetector(orbit.getDate().shiftedBy(-0.5 * period)). withMaxCheck(period / 3). withHandler(new ContinueOnEvent<DateDetector>()); for (int i = 0; i < 300; ++i) { raw.addEventDate(orbit.getDate().shiftedBy((i + 0.5) * period)); } // in fact, we will filter out half of these events, so we get only one event every 2 periods final EventEnablingPredicateFilter<DateDetector> filtered = new EventEnablingPredicateFilter<DateDetector>(raw, new EnablingPredicate<DateDetector>() { public boolean eventIsEnabled(SpacecraftState state, DateDetector eventDetector, double g) { double nbPeriod = state.getDate().durationFrom(orbit.getDate()) / period; return ((int) FastMath.floor(nbPeriod)) % 2 == 1; } }); Propagator propagator = new KeplerianPropagator(orbit); EventsLogger logger = new EventsLogger(); propagator.addEventDetector(logger.monitorDetector(filtered)); propagator.propagate(orbit.getDate().shiftedBy(301 * period)); List<LoggedEvent> events = logger.getLoggedEvents(); // 300 periods, 150 events as half of them are filtered out Assert.assertEquals(150, events.size()); // as we have encountered a lot of enabling status changes, we exceeded the internal history // if we try to display again the filtered g function for dates far in the past, // we will not see the zero crossings anymore, they have been lost propagator.clearEventsDetectors(); for (double dt = 5000.0; dt < 10000.0; dt += 3.0) { double filteredG = filtered.g(propagator.propagate(orbit.getDate().shiftedBy(dt))); Assert.assertTrue(filteredG < 0.0); } // on the other hand, if we try to display again the filtered g function for past dates // that are still inside the history, we still see the zero crossings for (double dt = 195400.0; dt < 196200.0; dt += 3.0) { double filteredG = filtered.g(propagator.propagate(orbit.getDate().shiftedBy(dt))); if (dt < 195750) { Assert.assertTrue(filteredG > 0.0); } else { Assert.assertTrue(filteredG < 0.0); } } } @Test public void testExceedHistoryBackward() throws OrekitException, IOException { final double period = 900.0; // the raw detector should trigger one event at each 900s period final DateDetector raw = new DateDetector(orbit.getDate().shiftedBy(+0.5 * period)). withMaxCheck(period / 3). withHandler(new ContinueOnEvent<DateDetector>()); for (int i = 0; i < 300; ++i) { raw.addEventDate(orbit.getDate().shiftedBy(-(i + 0.5) * period)); } // in fact, we will filter out half of these events, so we get only one event every 2 periods final EventEnablingPredicateFilter<DateDetector> filtered = new EventEnablingPredicateFilter<DateDetector>(raw, new EnablingPredicate<DateDetector>() { public boolean eventIsEnabled(SpacecraftState state, DateDetector eventDetector, double g) { double nbPeriod = orbit.getDate().durationFrom(state.getDate()) / period; return ((int) FastMath.floor(nbPeriod)) % 2 == 1; } }); Propagator propagator = new KeplerianPropagator(orbit); EventsLogger logger = new EventsLogger(); propagator.addEventDetector(logger.monitorDetector(filtered)); propagator.propagate(orbit.getDate().shiftedBy(-301 * period)); List<LoggedEvent> events = logger.getLoggedEvents(); // 300 periods, 150 events as half of them are filtered out Assert.assertEquals(150, events.size()); // as we have encountered a lot of enabling status changes, we exceeded the internal history // if we try to display again the filtered g function for dates far in the future, // we will not see the zero crossings anymore, they have been lost propagator.clearEventsDetectors(); for (double dt = -5000.0; dt > -10000.0; dt -= 3.0) { double filteredG = filtered.g(propagator.propagate(orbit.getDate().shiftedBy(dt))); Assert.assertTrue(filteredG < 0.0); } // on the other hand, if we try to display again the filtered g function for future dates // that are still inside the history, we still see the zero crossings for (double dt = -195400.0; dt > -196200.0; dt -= 3.0) { double filteredG = filtered.g(propagator.propagate(orbit.getDate().shiftedBy(dt))); if (dt < -195750) { Assert.assertTrue(filteredG < 0.0); } else { Assert.assertTrue(filteredG > 0.0); } } } @Test public void testNonSerializable() throws IOException { try { final EventEnablingPredicateFilter<DateDetector> filter = new EventEnablingPredicateFilter<DateDetector>(new DateDetector(AbsoluteDate.J2000_EPOCH), new DummyNonSerializablePredicate()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(filter); Assert.fail("an exception should habe been thrown"); } catch (NotSerializableException nse) { Assert.assertTrue(nse.getMessage().contains("DummyNonSerializablePredicate")); } } private static class DummyNonSerializablePredicate implements EnablingPredicate<DateDetector> { @Override public boolean eventIsEnabled(final SpacecraftState state, final DateDetector eventDetector, final double g) { return true; } } @Test public void testSerializable() throws IOException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException, SecurityException { final EventEnablingPredicateFilter<DateDetector> filter = new EventEnablingPredicateFilter<DateDetector>(new DateDetector(AbsoluteDate.J2000_EPOCH), new DummySerializablePredicate()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(filter); Assert.assertTrue(bos.size() > 900); Assert.assertTrue(bos.size() < 1000); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @SuppressWarnings("unchecked") EventEnablingPredicateFilter<DateDetector> deserialized = (EventEnablingPredicateFilter<DateDetector>) ois.readObject(); Field enabler = EventEnablingPredicateFilter.class.getDeclaredField("enabler"); enabler.setAccessible(true); Assert.assertEquals(DummySerializablePredicate.class, enabler.get(deserialized).getClass()); } private static class DummySerializablePredicate implements EnablingPredicate<DateDetector>, Serializable { private static final long serialVersionUID = 20160321L; @Override public boolean eventIsEnabled(final SpacecraftState state, final DateDetector eventDetector, final double g) { return true; } } @Before public void setUp() throws OrekitException { Utils.setDataRoot("regular-data"); earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, FramesFactory.getITRF(IERSConventions.IERS_2010, true)); gp = new GeodeticPoint(FastMath.toRadians(51.0), FastMath.toRadians(66.6), 300.0); final TimeScale utc = TimeScalesFactory.getUTC(); final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc); orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), FramesFactory.getEME2000(), date, Constants.EIGEN5C_EARTH_MU); } @After public void tearDown() { earth = null; gp = null; orbit = null; } }