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