AbstractMeasurementBuilder.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.generation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.hipparchus.random.CorrelatedRandomVectorGenerator;
import org.orekit.estimation.measurements.EstimatedMeasurementBase;
import org.orekit.estimation.measurements.EstimationModifier;
import org.orekit.estimation.measurements.ObservableSatellite;
import org.orekit.estimation.measurements.ObservedMeasurement;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.sampling.OrekitStepInterpolator;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.ParameterDriver;

/** Base class for {@link MeasurementBuilder measurements builders}.
 * @param <T> the type of the measurement
 * @author Luc Maisonobe
 * @since 9.3
 */
public abstract class AbstractMeasurementBuilder<T extends ObservedMeasurement<T>> implements MeasurementBuilder<T> {

    /** Noise source (may be null). */
    private final CorrelatedRandomVectorGenerator noiseSource;

    /** Modifiers that apply to the measurement.*/
    private final List<EstimationModifier<T>> modifiers;

    /** Theoretical standard deviation. */
    private final double[] sigma;

    /** Base weight. */
    private final double[] baseWeight;

    /** Satellites related to this measurement. */
    private final ObservableSatellite[] satellites;

    /** Start of the measurements time span. */
    private AbsoluteDate spanStart;

    /** End of the measurements time span. */
    private AbsoluteDate spanEnd;

    /** Simple constructor.
     * @param noiseSource noise source, may be null for generating perfect measurements
     * @param sigma theoretical standard deviation
     * @param baseWeight base weight
     * @param satellites satellites related to this builder
     */
    protected AbstractMeasurementBuilder(final CorrelatedRandomVectorGenerator noiseSource,
                                         final double sigma, final double baseWeight,
                                         final ObservableSatellite... satellites) {
        this(noiseSource,
             new double[] {
                 sigma
             }, new double[] {
                 baseWeight
             }, satellites);
    }

    /** Simple constructor.
     * @param noiseSource noise source, may be null for generating perfect measurements
     * @param sigma theoretical standard deviation
     * @param baseWeight base weight
     * @param satellites satellites related to this builder
     */
    protected AbstractMeasurementBuilder(final CorrelatedRandomVectorGenerator noiseSource,
                                         final double[] sigma, final double[] baseWeight,
                                         final ObservableSatellite... satellites) {
        this.noiseSource = noiseSource;
        this.modifiers   = new ArrayList<>();
        this.sigma       = sigma.clone();
        this.baseWeight  = baseWeight.clone();
        this.satellites  = satellites.clone();
    }

    /** {@inheritDoc}
     * <p>
     * This implementation stores the time span of the measurements generation.
     * </p>
     */
    @Override
    public void init(final AbsoluteDate start, final AbsoluteDate end) {
        spanStart = start;
        spanEnd   = end;
    }

    /** {@inheritDoc} */
    @Override
    public void addModifier(final EstimationModifier<T> modifier) {
        modifiers.add(modifier);
    }

    /** {@inheritDoc} */
    @Override
    public List<EstimationModifier<T>> getModifiers() {
        return Collections.unmodifiableList(modifiers);
    }

    /** Get the start of the measurements time span.
     * @return start of the measurements time span
     */
    protected AbsoluteDate getStart() {
        return spanStart;
    }

    /** Get the end of the measurements time span.
     * @return end of the measurements time span
     */
    protected AbsoluteDate getEnd() {
        return spanEnd;
    }

    /** Generate a noise vector.
     * @return noise vector (null if we generate perfect measurements)
     */
    protected double[] getNoise() {
        return noiseSource == null ? null : noiseSource.nextVector();
    }

    /** Get the theoretical standard deviation.
     * <p>
     * The theoretical standard deviation is a theoretical value
     * used for normalizing the residuals. It acts as a weighting
     * factor to mix appropriately measurements with different units
     * and different accuracy. The value has the same dimension as
     * the measurement itself (i.e. when a residual is divided by
     * this value, it becomes dimensionless).
     * </p>
     * @return expected standard deviation
     * @see #getBaseWeight()
     */
    protected double[] getTheoreticalStandardDeviation() {
        return sigma.clone();
    }

    /** Get the base weight associated with the measurement
     * <p>
     * The base weight is used on residuals already normalized thanks to
     * {@link #getTheoreticalStandardDeviation()} to increase or
     * decrease relative effect of some measurements with respect to
     * other measurements. It is a dimensionless value, typically between
     * 0 and 1 (but it can really have any non-negative value).
     * </p>
     * @return base weight
     * @see #getTheoreticalStandardDeviation()
     */
    protected double[] getBaseWeight() {
        return baseWeight.clone();
    }

    /** {@inheritDoc} */
    @Override
    public ObservableSatellite[] getSatellites() {
        return satellites.clone();
    }

    /**
     * Build a dummy observed measurement.
     *
     * @param date          measurement date
     * @param interpolators interpolators relevant for this builder
     * @return dummy observed measurement
     * @since 13.0
     */
    protected abstract T buildObserved(AbsoluteDate date,
                                       Map<ObservableSatellite, OrekitStepInterpolator> interpolators);

    /** {@inheritDoc} */
    @Override
    public EstimatedMeasurementBase<T> build(final AbsoluteDate date,
                                             final Map<ObservableSatellite, OrekitStepInterpolator> interpolators) {

        final SpacecraftState[] relevant = new SpacecraftState[satellites.length];
        for (int i = 0; i < relevant.length; ++i) {
            relevant[i] = interpolators.get(satellites[i]).getInterpolatedState(date);
        }

        // create a dummy observed measurement
        final T observed = buildObserved(date, interpolators);
        for (final EstimationModifier<T> modifier : getModifiers()) {
            observed.addModifier(modifier);
        }

        // set a reference date for parameters missing one
        for (final ParameterDriver driver : observed.getParametersDrivers()) {
            if (driver.getReferenceDate() == null) {
                final AbsoluteDate start = getStart();
                final AbsoluteDate end   = getEnd();
                driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end);
            }
        }

        // estimate the perfect value of the measurement
        final EstimatedMeasurementBase<T> estimated = observed.estimateWithoutDerivatives(relevant);
        final double[] value = estimated.getEstimatedValue();

        // add the noise
        final double[] noise = getNoise();
        if (noise != null) {
            for (int i = 0; i < value.length; ++i) {
                value[i] += noise[i];
            }
        }

        // update the dummy measurement (which is referenced by the estimated measurement)
        observed.setObservedValue(value);

        return estimated;

    }

}