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 import java.util.Optional;
24 import java.util.function.Consumer;
25 import java.util.function.Function;
26 import java.util.stream.Stream;
27
28 import org.hipparchus.RealFieldElement;
29 import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
30 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
31 import org.hipparchus.util.MathArrays;
32 import org.orekit.annotation.DefaultDataContext;
33 import org.orekit.data.DataContext;
34 import org.orekit.errors.OrekitException;
35 import org.orekit.errors.OrekitInternalError;
36 import org.orekit.errors.OrekitMessages;
37 import org.orekit.errors.TimeStampedCacheException;
38 import org.orekit.time.AbsoluteDate;
39 import org.orekit.time.FieldAbsoluteDate;
40 import org.orekit.time.TimeScales;
41 import org.orekit.time.TimeStamped;
42 import org.orekit.time.TimeVectorFunction;
43 import org.orekit.utils.Constants;
44 import org.orekit.utils.GenericTimeStampedCache;
45 import org.orekit.utils.IERSConventions;
46 import org.orekit.utils.ImmutableTimeStampedCache;
47 import org.orekit.utils.OrekitConfiguration;
48 import org.orekit.utils.TimeStampedCache;
49 import org.orekit.utils.TimeStampedGenerator;
50
51
52
53
54
55 public class EOPHistory implements Serializable {
56
57
58 private static final long serialVersionUID = 20191119L;
59
60
61 private static final int INTERPOLATION_POINTS = 4;
62
63
64
65
66
67
68 private final boolean hasData;
69
70
71 private final transient ImmutableTimeStampedCache<EOPEntry> cache;
72
73
74 private final IERSConventions conventions;
75
76
77 private final transient TimeVectorFunction tidalCorrection;
78
79
80 private final transient TimeScales timeScales;
81
82
83
84
85
86
87
88
89
90
91 @DefaultDataContext
92 protected EOPHistory(final IERSConventions conventions,
93 final Collection<? extends EOPEntry> data,
94 final boolean simpleEOP) {
95 this(conventions, data, simpleEOP, DataContext.getDefault().getTimeScales());
96 }
97
98
99
100
101
102
103
104
105 public EOPHistory(final IERSConventions conventions,
106 final Collection<? extends EOPEntry> data,
107 final boolean simpleEOP,
108 final TimeScales timeScales) {
109 this(conventions,
110 data,
111 simpleEOP ? null : new CachedCorrection(conventions.getEOPTidalCorrection(timeScales)),
112 timeScales);
113 }
114
115
116
117
118
119
120
121
122 private EOPHistory(final IERSConventions conventions,
123 final Collection<? extends EOPEntry> data,
124 final TimeVectorFunction tidalCorrection,
125 final TimeScales timeScales) {
126 this.conventions = conventions;
127 this.tidalCorrection = tidalCorrection;
128 this.timeScales = timeScales;
129 if (data.size() >= INTERPOLATION_POINTS) {
130
131 cache = new ImmutableTimeStampedCache<EOPEntry>(INTERPOLATION_POINTS, data);
132 hasData = true;
133 } else {
134
135 cache = ImmutableTimeStampedCache.emptyCache();
136 hasData = false;
137 }
138 }
139
140
141
142
143
144
145 public boolean isSimpleEop() {
146 return tidalCorrection == null;
147 }
148
149
150
151
152
153
154
155 public TimeScales getTimeScales() {
156 return timeScales;
157 }
158
159
160
161
162 public EOPHistory getNonInterpolatingEOPHistory() {
163 return new EOPHistory(conventions, getEntries(),
164 conventions.getEOPTidalCorrection(timeScales), timeScales);
165 }
166
167
168
169
170 public boolean usesInterpolation() {
171 return tidalCorrection != null && tidalCorrection instanceof CachedCorrection;
172 }
173
174
175
176
177 public IERSConventions getConventions() {
178 return conventions;
179 }
180
181
182
183
184 public AbsoluteDate getStartDate() {
185 return this.cache.getEarliest().getDate();
186 }
187
188
189
190
191 public AbsoluteDate getEndDate() {
192 return this.cache.getLatest().getDate();
193 }
194
195
196
197
198
199
200 public double getUT1MinusUTC(final AbsoluteDate date) {
201
202
203 if (!this.hasDataFor(date)) {
204
205 return (tidalCorrection == null) ? 0.0 : tidalCorrection.value(date)[2];
206 }
207
208
209 try {
210 final DUT1Interpolator interpolator = new DUT1Interpolator(date);
211 getNeighbors(date).forEach(interpolator);
212 double interpolated = interpolator.getInterpolated();
213 if (tidalCorrection != null) {
214 interpolated += tidalCorrection.value(date)[2];
215 }
216 return interpolated;
217 } catch (TimeStampedCacheException tce) {
218
219 throw new OrekitInternalError(tce);
220 }
221
222 }
223
224
225
226
227
228
229
230
231 public <T extends RealFieldElement<T>> T getUT1MinusUTC(final FieldAbsoluteDate<T> date) {
232
233
234 final AbsoluteDate absDate = date.toAbsoluteDate();
235 if (!this.hasDataFor(absDate)) {
236
237 return (tidalCorrection == null) ? date.getField().getZero() : tidalCorrection.value(date)[2];
238 }
239
240
241 try {
242 final FieldDUT1Interpolator<T> interpolator = new FieldDUT1Interpolator<>(date, absDate);
243 getNeighbors(absDate).forEach(interpolator);
244 T interpolated = interpolator.getInterpolated();
245 if (tidalCorrection != null) {
246 interpolated = interpolated.add(tidalCorrection.value(date)[2]);
247 }
248 return interpolated;
249 } catch (TimeStampedCacheException tce) {
250
251 throw new OrekitInternalError(tce);
252 }
253
254 }
255
256
257 private static class DUT1Interpolator implements Consumer<EOPEntry> {
258
259
260 private double firstDUT;
261
262
263 private boolean beforeLeap;
264
265
266 private final HermiteInterpolator interpolator;
267
268
269 private AbsoluteDate date;
270
271
272
273
274 DUT1Interpolator(final AbsoluteDate date) {
275 this.firstDUT = Double.NaN;
276 this.beforeLeap = true;
277 this.interpolator = new HermiteInterpolator();
278 this.date = date;
279 }
280
281
282 @Override
283 public void accept(final EOPEntry neighbor) {
284 if (Double.isNaN(firstDUT)) {
285 firstDUT = neighbor.getUT1MinusUTC();
286 }
287 final double dut;
288 if (neighbor.getUT1MinusUTC() - firstDUT > 0.9) {
289
290 dut = neighbor.getUT1MinusUTC() - 1.0;
291
292
293
294
295 if (neighbor.getDate().shiftedBy(-1).compareTo(date) <= 0) {
296 beforeLeap = false;
297 }
298 } else {
299 dut = neighbor.getUT1MinusUTC();
300 }
301 interpolator.addSamplePoint(neighbor.getDate().durationFrom(date),
302 new double[] {
303 dut
304 });
305 }
306
307
308
309
310 public double getInterpolated() {
311 final double interpolated = interpolator.value(0)[0];
312 return beforeLeap ? interpolated : interpolated + 1.0;
313 }
314
315 }
316
317
318 private static class FieldDUT1Interpolator<T extends RealFieldElement<T>> implements Consumer<EOPEntry> {
319
320
321 private double firstDUT;
322
323
324 private boolean beforeLeap;
325
326
327 private final FieldHermiteInterpolator<T> interpolator;
328
329
330 private FieldAbsoluteDate<T> date;
331
332
333 private AbsoluteDate absDate;
334
335
336
337
338
339 FieldDUT1Interpolator(final FieldAbsoluteDate<T> date, final AbsoluteDate absDate) {
340 this.firstDUT = Double.NaN;
341 this.beforeLeap = true;
342 this.interpolator = new FieldHermiteInterpolator<>();
343 this.date = date;
344 this.absDate = absDate;
345 }
346
347
348 @Override
349 public void accept(final EOPEntry neighbor) {
350 if (Double.isNaN(firstDUT)) {
351 firstDUT = neighbor.getUT1MinusUTC();
352 }
353 final double dut;
354 if (neighbor.getUT1MinusUTC() - firstDUT > 0.9) {
355
356 dut = neighbor.getUT1MinusUTC() - 1.0;
357 if (neighbor.getDate().compareTo(absDate) <= 0) {
358 beforeLeap = false;
359 }
360 } else {
361 dut = neighbor.getUT1MinusUTC();
362 }
363 final T[] array = MathArrays.buildArray(date.getField(), 1);
364 array[0] = date.getField().getZero().add(dut);
365 interpolator.addSamplePoint(date.durationFrom(neighbor.getDate()).negate(),
366 array);
367 }
368
369
370
371
372 public T getInterpolated() {
373 final T interpolated = interpolator.value(date.getField().getZero())[0];
374 return beforeLeap ? interpolated : interpolated.add(1.0);
375 }
376
377 }
378
379
380
381
382
383
384
385
386
387
388 protected Stream<EOPEntry> getNeighbors(final AbsoluteDate central) {
389 return cache.getNeighbors(central);
390 }
391
392
393
394
395
396
397 public double getLOD(final AbsoluteDate date) {
398
399
400 if (!this.hasDataFor(date)) {
401
402 return (tidalCorrection == null) ? 0.0 : tidalCorrection.value(date)[3];
403 }
404
405
406 double interpolated = interpolate(date, entry -> entry.getLOD());
407 if (tidalCorrection != null) {
408 interpolated += tidalCorrection.value(date)[3];
409 }
410 return interpolated;
411
412 }
413
414
415
416
417
418
419
420
421 public <T extends RealFieldElement<T>> T getLOD(final FieldAbsoluteDate<T> date) {
422
423 final AbsoluteDate aDate = date.toAbsoluteDate();
424
425
426 if (!this.hasDataFor(aDate)) {
427
428 return (tidalCorrection == null) ? date.getField().getZero() : tidalCorrection.value(date)[3];
429 }
430
431
432 T interpolated = interpolate(date, aDate, entry -> entry.getLOD());
433 if (tidalCorrection != null) {
434 interpolated = interpolated.add(tidalCorrection.value(date)[3]);
435 }
436
437 return interpolated;
438
439 }
440
441
442
443
444
445
446
447 public PoleCorrection getPoleCorrection(final AbsoluteDate date) {
448
449
450 if (!this.hasDataFor(date)) {
451
452 if (tidalCorrection == null) {
453 return PoleCorrection.NULL_CORRECTION;
454 } else {
455 final double[] correction = tidalCorrection.value(date);
456 return new PoleCorrection(correction[0], correction[1]);
457 }
458 }
459
460
461 final double[] interpolated = interpolate(date, entry -> entry.getX(), entry -> entry.getY());
462 if (tidalCorrection != null) {
463 final double[] correction = tidalCorrection.value(date);
464 interpolated[0] += correction[0];
465 interpolated[1] += correction[1];
466 }
467 return new PoleCorrection(interpolated[0], interpolated[1]);
468
469 }
470
471
472
473
474
475
476
477
478 public <T extends RealFieldElement<T>> FieldPoleCorrection<T> getPoleCorrection(final FieldAbsoluteDate<T> date) {
479
480 final AbsoluteDate aDate = date.toAbsoluteDate();
481
482
483 if (!this.hasDataFor(aDate)) {
484
485 if (tidalCorrection == null) {
486 return new FieldPoleCorrection<>(date.getField().getZero(), date.getField().getZero());
487 } else {
488 final T[] correction = tidalCorrection.value(date);
489 return new FieldPoleCorrection<>(correction[0], correction[1]);
490 }
491 }
492
493
494 final T[] interpolated = interpolate(date, aDate, entry -> entry.getX(), entry -> entry.getY());
495 if (tidalCorrection != null) {
496 final T[] correction = tidalCorrection.value(date);
497 interpolated[0] = interpolated[0].add(correction[0]);
498 interpolated[1] = interpolated[1].add(correction[1]);
499 }
500 return new FieldPoleCorrection<>(interpolated[0], interpolated[1]);
501
502 }
503
504
505
506
507
508
509
510 public double[] getEquinoxNutationCorrection(final AbsoluteDate date) {
511
512
513 if (!this.hasDataFor(date)) {
514
515 return new double[2];
516 }
517
518
519 return interpolate(date, entry -> entry.getDdPsi(), entry -> entry.getDdEps());
520
521 }
522
523
524
525
526
527
528
529
530 public <T extends RealFieldElement<T>> T[] getEquinoxNutationCorrection(final FieldAbsoluteDate<T> date) {
531
532 final AbsoluteDate aDate = date.toAbsoluteDate();
533
534
535 if (!this.hasDataFor(aDate)) {
536
537 return MathArrays.buildArray(date.getField(), 2);
538 }
539
540
541 return interpolate(date, aDate, entry -> entry.getDdPsi(), entry -> entry.getDdEps());
542
543 }
544
545
546
547
548
549
550
551 public double[] getNonRotatinOriginNutationCorrection(final AbsoluteDate date) {
552
553
554 if (!this.hasDataFor(date)) {
555
556 return new double[2];
557 }
558
559
560 return interpolate(date, entry -> entry.getDx(), entry -> entry.getDy());
561
562 }
563
564
565
566
567
568
569
570
571 public <T extends RealFieldElement<T>> T[] getNonRotatinOriginNutationCorrection(final FieldAbsoluteDate<T> date) {
572
573 final AbsoluteDate aDate = date.toAbsoluteDate();
574
575
576 if (!this.hasDataFor(aDate)) {
577
578 return MathArrays.buildArray(date.getField(), 2);
579 }
580
581
582 return interpolate(date, aDate, entry -> entry.getDx(), entry -> entry.getDy());
583
584 }
585
586
587
588
589
590
591 public ITRFVersion getITRFVersion(final AbsoluteDate date) {
592
593
594 if (!this.hasDataFor(date)) {
595
596 return ITRFVersion.ITRF_2014;
597 }
598
599 try {
600
601 final Optional<EOPEntry> first = getNeighbors(date).findFirst();
602 return first.isPresent() ? first.get().getITRFType() : ITRFVersion.ITRF_2014;
603
604 } catch (TimeStampedCacheException tce) {
605
606 throw new OrekitInternalError(tce);
607 }
608
609 }
610
611
612
613
614 public void checkEOPContinuity(final double maxGap) {
615 TimeStamped preceding = null;
616 for (final TimeStamped current : this.cache.getAll()) {
617
618
619 if ((preceding != null) && ((current.getDate().durationFrom(preceding.getDate())) > maxGap)) {
620 throw new OrekitException(OrekitMessages.MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES,
621 preceding.getDate(), current.getDate());
622 }
623
624
625 preceding = current;
626
627 }
628 }
629
630
631
632
633
634
635
636
637
638 protected boolean hasDataFor(final AbsoluteDate date) {
639
640
641
642
643 return this.hasData && this.getStartDate().compareTo(date) <= 0 &&
644 date.compareTo(this.getEndDate()) <= 0;
645 }
646
647
648
649
650 public List<EOPEntry> getEntries() {
651 return cache.getAll();
652 }
653
654
655
656
657
658
659
660
661
662 private double interpolate(final AbsoluteDate date, final Function<EOPEntry, Double> selector) {
663 try {
664 final HermiteInterpolator interpolator = new HermiteInterpolator();
665 getNeighbors(date).forEach(entry ->
666 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
667 new double[] {
668 selector.apply(entry)
669 }));
670 return interpolator.value(0)[0];
671 } catch (TimeStampedCacheException tce) {
672
673 throw new OrekitInternalError(tce);
674 }
675 }
676
677
678
679
680
681
682
683
684
685
686
687 private <T extends RealFieldElement<T>> T interpolate(final FieldAbsoluteDate<T> date,
688 final AbsoluteDate aDate,
689 final Function<EOPEntry, Double> selector) {
690 try {
691 final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
692 final T[] y = MathArrays.buildArray(date.getField(), 1);
693 final T zero = date.getField().getZero();
694 final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero);
695
696
697 getNeighbors(aDate).forEach(entry -> {
698 y[0] = zero.add(selector.apply(entry));
699 interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y);
700 });
701 return interpolator.value(date.durationFrom(central))[0];
702 } catch (TimeStampedCacheException tce) {
703
704 throw new OrekitInternalError(tce);
705 }
706 }
707
708
709
710
711
712
713
714
715
716
717 private double[] interpolate(final AbsoluteDate date,
718 final Function<EOPEntry, Double> selector1,
719 final Function<EOPEntry, Double> selector2) {
720 try {
721 final HermiteInterpolator interpolator = new HermiteInterpolator();
722 getNeighbors(date).forEach(entry ->
723 interpolator.addSamplePoint(entry.getDate().durationFrom(date),
724 new double[] {
725 selector1.apply(entry),
726 selector2.apply(entry)
727 }));
728 return interpolator.value(0);
729 } catch (TimeStampedCacheException tce) {
730
731 throw new OrekitInternalError(tce);
732 }
733 }
734
735
736
737
738
739
740
741
742
743
744
745
746 private <T extends RealFieldElement<T>> T[] interpolate(final FieldAbsoluteDate<T> date,
747 final AbsoluteDate aDate,
748 final Function<EOPEntry, Double> selector1,
749 final Function<EOPEntry, Double> selector2) {
750 try {
751 final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
752 final T[] y = MathArrays.buildArray(date.getField(), 2);
753 final T zero = date.getField().getZero();
754 final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero);
755
756
757 getNeighbors(aDate).forEach(entry -> {
758 y[0] = zero.add(selector1.apply(entry));
759 y[1] = zero.add(selector2.apply(entry));
760 interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y);
761 });
762 return interpolator.value(date.durationFrom(central));
763 } catch (TimeStampedCacheException tce) {
764
765 throw new OrekitInternalError(tce);
766 }
767 }
768
769
770
771
772
773
774
775 @DefaultDataContext
776 private Object writeReplace() {
777 return new DataTransferObject(conventions, getEntries(), tidalCorrection == null);
778 }
779
780
781 @DefaultDataContext
782 private static class DataTransferObject implements Serializable {
783
784
785 private static final long serialVersionUID = 20131010L;
786
787
788 private final IERSConventions conventions;
789
790
791 private final List<EOPEntry> entries;
792
793
794 private final boolean simpleEOP;
795
796
797
798
799
800
801 DataTransferObject(final IERSConventions conventions,
802 final List<EOPEntry> entries,
803 final boolean simpleEOP) {
804 this.conventions = conventions;
805 this.entries = entries;
806 this.simpleEOP = simpleEOP;
807 }
808
809
810
811
812 private Object readResolve() {
813 try {
814
815 return new EOPHistory(conventions, entries, simpleEOP);
816 } catch (OrekitException oe) {
817 throw new OrekitInternalError(oe);
818 }
819 }
820
821 }
822
823
824 private static class TidalCorrectionEntry implements TimeStamped {
825
826
827 private final AbsoluteDate date;
828
829
830 private final double[] correction;
831
832
833
834
835
836 TidalCorrectionEntry(final AbsoluteDate date, final double[] correction) {
837 this.date = date;
838 this.correction = correction;
839 }
840
841
842 @Override
843 public AbsoluteDate getDate() {
844 return date;
845 }
846
847 }
848
849
850 private static class CachedCorrection
851 implements TimeVectorFunction, TimeStampedGenerator<TidalCorrectionEntry> {
852
853
854 private final TimeVectorFunction tidalCorrection;
855
856
857 private final double step;
858
859
860 private final TimeStampedCache<TidalCorrectionEntry> cache;
861
862
863
864
865 CachedCorrection(final TimeVectorFunction tidalCorrection) {
866 this.step = 60 * 60;
867 this.tidalCorrection = tidalCorrection;
868 this.cache =
869 new GenericTimeStampedCache<TidalCorrectionEntry>(8,
870 OrekitConfiguration.getCacheSlotsNumber(),
871 Constants.JULIAN_DAY * 30,
872 Constants.JULIAN_DAY,
873 this);
874 }
875
876
877 @Override
878 public double[] value(final AbsoluteDate date) {
879 try {
880
881 final HermiteInterpolator interpolator = new HermiteInterpolator();
882 cache.getNeighbors(date).forEach(entry -> interpolator.addSamplePoint(entry.date.durationFrom(date), entry.correction));
883
884
885 return interpolator.value(0.0);
886 } catch (TimeStampedCacheException tsce) {
887
888 throw new OrekitInternalError(tsce);
889 }
890 }
891
892
893 @Override
894 public <T extends RealFieldElement<T>> T[] value(final FieldAbsoluteDate<T> date) {
895 try {
896
897 final AbsoluteDate aDate = date.toAbsoluteDate();
898
899 final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
900 final T[] y = MathArrays.buildArray(date.getField(), 4);
901 final T zero = date.getField().getZero();
902 final FieldAbsoluteDate<T> central = new FieldAbsoluteDate<>(aDate, zero);
903
904
905 cache.getNeighbors(aDate).forEach(entry -> {
906 for (int i = 0; i < y.length; ++i) {
907 y[i] = zero.add(entry.correction[i]);
908 }
909 interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y);
910 });
911
912
913 return interpolator.value(date.durationFrom(central));
914
915 } catch (TimeStampedCacheException tsce) {
916
917 throw new OrekitInternalError(tsce);
918 }
919 }
920
921
922 @Override
923 public List<TidalCorrectionEntry> generate(final AbsoluteDatete">AbsoluteDate existingDate, final AbsoluteDate date) {
924
925 final List<TidalCorrectionEntry> generated = new ArrayList<TidalCorrectionEntry>();
926
927 if (existingDate == null) {
928
929
930 for (int i = -cache.getNeighborsSize() / 2; generated.size() < cache.getNeighborsSize(); ++i) {
931 final AbsoluteDate t = date.shiftedBy(i * step);
932 generated.add(new TidalCorrectionEntry(t, tidalCorrection.value(t)));
933 }
934
935 } else {
936
937
938
939
940 AbsoluteDate t = existingDate;
941 if (date.compareTo(t) > 0) {
942
943 do {
944 t = t.shiftedBy(step);
945 generated.add(new TidalCorrectionEntry(t, tidalCorrection.value(t)));
946 } while (t.compareTo(date) <= 0);
947 } else {
948
949 do {
950 t = t.shiftedBy(-step);
951 generated.add(0, new TidalCorrectionEntry(t, tidalCorrection.value(t)));
952 } while (t.compareTo(date) >= 0);
953 }
954 }
955
956
957 return generated;
958
959 }
960 }
961
962 }