BatchLSEstimator.java

  1. /* Copyright 2002-2020 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.Comparator;
  22. import java.util.List;
  23. import java.util.Map;

  24. import org.hipparchus.exception.LocalizedCoreFormats;
  25. import org.hipparchus.exception.MathIllegalArgumentException;
  26. import org.hipparchus.exception.MathRuntimeException;
  27. import org.hipparchus.linear.RealMatrix;
  28. import org.hipparchus.linear.RealVector;
  29. import org.hipparchus.optim.ConvergenceChecker;
  30. import org.hipparchus.optim.nonlinear.vector.leastsquares.EvaluationRmsChecker;
  31. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
  32. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
  33. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum;
  34. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
  35. import org.hipparchus.optim.nonlinear.vector.leastsquares.ParameterValidator;
  36. import org.hipparchus.util.Incrementor;
  37. import org.orekit.errors.OrekitException;
  38. import org.orekit.estimation.measurements.EstimatedMeasurement;
  39. import org.orekit.estimation.measurements.EstimationsProvider;
  40. import org.orekit.estimation.measurements.ObservedMeasurement;
  41. import org.orekit.orbits.Orbit;
  42. import org.orekit.propagation.conversion.AbstractPropagatorBuilder;
  43. import org.orekit.propagation.conversion.IntegratedPropagatorBuilder;
  44. import org.orekit.propagation.conversion.PropagatorBuilder;
  45. import org.orekit.propagation.integration.AbstractIntegratedPropagator;
  46. import org.orekit.propagation.numerical.NumericalPropagator;
  47. import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
  48. import org.orekit.utils.ParameterDriver;
  49. import org.orekit.utils.ParameterDriversList;
  50. import org.orekit.utils.ParameterDriversList.DelegatingDriver;


  51. /** Least squares estimator for orbit determination.
  52.  * <p>
  53.  * Since 10.0, the least squares estimator can be used with both
  54.  * {@link NumericalPropagator numerical} and {@link DSSTPropagator DSST}
  55.  * orbit propagators.
  56.  * </p>
  57.  * @author Luc Maisonobe
  58.  * @since 8.0
  59.  */
  60. public class BatchLSEstimator {

  61.     /** Builders for propagator. */
  62.     private final IntegratedPropagatorBuilder[] builders;

  63.     /** Measurements. */
  64.     private final List<ObservedMeasurement<?>> measurements;

  65.     /** Solver for least squares problem. */
  66.     private final LeastSquaresOptimizer optimizer;

  67.     /** Convergence checker. */
  68.     private ConvergenceChecker<LeastSquaresProblem.Evaluation> convergenceChecker;

  69.     /** Builder for the least squares problem. */
  70.     private final LeastSquaresBuilder lsBuilder;

  71.     /** Observer for iterations. */
  72.     private BatchLSObserver observer;

  73.     /** Last estimations. */
  74.     private Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> estimations;

  75.     /** Last orbits. */
  76.     private Orbit[] orbits;

  77.     /** Optimum found. */
  78.     private Optimum optimum;

  79.     /** Counter for the evaluations. */
  80.     private Incrementor evaluationsCounter;

  81.     /** Counter for the iterations. */
  82.     private Incrementor iterationsCounter;

  83.     /** Simple constructor.
  84.      * <p>
  85.      * If multiple {@link PropagatorBuilder propagator builders} are set up,
  86.      * the orbits of several spacecrafts will be used simultaneously.
  87.      * This is useful if the propagators share some model or measurements
  88.      * parameters (typically pole motion, prime meridian correction or
  89.      * ground stations positions).
  90.      * </p>
  91.      * <p>
  92.      * Setting up multiple {@link PropagatorBuilder propagator builders} is
  93.      * also useful when inter-satellite measurements are used, even if only one
  94.      * of the orbit is estimated and the other ones are fixed. This is typically
  95.      * used when very high accuracy GNSS measurements are needed and the
  96.      * navigation bulletins are not considered accurate enough and the navigation
  97.      * constellation must be propagated numerically.
  98.      * </p>
  99.      * @param optimizer solver for least squares problem
  100.      * @param propagatorBuilder builders to use for propagation
  101.      */
  102.     public BatchLSEstimator(final LeastSquaresOptimizer optimizer,
  103.                             final IntegratedPropagatorBuilder... propagatorBuilder) {

  104.         this.builders                       = propagatorBuilder;
  105.         this.measurements                   = new ArrayList<ObservedMeasurement<?>>();
  106.         this.optimizer                      = optimizer;
  107.         this.lsBuilder                      = new LeastSquaresBuilder();
  108.         this.observer                       = null;
  109.         this.estimations                    = null;
  110.         this.orbits                         = new Orbit[builders.length];

  111.         setParametersConvergenceThreshold(Double.NaN);

  112.         // our model computes value and Jacobian in one call,
  113.         // so we don't use the lazy evaluation feature
  114.         lsBuilder.lazyEvaluation(false);

  115.         // we manage weight by ourselves, as we change them during
  116.         // iterations (setting to 0 the identified outliers measurements)
  117.         // so the least squares problem should not see our weights
  118.         lsBuilder.weight(null);

  119.     }

  120.     /** Set an observer for iterations.
  121.      * @param observer observer to be notified at the end of each iteration
  122.      */
  123.     public void setObserver(final BatchLSObserver observer) {
  124.         this.observer = observer;
  125.     }

  126.     /** Add a measurement.
  127.      * @param measurement measurement to add
  128.      */
  129.     public void addMeasurement(final ObservedMeasurement<?> measurement) {
  130.         measurements.add(measurement);
  131.     }

  132.     /** Set the maximum number of iterations.
  133.      * <p>
  134.      * The iterations correspond to the top level iterations of
  135.      * the {@link LeastSquaresOptimizer least squares optimizer}.
  136.      * </p>
  137.      * @param maxIterations maxIterations maximum number of iterations
  138.      * @see #setMaxEvaluations(int)
  139.      * @see #getIterationsCount()
  140.      */
  141.     public void setMaxIterations(final int maxIterations) {
  142.         lsBuilder.maxIterations(maxIterations);
  143.     }

  144.     /** Set the maximum number of model evaluations.
  145.      * <p>
  146.      * The evaluations correspond to the orbit propagations and
  147.      * measurements estimations performed with a set of estimated
  148.      * parameters.
  149.      * </p>
  150.      * <p>
  151.      * For {@link org.hipparchus.optim.nonlinear.vector.leastsquares.GaussNewtonOptimizer
  152.      * Gauss-Newton optimizer} there is one evaluation at each iteration,
  153.      * so the maximum numbers may be set to the same value. For {@link
  154.      * org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer
  155.      * Levenberg-Marquardt optimizer}, there can be several evaluations at
  156.      * some iterations (typically for the first couple of iterations), so the
  157.      * maximum number of evaluations may be set to a higher value than the
  158.      * maximum number of iterations.
  159.      * </p>
  160.      * @param maxEvaluations maximum number of model evaluations
  161.      * @see #setMaxIterations(int)
  162.      * @see #getEvaluationsCount()
  163.      */
  164.     public void setMaxEvaluations(final int maxEvaluations) {
  165.         lsBuilder.maxEvaluations(maxEvaluations);
  166.     }

  167.     /** Get the orbital parameters supported by this estimator.
  168.      * <p>
  169.      * If there are more than one propagator builder, then the names
  170.      * of the drivers have an index marker in square brackets appended
  171.      * to them in order to distinguish the various orbits. So for example
  172.      * with one builder generating Keplerian orbits the names would be
  173.      * simply "a", "e", "i"... but if there are several builders the
  174.      * names would be "a[0]", "e[0]", "i[0]"..."a[1]", "e[1]", "i[1]"...
  175.      * </p>
  176.      * @param estimatedOnly if true, only estimated parameters are returned
  177.      * @return orbital parameters supported by this estimator
  178.      */
  179.     public ParameterDriversList getOrbitalParametersDrivers(final boolean estimatedOnly) {

  180.         final ParameterDriversList estimated = new ParameterDriversList();
  181.         for (int i = 0; i < builders.length; ++i) {
  182.             final String suffix = builders.length > 1 ? "[" + i + "]" : null;
  183.             for (final DelegatingDriver delegating : builders[i].getOrbitalParametersDrivers().getDrivers()) {
  184.                 if (delegating.isSelected() || !estimatedOnly) {
  185.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  186.                         if (suffix != null && !driver.getName().endsWith(suffix)) {
  187.                             // we add suffix only conditionally because the method may already have been called
  188.                             // and suffixes may have already been appended
  189.                             driver.setName(driver.getName() + suffix);
  190.                         }
  191.                         estimated.add(driver);
  192.                     }
  193.                 }
  194.             }
  195.         }
  196.         return estimated;

  197.     }

  198.     /** Get the propagator parameters supported by this estimator.
  199.      * @param estimatedOnly if true, only estimated parameters are returned
  200.      * @return propagator parameters supported by this estimator
  201.      */
  202.     public ParameterDriversList getPropagatorParametersDrivers(final boolean estimatedOnly) {

  203.         final ParameterDriversList estimated = new ParameterDriversList();
  204.         for (PropagatorBuilder builder : builders) {
  205.             for (final DelegatingDriver delegating : builder.getPropagationParametersDrivers().getDrivers()) {
  206.                 if (delegating.isSelected() || !estimatedOnly) {
  207.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  208.                         estimated.add(driver);
  209.                     }
  210.                 }
  211.             }
  212.         }
  213.         return estimated;

  214.     }

  215.     /** Get the measurements parameters supported by this estimator (including measurements and modifiers).
  216.      * @param estimatedOnly if true, only estimated parameters are returned
  217.      * @return measurements parameters supported by this estimator
  218.      */
  219.     public ParameterDriversList getMeasurementsParametersDrivers(final boolean estimatedOnly) {

  220.         final ParameterDriversList parameters =  new ParameterDriversList();
  221.         for (final  ObservedMeasurement<?> measurement : measurements) {
  222.             for (final ParameterDriver driver : measurement.getParametersDrivers()) {
  223.                 if ((!estimatedOnly) || driver.isSelected()) {
  224.                     parameters.add(driver);
  225.                 }
  226.             }
  227.         }

  228.         parameters.sort();

  229.         return parameters;

  230.     }

  231.     /**
  232.      * Set convergence threshold.
  233.      * <p>
  234.      * The convergence used for estimation is based on the estimated
  235.      * parameters {@link ParameterDriver#getNormalizedValue() normalized values}.
  236.      * Convergence is considered to have been reached when the difference
  237.      * between previous and current normalized value is less than the
  238.      * convergence threshold for all parameters. The same value is used
  239.      * for all parameters since they are normalized and hence dimensionless.
  240.      * </p>
  241.      * <p>
  242.      * Normalized values are computed as {@code (current - reference)/scale},
  243.      * so convergence is reached when the following condition holds for
  244.      * all estimated parameters:
  245.      * {@code |current[i] - previous[i]| <= threshold * scale[i]}
  246.      * </p>
  247.      * <p>
  248.      * So the convergence threshold specified here can be considered as
  249.      * a multiplication factor applied to scale. Since for all parameters
  250.      * the scale is often small (typically about 1 m for orbital positions
  251.      * for example), then the threshold should not be too small. A value
  252.      * of 10⁻³ is often quite accurate.
  253.      * </p>
  254.      * <p>
  255.      * Calling this method overrides any checker that could have been set
  256.      * beforehand by calling {@link #setConvergenceChecker(ConvergenceChecker)}.
  257.      * Both methods are mutually exclusive.
  258.      * </p>
  259.      *
  260.      * @param parametersConvergenceThreshold convergence threshold on
  261.      * normalized parameters (dimensionless, related to parameters scales)
  262.      * @see #setConvergenceChecker(ConvergenceChecker)
  263.      * @see EvaluationRmsChecker
  264.      */
  265.     public void setParametersConvergenceThreshold(final double parametersConvergenceThreshold) {
  266.         setConvergenceChecker((iteration, previous, current) ->
  267.                               current.getPoint().getLInfDistance(previous.getPoint()) <= parametersConvergenceThreshold);
  268.     }

  269.     /** Set a custom convergence checker.
  270.      * <p>
  271.      * Calling this method overrides any checker that could have been set
  272.      * beforehand by calling {@link #setParametersConvergenceThreshold(double)}.
  273.      * Both methods are mutually exclusive.
  274.      * </p>
  275.      * @param convergenceChecker convergence checker to set
  276.      * @see #setParametersConvergenceThreshold(double)
  277.      * @since 10.1
  278.      */
  279.     public void setConvergenceChecker(final ConvergenceChecker<LeastSquaresProblem.Evaluation> convergenceChecker) {
  280.         this.convergenceChecker = convergenceChecker;
  281.     }

  282.     /** Estimate the orbital, propagation and measurements parameters.
  283.      * <p>
  284.      * The initial guess for all parameters must have been set before calling this method
  285.      * using {@link #getOrbitalParametersDrivers(boolean)}, {@link #getPropagatorParametersDrivers(boolean)},
  286.      * and {@link #getMeasurementsParametersDrivers(boolean)} and then {@link ParameterDriver#setValue(double)
  287.      * setting the values} of the parameters.
  288.      * </p>
  289.      * <p>
  290.      * For parameters whose reference date has not been set to a non-null date beforehand (i.e.
  291.      * the parameters for which {@link ParameterDriver#getReferenceDate()} returns {@code null},
  292.      * a default reference date will be set automatically at the start of the estimation to the
  293.      * {@link AbstractPropagatorBuilder#getInitialOrbitDate() initial orbit date} of the first
  294.      * propagator builder. For parameters whose reference date has been set to a non-null date,
  295.      * this reference date is untouched.
  296.      * </p>
  297.      * <p>
  298.      * After this method returns, the estimated parameters can be retrieved using
  299.      * {@link #getOrbitalParametersDrivers(boolean)}, {@link #getPropagatorParametersDrivers(boolean)},
  300.      * and {@link #getMeasurementsParametersDrivers(boolean)} and then {@link ParameterDriver#getValue()
  301.      * getting the values} of the parameters.
  302.      * </p>
  303.      * <p>
  304.      * As a convenience, the method also returns a fully configured and ready to use
  305.      * propagator set up with all the estimated values.
  306.      * </p>
  307.      * <p>
  308.      * For even more in-depth information, the {@link #getOptimum()} method provides detailed
  309.      * elements (covariance matrix, estimated parameters standard deviation, weighted Jacobian, RMS,
  310.      * χ², residuals and more).
  311.      * </p>
  312.      * @return propagators configured with estimated orbits as initial states, and all
  313.      * propagators estimated parameters also set
  314.      */
  315.     public AbstractIntegratedPropagator[] estimate() {

  316.         // set reference date for all parameters that lack one (including the not estimated parameters)
  317.         for (final ParameterDriver driver : getOrbitalParametersDrivers(false).getDrivers()) {
  318.             if (driver.getReferenceDate() == null) {
  319.                 driver.setReferenceDate(builders[0].getInitialOrbitDate());
  320.             }
  321.         }
  322.         for (final ParameterDriver driver : getPropagatorParametersDrivers(false).getDrivers()) {
  323.             if (driver.getReferenceDate() == null) {
  324.                 driver.setReferenceDate(builders[0].getInitialOrbitDate());
  325.             }
  326.         }
  327.         for (final ParameterDriver driver : getMeasurementsParametersDrivers(false).getDrivers()) {
  328.             if (driver.getReferenceDate() == null) {
  329.                 driver.setReferenceDate(builders[0].getInitialOrbitDate());
  330.             }
  331.         }

  332.         // get all estimated parameters
  333.         final ParameterDriversList estimatedOrbitalParameters      = getOrbitalParametersDrivers(true);
  334.         final ParameterDriversList estimatedPropagatorParameters   = getPropagatorParametersDrivers(true);
  335.         final ParameterDriversList estimatedMeasurementsParameters = getMeasurementsParametersDrivers(true);

  336.         // create start point
  337.         final double[] start = new double[estimatedOrbitalParameters.getNbParams() +
  338.                                           estimatedPropagatorParameters.getNbParams() +
  339.                                           estimatedMeasurementsParameters.getNbParams()];
  340.         int iStart = 0;
  341.         for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
  342.             start[iStart++] = driver.getNormalizedValue();
  343.         }
  344.         for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) {
  345.             start[iStart++] = driver.getNormalizedValue();
  346.         }
  347.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  348.             start[iStart++] = driver.getNormalizedValue();
  349.         }
  350.         lsBuilder.start(start);

  351.         // create target (which is an array set to 0, as we compute weighted residuals ourselves)
  352.         int p = 0;
  353.         for (final ObservedMeasurement<?> measurement : measurements) {
  354.             if (measurement.isEnabled()) {
  355.                 p += measurement.getDimension();
  356.             }
  357.         }
  358.         final double[] target = new double[p];
  359.         lsBuilder.target(target);

  360.         // set up the model
  361.         final ModelObserver modelObserver = new ModelObserver() {
  362.             /** {@inheritDoc} */
  363.             @Override
  364.             public void modelCalled(final Orbit[] newOrbits,
  365.                                     final Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> newEstimations) {
  366.                 BatchLSEstimator.this.orbits      = newOrbits;
  367.                 BatchLSEstimator.this.estimations = newEstimations;
  368.             }
  369.         };
  370.         final BatchLSODModel model = builders[0].buildLSModel(builders, measurements, estimatedMeasurementsParameters, modelObserver);
  371.         //final Model model = new Model(builders, measurements, estimatedMeasurementsParameters,
  372.                                       //modelObserver);
  373.         lsBuilder.model(model);

  374.         // add a validator for orbital parameters
  375.         lsBuilder.parameterValidator(new Validator(estimatedOrbitalParameters,
  376.                                                    estimatedPropagatorParameters,
  377.                                                    estimatedMeasurementsParameters));

  378.         lsBuilder.checker(convergenceChecker);

  379.         // set up the problem to solve
  380.         final LeastSquaresProblem problem = new TappedLSProblem(lsBuilder.build(),
  381.                                                                 model,
  382.                                                                 estimatedOrbitalParameters,
  383.                                                                 estimatedPropagatorParameters,
  384.                                                                 estimatedMeasurementsParameters);

  385.         try {

  386.             // solve the problem
  387.             optimum = optimizer.optimize(problem);

  388.             // create a new configured propagator with all estimated parameters
  389.             return model.createPropagators(optimum.getPoint());

  390.         } catch (MathRuntimeException mrte) {
  391.             throw new OrekitException(mrte);
  392.         }
  393.     }

  394.     /** Get the last estimations performed.
  395.      * @return last estimations performed
  396.      */
  397.     public Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> getLastEstimations() {
  398.         return Collections.unmodifiableMap(estimations);
  399.     }

  400.     /** Get the optimum found.
  401.      * <p>
  402.      * The {@link Optimum} object contains detailed elements (covariance matrix, estimated
  403.      * parameters standard deviation, weighted Jacobian, RMS, χ², residuals and more).
  404.      * </p>
  405.      * <p>
  406.      * Beware that the returned object is the raw view from the underlying mathematical
  407.      * library. At this raw level, parameters have {@link ParameterDriver#getNormalizedValue()
  408.      * normalized values} whereas the space flight parameters have {@link ParameterDriver#getValue()
  409.      * physical values} with their units. So there are {@link ParameterDriver#getScale() scaling
  410.      * factors} to apply when using these elements.
  411.      * </p>
  412.      * @return optimum found after last call to {@link #estimate()}
  413.      */
  414.     public Optimum getOptimum() {
  415.         return optimum;
  416.     }

  417.     /** Get the covariances matrix in space flight dynamics physical units.
  418.      * <p>
  419.      * This method retrieve the {@link
  420.      * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Evaluation#getCovariances(double)
  421.      * covariances} from the [@link {@link #getOptimum() optimum} and applies the scaling factors
  422.      * to it in order to convert it from raw normalized values back to physical values.
  423.      * </p>
  424.      * @param threshold threshold to identify matrix singularity
  425.      * @return covariances matrix in space flight dynamics physical units
  426.      * @since 9.1
  427.      */
  428.     public RealMatrix getPhysicalCovariances(final double threshold) {
  429.         final RealMatrix covariances;
  430.         try {
  431.             // get the normalized matrix
  432.             covariances = optimum.getCovariances(threshold).copy();
  433.         } catch (MathIllegalArgumentException miae) {
  434.             // the problem is singular
  435.             throw new OrekitException(miae);
  436.         }

  437.         // retrieve the scaling factors
  438.         final double[] scale = new double[covariances.getRowDimension()];
  439.         int index = 0;
  440.         for (final ParameterDriver driver : getOrbitalParametersDrivers(true).getDrivers()) {
  441.             scale[index++] = driver.getScale();
  442.         }
  443.         for (final ParameterDriver driver : getPropagatorParametersDrivers(true).getDrivers()) {
  444.             scale[index++] = driver.getScale();
  445.         }
  446.         for (final ParameterDriver driver : getMeasurementsParametersDrivers(true).getDrivers()) {
  447.             scale[index++] = driver.getScale();
  448.         }

  449.         // unnormalize the matrix, to retrieve physical covariances
  450.         for (int i = 0; i < covariances.getRowDimension(); ++i) {
  451.             for (int j = 0; j < covariances.getColumnDimension(); ++j) {
  452.                 covariances.setEntry(i, j, scale[i] * scale[j] * covariances.getEntry(i, j));
  453.             }
  454.         }

  455.         return covariances;

  456.     }

  457.     /** Get the number of iterations used for last estimation.
  458.      * @return number of iterations used for last estimation
  459.      * @see #setMaxIterations(int)
  460.      */
  461.     public int getIterationsCount() {
  462.         return iterationsCounter.getCount();
  463.     }

  464.     /** Get the number of evaluations used for last estimation.
  465.      * @return number of evaluations used for last estimation
  466.      * @see #setMaxEvaluations(int)
  467.      */
  468.     public int getEvaluationsCount() {
  469.         return evaluationsCounter.getCount();
  470.     }

  471.     /** Wrapper used to tap the various counters. */
  472.     private class TappedLSProblem implements LeastSquaresProblem {

  473.         /** Underlying problem. */
  474.         private final LeastSquaresProblem problem;

  475.         /** Multivariate function model. */
  476.         private final BatchLSODModel model;

  477.         /** Estimated orbital parameters. */
  478.         private final ParameterDriversList estimatedOrbitalParameters;

  479.         /** Estimated propagator parameters. */
  480.         private final ParameterDriversList estimatedPropagatorParameters;

  481.         /** Estimated measurements parameters. */
  482.         private final ParameterDriversList estimatedMeasurementsParameters;

  483.         /** Simple constructor.
  484.          * @param problem underlying problem
  485.          * @param model multivariate function model
  486.          * @param estimatedOrbitalParameters estimated orbital parameters
  487.          * @param estimatedPropagatorParameters estimated propagator parameters
  488.          * @param estimatedMeasurementsParameters estimated measurements parameters
  489.          */
  490.         TappedLSProblem(final LeastSquaresProblem problem,
  491.                         final BatchLSODModel model,
  492.                         final ParameterDriversList estimatedOrbitalParameters,
  493.                         final ParameterDriversList estimatedPropagatorParameters,
  494.                         final ParameterDriversList estimatedMeasurementsParameters) {
  495.             this.problem                         = problem;
  496.             this.model                           = model;
  497.             this.estimatedOrbitalParameters      = estimatedOrbitalParameters;
  498.             this.estimatedPropagatorParameters   = estimatedPropagatorParameters;
  499.             this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
  500.         }

  501.         /** {@inheritDoc} */
  502.         @Override
  503.         public Incrementor getEvaluationCounter() {
  504.             // tap the evaluations counter
  505.             BatchLSEstimator.this.evaluationsCounter = problem.getEvaluationCounter();
  506.             model.setEvaluationsCounter(BatchLSEstimator.this.evaluationsCounter);
  507.             return BatchLSEstimator.this.evaluationsCounter;
  508.         }

  509.         /** {@inheritDoc} */
  510.         @Override
  511.         public Incrementor getIterationCounter() {
  512.             // tap the iterations counter
  513.             BatchLSEstimator.this.iterationsCounter = problem.getIterationCounter();
  514.             model.setIterationsCounter(BatchLSEstimator.this.iterationsCounter);
  515.             return BatchLSEstimator.this.iterationsCounter;
  516.         }

  517.         /** {@inheritDoc} */
  518.         @Override
  519.         public ConvergenceChecker<Evaluation> getConvergenceChecker() {
  520.             return problem.getConvergenceChecker();
  521.         }

  522.         /** {@inheritDoc} */
  523.         @Override
  524.         public RealVector getStart() {
  525.             return problem.getStart();
  526.         }

  527.         /** {@inheritDoc} */
  528.         @Override
  529.         public int getObservationSize() {
  530.             return problem.getObservationSize();
  531.         }

  532.         /** {@inheritDoc} */
  533.         @Override
  534.         public int getParameterSize() {
  535.             return problem.getParameterSize();
  536.         }

  537.         /** {@inheritDoc} */
  538.         @Override
  539.         public Evaluation evaluate(final RealVector point) {

  540.             // perform the evaluation
  541.             final Evaluation evaluation = problem.evaluate(point);

  542.             // notify the observer
  543.             if (observer != null) {
  544.                 observer.evaluationPerformed(iterationsCounter.getCount(),
  545.                                              evaluationsCounter.getCount(),
  546.                                              orbits,
  547.                                              estimatedOrbitalParameters,
  548.                                              estimatedPropagatorParameters,
  549.                                              estimatedMeasurementsParameters,
  550.                                              new Provider(),
  551.                                              evaluation);
  552.             }

  553.             return evaluation;

  554.         }

  555.     }

  556.     /** Provider for evaluations. */
  557.     private class Provider implements EstimationsProvider {

  558.         /** Sorted estimations. */
  559.         private EstimatedMeasurement<?>[] sortedEstimations;

  560.         /** {@inheritDoc} */
  561.         @Override
  562.         public int getNumber() {
  563.             return estimations.size();
  564.         }

  565.         /** {@inheritDoc} */
  566.         @Override
  567.         public EstimatedMeasurement<?> getEstimatedMeasurement(final int index) {

  568.             // safety checks
  569.             if (index < 0 || index >= estimations.size()) {
  570.                 throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
  571.                                           index, 0, estimations.size());
  572.             }

  573.             if (sortedEstimations == null) {

  574.                 // lazy evaluation of the sorted array
  575.                 sortedEstimations = new EstimatedMeasurement<?>[estimations.size()];
  576.                 int i = 0;
  577.                 for (final Map.Entry<ObservedMeasurement<?>, EstimatedMeasurement<?>> entry : estimations.entrySet()) {
  578.                     sortedEstimations[i++] = entry.getValue();
  579.                 }

  580.                 // sort the array, primarily chronologically
  581.                 Arrays.sort(sortedEstimations, 0, sortedEstimations.length, Comparator.naturalOrder());

  582.             }

  583.             return sortedEstimations[index];

  584.         }

  585.     }

  586.     /** Validator for estimated parameters. */
  587.     private static class Validator implements ParameterValidator {

  588.         /** Estimated orbital parameters. */
  589.         private final ParameterDriversList estimatedOrbitalParameters;

  590.         /** Estimated propagator parameters. */
  591.         private final ParameterDriversList estimatedPropagatorParameters;

  592.         /** Estimated measurements parameters. */
  593.         private final ParameterDriversList estimatedMeasurementsParameters;

  594.         /** Simple constructor.
  595.          * @param estimatedOrbitalParameters estimated orbital parameters
  596.          * @param estimatedPropagatorParameters estimated propagator parameters
  597.          * @param estimatedMeasurementsParameters estimated measurements parameters
  598.          */
  599.         Validator(final ParameterDriversList estimatedOrbitalParameters,
  600.                   final ParameterDriversList estimatedPropagatorParameters,
  601.                   final ParameterDriversList estimatedMeasurementsParameters) {
  602.             this.estimatedOrbitalParameters      = estimatedOrbitalParameters;
  603.             this.estimatedPropagatorParameters   = estimatedPropagatorParameters;
  604.             this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
  605.         }

  606.         /** {@inheritDoc} */
  607.         @Override
  608.         public RealVector validate(final RealVector params) {

  609.             int i = 0;
  610.             for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
  611.                 // let the parameter handle min/max clipping
  612.                 driver.setNormalizedValue(params.getEntry(i));
  613.                 params.setEntry(i++, driver.getNormalizedValue());
  614.             }
  615.             for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) {
  616.                 // let the parameter handle min/max clipping
  617.                 driver.setNormalizedValue(params.getEntry(i));
  618.                 params.setEntry(i++, driver.getNormalizedValue());
  619.             }
  620.             for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  621.                 // let the parameter handle min/max clipping
  622.                 driver.setNormalizedValue(params.getEntry(i));
  623.                 params.setEntry(i++, driver.getNormalizedValue());
  624.             }

  625.             return params;
  626.         }
  627.     }

  628. }