diff --git a/src/main/java/org/orekit/rugged/api/Rugged.java b/src/main/java/org/orekit/rugged/api/Rugged.java
index 82063c611af68cb127dcc01812e543c8bd776b6c..8b273060d31d63fb441f433d21d371d05c0fde89 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 3de6f8801d691b29b21e89ea3bda916226bad8f0..fea1ff2540248603a0e477a1d16cede88f469263 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 40f6c2d444b5241f0a2acf5cc285932bf26a5b4e..57dc751644dfb3dc04a46034d3930a02eb218a0d 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 5705184fd4b04d8307feb0396a8990d7fa149a51..cf86debd14d225a8d4aacb4b54160020888d6c03 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 8f6aba220a6c60c575d49334531919f66015f80f..3638aee9e2042fb887c9c2ecd2909b5854d21f34 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 c61be99afe209ae92bed518ed3684a2802a784ac..4857bd5873ae8d8b02b1fdc81f0bb966a261535c 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 b54f6289dc0bec6c3c9d1912c3ba90937fcea228..1e894b08c9eba2885b579ca5ddaecb019ffa49ef 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 49df76a8d84b4481c736e38072c33c9f9ea7c6c3..158c6dce5f2d876563bbc612a4209664828962c3 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 9cf3ac73ee5bb3c8365f7b03fefae5d9976c6b3c..2a17c61b7689185b5b41ca52b7939780f9c13fb1 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 59ecbddffc9fab813a1c31abf205acbcfb8e17b6..17b16449a17bcfe76c9465f2e0bd9c57cad97d8d 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 0000000000000000000000000000000000000000..d757e2ea6fcb5a873f88a1eb549f41b2e5cf94ac
--- /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 0fafb4a841873b67f9360e5db4438e088a63245e..0000000000000000000000000000000000000000
--- 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 43771973bc33270042897da6d5a0fab9c9b3889d..f882dd9c6236e6b72eb2cd58b4f4ed621ac9ef58 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 {