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.geometry.euclidean.twod.Vector2D;
22 import org.hipparchus.random.UnitSphereRandomVectorGenerator;
23 import org.hipparchus.random.Well19937a;
24 import org.hipparchus.util.FastMath;
25 import org.hipparchus.util.MathUtils;
26 import org.junit.jupiter.api.Assertions;
27 import org.junit.jupiter.api.Test;
28 import org.orekit.attitudes.LofOffset;
29 import org.orekit.attitudes.NadirPointing;
30 import org.orekit.bodies.Ellipse;
31 import org.orekit.frames.FramesFactory;
32 import org.orekit.frames.LOFType;
33 import org.orekit.frames.Transform;
34 import org.orekit.propagation.events.VisibilityTrigger;
35 import org.orekit.time.AbsoluteDate;
36
37 import java.lang.reflect.InvocationTargetException;
38 import java.lang.reflect.Method;
39
40 public class EllipticalFieldOfViewTest extends AbstractSmoothFieldOfViewTest {
41
42 @Test
43 public void testPlanarProjection() {
44
45 EllipticalFieldOfView fov = new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
46 FastMath.toRadians(40.0), FastMath.toRadians(10.0),
47 0.0);
48
49
50
51 final Vector3D d = new Vector3D(0.4, 0.8, 0.2).normalize();
52
53
54 final Ellipse ellipse = new Ellipse(fov.getZ(), fov.getX(), fov.getY(),
55 FastMath.tan(fov.getHalfApertureAlongX()),
56 FastMath.tan(fov.getHalfApertureAlongY()),
57 FramesFactory.getGCRF());
58 final Vector3D projected = new Vector3D(1.0 / d.getZ(), d);
59 final Vector3D closestProj = ellipse.toSpace(ellipse.projectToEllipse(ellipse.toPlane(projected)));
60
61
62 Assertions.assertEquals(0.0,
63 fov.offsetFromBoundary(closestProj, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
64 2.0e-15);
65
66
67 Vector3D closestSphere = null;
68 for (double eta = 0; eta < MathUtils.TWO_PI; eta += 0.0001) {
69 Vector3D p = fov.directionAt(eta);
70 if (closestSphere == null || Vector3D.angle(p, d) < Vector3D.angle(closestSphere, d)) {
71 closestSphere = p;
72 }
73 }
74 Assertions.assertEquals(0.0,
75 fov.offsetFromBoundary(closestSphere, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
76 2.0e-15);
77
78
79
80 Assertions.assertEquals(Vector3D.angle(closestProj, d) - 0.0056958,
81 Vector3D.angle(closestSphere, d),
82 1.0e-7);
83
84 }
85
86 @Test
87 public void testFocalPoints() {
88
89 EllipticalFieldOfView fov = new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
90 FastMath.toRadians(40.0), FastMath.toRadians(10.0),
91 0.0);
92
93
94 final Vector3D f1Sphere = fov.getFocus1();
95 final Vector3D f2Sphere = fov.getFocus2();
96
97
98 Vector2D f1Plane = new Vector2D(f1Sphere.getX() * FastMath.cos(fov.getHalfApertureAlongY()), 0.0);
99 Vector2D f2Plane = new Vector2D(-f1Plane.getX(), f1Plane.getY());
100
101
102 Assertions.assertTrue(f1Sphere.getX() - f1Plane.getX() > +0.0095);
103 Assertions.assertTrue(f2Sphere.getX() - f2Plane.getX() < -0.0095);
104
105
106 final double angularDist = 2 * fov.getHalfApertureAlongX();
107 final double d = 2 * FastMath.sin(fov.getHalfApertureAlongX());
108
109 for (double angle = 0; angle < MathUtils.TWO_PI; angle += 0.001) {
110
111
112 final Vector3D pSphere = fov.directionAt(angle);
113 Assertions.assertEquals(angularDist, Vector3D.angle(pSphere, f1Sphere) + Vector3D.angle(pSphere, f2Sphere), 1.0e-14);
114
115
116 final Vector2D pPlane = new Vector2D(pSphere.getX(), pSphere.getY());
117 Assertions.assertEquals(d, Vector2D.distance(pPlane, f1Plane) + Vector2D.distance(pPlane, f2Plane), 5.0e-16);
118
119 }
120
121 }
122
123 @Test
124 public void testDirectionFromDistances()
125 throws NoSuchMethodException, SecurityException, IllegalAccessException,
126 IllegalArgumentException, InvocationTargetException {
127
128 final EllipticalFieldOfView fov = new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
129 FastMath.toRadians(40.0), FastMath.toRadians(10.0),
130 0.0);
131 final Vector3D f1Sphere = fov.getFocus1();
132 final Vector3D f2Sphere = fov.getFocus2();
133 final double a = FastMath.max(fov.getHalfApertureAlongX(), fov.getHalfApertureAlongY());
134 final double delta = Vector3D.angle(f1Sphere, f2Sphere);
135 final double dMin = a - delta / 2;
136 final double dMax = a + delta / 2;
137
138 Method directionAt = EllipticalFieldOfView.class.getDeclaredMethod("directionAt",
139 Double.TYPE, Double.TYPE, Double.TYPE);
140 directionAt.setAccessible(true);
141 for (double d1 = dMin; d1 <= dMax; d1 += 0.001) {
142 final double d2 = 2 * a - d1;
143 final Vector3D dPlus = (Vector3D) directionAt.invoke(fov, d1, d2, +1.0);
144 Assertions.assertEquals(d1, Vector3D.angle(dPlus, f1Sphere), 2.0e-14);
145 Assertions.assertEquals(d2, Vector3D.angle(dPlus, f2Sphere), 2.0e-14);
146 Assertions.assertEquals(0.0,
147 fov.offsetFromBoundary(dPlus, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
148 1.0e-13);
149 final Vector3D dMinus = (Vector3D) directionAt.invoke(fov, d1, d2, -1.0);
150 Assertions.assertEquals(d1, Vector3D.angle(dMinus, f1Sphere), 2.0e-14);
151 Assertions.assertEquals(d2, Vector3D.angle(dMinus, f2Sphere), 2.0e-14);
152 Assertions.assertEquals(0.0,
153 fov.offsetFromBoundary(dPlus, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
154 1.0e-13);
155
156 }
157
158 }
159
160 @Test
161 public void testNadirNoMargin() {
162 doTestFootprint(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
163 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
164 0.0),
165 new NadirPointing(orbit.getFrame(), earth),
166 2.0, 4.0, 83.8280, 86.9120, 120567.3, 241701.8);
167 }
168
169 @Test
170 public void testNadirMargin() {
171 doTestFootprint(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
172 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
173 0.01),
174 new NadirPointing(orbit.getFrame(), earth),
175 2.0, 4.0, 83.8280, 86.9120, 120567.3, 241701.8);
176 }
177
178 @Test
179 public void testRollPitchYaw() {
180 doTestFootprint(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
181 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
182 0.0),
183 new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYZ,
184 FastMath.toRadians(10),
185 FastMath.toRadians(20),
186 FastMath.toRadians(5)),
187 2.0, 4.0, 47.7675, 60.2403, 1219597.1, 1817011.0);
188 }
189
190 @Test
191 public void testFOVPartiallyTruncatedAtLimb() {
192 doTestFootprint(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
193 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
194 0.0),
195 new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYZ,
196 FastMath.toRadians(-10),
197 FastMath.toRadians(-39),
198 FastMath.toRadians(-5)),
199 0.3899, 4.0, 0.0, 24.7014, 3213727.9, 5346638.0);
200 }
201
202 @Test
203 public void testFOVLargerThanEarth() {
204 doTestFootprint(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
205 FastMath.toRadians(50.0), FastMath.toRadians(45.0),
206 0.0),
207 new NadirPointing(orbit.getFrame(), earth),
208 40.3505, 40.4655, 0.0, 0.0, 5323032.8, 5347029.8);
209 }
210
211 @Test
212 public void testFOVAwayFromEarth() {
213 doTestFOVAwayFromEarth(new EllipticalFieldOfView(Vector3D.MINUS_K, Vector3D.PLUS_I,
214 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
215 0.0),
216 new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYZ,
217 FastMath.toRadians(-10),
218 FastMath.toRadians(-39),
219 FastMath.toRadians(-5)),
220 Vector3D.MINUS_K);
221 }
222
223 @Test
224 public void testNoFootprintInside() {
225 doTestNoFootprintInside(new EllipticalFieldOfView(Vector3D.PLUS_K, Vector3D.PLUS_I,
226 FastMath.toRadians(4.0), FastMath.toRadians(2.0),
227 0.0),
228 new Transform(AbsoluteDate.J2000_EPOCH, new Vector3D(5e6, 3e6, 2e6)));
229 }
230
231 @Test
232 public void testConventionsTangentPoints()
233 throws NoSuchMethodException, SecurityException, IllegalAccessException,
234 IllegalArgumentException, InvocationTargetException {
235 Method directionAt = EllipticalFieldOfView.class.getDeclaredMethod("directionAt", Double.TYPE);
236 directionAt.setAccessible(true);
237 final EllipticalFieldOfView ang = new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
238 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
239 0.0);
240 final EllipticalFieldOfView cart = new EllipticalFieldOfView(ang.getCenter(), ang.getX(),
241 ang.getHalfApertureAlongX(), ang.getHalfApertureAlongY(),
242 ang.getMargin());
243 for (int i = 0; i < 4; ++i) {
244 final double theta = i * 0.5 * FastMath.PI;
245 final Vector3D pAng = (Vector3D) directionAt.invoke(ang, theta);
246 final Vector3D pCart = (Vector3D) directionAt.invoke(cart, theta);
247 Assertions.assertEquals(0.0, Vector3D.angle(pAng, pCart), 1.0e-15);
248 }
249 }
250
251 @Test
252 public void testPointsOnBoundary() {
253 doTestPointsOnBoundary(new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
254 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
255 0.0),
256 2.0e-12);
257 }
258
259 @Test
260 public void testPointsOutsideBoundary() {
261 doTestPointsNearBoundary(new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
262 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
263 0.0),
264 0.1, 0.0101573, 0.1, 1.0e-7);
265 }
266
267 @Test
268 public void testPointsInsideBoundary() {
269 doTestPointsNearBoundary(new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
270 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
271 0.0),
272 -0.1, -0.1, -0.0693260, 1.0e-7);
273 }
274
275 @Test
276 public void testPointsAlongPrincipalAxes() {
277
278 final EllipticalFieldOfView fov = new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
279 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
280 0.0);
281
282
283 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(11)),
284 FastMath.sin(-FastMath.toRadians(11)),
285 0.0),
286 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) > 0.0);
287 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(9)),
288 FastMath.sin(-FastMath.toRadians(9)),
289 0.0),
290 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0);
291 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(9)),
292 FastMath.sin(FastMath.toRadians(9)),
293 0.0),
294 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0);
295 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(11)),
296 FastMath.sin(FastMath.toRadians(11)),
297 0.0),
298 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) > 0.0);
299
300
301 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(41)),
302 0.0,
303 FastMath.sin(-FastMath.toRadians(41))),
304 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) > 0.0);
305 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(39)),
306 0.0,
307 FastMath.sin(-FastMath.toRadians(39))),
308 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0);
309 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(39)),
310 0.0,
311 FastMath.sin(FastMath.toRadians(39))),
312 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0);
313 Assertions.assertTrue(fov.offsetFromBoundary(new Vector3D(FastMath.cos(FastMath.toRadians(41)),
314 0.0,
315 FastMath.sin(FastMath.toRadians(41))),
316 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) > 0.0);
317 }
318
319 @Test
320 public void testOffsetAngularAccuracy() {
321
322 final EllipticalFieldOfView fov = new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
323 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
324 0.0);
325 final Vector3D f1 = fov.getFocus1();
326 final Vector3D f2 = fov.getFocus2();
327 final double a = FastMath.max(fov.getHalfApertureAlongX(), fov.getHalfApertureAlongY());
328
329 UnitSphereRandomVectorGenerator random =
330 new UnitSphereRandomVectorGenerator(3, new Well19937a(0xc9383d990d45a111l));
331 for (int i = 0; i < 300; ++i) {
332 final Vector3D los = new Vector3D(random.nextVector());
333 final double d1 = Vector3D.angle(los, f1);
334 final double d2 = Vector3D.angle(los, f2);
335 double etaMin;
336 double etaMax;
337 if (Vector3D.dotProduct(los, fov.getY()) > 0) {
338 if (Vector3D.dotProduct(los, fov.getX()) > 0) {
339 etaMin = 0;
340 etaMax = 0.5 * FastMath.PI;
341 } else {
342 etaMin = 0.5 * FastMath.PI;
343 etaMax = FastMath.PI;
344 }
345 } else {
346 if (Vector3D.dotProduct(los, fov.getX()) < 0) {
347 etaMin = FastMath.PI;
348 etaMax = 1.5 * FastMath.PI;
349 } else {
350 etaMin = 1.5 * FastMath.PI;
351 etaMax = 2.0 * FastMath.PI;
352 }
353 }
354 double minDist = Double.POSITIVE_INFINITY;
355 for (double eta = etaMin; eta < etaMax; eta += 0.0001) {
356 minDist = FastMath.min(minDist, Vector3D.angle(los, fov.directionAt(eta)));
357 }
358 minDist = FastMath.copySign(minDist, d1 + d2 - 2 * a);
359
360
361
362 double hugeRadius = FastMath.PI;
363 double realOffset = fov.offsetFromBoundary(los, hugeRadius, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV);
364 Assertions.assertEquals(minDist + hugeRadius, realOffset, 3.0e-7);
365
366
367
368 double approximateOffset = fov.offsetFromBoundary(los, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV);
369 Assertions.assertTrue(approximateOffset < minDist + 3.0e-7);
370
371 }
372
373 }
374
375 @Test
376 public void testBoundary() {
377 doTestBoundary(new EllipticalFieldOfView(Vector3D.PLUS_I, Vector3D.PLUS_J,
378 FastMath.toRadians(10.0), FastMath.toRadians(40.0),
379 0.01),
380 new Well19937a(0x5148b24d1bdf90cel),
381 2.0e-9);
382 }
383
384 private void doTestPointsOnBoundary(final EllipticalFieldOfView fov, double tol) {
385 try {
386 Method directionAt = EllipticalFieldOfView.class.getDeclaredMethod("directionAt", Double.TYPE);
387 directionAt.setAccessible(true);
388 for (double theta = 0; theta < MathUtils.TWO_PI; theta += 0.01) {
389 final Vector3D direction = (Vector3D) directionAt.invoke(fov, theta);
390 Assertions.assertEquals(0.0,
391 fov.offsetFromBoundary(direction, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV),
392 tol);
393 }
394 } catch (NoSuchMethodException | SecurityException | IllegalAccessException |
395 IllegalArgumentException | InvocationTargetException e) {
396 Assertions.fail(e.getLocalizedMessage());
397 }
398 }
399
400 private void doTestPointsNearBoundary(final EllipticalFieldOfView fov, final double delta,
401 final double expectedMin, final double expectedMax, final double tol) {
402 try {
403 final EllipticalFieldOfView near = new EllipticalFieldOfView(fov.getCenter(), fov.getX(),
404 fov.getHalfApertureAlongX() + delta,
405 fov.getHalfApertureAlongY() + delta,
406 fov.getMargin());
407 Method directionAt = EllipticalFieldOfView.class.getDeclaredMethod("directionAt", Double.TYPE);
408 directionAt.setAccessible(true);
409 double minOffset = Double.POSITIVE_INFINITY;
410 double maxOffset = Double.NEGATIVE_INFINITY;
411 for (double theta = 0; theta < MathUtils.TWO_PI; theta += 0.01) {
412 final Vector3D direction = (Vector3D) directionAt.invoke(near, theta);
413 final double offset = fov.offsetFromBoundary(direction, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV);
414 minOffset = FastMath.min(minOffset, offset);
415 maxOffset = FastMath.max(maxOffset, offset);
416 }
417 Assertions.assertEquals(expectedMin, minOffset, tol);
418 Assertions.assertEquals(expectedMax, maxOffset, tol);
419 } catch (NoSuchMethodException | SecurityException | IllegalAccessException |
420 IllegalArgumentException | InvocationTargetException e) {
421 Assertions.fail(e.getLocalizedMessage());
422 }
423 }
424
425 }