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