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