EstimatedMeasurement.java

/* Copyright 2002-2024 CS GROUP
 * Licensed to CS GROUP (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.measurements;

import java.util.IdentityHashMap;
import java.util.Map;
import java.util.stream.Stream;

import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitIllegalStateException;
import org.orekit.errors.OrekitMessages;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.ParameterDriver;
import org.orekit.utils.TimeSpanMap;
import org.orekit.utils.TimeStampedPVCoordinates;
import org.orekit.utils.TimeSpanMap.Span;

/** Class holding an estimated theoretical value associated to an {@link ObservedMeasurement observed measurement}.
 * @param <T> the type of the measurement
 * @author Luc Maisonobe
 * @since 8.0
 */
public class EstimatedMeasurement<T extends ObservedMeasurement<T>> extends EstimatedMeasurementBase<T> {

    /** Partial derivatives with respect to states. */
    private double[][][] stateDerivatives;

    /** Partial derivatives with respect to parameters. */
    private final Map<ParameterDriver, TimeSpanMap<double[]>> parametersDerivatives;

    /** Simple constructor.
     * @param observedMeasurement associated observed measurement
     * @param iteration iteration number
     * @param count evaluations counter
     * @param states states of the spacecrafts
     * @param participants coordinates of the participants in signal travel order
     * in inertial frame
     */
    public EstimatedMeasurement(final T observedMeasurement,
                                final int iteration, final int count,
                                final SpacecraftState[] states,
                                final TimeStampedPVCoordinates[] participants) {
        super(observedMeasurement, iteration, count, states, participants);
        this.stateDerivatives      = new double[states.length][][];
        this.parametersDerivatives = new IdentityHashMap<ParameterDriver, TimeSpanMap<double[]>>();
    }

    /** Constructor from measurement base.
     * @param estimatedMeasurementBase estimated measurement base
     * @since 12.1.3
     */
    public EstimatedMeasurement(final EstimatedMeasurementBase<T> estimatedMeasurementBase) {
        this(estimatedMeasurementBase.getObservedMeasurement(), estimatedMeasurementBase.getIteration(),
            estimatedMeasurementBase.getCount(), estimatedMeasurementBase.getStates(),
            estimatedMeasurementBase.getParticipants());
        this.setEstimatedValue(estimatedMeasurementBase.getEstimatedValue());
        this.setStatus(estimatedMeasurementBase.getStatus());
    }

    /** Get state size.
     * <p>
     * Warning, the {@link #setStateDerivatives(int, double[][])}
     * method must have been called before this method is called.
     * </p>
     * @return state size
     * @since 10.1
     */
    public int getStateSize() {
        return stateDerivatives[0][0].length;
    }

