1 /* Copyright 2002-2024 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 {@link TimeSpanMap.Span
146 * spans} with {@code null} {@link TimeSpanMap.Span#getData()} before the
147 * first segment, between all regular segments and after 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 }