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.osc2mean;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.Field;
25  import org.hipparchus.util.MathArrays;
26  import org.orekit.attitudes.Attitude;
27  import org.orekit.attitudes.AttitudeProvider;
28  import org.orekit.attitudes.FieldAttitude;
29  import org.orekit.attitudes.FrameAlignedProvider;
30  import org.orekit.orbits.FieldOrbit;
31  import org.orekit.orbits.Orbit;
32  import org.orekit.orbits.OrbitType;
33  import org.orekit.orbits.PositionAngleType;
34  import org.orekit.propagation.FieldSpacecraftState;
35  import org.orekit.propagation.PropagationType;
36  import org.orekit.propagation.Propagator;
37  import org.orekit.propagation.SpacecraftState;
38  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
39  import org.orekit.propagation.semianalytical.dsst.forces.FieldShortPeriodTerms;
40  import org.orekit.propagation.semianalytical.dsst.forces.ShortPeriodTerms;
41  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
42  import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
43  import org.orekit.time.FieldAbsoluteDate;
44  import org.orekit.utils.Constants;
45  
46  /**
47   * DSST theory for osculating to mean orbit conversion.
48   *
49   * @author Pascal Parraud
50   * @since 13.0
51   */
52  public class DSSTTheory implements MeanTheory {
53  
54      /** Theory used for converting from osculating to mean orbit. */
55      public static final String THEORY = "DSST";
56  
57      /** Force models used to compute short periodic terms. */
58      private final Collection<DSSTForceModel> forceModels;
59  
60      /** Spacecraft mass (kg). */
61      private final double mass;
62  
63      /** Attitude provider. */
64      private AttitudeProvider attitudeProvider;
65  
66      /**
67       * Constructor with default values.
68       * @param forceModels the force models
69       */
70      public DSSTTheory(final Collection<DSSTForceModel> forceModels) {
71          this(forceModels, null, Propagator.DEFAULT_MASS);
72      }
73  
74      /**
75       * Full constructor.
76       * @param forceModels the force models
77       * @param attitudeProvider the attitude law
78       * @param mass the mass (kg)
79       */
80      public DSSTTheory(final Collection<DSSTForceModel> forceModels,
81                        final AttitudeProvider attitudeProvider,
82                        final double mass) {
83          this.forceModels = forceModels;
84          this.attitudeProvider = attitudeProvider;
85          this.mass = mass;
86      }
87  
88      /** {@inheritDoc} */
89      @Override
90      public String getTheoryName() {
91          return THEORY;
92      }
93  
94      /** {@inheritDoc} */
95      @Override
96      public double getReferenceRadius() {
97          return Constants.IERS2010_EARTH_EQUATORIAL_RADIUS;
98      };
99  
100     /** {@inheritDoc} */
101     @Override
102     public Orbit preprocessing(final Orbit osculating) {
103         // If not defined
104         if (attitudeProvider == null) {
105             // Creates a default attitude provider
106             attitudeProvider = FrameAlignedProvider.of(osculating.getFrame());
107         }
108         // ensure all Gaussian force models can rely on attitude
109         for (final DSSTForceModel force : forceModels) {
110             force.registerAttitudeProvider(attitudeProvider);
111         }
112         // Returns an equinoctial orbit
113         return OrbitType.EQUINOCTIAL.convertType(osculating);
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public Orbit meanToOsculating(final Orbit mean) {
119 
120         // Create the spacecraft state
121         final Attitude attitude = attitudeProvider.getAttitude(mean, mean.getDate(), mean.getFrame());
122         final SpacecraftState meanState = new SpacecraftState(mean, attitude).withMass(mass);
123 
124         // Create the auxiliary object
125         final AuxiliaryElements aux = new AuxiliaryElements(mean, +1);
126 
127         // Set the force models
128         final List<ShortPeriodTerms> shortPeriodTerms = new ArrayList<>();
129         for (final DSSTForceModel force : forceModels) {
130             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING,
131                                                                      force.getParameters(mean.getDate())));
132             force.updateShortPeriodTerms(force.getParametersAllValues(), meanState);
133         }
134 
135         // recompute the osculating parameters from the current mean parameters
136         return computeOsculatingOrbit(mean, shortPeriodTerms);
137     }
138 
139     /** Compute osculating orbit from mean orbit.
140      * <p>
141      * Compute and add the short periodic variation to the mean orbit.
142      * </p>
143      * @param meanOrbit initial mean orbit
144      * @param shortPeriodTerms short period terms
145      * @return osculating orbit
146      */
147     private Orbit computeOsculatingOrbit(final Orbit meanOrbit,
148                                          final List<ShortPeriodTerms> shortPeriodTerms) {
149 
150         final double[] mean = new double[6];
151         final double[] meanDot = new double[6];
152         OrbitType.EQUINOCTIAL.mapOrbitToArray(meanOrbit, PositionAngleType.MEAN, mean, meanDot);
153         final double[] y = mean.clone();
154         for (final ShortPeriodTerms spt : shortPeriodTerms) {
155             final double[] shortPeriodic = spt.value(meanOrbit);
156             for (int i = 0; i < shortPeriodic.length; i++) {
157                 y[i] += shortPeriodic[i];
158             }
159         }
160         return OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot, PositionAngleType.MEAN,
161                                                      meanOrbit.getDate(), meanOrbit.getMu(),
162                                                      meanOrbit.getFrame());
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public <T extends CalculusFieldElement<T>> FieldOrbit<T> preprocessing(final FieldOrbit<T> osculating) {
168         // If not defined
169         if (attitudeProvider == null) {
170             // Creates a default attitude provider
171             attitudeProvider = FrameAlignedProvider.of(osculating.getFrame());
172         }
173         // ensure all Gaussian force models can rely on attitude
174         for (final DSSTForceModel force : forceModels) {
175             force.registerAttitudeProvider(attitudeProvider);
176         }
177         // Returns an equinoctial orbit
178         return OrbitType.EQUINOCTIAL.convertType(osculating);
179     }
180 
181     /** {@inheritDoc} */
182     @Override
183     @SuppressWarnings("unchecked")
184     public <T extends CalculusFieldElement<T>> FieldOrbit<T> meanToOsculating(final FieldOrbit<T> mean) {
185 
186         final FieldAbsoluteDate<T> date = mean.getDate();
187         final Field<T> field = date.getField();
188 
189         // Create the spacecraft state
190         final T fieldMass = field.getZero().newInstance(mass);
191         final FieldAttitude<T> attitude = attitudeProvider.getAttitude(mean, mean.getDate(), mean.getFrame());
192         final FieldSpacecraftState<T> meanState = new FieldSpacecraftState<>(mean, attitude).withMass(fieldMass);
193 
194         //Create the auxiliary object
195         final FieldAuxiliaryElements<T> aux = new FieldAuxiliaryElements<>(mean, +1);
196 
197         // Set the force models
198         final List<FieldShortPeriodTerms<T>> shortPeriodTerms = new ArrayList<>();
199         for (final DSSTForceModel force : forceModels) {
200             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING,
201                                                                      force.getParameters(field, date)));
202             force.updateShortPeriodTerms(force.getParametersAllValues(field), meanState);
203         }
204 
205         // recompute the osculating parameters from the current mean parameters
206         return computeOsculatingOrbit(mean, shortPeriodTerms);
207     }
208 
209     /** Compute osculating orbit from mean orbit.
210      * <p>
211      * Compute and add the short periodic variation to the mean {@link SpacecraftState}.
212      * </p>
213      * @param meanOrbit initial mean orbit
214      * @param shortPeriodTerms short period terms
215      * @param <T> type of the elements
216      * @return osculating orbit
217      */
218     private <T extends CalculusFieldElement<T>> FieldOrbit<T> computeOsculatingOrbit(final FieldOrbit<T> meanOrbit,
219                                                                                      final List<FieldShortPeriodTerms<T>> shortPeriodTerms) {
220 
221         final T[] mean = MathArrays.buildArray(meanOrbit.getDate().getField(), 6);
222         final T[] meanDot = MathArrays.buildArray(meanOrbit.getDate().getField(), 6);
223         OrbitType.EQUINOCTIAL.mapOrbitToArray(meanOrbit, PositionAngleType.MEAN, mean, meanDot);
224         final T[] y = mean.clone();
225         for (final FieldShortPeriodTerms<T> spt : shortPeriodTerms) {
226             final T[] shortPeriodic = spt.value(meanOrbit);
227             for (int i = 0; i < shortPeriodic.length; i++) {
228                 y[i] = y[i].add(shortPeriodic[i]);
229             }
230         }
231         return OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot,
232                                                      PositionAngleType.MEAN,
233                                                      meanOrbit.getDate(),
234                                                      meanOrbit.getMu(),
235                                                      meanOrbit.getFrame());
236     }
237 }