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.ilrs;
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.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.orekit.attitudes.AttitudeProvider;
27  import org.orekit.files.general.EphemerisFile;
28  import org.orekit.frames.Frame;
29  import org.orekit.propagation.BoundedPropagator;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.TimeScale;
32  import org.orekit.utils.CartesianDerivativesFilter;
33  import org.orekit.utils.TimeStampedPVCoordinates;
34  
35  /**
36   * This class stores all the information of the Consolidated laser ranging Prediction File (CPF) parsed
37   * by CPFParser. It contains the header and a list of ephemeris entry.
38   * @author Bryan Cazabonne
39   * @since 10.3
40   */
41  public class CPF implements EphemerisFile<CPF.CPFCoordinate, CPF.CPFEphemeris> {
42  
43      /** Default satellite ID, used if header is null when initializing the ephemeris. */
44      public static final String DEFAULT_ID = "9999999";
45  
46      /** Gravitational coefficient. */
47      private double mu;
48  
49      /** The interpolation sample. */
50      private int interpolationSample;
51  
52      /** Time scale of dates in the ephemeris file. */
53      private TimeScale timeScale;
54  
55      /** Indicates if data contains velocity or not. */
56      private CartesianDerivativesFilter filter;
57  
58      /** CPF file header. */
59      private final CPFHeader header;
60  
61      /** Map containing satellite information. */
62      private final Map<String, CPFEphemeris> ephemeris;
63  
64      /** List of comments contained in the file. */
65      private final List<String> comments;
66  
67      /**
68       * Constructor.
69       */
70      public CPF() {
71          this.mu        = Double.NaN;
72          this.ephemeris = new ConcurrentHashMap<>();
73          this.header    = new CPFHeader();
74          this.comments  = new ArrayList<>();
75      }
76  
77      /** {@inheritDoc}
78       * First key corresponds to String value of {@link CPFHeader#getIlrsSatelliteId()}
79       */
80      @Override
81      public Map<String, CPFEphemeris> getSatellites() {
82          // Return the map
83          return Collections.unmodifiableMap(ephemeris);
84      }
85  
86      /**
87       * Get the CPF file header.
88       * @return the CPF file header
89       */
90      public CPFHeader getHeader() {
91          return header;
92      }
93  
94      /**
95       * Get the time scale used in CPF file.
96       * @return the time scale used to parse epochs in CPF file.
97       */
98      public TimeScale getTimeScale() {
99          return timeScale;
100     }
101 
102     /**
103      * Get the comments contained in the file.
104      * @return the comments contained in the file
105      */
106     public List<String> getComments() {
107         return comments;
108     }
109 
110     /**
111      * Adds a set of P/V coordinates to the satellite.
112      * @param id satellite ILRS identifier
113      * @param coord set of coordinates
114      * @since 11.0.1
115      */
116     public void addSatelliteCoordinates(final String id, final List<CPFCoordinate> coord) {
117         final CPFEphemeris e;
118         synchronized (this) {
119             e = ephemeris.computeIfAbsent(id, CPFEphemeris::new);
120         }
121         e.coordinates.addAll(coord);
122     }
123 
124     /**
125      * Add a new P/V coordinates to the satellite.
126      * @param id satellite ILRS identifier
127      * @param coord the P/V coordinate of the satellite
128      * @since 11.0.1
129      */
130     public void addSatelliteCoordinate(final String id, final CPFCoordinate coord) {
131         final CPFEphemeris e;
132         synchronized (this) {
133             e = ephemeris.computeIfAbsent(id, CPFEphemeris::new);
134         }
135         e.coordinates.add(coord);
136     }
137 
138     /**
139      * Add the velocity to the last CPF coordinate entry.
140      * @param id satellite ILRS identifier
141      * @param velocity the velocity vector of the satellite
142      * @since 11.2
143      */
144     public void addSatelliteVelocityToCPFCoordinate(final String id, final Vector3D velocity) {
145         // Get the last coordinate entry, which contains the position vector
146         final CPFCoordinate lastCoordinate = ephemeris.get(id).coordinates.get(ephemeris.get(id).coordinates.size() - 1);
147 
148         // Create a new CPFCoordinate object with both position and velocity information
149         final CPFCoordinate CPFCoordUpdated = new CPFCoordinate(lastCoordinate.getDate(),
150                 lastCoordinate.getPosition(),
151                 velocity,
152                 lastCoordinate.getLeap());
153 
154         // Patch the last record
155         ephemeris.get(id).coordinates.set(ephemeris.get(id).coordinates.size() - 1, CPFCoordUpdated);
156     }
157 
158     /**
159      * Set the interpolation sample.
160      * @param interpolationSample interpolation sample
161      */
162     public void setInterpolationSample(final int interpolationSample) {
163         this.interpolationSample = interpolationSample;
164     }
165 
166     /**
167      * Set the gravitational coefficient.
168      * @param mu the coefficient to be set
169      */
170     public void setMu(final double mu) {
171         this.mu = mu;
172     }
173 
174     /**
175      * Set the time scale.
176      * @param timeScale use to parse dates in this file.
177      */
178     public void setTimeScale(final TimeScale timeScale) {
179         this.timeScale = timeScale;
180     }
181 
182     /**
183      * Set the derivatives filter.
184      * @param filter that indicates which derivatives of position are available.
185      */
186     public void setFilter(final CartesianDerivativesFilter filter) {
187         this.filter = filter;
188     }
189 
190     /** An ephemeris entry  for a single satellite contains in a CPF file. */
191     public class CPFEphemeris
192         implements EphemerisFile.SatelliteEphemeris<CPFCoordinate, CPFEphemeris>,
193                    EphemerisFile.EphemerisSegment<CPFCoordinate> {
194 
195         /** Satellite ID. */
196         private final String id;
197 
198         /** Ephemeris Data. */
199         private final List<CPFCoordinate> coordinates;
200 
201         /**
202          * Constructor.
203          * @param id satellite ID
204          */
205         public CPFEphemeris(final String id) {
206             this.id          = id;
207             this.coordinates = new ArrayList<>();
208         }
209 
210 
211         /** {@inheritDoc} */
212         @Override
213         public Frame getFrame() {
214             return header.getRefFrame();
215         }
216 
217         /** {@inheritDoc} */
218         @Override
219         public int getInterpolationSamples() {
220             return interpolationSample;
221         }
222 
223         /** {@inheritDoc} */
224         @Override
225         public CartesianDerivativesFilter getAvailableDerivatives() {
226             return filter;
227         }
228 
229         /** {@inheritDoc} */
230         @Override
231         public List<CPFCoordinate> getCoordinates() {
232             return Collections.unmodifiableList(this.coordinates);
233         }
234 
235         /** {@inheritDoc} */
236         @Override
237         public String getId() {
238             return id == null ? DEFAULT_ID : id;
239         }
240 
241         /** {@inheritDoc} */
242         @Override
243         public double getMu() {
244             return mu;
245         }
246 
247         /** Returns a list containing only {@code this}. */
248         @Override
249         public List<CPFEphemeris> getSegments() {
250             return Collections.singletonList(this);
251         }
252 
253         /** {@inheritDoc} */
254         @Override
255         public AbsoluteDate getStart() {
256             return coordinates.get(0).getDate();
257         }
258 
259         /** {@inheritDoc} */
260         @Override
261         public AbsoluteDate getStop() {
262             return coordinates.get(coordinates.size() - 1).getDate();
263         }
264 
265         /** {@inheritDoc} */
266         @Override
267         public BoundedPropagator getPropagator() {
268             return EphemerisSegment.super.getPropagator();
269         }
270 
271         /** {@inheritDoc} */
272         @Override
273         public BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) {
274             return EphemerisSegment.super.getPropagator(attitudeProvider);
275         }
276 
277         /** Get the list of Ephemerides data lines.
278          * @return a reference to the internal list of Ephemerides data lines
279          */
280         public List<CPFCoordinate> getEphemeridesDataLines() {
281             return this.coordinates;
282         }
283 
284     }
285 
286     /** A single record of position and possibility velocity in an SP3 file. */
287     public static class CPFCoordinate extends TimeStampedPVCoordinates {
288 
289         /** Leap second flag. */
290         private final int leap;
291 
292         /**
293          * Constructor with null velocity vector.
294          * @param date date of coordinates validity
295          * @param position position vector
296          * @param leap leap second flag (= 0 or the value of the new leap second)
297          */
298         public CPFCoordinate(final AbsoluteDate date,
299                              final Vector3D position,
300                              final int leap) {
301             this(date, position, Vector3D.ZERO, leap);
302         }
303 
304         /**
305          * Constructor.
306          * @param date date of coordinates validity
307          * @param position position vector
308          * @param velocity velocity vector
309          * @param leap leap second flag (= 0 or the value of the new leap second)
310          */
311         public CPFCoordinate(final AbsoluteDate date,
312                              final Vector3D position,
313                              final Vector3D velocity,
314                              final int leap) {
315             super(date, position, velocity);
316             this.leap = leap;
317         }
318 
319         /**
320          * Get the leap second flag (= 0 or the value of the new leap second).
321          * @return the leap second flag
322          */
323         public int getLeap() {
324             return leap;
325         }
326 
327     }
328 
329 }