From 7f1f244897f20a9c505f6e346c5ce7edb8f1c8be Mon Sep 17 00:00:00 2001
From: Luc Maisonobe <luc@orekit.org>
Date: Wed, 14 Jan 2015 16:11:47 +0100
Subject: [PATCH] Added a CONSTANT_ELEVATION_OVER_ELLIPSOID algorithm.

This algorithm is similar in spirit to the IGNORE_DEM_USE_ELLIPSOID, but
with a user-specified elevation instead of always using 0.0 elevation.

Implements feature #187.
---
 .../org/orekit/rugged/api/AlgorithmId.java    |  15 ++
 .../org/orekit/rugged/api/RuggedBuilder.java  |  79 ++++++++--
 .../ConstantElevationAlgorithm.java           |  85 +++++++++++
 .../design/digital-elevation-model.md         |   3 +-
 src/site/xdoc/changes.xml                     |   6 +
 .../intersection/AbstractAlgorithmTest.java   |   1 +
 .../ConstantElevationAlgorithmTest.java       | 143 ++++++++++++++++++
 7 files changed, 320 insertions(+), 12 deletions(-)
 create mode 100644 src/main/java/org/orekit/rugged/intersection/ConstantElevationAlgorithm.java
 create mode 100644 src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java

diff --git a/src/main/java/org/orekit/rugged/api/AlgorithmId.java b/src/main/java/org/orekit/rugged/api/AlgorithmId.java
index b8322f16..5616bf7a 100644
--- a/src/main/java/org/orekit/rugged/api/AlgorithmId.java
+++ b/src/main/java/org/orekit/rugged/api/AlgorithmId.java
@@ -57,10 +57,25 @@ public enum AlgorithmId {
      */
     BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY,
 
+    /** Algorithm that simply uses a constant elevation over ellipsoid.
+     * <p>
+     * Intersections are computed only with respect to the reference ellipsoid
+     * and a user-specified elevation. If the user-specified elevation is 0.0,
+     * then this algorithm is equivalent to {@link #IGNORE_DEM_USE_ELLIPSOID},
+     * only slower.
+     * </p>
+     */
+    CONSTANT_ELEVATION_OVER_ELLIPSOID,
+
     /** Dummy algorithm that simply ignores the Digital Elevation Model.
      * <p>
      * Intersections are computed only with respect to the reference ellipsoid.
      * </p>
+     * <p>
+     * This algorithm is equivalent to {@link #CONSTANT_ELEVATION_OVER_ELLIPSOID}
+     * when the elevation is set to 0.0, but this one is much faster in this
+     * specific case.
+     * </p>
      */
     IGNORE_DEM_USE_ELLIPSOID
 
diff --git a/src/main/java/org/orekit/rugged/api/RuggedBuilder.java b/src/main/java/org/orekit/rugged/api/RuggedBuilder.java
index dbcc1a35..17d73247 100644
--- a/src/main/java/org/orekit/rugged/api/RuggedBuilder.java
+++ b/src/main/java/org/orekit/rugged/api/RuggedBuilder.java
@@ -39,6 +39,7 @@ import org.orekit.propagation.sampling.OrekitFixedStepHandler;
 import org.orekit.rugged.errors.RuggedException;
 import org.orekit.rugged.errors.RuggedMessages;
 import org.orekit.rugged.intersection.BasicScanAlgorithm;
+import org.orekit.rugged.intersection.ConstantElevationAlgorithm;
 import org.orekit.rugged.intersection.IgnoreDEMAlgorithm;
 import org.orekit.rugged.intersection.IntersectionAlgorithm;
 import org.orekit.rugged.intersection.duvenhage.DuvenhageAlgorithm;
@@ -96,6 +97,10 @@ public class RuggedBuilder {
     /** Updater used to load Digital Elevation Model tiles. */
     private TileUpdater tileUpdater;
 
+    /** Constant elevation over ellipsoid.
+     * used only with {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID. */
+    private double constantElevation;
+
     /** Maximum number of tiles stored in the cache. */
     private int maxCachedTiles;
 
@@ -162,6 +167,7 @@ public class RuggedBuilder {
      */
     public RuggedBuilder() {
         sensors                     = new ArrayList<LineSensor>();
+        constantElevation           = Double.NaN;
         lightTimeCorrection         = true;
         aberrationOfLightCorrection = true;
     }
@@ -211,10 +217,21 @@ public class RuggedBuilder {
 
     /** Set the algorithm to use for Digital Elevation Model intersection.
      * <p>
-     * Note that when the specified algorithm is {@link AlgorithmId#IGNORE_DEM_USE_ELLIPSOID},
-     * then calling {@link #setDigitalElevationModel(TileUpdater, int)} is irrelevant and can either
-     * be completely avoided, or the method can be called with an updater set to {@code null}.
-     * For all other algorithms, the updater must be properly configured.
+     * Note that some algorithms require specific other methods to be called too:
+     * <ul>
+     *   <li>{@link AlgorithmId#DUVENHAGE DUVENHAGE},
+     *   {@link AlgorithmId#DUVENHAGE_FLAT_BODY DUVENHAGE_FLAT_BODY}
+     *   and {@link AlgorithmId#BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY
+     *   BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY} all
+     *   require {@link #setDigitalElevationModel(TileUpdater, int) setDigitalElevationModel}
+     *   to be called,</li>
+     *   <li>{@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID
+     *   CONSTANT_ELEVATION_OVER_ELLIPSOID} requires
+     *   {@link #setConstantElevation(double) setConstantElevation} to be called,</li>
+     *   <li>{@link AlgorithmId#IGNORE_DEM_USE_ELLIPSOID
+     *   IGNORE_DEM_USE_ELLIPSOID} does not require
+     *   any methods tobe called.</li>
+     * </ul>
      * </p>
      * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection
      * @return the builder instance
@@ -239,9 +256,12 @@ public class RuggedBuilder {
     /** Set the user-provided {@link TileUpdater tile updater}.
      * <p>
      * Note that when the algorithm specified in {@link #setAlgorithm(AlgorithmId)}
-     * is {@link AlgorithmId#IGNORE_DEM_USE_ELLIPSOID},then this method becomes irrelevant
-     * and can either be not called at all, or it can be called with an updater set to
-     * {@code null}. For all other algorithms, the updater must be properly configured.
+     * is either {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID
+     * CONSTANT_ELEVATION_OVER_ELLIPSOID} or {@link
+     * AlgorithmId#IGNORE_DEM_USE_ELLIPSOID IGNORE_DEM_USE_ELLIPSOID},
+     * then this method becomes irrelevant and can either be not called at all,
+     * or it can be called with an updater set to {@code null}. For all other
+     * algorithms, the updater must be properly configured.
      * </p>
      * @param tileUpdater updater used to load Digital Elevation Model tiles
      * @param maxCachedTiles maximum number of tiles stored in the cache
@@ -267,6 +287,33 @@ public class RuggedBuilder {
         return tileUpdater;
     }
 
+    /** Set the user-provided constant elevation model.
+     * <p>
+     * Note that this method is relevant <em>only</em> if the algorithm specified
+     * in {@link #setAlgorithm(AlgorithmId)} is {@link
+     * AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID CONSTANT_ELEVATION_OVER_ELLIPSOID}.
+     * If it is called for this algorithm, the elevation set here will be ignored.
+     * </p>
+     * @param constantElevation constant elevation to use
+     * @return the builder instance
+     * @see #setAlgorithm(AlgorithmId)
+     * @see #getConstantElevation()
+     */
+    // CHECKSTYLE: stop HiddenField check
+    public RuggedBuilder setConstantElevation(final double constantElevation) {
+        // CHECKSTYLE: resume HiddenField check
+        this.constantElevation = constantElevation;
+        return this;
+    }
+
+    /** Get the constant elevation over ellipsoid to use with {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID}.
+     * @return updater used to load Digital Elevation Model tiles
+     * @see #setConstantElevation(double)
+     */
+    public double getConstantElevation() {
+        return constantElevation;
+    }
+
     /** Get the maximum number of tiles stored in the cache.
      * @return maximum number of tiles stored in the cache
      * @see #setDigitalElevationModel(TileUpdater, int)
@@ -903,10 +950,12 @@ public class RuggedBuilder {
      * @param algorithmID intersection algorithm identifier
      * @param updater updater used to load Digital Elevation Model tiles
      * @param maxCachedTiles maximum number of tiles stored in the cache
+     * @param constantElevation constant elevation over ellipsoid
      * @return selected algorithm
      */
     private static IntersectionAlgorithm createAlgorithm(final AlgorithmId algorithmID,
-                                                         final TileUpdater updater, final int maxCachedTiles) {
+                                                         final TileUpdater updater, final int maxCachedTiles,
+                                                         final double constantElevation) {
 
         // set up the algorithm
         switch (algorithmID) {
@@ -916,6 +965,8 @@ public class RuggedBuilder {
             return new DuvenhageAlgorithm(updater, maxCachedTiles, true);
         case BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY :
             return new BasicScanAlgorithm(updater, maxCachedTiles);
+        case CONSTANT_ELEVATION_OVER_ELLIPSOID :
+            return new ConstantElevationAlgorithm(constantElevation);
         case IGNORE_DEM_USE_ELLIPSOID :
             return new IgnoreDEMAlgorithm();
         default :
@@ -934,11 +985,17 @@ public class RuggedBuilder {
         if (algorithmID == null) {
             throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setAlgorithmID()");
         }
-        if (algorithmID != AlgorithmId.IGNORE_DEM_USE_ELLIPSOID && (tileUpdater == null)) {
-            throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setDigitalElevationModel()");
+        if (algorithmID == AlgorithmId.CONSTANT_ELEVATION_OVER_ELLIPSOID) {
+            if (Double.isNaN(constantElevation)) {
+                throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setConstantElevation()");
+            }
+        } else if (algorithmID != AlgorithmId.IGNORE_DEM_USE_ELLIPSOID) {
+            if (tileUpdater == null) {
+                throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setDigitalElevationModel()");
+            }
         }
         createInterpolatorIfNeeded();
-        return new Rugged(createAlgorithm(algorithmID, tileUpdater, maxCachedTiles), ellipsoid,
+        return new Rugged(createAlgorithm(algorithmID, tileUpdater, maxCachedTiles, constantElevation), ellipsoid,
                           lightTimeCorrection, aberrationOfLightCorrection, scToBody, sensors);
     }
 
diff --git a/src/main/java/org/orekit/rugged/intersection/ConstantElevationAlgorithm.java b/src/main/java/org/orekit/rugged/intersection/ConstantElevationAlgorithm.java
new file mode 100644
index 00000000..8e9cb7b3
--- /dev/null
+++ b/src/main/java/org/orekit/rugged/intersection/ConstantElevationAlgorithm.java
@@ -0,0 +1,85 @@
+/* Copyright 2013-2015 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.rugged.intersection;
+
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.orekit.bodies.GeodeticPoint;
+import org.orekit.errors.OrekitException;
+import org.orekit.rugged.errors.RuggedException;
+import org.orekit.rugged.utils.ExtendedEllipsoid;
+import org.orekit.rugged.utils.NormalizedGeodeticPoint;
+
+/** Intersection ignoring Digital Elevation Model.
+ * <p>
+ * This dummy implementation simply uses the ellipsoid itself.
+ * </p>
+ * @author Luc Maisonobe
+ */
+public class ConstantElevationAlgorithm implements IntersectionAlgorithm {
+
+    /** Constant elevation over ellipsoid. */
+    private final double constantElevation;
+
+    /** Simple constructor.
+     * @param constantElevation constant elevation over ellipsoid
+     */
+    public ConstantElevationAlgorithm(final double constantElevation) {
+        this.constantElevation = constantElevation;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NormalizedGeodeticPoint intersection(final ExtendedEllipsoid ellipsoid,
+                                                final Vector3D position, final Vector3D los)
+        throws RuggedException {
+        try {
+            final Vector3D      p  = ellipsoid.pointAtAltitude(position, los, constantElevation);
+            final GeodeticPoint gp = ellipsoid.transform(p, ellipsoid.getFrame(), null);
+            return new NormalizedGeodeticPoint(gp.getLatitude(), gp.getLongitude(), gp.getAltitude(), 0.0);
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NormalizedGeodeticPoint refineIntersection(final ExtendedEllipsoid ellipsoid,
+                                                      final Vector3D position, final Vector3D los,
+                                                      final NormalizedGeodeticPoint closeGuess)
+        throws RuggedException {
+        try {
+            final Vector3D      p  = ellipsoid.pointAtAltitude(position, los, constantElevation);
+            final GeodeticPoint gp = ellipsoid.transform(p, ellipsoid.getFrame(), null);
+            return new NormalizedGeodeticPoint(gp.getLatitude(), gp.getLongitude(), gp.getAltitude(),
+                                               closeGuess.getLongitude());
+        } catch (OrekitException oe) {
+            throw new RuggedException(oe, oe.getSpecifier(), oe.getParts());
+        }
+    }
+
+    /** {@inheritDoc}
+     * <p>
+     * As this algorithm uses a constant elevation,
+     * this method always returns the same value.
+     * </p>
+     */
+    @Override
+    public double getElevation(final double latitude, final double longitude) {
+        return constantElevation;
+    }
+
+}
diff --git a/src/site/markdown/design/digital-elevation-model.md b/src/site/markdown/design/digital-elevation-model.md
index 0d51a6b2..cfb648e6 100644
--- a/src/site/markdown/design/digital-elevation-model.md
+++ b/src/site/markdown/design/digital-elevation-model.md
@@ -26,12 +26,13 @@ a nadir view), some algorithms are more suitable than others. This computation i
 programming unit possible in the Rugged library and an interface is defined with several different
 implementations among which user can select.
 
-Four different algorithms are predefined in Rugged:
+Five different algorithms are predefined in Rugged:
 
  * a recursive algorithm based on Bernardt Duvenhage's 2009 paper
    [Using An Implicit Min/Max KD-Tree for Doing Efficient Terrain Line of Sight Calculations](http://researchspace.csir.co.za/dspace/bitstream/10204/3041/1/Duvenhage_2009.pdf)
  * an alternate version of the Duvenhage algorithm using flat-body hypothesis,
  * a basic scan algorithm sequentially checking all pixels in the rectangular array defined by Digital Elevation Model entry and exit points,
+ * an algorithm that ignores the Digital Elevation Model and uses a constant elevation over the ellipsoid.
  * a no-operation algorithm that ignores the Digital Elevation Model and uses only the ellipsoid.
 
 It is expected that other algorithms like line-stepping (perhaps using Bresenham line algorithm) will be added afterwards.
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index df750dc7..8f9d7f45 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -22,6 +22,12 @@
   <body>
     <release version="1.0" date="TBD"
              description="TBD">
+      <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
+        instead of always using 0.0 elevation.
+        Implements feature #187.
+      </action>
       <action dev="luc" type="add" due-to="Espen Bjørntvedt">
         Added Norwegian translation of error messages.
       </action>
diff --git a/src/test/java/org/orekit/rugged/intersection/AbstractAlgorithmTest.java b/src/test/java/org/orekit/rugged/intersection/AbstractAlgorithmTest.java
index 51bbca8b..fcc8e22e 100644
--- a/src/test/java/org/orekit/rugged/intersection/AbstractAlgorithmTest.java
+++ b/src/test/java/org/orekit/rugged/intersection/AbstractAlgorithmTest.java
@@ -147,6 +147,7 @@ public abstract class AbstractAlgorithmTest {
 
         Vector3D      position = state.getPVCoordinates(earth.getBodyFrame()).getPosition();
         Vector3D      los      = groundP.subtract(position);
+        System.out.println(los.normalize());
         GeodeticPoint result   = algorithm.refineIntersection(earth, position, los,
                                                               algorithm.intersection(earth, position, los));
         checkIntersection(position, los, result);
diff --git a/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java b/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java
new file mode 100644
index 00000000..471180cf
--- /dev/null
+++ b/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java
@@ -0,0 +1,143 @@
+/* Copyright 2013-2015 CS Systèmes d'Information
+ * Licensed to CS Systèmes d'Information (CS) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * CS licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.orekit.rugged.intersection;
+
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.util.FastMath;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.orekit.attitudes.Attitude;
+import org.orekit.data.DataProvidersManager;
+import org.orekit.data.DirectoryCrawler;
+import org.orekit.errors.OrekitException;
+import org.orekit.frames.FramesFactory;
+import org.orekit.orbits.CartesianOrbit;
+import org.orekit.propagation.SpacecraftState;
+import org.orekit.rugged.errors.RuggedException;
+import org.orekit.rugged.intersection.duvenhage.DuvenhageAlgorithm;
+import org.orekit.rugged.raster.CheckedPatternElevationUpdater;
+import org.orekit.rugged.raster.TileUpdater;
+import org.orekit.rugged.utils.ExtendedEllipsoid;
+import org.orekit.rugged.utils.NormalizedGeodeticPoint;
+import org.orekit.time.AbsoluteDate;
+import org.orekit.time.TimeScalesFactory;
+import org.orekit.utils.Constants;
+import org.orekit.utils.IERSConventions;
+import org.orekit.utils.PVCoordinates;
+
+public class ConstantElevationAlgorithmTest {
+
+    @Test
+    public void testDuvenhageComparison() throws RuggedException {
+        final Vector3D los = new Vector3D(-0.626242839, 0.0124194184, -0.7795291301);
+        IntersectionAlgorithm duvenhage = new DuvenhageAlgorithm(new CheckedPatternElevationUpdater(FastMath.toRadians(1.0),
+                                                                                                    256, 150.0, 150.0),
+                                                                 8, false);
+        IntersectionAlgorithm constantElevation = new ConstantElevationAlgorithm(150.0);
+        NormalizedGeodeticPoint gpRef = duvenhage.intersection(earth, state.getPVCoordinates().getPosition(), los);
+        NormalizedGeodeticPoint gpConst = constantElevation.intersection(earth, state.getPVCoordinates().getPosition(), los);
+        Assert.assertEquals(gpRef.getLatitude(),  gpConst.getLatitude(),  1.0e-6);
+        Assert.assertEquals(gpRef.getLongitude(), gpConst.getLongitude(), 1.0e-6);
+        Assert.assertEquals(gpRef.getAltitude(),  gpConst.getAltitude(),  1.0e-3);
+        Assert.assertEquals(150.0,  constantElevation.getElevation(0.0, 0.0),  1.0e-3);
+
+        // shift longitude 2Ï€
+        NormalizedGeodeticPoint shifted =
+                constantElevation.refineIntersection(earth, state.getPVCoordinates().getPosition(), los,
+                                                     new NormalizedGeodeticPoint(gpConst.getLatitude(),
+                                                                                 gpConst.getLongitude(),
+                                                                                 gpConst.getAltitude(),
+                                                                                 2 * FastMath.PI));
+        Assert.assertEquals(2 * FastMath.PI + gpConst.getLongitude(), shifted.getLongitude(), 1.0e-6);
+
+    }
+
+    @Test
+    public void testIgnoreDEMComparison() throws RuggedException {
+        final Vector3D los = new Vector3D(-0.626242839, 0.0124194184, -0.7795291301);
+        IntersectionAlgorithm ignore = new IgnoreDEMAlgorithm();
+        IntersectionAlgorithm constantElevation = new ConstantElevationAlgorithm(0.0);
+        NormalizedGeodeticPoint gpRef = ignore.intersection(earth, state.getPVCoordinates().getPosition(), los);
+        NormalizedGeodeticPoint gpConst = constantElevation.intersection(earth, state.getPVCoordinates().getPosition(), los);
+        Assert.assertEquals(gpRef.getLatitude(),  gpConst.getLatitude(),  1.0e-6);
+        Assert.assertEquals(gpRef.getLongitude(), gpConst.getLongitude(), 1.0e-6);
+        Assert.assertEquals(gpRef.getAltitude(),  gpConst.getAltitude(),  1.0e-3);
+        Assert.assertEquals(0.0,  constantElevation.getElevation(0.0, 0.0),  1.0e-3);
+
+        // shift longitude 2Ï€
+        NormalizedGeodeticPoint shifted =
+                constantElevation.refineIntersection(earth, state.getPVCoordinates().getPosition(), los,
+                                                     new NormalizedGeodeticPoint(gpConst.getLatitude(),
+                                                                                 gpConst.getLongitude(),
+                                                                                 gpConst.getAltitude(),
+                                                                                 2 * FastMath.PI));
+        Assert.assertEquals(2 * FastMath.PI + gpConst.getLongitude(), shifted.getLongitude(), 1.0e-6);
+
+    }
+
+    @Before
+    public void setUp()
+            throws OrekitException, URISyntaxException {
+        String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath();
+        DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path)));
+        earth = new ExtendedEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
+                                      Constants.WGS84_EARTH_FLATTENING,
+                                      FramesFactory.getITRF(IERSConventions.IERS_2010, true));
+
+        AbsoluteDate crossing = new AbsoluteDate("2012-01-07T11:50:04.935272115", TimeScalesFactory.getUTC());
+        state = new SpacecraftState(new CartesianOrbit(new PVCoordinates(new Vector3D(  412324.544397459,
+                                                                                      -4325872.329311633,
+                                                                                       5692124.593989491),
+                                                                         new Vector3D(-1293.174701214779,
+                                                                                      -5900.764863603793,
+                                                                                      -4378.671036383179)),
+                                                       FramesFactory.getEME2000(),
+                                                       crossing,
+                                                       Constants.EIGEN5C_EARTH_MU),
+                                                       new Attitude(crossing,
+                                                                    FramesFactory.getEME2000(),
+                                                                    new Rotation(-0.17806699079182878,
+                                                                                  0.60143347387211290,
+                                                                                 -0.73251248177468900,
+                                                                                 -0.26456641385623986,
+                                                                                 true),
+                                                                    new Vector3D(-4.289600857433520e-05,
+                                                                                 -1.039151496480297e-03,
+                                                                                  5.811423736843181e-05),
+                                                                    Vector3D.ZERO));
+
+    }
+
+    @After
+    public void tearDown() {
+        earth     = null;
+        updater   = null;
+        state     = null;
+    }
+
+    protected ExtendedEllipsoid earth;
+    protected TileUpdater       updater;
+    protected SpacecraftState   state;
+
+}
-- 
GitLab