FixedPointTleGenerationAlgorithm.java
- /* Copyright 2002-2024 CS GROUP
- * 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.analytical.tle.generation;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.Field;
- import org.hipparchus.geometry.euclidean.threed.Rotation;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.MathUtils;
- import org.orekit.annotation.DefaultDataContext;
- import org.orekit.attitudes.FrameAlignedProvider;
- import org.orekit.data.DataContext;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.frames.Frame;
- import org.orekit.orbits.EquinoctialOrbit;
- import org.orekit.orbits.FieldEquinoctialOrbit;
- import org.orekit.orbits.FieldKeplerianOrbit;
- import org.orekit.orbits.FieldOrbit;
- import org.orekit.orbits.KeplerianOrbit;
- import org.orekit.orbits.Orbit;
- import org.orekit.orbits.OrbitType;
- import org.orekit.orbits.PositionAngleType;
- import org.orekit.propagation.FieldSpacecraftState;
- import org.orekit.propagation.SpacecraftState;
- import org.orekit.propagation.analytical.tle.FieldTLE;
- import org.orekit.propagation.analytical.tle.FieldTLEPropagator;
- import org.orekit.propagation.analytical.tle.TLE;
- import org.orekit.propagation.analytical.tle.TLEConstants;
- import org.orekit.propagation.analytical.tle.TLEPropagator;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.TimeScale;
- import org.orekit.utils.ParameterDriver;
- /**
- * Fixed Point method to reverse SGP4 and SDP4 propagation algorithm
- * and generate a usable TLE from a spacecraft state.
- * <p>
- * Using this algorithm, the B* value is not computed. In other words,
- * the B* value from the template TLE is set to the generated one.
- * </p>
- * @author Thomas Paulet
- * @author Bryan Cazabonne
- * @since 12.0
- */
- public class FixedPointTleGenerationAlgorithm implements TleGenerationAlgorithm {
- /** Default value for epsilon. */
- public static final double EPSILON_DEFAULT = 1.0e-10;
- /** Default value for maxIterations. */
- public static final int MAX_ITERATIONS_DEFAULT = 100;
- /** Default value for scale. */
- public static final double SCALE_DEFAULT = 1.0;
- /** Used to compute threshold for convergence check. */
- private final double epsilon;
- /** Maximum number of iterations for convergence. */
- private final int maxIterations;
- /** Scale factor of the Fixed Point algorithm. */
- private final double scale;
- /** UTC scale. */
- private final TimeScale utc;
- /** TEME frame. */
- private final Frame teme;
- /**
- * Default constructor.
- * <p>
- * Uses the {@link DataContext#getDefault() default data context}
- * as well as {@link #EPSILON_DEFAULT}, {@link #MAX_ITERATIONS_DEFAULT},
- * {@link #SCALE_DEFAULT} for method convergence.
- * </p>
- */
- @DefaultDataContext
- public FixedPointTleGenerationAlgorithm() {
- this(EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT, SCALE_DEFAULT);
- }
- /**
- * Constructor.
- * <p>
- * Uses the {@link DataContext#getDefault() default data context}.
- * </p>
- * @param epsilon used to compute threshold for convergence check
- * @param maxIterations maximum number of iterations for convergence
- * @param scale scale factor of the Fixed Point algorithm
- */
- @DefaultDataContext
- public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations,
- final double scale) {
- this(epsilon, maxIterations, scale,
- DataContext.getDefault().getTimeScales().getUTC(),
- DataContext.getDefault().getFrames().getTEME());
- }
- /**
- * Constructor.
- * @param epsilon used to compute threshold for convergence check
- * @param maxIterations maximum number of iterations for convergence
- * @param scale scale factor of the Fixed Point algorithm
- * @param utc UTC time scale
- * @param teme TEME frame
- */
- public FixedPointTleGenerationAlgorithm(final double epsilon, final int maxIterations,
- final double scale, final TimeScale utc,
- final Frame teme) {
- this.epsilon = epsilon;
- this.maxIterations = maxIterations;
- this.scale = scale;
- this.utc = utc;
- this.teme = teme;
- }
- /** {@inheritDoc} */
- @Override
- public TLE generate(final SpacecraftState state, final TLE templateTLE) {
- // Generation epoch
- final AbsoluteDate epoch = state.getDate();
- // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state
- final EquinoctialOrbit equinoctialOrbit = convert(state.getOrbit());
- double sma = equinoctialOrbit.getA();
- double ex = equinoctialOrbit.getEquinoctialEx();
- double ey = equinoctialOrbit.getEquinoctialEy();
- double hx = equinoctialOrbit.getHx();
- double hy = equinoctialOrbit.getHy();
- double lv = equinoctialOrbit.getLv();
- // rough initialization of the TLE
- final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(equinoctialOrbit);
- TLE current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc);
- // threshold for each parameter
- final double thrA = epsilon * (1 + sma);
- final double thrE = epsilon * (1 + FastMath.hypot(ex, ey));
- final double thrH = epsilon * (1 + FastMath.hypot(hx, hy));
- final double thrV = epsilon * FastMath.PI;
- int k = 0;
- while (k++ < maxIterations) {
- // recompute the state from the current TLE
- final TLEPropagator propagator = TLEPropagator.selectExtrapolator(current,
- new FrameAlignedProvider(Rotation.IDENTITY, teme),
- state.getMass(), teme);
- final Orbit recoveredOrbit = propagator.getInitialState().getOrbit();
- final EquinoctialOrbit recoveredEquiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit);
- // adapted parameters residuals
- final double deltaSma = equinoctialOrbit.getA() - recoveredEquiOrbit.getA();
- final double deltaEx = equinoctialOrbit.getEquinoctialEx() - recoveredEquiOrbit.getEquinoctialEx();
- final double deltaEy = equinoctialOrbit.getEquinoctialEy() - recoveredEquiOrbit.getEquinoctialEy();
- final double deltaHx = equinoctialOrbit.getHx() - recoveredEquiOrbit.getHx();
- final double deltaHy = equinoctialOrbit.getHy() - recoveredEquiOrbit.getHy();
- final double deltaLv = MathUtils.normalizeAngle(equinoctialOrbit.getLv() - recoveredEquiOrbit.getLv(), 0.0);
- // check convergence
- if (FastMath.abs(deltaSma) < thrA &&
- FastMath.abs(deltaEx) < thrE &&
- FastMath.abs(deltaEy) < thrE &&
- FastMath.abs(deltaHx) < thrH &&
- FastMath.abs(deltaHy) < thrH &&
- FastMath.abs(deltaLv) < thrV) {
- // verify if parameters are estimated
- for (final ParameterDriver templateDrivers : templateTLE.getParametersDrivers()) {
- if (templateDrivers.isSelected()) {
- // set to selected for the new TLE
- current.getParameterDriver(templateDrivers.getName()).setSelected(true);
- }
- }
- // return
- return current;
- }
- // update state
- sma += scale * deltaSma;
- ex += scale * deltaEx;
- ey += scale * deltaEy;
- hx += scale * deltaHx;
- hy += scale * deltaHy;
- lv += scale * deltaLv;
- final EquinoctialOrbit newEquinoctialOrbit =
- new EquinoctialOrbit(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE,
- equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu());
- final KeplerianOrbit newKeplerianOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit);
- // update TLE
- current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, templateTLE.getBStar(epoch), utc);
- }
- // unable to generate a TLE
- throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);
- }
- /** {@inheritDoc} */
- @Override
- public <T extends CalculusFieldElement<T>> FieldTLE<T> generate(final FieldSpacecraftState<T> state,
- final FieldTLE<T> templateTLE) {
- // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state
- final FieldEquinoctialOrbit<T> equinoctialOrbit = convert(state.getOrbit());
- T sma = equinoctialOrbit.getA();
- T ex = equinoctialOrbit.getEquinoctialEx();
- T ey = equinoctialOrbit.getEquinoctialEy();
- T hx = equinoctialOrbit.getHx();
- T hy = equinoctialOrbit.getHy();
- T lv = equinoctialOrbit.getLv();
- // rough initialization of the TLE
- final T bStar = state.getA().getField().getZero().newInstance(templateTLE.getBStar());
- final FieldKeplerianOrbit<T> keplerianOrbit = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(equinoctialOrbit);
- FieldTLE<T> current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, bStar, utc);
- // field
- final Field<T> field = state.getDate().getField();
- // threshold for each parameter
- final T thrA = sma.add(1).multiply(epsilon);
- final T thrE = FastMath.hypot(ex, ey).add(1).multiply(epsilon);
- final T thrH = FastMath.hypot(hx, hy).add(1).multiply(epsilon);
- final T thrV = sma.getPi().multiply(epsilon);
- int k = 0;
- while (k++ < maxIterations) {
- // recompute the state from the current TLE
- final FieldTLEPropagator<T> propagator = FieldTLEPropagator.selectExtrapolator(current, new FrameAlignedProvider(Rotation.IDENTITY, teme),
- state.getMass(), teme, templateTLE.getParameters(field));
- final FieldOrbit<T> recoveredOrbit = propagator.getInitialState().getOrbit();
- final FieldEquinoctialOrbit<T> recoveredEquinoctialOrbit = (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.convertType(recoveredOrbit);
- // adapted parameters residuals
- final T deltaSma = equinoctialOrbit.getA().subtract(recoveredEquinoctialOrbit.getA());
- final T deltaEx = equinoctialOrbit.getEquinoctialEx().subtract(recoveredEquinoctialOrbit.getEquinoctialEx());
- final T deltaEy = equinoctialOrbit.getEquinoctialEy().subtract(recoveredEquinoctialOrbit.getEquinoctialEy());
- final T deltaHx = equinoctialOrbit.getHx().subtract(recoveredEquinoctialOrbit.getHx());
- final T deltaHy = equinoctialOrbit.getHy().subtract(recoveredEquinoctialOrbit.getHy());
- final T deltaLv = MathUtils.normalizeAngle(equinoctialOrbit.getLv().subtract(recoveredEquinoctialOrbit.getLv()), field.getZero());
- // check convergence
- if (FastMath.abs(deltaSma.getReal()) < thrA.getReal() &&
- FastMath.abs(deltaEx.getReal()) < thrE.getReal() &&
- FastMath.abs(deltaEy.getReal()) < thrE.getReal() &&
- FastMath.abs(deltaHx.getReal()) < thrH.getReal() &&
- FastMath.abs(deltaHy.getReal()) < thrH.getReal() &&
- FastMath.abs(deltaLv.getReal()) < thrV.getReal()) {
- // return
- return current;
- }
- // update state
- sma = sma.add(deltaSma.multiply(scale));
- ex = ex.add(deltaEx.multiply(scale));
- ey = ey.add(deltaEy.multiply(scale));
- hx = hx.add(deltaHx.multiply(scale));
- hy = hy.add(deltaHy.multiply(scale));
- lv = lv.add(deltaLv.multiply(scale));
- final FieldEquinoctialOrbit<T> newEquinoctialOrbit =
- new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv, PositionAngleType.TRUE,
- equinoctialOrbit.getFrame(), equinoctialOrbit.getDate(), equinoctialOrbit.getMu());
- final FieldKeplerianOrbit<T> newKeplerianOrbit = (FieldKeplerianOrbit<T>) OrbitType.KEPLERIAN.convertType(newEquinoctialOrbit);
- // update TLE
- current = TleGenerationUtil.newTLE(newKeplerianOrbit, templateTLE, bStar, utc);
- }
- throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_TLE, k);
- }
- /**
- * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter.
- * @param orbitIn the orbit to convert
- * @return the converted orbit, i.e. equinoctial in TEME frame
- */
- private EquinoctialOrbit convert(final Orbit orbitIn) {
- return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, TLEConstants.MU);
- }
- /**
- * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter.
- * @param orbitIn the orbit to convert
- * @param <T> type of the element
- * @return the converted orbit, i.e. equinoctial in TEME frame
- */
- private <T extends CalculusFieldElement<T>> FieldEquinoctialOrbit<T> convert(final FieldOrbit<T> orbitIn) {
- return new FieldEquinoctialOrbit<T>(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu().newInstance(TLEConstants.MU));
- }
- }