1   /* Copyright 2002-2026 CS GROUP
2    * Licensed to CS GROUP (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.propagation;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.analysis.polynomials.SmoothStepFactory;
21  import org.orekit.errors.OrekitException;
22  import org.orekit.frames.Frame;
23  import org.orekit.orbits.AbstractFieldOrbitInterpolator;
24  import org.orekit.orbits.FieldCartesianOrbit;
25  import org.orekit.orbits.FieldOrbit;
26  import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
27  import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator;
28  import org.orekit.time.FieldAbsoluteDate;
29  import org.orekit.utils.FieldPVCoordinates;
30  
31  import java.util.List;
32  
33  /**
34   * Orbit blender.
35   * <p>
36   * Its purpose is to interpolate orbit state between tabulated orbit states using the concept of blending, exposed in :
37   * "Efficient Covariance Interpolation using Blending of Approximate State Error Transitions" by Sergei Tanygin, and applying
38   * it to orbit states instead of covariances.
39   * <p>
40   * It propagates tabulated values to the interpolating time using given analytical propagator and then blend each propagated
41   * states using a smoothstep function. It gives especially good results as explained
42   * <a href="https://orekit.org/doc/technical-notes/Implementation_of_covariance_interpolation_in_Orekit.pdf">here</a>
43   * compared to Hermite interpolation when time steps between tabulated values get significant (In LEO, &gt; 10 mn for
44   * example).
45   *
46   * @param <T> type of field element
47   *
48   * @author Vincent Cucchietti
49   * @see org.hipparchus.analysis.polynomials.SmoothStepFactory
50   * @see org.hipparchus.analysis.polynomials.SmoothStepFactory.FieldSmoothStepFunction
51   * @see OrbitBlender
52   */
53  public class FieldOrbitBlender<T extends CalculusFieldElement<T>> extends AbstractFieldOrbitInterpolator<T> {
54  
55      /** Analytical propagator used to propagate tabulated orbits to interpolating time. */
56      private final FieldAbstractAnalyticalPropagator<T> analyticalPropagator;
57  
58      /** Blending function. */
59      private final SmoothStepFactory.FieldSmoothStepFunction<T> blendingFunction;
60  
61      /**
62       * Default constructor.
63       *
64       * @param blendingFunction
65       * {@link org.hipparchus.analysis.polynomials.SmoothStepFactory.SmoothStepFunction smoothstep function} used for
66       * blending
67       * @param analyticalPropagator analytical propagator used to propagate tabulated orbits to interpolating time
68       * @param outputInertialFrame output inertial frame
69       *
70       * @throws OrekitException if output frame is not inertial
71       */
72      public FieldOrbitBlender(final SmoothStepFactory.FieldSmoothStepFunction<T> blendingFunction,
73                               final FieldAbstractAnalyticalPropagator<T> analyticalPropagator,
74                               final Frame outputInertialFrame) {
75          super(DEFAULT_INTERPOLATION_POINTS, 0., outputInertialFrame);
76          this.blendingFunction     = blendingFunction;
77          this.analyticalPropagator = analyticalPropagator;
78      }
79  
80      /** {@inheritDoc} */
81      @Override
82      public FieldOrbit<T> interpolate(final InterpolationData interpolationData) {
83  
84          // Get interpolation date
85          final FieldAbsoluteDate<T> interpolationDate = interpolationData.getInterpolationDate();
86  
87          // Get first and last entry
88          final List<FieldOrbit<T>> neighborList  = interpolationData.getNeighborList();
89          final FieldOrbit<T>       previousOrbit = neighborList.getFirst();
90          final FieldOrbit<T>       nextOrbit     = neighborList.get(1);
91  
92          // Propagate orbits
93          final FieldOrbit<T> forwardedOrbit  = propagateOrbitAnalytically(previousOrbit, interpolationDate);
94          final FieldOrbit<T> backwardedOrbit = propagateOrbitAnalytically(nextOrbit, interpolationDate);
95  
96          // Extract position-velocity-acceleration coordinates
97          final FieldPVCoordinates<T> forwardedPV  = forwardedOrbit.getPVCoordinates(getOutputInertialFrame());
98          final FieldPVCoordinates<T> backwardedPV = backwardedOrbit.getPVCoordinates(getOutputInertialFrame());
99  
100         // Blend PV coordinates
101         final T timeParameter = getTimeParameter(interpolationDate, previousOrbit.getDate(), nextOrbit.getDate());
102         final T blendingValue = blendingFunction.value(timeParameter);
103 
104         final FieldPVCoordinates<T> blendedPV = forwardedPV.blendArithmeticallyWith(backwardedPV, blendingValue);
105 
106         // Output new blended instance
107         return new FieldCartesianOrbit<>(blendedPV, getOutputInertialFrame(), interpolationDate, previousOrbit.getMu());
108     }
109 
110     /**
111      * Propagate orbit using predefined {@link AbstractAnalyticalPropagator analytical propagator}.
112      *
113      * @param tabulatedOrbit tabulated orbit to propagate
114      * @param propagationDate propagation date
115      *
116      * @return orbit propagated to propagation date
117      */
118     private FieldOrbit<T> propagateOrbitAnalytically(final FieldOrbit<T> tabulatedOrbit,
119                                                      final FieldAbsoluteDate<T> propagationDate) {
120 
121         analyticalPropagator.resetInitialState(new FieldSpacecraftState<>(tabulatedOrbit));
122 
123         return analyticalPropagator.propagate(propagationDate).getOrbit();
124     }
125 }