From f4c3d7702a76625a86ed5366dc968455b2ce188c Mon Sep 17 00:00:00 2001
From: Luc Maisonobe <luc@orekit.org>
Date: Tue, 20 Sep 2016 14:50:53 +0200
Subject: [PATCH] Improved tests.

---
 atmospheric-deviation.csv                     |  41 ----
 atmospheric-deviation.gp                      |  13 -
 atmospheric-deviation.pdf                     | Bin 8812 -> 0 bytes
 .../refraction/MultiLayerModelTest.java       | 230 ++++++++++++++----
 4 files changed, 177 insertions(+), 107 deletions(-)
 delete mode 100644 atmospheric-deviation.csv
 delete mode 100644 atmospheric-deviation.gp
 delete mode 100644 atmospheric-deviation.pdf

diff --git a/atmospheric-deviation.csv b/atmospheric-deviation.csv
deleted file mode 100644
index 2807245d..00000000
--- a/atmospheric-deviation.csv
+++ /dev/null
@@ -1,41 +0,0 @@
-angle,correction
-0.0,0
-0.01,0
-0.02,0
-0.03,0
-0.04,0
-0.05,0
-0.060000000000000005,0
-0.07,0
-0.08,0
-0.09,0
-0.09999999999999999,0
-0.10999999999999999,0
-0.11999999999999998,0
-0.12999999999999998,0
-0.13999999999999999,0
-0.15,0
-0.16,1
-0.17,1
-0.18000000000000002,1
-0.19000000000000003,1
-0.20000000000000004,1
-0.21000000000000005,1
-0.22000000000000006,1
-0.23000000000000007,1
-0.24000000000000007,1
-0.25000000000000006,1
-0.26000000000000006,1
-0.2700000000000001,1
-0.2800000000000001,1
-0.2900000000000001,1
-0.3000000000000001,1
-0.3100000000000001,1
-0.3200000000000001,1
-0.3300000000000001,1
-0.34000000000000014,1
-0.35000000000000014,1
-0.36000000000000015,1
-0.37000000000000016,1
-0.38000000000000017,2
-0.3900000000000002,2
diff --git a/atmospheric-deviation.gp b/atmospheric-deviation.gp
deleted file mode 100644
index 88716c6e..00000000
--- a/atmospheric-deviation.gp
+++ /dev/null
@@ -1,13 +0,0 @@
-# gnuplot script file for plotting bandwidth over time
-#!/usr/bin/gnuplot
-
-
-set terminal pdf  size 3,2
-set output 'atmospheric-deviation.pdf'
-set grid
-
-set title "Atmospheric Correction"
-set xlabel "angle (radians)"
-set ylabel "correction (meters)"
-
-plot "atmospheric-deviation.csv" using 1:0 with lines title ""
diff --git a/atmospheric-deviation.pdf b/atmospheric-deviation.pdf
deleted file mode 100644
index 1ce2e9bf36894482b4061c28bf544f2d59192897..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8812
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

diff --git a/src/test/java/org/orekit/rugged/refraction/MultiLayerModelTest.java b/src/test/java/org/orekit/rugged/refraction/MultiLayerModelTest.java
index 409a6a71..27c595f6 100644
--- a/src/test/java/org/orekit/rugged/refraction/MultiLayerModelTest.java
+++ b/src/test/java/org/orekit/rugged/refraction/MultiLayerModelTest.java
@@ -16,46 +16,63 @@
  */
 package org.orekit.rugged.refraction;
 
+import org.hipparchus.analysis.UnivariateFunction;
 import org.hipparchus.geometry.euclidean.threed.Rotation;
 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
 import org.hipparchus.geometry.euclidean.threed.Vector3D;
 import org.hipparchus.util.FastMath;
 import org.junit.Assert;
 import org.junit.Test;
-import org.orekit.bodies.GeodeticPoint;
 import org.orekit.errors.OrekitException;
 import org.orekit.rugged.errors.RuggedException;
