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