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