/* 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.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Line;
import org.hipparchus.geometry.euclidean.threed.RotationOrder;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.geometry.partitioning.RegionFactory;
import org.hipparchus.geometry.spherical.twod.S2Point;
import org.hipparchus.geometry.spherical.twod.Sphere2D;
import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
import org.hipparchus.util.FastMath;
import org.hipparchus.util.MathUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.orekit.Utils;
import org.orekit.attitudes.LofOffset;
import org.orekit.bodies.GeodeticPoint;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.errors.OrekitException;
import org.orekit.frames.FramesFactory;
import org.orekit.frames.LOFType;
import org.orekit.frames.Transform;
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.events.EventsLogger.LoggedEvent;
import org.orekit.propagation.events.handlers.ContinueOnEvent;
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 FootprintOverlapDetectorTest {
private Propagator propagator;
private Orbit initialOrbit;
private OneAxisEllipsoid earth;
@Test
public void testRightForwardView() throws OrekitException, IOException {
propagator.setAttitudeProvider(new LofOffset(initialOrbit.getFrame(), LOFType.VVLH,
RotationOrder.XYZ,
FastMath.toRadians(-20.0),
FastMath.toRadians(+20.0),
0.0));
// observe continental France plus Corsica
final SphericalPolygonsSet france = buildFrance();
// square field of view along Z axis (which is pointing sideways), aperture 5°, 0° margin
final FieldOfView fov = new FieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
FastMath.toRadians(2.5), 4, 0.0);
final FootprintOverlapDetector detector =
new FootprintOverlapDetector(fov, earth, france, 50000.0).
withMaxCheck(1.0).
withThreshold(1.0e-6).
withHandler(new ContinueOnEvent<FootprintOverlapDetector>());
final EventsLogger logger = new EventsLogger();
propagator.addEventDetector(logger.monitorDetector(detector));
// Extrapolate from the initial to the final date
propagator.propagate(initialOrbit.getDate().shiftedBy(635000),
initialOrbit.getDate().shiftedBy(735000));
List<LoggedEvent> events = logger.getLoggedEvents();
Assert.assertEquals(8, events.size());
// the first two consecutive close events occur during the same ascending orbit
// we first see Corsica, then lose visibility over the see, then see continental France
// above Mediterranean see, between Illes Balears and Sardigna,
// pointing to Corsica towards North-East
checkEventPair(events.get(0), events.get(1),
639010.0775, 33.9434, 39.2168, 6.5980, 42.0671, 9.0543);
// above Saint-Chamond (Loire), pointing near Saint-Dié-des-Vosges (Vosges) towards North-East
checkEventPair(events.get(2), events.get(3),
639111.1399, 40.8032, 45.4637, 4.5075, 48.3487, 7.1733);
// event is on a descending orbit, so the pointing direction,
// taking roll and pitch offsets, is towards South-West with respect to spacecraft
// above English Channel, pointing near Hanvec (Finistère) towards South-West
checkEventPair(events.get(4), events.get(5),
687772.4531, 27.0852, 50.2693, 0.0493, 48.3243, -4.1510);
// event on an ascending orbit
// above Atlantic ocean, pointing near to île d'Oléron (Charente-Maritime) towards North-East
checkEventPair(events.get(6), events.get(7),
727696.1033, 113.8829, 42.9785, -4.0426, 45.8492, -1.4656);
}
private void checkEventPair(final LoggedEvent start, final LoggedEvent end,
final double expectedStart, final double expectedDuration,
final double spacecraftLatitude, final double spacecraftLongitude,
final double fovCenterLatitude, final double fovCenterLongitude)
throws OrekitException {
Assert.assertFalse(start.isIncreasing());
Assert.assertTrue(end.isIncreasing());
Assert.assertEquals(expectedStart,
start.getState().getDate().durationFrom(initialOrbit.getDate()),
0.001);
Assert.assertEquals(expectedDuration,
end.getState().getDate().durationFrom(start.getState().getDate()),
0.001);
SpacecraftState middle = start.getState().shiftedBy(0.5 * expectedDuration);
// sub-satellite point
Vector3D p = middle.getPVCoordinates().getPosition();
GeodeticPoint gpSat = earth.transform(p, middle.getFrame(), middle.getDate());
Assert.assertEquals(spacecraftLatitude, FastMath.toDegrees(gpSat.getLatitude()), 0.001);
Assert.assertEquals(spacecraftLongitude, FastMath.toDegrees(gpSat.getLongitude()), 0.001);
// point at center of Field Of View
final Transform scToInert = middle.toTransform().getInverse();
GeodeticPoint gpFOV =
earth.getIntersectionPoint(new Line(p, scToInert.transformPosition(Vector3D.PLUS_K), 1.0e-6),
middle.getPVCoordinates().getPosition(),
middle.getFrame(), middle.getDate());
Assert.assertEquals(fovCenterLatitude, FastMath.toDegrees(gpFOV.getLatitude()), 0.001);
Assert.assertEquals(fovCenterLongitude, FastMath.toDegrees(gpFOV.getLongitude()), 0.001);
}
@Test
public void testSerialization()
throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, OrekitException {
// observe continental France plus Corsica
final SphericalPolygonsSet france = buildFrance();
// square field of view along Z axis (which is pointing sideways),
// aperture 5° (hence half-aperture 2.5°), 0.001 radians margin
final double alpha = FastMath.toRadians(2.5);
final FieldOfView fov = new FieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
alpha, 4, 0.001);
double eta = FastMath.acos(FastMath.sin(alpha) * FastMath.sin(alpha));
double theoreticalArea = MathUtils.TWO_PI - 4 * eta;
final FootprintOverlapDetector detector =
new FootprintOverlapDetector(fov, earth, france, 50000.0).
withMaxCheck(1.0).
withThreshold(1.0e-6).
withHandler(new ContinueOnEvent<FootprintOverlapDetector>());
Assert.assertEquals(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
((OneAxisEllipsoid) detector.getBody()).getEquatorialRadius(),
1.0e-12);
Assert.assertEquals(0.001, detector.getFieldOfView().getMargin(), 1.0e-12);
Assert.assertEquals(theoreticalArea, detector.getFieldOfView().getZone().getSize(), 1.0e-12);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(detector);
Assert.assertTrue(bos.size() > 2400);
Assert.assertTrue(bos.size() < 2500);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
FootprintOverlapDetector deserialized = (FootprintOverlapDetector) ois.readObject();
Assert.assertEquals(detector.getZone().getSize(), deserialized.getZone().getSize(), 1.0e-14);
Assert.assertEquals(detector.getZone().getBoundarySize(), deserialized.getZone().getBoundarySize(), 1.0e-14);
Assert.assertEquals(detector.getZone().getTolerance(), deserialized.getZone().getTolerance(), 1.0e-15);
Assert.assertEquals(detector.getMaxCheckInterval(), deserialized.getMaxCheckInterval(), 1.0e-15);
Assert.assertEquals(detector.getThreshold(), deserialized.getThreshold(), 1.0e-15);
Assert.assertEquals(detector.getMaxIterationCount(), deserialized.getMaxIterationCount());
Assert.assertTrue(new RegionFactory<Sphere2D>().difference(detector.getZone(), deserialized.getZone()).isEmpty());
}
@Before
public void setUp() {
try {
Utils.setDataRoot("regular-data");
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);
initialOrbit = new EquinoctialOrbit(new PVCoordinates(position, velocity),
FramesFactory.getEME2000(), date,
Constants.EIGEN5C_EARTH_MU);
propagator =
new EcksteinHechlerPropagator(initialOrbit,
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);
earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
Constants.WGS84_EARTH_FLATTENING,
FramesFactory.getITRF(IERSConventions.IERS_2010, true));
} catch (OrekitException oe) {
Assert.fail(oe.getMessage());
}
}
private SphericalPolygonsSet buildFrance() {
final SphericalPolygonsSet continental = buildSimpleZone(new double[][] {
{ 51.14850, 2.51357 }, { 50.94660, 1.63900 }, { 50.12717, 1.33876 }, { 49.34737, -0.98946 },
{ 49.77634, -1.93349 }, { 48.64442, -1.61651 }, { 48.90169, -3.29581 }, { 48.68416, -4.59234 },
{ 47.95495, -4.49155 }, { 47.57032, -2.96327 }, { 46.01491, -1.19379 }, { 44.02261, -1.38422 },
{ 43.42280, -1.90135 }, { 43.03401, -1.50277 }, { 42.34338, 1.82679 }, { 42.47301, 2.98599 },
{ 43.07520, 3.10041 }, { 43.39965, 4.55696 }, { 43.12889, 6.52924 }, { 43.69384, 7.43518 },
{ 44.12790, 7.54959 }, { 45.02851, 6.74995 }, { 45.33309, 7.09665 }, { 46.42967, 6.50009 },
{ 46.27298, 6.02260 }, { 46.72577, 6.03738 }, { 47.62058, 7.46675 }, { 49.01778, 8.09927 },
{ 49.20195, 6.65822 }, { 49.44266, 5.89775 }, { 49.98537, 4.79922 }
});
final SphericalPolygonsSet corsica = buildSimpleZone(new double[][] {
{ 42.15249, 9.56001 }, { 43.00998, 9.39000 }, { 42.62812, 8.74600 }, { 42.25651, 8.54421 },
{ 41.58361, 8.77572 }, { 41.38000, 9.22975 }
});
return (SphericalPolygonsSet) new RegionFactory<Sphere2D>().union(continental, corsica);
}
private SphericalPolygonsSet buildSimpleZone(double[][] points) {
final S2Point[] vertices = new S2Point[points.length];
for (int i = 0; i < points.length; ++i) {
vertices[i] = new S2Point(FastMath.toRadians(points[i][1]), // points[i][1] is longitude
FastMath.toRadians(90.0 - points[i][0])); // points[i][0] is latitude
}
return new SphericalPolygonsSet(1.0e-10, vertices);
}
}