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.Collections;
21  import java.util.List;
22  
23  import org.orekit.attitudes.Attitude;
24  import org.orekit.attitudes.AttitudeProvider;
25  import org.orekit.attitudes.InertialProvider;
26  import org.orekit.estimation.leastsquares.BatchLSModel;
27  import org.orekit.estimation.leastsquares.ModelObserver;
28  import org.orekit.estimation.measurements.ObservedMeasurement;
29  import org.orekit.estimation.sequential.CovarianceMatrixProvider;
30  import org.orekit.estimation.sequential.KalmanModel;
31  import org.orekit.forces.ForceModel;
32  import org.orekit.forces.gravity.NewtonianAttraction;
33  import org.orekit.orbits.Orbit;
34  import org.orekit.orbits.PositionAngle;
35  import org.orekit.propagation.Propagator;
36  import org.orekit.propagation.SpacecraftState;
37  import org.orekit.propagation.integration.AdditionalDerivativesProvider;
38  import org.orekit.propagation.numerical.NumericalPropagator;
39  import org.orekit.utils.ParameterDriver;
40  import org.orekit.utils.ParameterDriversList;
41  
42  /** Builder for numerical propagator.
43   * @author Pascal Parraud
44   * @since 6.0
45   */
46  public class NumericalPropagatorBuilder extends AbstractPropagatorBuilder implements OrbitDeterminationPropagatorBuilder {
47  
48      /** First order integrator builder for propagation. */
49      private final ODEIntegratorBuilder builder;
50  
51      /** Force models used during the extrapolation of the orbit. */
52      private final List<ForceModel> forceModels;
53  
54      /** Current mass for initial state (kg). */
55      private double mass;
56  
57      /** Build a new instance.
58       * <p>
59       * The reference orbit is used as a model to {@link
60       * #createInitialOrbit() create initial orbit}. It defines the
61       * inertial frame, the central attraction coefficient, and is also used together
62       * with the {@code positionScale} to convert from the {@link
63       * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
64       * callers of this builder to the real orbital parameters.
65       * </p>
66       *
67       * @param referenceOrbit reference orbit from which real orbits will be built
68       * @param builder first order integrator builder
69       * @param positionAngle position angle type to use
70       * @param positionScale scaling factor used for orbital parameters normalization
71       * (typically set to the expected standard deviation of the position)
72       * @since 8.0
73       * @see #NumericalPropagatorBuilder(Orbit, ODEIntegratorBuilder, PositionAngle,
74       * double, AttitudeProvider)
75       */
76      public NumericalPropagatorBuilder(final Orbit referenceOrbit,
77                                        final ODEIntegratorBuilder builder,
78                                        final PositionAngle positionAngle,
79                                        final double positionScale) {
80          this(referenceOrbit, builder, positionAngle, positionScale,
81                  InertialProvider.of(referenceOrbit.getFrame()));
82      }
83  
84      /** Build a new instance.
85       * <p>
86       * The reference orbit is used as a model to {@link
87       * #createInitialOrbit() create initial orbit}. It defines the
88       * inertial frame, the central attraction coefficient, and is also used together
89       * with the {@code positionScale} to convert from the {@link
90       * ParameterDriver#setNormalizedValue(double) normalized} parameters used by the
91       * callers of this builder to the real orbital parameters.
92       * </p>
93       * @param referenceOrbit reference orbit from which real orbits will be built
94       * @param builder first order integrator builder
95       * @param positionAngle position angle type to use
96       * @param positionScale scaling factor used for orbital parameters normalization
97       * (typically set to the expected standard deviation of the position)
98       * @param attitudeProvider attitude law.
99       * @since 10.1
100      */
101     public NumericalPropagatorBuilder(final Orbit referenceOrbit,
102                                       final ODEIntegratorBuilder builder,
103                                       final PositionAngle positionAngle,
104                                       final double positionScale,
105                                       final AttitudeProvider attitudeProvider) {
106         super(referenceOrbit, positionAngle, positionScale, true, attitudeProvider);
107         this.builder     = builder;
108         this.forceModels = new ArrayList<ForceModel>();
109         this.mass        = Propagator.DEFAULT_MASS;
110     }
111 
112     /** Create a copy of a NumericalPropagatorBuilder object.
113      * @return Copied version of the NumericalPropagatorBuilder
114      */
115     public NumericalPropagatorBuilder copy() {
116         final NumericalPropagatorBuilder copyBuilder =
117                         new NumericalPropagatorBuilder(createInitialOrbit(),
118                                                        builder,
119                                                        getPositionAngle(),
120                                                        getPositionScale(),
121                                                        getAttitudeProvider());
122         copyBuilder.setMass(mass);
123         for (ForceModel model : forceModels) {
124             copyBuilder.addForceModel(model);
125         }
126         return copyBuilder;
127     }
128 
129     /** Get the integrator builder.
130      * @return the integrator builder
131      * @since 9.2
132      */
133     public ODEIntegratorBuilder getIntegratorBuilder()
134     {
135         return builder;
136     }
137 
138     /** Get the list of all force models.
139      * @return the list of all force models
140      * @since 9.2
141      */
142     public List<ForceModel> getAllForceModels()
143     {
144         return Collections.unmodifiableList(forceModels);
145     }
146 
147     /** Add a force model to the global perturbation model.
148      * <p>If this method is not called at all, the integrated orbit will follow
149      * a Keplerian evolution only.</p>
150      * @param model perturbing {@link ForceModel} to add
151      */
152     public void addForceModel(final ForceModel model) {
153         if (model instanceof NewtonianAttraction) {
154             // we want to add the central attraction force model
155             if (hasNewtonianAttraction()) {
156                 // there is already a central attraction model, replace it
157                 forceModels.set(forceModels.size() - 1, model);
158             } else {
159                 // there are no central attraction model yet, add it at the end of the list
160                 forceModels.add(model);
161             }
162         } else {
163             // we want to add a perturbing force model
164             if (hasNewtonianAttraction()) {
165                 // insert the new force model before Newtonian attraction,
166                 // which should always be the last one in the list
167                 forceModels.add(forceModels.size() - 1, model);
168             } else {
169                 // we only have perturbing force models up to now, just append at the end of the list
170                 forceModels.add(model);
171             }
172         }
173 
174         for (final ParameterDriver driver : model.getParametersDrivers()) {
175             addSupportedParameter(driver);
176         }
177     }
178 
179     /** Get the mass.
180      * @return the mass
181      * @since 9.2
182      */
183     public double getMass()
184     {
185         return mass;
186     }
187 
188     /** Set the initial mass.
189      * @param mass the mass (kg)
190      */
191     public void setMass(final double mass) {
192         this.mass = mass;
193     }
194 
195     /** {@inheritDoc} */
196     @SuppressWarnings("deprecation")
197     public NumericalPropagator buildPropagator(final double[] normalizedParameters) {
198 
199         setParameters(normalizedParameters);
200         final Orbit           orbit    = createInitialOrbit();
201         final Attitude        attitude =
202                 getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame());
203         final SpacecraftState state    = new SpacecraftState(orbit, attitude, mass);
204 
205         final NumericalPropagator propagator = new NumericalPropagator(
206                 builder.buildIntegrator(orbit, getOrbitType()),
207                 getAttitudeProvider());
208         propagator.setOrbitType(getOrbitType());
209         propagator.setPositionAngleType(getPositionAngle());
210 
211         // Configure force models
212         if (!hasNewtonianAttraction()) {
213             // There are no central attraction model yet, add it at the end of the list
214             addForceModel(new NewtonianAttraction(orbit.getMu()));
215         }
216         for (ForceModel model : forceModels) {
217             propagator.addForceModel(model);
218         }
219 
220         propagator.resetInitialState(state);
221 
222         // Add additional derivatives providers to the propagator
223         for (AdditionalDerivativesProvider provider: getAdditionalDerivativesProviders()) {
224             propagator.addAdditionalDerivativesProvider(provider);
225         }
226 
227         // FIXME: remove in 12.0 when AdditionalEquations is removed
228         for (org.orekit.propagation.integration.AdditionalEquations equations : getAdditionalEquations()) {
229             propagator.addAdditionalDerivativesProvider(new org.orekit.propagation.integration.AdditionalEquationsAdapter(equations, propagator::getInitialState));
230         }
231 
232         return propagator;
233     }
234 
235     /** {@inheritDoc} */
236     public BatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders,
237                             final List<ObservedMeasurement<?>> measurements,
238                             final ParameterDriversList estimatedMeasurementsParameters,
239                             final ModelObserver observer) {
240         return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer);
241     }
242 
243     /** {@inheritDoc} */
244     @Override
245     public KalmanModel buildKalmanModel(final List<OrbitDeterminationPropagatorBuilder> propagatorBuilders,
246                                         final List<CovarianceMatrixProvider> covarianceMatricesProviders,
247                                         final ParameterDriversList estimatedMeasurementsParameters,
248                                         final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
249         return new KalmanModel(propagatorBuilders, covarianceMatricesProviders,
250                                estimatedMeasurementsParameters, measurementProcessNoiseMatrix);
251     }
252 
253     /** Check if Newtonian attraction force model is available.
254      * <p>
255      * Newtonian attraction is always the last force model in the list.
256      * </p>
257      * @return true if Newtonian attraction force model is available
258      */
259     private boolean hasNewtonianAttraction() {
260         final int last = forceModels.size() - 1;
261         return last >= 0 && forceModels.get(last) instanceof NewtonianAttraction;
262     }
263 
264 }