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