FixedPointTleGenerationAlgorithm.java

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

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.geometry.euclidean.threed.Rotation;
  21. import org.hipparchus.util.FastMath;
  22. import org.hipparchus.util.MathUtils;
  23. import org.orekit.annotation.DefaultDataContext;
  24. import org.orekit.attitudes.FrameAlignedProvider;
  25. import org.orekit.data.DataContext;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.orbits.EquinoctialOrbit;
  30. import org.orekit.orbits.FieldEquinoctialOrbit;
  31. import org.orekit.orbits.FieldKeplerianOrbit;
  32. import org.orekit.orbits.FieldOrbit;
  33. import org.orekit.orbits.KeplerianOrbit;
  34. import org.orekit.orbits.Orbit;
  35. import org.orekit.orbits.OrbitType;
  36. import org.orekit.orbits.PositionAngleType;
  37. import org.orekit.propagation.FieldSpacecraftState;
  38. import org.orekit.propagation.SpacecraftState;
  39. import org.orekit.propagation.analytical.tle.FieldTLE;
  40. import org.orekit.propagation.analytical.tle.FieldTLEPropagator;
  41. import org.orekit.propagation.analytical.tle.TLE;
  42. import org.orekit.propagation.analytical.tle.TLEConstants;
  43. import org.orekit.propagation.analytical.tle.TLEPropagator;
  44. import org.orekit.time.AbsoluteDate;
  45. import org.orekit.time.TimeScale;
  46. import org.orekit.utils.ParameterDriver;

  47. /**
  48.  * Fixed Point method to reverse SGP4 and SDP4 propagation algorithm
  49.  * and generate a usable TLE from a spacecraft state.
  50.  * <p>
  51.  * Using this algorithm, the B* value is not computed. In other words,
  52.  * the B* value from the template TLE is set to the generated one.
  53.  * </p>
  54.  * @author Thomas Paulet
  55.  * @author Bryan Cazabonne
  56.  * @since 12.0
  57.  */
  58. public class FixedPointTleGenerationAlgorithm implements TleGenerationAlgorithm {

  59.     /** Default value for epsilon. */
  60.     public static final double EPSILON_DEFAULT = 1.0e-10;

  61.     /** Default value for maxIterations. */
  62.     public static final int MAX_ITERATIONS_DEFAULT = 100;

  63.     /** Default value for scale. */
  64.     public static final double SCALE_DEFAULT = 1.0;

  65.     /** Used to compute threshold for convergence check. */
  66.     private final double epsilon;

  67.     /** Maximum number of iterations for convergence. */
  68.     private final int maxIterations;

  69.     /** Scale factor of the Fixed Point algorithm. */
  70.     private final double scale;

  71.     /** UTC scale. */
  72.     private final TimeScale utc;

  73.     /** TEME frame. */
  74.     private final Frame teme;

  75.     /**
  76.      * Default constructor.
  77.      * <p>
  78.      * Uses the {@link DataContext#getDefault() default data context}
  79.      * as well as {@link #EPSILON_DEFAULT}, {@link #MAX_ITERATIONS_DEFAULT},
  80.      * {@link #SCALE_DEFAULT} for method convergence.
  81.      * </p>
  82.      */
  83.     @DefaultDataContext
  84.     public FixedPointTleGenerationAlgorithm() {
  85.         this(EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT, SCALE_DEFAULT);
  86.     }

  87.     /**
  88.      * Constructor.
  89.      * <p>
  90.      * Uses the {@link DataContext#getDefault() default data context}.
  91.      * </p>
  92.      * @param epsilon used to compute threshold for convergence check
  93.      * @param maxIterations maximum number of iterations for convergence
  94.      * @param scale scale factor of the Fixed Point algorithm
  95.      */
  96.     @DefaultDataContext
  97.     public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations,
  98.                                             final double scale) {
  99.         this(epsilon, maxIterations, scale,
  100.              DataContext.getDefault().getTimeScales().getUTC(),
  101.              DataContext.getDefault().getFrames().getTEME());
  102.     }

  103.     /**
  104.      * Constructor.
  105.      * @param epsilon used to compute threshold for convergence check
  106.      * @param maxIterations maximum number of iterations for convergence
  107.      * @param scale scale factor of the Fixed Point algorithm
  108.      * @param utc UTC time scale
  109.      * @param teme TEME frame
  110.      */
  111.     public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations,
  112.                                             final double scale, final TimeScale utc,
  113.                                             final Frame teme) {
  114.         this.epsilon       = epsilon;
  115.         this.maxIterations = maxIterations;
  116.         this.scale         = scale;
  117.         this.utc           = utc;
  118.         this.teme          = teme;
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public TLE generate(final SpacecraftState state, final TLE templateTLE) {

  123.         // Generation epoch
  124.         final AbsoluteDate epoch = state.getDate();

  125.         // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state
  126.         final EquinoctialOrbit equinoctialOrbit = convert(state.getOrbit());
  127.         double sma = equinoctialOrbit.getA();
  128.         double ex  = equinoctialOrbit.getEquinoctialEx();
  129.         double ey  = equinoctialOrbit.getEquinoctialEy();
  130.         double hx  = equinoctialOrbit.getHx();
  131.         double hy  = equinoctialOrbit.getHy();
  132.         double lv  = equinoctialOrbit.getLv();

  133.         // rough initialization of the TLE
  134.         final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(equinoctialOrbit);
  135.         TLE current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc);

  136.         // threshold for each parameter
  137.         final double thrA = epsilon * (1 + sma);
  138.         final double thrE = epsilon * (1 + FastMath.hypot(ex, ey));
  139.         final double thrH = epsilon * (1 + FastMath.hypot(hx, hy));
  140.         final double thrV = epsilon * FastMath.PI;

  141.         int k = 0;
  142.         while (k++ < maxIterations) {

  143.             // recompute the state from the current TLE
  144.             final TLEPropagator propagator = TLEPropagator.selectExtrapolator(current,
  145.                                                                               new FrameAlignedProvider(Rotation.IDENTITY, teme),
  146.                                                                               state.getMass(), teme);
  147.             final Orbit recoveredOrbit = propagator.getInitialState().getOrbit();
  148.             final EquinoctialOrbit recoveredEquiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit);

  149.             // adapted parameters residuals
  150.             final double deltaSma = equinoctialOrbit.getA() - recoveredEquiOrbit.getA();
  151.             final double deltaEx  = equinoctialOrbit.getEquinoctialEx() - recoveredEquiOrbit.getEquinoctialEx();
  152.             final double deltaEy  = equinoctialOrbit.getEquinoctialEy() - recoveredEquiOrbit.getEquinoctialEy();
  153.             final double deltaHx  = equinoctialOrbit.getHx() - recoveredEquiOrbit.getHx();
  154.             final double deltaHy  = equinoctialOrbit.getHy() - recoveredEquiOrbit.getHy();
  155.             final double deltaLv  = MathUtils.normalizeAngle(equinoctialOrbit.getLv() - recoveredEquiOrbit.getLv(), 0.0);

  156.             // check convergence
  157.             if (FastMath.abs(deltaSma) < thrA &&
  158.                 FastMath.abs(deltaEx)  < thrE &&
  159.                 FastMath.abs(deltaEy)  < thrE &&
  160.                 FastMath.abs(deltaHx)  < thrH &&
  161.                 FastMath.abs(deltaHy)  < thrH &&
  162.                 FastMath.abs(deltaLv)  < thrV) {

  163.                 // verify if parameters are estimated
  164.                 for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) {
  165.                     if (templateDrivers.isSelected()) {
  166.                         // set to selected for the new TLE
  167.                         current.getParameterDriver(templateDrivers.getName()).setSelected(true);
  168.                     }
  169.                 }

  170.                 // return
  171.                 return current;
  172.             }

  173.             // update state
  174.             sma += scale * deltaSma;
  175.             ex  += scale * deltaEx;
  176.             ey  += scale * deltaEy;
  177.             hx  += scale * deltaHx;
  178.             hy  += scale * deltaHy;
  179.             lv  += scale * deltaLv;
  180.             final EquinoctialOrbit newEquinoctialOrbit =
  181.                                     new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE,
  182.                                                          equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu());
  183.             final KeplerianOrbit newKeplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit);

  184.             // update TLE
  185.             current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc);

  186.         }

  187.         // unable to generate a TLE
  188.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);

  189.     }

  190.     /** {@inheritDoc} */
  191.     @Override
  192.     public <T extends CalculusFieldElement<T>> FieldTLE<T> generate(final FieldSpacecraftState<T> state,
  193.                                                                     final FieldTLE<T> templateTLE) {

  194.         // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state
  195.         final FieldEquinoctialOrbit<T> equinoctialOrbit = convert(state.getOrbit());
  196.         T sma = equinoctialOrbit.getA();
  197.         T ex  = equinoctialOrbit.getEquinoctialEx();
  198.         T ey  = equinoctialOrbit.getEquinoctialEy();
  199.         T hx  = equinoctialOrbit.getHx();
  200.         T hy  = equinoctialOrbit.getHy();
  201.         T lv  = equinoctialOrbit.getLv();

  202.         // rough initialization of the TLE
  203.         final T bStar = state.getA().getField().getZero().newInstance(templateTLE.getBStar());
  204.         final FieldKeplerianOrbit<T> keplerianOrbit = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(equinoctialOrbit);
  205.         FieldTLE<T> current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, bStar, utc);

  206.         // field
  207.         final Field<T> field = state.getDate().getField();

  208.         // threshold for each parameter
  209.         final T thrA = sma.add(1).multiply(epsilon);
  210.         final T thrE = FastMath.hypot(ex, ey).add(1).multiply(epsilon);
  211.         final T thrH = FastMath.hypot(hx, hy).add(1).multiply(epsilon);
  212.         final T thrV = sma.getPi().multiply(epsilon);

  213.         int k = 0;
  214.         while (k++ < maxIterations) {

  215.             // recompute the state from the current TLE
  216.             final FieldTLEPropagator<T> propagator = FieldTLEPropagator.selectExtrapolator(current, new FrameAlignedProvider(Rotation.IDENTITY, teme),
  217.                                                                                            state.getMass(), teme, templateTLE.getParameters(field));
  218.             final FieldOrbit<T> recoveredOrbit = propagator.getInitialState().getOrbit();
  219.             final FieldEquinoctialOrbit<T> recoveredEquinoctialOrbit = (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit);

  220.             // adapted parameters residuals
  221.             final T deltaSma = equinoctialOrbit.getA().subtract(recoveredEquinoctialOrbit.getA());
  222.             final T deltaEx  = equinoctialOrbit.getEquinoctialEx().subtract(recoveredEquinoctialOrbit.getEquinoctialEx());
  223.             final T deltaEy  = equinoctialOrbit.getEquinoctialEy().subtract(recoveredEquinoctialOrbit.getEquinoctialEy());
  224.             final T deltaHx  = equinoctialOrbit.getHx().subtract(recoveredEquinoctialOrbit.getHx());
  225.             final T deltaHy  = equinoctialOrbit.getHy().subtract(recoveredEquinoctialOrbit.getHy());
  226.             final T deltaLv  = MathUtils.normalizeAngle(equinoctialOrbit.getLv().subtract(recoveredEquinoctialOrbit.getLv()), field.getZero());

  227.             // check convergence
  228.             if (FastMath.abs(deltaSma.getReal()) < thrA.getReal() &&
  229.                 FastMath.abs(deltaEx.getReal())  < thrE.getReal() &&
  230.                 FastMath.abs(deltaEy.getReal())  < thrE.getReal() &&
  231.                 FastMath.abs(deltaHx.getReal())  < thrH.getReal() &&
  232.                 FastMath.abs(deltaHy.getReal())  < thrH.getReal() &&
  233.                 FastMath.abs(deltaLv.getReal())  < thrV.getReal()) {

  234.                 // return
  235.                 return current;

  236.             }

  237.             // update state
  238.             sma = sma.add(deltaSma.multiply(scale));
  239.             ex  = ex.add(deltaEx.multiply(scale));
  240.             ey  = ey.add(deltaEy.multiply(scale));
  241.             hx  = hx.add(deltaHx.multiply(scale));
  242.             hy  = hy.add(deltaHy.multiply(scale));
  243.             lv  = lv.add(deltaLv.multiply(scale));
  244.             final FieldEquinoctialOrbit<T> newEquinoctialOrbit =
  245.                                     new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE,
  246.                                     equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu());
  247.             final FieldKeplerianOrbit<T> newKeplerianOrbit = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit);

  248.             // update TLE
  249.             current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, bStar, utc);

  250.         }

  251.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);

  252.     }

  253.     /**
  254.      * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter.
  255.      * @param orbitIn the orbit to convert
  256.      * @return the converted orbit, i.e. equinoctial in TEME frame
  257.      */
  258.     private EquinoctialOrbit convert(final Orbit orbitIn) {
  259.         return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, TLEConstants.MU);
  260.     }

  261.     /**
  262.      * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter.
  263.      * @param orbitIn the orbit to convert
  264.      * @param <T> type of the element
  265.      * @return the converted orbit, i.e. equinoctial in TEME frame
  266.      */
  267.     private <T extends CalculusFieldElement<T>> FieldEquinoctialOrbit<T> convert(final FieldOrbit<T> orbitIn) {
  268.         return new FieldEquinoctialOrbit<T>(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu().newInstance(TLEConstants.MU));
  269.     }

  270. }