1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.sp3;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.SortedSet;
27 import java.util.TreeSet;
28
29 import org.hipparchus.util.FastMath;
30 import org.hipparchus.util.Precision;
31 import org.orekit.errors.OrekitException;
32 import org.orekit.errors.OrekitMessages;
33 import org.orekit.files.general.EphemerisFile;
34 import org.orekit.frames.Frame;
35 import org.orekit.time.AbsoluteDate;
36 import org.orekit.time.ChronologicalComparator;
37
38
39
40
41
42
43 public class SP3 implements EphemerisFile<SP3Coordinate, SP3Segment> {
44
45
46
47
48 private final SP3Header header;
49
50
51 private final double mu;
52
53
54 private final int interpolationSamples;
55
56
57 private final Frame frame;
58
59
60 private Map<String, SP3Ephemeris> satellites;
61
62
63
64
65
66
67
68
69 public SP3(final double mu, final int interpolationSamples, final Frame frame) {
70 this.header = new SP3Header();
71 this.mu = mu;
72 this.interpolationSamples = interpolationSamples;
73 this.frame = frame;
74 this.satellites = new LinkedHashMap<>();
75 }
76
77
78
79
80
81
82
83
84 public void validate(final boolean parsing, final String fileName) throws OrekitException {
85
86
87 final SortedSet<AbsoluteDate> epochs = new TreeSet<>(new ChronologicalComparator());
88 boolean hasAccuracy = false;
89 for (final Map.Entry<String, SP3Ephemeris> entry : satellites.entrySet()) {
90 SP3Coordinate previous = null;
91 for (final SP3Segment segment : entry.getValue().getSegments()) {
92 for (final SP3Coordinate coordinate : segment.getCoordinates()) {
93 final AbsoluteDate previousDate = previous == null ? header.getEpoch() : previous.getDate();
94 final double nbSteps = coordinate.getDate().durationFrom(previousDate) / header.getEpochInterval();
95 if (FastMath.abs(nbSteps - FastMath.rint(nbSteps)) > 0.001) {
96
97 throw new OrekitException(OrekitMessages.INCONSISTENT_SAMPLING_DATE,
98 previousDate.shiftedBy(FastMath.rint(nbSteps) * header.getEpochInterval()),
99 coordinate.getDate());
100 }
101 epochs.add(coordinate.getDate());
102 previous = coordinate;
103 hasAccuracy |= !(coordinate.getPositionAccuracy() == null &&
104 coordinate.getVelocityAccuracy() == null &&
105 Double.isNaN(coordinate.getClockAccuracy()) &&
106 Double.isNaN(coordinate.getClockRateAccuracy()));
107 }
108 }
109 }
110
111
112 if (getSatelliteCount() > getMaxAllowedSatCount(parsing)) {
113 throw new OrekitException(OrekitMessages.SP3_TOO_MANY_SATELLITES_FOR_VERSION,
114 header.getVersion(), getMaxAllowedSatCount(parsing), getSatelliteCount(),
115 fileName);
116 }
117
118 header.validate(parsing, hasAccuracy, fileName);
119
120
121 if (epochs.size() != header.getNumberOfEpochs()) {
122 throw new OrekitException(OrekitMessages.SP3_NUMBER_OF_EPOCH_MISMATCH,
123 epochs.size(), fileName, header.getNumberOfEpochs());
124 }
125
126 }
127
128
129
130
131
132 public SP3Header getHeader() {
133 return header;
134 }
135
136
137
138
139
140
141
142
143 private int getMaxAllowedSatCount(final boolean parsing) {
144 return header.getVersion() < 'd' ? (parsing ? 99 : 85) : 999;
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 public static SP3 splice(final Collection<SP3> sp3) {
178
179
180 final ChronologicalComparator comparator = new ChronologicalComparator();
181 final SortedSet<SP3> sorted = new TreeSet<>((s1, s2) -> comparator.compare(s1.header.getEpoch(), s2.header.getEpoch()));
182 sorted.addAll(sp3);
183
184
185 final SP3 first = sorted.first();
186 final SP3 spliced = new SP3(first.mu, first.interpolationSamples, first.frame);
187 spliced.header.setFilter(first.header.getFilter());
188 spliced.header.setType(first.header.getType());
189 spliced.header.setTimeSystem(first.header.getTimeSystem());
190 spliced.header.setDataUsed(first.header.getDataUsed());
191 spliced.header.setEpoch(first.header.getEpoch());
192 spliced.header.setGpsWeek(first.header.getGpsWeek());
193 spliced.header.setSecondsOfWeek(first.header.getSecondsOfWeek());
194 spliced.header.setModifiedJulianDay(first.header.getModifiedJulianDay());
195 spliced.header.setDayFraction(first.header.getDayFraction());
196 spliced.header.setEpochInterval(first.header.getEpochInterval());
197 spliced.header.setCoordinateSystem(first.header.getCoordinateSystem());
198 spliced.header.setOrbitTypeKey(first.header.getOrbitTypeKey());
199 spliced.header.setAgency(first.header.getAgency());
200 spliced.header.setPosVelBase(first.header.getPosVelBase());
201 spliced.header.setClockBase(first.header.getClockBase());
202
203
204 final List<String> commonSats = new ArrayList<>(first.header.getSatIds());
205 for (final SP3 current : sorted) {
206 for (final Iterator<String> iter = commonSats.iterator(); iter.hasNext();) {
207 final String sat = iter.next();
208 if (!current.containsSatellite(sat)) {
209 iter.remove();
210 break;
211 }
212 }
213 }
214
215
216 for (final String sat : commonSats) {
217 spliced.addSatellite(sat);
218 }
219
220
221 for (int i = 0; i < commonSats.size(); ++i) {
222 final String sat = commonSats.get(i);
223 double accuracy = Double.POSITIVE_INFINITY;
224 for (final SP3 current : sorted) {
225 accuracy = FastMath.max(accuracy, current.header.getAccuracy(sat));
226 }
227 spliced.header.setAccuracy(i, accuracy);
228 }
229
230
231 SP3 previous = null;
232 int epochCount = 0;
233 for (final SP3 current : sorted) {
234
235 epochCount += current.header.getNumberOfEpochs();
236 if (previous != null) {
237
238
239 final boolean dropLast = current.checkSplice(previous);
240 if (dropLast) {
241 --epochCount;
242 }
243
244
245 for (final Map.Entry<String, SP3Ephemeris> entry : previous.satellites.entrySet()) {
246 if (commonSats.contains(entry.getKey())) {
247 final SP3Ephemeris splicedEphemeris = spliced.getEphemeris(entry.getKey());
248 for (final SP3Segment segment : entry.getValue().getSegments()) {
249 final List<SP3Coordinate> coordinates = segment.getCoordinates();
250 for (int i = 0; i < coordinates.size() - (dropLast ? 1 : 0); ++i) {
251 splicedEphemeris.addCoordinate(coordinates.get(i), spliced.header.getEpochInterval());
252 }
253 }
254 }
255 }
256
257 }
258
259 previous = current;
260
261 }
262 spliced.header.setNumberOfEpochs(epochCount);
263
264
265 for (final Map.Entry<String, SP3Ephemeris> entry : previous.satellites.entrySet()) {
266 if (commonSats.contains(entry.getKey())) {
267 final SP3Ephemeris splicedEphemeris = spliced.getEphemeris(entry.getKey());
268 for (final SP3Segment segment : entry.getValue().getSegments()) {
269 for (final SP3Coordinate coordinate : segment.getCoordinates()) {
270 splicedEphemeris.addCoordinate(coordinate, spliced.header.getEpochInterval());
271 }
272 }
273 }
274 }
275
276 return spliced;
277
278 }
279
280
281
282
283
284
285
286
287 private boolean checkSplice(final SP3 previous) throws OrekitException {
288
289 if (!(previous.header.getType() == header.getType() &&
290 previous.header.getTimeSystem() == header.getTimeSystem() &&
291 previous.header.getOrbitType() == header.getOrbitType() &&
292 previous.header.getCoordinateSystem().equals(header.getCoordinateSystem()) &&
293 previous.header.getDataUsed().equals(header.getDataUsed()) &&
294 previous.header.getAgency().equals(header.getAgency()))) {
295 throw new OrekitException(OrekitMessages.SP3_INCOMPATIBLE_FILE_METADATA);
296 }
297
298 boolean dropLast = false;
299 for (final Map.Entry<String, SP3Ephemeris> entry : previous.satellites.entrySet()) {
300 final SP3Ephemeris previousEphem = entry.getValue();
301 final SP3Ephemeris currentEphem = satellites.get(entry.getKey());
302 if (currentEphem != null) {
303 if (!(previousEphem.getAvailableDerivatives() == currentEphem.getAvailableDerivatives() &&
304 previousEphem.getFrame() == currentEphem.getFrame() &&
305 previousEphem.getInterpolationSamples() == currentEphem.getInterpolationSamples() &&
306 Precision.equals(previousEphem.getMu(), currentEphem.getMu(), 2))) {
307 throw new OrekitException(OrekitMessages.SP3_INCOMPATIBLE_SATELLITE_MEDATADA,
308 entry.getKey());
309 } else {
310 final double dt = currentEphem.getStart().durationFrom(previousEphem.getStop());
311 dropLast = dt < 0.001 * header.getEpochInterval();
312 }
313 }
314 }
315
316 return dropLast;
317
318 }
319
320
321
322
323
324 public void addSatellite(final String satId) {
325 header.addSatId(satId);
326 satellites.putIfAbsent(satId, new SP3Ephemeris(satId, mu, frame, interpolationSamples, header.getFilter()));
327 }
328
329 @Override
330 public Map<String, SP3Ephemeris> getSatellites() {
331 return Collections.unmodifiableMap(satellites);
332 }
333
334
335
336
337
338
339 public SP3Ephemeris getEphemeris(final int index) {
340 int n = index;
341 for (final Map.Entry<String, SP3Ephemeris> entry : satellites.entrySet()) {
342 if (n == 0) {
343 return entry.getValue();
344 }
345 n--;
346 }
347
348
349 throw new OrekitException(OrekitMessages.INVALID_SATELLITE_ID, index);
350
351 }
352
353
354
355
356
357
358 public SP3Ephemeris getEphemeris(final String satId) {
359 final SP3Ephemeris ephemeris = satellites.get(satId);
360 if (ephemeris == null) {
361 throw new OrekitException(OrekitMessages.INVALID_SATELLITE_ID, satId);
362 } else {
363 return ephemeris;
364 }
365 }
366
367
368
369
370 public int getSatelliteCount() {
371 return satellites.size();
372 }
373
374
375
376
377
378
379
380 public boolean containsSatellite(final String satId) {
381 return header.getSatIds().contains(satId);
382 }
383
384 }