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 }