From 2008f523c8743e42b2849b886711bde1f7e6a72a Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Wed, 10 Sep 2014 11:23:46 +0200 Subject: [PATCH] Added a way to reuse transform interpolator from one run to another. This is done by dumping its state into a file, thus avoiding costly initialization. --- .../java/org/orekit/rugged/api/Rugged.java | 269 +++++++++++++++--- .../org/orekit/rugged/api/RuggedMessages.java | 4 +- .../utils/SpacecraftToObservedBody.java | 28 +- .../org/orekit/rugged/RuggedMessages_en.utf8 | 6 + .../org/orekit/rugged/RuggedMessages_fr.utf8 | 6 + .../orekit/rugged/api/RuggedMessagesTest.java | 2 +- .../org/orekit/rugged/api/RuggedTest.java | 210 ++++++++++++++ src/site/xdoc/changes.xml | 4 + 8 files changed, 490 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/org/orekit/rugged/api/Rugged.java b/core/src/main/java/org/orekit/rugged/api/Rugged.java index e617532f..5ef06400 100644 --- a/core/src/main/java/org/orekit/rugged/api/Rugged.java +++ b/core/src/main/java/org/orekit/rugged/api/Rugged.java @@ -16,6 +16,11 @@ */ package org.orekit.rugged.api; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -23,6 +28,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.math3.analysis.differentiation.DerivativeStructure; +import org.apache.commons.math3.exception.util.LocalizedFormats; import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D; import org.apache.commons.math3.geometry.euclidean.threed.Rotation; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; @@ -169,26 +175,12 @@ public class Rugged { final List<TimeStampedAngularCoordinates> quaternions, final int aInterpolationNumber, final AngularDerivativesFilter aFilter, final double tStep) throws RuggedException { - - // space reference - this.ellipsoid = extend(ellipsoid); - - // orbit/attitude to body converter - this.scToBody = new SpacecraftToObservedBody(inertialFrame, ellipsoid.getBodyFrame(), - minDate, maxDate, overshootTolerance, - positionsVelocities, pvInterpolationNumber, pvFilter, - quaternions, aInterpolationNumber, aFilter, - tStep); - - // intersection algorithm - this.algorithm = selectAlgorithm(algorithmID, updater, maxCachedTiles); - - this.sensors = new HashMap<String, LineSensor>(); - this.finders = new HashMap<String, SensorMeanPlaneCrossing>(); - - setLightTimeCorrection(true); - setAberrationOfLightCorrection(true); - + this(updater, maxCachedTiles, algorithmID, ellipsoid, + createInterpolator(inertialFrame, ellipsoid.getBodyFrame(), + minDate, maxDate, overshootTolerance, + positionsVelocities, pvInterpolationNumber, pvFilter, + quaternions, aInterpolationNumber, aFilter, + tStep)); } /** Build a configured instance. @@ -263,10 +255,166 @@ public class Rugged { final CartesianDerivativesFilter pvFilter, final AngularDerivativesFilter aFilter, final double tStep, final Propagator propagator) throws RuggedException { - try { + this(updater, maxCachedTiles, algorithmID, ellipsoid, + createInterpolator(inertialFrame, ellipsoid.getBodyFrame(), + minDate, maxDate, overshootTolerance, + interpolationStep, interpolationNumber, + pvFilter, aFilter, tStep, propagator)); + } - // space reference - this.ellipsoid = extend(ellipsoid); + /** Build a configured instance, reusing the interpolator dumped from a previous instance. + * <p> + * 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} + * and {@link #setAberrationOfLightCorrection(boolean) setAberrationOfLightCorrection} + * can be made after construction if these phenomena should not be corrected. + * </p> + * @param updater updater used to load Digital Elevation Model tiles + * @param maxCachedTiles maximum number of tiles stored in the cache + * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection + * @param ellipsoidID identifier of reference ellipsoid + * @param inertialFrameID identifier of inertial frame + * @param bodyRotatingFrameID identifier of body rotating frame + * @param dumpStream stream from where to read previous instance dumped interpolator + * (caller opened it and remains responsible for closing it) + * @exception RuggedException if dump file cannot be loaded + * or if frames do not match the ones referenced in the dump file + * @see #dumpInterpolator(OutputStream) + */ + public Rugged(final TileUpdater updater, final int maxCachedTiles, + final AlgorithmId algorithmID, final EllipsoidId ellipsoidID, + final InertialFrameId inertialFrameID, final BodyRotatingFrameId bodyRotatingFrameID, + final InputStream dumpStream) + throws RuggedException { + this(updater, maxCachedTiles, algorithmID, + selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID)), + selectInertialFrame(inertialFrameID), dumpStream); + } + + /** Build a configured instance, reusing the interpolator dumped from a previous instance. + * <p> + * 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} + * and {@link #setAberrationOfLightCorrection(boolean) setAberrationOfLightCorrection} + * can be made after construction if these phenomena should not be corrected. + * </p> + * @param updater updater used to load Digital Elevation Model tiles + * @param maxCachedTiles maximum number of tiles stored in the cache + * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection + * @param ellipsoid reference ellipsoid + * @param inertialFrame inertial frame + * @param dumpStream stream from where to read previous instance dumped interpolator + * (caller opened it and remains responsible for closing it) + * @exception RuggedException if dump file cannot be loaded + * or if frames do not match the ones referenced in the dump file + * @see #dumpInterpolator(OutputStream) + */ + public Rugged(final TileUpdater updater, final int maxCachedTiles, + final AlgorithmId algorithmID, final OneAxisEllipsoid ellipsoid, final Frame inertialFrame, + final InputStream dumpStream) + throws RuggedException { + this(updater, maxCachedTiles, algorithmID, ellipsoid, + createInterpolator(inertialFrame, ellipsoid.getBodyFrame(), dumpStream)); + } + + /** Build a configured instance. + * <p> + * 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} + * and {@link #setAberrationOfLightCorrection(boolean) setAberrationOfLightCorrection} + * can be made after construction if these phenomena should not be corrected. + * </p> + * @param updater updater used to load Digital Elevation Model tiles + * @param maxCachedTiles maximum number of tiles stored in the cache + * @param algorithmID identifier of algorithm to use for Digital Elevation Model intersection + * @param ellipsoid f reference ellipsoid + * @param scToBody transforms interpolator + */ + private Rugged(final TileUpdater updater, final int maxCachedTiles, + final AlgorithmId algorithmID, final OneAxisEllipsoid ellipsoid, + final SpacecraftToObservedBody scToBody) { + + // space reference + this.ellipsoid = extend(ellipsoid); + + // orbit/attitude to body converter + this.scToBody = scToBody; + + // intersection algorithm + this.algorithm = selectAlgorithm(algorithmID, updater, maxCachedTiles); + + this.sensors = new HashMap<String, LineSensor>(); + this.finders = new HashMap<String, SensorMeanPlaneCrossing>(); + + setLightTimeCorrection(true); + setAberrationOfLightCorrection(true); + + } + + /** Create a transform interpolator from positions and quaternions lists. + * @param inertialFrame inertial frame + * @param bodyFrame observed body frame + * @param minDate start of search time span + * @param maxDate end of search time span + * @param overshootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting + * @param positionsVelocities satellite position and velocity + * @param pvInterpolationNumber number of points to use for position/velocity interpolation + * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation + * @param quaternions satellite quaternions + * @param aInterpolationNumber number of points to use for attitude interpolation + * @param aFilter filter for derivatives from the sample to use in attitude interpolation + * @param tStep step to use for inertial frame to body frame transforms cache computations + * @return transforms interpolator + * @exception RuggedException if data needed for some frame cannot be loaded or if position + * or attitude samples do not fully cover the [{@code minDate}, {@code maxDate}] search time span + */ + private static SpacecraftToObservedBody createInterpolator(final Frame inertialFrame, final Frame bodyFrame, + final AbsoluteDate minDate, final AbsoluteDate maxDate, + final double overshootTolerance, + final List<TimeStampedPVCoordinates> positionsVelocities, + final int pvInterpolationNumber, + final CartesianDerivativesFilter pvFilter, + final List<TimeStampedAngularCoordinates> quaternions, + final int aInterpolationNumber, + final AngularDerivativesFilter aFilter, final double tStep) + throws RuggedException { + return new SpacecraftToObservedBody(inertialFrame, bodyFrame, + minDate, maxDate, overshootTolerance, + positionsVelocities, pvInterpolationNumber, pvFilter, + quaternions, aInterpolationNumber, aFilter, + tStep); + } + + /** Create a transform interpolator from a propagator. + * @param inertialFrame inertial frame + * @param bodyFrame observed body frame + * @param minDate start of search time span + * @param maxDate end of search time span + * @param overshootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting + * @param interpolationStep step to use for inertial/Earth/spacraft transforms interpolations + * @param interpolationNumber number of points of to use for inertial/Earth/spacraft transforms interpolations + * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation + * @param aFilter filter for derivatives from the sample to use in attitude interpolation + * @param tStep step to use for inertial frame to body frame transforms cache computations + * @param propagator global propagator + * @return transforms interpolator + * @exception RuggedException if data needed for some frame cannot be loaded + */ + private static SpacecraftToObservedBody createInterpolator(final Frame inertialFrame, final Frame bodyFrame, + final AbsoluteDate minDate, final AbsoluteDate maxDate, + final double overshootTolerance, + final double interpolationStep, final int interpolationNumber, + final CartesianDerivativesFilter pvFilter, + final AngularDerivativesFilter aFilter, + final double tStep, final Propagator propagator) + throws RuggedException { + try { // extract position/attitude samples from propagator final List<TimeStampedPVCoordinates> positionsVelocities = @@ -299,25 +447,74 @@ public class Rugged { propagator.propagate(minDate.shiftedBy(-interpolationStep), maxDate.shiftedBy(interpolationStep)); // orbit/attitude to body converter - this.scToBody = new SpacecraftToObservedBody(inertialFrame, ellipsoid.getBodyFrame(), - minDate, maxDate, overshootTolerance, - positionsVelocities, interpolationNumber, pvFilter, - quaternions, interpolationNumber, aFilter, - tStep); - - // intersection algorithm - this.algorithm = selectAlgorithm(algorithmID, updater, maxCachedTiles); + return createInterpolator(inertialFrame, bodyFrame, + minDate, maxDate, overshootTolerance, + positionsVelocities, interpolationNumber, pvFilter, + quaternions, interpolationNumber, aFilter, + tStep); - this.sensors = new HashMap<String, LineSensor>(); - this.finders = new HashMap<String, SensorMeanPlaneCrossing>(); - - setLightTimeCorrection(true); - setAberrationOfLightCorrection(true); } catch (PropagationException pe) { throw new RuggedException(pe, pe.getSpecifier(), pe.getParts()); } } + /** Create a transform interpolator from positions and quaternions lists. + * @param inertialFrame inertial frame + * @param bodyFrame observed body frame + * @param dumpStream stream from where to read previous instance dumped interpolator + * (caller opened it and remains responsible for closing it) + * @return recovered interpolator from dump + * @exception RuggedException if dump file cannot be loaded or if + * frames do not match the ones referenced in the dump file + */ + private static SpacecraftToObservedBody createInterpolator(final Frame inertialFrame, final Frame bodyFrame, + final InputStream dumpStream) + throws RuggedException { + try { + final ObjectInputStream ois = new ObjectInputStream(dumpStream); + final SpacecraftToObservedBody scToBody = (SpacecraftToObservedBody) ois.readObject(); + if (!inertialFrame.getName().equals(scToBody.getInertialFrameName())) { + throw new RuggedException(RuggedMessages.FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP, + inertialFrame.getName(), scToBody.getInertialFrameName()); + } + if (!bodyFrame.getName().equals(scToBody.getBodyFrameName())) { + throw new RuggedException(RuggedMessages.FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP, + bodyFrame.getName(), scToBody.getBodyFrameName()); + } + return scToBody; + } catch (ClassNotFoundException cnfe) { + throw new RuggedException(cnfe, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA); + } catch (ClassCastException cce) { + throw new RuggedException(cce, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA); + } catch (IOException ioe) { + throw new RuggedException(ioe, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA); + } + } + + /** Dump frames transform interpolator. + * <p> + * This method allows to reuse the interpolator built in one instance, to build + * another instance by calling {@link #Rugged(TileUpdater, int, AlgorithmId, OneAxisEllipsoid, Frame, InputStream)} + * or {@link #Rugged(TileUpdater, int, AlgorithmId, EllipsoidId, InertialFrameId, BodyRotatingFrameId, InputStream)}. + * This reduces Rugged initialization time as setting up the interpolator can be long, it is + * mainly intended to be used when several runs are done (for example in an image processing chain) + * with the same configuration. + * </p> + * @param dumpStream stream where to dump the interpolator + * (caller opened it and remains responsible for closing it) + * @exception RuggedException if interpolator cannot be written to file + * @see #Rugged(TileUpdater, int, AlgorithmId, OneAxisEllipsoid, Frame, File) + * @see #Rugged(TileUpdater, int, AlgorithmId, EllipsoidId, InertialFrameId, BodyRotatingFrameId, File) + */ + public void dumpInterpolator(final OutputStream dumpStream) throws RuggedException { + try { + final ObjectOutputStream oos = new ObjectOutputStream(dumpStream); + oos.writeObject(scToBody); + } catch (IOException ioe) { + throw new RuggedException(ioe, LocalizedFormats.SIMPLE_MESSAGE, ioe.getMessage()); + } + } + /** Set flag for light time correction. * <p> * This methods set the flag for compensating or not light time between diff --git a/core/src/main/java/org/orekit/rugged/api/RuggedMessages.java b/core/src/main/java/org/orekit/rugged/api/RuggedMessages.java index 9e3f9409..044571dd 100644 --- a/core/src/main/java/org/orekit/rugged/api/RuggedMessages.java +++ b/core/src/main/java/org/orekit/rugged/api/RuggedMessages.java @@ -60,7 +60,9 @@ public enum RuggedMessages implements Localizable { LINE_OF_SIGHT_NEVER_CROSSES_LATITUDE("line-of-sight never crosses latitude {0}"), LINE_OF_SIGHT_NEVER_CROSSES_LONGITUDE("line-of-sight never crosses longitude {0}"), LINE_OF_SIGHT_NEVER_CROSSES_ALTITUDE("line-of-sight never crosses altitude {0}"), - DEM_ENTRY_POINT_IS_BEHIND_SPACECRAFT("line-of-sight enters the Digital Elevation Model behind spacecraft!"); + DEM_ENTRY_POINT_IS_BEHIND_SPACECRAFT("line-of-sight enters the Digital Elevation Model behind spacecraft!"), + FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP("frame {0} does not match frame {1} from interpolator dump"), + NOT_INTERPOLATOR_DUMP_DATA("data is not an interpolator dump"); // CHECKSTYLE: resume JavadocVariable check diff --git a/core/src/main/java/org/orekit/rugged/utils/SpacecraftToObservedBody.java b/core/src/main/java/org/orekit/rugged/utils/SpacecraftToObservedBody.java index 2d8b74d0..895623cf 100644 --- a/core/src/main/java/org/orekit/rugged/utils/SpacecraftToObservedBody.java +++ b/core/src/main/java/org/orekit/rugged/utils/SpacecraftToObservedBody.java @@ -16,6 +16,7 @@ */ package org.orekit.rugged.utils; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -36,7 +37,16 @@ import org.orekit.utils.TimeStampedPVCoordinates; /** Provider for observation transforms. * @author Luc Maisonobe */ -public class SpacecraftToObservedBody { +public class SpacecraftToObservedBody implements Serializable { + + /** Serializable UID. */ + private static final long serialVersionUID = 20140909L; + + /** Name of the inertial frame. */ + private final String inertialFrameName; + + /** Name of the body frame. */ + private final String bodyFrameName; /** Start of search time span. */ private final AbsoluteDate minDate; @@ -87,6 +97,8 @@ public class SpacecraftToObservedBody { throws RuggedException { try { + this.inertialFrameName = inertialFrame.getName(); + this.bodyFrameName = bodyFrame.getName(); this.minDate = minDate; this.maxDate = maxDate; this.overshootTolerance = overshootTolerance; @@ -170,6 +182,20 @@ public class SpacecraftToObservedBody { } } + /** Get the name of the inertial frame. + * @return inertial frame name + */ + public String getInertialFrameName() { + return inertialFrameName; + } + + /** Get the name of the body frame. + * @return body frame name + */ + public String getBodyFrameName() { + return bodyFrameName; + } + /** Get the start of search time span. * @return start of search time span */ diff --git a/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_en.utf8 b/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_en.utf8 index e7e552e8..587c7eb1 100644 --- a/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_en.utf8 +++ b/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_en.utf8 @@ -30,3 +30,9 @@ LINE_OF_SIGHT_NEVER_CROSSES_ALTITUDE = line-of-sight never crosses altitude {0} # line-of-sight enters the Digital Elevation Model behind spacecraft! DEM_ENTRY_POINT_IS_BEHIND_SPACECRAFT = line-of-sight enters the Digital Elevation Model behind spacecraft! + +# frame {0} does not match frame {1} from interpolator dump +FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP = frame {0} does not match frame {1} from interpolator dump + +# data is not an interpolator dump +NOT_INTERPOLATOR_DUMP_DATA = data is not an interpolator dump diff --git a/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_fr.utf8 b/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_fr.utf8 index db99c921..825795b4 100644 --- a/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_fr.utf8 +++ b/core/src/main/resources/assets/org/orekit/rugged/RuggedMessages_fr.utf8 @@ -30,3 +30,9 @@ LINE_OF_SIGHT_NEVER_CROSSES_ALTITUDE = la ligne de visée ne franchit jamais l'' # line-of-sight enters the Digital Elevation Model behind spacecraft! DEM_ENTRY_POINT_IS_BEHIND_SPACECRAFT = la ligne de visée entre dans le Modèle Numérique de Terrain derrière le satellite ! + +# frame {0} does not match frame {1} from interpolator dump +FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP = le repère {0} ne correspond pas au repère {1} de la sauvegarde d''interpolateur + +# data is not an interpolator dump +NOT_INTERPOLATOR_DUMP_DATA = les données ne correspondent pas à une sauvegarde d''interpolateur diff --git a/core/src/test/java/org/orekit/rugged/api/RuggedMessagesTest.java b/core/src/test/java/org/orekit/rugged/api/RuggedMessagesTest.java index 0d96c5d1..abde2c08 100644 --- a/core/src/test/java/org/orekit/rugged/api/RuggedMessagesTest.java +++ b/core/src/test/java/org/orekit/rugged/api/RuggedMessagesTest.java @@ -29,7 +29,7 @@ public class RuggedMessagesTest { @Test public void testMessageNumber() { - Assert.assertEquals(11, RuggedMessages.values().length); + Assert.assertEquals(13, RuggedMessages.values().length); } @Test diff --git a/core/src/test/java/org/orekit/rugged/api/RuggedTest.java b/core/src/test/java/org/orekit/rugged/api/RuggedTest.java index bcfc5401..63bc4b18 100644 --- a/core/src/test/java/org/orekit/rugged/api/RuggedTest.java +++ b/core/src/test/java/org/orekit/rugged/api/RuggedTest.java @@ -17,9 +17,13 @@ package org.orekit.rugged.api; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.io.StreamCorruptedException; import java.net.URISyntaxException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -534,6 +538,212 @@ public class RuggedTest { } + @Test + public void testInterpolatorDump() + throws RuggedException, OrekitException, URISyntaxException { + + int dimension = 200; + + 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-01T12:30:00.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, looking at 50° roll, ±1° aperture + Vector3D position = new Vector3D(1.5, 0, -0.2); + List<Vector3D> los = createLOS(new Rotation(Vector3D.PLUS_I, FastMath.toRadians(50.0)).applyTo(Vector3D.PLUS_K), + Vector3D.PLUS_I, + FastMath.toRadians(1.0), dimension); + + // linear datation model: at reference time we get line 100, and the rate is one line every 1.5ms + LineDatation lineDatation = new LinearLineDatation(crossing, dimension / 2, 1.0 / 1.5e-3); + int firstLine = 0; + int lastLine = dimension; + LineSensor lineSensor = new LineSensor("line", lineDatation, position, los); + AbsoluteDate minDate = lineSensor.getDate(firstLine); + AbsoluteDate maxDate = lineSensor.getDate(lastLine); + + TileUpdater updater = + new RandomLandscapeUpdater(0.0, 9000.0, 0.5, 0x84186d1344722b8fl, + FastMath.toRadians(1.0), 257); + + Rugged ruggedOriginal = new Rugged(updater, 8, AlgorithmId.DUVENHAGE, + EllipsoidId.WGS84, InertialFrameId.EME2000, BodyRotatingFrameId.ITRF, + minDate, maxDate, 5.0, + orbitToPV(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 8, + CartesianDerivativesFilter.USE_PV, + orbitToQ(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 2, + AngularDerivativesFilter.USE_R, 0.001); + ruggedOriginal.addLineSensor(lineSensor); + GeodeticPoint[] gpOriginal = ruggedOriginal.directLocalization("line", 100); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ruggedOriginal.dumpInterpolator(bos); + Assert.assertTrue(bos.size() > 100000); + Assert.assertTrue(bos.size() < 200000); + + Rugged ruggedRecovered = new Rugged(updater, 8, AlgorithmId.DUVENHAGE, + EllipsoidId.WGS84, InertialFrameId.EME2000, BodyRotatingFrameId.ITRF, + new ByteArrayInputStream(bos.toByteArray())); + ruggedRecovered.addLineSensor(lineSensor); + GeodeticPoint[] gpRecovered = ruggedRecovered.directLocalization("line", 100); + + for (int i = 0; i < gpOriginal.length; ++i) { + Vector3D pOriginal = earth.transform(gpOriginal[i]); + Vector3D pRecovered = earth.transform(gpRecovered[i]); + Assert.assertEquals(0.0, Vector3D.distance(pOriginal, pRecovered), 1.0e-15); + } + + } + + @Test + public void testInterpolatorDumpWrongFrame() + throws RuggedException, OrekitException, URISyntaxException { + + int dimension = 200; + + 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-01T12:30:00.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, looking at 50° roll, ±1° aperture + Vector3D position = new Vector3D(1.5, 0, -0.2); + List<Vector3D> los = createLOS(new Rotation(Vector3D.PLUS_I, FastMath.toRadians(50.0)).applyTo(Vector3D.PLUS_K), + Vector3D.PLUS_I, + FastMath.toRadians(1.0), dimension); + + // linear datation model: at reference time we get line 100, and the rate is one line every 1.5ms + LineDatation lineDatation = new LinearLineDatation(crossing, dimension / 2, 1.0 / 1.5e-3); + int firstLine = 0; + int lastLine = dimension; + LineSensor lineSensor = new LineSensor("line", lineDatation, position, los); + AbsoluteDate minDate = lineSensor.getDate(firstLine); + AbsoluteDate maxDate = lineSensor.getDate(lastLine); + + Rugged ruggedOriginal = new Rugged(null, -1, AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, InertialFrameId.EME2000, BodyRotatingFrameId.ITRF, + minDate, maxDate, 5.0, + orbitToPV(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 8, + CartesianDerivativesFilter.USE_PV, + orbitToQ(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), 2, + AngularDerivativesFilter.USE_R, 0.001); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ruggedOriginal.dumpInterpolator(bos); + Assert.assertTrue(bos.size() > 100000); + Assert.assertTrue(bos.size() < 200000); + + for (InertialFrameId iId : Arrays.asList(InertialFrameId.GCRF, + InertialFrameId.MOD, + InertialFrameId.TOD, + InertialFrameId.VEIS1950)) { + try { + new Rugged(null, -1, AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, iId, BodyRotatingFrameId.ITRF, + new ByteArrayInputStream(bos.toByteArray())); + Assert.fail("an exception should have been thrown"); + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP, + re.getSpecifier()); + } + } + + for (BodyRotatingFrameId bId : Arrays.asList(BodyRotatingFrameId.GTOD, + BodyRotatingFrameId.ITRF_EQUINOX)) { + try { + new Rugged(null, -1, AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, InertialFrameId.EME2000, bId, + new ByteArrayInputStream(bos.toByteArray())); + Assert.fail("an exception should have been thrown"); + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP, + re.getSpecifier()); + } + } + } + + @Test + public void testInterpolatorNotADump() + throws RuggedException, OrekitException, URISyntaxException { + + String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); + DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); + + // the following array is a real serialization file corresponding to the following + // made-up empty class that does not exist in Rugged: + // public class NonExistentClass implements java.io.Serializable { + // private static final long serialVersionUID = -1; + // } + byte[] nonExistentClass = new byte[] { + -84, -19, 0, 5, 115, 114, + 0, 16, 78, 111, 110, 69, + 120, 105, 115, 116, 101, 110, + 116, 67, 108, 97, 115, 115, + -1, -1, -1, -1, -1, -1, + -1, -1, 2, 0, 0, 120, + 112 + }; + + // the following array is a real serialization file of object Integer.valueOf(1) + byte[] integerOne = new byte[] { + -84, -19, 0, 5, 115, 114, + 0, 17, 106, 97, 118, 97, + 46, 108, 97, 110, 103, 46, + 73, 110, 116, 101, 103, 101, + 114, 18, -30, -96, -92, -9, + -127,-121, 56, 2, 0, 1, + 73, 0, 5, 118, 97, 108, + 117, 101, 120, 114, 0, 16, + 106, 97, 118, 97, 46, 108, + 97, 110, 103, 46, 78, 117, + 109, 98, 101, 114,-122, -84, + -107, 29, 11,-108, -32,-117, + 2, 0, 0, 120, 112, 0, + 0, 0, 1 + }; + + // the following array is a truncation of the previous one + byte[] truncatedDump = new byte[] { + -84, -19, 0, 5, 115, 114, + 0, 17, 106, 97, 118, 97 + }; + + byte[] notSerialization = new byte[] { + 1, 2, 3, 4, 5, 6 + }; + + for (byte[] array : Arrays.asList(nonExistentClass, integerOne, truncatedDump, notSerialization)) { + try { + new Rugged(null, -1, AlgorithmId.IGNORE_DEM_USE_ELLIPSOID, + EllipsoidId.WGS84, InertialFrameId.EME2000, BodyRotatingFrameId.ITRF, + new ByteArrayInputStream(array)); + Assert.fail("an exception should have been thrown"); + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA, + re.getSpecifier()); + if (array == nonExistentClass) { + Assert.assertEquals(ClassNotFoundException.class, re.getCause().getClass()); + } else if (array == integerOne) { + Assert.assertEquals(ClassCastException.class, re.getCause().getClass()); + } else if (array == truncatedDump) { + Assert.assertEquals(EOFException.class, re.getCause().getClass()); + } else if (array == notSerialization) { + Assert.assertEquals(StreamCorruptedException.class, re.getCause().getClass()); + } + } + } + + } + // the following test is disabled by default // it is only used to check timings, and also creates a large (38M) temporary file @Test diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 5b656467..2751dc15 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -22,6 +22,10 @@ <body> <release version="1.0" date="TBD" description="TBD"> + <action dev="luc" type="add"> + Added a way to reuse transform interpolator from one run to another + by dumping its state into a file, thus avoiding costly initialization. + </action> <action dev="luc" type="add"> Time step for internal transform caching is now user-configurable. </action> -- GitLab