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