Inverse Location and INVLOC_MARGIN

Hi,

I’ve been trying out Rugged. Overall it seems very nice, especially with the all the effects it considers and the tutorial on image location.

When using an atmosphere I keep getting the following error. (The message is a bit cryptic.) If I understand the code correctly it seems the pixel shift due to the atmosphere is greater than 0.8 pixels, the value of INVLOC_MARGIN. The 0.8 threshold seems well suited to looking nadir or large pixels, but in other cases the atmosphere can produce significant pixel shifts. (For details see [1].) Does the Rugged inverse location algorithm assume that refraction shifts are less than 1 pixel? Can INVLOC_MARGIN be safely increased, perhaps exposing a settable parameter in the RuggedBuilder? Or is there a better way for dealing with more atmospheric refraction?

Best Regards,
Evan

org.orekit.rugged.errors.RuggedException: range between min line 5,000 and max line 9,000 is invalid 

	at org.orekit.rugged.api.Rugged.computeInverseLocOnGridWithoutAtmosphere(Rugged.java:824)
	at org.orekit.rugged.api.Rugged.findSensorPixelWithAtmosphere(Rugged.java:708)
	at org.orekit.rugged.api.Rugged.inverseLocation(Rugged.java:539)

[1] Sensors | Free Full-Text | A Systematic Sensitivity Study on Surface Pixel Shifts in High Spatial Resolution Satellite Images Resulting from Atmospheric Refraction in the Sensor to Surface Ray Path | HTML

You are right, and your case is very interesting to us, because we were not able to find a realistic case where we entered this part of the code (it was defensive programming).

Before fixing the problem, could you try to run your case with dump manager activated? You just need to call DumpManager.activate(file) before the final call that trigger the exception. This will create a text file with just the required data that allows to reproduce the bug (using DumpReplayer), without the need to have the full calling application and most importantly without the need to have the full DEM. Of course, you should review the text file before to see it does not contain any sensitive data.

Then, I agree the margin should be user-adjustable. As we are working on publishing a new version of Rugged very soon, it would be nice if we could include such a fix. Do you want to give it a try?

Hi Luc,

Here is an example I made up, both the dump and the code that produced it. Changing the value of maxLine passed to the inverse location method will make the test case pass or fail. I can work towards making the margin settable via the RuggedBuilder.

