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.analytical;
18  
19  
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.util.MathArrays;
25  import org.orekit.attitudes.AttitudeProvider;
26  import org.orekit.attitudes.FieldAttitude;
27  import org.orekit.attitudes.FrameAlignedProvider;
28  import org.orekit.orbits.FieldOrbit;
29  import org.orekit.orbits.Orbit;
30  import org.orekit.orbits.OrbitType;
31  import org.orekit.orbits.PositionAngleType;
32  import org.orekit.propagation.FieldSpacecraftState;
33  import org.orekit.time.FieldAbsoluteDate;
34  import org.orekit.utils.FieldArrayDictionary;
35  import org.orekit.utils.FieldDataDictionary;
36  import org.orekit.utils.FieldTimeSpanMap;
37  import org.orekit.utils.ParameterDriver;
38  
39  /** Simple Keplerian orbit propagator.
40   * @see FieldOrbit
41   * @author Guylaine Prat
42   * @param <T> type of the field elements
43   */
44  public class FieldKeplerianPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractAnalyticalPropagator<T> {
45  
46  
47      /** All states. */
48      private transient FieldTimeSpanMap<FieldSpacecraftState<T>, T> states;
49  
50      /** Build a propagator from orbit only.
51       * <p>The central attraction coefficient μ is set to the same value used
52       * for the initial orbit definition. Mass and attitude provider are set to
53       * unspecified non-null arbitrary values.</p>
54       *
55       * @param initialFieldOrbit initial orbit
56       * @see #FieldKeplerianPropagator(FieldOrbit, AttitudeProvider)
57       */
58      public FieldKeplerianPropagator(final FieldOrbit<T> initialFieldOrbit) {
59          this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()),
60               initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS));
61      }
62  
63      /** Build a propagator from orbit and central attraction coefficient μ.
64       * <p>Mass and attitude provider are set to unspecified non-null arbitrary values.</p>
65       *
66       * @param initialFieldOrbit initial orbit
67       * @param mu central attraction coefficient (m³/s²)
68       * @see #FieldKeplerianPropagator(FieldOrbit, AttitudeProvider, CalculusFieldElement)
69       */
70      public FieldKeplerianPropagator(final FieldOrbit<T> initialFieldOrbit, final T mu) {
71          this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()),
72               mu, initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS));
73      }
74  
75      /** Build a propagator from orbit and attitude provider.
76       * <p>The central attraction coefficient μ is set to the same value
77       * used for the initial orbit definition. Mass is set to an unspecified
78       * non-null arbitrary value.</p>
79       * @param initialFieldOrbit initial orbit
80       * @param attitudeProv  attitude provider
81       */
82      public FieldKeplerianPropagator(final FieldOrbit<T> initialFieldOrbit,
83                                      final AttitudeProvider attitudeProv) {
84          this(initialFieldOrbit, attitudeProv, initialFieldOrbit.getMu(),
85                  initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS));
86      }
87  
88      /** Build a propagator from orbit, attitude provider and central attraction
89       * coefficient μ.
90       * <p>Mass is set to an unspecified non-null arbitrary value.</p>
91       * @param initialFieldOrbit initial orbit
92       * @param attitudeProv attitude provider
93       * @param mu central attraction coefficient (m³/s²)
94       */
95      public FieldKeplerianPropagator(final FieldOrbit<T> initialFieldOrbit,
96                                      final AttitudeProvider attitudeProv,
97                                      final T mu) {
98          this(initialFieldOrbit, attitudeProv, mu, initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS));
99      }
100 
101     /** Build propagator from orbit, attitude provider, central attraction
102      * coefficient μ and mass.
103      * @param initialOrbit initial orbit
104      * @param attitudeProv attitude provider
105      * @param mu central attraction coefficient (m³/s²)
106      * @param mass spacecraft mass (kg)
107      */
108     public FieldKeplerianPropagator(final FieldOrbit<T> initialOrbit, final AttitudeProvider attitudeProv,
109                                     final T mu, final T mass) {
110 
111         super(initialOrbit.getA().getField(), attitudeProv);
112 
113         // ensure the orbit use the specified mu and has no non-Keplerian derivatives
114         final FieldSpacecraftState<T> initial = fixState(initialOrbit,
115                                                          getAttitudeProvider().getAttitude(initialOrbit,
116                                                                                            initialOrbit.getDate(),
117                                                                                            initialOrbit.getFrame()),
118                                                          mass, mu, null, null);
119         states = new FieldTimeSpanMap<>(initial, initialOrbit.getA().getField());
120         super.resetInitialState(initial);
121     }
122 
123     /** Fix state to use a specified mu and remove derivatives.
124      * <p>
125      * This ensures the propagation model (which is based on calling
126      * {@link Orbit#shiftedBy(double)}) is Keplerian only and uses a specified mu.
127      * </p>
128      * @param orbit orbit to fix
129      * @param attitude current attitude
130      * @param mass current mass
131      * @param mu gravity coefficient to use
132      * @param additionalStates additional states (may be null)
133      * @param additionalStatesderivatives additional states derivatives (may be null)
134      * @return fixed orbit
135      */
136     private FieldSpacecraftState<T> fixState(final FieldOrbit<T> orbit, final FieldAttitude<T> attitude, final T mass, final T mu,
137                                              final FieldDataDictionary<T> additionalStates,
138                                              final FieldArrayDictionary<T> additionalStatesderivatives) {
139         final OrbitType type = orbit.getType();
140         final T[] stateVector = MathArrays.buildArray(mass.getField(), 6);
141         final PositionAngleType positionAngleType = PositionAngleType.MEAN;
142         type.mapOrbitToArray(orbit, positionAngleType, stateVector, null);
143         final FieldOrbit<T> fixedOrbit = type.mapArrayToOrbit(stateVector, null, positionAngleType,
144                                                               orbit.getDate(), mu, orbit.getFrame());
145         FieldSpacecraftState<T> fixedState = new FieldSpacecraftState<>(fixedOrbit, attitude).withMass(mass);
146         if (additionalStates != null) {
147             for (final FieldDataDictionary<T>.Entry entry : additionalStates.getData()) {
148                 fixedState = fixedState.addAdditionalData(entry.getKey(), entry.getValue());
149             }
150         }
151         if (additionalStatesderivatives != null) {
152             for (final FieldArrayDictionary<T>.Entry entry : additionalStatesderivatives.getData()) {
153                 fixedState = fixedState.addAdditionalStateDerivative(entry.getKey(), entry.getValue());
154             }
155         }
156         return fixedState;
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     public void resetInitialState(final FieldSpacecraftState<T> state) {
162 
163         // ensure the orbit use the specified mu and has no non-Keplerian derivatives
164         final FieldSpacecraftState<T> formerInitial = getInitialState();
165         final T mu = formerInitial == null ? state.getOrbit().getMu() : formerInitial.getOrbit().getMu();
166         final FieldSpacecraftState<T> fixedState = fixState(state.getOrbit(),
167                                                             state.getAttitude(),
168                                                             state.getMass(),
169                                                             mu,
170                                                             state.getAdditionalDataValues(),
171                                                             state.getAdditionalStatesDerivatives());
172 
173         states = new FieldTimeSpanMap<>(fixedState, state.getDate().getField());
174         super.resetInitialState(fixedState);
175 
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
181         if (forward) {
182             states.addValidAfter(state, state.getDate());
183         } else {
184             states.addValidBefore(state, state.getDate());
185         }
186         stateChanged(state);
187     }
188 
189     /** {@inheritDoc} */
190     @Override
191     public FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date,
192                                         final T[] parameters) {
193         // propagate orbit
194         FieldOrbit<T> orbit = states.get(date).getOrbit();
195         do {
196             // we use a loop here to compensate for very small date shifts error
197             // that occur with long propagation time
198             orbit = orbit.shiftedBy(date.durationFrom(orbit.getDate()));
199         } while (!date.equals(orbit.getDate()));
200         return orbit;
201     }
202 
203     /** {@inheritDoc}*/
204     @Override
205     protected T getMass(final FieldAbsoluteDate<T> date) {
206         return states.get(date).getMass();
207     }
208 
209     /** {@inheritDoc} */
210     @Override
211     public List<ParameterDriver> getParametersDrivers() {
212         // Keplerian propagation model does not have parameter drivers.
213         return Collections.emptyList();
214     }
215 
216 }