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