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.List;
20
21 import org.hipparchus.CalculusFieldElement;
22 import org.hipparchus.analysis.polynomials.PolynomialFunction;
23 import org.hipparchus.analysis.polynomials.PolynomialsUtils;
24 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
25 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26 import org.hipparchus.geometry.euclidean.threed.Rotation;
27 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
28 import org.hipparchus.geometry.euclidean.threed.Vector3D;
29 import org.hipparchus.util.FastMath;
30 import org.hipparchus.util.FieldSinCos;
31 import org.hipparchus.util.MathUtils;
32 import org.hipparchus.util.SinCos;
33 import org.orekit.annotation.DefaultDataContext;
34 import org.orekit.data.DataContext;
35 import org.orekit.forces.ForceModel;
36 import org.orekit.frames.Frame;
37 import org.orekit.propagation.FieldSpacecraftState;
38 import org.orekit.propagation.SpacecraftState;
39 import org.orekit.time.AbsoluteDate;
40 import org.orekit.time.FieldAbsoluteDate;
41 import org.orekit.time.TimeScale;
42 import org.orekit.utils.Constants;
43 import org.orekit.utils.ExtendedPositionProvider;
44 import org.orekit.utils.ParameterDriver;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class KnockeRediffusedForceModel implements ForceModel {
66
67
68 private static final double EARTH_AROUND_SUN_PULSATION = MathUtils.TWO_PI / Constants.JULIAN_YEAR;
69
70
71 private static final double ES_COEFF = 4.5606E-6;
72
73
74 private static final double A0 = 0.34;
75
76
77 private static final double C0 = 0.;
78
79
80 private static final double C1 = 0.10;
81
82
83 private static final double C2 = 0.;
84
85
86 private static final double A2 = 0.29;
87
88
89 private static final double E0 = 0.68;
90
91
92 private static final double K0 = 0.;
93
94
95 private static final double K1 = -0.07;
96
97
98 private static final double K2 = 0.;
99
100
101 private static final double E2 = -0.18;
102
103
104 private final ExtendedPositionProvider sun;
105
106
107 private final RadiationSensitive spacecraft;
108
109
110 private final double angularResolution;
111
112
113 private final double equatorialRadius;
114
115
116
117 private final AbsoluteDate referenceEpoch;
118
119
120
121
122
123
124
125
126 @DefaultDataContext
127 public KnockeRediffusedForceModel (final ExtendedPositionProvider sun,
128 final RadiationSensitive spacecraft,
129 final double equatorialRadius,
130 final double angularResolution) {
131
132 this(sun, spacecraft, equatorialRadius, angularResolution, DataContext.getDefault().getTimeScales().getUTC());
133 }
134
135
136
137
138
139
140
141
142 public KnockeRediffusedForceModel (final ExtendedPositionProvider sun,
143 final RadiationSensitive spacecraft,
144 final double equatorialRadius,
145 final double angularResolution,
146 final TimeScale utc) {
147 this.sun = sun;
148 this.spacecraft = spacecraft;
149 this.equatorialRadius = equatorialRadius;
150 this.angularResolution = angularResolution;
151 this.referenceEpoch = new AbsoluteDate(1981, 12, 22, 0, 0, 0.0, utc);
152 }
153
154
155
156 @Override
157 public boolean dependsOnPositionOnly() {
158 return false;
159 }
160
161
162 @Override
163 public Vector3D acceleration(final SpacecraftState s,
164 final double[] parameters) {
165
166
167 final AbsoluteDate date = s.getDate();
168
169
170 final Frame frame = s.getFrame();
171
172
173 final Vector3D satellitePosition = s.getPosition();
174
175
176 final Vector3D sunPosition = sun.getPosition(date, frame);
177
178
179 final Vector3D projectedToGround = satellitePosition.normalize().scalarMultiply(equatorialRadius);
180
181
182 final double q = 1.0 / FastMath.hypot(satellitePosition.getX(), satellitePosition.getY());
183 final Vector3D east = new Vector3D(-q * satellitePosition.getY(), q * satellitePosition.getX(), 0);
184
185
186 final double centerArea = MathUtils.TWO_PI * equatorialRadius * equatorialRadius *
187 (1.0 - FastMath.cos(angularResolution));
188 Vector3D rediffusedFlux = computeElementaryFlux(s, projectedToGround, sunPosition, centerArea);
189
190
191 for (double eastAxisOffset = 1.5 * angularResolution;
192 eastAxisOffset < FastMath.asin(equatorialRadius / satellitePosition.getNorm());
193 eastAxisOffset = eastAxisOffset + angularResolution) {
194
195
196 final Rotation eastRotation = new Rotation(east, eastAxisOffset, RotationConvention.VECTOR_OPERATOR);
197
198
199 final Vector3D firstCrownSectorCenter = eastRotation.applyTo(projectedToGround);
200
201
202
203 final double sectorArea = equatorialRadius * equatorialRadius *
204 2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) *
205 FastMath.sin(eastAxisOffset);
206
207
208 for (double radialAxisOffset = 0.5 * angularResolution;
209 radialAxisOffset < MathUtils.TWO_PI;
210 radialAxisOffset = radialAxisOffset + angularResolution) {
211
212
213 final Rotation radialRotation = new Rotation(projectedToGround, radialAxisOffset, RotationConvention.VECTOR_OPERATOR);
214
215
216 final Vector3D currentCenter = radialRotation.applyTo(firstCrownSectorCenter);
217
218
219 rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, sectorArea));
220 }
221 }
222
223 return spacecraft.radiationPressureAcceleration(s, rediffusedFlux, parameters);
224 }
225
226
227
228 @Override
229 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
230 final T[] parameters) {
231
232 final FieldAbsoluteDate<T> date = s.getDate();
233
234
235 final Frame frame = s.getFrame();
236
237
238 final T zero = date.getField().getZero();
239
240
241 final FieldVector3D<T> satellitePosition = s.getPosition();
242
243
244 final FieldVector3D<T> sunPosition = sun.getPosition(date, frame);
245
246
247 final FieldVector3D<T> projectedToGround = satellitePosition.normalize().scalarMultiply(equatorialRadius);
248
249
250 final T q = FastMath.hypot(satellitePosition.getX(), satellitePosition.getY()).reciprocal();
251 final FieldVector3D<T> east = new FieldVector3D<>(q.negate().multiply(satellitePosition.getY()),
252 q.multiply(satellitePosition.getX()),
253 zero);
254
255
256 final T centerArea = zero.getPi().multiply(2.0).multiply(equatorialRadius).multiply(equatorialRadius).
257 multiply(1.0 - FastMath.cos(angularResolution));
258 FieldVector3D<T> rediffusedFlux = computeElementaryFlux(s, projectedToGround, sunPosition, centerArea);
259
260
261 for (double eastAxisOffset = 1.5 * angularResolution;
262 eastAxisOffset < FastMath.asin(equatorialRadius / satellitePosition.getNorm().getReal());
263 eastAxisOffset = eastAxisOffset + angularResolution) {
264
265
266 final FieldRotation<T> eastRotation = new FieldRotation<>(east, zero.newInstance(eastAxisOffset),
267 RotationConvention.VECTOR_OPERATOR);
268
269
270 final FieldVector3D<T> firstCrownSectorCenter = eastRotation.applyTo(projectedToGround);
271
272
273
274 final T sectorArea = zero.newInstance(equatorialRadius * equatorialRadius *
275 2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) *
276 FastMath.sin(eastAxisOffset));
277
278
279 for (double radialAxisOffset = 0.5 * angularResolution;
280 radialAxisOffset < MathUtils.TWO_PI;
281 radialAxisOffset = radialAxisOffset + angularResolution) {
282
283
284 final FieldRotation<T> radialRotation = new FieldRotation<>(projectedToGround,
285 zero.newInstance(radialAxisOffset),
286 RotationConvention.VECTOR_OPERATOR);
287
288
289 final FieldVector3D<T> currentCenter = radialRotation.applyTo(firstCrownSectorCenter);
290
291
292 rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, sectorArea));
293 }
294 }
295
296 return spacecraft.radiationPressureAcceleration(s, rediffusedFlux, parameters);
297 }
298
299
300
301 @Override
302 public List<ParameterDriver> getParametersDrivers() {
303 return spacecraft.getRadiationParametersDrivers();
304 }
305
306
307
308
309
310
311
312
313 public double computeAlbedo(final AbsoluteDate date, final double phi) {
314
315
316 final double deltaT = date.durationFrom(referenceEpoch);
317
318
319 final SinCos sc = FastMath.sinCos(EARTH_AROUND_SUN_PULSATION * deltaT);
320 final double A1 = C0 +
321 C1 * sc.cos() +
322 C2 * sc.sin();
323
324
325 final PolynomialFunction firstLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(1);
326 final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
327
328
329 final double sinPhi = FastMath.sin(phi);
330
331
332 return A0 +
333 A1 * firstLegendrePolynomial.value(sinPhi) +
334 A2 * secondLegendrePolynomial.value(sinPhi);
335
336 }
337
338
339
340
341
342
343
344
345
346
347 public <T extends CalculusFieldElement<T>> T computeAlbedo(final FieldAbsoluteDate<T> date, final T phi) {
348
349
350 final T deltaT = date.durationFrom(referenceEpoch);
351
352
353 final FieldSinCos<T> sc = FastMath.sinCos(deltaT.multiply(EARTH_AROUND_SUN_PULSATION));
354 final T A1 = sc.cos().multiply(C1).add(
355 sc.sin().multiply(C2)).add(C0);
356
357
358 final PolynomialFunction firstLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(1);
359 final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
360
361
362 final T sinPhi = FastMath.sin(phi);
363
364
365 return firstLegendrePolynomial.value(sinPhi).multiply(A1).add(
366 secondLegendrePolynomial.value(sinPhi).multiply(A2)).add(A0);
367
368 }
369
370
371
372
373
374
375
376
377 public double computeEmissivity(final AbsoluteDate date, final double phi) {
378
379
380 final double deltaT = date.durationFrom(referenceEpoch);
381
382
383 final SinCos sc = FastMath.sinCos(EARTH_AROUND_SUN_PULSATION * deltaT);
384 final double E1 = K0 +
385 K1 * sc.cos() +
386 K2 * sc.sin();
387
388
389 final PolynomialFunction firstLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(1);
390 final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
391
392
393 final double sinPhi = FastMath.sin(phi);
394
395
396 return E0 +
397 E1 * firstLegendrePolynomial.value(sinPhi) +
398 E2 * secondLegendrePolynomial.value(sinPhi);
399
400 }
401
402
403
404
405
406
407
408
409
410
411 public <T extends CalculusFieldElement<T>> T computeEmissivity(final FieldAbsoluteDate<T> date, final T phi) {
412
413
414 final T deltaT = date.durationFrom(referenceEpoch);
415
416
417 final FieldSinCos<T> sc = FastMath.sinCos(deltaT.multiply(EARTH_AROUND_SUN_PULSATION));
418 final T E1 = sc.cos().multiply(K1).add(
419 sc.sin().multiply(K2)).add(K0);
420
421
422 final PolynomialFunction firstLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(1);
423 final PolynomialFunction secondLegendrePolynomial = PolynomialsUtils.createLegendrePolynomial(2);
424
425
426 final T sinPhi = FastMath.sin(phi);
427
428
429 return firstLegendrePolynomial.value(sinPhi).multiply(E1).add(
430 secondLegendrePolynomial.value(sinPhi).multiply(E2)).add(E0);
431
432 }
433
434
435
436
437
438 public double computeSolarFlux(final Vector3D sunPosition) {
439
440
441 final double earthSunDistance = sunPosition.getNorm() / Constants.JPL_SSD_ASTRONOMICAL_UNIT;
442
443
444 return ES_COEFF * Constants.SPEED_OF_LIGHT / (earthSunDistance * earthSunDistance);
445 }
446
447
448
449
450
451
452
453 public <T extends CalculusFieldElement<T>> T computeSolarFlux(final FieldVector3D<T> sunPosition) {
454
455
456 final T earthSunDistance = sunPosition.getNorm().divide(Constants.JPL_SSD_ASTRONOMICAL_UNIT);
457
458
459 return earthSunDistance.multiply(earthSunDistance).reciprocal().multiply(ES_COEFF * Constants.SPEED_OF_LIGHT);
460 }
461
462
463
464
465
466
467
468
469
470 public Vector3D computeElementaryFlux(final SpacecraftState state,
471 final Vector3D elementCenter,
472 final Vector3D sunPosition,
473 final double elementArea) {
474
475
476 final Vector3D satellitePosition = state.getPosition();
477
478
479 final AbsoluteDate date = state.getDate();
480
481
482 final double solarFlux = computeSolarFlux(sunPosition);
483
484
485 final double centerNorm = elementCenter.getNorm();
486 final double cosAlpha = Vector3D.dotProduct(elementCenter, satellitePosition) /
487 (centerNorm * satellitePosition.getNorm());
488
489
490 if (cosAlpha > 0) {
491
492
493 final double currentLatitude = elementCenter.getDelta();
494
495
496 final double e = computeEmissivity(date, currentLatitude);
497
498
499 double a = 0.0;
500
501
502 final double cosSunAngle = Vector3D.dotProduct(elementCenter, sunPosition) /
503 (centerNorm * sunPosition.getNorm());
504
505 if (cosSunAngle > 0) {
506
507 a = computeAlbedo(date, currentLatitude);
508 }
509
510
511 final double albedoAndIR = a * solarFlux * cosSunAngle + e * solarFlux * 0.25;
512
513
514 final Vector3D r = satellitePosition.subtract(elementCenter);
515 final double rNorm = r.getNorm();
516
517
518 final Vector3D projectedAreaVector = r.scalarMultiply(elementArea * cosAlpha /
519 (FastMath.PI * rNorm * rNorm * rNorm));
520
521
522 return projectedAreaVector.scalarMultiply(albedoAndIR / Constants.SPEED_OF_LIGHT);
523
524 } else {
525
526
527 return new Vector3D(0.0, 0.0, 0.0);
528 }
529
530 }
531
532
533
534
535
536
537
538
539
540
541 public <T extends CalculusFieldElement<T>> FieldVector3D<T> computeElementaryFlux(final FieldSpacecraftState<T> state,
542 final FieldVector3D<T> elementCenter,
543 final FieldVector3D<T> sunPosition,
544 final T elementArea) {
545
546
547 final FieldVector3D<T> satellitePosition = state.getPosition();
548
549
550 final FieldAbsoluteDate<T> date = state.getDate();
551
552
553 final T zero = date.getField().getZero();
554
555
556 final T solarFlux = computeSolarFlux(sunPosition);
557
558
559 final T centerNorm = elementCenter.getNorm();
560 final T cosAlpha = FieldVector3D.dotProduct(elementCenter, satellitePosition).
561 divide(centerNorm.multiply(satellitePosition.getNorm()));
562
563
564 if (cosAlpha.getReal() > 0) {
565
566
567 final T currentLatitude = elementCenter.getDelta();
568
569
570 final T e = computeEmissivity(date, currentLatitude);
571
572
573 T a = zero;
574
575
576 final T cosSunAngle = FieldVector3D.dotProduct(elementCenter, sunPosition).
577 divide(centerNorm.multiply(sunPosition.getNorm()));
578
579 if (cosSunAngle.getReal() > 0) {
580
581 a = computeAlbedo(date, currentLatitude);
582 }
583
584
585 final T albedoAndIR = a.multiply(solarFlux).multiply(cosSunAngle).
586 add(e.multiply(solarFlux).multiply(0.25));
587
588
589 final FieldVector3D<T> r = satellitePosition.subtract(elementCenter);
590 final T rNorm = r.getNorm();
591
592
593 final FieldVector3D<T> projectedAreaVector = r.scalarMultiply(elementArea.multiply(cosAlpha).
594 divide(rNorm.square().multiply(rNorm).multiply(zero.getPi())));
595
596
597 return projectedAreaVector.scalarMultiply(albedoAndIR.divide(Constants.SPEED_OF_LIGHT));
598
599 } else {
600
601
602 return new FieldVector3D<>(zero, zero, zero);
603 }
604
605 }
606
607 }