UnivariateProcessNoise.java

/* Copyright 2002-2018 CS Systèmes d'Information
 * Licensed to CS Systèmes d'Information (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.estimation.sequential;

import org.hipparchus.analysis.UnivariateFunction;
import org.hipparchus.exception.LocalizedCoreFormats;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.linear.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
import org.orekit.errors.OrekitException;
import org.orekit.frames.LOFType;
import org.orekit.frames.Transform;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.SpacecraftState;

/** Provider for a temporal evolution of the process noise matrix.
 * All parameters (orbital or propagation) are time dependent and provided as {@link UnivariateFunction}.
 * The argument of the functions is a duration in seconds (between current and previous spacecraft state).
 * The output of the functions must be of the dimension of a standard deviation.
 * The method {@link #getProcessNoiseMatrix} then square the values so that they are consistent with a covariance matrix.
 * <p>
 * The orbital parameters evolutions are provided in LOF frame and Cartesian (PV);
 * then converted in inertial frame and current {@link OrbitType} and {@link PositionAngle}
 * when method {@link #getProcessNoiseMatrix} is called.
 * </p>
 * <p>
 * The time-dependent functions define a process noise matrix that is diagonal
 * <em>in the Local Orbital Frame</em>, corresponds to Cartesian elements, abd represents
 * the temporal evolution of (the standard deviation of) the process noise model. The
 * first function is therefore the standard deviation along the LOF X axis, the second
 * function represents the standard deviation along the LOF Y axis...
 * This allows to set up simply a process noise representing an uncertainty that grows
 * mainly along the track. The 6x6 upper left part of output matrix will however not be
 * diagonal as it will be converted to the same inertial frame and orbit type as the
 * {@link SpacecraftState state} used by the {@link KalmanEstimator Kalman estimator}.
 * </p>
 * <p>
 * The propagation parameters are not associated to a specific frame and are appended as
 * is in the lower right part diagonal of the output matrix. This implies this simplified
 * model does not include correlation between the parameters and the orbit, but only
 * evolution of the parameters themselves. If such correlations are needed, users must
 * set up a custom {@link CovarianceMatrixProvider covariance matrix provider}. In most
 * cases, the parameters are constant and their evolution noise is always 0, so the
 * functions can be set to {@code x -> 0}.
 * </p>
 * <p>
 * This class always provides one initial noise matrix or initial covariance matrix and one process noise matrix.
 * </p>
 * <p>
 * WARNING: as of release 9.2, this feature is still considered experimental
 * </p>
 * @author Luc Maisonobe
 * @author Maxime Journot
 * @since 9.2
 */
public class UnivariateProcessNoise extends AbstractCovarianceMatrixProvider {

    /** Local Orbital Frame (LOF) type used. */
    private final LOFType lofType;

    /** Position angle for the orbital process noise matrix. */
    private final PositionAngle positionAngle;

    /** Array of univariate functions for the six orbital parameters process noise evolution in LOF frame and Cartesian formalism. */
    private final UnivariateFunction[] lofCartesianOrbitalParametersEvolution;

    /** Array of univariate functions for the propagation parameters process noise evolution. */
    private final UnivariateFunction[] propagationParametersEvolution;

