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 boolean dependsOnPositionOnly() {
156 return spacecraft instanceof IsotropicRadiationClassicalConvention || spacecraft instanceof IsotropicRadiationCNES95Convention || spacecraft instanceof IsotropicRadiationSingleCoefficient;
157 }
158
159
160 @Override
161 public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
162
163 final AbsoluteDate date = s.getDate();
164 final Frame frame = s.getFrame();
165 final Vector3D position = s.getPosition();
166 final Vector3D sunPosition = sun.getPosition(date, frame);
167 final Vector3D sunSatVector = position.subtract(sunPosition);
168 final double r2 = sunSatVector.getNormSq();
169
170
171 final double ratio = getLightingRatio(s, sunPosition);
172 final double rawP = ratio * kRef / r2;
173 final Vector3D flux = new Vector3D(rawP / FastMath.sqrt(r2), sunSatVector);
174
175 return spacecraft.radiationPressureAcceleration(s, flux, parameters);
176
177 }
178
179
180 @Override
181 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
182 final T[] parameters) {
183
184 final FieldAbsoluteDate<T> date = s.getDate();
185 final Frame frame = s.getFrame();
186 final FieldVector3D<T> position = s.getPosition();
187 final FieldVector3D<T> sunPosition = sun.getPosition(date, frame);
188 final FieldVector3D<T> sunSatVector = position.subtract(sunPosition);
189 final T r2 = sunSatVector.getNormSq();
190
191
192 final T ratio = getLightingRatio(s, sunPosition);
193 final T rawP = ratio.multiply(kRef).divide(r2);
194 final FieldVector3D<T> flux = new FieldVector3D<>(rawP.divide(r2.sqrt()), sunSatVector);
195
196 return spacecraft.radiationPressureAcceleration(s, flux, parameters);
197
198 }
199
200
201
202
203
204
205
206 private boolean isSunCenteredFrame(final Vector3D sunPositionInFrame) {
207
208
209 return sunPositionInFrame.getNorm() < SUN_CENTERED_FRAME_THRESHOLD;
210 }
211
212
213
214
215
216
217
218
219 private double getLightingRatio(final SpacecraftState state, final Vector3D sunPosition) {
220
221
222 if (isSunCenteredFrame(sunPosition)) {
223
224
225 return 1.0;
226 }
227
228 final List<OccultationEngine> occultingBodies = getOccultingBodies();
229 final int n = occultingBodies.size();
230
231 final OccultationEngine.OccultationAngles[] angles = new OccultationEngine.OccultationAngles[n];
232 for (int i = 0; i < n; ++i) {
233 angles[i] = occultingBodies.get(i).angles(state);
234 }
235 final double alphaSunSq = angles[0].getOccultedApparentRadius() * angles[0].getOccultedApparentRadius();
236
237 double result = 0.0;
238 for (int i = 0; i < n; ++i) {
239
240
241 final OccultationEngine oi = occultingBodies.get(i);
242 final double lightingRatioI = maskingRatio(angles[i]);
243 if (lightingRatioI == 0.0) {
244
245 return 0.0;
246 }
247 result += lightingRatioI;
248
249
250 for (int j = i + 1; j < n; ++j) {
251
252 final OccultationEngine oj = occultingBodies.get(j);
253 final double lightingRatioJ = maskingRatio(angles[j]);
254 if (lightingRatioJ == 0.0) {
255
256 return 0.0;
257 } else if (lightingRatioJ != 1) {
258
259
260 final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()),
261 oi.getOcculting().getEquatorialRadius(),
262 oj.getOcculting());
263 final OccultationEngine.OccultationAngles aij = oij.angles(state);
264 final double maskingRatioIJ = maskingRatio(aij);
265 final double alphaJSq = aij.getOccultedApparentRadius() * aij.getOccultedApparentRadius();
266
267 final double mutualEclipseCorrection = (1 - maskingRatioIJ) * alphaJSq / alphaSunSq;
268 result -= mutualEclipseCorrection;
269
270 }
271
272 }
273 }
274
275
276 result -= n - 1;
277
278 return result;
279 }
280
281
282
283
284
285
286 public double getLightingRatio(final SpacecraftState state) {
287 return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame()));
288 }
289
290
291
292
293
294
295 private double maskingRatio(final OccultationEngine.OccultationAngles angles) {
296
297
298 final double sunSatCentralBodyAngle = angles.getSeparation();
299
300
301 final double alphaCentral = angles.getLimbRadius();
302
303
304 final double alphaSun = angles.getOccultedApparentRadius();
305
306
307 if (sunSatCentralBodyAngle - alphaCentral + alphaSun <= ANGULAR_MARGIN) {
308 return 0.0;
309 } else if (sunSatCentralBodyAngle - alphaCentral - alphaSun < -ANGULAR_MARGIN) {
310
311 final double sEA2 = sunSatCentralBodyAngle * sunSatCentralBodyAngle;
312 final double oo2sEA = 1.0 / (2. * sunSatCentralBodyAngle);
313 final double aS2 = alphaSun * alphaSun;
314 final double aE2 = alphaCentral * alphaCentral;
315 final double aE2maS2 = aE2 - aS2;
316
317 final double alpha1 = (sEA2 - aE2maS2) * oo2sEA;
318 final double alpha2 = (sEA2 + aE2maS2) * oo2sEA;
319
320
321 final double almost0 = Precision.SAFE_MIN;
322 final double almost1 = FastMath.nextDown(1.0);
323 final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaSun));
324 final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1);
325 final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaCentral));
326 final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2);
327
328 final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12);
329 final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22);
330
331 return 1. - (P1 + P2) / (FastMath.PI * aS2);
332 } else {
333 return 1.0;
334 }
335
336 }
337
338
339
340
341
342
343
344
345 private <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldSpacecraftState<T> state, final FieldVector3D<T> sunPosition) {
346
347 final T one = state.getDate().getField().getOne();
348 if (isSunCenteredFrame(sunPosition.toVector3D())) {
349
350
351 return one;
352 }
353 final T zero = state.getDate().getField().getZero();
354 final List<OccultationEngine> occultingBodies = getOccultingBodies();
355 final int n = occultingBodies.size();
356
357 @SuppressWarnings("unchecked")
358 final OccultationEngine.FieldOccultationAngles<T>[] angles =
359 (OccultationEngine.FieldOccultationAngles<T>[]) Array.newInstance(OccultationEngine.FieldOccultationAngles.class, n);
360 for (int i = 0; i < n; ++i) {
361 angles[i] = occultingBodies.get(i).angles(state);
362 }
363 final T alphaSunSq = angles[0].getOccultedApparentRadius().multiply(angles[0].getOccultedApparentRadius());
364
365 T result = state.getDate().getField().getZero();
366 for (int i = 0; i < n; ++i) {
367
368
369 final OccultationEngine oi = occultingBodies.get(i);
370 final T lightingRatioI = maskingRatio(angles[i]);
371 if (lightingRatioI.isZero()) {
372
373 return zero;
374 }
375 result = result.add(lightingRatioI);
376
377
378 for (int j = i + 1; j < n; ++j) {
379
380 final OccultationEngine oj = occultingBodies.get(j);
381 final T lightingRatioJ = maskingRatio(angles[j]);
382 if (lightingRatioJ.isZero()) {
383
384 return zero;
385 } else if (lightingRatioJ.getReal() != 1) {
386
387
388 final OccultationEngine oij = new OccultationEngine(new FrameAdapter(oi.getOcculting().getBodyFrame()),
389 oi.getOcculting().getEquatorialRadius(),
390 oj.getOcculting());
391 final OccultationEngine.FieldOccultationAngles<T> aij = oij.angles(state);
392 final T maskingRatioIJ = maskingRatio(aij);
393 final T alphaJSq = aij.getOccultedApparentRadius().multiply(aij.getOccultedApparentRadius());
394
395 final T mutualEclipseCorrection = one.subtract(maskingRatioIJ).multiply(alphaJSq).divide(alphaSunSq);
396 result = result.subtract(mutualEclipseCorrection);
397
398 }
399
400 }
401 }
402
403
404 result = result.subtract(n - 1);
405
406 return result;
407 }
408
409
410
411
412
413
414
415 public <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldSpacecraftState<T> state) {
416 return getLightingRatio(state, sun.getPosition(state.getDate(), state.getFrame()));
417 }
418
419
420
421
422
423
424
425 private <T extends CalculusFieldElement<T>> T maskingRatio(final OccultationEngine.FieldOccultationAngles<T> angles) {
426
427
428
429 final T occultedSatOcculting = angles.getSeparation();
430
431
432 final T alphaOcculting = angles.getLimbRadius();
433
434
435 final T alphaOcculted = angles.getOccultedApparentRadius();
436
437
438 if (occultedSatOcculting.getReal() - alphaOcculting.getReal() + alphaOcculted.getReal() <= ANGULAR_MARGIN) {
439 return occultedSatOcculting.getField().getZero();
440 } else if (occultedSatOcculting.getReal() - alphaOcculting.getReal() - alphaOcculted.getReal() < -ANGULAR_MARGIN) {
441
442 final T sEA2 = occultedSatOcculting.multiply(occultedSatOcculting);
443 final T oo2sEA = occultedSatOcculting.multiply(2).reciprocal();
444 final T aS2 = alphaOcculted.multiply(alphaOcculted);
445 final T aE2 = alphaOcculting.multiply(alphaOcculting);
446 final T aE2maS2 = aE2.subtract(aS2);
447
448 final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA);
449 final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA);
450
451
452 final double almost0 = Precision.SAFE_MIN;
453 final double almost1 = FastMath.nextDown(1.0);
454 final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaOcculted)));
455 final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1)));
456 final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaOcculting)));
457 final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2)));
458
459 final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt()));
460 final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt()));
461
462 return occultedSatOcculting.getField().getOne().subtract(P1.add(P2).divide(aS2.multiply(occultedSatOcculting.getPi())));
463 } else {
464 return occultedSatOcculting.getField().getOne();
465 }
466
467 }
468
469
470 @Override
471 public List<ParameterDriver> getParametersDrivers() {
472 return spacecraft.getRadiationParametersDrivers();
473 }
474
475
476
477
478
479
480
481 private <T extends CalculusFieldElement<T>> T min(final double d, final T f) {
482 return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f;
483 }
484
485
486
487
488
489
490
491 private <T extends CalculusFieldElement<T>> T max(final double d, final T f) {
492 return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f;
493 }
494
495 }