    /** Get the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to state Cartesian coordinates.
     * @param index index of the state, according to the {@code states}
     * passed at construction
     * @return partial derivatives of the simulated value (array of size
     * {@link ObservedMeasurement#getDimension() dimension} x 6)
     */
    public double[][] getStateDerivatives(final int index) {
        final double[][] sd = new double[getObservedMeasurement().getDimension()][];
        for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
            sd[i] = stateDerivatives[index][i].clone();
        }
        return sd;
    }

    /** Set the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to state Cartesian coordinates.
     * @param index index of the state, according to the {@code states}
     * passed at construction
     * @param derivatives partial derivatives with respect to state
     */
    public void setStateDerivatives(final int index, final double[]... derivatives) {
        this.stateDerivatives[index] = new double[getObservedMeasurement().getDimension()][];
        for (int i = 0; i < getObservedMeasurement().getDimension(); ++i) {
            this.stateDerivatives[index][i] = derivatives[i].clone();
        }
    }

    /** Get all the drivers with set derivatives.
     * @return all the drivers with set derivatives
     * @since 9.0
     */
    public Stream<ParameterDriver> getDerivativesDrivers() {
        return parametersDerivatives.entrySet().stream().map(entry -> entry.getKey());
    }

    /** Get the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to a parameter.
     * @param driver name of the span of the driver for the parameter for which
     * the derivative wants to be known.
     * @return partial derivatives of the simulated value
     * @exception OrekitIllegalArgumentException if parameter is unknown or
     * OrekitIllegalStateException if this function is used on a PDriver having several
     * values driven, in this case the method
     * {@link #getParameterDerivatives(ParameterDriver, AbsoluteDate)} must be called
     */
    public double[] getParameterDerivatives(final ParameterDriver driver)
        throws OrekitIllegalArgumentException {
        if (driver.getNbOfValues() == 1) {
            final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
            if (p == null) {
                final StringBuilder builder = new StringBuilder();
                for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
                    if (builder.length() > 0) {
                        builder.append(",  ");
                    }
                    builder.append(entry.getKey());
                }
                throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
                                                         driver,
                                                         builder.length() > 0 ? builder.toString() : " <none>");
            }
            return p.get(AbsoluteDate.ARBITRARY_EPOCH);
        } else {
            throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, driver.getName(), "getParameterDerivatives(driver, date)");
        }
    }

    /** Get the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to a parameter.
     * @param driver name of the span of the driver for the parameter for which
     * the derivative wants to be known.
     * @param date date at which the parameter derivatives wants to be known
     * @return partial derivatives of the simulated value
     * @exception OrekitIllegalArgumentException if parameter is unknown
     */
    public double[] getParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date)
        throws OrekitIllegalArgumentException {
        final TimeSpanMap<double[]> p = parametersDerivatives.get(driver);
        if (p == null) {
            final StringBuilder builder = new StringBuilder();
            for (final Map.Entry<ParameterDriver, TimeSpanMap<double[]>> entry : parametersDerivatives.entrySet()) {
                if (builder.length() > 0) {
                    builder.append(", ");
                }
                builder.append(entry.getKey());
            }
            throw new OrekitIllegalArgumentException(OrekitMessages.UNSUPPORTED_PARAMETER_NAME,
                                                     driver,
                                                     builder.length() > 0 ? builder.toString() : "<none>");
        }
        return p.get(date);
    }

    /** Set the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to parameter.
     * @param driver name of the span of the driver for the parameter for which
     * the derivative wants to be known.
     * @param date date at which the parameterDerivative wants to be set
     * @param parameterDerivatives partial derivatives with respect to parameter
     */
    public void setParameterDerivatives(final ParameterDriver driver, final AbsoluteDate date, final double... parameterDerivatives) {
        if (!parametersDerivatives.containsKey(driver) || parametersDerivatives.get(driver) == null) {
            final TimeSpanMap<double[]> derivativeSpanMap = new TimeSpanMap<double[]>(parameterDerivatives);
            final TimeSpanMap<String> driverNameSpan = driver.getNamesSpanMap();
            for (Span<String> span = driverNameSpan.getSpan(driverNameSpan.getFirstSpan().getEnd()); span != null; span = span.next()) {
                derivativeSpanMap.addValidAfter(parameterDerivatives, span.getStart(), false);
            }
            parametersDerivatives.put(driver, derivativeSpanMap);

        } else {

            AbsoluteDate dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getStart();
            if (dateToAddAfter.equals(AbsoluteDate.PAST_INFINITY)) {
                dateToAddAfter = driver.getNamesSpanMap().getSpan(date).getEnd();
                parametersDerivatives.get(driver).addValidBefore(parameterDerivatives, dateToAddAfter, false);
            } else {
                parametersDerivatives.get(driver).addValidAfter(parameterDerivatives, dateToAddAfter, false);
            }

        }

    }

    /** Set the partial derivatives of the {@link #getEstimatedValue()
     * simulated measurement} with respect to parameter.
     * @param driver driver for the parameter
     * @param parameterDerivativesMap partial derivatives with respect to parameter
     */
    public void setParameterDerivatives(final ParameterDriver driver, final TimeSpanMap<double[]> parameterDerivativesMap) {
        parametersDerivatives.put(driver, parameterDerivativesMap);
    }

}