From dc2b799eb0404f23c2c07c40c597bbf2545cdcfa Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Wed, 23 Apr 2014 16:48:59 +0200 Subject: [PATCH] Added optional aberration of light correction. --- .../java/org/orekit/rugged/api/Rugged.java | 84 ++++++++-- .../org/orekit/rugged/api/RuggedTest.java | 147 ++++++++++++++---- 2 files changed, 187 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/orekit/rugged/api/Rugged.java b/src/main/java/org/orekit/rugged/api/Rugged.java index 2091f5c2..1fe99247 100644 --- a/src/main/java/org/orekit/rugged/api/Rugged.java +++ b/src/main/java/org/orekit/rugged/api/Rugged.java @@ -75,11 +75,17 @@ public class Rugged { /** Flag for light time correction. */ private boolean lightTimeCorrection; + /** Flag for aberration of light correction. */ + private boolean aberrationOfLightCorrection; + /** Build a configured instance. * <p> - * By default, the instance performs light time correction, an explicit call + * By default, the instance performs both light time correction (which refers + * to ground point motion with respect to inertial frame) and aberration of + * light correction (which refers to spacecraft proper velocity). Explicit calls * to {@link #setLightTimeCorrection(boolean) setLightTimeCorrection} - * can be made after construction if light time should not be corrected. + * and {@link #setAberrationOfLightCorrection(boolean) setAberrationOfLightCorrection} + * can be made after construction if these phenomena should not be corrected. * </p> * @param referenceDate reference date from which all other dates are computed * @param updater updater used to load Digital Elevation Model tiles @@ -121,6 +127,7 @@ public class Rugged { sensors = new HashMap<String, Sensor>(); setLightTimeCorrection(true); + setAberrationOfLightCorrection(true); } catch (OrekitException oe) { throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone()); @@ -129,9 +136,12 @@ public class Rugged { /** Build a configured instance. * <p> - * By default, the instance performs light time correction, an explicit call + * By default, the instance performs both light time correction (which refers + * to ground point motion with respect to inertial frame) and aberration of + * light correction (which refers to spacecraft proper velocity). Explicit calls * to {@link #setLightTimeCorrection(boolean) setLightTimeCorrection} - * can be made after construction if light time should not be corrected. + * and {@link #setAberrationOfLightCorrection(boolean) setAberrationOfLightCorrection} + * can be made after construction if these phenomena should not be corrected. * </p> * @param newReferenceDate reference date from which all other dates are computed * @param updater updater used to load Digital Elevation Model tiles @@ -167,6 +177,7 @@ public class Rugged { sensors = new HashMap<String, Sensor>(); setLightTimeCorrection(true); + setAberrationOfLightCorrection(true); } catch (OrekitException oe) { throw new RuggedException(oe, oe.getSpecifier(), oe.getParts().clone()); @@ -204,6 +215,35 @@ public class Rugged { return lightTimeCorrection; } + /** Set flag for aberration of light correction. + * <p> + * This methods set the flag for compensating or not aberration of light, + * which is velocity composition between light and spacecraft when the + * light from ground points reaches the sensor. + * Compensating this velocity composition improves localization + * accuracy and is enabled by default. Not compensating it is useful + * in two cases: for validation purposes against system that do not + * compensate it or when the pixels line of sight already include the + * correction. + * </p> + * @param aberrationOfLightCorrection if true, the aberration of light + * is corrected for more accurate localization + * @see #isAberrationOfLightCorrected() + * @see #setLineSensor(String, List, LineDatation) + */ + public void setAberrationOfLightCorrection(final boolean aberrationOfLightCorrection) { + this.aberrationOfLightCorrection = aberrationOfLightCorrection; + } + + /** Get flag for aberration of light correction. + * @return true if the aberration of light time is corrected + * for more accurate localization + * @see #setAberrationOfLightCorrection(boolean) + */ + public boolean isAberrationOfLightCorrected() { + return aberrationOfLightCorrection; + } + /** Set up line sensor model. * @param sensorName name of the line sensor. * @param linesOfSigth lines of sight for each pixels @@ -214,7 +254,7 @@ public class Rugged { final List<Vector3D> los = new ArrayList<Vector3D>(linesOfSigth.size()); for (final PixelLOS plos : linesOfSigth) { positions.add(new Vector3D(plos.getPx(), plos.getPy(), plos.getPz())); - los.add(new Vector3D(plos.getDx(), plos.getDy(), plos.getDz())); + los.add(new Vector3D(plos.getDx(), plos.getDy(), plos.getDz()).normalize()); } final Sensor sensor = new Sensor(sensorName, referenceDate, datationModel, positions, los); sensors.put(sensor.getName(), sensor); @@ -403,22 +443,40 @@ public class Rugged { final GeodeticPoint[] gp = new GeodeticPoint[sensor.getNbPixels()]; for (int i = 0; i < gp.length; ++i) { - final Transform fixed; + final Vector3D pInert = scToInert.transformPosition(sensor.getPosition(i)); + final Vector3D lInert; + if (aberrationOfLightCorrection) { + // apply aberration of light correction + // as the spacecraft velocity is small with respect to speed of light, + // we use classical velocity addition and not relativistic velocity addition + final Vector3D spacecraftVelocity = scToInert.transformPVCoordinates(PVCoordinates.ZERO).getVelocity(); + lInert = new Vector3D(Constants.SPEED_OF_LIGHT, scToInert.transformVector(sensor.getLos(i)), + 1.0, spacecraftVelocity).normalize(); + } else { + lInert = scToInert.transformVector(sensor.getLos(i)); + // don't apply aberration of light correction + } + + final Vector3D pBody; + final Vector3D lBody; if (lightTimeCorrection) { - // compensate light travel time + // apply light time correction final Vector3D sP = approximate.transformPosition(sensor.getPosition(i)); final Vector3D sL = approximate.transformVector(sensor.getLos(i)); final Vector3D eP = ellipsoid.transform(ellipsoid.pointOnGround(sP, sL)); final double deltaT = eP.distance(sP) / Constants.SPEED_OF_LIGHT; - fixed = new Transform(date, scToInert, inertToBody.shiftedBy(-deltaT)); + final Transform shifted = inertToBody.shiftedBy(-deltaT); + pBody = shifted.transformPosition(pInert); + lBody = shifted.transformVector(lInert); } else { - // don't compensate light travel time - fixed = new Transform(date, scToInert, inertToBody); + // don't apply light time correction + pBody = inertToBody.transformPosition(pInert); + lBody = inertToBody.transformVector(lInert); } - gp[i] = algorithm.intersection(ellipsoid, - fixed.transformPosition(sensor.getPosition(i)), - fixed.transformVector(sensor.getLos(i))); + // compute DEM intersection + gp[i] = algorithm.intersection(ellipsoid, pBody, lBody); + } return gp; diff --git a/src/test/java/org/orekit/rugged/api/RuggedTest.java b/src/test/java/org/orekit/rugged/api/RuggedTest.java index 2d326895..c9126563 100644 --- a/src/test/java/org/orekit/rugged/api/RuggedTest.java +++ b/src/test/java/org/orekit/rugged/api/RuggedTest.java @@ -59,17 +59,8 @@ import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngle; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; import org.orekit.propagation.numerical.NumericalPropagator; -import org.orekit.rugged.api.AlgorithmId; -import org.orekit.rugged.api.BodyRotatingFrameId; -import org.orekit.rugged.api.EllipsoidId; -import org.orekit.rugged.api.InertialFrameId; -import org.orekit.rugged.api.LineDatation; -import org.orekit.rugged.api.LinearLineDatation; -import org.orekit.rugged.api.PixelLOS; -import org.orekit.rugged.api.Rugged; -import org.orekit.rugged.api.RuggedException; -import org.orekit.rugged.core.raster.CliffsElevationUpdater; import org.orekit.rugged.core.raster.VolcanicConeElevationUpdater; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; @@ -155,6 +146,9 @@ public class RuggedTest { Assert.assertTrue(rugged.isLightTimeCorrected()); rugged.setLightTimeCorrection(false); Assert.assertFalse(rugged.isLightTimeCorrected()); + Assert.assertTrue(rugged.isAberrationOfLightCorrected()); + rugged.setAberrationOfLightCorrection(false); + Assert.assertFalse(rugged.isAberrationOfLightCorrected()); } @@ -166,7 +160,7 @@ public class RuggedTest { DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); BodyShape earth = createEarth(); NormalizedSphericalHarmonicsProvider gravityField = createGravityField(); - Orbit orbit = createOrbit(gravityField); + Orbit orbit = createOrbit(gravityField.getMu()); Propagator propagator = createPropagator(earth, gravityField, orbit); TileUpdater updater = @@ -185,6 +179,9 @@ public class RuggedTest { Assert.assertTrue(rugged.isLightTimeCorrected()); rugged.setLightTimeCorrection(false); Assert.assertFalse(rugged.isLightTimeCorrected()); + Assert.assertTrue(rugged.isAberrationOfLightCorrected()); + rugged.setAberrationOfLightCorrection(false); + Assert.assertFalse(rugged.isAberrationOfLightCorrected()); } @@ -199,7 +196,7 @@ public class RuggedTest { DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); BodyShape earth = createEarth(); NormalizedSphericalHarmonicsProvider gravityField = createGravityField(); - Orbit orbit = createOrbit(gravityField); + Orbit orbit = createOrbit(gravityField.getMu()); // Mayon Volcano location according to Wikipedia: 13°15′24″N 123°41′6″E GeodeticPoint summit = @@ -233,6 +230,8 @@ public class RuggedTest { InertialFrameId.EME2000, BodyRotatingFrameId.ITRF, ephemeris); + rugged.setLightTimeCorrection(true); + rugged.setAberrationOfLightCorrection(true); rugged.setLineSensor("line", los, lineDatation); @@ -275,30 +274,116 @@ public class RuggedTest { } @Test - public void testCliffsOfMoher() + public void testLightTimeCorrection() throws RuggedException, OrekitException, URISyntaxException { + int dimension = 400; + String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); - final BodyShape earth = createEarth(); - NormalizedSphericalHarmonicsProvider gravityField = createGravityField(); - Orbit orbit = createOrbit(gravityField); - Propagator propagator = createPropagator(earth, gravityField, orbit); + final BodyShape earth = createEarth(); + final Orbit orbit = createOrbit(Constants.EIGEN5C_EARTH_MU); + + AbsoluteDate crossing = new AbsoluteDate("2012-01-07T11:21:15.000", TimeScalesFactory.getUTC()); + + // one line sensor + // position: 1.5m in front (+X) and 20 cm above (-Z) of the S/C center of mass + // los: swath in the (YZ) plane, centered at +Z, ±10° aperture, 960 pixels + List<PixelLOS> los = createLOS(new Vector3D(1.5, 0, -0.2), Vector3D.PLUS_K, Vector3D.PLUS_I, + FastMath.toRadians(10.0), dimension); + + // linear datation model: at reference time we get line 200, and the rate is one line every 1.5ms + LineDatation lineDatation = new LinearLineDatation(dimension / 2, 1.0 / 1.5e-3); + int firstLine = 0; + int lastLine = dimension; + + Propagator propagator = new KeplerianPropagator(orbit); + propagator.setAttitudeProvider(new YawCompensation(new NadirPointing(earth))); + propagator.propagate(crossing.shiftedBy(lineDatation.getDate(firstLine) - 1.0)); + propagator.setEphemerisMode(); + propagator.propagate(crossing.shiftedBy(lineDatation.getDate(lastLine) + 1.0)); + Propagator ephemeris = propagator.getGeneratedEphemeris(); + + Rugged rugged = new Rugged(crossing, null, -1, + AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, + InertialFrameId.EME2000, + BodyRotatingFrameId.ITRF, + ephemeris); + + rugged.setLineSensor("line", los, lineDatation); + + rugged.setLightTimeCorrection(true); + rugged.setAberrationOfLightCorrection(false); + GeodeticPoint[] gpWithLightTimeCorrection = rugged.directLocalization("line", 200); + + rugged.setLightTimeCorrection(false); + rugged.setAberrationOfLightCorrection(false); + GeodeticPoint[] gpWithoutLightTimeCorrection = rugged.directLocalization("line", 200); + + for (int i = 0; i < gpWithLightTimeCorrection.length; ++i) { + Vector3D pWith = earth.transform(gpWithLightTimeCorrection[i]); + Vector3D pWithout = earth.transform(gpWithoutLightTimeCorrection[i]); + Assert.assertTrue(Vector3D.distance(pWith, pWithout) > 1.23); + Assert.assertTrue(Vector3D.distance(pWith, pWithout) < 1.27); + } + + } - // cliffs of Moher location according to Wikipedia: 52°56′10″N 9°28′15″ W - GeodeticPoint north = new GeodeticPoint(FastMath.toRadians(52.9984), - FastMath.toRadians(-9.4072), - 120.0); - GeodeticPoint south = new GeodeticPoint(FastMath.toRadians(52.9625), - FastMath.toRadians(-9.4369), - 120.0); - CliffsElevationUpdater updater = new CliffsElevationUpdater(north, south, - 120.0, 0.0, - FastMath.toRadians(1.0), 1201); + @Test + public void testAberrationOfLightCorrection() + throws RuggedException, OrekitException, URISyntaxException { + + int dimension = 400; + + String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); + DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); + final BodyShape earth = createEarth(); + final Orbit orbit = createOrbit(Constants.EIGEN5C_EARTH_MU); + + AbsoluteDate crossing = new AbsoluteDate("2012-01-07T11:46:35.000", TimeScalesFactory.getUTC()); + + // one line sensor + // position: 1.5m in front (+X) and 20 cm above (-Z) of the S/C center of mass + // los: swath in the (YZ) plane, centered at +Z, ±10° aperture, 960 pixels + List<PixelLOS> los = createLOS(new Vector3D(1.5, 0, -0.2), Vector3D.PLUS_K, Vector3D.PLUS_I, + FastMath.toRadians(10.0), dimension); + + // linear datation model: at reference time we get line 200, and the rate is one line every 1.5ms + LineDatation lineDatation = new LinearLineDatation(dimension / 2, 1.0 / 1.5e-3); + int firstLine = 0; + int lastLine = dimension; + + Propagator propagator = new KeplerianPropagator(orbit); + propagator.setAttitudeProvider(new YawCompensation(new NadirPointing(earth))); + propagator.propagate(crossing.shiftedBy(lineDatation.getDate(firstLine) - 1.0)); + propagator.setEphemerisMode(); + propagator.propagate(crossing.shiftedBy(lineDatation.getDate(lastLine) + 1.0)); + Propagator ephemeris = propagator.getGeneratedEphemeris(); + + Rugged rugged = new Rugged(crossing, null, -1, + AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, + InertialFrameId.EME2000, + BodyRotatingFrameId.ITRF, + ephemeris); + + rugged.setLineSensor("line", los, lineDatation); - AbsoluteDate crossing = new AbsoluteDate("2012-01-07T11:50:05.778", TimeScalesFactory.getUTC()); + rugged.setLightTimeCorrection(false); + rugged.setAberrationOfLightCorrection(true); + GeodeticPoint[] gpWithAberrationOfLightCorrection = rugged.directLocalization("line", 200); - // TODO: test the direct localization + rugged.setLightTimeCorrection(false); + rugged.setAberrationOfLightCorrection(false); + GeodeticPoint[] gpWithoutAberrationOfLightCorrection = rugged.directLocalization("line", 200); + + for (int i = 0; i < gpWithAberrationOfLightCorrection.length; ++i) { + Vector3D pWith = earth.transform(gpWithAberrationOfLightCorrection[i]); + Vector3D pWithout = earth.transform(gpWithoutAberrationOfLightCorrection[i]); + Assert.assertTrue(Vector3D.distance(pWith, pWithout) > 20.0); + Assert.assertTrue(Vector3D.distance(pWith, pWithout) < 20.5); + } } @@ -314,7 +399,7 @@ public class RuggedTest { return GravityFieldFactory.getNormalizedProvider(12, 12); } - private Orbit createOrbit(NormalizedSphericalHarmonicsProvider gravityField) + private Orbit createOrbit(double mu) throws OrekitException { // the following orbital parameters have been computed using // Orekit tutorial about phasing, using the following configuration: @@ -334,7 +419,7 @@ public class RuggedTest { FastMath.toRadians(98.63218182243709), FastMath.toRadians(77.55565567747836), FastMath.PI, PositionAngle.TRUE, - eme2000, date, gravityField.getMu()); + eme2000, date, mu); } private Propagator createPropagator(BodyShape earth, -- GitLab