1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.frames;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
23
24 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitInternalError;
27 import org.orekit.errors.OrekitMessages;
28 import org.orekit.errors.TimeStampedCacheException;
29 import org.orekit.time.AbsoluteDate;
30 import org.orekit.time.TimeFunction;
31 import org.orekit.time.TimeStamped;
32 import org.orekit.utils.Constants;
33 import org.orekit.utils.GenericTimeStampedCache;
34 import org.orekit.utils.IERSConventions;
35 import org.orekit.utils.ImmutableTimeStampedCache;
36 import org.orekit.utils.OrekitConfiguration;
37 import org.orekit.utils.TimeStampedCache;
38 import org.orekit.utils.TimeStampedGenerator;
39
40
41
42
43 public class EOPHistory implements Serializable {
44
45
46 private static final long serialVersionUID = 20131010L;
47
48
49 private static final int INTERPOLATION_POINTS = 4;
50
51
52
53
54
55
56 private final boolean hasData;
57
58
59 private final transient ImmutableTimeStampedCache<EOPEntry> cache;
60
61
62 private final IERSConventions conventions;
63
64
65 private final transient TimeFunction<double[]> tidalCorrection;
66
67
68
69
70
71
72
73 protected EOPHistory(final IERSConventions conventions,
74 final Collection<EOPEntry> data,
75 final boolean simpleEOP)
76 throws OrekitException {
77 this(conventions, data, simpleEOP ? null : new CachedCorrection(conventions.getEOPTidalCorrection()));
78 }
79
80
81
82
83
84
85
86 private EOPHistory(final IERSConventions conventions,
87 final Collection<EOPEntry> data,
88 final TimeFunction<double[]> tidalCorrection)
89 throws OrekitException {
90 this.conventions = conventions;
91 this.tidalCorrection = tidalCorrection;
92 if (data.size() >= INTERPOLATION_POINTS) {
93
94 cache = new ImmutableTimeStampedCache<EOPEntry>(INTERPOLATION_POINTS, data);
95 hasData = true;
96 } else {
97
98 cache = ImmutableTimeStampedCache.emptyCache();
99 hasData = false;
100 }
101 }
102
103
104
105
106
107 public EOPHistory getNonInterpolatingEOPHistory()
108 throws OrekitException {
109 return new EOPHistory(conventions, getEntries(), conventions.getEOPTidalCorrection());
110 }
111
112
113
114
115 public boolean usesInterpolation() {
116 return tidalCorrection != null && tidalCorrection instanceof CachedCorrection;
117 }
118
119
120
121
122 public IERSConventions getConventions() {
123 return conventions;
124 }
125
126
127
128
129 public AbsoluteDate getStartDate() {
130 return this.cache.getEarliest().getDate();
131 }
132
133
134
135
136 public AbsoluteDate getEndDate() {
137 return this.cache.getLatest().getDate();
138 }
139
140
141
142
143
144
145 public double getUT1MinusUTC(final AbsoluteDate date) {
146
147 if (!this.hasDataFor(date)) {
148
149 return (tidalCorrection == null) ? 0.0 : tidalCorrection.value(date)[2];
150 }
151
152 try {
153 final List<EOPEntry> neighbors = getNeighbors(date);
154 final HermiteInterpolator interpolator = new HermiteInterpolator();
155 final double firstDUT = neighbors.get(0).getUT1MinusUTC();
156 boolean beforeLeap = true;
157 for (final EOPEntry neighbor : neighbors) {
158 final double dut;
159 if (neighbor.getUT1MinusUTC() - firstDUT > 0.9) {
160
161 dut = neighbor.getUT1MinusUTC() - 1.0;
162 if (neighbor.getDate().compareTo(date) <= 0) {
163 beforeLeap = false;
164 }
165 } else {
166 dut = neighbor.getUT1MinusUTC();
167 }
168 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
169 new double[] {
170 dut
171 });
172 }
173 double interpolated = interpolator.value(0)[0];
174 if (tidalCorrection != null) {
175 interpolated += tidalCorrection.value(date)[2];
176 }
177 return beforeLeap ? interpolated : interpolated + 1.0;
178 } catch (TimeStampedCacheException tce) {
179
180 throw new OrekitInternalError(tce);
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193
194 protected List<EOPEntry> getNeighbors(final AbsoluteDate central) throws TimeStampedCacheException {
195 return cache.getNeighbors(central);
196 }
197
198
199
200
201
202
203 public double getLOD(final AbsoluteDate date) {
204
205 if (!this.hasDataFor(date)) {
206
207 return (tidalCorrection == null) ? 0.0 : tidalCorrection.value(date)[3];
208 }
209
210 try {
211 final HermiteInterpolator interpolator = new HermiteInterpolator();
212 for (final EOPEntry entry : getNeighbors(date)) {
213 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
214 new double[] {
215 entry.getLOD()
216 });
217 }
218 double interpolated = interpolator.value(0)[0];
219 if (tidalCorrection != null) {
220 interpolated += tidalCorrection.value(date)[3];
221 }
222 return interpolated;
223 } catch (TimeStampedCacheException tce) {
224
225 throw new OrekitInternalError(tce);
226 }
227 }
228
229
230
231
232
233
234
235 public PoleCorrection getPoleCorrection(final AbsoluteDate date) {
236
237 if (!this.hasDataFor(date)) {
238
239 if (tidalCorrection == null) {
240 return PoleCorrection.NULL_CORRECTION;
241 } else {
242 final double[] correction = tidalCorrection.value(date);
243 return new PoleCorrection(correction[0], correction[1]);
244 }
245 }
246
247 try {
248 final HermiteInterpolator interpolator = new HermiteInterpolator();
249 for (final EOPEntry entry : getNeighbors(date)) {
250 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
251 new double[] {
252 entry.getX(), entry.getY()
253 });
254 }
255 final double[] interpolated = interpolator.value(0);
256 if (tidalCorrection != null) {
257 final double[] correction = tidalCorrection.value(date);
258 interpolated[0] += correction[0];
259 interpolated[1] += correction[1];
260 }
261 return new PoleCorrection(interpolated[0], interpolated[1]);
262 } catch (TimeStampedCacheException tce) {
263
264 throw new OrekitInternalError(tce);
265 }
266 }
267
268
269
270
271
272
273
274 public double[] getEquinoxNutationCorrection(final AbsoluteDate date) {
275
276 if (!this.hasDataFor(date)) {
277
278 return new double[2];
279 }
280
281 try {
282 final HermiteInterpolator interpolator = new HermiteInterpolator();
283 for (final EOPEntry entry : getNeighbors(date)) {
284 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
285 new double[] {
286 entry.getDdPsi(), entry.getDdEps()
287 });
288 }
289 return interpolator.value(0);
290 } catch (TimeStampedCacheException tce) {
291
292 throw new OrekitInternalError(tce);
293 }
294 }
295
296
297
298
299
300
301
302 public double[] getNonRotatinOriginNutationCorrection(final AbsoluteDate date) {
303
304 if (!this.hasDataFor(date)) {
305
306 return new double[2];
307 }
308
309 try {
310 final HermiteInterpolator interpolator = new HermiteInterpolator();
311 for (final EOPEntry entry : getNeighbors(date)) {
312 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
313 new double[] {
314 entry.getDx(), entry.getDy()
315 });
316 }
317 return interpolator.value(0);
318 } catch (TimeStampedCacheException tce) {
319
320 throw new OrekitInternalError(tce);
321 }
322 }
323
324
325
326
327
328 public void checkEOPContinuity(final double maxGap) throws OrekitException {
329 TimeStamped preceding = null;
330 for (final TimeStamped current : this.cache.getAll()) {
331
332
333 if ((preceding != null) && ((current.getDate().durationFrom(preceding.getDate())) > maxGap)) {
334 throw new OrekitException(OrekitMessages.MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES,
335 preceding.getDate(), current.getDate());
336 }
337
338
339 preceding = current;
340
341 }
342 }
343
344
345
346
347
348
349
350
351
352 protected boolean hasDataFor(final AbsoluteDate date) {
353
354
355
356
357 return this.hasData && this.getStartDate().compareTo(date) <= 0 &&
358 date.compareTo(this.getEndDate()) <= 0;
359 }
360
361
362
363
364 List<EOPEntry> getEntries() {
365 return cache.getAll();
366 }
367
368
369
370
371
372
373
374 private Object writeReplace() {
375 return new DataTransferObject(conventions, getEntries(), tidalCorrection == null);
376 }
377
378
379 private static class DataTransferObject implements Serializable {
380
381
382 private static final long serialVersionUID = 20131010L;
383
384
385 private final IERSConventions conventions;
386
387
388 private final List<EOPEntry> entries;
389
390
391 private final boolean simpleEOP;
392
393
394
395
396
397
398 DataTransferObject(final IERSConventions conventions,
399 final List<EOPEntry> entries,
400 final boolean simpleEOP) {
401 this.conventions = conventions;
402 this.entries = entries;
403 this.simpleEOP = simpleEOP;
404 }
405
406
407
408
409 private Object readResolve() {
410 try {
411
412 return new EOPHistory(conventions, entries, simpleEOP);
413 } catch (OrekitException oe) {
414 throw new OrekitInternalError(oe);
415 }
416 }
417
418 }
419
420
421 private static class TidalCorrectionEntry implements TimeStamped {
422
423
424 private final AbsoluteDate date;
425
426
427 private final double[] correction;
428
429
430
431
432
433 TidalCorrectionEntry(final AbsoluteDate date, final double[] correction) {
434 this.date = date;
435 this.correction = correction;
436 }
437
438
439 @Override
440 public AbsoluteDate getDate() {
441 return date;
442 }
443
444 }
445
446
447 private static class CachedCorrection
448 implements TimeFunction<double[]>, TimeStampedGenerator<TidalCorrectionEntry> {
449
450
451 private final TimeFunction<double[]> tidalCorrection;
452
453
454 private final double step;
455
456
457 private final TimeStampedCache<TidalCorrectionEntry> cache;
458
459
460
461
462 CachedCorrection(final TimeFunction<double[]> tidalCorrection) {
463 this.step = 60 * 60;
464 this.tidalCorrection = tidalCorrection;
465 this.cache =
466 new GenericTimeStampedCache<TidalCorrectionEntry>(8,
467 OrekitConfiguration.getCacheSlotsNumber(),
468 Constants.JULIAN_DAY * 30,
469 Constants.JULIAN_DAY,
470 this,
471 TidalCorrectionEntry.class);
472 }
473
474
475 @Override
476 public double[] value(final AbsoluteDate date) {
477 try {
478
479 final HermiteInterpolator interpolator = new HermiteInterpolator();
480 for (final TidalCorrectionEntry entry : cache.getNeighbors(date)) {
481 interpolator.addSamplePoint(entry.date.durationFrom(date), entry.correction);
482 }
483
484
485 return interpolator.value(0.0);
486 } catch (TimeStampedCacheException tsce) {
487
488 throw new OrekitInternalError(tsce);
489 }
490 }
491
492
493 @Override
494 public List<TidalCorrectionEntry> generate(final TidalCorrectionEntry existing, final AbsoluteDate date) {
495
496 final List<TidalCorrectionEntry> generated = new ArrayList<TidalCorrectionEntry>();
497
498 if (existing == null) {
499
500
501 for (int i = -cache.getNeighborsSize() / 2; generated.size() < cache.getNeighborsSize(); ++i) {
502 final AbsoluteDate t = date.shiftedBy(i * step);
503 generated.add(new TidalCorrectionEntry(t, tidalCorrection.value(t)));
504 }
505
506 } else {
507
508
509
510
511 AbsoluteDate t = existing.getDate();
512 if (date.compareTo(t) > 0) {
513
514 do {
515 t = t.shiftedBy(step);
516 generated.add(new TidalCorrectionEntry(t, tidalCorrection.value(t)));
517 } while (t.compareTo(date) <= 0);
518 } else {
519
520 do {
521 t = t.shiftedBy(-step);
522 generated.add(0, new TidalCorrectionEntry(t, tidalCorrection.value(t)));
523 } while (t.compareTo(date) >= 0);
524 }
525 }
526
527
528 return generated;
529
530 }
531 }
532
533 }