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