1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.stream.Stream;
26
27 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
28 import org.hipparchus.exception.LocalizedCoreFormats;
29 import org.hipparchus.exception.MathIllegalStateException;
30 import org.hipparchus.geometry.euclidean.threed.Rotation;
31 import org.hipparchus.geometry.euclidean.threed.Vector3D;
32 import org.hipparchus.util.FastMath;
33 import org.orekit.attitudes.Attitude;
34 import org.orekit.attitudes.LofOffset;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.errors.OrekitIllegalArgumentException;
37 import org.orekit.errors.OrekitIllegalStateException;
38 import org.orekit.errors.OrekitMessages;
39 import org.orekit.frames.Frame;
40 import org.orekit.frames.LOFType;
41 import org.orekit.frames.Transform;
42 import org.orekit.orbits.Orbit;
43 import org.orekit.time.AbsoluteDate;
44 import org.orekit.time.TimeInterpolable;
45 import org.orekit.time.TimeShiftable;
46 import org.orekit.time.TimeStamped;
47 import org.orekit.utils.AbsolutePVCoordinates;
48 import org.orekit.utils.TimeStampedAngularCoordinates;
49 import org.orekit.utils.TimeStampedPVCoordinates;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public class SpacecraftState
77 implements TimeStamped, TimeShiftable<SpacecraftState>, TimeInterpolable<SpacecraftState>, Serializable {
78
79
80 private static final long serialVersionUID = 20130407L;
81
82
83 private static final double DEFAULT_MASS = 1000.0;
84
85
86
87
88
89 private static final double DATE_INCONSISTENCY_THRESHOLD = 100e-9;
90
91
92 private final Orbit orbit;
93
94
95 private final AbsolutePVCoordinates absPva;
96
97
98 private final Attitude attitude;
99
100
101 private final double mass;
102
103
104 private final Map<String, double[]> additional;
105
106
107
108
109
110 public SpacecraftState(final Orbit orbit) {
111 this(orbit,
112 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
113 DEFAULT_MASS, null);
114 }
115
116
117
118
119
120
121
122
123 public SpacecraftState(final Orbit orbit, final Attitude attitude)
124 throws IllegalArgumentException {
125 this(orbit, attitude, DEFAULT_MASS, null);
126 }
127
128
129
130
131
132
133 public SpacecraftState(final Orbit orbit, final double mass) {
134 this(orbit,
135 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
136 mass, null);
137 }
138
139
140
141
142
143
144
145
146 public SpacecraftState(final Orbit orbit, final Attitude attitude, final double mass)
147 throws IllegalArgumentException {
148 this(orbit, attitude, mass, null);
149 }
150
151
152
153
154
155
156 public SpacecraftState(final Orbit orbit, final Map<String, double[]> additional) {
157 this(orbit,
158 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
159 DEFAULT_MASS, additional);
160 }
161
162
163
164
165
166
167
168
169
170 public SpacecraftState(final Orbit orbit, final Attitude attitude, final Map<String, double[]> additional)
171 throws IllegalArgumentException {
172 this(orbit, attitude, DEFAULT_MASS, additional);
173 }
174
175
176
177
178
179
180
181 public SpacecraftState(final Orbit orbit, final double mass, final Map<String, double[]> additional) {
182 this(orbit,
183 new LofOffset(orbit.getFrame(), LOFType.VVLH).getAttitude(orbit, orbit.getDate(), orbit.getFrame()),
184 mass, additional);
185 }
186
187
188
189
190
191
192
193
194
195 public SpacecraftState(final Orbit orbit, final Attitude attitude,
196 final double mass, final Map<String, double[]> additional)
197 throws IllegalArgumentException {
198 checkConsistency(orbit, attitude);
199 this.orbit = orbit;
200 this.absPva = null;
201 this.attitude = attitude;
202 this.mass = mass;
203 if (additional == null) {
204 this.additional = Collections.emptyMap();
205 } else {
206 this.additional = new HashMap<String, double[]>(additional.size());
207 for (final Map.Entry<String, double[]> entry : additional.entrySet()) {
208 this.additional.put(entry.getKey(), entry.getValue().clone());
209 }
210 }
211 }
212
213
214
215
216
217
218
219 public SpacecraftState(final AbsolutePVCoordinates absPva) {
220 this(absPva,
221 new LofOffset(absPva.getFrame(), LOFType.VVLH).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
222 DEFAULT_MASS, null);
223 }
224
225
226
227
228
229
230
231
232 public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude)
233 throws IllegalArgumentException {
234 this(absPva, attitude, DEFAULT_MASS, null);
235 }
236
237
238
239
240
241
242 public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass) {
243 this(absPva,
244 new LofOffset(absPva.getFrame(), LOFType.VVLH).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
245 mass, null);
246 }
247
248
249
250
251
252
253
254
255 public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude, final double mass)
256 throws IllegalArgumentException {
257 this(absPva, attitude, mass, null);
258 }
259
260
261
262
263
264
265 public SpacecraftState(final AbsolutePVCoordinates absPva, final Map<String, double[]> additional) {
266 this(absPva,
267 new LofOffset(absPva.getFrame(), LOFType.VVLH).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
268 DEFAULT_MASS, additional);
269 }
270
271
272
273
274
275
276
277
278
279 public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude, final Map<String, double[]> additional)
280 throws IllegalArgumentException {
281 this(absPva, attitude, DEFAULT_MASS, additional);
282 }
283
284
285
286
287
288
289
290 public SpacecraftState(final AbsolutePVCoordinates absPva, final double mass, final Map<String, double[]> additional) {
291 this(absPva,
292 new LofOffset(absPva.getFrame(), LOFType.VVLH).getAttitude(absPva, absPva.getDate(), absPva.getFrame()),
293 mass, additional);
294 }
295
296
297
298
299
300
301
302
303
304 public SpacecraftState(final AbsolutePVCoordinates absPva, final Attitude attitude,
305 final double mass, final Map<String, double[]> additional)
306 throws IllegalArgumentException {
307 checkConsistency(absPva, attitude);
308 this.orbit = null;
309 this.absPva = absPva;
310 this.attitude = attitude;
311 this.mass = mass;
312 if (additional == null) {
313 this.additional = Collections.emptyMap();
314 } else {
315 this.additional = new HashMap<String, double[]>(additional.size());
316 for (final Map.Entry<String, double[]> entry : additional.entrySet()) {
317 this.additional.put(entry.getKey(), entry.getValue().clone());
318 }
319 }
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 public SpacecraftState addAdditionalState(final String name, final double... value) {
341 final Map<String, double[]> newMap = new HashMap<String, double[]>(additional.size() + 1);
342 newMap.putAll(additional);
343 newMap.put(name, value.clone());
344 if (absPva == null) {
345 return new SpacecraftState(orbit, attitude, mass, newMap);
346 } else {
347 return new SpacecraftState(absPva, attitude, mass, newMap);
348 }
349 }
350
351
352
353
354
355
356
357 private static void checkConsistency(final Orbit orbit, final Attitude attitude)
358 throws IllegalArgumentException {
359 if (FastMath.abs(orbit.getDate().durationFrom(attitude.getDate())) >
360 DATE_INCONSISTENCY_THRESHOLD) {
361 throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
362 orbit.getDate(), attitude.getDate());
363 }
364 if (orbit.getFrame() != attitude.getReferenceFrame()) {
365 throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
366 orbit.getFrame().getName(),
367 attitude.getReferenceFrame().getName());
368 }
369 }
370
371
372
373
374
375
376
377
378
379
380
381 public boolean isOrbitDefined() {
382 return orbit != null;
383 }
384
385
386
387
388
389
390
391 private static void checkConsistency(final AbsolutePVCoordinates absPva, final Attitude attitude)
392 throws IllegalArgumentException {
393 if (FastMath.abs(absPva.getDate().durationFrom(attitude.getDate())) >
394 DATE_INCONSISTENCY_THRESHOLD) {
395 throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_AND_ATTITUDE_DATES_MISMATCH,
396 absPva.getDate(), attitude.getDate());
397 }
398 if (absPva.getFrame() != attitude.getReferenceFrame()) {
399 throw new OrekitIllegalArgumentException(OrekitMessages.FRAMES_MISMATCH,
400 absPva.getFrame().getName(),
401 attitude.getReferenceFrame().getName());
402 }
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438 public SpacecraftState shiftedBy(final double dt) {
439 if (absPva == null) {
440 return new SpacecraftState(orbit.shiftedBy(dt), attitude.shiftedBy(dt),
441 mass, additional);
442 } else {
443 return new SpacecraftState(absPva.shiftedBy(dt), attitude.shiftedBy(dt),
444 mass, additional);
445 }
446 }
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470 public SpacecraftState interpolate(final AbsoluteDate date,
471 final Stream<SpacecraftState> sample) {
472
473
474 final List<Orbit> orbits;
475 final List<AbsolutePVCoordinates> absPvas;
476 if (isOrbitDefined()) {
477 orbits = new ArrayList<Orbit>();
478 absPvas = null;
479 } else {
480 orbits = null;
481 absPvas = new ArrayList<AbsolutePVCoordinates>();
482 }
483 final List<Attitude> attitudes = new ArrayList<Attitude>();
484 final HermiteInterpolator massInterpolator = new HermiteInterpolator();
485 final Map<String, HermiteInterpolator> additionalInterpolators =
486 new HashMap<String, HermiteInterpolator>(additional.size());
487 for (final String name : additional.keySet()) {
488 additionalInterpolators.put(name, new HermiteInterpolator());
489 }
490
491
492 sample.forEach(state -> {
493 final double deltaT = state.getDate().durationFrom(date);
494 if (isOrbitDefined()) {
495 orbits.add(state.getOrbit());
496 } else {
497 absPvas.add(state.getAbsPVA());
498 }
499 attitudes.add(state.getAttitude());
500 massInterpolator.addSamplePoint(deltaT,
501 new double[] {
502 state.getMass()
503 });
504 for (final Map.Entry<String, HermiteInterpolator> entry : additionalInterpolators.entrySet()) {
505 entry.getValue().addSamplePoint(deltaT, state.getAdditionalState(entry.getKey()));
506 }
507
508 });
509
510
511 final Orbit interpolatedOrbit;
512 final AbsolutePVCoordinates interpolatedAbsPva;
513 if (isOrbitDefined()) {
514 interpolatedOrbit = orbit.interpolate(date, orbits);
515 interpolatedAbsPva = null;
516 } else {
517 interpolatedOrbit = null;
518 interpolatedAbsPva = absPva.interpolate(date, absPvas);
519 }
520 final Attitude interpolatedAttitude = attitude.interpolate(date, attitudes);
521 final double interpolatedMass = massInterpolator.value(0)[0];
522 final Map<String, double[]> interpolatedAdditional;
523 if (additional.isEmpty()) {
524 interpolatedAdditional = null;
525 } else {
526 interpolatedAdditional = new HashMap<String, double[]>(additional.size());
527 for (final Map.Entry<String, HermiteInterpolator> entry : additionalInterpolators.entrySet()) {
528 interpolatedAdditional.put(entry.getKey(), entry.getValue().value(0));
529 }
530 }
531
532
533 if (isOrbitDefined()) {
534 return new SpacecraftState(interpolatedOrbit, interpolatedAttitude,
535 interpolatedMass, interpolatedAdditional);
536 } else {
537 return new SpacecraftState(interpolatedAbsPva, interpolatedAttitude,
538 interpolatedMass, interpolatedAdditional);
539 }
540
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555 public AbsolutePVCoordinates getAbsPVA() throws OrekitIllegalStateException {
556 if (absPva == null) {
557 throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ABSOLUTE_PVCOORDINATES);
558 }
559 return absPva;
560 }
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575 public Orbit getOrbit() throws OrekitIllegalStateException {
576 if (orbit == null) {
577 throw new OrekitIllegalStateException(OrekitMessages.UNDEFINED_ORBIT);
578 }
579 return orbit;
580 }
581
582
583
584
585 public AbsoluteDate getDate() {
586 return (absPva == null) ? orbit.getDate() : absPva.getDate();
587 }
588
589
590
591
592 public Frame getFrame() {
593 return (absPva == null) ? orbit.getFrame() : absPva.getFrame();
594 }
595
596
597
598
599
600
601
602
603 public boolean hasAdditionalState(final String name) {
604 return additional.containsKey(name);
605 }
606
607
608
609
610
611
612
613
614
615
616 public void ensureCompatibleAdditionalStates(final SpacecraftState state)
617 throws MathIllegalStateException {
618
619
620 for (final Map.Entry<String, double[]> entry : additional.entrySet()) {
621 final double[] other = state.additional.get(entry.getKey());
622 if (other == null) {
623 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
624 entry.getKey());
625 }
626 if (other.length != entry.getValue().length) {
627 throw new MathIllegalStateException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
628 other.length, entry.getValue().length);
629 }
630 }
631
632 if (state.additional.size() > additional.size()) {
633
634 for (final String name : state.additional.keySet()) {
635 if (!additional.containsKey(name)) {
636 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE,
637 name);
638 }
639 }
640 }
641
642 }
643
644
645
646
647
648
649
650
651 public double[] getAdditionalState(final String name) {
652 if (!additional.containsKey(name)) {
653 throw new OrekitException(OrekitMessages.UNKNOWN_ADDITIONAL_STATE, name);
654 }
655 return additional.get(name).clone();
656 }
657
658
659
660
661
662
663
664 public Map<String, double[]> getAdditionalStates() {
665 return Collections.unmodifiableMap(additional);
666 }
667
668
669
670
671
672
673
674 public Transform toTransform() {
675 final TimeStampedPVCoordinates pv = getPVCoordinates();
676 return new Transform(pv.getDate(),
677 new Transform(pv.getDate(), pv.negate()),
678 new Transform(pv.getDate(), attitude.getOrientation()));
679 }
680
681
682
683
684
685 public double getMu() {
686 return (absPva == null) ? orbit.getMu() : Double.NaN;
687 }
688
689
690
691
692
693
694
695
696 public double getKeplerianPeriod() {
697 return (absPva == null) ? orbit.getKeplerianPeriod() : Double.NaN;
698 }
699
700
701
702
703
704
705
706
707 public double getKeplerianMeanMotion() {
708 return (absPva == null) ? orbit.getKeplerianMeanMotion() : Double.NaN;
709 }
710
711
712
713
714
715
716 public double getA() {
717 return (absPva == null) ? orbit.getA() : Double.NaN;
718 }
719
720
721
722
723
724
725
726 public double getEquinoctialEx() {
727 return (absPva == null) ? orbit.getEquinoctialEx() : Double.NaN;
728 }
729
730
731
732
733
734
735
736 public double getEquinoctialEy() {
737 return (absPva == null) ? orbit.getEquinoctialEy() : Double.NaN;
738 }
739
740
741
742
743
744
745
746 public double getHx() {
747 return (absPva == null) ? orbit.getHx() : Double.NaN;
748 }
749
750
751
752
753
754
755
756 public double getHy() {
757 return (absPva == null) ? orbit.getHy() : Double.NaN;
758 }
759
760
761
762
763
764
765
766
767 public double getLv() {
768 return (absPva == null) ? orbit.getLv() : Double.NaN;
769 }
770
771
772
773
774
775
776
777
778 public double getLE() {
779 return (absPva == null) ? orbit.getLE() : Double.NaN;
780 }
781
782
783
784
785
786
787
788
789 public double getLM() {
790 return (absPva == null) ? orbit.getLM() : Double.NaN;
791 }
792
793
794
795
796
797
798
799
800
801
802 public double getE() {
803 return (absPva == null) ? orbit.getE() : Double.NaN;
804 }
805
806
807
808
809
810
811 public double getI() {
812 return (absPva == null) ? orbit.getI() : Double.NaN;
813 }
814
815
816
817
818
819
820
821
822
823
824
825 public TimeStampedPVCoordinates getPVCoordinates() {
826 return (absPva == null) ? orbit.getPVCoordinates() : absPva.getPVCoordinates();
827 }
828
829
830
831
832
833
834
835
836
837
838
839
840 public TimeStampedPVCoordinates getPVCoordinates(final Frame outputFrame) {
841 return (absPva == null) ? orbit.getPVCoordinates(outputFrame) : absPva.getPVCoordinates(outputFrame);
842 }
843
844
845
846
847 public Attitude getAttitude() {
848 return attitude;
849 }
850
851
852
853
854 public double getMass() {
855 return mass;
856 }
857
858
859
860
861 private Object writeReplace() {
862 return isOrbitDefined() ? new DTOO(this) : new DTOA(this);
863 }
864
865
866 private static class DTOO implements Serializable {
867
868
869 private static final long serialVersionUID = 20150916L;
870
871
872 private final Orbit orbit;
873
874
875 private double[] d;
876
877
878 private final Map<String, double[]> additional;
879
880
881
882
883 private DTOO(final SpacecraftState state) {
884
885 this.orbit = state.orbit;
886 this.additional = state.additional.isEmpty() ? null : state.additional;
887
888 final Rotation rotation = state.attitude.getRotation();
889 final Vector3D spin = state.attitude.getSpin();
890 final Vector3D rotationAcceleration = state.attitude.getRotationAcceleration();
891 this.d = new double[] {
892 rotation.getQ0(), rotation.getQ1(), rotation.getQ2(), rotation.getQ3(),
893 spin.getX(), spin.getY(), spin.getZ(),
894 rotationAcceleration.getX(), rotationAcceleration.getY(), rotationAcceleration.getZ(),
895 state.mass
896 };
897
898 }
899
900
901
902
903 private Object readResolve() {
904 return new SpacecraftState(orbit,
905 new Attitude(orbit.getFrame(),
906 new TimeStampedAngularCoordinates(orbit.getDate(),
907 new Rotation(d[0], d[1], d[2], d[3], false),
908 new Vector3D(d[4], d[5], d[6]),
909 new Vector3D(d[7], d[8], d[9]))),
910 d[10], additional);
911 }
912
913 }
914
915
916 private static class DTOA implements Serializable {
917
918
919 private static final long serialVersionUID = 20150916L;
920
921
922 private final AbsolutePVCoordinates absPva;
923
924
925 private double[] d;
926
927
928 private final Map<String, double[]> additional;
929
930
931
932
933 private DTOA(final SpacecraftState state) {
934
935 this.absPva = state.absPva;
936 this.additional = state.additional.isEmpty() ? null : state.additional;
937
938 final Rotation rotation = state.attitude.getRotation();
939 final Vector3D spin = state.attitude.getSpin();
940 final Vector3D rotationAcceleration = state.attitude.getRotationAcceleration();
941 this.d = new double[] {
942 rotation.getQ0(), rotation.getQ1(), rotation.getQ2(), rotation.getQ3(),
943 spin.getX(), spin.getY(), spin.getZ(),
944 rotationAcceleration.getX(), rotationAcceleration.getY(), rotationAcceleration.getZ(),
945 state.mass
946 };
947
948 }
949
950
951
952
953 private Object readResolve() {
954 return new SpacecraftState(absPva,
955 new Attitude(absPva.getFrame(),
956 new TimeStampedAngularCoordinates(absPva.getDate(),
957 new Rotation(d[0], d[1], d[2], d[3], false),
958 new Vector3D(d[4], d[5], d[6]),
959 new Vector3D(d[7], d[8], d[9]))),
960 d[10], additional);
961 }
962 }
963
964 @Override
965 public String toString() {
966 return "SpacecraftState{" +
967 "orbit=" + orbit +
968 ", attitude=" + attitude +
969 ", mass=" + mass +
970 ", additional=" + additional +
971 '}';
972 }
973 }