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 /**
110 * Add impulse maneuver.
111 * @param impulseManeuver impulse maneuver
112 * @since 12.2
113 */
114 public void addImpulseManeuver(final ImpulseManeuver impulseManeuver) {
115 impulseManeuvers.add(impulseManeuver);
116 }
117
118 /**
119 * Remove all impulse maneuvers.
120 * @since 12.2
121 */
122 public void clearImpulseManeuvers() {
123 impulseManeuvers.clear();
124 }
125
126 /** Get the list of all force models.
127 * @return the list of all force models
128 * @since 9.2
129 */
130 public List<ForceModel> getAllForceModels()
131 {
132 return Collections.unmodifiableList(forceModels);
133 }
134
135 /** Add a force model to the global perturbation model.
136 * <p>If this method is not called at all, the integrated orbit will follow
137 * a Keplerian evolution only.</p>
138 * @param model perturbing {@link ForceModel} to add
139 */
140 public void addForceModel(final ForceModel model) {
141 if (model instanceof NewtonianAttraction) {
142 // we want to add the central attraction force model
143 if (hasNewtonianAttraction()) {
144 // there is already a central attraction model, replace it
145 forceModels.set(forceModels.size() - 1, model);
146 } else {
147 // there are no central attraction model yet, add it at the end of the list
148 forceModels.add(model);
149 }
150 } else {
151 // we want to add a perturbing force model
152 if (hasNewtonianAttraction()) {
153 // insert the new force model before Newtonian attraction,
154 // which should always be the last one in the list
155 forceModels.add(forceModels.size() - 1, model);
156 } else {
157 // we only have perturbing force models up to now, just append at the end of the list
158 forceModels.add(model);
159 }
160 }
161
162 addSupportedParameters(model.getParametersDrivers());
163 }
164
165 /** {@inheritDoc} */
166 public NumericalPropagator buildPropagator(final double[] normalizedParameters) {
167
168 setParameters(normalizedParameters);
169 final Orbit orbit = createInitialOrbit();
170 final Attitude attitude =
171 getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame());
172 final SpacecraftState state = new SpacecraftState(orbit, attitude).withMass(getMass());
173
174 final NumericalPropagator propagator = new NumericalPropagator(
175 getIntegratorBuilder().buildIntegrator(orbit, getOrbitType(), getPositionAngleType()),
176 getAttitudeProvider());
177 propagator.setOrbitType(getOrbitType());
178 propagator.setPositionAngleType(getPositionAngleType());
179
180 // Configure force models
181 if (!hasNewtonianAttraction()) {
182 // There are no central attraction model yet, add it at the end of the list
183 addForceModel(new NewtonianAttraction(orbit.getMu()));
184 }
185 for (ForceModel model : forceModels) {
186 propagator.addForceModel(model);
187 }
188 impulseManeuvers.forEach(propagator::addEventDetector);
189
190 propagator.resetInitialState(state);
191
192 // Add additional derivatives providers to the propagator
193 for (AdditionalDerivativesProvider provider: getAdditionalDerivativesProviders()) {
194 propagator.addAdditionalDerivativesProvider(provider);
195 }
196
197 return propagator;
198
199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public BatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders,
204 final List<ObservedMeasurement<?>> measurements,
205 final ParameterDriversList estimatedMeasurementsParameters,
206 final ModelObserver observer) {
207 return new BatchLSModel(builders, measurements, estimatedMeasurementsParameters, observer);
208 }
209
210 /** Check if Newtonian attraction force model is available.
211 * <p>
212 * Newtonian attraction is always the last force model in the list.
213 * </p>
214 * @return true if Newtonian attraction force model is available
215 */
216 private boolean hasNewtonianAttraction() {
217 final int last = forceModels.size() - 1;
218 return last >= 0 && forceModels.get(last) instanceof NewtonianAttraction;
219 }
220
221 }