Model.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.sequential;

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

  24. import org.hipparchus.filtering.kalman.ProcessEstimate;
  25. import org.hipparchus.filtering.kalman.extended.NonLinearEvolution;
  26. import org.hipparchus.filtering.kalman.extended.NonLinearProcess;
  27. import org.hipparchus.linear.Array2DRowRealMatrix;
  28. import org.hipparchus.linear.ArrayRealVector;
  29. import org.hipparchus.linear.MatrixUtils;
  30. import org.hipparchus.linear.RealMatrix;
  31. import org.hipparchus.linear.RealVector;
  32. import org.hipparchus.util.FastMath;
  33. import org.orekit.errors.OrekitException;
  34. import org.orekit.errors.OrekitMessages;
  35. import org.orekit.estimation.measurements.EstimatedMeasurement;
  36. import org.orekit.estimation.measurements.EstimationModifier;
  37. import org.orekit.estimation.measurements.ObservedMeasurement;
  38. import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter;
  39. import org.orekit.orbits.Orbit;
  40. import org.orekit.propagation.SpacecraftState;
  41. import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
  42. import org.orekit.propagation.numerical.JacobiansMapper;
  43. import org.orekit.propagation.numerical.NumericalPropagator;
  44. import org.orekit.propagation.numerical.PartialDerivativesEquations;
  45. import org.orekit.time.AbsoluteDate;
  46. import org.orekit.utils.ParameterDriver;
  47. import org.orekit.utils.ParameterDriversList;
  48. import org.orekit.utils.ParameterDriversList.DelegatingDriver;


  49. /** Class defining the process model dynamics to use with a {@link KalmanEstimator}.
  50.  * @author Romain Gerbaud
  51.  * @author Maxime Journot
  52.  * @since 9.2
  53.  */
  54. public class Model implements KalmanEstimation, NonLinearProcess<MeasurementDecorator> {

  55.     /** Builders for propagators. */
  56.     private final List<NumericalPropagatorBuilder> builders;

  57.     /** Estimated orbital parameters. */
  58.     private final ParameterDriversList allEstimatedOrbitalParameters;

  59.     /** Estimated propagation drivers. */
  60.     private final ParameterDriversList allEstimatedPropagationParameters;

  61.     /** Per-builder estimated propagation drivers. */
  62.     private final ParameterDriversList[] estimatedPropagationParameters;

  63.     /** Estimated measurements parameters. */
  64.     private final ParameterDriversList estimatedMeasurementsParameters;

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

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

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

  71.     /** Providers for covariance matrices. */
  72.     private final List<CovarianceMatrixProvider> covarianceMatricesProviders;

  73.     /** Indirection arrays to extract the noise components for estimated parameters. */
  74.     private final int[][] covarianceIndirection;

  75.     /** Scaling factors. */
  76.     private final double[] scale;

  77.     /** Mappers for extracting Jacobians from integrated states. */
  78.     private final JacobiansMapper[] mappers;

  79.     /** Propagators for the reference trajectories, up to current date. */
  80.     private NumericalPropagator[] referenceTrajectories;

  81.     /** Current corrected estimate. */
  82.     private ProcessEstimate correctedEstimate;

  83.     /** Current number of measurement. */
  84.     private int currentMeasurementNumber;

  85.     /** Reference date. */
  86.     private AbsoluteDate referenceDate;

  87.     /** Current date. */
  88.     private AbsoluteDate currentDate;

  89.     /** Predicted spacecraft states. */
  90.     private SpacecraftState[] predictedSpacecraftStates;

  91.     /** Corrected spacecraft states. */
  92.     private SpacecraftState[] correctedSpacecraftStates;

  93.     /** Predicted measurement. */
  94.     private EstimatedMeasurement<?> predictedMeasurement;

  95.     /** Corrected measurement. */
  96.     private EstimatedMeasurement<?> correctedMeasurement;

  97.     /** Kalman process model constructor (package private).
  98.      * @param propagatorBuilders propagators builders used to evaluate the orbits.
  99.      * @param covarianceMatricesProviders providers for covariance matrices
  100.      * @param estimatedMeasurementParameters measurement parameters to estimate
  101.      */
  102.     Model(final List<NumericalPropagatorBuilder> propagatorBuilders,
  103.           final List<CovarianceMatrixProvider> covarianceMatricesProviders,
  104.           final ParameterDriversList estimatedMeasurementParameters) {

  105.         this.builders                        = propagatorBuilders;
  106.         this.estimatedMeasurementsParameters = estimatedMeasurementParameters;
  107.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  108.         this.currentMeasurementNumber        = 0;
  109.         this.referenceDate                   = propagatorBuilders.get(0).getInitialOrbitDate();
  110.         this.currentDate                     = referenceDate;

  111.         final Map<String, Integer> orbitalParameterColumns = new HashMap<>(6 * builders.size());
  112.         orbitsStartColumns      = new int[builders.size()];
  113.         orbitsEndColumns        = new int[builders.size()];
  114.         int columns = 0;
  115.         allEstimatedOrbitalParameters = new ParameterDriversList();
  116.         for (int k = 0; k < builders.size(); ++k) {
  117.             orbitsStartColumns[k] = columns;
  118.             final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null;
  119.             for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  120.                 if (driver.getReferenceDate() == null) {
  121.                     driver.setReferenceDate(currentDate);
  122.                 }
  123.                 if (suffix != null && !driver.getName().endsWith(suffix)) {
  124.                     // we add suffix only conditionally because the method may already have been called
  125.                     // and suffixes may have already been appended
  126.                     driver.setName(driver.getName() + suffix);
  127.                 }
  128.                 if (driver.isSelected()) {
  129.                     allEstimatedOrbitalParameters.add(driver);
  130.                     orbitalParameterColumns.put(driver.getName(), columns++);
  131.                 }
  132.             }
  133.             orbitsEndColumns[k] = columns;
  134.         }

  135.         // Gather all the propagation drivers names in a list
  136.         allEstimatedPropagationParameters = new ParameterDriversList();
  137.         estimatedPropagationParameters    = new ParameterDriversList[builders.size()];
  138.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  139.         for (int k = 0; k < builders.size(); ++k) {
  140.             estimatedPropagationParameters[k] = new ParameterDriversList();
  141.             for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) {
  142.                 if (driver.getReferenceDate() == null) {
  143.                     driver.setReferenceDate(currentDate);
  144.                 }
  145.                 if (driver.isSelected()) {
  146.                     allEstimatedPropagationParameters.add(driver);
  147.                     estimatedPropagationParameters[k].add(driver);
  148.                     final String driverName = driver.getName();
  149.                     // Add the driver name if it has not been added yet
  150.                     if (!estimatedPropagationParametersNames.contains(driverName)) {
  151.                         estimatedPropagationParametersNames.add(driverName);
  152.                     }
  153.                 }
  154.             }
  155.         }
  156.         estimatedPropagationParametersNames.sort(Comparator.naturalOrder());

  157.         // Populate the map of propagation drivers' columns and update the total number of columns
  158.         final Map<String, Integer> propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  159.         for (final String driverName : estimatedPropagationParametersNames) {
  160.             propagationParameterColumns.put(driverName, columns);
  161.             ++columns;
  162.         }

  163.         // Populate the map of measurement drivers' columns and update the total number of columns
  164.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  165.             if (parameter.getReferenceDate() == null) {
  166.                 parameter.setReferenceDate(currentDate);
  167.             }
  168.             measurementParameterColumns.put(parameter.getName(), columns);
  169.             ++columns;
  170.         }

  171.         // Store providers for process noise matrices
  172.         this.covarianceMatricesProviders = covarianceMatricesProviders;
  173.         this.covarianceIndirection       = new int[covarianceMatricesProviders.size()][columns];
  174.         for (int k = 0; k < covarianceIndirection.length; ++k) {
  175.             final ParameterDriversList orbitDrivers      = builders.get(k).getOrbitalParametersDrivers();
  176.             final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers();
  177.             Arrays.fill(covarianceIndirection[k], -1);
  178.             int i = 0;
  179.             for (final ParameterDriver driver : orbitDrivers.getDrivers()) {
  180.                 final Integer c = orbitalParameterColumns.get(driver.getName());
  181.                 covarianceIndirection[k][i++] = (c == null) ? -1 : c.intValue();
  182.             }
  183.             for (final ParameterDriver driver : parametersDrivers.getDrivers()) {
  184.                 final Integer c = propagationParameterColumns.get(driver.getName());
  185.                 if (c != null) {
  186.                     covarianceIndirection[k][i++] = c.intValue();
  187.                 }
  188.             }
  189.             for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) {
  190.                 final Integer c = measurementParameterColumns.get(driver.getName());
  191.                 if (c != null) {
  192.                     covarianceIndirection[k][i++] = c.intValue();
  193.                 }
  194.             }
  195.         }

  196.         // Compute the scale factors
  197.         this.scale = new double[columns];
  198.         int index = 0;
  199.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  200.             scale[index++] = driver.getScale();
  201.         }
  202.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  203.             scale[index++] = driver.getScale();
  204.         }
  205.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  206.             scale[index++] = driver.getScale();
  207.         }

  208.         // Build the reference propagators and add their partial derivatives equations implementation
  209.         mappers = new JacobiansMapper[builders.size()];
  210.         updateReferenceTrajectories(getEstimatedPropagators());
  211.         this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length];
  212.         for (int i = 0; i < predictedSpacecraftStates.length; ++i) {
  213.             predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState();
  214.         };
  215.         this.correctedSpacecraftStates = predictedSpacecraftStates.clone();

  216.         // Initialize the estimated normalized state and fill its values
  217.         final RealVector correctedState      = MatrixUtils.createRealVector(columns);

  218.         int p = 0;
  219.         for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) {
  220.             correctedState.setEntry(p++, driver.getNormalizedValue());
  221.         }
  222.         for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) {
  223.             correctedState.setEntry(p++, driver.getNormalizedValue());
  224.         }
  225.         for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
  226.             correctedState.setEntry(p++, driver.getNormalizedValue());
  227.         }

  228.         // Set up initial covariance
  229.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns);
  230.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {
  231.             final RealMatrix noiseK = covarianceMatricesProviders.get(k).
  232.                                       getInitialCovarianceMatrix(correctedSpacecraftStates[k]);
  233.             checkDimension(noiseK.getRowDimension(),
  234.                            builders.get(k).getOrbitalParametersDrivers(),
  235.                            builders.get(k).getPropagationParametersDrivers(),
  236.                            estimatedMeasurementsParameters);
  237.             final int[] indK = covarianceIndirection[k];
  238.             for (int i = 0; i < indK.length; ++i) {
  239.                 if (indK[i] >= 0) {
  240.                     for (int j = 0; j < indK.length; ++j) {
  241.                         if (indK[j] >= 0) {
  242.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  243.                         }
  244.                     }
  245.                 }
  246.             }

  247.         }
  248.         final RealMatrix correctedCovariance = normalizeCovarianceMatrix(physicalProcessNoise);

  249.         correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance);

  250.     }

  251.     /** Check dimension.
  252.      * @param dimension dimension to check
  253.      * @param orbitalParameters orbital parameters
  254.      * @param propagationParameters propagation parameters
  255.      * @param measurementParameters measurements parameters
  256.      */
  257.     private void checkDimension(final int dimension,
  258.                                 final ParameterDriversList orbitalParameters,
  259.                                 final ParameterDriversList propagationParameters,
  260.                                 final ParameterDriversList measurementParameters) {

  261.         // count parameters, taking care of counting all orbital parameters
  262.         // regardless of them being estimated or not
  263.         int requiredDimension = orbitalParameters.getNbParams();
  264.         for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  265.             if (driver.isSelected()) {
  266.                 ++requiredDimension;
  267.             }
  268.         }
  269.         for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  270.             if (driver.isSelected()) {
  271.                 ++requiredDimension;
  272.             }
  273.         }

  274.         if (dimension != requiredDimension) {
  275.             // there is a problem, set up an explicit error message
  276.             final StringBuilder builder = new StringBuilder();
  277.             for (final ParameterDriver driver : orbitalParameters.getDrivers()) {
  278.                 if (builder.length() > 0) {
  279.                     builder.append(", ");
  280.                 }
  281.                 builder.append(driver.getName());
  282.             }
  283.             for (final ParameterDriver driver : propagationParameters.getDrivers()) {
  284.                 if (driver.isSelected()) {
  285.                     builder.append(driver.getName());
  286.                 }
  287.             }
  288.             for (final ParameterDriver driver : measurementParameters.getDrivers()) {
  289.                 if (driver.isSelected()) {
  290.                     builder.append(driver.getName());
  291.                 }
  292.             }
  293.             throw new OrekitException(OrekitMessages.DIMENSION_INCONSISTENT_WITH_PARAMETERS,
  294.                                       dimension, builder.toString());
  295.         }

  296.     }

  297.     /** {@inheritDoc} */
  298.     @Override
  299.     public ParameterDriversList getEstimatedOrbitalParameters() {
  300.         return allEstimatedOrbitalParameters;
  301.     }

  302.     /** {@inheritDoc} */
  303.     @Override
  304.     public ParameterDriversList getEstimatedPropagationParameters() {
  305.         return allEstimatedPropagationParameters;
  306.     }

  307.     /** {@inheritDoc} */
  308.     @Override
  309.     public ParameterDriversList getEstimatedMeasurementsParameters() {
  310.         return estimatedMeasurementsParameters;
  311.     }

  312.     /** {@inheritDoc} */
  313.     @Override
  314.     public SpacecraftState[] getPredictedSpacecraftStates() {
  315.         return predictedSpacecraftStates.clone();
  316.     }

  317.     /** {@inheritDoc} */
  318.     @Override
  319.     public SpacecraftState[] getCorrectedSpacecraftStates() {
  320.         return correctedSpacecraftStates.clone();
  321.     }

  322.     /** {@inheritDoc} */
  323.     @Override
  324.     public RealVector getPhysicalEstimatedState() {
  325.         // Method {@link ParameterDriver#getValue()} is used to get
  326.         // the physical values of the state.
  327.         // The scales'array is used to get the size of the state vector
  328.         final RealVector physicalEstimatedState = new ArrayRealVector(scale.length);
  329.         int i = 0;
  330.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  331.             physicalEstimatedState.setEntry(i++, driver.getValue());
  332.         }
  333.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  334.             physicalEstimatedState.setEntry(i++, driver.getValue());
  335.         }
  336.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  337.             physicalEstimatedState.setEntry(i++, driver.getValue());
  338.         }

  339.         return physicalEstimatedState;
  340.     }


  341.     /** {@inheritDoc} */
  342.     @Override
  343.     public RealMatrix getPhysicalEstimatedCovarianceMatrix() {
  344.         // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it.
  345.         // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  346.         // For each element [i,j] of P the corresponding normalized value is:
  347.         // Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  348.         // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j]

  349.         // Normalized covariance matrix
  350.         final RealMatrix normalizedP = correctedEstimate.getCovariance();

  351.         // Initialize physical covariance matrix
  352.         final int nbParams = normalizedP.getRowDimension();
  353.         final RealMatrix physicalP = MatrixUtils.createRealMatrix(nbParams, nbParams);

  354.         // Un-normalize the covairance matrix
  355.         for (int i = 0; i < nbParams; ++i) {
  356.             for (int j = 0; j < nbParams; ++j) {
  357.                 physicalP.setEntry(i, j, normalizedP.getEntry(i, j) * scale[i] * scale[j]);
  358.             }
  359.         }
  360.         return physicalP;
  361.     }

  362.     /** {@inheritDoc} */
  363.     @Override
  364.     public RealMatrix getPhysicalStateTransitionMatrix() {
  365.         //  Un-normalize the state transition matrix (φ) from Hipparchus and return it.
  366.         // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  367.         // For each element [i,j] of normalized φ (φn), the corresponding physical value is:
  368.         // φ[i,j] = φn[i,j] * scale[i] / scale[j]

  369.         // Normalized matrix
  370.         final RealMatrix normalizedSTM = correctedEstimate.getStateTransitionMatrix();

  371.         if (normalizedSTM == null) {
  372.             return null;
  373.         } else {
  374.             // Initialize physical matrix
  375.             final int nbParams = normalizedSTM.getRowDimension();
  376.             final RealMatrix physicalSTM = MatrixUtils.createRealMatrix(nbParams, nbParams);

  377.             // Un-normalize the matrix
  378.             for (int i = 0; i < nbParams; ++i) {
  379.                 for (int j = 0; j < nbParams; ++j) {
  380.                     physicalSTM.setEntry(i, j,
  381.                                          normalizedSTM.getEntry(i, j) * scale[i] / scale[j]);
  382.                 }
  383.             }
  384.             return physicalSTM;
  385.         }
  386.     }

  387.     /** {@inheritDoc} */
  388.     @Override
  389.     public RealMatrix getPhysicalMeasurementJacobian() {
  390.         // Un-normalize the measurement matrix (H) from Hipparchus and return it.
  391.         // H is an nxm matrix where:
  392.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  393.         //  - n is the size of the measurement being processed by the filter
  394.         // For each element [i,j] of normalized H (Hn) the corresponding physical value is:
  395.         // H[i,j] = Hn[i,j] * σ[i] / scale[j]

  396.         // Normalized matrix
  397.         final RealMatrix normalizedH = correctedEstimate.getMeasurementJacobian();

  398.         if (normalizedH == null) {
  399.             return null;
  400.         } else {
  401.             // Get current measurement sigmas
  402.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  403.             // Initialize physical matrix
  404.             final int nbLine = normalizedH.getRowDimension();
  405.             final int nbCol  = normalizedH.getColumnDimension();
  406.             final RealMatrix physicalH = MatrixUtils.createRealMatrix(nbLine, nbCol);

  407.             // Un-normalize the matrix
  408.             for (int i = 0; i < nbLine; ++i) {
  409.                 for (int j = 0; j < nbCol; ++j) {
  410.                     physicalH.setEntry(i, j, normalizedH.getEntry(i, j) * sigmas[i] / scale[j]);
  411.                 }
  412.             }
  413.             return physicalH;
  414.         }
  415.     }

  416.     /** {@inheritDoc} */
  417.     @Override
  418.     public RealMatrix getPhysicalInnovationCovarianceMatrix() {
  419.         // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it.
  420.         // S is an nxn matrix where n is the size of the measurement being processed by the filter
  421.         // For each element [i,j] of normalized S (Sn) the corresponding physical value is:
  422.         // S[i,j] = Sn[i,j] * σ[i] * σ[j]

  423.         // Normalized matrix
  424.         final RealMatrix normalizedS = correctedEstimate.getInnovationCovariance();

  425.         if (normalizedS == null) {
  426.             return null;
  427.         } else {
  428.             // Get current measurement sigmas
  429.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  430.             // Initialize physical matrix
  431.             final int nbMeas = sigmas.length;
  432.             final RealMatrix physicalS = MatrixUtils.createRealMatrix(nbMeas, nbMeas);

  433.             // Un-normalize the matrix
  434.             for (int i = 0; i < nbMeas; ++i) {
  435.                 for (int j = 0; j < nbMeas; ++j) {
  436.                     physicalS.setEntry(i, j, normalizedS.getEntry(i, j) * sigmas[i] *   sigmas[j]);
  437.                 }
  438.             }
  439.             return physicalS;
  440.         }
  441.     }

  442.     /** {@inheritDoc} */
  443.     @Override
  444.     public RealMatrix getPhysicalKalmanGain() {
  445.         // Un-normalize the Kalman gain (K) from Hipparchus and return it.
  446.         // K is an mxn matrix where:
  447.         //  - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters
  448.         //  - n is the size of the measurement being processed by the filter
  449.         // For each element [i,j] of normalized K (Kn) the corresponding physical value is:
  450.         // K[i,j] = Kn[i,j] * scale[i] / σ[j]

  451.         // Normalized matrix
  452.         final RealMatrix normalizedK = correctedEstimate.getKalmanGain();

  453.         if (normalizedK == null) {
  454.             return null;
  455.         } else {
  456.             // Get current measurement sigmas
  457.             final double[] sigmas = correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();

  458.             // Initialize physical matrix
  459.             final int nbLine = normalizedK.getRowDimension();
  460.             final int nbCol  = normalizedK.getColumnDimension();
  461.             final RealMatrix physicalK = MatrixUtils.createRealMatrix(nbLine, nbCol);

  462.             // Un-normalize the matrix
  463.             for (int i = 0; i < nbLine; ++i) {
  464.                 for (int j = 0; j < nbCol; ++j) {
  465.                     physicalK.setEntry(i, j, normalizedK.getEntry(i, j) * scale[i] / sigmas[j]);
  466.                 }
  467.             }
  468.             return physicalK;
  469.         }
  470.     }

  471.     /** {@inheritDoc} */
  472.     @Override
  473.     public int getCurrentMeasurementNumber() {
  474.         return currentMeasurementNumber;
  475.     }

  476.     /** {@inheritDoc} */
  477.     @Override
  478.     public AbsoluteDate getCurrentDate() {
  479.         return currentDate;
  480.     }

  481.     /** {@inheritDoc} */
  482.     @Override
  483.     public EstimatedMeasurement<?> getPredictedMeasurement() {
  484.         return predictedMeasurement;
  485.     }

  486.     /** {@inheritDoc} */
  487.     @Override
  488.     public EstimatedMeasurement<?> getCorrectedMeasurement() {
  489.         return correctedMeasurement;
  490.     }

  491.     /** Get the current corrected estimate.
  492.      * @return current corrected estimate
  493.      */
  494.     public ProcessEstimate getEstimate() {
  495.         return correctedEstimate;
  496.     }

  497.     /** Get the propagators estimated with the values set in the propagators builders.
  498.      * @return numerical propagators based on the current values in the builder
  499.      */
  500.     public NumericalPropagator[] getEstimatedPropagators() {

  501.         // Return propagators built with current instantiation of the propagator builders
  502.         final NumericalPropagator[] propagators = new NumericalPropagator[builders.size()];
  503.         for (int k = 0; k < builders.size(); ++k) {
  504.             propagators[k] = builders.get(k).buildPropagator(builders.get(k).getSelectedNormalizedParameters());
  505.         }
  506.         return propagators;
  507.     }

  508.     /** Get the normalized error state transition matrix (STM) from previous point to current point.
  509.      * The STM contains the partial derivatives of current state with respect to previous state.
  510.      * The  STM is an mxm matrix where m is the size of the state vector.
  511.      * m = nbOrb + nbPropag + nbMeas
  512.      * @return the normalized error state transition matrix
  513.      */
  514.     private RealMatrix getErrorStateTransitionMatrix() {

  515.         /* The state transition matrix is obtained as follows, with:
  516.          *  - Y  : Current state vector
  517.          *  - Y0 : Initial state vector
  518.          *  - Pp : Current propagation parameter
  519.          *  - Pp0: Initial propagation parameter
  520.          *  - Mp : Current measurement parameter
  521.          *  - Mp0: Initial measurement parameter
  522.          *
  523.          *       |        |         |         |   |        |        |   .    |
  524.          *       | dY/dY0 | dY/dPp  | dY/dMp  |   | dY/dY0 | dY/dPp | ..0..  |
  525.          *       |        |         |         |   |        |        |   .    |
  526.          *       |--------|---------|---------|   |--------|--------|--------|
  527.          *       |        |         |         |   |   .    | 1 0 0..|   .    |
  528.          * STM = | dP/dY0 | dP/dPp0 | dP/dMp  | = | ..0..  | 0 1 0..| ..0..  |
  529.          *       |        |         |         |   |   .    | 0 0 1..|   .    |
  530.          *       |--------|---------|---------|   |--------|--------|--------|
  531.          *       |        |         |         |   |   .    |   .    | 1 0 0..|
  532.          *       | dM/dY0 | dM/dPp0 | dM/dMp0 |   | ..0..  | ..0..  | 0 1 0..|
  533.          *       |        |         |         |   |   .    |   .    | 0 0 1..|
  534.          */

  535.         // Initialize to the proper size identity matrix
  536.         final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension());

  537.         // loop over all orbits
  538.         for (int k = 0; k < predictedSpacecraftStates.length; ++k) {

  539.             // Derivatives of the state vector with respect to initial state vector
  540.             final double[][] dYdY0 = new double[6][6];
  541.             mappers[k].getStateJacobian(predictedSpacecraftStates[k], dYdY0 );

  542.             // Fill upper left corner (dY/dY0)
  543.             final List<ParameterDriversList.DelegatingDriver> drivers =
  544.                             builders.get(k).getOrbitalParametersDrivers().getDrivers();
  545.             for (int i = 0; i < dYdY0.length; ++i) {
  546.                 if (drivers.get(i).isSelected()) {
  547.                     int jOrb = orbitsStartColumns[k];
  548.                     for (int j = 0; j < dYdY0[i].length; ++j) {
  549.                         if (drivers.get(j).isSelected()) {
  550.                             stm.setEntry(i, jOrb++, dYdY0[i][j]);
  551.                         }
  552.                     }
  553.                 }
  554.             }

  555.             // Derivatives of the state vector with respect to propagation parameters
  556.             final int nbParams = estimatedPropagationParameters[k].getNbParams();
  557.             if (nbParams > 0) {
  558.                 final double[][] dYdPp  = new double[6][nbParams];
  559.                 mappers[k].getParametersJacobian(predictedSpacecraftStates[k], dYdPp);

  560.                 // Fill 1st row, 2nd column (dY/dPp)
  561.                 for (int i = 0; i < dYdPp.length; ++i) {
  562.                     for (int j = 0; j < nbParams; ++j) {
  563.                         stm.setEntry(i, orbitsEndColumns[k] + j, dYdPp[i][j]);
  564.                     }
  565.                 }

  566.             }

  567.         }

  568.         // Normalization of the STM
  569.         // normalized(STM)ij = STMij*Sj/Si
  570.         for (int i = 0; i < scale.length; i++) {
  571.             for (int j = 0; j < scale.length; j++ ) {
  572.                 stm.setEntry(i, j, stm.getEntry(i, j) * scale[j] / scale[i]);
  573.             }
  574.         }

  575.         // Return the error state transition matrix
  576.         return stm;

  577.     }

  578.     /** Get the normalized measurement matrix H.
  579.      * H contains the partial derivatives of the measurement with respect to the state.
  580.      * H is an nxm matrix where n is the size of the measurement vector and m the size of the state vector.
  581.      * @return the normalized measurement matrix H
  582.      */
  583.     private RealMatrix getMeasurementMatrix() {

  584.         // Observed measurement characteristics
  585.         final SpacecraftState[]      evaluationStates    = predictedMeasurement.getStates();
  586.         final ObservedMeasurement<?> observedMeasurement = predictedMeasurement.getObservedMeasurement();
  587.         final double[] sigma  = observedMeasurement.getTheoreticalStandardDeviation();

  588.         // Initialize measurement matrix H: nxm
  589.         // n: Number of measurements in current measurement
  590.         // m: State vector size
  591.         final RealMatrix measurementMatrix = MatrixUtils.
  592.                         createRealMatrix(observedMeasurement.getDimension(),
  593.                                          correctedEstimate.getState().getDimension());

  594.         // loop over all orbits involved in the measurement
  595.         for (int k = 0; k < evaluationStates.length; ++k) {
  596.             final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex();

  597.             // Predicted orbit
  598.             final Orbit predictedOrbit = evaluationStates[k].getOrbit();

  599.             // Measurement matrix's columns related to orbital parameters
  600.             // ----------------------------------------------------------

  601.             // Partial derivatives of the current Cartesian coordinates with respect to current orbital state
  602.             final double[][] aCY = new double[6][6];
  603.             predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngle(), aCY);   //dC/dY
  604.             final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

  605.             // Jacobian of the measurement with respect to current Cartesian coordinates
  606.             final RealMatrix dMdC = new Array2DRowRealMatrix(predictedMeasurement.getStateDerivatives(k), false);

  607.             // Jacobian of the measurement with respect to current orbital state
  608.             final RealMatrix dMdY = dMdC.multiply(dCdY);

  609.             // Fill the normalized measurement matrix's columns related to estimated orbital parameters
  610.             for (int i = 0; i < dMdY.getRowDimension(); ++i) {
  611.                 int jOrb = orbitsStartColumns[p];
  612.                 for (int j = 0; j < dMdY.getColumnDimension(); ++j) {
  613.                     final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j);
  614.                     if (driver.isSelected()) {
  615.                         measurementMatrix.setEntry(i, jOrb++,
  616.                                                    dMdY.getEntry(i, j) / sigma[i] * driver.getScale());
  617.                     }
  618.                 }
  619.             }

  620.             // Normalized measurement matrix's columns related to propagation parameters
  621.             // --------------------------------------------------------------

  622.             // Jacobian of the measurement with respect to propagation parameters
  623.             final int nbParams = estimatedPropagationParameters[p].getNbParams();
  624.             if (nbParams > 0) {
  625.                 final double[][] aYPp  = new double[6][nbParams];
  626.                 mappers[p].getParametersJacobian(evaluationStates[k], aYPp);
  627.                 final RealMatrix dYdPp = new Array2DRowRealMatrix(aYPp, false);
  628.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  629.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  630.                     for (int j = 0; j < nbParams; ++j) {
  631.                         final ParameterDriver delegating = allEstimatedPropagationParameters.getDrivers().get(j);
  632.                         measurementMatrix.setEntry(i, orbitsEndColumns[p] + j,
  633.                                                    dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  634.                     }
  635.                 }
  636.             }

  637.             // Normalized measurement matrix's columns related to measurement parameters
  638.             // --------------------------------------------------------------

  639.             // Jacobian of the measurement with respect to measurement parameters
  640.             // Gather the measurement parameters linked to current measurement
  641.             for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  642.                 if (driver.isSelected()) {
  643.                     // Derivatives of current measurement w/r to selected measurement parameter
  644.                     final double[] aMPm = predictedMeasurement.getParameterDerivatives(driver);

  645.                     // Check that the measurement parameter is managed by the filter
  646.                     if (measurementParameterColumns.get(driver.getName()) != null) {
  647.                         // Column of the driver in the measurement matrix
  648.                         final int driverColumn = measurementParameterColumns.get(driver.getName());

  649.                         // Fill the corresponding indexes of the measurement matrix
  650.                         for (int i = 0; i < aMPm.length; ++i) {
  651.                             measurementMatrix.setEntry(i, driverColumn,
  652.                                                        aMPm[i] / sigma[i] * driver.getScale());
  653.                         }
  654.                     }
  655.                 }
  656.             }
  657.         }

  658.         // Return the normalized measurement matrix
  659.         return measurementMatrix;

  660.     }


  661.     /** Update the reference trajectories using the propagators as input.
  662.      * @param propagators The new propagators to use
  663.      */
  664.     private void updateReferenceTrajectories(final NumericalPropagator[] propagators) {

  665.         // Update the reference trajectory propagator
  666.         referenceTrajectories = propagators;

  667.         for (int k = 0; k < propagators.length; ++k) {
  668.             // Link the partial derivatives to this new propagator
  669.             final String equationName = KalmanEstimator.class.getName() + "-derivatives-" + k;
  670.             final PartialDerivativesEquations pde = new PartialDerivativesEquations(equationName, referenceTrajectories[k]);

  671.             // Reset the Jacobians
  672.             final SpacecraftState rawState = referenceTrajectories[k].getInitialState();
  673.             final SpacecraftState stateWithDerivatives = pde.setInitialJacobians(rawState);
  674.             referenceTrajectories[k].resetInitialState(stateWithDerivatives);
  675.             mappers[k] = pde.getMapper();
  676.         }

  677.     }

  678.     /** Normalize a covariance matrix.
  679.      * The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas
  680.      * For each element [i,j] of P the corresponding normalized value is:
  681.      * Pn[i,j] = P[i,j] / (scale[i]*scale[j])
  682.      * @param physicalCovarianceMatrix The "physical" covariance matrix in input
  683.      * @return the normalized covariance matrix
  684.      */
  685.     private RealMatrix normalizeCovarianceMatrix(final RealMatrix physicalCovarianceMatrix) {

  686.         // Initialize output matrix
  687.         final int nbParams = physicalCovarianceMatrix.getRowDimension();
  688.         final RealMatrix normalizedCovarianceMatrix = MatrixUtils.createRealMatrix(nbParams, nbParams);

  689.         // Normalize the state matrix
  690.         for (int i = 0; i < nbParams; ++i) {
  691.             for (int j = 0; j < nbParams; ++j) {
  692.                 normalizedCovarianceMatrix.setEntry(i, j,
  693.                                                     physicalCovarianceMatrix.getEntry(i, j) /
  694.                                                     (scale[i] * scale[j]));
  695.             }
  696.         }
  697.         return normalizedCovarianceMatrix;
  698.     }

  699.     /** <p>Set and apply a dynamic outlier filter on a measurement.</p>
  700.      * Loop on the modifiers to see if a dynamic outlier filter needs to be applied.<br>
  701.      * Compute the sigma array using the matrix in input and set it in the filter.<br>
  702.      * Apply the filter by calling the modify method on the estimated measurement.<br>
  703.      * Reset the filter.
  704.      * @param measurement measurement to filter
  705.      * @param innovationCovarianceMatrix So called innovation covariance matrix S, with:
  706.      *        <p>S = H.Ppred.Ht + R</p>
  707.      *        Where:<ul>
  708.      *         <li>H is the normalized measurement matrix (Ht its transpose)
  709.      *         <li>Ppred is the normalized predicted covariance matrix
  710.      *         <li>R is the normalized measurement noise matrix</ul>
  711.      * @param <T> the type of measurement
  712.      */
  713.     private <T extends ObservedMeasurement<T>> void applyDynamicOutlierFilter(final EstimatedMeasurement<T> measurement,
  714.                                                                               final RealMatrix innovationCovarianceMatrix) {

  715.         // Observed measurement associated to the predicted measurement
  716.         final ObservedMeasurement<T> observedMeasurement = measurement.getObservedMeasurement();

  717.         // Check if a dynamic filter was added to the measurement
  718.         // If so, update its sigma value and apply it
  719.         for (EstimationModifier<T> modifier : observedMeasurement.getModifiers()) {
  720.             if (modifier instanceof DynamicOutlierFilter<?>) {
  721.                 final DynamicOutlierFilter<T> dynamicOutlierFilter = (DynamicOutlierFilter<T>) modifier;

  722.                 // Initialize the values of the sigma array used in the dynamic filter
  723.                 final double[] sigmaDynamic     = new double[innovationCovarianceMatrix.getColumnDimension()];
  724.                 final double[] sigmaMeasurement = observedMeasurement.getTheoreticalStandardDeviation();

  725.                 // Set the sigma value for each element of the measurement
  726.                 // Here we do use the value suggested by David A. Vallado (see [1]§10.6):
  727.                 // sigmaDynamic[i] = sqrt(diag(S))*sigma[i]
  728.                 // With S = H.Ppred.Ht + R
  729.                 // Where:
  730.                 //  - S is the measurement error matrix in input
  731.                 //  - H is the normalized measurement matrix (Ht its transpose)
  732.                 //  - Ppred is the normalized predicted covariance matrix
  733.                 //  - R is the normalized measurement noise matrix
  734.                 //  - sigma[i] is the theoretical standard deviation of the ith component of the measurement.
  735.                 //    It is used here to un-normalize the value before it is filtered
  736.                 for (int i = 0; i < sigmaDynamic.length; i++) {
  737.                     sigmaDynamic[i] = FastMath.sqrt(innovationCovarianceMatrix.getEntry(i, i)) * sigmaMeasurement[i];
  738.                 }
  739.                 dynamicOutlierFilter.setSigma(sigmaDynamic);

  740.                 // Apply the modifier on the estimated measurement
  741.                 modifier.modify(measurement);

  742.                 // Re-initialize the value of the filter for the next measurement of the same type
  743.                 dynamicOutlierFilter.setSigma(null);
  744.             }
  745.         }
  746.     }

  747.     /** {@inheritDoc} */
  748.     @Override
  749.     public NonLinearEvolution getEvolution(final double previousTime, final RealVector previousState,
  750.                                            final MeasurementDecorator measurement) {

  751.         // Set a reference date for all measurements parameters that lack one (including the not estimated ones)
  752.         final ObservedMeasurement<?> observedMeasurement = measurement.getObservedMeasurement();
  753.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  754.             if (driver.getReferenceDate() == null) {
  755.                 driver.setReferenceDate(builders.get(0).getInitialOrbitDate());
  756.             }
  757.         }

  758.         ++currentMeasurementNumber;
  759.         currentDate = measurement.getObservedMeasurement().getDate();

  760.         // Note:
  761.         // - n = size of the current measurement
  762.         //  Example:
  763.         //   * 1 for Range, RangeRate and TurnAroundRange
  764.         //   * 2 for Angular (Azimuth/Elevation or Right-ascension/Declination)
  765.         //   * 6 for Position/Velocity
  766.         // - m = size of the state vector. m = nbOrb + nbPropag + nbMeas

  767.         // Predict the state vector (mx1)
  768.         final RealVector predictedState = predictState(observedMeasurement.getDate());

  769.         // Get the error state transition matrix (mxm)
  770.         final RealMatrix stateTransitionMatrix = getErrorStateTransitionMatrix();

  771.         // Predict the measurement based on predicted spacecraft state
  772.         // Compute the innovations (i.e. residuals of the predicted measurement)
  773.         // ------------------------------------------------------------

  774.         // Predicted measurement
  775.         // Note: here the "iteration/evaluation" formalism from the batch LS method
  776.         // is twisted to fit the need of the Kalman filter.
  777.         // The number of "iterations" is actually the number of measurements processed by the filter
  778.         // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement.
  779.         predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  780.                                                             currentMeasurementNumber,
  781.                                                             predictedSpacecraftStates);

  782.         // Normalized measurement matrix (nxm)
  783.         final RealMatrix measurementMatrix = getMeasurementMatrix();

  784.         // compute process noise matrix
  785.         final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(),
  786.                                                                              previousState.getDimension());
  787.         for (int k = 0; k < covarianceMatricesProviders.size(); ++k) {
  788.             final RealMatrix noiseK = covarianceMatricesProviders.get(k).
  789.                                       getProcessNoiseMatrix(correctedSpacecraftStates[k],
  790.                                                             predictedSpacecraftStates[k]);
  791.             checkDimension(noiseK.getRowDimension(),
  792.                            builders.get(k).getOrbitalParametersDrivers(),
  793.                            builders.get(k).getPropagationParametersDrivers(),
  794.                            estimatedMeasurementsParameters);
  795.             final int[] indK = covarianceIndirection[k];
  796.             for (int i = 0; i < indK.length; ++i) {
  797.                 if (indK[i] >= 0) {
  798.                     for (int j = 0; j < indK.length; ++j) {
  799.                         if (indK[j] >= 0) {
  800.                             physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j));
  801.                         }
  802.                     }
  803.                 }
  804.             }

  805.         }
  806.         final RealMatrix normalizedProcessNoise = normalizeCovarianceMatrix(physicalProcessNoise);

  807.         return new NonLinearEvolution(measurement.getTime(), predictedState,
  808.                                       stateTransitionMatrix, normalizedProcessNoise, measurementMatrix);

  809.     }

  810.     /** {@inheritDoc} */
  811.     @Override
  812.     public RealVector getInnovation(final MeasurementDecorator measurement, final NonLinearEvolution evolution,
  813.                                     final RealMatrix innovationCovarianceMatrix) {

  814.         // Apply the dynamic outlier filter, if it exists
  815.         applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix);
  816.         if (predictedMeasurement.getStatus() == EstimatedMeasurement.Status.REJECTED)  {
  817.             // set innovation to null to notify filter measurement is rejected
  818.             return null;
  819.         } else {
  820.             // Normalized innovation of the measurement (Nx1)
  821.             final double[] observed  = predictedMeasurement.getObservedMeasurement().getObservedValue();
  822.             final double[] predicted = predictedMeasurement.getEstimatedValue();
  823.             final double[] sigma     = predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation();
  824.             final double[] residuals = new double[observed.length];

  825.             for (int i = 0; i < observed.length; i++) {
  826.                 residuals[i] = (observed[i] - predicted[i]) / sigma[i];
  827.             }
  828.             return MatrixUtils.createRealVector(residuals);
  829.         }
  830.     }

  831.     /** Finalize estimation.
  832.      * @param observedMeasurement measurement that has just been processed
  833.      * @param estimate corrected estimate
  834.      */
  835.     public void finalizeEstimation(final ObservedMeasurement<?> observedMeasurement,
  836.                                    final ProcessEstimate estimate) {
  837.         // Update the parameters with the estimated state
  838.         // The min/max values of the parameters are handled by the ParameterDriver implementation
  839.         correctedEstimate = estimate;
  840.         updateParameters();

  841.         // Get the estimated propagator (mirroring parameter update in the builder)
  842.         // and the estimated spacecraft state
  843.         final NumericalPropagator[] estimatedPropagators = getEstimatedPropagators();
  844.         for (int k = 0; k < estimatedPropagators.length; ++k) {
  845.             correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState();
  846.         }

  847.         // Compute the estimated measurement using estimated spacecraft state
  848.         correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber,
  849.                                                             currentMeasurementNumber,
  850.                                                             correctedSpacecraftStates);
  851.         // Update the trajectory
  852.         // ---------------------
  853.         updateReferenceTrajectories(estimatedPropagators);

  854.     }

  855.     /** Set the predicted normalized state vector.
  856.      * The predicted/propagated orbit is used to update the state vector
  857.      * @param date prediction date
  858.      * @return predicted state
  859.      */
  860.     private RealVector predictState(final AbsoluteDate date) {

  861.         // Predicted state is initialized to previous estimated state
  862.         final RealVector predictedState = correctedEstimate.getState().copy();

  863.         // Orbital parameters counter
  864.         int jOrb = 0;

  865.         for (int k = 0; k < predictedSpacecraftStates.length; ++k) {

  866.             // Propagate the reference trajectory to measurement date
  867.             predictedSpacecraftStates[k] = referenceTrajectories[k].propagate(date);

  868.             // Update the builder with the predicted orbit
  869.             // This updates the orbital drivers with the values of the predicted orbit
  870.             builders.get(k).resetOrbit(predictedSpacecraftStates[k].getOrbit());

  871.             // The orbital parameters in the state vector are replaced with their predicted values
  872.             // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation)
  873.             // As the propagator builder was previously updated with the predicted orbit,
  874.             // the selected orbital drivers are already up to date with the prediction
  875.             for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) {
  876.                 if (orbitalDriver.isSelected()) {
  877.                     predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue());
  878.                 }
  879.             }

  880.         }

  881.         return predictedState;

  882.     }

  883.     /** Update the estimated parameters after the correction phase of the filter.
  884.      * The min/max allowed values are handled by the parameter themselves.
  885.      */
  886.     private void updateParameters() {
  887.         final RealVector correctedState = correctedEstimate.getState();
  888.         int i = 0;
  889.         for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) {
  890.             // let the parameter handle min/max clipping
  891.             driver.setNormalizedValue(correctedState.getEntry(i));
  892.             correctedState.setEntry(i++, driver.getNormalizedValue());
  893.         }
  894.         for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) {
  895.             // let the parameter handle min/max clipping
  896.             driver.setNormalizedValue(correctedState.getEntry(i));
  897.             correctedState.setEntry(i++, driver.getNormalizedValue());
  898.         }
  899.         for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) {
  900.             // let the parameter handle min/max clipping
  901.             driver.setNormalizedValue(correctedState.getEntry(i));
  902.             correctedState.setEntry(i++, driver.getNormalizedValue());
  903.         }
  904.     }

  905. }