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.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.attitudes.BoundedAttitudeProvider;
26  import org.orekit.attitudes.FixedFrameBuilder;
27  import org.orekit.attitudes.TabulatedProvider;
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.utils.AngularDerivativesFilter;
34  import org.orekit.utils.TimeStampedAngularCoordinates;
35  
36  /**
37   * A class for encapsulating Orekit propagators within an {@link AttitudeEphemerisFile}
38   * complaint object that makes for easy serialization to external ephemeris
39   * formats like AEM.
40   *
41   * @author Raphaël Fermé
42   * @since 10.3
43   *
44   */
45  public class OrekitAttitudeEphemerisFile
46      implements AttitudeEphemerisFile<TimeStampedAngularCoordinates,
47                                       OrekitAttitudeEphemerisFile.OrekitAttitudeEphemerisSegment> {
48  
49      /** Hashmap of satellite ephemeris. **/
50      private final Map<String, OrekitSatelliteAttitudeEphemeris> satellites;
51  
52      /**
53       * Standard default constructor.
54       */
55      public OrekitAttitudeEphemerisFile() {
56          this.satellites = new ConcurrentHashMap<>();
57      }
58  
59      /** {@inheritDoc} */
60      @Override
61      public Map<String, OrekitSatelliteAttitudeEphemeris> 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 OrekitSatelliteAttitudeEphemeris addSatellite(final String id) {
73          final OrekitSatelliteAttitudeEphemeris newSat = new OrekitSatelliteAttitudeEphemeris(id);
74          this.satellites.put(id, newSat);
75          return newSat;
76      }
77  
78      /**
79       * Inner class of {@link OrekitAttitudeEphemerisFile} that defines the
80       * {@link OrekitSatelliteAttitudeEphemeris} corresponding object for this ephemeris type.
81       *
82       */
83      public static class OrekitSatelliteAttitudeEphemeris
84          implements SatelliteAttitudeEphemeris<TimeStampedAngularCoordinates,
85                                                OrekitAttitudeEphemerisFile.OrekitAttitudeEphemerisSegment> {
86  
87          /** Default interpolation sample size if it is not specified. **/
88          public static final String DEFAULT_INTERPOLATION_METHOD = "LINEAR";
89  
90          /** Default interpolation sample size if it is not specified. **/
91          public static final int DEFAULT_INTERPOLATION_SIZE = 2;
92  
93          /** ID of the space object encapsulated here. **/
94          private final String id;
95  
96          /** Earliest date of this file. **/
97          private AbsoluteDate startDate;
98  
99          /** Latest date of this file. **/
100         private AbsoluteDate stopDate;
101 
102         /** List of segments in the file. **/
103         private final List<OrekitAttitudeEphemerisSegment> segments;
104 
105         /**
106          * Standard constructor for building the satellite Ephemeris object.
107          *
108          * @param id
109          *            the ID of the space object for this data
110          */
111         public OrekitSatelliteAttitudeEphemeris(final String id) {
112             this.id = id;
113             this.segments = new ArrayList<>();
114         }
115 
116         /** {@inheritDoc} */
117         @Override
118         public String getId() {
119             return id;
120         }
121 
122         /** {@inheritDoc} */
123         @Override
124         public List<OrekitAttitudeEphemerisSegment> getSegments() {
125             return Collections.unmodifiableList(this.segments);
126         }
127 
128         /** {@inheritDoc} */
129         @Override
130         public AbsoluteDate getStart() {
131             return this.startDate;
132         }
133 
134         /** {@inheritDoc} */
135         @Override
136         public AbsoluteDate getStop() {
137             return this.stopDate;
138         }
139 
140         /**
141          * Injects pre-computed satellite states into this attitude ephemeris file
142          * object, returning the generated {@link OrekitAttitudeEphemerisSegment} that
143          * has been stored internally.
144          *
145          * @param states
146          *          a list of {@link SpacecraftState} that will comprise this
147          *          new unit
148          * @param interpolationMethod
149          *          the interpolation method that should be used when processed
150          *          by another system
151          * @param interpolationSamples
152          *          the number of interpolation samples that should be used
153          *          when processed by another system
154          * @param availableDerivatives derivatives to use for interpolation
155          * @return the generated {@link OrekitAttitudeEphemerisSegment}
156          */
157         public OrekitAttitudeEphemerisSegment addNewSegment(final List<SpacecraftState> states,
158                                                             final String interpolationMethod,
159                                                             final int interpolationSamples,
160                                                             final AngularDerivativesFilter availableDerivatives) {
161             final int minimumSampleSize = 2;
162             if (states == null || states.size() == 0) {
163                 throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "states");
164             }
165 
166             if (interpolationSamples < minimumSampleSize) {
167                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA,
168                         interpolationSamples);
169             }
170 
171             final AbsoluteDate start = states.get(0).getDate();
172             final AbsoluteDate stop = states.get(states.size() - 1).getDate();
173 
174             if (this.startDate == null || start.compareTo(this.startDate) < 0) {
175                 this.startDate = start;
176             }
177 
178             if (this.stopDate == null || stop.compareTo(this.stopDate) > 0) {
179                 this.stopDate = stop;
180             }
181 
182             final List<TimeStampedAngularCoordinates> attitudeDataLines = new ArrayList<>();
183             for (SpacecraftState state : states) {
184                 attitudeDataLines.add(state.getAttitude().getOrientation());
185             }
186 
187             final OrekitAttitudeEphemerisSegment newSeg =
188                             new OrekitAttitudeEphemerisSegment(attitudeDataLines, interpolationMethod, interpolationSamples,
189                                                                states.get(0).getFrame(), availableDerivatives);
190             this.segments.add(newSeg);
191             return newSeg;
192         }
193     }
194 
195     /** Ephemeris segment. */
196     public static class OrekitAttitudeEphemerisSegment
197         implements AttitudeEphemerisFile.AttitudeEphemerisSegment<TimeStampedAngularCoordinates> {
198 
199         /** List of attitude data lines. */
200         private List<TimeStampedAngularCoordinates> attitudeDataLines;
201 
202         /** The interpolation method to be used. */
203         private String interpolationMethod;
204 
205         /** The number of interpolation samples. */
206         private int interpolationSamples;
207 
208         /** Enumerate for selecting which derivatives to use in {@link #attitudeDataLines} interpolation. */
209         private AngularDerivativesFilter availableDerivatives;
210 
211         /** Reference frame from which attitude is defined. */
212         private Frame referenceFrame;
213 
214         /**
215          * Constructor for OrekitAttitudeEphemerisSegment.
216          *
217          * @param attitudeDataLines
218          *          attitude data lines for this segment.
219          * @param interpolationMethod
220          *          the interpolation method to use.
221          * @param interpolationSamples
222          *          the number of samples to use during interpolation.
223          * @param referenceFrame
224          *          reference frame from which the attitude is defined
225          * @param availableDerivatives derivatives to use for interpolation
226          */
227         public OrekitAttitudeEphemerisSegment(final List<TimeStampedAngularCoordinates> attitudeDataLines,
228                                               final String interpolationMethod,
229                                               final int interpolationSamples,
230                                               final Frame referenceFrame,
231                                               final AngularDerivativesFilter availableDerivatives) {
232             this.attitudeDataLines    = attitudeDataLines;
233             this.interpolationMethod  = interpolationMethod;
234             this.interpolationSamples = interpolationSamples;
235             this.referenceFrame       = referenceFrame;
236             this.availableDerivatives = availableDerivatives;
237         }
238 
239         /** {@inheritDoc} */
240         @Override
241         public List<TimeStampedAngularCoordinates> getAngularCoordinates() {
242             return Collections.unmodifiableList(attitudeDataLines);
243         }
244 
245         /** {@inheritDoc} */
246         @Override
247         public Frame getReferenceFrame() {
248             return referenceFrame;
249         }
250 
251         /** {@inheritDoc} */
252         @Override
253         public AbsoluteDate getStart() {
254             return attitudeDataLines.get(0).getDate();
255         }
256 
257         /** {@inheritDoc} */
258         @Override
259         public AbsoluteDate getStop() {
260             return attitudeDataLines.get(attitudeDataLines.size() - 1).getDate();
261         }
262 
263         /** {@inheritDoc} */
264         @Override
265         public String getInterpolationMethod() {
266             return interpolationMethod;
267         }
268 
269         /** {@inheritDoc} */
270         @Override
271         public int getInterpolationSamples() {
272             return interpolationSamples;
273         }
274 
275         /** {@inheritDoc} */
276         @Override
277         public AngularDerivativesFilter getAvailableDerivatives() {
278             return availableDerivatives;
279         }
280 
281         /** {@inheritDoc} */
282         @Override
283         public BoundedAttitudeProvider getAttitudeProvider() {
284             return new TabulatedProvider(getAngularCoordinates(),
285                                          getInterpolationSamples(), getAvailableDerivatives(),
286                                          getStart(), getStop(),
287                                          new FixedFrameBuilder(getReferenceFrame()));
288         }
289 
290     }
291 
292 }