FixedPointConverter.java

  1. /* Copyright 2002-2025 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.conversion.osc2mean;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.util.FastMath;
  21. import org.hipparchus.util.MathUtils;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.orbits.EquinoctialOrbit;
  25. import org.orekit.orbits.FieldEquinoctialOrbit;
  26. import org.orekit.orbits.FieldOrbit;
  27. import org.orekit.orbits.Orbit;
  28. import org.orekit.orbits.PositionAngleType;
  29. import org.orekit.time.FieldAbsoluteDate;

  30. /**
  31.  * Class enabling conversion from osculating to mean orbit
  32.  * for a given theory using a fixed-point algorithm.
  33.  *
  34.  * @author Pascal Parraud
  35.  * @since 13.0
  36.  */
  37. public class FixedPointConverter implements OsculatingToMeanConverter {

  38.     /** Default convergence threshold. */
  39.     public static final double DEFAULT_THRESHOLD   = 1e-12;

  40.     /** Default maximum number of iterations. */
  41.     public static final int DEFAULT_MAX_ITERATIONS = 100;

  42.     /** Default damping ratio. */
  43.     public static final double DEFAULT_DAMPING     = 1.;

  44.     /** Mean theory used. */
  45.     private MeanTheory theory;

  46.     /** Convergence threshold. */
  47.     private double threshold;

  48.     /** Maximum number of iterations. */
  49.     private int maxIterations;

  50.     /** Damping ratio. */
  51.     private double damping;

  52.     /** Number of iterations performed. */
  53.     private int iterationsNb;

  54.     /**
  55.      * Default constructor.
  56.      * <p>
  57.      * The mean theory must be set before converting.
  58.      */
  59.     public FixedPointConverter() {
  60.         this(null, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS, DEFAULT_DAMPING);
  61.     }

  62.     /**
  63.      * Constructor.
  64.      * @param theory mean theory to be used
  65.      */
  66.     public FixedPointConverter(final MeanTheory theory) {
  67.         this(theory, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS, DEFAULT_DAMPING);
  68.     }

  69.     /**
  70.      * Constructor.
  71.      * <p>
  72.      * The mean theory must be set before converting.
  73.      *
  74.      * @param threshold tolerance for convergence
  75.      * @param maxIterations maximum number of iterations
  76.      * @param damping damping ratio
  77.      */
  78.     public FixedPointConverter(final double threshold,
  79.                                final int maxIterations,
  80.                                final double damping) {
  81.         this(null, threshold, maxIterations, damping);
  82.     }

  83.     /**
  84.      * Constructor.
  85.      * @param theory mean theory to be used
  86.      * @param threshold tolerance for convergence
  87.      * @param maxIterations maximum number of iterations
  88.      * @param damping damping ratio
  89.      */
  90.     public FixedPointConverter(final MeanTheory theory,
  91.                                final double threshold,
  92.                                final int maxIterations,
  93.                                final double damping) {
  94.         setMeanTheory(theory);
  95.         setThreshold(threshold);
  96.         setMaxIterations(maxIterations);
  97.         setDamping(damping);
  98.     }

  99.     /** {@inheritDoc} */
  100.     @Override
  101.     public MeanTheory getMeanTheory() {
  102.         return theory;
  103.     }

  104.     /** {@inheritDoc} */
  105.     @Override
  106.     public void setMeanTheory(final MeanTheory meanTheory) {
  107.         this.theory = meanTheory;
  108.     }

  109.     /**
  110.      * Gets convergence threshold.
  111.      * @return convergence threshold
  112.      */
  113.     public double getThreshold() {
  114.         return threshold;
  115.     }

  116.     /**
  117.      * Sets convergence threshold.
  118.      * @param threshold convergence threshold
  119.      */
  120.     public void setThreshold(final double threshold) {
  121.         this.threshold = threshold;
  122.     }

  123.     /**
  124.      * Gets maximum number of iterations.
  125.      * @return maximum number of iterations
  126.      */
  127.     public int getMaxIterations() {
  128.         return maxIterations;
  129.     }

  130.     /**
  131.      * Sets maximum number of iterations.
  132.      * @param maxIterations maximum number of iterations
  133.      */
  134.     public void setMaxIterations(final int maxIterations) {
  135.         this.maxIterations = maxIterations;
  136.     }

  137.     /**
  138.      * Gets damping ratio.
  139.      * @return damping ratio
  140.      */
  141.     public double getDamping() {
  142.         return damping;
  143.     }

  144.     /**
  145.      * Sets damping ratio.
  146.      * @param damping damping ratio
  147.      */
  148.     public void setDamping(final double damping) {
  149.         this.damping = damping;
  150.     }

  151.     /**
  152.      * Gets the number of iterations performed by the last conversion.
  153.      * @return number of iterations
  154.      */
  155.     public int getIterationsNb() {
  156.         return iterationsNb;
  157.     }

  158.     /** {@inheritDoc}
  159.      *  Uses a fixed-point algorithm.
  160.      */
  161.     @Override
  162.     public Orbit convertToMean(final Orbit osculating) {

  163.         // sanity check
  164.         if (osculating.getA() < theory.getReferenceRadius()) {
  165.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE,
  166.                                       osculating.getA());
  167.         }

  168.         // Get equinoctial osculating parameters
  169.         final Orbit equinoctial = theory.preprocessing(osculating);
  170.         double sma = equinoctial.getA();
  171.         double ex  = equinoctial.getEquinoctialEx();
  172.         double ey  = equinoctial.getEquinoctialEy();
  173.         double hx  = equinoctial.getHx();
  174.         double hy  = equinoctial.getHy();
  175.         double lv  = equinoctial.getLv();

  176.         // Set threshold for each parameter
  177.         final double thresholdA  = threshold * FastMath.abs(sma);
  178.         final double thresholdE  = threshold * (1 + FastMath.hypot(ex, ey));
  179.         final double thresholdH  = threshold * (1 + FastMath.hypot(hx, hy));
  180.         final double thresholdLv = threshold * FastMath.PI;

  181.         // Rough initialization of the mean parameters
  182.         Orbit mean = theory.initialize(equinoctial);

  183.         int i = 0;
  184.         while (i++ < maxIterations) {

  185.             // Update osculating parameters from current mean parameters
  186.             final Orbit updated = theory.meanToOsculating(mean);

  187.             // Updated parameters residuals
  188.             final double deltaA  = equinoctial.getA() - updated.getA();
  189.             final double deltaEx = equinoctial.getEquinoctialEx() - updated.getEquinoctialEx();
  190.             final double deltaEy = equinoctial.getEquinoctialEy() - updated.getEquinoctialEy();
  191.             final double deltaHx = equinoctial.getHx() - updated.getHx();
  192.             final double deltaHy = equinoctial.getHy() - updated.getHy();
  193.             final double deltaLv = MathUtils.normalizeAngle(equinoctial.getLv() - updated.getLv(), 0.0);

  194.             // Check convergence
  195.             if (FastMath.abs(deltaA)  < thresholdA &&
  196.                 FastMath.abs(deltaEx) < thresholdE &&
  197.                 FastMath.abs(deltaEy) < thresholdE &&
  198.                 FastMath.abs(deltaHx) < thresholdH &&
  199.                 FastMath.abs(deltaHy) < thresholdH &&
  200.                 FastMath.abs(deltaLv) < thresholdLv) {
  201.                 // Records number of iterations performed
  202.                 iterationsNb = i;
  203.                 // Returns the mean orbit
  204.                 return theory.postprocessing(osculating, mean);
  205.             }

  206.             // Update mean parameters
  207.             sma += damping * deltaA;
  208.             ex  += damping * deltaEx;
  209.             ey  += damping * deltaEy;
  210.             hx  += damping * deltaHx;
  211.             hy  += damping * deltaHy;
  212.             lv  += damping * deltaLv;

  213.             // Update mean orbit
  214.             mean = new EquinoctialOrbit(sma, ex, ey, hx, hy, lv,
  215.                                         PositionAngleType.TRUE,
  216.                                         equinoctial.getFrame(),
  217.                                         equinoctial.getDate(),
  218.                                         equinoctial.getMu());
  219.         }
  220.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_MEAN_PARAMETERS, theory.getTheoryName(), i);
  221.     }

  222.     /** {@inheritDoc}
  223.      *  Uses a fixed-point algorithm.
  224.      */
  225.     @Override
  226.     public <T extends CalculusFieldElement<T>> FieldOrbit<T> convertToMean(final FieldOrbit<T> osculating) {

  227.         // Sanity check
  228.         if (osculating.getA().getReal() < theory.getReferenceRadius()) {
  229.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE,
  230.                                            osculating.getA());
  231.         }

  232.         // Get field
  233.         final FieldAbsoluteDate<T> date = osculating.getDate();
  234.         final Field<T> field = date.getField();
  235.         final T zero = field.getZero();
  236.         final T pi   = zero.getPi();

  237.         // Get equinoctial parameters
  238.         final FieldOrbit<T> equinoctial = theory.preprocessing(osculating);
  239.         T sma = equinoctial.getA();
  240.         T ex  = equinoctial.getEquinoctialEx();
  241.         T ey  = equinoctial.getEquinoctialEy();
  242.         T hx  = equinoctial.getHx();
  243.         T hy  = equinoctial.getHy();
  244.         T lv  = equinoctial.getLv();

  245.         // Set threshold for each parameter
  246.         final T thresholdA  = sma.abs().multiply(threshold);
  247.         final T thresholdE  = FastMath.hypot(ex, ey).add(1).multiply(threshold);
  248.         final T thresholdH  = FastMath.hypot(hx, hy).add(1).multiply(threshold);
  249.         final T thresholdLv = pi.multiply(threshold);

  250.         // Rough initialization of the mean parameters
  251.         FieldOrbit<T> mean = theory.initialize(equinoctial);

  252.         int i = 0;
  253.         while (i++ < maxIterations) {

  254.             // recompute the osculating parameters from the current mean parameters
  255.             final FieldOrbit<T> updated = theory.meanToOsculating(mean);

  256.             // Updated parameters residuals
  257.             final T deltaA  = equinoctial.getA().subtract(updated.getA());
  258.             final T deltaEx = equinoctial.getEquinoctialEx().subtract(updated.getEquinoctialEx());
  259.             final T deltaEy = equinoctial.getEquinoctialEy().subtract(updated.getEquinoctialEy());
  260.             final T deltaHx = equinoctial.getHx().subtract(updated.getHx());
  261.             final T deltaHy = equinoctial.getHy().subtract(updated.getHy());
  262.             final T deltaLv = MathUtils.normalizeAngle(equinoctial.getLv().subtract(updated.getLv()), zero);

  263.             // Check convergence
  264.             if (FastMath.abs(deltaA.getReal())  < thresholdA.getReal() &&
  265.                 FastMath.abs(deltaEx.getReal()) < thresholdE.getReal() &&
  266.                 FastMath.abs(deltaEy.getReal()) < thresholdE.getReal() &&
  267.                 FastMath.abs(deltaHx.getReal()) < thresholdH.getReal() &&
  268.                 FastMath.abs(deltaHy.getReal()) < thresholdH.getReal() &&
  269.                 FastMath.abs(deltaLv.getReal()) < thresholdLv.getReal()) {
  270.                 // Records number of iterations performed
  271.                 iterationsNb = i;
  272.                 // Returns the mean orbit
  273.                 return theory.postprocessing(osculating, mean);
  274.             }

  275.             // Update mean parameters
  276.             sma = sma.add(deltaA.multiply(damping));
  277.             ex  = ex.add(deltaEx.multiply(damping));
  278.             ey  = ey.add(deltaEy.multiply(damping));
  279.             hx  = hx.add(deltaHx.multiply(damping));
  280.             hy  = hy.add(deltaHy.multiply(damping));
  281.             lv  = lv.add(deltaLv.multiply(damping));

  282.             // Update mean orbit
  283.             mean = new FieldEquinoctialOrbit<>(sma, ex, ey, hx, hy, lv,
  284.                                                PositionAngleType.TRUE,
  285.                                                equinoctial.getFrame(),
  286.                                                equinoctial.getDate(),
  287.                                                equinoctial.getMu());
  288.         }
  289.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_MEAN_PARAMETERS, theory.getTheoryName(), i);
  290.     }
  291. }