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