1   /* Copyright 2016 Applied Defense Solutions (ADS)
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    * ADS 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.files.general;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  
25  import org.orekit.annotation.DefaultDataContext;
26  import org.orekit.bodies.CelestialBody;
27  import org.orekit.data.DataContext;
28  import org.orekit.errors.OrekitIllegalArgumentException;
29  import org.orekit.errors.OrekitMessages;
30  import org.orekit.frames.Frame;
31  import org.orekit.propagation.SpacecraftState;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.time.TimeScale;
34  import org.orekit.utils.CartesianDerivativesFilter;
35  import org.orekit.utils.TimeStampedPVCoordinates;
36  
37  /**
38   * A class for encapsulating Orekit propagators within an {@link EphemerisFile}
39   * complaint object that makes for easy serialization to external ephemeris
40   * formats like OEM.
41   *
42   * @author Hank Grabowski
43   * @since 9.0
44   *
45   */
46  public class OrekitEphemerisFile
47      implements EphemerisFile<TimeStampedPVCoordinates, OrekitEphemerisFile.OrekitEphemerisSegment> {
48  
49      /** Hashmap of satellite ephemeris. **/
50      private final Map<String, OrekitSatelliteEphemeris> satellites;
51  
52      /**
53       * Standard default constructor.
54       */
55      public OrekitEphemerisFile() {
56          this.satellites = new ConcurrentHashMap<>();
57      }
58  
59      /** {@inheritDoc} */
60      @Override
61      public Map<String, OrekitSatelliteEphemeris> getSatellites() {
62          return Collections.unmodifiableMap(satellites);
63      }
64  
65      /**
66       * Adds a new satellite to this object.
67       *
68       * @param id
69       *            ID to use for this satellite
70       * @return the new satellite object
71       */
72      public OrekitSatelliteEphemeris addSatellite(final String id) {
73          final OrekitSatelliteEphemeris newSat = new OrekitSatelliteEphemeris(id);
74          this.satellites.put(id, newSat);
75          return newSat;
76      }
77  
78      /**
79       * Inner class of {@link OrekitEphemerisFile} that defines the
80       * {@link OrekitSatelliteEphemeris} corresponding object for this ephemeris type.
81       */
82      public static class OrekitSatelliteEphemeris
83          implements EphemerisFile.SatelliteEphemeris<TimeStampedPVCoordinates, OrekitEphemerisSegment> {
84  
85          /**
86           * Defines the default interpolation sample size if it is not specified
87           * on a segment.
88           **/
89          public static final int DEFAULT_INTERPOLATION_SIZE = 2;
90  
91          /** ID of the space object encapsulated here. **/
92          private final String id;
93  
94          /** Earliest date of this file. **/
95          private AbsoluteDate startDate;
96  
97          /** Latest date of this file. **/
98          private AbsoluteDate stopDate;
99  
100         /** List of segments in the file. **/
101         private final List<OrekitEphemerisSegment> segments;
102 
103         /**
104          * Standard constructor for building the satellite Ephemeris object.
105          *
106          * @param id
107          *            the ID of the space object for this data
108          */
109         public OrekitSatelliteEphemeris(final String id) {
110             this.id = id;
111             this.segments = new ArrayList<>();
112         }
113 
114         /** {@inheritDoc} */
115         @Override
116         public String getId() {
117             return id;
118         }
119 
120         /** {@inheritDoc} */
121         @Override
122         public double getMu() {
123             if (this.segments.size() == 0) {
124                 return 0;
125             } else {
126                 return this.segments.get(0).getMu();
127             }
128         }
129 
130         /** {@inheritDoc} */
131         @Override
132         public List<OrekitEphemerisSegment> getSegments() {
133             return Collections.unmodifiableList(this.segments);
134         }
135 
136         /** {@inheritDoc} */
137         @Override
138         public AbsoluteDate getStart() {
139             return this.startDate;
140         }
141 
142         /** {@inheritDoc} */
143         @Override
144         public AbsoluteDate getStop() {
145             return this.stopDate;
146         }
147 
148         /**
149          * Injects pre-computed satellite states into this ephemeris file
150          * object, returning the generated {@link OrekitEphemerisSegment} that
151          * has been stored internally. Defaults the celestial body to earth and
152          * the interpolation size to the default.
153          *
154          * <p>This method uses the {@link DataContext#getDefault() default data context}.
155          *
156          * @param states
157          *            a list of {@link SpacecraftState} that will comprise this
158          *            new unit.
159          * @return the generated {@link OrekitEphemerisSegment}
160          * @see #addNewSegment(List, CelestialBody, int, TimeScale)
161          */
162         @DefaultDataContext
163         public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states) {
164             return this.addNewSegment(states, DEFAULT_INTERPOLATION_SIZE);
165         }
166 
167         /**
168          * Injects pre-computed satellite states into this ephemeris file
169          * object, returning the generated {@link OrekitEphemerisSegment} that
170          * has been stored internally. Defaults the Celestial Body to be Earths
171          *
172          * <p>This method uses the {@link DataContext#getDefault() default data context}.
173          *
174          * @param states
175          *            a list of {@link SpacecraftState} that will comprise this
176          *            new unit.
177          * @param interpolationSampleSize
178          *            the number of interpolation samples that should be used
179          *            when processed by another system
180          * @return the generated {@link OrekitEphemerisSegment}
181          * @see #addNewSegment(List, CelestialBody, int, TimeScale)
182          */
183         @DefaultDataContext
184         public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states,
185                 final int interpolationSampleSize) {
186             return this.addNewSegment(
187                     states,
188                     DataContext.getDefault().getCelestialBodies().getEarth(),
189                     interpolationSampleSize);
190         }
191 
192         /**
193          * Injects pre-computed satellite states into this ephemeris file
194          * object, returning the generated {@link OrekitEphemerisSegment} that
195          * has been stored internally.
196          *
197          * <p>This method uses the {@link DataContext#getDefault() default data context}.
198          *
199          * @param states
200          *            a list of {@link SpacecraftState} that will comprise this
201          *            new unit.
202          * @param body
203          *            the celestial body the state's frames are with respect to
204          * @param interpolationSampleSize
205          *            the number of interpolation samples that should be used
206          *            when processed by another system
207          * @return the generated {@link OrekitEphemerisSegment}
208          * @see #addNewSegment(List, CelestialBody, int, TimeScale)
209          */
210         @DefaultDataContext
211         public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states, final CelestialBody body,
212                                                     final int interpolationSampleSize) {
213             return addNewSegment(states, body, interpolationSampleSize,
214                     DataContext.getDefault().getTimeScales().getUTC());
215         }
216 
217         /**
218          * Injects pre-computed satellite states into this ephemeris file
219          * object, returning the generated {@link OrekitEphemerisSegment} that
220          * has been stored internally.
221          *
222          * @param states
223          *            a list of {@link SpacecraftState} that will comprise this
224          *            new unit.
225          * @param body
226          *            the celestial body from which the frames are defined
227          * @param interpolationSampleSize
228          *            the number of interpolation samples that should be used
229          *            when processed by another system
230          * @param timeScale
231          *            the time scale used in the new segment.
232          * @return the generated {@link OrekitEphemerisSegment}
233          * @since 10.1
234          */
235         public OrekitEphemerisSegment addNewSegment(final List<SpacecraftState> states,
236                                                     final CelestialBody body,
237                                                     final int interpolationSampleSize,
238                                                     final TimeScale timeScale) {
239             final int minimumSampleSize = 2;
240             if (states == null || states.size() == 0) {
241                 throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "states");
242             }
243 
244             if (interpolationSampleSize < minimumSampleSize) {
245                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA,
246                         interpolationSampleSize);
247             }
248 
249             final AbsoluteDate start = states.get(0).getDate();
250             final AbsoluteDate stop = states.get(states.size() - 1).getDate();
251 
252             if (this.startDate == null || start.compareTo(this.startDate) < 0) {
253                 this.startDate = start;
254             }
255 
256             if (this.stopDate == null || stop.compareTo(this.stopDate) > 0) {
257                 this.stopDate = stop;
258             }
259 
260             final List<TimeStampedPVCoordinates> coordinates = new ArrayList<>();
261             for (SpacecraftState state : states) {
262                 coordinates.add(state.getPVCoordinates());
263             }
264 
265             final Frame frame = states.get(0).getFrame();
266 
267             final OrekitEphemerisSegment newSeg =
268                             new OrekitEphemerisSegment(coordinates, frame, body.getGM(), interpolationSampleSize);
269             this.segments.add(newSeg);
270 
271             return newSeg;
272         }
273     }
274 
275     /** Ephemeris segment. */
276     public static class OrekitEphemerisSegment
277         implements EphemerisFile.EphemerisSegment<TimeStampedPVCoordinates> {
278 
279         /** Coordinates for this ephemeris segment. **/
280         private final List<TimeStampedPVCoordinates> coordinates;
281 
282         /** The reference frame for this ephemeris segment. **/
283         private final Frame frame;
284 
285         /** Standard gravitational parameter for the satellite. **/
286         private final double mu;
287 
288         /** The number of interpolation samples. */
289         private final int interpolationSamples;
290 
291         /**
292          * constructor for OrekitEphemerisSegment.
293          *
294          * @param coordinates
295          *            coordinates making up the ephemeris for this segment
296          * @param frame
297          *            the frame the coordinates are in
298          * @param mu
299          *            the gravitational constant used in force model evaluations
300          * @param interpolationSamples
301          *            the number of samples to use during interpolation
302          */
303         public OrekitEphemerisSegment(final List<TimeStampedPVCoordinates> coordinates, final Frame frame,
304                                       final double mu, final int interpolationSamples) {
305             this.coordinates          = coordinates;
306             this.frame                = frame;
307             this.mu                   = mu;
308             this.interpolationSamples = interpolationSamples;
309         }
310 
311         /** {@inheritDoc} */
312         @Override
313         public double getMu() {
314             return mu;
315         }
316 
317         /** {@inheritDoc} */
318         @Override
319         public Frame getFrame() {
320             return frame;
321         }
322 
323         /** {@inheritDoc} */
324         @Override
325         public Frame getInertialFrame() {
326             return frame;
327         }
328 
329         /** {@inheritDoc} */
330         @Override
331         public int getInterpolationSamples() {
332             return interpolationSamples;
333         }
334 
335         /** {@inheritDoc} */
336         @Override
337         public CartesianDerivativesFilter getAvailableDerivatives() {
338             return CartesianDerivativesFilter.USE_PV;
339         }
340 
341         /** {@inheritDoc} */
342         @Override
343         public List<TimeStampedPVCoordinates> getCoordinates() {
344             return Collections.unmodifiableList(coordinates);
345         }
346 
347         /** {@inheritDoc} */
348         @Override
349         public AbsoluteDate getStart() {
350             return coordinates.get(0).getDate();
351         }
352 
353         /** {@inheritDoc} */
354         @Override
355         public AbsoluteDate getStop() {
356             return coordinates.get(coordinates.size() - 1).getDate();
357         }
358 
359     }
360 }