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.frames.Frame;
28 import org.orekit.orbits.Orbit;
29 import org.orekit.orbits.OrbitType;
30 import org.orekit.orbits.PositionAngleType;
31 import org.orekit.propagation.AbstractPropagator;
32 import org.orekit.propagation.Propagator;
33 import org.orekit.propagation.integration.AdditionalDerivativesProvider;
34 import org.orekit.time.AbsoluteDate;
35 import org.orekit.utils.ParameterDriver;
36 import org.orekit.utils.ParameterDriversList;
37 import org.orekit.utils.ParameterDriversList.DelegatingDriver;
38 import org.orekit.utils.ParameterObserver;
39 import org.orekit.utils.TimeSpanMap;
40 import org.orekit.utils.TimeSpanMap.Span;
41
42 import java.util.ArrayList;
43 import java.util.List;
44
45 /** Base class for propagator builders.
46 * @param <T> type of the propagator
47 * @author Pascal Parraud
48 * @since 7.1
49 */
50 public abstract class AbstractPropagatorBuilder<T extends AbstractPropagator> implements PropagatorBuilder {
51
52 /** Central attraction scaling factor.
53 * <p>
54 * We use a power of 2 to avoid numeric noise introduction
55 * in the multiplications/divisions sequences.
56 * </p>
57 */
58 private static final double MU_SCALE = FastMath.scalb(1.0, 32);
59
60 /** Date of the initial orbit. */
61 private AbsoluteDate initialOrbitDate;
62
63 /** Frame in which the orbit is propagated. */
64 private final Frame frame;
65
66 /** Central attraction coefficient (m³/s²). */
67 private double mu;
68
69 /** Initial mass. */
70 private double mass;
71
72 /** Drivers for orbital parameters. */
73 private final ParameterDriversList orbitalDrivers;
74
75 /** List of the supported parameters. */
76 private final ParameterDriversList propagationDrivers;
77
78 /** Orbit type to use. */
79 private final OrbitType orbitType;
80
81 /** Position angle type to use. */
82 private final PositionAngleType positionAngleType;
83
84 /** Position scale to use for the orbital drivers. */
85 private final double positionScale;
86
87 /** Attitude provider for the propagator. */
88 private AttitudeProvider attitudeProvider;
89
90 /** Additional derivatives providers.
91 * @since 11.1
92 */
93 private final List<AdditionalDerivativesProvider> additionalDerivativesProviders;
94
95 /** Build a new instance.
96 * <p>
97 * The template orbit is used as a model to {@link
98 * #createInitialOrbit() create initial orbit}. It defines the
99 * inertial frame, the central attraction coefficient, the orbit type, and is also
100 * used together with the {@code positionScale} to convert from the {@link
101 * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
102 * callers of this builder to the real orbital parameters. The default attitude
103 * provider is aligned with the orbit's inertial frame.
104 * </p>
105 * <p>
106 * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
107 * are selected, which means that if the builder is used for orbit determination or
108 * propagator conversion, all orbital parameters will be estimated. If only a subset
109 * of the orbital parameters must be estimated, caller must retrieve the orbital
110 * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
111 * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
112 * </p>
113 * @param templateOrbit reference orbit from which real orbits will be built
114 * @param positionAngleType position angle type to use
115 * @param positionScale scaling factor used for orbital parameters normalization
116 * (typically set to the expected standard deviation of the position)
117 * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
118 * be set up for central attraction coefficient
119 * @since 8.0
120 * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean,
121 * AttitudeProvider)
122 */
123 protected AbstractPropagatorBuilder(final Orbit templateOrbit, final PositionAngleType positionAngleType,
124 final double positionScale, final boolean addDriverForCentralAttraction) {
125 this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction,
126 new FrameAlignedProvider(templateOrbit.getFrame()), Propagator.DEFAULT_MASS);
127 }
128 /** Build a new instance.
129 * <p>
130 * The template orbit is used as a model to {@link
131 * #createInitialOrbit() create initial orbit}. It defines the
132 * inertial frame, the central attraction coefficient, the orbit type, and is also
133 * used together with the {@code positionScale} to convert from the {@link
134 * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
135 * callers of this builder to the real orbital parameters.
136 * </p>
137 * <p>
138 * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
139 * are selected, which means that if the builder is used for orbit determination or
140 * propagator conversion, all orbital parameters will be estimated. If only a subset
141 * of the orbital parameters must be estimated, caller must retrieve the orbital
142 * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
143 * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
144 * </p>
145 * @param templateOrbit reference orbit from which real orbits will be built
146 * @param positionAngleType position angle type to use
147 * @param positionScale scaling factor used for orbital parameters normalization
148 * (typically set to the expected standard deviation of the position)
149 * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
150 * be set up for central attraction coefficient
151 * @param attitudeProvider for the propagator.
152 * @since 10.1
153 * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean)
154 */
155 protected AbstractPropagatorBuilder(final Orbit templateOrbit,
156 final PositionAngleType positionAngleType,
157 final double positionScale,
158 final boolean addDriverForCentralAttraction,
159 final AttitudeProvider attitudeProvider) {
160 this(templateOrbit, positionAngleType, positionScale, addDriverForCentralAttraction, attitudeProvider,
161 Propagator.DEFAULT_MASS);
162 }
163
164 /** Build a new instance.
165 * <p>
166 * The template orbit is used as a model to {@link
167 * #createInitialOrbit() create initial orbit}. It defines the
168 * inertial frame, the central attraction coefficient, the orbit type, and is also
169 * used together with the {@code positionScale} to convert from the {@link
170 * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
171 * callers of this builder to the real orbital parameters.
172 * </p>
173 * <p>
174 * By default, all the {@link #getOrbitalParametersDrivers() orbital parameters drivers}
175 * are selected, which means that if the builder is used for orbit determination or
176 * propagator conversion, all orbital parameters will be estimated. If only a subset
177 * of the orbital parameters must be estimated, caller must retrieve the orbital
178 * parameters by calling {@link #getOrbitalParametersDrivers()} and then call
179 * {@link ParameterDriver#setSelected(boolean) setSelected(false)}.
180 * </p>
181 * @param templateOrbit reference orbit from which real orbits will be built
182 * @param positionAngleType position angle type to use
183 * @param positionScale scaling factor used for orbital parameters normalization
184 * (typically set to the expected standard deviation of the position)
185 * @param addDriverForCentralAttraction if true, a {@link ParameterDriver} should
186 * be set up for central attraction coefficient
187 * @param attitudeProvider for the propagator.
188 * @param initialMass mass
189 * @since 12.2
190 * @see #AbstractPropagatorBuilder(Orbit, PositionAngleType, double, boolean)
191 */
192 protected AbstractPropagatorBuilder(final Orbit templateOrbit,
193 final PositionAngleType positionAngleType,
194 final double positionScale,
195 final boolean addDriverForCentralAttraction,
196 final AttitudeProvider attitudeProvider, final double initialMass) {
197
198 this.initialOrbitDate = templateOrbit.getDate();
199 this.frame = templateOrbit.getFrame();
200 this.mu = templateOrbit.getMu();
201 this.propagationDrivers = new ParameterDriversList();
202 this.orbitType = templateOrbit.getType();
203 this.positionAngleType = positionAngleType;
204 this.positionScale = positionScale;
205 this.orbitalDrivers = orbitType.getDrivers(positionScale, templateOrbit, positionAngleType);
206 this.attitudeProvider = attitudeProvider;
207 this.mass = initialMass;
208 for (final DelegatingDriver driver : orbitalDrivers.getDrivers()) {
209 driver.setSelected(true);
210 }
211
212 this.additionalDerivativesProviders = new ArrayList<>();
213
214 if (addDriverForCentralAttraction) {
215 final ParameterDriver muDriver = new ParameterDriver(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
216 mu, MU_SCALE, 0, Double.POSITIVE_INFINITY);
217 muDriver.addObserver(new ParameterObserver() {
218 /** {@inheritDoc} */
219 @Override
220 public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
221 // getValue(), can be called without argument as mu driver should have only one span
222 AbstractPropagatorBuilder.this.mu = driver.getValue();
223 }
224
225 @Override
226 public void valueSpanMapChanged(final TimeSpanMap<Double> previousValueSpanMap, final ParameterDriver driver) {
227 // getValue(), can be called without argument as mu driver should have only one span
228 AbstractPropagatorBuilder.this.mu = driver.getValue();
229 }
230 });
231 propagationDrivers.add(muDriver);
232 }
233 }
234
235 /** Get the mass.
236 * @return the mass (kg)
237 * @since 9.2
238 */
239 public double getMass()
240 {
241 return mass;
242 }
243
244 /** Set the initial mass.
245 * @param mass the mass (kg)
246 */
247 public void setMass(final double mass) {
248 this.mass = mass;
249 }
250
251 /** {@inheritDoc} */
252 public OrbitType getOrbitType() {
253 return orbitType;
254 }
255
256 /** {@inheritDoc} */
257 public PositionAngleType getPositionAngleType() {
258 return positionAngleType;
259 }
260
261 /** {@inheritDoc} */
262 public AbsoluteDate getInitialOrbitDate() {
263 return initialOrbitDate;
264 }
265
266 /** {@inheritDoc} */
267 public Frame getFrame() {
268 return frame;
269 }
270
271 /** {@inheritDoc} */
272 public ParameterDriversList getOrbitalParametersDrivers() {
273 return orbitalDrivers;
274 }
275
276 /** {@inheritDoc} */
277 public ParameterDriversList getPropagationParametersDrivers() {
278 return propagationDrivers;
279 }
280
281 /** {@inheritDoc}. */
282 @Override
283 @SuppressWarnings("unchecked")
284 public AbstractPropagatorBuilder<T> clone() {
285 try {
286 return (AbstractPropagatorBuilder<T>) super.clone();
287 } catch (CloneNotSupportedException cnse) {
288 throw new OrekitException(OrekitMessages.PROPAGATOR_BUILDER_NOT_CLONEABLE);
289 }
290 }
291
292 /**
293 * Get the attitude provider.
294 *
295 * @return the attitude provider
296 * @since 10.1
297 */
298 public AttitudeProvider getAttitudeProvider() {
299 return attitudeProvider;
300 }
301
302 /**
303 * Set the attitude provider.
304 *
305 * @param attitudeProvider attitude provider
306 * @since 10.1
307 */
308 public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
309 this.attitudeProvider = attitudeProvider;
310 }
311
312 /** Get the position scale.
313 * @return the position scale used to scale the orbital drivers
314 */
315 public double getPositionScale() {
316 return positionScale;
317 }
318
319 /** {@inheritDoc} */
320 @Override
321 public double getMu() {
322 return mu;
323 }
324
325 /** Get the number of estimated values for selected parameters.
326 * @return number of estimated values for selected parameters
327 */
328 private int getNbValuesForSelected() {
329
330 int count = 0;
331
332 // count orbital parameters
333 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
334 if (driver.isSelected()) {
335 count += driver.getNbOfValues();
336 }
337 }
338
339 // count propagation parameters
340 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
341 if (driver.isSelected()) {
342 count += driver.getNbOfValues();
343 }
344 }
345
346 return count;
347
348 }
349
350 /** {@inheritDoc} */
351 public double[] getSelectedNormalizedParameters() {
352
353 // allocate array
354 final double[] selected = new double[getNbValuesForSelected()];
355
356 // fill data
357 int index = 0;
358 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
359 if (driver.isSelected()) {
360 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
361 selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
362 }
363 }
364 }
365 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
366 if (driver.isSelected()) {
367 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
368 selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
369 }
370 }
371 }
372
373 return selected;
374
375 }
376
377 /** {@inheritDoc} */
378 @Override
379 public abstract T buildPropagator(double[] normalizedParameters);
380
381 /** {@inheritDoc} */
382 @Override
383 public T buildPropagator() {
384 return buildPropagator(getSelectedNormalizedParameters());
385 }
386
387 /** Build an initial orbit using the current selected parameters.
388 * <p>
389 * This method is a stripped down version of {@link #buildPropagator(double[])}
390 * that only builds the initial orbit and not the full propagator.
391 * </p>
392 * @return an initial orbit
393 * @since 8.0
394 */
395 protected Orbit createInitialOrbit() {
396 final double[] unNormalized = new double[orbitalDrivers.getNbParams()];
397 for (int i = 0; i < unNormalized.length; ++i) {
398 unNormalized[i] = orbitalDrivers.getDrivers().get(i).getValue(initialOrbitDate);
399 }
400 return getOrbitType().mapArrayToOrbit(unNormalized, null, positionAngleType, initialOrbitDate, mu, frame);
401 }
402
403 /** Set the selected parameters.
404 * @param normalizedParameters normalized values for the selected parameters
405 */
406 protected void setParameters(final double[] normalizedParameters) {
407
408
409 if (normalizedParameters.length != getNbValuesForSelected()) {
410 throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
411 normalizedParameters.length,
412 getNbValuesForSelected());
413 }
414
415 int index = 0;
416
417 // manage orbital parameters
418 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
419 if (driver.isSelected()) {
420 // If the parameter driver contains only 1 value to estimate over the all time range, which
421 // is normally always the case for orbital drivers
422 if (driver.getNbOfValues() == 1) {
423 driver.setNormalizedValue(normalizedParameters[index++], null);
424
425 } else {
426
427 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
428 driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
429 }
430 }
431 }
432 }
433
434 // manage propagation parameters
435 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
436
437 if (driver.isSelected()) {
438
439 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
440 driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
441 }
442 }
443 }
444 }
445
446 /**
447 * Add supported parameters.
448 *
449 * @param drivers drivers for the parameters
450 */
451 protected void addSupportedParameters(final List<ParameterDriver> drivers) {
452 drivers.forEach(propagationDrivers::add);
453 propagationDrivers.sort();
454 }
455
456 /** Reset the orbit in the propagator builder.
457 * @param newOrbit New orbit to set in the propagator builder
458 */
459 public void resetOrbit(final Orbit newOrbit) {
460
461 // Map the new orbit in an array of double
462 final double[] orbitArray = new double[6];
463 final Orbit orbitInCorrectFrame = (newOrbit.getFrame() == frame) ? newOrbit : newOrbit.inFrame(frame);
464 orbitType.mapOrbitToArray(orbitInCorrectFrame, getPositionAngleType(), orbitArray, null);
465
466 // Update all the orbital drivers, selected or unselected
467 // Reset values and reference values
468 final List<DelegatingDriver> orbitalDriversList = getOrbitalParametersDrivers().getDrivers();
469 int i = 0;
470 for (DelegatingDriver driver : orbitalDriversList) {
471 driver.setReferenceValue(orbitArray[i]);
472 driver.setValue(orbitArray[i++], newOrbit.getDate());
473 }
474
475 // Change the initial orbit date in the builder
476 this.initialOrbitDate = newOrbit.getDate();
477 }
478
479 /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer).
480 * @param provider provider for additional derivatives
481 * @since 11.1
482 */
483 public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {
484 additionalDerivativesProviders.add(provider);
485 }
486
487 /** Get the list of additional equations.
488 * @return the list of additional equations
489 * @since 11.1
490 */
491 protected List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
492 return additionalDerivativesProviders;
493 }
494
495 /** Deselects orbital and propagation drivers. */
496 public void deselectDynamicParameters() {
497 for (ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
498 driver.setSelected(false);
499 }
500 for (ParameterDriver driver : getOrbitalParametersDrivers().getDrivers()) {
501 driver.setSelected(false);
502 }
503 }
504 }