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