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 org.orekit.attitudes.Attitude;
20  import org.orekit.attitudes.AttitudeProvider;
21  import org.orekit.attitudes.FrameAlignedProvider;
22  import org.orekit.estimation.leastsquares.DSSTBatchLSModel;
23  import org.orekit.estimation.leastsquares.ModelObserver;
24  import org.orekit.estimation.measurements.ObservedMeasurement;
25  import org.orekit.orbits.EquinoctialOrbit;
26  import org.orekit.orbits.Orbit;
27  import org.orekit.orbits.OrbitType;
28  import org.orekit.orbits.PositionAngleType;
29  import org.orekit.propagation.PropagationType;
30  import org.orekit.propagation.Propagator;
31  import org.orekit.propagation.SpacecraftState;
32  import org.orekit.propagation.integration.AdditionalDerivativesProvider;
33  import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
34  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
35  import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction;
36  import org.orekit.utils.ParameterDriver;
37  import org.orekit.utils.ParameterDriversList;
38  
39  import java.util.ArrayList;
40  import java.util.Collections;
41  import java.util.List;
42  
43  /** Builder for DSST propagator.
44   * @author Bryan Cazabonne
45   * @since 10.0
46   */
47  public class DSSTPropagatorBuilder extends AbstractIntegratedPropagatorBuilder<DSSTPropagator> {
48  
49      /** Force models used during the extrapolation of the orbit. */
50      private final List<DSSTForceModel> forceModels;
51  
52      /** Type of the elements used to define the orbital state.*/
53      private PropagationType stateType;
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 positionScale scaling factor used for orbital parameters normalization
69       * (typically set to the expected standard deviation of the position)
70       * @param propagationType type of the orbit used for the propagation (mean or osculating)
71       * @param stateType type of the elements used to define the orbital state (mean or osculating)
72       * @see #DSSTPropagatorBuilder(Orbit, ODEIntegratorBuilder, double, PropagationType,
73       * PropagationType, AttitudeProvider)
74       */
75      public DSSTPropagatorBuilder(final Orbit referenceOrbit,
76                                   final ODEIntegratorBuilder builder,
77                                   final double positionScale,
78                                   final PropagationType propagationType,
79                                   final PropagationType stateType) {
80          this(referenceOrbit, builder, positionScale, propagationType, stateType,
81               FrameAlignedProvider.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 positionScale scaling factor used for orbital parameters normalization
96       * (typically set to the expected standard deviation of the position)
97       * @param propagationType type of the orbit used for the propagation (mean or osculating)
98       * @param stateType type of the elements used to define the orbital state (mean or osculating)
99       * @param attitudeProvider attitude law.
100      * @since 10.1
101      */
102     public DSSTPropagatorBuilder(final Orbit referenceOrbit,
103                                  final ODEIntegratorBuilder builder,
104                                  final double positionScale,
105                                  final PropagationType propagationType,
106                                  final PropagationType stateType,
107                                  final AttitudeProvider attitudeProvider) {
108         super(referenceOrbit, builder, PositionAngleType.MEAN, positionScale, propagationType, attitudeProvider, Propagator.DEFAULT_MASS);
109         this.forceModels       = new ArrayList<>();
110         this.stateType         = stateType;
111     }
112 
113     /** Copy constructor.
114      * @param builder builder to copy from
115      */
116     private DSSTPropagatorBuilder(final DSSTPropagatorBuilder builder) {
117         this(builder.createInitialOrbit(), builder.getIntegratorBuilder(),
118              builder.getPositionScale(), builder.getPropagationType(),
119              builder.getStateType(), builder.getAttitudeProvider());
120     }
121 
122     /** {@inheritDoc}. */
123     @Override
124     public DSSTPropagatorBuilder clone() {
125         // Call to super clone() method to avoid warning
126         final DSSTPropagatorBuilder clonedBuilder = (DSSTPropagatorBuilder) super.clone();
127 
128         // Use copy constructor to unlink orbital drivers
129         final DSSTPropagatorBuilder copyBuilder = new DSSTPropagatorBuilder(clonedBuilder);
130 
131         // Update mass and force models
132         copyBuilder.setMass(getMass());
133         for (DSSTForceModel model : forceModels) {
134             copyBuilder.addForceModel(model);
135         }
136         return copyBuilder;
137 
138     }
139 
140     /** Get the type of the elements used to define the orbital state (mean or osculating).
141      * @return the type of the elements used to define the orbital state
142      */
143     public PropagationType getStateType() {
144         return stateType;
145     }
146 
147     /** Get the list of all force models.
148      * @return the list of all force models
149      */
150     public List<DSSTForceModel> getAllForceModels()
151     {
152         return Collections.unmodifiableList(forceModels);
153     }
154 
155     /** Add a force model to the global perturbation model.
156      * <p>If this method is not called at all, the integrated orbit will follow
157      * a Keplerian evolution only.</p>
158      * @param model perturbing {@link DSSTForceModel} to add
159      */
160     public void addForceModel(final DSSTForceModel model) {
161         if (model instanceof DSSTNewtonianAttraction) {
162             // we want to add the central attraction force model
163             if (hasNewtonianAttraction()) {
164                 // there is already a central attraction model, replace it
165                 forceModels.set(forceModels.size() - 1, model);
166             } else {
167                 // there are no central attraction model yet, add it at the end of the list
168                 forceModels.add(model);
169             }
170         } else {
171             // we want to add a perturbing force model
172             if (hasNewtonianAttraction()) {
173                 // insert the new force model before Newtonian attraction,
174                 // which should always be the last one in the list
175                 forceModels.add(forceModels.size() - 1, model);
176             } else {
177                 // we only have perturbing force models up to now, just append at the end of the list
178                 forceModels.add(model);
179             }
180         }
181 
182         addSupportedParameters(model.getParametersDrivers());
183     }
184 
185     /** Reset the orbit in the propagator builder.
186      * @param newOrbit newOrbit New orbit to set in the propagator builder
187      * @param orbitType orbit type (MEAN or OSCULATING)
188      */
189     public void resetOrbit(final Orbit newOrbit, final PropagationType orbitType) {
190         this.stateType = orbitType;
191         super.resetOrbit(newOrbit);
192     }
193 
194     /** {@inheritDoc} */
195     public DSSTPropagator buildPropagator(final double[] normalizedParameters) {
196 
197         setParameters(normalizedParameters);
198         final EquinoctialOrbit orbit    = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(createInitialOrbit());
199         final Attitude         attitude = getAttitudeProvider().getAttitude(orbit, orbit.getDate(), getFrame());
200         final SpacecraftState  state    = new SpacecraftState(orbit, attitude).withMass(getMass());
201 
202         final DSSTPropagator propagator = new DSSTPropagator(
203                 getIntegratorBuilder().buildIntegrator(orbit, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN),
204                 getPropagationType(), getAttitudeProvider());
205 
206         // Configure force models
207         if (!hasNewtonianAttraction()) {
208             // There are no central attraction model yet, add it at the end of the list
209             addForceModel(new DSSTNewtonianAttraction(orbit.getMu()));
210         }
211         for (DSSTForceModel model : forceModels) {
212             propagator.addForceModel(model);
213         }
214 
215         propagator.setInitialState(state, stateType);
216 
217         // Add additional derivatives providers to the propagator
218         for (AdditionalDerivativesProvider provider: getAdditionalDerivativesProviders()) {
219             propagator.addAdditionalDerivativesProvider(provider);
220         }
221 
222         return propagator;
223 
224     }
225 
226     /** {@inheritDoc} */
227     @Override
228     public DSSTBatchLSModel buildLeastSquaresModel(final PropagatorBuilder[] builders,
229                                                    final List<ObservedMeasurement<?>> measurements,
230                                                    final ParameterDriversList estimatedMeasurementsParameters,
231                                                    final ModelObserver observer) {
232         return new DSSTBatchLSModel(builders,
233                                     measurements,
234                                     estimatedMeasurementsParameters,
235                                     observer,
236                                     getPropagationType());
237     }
238 
239     /** Check if Newtonian attraction force model is available.
240      * <p>
241      * Newtonian attraction is always the last force model in the list.
242      * </p>
243      * @return true if Newtonian attraction force model is available
244      */
245     private boolean hasNewtonianAttraction() {
246         final int last = forceModels.size() - 1;
247         return last >= 0 && forceModels.get(last) instanceof DSSTNewtonianAttraction;
248     }
249 
250 }