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