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