1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.forces.radiation;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.hipparchus.CalculusFieldElement;
24 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.hipparchus.util.FastMath;
27 import org.hipparchus.util.MathArrays;
28 import org.hipparchus.util.Precision;
29 import org.orekit.frames.Frame;
30 import org.orekit.propagation.FieldSpacecraftState;
31 import org.orekit.propagation.SpacecraftState;
32 import org.orekit.time.AbsoluteDate;
33 import org.orekit.time.FieldAbsoluteDate;
34 import org.orekit.utils.Constants;
35 import org.orekit.utils.ExtendedPVCoordinatesProvider;
36 import org.orekit.utils.ParameterDriver;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 public class SolarRadiationPressure extends AbstractRadiationForceModel {
58
59
60 private static final double D_REF = 149597870000.0;
61
62
63 private static final double P_REF = 4.56e-6;
64
65
66 private static final double ANGULAR_MARGIN = 1.0e-10;
67
68
69 private final double kRef;
70
71
72 private final ExtendedPVCoordinatesProvider sun;
73
74
75 private final RadiationSensitive spacecraft;
76
77
78
79
80
81
82
83
84
85
86
87
88 public SolarRadiationPressure(final ExtendedPVCoordinatesProvider sun, final double equatorialRadius,
89 final RadiationSensitive spacecraft) {
90 this(D_REF, P_REF, sun, equatorialRadius, spacecraft);
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public SolarRadiationPressure(final double dRef, final double pRef,
107 final ExtendedPVCoordinatesProvider sun,
108 final double equatorialRadius,
109 final RadiationSensitive spacecraft) {
110 super(sun, equatorialRadius);
111 this.kRef = pRef * dRef * dRef;
112 this.sun = sun;
113 this.spacecraft = spacecraft;
114 }
115
116
117 @Override
118 public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
119
120 final AbsoluteDate date = s.getDate();
121 final Frame frame = s.getFrame();
122 final Vector3D position = s.getPVCoordinates().getPosition();
123 final Vector3D sunSatVector = position.subtract(sun.getPVCoordinates(date, frame).getPosition());
124 final double r2 = sunSatVector.getNormSq();
125
126
127 final double ratio = getTotalLightingRatio(position, frame, date);
128 final double rawP = ratio * kRef / r2;
129 final Vector3D flux = new Vector3D(rawP / FastMath.sqrt(r2), sunSatVector);
130
131 return spacecraft.radiationPressureAcceleration(date, frame, position, s.getAttitude().getRotation(),
132 s.getMass(), flux, parameters);
133
134 }
135
136
137 @Override
138 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
139 final T[] parameters) {
140
141 final FieldAbsoluteDate<T> date = s.getDate();
142 final Frame frame = s.getFrame();
143 final FieldVector3D<T> position = s.getPVCoordinates().getPosition();
144 final FieldVector3D<T> sunSatVector = position.subtract(sun.getPVCoordinates(date, frame).getPosition());
145 final T r2 = sunSatVector.getNormSq();
146
147
148 final T ratio = getTotalLightingRatio(position, frame, date);
149 final T rawP = ratio.multiply(kRef).divide(r2);
150 final FieldVector3D<T> flux = new FieldVector3D<>(rawP.divide(r2.sqrt()), sunSatVector);
151
152 return spacecraft.radiationPressureAcceleration(date, frame, position, s.getAttitude().getRotation(),
153 s.getMass(), flux, parameters);
154
155 }
156
157
158
159
160
161
162
163
164
165 public double getLightingRatio(final Vector3D position, final Frame frame, final AbsoluteDate date) {
166
167 final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition();
168 if (sunPosition.getNorm() < 2 * Constants.SUN_RADIUS) {
169
170
171 return 1.0;
172 }
173
174
175 final double[] angle = getEclipseAngles(sunPosition, position);
176
177
178 final double sunSatCentralBodyAngle = angle[0];
179
180
181 final double alphaCentral = angle[1];
182
183
184 final double alphaSun = angle[2];
185
186 double result = 1.0;
187
188
189 if (sunSatCentralBodyAngle - alphaCentral + alphaSun <= ANGULAR_MARGIN) {
190 result = 0.0;
191 } else if (sunSatCentralBodyAngle - alphaCentral - alphaSun < -ANGULAR_MARGIN) {
192
193 final double sEA2 = sunSatCentralBodyAngle * sunSatCentralBodyAngle;
194 final double oo2sEA = 1.0 / (2. * sunSatCentralBodyAngle);
195 final double aS2 = alphaSun * alphaSun;
196 final double aE2 = alphaCentral * alphaCentral;
197 final double aE2maS2 = aE2 - aS2;
198
199 final double alpha1 = (sEA2 - aE2maS2) * oo2sEA;
200 final double alpha2 = (sEA2 + aE2maS2) * oo2sEA;
201
202
203 final double almost0 = Precision.SAFE_MIN;
204 final double almost1 = FastMath.nextDown(1.0);
205 final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaSun));
206 final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1);
207 final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaCentral));
208 final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2);
209
210 final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12);
211 final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22);
212
213 result = 1. - (P1 + P2) / (FastMath.PI * aS2);
214 }
215
216 return result;
217
218 }
219
220
221
222
223
224
225
226
227
228
229 public double getGeneralEclipseRatio(final Vector3D position,
230 final Vector3D occultingPosition,
231 final double occultingRadius,
232 final Vector3D occultedPosition,
233 final double occultedRadius) {
234
235
236
237 final double[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius);
238
239
240 final double occultedSatOcculting = angle[0];
241
242
243 final double alphaOcculting = angle[1];
244
245
246 final double alphaOcculted = angle[2];
247
248 double result = 1.0;
249
250
251 if (occultedSatOcculting - alphaOcculting + alphaOcculted <= ANGULAR_MARGIN) {
252 result = 0.0;
253 } else if (occultedSatOcculting - alphaOcculting - alphaOcculted < -ANGULAR_MARGIN) {
254
255 final double sEA2 = occultedSatOcculting * occultedSatOcculting;
256 final double oo2sEA = 1.0 / (2. * occultedSatOcculting);
257 final double aS2 = alphaOcculted * alphaOcculted;
258 final double aE2 = alphaOcculting * alphaOcculting;
259 final double aE2maS2 = aE2 - aS2;
260
261 final double alpha1 = (sEA2 - aE2maS2) * oo2sEA;
262 final double alpha2 = (sEA2 + aE2maS2) * oo2sEA;
263
264
265 final double almost0 = Precision.SAFE_MIN;
266 final double almost1 = FastMath.nextDown(1.0);
267 final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaOcculted));
268 final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1);
269 final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaOcculting));
270 final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2);
271
272 final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12);
273 final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22);
274
275 result = 1. - (P1 + P2) / (FastMath.PI * aS2);
276 }
277
278 return result;
279 }
280
281
282
283
284
285
286
287
288 public double getTotalLightingRatio(final Vector3D position, final Frame frame, final AbsoluteDate date) {
289
290 double result = 0.0;
291 final Map<ExtendedPVCoordinatesProvider, Double> otherOccultingBodies = getOtherOccultingBodies();
292 final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition();
293 final int n = otherOccultingBodies.size() + 1;
294
295 if (n > 1) {
296
297 final Vector3D[] occultingBodyPositions = new Vector3D[n];
298 final double[] occultingBodyRadiuses = new double[n];
299
300
301 occultingBodyPositions[0] = new Vector3D(0.0, 0.0, 0.0);
302 occultingBodyRadiuses[0] = getEquatorialRadius();
303
304
305 int k = 1;
306 for (Map.Entry<ExtendedPVCoordinatesProvider, Double> entry : otherOccultingBodies.entrySet()) {
307 occultingBodyPositions[k] = entry.getKey().getPVCoordinates(date, frame).getPosition();
308 occultingBodyRadiuses[k] = entry.getValue();
309 ++k;
310 }
311 for (int i = 0; i < n; ++i) {
312
313
314 final double eclipseRatioI = getGeneralEclipseRatio(position,
315 occultingBodyPositions[i],
316 occultingBodyRadiuses[i],
317 sunPosition,
318 Constants.SUN_RADIUS);
319
320
321 if (eclipseRatioI == 0.0) {
322 return 0.0;
323 }
324
325
326 result += eclipseRatioI;
327
328
329
330 for (int j = i + 1; j < n; ++j) {
331
332 final double eclipseRatioJ = getGeneralEclipseRatio(position,
333 occultingBodyPositions[j],
334 occultingBodyRadiuses[j],
335 sunPosition,
336 Constants.SUN_RADIUS);
337 final double eclipseRatioIJ = getGeneralEclipseRatio(position,
338 occultingBodyPositions[i],
339 occultingBodyRadiuses[i],
340 occultingBodyPositions[j],
341 occultingBodyRadiuses[j]);
342
343 final double alphaJ = getGeneralEclipseAngles(position,
344 occultingBodyPositions[i],
345 occultingBodyRadiuses[i],
346 occultingBodyPositions[j],
347 occultingBodyRadiuses[j])[2];
348
349 final double alphaSun = getEclipseAngles(sunPosition, position)[2];
350 final double alphaJSq = alphaJ * alphaJ;
351 final double alphaSunSq = alphaSun * alphaSun;
352 final double mutualEclipseCorrection = (1 - eclipseRatioIJ) * alphaJSq / alphaSunSq;
353
354
355 if (eclipseRatioJ == 0.0 ) {
356 return 0.0;
357 }
358
359
360 else if (eclipseRatioJ != 1) {
361 result -= mutualEclipseCorrection;
362 }
363 }
364 }
365
366 result -= n - 1;
367 } else {
368
369 result = getLightingRatio(position, frame, date);
370 }
371 return result;
372 }
373
374
375
376
377
378
379
380
381
382
383
384 public <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldVector3D<T> position,
385 final Frame frame,
386 final FieldAbsoluteDate<T> date) {
387
388 final T one = date.getField().getOne();
389
390 final FieldVector3D<T> sunPosition = sun.getPVCoordinates(date, frame).getPosition();
391 if (sunPosition.getNorm().getReal() < 2 * Constants.SUN_RADIUS) {
392
393
394 return one;
395 }
396
397
398 final T[] angle = getEclipseAngles(sunPosition, position);
399
400
401 final T sunsatCentralBodyAngle = angle[0];
402
403
404 final T alphaCentral = angle[1];
405
406
407 final T alphaSun = angle[2];
408
409 T result = one;
410
411 if (sunsatCentralBodyAngle.getReal() - alphaCentral.getReal() + alphaSun.getReal() <= ANGULAR_MARGIN) {
412 result = date.getField().getZero();
413 } else if (sunsatCentralBodyAngle.getReal() - alphaCentral.getReal() - alphaSun.getReal() < -ANGULAR_MARGIN) {
414
415 final T sEA2 = sunsatCentralBodyAngle.multiply(sunsatCentralBodyAngle);
416 final T oo2sEA = sunsatCentralBodyAngle.multiply(2).reciprocal();
417 final T aS2 = alphaSun.multiply(alphaSun);
418 final T aE2 = alphaCentral.multiply(alphaCentral);
419 final T aE2maS2 = aE2.subtract(aS2);
420
421 final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA);
422 final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA);
423
424
425 final double almost0 = Precision.SAFE_MIN;
426 final double almost1 = FastMath.nextDown(1.0);
427 final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaSun)));
428 final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1)));
429 final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaCentral)));
430 final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2)));
431
432 final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt()));
433 final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt()));
434
435 result = one.subtract(P1.add(P2).divide(aS2.multiply(one.getPi())));
436 }
437
438 return result;
439 }
440
441
442
443
444
445
446
447
448
449
450
451 public <T extends CalculusFieldElement<T>> T getGeneralEclipseRatio(final FieldVector3D<T> position,
452 final FieldVector3D<T> occultingPosition,
453 final T occultingRadius,
454 final FieldVector3D<T> occultedPosition,
455 final T occultedRadius) {
456
457
458 final T one = occultingRadius.getField().getOne();
459
460
461 final T[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius);
462
463
464 final T occultedSatOcculting = angle[0];
465
466
467 final T alphaOcculting = angle[1];
468
469
470 final T alphaOcculted = angle[2];
471
472 T result = one;
473
474
475 if (occultedSatOcculting.getReal() - alphaOcculting.getReal() + alphaOcculted.getReal() <= ANGULAR_MARGIN) {
476 result = occultingRadius.getField().getZero();
477 } else if (occultedSatOcculting.getReal() - alphaOcculting.getReal() - alphaOcculted.getReal() < -ANGULAR_MARGIN) {
478
479 final T sEA2 = occultedSatOcculting.multiply(occultedSatOcculting);
480 final T oo2sEA = occultedSatOcculting.multiply(2).reciprocal();
481 final T aS2 = alphaOcculted.multiply(alphaOcculted);
482 final T aE2 = alphaOcculting.multiply(alphaOcculting);
483 final T aE2maS2 = aE2.subtract(aS2);
484
485 final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA);
486 final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA);
487
488
489 final double almost0 = Precision.SAFE_MIN;
490 final double almost1 = FastMath.nextDown(1.0);
491 final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaOcculted)));
492 final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1)));
493 final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaOcculting)));
494 final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2)));
495
496 final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt()));
497 final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt()));
498
499 result = one.subtract(P1.add(P2).divide(aS2.multiply(one.getPi())));
500 }
501
502 return result;
503 }
504
505
506
507
508
509
510
511
512
513 public <T extends CalculusFieldElement<T>> T getTotalLightingRatio(final FieldVector3D<T> position, final Frame frame, final FieldAbsoluteDate<T> date) {
514
515 final T zero = position.getAlpha().getField().getZero();
516 T result = zero;
517 final Map<ExtendedPVCoordinatesProvider, Double> otherOccultingBodies = getOtherOccultingBodies();
518 final FieldVector3D<T> sunPosition = sun.getPVCoordinates(date, frame).getPosition();
519 final int n = otherOccultingBodies.size() + 1;
520
521 if (n > 1) {
522
523 final List<FieldVector3D<T>> occultingBodyPositions = new ArrayList<FieldVector3D<T>>(n);
524 final T[] occultingBodyRadiuses = MathArrays.buildArray(zero.getField(), n);
525
526
527 occultingBodyPositions.add(0, new FieldVector3D<>(zero, zero, zero));
528 occultingBodyRadiuses[0] = zero.add(getEquatorialRadius());
529
530
531 int k = 1;
532 for (Map.Entry<ExtendedPVCoordinatesProvider, Double> entry: otherOccultingBodies.entrySet()) {
533 occultingBodyPositions.add(k, entry.getKey().getPVCoordinates(date, frame).getPosition());
534 occultingBodyRadiuses[k] = zero.newInstance(entry.getValue());
535 ++k;
536 }
537 for (int i = 0; i < n; ++i) {
538
539
540 final T eclipseRatioI = getGeneralEclipseRatio(position,
541 occultingBodyPositions.get(i),
542 occultingBodyRadiuses[i],
543 sunPosition,
544 zero.add(Constants.SUN_RADIUS));
545
546
547 if (eclipseRatioI.getReal() == 0.0) {
548 return zero;
549 }
550
551
552 result = result.add(eclipseRatioI);
553
554
555
556 for (int j = i + 1; j < n; ++j) {
557
558 final T eclipseRatioJ = getGeneralEclipseRatio(position,
559 occultingBodyPositions.get(i),
560 occultingBodyRadiuses[j],
561 sunPosition,
562 zero.add(Constants.SUN_RADIUS));
563 final T eclipseRatioIJ = getGeneralEclipseRatio(position,
564 occultingBodyPositions.get(i),
565 occultingBodyRadiuses[i],
566 occultingBodyPositions.get(j),
567 occultingBodyRadiuses[j]);
568
569 final T alphaJ = getGeneralEclipseAngles(position,
570 occultingBodyPositions.get(i),
571 occultingBodyRadiuses[i],
572 occultingBodyPositions.get(j),
573 occultingBodyRadiuses[j])[2];
574
575 final T alphaSun = getEclipseAngles(sunPosition, position)[2];
576 final T alphaJSq = alphaJ.multiply(alphaJ);
577 final T alphaSunSq = alphaSun.multiply(alphaSun);
578 final T mutualEclipseCorrection = eclipseRatioIJ.negate().add(1).multiply(alphaJSq).divide(alphaSunSq);
579
580
581 if (eclipseRatioJ.getReal() == 0.0 ) {
582 return zero;
583 }
584
585
586 else if (eclipseRatioJ.getReal() != 1) {
587 result = result.subtract(mutualEclipseCorrection);
588 }
589 }
590 }
591
592 result = result.subtract(n - 1);
593 } else {
594
595 result = getLightingRatio(position, frame, date);
596 }
597 return result;
598 }
599
600
601
602 @Override
603 public List<ParameterDriver> getParametersDrivers() {
604 return spacecraft.getRadiationParametersDrivers();
605 }
606
607
608
609
610
611
612
613 private <T extends CalculusFieldElement<T>> T min(final double d, final T f) {
614 return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f;
615 }
616
617
618
619
620
621
622
623 private <T extends CalculusFieldElement<T>> T max(final double d, final T f) {
624 return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f;
625 }
626
627 }