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.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.AdditionalEquations;
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     public NumericalPropagator buildPropagator(final double[] normalizedParameters) {
197 
198         setParameters(normalizedParameters);
199         final Orbit           orbit    = createInitialOrbit();
200         final Attitude        attitude =
201                 getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame());
202         final SpacecraftState state    = new SpacecraftState(orbit, attitude, mass);
203 
204         final NumericalPropagator propagator = new NumericalPropagator(
205                 builder.buildIntegrator(orbit, getOrbitType()),
206                 getAttitudeProvider());
207         propagator.setOrbitType(getOrbitType());
208         propagator.setPositionAngleType(getPositionAngle());
209 
210         // Configure force models
211         if (!hasNewtonianAttraction()) {
212             // There are no central attraction model yet, add it at the end of the list
213             addForceModel(new NewtonianAttraction(orbit.getMu()));
214         }
215         for (ForceModel model : forceModels) {
216             propagator.addForceModel(model);
217         }
218 
219         propagator.resetInitialState(state);
220 
221         // Add additional equations to the propagator
222         for (AdditionalEquations equation: getAdditionalEquations()) {
223             propagator.addAdditionalEquations(equation);
224         }
225 
226         return propagator;
227     }
228 
229     /** {@inheritDoc} */
230     public BatchLSModel buildLSModel(final OrbitDeterminationPropagatorBuilder[] builders,
231                             final List<ObservedMeasurement<?>> measurements,
232                             final ParameterDriversList estimatedMeasurementsParameters,
233                             final ModelObserver observer) {
234         return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer);
235     }
236 
237     /** {@inheritDoc} */
238     @Override
239     public KalmanModel buildKalmanModel(final List<OrbitDeterminationPropagatorBuilder> propagatorBuilders,
240                                         final List<CovarianceMatrixProvider> covarianceMatricesProviders,
241                                         final ParameterDriversList estimatedMeasurementsParameters,
242                                         final CovarianceMatrixProvider measurementProcessNoiseMatrix) {
243         return new KalmanModel(propagatorBuilders, covarianceMatricesProviders,
244                                estimatedMeasurementsParameters, measurementProcessNoiseMatrix);
245     }
246 
247     /** Check if Newtonian attraction force model is available.
248      * <p>
249      * Newtonian attraction is always the last force model in the list.
250      * </p>
251      * @return true if Newtonian attraction force model is available
252      */
253     private boolean hasNewtonianAttraction() {
254         final int last = forceModels.size() - 1;
255         return last >= 0 && forceModels.get(last) instanceof NewtonianAttraction;
256     }
257 
258 }