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