    /** Simple constructor.
     * @param initialCovarianceMatrix initial covariance matrix
     * @param lofType the LOF type used
     * @param positionAngle the position angle used for the computation of the process noise
     * @param lofCartesianOrbitalParametersEvolution Array of univariate functions for the six orbital parameters process noise evolution in LOF frame and Cartesian orbit type
     * @param propagationParametersEvolution Array of univariate functions for the propagation parameters process noise evolution
     * @throws OrekitException if lofOrbitalParametersEvolution array size is different from 6
     */
    public UnivariateProcessNoise(final RealMatrix initialCovarianceMatrix,
                                  final LOFType lofType,
                                  final PositionAngle positionAngle,
                                  final UnivariateFunction[] lofCartesianOrbitalParametersEvolution,
                                  final UnivariateFunction[] propagationParametersEvolution)
        throws OrekitException {
        super(initialCovarianceMatrix);
        this.lofType = lofType;
        this.positionAngle = positionAngle;
        this.lofCartesianOrbitalParametersEvolution  = lofCartesianOrbitalParametersEvolution;
        this.propagationParametersEvolution = propagationParametersEvolution;

        // Ensure that the orbital evolution array size is 6
        if (lofCartesianOrbitalParametersEvolution.length != 6) {
            throw new OrekitException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
                                      lofCartesianOrbitalParametersEvolution, 6);
        }
    }

    /** Getter for the lofType.
     * @return the lofType
     */
    public LOFType getLofType() {
        return lofType;
    }

    /** Getter for the positionAngle.
     * @return the positionAngle
     */
    public PositionAngle getPositionAngle() {
        return positionAngle;
    }

    /** Getter for the lofCartesianOrbitalParametersEvolution.
     * @return the lofCartesianOrbitalParametersEvolution
     */
    public UnivariateFunction[] getLofCartesianOrbitalParametersEvolution() {
        return lofCartesianOrbitalParametersEvolution;
    }

    /** Getter for the propagationParametersEvolution.
     * @return the propagationParametersEvolution
     */
    public UnivariateFunction[] getPropagationParametersEvolution() {
        return propagationParametersEvolution;
    }

    /** {@inheritDoc} */
    @Override
    public RealMatrix getProcessNoiseMatrix(final SpacecraftState previous,
                                            final SpacecraftState current) {

        // Orbital parameters process noise matrix in inertial frame and current orbit type
        final RealMatrix inertialOrbitalProcessNoiseMatrix = getInertialOrbitalProcessNoiseMatrix(previous, current);

        if (propagationParametersEvolution.length == 0) {

            // no propagation parameters contribution, just return the orbital part
            return inertialOrbitalProcessNoiseMatrix;

        } else {

            // Propagation parameters process noise matrix
            final RealMatrix propagationProcessNoiseMatrix = getPropagationProcessNoiseMatrix(previous, current);

            // Concatenate the matrices
            final int        orbitalMatrixSize     = lofCartesianOrbitalParametersEvolution.length;
            final int        propagationMatrixSize = propagationParametersEvolution.length;
            final RealMatrix processNoiseMatrix    = MatrixUtils.createRealMatrix(orbitalMatrixSize + propagationMatrixSize,
                                                                                  orbitalMatrixSize + propagationMatrixSize);
            processNoiseMatrix.setSubMatrix(inertialOrbitalProcessNoiseMatrix.getData(), 0, 0);
            processNoiseMatrix.setSubMatrix(propagationProcessNoiseMatrix.getData(), orbitalMatrixSize, orbitalMatrixSize);
            return processNoiseMatrix;

        }
    }

    /** Get the process noise for the six orbital parameters in current spacecraft inertial frame and current orbit type.
     * @param previous previous state
     * @param current current state
     * @return physical (i.e. non normalized) orbital process noise matrix in inertial frame
     */
    private RealMatrix getInertialOrbitalProcessNoiseMatrix(final SpacecraftState previous,
                                                            final SpacecraftState current) {

        // ΔT = duration in seconds from previous to current spacecraft state
        final double deltaT = current.getDate().durationFrom(previous.getDate());

        // Evaluate the functions, using ΔT as argument
        final int      lofOrbitalprocessNoiseLength = lofCartesianOrbitalParametersEvolution.length;
        final double[] lofOrbitalProcessNoiseValues = new double[lofOrbitalprocessNoiseLength];

        // The function return a value which dimension is that of a standard deviation
        // It needs to be squared before being put in the process noise covariance matrix
        for (int i = 0; i < lofOrbitalprocessNoiseLength; i++) {
            final double functionValue =  lofCartesianOrbitalParametersEvolution[i].value(deltaT);
            lofOrbitalProcessNoiseValues[i] = functionValue * functionValue;
        }

        // Form the diagonal matrix in LOF frame and Cartesian formalism
        final RealMatrix lofCartesianProcessNoiseMatrix = MatrixUtils.createRealDiagonalMatrix(lofOrbitalProcessNoiseValues);

        // Get the rotation matrix from LOF to inertial frame
        final double[][] lofToInertialRotation = lofType.rotationFromInertial(current.getPVCoordinates()).
                                                 revert().getMatrix();

        // Jacobian from LOF to inertial frame
        final RealMatrix jacLofToInertial = MatrixUtils.createRealMatrix(6, 6);
        jacLofToInertial.setSubMatrix(lofToInertialRotation, 0, 0);
        jacLofToInertial.setSubMatrix(lofToInertialRotation, 3, 3);

        // FIXME: Trying to fix the transform from LOF to inertial
        final Transform lofToInertial = lofType.transformFromInertial(current.getDate(), current.getPVCoordinates()).getInverse();
        final Vector3D OM = lofToInertial.getRotationRate().negate();
        final double[][] MOM = new double[3][3];
        MOM[0][1] = -OM.getZ();
        MOM[0][2] = OM.getY();
        MOM[1][0] = OM.getZ();
        MOM[1][2] = -OM.getX();
        MOM[2][0] = -OM.getY();
        MOM[2][1] = OM.getX();
        //jacLofToInertial.setSubMatrix(MOM, 3, 0);
        //debug

        // Jacobian of orbit parameters with respect to Cartesian parameters
        final double[][] dYdC = new double[6][6];
        current.getOrbit().getJacobianWrtCartesian(positionAngle, dYdC);
        final RealMatrix jacParametersWrtCartesian = MatrixUtils.createRealMatrix(dYdC);

        // Complete Jacobian of the transformation
        final RealMatrix jacobian = jacParametersWrtCartesian.multiply(jacLofToInertial);

        // Return the orbital process noise matrix in inertial frame and proper orbit type
        final RealMatrix inertialOrbitalProcessNoiseMatrix = jacobian.
                         multiply(lofCartesianProcessNoiseMatrix).
                         multiply(jacobian.transpose());
        return inertialOrbitalProcessNoiseMatrix;
    }

    /** Get the process noise for the propagation parameters.
     * @param previous previous state
     * @param current current state
     * @return physical (i.e. non normalized) propagation process noise matrix
     */
    private RealMatrix getPropagationProcessNoiseMatrix(final SpacecraftState previous,
                                                        final SpacecraftState current) {

        // ΔT = duration from previous to current spacecraft state (in seconds)
        final double deltaT = current.getDate().durationFrom(previous.getDate());

        // Evaluate the functions, using ΔT as argument
        final int      propagationProcessNoiseLength = propagationParametersEvolution.length;
        final double[] propagationProcessNoiseValues = new double[propagationProcessNoiseLength];

        // The function return a value which dimension is that of a standard deviation
        // It needs to be squared before being put in the process noise covariance matrix
        for (int i = 0; i < propagationProcessNoiseLength; i++) {
            final double functionValue =  propagationParametersEvolution[i].value(deltaT);
            propagationProcessNoiseValues[i] = functionValue * functionValue;
        }

        // Form the diagonal matrix corresponding to propagation parameters process noise
        return MatrixUtils.createRealDiagonalMatrix(propagationProcessNoiseValues);
    }

}