1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.orbits;
18
19 import java.io.Serializable;
20 import java.util.stream.Stream;
21
22 import org.hipparchus.analysis.differentiation.UnivariateDerivative2;
23 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24 import org.hipparchus.geometry.euclidean.threed.Rotation;
25 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.hipparchus.util.FastMath;
28 import org.hipparchus.util.SinCos;
29 import org.orekit.annotation.DefaultDataContext;
30 import org.orekit.data.DataContext;
31 import org.orekit.frames.Frame;
32 import org.orekit.time.AbsoluteDate;
33 import org.orekit.utils.CartesianDerivativesFilter;
34 import org.orekit.utils.FieldPVCoordinates;
35 import org.orekit.utils.PVCoordinates;
36 import org.orekit.utils.TimeStampedPVCoordinates;
37
38
39
40
41
42
43
44
45
46
47
48
49
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 public class CartesianOrbit extends Orbit {
75
76
77 private static final long serialVersionUID = 20170414L;
78
79
80 private final transient boolean hasNonKeplerianAcceleration;
81
82
83 private transient EquinoctialOrbit equinoctial;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public CartesianOrbit(final TimeStampedPVCoordinates pvaCoordinates,
100 final Frame frame, final double mu)
101 throws IllegalArgumentException {
102 super(pvaCoordinates, frame, mu);
103 hasNonKeplerianAcceleration = hasNonKeplerianAcceleration(pvaCoordinates, mu);
104 equinoctial = null;
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public CartesianOrbit(final PVCoordinates pvaCoordinates, final Frame frame,
123 final AbsoluteDate date, final double mu)
124 throws IllegalArgumentException {
125 this(new TimeStampedPVCoordinates(date, pvaCoordinates), frame, mu);
126 }
127
128
129
130
131 public CartesianOrbit(final Orbit op) {
132 super(op.getPVCoordinates(), op.getFrame(), op.getMu());
133 hasNonKeplerianAcceleration = op.hasDerivatives();
134 if (op instanceof EquinoctialOrbit) {
135 equinoctial = (EquinoctialOrbit) op;
136 } else if (op instanceof CartesianOrbit) {
137 equinoctial = ((CartesianOrbit) op).equinoctial;
138 } else {
139 equinoctial = null;
140 }
141 }
142
143
144 public OrbitType getType() {
145 return OrbitType.CARTESIAN;
146 }
147
148
149 private void initEquinoctial() {
150 if (equinoctial == null) {
151 if (hasDerivatives()) {
152
153 equinoctial = new EquinoctialOrbit(getPVCoordinates(), getFrame(), getDate(), getMu());
154 } else {
155
156
157 equinoctial = new EquinoctialOrbit(new PVCoordinates(getPVCoordinates().getPosition(),
158 getPVCoordinates().getVelocity()),
159 getFrame(), getDate(), getMu());
160 }
161 }
162 }
163
164
165
166
167
168 private FieldPVCoordinates<UnivariateDerivative2> getPVDerivatives() {
169
170 final PVCoordinates pva = getPVCoordinates();
171 final Vector3D p = pva.getPosition();
172 final Vector3D v = pva.getVelocity();
173 final Vector3D a = pva.getAcceleration();
174
175 final FieldVector3D<UnivariateDerivative2> pG = new FieldVector3D<>(new UnivariateDerivative2(p.getX(), v.getX(), a.getX()),
176 new UnivariateDerivative2(p.getY(), v.getY(), a.getY()),
177 new UnivariateDerivative2(p.getZ(), v.getZ(), a.getZ()));
178 final FieldVector3D<UnivariateDerivative2> vG = new FieldVector3D<>(new UnivariateDerivative2(v.getX(), a.getX(), 0.0),
179 new UnivariateDerivative2(v.getY(), a.getY(), 0.0),
180 new UnivariateDerivative2(v.getZ(), a.getZ(), 0.0));
181 return new FieldPVCoordinates<>(pG, vG);
182 }
183
184
185 public double getA() {
186 final double r = getPVCoordinates().getPosition().getNorm();
187 final double V2 = getPVCoordinates().getVelocity().getNormSq();
188 return r / (2 - r * V2 / getMu());
189 }
190
191
192 public double getADot() {
193 if (hasDerivatives()) {
194 final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
195 final UnivariateDerivative2 r = pv.getPosition().getNorm();
196 final UnivariateDerivative2 V2 = pv.getVelocity().getNormSq();
197 final UnivariateDerivative2 a = r.divide(r.multiply(V2).divide(getMu()).subtract(2).negate());
198 return a.getDerivative(1);
199 } else {
200 return Double.NaN;
201 }
202 }
203
204
205 public double getE() {
206 final double muA = getMu() * getA();
207 if (muA > 0) {
208
209 final Vector3D pvP = getPVCoordinates().getPosition();
210 final Vector3D pvV = getPVCoordinates().getVelocity();
211 final double rV2OnMu = pvP.getNorm() * pvV.getNormSq() / getMu();
212 final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
213 final double eCE = rV2OnMu - 1;
214 return FastMath.sqrt(eCE * eCE + eSE * eSE);
215 } else {
216
217 final Vector3D pvM = getPVCoordinates().getMomentum();
218 return FastMath.sqrt(1 - pvM.getNormSq() / muA);
219 }
220 }
221
222
223 public double getEDot() {
224 if (hasDerivatives()) {
225 final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
226 final FieldVector3D<UnivariateDerivative2> pvP = pv.getPosition();
227 final FieldVector3D<UnivariateDerivative2> pvV = pv.getVelocity();
228 final UnivariateDerivative2 r = pvP.getNorm();
229 final UnivariateDerivative2 V2 = pvV.getNormSq();
230 final UnivariateDerivative2 rV2OnMu = r.multiply(V2).divide(getMu());
231 final UnivariateDerivative2 a = r.divide(rV2OnMu.negate().add(2));
232 final UnivariateDerivative2 eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(getMu()).sqrt());
233 final UnivariateDerivative2 eCE = rV2OnMu.subtract(1);
234 final UnivariateDerivative2 e = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt();
235 return e.getDerivative(1);
236 } else {
237 return Double.NaN;
238 }
239 }
240
241
242 public double getI() {
243 return Vector3D.angle(Vector3D.PLUS_K, getPVCoordinates().getMomentum());
244 }
245
246
247 public double getIDot() {
248 if (hasDerivatives()) {
249 final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
250 final FieldVector3D<UnivariateDerivative2> momentum =
251 FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity());
252 final UnivariateDerivative2 i = FieldVector3D.angle(Vector3D.PLUS_K, momentum);
253 return i.getDerivative(1);
254 } else {
255 return Double.NaN;
256 }
257 }
258
259
260 public double getEquinoctialEx() {
261 initEquinoctial();
262 return equinoctial.getEquinoctialEx();
263 }
264
265
266 public double getEquinoctialExDot() {
267 initEquinoctial();
268 return equinoctial.getEquinoctialExDot();
269 }
270
271
272 public double getEquinoctialEy() {
273 initEquinoctial();
274 return equinoctial.getEquinoctialEy();
275 }
276
277
278 public double getEquinoctialEyDot() {
279 initEquinoctial();
280 return equinoctial.getEquinoctialEyDot();
281 }
282
283
284 public double getHx() {
285 final Vector3D w = getPVCoordinates().getMomentum().normalize();
286
287 if ((w.getX() * w.getX() + w.getY() * w.getY()) == 0 && w.getZ() < 0) {
288 return Double.NaN;
289 }
290 return -w.getY() / (1 + w.getZ());
291 }
292
293
294 public double getHxDot() {
295 if (hasDerivatives()) {
296 final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
297 final FieldVector3D<UnivariateDerivative2> w =
298 FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
299
300 final double x = w.getX().getValue();
301 final double y = w.getY().getValue();
302 final double z = w.getZ().getValue();
303 if ((x * x + y * y) == 0 && z < 0) {
304 return Double.NaN;
305 }
306 final UnivariateDerivative2 hx = w.getY().negate().divide(w.getZ().add(1));
307 return hx.getDerivative(1);
308 } else {
309 return Double.NaN;
310 }
311 }
312
313
314 public double getHy() {
315 final Vector3D w = getPVCoordinates().getMomentum().normalize();
316
317 if ((w.getX() * w.getX() + w.getY() * w.getY()) == 0 && w.getZ() < 0) {
318 return Double.NaN;
319 }
320 return w.getX() / (1 + w.getZ());
321 }
322
323
324 public double getHyDot() {
325 if (hasDerivatives()) {
326 final FieldPVCoordinates<UnivariateDerivative2> pv = getPVDerivatives();
327 final FieldVector3D<UnivariateDerivative2> w =
328 FieldVector3D.crossProduct(pv.getPosition(), pv.getVelocity()).normalize();
329
330 final double x = w.getX().getValue();
331 final double y = w.getY().getValue();
332 final double z = w.getZ().getValue();
333 if ((x * x + y * y) == 0 && z < 0) {
334 return Double.NaN;
335 }
336 final UnivariateDerivative2 hy = w.getX().divide(w.getZ().add(1));
337 return hy.getDerivative(1);
338 } else {
339 return Double.NaN;
340 }
341 }
342
343
344 public double getLv() {
345 initEquinoctial();
346 return equinoctial.getLv();
347 }
348
349
350 public double getLvDot() {
351 initEquinoctial();
352 return equinoctial.getLvDot();
353 }
354
355
356 public double getLE() {
357 initEquinoctial();
358 return equinoctial.getLE();
359 }
360
361
362 public double getLEDot() {
363 initEquinoctial();
364 return equinoctial.getLEDot();
365 }
366
367
368 public double getLM() {
369 initEquinoctial();
370 return equinoctial.getLM();
371 }
372
373
374 public double getLMDot() {
375 initEquinoctial();
376 return equinoctial.getLMDot();
377 }
378
379
380 public boolean hasDerivatives() {
381 return hasNonKeplerianAcceleration;
382 }
383
384
385 protected TimeStampedPVCoordinates initPVCoordinates() {
386
387 return getPVCoordinates();
388 }
389
390
391 public CartesianOrbit shiftedBy(final double dt) {
392 final PVCoordinates shiftedPV = (getA() < 0) ? shiftPVHyperbolic(dt) : shiftPVElliptic(dt);
393 return new CartesianOrbit(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu());
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415 public CartesianOrbit interpolate(final AbsoluteDate date, final Stream<Orbit> sample) {
416 final TimeStampedPVCoordinates interpolated =
417 TimeStampedPVCoordinates.interpolate(date, CartesianDerivativesFilter.USE_PVA,
418 sample.map(orbit -> orbit.getPVCoordinates()));
419 return new CartesianOrbit(interpolated, getFrame(), date, getMu());
420 }
421
422
423
424
425
426 private PVCoordinates shiftPVElliptic(final double dt) {
427
428 final PVCoordinates pv = getPVCoordinates();
429 final Vector3D pvP = pv.getPosition();
430 final Vector3D pvV = pv.getVelocity();
431 final Vector3D pvM = pv.getMomentum();
432 final double r2 = pvP.getNormSq();
433 final double r = FastMath.sqrt(r2);
434 final double rV2OnMu = r * pvV.getNormSq() / getMu();
435 final double a = r / (2 - rV2OnMu);
436 final double muA = getMu() * a;
437
438
439 final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA);
440 final double eCE = rV2OnMu - 1;
441 final double E0 = FastMath.atan2(eSE, eCE);
442 final double M0 = E0 - eSE;
443
444 final double e = FastMath.sqrt(eCE * eCE + eSE * eSE);
445 final double sqrt = FastMath.sqrt((1 + e) / (1 - e));
446
447
448 final double v0 = 2 * FastMath.atan(sqrt * FastMath.tan(E0 / 2));
449 final Vector3D p = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize();
450 final Vector3D q = Vector3D.crossProduct(pvM, p).normalize();
451
452
453 final double M1 = M0 + getKeplerianMeanMotion() * dt;
454 final double E1 = KeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1);
455
456
457 final SinCos scE = FastMath.sinCos(E1);
458 final double cE = scE.cos();
459 final double sE = scE.sin();
460 final double sE2m1 = FastMath.sqrt((1 - e) * (1 + e));
461
462
463 final double x = a * (cE - e);
464 final double y = a * sE2m1 * sE;
465 final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cE);
466 final double xDot = -factor * sE;
467 final double yDot = factor * sE2m1 * cE;
468
469 final Vector3D shiftedP = new Vector3D(x, p, y, q);
470 final Vector3D shiftedV = new Vector3D(xDot, p, yDot, q);
471 if (hasNonKeplerianAcceleration) {
472
473
474 final Vector3D nonKeplerianAcceleration = new Vector3D(1, getPVCoordinates().getAcceleration(),
475 getMu() / (r2 * r), pvP);
476
477
478 final Vector3D fixedP = new Vector3D(1, shiftedP,
479 0.5 * dt * dt, nonKeplerianAcceleration);
480 final double fixedR2 = fixedP.getNormSq();
481 final double fixedR = FastMath.sqrt(fixedR2);
482 final Vector3D fixedV = new Vector3D(1, shiftedV,
483 dt, nonKeplerianAcceleration);
484 final Vector3D fixedA = new Vector3D(-getMu() / (fixedR2 * fixedR), shiftedP,
485 1, nonKeplerianAcceleration);
486
487 return new PVCoordinates(fixedP, fixedV, fixedA);
488
489 } else {
490
491
492 return new PVCoordinates(shiftedP, shiftedV);
493 }
494
495 }
496
497
498
499
500
501 private PVCoordinates shiftPVHyperbolic(final double dt) {
502
503 final PVCoordinates pv = getPVCoordinates();
504 final Vector3D pvP = pv.getPosition();
505 final Vector3D pvV = pv.getVelocity();
506 final Vector3D pvM = pv.getMomentum();
507 final double r2 = pvP.getNormSq();
508 final double r = FastMath.sqrt(r2);
509 final double rV2OnMu = r * pvV.getNormSq() / getMu();
510 final double a = getA();
511 final double muA = getMu() * a;
512 final double e = FastMath.sqrt(1 - Vector3D.dotProduct(pvM, pvM) / muA);
513 final double sqrt = FastMath.sqrt((e + 1) / (e - 1));
514
515
516 final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA);
517 final double eCH = rV2OnMu - 1;
518 final double H0 = FastMath.log((eCH + eSH) / (eCH - eSH)) / 2;
519 final double M0 = e * FastMath.sinh(H0) - H0;
520
521
522 final double v0 = 2 * FastMath.atan(sqrt * FastMath.tanh(H0 / 2));
523 final Vector3D p = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize();
524 final Vector3D q = Vector3D.crossProduct(pvM, p).normalize();
525
526
527 final double M1 = M0 + getKeplerianMeanMotion() * dt;
528 final double H1 = KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1);
529
530
531 final double cH = FastMath.cosh(H1);
532 final double sH = FastMath.sinh(H1);
533 final double sE2m1 = FastMath.sqrt((e - 1) * (e + 1));
534
535
536 final double x = a * (cH - e);
537 final double y = -a * sE2m1 * sH;
538 final double factor = FastMath.sqrt(getMu() / -a) / (e * cH - 1);
539 final double xDot = -factor * sH;
540 final double yDot = factor * sE2m1 * cH;
541
542 final Vector3D shiftedP = new Vector3D(x, p, y, q);
543 final Vector3D shiftedV = new Vector3D(xDot, p, yDot, q);
544 if (hasNonKeplerianAcceleration) {
545
546
547 final Vector3D nonKeplerianAcceleration = new Vector3D(1, getPVCoordinates().getAcceleration(),
548 getMu() / (r2 * r), pvP);
549
550
551 final Vector3D fixedP = new Vector3D(1, shiftedP,
552 0.5 * dt * dt, nonKeplerianAcceleration);
553 final double fixedR2 = fixedP.getNormSq();
554 final double fixedR = FastMath.sqrt(fixedR2);
555 final Vector3D fixedV = new Vector3D(1, shiftedV,
556 dt, nonKeplerianAcceleration);
557 final Vector3D fixedA = new Vector3D(-getMu() / (fixedR2 * fixedR), shiftedP,
558 1, nonKeplerianAcceleration);
559
560 return new PVCoordinates(fixedP, fixedV, fixedA);
561
562 } else {
563
564
565 return new PVCoordinates(shiftedP, shiftedV);
566 }
567
568 }
569
570
571
572
573 private double[][] create6x6Identity() {
574
575 final double[][] identity = new double[6][6];
576 for (int i = 0; i < 6; i++) {
577 identity[i][i] = 1.0;
578 }
579 return identity;
580 }
581
582 @Override
583 protected double[][] computeJacobianMeanWrtCartesian() {
584 return create6x6Identity();
585 }
586
587 @Override
588 protected double[][] computeJacobianEccentricWrtCartesian() {
589 return create6x6Identity();
590 }
591
592 @Override
593 protected double[][] computeJacobianTrueWrtCartesian() {
594 return create6x6Identity();
595 }
596
597
598 public void addKeplerContribution(final PositionAngle type, final double gm,
599 final double[] pDot) {
600
601 final PVCoordinates pv = getPVCoordinates();
602
603
604 final Vector3D velocity = pv.getVelocity();
605 pDot[0] += velocity.getX();
606 pDot[1] += velocity.getY();
607 pDot[2] += velocity.getZ();
608
609
610 final Vector3D position = pv.getPosition();
611 final double r2 = position.getNormSq();
612 final double coeff = -gm / (r2 * FastMath.sqrt(r2));
613 pDot[3] += coeff * position.getX();
614 pDot[4] += coeff * position.getY();
615 pDot[5] += coeff * position.getZ();
616
617 }
618
619
620
621
622 public String toString() {
623
624 final String comma = ", ";
625 final PVCoordinates pv = getPVCoordinates();
626 final Vector3D p = pv.getPosition();
627 final Vector3D v = pv.getVelocity();
628 return "Cartesian parameters: {P(" +
629 p.getX() + comma +
630 p.getY() + comma +
631 p.getZ() + "), V(" +
632 v.getX() + comma +
633 v.getY() + comma +
634 v.getZ() + ")}";
635 }
636
637
638
639
640
641
642
643
644
645 @DefaultDataContext
646 private Object writeReplace() {
647 return new DTO(this);
648 }
649
650
651 @DefaultDataContext
652 private static class DTO implements Serializable {
653
654
655 private static final long serialVersionUID = 20170414L;
656
657
658 private double[] d;
659
660
661 private final Frame frame;
662
663
664
665
666 private DTO(final CartesianOrbit orbit) {
667
668 final TimeStampedPVCoordinates pv = orbit.getPVCoordinates();
669
670
671 final AbsoluteDate j2000Epoch =
672 DataContext.getDefault().getTimeScales().getJ2000Epoch();
673 final double epoch = FastMath.floor(pv.getDate().durationFrom(j2000Epoch));
674 final double offset = pv.getDate().durationFrom(j2000Epoch.shiftedBy(epoch));
675
676 if (orbit.hasDerivatives()) {
677 this.d = new double[] {
678 epoch, offset, orbit.getMu(),
679 pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(),
680 pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ(),
681 pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ()
682 };
683 } else {
684 this.d = new double[] {
685 epoch, offset, orbit.getMu(),
686 pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(),
687 pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ()
688 };
689 }
690
691 this.frame = orbit.getFrame();
692
693 }
694
695
696
697
698 private Object readResolve() {
699 final AbsoluteDate j2000Epoch =
700 DataContext.getDefault().getTimeScales().getJ2000Epoch();
701 if (d.length >= 12) {
702
703 return new CartesianOrbit(new TimeStampedPVCoordinates(j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
704 new Vector3D(d[3], d[ 4], d[ 5]),
705 new Vector3D(d[6], d[ 7], d[ 8]),
706 new Vector3D(d[9], d[10], d[11])),
707 frame, d[2]);
708 } else {
709
710 return new CartesianOrbit(new TimeStampedPVCoordinates(j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]),
711 new Vector3D(d[3], d[ 4], d[ 5]),
712 new Vector3D(d[6], d[ 7], d[ 8])),
713 frame, d[2]);
714 }
715 }
716
717 }
718
719 }