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 }