1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.geometry.fov;
18
19 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
20 import org.hipparchus.geometry.euclidean.threed.Vector3D;
21 import org.hipparchus.util.FastMath;
22 import org.junit.jupiter.api.AfterEach;
23 import org.junit.jupiter.api.Assertions;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 import org.orekit.Utils;
27 import org.orekit.attitudes.AttitudeProvider;
28 import org.orekit.attitudes.LofOffset;
29 import org.orekit.attitudes.NadirPointing;
30 import org.orekit.bodies.GeodeticPoint;
31 import org.orekit.bodies.OneAxisEllipsoid;
32 import org.orekit.errors.OrekitException;
33 import org.orekit.errors.OrekitMessages;
34 import org.orekit.frames.FramesFactory;
35 import org.orekit.frames.LOFType;
36 import org.orekit.frames.TopocentricFrame;
37 import org.orekit.frames.Transform;
38 import org.orekit.geometry.fov.PolygonalFieldOfView.DefiningConeType;
39 import org.orekit.orbits.KeplerianOrbit;
40 import org.orekit.orbits.Orbit;
41 import org.orekit.propagation.Propagator;
42 import org.orekit.propagation.SpacecraftState;
43 import org.orekit.propagation.analytical.KeplerianPropagator;
44 import org.orekit.propagation.events.VisibilityTrigger;
45 import org.orekit.time.AbsoluteDate;
46 import org.orekit.utils.Constants;
47 import org.orekit.utils.IERSConventions;
48 import org.orekit.utils.PVCoordinates;
49
50 import java.util.List;
51
52 public class PolygonalFieldOfViewTest {
53
54 @Test
55 public void testRegularPolygon() {
56 double delta = 0.25;
57 double margin = 0.01;
58 double maxAreaError = 0;
59 double maxOffsetError = 0;
60 for (int n = 3; n < 32; ++n) {
61 PolygonalFieldOfView base = new PolygonalFieldOfView(Vector3D.PLUS_K,
62 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
63 Vector3D.PLUS_I, delta, n, margin);
64 PolygonalFieldOfView fov = new PolygonalFieldOfView(base.getZone(), margin);
65 double eta = FastMath.acos(FastMath.sin(FastMath.PI / n) * FastMath.cos(delta));
66 double theoreticalArea = 2 * n * eta - (n - 2) * FastMath.PI;
67 double areaError = theoreticalArea - fov.getZone().getSize();
68 maxAreaError = FastMath.max(FastMath.abs(areaError), maxAreaError);
69 for (double lambda = -0.5 * FastMath.PI; lambda < 0.5 * FastMath.PI; lambda += 0.1) {
70 Vector3D v = new Vector3D(0.0, lambda).scalarMultiply(1.0e6);
71 double theoreticalOffset = 0.5 * FastMath.PI - lambda - delta - margin;
72 double offset = fov.offsetFromBoundary(v, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV);
73 if (theoreticalOffset > 0.01) {
74
75
76
77
78 Assertions.assertTrue(offset > 0);
79 Assertions.assertTrue(offset <= theoreticalOffset + 5e-16);
80 } else {
81 double offsetError = theoreticalOffset - offset;
82 maxOffsetError = FastMath.max(FastMath.abs(offsetError), maxOffsetError);
83 }
84 Assertions.assertEquals(-margin,
85 fov.offsetFromBoundary(fov.projectToBoundary(v), 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
86 1.0e-12);
87 }
88 }
89 Assertions.assertEquals(0.0, maxAreaError, 5.0e-14);
90 Assertions.assertEquals(0.0, maxOffsetError, 2.0e-15);
91 }
92
93 @Test
94 public void testNoFootprintInside() {
95 Utils.setDataRoot("regular-data");
96 PolygonalFieldOfView fov = new PolygonalFieldOfView(Vector3D.PLUS_K,
97 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
98 Vector3D.PLUS_I,
99 FastMath.toRadians(3.0), 6, 0.0);
100 OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
101 Constants.WGS84_EARTH_FLATTENING,
102 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
103 Transform fovToBody = new Transform(AbsoluteDate.J2000_EPOCH, new Vector3D(5e6, 3e6, 2e6));
104 try {
105 fov.getFootprint(fovToBody, earth, FastMath.toRadians(0.1));
106 Assertions.fail("an exception should have been thrown");
107 } catch (OrekitException oe) {
108 Assertions.assertEquals(OrekitMessages.POINT_INSIDE_ELLIPSOID, oe.getSpecifier());
109 }
110 }
111
112 @Test
113 public void testNadirHexagonalFootprint() {
114 doTest(new PolygonalFieldOfView(Vector3D.PLUS_K,
115 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
116 Vector3D.PLUS_I,
117 FastMath.toRadians(3.0), 6, 0.0),
118 new NadirPointing(orbit.getFrame(), earth),
119 210, 84.6497, 85.3729, 181052.2, 209092.8);
120 }
121
122 @Test
123 public void testRollPitchYawHexagonalFootprint() {
124 doTest(new PolygonalFieldOfView(Vector3D.PLUS_K,
125 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
126 Vector3D.PLUS_I,
127 FastMath.toRadians(3.0), 6, 0.0),
128 new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYZ,
129 FastMath.toRadians(10),
130 FastMath.toRadians(20),
131 FastMath.toRadians(5)),
132 210, 48.0026, 60.1975, 1221543.6, 1804921.6);
133 }
134
135 @Test
136 public void testFOVPartiallyTruncatedAtLimb() {
137 doTest(new PolygonalFieldOfView(Vector3D.PLUS_K,
138 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
139 Vector3D.PLUS_I,
140 FastMath.toRadians(40.0), 6, 0.0),
141 new NadirPointing(orbit.getFrame(), earth),
142 2448, 0.0, 7.9089, 4583054.6, 5347029.8);
143 }
144
145 @Test
146 public void testFOVLargerThanEarth() {
147 doTest(new PolygonalFieldOfView(Vector3D.PLUS_K,
148 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
149 Vector3D.PLUS_I,
150 FastMath.toRadians(45.0), 6, 0.0),
151 new NadirPointing(orbit.getFrame(), earth),
152 2337, 0.0, 0.0, 5323032.8, 5347029.8);
153 }
154
155 @Test
156 public void testFOVLargerThanEarthOld() {
157 Utils.setDataRoot("regular-data");
158 PolygonalFieldOfView fov = new PolygonalFieldOfView(Vector3D.PLUS_K,
159 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
160 Vector3D.PLUS_I,
161 FastMath.toRadians(45.0), 6, 0.0);
162 OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
163 Constants.WGS84_EARTH_FLATTENING,
164 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
165 KeplerianOrbit orbit = new KeplerianOrbit(new PVCoordinates(new Vector3D(7.0e6, 1.0e6, 4.0e6),
166 new Vector3D(-500.0, 8000.0, 1000.0)),
167 FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH,
168 Constants.EIGEN5C_EARTH_MU);
169 Propagator propagator = new KeplerianPropagator(orbit);
170 propagator.setAttitudeProvider(new NadirPointing(orbit.getFrame(), earth));
171 SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(1000.0));
172 Transform inertToBody = state.getFrame().getTransformTo(earth.getBodyFrame(), state.getDate());
173 Transform fovToBody = new Transform(state.getDate(),
174 state.toTransform().getInverse(),
175 inertToBody);
176 List<List<GeodeticPoint>> footprint = fov.getFootprint(fovToBody, earth, FastMath.toRadians(1.0));
177 Vector3D subSat = earth.projectToGround(state.getPosition(earth.getBodyFrame()),
178 state.getDate(), earth.getBodyFrame());
179 Assertions.assertEquals(1, footprint.size());
180 List<GeodeticPoint> loop = footprint.get(0);
181 Assertions.assertEquals(234, loop.size());
182 double minEl = Double.POSITIVE_INFINITY;
183 double maxEl = 0;
184 double minDist = Double.POSITIVE_INFINITY;
185 double maxDist = 0;
186 for (int i = 0; i < loop.size(); ++i) {
187 Assertions.assertEquals(0.0, loop.get(i).getAltitude(), 3.0e-7);
188 TopocentricFrame topo = new TopocentricFrame(earth, loop.get(i), "atLimb");
189 final double elevation = topo.
190 getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()).
191 getElevation();
192 minEl = FastMath.min(minEl, elevation);
193 maxEl = FastMath.max(maxEl, elevation);
194 final double dist = Vector3D.distance(subSat, earth.transform(loop.get(i)));
195 minDist = FastMath.min(minDist, dist);
196 maxDist = FastMath.max(maxDist, dist);
197 }
198 Assertions.assertEquals(0.0, FastMath.toDegrees(minEl), 2.0e-12);
199 Assertions.assertEquals(0.0, FastMath.toDegrees(maxEl), 1.7e-12);
200 Assertions.assertEquals(5323036.6, minDist, 1.0);
201 Assertions.assertEquals(5347029.8, maxDist, 1.0);
202 }
203
204 @Test
205 public void testFOVAwayFromEarth() {
206 PolygonalFieldOfView fov = new PolygonalFieldOfView(Vector3D.MINUS_K,
207 DefiningConeType.INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE,
208 Vector3D.PLUS_I,
209 FastMath.toRadians(3.0), 6, 0.0);
210 Propagator propagator = new KeplerianPropagator(orbit);
211 propagator.setAttitudeProvider(new NadirPointing(orbit.getFrame(), earth));
212 SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(1000.0));
213 Transform inertToBody = state.getFrame().getTransformTo(earth.getBodyFrame(), state.getDate());
214 Transform fovToBody = new Transform(state.getDate(),
215 state.toTransform().getInverse(),
216 inertToBody);
217 List<List<GeodeticPoint>> footprint = fov.getFootprint(fovToBody, earth, FastMath.toRadians(1.0));
218 Assertions.assertEquals(0, footprint.size());
219 }
220
221 private void doTest(final PolygonalFieldOfView fov, final AttitudeProvider attitude, final int expectedPoints,
222 final double expectedMinElevation, final double expectedMaxElevation,
223 final double expectedMinDist, final double expectedMaxDist) {
224
225 Propagator propagator = new KeplerianPropagator(orbit);
226 propagator.setAttitudeProvider(attitude);
227 SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(1000.0));
228 Transform inertToBody = state.getFrame().getTransformTo(earth.getBodyFrame(), state.getDate());
229 Transform fovToBody = new Transform(state.getDate(),
230 state.toTransform().getInverse(),
231 inertToBody);
232 List<List<GeodeticPoint>> footprint = fov.getFootprint(fovToBody, earth, FastMath.toRadians(0.1));
233 Vector3D subSat = earth.projectToGround(state.getPosition(earth.getBodyFrame()),
234 state.getDate(), earth.getBodyFrame());
235 Assertions.assertEquals(1, footprint.size());
236 List<GeodeticPoint> loop = footprint.get(0);
237 Assertions.assertEquals(expectedPoints, loop.size());
238 double minEl = Double.POSITIVE_INFINITY;
239 double maxEl = 0;
240 double minDist = Double.POSITIVE_INFINITY;
241 double maxDist = 0;
242 for (int i = 0; i < loop.size(); ++i) {
243
244 Assertions.assertEquals(0.0, loop.get(i).getAltitude(), 9.0e-9);
245
246 TopocentricFrame topo = new TopocentricFrame(earth, loop.get(i), "onFootprint");
247 final double elevation = topo.
248 getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()).
249 getElevation();
250 if (elevation > 0.001) {
251 Vector3D los = fovToBody.getStaticInverse().transformPosition(earth.transform(loop.get(i)));
252 Assertions.assertEquals(-fov.getMargin(),
253 fov.offsetFromBoundary(los, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
254 4.0e-15);
255 Assertions.assertEquals(0.125 - fov.getMargin(),
256 fov.offsetFromBoundary(los, 0.125, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
257 4.0e-15);
258 Assertions.assertEquals(-0.125 - fov.getMargin(),
259 fov.offsetFromBoundary(los, 0.125, VisibilityTrigger.VISIBLE_AS_SOON_AS_PARTIALLY_IN_FOV),
260 4.0e-15);
261 }
262 minEl = FastMath.min(minEl, elevation);
263 maxEl = FastMath.max(maxEl, elevation);
264 final double dist = Vector3D.distance(subSat, earth.transform(loop.get(i)));
265 minDist = FastMath.min(minDist, dist);
266 maxDist = FastMath.max(maxDist, dist);
267
268 }
269
270 Assertions.assertEquals(expectedMinElevation, FastMath.toDegrees(minEl), 0.001);
271 Assertions.assertEquals(expectedMaxElevation, FastMath.toDegrees(maxEl), 0.001);
272 Assertions.assertEquals(expectedMinDist, minDist, 1.0);
273 Assertions.assertEquals(expectedMaxDist, maxDist, 1.0);
274
275 }
276
277 @BeforeEach
278 public void setUp() {
279 Utils.setDataRoot("regular-data");
280 earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
281 Constants.WGS84_EARTH_FLATTENING,
282 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
283 orbit = new KeplerianOrbit(new PVCoordinates(new Vector3D(7.0e6, 1.0e6, 4.0e6),
284 new Vector3D(-500.0, 8000.0, 1000.0)),
285 FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH,
286 Constants.EIGEN5C_EARTH_MU);
287 }
288
289 @AfterEach
290 public void tearDown() {
291 earth = null;
292 orbit = null;
293 }
294
295 private OneAxisEllipsoid earth;
296 private Orbit orbit;
297
298 }