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

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

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

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

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

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

  211.     /** Override central attraction coefficient.
  212.      * @param templateOrbit template orbit
  213.      * @param provider gravity field provider
  214.      * @param positionAngleType position angle type to use
  215.      * @return orbit with overridden central attraction coefficient
  216.      */
  217.     private static Orbit overrideMu(final Orbit templateOrbit,
  218.                                     final UnnormalizedSphericalHarmonicsProvider provider,
  219.                                     final PositionAngleType positionAngleType) {
  220.         final double[] parameters    = new double[6];
  221.         final double[] parametersDot = parameters.clone();
  222.         templateOrbit.getType().mapOrbitToArray(templateOrbit, positionAngleType, parameters, parametersDot);
  223.         return templateOrbit.getType().mapArrayToOrbit(parameters, parametersDot, positionAngleType,
  224.                                                        templateOrbit.getDate(),
  225.                                                        provider.getMu(),
  226.                                                        templateOrbit.getFrame());
  227.     }

  228.     /** {@inheritDoc} */
  229.     public BrouwerLyddanePropagator buildPropagator(final double[] normalizedParameters) {
  230.         setParameters(normalizedParameters);

  231.         // Update M2 value and selection
  232.         double  newM2      = 0.0;
  233.         boolean isSelected = false;
  234.         for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
  235.             if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
  236.                 // it is OK as BL m2 parameterDriver has 1 value estimated from -INF to +INF, and
  237.                 // setPeriod method should not be called on this driver (to have several values estimated)
  238.                 newM2      = driver.getValue();
  239.                 isSelected = driver.isSelected();
  240.             }
  241.         }

  242.         // Initialize propagator
  243.         final BrouwerLyddanePropagator propagator = new BrouwerLyddanePropagator(createInitialOrbit(), getAttitudeProvider(), getMass(),
  244.             provider, newM2);
  245.         propagator.getParametersDrivers().get(0).setSelected(isSelected);
  246.         getImpulseManeuvers().forEach(propagator::addEventDetector);

  247.         // Return
  248.         return propagator;

  249.     }

  250.     /**
  251.      * Get the value of the M2 parameter.
  252.      * <p>
  253.      *  M2 represents the combination of all unmodeled secular along-track effects
  254.      *  (e.g. drag). It is usually fitted during an orbit determination.
  255.      * </p>
  256.      * @return the value of the M2 parameter
  257.      * @since 12.2
  258.      */
  259.     public double getM2Value() {
  260.         double m2 = 0.0;
  261.         for (final ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
  262.             if (BrouwerLyddanePropagator.M2_NAME.equals(driver.getName())) {
  263.                 m2 = driver.getValue();
  264.             }
  265.         }
  266.         return m2;
  267.     }
  268. }