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 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 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
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 @Override
282 public AbstractPropagatorBuilder<T> clone() {
283 try {
284 return (AbstractPropagatorBuilder<T>) super.clone();
285 } catch (CloneNotSupportedException cnse) {
286 throw new OrekitException(OrekitMessages.PROPAGATOR_BUILDER_NOT_CLONEABLE);
287 }
288 }
289
290 /**
291 * Get the attitude provider.
292 *
293 * @return the attitude provider
294 * @since 10.1
295 */
296 public AttitudeProvider getAttitudeProvider() {
297 return attitudeProvider;
298 }
299
300 /**
301 * Set the attitude provider.
302 *
303 * @param attitudeProvider attitude provider
304 * @since 10.1
305 */
306 public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
307 this.attitudeProvider = attitudeProvider;
308 }
309
310 /** Get the position scale.
311 * @return the position scale used to scale the orbital drivers
312 */
313 public double getPositionScale() {
314 return positionScale;
315 }
316
317 /** {@inheritDoc} */
318 @Override
319 public double getMu() {
320 return mu;
321 }
322
323 /** Get the number of estimated values for selected parameters.
324 * @return number of estimated values for selected parameters
325 */
326 private int getNbValuesForSelected() {
327
328 int count = 0;
329
330 // count orbital parameters
331 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
332 if (driver.isSelected()) {
333 count += driver.getNbOfValues();
334 }
335 }
336
337 // count propagation parameters
338 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
339 if (driver.isSelected()) {
340 count += driver.getNbOfValues();
341 }
342 }
343
344 return count;
345
346 }
347
348 /** {@inheritDoc} */
349 public double[] getSelectedNormalizedParameters() {
350
351 // allocate array
352 final double[] selected = new double[getNbValuesForSelected()];
353
354 // fill data
355 int index = 0;
356 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
357 if (driver.isSelected()) {
358 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
359 selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
360 }
361 }
362 }
363 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
364 if (driver.isSelected()) {
365 for (int spanNumber = 0; spanNumber < driver.getNbOfValues(); ++spanNumber ) {
366 selected[index++] = driver.getNormalizedValue(AbsoluteDate.ARBITRARY_EPOCH);
367 }
368 }
369 }
370
371 return selected;
372
373 }
374
375 /** {@inheritDoc} */
376 @Override
377 public abstract T buildPropagator(double[] normalizedParameters);
378
379 /** {@inheritDoc} */
380 @Override
381 public T buildPropagator() {
382 return buildPropagator(getSelectedNormalizedParameters());
383 }
384
385 /** Build an initial orbit using the current selected parameters.
386 * <p>
387 * This method is a stripped down version of {@link #buildPropagator(double[])}
388 * that only builds the initial orbit and not the full propagator.
389 * </p>
390 * @return an initial orbit
391 * @since 8.0
392 */
393 protected Orbit createInitialOrbit() {
394 final double[] unNormalized = new double[orbitalDrivers.getNbParams()];
395 for (int i = 0; i < unNormalized.length; ++i) {
396 unNormalized[i] = orbitalDrivers.getDrivers().get(i).getValue(initialOrbitDate);
397 }
398 return getOrbitType().mapArrayToOrbit(unNormalized, null, positionAngleType, initialOrbitDate, mu, frame);
399 }
400
401 /** Set the selected parameters.
402 * @param normalizedParameters normalized values for the selected parameters
403 */
404 protected void setParameters(final double[] normalizedParameters) {
405
406
407 if (normalizedParameters.length != getNbValuesForSelected()) {
408 throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
409 normalizedParameters.length,
410 getNbValuesForSelected());
411 }
412
413 int index = 0;
414
415 // manage orbital parameters
416 for (final ParameterDriver driver : orbitalDrivers.getDrivers()) {
417 if (driver.isSelected()) {
418 // If the parameter driver contains only 1 value to estimate over the all time range, which
419 // is normally always the case for orbital drivers
420 if (driver.getNbOfValues() == 1) {
421 driver.setNormalizedValue(normalizedParameters[index++], null);
422
423 } else {
424
425 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
426 driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
427 }
428 }
429 }
430 }
431
432 // manage propagation parameters
433 for (final ParameterDriver driver : propagationDrivers.getDrivers()) {
434
435 if (driver.isSelected()) {
436
437 for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
438 driver.setNormalizedValue(normalizedParameters[index++], span.getStart());
439 }
440 }
441 }
442 }
443
444 /**
445 * Add supported parameters.
446 *
447 * @param drivers drivers for the parameters
448 */
449 protected void addSupportedParameters(final List<ParameterDriver> drivers) {
450 drivers.forEach(propagationDrivers::add);
451 propagationDrivers.sort();
452 }
453
454 /** Reset the orbit in the propagator builder.
455 * @param newOrbit New orbit to set in the propagator builder
456 */
457 public void resetOrbit(final Orbit newOrbit) {
458
459 // Map the new orbit in an array of double
460 final double[] orbitArray = new double[6];
461 final Orbit orbitInCorrectFrame = (newOrbit.getFrame() == frame) ? newOrbit : newOrbit.inFrame(frame);
462 orbitType.mapOrbitToArray(orbitInCorrectFrame, getPositionAngleType(), orbitArray, null);
463
464 // Update all the orbital drivers, selected or unselected
465 // Reset values and reference values
466 final List<DelegatingDriver> orbitalDriversList = getOrbitalParametersDrivers().getDrivers();
467 int i = 0;
468 for (DelegatingDriver driver : orbitalDriversList) {
469 driver.setReferenceValue(orbitArray[i]);
470 driver.setValue(orbitArray[i++], newOrbit.getDate());
471 }
472
473 // Change the initial orbit date in the builder
474 this.initialOrbitDate = newOrbit.getDate();
475 }
476
477 /** Add a set of user-specified equations to be integrated along with the orbit propagation (author Shiva Iyer).
478 * @param provider provider for additional derivatives
479 * @since 11.1
480 */
481 public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {
482 additionalDerivativesProviders.add(provider);
483 }
484
485 /** Get the list of additional equations.
486 * @return the list of additional equations
487 * @since 11.1
488 */
489 protected List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
490 return additionalDerivativesProviders;
491 }
492
493 /** Deselects orbital and propagation drivers. */
494 public void deselectDynamicParameters() {
495 for (ParameterDriver driver : getPropagationParametersDrivers().getDrivers()) {
496 driver.setSelected(false);
497 }
498 for (ParameterDriver driver : getOrbitalParametersDrivers().getDrivers()) {
499 driver.setSelected(false);
500 }
501 }
502
503 }