AbstractBatchLSModel.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.estimation.leastsquares;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.IdentityHashMap;
  23. import java.util.List;
  24. import java.util.Map;

  25. import org.hipparchus.linear.Array2DRowRealMatrix;
  26. import org.hipparchus.linear.ArrayRealVector;
  27. import org.hipparchus.linear.MatrixUtils;
  28. import org.hipparchus.linear.RealMatrix;
  29. import org.hipparchus.linear.RealVector;
  30. import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
  31. import org.hipparchus.util.FastMath;
  32. import org.hipparchus.util.Incrementor;
  33. import org.hipparchus.util.Pair;
  34. import org.orekit.estimation.measurements.EstimatedMeasurement;
  35. import org.orekit.estimation.measurements.ObservedMeasurement;
  36. import org.orekit.orbits.Orbit;
  37. import org.orekit.propagation.MatricesHarvester;
  38. import org.orekit.propagation.Propagator;
  39. import org.orekit.propagation.PropagatorsParallelizer;
  40. import org.orekit.propagation.SpacecraftState;
  41. import org.orekit.propagation.conversion.PropagatorBuilder;
  42. import org.orekit.propagation.sampling.MultiSatStepHandler;
  43. import org.orekit.time.AbsoluteDate;
  44. import org.orekit.time.ChronologicalComparator;
  45. import org.orekit.utils.ParameterDriver;
  46. import org.orekit.utils.ParameterDriversList;
  47. import org.orekit.utils.ParameterDriversList.DelegatingDriver;
  48. import org.orekit.utils.TimeSpanMap.Span;
  49. import org.orekit.utils.TimeSpanMap;

  50. /** Bridge between {@link ObservedMeasurement measurements} and {@link
  51.  * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem
  52.  * least squares problems}.
  53.  * @author Luc Maisonobe
  54.  * @author Bryan Cazabonne
  55.  * @author Thomas Paulet
  56.  * @author Melina Vanel
  57.  * @since 11.0
  58.  */
  59. public abstract class AbstractBatchLSModel implements MultivariateJacobianFunction {

  60.     /** Builders for propagators. */
  61.     private final PropagatorBuilder[] builders;

  62.     /** Array of each builder's selected orbit drivers. Orbit drivers
  63.      * should have only 1 span on their value TimeSpanMap.
  64.      * @since 11.1
  65.      */
  66.     private final ParameterDriversList[] estimatedOrbitalParameters;

  67.     /** Array of each builder's selected propagation drivers. */
  68.     private final ParameterDriversList[] estimatedPropagationParameters;

  69.     /** Estimated measurements parameters. */
  70.     private final ParameterDriversList estimatedMeasurementsParameters;

  71.     /** Measurements. */
  72.     private final List<ObservedMeasurement<?>> measurements;

  73.     /** Start columns for each estimated orbit. */
  74.     private final int[] orbitsStartColumns;

  75.     /** End columns for each estimated orbit. */
  76.     private final int[] orbitsEndColumns;

  77.     /** Indirection array in measurements jacobians.
  78.      * @since 11.2
  79.      */
  80.     private final int[] orbitsJacobianColumns;

  81.     /** Map for propagation parameters columns. */
  82.     private final Map<String, Integer> propagationParameterColumns;

  83.     /** Map for measurements parameters columns. */
  84.     private final Map<String, Integer> measurementParameterColumns;

  85.     /** Last evaluations. */
  86.     private final Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> evaluations;

  87.     /** Observer to be notified at orbit changes. */
  88.     private final ModelObserver observer;

  89.     /** Counter for the evaluations. */
  90.     private Incrementor evaluationsCounter;

  91.     /** Counter for the iterations. */
  92.     private Incrementor iterationsCounter;

  93.     /** Date of the first enabled measurement. */
  94.     private AbsoluteDate firstDate;

  95.     /** Date of the last enabled measurement. */
  96.     private AbsoluteDate lastDate;

  97.     /** Boolean indicating if the propagation will go forward or backward. */
  98.     private final boolean forwardPropagation;

  99.     /** Model function value. */
  100.     private RealVector value;

  101.     /** Harvesters for extracting State Transition Matrices and Jacobians from integrated states.
  102.      * @since 11.1
  103.      */
  104.     private final MatricesHarvester[] harvesters;

  105.     /** Model function Jacobian. */
  106.     private RealMatrix jacobian;

  107.     /**
  108.      * Constructor.
  109.      * @param propagatorBuilders builders to use for propagation
  110.      * @param measurements measurements
  111.      * @param estimatedMeasurementsParameters estimated measurements parameters
  112.      * @param observer observer to be notified at model calls
  113.      */
  114.     public AbstractBatchLSModel(final PropagatorBuilder[] propagatorBuilders,
  115.                                 final List<ObservedMeasurement<?>> measurements,
  116.                                 final ParameterDriversList estimatedMeasurementsParameters,
  117.                                 final ModelObserver observer) {

  118.         this.builders                        = propagatorBuilders.clone();
  119.         this.measurements                    = measurements;
  120.         this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
  121.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getNbValuesToEstimate());
  122.         this.estimatedOrbitalParameters      = new ParameterDriversList[builders.length];
  123.         this.estimatedPropagationParameters  = new ParameterDriversList[builders.length];
  124.         this.evaluations                     = new IdentityHashMap<>(measurements.size());
  125.         this.observer                        = observer;
  126.         this.harvesters                      = new MatricesHarvester[builders.length];

  127.         // allocate vector and matrix
  128.         int rows = 0;
  129.         for (final ObservedMeasurement<?> measurement : measurements) {
  130.             rows += measurement.getDimension();
  131.         }

  132.         this.orbitsStartColumns    = new int[builders.length];
  133.         this.orbitsEndColumns      = new int[builders.length];
  134.         this.orbitsJacobianColumns = new int[builders.length * 6];
  135.         Arrays.fill(orbitsJacobianColumns, -1);
  136.         int columns = 0;
  137.         for (int i = 0; i < builders.length; ++i) {
  138.             this.orbitsStartColumns[i] = columns;
  139.             final List<ParameterDriversList.DelegatingDriver> orbitalParametersDrivers =
  140.                             builders[i].getOrbitalParametersDrivers().getDrivers();
  141.             for (int j = 0; j < orbitalParametersDrivers.size(); ++j) {
  142.                 if (orbitalParametersDrivers.get(j).isSelected()) {
  143.                     orbitsJacobianColumns[columns] = j;
  144.                     ++columns;
  145.                 }
  146.             }
  147.             this.orbitsEndColumns[i] = columns;
  148.         }

  149.         // Gather all the propagation drivers names in a list
  150.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  151.         for (int i = 0; i < builders.length; ++i) {
  152.             // The index i in array estimatedPropagationParameters (attribute of the class) is populated
  153.             // when the first call to getSelectedPropagationDriversForBuilder(i) is made
  154.             for (final DelegatingDriver delegating : getSelectedPropagationDriversForBuilder(i).getDrivers()) {

  155.                 final TimeSpanMap<String> delegatingNameSpanMap = delegating.getNamesSpanMap();
  156.                 // for each span (for each estimated value) corresponding name is added
  157.                 Span<String> currentNameSpan = delegatingNameSpanMap.getFirstSpan();
  158.                 // Add the driver name if it has not been added yet and the number of estimated values for this param
  159.                 if (!estimatedPropagationParametersNames.contains(currentNameSpan.getData())) {
  160.                     estimatedPropagationParametersNames.add(currentNameSpan.getData());
  161.                 }
  162.                 for (int spanNumber = 1; spanNumber < delegatingNameSpanMap.getSpansNumber(); ++spanNumber) {
  163.                     currentNameSpan = delegatingNameSpanMap.getSpan(currentNameSpan.getEnd());
  164.                     // Add the driver name if it has not been added yet and the number of estimated values for this param
  165.                     if (!estimatedPropagationParametersNames.contains(currentNameSpan.getData())) {
  166.                         estimatedPropagationParametersNames.add(currentNameSpan.getData());
  167.                     }
  168.                 }
  169.             }
  170.         }

  171.         // Populate the map of propagation drivers' columns and update the total number of columns
  172.         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  173.         for (final String driverName : estimatedPropagationParametersNames) {
  174.             propagationParameterColumns.put(driverName, columns);
  175.             ++columns;
  176.         }
  177.         // Populate the map of measurement drivers' columns and update the total number of columns
  178.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  179.             for (Span<String> span = parameter.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
  180.                 measurementParameterColumns.put(span.getData(), columns);
  181.                 columns++;
  182.             }
  183.         }

  184.         // Initialize point and value
  185.         value    = new ArrayRealVector(rows);
  186.         jacobian = MatrixUtils.createRealMatrix(rows, columns);

  187.         // Decide whether the propagation will be done forward or backward.
  188.         // Minimize the duration between first measurement treated and orbit determination date
  189.         // Propagator builder number 0 holds the reference date for orbit determination
  190.         final AbsoluteDate refDate = builders[0].getInitialOrbitDate();

  191.         // Sort the measurement list chronologically
  192.         measurements.sort(new ChronologicalComparator());
  193.         firstDate = measurements.get(0).getDate();
  194.         lastDate  = measurements.get(measurements.size() - 1).getDate();

  195.         // Decide the direction of propagation
  196.         if (FastMath.abs(refDate.durationFrom(firstDate)) <= FastMath.abs(refDate.durationFrom(lastDate))) {
  197.             // Propagate forward from firstDate
  198.             forwardPropagation = true;
  199.         } else {
  200.             // Propagate backward from lastDate
  201.             forwardPropagation = false;
  202.         }
  203.     }

  204.     /** Set the counter for evaluations.
  205.      * @param evaluationsCounter counter for evaluations
  206.      */
  207.     public void setEvaluationsCounter(final Incrementor evaluationsCounter) {
  208.         this.evaluationsCounter = evaluationsCounter;
  209.     }

  210.     /** Set the counter for iterations.
  211.      * @param iterationsCounter counter for iterations
  212.      */
  213.     public void setIterationsCounter(final Incrementor iterationsCounter) {
  214.         this.iterationsCounter = iterationsCounter;
  215.     }

  216.     /** Return the forward propagation flag.
  217.      * @return the forward propagation flag
  218.      */
  219.     public boolean isForwardPropagation() {
  220.         return forwardPropagation;
  221.     }

  222.     /** Configure the propagator to compute derivatives.
  223.      * @param propagator {@link Propagator} to configure
  224.      * @return harvester harvester to retrive the State Transition Matrix and Jacobian Matrix
  225.      */
  226.     protected abstract MatricesHarvester configureHarvester(Propagator propagator);

  227.     /** Configure the current estimated orbits.
  228.      * <p>
  229.      * For DSST orbit determination, short period derivatives are also calculated.
  230.      * </p>
  231.      * @param harvester harvester for matrices
  232.      * @param propagator the orbit propagator
  233.      * @return the current estimated orbits
  234.      */
  235.     protected abstract Orbit configureOrbits(MatricesHarvester harvester, Propagator propagator);

  236.     /** {@inheritDoc} */
  237.     @Override
  238.     public Pair<RealVector, RealMatrix> value(final RealVector point) {

  239.         // Set up the propagators parallelizer
  240.         final Propagator[] propagators = createPropagators(point);
  241.         final Orbit[] orbits = new Orbit[propagators.length];
  242.         for (int i = 0; i < propagators.length; ++i) {
  243.             harvesters[i] = configureHarvester(propagators[i]);
  244.             orbits[i]     = configureOrbits(harvesters[i], propagators[i]);
  245.         }
  246.         final PropagatorsParallelizer parallelizer =
  247.                         new PropagatorsParallelizer(Arrays.asList(propagators), configureMeasurements(point));

  248.         // Reset value and Jacobian
  249.         evaluations.clear();
  250.         value.set(0.0);
  251.         for (int i = 0; i < jacobian.getRowDimension(); ++i) {
  252.             for (int j = 0; j < jacobian.getColumnDimension(); ++j) {
  253.                 jacobian.setEntry(i, j, 0.0);
  254.             }
  255.         }

  256.         // Run the propagation, gathering residuals on the fly
  257.         if (isForwardPropagation()) {
  258.             // Propagate forward from firstDate
  259.             parallelizer.propagate(firstDate.shiftedBy(-1.0), lastDate.shiftedBy(+1.0));
  260.         } else {
  261.             // Propagate backward from lastDate
  262.             parallelizer.propagate(lastDate.shiftedBy(+1.0), firstDate.shiftedBy(-1.0));
  263.         }

  264.         observer.modelCalled(orbits, evaluations);

  265.         return new Pair<RealVector, RealMatrix>(value, jacobian);

  266.     }

  267.     /** Get the selected orbital drivers for a propagatorBuilder.
  268.      * @param iBuilder index of the builder in the builders' array
  269.      * @return the list of selected orbital drivers for propagatorBuilder of index iBuilder
  270.      * @since 11.1
  271.      */
  272.     public ParameterDriversList getSelectedOrbitalParametersDriversForBuilder(final int iBuilder) {

  273.         // Lazy evaluation, create the list only if it hasn't been created yet
  274.         if (estimatedOrbitalParameters[iBuilder] == null) {

  275.             // Gather the drivers
  276.             final ParameterDriversList selectedOrbitalDrivers = new ParameterDriversList();
  277.             for (final DelegatingDriver delegating : builders[iBuilder].getOrbitalParametersDrivers().getDrivers()) {
  278.                 if (delegating.isSelected()) {
  279.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  280.                         selectedOrbitalDrivers.add(driver);
  281.                     }
  282.                 }
  283.             }

  284.             // Add the list of selected orbital parameters drivers to the array
  285.             estimatedOrbitalParameters[iBuilder] = selectedOrbitalDrivers;
  286.         }
  287.         return estimatedOrbitalParameters[iBuilder];
  288.     }

  289.     /** Get the selected propagation drivers for a propagatorBuilder.
  290.      * @param iBuilder index of the builder in the builders' array
  291.      * @return the list of selected propagation drivers for propagatorBuilder of index iBuilder
  292.      */
  293.     public ParameterDriversList getSelectedPropagationDriversForBuilder(final int iBuilder) {

  294.         // Lazy evaluation, create the list only if it hasn't been created yet
  295.         if (estimatedPropagationParameters[iBuilder] == null) {

  296.             // Gather the drivers
  297.             final ParameterDriversList selectedPropagationDrivers = new ParameterDriversList();
  298.             for (final DelegatingDriver delegating : builders[iBuilder].getPropagationParametersDrivers().getDrivers()) {
  299.                 if (delegating.isSelected()) {
  300.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  301.                         selectedPropagationDrivers.add(driver);
  302.                     }
  303.                 }
  304.             }

  305.             // List of propagation drivers are sorted in the BatchLSEstimator class.
  306.             // Hence we need to sort this list so the parameters' indexes match
  307.             selectedPropagationDrivers.sort();

  308.             // Add the list of selected propagation drivers to the array
  309.             estimatedPropagationParameters[iBuilder] = selectedPropagationDrivers;
  310.         }
  311.         return estimatedPropagationParameters[iBuilder];
  312.     }

  313.     /** Create the propagators and parameters corresponding to an evaluation point.
  314.      * @param point evaluation point
  315.      * @return an array of new propagators
  316.      */
  317.     public Propagator[] createPropagators(final RealVector point) {

  318.         final Propagator[] propagators = new Propagator[builders.length];


  319.         // Set up the propagators
  320.         for (int i = 0; i < builders.length; ++i) {

  321.             int element = 0;
  322.             // Get the number of values to estimate for selected orbital drivers in the builder
  323.             final int nbOrb    = orbitsEndColumns[i] - orbitsStartColumns[i];

  324.             // Get the list of selected propagation drivers in the builder and its size
  325.             final ParameterDriversList selectedPropagationDrivers = getSelectedPropagationDriversForBuilder(i);
  326.             final int nbParams = selectedPropagationDrivers.getNbParams();
  327.             final int nbValuesToEstimate = selectedPropagationDrivers.getNbValuesToEstimate();

  328.             // Init the array of normalized parameters for the builder
  329.             final double[] propagatorArray = new double[nbOrb + nbValuesToEstimate];

  330.             // Add the orbital drivers normalized values
  331.             for (int j = 0; j < nbOrb; ++j) {
  332.                 propagatorArray[element++] = point.getEntry(orbitsStartColumns[i] + j);
  333.             }

  334.             // Add the propagation drivers normalized values
  335.             for (int j = 0; j < nbParams; ++j) {
  336.                 final DelegatingDriver driver = selectedPropagationDrivers.getDrivers().get(j);
  337.                 final TimeSpanMap<String> delegatingNameSpanMap = driver.getNamesSpanMap();
  338.                 // get point entry for each span (for each estimated value), point is sorted
  339.                 // with following parameters values and for each parameter driver
  340.                 // span value are sorted in chronological order
  341.                 Span<String> currentNameSpan = delegatingNameSpanMap.getFirstSpan();
  342.                 propagatorArray[element++] = point.getEntry(propagationParameterColumns.get(currentNameSpan.getData()));

  343.                 for (int spanNumber = 1; spanNumber < delegatingNameSpanMap.getSpansNumber(); ++spanNumber) {
  344.                     currentNameSpan = delegatingNameSpanMap.getSpan(currentNameSpan.getEnd());
  345.                     propagatorArray[element++] = point.getEntry(propagationParameterColumns.get(currentNameSpan.getData()));

  346.                 }
  347.             }

  348.             // Build the propagator
  349.             propagators[i] = builders[i].buildPropagator(propagatorArray);
  350.         }

  351.         return propagators;

  352.     }

  353.     /** Fetch a measurement that was evaluated during propagation.
  354.      * @param index index of the measurement first component
  355.      * @param evaluation measurement evaluation
  356.      */
  357.     public void fetchEvaluatedMeasurement(final int index, final EstimatedMeasurement<?> evaluation) {

  358.         // States and observed measurement
  359.         final SpacecraftState[]      evaluationStates    = evaluation.getStates();
  360.         final ObservedMeasurement<?> observedMeasurement = evaluation.getObservedMeasurement();

  361.         // compute weighted residuals
  362.         evaluations.put(observedMeasurement, evaluation);
  363.         if (evaluation.getStatus() == EstimatedMeasurement.Status.REJECTED) {
  364.             return;
  365.         }

  366.         final double[] evaluated = evaluation.getEstimatedValue();
  367.         final double[] observed  = observedMeasurement.getObservedValue();
  368.         final double[] sigma     = observedMeasurement.getTheoreticalStandardDeviation();
  369.         final double[] weight    = evaluation.getObservedMeasurement().getBaseWeight();
  370.         for (int i = 0; i < evaluated.length; ++i) {
  371.             value.setEntry(index + i, weight[i] * (evaluated[i] - observed[i]) / sigma[i]);
  372.         }

  373.         for (int k = 0; k < evaluationStates.length; ++k) {

  374.             final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex();

  375.             // partial derivatives of the current Cartesian coordinates with respect to current orbital state
  376.             final double[][] aCY = new double[6][6];
  377.             final Orbit currentOrbit = evaluationStates[k].getOrbit();
  378.             currentOrbit.getJacobianWrtParameters(builders[p].getPositionAngleType(), aCY);
  379.             final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

  380.             // Jacobian of the measurement with respect to current orbital state
  381.             final RealMatrix dMdC = new Array2DRowRealMatrix(evaluation.getStateDerivatives(k), false);
  382.             final RealMatrix dMdY = dMdC.multiply(dCdY);

  383.             // Jacobian of the measurement with respect to initial orbital state
  384.             final ParameterDriversList selectedOrbitalDrivers = getSelectedOrbitalParametersDriversForBuilder(p);
  385.             final int nbOrbParams = selectedOrbitalDrivers.getNbParams();
  386.             if (nbOrbParams > 0) {
  387.                 final RealMatrix dYdY0 = harvesters[p].getStateTransitionMatrix(evaluationStates[k]);
  388.                 final RealMatrix dMdY0 = dMdY.multiply(dYdY0);
  389.                 for (int i = 0; i < dMdY0.getRowDimension(); ++i) {
  390.                     for (int j = orbitsStartColumns[p]; j < orbitsEndColumns[p]; ++j) {
  391.                         final ParameterDriver driver =
  392.                                         selectedOrbitalDrivers.getDrivers().get(j - orbitsStartColumns[p]);
  393.                         final double partial = dMdY0.getEntry(i, orbitsJacobianColumns[j]);
  394.                         jacobian.setEntry(index + i, j,
  395.                                           weight[i] * partial / sigma[i] * driver.getScale());
  396.                     }
  397.                 }
  398.             }

  399.             // Jacobian of the measurement with respect to propagation parameters
  400.             final ParameterDriversList selectedPropagationDrivers = getSelectedPropagationDriversForBuilder(p);
  401.             final int nbParams = selectedPropagationDrivers.getNbParams();
  402.             if (nbParams > 0) {
  403.                 final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]);
  404.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);

  405.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  406.                     int col = 0;

  407.                     // Add the propagation drivers normalized values
  408.                     for (int j = 0; j < nbParams; ++j) {
  409.                         final ParameterDriver delegating = selectedPropagationDrivers.getDrivers().get(j);
  410.                         final TimeSpanMap<String> delegatingNameSpanMap = delegating.getNamesSpanMap();
  411.                         // get point entry for each span (for each estimated value), point is sorted
  412.                         for (Span<String> currentNameSpan = delegatingNameSpanMap.getFirstSpan(); currentNameSpan != null; currentNameSpan = currentNameSpan.next()) {
  413.                             jacobian.addToEntry(index + i, propagationParameterColumns.get(currentNameSpan.getData()),
  414.                                     weight[i] * dMdPp.getEntry(i, col++) / sigma[i] * delegating.getScale());
  415.                         }
  416.                     }
  417.                 }
  418.             }
  419.         }
  420.         // Jacobian of the measurement with respect to measurements parameters
  421.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  422.             if (driver.isSelected()) {
  423.                 for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
  424.                     final double[] aMPm = evaluation.getParameterDerivatives(driver, span.getStart());
  425.                     for (int i = 0; i < aMPm.length; ++i) {
  426.                         jacobian.setEntry(index + i, measurementParameterColumns.get(span.getData()),
  427.                                           weight[i] * aMPm[i] / sigma[i] * driver.getScale());
  428.                     }
  429.                 }
  430.             }
  431.         }

  432.     }

  433.     /** Configure the multi-satellites handler to handle measurements.
  434.      * @param point evaluation point
  435.      * @return multi-satellites handler to handle measurements
  436.      */
  437.     private MultiSatStepHandler configureMeasurements(final RealVector point) {

  438.         // Set up the measurement parameters
  439.         int index = orbitsEndColumns[builders.length - 1] + propagationParameterColumns.size();
  440.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {

  441.             for (Span<Double> span = parameter.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
  442.                 parameter.setNormalizedValue(point.getEntry(index++), span.getStart());
  443.             }
  444.         }

  445.         // Set up measurements handler
  446.         final List<PreCompensation> precompensated = new ArrayList<>();
  447.         for (final ObservedMeasurement<?> measurement : measurements) {
  448.             if (measurement.isEnabled()) {
  449.                 precompensated.add(new PreCompensation(measurement, evaluations.get(measurement)));
  450.             }
  451.         }
  452.         precompensated.sort(new ChronologicalComparator());

  453.         // Assign first and last date
  454.         firstDate = precompensated.get(0).getDate();
  455.         lastDate  = precompensated.get(precompensated.size() - 1).getDate();

  456.         // Reverse the list in case of backward propagation
  457.         if (!forwardPropagation) {
  458.             Collections.reverse(precompensated);
  459.         }

  460.         return new MeasurementHandler(this, precompensated);

  461.     }

  462.     /** Get the iterations count.
  463.      * @return iterations count
  464.      */
  465.     public int getIterationsCount() {
  466.         return iterationsCounter.getCount();
  467.     }

  468.     /** Get the evaluations count.
  469.      * @return evaluations count
  470.      */
  471.     public int getEvaluationsCount() {
  472.         return evaluationsCounter.getCount();
  473.     }

  474. }