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.Field;
21 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22 import org.hipparchus.geometry.euclidean.threed.Vector3D;
23 import org.hipparchus.util.FastMath;
24 import org.orekit.frames.Frame;
25 import org.orekit.propagation.FieldSpacecraftState;
26 import org.orekit.propagation.SpacecraftState;
27 import org.orekit.propagation.events.intervals.AdaptableInterval;
28 import org.orekit.propagation.events.EventDetector;
29 import org.orekit.propagation.events.EventDetectionSettings;
30 import org.orekit.propagation.events.intervals.FieldAdaptableInterval;
31 import org.orekit.propagation.events.FieldEventDetector;
32 import org.orekit.propagation.events.FieldEventDetectionSettings;
33 import org.orekit.propagation.events.handlers.EventHandler;
34 import org.orekit.propagation.events.handlers.FieldEventHandler;
35 import org.orekit.propagation.events.handlers.FieldResetDerivativesOnEvent;
36 import org.orekit.propagation.events.handlers.ResetDerivativesOnEvent;
37 import org.orekit.time.AbsoluteDate;
38 import org.orekit.time.FieldAbsoluteDate;
39 import org.orekit.utils.ExtendedPositionProvider;
40
41 import java.util.ArrayList;
42 import java.util.List;
43
44
45
46
47
48
49
50
51
52
53
54
55 public class ConicallyShadowedLightFluxModel extends AbstractSolarLightFluxModel {
56
57
58
59
60 private static final double CONICAL_ECLIPSE_MAX_CHECK = 60;
61
62
63
64
65 private static final double CONICAL_ECLIPSE_THRESHOLD = 1e-7;
66
67
68 private final double occultedBodyRadius;
69
70
71 private AbsoluteDate lastDate;
72
73
74 private Frame propagationFrame;
75
76
77 private Vector3D lastPosition;
78
79
80
81
82
83
84
85
86
87 public ConicallyShadowedLightFluxModel(final double kRef, final double occultedBodyRadius,
88 final ExtendedPositionProvider occultedBody,
89 final double occultingBodyRadius, final EventDetectionSettings eventDetectionSettings) {
90 super(kRef, occultedBody, occultingBodyRadius, eventDetectionSettings);
91 this.occultedBodyRadius = occultedBodyRadius;
92 }
93
94
95
96
97
98
99
100
101 public ConicallyShadowedLightFluxModel(final double kRef, final double occultedBodyRadius,
102 final ExtendedPositionProvider occultedBody, final double occultingBodyRadius) {
103 this(kRef, occultedBodyRadius, occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings());
104 }
105
106
107
108
109
110
111
112 public ConicallyShadowedLightFluxModel(final double occultedBodyRadius, final ExtendedPositionProvider occultedBody,
113 final double occultingBodyRadius) {
114 super(occultedBody, occultingBodyRadius, getDefaultEclipseDetectionSettings());
115 this.occultedBodyRadius = occultedBodyRadius;
116 }
117
118
119
120
121
122 public static EventDetectionSettings getDefaultEclipseDetectionSettings() {
123 return new EventDetectionSettings(CONICAL_ECLIPSE_MAX_CHECK, CONICAL_ECLIPSE_THRESHOLD,
124 EventDetectionSettings.DEFAULT_MAX_ITER);
125 }
126
127
128 @Override
129 public void init(final SpacecraftState initialState, final AbsoluteDate targetDate) {
130 super.init(initialState, targetDate);
131 lastDate = initialState.getDate();
132 propagationFrame = initialState.getFrame();
133 lastPosition = getOccultedBodyPosition(lastDate, propagationFrame);
134 }
135
136
137 @Override
138 public <T extends CalculusFieldElement<T>> void init(final FieldSpacecraftState<T> initialState,
139 final FieldAbsoluteDate<T> targetDate) {
140 super.init(initialState, targetDate);
141 lastDate = initialState.getDate().toAbsoluteDate();
142 propagationFrame = initialState.getFrame();
143 lastPosition = getOccultedBodyPosition(initialState.getDate(), propagationFrame).toVector3D();
144 }
145
146
147
148
149
150
151 private Vector3D getOccultedBodyPosition(final AbsoluteDate date) {
152 if (!lastDate.isEqualTo(date)) {
153 lastPosition = getOccultedBodyPosition(date, propagationFrame);
154 lastDate = date;
155 }
156 return lastPosition;
157 }
158
159
160
161
162
163
164
165 private <T extends CalculusFieldElement<T>> FieldVector3D<T> getOccultedBodyPosition(final FieldAbsoluteDate<T> fieldDate) {
166 if (fieldDate.hasZeroField()) {
167 final AbsoluteDate date = fieldDate.toAbsoluteDate();
168 if (!lastDate.isEqualTo(date)) {
169 lastPosition = getOccultedBodyPosition(date, propagationFrame);
170 lastDate = date;
171 }
172 return new FieldVector3D<>(fieldDate.getField(), lastPosition);
173 } else {
174 return getOccultedBodyPosition(fieldDate, propagationFrame);
175 }
176 }
177
178
179 @Override
180 protected double getLightingRatio(final Vector3D position, final Vector3D occultedBodyPosition) {
181 final double distanceSun = occultedBodyPosition.getNorm();
182 final double squaredDistance = position.getNormSq();
183 final Vector3D occultedBodyDirection = occultedBodyPosition.normalize();
184 final double s0 = -position.dotProduct(occultedBodyDirection);
185 if (s0 > 0.0) {
186 final double l = FastMath.sqrt(squaredDistance - s0 * s0);
187 final double sinf2 = (occultedBodyRadius - getOccultingBodyRadius()) / distanceSun;
188 final double l2 = (s0 * sinf2 - getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf2 * sinf2);
189 if (FastMath.abs(l2) - l >= 0.0) {
190 return 0.;
191 }
192 final double sinf1 = (occultedBodyRadius + getOccultingBodyRadius()) / distanceSun;
193 final double l1 = (s0 * sinf1 + getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf1 * sinf1);
194 if (l1 - l > 0.0) {
195 final Vector3D relativePosition = occultedBodyPosition.subtract(position);
196 final double relativeDistance = relativePosition.getNorm();
197 final double a = FastMath.asin(occultedBodyRadius / relativeDistance);
198 final double a2 = a * a;
199 final double r = FastMath.sqrt(squaredDistance);
200 final double b = FastMath.asin(getOccultingBodyRadius() / r);
201 final double c = FastMath.acos(-(relativePosition.dotProduct(position)) / (r * relativeDistance));
202 final double x = (c * c + a2 - b * b) / (2 * c);
203 final double y = FastMath.sqrt(FastMath.max(0., a2 - x * x));
204 final double arcCosXOverA = FastMath.acos(FastMath.max(-1, x / a));
205 final double intermediate = (arcCosXOverA + (b * b * FastMath.acos((c - x) / b) - c * y) / a2) / FastMath.PI;
206 return 1. - intermediate;
207 }
208 }
209 return 1.;
210 }
211
212
213 @Override
214 protected <T extends CalculusFieldElement<T>> T getLightingRatio(final FieldVector3D<T> position,
215 final FieldVector3D<T> occultedBodyPosition) {
216 final Field<T> field = position.getX().getField();
217 final T distanceSun = occultedBodyPosition.getNorm();
218 final T squaredDistance = position.getNormSq();
219 final FieldVector3D<T> occultedBodyDirection = occultedBodyPosition.normalize();
220 final T s0 = position.dotProduct(occultedBodyDirection).negate();
221 if (s0.getReal() > 0.0) {
222 final T reciprocalDistanceSun = distanceSun.reciprocal();
223 final T sinf2 = reciprocalDistanceSun.multiply(occultedBodyRadius - getOccultingBodyRadius());
224 final T l2 = (s0.multiply(sinf2).subtract(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf2.square().negate().add(1)));
225 final T l = FastMath.sqrt(squaredDistance.subtract(s0.square()));
226 if (FastMath.abs(l2).subtract(l).getReal() >= 0.0) {
227 return field.getZero();
228 }
229 final T sinf1 = reciprocalDistanceSun.multiply(occultedBodyRadius + getOccultingBodyRadius());
230 final T l1 = (s0.multiply(sinf1).add(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf1.square().negate().add(1)));
231 if (l1.subtract(l).getReal() > 0.0) {
232 final FieldVector3D<T> relativePosition = occultedBodyPosition.subtract(position);
233 final T relativeDistance = relativePosition.getNorm();
234 final T a = FastMath.asin(relativeDistance.reciprocal().multiply(occultedBodyRadius));
235 final T a2 = a.square();
236 final T r = FastMath.sqrt(squaredDistance);
237 final T b = FastMath.asin(r.reciprocal().multiply(getOccultingBodyRadius()));
238 final T b2 = b.square();
239 final T c = FastMath.acos((relativePosition.dotProduct(position).negate()).divide(r.multiply(relativeDistance)));
240 final T x = (c.square().add(a2).subtract(b2)).divide(c.multiply(2));
241 final T x2 = x.square();
242 final T y = (a2.getReal() - x2.getReal() <= 0) ? s0.getField().getZero() : FastMath.sqrt(a2.subtract(x2));
243 final T arcCosXOverA = (x.getReal() / a.getReal() < -1) ? s0.getPi().negate() : FastMath.acos(x.divide(a));
244 final T intermediate = arcCosXOverA.add(((b2.multiply(FastMath.acos((c.subtract(x)).divide(b))))
245 .subtract(c.multiply(y))).divide(a2));
246 return intermediate.divide(-FastMath.PI).add(1);
247 }
248 }
249 return field.getOne();
250 }
251
252
253 @Override
254 public List<EventDetector> getEclipseConditionsDetector() {
255 final List<EventDetector> detectors = new ArrayList<>();
256 detectors.add(createUmbraEclipseDetector());
257 detectors.add(createPenumbraEclipseDetector());
258 return detectors;
259 }
260
261
262
263
264
265 private InternalEclipseDetector createUmbraEclipseDetector() {
266 return new InternalEclipseDetector() {
267 @Override
268 public double g(final SpacecraftState s) {
269 final Vector3D position = s.getPosition();
270 final Vector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate());
271 final Vector3D occultedBodyDirection = occultedBodyPosition.normalize();
272 final double s0 = -position.dotProduct(occultedBodyDirection);
273 final double distanceSun = occultedBodyPosition.getNorm();
274 final double squaredDistance = position.getNormSq();
275 final double sinf2 = (occultedBodyRadius - getOccultingBodyRadius()) / distanceSun;
276 final double l = FastMath.sqrt(squaredDistance - s0 * s0);
277 final double l2 = (s0 * sinf2 - getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf2 * sinf2);
278 return FastMath.abs(l2) / l - 1.;
279 }
280 };
281 }
282
283
284
285
286
287 private InternalEclipseDetector createPenumbraEclipseDetector() {
288 return new InternalEclipseDetector() {
289 @Override
290 public double g(final SpacecraftState s) {
291 final Vector3D position = s.getPosition();
292 final Vector3D occultedBodyPosition = getOccultedBodyPosition(s.getDate());
293 final Vector3D occultedBodyDirection = occultedBodyPosition.normalize();
294 final double s0 = -position.dotProduct(occultedBodyDirection);
295 final double distanceSun = occultedBodyPosition.getNorm();
296 final double squaredDistance = position.getNormSq();
297 final double l = FastMath.sqrt(squaredDistance - s0 * s0);
298 final double sinf1 = (occultedBodyRadius + getOccultingBodyRadius()) / distanceSun;
299 final double l1 = (s0 * sinf1 + getOccultingBodyRadius()) / FastMath.sqrt(1.0 - sinf1 * sinf1);
300 return l1 / l - 1.;
301 }
302 };
303 }
304
305
306
307
308 private abstract class InternalEclipseDetector implements EventDetector {
309
310 private final ResetDerivativesOnEvent handler;
311
312
313
314
315 InternalEclipseDetector() {
316 this.handler = new ResetDerivativesOnEvent();
317 }
318
319 @Override
320 public EventDetectionSettings getDetectionSettings() {
321 return getEventDetectionSettings();
322 }
323
324 @Override
325 public double getThreshold() {
326 return getDetectionSettings().getThreshold();
327 }
328
329 @Override
330 public AdaptableInterval getMaxCheckInterval() {
331 return getDetectionSettings().getMaxCheckInterval();
332 }
333
334 @Override
335 public int getMaxIterationCount() {
336 return getDetectionSettings().getMaxIterationCount();
337 }
338
339 @Override
340 public EventHandler getHandler() {
341 return handler;
342 }
343 }
344
345
346 @Override
347 public <T extends CalculusFieldElement<T>> List<FieldEventDetector<T>> getFieldEclipseConditionsDetector(final Field<T> field) {
348 final List<FieldEventDetector<T>> detectors = new ArrayList<>();
349 final FieldEventDetectionSettings<T> detectionSettings = new FieldEventDetectionSettings<>(field,
350 getEventDetectionSettings());
351 detectors.add(createFieldUmbraEclipseDetector(detectionSettings));
352 detectors.add(createFieldPenumbraEclipseDetector(detectionSettings));
353 return detectors;
354 }
355
356
357
358
359
360
361
362 private <T extends CalculusFieldElement<T>> FieldInternalEclipseDetector<T> createFieldUmbraEclipseDetector(final FieldEventDetectionSettings<T> detectionSettings) {
363 return new FieldInternalEclipseDetector<T>(detectionSettings) {
364 @Override
365 public T g(final FieldSpacecraftState<T> s) {
366 final FieldVector3D<T> position = s.getPosition();
367 final FieldVector3D<T> occultedBodyPosition = getOccultedBodyPosition(s.getDate());
368 final FieldVector3D<T> occultedBodyDirection = occultedBodyPosition.normalize();
369 final T s0 = position.dotProduct(occultedBodyDirection).negate();
370 final T distanceSun = occultedBodyPosition.getNorm();
371 final T squaredDistance = position.getNormSq();
372 final T reciprocalDistanceSun = distanceSun.reciprocal();
373 final T sinf2 = reciprocalDistanceSun.multiply(occultedBodyRadius - getOccultingBodyRadius());
374 final T l2 = (s0.multiply(sinf2).subtract(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf2.square().negate().add(1)));
375 final T l = FastMath.sqrt(squaredDistance.subtract(s0.square()));
376 return FastMath.abs(l2).divide(l).subtract(1);
377 }
378 };
379 }
380
381
382
383
384
385
386
387 private <T extends CalculusFieldElement<T>> FieldInternalEclipseDetector<T> createFieldPenumbraEclipseDetector(final FieldEventDetectionSettings<T> detectionSettings) {
388 return new FieldInternalEclipseDetector<T>(detectionSettings) {
389 @Override
390 public T g(final FieldSpacecraftState<T> s) {
391 final FieldVector3D<T> position = s.getPosition();
392 final FieldVector3D<T> occultedBodyPosition = getOccultedBodyPosition(s.getDate());
393 final FieldVector3D<T> occultedBodyDirection = occultedBodyPosition.normalize();
394 final T s0 = position.dotProduct(occultedBodyDirection).negate();
395 final T distanceSun = occultedBodyPosition.getNorm();
396 final T squaredDistance = position.getNormSq();
397 final T reciprocalDistanceSun = distanceSun.reciprocal();
398 final T sinf1 = reciprocalDistanceSun.multiply(occultedBodyRadius + getOccultingBodyRadius());
399 final T l1 = (s0.multiply(sinf1).add(getOccultingBodyRadius())).divide(FastMath.sqrt(sinf1.square().negate().add(1)));
400 final T l = FastMath.sqrt(squaredDistance.subtract(s0.square()));
401 return l1.divide(l).subtract(1);
402 }
403 };
404 }
405
406
407
408
409 private abstract static class FieldInternalEclipseDetector<T extends CalculusFieldElement<T>> implements FieldEventDetector<T> {
410
411 private final FieldResetDerivativesOnEvent<T> handler;
412
413
414 private final FieldEventDetectionSettings<T> fieldEventDetectionSettings;
415
416
417
418
419
420 FieldInternalEclipseDetector(final FieldEventDetectionSettings<T> fieldEventDetectionSettings) {
421 this.handler = new FieldResetDerivativesOnEvent<>();
422 this.fieldEventDetectionSettings = fieldEventDetectionSettings;
423 }
424
425 @Override
426 public FieldEventDetectionSettings<T> getDetectionSettings() {
427 return fieldEventDetectionSettings;
428 }
429
430 @Override
431 public T getThreshold() {
432 return getDetectionSettings().getThreshold();
433 }
434
435 @Override
436 public FieldAdaptableInterval<T> getMaxCheckInterval() {
437 return getDetectionSettings().getMaxCheckInterval();
438 }
439
440 @Override
441 public int getMaxIterationCount() {
442 return getDetectionSettings().getMaxIterationCount();
443 }
444
445 @Override
446 public FieldEventHandler<T> getHandler() {
447 return handler;
448 }
449 }
450 }