From 013115ea1e301b74384ae3d77b57d9aaecde9294 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Thu, 18 Jun 2015 11:01:39 +0200 Subject: [PATCH] Added a protection against infinite loops in Duvenhage algorithm. --- .../duvenhage/DuvenhageAlgorithm.java | 134 +++++++++++++++--- src/site/xdoc/changes.xml | 3 + .../org/orekit/rugged/api/RuggedTest.java | 2 +- .../orekit/rugged/errors/DumpManagerTest.java | 2 +- .../rugged/errors/DumpReplayerTest.java | 25 +++- .../duvenhage/DuvenhageAlgorithmTest.java | 2 +- .../resources/replay/replay-direct-loc-03.txt | 29 ++++ 7 files changed, 176 insertions(+), 21 deletions(-) create mode 100644 src/test/resources/replay/replay-direct-loc-03.txt diff --git a/src/main/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithm.java b/src/main/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithm.java index 33c13a4a..24e756b8 100644 --- a/src/main/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithm.java +++ b/src/main/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithm.java @@ -121,9 +121,9 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { final int exitLon = FastMath.max(0, FastMath.min(tile.getLongitudeColumns() - 1, tile.getFloorLongitudeIndex(exit.getPoint().getLongitude()))); - final NormalizedGeodeticPoint intersection = recurseIntersection(0, ellipsoid, position, los, tile, - current, entryLat, entryLon, - exit.getPoint(), exitLat, exitLon); + NormalizedGeodeticPoint intersection = recurseIntersection(0, ellipsoid, position, los, tile, + current, entryLat, entryLon, + exit.getPoint(), exitLat, exitLon); if (intersection != null) { // we have found the intersection @@ -144,9 +144,19 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { } } else { + // this should never happen // we should have left the loop with an intersection point - throw RuggedException.createInternalError(null); + // try a fallback non-recursive search + intersection = noRecurseIntersection(ellipsoid, position, los, tile, + current, entryLat, entryLon, + exitLat, exitLon); + if (intersection != null) { + return intersection; + } else { + throw RuggedException.createInternalError(null); + } + } @@ -233,9 +243,10 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { throw RuggedException.createInternalError(null); } - if (entryLat == exitLat && entryLon == exitLon) { - // we have narrowed the search down to a single Digital Elevation Model cell - return tile.cellIntersection(entry, ellipsoid.convertLos(entry, los), exitLat, exitLon); + if (searchDomainSize(entryLat, entryLon, exitLat, exitLon) < 4) { + // we have narrowed the search down to a few cells + return noRecurseIntersection(ellipsoid, position, los, tile, + entry, entryLat, entryLon, exitLat, exitLon); } // find the deepest level in the min/max kd-tree at which entry and exit share a sub-tile @@ -297,10 +308,18 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { if (inRange(crossingLonBefore, entryLon, exitLon)) { // look for intersection - final NormalizedGeodeticPoint intersection = - recurseIntersection(depth + 1, ellipsoid, position, los, tile, - previousGP, previousLat, previousLon, - crossingGP, crossingLat, crossingLonBefore); + final NormalizedGeodeticPoint intersection; + if (searchDomainSize(previousLat, previousLon, crossingLat, crossingLonBefore) < + searchDomainSize(entryLat, entryLon, exitLat, exitLon)) { + intersection = recurseIntersection(depth + 1, ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + crossingGP, crossingLat, crossingLonBefore); + } else { + // we failed to reduce domain size, probably due to numerical problems + intersection = noRecurseIntersection(ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + crossingLat, crossingLonBefore); + } if (intersection != null) { return intersection; } @@ -359,9 +378,17 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { if (inRange(crossingLatBefore, entryLat, exitLat)) { // look for intersection - final NormalizedGeodeticPoint intersection = recurseIntersection(depth + 1, ellipsoid, position, los, tile, - previousGP, previousLat, previousLon, - crossingGP, crossingLatBefore, crossingLon); + final NormalizedGeodeticPoint intersection; + if (searchDomainSize(previousLat, previousLon, crossingLatBefore, crossingLon) < + searchDomainSize(entryLat, entryLon, exitLat, exitLon)) { + intersection = recurseIntersection(depth + 1, ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + crossingGP, crossingLatBefore, crossingLon); + } else { + intersection = noRecurseIntersection(ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + crossingLatBefore, crossingLon); + } if (intersection != null) { return intersection; } @@ -379,15 +406,88 @@ public class DuvenhageAlgorithm implements IntersectionAlgorithm { if (inRange(previousLat, entryLat, exitLat) && inRange(previousLon, entryLon, exitLon)) { // last part of the segment, up to exit point - return recurseIntersection(depth + 1, ellipsoid, position, los, tile, - previousGP, previousLat, previousLon, - exit, exitLat, exitLon); + if (searchDomainSize(previousLat, previousLon, exitLat, exitLon) < + searchDomainSize(entryLat, entryLon, exitLat, exitLon)) { + return recurseIntersection(depth + 1, ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + exit, exitLat, exitLon); + } else { + return noRecurseIntersection(ellipsoid, position, los, tile, + previousGP, previousLat, previousLon, + exitLat, exitLon); + } } else { return null; } } + /** Compute intersection of line with Digital Elevation Model in a sub-tile, without recursion. + * @param ellipsoid reference ellipsoid + * @param position pixel position in ellipsoid frame + * @param los pixel line-of-sight in ellipsoid frame + * @param tile Digital Elevation Model tile + * @param entry line-of-sight entry point in the sub-tile + * @param entryLat index to use for interpolating entry point elevation + * @param entryLon index to use for interpolating entry point elevation + * @param exitLat index to use for interpolating exit point elevation + * @param exitLon index to use for interpolating exit point elevation + * @return point at which the line first enters ground, or null if does not enter + * ground in the search sub-tile + * @exception RuggedException if intersection cannot be found + * @exception OrekitException if points cannot be converted to geodetic coordinates + */ + private NormalizedGeodeticPoint noRecurseIntersection(final ExtendedEllipsoid ellipsoid, + final Vector3D position, final Vector3D los, + final MinMaxTreeTile tile, + final NormalizedGeodeticPoint entry, + final int entryLat, final int entryLon, + final int exitLat, final int exitLon) + throws OrekitException, RuggedException { + + NormalizedGeodeticPoint intersectionGP = null; + double intersectionDot = Double.POSITIVE_INFINITY; + for (int i = FastMath.min(entryLat, exitLat); i <= FastMath.max(entryLat, exitLat); ++i) { + for (int j = FastMath.min(entryLon, exitLon); j <= FastMath.max(entryLon, exitLon); ++j) { + final NormalizedGeodeticPoint gp = tile.cellIntersection(entry, ellipsoid.convertLos(entry, los), i, j); + if (gp != null) { + + // improve the point, by projecting it back on the 3D line, fixing the small body curvature at cell level + final Vector3D delta = ellipsoid.transform(gp).subtract(position); + final double s = Vector3D.dotProduct(delta, los) / los.getNormSq(); + final GeodeticPoint projected = ellipsoid.transform(new Vector3D(1, position, s, los), + ellipsoid.getBodyFrame(), null); + final NormalizedGeodeticPoint gpImproved = tile.cellIntersection(projected, ellipsoid.convertLos(projected, los), i, j); + + if (gpImproved != null) { + final Vector3D point = ellipsoid.transform(gpImproved); + final double dot = Vector3D.dotProduct(point.subtract(position), los); + if (dot < intersectionDot) { + intersectionGP = gpImproved; + intersectionDot = dot; + } + } + + } + } + } + + return intersectionGP; + + } + + /** Compute the size of a search domain. + * @param entryLat index to use for interpolating entry point elevation + * @param entryLon index to use for interpolating entry point elevation + * @param exitLat index to use for interpolating exit point elevation + * @param exitLon index to use for interpolating exit point elevation + * @return size of the search domain + */ + private int searchDomainSize(final int entryLat, final int entryLon, + final int exitLat, final int exitLon) { + return (FastMath.abs(entryLat - exitLat) + 1) * (FastMath.abs(entryLon - exitLon) + 1); + } + /** Check if an index is inside a range. * @param i index to check * @param a first bound of the range (may be either below or above b) diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 8f9d7f45..9d1d111f 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -22,6 +22,9 @@ <body> <release version="1.0" date="TBD" description="TBD"> + <action dev="luc" type="add" > + Added a protection against infinite loops in Duvenhage algorithm. + </action> <action dev="luc" type="add" > Added a CONSTANT_ELEVATION_OVER_ELLIPSOID algorithm, similar in spirit to the IGNORE_DEM_USE_ELLIPSOID, but with a user-specified elevation diff --git a/src/test/java/org/orekit/rugged/api/RuggedTest.java b/src/test/java/org/orekit/rugged/api/RuggedTest.java index 00252890..853b4088 100644 --- a/src/test/java/org/orekit/rugged/api/RuggedTest.java +++ b/src/test/java/org/orekit/rugged/api/RuggedTest.java @@ -656,7 +656,7 @@ public class RuggedTest { public void testInverseLocation() throws RuggedException, OrekitException, URISyntaxException { checkInverseLocation(2000, false, false, 3.0e-7, 5.0e-6); - checkInverseLocation(2000, false, true, 6.0e-6, 2.0e-7); + checkInverseLocation(2000, false, true, 1.0e-5, 2.0e-7); checkInverseLocation(2000, true, false, 4.0e-7, 4.0e-7); checkInverseLocation(2000, true, true, 2.0e-5, 3.0e-7); } diff --git a/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java b/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java index 10a12f37..46cb3d98 100644 --- a/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java +++ b/src/test/java/org/orekit/rugged/errors/DumpManagerTest.java @@ -119,7 +119,7 @@ public class DumpManagerTest { Assert.assertEquals(1, countSpan); Assert.assertEquals(1, countTransform); Assert.assertEquals(2, countDEMTile); - Assert.assertEquals(887, countDEMCell); + Assert.assertEquals(812, countDEMCell); Assert.assertEquals(400, countDirectLoc); Assert.assertEquals(400, countDirectLocResult); Assert.assertEquals(1, countSensor); diff --git a/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java b/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java index 572a74f2..31afa97d 100644 --- a/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java +++ b/src/test/java/org/orekit/rugged/errors/DumpReplayerTest.java @@ -51,7 +51,7 @@ public class DumpReplayerTest { GeodeticPoint replayedGP = (GeodeticPoint) result.getReplayed(); double distance = Vector3D.distance(rugged.getEllipsoid().transform(expectedGP), rugged.getEllipsoid().transform(replayedGP)); - Assert.assertEquals(0.0, distance, 1.0e-8); + Assert.assertEquals(0.0, distance, 6.0e-8); } } @@ -79,6 +79,29 @@ public class DumpReplayerTest { } + @Test + public void testDirectLoc03() throws URISyntaxException, IOException, OrekitException, RuggedException { + + String orekitPath = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); + DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(orekitPath))); + + String dumpPath = getClass().getClassLoader().getResource("replay/replay-direct-loc-03.txt").toURI().getPath(); + DumpReplayer replayer = new DumpReplayer(); + replayer.parse(new File(dumpPath)); + Rugged rugged = replayer.createRugged(); + DumpReplayer.Result[] results = replayer.execute(rugged); + + Assert.assertEquals(1, results.length); + for (final DumpReplayer.Result result : results) { + GeodeticPoint expectedGP = (GeodeticPoint) result.getExpected(); + GeodeticPoint replayedGP = (GeodeticPoint) result.getReplayed(); + double distance = Vector3D.distance(rugged.getEllipsoid().transform(expectedGP), + rugged.getEllipsoid().transform(replayedGP)); + Assert.assertEquals(0.0, distance, 1.0e-8); + } + + } + @Test public void testInverseLoc01() throws URISyntaxException, IOException, OrekitException, RuggedException { diff --git a/src/test/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithmTest.java b/src/test/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithmTest.java index 7f1e551f..a0a3fb51 100644 --- a/src/test/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithmTest.java +++ b/src/test/java/org/orekit/rugged/intersection/duvenhage/DuvenhageAlgorithmTest.java @@ -119,7 +119,7 @@ public class DuvenhageAlgorithmTest extends AbstractAlgorithmTest { algorithm.intersection(earth, new Vector3D(-3041185.154503948, 6486750.132281409, -32335.022880173332), new Vector3D(0.5660218606298548 , -0.8233939240951769, 0.040517885584811814)); - Assert.assertEquals(1164.339, gp.getAltitude(), 1.0e-3); + Assert.assertEquals(1164.35, gp.getAltitude(), 0.02); } @Test diff --git a/src/test/resources/replay/replay-direct-loc-03.txt b/src/test/resources/replay/replay-direct-loc-03.txt new file mode 100644 index 00000000..1643cd1a --- /dev/null +++ b/src/test/resources/replay/replay-direct-loc-03.txt @@ -0,0 +1,29 @@ +# Rugged library dump file, created on 2015-06-17T12:40:59Z +# all units are SI units (m, m/s, rad ...) +direct location: date 2009-12-10T19:49:15.69409696600003Z position 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 los -2.222227735527040e-02 -9.103563670459193e-02 9.955996601239552e-01 lightTime false aberration false +span: minDate 2009-12-10T19:43:11.00000000000000Z maxDate 2009-12-10T19:54:00.00000000000000Z tStep 1.000000000000000e-01 tolerance 1.000000000000000e+01 inertialFrame EME2000 +transform: index 3647 body r -9.892006943727071e-01 6.548538144006332e-05 4.928252940714298e-04 1.465665005635631e-01 Ω -7.256918583080753e-08 -1.041477941363816e-09 -7.292111444123439e-05 ΩDot -1.633367752479824e-16 1.747255489539140e-16 1.600518399853031e-19 spacecraft p 1.915468890219927e-01 -8.990556518547237e-01 -7.156923547128990e+06 v -2.488758510805678e-02 8.481932866150421e-02 -4.020837052430704e+00 a -2.066722296571628e-01 -2.090489712840085e+00 -7.003752909213540e-03 r -1.763451310531075e-01 -4.315699021299694e-01 8.655337192606597e-01 1.830333170551214e-01 Ω -8.737537450216512e-04 5.311070290168704e-04 2.103617306756347e-04 ΩDot 1.145446212396803e-07 1.919398959490098e-07 6.635983002829190e-08 +algorithm: DUVENHAGE +ellipsoid: ae 6.378137000000000e+06 f 3.352810664747481e-03 frame ITRF_CIO_CONV_2010_SIMPLE_EOP +DEM tile: t0 latMin 1.047197551196598e+00 latStep 1.454441043328608e-05 latRows 1201 lonMin -2.164208272472969e+00 lonStep 2.908882086657216e-05 lonCols 601 +DEM cell: t0 latIndex 1200 lonIndex 454 elevation 1.437147018278546e+02 +DEM cell: t0 latIndex 928 lonIndex 17 elevation 1.615747402326250e+03 +DEM cell: t0 latIndex 749 lonIndex 584 elevation 3.826208316815853e+02 +DEM cell: t0 latIndex 749 lonIndex 585 elevation 3.856180070818848e+02 +DEM cell: t0 latIndex 749 lonIndex 586 elevation 3.886151824821843e+02 +DEM cell: t0 latIndex 750 lonIndex 584 elevation 3.836203882299900e+02 +DEM cell: t0 latIndex 750 lonIndex 585 elevation 3.856175611585988e+02 +DEM cell: t0 latIndex 750 lonIndex 586 elevation 3.886147340872076e+02 +DEM cell: t0 latIndex 751 lonIndex 588 elevation 3.936086266060671e+02 +DEM cell: t0 latIndex 752 lonIndex 588 elevation 3.936081732677089e+02 +DEM cell: t0 latIndex 751 lonIndex 589 elevation 3.956057970629851e+02 +DEM cell: t0 latIndex 752 lonIndex 589 elevation 3.966053412529362e+02 +DEM cell: t0 latIndex 736 lonIndex 600 elevation 4.295819170687835e+02 +DEM cell: t0 latIndex 737 lonIndex 600 elevation 4.285814340701368e+02 +DEM cell: t0 latIndex 736 lonIndex 601 elevation 5.754850304128064e+02 +DEM cell: t0 latIndex 737 lonIndex 601 elevation 5.764867282588906e+02 +DEM cell: t0 latIndex 748 lonIndex 592 elevation 4.015986981091023e+02 +DEM cell: t0 latIndex 749 lonIndex 592 elevation 4.015982348839813e+02 +DEM cell: t0 latIndex 748 lonIndex 593 elevation 4.045958759810924e+02 +DEM cell: t0 latIndex 749 lonIndex 593 elevation 4.045954102842808e+02 +direct location result: latitude 1.058104370487310e+00 longitude -2.147166209870915e+00 elevation 3.882040377058536e+02 -- GitLab