1   /* Copyright 2002-2026 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  
19  import org.hipparchus.exception.LocalizedCoreFormats;
20  import org.hipparchus.util.FastMath;
21  import org.orekit.attitudes.AttitudeProvider;
22  import org.orekit.attitudes.FrameAlignedProvider;
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitIllegalArgumentException;
25  import org.orekit.errors.OrekitMessages;
26  import org.orekit.forces.gravity.NewtonianAttraction;
27  import org.orekit.orbits.Orbit;
28  import org.orekit.orbits.OrbitalParameterFactory;
29  import org.orekit.orbits.OrbitalParameters;
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.ParameterObserver;
37  import org.orekit.utils.TimeSpanMap;
38  import org.orekit.utils.TimeSpanMap.Span;
39  
40  import java.util.ArrayList;
41  import java.util.List;
42  
43  /** Base class for propagator builders.
44   * @param <T> type of the propagator
45   * @param <O> type of the orbital parameters
46   * @param <F> type of the orbital parameters factory
47   * @author Pascal Parraud
48   * @since 7.1
49   */
50  public abstract class AbstractPropagatorBuilder<T extends AbstractPropagator,
51                                                  O extends OrbitalParameters,
52                                                  F extends OrbitalParameterFactory<O>>
53      implements PropagatorBuilder {
54  
55      /** Central attraction scaling factor.
56       * <p>
57       * We use a power of 2 to avoid numeric noise introduction
58       * in the multiplications/divisions sequences.
59       * </p>
60       */
61      private static final double MU_SCALE = FastMath.scalb(1.0, 32);
62  
63      /** Factory for initial orbit.
64       * @since 14.0
65       */
66      private final F factory;
67  
68      /** Initial mass. */
69      private double mass;
70  
71      /** List of the supported propagation parameters. */
72      private final ParameterDriversList propagationDrivers;
73  
74      /** Attitude provider for the propagator. */
75      private AttitudeProvider attitudeProvider;
76  
77      /** Additional derivatives providers.
78       * @since 11.1
79       */
80      private final List<AdditionalDerivativesProvider> additionalDerivativesProviders;
81  
82      /** Build a new instance.
83       * <p>
84       * By default, all the orbital parameters drivers
85       * are selected, which means that if the builder is used for orbit determination or
86       * propagator conversion, all orbital parameters will be estimated. If only a subset
87       * of the orbital parameters must be estimated, caller must retrieve the orbital
88       * parameters by calling {@link #getOrbitalParameterFactory()}.{@link OrbitalParameterFactory#getOrbitalParametersDrivers()}
89       * and then call {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
90       * </p>
91       * @param factory factory for initial orbit
92       * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
93       * be set up for central attraction coefficient
94       * @since 14.0
95       */
96      protected AbstractPropagatorBuilder(final F factory,
97                                          final boolean addDriverForCentralAttraction) {
98          this(factory, addDriverForCentralAttraction,
99               new FrameAlignedProvider(factory.getFrame()), Propagator.DEFAULT_MASS);
100     }
101     /** Build a new instance.
102      * <p>
103      * By default, all the orbital parameters drivers
104      * are selected, which means that if the builder is used for orbit determination or
105      * propagator conversion, all orbital parameters will be estimated. If only a subset
106      * of the orbital parameters must be estimated, caller must retrieve the orbital
107      * parameters by calling {@link #getOrbitalParameterFactory()}.{@link OrbitalParameterFactory#getOrbitalParametersDrivers()}
108      * and then call {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
109      * </p>
110      * @param factory factory for initial orbit
111      * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
112      * be set up for central attraction coefficient
113      * @param attitudeProvider for the propagator.
114      * @since 14.0
115      */
116     protected AbstractPropagatorBuilder(final F factory,
117                                         final boolean addDriverForCentralAttraction,
118                                         final AttitudeProvider attitudeProvider) {
119         this(factory, addDriverForCentralAttraction, attitudeProvider,
120                 Propagator.DEFAULT_MASS);
121     }
122 
123     /** Build a new instance.
124      * <p>
125      * By default, all the orbital parameters drivers
126      * are selected, which means that if the builder is used for orbit determination or
127      * propagator conversion, all orbital parameters will be estimated. If only a subset
128      * of the orbital parameters must be estimated, caller must retrieve the orbital
129      * parameters by calling {@link #getOrbitalParameterFactory()}.{@link OrbitalParameterFactory#getOrbitalParametersDrivers()}
130      * and then call {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
131      * </p>
132      * @param factory factory for initial orbit
133      * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
134      * be set up for central attraction coefficient
135      * @param attitudeProvider for the propagator.
136      * @param initialMass mass
137      * @since 14.0
138      */
139     protected AbstractPropagatorBuilder(final F factory,
140                                         final boolean addDriverForCentralAttraction,
141                                         final AttitudeProvider attitudeProvider, final double initialMass) {
142 
143         this.factory             = factory;
144         this.attitudeProvider    = attitudeProvider;
145         this.mass                = initialMass;
146         for (final ParameterDriver driver : factory.getOrbitalParametersDrivers().getDrivers()) {
147             // by default, we always select the orbital parameters
148             driver.setSelected(true);
149         }
150 
151         this.additionalDerivativesProviders = new ArrayList<>();
152 
153         if (addDriverForCentralAttraction) {
154 
155             // we need to use a separate list, to avoid messing with the one from the factory
156             // when adding the driver for mu
157             propagationDrivers = new ParameterDriversList();
158             factory.getNonKeplerianParametersDrivers().getDrivers().forEach(propagationDrivers::add);
159 
160             final ParameterDriver muDriver = new ParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
161                                                                  factory.getMu(), MU_SCALE, 0, Double.POSITIVE_INFINITY);
162             muDriver.addObserver(new ParameterObserver() {
163                 /** {@inheritDoc} */
164                 @Override
165                 public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
166                     // getValue(), can be called without argument as mu driver should have only one span
167                     factory.setMu(driver.getValue());
168                 }
169 
170                 @Override
171                 public void valueSpanMapChanged(final TimeSpanMap<Double> previousValueSpanMap, final ParameterDriver driver) {
172                     // getValue(), can be called without argument as mu driver should have only one span
173                     factory.setMu(driver.getValue());
174                 }
175             });
176             propagationDrivers.add(muDriver);
177         } else {
178             // we just reuse the original list from the factory
179             propagationDrivers = factory.getNonKeplerianParametersDrivers();
180         }
181 
182     }
183 
184     /** Get the mass.
185      * @return the mass (kg)
186      * @since 9.2
187      */
188     public double getMass()
189     {
190         return mass;
191     }
192 
193     /** Set the initial mass.
194      * @param mass the mass (kg)
195      */
196     public void setMass(final double mass) {
197         this.mass = mass;
198     }
199 
200     /** {@inheritDoc} */
201     public F getOrbitalParameterFactory() {
202         return factory;
203     }
204 
205     /** {@inheritDoc} */
206     public ParameterDriversList getPropagationParametersDrivers() {
207         return propagationDrivers;
208     }
209 
210     /** {@inheritDoc}. */
211     @Override
212     @SuppressWarnings("unchecked")
213     public AbstractPropagatorBuilder<T, O, F> clone() {
214         try {
215             return (AbstractPropagatorBuilder<T, O, F>) super.clone();
216         } catch (CloneNotSupportedException cnse) {
217             throw new OrekitException(OrekitMessages.PROPAGATOR_BUILDER_NOT_CLONEABLE);
218         }
219     }
220 
221     /**
222      * Get the attitude provider.
223      *
224      * @return the attitude provider
225      * @since 10.1
226      */
227     public AttitudeProvider getAttitudeProvider() {
228         return attitudeProvider;
229     }
230 
231     /**
232      * Set the attitude provider.
233      *
234      * @param attitudeProvider attitude provider
235      * @since 10.1
236      */
237     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
238         this.attitudeProvider = attitudeProvider;
239     }
240 
241     /** Get the number of estimated values for selected parameters.
242      * @return number of estimated values for selected parameters
243      */
244     private int getNbValuesForSelected() {
245 
246         int count = 0;
247 
248         // count orbital parameters
249         for (final ParameterDriver driver : factory.getOrbitalParametersDrivers().getDrivers()) {
250             if (driver.isSelected()) {
251                 count += driver.getNbOfValues();
252             }
253         }
254 
255         // count propagation parameters
256         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
257             if (driver.isSelected()) {
258                 count += driver.getNbOfValues();
259             }
260         }
261 
262         return count;
263 
264     }
265 
266     /** {@inheritDoc} */
267     public double[] getSelectedNormalizedParameters() {
268 
269         // allocate array
270         final double[] selected = new double[getNbValuesForSelected()];
271 
272         // fill data
273         int index = 0;
274         for (final ParameterDriver driver : factory.getOrbitalParametersDrivers().getDrivers()) {
275             if (driver.isSelected()) {
276                 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
277                     selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
278                 }
279             }
280         }
281         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
282             if (driver.isSelected()) {
283                 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
284                     selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
285                 }
286             }
287         }
288 
289         return selected;
290 
291     }
292 
293     /** {@inheritDoc} */
294     @Override
295     public abstract T buildPropagator(double[] normalizedParameters);
296 
297     /** {@inheritDoc} */
298     @Override
299     public T buildPropagator() {
300         return buildPropagator(getSelectedNormalizedParameters());
301     }
302 
303     /** Set the selected parameters.
304      * @param normalizedParameters normalized values for the selected parameters
305      */
306     protected void setParameters(final double[] normalizedParameters) {
307 
308 
309         if (normalizedParameters.length != getNbValuesForSelected()) {
310             throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
311                                                      normalizedParameters.length,
312                                                      getNbValuesForSelected());
313         }
314 
315         int index = 0;
316 
317         // manage orbital parameters
318         for (final ParameterDriver driver : factory.getOrbitalParametersDrivers().getDrivers()) {
319             if (driver.isSelected()) {
320                 // If the parameter driver contains only 1 value to estimate over the all time range, which
321                 // is normally always the case for orbital drivers
322                 if (driver.getNbOfValues() == 1) {
323                     driver.setNormalizedValue(normalizedParameters[index++], null);
324 
325                 } else {
326 
327                     for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
328                         driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
329                     }
330                 }
331             }
332         }
333 
334         // manage propagation parameters
335         for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
336 
337             if (driver.isSelected()) {
338 
339                 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
340                     driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
341                 }
342             }
343         }
344     }
345 
346     /**
347      * Add propagation parameters.
348      *
349      * @param drivers drivers for the propagation parameters
350      */
351     protected void addPropagationParameters(final List<ParameterDriver> drivers) {
352         drivers.forEach(propagationDrivers::add);
353         propagationDrivers.sort();
354     }
355 
356     /** Reset the orbit in the propagator builder.
357      * @param newOrbit New orbit to set in the propagator builder
358      */
359     public void resetOrbit(final Orbit newOrbit) {
360         factory.reset(newOrbit);
361     }
362 
363     /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer).
364      * @param provider provider for additional derivatives
365      * @since 11.1
366      */
367     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {
368         additionalDerivativesProviders.add(provider);
369     }
370 
371     /** Get the list of additional equations.
372      * @return the list of additional equations
373      * @since 11.1
374      */
375     protected List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
376         return additionalDerivativesProviders;
377     }
378 
379     /** Deselects orbital and propagation drivers. */
380     public void deselectDynamicParameters() {
381         for (ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
382             driver.setSelected(false);
383         }
384         for (ParameterDriver driver : factory.getOrbitalParametersDrivers().getDrivers()) {
385             driver.setSelected(false);
386         }
387     }
388 }