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.integration;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  
22  import org.hipparchus.ode.DenseOutputModel;
23  import org.hipparchus.ode.ODEStateAndDerivative;
24  import org.orekit.attitudes.AttitudeProvider;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.frames.Frame;
28  import org.orekit.orbits.Orbit;
29  import org.orekit.propagation.AdditionalStateProvider;
30  import org.orekit.propagation.BoundedPropagator;
31  import org.orekit.propagation.PropagationType;
32  import org.orekit.propagation.SpacecraftState;
33  import org.orekit.propagation.analytical.AbstractAnalyticalPropagator;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.utils.DoubleArrayDictionary;
36  
37  /** This class stores sequentially generated orbital parameters for
38   * later retrieval.
39   *
40   * <p>
41   * Instances of this class are built automatically when the {@link
42   * org.orekit.propagation.Propagator#getEphemerisGenerator()
43   * getEphemerisGenerator} method has been called. They are created when propagation is over.
44   * Random access to any intermediate state of the orbit throughout the propagation range is
45   * possible afterwards through this object.
46   * </p>
47   * <p>
48   * A typical use case is for numerically integrated orbits, which can be used by
49   * algorithms that need to wander around according to their own algorithm without
50   * cumbersome tight links with the integrator.
51   * </p>
52   * <p>
53   * As this class implements the {@link org.orekit.propagation.Propagator Propagator}
54   * interface, it can itself be used in batch mode to build another instance of the
55   * same type. This is however not recommended since it would be a waste of resources.
56   * </p>
57   * <p>
58   * Note that this class stores all intermediate states along with interpolation
59   * models, so it may be memory intensive.
60   * </p>
61   *
62   * @see org.orekit.propagation.numerical.NumericalPropagator
63   * @author Mathieu Rom&eacute;ro
64   * @author Luc Maisonobe
65   * @author V&eacute;ronique Pommier-Maurussane
66   */
67  public class IntegratedEphemeris
68      extends AbstractAnalyticalPropagator implements BoundedPropagator {
69  
70      /** Event detection requires evaluating the state slightly before / past an event. */
71      private static final double EXTRAPOLATION_TOLERANCE = 1.0;
72  
73      /** Mapper between raw double components and spacecraft state. */
74      private final StateMapper mapper;
75  
76      /** Type of orbit to output (mean or osculating).
77       * <p>
78       * This is used only in the case of semianalitical propagators where there is a clear separation between
79       * mean and short periodic elements. It is ignored by the Numerical propagator.
80       * </p>
81       */
82      private PropagationType type;
83  
84      /** Start date of the integration (can be min or max). */
85      private final AbsoluteDate startDate;
86  
87      /** First date of the range. */
88      private final AbsoluteDate minDate;
89  
90      /** Last date of the range. */
91      private final AbsoluteDate maxDate;
92  
93      /** Underlying raw mathematical model. */
94      private DenseOutputModel model;
95  
96      /** Unmanaged additional states that must be simply copied. */
97      private final DoubleArrayDictionary unmanaged;
98  
99      /** Names of additional equations.
100      * @since 11.2
101      */
102     private final String[] equations;
103 
104     /** Dimensions of additional equations.
105      * @since 11.2
106      */
107     private final int[] dimensions;
108 
109     /** Creates a new instance of IntegratedEphemeris.
110      * @param startDate Start date of the integration (can be minDate or maxDate)
111      * @param minDate first date of the range
112      * @param maxDate last date of the range
113      * @param mapper mapper between raw double components and spacecraft state
114      * @param type type of orbit to output (mean or osculating)
115      * @param model underlying raw mathematical model
116      * @param unmanaged unmanaged additional states that must be simply copied
117      * @param providers providers for pre-integrated states
118      * @param equations names of additional equations
119      * @param dimensions dimensions of additional equations
120      * @since 11.1.2
121      */
122     public IntegratedEphemeris(final AbsoluteDate startDate,
123                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
124                                final StateMapper mapper, final PropagationType type,
125                                final DenseOutputModel model,
126                                final DoubleArrayDictionary unmanaged,
127                                final List<AdditionalStateProvider> providers,
128                                final String[] equations, final int[] dimensions) {
129 
130         super(mapper.getAttitudeProvider());
131 
132         this.startDate = startDate;
133         this.minDate   = minDate;
134         this.maxDate   = maxDate;
135         this.mapper    = mapper;
136         this.type      = type;
137         this.model     = model;
138         this.unmanaged = unmanaged;
139 
140         // set up the pre-integrated providers
141         for (final AdditionalStateProvider provider : providers) {
142             addAdditionalStateProvider(provider);
143         }
144 
145         this.equations  = equations.clone();
146         this.dimensions = dimensions.clone();
147 
148         // set up initial state
149         super.resetInitialState(getInitialState());
150 
151     }
152 
153     /** Interpolate the model at some date.
154      * @param date desired interpolation date
155      * @return state interpolated at date
156      */
157     private ODEStateAndDerivative getInterpolatedState(final AbsoluteDate date) {
158 
159         // compare using double precision instead of AbsoluteDate.compareTo(...)
160         // because time is expressed as a double when searching for events
161         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
162             // date is outside of supported range
163             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
164                     date, minDate, maxDate, minDate.durationFrom(date));
165         }
166         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
167             // date is outside of supported range
168             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
169                     date, minDate, maxDate, date.durationFrom(maxDate));
170         }
171 
172         return model.getInterpolatedState(date.durationFrom(startDate));
173 
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     protected SpacecraftState basicPropagate(final AbsoluteDate date) {
179         final ODEStateAndDerivative os = getInterpolatedState(date);
180         SpacecraftState state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
181                                                        os.getPrimaryState(), os.getPrimaryDerivative(),
182                                                        type);
183         for (DoubleArrayDictionary.Entry initial : unmanaged.getData()) {
184             state = state.addAdditionalState(initial.getKey(), initial.getValue());
185         }
186         return state;
187     }
188 
189     /** {@inheritDoc} */
190     protected Orbit propagateOrbit(final AbsoluteDate date) {
191         return basicPropagate(date).getOrbit();
192     }
193 
194     /** {@inheritDoc} */
195     protected double getMass(final AbsoluteDate date) {
196         return basicPropagate(date).getMass();
197     }
198 
199     /** Get the first date of the range.
200      * @return the first date of the range
201      */
202     public AbsoluteDate getMinDate() {
203         return minDate;
204     }
205 
206     /** Get the last date of the range.
207      * @return the last date of the range
208      */
209     public AbsoluteDate getMaxDate() {
210         return maxDate;
211     }
212 
213     @Override
214     public Frame getFrame() {
215         return this.mapper.getFrame();
216     }
217 
218     /** {@inheritDoc} */
219     public void resetInitialState(final SpacecraftState state) {
220         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
221     }
222 
223     /** {@inheritDoc} */
224     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
225         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
231         super.setAttitudeProvider(attitudeProvider);
232         if (mapper != null) {
233             // At the construction, the mapper is not set yet
234             // However, if the attitude provider is changed afterwards, it must be changed in the mapper too
235             mapper.setAttitudeProvider(attitudeProvider);
236         }
237     }
238 
239     /** {@inheritDoc} */
240     public SpacecraftState getInitialState() {
241         return updateAdditionalStates(basicPropagate(getMinDate()));
242     }
243 
244     /** {@inheritDoc} */
245     @Override
246     protected SpacecraftState updateAdditionalStates(final SpacecraftState original) {
247 
248         SpacecraftState updated = super.updateAdditionalStates(original);
249 
250         if (equations.length > 0) {
251             final ODEStateAndDerivative osd                = getInterpolatedState(updated.getDate());
252             final double[]              combinedState      = osd.getSecondaryState(1);
253             final double[]              combinedDerivative = osd.getSecondaryDerivative(1);
254             int index = 0;
255             for (int i = 0; i < equations.length; ++i) {
256                 final double[] state      = Arrays.copyOfRange(combinedState,      index, index + dimensions[i]);
257                 final double[] derivative = Arrays.copyOfRange(combinedDerivative, index, index + dimensions[i]);
258                 updated = updated.
259                           addAdditionalState(equations[i], state).
260                           addAdditionalStateDerivative(equations[i], derivative);
261                 index += dimensions[i];
262             }
263         }
264 
265         return updated;
266 
267     }
268 
269 }