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.lang.reflect.Array;
20 import java.util.List;
21
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24 import org.hipparchus.geometry.euclidean.threed.Vector3D;
25 import org.hipparchus.util.FastMath;
26 import org.hipparchus.util.Precision;
27 import org.orekit.bodies.OneAxisEllipsoid;
28 import org.orekit.frames.Frame;
29 import org.orekit.propagation.FieldSpacecraftState;
30 import org.orekit.propagation.SpacecraftState;
31 import org.orekit.propagation.events.EventDetectionSettings;
32 import org.orekit.time.AbsoluteDate;
33 import org.orekit.time.FieldAbsoluteDate;
34 import org.orekit.utils.Constants;
35 import org.orekit.utils.ExtendedPositionProvider;
36 import org.orekit.utils.FrameAdapter;
37 import org.orekit.utils.OccultationEngine;
38 import org.orekit.utils.ParameterDriver;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class SolarRadiationPressure extends AbstractRadiationForceModel {
60
61
62 private static final double D_REF = 149597870000.0;
63
64
65 private static final double P_REF = 4.56e-6;
66
67
68 private static final double ANGULAR_MARGIN = 1.0e-10;
69
70
71 private static final double SUN_CENTERED_FRAME_THRESHOLD = 2. * Constants.SUN_RADIUS;
72
73
74 private final double kRef;
75
76
77 private final ExtendedPositionProvider sun;
78
79
80 private final RadiationSensitive spacecraft;
81
82
83
84
85
86
87
88
89
90
91
92
93 public SolarRadiationPressure(final ExtendedPositionProvider sun,
94 final OneAxisEllipsoid centralBody,
95 final RadiationSensitive spacecraft) {
96 this(D_REF, P_REF, sun, centralBody, spacecraft);
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 public SolarRadiationPressure(final double dRef, final double pRef,
113 final ExtendedPositionProvider sun,
114 final OneAxisEllipsoid centralBody,
115 final RadiationSensitive spacecraft) {
116 this(dRef, pRef, sun, centralBody, spacecraft, getDefaultEclipseDetectionSettings());
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public SolarRadiationPressure(final double dRef, final double pRef,
134 final ExtendedPositionProvider sun,
135 final OneAxisEllipsoid centralBody,
136 final RadiationSensitive spacecraft,
137 final EventDetectionSettings eclipseDetectionSettings) {
138 super(sun, centralBody, eclipseDetectionSettings);
139 this.kRef = pRef * dRef * dRef;
140 this.sun = sun;
141 this.spacecraft = spacecraft;
142 }
143
144
145
146
147
148
149 public RadiationSensitive getRadiationSensitiveSpacecraft() {
150 return spacecraft;
151 }
152
153
154 @Override
155 public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
156
157 final AbsoluteDate date = s.getDate();
158 final Frame frame = s.getFrame();
159 final Vector3D position = s.getPosition();
160 final Vector3D sunPosition = sun.getPosition(date, frame);
161 final Vector3D sunSatVector = position.subtract(sunPosition);
162 final double r2 = sunSatVector.getNormSq();
163
164
165 final double ratio = getLightingRatio(s, sunPosition);
166 final double rawP = ratio * kRef / r2;
167 final Vector3D flux = new Vector3D(rawP / FastMath.sqrt(r2), sunSatVector);
168
169 return spacecraft.radiationPressureAcceleration(s, flux, parameters);
170
171 }
172
173
174 @Override
175 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
176 final T[] parameters) {
177
178 final FieldAbsoluteDate<T> date = s.getDate();
179 final Frame frame = s.getFrame();
180 final FieldVector3D<T> position = s.getPosition();
181 final FieldVector3D<T> sunPosition = sun.getPosition(date, frame);
182 final FieldVector3D<T> sunSatVector = position.subtract(sunPosition);
183 final T r2 = sunSatVector.getNormSq();
184
185
186 final T ratio = getLightingRatio(s, sunPosition);
187 final T rawP = ratio.multiply(kRef).divide(r2);
188 final FieldVector3D<T> flux = new FieldVector3D<>(rawP.divide(r2.sqrt()), sunSatVector);
189
190 return spacecraft.radiationPressureAcceleration(s, flux, parameters);
191
192 }
193
194
195
196
197
198
199
200 private boolean isSunCenteredFrame(final Vector3D sunPositionInFrame) {
201
202
203 return sunPositionInFrame.getNorm() < SUN_CENTERED_FRAME_THRESHOLD;
204 }
205
206
207
208
209
210
211
212
213 private double getLightingRatio(final SpacecraftState state, final Vector3D sunPosition) {
214
215
216 if (isSunCenteredFrame(sunPosition)) {
217
218
219 return 1.0;
220 }
221
222 final List<OccultationEngine> occultingBodies = getOccultingBodies();
223 final int n = occultingBodies.size();
224
225 final OccultationEngine.OccultationAngles[] angles = new OccultationEngine.OccultationAngles[n];
226 for (int i = 0; i < n; ++i) {
227 angles[i] = occultingBodies.get(i).angles(state);
228 }
229 final double alphaSunSq = angles[0].getOccultedApparentRadius() * angles[0].getOccultedApparentRadius();
230
231 double result = 0.0;
232 for (int i = 0; i < n; ++i) {
233
234
235 final OccultationEngine oi = occultingBodies.get(i);
236 final double lightingRatioI = maskingRatio(angles[i]);
237 if (lightingRatioI == 0.0) {
238
239 return 0.0;
240 }
241 result += lightingRatioI;
242
243
244 for (int j = i + 1; j < n; ++j) {
245
246 final OccultationEngine oj = occultingBodies.get(j);
247 final double lightingRatioJ = maskingRatio(angles[j]);
248 if (lightingRatioJ == 0.0) {
249
250 return 0.0;
251 } else if (lightingRatioJ != 1) {
252
253
254 final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()),
255 oi.getOcculting().getEquatorialRadius(),
256 oj.getOcculting());
257 final OccultationEngine.OccultationAngles aij = oij.angles(state);
258 final double maskingRatioIJ = maskingRatio(aij);
259 final double alphaJSq = aij.getOccultedApparentRadius() * aij.getOccultedApparentRadius();
260
261 final double mutualEclipseCorrection = (1 - maskingRatioIJ) * alphaJSq / alphaSunSq;
262 result -= mutualEclipseCorrection;
263
264 }
265
266 }
267 }
268
269
270 result -= n - 1;
271
272 return result;
273 }
274
275
276
277
278
279
280 public double getLightingRatio(final SpacecraftState state) {
281 return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame()));
282 }
283
284
285
286
287
288
289 private double maskingRatio(final OccultationEngine.OccultationAngles angles) {
290
291
292 final double sunSatCentralBodyAngle = angles.getSeparation();
293
294
295 final double alphaCentral = angles.getLimbRadius();
296
297
298 final double alphaSun = angles.getOccultedApparentRadius();
299
300
301 if (sunSatCentralBodyAngle - alphaCentral + alphaSun <= ANGULAR_MARGIN) {
302 return 0.0;
303 } else if (sunSatCentralBodyAngle - alphaCentral - alphaSun < -ANGULAR_MARGIN) {
304
305 final double sEA2 = sunSatCentralBodyAngle * sunSatCentralBodyAngle;
306 final double oo2sEA = 1.0 / (2. * sunSatCentralBodyAngle);
307 final double aS2 = alphaSun * alphaSun;
308 final double aE2 = alphaCentral * alphaCentral;
309 final double aE2maS2 = aE2 - aS2;
310
311 final double alpha1 = (sEA2 - aE2maS2) * oo2sEA;
312 final double alpha2 = (sEA2 + aE2maS2) * oo2sEA;
313
314
315 final double almost0 = Precision.SAFE_MIN;
316 final double almost1 = FastMath.nextDown(1.0);
317 final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaSun));
318 final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1);
319 final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaCentral));
320 final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2);
321
322 final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12);
323 final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22);
324
325 return 1. - (P1 + P2) / (FastMath.PI * aS2);
326 } else {
327 return 1.0;
328 }
329
330 }
331
332
333
334
335
336
337
338
339 private <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldSpacecraftState<T> state, final FieldVector3D<T> sunPosition) {
340
341 final T one = state.getDate().getField().getOne();
342 if (isSunCenteredFrame(sunPosition.toVector3D())) {
343
344
345 return one;
346 }
347 final T zero = state.getDate().getField().getZero();
348 final List<OccultationEngine> occultingBodies = getOccultingBodies();
349 final int n = occultingBodies.size();
350
351 @SuppressWarnings("unchecked")
352 final OccultationEngine.FieldOccultationAngles<T>[] angles =
353 (OccultationEngine.FieldOccultationAngles<T>[]) Array.newInstance(OccultationEngine.FieldOccultationAngles.class, n);
354 for (int i = 0; i < n; ++i) {
355 angles[i] = occultingBodies.get(i).angles(state);
356 }
357 final T alphaSunSq = angles[0].getOccultedApparentRadius().multiply(angles[0].getOccultedApparentRadius());
358
359 T result = state.getDate().getField().getZero();
360 for (int i = 0; i < n; ++i) {
361
362
363 final OccultationEngine oi = occultingBodies.get(i);
364 final T lightingRatioI = maskingRatio(angles[i]);
365 if (lightingRatioI.isZero()) {
366
367 return zero;
368 }
369 result = result.add(lightingRatioI);
370
371
372 for (int j = i + 1; j < n; ++j) {
373
374 final OccultationEngine oj = occultingBodies.get(j);
375 final T lightingRatioJ = maskingRatio(angles[j]);
376 if (lightingRatioJ.isZero()) {
377
378 return zero;
379 } else if (lightingRatioJ.getReal() != 1) {
380
381
382 final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()),
383 oi.getOcculting().getEquatorialRadius(),
384 oj.getOcculting());
385 final OccultationEngine.FieldOccultationAngles<T> aij = oij.angles(state);
386 final T maskingRatioIJ = maskingRatio(aij);
387 final T alphaJSq = aij.getOccultedApparentRadius().multiply(aij.getOccultedApparentRadius());
388
389 final T mutualEclipseCorrection = one.subtract(maskingRatioIJ).multiply(alphaJSq).divide(alphaSunSq);
390 result = result.subtract(mutualEclipseCorrection);
391
392 }
393
394 }
395 }
396
397
398 result = result.subtract(n - 1);
399
400 return result;
401 }
402
403
404
405
406
407
408
409 public <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldSpacecraftState<T> state) {
410 return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame()));
411 }
412
413
414
415
416
417
418
419 private <T extends CalculusFieldElement<T>> T maskingRatio(final OccultationEngine.FieldOccultationAngles<T> angles) {
420
421
422
423 final T occultedSatOcculting = angles.getSeparation();
424
425
426 final T alphaOcculting = angles.getLimbRadius();
427
428
429 final T alphaOcculted = angles.getOccultedApparentRadius();
430
431
432 if (occultedSatOcculting.getReal() - alphaOcculting.getReal() + alphaOcculted.getReal() <= ANGULAR_MARGIN) {
433 return occultedSatOcculting.getField().getZero();
434 } else if (occultedSatOcculting.getReal() - alphaOcculting.getReal() - alphaOcculted.getReal() < -ANGULAR_MARGIN) {
435
436 final T sEA2 = occultedSatOcculting.multiply(occultedSatOcculting);
437 final T oo2sEA = occultedSatOcculting.multiply(2).reciprocal();
438 final T aS2 = alphaOcculted.multiply(alphaOcculted);
439 final T aE2 = alphaOcculting.multiply(alphaOcculting);
440 final T aE2maS2 = aE2.subtract(aS2);
441
442 final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA);
443 final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA);
444
445
446 final double almost0 = Precision.SAFE_MIN;
447 final double almost1 = FastMath.nextDown(1.0);
448 final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaOcculted)));
449 final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1)));
450 final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaOcculting)));
451 final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2)));
452
453 final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt()));
454 final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt()));
455
456 return occultedSatOcculting.getField().getOne().subtract(P1.add(P2).divide(aS2.multiply(occultedSatOcculting.getPi())));
457 } else {
458 return occultedSatOcculting.getField().getOne();
459 }
460
461 }
462
463
464 @Override
465 public List<ParameterDriver> getParametersDrivers() {
466 return spacecraft.getRadiationParametersDrivers();
467 }
468
469
470
471
472
473
474
475 private <T extends CalculusFieldElement<T>> T min(final double d, final T f) {
476 return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f;
477 }
478
479
480
481
482
483
484
485 private <T extends CalculusFieldElement<T>> T max(final double d, final T f) {
486 return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f;
487 }
488
489 }