LinearKeplerianCovarianceHandler.java

/* Copyright 2022-2025 Romain Serra
 * 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.propagation;

import org.hipparchus.util.Pair;
import org.orekit.orbits.Orbit;
import org.orekit.propagation.sampling.OrekitFixedStepHandler;
import org.orekit.propagation.sampling.OrekitStepHandler;
import org.orekit.propagation.sampling.OrekitStepInterpolator;
import org.orekit.time.AbsoluteDate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Class implementing step handlers to propagate orbital covariance using linearized Keplerian motion, no matter the propagation model.
 * Although less precise than using the same perturbations than the propagator, it is more computationally performant.
 *
 * @author Romain Serra
 * @since 13.1
 * @see StateCovarianceMatrixProvider
 */
public class LinearKeplerianCovarianceHandler implements OrekitFixedStepHandler {

    /** Initial orbital covariance. */
    private final StateCovariance initialCovariance;

    /** Logged states and covariances. */
    private final List<Pair<SpacecraftState, StateCovariance>> statesWithCovariances = new ArrayList<>();

    /**
     * Constructor.
     * @param initialCovariance initial orbital covariance
     */
    public LinearKeplerianCovarianceHandler(final StateCovariance initialCovariance) {
        this.initialCovariance = initialCovariance;
    }

    /**
     * Gets a copy of the covariances.
     * @return state covariances
     */
    public List<StateCovariance> getStatesCovariances() {
        return statesWithCovariances.stream().map(Pair::getValue).collect(Collectors.toList());
    }

    @Override
    public void init(final SpacecraftState s0, final AbsoluteDate t, final double dt) {
        OrekitFixedStepHandler.super.init(s0, t, dt);
        statesWithCovariances.clear();
        statesWithCovariances.add(new Pair<>(s0, initialCovariance));
    }

    @Override
    public void handleStep(final SpacecraftState currentState) {
        final Pair<SpacecraftState, StateCovariance> lastPair = statesWithCovariances.get(statesWithCovariances.size() - 1);
        final Orbit lastOrbit = lastPair.getKey().getOrbit();
        final LinearKeplerianCovarianceMapper covarianceHandler = new LinearKeplerianCovarianceMapper(lastOrbit,
                lastPair.getValue());
        final StateCovariance currentCovariance = covarianceHandler.map(currentState.getOrbit());
        statesWithCovariances.add(new Pair<>(currentState, currentCovariance));
    }

    /**
     * Convert into a non-fixed step handler, based on the instance (so do not use it elsewhere for something else).
     * @return fixed-step handler
     * @see OrekitStepHandler
     */
    public OrekitStepHandler toOrekitStepHandler() {
        final LinearKeplerianCovarianceHandler handler = new LinearKeplerianCovarianceHandler(initialCovariance);

        return new OrekitStepHandler() {
            @Override
            public void init(final SpacecraftState s0, final AbsoluteDate t) {
                OrekitStepHandler.super.init(s0, t);
                handler.init(s0, t, 0.);
                statesWithCovariances.clear();
            }

            @Override
            public void handleStep(final OrekitStepInterpolator interpolator) {
                handler.handleStep(interpolator.getCurrentState());
            }

            @Override
            public void finish(final SpacecraftState finalState) {
                OrekitStepHandler.super.finish(finalState);
                statesWithCovariances.addAll(handler.statesWithCovariances);
            }
        };
    }
}