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