From e0f1fa5be8470e43880a8771f13d183dd47aa315 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe <luc@orekit.org> Date: Tue, 23 Aug 2016 18:18:26 +0200 Subject: [PATCH] First working version of viewing model parameters estimation. --- .../java/org/orekit/rugged/api/Rugged.java | 177 +++++++++----- .../rugged/api/SensorToGroundMapping.java | 19 +- .../orekit/rugged/errors/DumpReplayer.java | 35 ++- .../orekit/rugged/linesensor/LineSensor.java | 13 +- .../org/orekit/rugged/los/FixedRotation.java | 28 +-- .../org/orekit/rugged/los/LOSBuilder.java | 32 ++- .../org/orekit/rugged/los/LOSTransform.java | 9 +- .../orekit/rugged/los/PolynomialRotation.java | 28 +-- .../orekit/rugged/los/TimeDependentLOS.java | 11 +- .../los/TimeIndependentLOSTransform.java | 9 +- .../org/orekit/rugged/utils/DSGenerator.java | 55 +++++ .../rugged/utils/ExtendedParameterDriver.java | 85 ------- .../org/orekit/rugged/api/RuggedTest.java | 226 +++++++++++++++--- 13 files changed, 452 insertions(+), 275 deletions(-) create mode 100644 src/main/java/org/orekit/rugged/utils/DSGenerator.java delete mode 100644 src/main/java/org/orekit/rugged/utils/ExtendedParameterDriver.java diff --git a/src/main/java/org/orekit/rugged/api/Rugged.java b/src/main/java/org/orekit/rugged/api/Rugged.java index 82063c61..8b273060 100644 --- a/src/main/java/org/orekit/rugged/api/Rugged.java +++ b/src/main/java/org/orekit/rugged/api/Rugged.java @@ -23,7 +23,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -35,6 +34,7 @@ import org.hipparchus.linear.RealVector; import org.hipparchus.optim.ConvergenceChecker; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem; import org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer; import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction; @@ -54,13 +54,14 @@ import org.orekit.rugged.linesensor.LineSensor; import org.orekit.rugged.linesensor.SensorMeanPlaneCrossing; import org.orekit.rugged.linesensor.SensorPixel; import org.orekit.rugged.linesensor.SensorPixelCrossing; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.rugged.utils.ExtendedEllipsoid; -import org.orekit.rugged.utils.ExtendedParameterDriver; import org.orekit.rugged.utils.NormalizedGeodeticPoint; import org.orekit.rugged.utils.SpacecraftToObservedBody; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; /** Main class of Rugged library API. * @see RuggedBuilder @@ -630,6 +631,7 @@ public class Rugged { * @param point point to localize * @param minLine minimum line number * @param maxLine maximum line number + * @param generator generator to use for building {@link DerivativeStructure} instances * @return sensor pixel seeing point with derivatives, or null if point cannot be seen between the * prescribed line numbers * @exception RuggedException if line cannot be localized, or sensor is unknown @@ -638,7 +640,8 @@ public class Rugged { private DerivativeStructure[] inverseLocationDerivatives(final String sensorName, final GeodeticPoint point, final int minLine, - final int maxLine) + final int maxLine, + final DSGenerator generator) throws RuggedException { final LineSensor sensor = getLineSensor(sensorName); @@ -668,8 +671,8 @@ public class Rugged { // (this pixel might point towards a direction slightly above or below the mean sensor plane) final int lowIndex = FastMath.max(0, FastMath.min(sensor.getNbPixels() - 2, (int) FastMath.floor(coarsePixel))); final FieldVector3D<DerivativeStructure> lowLOS = - sensor.getLOSDerivatives(crossingResult.getDate(), lowIndex); - final FieldVector3D<DerivativeStructure> highLOS = sensor.getLOSDerivatives(crossingResult.getDate(), lowIndex + 1); + sensor.getLOSDerivatives(crossingResult.getDate(), lowIndex, generator); + final FieldVector3D<DerivativeStructure> highLOS = sensor.getLOSDerivatives(crossingResult.getDate(), lowIndex + 1, generator); final FieldVector3D<DerivativeStructure> localZ = FieldVector3D.crossProduct(lowLOS, highLOS).normalize(); final DerivativeStructure beta = FieldVector3D.dotProduct(crossingResult.getTargetDirection(), localZ).acos(); final DerivativeStructure s = FieldVector3D.dotProduct(crossingResult.getTargetDirectionDerivative(), localZ); @@ -682,8 +685,8 @@ public class Rugged { // fix neighbouring pixels final AbsoluteDate fixedDate = sensor.getDate(fixedLine.getValue()); - final FieldVector3D<DerivativeStructure> fixedX = sensor.getLOSDerivatives(fixedDate, lowIndex); - final FieldVector3D<DerivativeStructure> fixedZ = FieldVector3D.crossProduct(fixedX, sensor.getLOSDerivatives(fixedDate, lowIndex + 1)); + final FieldVector3D<DerivativeStructure> fixedX = sensor.getLOSDerivatives(fixedDate, lowIndex, generator); + final FieldVector3D<DerivativeStructure> fixedZ = FieldVector3D.crossProduct(fixedX, sensor.getLOSDerivatives(fixedDate, lowIndex + 1, generator)); final FieldVector3D<DerivativeStructure> fixedY = FieldVector3D.crossProduct(fixedZ, fixedX); // fix pixel @@ -708,15 +711,15 @@ public class Rugged { * like rotation angles polynomial coefficients. * </p> * <p> - * Before using this method, the {@link ExtendedParameterDriver viewing model + * Before using this method, the {@link ParameterDriver viewing model * parameters} retrieved by calling the {@link * LineSensor#getExtendedParametersDrivers() getExtendedParametersDrivers()} * method on the desired sensors must be configured. The parameters that should - * be estimated must have their {@link ExtendedParameterDriver#setSelected(boolean) + * be estimated must have their {@link ParameterDriver#setSelected(boolean) * selection status} set to {@link true} whereas the parameters that should retain - * their current value must have their {@link ExtendedParameterDriver#setSelected(boolean) + * their current value must have their {@link ParameterDriver#setSelected(boolean) * selection status} set to {@link false}. If needed, the {@link - * ExtendedParameterDriver#setValue(double) value} of the estimated/selected parameters + * ParameterDriver#setValue(double) value} of the estimated/selected parameters * can also be changed before calling the method, as this value will serve as the * initial value in the estimation process. * </p> @@ -739,52 +742,27 @@ public class Rugged { * @param maxEvaluations maximum number of evaluations * @param parametersConvergenceThreshold convergence threshold on * normalized parameters (dimensionless, related to parameters scales) - * @return a list of the estimated parameters (this return value is for convenience - * only, the viewing model is already aware of the estimated values, there is no need - * to do any updating) + * @return optimum of the least squares problem * @exception RuggedException if several parameters with the same name exist, * or if parameters cannot be estimated (too few measurements, ill-conditioned problem ...) */ - public List<ExtendedParameterDriver> estimateFreeParameters(final Collection<SensorToGroundMapping> references, - final int maxEvaluations, - final double parametersConvergenceThreshold) + public Optimum estimateFreeParameters(final Collection<SensorToGroundMapping> references, + final int maxEvaluations, + final double parametersConvergenceThreshold) throws RuggedException { try { - // we are more stringent than Orekit orbit determination: - // we do not allow different parameters with the same name - final Set<String> names = new HashSet<>(); + final List<LineSensor> selectedSensors = new ArrayList<>(); for (final SensorToGroundMapping reference : references) { - reference.getSensor().getExtendedParametersDrivers().forEach(driver -> { - if (names.contains(driver.getName())) { - throw new RuggedExceptionWrapper(new RuggedException(RuggedMessages.DUPLICATED_PARAMETER_NAME, - driver.getName())); - } - }); - } - - // gather free parameters from all reference mappings - final List<ExtendedParameterDriver> freeParameters = new ArrayList<>(); - for (final SensorToGroundMapping reference : references) { - freeParameters.addAll(reference. - getSensor(). - getExtendedParametersDrivers(). - filter(driver -> driver.isSelected()). - collect(Collectors.toList())); - } - - // set up the indices and number of estimated parameters, - // so DerivativeStructure instances with the proper characteristics can be built - int index = 0; - for (final ExtendedParameterDriver driver : freeParameters) { - driver.setNbEstimated(freeParameters.size()); - driver.setIndex(index++); + selectedSensors.add(getLineSensor(reference.getSensorName())); } + final DSGenerator generator = createGenerator(selectedSensors); + final List<ParameterDriver> selected = generator.getSelected(); // get start point (as a normalized value) - final double[] start = new double[freeParameters.size()]; + final double[] start = new double[selected.size()]; for (int i = 0; i < start.length; ++i) { - start[i] = freeParameters.get(i).getNormalizedValue(); + start[i] = selected.get(i).getNormalizedValue(); } // set up target in sensor domain @@ -812,7 +790,7 @@ public class Rugged { final ParameterValidator validator = params -> { try { int i = 0; - for (final ExtendedParameterDriver driver : freeParameters) { + for (final ParameterDriver driver : selected) { // let the parameter handle min/max clipping driver.setNormalizedValue(params.getEntry(i)); params.setEntry(i++, driver.getNormalizedValue()); @@ -834,32 +812,38 @@ public class Rugged { // set the current parameters values int i = 0; - for (final ExtendedParameterDriver driver : freeParameters) { + for (final ParameterDriver driver : selected) { driver.setNormalizedValue(point.getEntry(i++)); } // compute inverse loc and its partial derivatives final RealVector value = new ArrayRealVector(target.length); - final RealMatrix jacobian = new Array2DRowRealMatrix(target.length, freeParameters.size()); + final RealMatrix jacobian = new Array2DRowRealMatrix(target.length, selected.size()); int l = 0; for (final SensorToGroundMapping reference : references) { for (final Map.Entry<SensorPixel, GeodeticPoint> mapping : reference.getMappings()) { final GeodeticPoint gp = mapping.getValue(); final DerivativeStructure[] ilResult = - inverseLocationDerivatives(reference.getSensor().getName(), - gp, minLine, maxLine); - - // extract the value - value.setEntry(l, ilResult[0].getValue()); - value.setEntry(l + 1, ilResult[1].getValue()); - - // extract the Jacobian - final int[] orders = new int[freeParameters.size()]; - for (int m = 0; m < freeParameters.size(); ++m) { - orders[m] = 1; - jacobian.setEntry(l, m, ilResult[0].getPartialDerivative(orders)); - jacobian.setEntry(l + 1, m, ilResult[1].getPartialDerivative(orders)); - orders[m] = 0; + inverseLocationDerivatives(reference.getSensorName(), + gp, minLine, maxLine, generator); + + if (ilResult == null) { + value.setEntry(l, minLine - 100.0); // arbitrary line far away + value.setEntry(l + 1, -100.0); // arbitrary pixel far away + } else { + // extract the value + value.setEntry(l, ilResult[0].getValue()); + value.setEntry(l + 1, ilResult[1].getValue()); + + // extract the Jacobian + final int[] orders = new int[selected.size()]; + for (int m = 0; m < selected.size(); ++m) { + final double scale = selected.get(m).getScale(); + orders[m] = 1; + jacobian.setEntry(l, m, ilResult[0].getPartialDerivative(orders) * scale); + jacobian.setEntry(l + 1, m, ilResult[1].getPartialDerivative(orders) * scale); + orders[m] = 0; + } } l += 2; @@ -880,6 +864,8 @@ public class Rugged { // set up the least squares problem final LeastSquaresProblem problem = new LeastSquaresBuilder(). lazyEvaluation(false). + maxIterations(maxEvaluations). + maxEvaluations(maxEvaluations). weight(null). start(start). target(target). @@ -892,9 +878,7 @@ public class Rugged { final LeastSquaresOptimizer optimizer = new LevenbergMarquardtOptimizer(); // solve the least squares problem - optimizer.optimize(problem); - - return freeParameters; + return optimizer.optimize(problem); } catch (RuggedExceptionWrapper rew) { throw rew.getException(); @@ -904,6 +888,67 @@ public class Rugged { } } + /** Create the generator for {@link DerivativeStructure} instances. + * @param selectedSensors sensors referencing the parameters drivers + * @return a new generator + * @exception RuggedException if several parameters with the same name exist + */ + private DSGenerator createGenerator(final List<LineSensor> selectedSensors) + throws RuggedException { + + // we are more stringent than Orekit orbit determination: + // we do not allow different parameters with the same name + final Set<String> names = new HashSet<>(); + for (final LineSensor sensor : selectedSensors) { + sensor.getParametersDrivers().forEach(driver -> { + if (names.contains(driver.getName())) { + throw new RuggedExceptionWrapper(new RuggedException(RuggedMessages.DUPLICATED_PARAMETER_NAME, + driver.getName())); + } + }); + } + + // set up generator list and map + final List<ParameterDriver> selected = new ArrayList<>(); + final Map<String, Integer> map = new HashMap<>(); + for (final LineSensor sensor : selectedSensors) { + sensor. + getParametersDrivers(). + filter(driver -> driver.isSelected()). + forEach(driver -> { + selected.add(driver); + map.put(driver.getName(), map.size()); + }); + } + + return new DSGenerator() { + + /** {@inheritDoc} */ + @Override + public List<ParameterDriver> getSelected() { + return selected; + } + + /** {@inheritDoc} */ + @Override + public DerivativeStructure constant(final double value) { + return new DerivativeStructure(map.size(), 1, value); + } + + /** {@inheritDoc} */ + @Override + public DerivativeStructure variable(final ParameterDriver driver) { + final Integer index = map.get(driver.getName()); + if (index == null) { + return constant(driver.getValue()); + } else { + return new DerivativeStructure(map.size(), 1, index.intValue(), driver.getValue()); + } + } + + }; + } + /** Get transform from spacecraft to inertial frame. * @param date date of the transform * @return transform from spacecraft to inertial frame diff --git a/src/main/java/org/orekit/rugged/api/SensorToGroundMapping.java b/src/main/java/org/orekit/rugged/api/SensorToGroundMapping.java index 3de6f880..fea1ff25 100644 --- a/src/main/java/org/orekit/rugged/api/SensorToGroundMapping.java +++ b/src/main/java/org/orekit/rugged/api/SensorToGroundMapping.java @@ -22,7 +22,6 @@ import java.util.Map; import java.util.Set; import org.orekit.bodies.GeodeticPoint; -import org.orekit.rugged.linesensor.LineSensor; import org.orekit.rugged.linesensor.SensorPixel; /** Container for mapping between sensor pixels and ground points. @@ -31,8 +30,8 @@ import org.orekit.rugged.linesensor.SensorPixel; */ public class SensorToGroundMapping { - /** Sensor to which mapping applies. */ - private final LineSensor sensor; + /** Name of the sensor to which mapping applies. */ + private final String sensorName; /** Mapping from sensor to ground. */ private final Map<SensorPixel, GeodeticPoint> sensorToGround; @@ -41,19 +40,19 @@ public class SensorToGroundMapping { private final Map<GeodeticPoint, SensorPixel> groundToSensor; /** Build a new instance. - * @param sensor sensor to which mapping applies + * @param sensorName name of the sensor to which mapping applies */ - public SensorToGroundMapping(final LineSensor sensor) { - this.sensor = sensor; + public SensorToGroundMapping(final String sensorName) { + this.sensorName = sensorName; this.sensorToGround = new IdentityHashMap<>(); this.groundToSensor = new IdentityHashMap<>(); } - /** Get the sensor to which mapping applies. - * @return sensor to which mapping applies + /** Get the name of the sensor to which mapping applies. + * @return name of the sensor to which mapping applies */ - public LineSensor getSensor() { - return sensor; + public String getSensorName() { + return sensorName; } /** Add a mapping between one sensor pixel and one ground point. diff --git a/src/main/java/org/orekit/rugged/errors/DumpReplayer.java b/src/main/java/org/orekit/rugged/errors/DumpReplayer.java index 40f6c2d4..57dc7516 100644 --- a/src/main/java/org/orekit/rugged/errors/DumpReplayer.java +++ b/src/main/java/org/orekit/rugged/errors/DumpReplayer.java @@ -16,14 +16,6 @@ */ package org.orekit.rugged.errors; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.exception.LocalizedCoreFormats; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.OpenIntToDoubleHashMap; -import org.hipparchus.util.Pair; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -40,10 +32,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; -import java.util.Optional; import java.util.TreeMap; import java.util.stream.Stream; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.OpenIntToDoubleHashMap; +import org.hipparchus.util.Pair; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; @@ -62,10 +61,11 @@ import org.orekit.rugged.linesensor.SensorPixel; import org.orekit.rugged.los.TimeDependentLOS; import org.orekit.rugged.raster.TileUpdater; import org.orekit.rugged.raster.UpdatableTile; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.rugged.utils.SpacecraftToObservedBody; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.ParameterDriver; /** Replayer for Rugged debug dumps. * @author Luc Maisonobe @@ -1209,13 +1209,12 @@ public class DumpReplayer { /** {@inheritDoc} */ @Override - public FieldVector3D<DerivativeStructure> getLOSDerivatives(final int index, final AbsoluteDate date) { - final Optional<ExtendedParameterDriver> first = getExtendedParametersDrivers().findFirst(); - final int nbEstimated = first.isPresent() ? first.get().getNbEstimated() : 0; + public FieldVector3D<DerivativeStructure> getLOSDerivatives(final int index, final AbsoluteDate date, + final DSGenerator generator) { final Vector3D los = getLOS(index, date); - return new FieldVector3D<DerivativeStructure>(new DerivativeStructure(nbEstimated, 1, los.getX()), - new DerivativeStructure(nbEstimated, 1, los.getY()), - new DerivativeStructure(nbEstimated, 1, los.getZ())); + return new FieldVector3D<DerivativeStructure>(generator.constant(los.getX()), + generator.constant(los.getY()), + generator.constant(los.getZ())); } /** Set a datation pair. @@ -1333,8 +1332,8 @@ public class DumpReplayer { /** {@inheritDoc} */ @Override - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { - return Stream.<ExtendedParameterDriver>empty(); + public Stream<ParameterDriver> getParametersDrivers() { + return Stream.<ParameterDriver>empty(); } } diff --git a/src/main/java/org/orekit/rugged/linesensor/LineSensor.java b/src/main/java/org/orekit/rugged/linesensor/LineSensor.java index 5705184f..cf86debd 100644 --- a/src/main/java/org/orekit/rugged/linesensor/LineSensor.java +++ b/src/main/java/org/orekit/rugged/linesensor/LineSensor.java @@ -24,8 +24,9 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.rugged.errors.DumpManager; import org.orekit.rugged.errors.RuggedException; import org.orekit.rugged.los.TimeDependentLOS; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; /** Line sensor model. * @author Luc Maisonobe @@ -79,8 +80,8 @@ public class LineSensor { * @return drivers for LOS parameters * @since 2.0 */ - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { - return los.getExtendedParametersDrivers(); + public Stream<ParameterDriver> getParametersDrivers() { + return los.getParametersDrivers(); } /** Get the pixel normalized line-of-sight at some date. @@ -100,10 +101,12 @@ public class LineSensor { * and their derivatives with respect to estimated parameters. * @param date current date * @param i pixel index (must be between 0 and {@link #getNbPixels()} - 1 + * @param generator generator to use for building {@link DerivativeStructure} instances * @return pixel normalized line-of-sight */ - public FieldVector3D<DerivativeStructure> getLOSDerivatives(final AbsoluteDate date, final int i) { - return los.getLOSDerivatives(i, date); + public FieldVector3D<DerivativeStructure> getLOSDerivatives(final AbsoluteDate date, final int i, + final DSGenerator generator) { + return los.getLOSDerivatives(i, date, generator); } /** Get the date. diff --git a/src/main/java/org/orekit/rugged/los/FixedRotation.java b/src/main/java/org/orekit/rugged/los/FixedRotation.java index 8f6aba22..3638aee9 100644 --- a/src/main/java/org/orekit/rugged/los/FixedRotation.java +++ b/src/main/java/org/orekit/rugged/los/FixedRotation.java @@ -27,7 +27,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.rugged.errors.RuggedException; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; @@ -55,7 +55,7 @@ public class FixedRotation implements TimeIndependentLOSTransform { private FieldRotation<DerivativeStructure> rDS; /** Driver for rotation angle. */ - private final ExtendedParameterDriver angleDriver; + private final ParameterDriver angleDriver; /** Simple constructor. * <p> @@ -70,7 +70,7 @@ public class FixedRotation implements TimeIndependentLOSTransform { this.rotation = null; this.rDS = null; try { - this.angleDriver = new ExtendedParameterDriver(name, angle, SCALE, -2 * FastMath.PI, 2 * FastMath.PI); + this.angleDriver = new ParameterDriver(name, angle, SCALE, -2 * FastMath.PI, 2 * FastMath.PI); angleDriver.addObserver(new ParameterObserver() { @Override public void valueChanged(final double previousValue, final ParameterDriver driver) { @@ -87,7 +87,7 @@ public class FixedRotation implements TimeIndependentLOSTransform { /** {@inheritDoc} */ @Override - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { + public Stream<ParameterDriver> getParametersDrivers() { return Stream.of(angleDriver); } @@ -103,23 +103,15 @@ public class FixedRotation implements TimeIndependentLOSTransform { /** {@inheritDoc} */ @Override - public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los) { + public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los, + final DSGenerator generator) { if (rDS == null) { // lazy evaluation of the rotation - final int nbEstimated = angleDriver.getNbEstimated(); final FieldVector3D<DerivativeStructure> axisDS = - new FieldVector3D<DerivativeStructure>(new DerivativeStructure(nbEstimated, 1, axis.getX()), - new DerivativeStructure(nbEstimated, 1, axis.getY()), - new DerivativeStructure(nbEstimated, 1, axis.getZ())); - final double value = angleDriver.getValue(); - final DerivativeStructure angleDS; - if (angleDriver.isSelected()) { - // the angle is estimated - angleDS = new DerivativeStructure(nbEstimated, 1, angleDriver.getIndex(), value); - } else { - // the angle is not estimated - angleDS = new DerivativeStructure(nbEstimated, 1, value); - } + new FieldVector3D<DerivativeStructure>(generator.constant(axis.getX()), + generator.constant(axis.getY()), + generator.constant(axis.getZ())); + final DerivativeStructure angleDS = generator.variable(angleDriver); rDS = new FieldRotation<DerivativeStructure>(axisDS, angleDS, RotationConvention.VECTOR_OPERATOR); } return rDS.applyTo(los); diff --git a/src/main/java/org/orekit/rugged/los/LOSBuilder.java b/src/main/java/org/orekit/rugged/los/LOSBuilder.java index c61be99a..4857bd58 100644 --- a/src/main/java/org/orekit/rugged/los/LOSBuilder.java +++ b/src/main/java/org/orekit/rugged/los/LOSBuilder.java @@ -19,7 +19,6 @@ package org.orekit.rugged.los; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.DerivativeStructure; @@ -27,7 +26,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitException; import org.orekit.rugged.errors.RuggedException; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; @@ -126,14 +125,14 @@ public class LOSBuilder { /** {@inheritDoc} */ @Override public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los, - final AbsoluteDate date) { - return transform.transformLOS(i, los); + final AbsoluteDate date, final DSGenerator generator) { + return transform.transformLOS(i, los, generator); } /** {@inheritDoc} */ @Override - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { - return transform.getExtendedParametersDrivers(); + public Stream<ParameterDriver> getParametersDrivers() { + return transform.getParametersDrivers(); } } @@ -182,19 +181,18 @@ public class LOSBuilder { /** {@inheritDoc} */ @Override - public FieldVector3D<DerivativeStructure> getLOSDerivatives(final int index, final AbsoluteDate date) { + public FieldVector3D<DerivativeStructure> getLOSDerivatives(final int index, final AbsoluteDate date, + final DSGenerator generator) { // the raw line of sights are considered to be constant - final Optional<ExtendedParameterDriver> first = getExtendedParametersDrivers().findFirst(); - final int nbEstimated = first.isPresent() ? first.get().getNbEstimated() : 0; FieldVector3D<DerivativeStructure> los = - new FieldVector3D<DerivativeStructure>(new DerivativeStructure(nbEstimated, 1, raw[index].getX()), - new DerivativeStructure(nbEstimated, 1, raw[index].getY()), - new DerivativeStructure(nbEstimated, 1, raw[index].getZ())); + new FieldVector3D<DerivativeStructure>(generator.constant(raw[index].getX()), + generator.constant(raw[index].getY()), + generator.constant(raw[index].getZ())); // apply the transforms, which depend on parameters and hence may introduce non-zero derivatives for (final LOSTransform transform : transforms) { - los = transform.transformLOS(index, los, date); + los = transform.transformLOS(index, los, date, generator); } return los.normalize(); @@ -202,10 +200,10 @@ public class LOSBuilder { } @Override - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { - Stream<ExtendedParameterDriver> drivers = Stream.<ExtendedParameterDriver>empty(); + public Stream<ParameterDriver> getParametersDrivers() { + Stream<ParameterDriver> drivers = Stream.<ParameterDriver>empty(); for (final LOSTransform transform : transforms) { - drivers = Stream.concat(drivers, transform.getExtendedParametersDrivers()); + drivers = Stream.concat(drivers, transform.getParametersDrivers()); } return drivers; } @@ -235,7 +233,7 @@ public class LOSBuilder { Arrays.fill(transformed, null); } }; - getExtendedParametersDrivers().forEach(driver -> { + getParametersDrivers().forEach(driver -> { try { driver.addObserver(resettingObserver); } catch (OrekitException oe) { diff --git a/src/main/java/org/orekit/rugged/los/LOSTransform.java b/src/main/java/org/orekit/rugged/los/LOSTransform.java index b54f6289..1e894b08 100644 --- a/src/main/java/org/orekit/rugged/los/LOSTransform.java +++ b/src/main/java/org/orekit/rugged/los/LOSTransform.java @@ -21,8 +21,9 @@ import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; /** Interface for lines-of-sight tranforms. * @author Luc Maisonobe @@ -48,14 +49,16 @@ public interface LOSTransform { * @param index los pixel index * @param date date * @param los line-of-sight to transform + * @param generator generator to use for building {@link DerivativeStructure} instances * @return line of sight, and its first partial derivatives with respect to the parameters */ - FieldVector3D<DerivativeStructure> transformLOS(int index, FieldVector3D<DerivativeStructure> los, AbsoluteDate date); + FieldVector3D<DerivativeStructure> transformLOS(int index, FieldVector3D<DerivativeStructure> los, + AbsoluteDate date, DSGenerator generator); /** Get the drivers for LOS parameters. * @return drivers for LOS parameters * @since 2.0 */ - Stream<ExtendedParameterDriver> getExtendedParametersDrivers(); + Stream<ParameterDriver> getParametersDrivers(); } diff --git a/src/main/java/org/orekit/rugged/los/PolynomialRotation.java b/src/main/java/org/orekit/rugged/los/PolynomialRotation.java index 49df76a8..158c6dce 100644 --- a/src/main/java/org/orekit/rugged/los/PolynomialRotation.java +++ b/src/main/java/org/orekit/rugged/los/PolynomialRotation.java @@ -28,7 +28,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.rugged.errors.RuggedException; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterObserver; @@ -63,7 +63,7 @@ public class PolynomialRotation implements LOSTransform { private final AbsoluteDate referenceDate; /** Drivers for rotation angle polynomial coefficients. */ - private final ExtendedParameterDriver[] coefficientsDrivers; + private final ParameterDriver[] coefficientsDrivers; /** Simple constructor. * <p> @@ -84,7 +84,7 @@ public class PolynomialRotation implements LOSTransform { final double ... angleCoeffs) { this.axis = axis; this.referenceDate = referenceDate; - this.coefficientsDrivers = new ExtendedParameterDriver[angleCoeffs.length]; + this.coefficientsDrivers = new ParameterDriver[angleCoeffs.length]; final ParameterObserver resettingObserver = new ParameterObserver() { @Override public void valueChanged(final double previousValue, final ParameterDriver driver) { @@ -96,7 +96,7 @@ public class PolynomialRotation implements LOSTransform { }; try { for (int i = 0; i < angleCoeffs.length; ++i) { - coefficientsDrivers[i] = new ExtendedParameterDriver(name + "[" + i + "]", angleCoeffs[i], SCALE, + coefficientsDrivers[i] = new ParameterDriver(name + "[" + i + "]", angleCoeffs[i], SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); coefficientsDrivers[i].addObserver(resettingObserver); } @@ -127,7 +127,7 @@ public class PolynomialRotation implements LOSTransform { /** {@inheritDoc} */ @Override - public Stream<ExtendedParameterDriver> getExtendedParametersDrivers() { + public Stream<ParameterDriver> getParametersDrivers() { return Stream.of(coefficientsDrivers); } @@ -150,24 +150,16 @@ public class PolynomialRotation implements LOSTransform { /** {@inheritDoc} */ @Override public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los, - final AbsoluteDate date) { + final AbsoluteDate date, final DSGenerator generator) { if (angleDS == null) { // lazy evaluation of the rotation - final int nbEstimated = coefficientsDrivers[0].getNbEstimated(); - axisDS = new FieldVector3D<DerivativeStructure>(new DerivativeStructure(nbEstimated, 1, axis.getX()), - new DerivativeStructure(nbEstimated, 1, axis.getY()), - new DerivativeStructure(nbEstimated, 1, axis.getZ())); + axisDS = new FieldVector3D<DerivativeStructure>(generator.constant(axis.getX()), + generator.constant(axis.getY()), + generator.constant(axis.getZ())); angleDS = new DerivativeStructure[coefficientsDrivers.length]; for (int k = 0; k < angleDS.length; ++k) { - final double value = coefficientsDrivers[k].getValue(); - if (coefficientsDrivers[k].isSelected()) { - // the coefficient is estimated - angleDS[k] = new DerivativeStructure(nbEstimated, 1, coefficientsDrivers[k].getIndex(), value); - } else { - // the coefficient is not estimated - angleDS[k] = new DerivativeStructure(nbEstimated, 1, value); - } + angleDS[k] = generator.variable(coefficientsDrivers[k]); } } // evaluate polynomial, with all its partial derivatives diff --git a/src/main/java/org/orekit/rugged/los/TimeDependentLOS.java b/src/main/java/org/orekit/rugged/los/TimeDependentLOS.java index 9cf3ac73..2a17c61b 100644 --- a/src/main/java/org/orekit/rugged/los/TimeDependentLOS.java +++ b/src/main/java/org/orekit/rugged/los/TimeDependentLOS.java @@ -21,8 +21,9 @@ import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; /** Interface representing a line-of-sight which depends on time. * @see org.orekit.rugged.linesensor.LineSensor @@ -53,18 +54,20 @@ public interface TimeDependentLOS { * Note that in order for the partial derivatives to be properly set up, the * {@link org.orekit.utils.ParameterDriver#setSelected(boolean) setSelected} * method must have been set to {@code true} for the various parameters returned - * by {@link #getExtendedParametersDrivers()} that should be estimated. + * by {@link #getParametersDrivers()} that should be estimated. * </p> * @param index los pixel index * @param date date + * @param generator generator to use for building {@link DerivativeStructure} instances * @return line of sight, and its first partial derivatives with respect to the parameters */ - FieldVector3D<DerivativeStructure> getLOSDerivatives(int index, AbsoluteDate date); + FieldVector3D<DerivativeStructure> getLOSDerivatives(int index, AbsoluteDate date, + DSGenerator generator); /** Get the drivers for LOS parameters. * @return drivers for LOS parameters * @since 2.0 */ - Stream<ExtendedParameterDriver> getExtendedParametersDrivers(); + Stream<ParameterDriver> getParametersDrivers(); } diff --git a/src/main/java/org/orekit/rugged/los/TimeIndependentLOSTransform.java b/src/main/java/org/orekit/rugged/los/TimeIndependentLOSTransform.java index 59ecbddf..17b16449 100644 --- a/src/main/java/org/orekit/rugged/los/TimeIndependentLOSTransform.java +++ b/src/main/java/org/orekit/rugged/los/TimeIndependentLOSTransform.java @@ -21,7 +21,8 @@ import java.util.stream.Stream; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; +import org.orekit.utils.ParameterDriver; /** Interface for lines-of-sight tranforms that do not depend on time. * @author Luc Maisonobe @@ -53,14 +54,16 @@ public interface TimeIndependentLOSTransform { * </p> * @param index los pixel index * @param los line-of-sight to transform + * @param generator generator to use for building {@link DerivativeStructure} instances * @return line of sight, and its first partial derivatives with respect to the parameters */ - FieldVector3D<DerivativeStructure> transformLOS(int index, FieldVector3D<DerivativeStructure> los); + FieldVector3D<DerivativeStructure> transformLOS(int index, FieldVector3D<DerivativeStructure> los, + DSGenerator generator); /** Get the drivers for LOS parameters. * @return drivers for LOS parameters * @since 2.0 */ - Stream<ExtendedParameterDriver> getExtendedParametersDrivers(); + Stream<ParameterDriver> getParametersDrivers(); } diff --git a/src/main/java/org/orekit/rugged/utils/DSGenerator.java b/src/main/java/org/orekit/rugged/utils/DSGenerator.java new file mode 100644 index 00000000..d757e2ea --- /dev/null +++ b/src/main/java/org/orekit/rugged/utils/DSGenerator.java @@ -0,0 +1,55 @@ +/* Copyright 2013-2016 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.utils; + +import java.util.List; + +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.orekit.utils.ParameterDriver; + +/** Generator for {@link DerivativeStructure} instances from {@link ParameterDriver}. + * <p> + * Note that this interface is for Rugged library internal use only. + * </p> + * @author Luc Maisonobe + * @since 2.0 + */ +public interface DSGenerator { + + /** Get the parameters selected for estimation. + * @return parameters selected for estimation + */ + List<ParameterDriver> getSelected(); + + /** Generate a constant {@link DerivativeStructure}. + * @param value value of the constant + * @return constant {@link DerivativeStructure} + */ + DerivativeStructure constant(double value); + + /** Generate a {@link DerivativeStructure} representing the + * parameter driver either as a canonical variable or a constant. + * <p> + * The instance created is a variable only if the parameter + * has been selected for estimation, otherwise it is a constant. + * </p> + * @param driver driver for the variable + * @return variable {@link DerivativeStructure} + */ + DerivativeStructure variable(ParameterDriver driver); + +} diff --git a/src/main/java/org/orekit/rugged/utils/ExtendedParameterDriver.java b/src/main/java/org/orekit/rugged/utils/ExtendedParameterDriver.java deleted file mode 100644 index 0fafb4a8..00000000 --- a/src/main/java/org/orekit/rugged/utils/ExtendedParameterDriver.java +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright 2013-2016 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.utils; - -import org.orekit.errors.OrekitException; -import org.orekit.utils.ParameterDriver; - -/** {@link ParameterDriver} belonging to a list. - * @since 2.0 - * @author Luc Maisonobe - */ -public class ExtendedParameterDriver extends ParameterDriver { - - /** Total number of estimated parameters. */ - private int nbEstimated; - - /** Index of this parameter in the estimated parameters list. */ - private int index; - - /** Simple constructor. - * <p> - * At construction, the parameter is configured as <em>not</em> selected, - * and the value is set to the {@code referenceValue}. - * </p> - * @param name name of the parameter - * @param referenceValue reference value of the parameter - * @param scale scaling factor to convert the parameters value to - * non-dimensional (typically set to the expected standard deviation of the - * parameter), it must be non-zero - * @param minValue minimum value - * @param maxValue maximum value - * @exception OrekitException if scale is too close to zero - */ - public ExtendedParameterDriver(final String name, final double referenceValue, - final double scale, final double minValue, - final double maxValue) - throws OrekitException { - super(name, referenceValue, scale, minValue, maxValue); - nbEstimated = 0; - index = -1; - } - - /** Set the total number of estimated parameters. - * @param nbEstimated total number of estimated parameters - */ - public void setNbEstimated(final int nbEstimated) { - this.nbEstimated = nbEstimated; - } - - /** Get the total number of estimated parameters. - * @return total number of estimated parameters - */ - public int getNbEstimated() { - return nbEstimated; - } - - /** Set the index of this parameter in the estimated parameters list. - * @param index index of this parameter in the estimated parameters list - */ - public void setIndex(final int index) { - this.index = index; - } - - /** Get the index of this parameter in the estimated parameters list. - * @return index of this parameter in the estimated parameters list - */ - public int getIndex() { - return index; - } - -} diff --git a/src/test/java/org/orekit/rugged/api/RuggedTest.java b/src/test/java/org/orekit/rugged/api/RuggedTest.java index 43771973..f882dd9c 100644 --- a/src/test/java/org/orekit/rugged/api/RuggedTest.java +++ b/src/test/java/org/orekit/rugged/api/RuggedTest.java @@ -26,10 +26,9 @@ import java.net.URISyntaxException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Map; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator; @@ -37,6 +36,7 @@ import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum; import org.hipparchus.stat.descriptive.StreamingStatistics; import org.hipparchus.stat.descriptive.rank.Percentile; import org.hipparchus.util.FastMath; @@ -70,7 +70,7 @@ import org.orekit.rugged.los.TimeDependentLOS; import org.orekit.rugged.raster.RandomLandscapeUpdater; import org.orekit.rugged.raster.TileUpdater; import org.orekit.rugged.raster.VolcanicConeElevationUpdater; -import org.orekit.rugged.utils.ExtendedParameterDriver; +import org.orekit.rugged.utils.DSGenerator; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -78,6 +78,7 @@ import org.orekit.utils.AngularDerivativesFilter; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; +import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedAngularCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -1124,40 +1125,33 @@ public class RuggedTest { build(); // we want to adjust sensor roll and pitch angles - ExtendedParameterDriver rollDriver = - rugged.getLineSensor("line").getExtendedParametersDrivers(). - filter(driver -> driver.getName().equals("roll")).findFirst().get(); + ParameterDriver rollDriver = + lineSensor.getParametersDrivers(). + filter(driver -> driver.getName().equals("roll")).findFirst().get(); rollDriver.setSelected(true); - ExtendedParameterDriver pitchDriver = - rugged.getLineSensor("line").getExtendedParametersDrivers(). - filter(driver -> driver.getName().equals("pitch")).findFirst().get(); + ParameterDriver pitchDriver = + lineSensor.getParametersDrivers(). + filter(driver -> driver.getName().equals("pitch")).findFirst().get(); pitchDriver.setSelected(true); - // prepare parameters (this is normally done by Rugged.estimateFreeParameters) - // this setup may seem complex, but DerivativeStructure are really an - // internal feature, user do not need to deal with them - final Map<String, int[]> map = new HashMap<>(); - rugged.getLineSensor("line").getExtendedParametersDrivers(). - filter(driver -> driver.isSelected()). - forEach(driver -> { - driver.setNbEstimated(2); - driver.setIndex(map.size()); - int[] orders = new int[2]; - orders[map.size()] = 1; - map.put(driver.getName(), orders); - }); + // prepare generator + Method createGenerator = Rugged.class.getDeclaredMethod("createGenerator", List.class); + createGenerator.setAccessible(true); + DSGenerator generator = (DSGenerator) createGenerator.invoke(rugged, Collections.singletonList(lineSensor)); double referenceLine = 0.87654 * dimension; GeodeticPoint[] gp = rugged.directLocation("line", referenceLine); Method inverseLoc = Rugged.class.getDeclaredMethod("inverseLocationDerivatives", String.class, GeodeticPoint.class, - Integer.TYPE, Integer.TYPE); + Integer.TYPE, Integer.TYPE, + DSGenerator.class); inverseLoc.setAccessible(true); int referencePixel = (3 * dimension) / 4; DerivativeStructure[] result = (DerivativeStructure[]) inverseLoc.invoke(rugged, - "line", gp[referencePixel], 0, dimension); + "line", gp[referencePixel], 0, dimension, + generator); Assert.assertEquals(referenceLine, result[0].getValue(), lineTolerance); Assert.assertEquals(referencePixel, result[1].getValue(), pixelTolerance); Assert.assertEquals(2, result[0].getFreeParameters()); @@ -1180,7 +1174,7 @@ public class RuggedTest { } }); double dLdR = lineVSroll.value(new DerivativeStructure(1, 1, 0, 0.0)).getPartialDerivative(1); - Assert.assertEquals(dLdR, result[0].getPartialDerivative(map.get("roll")), dLdR * lineDerivativeRelativeTolerance); + Assert.assertEquals(dLdR, result[0].getPartialDerivative(1, 0), dLdR * lineDerivativeRelativeTolerance); UnivariateDifferentiableFunction lineVSpitch = differentiator.differentiate((double pitch) -> { @@ -1195,7 +1189,7 @@ public class RuggedTest { } }); double dLdP = lineVSpitch.value(new DerivativeStructure(1, 1, 0, 0.0)).getPartialDerivative(1); - Assert.assertEquals(dLdP, result[0].getPartialDerivative(map.get("pitch")), dLdP * lineDerivativeRelativeTolerance); + Assert.assertEquals(dLdP, result[0].getPartialDerivative(0, 1), dLdP * lineDerivativeRelativeTolerance); UnivariateDifferentiableFunction pixelVSroll = differentiator.differentiate((double roll) -> { @@ -1210,7 +1204,7 @@ public class RuggedTest { } }); double dXdR = pixelVSroll.value(new DerivativeStructure(1, 1, 0, 0.0)).getPartialDerivative(1); - Assert.assertEquals(dXdR, result[1].getPartialDerivative(map.get("roll")), dXdR * pixelDerivativeRelativeTolerance); + Assert.assertEquals(dXdR, result[1].getPartialDerivative(1, 0), dXdR * pixelDerivativeRelativeTolerance); UnivariateDifferentiableFunction pixelVSpitch = differentiator.differentiate((double pitch) -> { @@ -1225,7 +1219,7 @@ public class RuggedTest { } }); double dXdP = pixelVSpitch.value(new DerivativeStructure(1, 1, 0, 0.0)).getPartialDerivative(1); - Assert.assertEquals(dXdP, result[1].getPartialDerivative(map.get("pitch")), dXdP * pixelDerivativeRelativeTolerance); + Assert.assertEquals(dXdP, result[1].getPartialDerivative(0, 1), dXdP * pixelDerivativeRelativeTolerance); } catch (InvocationTargetException | NoSuchMethodException | SecurityException | IllegalAccessException | @@ -1235,6 +1229,182 @@ public class RuggedTest { } } + @Test + public void testViewingModelParametersEstimationWithoutCorrections() + throws OrekitException, RuggedException { + doTestViewingModelParametersEstimation(2000, false, false, + FastMath.toRadians( 0.01), 1.0e-8, + FastMath.toRadians(-0.03), 2.0e-9, + 6); + } + + @Test + public void testViewingModelParametersEstimationWithLightTimeCorrection() + throws OrekitException, RuggedException { + doTestViewingModelParametersEstimation(2000, true, false, + FastMath.toRadians( 0.01), 1.0e-8, + FastMath.toRadians(-0.03), 2.0e-9, + 6); + } + + @Test + public void testViewingModelParametersEstimationWithAberrationOfLightCorrection() + throws OrekitException, RuggedException { + doTestViewingModelParametersEstimation(2000, false, true, + FastMath.toRadians( 0.01), 1.0e-8, + FastMath.toRadians(-0.03), 2.0e-9, + 6); + } + + @Test + public void testViewingModelParametersEstimationWithAllCorrections() + throws OrekitException, RuggedException { + doTestViewingModelParametersEstimation(2000, true, true, + FastMath.toRadians( 0.01), 1.0e-8, + FastMath.toRadians(-0.03), 3.0e-9, + 6); + } + + private void doTestViewingModelParametersEstimation(int dimension, + boolean lightTimeCorrection, + boolean aberrationOfLightCorrection, + double rollValue, double rollTolerance, + double pitchValue, double pitchTolerance, + int expectedEvaluations) + throws OrekitException, RuggedException { + try { + + // set up a perfect Rugged instance, with prescribed roll and pitch values + Rugged perfect = createParameterEstimationContext(dimension, + lightTimeCorrection, + aberrationOfLightCorrection); + perfect. + getLineSensor("line"). + getParametersDrivers(). + filter(driver -> driver.getName().equals("roll")). + findFirst().get().setValue(rollValue); + + perfect. + getLineSensor("line"). + getParametersDrivers(). + filter(driver -> driver.getName().equals("pitch")). + findFirst().get().setValue(pitchValue); + + // generate reference mapping + SensorToGroundMapping mapping = new SensorToGroundMapping("line"); + LineSensor sensor = perfect.getLineSensor(mapping.getSensorName()); + for (double line = 0; line < dimension; line += 100) { + AbsoluteDate date = sensor.getDate(line); + for (int pixel = 0; pixel < sensor.getNbPixels(); pixel += 100) { + GeodeticPoint gp = perfect.directLocation(date, sensor.getPosition(), + sensor.getLOS(date, pixel)); + mapping.addMapping(new SensorPixel(line, pixel), gp); + } + } + + // set up a completely independent Rugged instance, + // with 0.0 roll and pitch values and estimating rool and pitch + Rugged normal = createParameterEstimationContext(dimension, + lightTimeCorrection, + aberrationOfLightCorrection); + normal. + getLineSensor("line"). + getParametersDrivers(). + filter(driver -> driver.getName().equals("roll") || driver.getName().equals("pitch")). + forEach(driver -> { + try { + driver.setSelected(true); + driver.setValue(0.0); + } catch (OrekitException e) { + throw new OrekitExceptionWrapper(e); + } + }); + + // perform parameters estimation + Optimum optimum = normal.estimateFreeParameters(Collections.singletonList(mapping), 10, 1.0e-9); + Assert.assertEquals(expectedEvaluations, optimum.getEvaluations()); + + // check estimated values + double estimatedRoll = normal.getLineSensor("line"). + getParametersDrivers(). + filter(driver -> driver.getName().equals("roll")). + findFirst().get().getValue(); + Assert.assertEquals("roll error = " + (estimatedRoll - rollValue), + rollValue, estimatedRoll, rollTolerance); + + double estimatedPitch = normal.getLineSensor("line"). + getParametersDrivers(). + filter(driver -> driver.getName().equals("pitch")). + findFirst().get().getValue(); + Assert.assertEquals("pitch error = " + (estimatedPitch - pitchValue), + pitchValue, estimatedPitch, pitchTolerance); + + } catch (OrekitExceptionWrapper oew) { + throw oew.getException(); + } + } + + private Rugged createParameterEstimationContext(int dimension, + boolean lightTimeCorrection, + boolean aberrationOfLightCorrection) + throws OrekitException, RuggedException { + + try { + + String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); + DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); + final BodyShape earth = TestUtils.createEarth(); + final Orbit orbit = TestUtils.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, 2.6" per pixel + Vector3D position = new Vector3D(1.5, 0, -0.2); + LOSBuilder losBuilder = + TestUtils.createLOSPerfectLine(new Rotation(Vector3D.PLUS_I, + FastMath.toRadians(50.0), + RotationConvention.VECTOR_OPERATOR).applyTo(Vector3D.PLUS_K), + Vector3D.PLUS_I, + FastMath.toRadians(dimension * 2.6 / 3600.0), dimension); + losBuilder.addTransform(new FixedRotation("roll", Vector3D.MINUS_I, 0.0)); + losBuilder.addTransform(new FixedRotation("pitch", Vector3D.MINUS_J, 0.0)); + TimeDependentLOS los = losBuilder.build(); + + // linear datation model: at reference time we get the middle line, 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; + final LineSensor lineSensor = new LineSensor("line", lineDatation, position, los); + AbsoluteDate minDate = lineSensor.getDate(firstLine).shiftedBy(-1.0); + AbsoluteDate maxDate = lineSensor.getDate(lastLine).shiftedBy(+1.0); + + TileUpdater updater = + new RandomLandscapeUpdater(0.0, 9000.0, 0.3, 0xf0a401650191f9f6l, + FastMath.toRadians(1.0), 257); + + return new RuggedBuilder(). + setDigitalElevationModel(updater, 8). + setAlgorithm(AlgorithmId.DUVENHAGE). + setEllipsoid(EllipsoidId.WGS84, BodyRotatingFrameId.ITRF). + setTimeSpan(minDate, maxDate, 0.001, 5.0). + setTrajectory(InertialFrameId.EME2000, + TestUtils.orbitToPV(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), + 8, CartesianDerivativesFilter.USE_PV, + TestUtils.orbitToQ(orbit, earth, minDate.shiftedBy(-1.0), maxDate.shiftedBy(+1.0), 0.25), + 2, AngularDerivativesFilter.USE_R). + setLightTimeCorrection(lightTimeCorrection). + setAberrationOfLightCorrection(aberrationOfLightCorrection). + addLineSensor(lineSensor). + build(); + + } catch (URISyntaxException e) { + Assert.fail(e.getLocalizedMessage()); + return null; // never reached + } + } + private void checkDateLocation(int dimension, boolean lightTimeCorrection, boolean aberrationOfLightCorrection, double maxDateError) throws RuggedException, OrekitException, URISyntaxException { -- GitLab