1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.bodies;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.charset.StandardCharsets;
22 import java.text.ParseException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.SortedSet;
28 import java.util.TreeSet;
29 import java.util.concurrent.atomic.AtomicReference;
30
31 import org.hipparchus.RealFieldElement;
32 import org.hipparchus.util.FastMath;
33 import org.orekit.annotation.DefaultDataContext;
34 import org.orekit.data.AbstractSelfFeedingLoader;
35 import org.orekit.data.DataContext;
36 import org.orekit.data.DataLoader;
37 import org.orekit.data.DataProvidersManager;
38 import org.orekit.errors.OrekitException;
39 import org.orekit.errors.OrekitInternalError;
40 import org.orekit.errors.OrekitMessages;
41 import org.orekit.errors.TimeStampedCacheException;
42 import org.orekit.frames.Frame;
43 import org.orekit.frames.Predefined;
44 import org.orekit.time.AbsoluteDate;
45 import org.orekit.time.ChronologicalComparator;
46 import org.orekit.time.DateComponents;
47 import org.orekit.time.FieldAbsoluteDate;
48 import org.orekit.time.TimeComponents;
49 import org.orekit.time.TimeScale;
50 import org.orekit.time.TimeScales;
51 import org.orekit.utils.Constants;
52 import org.orekit.utils.FieldPVCoordinates;
53 import org.orekit.utils.OrekitConfiguration;
54 import org.orekit.utils.PVCoordinates;
55 import org.orekit.utils.GenericTimeStampedCache;
56 import org.orekit.utils.TimeStampedGenerator;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public class JPLEphemeridesLoader extends AbstractSelfFeedingLoader
77 implements CelestialBodyLoader {
78
79
80 public static final String DEFAULT_DE_SUPPORTED_NAMES = "^[lu]nx([mp](\\d\\d\\d\\d))+\\.(?:4\\d\\d)$";
81
82
83 public static final String DEFAULT_INPOP_SUPPORTED_NAMES = "^inpop.*\\.dat$";
84
85
86 private static final double FIFTY_DAYS = 50 * Constants.JULIAN_DAY;
87
88
89 private static final int INPOP_DE_NUMBER = 100;
90
91
92 private static final int CONSTANTS_MAX_NUMBER = 400;
93
94
95 private static final int HEADER_EPHEMERIS_TYPE_OFFSET = 2840;
96
97
98 private static final int HEADER_RECORD_SIZE_OFFSET = 2856;
99
100
101 private static final int HEADER_START_EPOCH_OFFSET = 2652;
102
103
104 private static final int HEADER_END_EPOCH_OFFSET = 2660;
105
106
107 private static final int HEADER_ASTRONOMICAL_UNIT_OFFSET = 2680;
108
109
110 private static final int HEADER_EM_RATIO_OFFSET = 2688;
111
112
113 private static final int HEADER_CHEBISHEV_INDICES_OFFSET = 2696;
114
115
116 private static final int HEADER_LIBRATION_INDICES_OFFSET = 2844;
117
118
119 private static final int HEADER_CHUNK_DURATION_OFFSET = 2668;
120
121
122 private static final int HEADER_CONSTANTS_NAMES_OFFSET = 252;
123
124
125 private static final int HEADER_CONSTANTS_VALUES_OFFSET = 0;
126
127
128 private static final int DATA_START_RANGE_OFFSET = 0;
129
130
131 private static final int DATE_END_RANGE_OFFSET = 8;
132
133
134 private static final String CONSTANT_AU = "AU";
135
136
137 private static final String CONSTANT_EMRAT = "EMRAT";
138
139
140 public enum EphemerisType {
141
142
143 SOLAR_SYSTEM_BARYCENTER,
144
145
146 SUN,
147
148
149 MERCURY,
150
151
152 VENUS,
153
154
155 EARTH_MOON,
156
157
158 EARTH,
159
160
161 MOON,
162
163
164 MARS,
165
166
167 JUPITER,
168
169
170 SATURN,
171
172
173 URANUS,
174
175
176 NEPTUNE,
177
178
179 PLUTO
180
181 }
182
183
184 public interface RawPVProvider {
185
186
187
188
189
190 PVCoordinates getRawPV(AbsoluteDate date);
191
192
193
194
195
196
197 <T extends RealFieldElement<T>> FieldPVCoordinates<T> getRawPV(FieldAbsoluteDate<T> date);
198
199 }
200
201
202 private final GenericTimeStampedCache<PosVelChebyshev> ephemerides;
203
204
205 private final AtomicReference<Map<String, Double>> constants;
206
207
208 private final EphemerisType generateType;
209
210
211 private final EphemerisType loadType;
212
213
214 private final TimeScales timeScales;
215
216
217 private final Frame gcrf;
218
219
220 private AbsoluteDate startEpoch;
221
222
223 private AbsoluteDate finalEpoch;
224
225
226 private double maxChunksDuration;
227
228
229 private double chunksDuration;
230
231
232 private int firstIndex;
233
234
235 private int coeffs;
236
237
238 private int chunks;
239
240
241 private int components;
242
243
244 private double positionUnit;
245
246
247 private TimeScale timeScale;
248
249
250 private boolean bigEndian;
251
252
253
254
255
256
257
258
259
260 @DefaultDataContext
261 public JPLEphemeridesLoader(final String supportedNames, final EphemerisType generateType) {
262 this(supportedNames, generateType,
263 DataContext.getDefault().getDataProvidersManager(),
264 DataContext.getDefault().getTimeScales(),
265 DataContext.getDefault().getFrames().getGCRF());
266 }
267
268
269
270
271
272
273
274
275
276 public JPLEphemeridesLoader(final String supportedNames,
277 final EphemerisType generateType,
278 final DataProvidersManager dataProvidersManager,
279 final TimeScales timeScales,
280 final Frame gcrf) {
281 super(supportedNames, dataProvidersManager);
282
283 this.timeScales = timeScales;
284 this.gcrf = gcrf;
285 constants = new AtomicReference<>();
286
287 this.generateType = generateType;
288 if (generateType == EphemerisType.SOLAR_SYSTEM_BARYCENTER) {
289 loadType = EphemerisType.EARTH_MOON;
290 } else if (generateType == EphemerisType.EARTH_MOON) {
291 loadType = EphemerisType.MOON;
292 } else {
293 loadType = generateType;
294 }
295
296 ephemerides = new GenericTimeStampedCache<>(
297 2, OrekitConfiguration.getCacheSlotsNumber(),
298 Double.POSITIVE_INFINITY, FIFTY_DAYS,
299 new EphemerisParser());
300 maxChunksDuration = Double.NaN;
301 chunksDuration = Double.NaN;
302
303 }
304
305
306
307
308
309 public CelestialBody loadCelestialBody(final String name) {
310
311 final double gm = getLoadedGravitationalCoefficient(generateType);
312 final IAUPole iauPole = PredefinedIAUPoles
313 .getIAUPole(generateType, timeScales);
314 final double scale;
315 final Frame definingFrameAlignedWithICRF;
316 final RawPVProvider rawPVProvider;
317 String inertialFrameName = null;
318 String bodyOrientedFrameName = null;
319 switch (generateType) {
320 case SOLAR_SYSTEM_BARYCENTER : {
321 scale = -1.0;
322 final JPLEphemeridesLoadermeridesLoader">JPLEphemeridesLoader parentLoader = new JPLEphemeridesLoader(
323 getSupportedNames(),
324 EphemerisType.EARTH_MOON,
325 getDataProvidersManager(),
326 timeScales,
327 gcrf);
328 final CelestialBody parentBody =
329 parentLoader.loadCelestialBody(CelestialBodyFactory.EARTH_MOON);
330 definingFrameAlignedWithICRF = parentBody.getInertiallyOrientedFrame();
331 rawPVProvider = new EphemerisRawPVProvider();
332 inertialFrameName = Predefined.ICRF.getName();
333 bodyOrientedFrameName = null;
334 break;
335 }
336 case EARTH_MOON :
337 scale = 1.0 / (1.0 + getLoadedEarthMoonMassRatio());
338 definingFrameAlignedWithICRF = gcrf;
339 rawPVProvider = new EphemerisRawPVProvider();
340 break;
341 case EARTH :
342 scale = 1.0;
343 definingFrameAlignedWithICRF = gcrf;
344 rawPVProvider = new ZeroRawPVProvider();
345 break;
346 case MOON :
347 scale = 1.0;
348 definingFrameAlignedWithICRF = gcrf;
349 rawPVProvider = new EphemerisRawPVProvider();
350 break;
351 default : {
352 scale = 1.0;
353 final JPLEphemeridesLoadermeridesLoader">JPLEphemeridesLoader parentLoader = new JPLEphemeridesLoader(
354 getSupportedNames(),
355 EphemerisType.SOLAR_SYSTEM_BARYCENTER,
356 getDataProvidersManager(),
357 timeScales,
358 gcrf);
359 final CelestialBody parentBody =
360 parentLoader.loadCelestialBody(CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER);
361 definingFrameAlignedWithICRF = parentBody.getInertiallyOrientedFrame();
362 rawPVProvider = new EphemerisRawPVProvider();
363 }
364 }
365
366
367 return new JPLCelestialBody(name, getSupportedNames(), generateType, rawPVProvider,
368 gm, scale, iauPole, definingFrameAlignedWithICRF,
369 inertialFrameName, bodyOrientedFrameName);
370
371 }
372
373
374
375
376 public double getLoadedAstronomicalUnit() {
377 return 1000.0 * getLoadedConstant(CONSTANT_AU);
378 }
379
380
381
382
383 public double getLoadedEarthMoonMassRatio() {
384 return getLoadedConstant(CONSTANT_EMRAT);
385 }
386
387
388
389
390
391 public double getLoadedGravitationalCoefficient(final EphemerisType body) {
392
393
394 final double rawGM;
395 switch (body) {
396 case SOLAR_SYSTEM_BARYCENTER :
397 return getLoadedGravitationalCoefficient(EphemerisType.SUN) +
398 getLoadedGravitationalCoefficient(EphemerisType.MERCURY) +
399 getLoadedGravitationalCoefficient(EphemerisType.VENUS) +
400 getLoadedGravitationalCoefficient(EphemerisType.EARTH_MOON) +
401 getLoadedGravitationalCoefficient(EphemerisType.MARS) +
402 getLoadedGravitationalCoefficient(EphemerisType.JUPITER) +
403 getLoadedGravitationalCoefficient(EphemerisType.SATURN) +
404 getLoadedGravitationalCoefficient(EphemerisType.URANUS) +
405 getLoadedGravitationalCoefficient(EphemerisType.NEPTUNE) +
406 getLoadedGravitationalCoefficient(EphemerisType.PLUTO);
407 case SUN :
408 rawGM = getLoadedConstant("GMS", "GM_Sun");
409 break;
410 case MERCURY :
411 rawGM = getLoadedConstant("GM1", "GM_Mer");
412 break;
413 case VENUS :
414 rawGM = getLoadedConstant("GM2", "GM_Ven");
415 break;
416 case EARTH_MOON :
417 rawGM = getLoadedConstant("GMB", "GM_EMB");
418 break;
419 case EARTH :
420 return getLoadedEarthMoonMassRatio() *
421 getLoadedGravitationalCoefficient(EphemerisType.MOON);
422 case MOON :
423 return getLoadedGravitationalCoefficient(EphemerisType.EARTH_MOON) /
424 (1.0 + getLoadedEarthMoonMassRatio());
425 case MARS :
426 rawGM = getLoadedConstant("GM4", "GM_Mar");
427 break;
428 case JUPITER :
429 rawGM = getLoadedConstant("GM5", "GM_Jup");
430 break;
431 case SATURN :
432 rawGM = getLoadedConstant("GM6", "GM_Sat");
433 break;
434 case URANUS :
435 rawGM = getLoadedConstant("GM7", "GM_Ura");
436 break;
437 case NEPTUNE :
438 rawGM = getLoadedConstant("GM8", "GM_Nep");
439 break;
440 case PLUTO :
441 rawGM = getLoadedConstant("GM9", "GM_Plu");
442 break;
443 default :
444 throw new OrekitInternalError(null);
445 }
446
447 final double au = getLoadedAstronomicalUnit();
448 return rawGM * au * au * au / (Constants.JULIAN_DAY * Constants.JULIAN_DAY);
449
450 }
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 public double getLoadedConstant(final String... names) {
466
467
468 Map<String, Double> map = constants.get();
469 if (map == null) {
470 final ConstantsParser parser = new ConstantsParser();
471 if (!feed(parser)) {
472 throw new OrekitException(OrekitMessages.NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND);
473 }
474 map = parser.getConstants();
475 constants.compareAndSet(null, map);
476 }
477
478 for (final String name : names) {
479 if (map.containsKey(name)) {
480 return map.get(name);
481 }
482 }
483
484 return Double.NaN;
485
486 }
487
488
489
490
491 public double getMaxChunksDuration() {
492 return maxChunksDuration;
493 }
494
495
496
497
498
499 private void parseFirstHeaderRecord(final byte[] record, final String name) {
500
501
502 final int deNum = extractInt(record, HEADER_EPHEMERIS_TYPE_OFFSET);
503
504
505
506
507 components = 3;
508 positionUnit = 1000.0;
509 timeScale = timeScales.getTDB();
510
511 if (deNum == INPOP_DE_NUMBER) {
512
513 final double format = getLoadedConstant("FORMAT");
514 if (!Double.isNaN(format) && (int) FastMath.IEEEremainder(format, 10) != 1) {
515 components = 6;
516 }
517
518
519 final double unite = getLoadedConstant("UNITE");
520 if (!Double.isNaN(unite) && (int) unite == 0) {
521 positionUnit = getLoadedAstronomicalUnit();
522 }
523
524
525 final double timesc = getLoadedConstant("TIMESC");
526 if (!Double.isNaN(timesc) && (int) timesc == 1) {
527 timeScale = timeScales.getTCB();
528 }
529
530 }
531
532
533 startEpoch = extractDate(record, HEADER_START_EPOCH_OFFSET);
534 finalEpoch = extractDate(record, HEADER_END_EPOCH_OFFSET);
535 boolean ok = finalEpoch.compareTo(startEpoch) > 0;
536
537
538 for (int i = 0; i < 12; ++i) {
539 final int row1 = extractInt(record, HEADER_CHEBISHEV_INDICES_OFFSET + 12 * i);
540 final int row2 = extractInt(record, HEADER_CHEBISHEV_INDICES_OFFSET + 4 + 12 * i);
541 final int row3 = extractInt(record, HEADER_CHEBISHEV_INDICES_OFFSET + 8 + 12 * i);
542 ok = ok && (row1 >= 0) && (row2 >= 0) && (row3 >= 0);
543 if (((i == 0) && (loadType == EphemerisType.MERCURY)) ||
544 ((i == 1) && (loadType == EphemerisType.VENUS)) ||
545 ((i == 2) && (loadType == EphemerisType.EARTH_MOON)) ||
546 ((i == 3) && (loadType == EphemerisType.MARS)) ||
547 ((i == 4) && (loadType == EphemerisType.JUPITER)) ||
548 ((i == 5) && (loadType == EphemerisType.SATURN)) ||
549 ((i == 6) && (loadType == EphemerisType.URANUS)) ||
550 ((i == 7) && (loadType == EphemerisType.NEPTUNE)) ||
551 ((i == 8) && (loadType == EphemerisType.PLUTO)) ||
552 ((i == 9) && (loadType == EphemerisType.MOON)) ||
553 ((i == 10) && (loadType == EphemerisType.SUN))) {
554 firstIndex = row1;
555 coeffs = row2;
556 chunks = row3;
557 }
558 }
559
560
561 final double timeSpan = extractDouble(record, HEADER_CHUNK_DURATION_OFFSET);
562 ok = ok && (timeSpan > 0) && (timeSpan < 100);
563 chunksDuration = Constants.JULIAN_DAY * (timeSpan / chunks);
564 if (Double.isNaN(maxChunksDuration)) {
565 maxChunksDuration = chunksDuration;
566 } else {
567 maxChunksDuration = FastMath.max(maxChunksDuration, chunksDuration);
568 }
569
570
571 if (!ok) {
572 throw new OrekitException(OrekitMessages.NOT_A_JPL_EPHEMERIDES_BINARY_FILE, name);
573 }
574
575 }
576
577
578
579
580
581
582
583 private byte[] readFirstRecord(final InputStream input, final String name)
584 throws IOException {
585
586
587 final byte[] firstPart = new byte[HEADER_RECORD_SIZE_OFFSET + 4];
588 if (!readInRecord(input, firstPart, 0)) {
589 throw new OrekitException(OrekitMessages.UNABLE_TO_READ_JPL_HEADER, name);
590 }
591
592
593 detectEndianess(firstPart);
594
595
596 final int deNum = extractInt(firstPart, HEADER_EPHEMERIS_TYPE_OFFSET);
597
598
599 final int recordSize;
600
601 if (deNum == INPOP_DE_NUMBER) {
602
603 recordSize = extractInt(firstPart, HEADER_RECORD_SIZE_OFFSET) << 3;
604 } else {
605
606 recordSize = computeRecordSize(firstPart, name);
607 }
608
609 if (recordSize <= 0) {
610 throw new OrekitException(OrekitMessages.UNABLE_TO_READ_JPL_HEADER, name);
611 }
612
613
614 final int start = firstPart.length;
615 final byte[] record = new byte[recordSize];
616 System.arraycopy(firstPart, 0, record, 0, firstPart.length);
617 if (!readInRecord(input, record, start)) {
618 throw new OrekitException(OrekitMessages.UNABLE_TO_READ_JPL_HEADER, name);
619 }
620
621 return record;
622
623 }
624
625
626
627
628
629
630 private Map<String, Double> parseConstants(final byte[] first, final byte[] second) {
631
632 final Map<String, Double> map = new HashMap<>();
633
634 for (int i = 0; i < CONSTANTS_MAX_NUMBER; ++i) {
635
636
637 final String constantName = extractString(first, HEADER_CONSTANTS_NAMES_OFFSET + i * 6, 6);
638 if (constantName.length() == 0) {
639
640 break;
641 }
642 final double constantValue = extractDouble(second, HEADER_CONSTANTS_VALUES_OFFSET + 8 * i);
643 map.put(constantName, constantValue);
644 }
645
646
647
648 if (!map.containsKey(CONSTANT_AU)) {
649 map.put(CONSTANT_AU, extractDouble(first, HEADER_ASTRONOMICAL_UNIT_OFFSET));
650 }
651
652 if (!map.containsKey(CONSTANT_EMRAT)) {
653 map.put(CONSTANT_EMRAT, extractDouble(first, HEADER_EM_RATIO_OFFSET));
654 }
655
656 return map;
657
658 }
659
660
661
662
663
664
665
666
667 private boolean readInRecord(final InputStream input, final byte[] record, final int start)
668 throws IOException {
669 int index = start;
670 while (index != record.length) {
671 final int n = input.read(record, index, record.length - index);
672 if (n < 0) {
673 return false;
674 }
675 index += n;
676 }
677 return true;
678 }
679
680
681
682
683
684 private void detectEndianess(final byte[] record) {
685
686
687 bigEndian = true;
688
689
690
691 final long deNum = extractInt(record, HEADER_EPHEMERIS_TYPE_OFFSET) & 0xffffffffL;
692
693
694
695 if (deNum > (1 << 15)) {
696 bigEndian = false;
697 }
698
699 }
700
701
702
703
704
705
706 private int computeRecordSize(final byte[] record, final String name) {
707
708 int recordSize = 0;
709 boolean ok = true;
710
711 final int nComp = 3;
712
713
714
715 for (int j = 0; j < 12; j++) {
716 final int nCompCur = (j == 11) ? 2 : nComp;
717
718
719 final int idx = HEADER_CHEBISHEV_INDICES_OFFSET + j * nComp * 4;
720 final int coeffPtr1 = extractInt(record, idx + 4);
721 final int coeffPtr2 = extractInt(record, idx + 8);
722
723
724 ok = ok && (coeffPtr1 >= 0 || coeffPtr2 >= 0);
725
726 recordSize += coeffPtr1 * coeffPtr2 * nCompCur;
727 }
728
729
730
731 final int libratPtr1 = extractInt(record, HEADER_LIBRATION_INDICES_OFFSET + 4);
732 final int libratPtr2 = extractInt(record, HEADER_LIBRATION_INDICES_OFFSET + 8);
733
734
735 ok = ok && (libratPtr1 >= 0 || libratPtr2 >= 0);
736
737 recordSize += libratPtr1 * libratPtr2 * nComp + 2;
738 recordSize <<= 3;
739
740 if (!ok || recordSize <= 0) {
741 throw new OrekitException(OrekitMessages.NOT_A_JPL_EPHEMERIDES_BINARY_FILE, name);
742 }
743
744 return recordSize;
745
746 }
747
748
749
750
751
752
753 private AbsoluteDate extractDate(final byte[] record, final int offset) {
754
755 final double t = extractDouble(record, offset);
756 int jDay = (int) FastMath.floor(t);
757 double seconds = (t + 0.5 - jDay) * Constants.JULIAN_DAY;
758 if (seconds >= Constants.JULIAN_DAY) {
759 ++jDay;
760 seconds -= Constants.JULIAN_DAY;
761 }
762 return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jDay),
763 new TimeComponents(seconds), timeScale);
764 }
765
766
767
768
769
770
771
772
773 private double extractDouble(final byte[] record, final int offset) {
774 final long l8 = ((long) record[offset + 0]) & 0xffl;
775 final long l7 = ((long) record[offset + 1]) & 0xffl;
776 final long l6 = ((long) record[offset + 2]) & 0xffl;
777 final long l5 = ((long) record[offset + 3]) & 0xffl;
778 final long l4 = ((long) record[offset + 4]) & 0xffl;
779 final long l3 = ((long) record[offset + 5]) & 0xffl;
780 final long l2 = ((long) record[offset + 6]) & 0xffl;
781 final long l1 = ((long) record[offset + 7]) & 0xffl;
782 final long l;
783 if (bigEndian) {
784 l = (l8 << 56) | (l7 << 48) | (l6 << 40) | (l5 << 32) |
785 (l4 << 24) | (l3 << 16) | (l2 << 8) | l1;
786 } else {
787 l = (l1 << 56) | (l2 << 48) | (l3 << 40) | (l4 << 32) |
788 (l5 << 24) | (l6 << 16) | (l7 << 8) | l8;
789 }
790 return Double.longBitsToDouble(l);
791 }
792
793
794
795
796
797
798 private int extractInt(final byte[] record, final int offset) {
799 final int l4 = ((int) record[offset + 0]) & 0xff;
800 final int l3 = ((int) record[offset + 1]) & 0xff;
801 final int l2 = ((int) record[offset + 2]) & 0xff;
802 final int l1 = ((int) record[offset + 3]) & 0xff;
803
804 if (bigEndian) {
805 return (l4 << 24) | (l3 << 16) | (l2 << 8) | l1;
806 } else {
807 return (l1 << 24) | (l2 << 16) | (l3 << 8) | l4;
808 }
809 }
810
811
812
813
814
815
816
817 private String extractString(final byte[] record, final int offset, final int length) {
818 return new String(record, offset, length, StandardCharsets.US_ASCII).trim();
819 }
820
821
822 private class ConstantsParser implements DataLoader {
823
824
825 private Map<String, Double> localConstants;
826
827
828
829
830 public Map<String, Double> getConstants() {
831 return localConstants;
832 }
833
834
835 public boolean stillAcceptsData() {
836 return localConstants == null;
837 }
838
839
840 public void loadData(final InputStream input, final String name)
841 throws IOException, ParseException, OrekitException {
842
843
844 final byte[] first = readFirstRecord(input, name);
845
846
847 final byte[] second = new byte[first.length];
848 if (!readInRecord(input, second, 0)) {
849 throw new OrekitException(OrekitMessages.UNABLE_TO_READ_JPL_HEADER, name);
850 }
851
852 localConstants = parseConstants(first, second);
853
854 }
855
856 }
857
858
859 private class EphemerisParser implements DataLoader, TimeStampedGenerator<PosVelChebyshev> {
860
861
862 private final SortedSet<PosVelChebyshev> entries;
863
864
865 private AbsoluteDate start;
866
867
868 private AbsoluteDate end;
869
870
871
872 EphemerisParser() {
873 entries = new TreeSet<>(new ChronologicalComparator());
874 }
875
876
877 public List<PosVelChebyshev> generate(final AbsoluteDatete">AbsoluteDate existingDate, final AbsoluteDate date) {
878 try {
879
880
881 entries.clear();
882 if (existingDate == null) {
883
884 start = date.shiftedBy(-FIFTY_DAYS);
885 end = date.shiftedBy(+FIFTY_DAYS);
886 } else if (existingDate.compareTo(date) <= 0) {
887
888 start = existingDate;
889 end = date;
890 } else {
891
892 start = date;
893 end = existingDate;
894 }
895
896
897 if (!feed(this)) {
898 throw new OrekitException(OrekitMessages.NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND);
899 }
900
901 return new ArrayList<>(entries);
902
903 } catch (OrekitException oe) {
904 throw new TimeStampedCacheException(oe);
905 }
906 }
907
908
909 public boolean stillAcceptsData() {
910
911
912 if (generateType == EphemerisType.EARTH) {
913 return false;
914 }
915
916
917
918 if (entries.isEmpty()) {
919 return true;
920 } else {
921
922 return !(entries.first().getDate().compareTo(start) < 0 &&
923 entries.last().getDate().compareTo(end) > 0);
924 }
925
926 }
927
928
929 public void loadData(final InputStream input, final String name)
930 throws IOException {
931
932
933 final byte[] first = readFirstRecord(input, name);
934
935
936 final byte[] second = new byte[first.length];
937 if (!readInRecord(input, second, 0)) {
938 throw new OrekitException(OrekitMessages.UNABLE_TO_READ_JPL_HEADER, name);
939 }
940
941 if (constants.get() == null) {
942 constants.compareAndSet(null, parseConstants(first, second));
943 }
944
945
946 final double au = 1000 * extractDouble(first, HEADER_ASTRONOMICAL_UNIT_OFFSET);
947 if ((au < 1.4e11) || (au > 1.6e11)) {
948 throw new OrekitException(OrekitMessages.NOT_A_JPL_EPHEMERIDES_BINARY_FILE, name);
949 }
950 if (FastMath.abs(getLoadedAstronomicalUnit() - au) >= 10.0) {
951 throw new OrekitException(OrekitMessages.INCONSISTENT_ASTRONOMICAL_UNIT_IN_FILES,
952 getLoadedAstronomicalUnit(), au);
953 }
954
955
956 final double emRat = extractDouble(first, HEADER_EM_RATIO_OFFSET);
957 if ((emRat < 80) || (emRat > 82)) {
958 throw new OrekitException(OrekitMessages.NOT_A_JPL_EPHEMERIDES_BINARY_FILE, name);
959 }
960 if (FastMath.abs(getLoadedEarthMoonMassRatio() - emRat) >= 1.0e-5) {
961 throw new OrekitException(OrekitMessages.INCONSISTENT_EARTH_MOON_RATIO_IN_FILES,
962 getLoadedEarthMoonMassRatio(), emRat);
963 }
964
965
966 parseFirstHeaderRecord(first, name);
967
968 if (startEpoch.compareTo(end) < 0 && finalEpoch.compareTo(start) > 0) {
969
970 final byte[] record = new byte[first.length];
971 while (readInRecord(input, record, 0)) {
972 final AbsoluteDate rangeStart = parseDataRecord(record);
973 if (rangeStart.compareTo(end) > 0) {
974
975
976 return;
977 }
978 }
979 }
980
981 }
982
983
984
985
986
987 private AbsoluteDate parseDataRecord(final byte[] record) {
988
989
990 final AbsoluteDate rangeStart = extractDate(record, DATA_START_RANGE_OFFSET);
991 if (rangeStart.compareTo(startEpoch) < 0) {
992 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE, rangeStart, startEpoch, finalEpoch);
993 }
994
995 final AbsoluteDate rangeEnd = extractDate(record, DATE_END_RANGE_OFFSET);
996 if (rangeEnd.compareTo(finalEpoch) > 0) {
997 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE, rangeEnd, startEpoch, finalEpoch);
998 }
999
1000 if (rangeStart.compareTo(end) > 0 || rangeEnd.compareTo(start) < 0) {
1001
1002 return rangeEnd;
1003 }
1004
1005
1006 AbsoluteDate chunkEnd = rangeStart;
1007 final int nbChunks = chunks;
1008 final int nbCoeffs = coeffs;
1009 final int first = firstIndex;
1010 final double duration = chunksDuration;
1011 for (int i = 0; i < nbChunks; ++i) {
1012
1013
1014 final AbsoluteDate chunkStart = chunkEnd;
1015 chunkEnd = (i == nbChunks - 1) ? rangeEnd : rangeStart.shiftedBy((i + 1) * duration);
1016
1017
1018
1019 final double[] xCoeffs = new double[nbCoeffs];
1020 final double[] yCoeffs = new double[nbCoeffs];
1021 final double[] zCoeffs = new double[nbCoeffs];
1022
1023 for (int k = 0; k < nbCoeffs; ++k) {
1024
1025
1026 final int index = first + components * i * nbCoeffs + k - 1;
1027 xCoeffs[k] = positionUnit * extractDouble(record, 8 * index);
1028 yCoeffs[k] = positionUnit * extractDouble(record, 8 * (index + nbCoeffs));
1029 zCoeffs[k] = positionUnit * extractDouble(record, 8 * (index + 2 * nbCoeffs));
1030 }
1031
1032
1033 entries.add(new PosVelChebyshev(chunkStart, timeScale, duration, xCoeffs, yCoeffs, zCoeffs));
1034
1035 }
1036
1037 return rangeStart;
1038
1039 }
1040
1041 }
1042
1043
1044 private class EphemerisRawPVProvider implements RawPVProvider {
1045
1046
1047 public PVCoordinates getRawPV(final AbsoluteDate date) {
1048
1049
1050 PosVelChebyshev chebyshev;
1051 try {
1052 chebyshev = ephemerides.getNeighbors(date).findFirst().get();
1053 } catch (TimeStampedCacheException tce) {
1054
1055 chebyshev = ephemerides.getLatest();
1056 if (!chebyshev.inRange(date)) {
1057
1058 throw tce;
1059 }
1060 }
1061
1062
1063 return chebyshev.getPositionVelocityAcceleration(date);
1064
1065 }
1066
1067
1068 public <T extends RealFieldElement<T>> FieldPVCoordinates<T> getRawPV(final FieldAbsoluteDate<T> date) {
1069
1070
1071 PosVelChebyshev chebyshev;
1072 try {
1073 chebyshev = ephemerides.getNeighbors(date.toAbsoluteDate()).findFirst().get();
1074 } catch (TimeStampedCacheException tce) {
1075
1076 chebyshev = ephemerides.getLatest();
1077 if (!chebyshev.inRange(date.toAbsoluteDate())) {
1078
1079 throw tce;
1080 }
1081 }
1082
1083
1084 return chebyshev.getPositionVelocityAcceleration(date);
1085
1086 }
1087
1088 }
1089
1090
1091 private static class ZeroRawPVProvider implements RawPVProvider {
1092
1093
1094 public PVCoordinates getRawPV(final AbsoluteDate date) {
1095 return PVCoordinates.ZERO;
1096 }
1097
1098
1099 public <T extends RealFieldElement<T>> FieldPVCoordinates<T> getRawPV(final FieldAbsoluteDate<T> date) {
1100 return FieldPVCoordinates.getZero(date.getField());
1101 }
1102
1103 }
1104
1105 }