1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.models.earth;
18
19 import org.hipparchus.Field;
20 import org.hipparchus.RealFieldElement;
21 import org.hipparchus.analysis.RealFieldUnivariateFunction;
22 import org.hipparchus.analysis.UnivariateFunction;
23 import org.hipparchus.analysis.solvers.AllowedSolution;
24 import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
25 import org.hipparchus.analysis.solvers.FieldBracketingNthOrderBrentSolver;
26 import org.hipparchus.analysis.solvers.UnivariateSolver;
27 import org.hipparchus.exception.MathRuntimeException;
28 import org.hipparchus.geometry.euclidean.threed.FieldLine;
29 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
30 import org.hipparchus.geometry.euclidean.threed.Line;
31 import org.hipparchus.geometry.euclidean.threed.Vector3D;
32 import org.hipparchus.util.FastMath;
33 import org.orekit.bodies.FieldGeodeticPoint;
34 import org.orekit.bodies.GeodeticPoint;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
37 import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
38 import org.orekit.forces.gravity.potential.TideSystem;
39 import org.orekit.frames.FieldTransform;
40 import org.orekit.frames.Frame;
41 import org.orekit.frames.Transform;
42 import org.orekit.time.AbsoluteDate;
43 import org.orekit.time.FieldAbsoluteDate;
44 import org.orekit.utils.TimeStampedPVCoordinates;
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
76
77
78
79
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
108
109
110
111
112
113
114
115
116
117 public class Geoid implements EarthShape {
118
119
120
121
122 private static final long serialVersionUID = 20150312L;
123
124
125
126
127
128 private static final double MAX_UNDULATION = 100;
129
130
131
132
133 private static final double MIN_UNDULATION = -150;
134
135
136
137
138 private static final int MAX_EVALUATIONS = 100;
139
140
141
142
143
144 private final AbsoluteDate defaultDate;
145
146
147
148 private final ReferenceEllipsoid referenceEllipsoid;
149
150
151
152
153 private final transient HolmesFeatherstoneAttractionModel harmonics;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 public Geoid(final NormalizedSphericalHarmonicsProvider geopotential,
171 final ReferenceEllipsoid referenceEllipsoid) {
172
173 if (geopotential == null || referenceEllipsoid == null) {
174 throw new NullPointerException();
175 }
176
177
178 final SubtractEllipsoid potential = new SubtractEllipsoid(geopotential,
179 referenceEllipsoid);
180
181
182 this.referenceEllipsoid = referenceEllipsoid;
183 this.harmonics = new HolmesFeatherstoneAttractionModel(
184 referenceEllipsoid.getBodyFrame(), potential);
185 this.defaultDate = geopotential.getReferenceDate();
186 }
187
188 @Override
189 public Frame getBodyFrame() {
190
191 return this.getEllipsoid().getBodyFrame();
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 public double getUndulation(final double geodeticLatitude,
216 final double longitude,
217 final AbsoluteDate date) {
218
219
220
221
222
223 final ReferenceEllipsoid ellipsoid = this.getEllipsoid();
224
225
226 final GeodeticPoint.html#GeodeticPoint">GeodeticPoint gp = new GeodeticPoint(geodeticLatitude, longitude, 0);
227
228
229 final Vector3D position = ellipsoid.transform(gp);
230
231
232 final double normalGravity = ellipsoid
233 .getNormalGravity(geodeticLatitude);
234
235
236 final double mu = this.harmonics.getMu();
237 final double T = this.harmonics.nonCentralPart(date, position, mu);
238
239 return T / normalGravity;
240 }
241
242 @Override
243 public ReferenceEllipsoid getEllipsoid() {
244 return this.referenceEllipsoid;
245 }
246
247
248
249
250
251
252
253 private static final class SubtractEllipsoid implements
254 NormalizedSphericalHarmonicsProvider {
255
256
257
258
259 private final NormalizedSphericalHarmonicsProvider provider;
260
261
262
263 private final ReferenceEllipsoid ellipsoid;
264
265
266
267
268
269
270
271 private SubtractEllipsoid(
272 final NormalizedSphericalHarmonicsProvider provider,
273 final ReferenceEllipsoid ellipsoid) {
274 super();
275 this.provider = provider;
276 this.ellipsoid = ellipsoid;
277 }
278
279 @Override
280 public int getMaxDegree() {
281 return this.provider.getMaxDegree();
282 }
283
284 @Override
285 public int getMaxOrder() {
286 return this.provider.getMaxOrder();
287 }
288
289 @Override
290 public double getMu() {
291 return this.provider.getMu();
292 }
293
294 @Override
295 public double getAe() {
296 return this.provider.getAe();
297 }
298
299 @Override
300 public AbsoluteDate getReferenceDate() {
301 return this.provider.getReferenceDate();
302 }
303
304 @Override
305 public double getOffset(final AbsoluteDate date) {
306 return this.provider.getOffset(date);
307 }
308
309 @Override
310 public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) {
311 return new NormalizedSphericalHarmonics() {
312
313
314 private final NormalizedSphericalHarmonics delegate = provider.onDate(date);
315
316 @Override
317 public double getNormalizedCnm(final int n, final int m) {
318 return getCorrectedCnm(n, m, this.delegate.getNormalizedCnm(n, m));
319 }
320
321 @Override
322 public double getNormalizedSnm(final int n, final int m) {
323 return this.delegate.getNormalizedSnm(n, m);
324 }
325
326 @Override
327 public AbsoluteDate getDate() {
328 return date;
329 }
330 };
331 }
332
333
334
335
336
337
338
339
340
341 private double getCorrectedCnm(final int n,
342 final int m,
343 final double uncorrectedCnm) {
344 double Cnm = uncorrectedCnm;
345
346 if (m == 0 && n <= 10 && n % 2 == 0 && n > 0) {
347
348 final double gmRatio = this.ellipsoid.getGM() / this.getMu();
349 final double aRatio = this.ellipsoid.getEquatorialRadius() /
350 this.getAe();
351
352
353
354
355
356 final int halfN = n / 2;
357 Cnm = Cnm - gmRatio * FastMath.pow(aRatio, halfN) *
358 this.ellipsoid.getC2n0(halfN);
359 }
360
361 return Cnm;
362 }
363
364 @Override
365 public TideSystem getTideSystem() {
366 return this.provider.getTideSystem();
367 }
368
369 }
370
371
372
373
374
375
376
377 @Override
378 public GeodeticPoint getIntersectionPoint(final Line lineInFrame,
379 final Vector3D closeInFrame,
380 final Frame frame,
381 final AbsoluteDate date) {
382
383
384
385
386
387 final Frame bodyFrame = this.getBodyFrame();
388 final Transform frameToBody = frame.getTransformTo(bodyFrame, date);
389 final Vector3D close = frameToBody.transformPosition(closeInFrame);
390 final Line lineInBodyFrame = frameToBody.transformLine(lineInFrame);
391
392
393 final Line line;
394 if (lineInBodyFrame.getAbscissa(close) < 0) {
395 line = lineInBodyFrame.revert();
396 } else {
397 line = lineInBodyFrame;
398 }
399
400 final ReferenceEllipsoid ellipsoid = this.getEllipsoid();
401
402
403 final double d2 = line.pointAt(0.0).getNormSq();
404
405 final double n = ellipsoid.getPolarRadius() + MIN_UNDULATION;
406 final double minAbscissa2 = n * n - d2;
407
408
409 final double lowPoint = FastMath.sqrt(FastMath.max(minAbscissa2, 0.0));
410
411 final double x = ellipsoid.getEquatorialRadius() + MAX_UNDULATION;
412 final double maxAbscissa2 = x * x - d2;
413
414 final double highPoint = FastMath.sqrt(maxAbscissa2);
415
416
417 final UnivariateFunction heightFunction = new UnivariateFunction() {
418 @Override
419 public double value(final double x) {
420 try {
421 final GeodeticPoint geodetic =
422 transform(line.pointAt(x), bodyFrame, date);
423 return geodetic.getAltitude();
424 } catch (OrekitException e) {
425
426 throw new RuntimeException(e);
427 }
428 }
429 };
430
431
432 if (maxAbscissa2 < 0) {
433
434 return null;
435 }
436
437 final UnivariateSolver solver = new BracketingNthOrderBrentSolver();
438 try {
439 final double abscissa = solver.solve(MAX_EVALUATIONS, heightFunction, lowPoint, highPoint);
440
441 return this.transform(line.pointAt(abscissa), bodyFrame, date);
442 } catch (MathRuntimeException e) {
443
444 return null;
445 }
446 }
447
448 @Override
449 public Vector3D projectToGround(final Vector3D point,
450 final AbsoluteDate date,
451 final Frame frame) {
452 final GeodeticPoint gp = this.transform(point, frame, date);
453 final GeodeticPoint gpZero =
454 new GeodeticPoint(gp.getLatitude(), gp.getLongitude(), 0);
455 final Transform bodyToFrame = this.getBodyFrame().getTransformTo(frame, date);
456 return bodyToFrame.transformPosition(this.transform(gpZero));
457 }
458
459
460
461
462
463
464
465 @Override
466 public <T extends RealFieldElement<T>> FieldGeodeticPoint<T> getIntersectionPoint(final FieldLine<T> lineInFrame,
467 final FieldVector3D<T> closeInFrame,
468 final Frame frame,
469 final FieldAbsoluteDate<T> date) {
470
471 final Field<T> field = date.getField();
472
473
474
475
476
477 final Frame bodyFrame = this.getBodyFrame();
478 final FieldTransform<T> frameToBody = frame.getTransformTo(bodyFrame, date);
479 final FieldVector3D<T> close = frameToBody.transformPosition(closeInFrame);
480 final FieldLine<T> lineInBodyFrame = frameToBody.transformLine(lineInFrame);
481
482
483 final FieldLine<T> line;
484 if (lineInBodyFrame.getAbscissa(close).getReal() < 0) {
485 line = lineInBodyFrame.revert();
486 } else {
487 line = lineInBodyFrame;
488 }
489
490 final ReferenceEllipsoid ellipsoid = this.getEllipsoid();
491
492
493 final T d2 = line.pointAt(0.0).getNormSq();
494
495 final double n = ellipsoid.getPolarRadius() + MIN_UNDULATION;
496 final T minAbscissa2 = d2.negate().add(n * n);
497
498
499 final T lowPoint = minAbscissa2.getReal() < 0 ? field.getZero() : minAbscissa2.sqrt();
500
501 final double x = ellipsoid.getEquatorialRadius() + MAX_UNDULATION;
502 final T maxAbscissa2 = d2.negate().add(x * x);
503
504 final T highPoint = maxAbscissa2.sqrt();
505
506
507 final RealFieldUnivariateFunction<T> heightFunction = z -> {
508 try {
509 final FieldGeodeticPoint<T> geodetic =
510 transform(line.pointAt(z), bodyFrame, date);
511 return geodetic.getAltitude();
512 } catch (OrekitException e) {
513
514 throw new RuntimeException(e);
515 }
516 };
517
518
519 if (maxAbscissa2.getReal() < 0) {
520
521 return null;
522 }
523
524 final FieldBracketingNthOrderBrentSolver<T> solver =
525 new FieldBracketingNthOrderBrentSolver<>(field.getZero().add(1.0e-14),
526 field.getZero().add(1.0e-6),
527 field.getZero().add(1.0e-15),
528 5);
529 try {
530 final T abscissa = solver.solve(MAX_EVALUATIONS, heightFunction, lowPoint, highPoint,
531 AllowedSolution.ANY_SIDE);
532
533 return this.transform(line.pointAt(abscissa), bodyFrame, date);
534 } catch (MathRuntimeException e) {
535
536 return null;
537 }
538 }
539
540 @Override
541 public TimeStampedPVCoordinates projectToGround(
542 final TimeStampedPVCoordinates pv,
543 final Frame frame) {
544 throw new UnsupportedOperationException();
545 }
546
547
548
549
550
551
552
553
554
555
556
557
558
559 @Override
560 public GeodeticPoint transform(final Vector3D point, final Frame frame,
561 final AbsoluteDate date) {
562
563 final GeodeticPoint ellipsoidal = this.getEllipsoid().transform(
564 point, frame, date);
565
566 final double undulation = this.getUndulation(ellipsoidal.getLatitude(),
567 ellipsoidal.getLongitude(), date);
568
569 return new GeodeticPoint(
570 ellipsoidal.getLatitude(),
571 ellipsoidal.getLongitude(),
572 ellipsoidal.getAltitude() - undulation
573 );
574 }
575
576
577
578
579
580
581
582
583
584
585
586
587
588 @Override
589 public <T extends RealFieldElement<T>> FieldGeodeticPoint<T> transform(final FieldVector3D<T> point, final Frame frame,
590 final FieldAbsoluteDate<T> date) {
591
592 final FieldGeodeticPoint<T> ellipsoidal = this.getEllipsoid().transform(
593 point, frame, date);
594
595 final double undulation = this.getUndulation(ellipsoidal.getLatitude().getReal(),
596 ellipsoidal.getLongitude().getReal(),
597 date.toAbsoluteDate());
598
599 return new FieldGeodeticPoint<>(
600 ellipsoidal.getLatitude(),
601 ellipsoidal.getLongitude(),
602 ellipsoidal.getAltitude().subtract(undulation)
603 );
604 }
605
606
607
608
609
610
611
612
613
614
615
616
617 @Override
618 public Vector3D transform(final GeodeticPoint point) {
619 try {
620
621
622 final double undulation = this.getUndulation(
623 point.getLatitude(),
624 point.getLongitude(),
625 this.defaultDate
626 );
627 final GeodeticPointdeticPoint">GeodeticPoint ellipsoidal = new GeodeticPoint(
628 point.getLatitude(),
629 point.getLongitude(),
630 point.getAltitude() + undulation
631 );
632
633 return this.getEllipsoid().transform(ellipsoidal);
634 } catch (OrekitException e) {
635
636
637 throw new RuntimeException(e);
638 }
639 }
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654 @Override
655 public <T extends RealFieldElement<T>> FieldVector3D<T> transform(final FieldGeodeticPoint<T> point) {
656 try {
657
658
659 final double undulation = this.getUndulation(
660 point.getLatitude().getReal(),
661 point.getLongitude().getReal(),
662 this.defaultDate
663 );
664 final FieldGeodeticPoint<T> ellipsoidal = new FieldGeodeticPoint<>(
665 point.getLatitude(),
666 point.getLongitude(),
667 point.getAltitude().add(undulation)
668 );
669
670 return this.getEllipsoid().transform(ellipsoidal);
671 } catch (OrekitException e) {
672
673
674 throw new RuntimeException(e);
675 }
676 }
677
678 }