1   /* Copyright 2022-2025 Luc Maisonobe
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.sp3;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.orekit.files.general.EphemerisFile;
24  import org.orekit.frames.Frame;
25  import org.orekit.time.AbsoluteDate;
26  import org.orekit.time.AggregatedClockModel;
27  import org.orekit.time.ClockModel;
28  import org.orekit.utils.CartesianDerivativesFilter;
29  import org.orekit.utils.TimeSpanMap;
30  
31  /** Single satellite ephemeris from an {@link SP3 SP3} file.
32   * @author Luc Maisonobe
33   * @since 12.0
34   */
35  public class SP3Ephemeris implements EphemerisFile.SatelliteEphemeris<SP3Coordinate, SP3Segment> {
36  
37      /** Satellite ID. */
38      private final String id;
39  
40      /** Standard gravitational parameter in m³ / s². */
41      private final double mu;
42  
43      /** Reference frame. */
44      private final Frame frame;
45  
46      /** Number of points to use for interpolation. */
47      private final int interpolationSamples;
48  
49      /** Available derivatives. */
50      private final CartesianDerivativesFilter filter;
51  
52      /** Segments. */
53      private final List<SP3Segment> segments;
54  
55      /** Create an ephemeris for a single satellite.
56       * @param id of the satellite.
57       * @param mu standard gravitational parameter to use for creating
58       * {@link org.orekit.orbits.Orbit Orbits} from the ephemeris data.
59       * @param frame reference frame
60       * @param interpolationSamples number of points to use for interpolation
61       * @param filter available derivatives
62       */
63      public SP3Ephemeris(final String id, final double mu, final Frame frame,
64                          final int interpolationSamples, final CartesianDerivativesFilter filter) {
65          this.id                   = id;
66          this.mu                   = mu;
67          this.frame                = frame;
68          this.interpolationSamples = interpolationSamples;
69          this.filter               = filter;
70          this.segments             = new ArrayList<>();
71      }
72  
73      /** {@inheritDoc} */
74      @Override
75      public String getId() {
76          return this.id;
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public double getMu() {
82          return mu;
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      public List<SP3Segment> getSegments() {
88          return Collections.unmodifiableList(segments);
89      }
90  
91      /** {@inheritDoc} */
92      @Override
93      public AbsoluteDate getStart() {
94          return segments.isEmpty() ? null : segments.get(0).getStart();
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public AbsoluteDate getStop() {
100         return segments.isEmpty() ? null : segments.get(segments.size() - 1).getStop();
101     }
102 
103     /** Get the reference frame.
104      * @return reference frame
105      */
106     public Frame getFrame() {
107         return frame;
108     }
109 
110     /** Get the number of points to use for interpolation.
111      * @return number of points to use for interpolation
112      */
113     public int getInterpolationSamples() {
114         return interpolationSamples;
115     }
116 
117     /** Get the available derivatives.
118      * @return available derivatives
119      */
120     public CartesianDerivativesFilter getAvailableDerivatives() {
121         return filter;
122     }
123 
124     /** Adds a new P/V coordinate.
125      * @param coord the P/V coordinate of the satellite
126      * @param maxGap maximum gap between segments
127      */
128     public void addCoordinate(final SP3Coordinate coord, final double maxGap) {
129         final AbsoluteDate lastDate = getStop();
130         final SP3Segment segment;
131         if (lastDate == null || coord.getDate().durationFrom(lastDate) > maxGap) {
132             // we need to create a new segment
133             segment = new SP3Segment(mu, frame,  interpolationSamples, filter);
134             segments.add(segment);
135         } else {
136             segment = segments.get(segments.size() - 1);
137         }
138         segment.addCoordinate(coord);
139     }
140 
141     /** Extract the clock model.
142      * <p>
143      *  There are always 2n+1 {@link AggregatedClockModel#getModels()}
144      *  underlying clock models when there are n {@link #getSegments() segments}
145      *  in the ephemeris. This happens because there are spans with {@code null}
146      *  data before the first segment, between all regular segments and after
147      *  last segment.
148      * </p>
149      * @return extracted clock model
150      * @since 12.1
151      */
152     public AggregatedClockModel extractClockModel() {
153         // set up the map for all segments clock models
154         final TimeSpanMap<ClockModel> models = new TimeSpanMap<>(null);
155         segments.forEach(segment -> models.addValidBetween(segment.extractClockModel(),
156                                                            segment.getStart(),
157                                                            segment.getStop()));
158         return new AggregatedClockModel(models);
159     }
160 
161 }