AbstractPropagatorBuilder.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;

  18. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.hipparchus.util.FastMath;
  20. import org.orekit.attitudes.AttitudeProvider;
  21. import org.orekit.attitudes.FrameAlignedProvider;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitIllegalArgumentException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.forces.gravity.NewtonianAttraction;
  26. import org.orekit.frames.Frame;
  27. import org.orekit.orbits.Orbit;
  28. import org.orekit.orbits.OrbitType;
  29. import org.orekit.orbits.PositionAngleType;
  30. import org.orekit.propagation.AbstractPropagator;
  31. import org.orekit.propagation.Propagator;
  32. import org.orekit.propagation.integration.AdditionalDerivativesProvider;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.utils.ParameterDriver;
  35. import org.orekit.utils.ParameterDriversList;
  36. import org.orekit.utils.ParameterDriversList.DelegatingDriver;
  37. import org.orekit.utils.ParameterObserver;
  38. import org.orekit.utils.TimeSpanMap;
  39. import org.orekit.utils.TimeSpanMap.Span;

  40. import java.util.ArrayList;
  41. import java.util.List;

  42. /** Base class for propagator builders.
  43.  * @author Pascal Parraud
  44.  * @since 7.1
  45.  */
  46. public abstract class AbstractPropagatorBuilder<T extends AbstractPropagator> implements PropagatorBuilder {

  47.     /** Central attraction scaling factor.
  48.      * <p>
  49.      * We use a power of 2 to avoid numeric noise introduction
  50.      * in the multiplications/divisions sequences.
  51.      * </p>
  52.      */
  53.     private static final double MU_SCALE = FastMath.scalb(1.0, 32);

  54.     /** Date of the initial orbit. */
  55.     private AbsoluteDate initialOrbitDate;

  56.     /** Frame in which the orbit is propagated. */
  57.     private final Frame frame;

  58.     /** Central attraction coefficient (m³/s²). */
  59.     private double mu;

  60.     /** Initial mass. */
  61.     private double mass;

  62.     /** Drivers for orbital parameters. */
  63.     private final ParameterDriversList orbitalDrivers;

  64.     /** List of the supported parameters. */
  65.     private ParameterDriversList propagationDrivers;

  66.     /** Orbit type to use. */
  67.     private final OrbitType orbitType;

  68.     /** Position angle type to use. */
  69.     private final PositionAngleType positionAngleType;

  70.     /** Position scale to use for the orbital drivers. */
  71.     private final double positionScale;

  72.     /** Attitude provider for the propagator. */
  73.     private AttitudeProvider attitudeProvider;

  74.     /** Additional derivatives providers.
  75.      * @since 11.1
  76.      */
  77.     private List<AdditionalDerivativesProvider> additionalDerivativesProviders;

  78.     /** Build a new instance.
  79.      * <p>
  80.      * The template orbit is used as a model to {@link
  81.      * #createInitialOrbit() create initial orbit}. It defines the
  82.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  83.      * used together with the {@code positionScale} to convert from the {@link
  84.      * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
  85.      * callers of this builder to the real orbital parameters. The default attitude
  86.      * provider is aligned with the orbit's inertial frame.
  87.      * </p>
  88.      * <p>
  89.      * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
  90.      * are selected, which means that if the builder is used for orbit determination or
  91.      * propagator conversion, all orbital parameters will be estimated. If only a subset
  92.      * of the orbital parameters must be estimated, caller must retrieve the orbital
  93.      * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
  94.      * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
  95.      * </p>
  96.      * @param templateOrbit reference orbit from which real orbits will be built
  97.      * @param positionAngleType position angle type to use
  98.      * @param positionScale scaling factor used for orbital parameters normalization
  99.      * (typically set to the expected standard deviation of the position)
  100.      * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
  101.      * be set up for central attraction coefficient
  102.      * @since 8.0
  103.      * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean,
  104.      * AttitudeProvider)
  105.      */
  106.     protected AbstractPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType,
  107.                                         final double positionScale, final boolean addDriverForCentralAttraction) {
  108.         this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction,
  109.              new FrameAlignedProvider(templateOrbit.getFrame()), Propagator.DEFAULT_MASS);
  110.     }
  111.     /** Build a new instance.
  112.      * <p>
  113.      * The template orbit is used as a model to {@link
  114.      * #createInitialOrbit() create initial orbit}. It defines the
  115.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  116.      * used together with the {@code positionScale} to convert from the {@link
  117.      * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
  118.      * callers of this builder to the real orbital parameters.
  119.      * </p>
  120.      * <p>
  121.      * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
  122.      * are selected, which means that if the builder is used for orbit determination or
  123.      * propagator conversion, all orbital parameters will be estimated. If only a subset
  124.      * of the orbital parameters must be estimated, caller must retrieve the orbital
  125.      * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
  126.      * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
  127.      * </p>
  128.      * @param templateOrbit reference orbit from which real orbits will be built
  129.      * @param positionAngleType position angle type to use
  130.      * @param positionScale scaling factor used for orbital parameters normalization
  131.      * (typically set to the expected standard deviation of the position)
  132.      * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
  133.      * be set up for central attraction coefficient
  134.      * @param attitudeProvider for the propagator.
  135.      * @since 10.1
  136.      * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean)
  137.      */
  138.     protected AbstractPropagatorBuilder(final Orbit templateOrbit,
  139.                                         final PositionAngleType positionAngleType,
  140.                                         final double positionScale,
  141.                                         final boolean addDriverForCentralAttraction,
  142.                                         final AttitudeProvider attitudeProvider) {
  143.         this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction, attitudeProvider,
  144.                 Propagator.DEFAULT_MASS);
  145.     }

  146.     /** Build a new instance.
  147.      * <p>
  148.      * The template orbit is used as a model to {@link
  149.      * #createInitialOrbit() create initial orbit}. It defines the
  150.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  151.      * used together with the {@code positionScale} to convert from the {@link
  152.      * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
  153.      * callers of this builder to the real orbital parameters.
  154.      * </p>
  155.      * <p>
  156.      * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
  157.      * are selected, which means that if the builder is used for orbit determination or
  158.      * propagator conversion, all orbital parameters will be estimated. If only a subset
  159.      * of the orbital parameters must be estimated, caller must retrieve the orbital
  160.      * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
  161.      * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
  162.      * </p>
  163.      * @param templateOrbit reference orbit from which real orbits will be built
  164.      * @param positionAngleType position angle type to use
  165.      * @param positionScale scaling factor used for orbital parameters normalization
  166.      * (typically set to the expected standard deviation of the position)
  167.      * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
  168.      * be set up for central attraction coefficient
  169.      * @param attitudeProvider for the propagator.
  170.      * @param initialMass mass
  171.      * @since 12.2
  172.      * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean)
  173.      */
  174.     protected AbstractPropagatorBuilder(final Orbit templateOrbit,
  175.                                         final PositionAngleType positionAngleType,
  176.                                         final double positionScale,
  177.                                         final boolean addDriverForCentralAttraction,
  178.                                         final AttitudeProvider attitudeProvider, final double initialMass) {

  179.         this.initialOrbitDate    = templateOrbit.getDate();
  180.         this.frame               = templateOrbit.getFrame();
  181.         this.mu                  = templateOrbit.getMu();
  182.         this.propagationDrivers  = new ParameterDriversList();
  183.         this.orbitType           = templateOrbit.getType();
  184.         this.positionAngleType = positionAngleType;
  185.         this.positionScale       = positionScale;
  186.         this.orbitalDrivers      = orbitType.getDrivers(positionScale, templateOrbit, positionAngleType);
  187.         this.attitudeProvider = attitudeProvider;
  188.         this.mass         = initialMass;
  189.         for (final DelegatingDriver driver : orbitalDrivers.getDrivers()) {
  190.             driver.setSelected(true);
  191.         }

  192.         this.additionalDerivativesProviders  = new ArrayList<>();

  193.         if (addDriverForCentralAttraction) {
  194.             final ParameterDriver muDriver = new ParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  195.                                                                  mu, MU_SCALE, 0, Double.POSITIVE_INFINITY);
  196.             muDriver.addObserver(new ParameterObserver() {
  197.                 /** {@inheritDoc} */
  198.                 @Override
  199.                 public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
  200.                     // getValue(), can be called without argument as mu driver should have only one span
  201.                     AbstractPropagatorBuilder.this.mu = driver.getValue();
  202.                 }

  203.                 @Override
  204.                 public void valueSpanMapChanged(final TimeSpanMap<Double> previousValueSpanMap, final ParameterDriver driver) {
  205.                     // getValue(), can be called without argument as mu driver should have only one span
  206.                     AbstractPropagatorBuilder.this.mu = driver.getValue();
  207.                 }
  208.             });
  209.             propagationDrivers.add(muDriver);
  210.         }

  211.     }

  212.     /** Get the mass.
  213.      * @return the mass (kg)
  214.      * @since 9.2
  215.      */
  216.     public double getMass()
  217.     {
  218.         return mass;
  219.     }

  220.     /** Set the initial mass.
  221.      * @param mass the mass (kg)
  222.      */
  223.     public void setMass(final double mass) {
  224.         this.mass = mass;
  225.     }

  226.     /** {@inheritDoc} */
  227.     public OrbitType getOrbitType() {
  228.         return orbitType;
  229.     }

  230.     /** {@inheritDoc} */
  231.     public PositionAngleType getPositionAngleType() {
  232.         return positionAngleType;
  233.     }

  234.     /** {@inheritDoc} */
  235.     public AbsoluteDate getInitialOrbitDate() {
  236.         return initialOrbitDate;
  237.     }

  238.     /** {@inheritDoc} */
  239.     public Frame getFrame() {
  240.         return frame;
  241.     }

  242.     /** {@inheritDoc} */
  243.     public ParameterDriversList getOrbitalParametersDrivers() {
  244.         return orbitalDrivers;
  245.     }

  246.     /** {@inheritDoc} */
  247.     public ParameterDriversList getPropagationParametersDrivers() {
  248.         return propagationDrivers;
  249.     }

  250.     @Override
  251.     public AbstractPropagatorBuilder<T> clone() {
  252.         try {
  253.             return (AbstractPropagatorBuilder<T>) super.clone();
  254.         } catch (CloneNotSupportedException cnse) {
  255.             throw new OrekitException(OrekitMessages.PROPAGATOR_BUILDER_NOT_CLONEABLE);
  256.         }
  257.     }

  258.     /**
  259.      * Get the attitude provider.
  260.      *
  261.      * @return the attitude provider
  262.      * @since 10.1
  263.      */
  264.     public AttitudeProvider getAttitudeProvider() {
  265.         return attitudeProvider;
  266.     }

  267.     /**
  268.      * Set the attitude provider.
  269.      *
  270.      * @param attitudeProvider attitude provider
  271.      * @since 10.1
  272.      */
  273.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  274.         this.attitudeProvider = attitudeProvider;
  275.     }

  276.     /** Get the position scale.
  277.      * @return the position scale used to scale the orbital drivers
  278.      */
  279.     public double getPositionScale() {
  280.         return positionScale;
  281.     }

  282.     /** {@inheritDoc} */
  283.     @Override
  284.     public double getMu() {
  285.         return mu;
  286.     }

  287.     /** Get the number of estimated values for selected parameters.
  288.      * @return number of estimated values for selected parameters
  289.      */
  290.     private int getNbValuesForSelected() {

  291.         int count = 0;

  292.         // count orbital parameters
  293.         for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
  294.             if (driver.isSelected()) {
  295.                 count += driver.getNbOfValues();
  296.             }
  297.         }

  298.         // count propagation parameters
  299.         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
  300.             if (driver.isSelected()) {
  301.                 count += driver.getNbOfValues();
  302.             }
  303.         }

  304.         return count;

  305.     }

  306.     /** {@inheritDoc} */
  307.     public double[] getSelectedNormalizedParameters() {

  308.         // allocate array
  309.         final double[] selected = new double[getNbValuesForSelected()];

  310.         // fill data
  311.         int index = 0;
  312.         for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
  313.             if (driver.isSelected()) {
  314.                 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
  315.                     selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
  316.                 }
  317.             }
  318.         }
  319.         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
  320.             if (driver.isSelected()) {
  321.                 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
  322.                     selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
  323.                 }
  324.             }
  325.         }

  326.         return selected;

  327.     }

  328.     /** {@inheritDoc} */
  329.     @Override
  330.     public abstract T buildPropagator(double[] normalizedParameters);

  331.     /** {@inheritDoc} */
  332.     @Override
  333.     public T buildPropagator() {
  334.         return buildPropagator(getSelectedNormalizedParameters());
  335.     }

  336.     /** Build an initial orbit using the current selected parameters.
  337.      * <p>
  338.      * This method is a stripped down version of {@link #buildPropagator(double[])}
  339.      * that only builds the initial orbit and not the full propagator.
  340.      * </p>
  341.      * @return an initial orbit
  342.      * @since 8.0
  343.      */
  344.     protected Orbit createInitialOrbit() {
  345.         final double[] unNormalized = new double[orbitalDrivers.getNbParams()];
  346.         for (int i = 0; i < unNormalized.length; ++i) {
  347.             unNormalized[i] = orbitalDrivers.getDrivers().get(i).getValue(initialOrbitDate);
  348.         }
  349.         return getOrbitType().mapArrayToOrbit(unNormalized, null, positionAngleType, initialOrbitDate, mu, frame);
  350.     }

  351.     /** Set the selected parameters.
  352.      * @param normalizedParameters normalized values for the selected parameters
  353.      */
  354.     protected void setParameters(final double[] normalizedParameters) {


  355.         if (normalizedParameters.length != getNbValuesForSelected()) {
  356.             throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  357.                                                      normalizedParameters.length,
  358.                                                      getNbValuesForSelected());
  359.         }

  360.         int index = 0;

  361.         // manage orbital parameters
  362.         for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
  363.             if (driver.isSelected()) {
  364.                 // If the parameter driver contains only 1 value to estimate over the all time range, which
  365.                 // is normally always the case for orbital drivers
  366.                 if (driver.getNbOfValues() == 1) {
  367.                     driver.setNormalizedValue(normalizedParameters[index++], null);

  368.                 } else {

  369.                     for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
  370.                         driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
  371.                     }
  372.                 }
  373.             }
  374.         }

  375.         // manage propagation parameters
  376.         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {

  377.             if (driver.isSelected()) {

  378.                 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
  379.                     driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
  380.                 }
  381.             }
  382.         }
  383.     }

  384.     /**
  385.      * Add supported parameters.
  386.      *
  387.      * @param drivers drivers for the parameters
  388.      */
  389.     protected void addSupportedParameters(final List<ParameterDriver> drivers) {
  390.         drivers.forEach(propagationDrivers::add);
  391.         propagationDrivers.sort();
  392.     }

  393.     /** Reset the orbit in the propagator builder.
  394.      * @param newOrbit New orbit to set in the propagator builder
  395.      */
  396.     public void resetOrbit(final Orbit newOrbit) {

  397.         // Map the new orbit in an array of double
  398.         final double[] orbitArray = new double[6];
  399.         final Orbit orbitInCorrectFrame = (newOrbit.getFrame() == frame) ? newOrbit : newOrbit.inFrame(frame);
  400.         orbitType.mapOrbitToArray(orbitInCorrectFrame, getPositionAngleType(), orbitArray, null);

  401.         // Update all the orbital drivers, selected or unselected
  402.         // Reset values and reference values
  403.         final List<DelegatingDriver> orbitalDriversList = getOrbitalParametersDrivers().getDrivers();
  404.         int i = 0;
  405.         for (DelegatingDriver driver : orbitalDriversList) {
  406.             driver.setReferenceValue(orbitArray[i]);
  407.             driver.setValue(orbitArray[i++], newOrbit.getDate());
  408.         }

  409.         // Change the initial orbit date in the builder
  410.         this.initialOrbitDate = newOrbit.getDate();
  411.     }

  412.     /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer).
  413.      * @param provider provider for additional derivatives
  414.      * @since 11.1
  415.      */
  416.     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {
  417.         additionalDerivativesProviders.add(provider);
  418.     }

  419.     /** Get the list of additional equations.
  420.      * @return the list of additional equations
  421.      * @since 11.1
  422.      */
  423.     protected List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
  424.         return additionalDerivativesProviders;
  425.     }

  426.     /** Deselects orbital and propagation drivers. */
  427.     public void deselectDynamicParameters() {
  428.         for (ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
  429.             driver.setSelected(false);
  430.         }
  431.         for (ParameterDriver driver : getOrbitalParametersDrivers().getDrivers()) {
  432.             driver.setSelected(false);
  433.         }
  434.     }

  435. }