BrouwerLyddanePropagatorBuilder.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 java.util.Collections;
  19. import org.hipparchus.util.FastMath;
  20. import org.orekit.attitudes.AttitudeProvider;
  21. import org.orekit.attitudes.FrameAlignedProvider;
  22. import org.orekit.forces.gravity.potential.GravityFieldFactory;
  23. import org.orekit.forces.gravity.potential.TideSystem;
  24. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  25. import org.orekit.orbits.Orbit;
  26. import org.orekit.orbits.OrbitType;
  27. import org.orekit.orbits.PositionAngleType;
  28. import org.orekit.propagation.Propagator;
  29. import org.orekit.propagation.analytical.BrouwerLyddanePropagator;
  30. import org.orekit.propagation.analytical.tle.TLE;
  31. import org.orekit.utils.ParameterDriver;
  32. import org.orekit.utils.ParameterDriversList;

  33. /** Builder for Brouwer-Lyddane propagator.
  34.  * <p>
  35.  * By default, Brouwer-Lyddane model considers only the perturbations due to zonal harmonics.
  36.  * However, for low Earth orbits, the magnitude of the perturbative acceleration due to
  37.  * atmospheric drag can be significant. Warren Phipps' 1992 thesis considered the atmospheric
  38.  * drag by time derivatives of the <i>mean</i> mean anomaly using the catch-all coefficient M2.
  39.  * <p>
  40.  * Usually, M2 is adjusted during an orbit determination process, and it represents the
  41.  * combination of all un-modeled secular along-track effects (i.e. not just the atmospheric drag).
  42.  * The behavior of M2 is closed to the {@link TLE#getBStar()} parameter for the TLE.
  43.  * <p>
  44.  * If the value of M2 is equal to {@link BrouwerLyddanePropagator#M2 0.0}, the along-track
  45.  * secular effects are not considered in the dynamical model. Typical values for M2 are not known.
  46.  * It depends on the orbit type. However, the value of M2 must be very small (e.g. between 1.0e-14 and 1.0e-15).
  47.  * The unit of M2 is rad/s².
  48.  * <p>
  49.  * To estimate the M2 parameter, it is necessary to call the {@link #getPropagationParametersDrivers()} method
  50.  * as follows:
  51.  * <pre>
  52.  *  for (ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {
  53.  *     if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
  54.  *        driver.setSelected(true);
  55.  *     }
  56.  *  }
  57.  * </pre>
  58.  * @author Melina Vanel
  59.  * @author Bryan Cazabonne
  60.  * @since 11.1
  61.  */
  62. public class BrouwerLyddanePropagatorBuilder extends AbstractAnalyticalPropagatorBuilder<BrouwerLyddanePropagator> {

  63.     /** Parameters scaling factor.
  64.      * <p>
  65.      * We use a power of 2 to avoid numeric noise introduction
  66.      * in the multiplications/divisions sequences.
  67.      * </p>
  68.      */
  69.     private static final double SCALE = FastMath.scalb(1.0, -32);

  70.     /** Provider for un-normalized coefficients. */
  71.     private final UnnormalizedSphericalHarmonicsProvider provider;

  72.     /** Build a new instance.
  73.      * <p>
  74.      * The template orbit is used as a model to {@link
  75.      * #createInitialOrbit() create initial orbit}. It defines the
  76.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  77.      * used together with the {@code positionScale} to convert from the {@link
  78.      * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters
  79.      * used by the callers of this builder to the real orbital parameters.
  80.      * The default attitude provider is aligned with the orbit's inertial frame.
  81.      * </p>
  82.      *
  83.      * @param templateOrbit reference orbit from which real orbits will be built
  84.      * (note that the mu from this orbit will be overridden with the mu from the
  85.      * {@code provider})
  86.      * @param provider for un-normalized zonal coefficients
  87.      * @param positionAngleType position angle type to use
  88.      * @param positionScale scaling factor used for orbital parameters normalization
  89.      * (typically set to the expected standard deviation of the position)
  90.      * @param M2 value of empirical drag coefficient in rad/s².
  91.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  92.      * @see #BrouwerLyddanePropagatorBuilder(Orbit,
  93.      * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider, double)
  94.      */
  95.     public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit,
  96.                                            final UnnormalizedSphericalHarmonicsProvider provider,
  97.                                            final PositionAngleType positionAngleType,
  98.                                            final double positionScale,
  99.                                            final double M2) {
  100.         this(templateOrbit, provider, positionAngleType, positionScale,
  101.              FrameAlignedProvider.of(templateOrbit.getFrame()), M2);
  102.     }

  103.     /** Build a new instance.
  104.      * <p>
  105.      * The template orbit is used as a model to {@link
  106.      * #createInitialOrbit() create initial orbit}. It defines the
  107.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  108.      * used together with the {@code positionScale} to convert from the {@link
  109.      * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
  110.      * callers of this builder to the real orbital parameters.
  111.      * </p>
  112.      *
  113.      * @param templateOrbit reference orbit from which real orbits will be built
  114.      * (note that the mu from this orbit will be overridden with the mu from the
  115.      * {@code provider})
  116.      * @param referenceRadius reference radius of the Earth for the potential model (m)
  117.      * @param mu central attraction coefficient (m³/s²)
  118.      * @param tideSystem tide system
  119.      * @param c20 un-normalized zonal coefficient (about -1.08e-3 for Earth)
  120.      * @param c30 un-normalized zonal coefficient (about +2.53e-6 for Earth)
  121.      * @param c40 un-normalized zonal coefficient (about +1.62e-6 for Earth)
  122.      * @param c50 un-normalized zonal coefficient (about +2.28e-7 for Earth)
  123.      * @param orbitType orbit type to use
  124.      * @param positionAngleType position angle type to use
  125.      * @param positionScale scaling factor used for orbital parameters normalization
  126.      * (typically set to the expected standard deviation of the position)
  127.      * @param M2 value of empirical drag coefficient in rad/s².
  128.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  129.      * @see #BrouwerLyddanePropagatorBuilder(Orbit,
  130.      * UnnormalizedSphericalHarmonicsProvider, PositionAngleType, double, AttitudeProvider, double)
  131.      */
  132.     public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit,
  133.                                            final double referenceRadius,
  134.                                            final double mu,
  135.                                            final TideSystem tideSystem,
  136.                                            final double c20,
  137.                                            final double c30,
  138.                                            final double c40,
  139.                                            final double c50,
  140.                                            final OrbitType orbitType,
  141.                                            final PositionAngleType positionAngleType,
  142.                                            final double positionScale,
  143.                                            final double M2) {
  144.         this(templateOrbit,
  145.              GravityFieldFactory.getUnnormalizedProvider(referenceRadius, mu, tideSystem,
  146.                                                          new double[][] {
  147.                                                              {
  148.                                                                  0
  149.                                                              }, {
  150.                                                                  0
  151.                                                              }, {
  152.                                                                  c20
  153.                                                              }, {
  154.                                                                  c30
  155.                                                              }, {
  156.                                                                  c40
  157.                                                              }, {
  158.                                                                  c50
  159.                                                              }
  160.                                                          }, new double[][] {
  161.                                                              {
  162.                                                                  0
  163.                                                              }, {
  164.                                                                  0
  165.                                                              }, {
  166.                                                                  0
  167.                                                              }, {
  168.                                                                  0
  169.                                                              }, {
  170.                                                                  0
  171.                                                              }, {
  172.                                                                  0
  173.                                                              }
  174.                                                          }),
  175.                 positionAngleType, positionScale, M2);
  176.     }

  177.     /** Build a new instance.
  178.      * <p>
  179.      * The template orbit is used as a model to {@link
  180.      * #createInitialOrbit() create initial orbit}. It defines the
  181.      * inertial frame, the central attraction coefficient, the orbit type, and is also
  182.      * used together with the {@code positionScale} to convert from the {@link
  183.      * org.orekit.utils.ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
  184.      * callers of this builder to the real orbital parameters.
  185.      * </p>
  186.      * @param templateOrbit reference orbit from which real orbits will be built
  187.      * (note that the mu from this orbit will be overridden with the mu from the
  188.      * {@code provider})
  189.      * @param provider for un-normalized zonal coefficients
  190.      * @param positionAngleType position angle type to use
  191.      * @param positionScale scaling factor used for orbital parameters normalization
  192.      * (typically set to the expected standard deviation of the position)
  193.      * @param attitudeProvider attitude law to use
  194.      * @param M2 value of empirical drag coefficient in rad/s².
  195.      *        If equal to {@link BrouwerLyddanePropagator#M2} drag is not computed
  196.      */
  197.     public BrouwerLyddanePropagatorBuilder(final Orbit templateOrbit,
  198.                                            final UnnormalizedSphericalHarmonicsProvider provider,
  199.                                            final PositionAngleType positionAngleType,
  200.                                            final double positionScale,
  201.                                            final AttitudeProvider attitudeProvider,
  202.                                            final double M2) {
  203.         super(overrideMu(templateOrbit, provider, positionAngleType), positionAngleType, positionScale, true, attitudeProvider,
  204.             Propagator.DEFAULT_MASS);
  205.         this.provider = provider;
  206.         // initialize M2 driver
  207.         final ParameterDriver M2Driver = new ParameterDriver(BrouwerLyddanePropagator.M2_NAME, M2, SCALE,
  208.                                                              Double.NEGATIVE_INFINITY,
  209.                                                              Double.POSITIVE_INFINITY);
  210.         addSupportedParameters(Collections.singletonList(M2Driver));
  211.     }

  212.     /** Copy constructor.
  213.      * @param builder builder to copy from
  214.      */
  215.     private BrouwerLyddanePropagatorBuilder(final BrouwerLyddanePropagatorBuilder builder) {
  216.         this(builder.createInitialOrbit(), builder.provider, builder.getPositionAngleType(),
  217.              builder.getPositionScale(), builder.getAttitudeProvider(), builder.getM2Value());
  218.     }

  219.     /** {@inheritDoc}. */
  220.     @Override
  221.     public BrouwerLyddanePropagatorBuilder clone() {
  222.         // Call to super clone() method to avoid warning
  223.         final BrouwerLyddanePropagatorBuilder clonedBuilder = (BrouwerLyddanePropagatorBuilder) super.clone();

  224.         // Use copy constructor to unlink orbital drivers
  225.         final BrouwerLyddanePropagatorBuilder builder = new BrouwerLyddanePropagatorBuilder(clonedBuilder);

  226.         // Set mass
  227.         builder.setMass(getMass());

  228.         // Ensure drivers' selection consistency
  229.         final ParameterDriversList propDrivers = clonedBuilder.getPropagationParametersDrivers();
  230.         builder.getPropagationParametersDrivers().getDrivers().
  231.                         forEach(driver -> driver.setSelected(propDrivers.findByName(driver.getName()).isSelected()));

  232.         // Return cloned builder
  233.         return builder;
  234.     }

  235.     /** Override central attraction coefficient.
  236.      * @param templateOrbit template orbit
  237.      * @param provider gravity field provider
  238.      * @param positionAngleType position angle type to use
  239.      * @return orbit with overridden central attraction coefficient
  240.      */
  241.     private static Orbit overrideMu(final Orbit templateOrbit,
  242.                                     final UnnormalizedSphericalHarmonicsProvider provider,
  243.                                     final PositionAngleType positionAngleType) {
  244.         final double[] parameters    = new double[6];
  245.         final double[] parametersDot = parameters.clone();
  246.         templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngleType, parameters, parametersDot);
  247.         return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngleType,
  248.                                                        templateOrbit.getDate(),
  249.                                                        provider.getMu(),
  250.                                                        templateOrbit.getFrame());
  251.     }

  252.     /** {@inheritDoc} */
  253.     public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParameters) {
  254.         setParameters(normalizedParameters);

  255.         // Update M2 value and selection
  256.         double  newM2      = 0.0;
  257.         boolean isSelected = false;
  258.         for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
  259.             if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
  260.                 // it is OK as BL m2 parameterDriver has 1 value estimated from -INF to +INF, and
  261.                 // setPeriod method should not be called on this driver (to have several values estimated)
  262.                 newM2      = driver.getValue();
  263.                 isSelected = driver.isSelected();
  264.             }
  265.         }

  266.         // Initialize propagator
  267.         final BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(createInitialOrbit(), getAttitudeProvider(), getMass(),
  268.             provider, newM2);
  269.         propagator.getParametersDrivers().get(0).setSelected(isSelected);
  270.         getImpulseManeuvers().forEach(propagator::addEventDetector);

  271.         // Return
  272.         return propagator;

  273.     }

  274.     /**
  275.      * Get the value of the M2 parameter.
  276.      * <p>
  277.      *  M2 represents the combination of all un-modeled secular along-track effects
  278.      *  (e.g. drag). It is usually fitted during an orbit determination.
  279.      * </p>
  280.      * @return the value of the M2 parameter
  281.      * @since 12.2
  282.      */
  283.     public double getM2Value() {
  284.         double m2 = 0.0;
  285.         for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
  286.             if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
  287.                 m2 = driver.getValue();
  288.             }
  289.         }
  290.         return m2;
  291.     }
  292. }