+import org.orekit.rugged.errors.RuggedMessages;
 import org.orekit.rugged.intersection.AbstractAlgorithmTest;
 import org.orekit.rugged.intersection.IntersectionAlgorithm;
 import org.orekit.rugged.intersection.duvenhage.DuvenhageAlgorithm;
 import org.orekit.rugged.raster.TileUpdater;
 import org.orekit.rugged.utils.NormalizedGeodeticPoint;
 
-import java.io.FileNotFoundException;
-import java.io.PrintWriter;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class MultiLayerModelTest extends AbstractAlgorithmTest {
 
     @Test
-    public void testApplyCorrection() throws OrekitException, RuggedException, FileNotFoundException {
+    public void testAlmostNadir() throws OrekitException, RuggedException {
 
         setUpMayonVolcanoContext();
         final IntersectionAlgorithm algorithm = createAlgorithm(updater, 8);
         final Vector3D position = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
         final Vector3D los = new Vector3D( 0.5127552821932051, -0.8254313129088879, -0.2361041470463311);
-        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
-                algorithm.intersection(earth, position, los));
+        final NormalizedGeodeticPoint rawIntersection =
+                        algorithm.refineIntersection(earth, position, los,
+                                                     algorithm.intersection(earth, position, los));
 
         MultiLayerModel model = new MultiLayerModel(earth);
-        GeodeticPoint correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
+        NormalizedGeodeticPoint correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
         double distance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
 
         // this is almost a Nadir observation (LOS deviates between 1.4 and 1.6 degrees from vertical)
         // so the refraction correction is small
         Assert.assertEquals(0.0553797, distance, 1.0e-6);
 
+    }
+
+    @Test
+    public void testNoOpRefraction() throws OrekitException, RuggedException {
+
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm   algorithm       = createAlgorithm(updater, 8);
+        final Vector3D                position        = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        final Vector3D                los             = los(position, FastMath.toRadians(50.0));
+        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
+                                                                                     algorithm.intersection(earth, position, los));
+
+        MultiLayerModel model = new MultiLayerModel(earth);
+        NormalizedGeodeticPoint correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
+        double distance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
 
         // a test with indices all set to 1.0 - correction must be zero
         final int numberOfLayers = 16;
@@ -66,78 +83,185 @@ public class MultiLayerModelTest extends AbstractAlgorithmTest {
         model = new MultiLayerModel(earth, refractionLayers);
         correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
         distance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
-        Assert.assertEquals(0.0, distance, 0.001);
+        Assert.assertEquals(0.0, distance, 1.0e-9);
 
+    }
+
+    @Test
+    public void testReversedAtmosphere()
+        throws OrekitException, RuggedException, IllegalArgumentException,
+               IllegalAccessException, NoSuchFieldException, SecurityException {
+
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm   algorithm       = createAlgorithm(updater, 8);
+        final Vector3D                position        = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        final Vector3D                los             = los(position, FastMath.toRadians(50.0));
+        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
+                                                                                     algorithm.intersection(earth, position, los));
+
+        MultiLayerModel baseModel = new MultiLayerModel(earth);
+        NormalizedGeodeticPoint correctedIntersection = baseModel.applyCorrection(position, los, rawIntersection, algorithm);
 
         // an intentionally flawed atmosphere with refractive indices decreasing with altitude,
         // that should exhibit a LOS bending upwards
-        refractionLayers = new ArrayList<ConstantRefractionLayer>(numberOfLayers);
-        for(int i = numberOfLayers - 1; i >= 0; i--) {
-            refractionLayers.add(new ConstantRefractionLayer(i * 1.0e4, 1.0 + i*i*1e-6));
+        Field refractionLayersField = MultiLayerModel.class.getDeclaredField("refractionLayers");
+        refractionLayersField.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        List<ConstantRefractionLayer> baseRefractionLayers =
+                        (List<ConstantRefractionLayer>) refractionLayersField.get(baseModel);
+        List<ConstantRefractionLayer> denserRefractionLayers = new ArrayList<>();
+        for (final ConstantRefractionLayer layer : baseRefractionLayers) {
+            denserRefractionLayers.add(new ConstantRefractionLayer(layer.getLowestAltitude(),
+                                                               1.0 / layer.getRefractiveIndex()));
         }
-        model = new MultiLayerModel(earth, refractionLayers);
-        correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
-        double anglePosRawIntersection = Vector3D.angle(position, earth.transform(rawIntersection));
+        MultiLayerModel reversedModel = new MultiLayerModel(earth, denserRefractionLayers);
+        NormalizedGeodeticPoint reversedIntersection = reversedModel.applyCorrection(position, los, rawIntersection, algorithm);
+        double anglePosRawIntersection       = Vector3D.angle(position, earth.transform(rawIntersection));
         double anglePosCorrectedIntersection = Vector3D.angle(position, earth.transform(correctedIntersection));
-        Assert.assertTrue(anglePosRawIntersection < anglePosCorrectedIntersection);
+        double anglePosReversedIntersection  = Vector3D.angle(position, earth.transform(reversedIntersection));
+
+        // with regular atmosphere, the ray bends downwards,
+        // so the ground point is closer to the sub-satellite point than the raw intersection
+        Assert.assertTrue(anglePosCorrectedIntersection < anglePosRawIntersection);
+
+        // with reversed atmosphere, the ray bends upwards,
+        // so the ground point is farther from the sub-satellite point than the raw intersection
+        Assert.assertTrue(anglePosReversedIntersection > anglePosRawIntersection);
 
+        // the points are almost aligned (for distances around 20m, Earth curvature is small enough)
+        double dRawCorrected      = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
+        double dRawReversed       = Vector3D.distance(earth.transform(rawIntersection), earth.transform(reversedIntersection));
+        double dReversedCorrected = Vector3D.distance(earth.transform(reversedIntersection), earth.transform(correctedIntersection));
+        Assert.assertEquals(dRawCorrected + dRawReversed, dReversedCorrected, 1.0e-12 * dReversedCorrected);
+
+    }
+
+    @Test
+    public void testTwoAtmospheres()
+        throws OrekitException, RuggedException, IllegalArgumentException,
+               IllegalAccessException, NoSuchFieldException, SecurityException {
+
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm   algorithm       = createAlgorithm(updater, 8);
+        final Vector3D                position        = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        final Vector3D                los             = los(position, FastMath.toRadians(50.0));
+        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
+                                                                                     algorithm.intersection(earth, position, los));
 
         // a comparison between two atmospheres, one more dense than the other and showing correction
         // is more important with high indices
-        List<ConstantRefractionLayer> baseRefracLayers = new ArrayList<ConstantRefractionLayer>(numberOfLayers);
-        List<ConstantRefractionLayer> denserRefracLayers = new ArrayList<ConstantRefractionLayer>(numberOfLayers);
-        for(int i = numberOfLayers - 1; i >= 0; i--) {
-            double baseRefractiveIndex = FastMath.pow(numberOfLayers - i + 1, 2) * 1e-6;
-            baseRefracLayers.add(new ConstantRefractionLayer(i * 1.0e4, baseRefractiveIndex));
-            denserRefracLayers.add(new ConstantRefractionLayer(i * 1.0e4, baseRefractiveIndex + 1e-3));
+        MultiLayerModel baseModel = new MultiLayerModel(earth);
+        Field refractionLayersField = MultiLayerModel.class.getDeclaredField("refractionLayers");
+        refractionLayersField.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        List<ConstantRefractionLayer> baseRefractionLayers =
+                        (List<ConstantRefractionLayer>) refractionLayersField.get(baseModel);
+        List<ConstantRefractionLayer> denserRefractionLayers = new ArrayList<>();
+        double previousBaseN   = 1.0;
+        double previousDenserN = 1.0;
+        double factor          = 1.00001;
+        for (final ConstantRefractionLayer layer : baseRefractionLayers) {
+            final double currentBaseN   = layer.getRefractiveIndex();
+            final double baseRatio      = currentBaseN / previousBaseN;
+            final double currentDenserN = previousDenserN * factor * baseRatio;
+            denserRefractionLayers.add(new ConstantRefractionLayer(layer.getLowestAltitude(),
+                                                                   currentDenserN));
+            previousBaseN   = currentBaseN;
+            previousDenserN = currentDenserN;
         }
-        MultiLayerModel baseModel = new MultiLayerModel(earth, baseRefracLayers);
-        MultiLayerModel denserModel = new MultiLayerModel(earth, denserRefracLayers);
-        GeodeticPoint baseIntersection = baseModel.applyCorrection(position, los, rawIntersection, algorithm);
-        GeodeticPoint denserIntersection = denserModel.applyCorrection(position, los, rawIntersection, algorithm);
-        double baseDistance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(baseIntersection));
+        MultiLayerModel denserModel = new MultiLayerModel(earth, denserRefractionLayers);
+        NormalizedGeodeticPoint baseIntersection   = baseModel.applyCorrection(position, los, rawIntersection, algorithm);
+        NormalizedGeodeticPoint denserIntersection = denserModel.applyCorrection(position, los, rawIntersection, algorithm);
+        double baseDistance   = Vector3D.distance(earth.transform(rawIntersection),
+                                                  earth.transform(baseIntersection));
         double denserDistance = Vector3D.distance(earth.transform(rawIntersection),
-                earth.transform(denserIntersection));
-        // denserDistance: 291.6042252928431, baseDistance: 2710.1036961651967
-        // Assert.assertTrue(denserDistance > baseDistance);
+                                                  earth.transform(denserIntersection));
+        Assert.assertTrue(denserDistance > baseDistance);
 
