LeastSquaresTleGenerationAlgorithm.java

  1. /* Copyright 2002-2024 Mark Rutten
  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.  * Mark Rutten 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.analytical.tle.generation;

  18. import java.util.function.UnaryOperator;

  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.Field;
  21. import org.hipparchus.analysis.differentiation.Gradient;
  22. import org.hipparchus.analysis.differentiation.GradientField;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.linear.DiagonalMatrix;
  26. import org.hipparchus.linear.FieldVector;
  27. import org.hipparchus.linear.MatrixUtils;
  28. import org.hipparchus.linear.RealMatrix;
  29. import org.hipparchus.linear.RealVector;
  30. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
  31. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
  32. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
  33. import org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer;
  34. import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
  35. import org.hipparchus.util.Pair;
  36. import org.orekit.annotation.DefaultDataContext;
  37. import org.orekit.data.DataContext;
  38. import org.orekit.frames.Frame;
  39. import org.orekit.orbits.FieldKeplerianOrbit;
  40. import org.orekit.orbits.KeplerianOrbit;
  41. import org.orekit.propagation.FieldSpacecraftState;
  42. import org.orekit.propagation.SpacecraftState;
  43. import org.orekit.propagation.analytical.tle.FieldTLE;
  44. import org.orekit.propagation.analytical.tle.FieldTLEPropagator;
  45. import org.orekit.propagation.analytical.tle.TLE;
  46. import org.orekit.propagation.analytical.tle.TLEPropagator;
  47. import org.orekit.time.AbsoluteDate;
  48. import org.orekit.time.FieldAbsoluteDate;
  49. import org.orekit.time.TimeScale;
  50. import org.orekit.utils.FieldPVCoordinates;
  51. import org.orekit.utils.PVCoordinates;
  52. import org.orekit.utils.ParameterDriver;

  53. /**
  54.  * Least squares method to generate a usable TLE from a spacecraft state.
  55.  *
  56.  * @author Mark Rutten
  57.  * @since 12.0
  58.  */
  59. public class LeastSquaresTleGenerationAlgorithm implements TleGenerationAlgorithm {

  60.     /** Default value for maximum number of iterations.*/
  61.     public static final int DEFAULT_MAX_ITERATIONS = 1000;

  62.     /** UTC scale. */
  63.     private final TimeScale utc;

  64.     /** TEME frame. */
  65.     private final Frame teme;

  66.     /** Maximum number of iterations. */
  67.     private final int maxIterations;

  68.     /** RMS. */
  69.     private double rms;

  70.     /**
  71.      * Default constructor.
  72.      * <p>
  73.      * Uses the {@link DataContext#getDefault() default data context}  as well as
  74.      * {@link #DEFAULT_MAX_ITERATIONS}.
  75.      * </p>
  76.      */
  77.     @DefaultDataContext
  78.     public LeastSquaresTleGenerationAlgorithm() {
  79.         this(DEFAULT_MAX_ITERATIONS);
  80.     }

  81.     /**
  82.      * Default constructor.
  83.      * <p>
  84.      * Uses the {@link DataContext#getDefault() default data context}.
  85.      * </p>
  86.      * @param maxIterations maximum number of iterations for convergence
  87.      */
  88.     @DefaultDataContext
  89.     public LeastSquaresTleGenerationAlgorithm(final int maxIterations) {
  90.         this(maxIterations, DataContext.getDefault().getTimeScales().getUTC(),
  91.              DataContext.getDefault().getFrames().getTEME());
  92.     }

  93.     /**
  94.      * Constructor.
  95.      * @param maxIterations maximum number of iterations for convergence
  96.      * @param utc UTC time scale
  97.      * @param teme TEME frame
  98.      */
  99.     public LeastSquaresTleGenerationAlgorithm(final int maxIterations,
  100.                                               final TimeScale utc, final Frame teme) {
  101.         this.maxIterations = maxIterations;
  102.         this.utc           = utc;
  103.         this.teme          = teme;
  104.         this.rms           = Double.MAX_VALUE;
  105.     }

  106.     /** {@inheritDoc} */
  107.     @Override
  108.     public TLE generate(final SpacecraftState state, final TLE templateTLE) {

  109.         // Generation epoch
  110.         final AbsoluteDate epoch = state.getDate();

  111.         // State vector
  112.         final RealVector stateVector = MatrixUtils.createRealVector(6);
  113.         // Position/Velocity
  114.         final Vector3D position = state.getPVCoordinates().getPosition();
  115.         final Vector3D velocity = state.getPVCoordinates().getVelocity();

  116.         // Fill state vector
  117.         stateVector.setEntry(0, position.getX());
  118.         stateVector.setEntry(1, position.getY());
  119.         stateVector.setEntry(2, position.getZ());
  120.         stateVector.setEntry(3, velocity.getX());
  121.         stateVector.setEntry(4, velocity.getY());
  122.         stateVector.setEntry(5, velocity.getZ());

  123.         // Create the initial guess of the least squares problem
  124.         final RealVector startState = MatrixUtils.createRealVector(7);
  125.         startState.setSubVector(0, stateVector.getSubVector(0, 6));

  126.         // Weights
  127.         final double[] weights = new double[6];
  128.         final double velocityWeight = state.getPVCoordinates().getVelocity().getNorm() * state.getPosition().getNormSq() / state.getMu();
  129.         for (int i = 0; i < 3; i++) {
  130.             weights[i] = 1.0;
  131.             weights[i + 3] = velocityWeight;
  132.         }

  133.         // Time difference between template TLE and spacecraft state
  134.         final double dt = state.getDate().durationFrom(templateTLE.getDate());

  135.         // Construct least squares problem
  136.         final LeastSquaresProblem problem =
  137.                         new LeastSquaresBuilder().maxIterations(maxIterations)
  138.                                                  .maxEvaluations(maxIterations)
  139.                                                  .model(new ObjectiveFunction(templateTLE, dt))
  140.                                                  .target(stateVector)
  141.                                                  .weight(new DiagonalMatrix(weights))
  142.                                                  .start(startState)
  143.                                                  .build();

  144.         // Solve least squares
  145.         final LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer();
  146.         final LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(problem);
  147.         rms = optimum.getRMS();

  148.         // Create new TLE from mean state
  149.         final Vector3D positionEstimated  = new Vector3D(optimum.getPoint().getSubVector(0, 3).toArray());
  150.         final Vector3D velocityEstimated  = new Vector3D(optimum.getPoint().getSubVector(3, 3).toArray());
  151.         final PVCoordinates pvCoordinates = new PVCoordinates(positionEstimated, velocityEstimated);
  152.         final KeplerianOrbit orbit = new KeplerianOrbit(pvCoordinates, teme, epoch, state.getMu());
  153.         final TLE generated = TleGenerationUtil.newTLE(orbit, templateTLE, templateTLE.getBStar(), utc);

  154.         // Verify if parameters are estimated
  155.         for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) {
  156.             if (templateDrivers.isSelected()) {
  157.                 // Set to selected for the new TLE
  158.                 generated.getParameterDriver(templateDrivers.getName()).setSelected(true);
  159.             }
  160.         }

  161.         // Return
  162.         return generated;

  163.     }

  164.     /**
  165.      * Get the Root Mean Square of the TLE estimation.
  166.      * <p>
  167.      * Be careful that the RMS is updated each time the
  168.      * {@link LeastSquaresTleGenerationAlgorithm#generate(SpacecraftState, TLE)}
  169.      * method is called.
  170.      * </p>
  171.      * @return the RMS
  172.      */
  173.     public double getRms() {
  174.         return rms;
  175.     }

  176.     /** {@inheritDoc} */
  177.     @Override
  178.     public <T extends CalculusFieldElement<T>> FieldTLE<T> generate(final FieldSpacecraftState<T> state,
  179.                                                                     final FieldTLE<T> templateTLE) {
  180.         throw new UnsupportedOperationException();
  181.     }

  182.     /** Least squares model. */
  183.     private class ObjectiveFunction implements MultivariateJacobianFunction {

  184.         /** Template TLE. */
  185.         private final FieldTLE<Gradient> templateTLE;

  186.         /** Time difference between template TLE and spacecraft state (s). */
  187.         private final double dt;

  188.         /**
  189.          * Constructor.
  190.          * @param templateTLE template TLE
  191.          * @param dt time difference between template TLE and spacecraft state (s)
  192.          */
  193.         ObjectiveFunction(final TLE templateTLE, final double dt) {
  194.             this.dt = dt;
  195.             // Conversion of template TLE to a field TLE
  196.             final Field<Gradient> field = GradientField.getField(7);
  197.             this.templateTLE = new FieldTLE<>(field, templateTLE.getLine1(), templateTLE.getLine2(), utc);
  198.         }

  199.         /**  {@inheritDoc} */
  200.         @Override
  201.         public Pair<RealVector, RealMatrix> value(final RealVector point) {
  202.             final RealVector objectiveOscState = MatrixUtils.createRealVector(6);
  203.             final RealMatrix objectiveJacobian = MatrixUtils.createRealMatrix(6, 7);
  204.             getTransformedAndJacobian(state -> meanStateToPV(state), point, objectiveOscState, objectiveJacobian);
  205.             return new Pair<>(objectiveOscState, objectiveJacobian);
  206.         }

  207.         /**
  208.          * Fill model.
  209.          * @param operator state vector propagation
  210.          * @param state state vector
  211.          * @param transformed value to fill
  212.          * @param jacobian Jacobian to fill
  213.          */
  214.         private void getTransformedAndJacobian(final UnaryOperator<FieldVector<Gradient>> operator,
  215.                                                final RealVector state, final RealVector transformed,
  216.                                                final RealMatrix jacobian) {

  217.             // State dimension
  218.             final int stateDim = state.getDimension();

  219.             // Initialise the state as field to calculate the gradient
  220.             final GradientField field = GradientField.getField(stateDim);
  221.             final FieldVector<Gradient> fieldState = MatrixUtils.createFieldVector(field, stateDim);
  222.             for (int i = 0; i < stateDim; ++i) {
  223.                 fieldState.setEntry(i, Gradient.variable(stateDim, i, state.getEntry(i)));
  224.             }

  225.             // Call operator
  226.             final FieldVector<Gradient> fieldTransformed = operator.apply(fieldState);

  227.             // Output dimension
  228.             final int outDim = fieldTransformed.getDimension();

  229.             // Extract transform and Jacobian as real values
  230.             for (int i = 0; i < outDim; ++i) {
  231.                 transformed.setEntry(i, fieldTransformed.getEntry(i).getReal());
  232.                 jacobian.setRow(i, fieldTransformed.getEntry(i).getGradient());
  233.             }

  234.         }

  235.         /**
  236.          * Operator to propagate the state vector.
  237.          * @param state state vector
  238.          * @return propagated state vector
  239.          */
  240.         private FieldVector<Gradient> meanStateToPV(final FieldVector<Gradient> state) {
  241.             // Epoch
  242.             final FieldAbsoluteDate<Gradient> epoch = templateTLE.getDate();

  243.             // B*
  244.             final Gradient[] bStar = state.getSubVector(6, 1).toArray();

  245.             // Field
  246.             final Field<Gradient> field = epoch.getField();

  247.             // Extract mean state
  248.             final FieldVector3D<Gradient> position = new FieldVector3D<>(state.getSubVector(0, 3).toArray());
  249.             final FieldVector3D<Gradient> velocity = new FieldVector3D<>(state.getSubVector(3, 3).toArray());
  250.             final FieldPVCoordinates<Gradient> pvCoordinates = new FieldPVCoordinates<>(position, velocity);
  251.             final FieldKeplerianOrbit<Gradient> orbit = new FieldKeplerianOrbit<>(pvCoordinates, teme, epoch, field.getZero().newInstance(TLEPropagator.getMU()));

  252.             // Convert to TLE
  253.             final FieldTLE<Gradient> tle = TleGenerationUtil.newTLE(orbit, templateTLE, bStar[0], utc);

  254.             // Propagate to epoch
  255.             final FieldPVCoordinates<Gradient> propagatedCoordinates =
  256.                     FieldTLEPropagator.selectExtrapolator(tle, teme, bStar).getPVCoordinates(epoch.shiftedBy(dt), bStar);

  257.             // Osculating
  258.             final FieldVector<Gradient> osculating = MatrixUtils.createFieldVector(field, 6);
  259.             osculating.setEntry(0, propagatedCoordinates.getPosition().getX());
  260.             osculating.setEntry(1, propagatedCoordinates.getPosition().getY());
  261.             osculating.setEntry(2, propagatedCoordinates.getPosition().getZ());
  262.             osculating.setEntry(3, propagatedCoordinates.getVelocity().getX());
  263.             osculating.setEntry(4, propagatedCoordinates.getVelocity().getY());
  264.             osculating.setEntry(5, propagatedCoordinates.getVelocity().getZ());

  265.             // Return
  266.             return osculating;

  267.         }

  268.     }

  269. }