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