+    }
 
-        // a test with a single refraction layer
-        refractionLayers = new ArrayList<ConstantRefractionLayer>(numberOfLayers);
-        refractionLayers.add(new ConstantRefractionLayer(0, 1.2));
-        model = new MultiLayerModel(earth, refractionLayers);
-        correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
-        distance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
-        Assert.assertEquals(0.0, distance, 0.0);
+    @Test
+    public void testMissingLayers() throws OrekitException, RuggedException {
 
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm   algorithm       = createAlgorithm(updater, 8);
+        final Vector3D                position        = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        final Vector3D                los             = los(position, FastMath.toRadians(50.0));
+        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
+                                                                                     algorithm.intersection(earth, position, los));
+        final double h = rawIntersection.getAltitude();
 
-        // deviation should increase as the angle between los and zenith increases
-        PrintWriter writer = new PrintWriter("atmospheric-deviation.csv");
-        writer.println("angle,correction");
+        MultiLayerModel model = new MultiLayerModel(earth,
+                                                    Collections.singletonList(new ConstantRefractionLayer(h + 100.0,
+                                                                                                          1.5)));
+        try {
+            model.applyCorrection(position, los, rawIntersection, algorithm);
+            Assert.fail("an exception should have been thrown");
+        } catch (RuggedException re) {
+            Assert.assertEquals(RuggedMessages.NO_LAYER_DATA, re.getSpecifier());
+            Assert.assertEquals(h,         ((Double) re.getParts()[0]).doubleValue(), 1.0e-6);
+            Assert.assertEquals(h + 100.0, ((Double) re.getParts()[1]).doubleValue(), 1.0e-6);
+        }
 