Here is the test case, which ended up being rather long:

    /** Illustrative test case where refraction can cause errors. */
    @Test
    public void testRefraction() {
        // setup
        System.setProperty(DataProvidersManager.OREKIT_DATA_PATH, "/home/ward/eop/");
        DumpManager.activate(new File("refraction.dump"));
        Frame ecf = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
        RuggedBuilder builder = new RuggedBuilder();
        builder.setEllipsoid(ReferenceEllipsoid.getWgs84(ecf));
        MultiLayerModel atmosphere = new MultiLayerModel(builder.getEllipsoid());
        builder.setRefractionCorrection(atmosphere);
        builder.setLightTimeCorrection(true);
        builder.setAberrationOfLightCorrection(true);
        builder.setAlgorithm(AlgorithmId.IGNORE_DEM_USE_ELLIPSOID);
        AbsoluteDate start = AbsoluteDate.ARBITRARY_EPOCH;
        AbsoluteDate end = start.shiftedBy(10);
        AbsoluteDate middle = start.shiftedBy(end.durationFrom(start) / 2);
        builder.setTimeSpan(start, end, 1e-3, 1e-3);
        final double h = 500e3;
        Vector3D p = new Vector3D(6378137 + h, 0, 0);
        Vector3D v = Vector3D.ZERO;
        List<TimeStampedPVCoordinates> pvs = Arrays.asList(
                new TimeStampedPVCoordinates(start, p, v),
                new TimeStampedPVCoordinates(end, p, v));
        Rotation rotation = new Rotation(Vector3D.MINUS_I, Vector3D.MINUS_K, Vector3D.PLUS_K, Vector3D.PLUS_I);
        TimeStampedAngularCoordinates attitude =
                new TimeStampedAngularCoordinates(
                        middle, rotation,
                        Vector3D.PLUS_I.scalarMultiply(0.1), Vector3D.ZERO);
        List<TimeStampedAngularCoordinates> attitudes = Arrays.asList(
                attitude.shiftedBy(start.durationFrom(attitude.getDate())),
                attitude,
                attitude.shiftedBy(end.durationFrom(attitude.getDate())));
        builder.setTrajectory(ecf,
                pvs, 2, CartesianDerivativesFilter.USE_P,
                attitudes, 2, AngularDerivativesFilter.USE_R);
        final double iFov = 1e-6;
        TimeDependentLOS los = new TimeDependentLOS() {
            @Override
            public int getNbPixels() {
                return 1000;
            }

            @Override
            public Vector3D getLOS(int index, AbsoluteDate date) {
                // simplistic pinhole camera, assumes small angle
                final double center = getNbPixels() / 2.0;
                final double x = (index - center);
                final double los = x * iFov;
                return new Vector3D(los, 0, 1);
            }

            @Override
            public <T extends Derivative<T>> FieldVector3D<T> getLOSDerivatives(
                    int index,
                    AbsoluteDate date,
                    DerivativeGenerator<T> generator) {
                throw new UnsupportedOperationException("not implemented");
            }

            @Override
            public Stream<ParameterDriver> getParametersDrivers() {
                return Stream.empty();
            }
        };
        LineSensor sensor = new LineSensor("sensor",
                new LinearLineDatation(middle, 0, 1000),
                Vector3D.ZERO,
                los);
        builder.addLineSensor(sensor);

        Rugged rugged = builder.build();

        Vector3D losVector = new Vector3D(-1, 0.1 * 5, 500e-6);
        NormalizedGeodeticPoint guess = rugged.getEllipsoid().pointOnGround(p, losVector, 0);
        NormalizedGeodeticPoint correctedGp = atmosphere.applyCorrection(p, losVector, guess, rugged.getAlgorithm());
        Vector3D correctedP = rugged.getEllipsoid().transform(correctedGp);
        double correction = Vector3D.angle(losVector, correctedP.subtract(p));
        System.out.format("Max refraction correction: %s rad, %s px\n", correction, correction / iFov);

        final int maxLine = 4999; // works with 4500, fails with 4999
        GeodeticPoint point = rugged.directLocation(sensor.getName(), 0)[0];
        System.out.println("(0, 0) is " + point);
        point = rugged.directLocation(sensor.getName(), 0)[500];
        System.out.println("(0, 500) is " + point);
        point = rugged.directLocation(sensor.getName(), 0)[999];
        System.out.println("(0, 1000) is " + point);
        point = rugged.directLocation(sensor.getName(), 1000)[500];
        System.out.println("(1000, 500) is " + point);
        SensorPixel pixel = rugged.inverseLocation(sensor.getName(), point, 0, maxLine);
        System.out.format("%s is (%s, %s)\n", point, pixel.getLineNumber(), pixel.getPixelNumber());
        point = rugged.directLocation(sensor.getName(), 2000)[500];
        System.out.println("(2000, 500) is " + point);
        pixel = rugged.inverseLocation(sensor.getName(), point, 0, maxLine);
        System.out.format("%s is (%s, %s)\n", point, pixel.getLineNumber(), pixel.getPixelNumber());
        point = rugged.directLocation(sensor.getName(), 4000)[500];
        System.out.println("(2000, 500) is " + point);
        pixel = rugged.inverseLocation(sensor.getName(), point, 0, maxLine);
        System.out.format("%s is (%s, %s)\n", point, pixel.getLineNumber(), pixel.getPixelNumber());
        DumpManager.deactivate();
    }

Here is the output from when I run it:

Max refraction correction: 3.036616178093026E-6 rad, 3.036616178093026 px
(0, 0) is {lat: 0.0022609104 deg, lon: 0 deg, alt: 0}
(0, 500) is {lat: 0 deg, lon: 0 deg, alt: 0}
(0, 1000) is {lat: -0.0022563886 deg, lon: 0 deg, alt: 0}
(1000, 500) is {lat: -0.0860411513 deg, lon: 0.5038531532 deg, alt: 0}

org.orekit.rugged.errors.RuggedException: range between min line 0 and max line 4,999 is invalid 

	at org.orekit.rugged.api.Rugged.computeInverseLocOnGridWithoutAtmosphere(Rugged.java:824)
	at org.orekit.rugged.api.Rugged.findSensorPixelWithAtmosphere(Rugged.java:708)
	at org.orekit.rugged.api.Rugged.inverseLocation(Rugged.java:539)

refraction.dump (1.9 MB)

Regards,
Evan