1   /* Contributed in the public domain.
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 java.util.Collection;
20  import java.util.NavigableMap;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.orekit.attitudes.Attitude;
27  import org.orekit.attitudes.AttitudeProvider;
28  import org.orekit.attitudes.FieldAttitude;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitMessages;
31  import org.orekit.frames.Frame;
32  import org.orekit.orbits.Orbit;
33  import org.orekit.propagation.BoundedPropagator;
34  import org.orekit.propagation.SpacecraftState;
35  import org.orekit.time.AbsoluteDate;
36  import org.orekit.time.FieldAbsoluteDate;
37  import org.orekit.utils.FieldPVCoordinatesProvider;
38  import org.orekit.utils.PVCoordinatesProvider;
39  import org.orekit.utils.TimeSpanMap;
40  import org.orekit.utils.TimeSpanMap.Span;
41  import org.orekit.utils.TimeStampedPVCoordinates;
42  
43  /**
44   * A {@link BoundedPropagator} that covers a larger time span from several constituent
45   * propagators that cover shorter time spans.
46   *
47   * @author Evan Ward
48   * @see #AggregateBoundedPropagator(Collection)
49   * @since 9.0
50   */
51  public class AggregateBoundedPropagator extends AbstractAnalyticalPropagator
52          implements BoundedPropagator {
53  
54      /** Constituent propagators. */
55      private final TimeSpanMap<BoundedPropagator> map;
56  
57      /** Minimum date for {@link #getMinDate()}. */
58      private final AbsoluteDate min;
59  
60      /** Maximum date for {@link #getMaxDate()}. */
61      private final AbsoluteDate max;
62  
63      /**
64       * Create a propagator by concatenating several {@link BoundedPropagator}s.
65       *
66       * @param propagators that provide the backing data for this instance. There must be
67       *                    at least one propagator in the collection. If there are gaps
68       *                    between the {@link BoundedPropagator#getMaxDate()} of one
69       *                    propagator and the {@link BoundedPropagator#getMinDate()} of the
70       *                    next propagator an exception may be thrown by any method of this
71       *                    class at any time. If there are overlaps between the {@link
72       *                    BoundedPropagator#getMaxDate()} of one propagator and the {@link
73       *                    BoundedPropagator#getMinDate()} of the next propagator then the
74       *                    propagator with the latest {@link BoundedPropagator#getMinDate()}
75       *                    is used.
76       */
77      public AggregateBoundedPropagator(final Collection<? extends BoundedPropagator> propagators) {
78          super(null);
79          map = new TimeSpanMap<>(null);
80          propagators.forEach(p -> map.addValidAfter(p, p.getMinDate(), false));
81          setAttitudeProvider();
82          this.min = map.getFirstNonNullSpan().getData().getMinDate();
83          this.max = map.getLastNonNullSpan().getData().getMaxDate();
84          super.resetInitialState(getInitialState());
85      }
86  
87      /**
88       * Create a propagator from several constituent propagators.
89       *
90       * @param propagators that provide the backing data for this instance. Each
91       *                    propagator is used from the date of it's key in the
92       *                    map until the date of the next key. The first
93       *                    propagator is also used before the first key and the
94       *                    last propagator after the last key.
95       * @param min         the value for {@link #getMinDate()}.
96       * @param max         the value for {@link #getMaxDate()}.
97       */
98      public AggregateBoundedPropagator(final NavigableMap<AbsoluteDate, ? extends BoundedPropagator> propagators,
99                                        final AbsoluteDate min, final AbsoluteDate max) {
100         super(null);
101         map = new TimeSpanMap<>(null);
102         propagators.forEach((d, p) -> map.addValidAfter(p, p.getMinDate(), false));
103         setAttitudeProvider();
104         this.min = min;
105         this.max = max;
106         super.resetInitialState(getInitialState());
107     }
108 
109     /** Set the attitude provider.
110      * <p>
111      * Note that in order to be consistent with {@link Ephemeris}, we allow
112      * here to have a {@code null} attitude provider, when <em>all</em> constituent
113      * propagators have {@code null} attitude providers.
114      * </p>
115      */
116     private void setAttitudeProvider() {
117 
118         // inspect all constituent propagators
119         boolean nullFound    = false;
120         boolean nonNullFound = false;
121         for (Span<BoundedPropagator> span = map.getFirstSpan(); span != null; span = span.next()) {
122             if (span.getData() != null) {
123                 final AttitudeProvider attitudeProvider = span.getData().getAttitudeProvider();
124                 nullFound    |=  attitudeProvider == null;
125                 nonNullFound |=  attitudeProvider != null;
126             }
127         }
128 
129         // check consistency
130         if (nullFound ) {
131             if (nonNullFound) {
132                 throw new OrekitException(OrekitMessages.BOTH_NULL_AND_NON_NULL_ATTITUDE_PROVIDERS);
133             } else {
134                 // all attitude providers are null, we set up a null attitude provider too
135                 setAttitudeProvider(null);
136             }
137         } else {
138             if (nonNullFound) {
139                 // all attitude providers are non-null, we set up an aggregate provider
140                 setAttitudeProvider(new AggregateAttitudeProvider());
141             } else {
142                 // nothing found!
143                 throw new OrekitException(OrekitMessages.NOT_ENOUGH_PROPAGATORS);
144             }
145         }
146 
147     }
148 
149     /** Get the propagators map.
150      * @return propagators map
151      * @since 12.1
152      */
153     public TimeSpanMap<BoundedPropagator> getPropagatorsMap() {
154         return map;
155     }
156 
157     @Override
158     public SpacecraftState basicPropagate(final AbsoluteDate date) {
159         // #589 override this method for a performance benefit,
160         // getPropagator(date).propagate(date) is only called once
161 
162         // do propagation
163         final SpacecraftState state = getPropagator(date).propagate(date);
164 
165         if (getAttitudeProvider() == null ||
166             getAttitudeProvider() instanceof AggregateAttitudeProvider) {
167             // we did not override attitude provider
168             // don't waste time recomputed the already known attitude
169             return state;
170         } else {
171             // attitude provider has been overridden
172 
173             // evaluate attitude
174             final Attitude attitude =
175                     getAttitudeProvider().getAttitude(this, date, state.getFrame());
176 
177             // build raw state
178             if (state.isOrbitDefined()) {
179                 return new SpacecraftState(state.getOrbit(), attitude, state.getMass(),
180                                            state.getAdditionalDataValues(), state.getAdditionalStatesDerivatives());
181             } else {
182                 return new SpacecraftState(state.getAbsPVA(), attitude, state.getMass(),
183                                            state.getAdditionalDataValues(), state.getAdditionalStatesDerivatives());
184             }
185         }
186     }
187 
188     @Override
189     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
190         return getPropagator(date).getPVCoordinates(date, frame);
191     }
192 
193     @Override
194     public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
195         return getPropagator(date).propagate(date).getPosition(frame);
196     }
197 
198     @Override
199     public Orbit propagateOrbit(final AbsoluteDate date) {
200         return getPropagator(date).propagate(date).getOrbit();
201     }
202 
203     @Override
204     public AbsoluteDate getMinDate() {
205         return min;
206     }
207 
208     @Override
209     public AbsoluteDate getMaxDate() {
210         return max;
211     }
212 
213     @Override
214     protected double getMass(final AbsoluteDate date) {
215         return getPropagator(date).propagate(date).getMass();
216     }
217 
218     @Override
219     public SpacecraftState getInitialState() {
220         return map.getFirstNonNullSpan().getData().getInitialState();
221     }
222 
223     @Override
224     protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
225         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
226     }
227 
228     @Override
229     public void resetInitialState(final SpacecraftState state) {
230         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
231     }
232 
233     /**
234      * Get the propagator to use for the given date.
235      *
236      * @param date of query
237      * @return propagator to use on date.
238      */
239     private BoundedPropagator getPropagator(final AbsoluteDate date) {
240         final BoundedPropagator propagator = map.get(date);
241         if (propagator != null) {
242             return propagator;
243         } else {
244             // let the first propagator throw the exception
245             return map.getFirstNonNullSpan().getData();
246         }
247     }
248 
249     /** Local attitude provider. */
250     private class AggregateAttitudeProvider implements AttitudeProvider {
251 
252         /** {@inheritDoc} */
253         @Override
254         public Attitude getAttitude(final PVCoordinatesProvider pvProv,
255                                     final AbsoluteDate date,
256                                     final Frame frame) {
257             return getPropagator(date).getAttitudeProvider().getAttitude(pvProv, date, frame);
258         }
259 
260         /** {@inheritDoc} */
261         @Override
262         public <T extends CalculusFieldElement<T>> FieldAttitude<T> getAttitude(final FieldPVCoordinatesProvider<T> pvProv,
263                                                                                 final FieldAbsoluteDate<T> date,
264                                                                                 final Frame frame) {
265             return getPropagator(date.toAbsoluteDate()).getAttitudeProvider().getAttitude(pvProv, date, frame);
266         }
267 
268         /** {@inheritDoc} */
269         @Override
270         public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) {
271             return getPropagator(date).getAttitudeProvider().getAttitudeRotation(pvProv, date, frame);
272         }
273 
274         /** {@inheritDoc} */
275         @Override
276         public <T extends CalculusFieldElement<T>> FieldRotation<T> getAttitudeRotation(final FieldPVCoordinatesProvider<T> pvProv,
277                                                                                         final FieldAbsoluteDate<T> date,
278                                                                                         final Frame frame) {
279             return getPropagator(date.toAbsoluteDate()).getAttitudeProvider().getAttitudeRotation(pvProv, date, frame);
280         }
281     }
282 
283 }