-        GeodeticPoint satGP = earth.transform(position, earth.getBodyFrame(), null);
-        Vector3D nadir = satGP.getNadir();
-        Vector3D horizontal = nadir.orthogonal();
-        for (double alpha = 0; alpha < 0.4; alpha += 0.01) {
-            Vector3D rotatingLos = new Rotation(horizontal, alpha, RotationConvention.VECTOR_OPERATOR).applyTo(nadir);
-            NormalizedGeodeticPoint uncorrectedIntersection = algorithm.refineIntersection(earth, position, rotatingLos,
-                    algorithm.intersection(earth, position, rotatingLos));
+    }
 
-            model = new MultiLayerModel(earth);
-            correctedIntersection = model.applyCorrection(position, rotatingLos, uncorrectedIntersection, algorithm);
-            distance = Vector3D.distance(earth.transform(uncorrectedIntersection),
-                    earth.transform(correctedIntersection));
+    @Test
+    public void testLayersBelowDEM() throws OrekitException, RuggedException {
 
-            writer.println(alpha + "," + FastMath.round(distance));
-        }
-        writer.close();
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm   algorithm       = createAlgorithm(updater, 8);
+        final Vector3D                position        = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        final Vector3D                los             = los(position, FastMath.toRadians(50.0));
+        final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, los,
+                                                                                     algorithm.intersection(earth, position, los));
 
