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.Serializable;
20
21 import org.hipparchus.RealFieldElement;
22 import org.hipparchus.analysis.differentiation.DerivativeStructure;
23 import org.hipparchus.geometry.euclidean.threed.FieldLine;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Line;
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.hipparchus.geometry.euclidean.twod.Vector2D;
28 import org.hipparchus.util.FastMath;
29 import org.hipparchus.util.MathArrays;
30 import org.orekit.frames.FieldTransform;
31 import org.orekit.frames.Frame;
32 import org.orekit.frames.Transform;
33 import org.orekit.time.AbsoluteDate;
34 import org.orekit.time.FieldAbsoluteDate;
35 import org.orekit.utils.PVCoordinates;
36 import org.orekit.utils.TimeStampedPVCoordinates;
37
38
39
40
41
42
43
44
45
46
47
48 public class OneAxisEllipsoid extends Ellipsoid implements BodyShape {
49
50
51 private static final long serialVersionUID = 20130518L;
52
53
54 private static final double ANGULAR_THRESHOLD = 1.0e-4;
55
56
57 private final Frame bodyFrame;
58
59
60 private final double ae2;
61
62
63 private final double ap2;
64
65
66 private final double f;
67
68
69 private final double e2;
70
71
72 private final double g;
73
74
75 private final double g2;
76
77
78 private double angularThreshold;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public OneAxisEllipsoid(final double ae, final double f,
98 final Frame bodyFrame) {
99 super(bodyFrame, ae, ae, ae * (1.0 - f));
100 this.f = f;
101 this.ae2 = ae * ae;
102 this.e2 = f * (2.0 - f);
103 this.g = 1.0 - f;
104 this.g2 = g * g;
105 this.ap2 = ae2 * g2;
106 setAngularThreshold(1.0e-12);
107 this.bodyFrame = bodyFrame;
108 }
109
110
111
112
113
114
115
116
117
118
119 public void setAngularThreshold(final double angularThreshold) {
120 this.angularThreshold = angularThreshold;
121 }
122
123
124
125
126 public double getEquatorialRadius() {
127 return getA();
128 }
129
130
131
132
133 public double getFlattening() {
134 return f;
135 }
136
137
138 public Frame getBodyFrame() {
139 return bodyFrame;
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public Vector3D getCartesianIntersectionPoint(final Line line, final Vector3D close,
157 final Frame frame, final AbsoluteDate date) {
158
159
160 final Transform frameToBodyFrame = frame.getTransformTo(bodyFrame, date);
161 final Line lineInBodyFrame = frameToBodyFrame.transformLine(line);
162
163
164 final Vector3D point = lineInBodyFrame.getOrigin();
165 final double x = point.getX();
166 final double y = point.getY();
167 final double z = point.getZ();
168 final double z2 = z * z;
169 final double r2 = x * x + y * y;
170
171 final Vector3D direction = lineInBodyFrame.getDirection();
172 final double dx = direction.getX();
173 final double dy = direction.getY();
174 final double dz = direction.getZ();
175 final double cz2 = dx * dx + dy * dy;
176
177
178
179 final double a = 1.0 - e2 * cz2;
180 final double b = -(g2 * (x * dx + y * dy) + z * dz);
181 final double c = g2 * (r2 - ae2) + z2;
182 final double b2 = b * b;
183 final double ac = a * c;
184 if (b2 < ac) {
185 return null;
186 }
187 final double s = FastMath.sqrt(b2 - ac);
188 final double k1 = (b < 0) ? (b - s) / a : c / (b + s);
189 final double k2 = c / (a * k1);
190
191
192 final Vector3D closeInBodyFrame = frameToBodyFrame.transformPosition(close);
193 final double closeAbscissa = lineInBodyFrame.getAbscissa(closeInBodyFrame);
194 final double k =
195 (FastMath.abs(k1 - closeAbscissa) < FastMath.abs(k2 - closeAbscissa)) ? k1 : k2;
196 return lineInBodyFrame.pointAt(k);
197
198 }
199
200
201 public GeodeticPoint getIntersectionPoint(final Line line, final Vector3D close,
202 final Frame frame, final AbsoluteDate date) {
203
204 final Vector3D intersection = getCartesianIntersectionPoint(line, close, frame, date);
205 if (intersection == null) {
206 return null;
207 }
208 final double ix = intersection.getX();
209 final double iy = intersection.getY();
210 final double iz = intersection.getZ();
211
212 final double lambda = FastMath.atan2(iy, ix);
213 final double phi = FastMath.atan2(iz, g2 * FastMath.sqrt(ix * ix + iy * iy));
214 return new GeodeticPoint(phi, lambda, 0.0);
215
216 }
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 public <T extends RealFieldElement<T>> FieldVector3D<T> getCartesianIntersectionPoint(final FieldLine<T> line,
234 final FieldVector3D<T> close,
235 final Frame frame,
236 final FieldAbsoluteDate<T> date) {
237
238
239 final FieldTransform<T> frameToBodyFrame = frame.getTransformTo(bodyFrame, date);
240 final FieldLine<T> lineInBodyFrame = frameToBodyFrame.transformLine(line);
241
242
243 final FieldVector3D<T> point = lineInBodyFrame.getOrigin();
244 final T x = point.getX();
245 final T y = point.getY();
246 final T z = point.getZ();
247 final T z2 = z.multiply(z);
248 final T r2 = x.multiply(x).add(y.multiply(y));
249
250 final FieldVector3D<T> direction = lineInBodyFrame.getDirection();
251 final T dx = direction.getX();
252 final T dy = direction.getY();
253 final T dz = direction.getZ();
254 final T cz2 = dx.multiply(dx).add(dy.multiply(dy));
255
256
257
258 final T a = cz2.multiply(e2).subtract(1.0).negate();
259 final T b = x.multiply(dx).add(y.multiply(dy)).multiply(g2).add(z.multiply(dz)).negate();
260 final T c = r2.subtract(ae2).multiply(g2).add(z2);
261 final T b2 = b.multiply(b);
262 final T ac = a.multiply(c);
263 if (b2.getReal() < ac.getReal()) {
264 return null;
265 }
266 final T s = b2.subtract(ac).sqrt();
267 final T k1 = (b.getReal() < 0) ? b.subtract(s).divide(a) : c.divide(b.add(s));
268 final T k2 = c.divide(a.multiply(k1));
269
270
271 final FieldVector3D<T> closeInBodyFrame = frameToBodyFrame.transformPosition(close);
272 final T closeAbscissa = lineInBodyFrame.getAbscissa(closeInBodyFrame);
273 final T k = (FastMath.abs(k1.getReal() - closeAbscissa.getReal()) < FastMath.abs(k2.getReal() - closeAbscissa.getReal())) ?
274 k1 : k2;
275 return lineInBodyFrame.pointAt(k);
276 }
277
278
279 public <T extends RealFieldElement<T>> FieldGeodeticPoint<T> getIntersectionPoint(final FieldLine<T> line,
280 final FieldVector3D<T> close,
281 final Frame frame,
282 final FieldAbsoluteDate<T> date) {
283
284 final FieldVector3D<T> intersection = getCartesianIntersectionPoint(line, close, frame, date);
285 if (intersection == null) {
286 return null;
287 }
288 final T ix = intersection.getX();
289 final T iy = intersection.getY();
290 final T iz = intersection.getZ();
291
292 final T lambda = iy.atan2(ix);
293 final T phi = iz.atan2(ix.multiply(ix).add(iy.multiply(iy)).sqrt().multiply(g2));
294 return new FieldGeodeticPoint<>(phi, lambda, phi.getField().getZero());
295
296 }
297
298
299 public Vector3D transform(final GeodeticPoint point) {
300 final double longitude = point.getLongitude();
301 final double cLambda = FastMath.cos(longitude);
302 final double sLambda = FastMath.sin(longitude);
303 final double latitude = point.getLatitude();
304 final double cPhi = FastMath.cos(latitude);
305 final double sPhi = FastMath.sin(latitude);
306 final double h = point.getAltitude();
307 final double n = getA() / FastMath.sqrt(1.0 - e2 * sPhi * sPhi);
308 final double r = (n + h) * cPhi;
309 return new Vector3D(r * cLambda, r * sLambda, (g2 * n + h) * sPhi);
310 }
311
312
313 public <T extends RealFieldElement<T>> FieldVector3D<T> transform(final FieldGeodeticPoint<T> point) {
314
315 final T latitude = point.getLatitude();
316 final T longitude = point.getLongitude();
317 final T altitude = point.getAltitude();
318
319 final T cLambda = longitude.cos();
320 final T sLambda = longitude.sin();
321 final T cPhi = latitude.cos();
322 final T sPhi = latitude.sin();
323 final T n = sPhi.multiply(sPhi).multiply(e2).subtract(1.0).negate().sqrt().reciprocal().multiply(getA());
324 final T r = n.add(altitude).multiply(cPhi);
325
326 return new FieldVector3D<>(r.multiply(cLambda),
327 r.multiply(sLambda),
328 sPhi.multiply(altitude.add(n.multiply(g2))));
329 }
330
331
332 public Vector3D projectToGround(final Vector3D point, final AbsoluteDate date, final Frame frame) {
333
334
335 final Transform toBody = frame.getTransformTo(bodyFrame, date);
336 final Vector3D p = toBody.transformPosition(point);
337 final double z = p.getZ();
338 final double r = FastMath.hypot(p.getX(), p.getY());
339
340
341 final Ellipseipse">Ellipse meridian = new Ellipse(Vector3D.ZERO,
342 new Vector3D(p.getX() / r, p.getY() / r, 0),
343 Vector3D.PLUS_K,
344 getA(), getC(), bodyFrame);
345
346
347 final Vector3D groundPoint = meridian.toSpace(meridian.projectToEllipse(new Vector2D(r, z)));
348
349
350 return toBody.getInverse().transformPosition(groundPoint);
351
352 }
353
354
355 public TimeStampedPVCoordinatesPVCoordinates">TimeStampedPVCoordinates projectToGround(final TimeStampedPVCoordinates pv, final Frame frame) {
356
357
358 final Transform toBody = frame.getTransformTo(bodyFrame, pv.getDate());
359 final TimeStampedPVCoordinates pvInBodyFrame = toBody.transformPVCoordinates(pv);
360 final Vector3D p = pvInBodyFrame.getPosition();
361 final double r = FastMath.hypot(p.getX(), p.getY());
362
363
364 final Vector3D meridian = new Vector3D(p.getX() / r, p.getY() / r, 0);
365 final Ellipse firstPrincipalCurvature =
366 new Ellipse(Vector3D.ZERO, meridian, Vector3D.PLUS_K, getA(), getC(), bodyFrame);
367
368
369 final TimeStampedPVCoordinates gpFirst = firstPrincipalCurvature.projectToEllipse(pvInBodyFrame);
370 final Vector3D gpP = gpFirst.getPosition();
371 final double gr = MathArrays.linearCombination(gpP.getX(), meridian.getX(),
372 gpP.getY(), meridian.getY());
373 final double gz = gpP.getZ();
374
375
376 final Vector3D east = new Vector3D(-meridian.getY(), meridian.getX(), 0);
377 final Vector3D zenith = new Vector3D(gr * getC() / getA(), meridian, gz * getA() / getC(), Vector3D.PLUS_K).normalize();
378 final Vector3D north = Vector3D.crossProduct(zenith, east);
379
380
381 final Ellipse secondPrincipalCurvature = getPlaneSection(gpP, north);
382 final TimeStampedPVCoordinates gpSecond = secondPrincipalCurvature.projectToEllipse(pvInBodyFrame);
383
384 final Vector3D gpV = gpFirst.getVelocity().add(gpSecond.getVelocity());
385 final Vector3D gpA = gpFirst.getAcceleration().add(gpSecond.getAcceleration());
386
387
388 final TimeStampedPVCoordinates groundPV =
389 new TimeStampedPVCoordinates(pv.getDate(), gpP, gpV, gpA);
390
391
392 return toBody.getInverse().transformPVCoordinates(groundPV);
393
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412 public GeodeticPoint transform(final Vector3D point, final Frame frame, final AbsoluteDate date) {
413
414
415 final Vector3D pointInBodyFrame = frame.getTransformTo(bodyFrame, date).transformPosition(point);
416 final double r2 = pointInBodyFrame.getX() * pointInBodyFrame.getX() +
417 pointInBodyFrame.getY() * pointInBodyFrame.getY();
418 final double r = FastMath.sqrt(r2);
419 final double z = pointInBodyFrame.getZ();
420
421 final double lambda = FastMath.atan2(pointInBodyFrame.getY(), pointInBodyFrame.getX());
422
423 double h;
424 double phi;
425 if (r <= ANGULAR_THRESHOLD * FastMath.abs(z)) {
426
427
428 final double osculatingRadius = ae2 / getC();
429 final double evoluteCuspZ = FastMath.copySign(getA() * e2 / g, -z);
430 final double deltaZ = z - evoluteCuspZ;
431
432 phi = FastMath.copySign(0.5 * FastMath.PI - FastMath.atan(r / FastMath.abs(deltaZ)), deltaZ);
433 h = FastMath.hypot(deltaZ, r) - osculatingRadius;
434 } else if (FastMath.abs(z) <= ANGULAR_THRESHOLD * r) {
435
436
437 final double osculatingRadius = ap2 / getA();
438 final double evoluteCuspR = getA() * e2;
439 final double deltaR = r - evoluteCuspR;
440 if (deltaR >= 0) {
441
442
443 phi = (deltaR == 0) ? 0.0 : FastMath.atan(z / deltaR);
444 h = FastMath.hypot(deltaR, z) - osculatingRadius;
445 } else {
446
447
448 final double rClose = r / e2;
449 final double zClose = FastMath.copySign(g * FastMath.sqrt(ae2 - rClose * rClose), z);
450 phi = FastMath.atan((zClose - z) / (rClose - r));
451 h = -FastMath.hypot(r - rClose, z - zClose);
452 }
453
454 } else {
455
456 final double epsPhi = 1.0e-15;
457 final double epsH = 1.0e-14 * FastMath.max(getA(), FastMath.sqrt(r2 + z * z));
458 final double c = getA() * e2;
459 final double absZ = FastMath.abs(z);
460 final double zc = g * absZ;
461 double sn = absZ;
462 double sn2 = sn * sn;
463 double cn = g * r;
464 double cn2 = cn * cn;
465 double an2 = cn2 + sn2;
466 double an = FastMath.sqrt(an2);
467 double bn = 0;
468 phi = Double.POSITIVE_INFINITY;
469 h = Double.POSITIVE_INFINITY;
470 for (int i = 0; i < 10; ++i) {
471 final double oldSn = sn;
472 final double oldCn = cn;
473 final double oldPhi = phi;
474 final double oldH = h;
475 final double an3 = an2 * an;
476 final double csncn = c * sn * cn;
477 bn = 1.5 * csncn * ((r * sn - zc * cn) * an - csncn);
478 sn = (zc * an3 + c * sn2 * sn) * an3 - bn * sn;
479 cn = (r * an3 - c * cn2 * cn) * an3 - bn * cn;
480 if (sn * oldSn < 0 || cn < 0) {
481
482 while (sn * oldSn < 0 || cn < 0) {
483 sn = (sn + oldSn) / 2;
484 cn = (cn + oldCn) / 2;
485 }
486 } else {
487
488
489 final int exp = (FastMath.getExponent(sn) + FastMath.getExponent(cn)) / 2;
490 sn = FastMath.scalb(sn, -exp);
491 cn = FastMath.scalb(cn, -exp);
492
493 sn2 = sn * sn;
494 cn2 = cn * cn;
495 an2 = cn2 + sn2;
496 an = FastMath.sqrt(an2);
497
498 final double cc = g * cn;
499 h = (r * cc + absZ * sn - getA() * g * an) / FastMath.sqrt(an2 - e2 * cn2);
500 if (FastMath.abs(oldH - h) < epsH) {
501 phi = FastMath.copySign(FastMath.atan(sn / cc), z);
502 if (FastMath.abs(oldPhi - phi) < epsPhi) {
503 break;
504 }
505 }
506
507 }
508
509 }
510 }
511
512 return new GeodeticPoint(phi, lambda, h);
513
514 }
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532 public <T extends RealFieldElement<T>> FieldGeodeticPoint<T> transform(final FieldVector3D<T> point,
533 final Frame frame,
534 final FieldAbsoluteDate<T> date) {
535
536
537 final FieldVector3D<T> pointInBodyFrame = frame.getTransformTo(bodyFrame, date).transformPosition(point);
538 final T r2 = pointInBodyFrame.getX().multiply(pointInBodyFrame.getX()).
539 add(pointInBodyFrame.getY().multiply(pointInBodyFrame.getY()));
540 final T r = r2.sqrt();
541 final T z = pointInBodyFrame.getZ();
542
543 final T lambda = pointInBodyFrame.getY().atan2(pointInBodyFrame.getX());
544
545 T h;
546 T phi;
547 if (r.getReal() <= ANGULAR_THRESHOLD * FastMath.abs(z.getReal())) {
548
549
550 final double osculatingRadius = ae2 / getC();
551 final double evoluteCuspZ = FastMath.copySign(getA() * e2 / g, -z.getReal());
552 final T deltaZ = z.subtract(evoluteCuspZ);
553
554 phi = r.divide(deltaZ.abs()).atan().negate().add(0.5 * FastMath.PI).copySign(deltaZ);
555 h = deltaZ.hypot(r).subtract(osculatingRadius);
556 } else if (FastMath.abs(z.getReal()) <= ANGULAR_THRESHOLD * r.getReal()) {
557
558
559 final double osculatingRadius = ap2 / getA();
560 final double evoluteCuspR = getA() * e2;
561 final T deltaR = r.subtract(evoluteCuspR);
562 if (deltaR.getReal() >= 0) {
563
564
565 phi = (deltaR.getReal() == 0) ? z.getField().getZero() : z.divide(deltaR).atan();
566 h = deltaR.hypot(z).subtract(osculatingRadius);
567 } else {
568
569
570 final T rClose = r.divide(e2);
571 final T zClose = rClose.multiply(rClose).negate().add(ae2).sqrt().multiply(g).copySign(z);
572 phi = zClose.subtract(z).divide(rClose.subtract(r)).atan();
573 h = r.subtract(rClose).hypot(z.subtract(zClose)).negate();
574 }
575
576 } else {
577
578 final double epsPhi = 1.0e-15;
579 final double epsH = 1.0e-14 * getA();
580 final double c = getA() * e2;
581 final T absZ = z.abs();
582 final T zc = absZ.multiply(g);
583 T sn = absZ;
584 T sn2 = sn.multiply(sn);
585 T cn = r.multiply(g);
586 T cn2 = cn.multiply(cn);
587 T an2 = cn2.add(sn2);
588 T an = an2.sqrt();
589 T bn = an.getField().getZero();
590 phi = an.getField().getZero().add(Double.POSITIVE_INFINITY);
591 h = an.getField().getZero().add(Double.POSITIVE_INFINITY);
592 for (int i = 0; i < 10; ++i) {
593 final T oldSn = sn;
594 final T oldCn = cn;
595 final T oldPhi = phi;
596 final T oldH = h;
597 final T an3 = an2.multiply(an);
598 final T csncn = sn.multiply(cn).multiply(c);
599 bn = csncn.multiply(1.5).multiply((r.multiply(sn).subtract(zc.multiply(cn))).multiply(an).subtract(csncn));
600 sn = zc.multiply(an3).add(sn2.multiply(sn).multiply(c)).multiply(an3).subtract(bn.multiply(sn));
601 cn = r.multiply(an3).subtract(cn2.multiply(cn).multiply(c)).multiply(an3).subtract(bn.multiply(cn));
602 if (sn.getReal() * oldSn.getReal() < 0 || cn.getReal() < 0) {
603
604 while (sn.getReal() * oldSn.getReal() < 0 || cn.getReal() < 0) {
605 sn = sn.add(oldSn).multiply(0.5);
606 cn = cn.add(oldCn).multiply(0.5);
607 }
608 } else {
609
610
611 final int exp = (FastMath.getExponent(sn.getReal()) + FastMath.getExponent(cn.getReal())) / 2;
612 sn = sn.scalb(-exp);
613 cn = cn.scalb(-exp);
614
615 sn2 = sn.multiply(sn);
616 cn2 = cn.multiply(cn);
617 an2 = cn2.add(sn2);
618 an = an2.sqrt();
619
620 final T cc = cn.multiply(g);
621 h = r.multiply(cc).add(absZ.multiply(sn)).subtract(an.multiply(getA() * g)).divide(an2.subtract(cn2.multiply(e2)).sqrt());
622 if (FastMath.abs(oldH.getReal() - h.getReal()) < epsH) {
623 phi = sn.divide(cc).atan().copySign(z);
624 if (FastMath.abs(oldPhi.getReal() - phi.getReal()) < epsPhi) {
625 break;
626 }
627 }
628
629 }
630
631 }
632 }
633
634 return new FieldGeodeticPoint<>(phi, lambda, h);
635
636 }
637
638
639
640
641
642
643
644
645 public FieldGeodeticPoint<DerivativeStructure> transform(final PVCoordinates point,
646 final Frame frame, final AbsoluteDate date) {
647
648
649 final Transform toBody = frame.getTransformTo(bodyFrame, date);
650 final PVCoordinates pointInBodyFrame = toBody.transformPVCoordinates(point);
651 final FieldVector3D<DerivativeStructure> p = pointInBodyFrame.toDerivativeStructureVector(2);
652 final DerivativeStructure pr2 = p.getX().multiply(p.getX()).add(p.getY().multiply(p.getY()));
653 final DerivativeStructure pr = pr2.sqrt();
654 final DerivativeStructure pz = p.getZ();
655
656
657 final TimeStampedPVCoordinatess">TimeStampedPVCoordinates groundPoint = projectToGround(new TimeStampedPVCoordinates(date, pointInBodyFrame),
658 bodyFrame);
659 final FieldVector3D<DerivativeStructure> gp = groundPoint.toDerivativeStructureVector(2);
660 final DerivativeStructure gpr2 = gp.getX().multiply(gp.getX()).add(gp.getY().multiply(gp.getY()));
661 final DerivativeStructure gpr = gpr2.sqrt();
662 final DerivativeStructure gpz = gp.getZ();
663
664
665 final DerivativeStructure dr = pr.subtract(gpr);
666 final DerivativeStructure dz = pz.subtract(gpz);
667 final double insideIfNegative = g2 * (pr2.getReal() - ae2) + pz.getReal() * pz.getReal();
668
669 return new FieldGeodeticPoint<>(DerivativeStructure.atan2(gpz, gpr.multiply(g2)),
670 DerivativeStructure.atan2(p.getY(), p.getX()),
671 DerivativeStructure.hypot(dr, dz).copySign(insideIfNegative));
672 }
673
674
675
676
677
678
679
680
681 private Object writeReplace() {
682 return new DataTransferObject(getA(), f, bodyFrame, angularThreshold);
683 }
684
685
686 private static class DataTransferObject implements Serializable {
687
688
689 private static final long serialVersionUID = 20130518L;
690
691
692 private final double ae;
693
694
695 private final double f;
696
697
698 private final Frame bodyFrame;
699
700
701 private final double angularThreshold;
702
703
704
705
706
707
708
709 DataTransferObject(final double ae, final double f,
710 final Frame bodyFrame, final double angularThreshold) {
711 this.ae = ae;
712 this.f = f;
713 this.bodyFrame = bodyFrame;
714 this.angularThreshold = angularThreshold;
715 }
716
717
718
719
720
721 private Object readResolve() {
722 final OneAxisEllipsoidxisEllipsoid">OneAxisEllipsoid ellipsoid = new OneAxisEllipsoid(ae, f, bodyFrame);
723 ellipsoid.setAngularThreshold(angularThreshold);
724 return ellipsoid;
725 }
726
727 }
728
729 }