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