1   /* Copyright 2022-2026 Romain Serra
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.control.heuristics;
18  
19  import org.hipparchus.Field;
20  import org.hipparchus.analysis.differentiation.Derivative;
21  import org.hipparchus.analysis.differentiation.UnivariateDifferentiableFunction;
22  import org.hipparchus.analysis.solvers.NewtonRaphsonSolver;
23  import org.hipparchus.exception.MathIllegalArgumentException;
24  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.orekit.orbits.FieldCartesianOrbit;
27  import org.orekit.orbits.Orbit;
28  import org.orekit.propagation.SpacecraftState;
29  import org.orekit.propagation.conversion.osc2mean.OsculatingToMeanConverter;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.utils.FieldPVCoordinates;
32  
33  /**
34   * Class modelling impulsive maneuvers to set the mean semi-major axis to a given value.
35   * The impulse vector is tangential and computed in the same frame as the orbit.
36   * The resulting osculating eccentricity depends on the execution location. The instantaneous orbital plane is left unchanged.
37   * A constraint on the maximum magnitude can be optionally set.
38   * @see OsculatingSmaChangeImpulseProvider
39   * @see OsculatingToMeanConverter
40   * @author Romain Serra
41   * @since 14.0
42   */
43  public class MeanSmaChangeImpulseProvider extends AbstractInPlaneImpulseProvider {
44  
45      /** Target osculating semi-major axis. */
46      private final double targetSemiMajorAxis;
47  
48      /** Mean orbit converter. */
49      private final OsculatingToMeanConverter osculatingToMeanConverter;
50  
51      /**
52       * Constructor with default maximum magnitude set to positive infinity (unconstrained).
53       * @param targetSemiMajorAxis osculating value to achieve
54       * @param osculatingToMeanConverter mean orbit converter
55       */
56      public MeanSmaChangeImpulseProvider(final double targetSemiMajorAxis, final OsculatingToMeanConverter osculatingToMeanConverter) {
57          this(Double.POSITIVE_INFINITY, targetSemiMajorAxis, osculatingToMeanConverter);
58      }
59  
60      /**
61       * Constructor.
62       * @param maximumMagnitude maximum magnitude
63       * @param targetSemiMajorAxis osculating value to achieve
64       * @param osculatingToMeanConverter mean orbit converter
65       */
66      public MeanSmaChangeImpulseProvider(final double maximumMagnitude, final double targetSemiMajorAxis,
67                                          final OsculatingToMeanConverter osculatingToMeanConverter) {
68          super(maximumMagnitude);
69          this.targetSemiMajorAxis = targetSemiMajorAxis;
70          this.osculatingToMeanConverter = osculatingToMeanConverter;
71      }
72  
73      @Override
74      public Vector3D getUnconstrainedImpulse(final SpacecraftState state, final boolean isForward) {
75          final Orbit orbit = state.getOrbit();
76          final Vector3D velocity = state.getVelocity();
77          final OsculatingSmaChangeImpulseProvider osculatingSmaChangeImpulseProvider = new OsculatingSmaChangeImpulseProvider(getMaximumMagnitude(),
78                  targetSemiMajorAxis);
79          final Vector3D osculatingImpulse = osculatingSmaChangeImpulseProvider.getUnconstrainedImpulse(state, isForward);
80          final Vector3D direction = velocity.normalize();
81          final NewtonRaphsonSolver solver = new NewtonRaphsonSolver();
82          final NewtonFunction function = new NewtonFunction(orbit, direction, osculatingToMeanConverter,
83                  targetSemiMajorAxis);
84          final double guess = osculatingImpulse.add(velocity).getNorm2();
85          final double optimal = solver.solve(100, function, guess);
86          return direction.scalarMultiply(optimal).subtract(velocity);
87      }
88  
89      /**
90       * Function for Newton-Raphson method.
91       *
92       * @param templateOrbit Reference orbit whose velocity is to be changed.
93       * @param direction     Direction of velocity.
94       * @param converter     Mean orbit converter.
95       * @param targetMeanSma Target mean semi-major axis.
96       */
97      private record NewtonFunction(Orbit templateOrbit, Vector3D direction, OsculatingToMeanConverter converter,
98                                    double targetMeanSma) implements UnivariateDifferentiableFunction {
99  
100         @Override
101         public <T extends Derivative<T>> T value(final T t) throws MathIllegalArgumentException {
102             final Field<T> field = t.getField();
103             final FieldVector3D<T> velocity = new FieldVector3D<>(t, direction);
104             final FieldVector3D<T> position = new FieldVector3D<>(field, templateOrbit.getPosition());
105             final FieldCartesianOrbit<T> orbit = new FieldCartesianOrbit<>(new FieldPVCoordinates<>(position, velocity),
106                     templateOrbit.getFrame(), new FieldAbsoluteDate<>(field, templateOrbit.getDate()),
107                     field.getOne().newInstance(templateOrbit.getMu()));
108             return converter.convertToMean(orbit).getA().subtract(targetMeanSma);
109         }
110     }
111 }