+        MultiLayerModel model = new MultiLayerModel(earth,
+                                                    Collections.singletonList(new ConstantRefractionLayer(rawIntersection.getAltitude() - 100.0,
+                                                                                                          1.5)));
+        NormalizedGeodeticPoint correctedIntersection = model.applyCorrection(position, los, rawIntersection, algorithm);
+        double distance = Vector3D.distance(earth.transform(rawIntersection), earth.transform(correctedIntersection));
+        Assert.assertEquals(0.0, distance, 1.0e-20);
 
+    }
+
+    @Test
+    public void testDivingAngleChange() throws OrekitException, RuggedException {
+
+        setUpMayonVolcanoContext();
+        final IntersectionAlgorithm algorithm = createAlgorithm(updater, 8);
+        final Vector3D position = new Vector3D(-3787079.6453602533, 5856784.405679551, 1655869.0582939098);
+        AtmosphericRefraction model = new MultiLayerModel(earth);
+
+        // deviation should increase from 0 to about 17m
+        // as the angle between los and nadir increases from 0 to 50 degrees
+        // the reference model below has been obtained by fitting the test results themselves
+        // it is NOT considered a full featured model, it's just a helper function for this specific test
+        UnivariateFunction reference = alpha -> 1.17936 * FastMath.tan((2.94613 - 1.40162 * alpha) * alpha);
+
+        for (double alpha = 0; alpha < FastMath.toRadians(50.0); alpha += 0.1) {
+            final Vector3D rotatingLos = los(position, alpha);
+            final NormalizedGeodeticPoint rawIntersection = algorithm.refineIntersection(earth, position, rotatingLos,
+                                                                                         algorithm.intersection(earth, position, rotatingLos));
+
+            final NormalizedGeodeticPoint correctedIntersection = model.applyCorrection(position, rotatingLos, rawIntersection, algorithm);
+            final double distance = Vector3D.distance(earth.transform(rawIntersection),
+                                                      earth.transform(correctedIntersection));
+            Assert.assertEquals(reference.value(alpha), distance, 0.12);
+        }
 
     }
 
+    private Vector3D los(final Vector3D position, final double angleFromNadir)
+        throws OrekitException {
+        final Vector3D nadir       = earth.transform(position, earth.getBodyFrame(), null).getNadir();
+        final Rotation losRotation = new Rotation(nadir.orthogonal(), angleFromNadir,
+                                                  RotationConvention.VECTOR_OPERATOR);
+        return losRotation.applyTo(nadir);
+    }
+
     @Override
     protected IntersectionAlgorithm createAlgorithm(TileUpdater updater, int maxCachedTiles) {
         return new DuvenhageAlgorithm(updater, maxCachedTiles, false);
     }
+
 }
-- 
GitLab