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.lang.reflect.Array;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.stream.Stream;
24
25 import org.hipparchus.CalculusFieldElement;
26 import org.hipparchus.Field;
27 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28 import org.hipparchus.geometry.euclidean.threed.Vector3D;
29 import org.hipparchus.ode.events.Action;
30 import org.hipparchus.util.FastMath;
31 import org.hipparchus.util.MathArrays;
32 import org.orekit.bodies.OneAxisEllipsoid;
33 import org.orekit.forces.ForceModel;
34 import org.orekit.frames.Frame;
35 import org.orekit.propagation.events.EclipseDetector;
36 import org.orekit.propagation.events.EventDetector;
37 import org.orekit.propagation.events.FieldEclipseDetector;
38 import org.orekit.propagation.events.FieldEventDetector;
39 import org.orekit.utils.Constants;
40 import org.orekit.utils.ExtendedPVCoordinatesProvider;
41 import org.orekit.utils.ExtendedPVCoordinatesProviderAdapter;
42 import org.orekit.utils.OccultationEngine;
43
44
45
46
47
48
49
50 public abstract class AbstractRadiationForceModel implements ForceModel {
51
52
53 private static final double ANGULAR_MARGIN = 1.0e-10;
54
55
56 private static final double ECLIPSE_MAX_CHECK = 60.0;
57
58
59 private static final double ECLIPSE_THRESHOLD = 1.0e-7;
60
61
62 private static final double SPHERICAL_BODY_FLATNESS = 0.0;
63
64
65 private static final String OCCULTING_PREFIX = "occulting-";
66
67
68
69
70 private final List<OccultationEngine> occultingBodies;
71
72
73
74
75
76
77
78
79 protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, final OneAxisEllipsoid centralBody) {
80
81 occultingBodies = new ArrayList<>(2);
82 occultingBodies.add(new OccultationEngine(sun, Constants.SUN_RADIUS, centralBody));
83 }
84
85
86 @Override
87 public boolean dependsOnPositionOnly() {
88 return false;
89 }
90
91
92 @Override
93 public Stream<EventDetector> getEventDetectors() {
94 final EventDetector[] detectors = new EventDetector[2 * occultingBodies.size()];
95 for (int i = 0; i < occultingBodies.size(); ++i) {
96 final OccultationEngine occulting = occultingBodies.get(i);
97 detectors[2 * i] = new EclipseDetector(occulting).
98 withUmbra().
99 withMargin(-ANGULAR_MARGIN).
100 withMaxCheck(ECLIPSE_MAX_CHECK).
101 withThreshold(ECLIPSE_THRESHOLD).
102 withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
103 detectors[2 * i + 1] = new EclipseDetector(occulting).
104 withPenumbra().
105 withMargin(ANGULAR_MARGIN).
106 withMaxCheck(ECLIPSE_MAX_CHECK).
107 withThreshold(ECLIPSE_THRESHOLD).
108 withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
109 }
110
111
112 return Stream.concat(Stream.of(detectors), ForceModel.super.getEventDetectors());
113 }
114
115
116 @Override
117 public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
118 final T zero = field.getZero();
119 @SuppressWarnings("unchecked")
120 final FieldEventDetector<T>[] detectors = (FieldEventDetector<T>[]) Array.newInstance(FieldEventDetector.class,
121 2 * occultingBodies.size());
122 for (int i = 0; i < occultingBodies.size(); ++i) {
123 final OccultationEngine occulting = occultingBodies.get(i);
124 detectors[2 * i] = new FieldEclipseDetector<>(field, occulting).
125 withUmbra().
126 withMargin(zero.newInstance(-ANGULAR_MARGIN)).
127 withMaxCheck(ECLIPSE_MAX_CHECK).
128 withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)).
129 withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
130 detectors[2 * i + 1] = new FieldEclipseDetector<>(field, occulting).
131 withPenumbra().
132 withMargin(zero.newInstance(ANGULAR_MARGIN)).
133 withMaxCheck(ECLIPSE_MAX_CHECK).
134 withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)).
135 withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES);
136 }
137 return Stream.concat(Stream.of(detectors), ForceModel.super.getFieldEventDetectors(field));
138 }
139
140
141
142
143
144
145
146
147
148
149 protected double[] getGeneralEclipseAngles(final Vector3D position, final Vector3D occultingPosition, final double occultingRadius,
150 final Vector3D occultedPosition, final double occultedRadius) {
151 final double[] angle = new double[3];
152
153 final Vector3D satOccultedVector = occultedPosition.subtract(position);
154 final Vector3D satOccultingVector = occultingPosition.subtract(position);
155
156
157 angle[0] = Vector3D.angle(satOccultedVector, satOccultingVector);
158
159
160 angle[1] = FastMath.asin(occultingRadius / satOccultingVector.getNorm());
161
162
163 angle[2] = FastMath.asin(occultedRadius / satOccultedVector.getNorm());
164
165 return angle;
166 }
167
168
169
170
171
172
173
174
175
176
177
178 protected <T extends CalculusFieldElement<T>> T[] getGeneralEclipseAngles(final FieldVector3D<T> position,
179 final FieldVector3D<T> occultingPosition, final T occultingRadius,
180 final FieldVector3D<T> occultedPosition, final T occultedRadius) {
181 final T[] angle = MathArrays.buildArray(position.getX().getField(), 3);
182
183 final FieldVector3D<T> satOccultedVector = occultedPosition.subtract(position);
184 final FieldVector3D<T> satOccultingVector = occultingPosition.subtract(position);
185
186
187 angle[0] = FieldVector3D.angle(satOccultedVector, satOccultingVector);
188
189
190 angle[1] = occultingRadius.divide(satOccultingVector.getNorm()).asin();
191
192
193 angle[2] = occultedRadius.divide(satOccultedVector.getNorm()).asin();
194
195 return angle;
196 }
197
198
199
200
201
202
203
204
205
206
207 public void addOccultingBody(final ExtendedPVCoordinatesProvider provider, final double radius) {
208
209
210
211 Frame parent = occultingBodies.get(0).getOcculting().getBodyFrame();
212 while (!parent.isPseudoInertial()) {
213 parent = parent.getParent();
214 }
215
216
217 final Frame inertiallyOrientedBodyFrame =
218 new ExtendedPVCoordinatesProviderAdapter(parent,
219 provider,
220 OCCULTING_PREFIX + occultingBodies.size());
221
222
223 final OneAxisEllipsoid sphericalOccultingBody =
224 new OneAxisEllipsoid(radius, SPHERICAL_BODY_FLATNESS, inertiallyOrientedBodyFrame);
225
226 addOccultingBody(sphericalOccultingBody);
227
228 }
229
230
231
232
233
234
235
236
237
238
239 public void addOccultingBody(final OneAxisEllipsoid occulting) {
240
241
242 final OccultationEngine central = occultingBodies.get(0);
243
244
245 final OccultationEngine additional =
246 new OccultationEngine(central.getOcculted(), central.getOccultedRadius(), occulting);
247
248
249 occultingBodies.add(additional);
250
251 }
252
253
254
255
256
257
258
259
260
261
262 public List<OccultationEngine> getOccultingBodies() {
263 return Collections.unmodifiableList(occultingBodies);
264 }
265
266 }