1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.attitudes;
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.Rotation;
23 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
24 import org.hipparchus.geometry.euclidean.threed.Vector3D;
25 import org.hipparchus.ode.events.Action;
26 import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
27 import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
28 import org.hipparchus.util.Binary64Field;
29 import org.hipparchus.util.FastMath;
30 import org.junit.jupiter.api.Assertions;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33 import org.orekit.Utils;
34 import org.orekit.bodies.CelestialBodyFactory;
35 import org.orekit.bodies.GeodeticPoint;
36 import org.orekit.bodies.OneAxisEllipsoid;
37 import org.orekit.errors.OrekitException;
38 import org.orekit.errors.OrekitMessages;
39 import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
40 import org.orekit.forces.gravity.potential.GravityFieldFactory;
41 import org.orekit.forces.gravity.potential.ICGEMFormatReader;
42 import org.orekit.frames.FramesFactory;
43 import org.orekit.frames.LOFType;
44 import org.orekit.frames.TopocentricFrame;
45 import org.orekit.orbits.FieldKeplerianOrbit;
46 import org.orekit.orbits.FieldOrbit;
47 import org.orekit.orbits.KeplerianOrbit;
48 import org.orekit.orbits.Orbit;
49 import org.orekit.propagation.*;
50 import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
51 import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator;
52 import org.orekit.propagation.analytical.KeplerianPropagator;
53 import org.orekit.propagation.events.*;
54 import org.orekit.propagation.events.handlers.ContinueOnEvent;
55 import org.orekit.propagation.events.handlers.CountAndContinue;
56 import org.orekit.propagation.numerical.NumericalPropagator;
57 import org.orekit.time.AbsoluteDate;
58 import org.orekit.time.FieldAbsoluteDate;
59 import org.orekit.time.TimeScalesFactory;
60 import org.orekit.utils.*;
61
62 import java.util.ArrayList;
63 import java.util.List;
64
65 import static org.mockito.Mockito.mock;
66 import static org.mockito.Mockito.when;
67
68 public class AttitudesSequenceTest {
69
70 private AbsoluteDate lastChange;
71 private boolean inEclipse;
72
73 @Test
74 void testDayNightSwitch() {
75
76 final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
77 final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
78 final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231);
79 final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
80 FramesFactory.getEME2000(), initialDate,
81 Constants.EIGEN5C_EARTH_MU);
82
83 final
84
85
86 EventsLogger logger = new EventsLogger();
87 final AttitudesSequence attitudesSequence = new AttitudesSequence();
88 final AttitudeProvider dayObservationLaw = new LofOffset(initialOrbit.getFrame(), LOFType.LVLH_CCSDS,
89 RotationOrder.XYZ, FastMath.toRadians(20), FastMath.toRadians(40), 0);
90 final AttitudeProvider nightRestingLaw = new LofOffset(initialOrbit.getFrame(), LOFType.LVLH_CCSDS);
91 final ExtendedPositionProvider sun = CelestialBodyFactory.getSun();
92 final EclipseDetector ed =
93 new EclipseDetector(sun, 696000000.,
94 new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
95 0.0,
96 FramesFactory.getGTOD(IERSConventions.IERS_2010, true))).
97 withMaxCheck(600.).
98 withHandler(new ContinueOnEvent() {
99 public Action eventOccurred(final SpacecraftState s, final EventDetector d, final boolean increasing) {
100 setInEclipse(s.getDate(), !increasing);
101 return Action.RESET_STATE;
102 }
103 });
104 final EventDetector monitored = logger.monitorDetector(ed);
105 final Handler dayToNightHandler = new Handler(dayObservationLaw, nightRestingLaw);
106 final Handler nightToDayHandler = new Handler(nightRestingLaw, dayObservationLaw);
107 final double transitionTime = 300.;
108 attitudesSequence.addSwitchingCondition(dayObservationLaw, nightRestingLaw,
109 monitored, false, true, transitionTime,
110 AngularDerivativesFilter.USE_RRA, dayToNightHandler);
111 attitudesSequence.addSwitchingCondition(nightRestingLaw, dayObservationLaw,
112 monitored, true, false, transitionTime,
113 AngularDerivativesFilter.USE_RRA, nightToDayHandler);
114 SpacecraftState initialState = new SpacecraftState(initialOrbit);
115 initialState = initialState.addAdditionalData("fortyTwo", 42.0);
116 if (ed.g(initialState) >= 0) {
117
118 setInEclipse(initialDate, false);
119 attitudesSequence.resetActiveProvider(dayObservationLaw);
120 } else {
121
122 setInEclipse(initialDate, true);
123 attitudesSequence.resetActiveProvider(nightRestingLaw);
124 }
125
126
127 final Propagator propagator = new EcksteinHechlerPropagator(initialOrbit, attitudesSequence,
128 Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
129 Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
130 Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
131 Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
132
133 propagator.setStepHandler(60.0, currentState -> {
134
135
136 final Vector3D earth = currentState.toStaticTransform().transformPosition(Vector3D.ZERO);
137 final double pointingOffset = Vector3D.angle(earth, Vector3D.PLUS_K);
138
139
140
141 final double eclipseAngle = ed.g(currentState);
142
143 if (currentState.getDate().durationFrom(lastChange) > 300) {
144 if (inEclipse) {
145 Assertions.assertTrue(eclipseAngle <= 0);
146 Assertions.assertEquals(0.0, pointingOffset, 1.0e-6);
147 } else {
148 Assertions.assertTrue(eclipseAngle >= 0);
149 Assertions.assertEquals(0.767215, pointingOffset, 1.0e-6);
150 }
151 } else {
152
153 Assertions.assertTrue(pointingOffset <= 0.7672155,
154 pointingOffset + " " + (0.767215 - pointingOffset));
155 }
156 });
157
158
159 propagator.propagate(initialDate.shiftedBy(initialOrbit.getKeplerianPeriod() * 2));
160
161
162
163
164 Assertions.assertEquals(8, logger.getLoggedEvents().size());
165
166
167 Assertions.assertEquals(2, dayToNightHandler.dates.size());
168 Assertions.assertEquals(2, nightToDayHandler.dates.size());
169
170 }
171
172 @Test
173 void testDayNightSwitchField() {
174 doTestDayNightSwitchField(Binary64Field.getInstance());
175 }
176
177 private <T extends CalculusFieldElement<T>> void doTestDayNightSwitchField(final Field<T> field)
178 {
179
180
181 final FieldAbsoluteDate<T> initialDate = new FieldAbsoluteDate<>(field, 2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
182 final FieldVector3D<T> position = new FieldVector3D<>(field,
183 new Vector3D(-6142438.668, 3492467.560, -25767.25680));
184 final FieldVector3D<T> velocity = new FieldVector3D<>(field,
185 new Vector3D(505.8479685, 942.7809215, 7435.922231));
186 final FieldOrbit<T> initialOrbit = new FieldKeplerianOrbit<>(new FieldPVCoordinates<>(position, velocity),
187 FramesFactory.getEME2000(), initialDate,
188 field.getZero().add(Constants.EIGEN5C_EARTH_MU));
189
190
191 EventsLogger logger = new EventsLogger();
192 final AttitudesSequence attitudesSequence = new AttitudesSequence();
193 final AttitudeProvider dayObservationLaw = new LofOffset(initialOrbit.getFrame(), LOFType.LVLH_CCSDS,
194 RotationOrder.XYZ, FastMath.toRadians(20), FastMath.toRadians(40), 0);
195 final AttitudeProvider nightRestingLaw = new LofOffset(initialOrbit.getFrame(), LOFType.LVLH_CCSDS);
196 final ExtendedPositionProvider sun = CelestialBodyFactory.getSun();
197 final EclipseDetector ed =
198 new EclipseDetector(sun, 696000000.,
199 new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
200 0.0,
201 FramesFactory.getGTOD(IERSConventions.IERS_2010, true))).
202 withMaxCheck(600).
203 withHandler(new CountAndContinue(0) {
204 @Override
205 public Action eventOccurred(final SpacecraftState s,
206 final EventDetector d,
207 final boolean increasing) {
208 setInEclipse(s.getDate(), !increasing);
209 if (getCount() == 7) {
210 increment();
211 return Action.STOP;
212 } else {
213 switch (getCount() % 3) {
214 case 0 :
215 return Action.CONTINUE;
216 case 1 :
217 return Action.RESET_DERIVATIVES;
218 default :
219 return Action.RESET_STATE;
220 }
221 }
222 }
223 });
224 final EventDetector monitored = logger.monitorDetector(ed);
225 final Handler dayToNightHandler = new Handler(dayObservationLaw, nightRestingLaw);
226 final Handler nightToDayHandler = new Handler(nightRestingLaw, dayObservationLaw);
227 final double transitionTime = 300.;
228 attitudesSequence.addSwitchingCondition(dayObservationLaw, nightRestingLaw,
229 monitored, false, true, transitionTime,
230 AngularDerivativesFilter.USE_RRA, dayToNightHandler);
231 attitudesSequence.addSwitchingCondition(nightRestingLaw, dayObservationLaw,
232 monitored, true, false, transitionTime,
233 AngularDerivativesFilter.USE_RRA, nightToDayHandler);
234 FieldSpacecraftState<T> initialState = new FieldSpacecraftState<>(initialOrbit);
235 initialState = initialState.addAdditionalData("fortyTwo", field.getZero().add(42.0));
236 if (ed.g(initialState.toSpacecraftState()) >= 0) {
237
238 setInEclipse(initialDate.toAbsoluteDate(), false);
239 attitudesSequence.resetActiveProvider(dayObservationLaw);
240 } else {
241
242 setInEclipse(initialDate.toAbsoluteDate(), true);
243 attitudesSequence.resetActiveProvider(nightRestingLaw);
244 }
245
246
247 final FieldPropagator<T> propagator = new FieldEcksteinHechlerPropagator<>(initialOrbit, attitudesSequence,
248 Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
249 field.getZero().add(Constants.EIGEN5C_EARTH_MU),
250 Constants.EIGEN5C_EARTH_C20,
251 Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
252 Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
253
254
255 propagator.setStepHandler(field.getZero().add(60.0), currentState -> {
256
257
258 final FieldVector3D<T> earth = currentState.toTransform().transformPosition(Vector3D.ZERO);
259 final T pointingOffset = FieldVector3D.angle(earth, Vector3D.PLUS_K);
260
261
262
263 final double eclipseAngle = ed.g(currentState.toSpacecraftState());
264
265 if (currentState.getDate().durationFrom(lastChange).getReal() > 300) {
266 if (inEclipse) {
267 Assertions.assertTrue(eclipseAngle <= 0);
268 Assertions.assertEquals(0.0, pointingOffset.getReal(), 1.0e-6);
269 } else {
270 Assertions.assertTrue(eclipseAngle >= 0);
271 Assertions.assertEquals(0.767215, pointingOffset.getReal(), 1.0e-6);
272 }
273 } else {
274
275 Assertions.assertTrue(pointingOffset.getReal() <= 0.7672155,
276 pointingOffset.getReal() + " " + (0.767215 - pointingOffset.getReal()));
277 }
278 });
279
280
281 propagator.propagate(initialDate.shiftedBy(12600.));
282
283
284
285
286 Assertions.assertEquals(8, logger.getLoggedEvents().size());
287
288
289 Assertions.assertEquals(2, dayToNightHandler.dates.size());
290 Assertions.assertEquals(2, nightToDayHandler.dates.size());
291
292 }
293
294 @Test
295 void testBackwardPropagation() {
296
297
298 final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
299 final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
300 final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231);
301 final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
302 FramesFactory.getEME2000(), initialDate,
303 Constants.EIGEN5C_EARTH_MU);
304
305 final AttitudesSequence attitudesSequence = new AttitudesSequence();
306 final AttitudeProvider past = new FrameAlignedProvider(Rotation.IDENTITY);
307 final AttitudeProvider current = new FrameAlignedProvider(Rotation.IDENTITY);
308 final AttitudeProvider future = new FrameAlignedProvider(Rotation.IDENTITY);
309 final Handler handler = new Handler(current, past);
310 attitudesSequence.addSwitchingCondition(past, current,
311 new DateDetector(initialDate.shiftedBy(-500.0)),
312 true, false, 10.0, AngularDerivativesFilter.USE_R, handler);
313 attitudesSequence.addSwitchingCondition(current, future,
314 new DateDetector(initialDate.shiftedBy(+500.0)),
315 true, false, 10.0, AngularDerivativesFilter.USE_R, null);
316 attitudesSequence.resetActiveProvider(current);
317
318 SpacecraftState initialState = new SpacecraftState(initialOrbit);
319 initialState = initialState.addAdditionalData("fortyTwo", 42.0);
320 final Propagator propagator = new EcksteinHechlerPropagator(initialOrbit, attitudesSequence,
321 Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
322 Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20,
323 Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
324 Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
325 propagator.resetInitialState(initialState);
326 Assertions.assertEquals(42.0, propagator.getInitialState().getAdditionalState("fortyTwo")[0], 1.0e-10);
327
328 SpacecraftState finalState = propagator.propagate(initialDate.shiftedBy(-10000.0));
329 Assertions.assertEquals(42.0, finalState.getAdditionalState("fortyTwo")[0], 1.0e-10);
330 Assertions.assertEquals(1, handler.dates.size());
331 Assertions.assertEquals(-500.0, handler.dates.get(0).durationFrom(initialDate), 1.0e-3);
332 Assertions.assertEquals(-490.0, finalState.getDate().durationFrom(initialDate), 1.0e-3);
333
334 }
335
336 @Test
337 void testTooShortTransition() {
338 double threshold = 1.5;
339 double transitionTime = 0.5;
340 try {
341 new AttitudesSequence().addSwitchingCondition(new FrameAlignedProvider(Rotation.IDENTITY),
342 new FrameAlignedProvider(Rotation.IDENTITY),
343 new DateDetector(AbsoluteDate.J2000_EPOCH).
344 withMaxCheck(1000.0).
345 withThreshold(threshold),
346 true, false, transitionTime,
347 AngularDerivativesFilter.USE_R, null);
348 Assertions.fail("an exception should have been thrown");
349 } catch (OrekitException oe) {
350 Assertions.assertEquals(OrekitMessages.TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH, oe.getSpecifier());
351 Assertions.assertEquals(transitionTime, (Double) oe.getParts()[0], 1.0e-10);
352 Assertions.assertEquals(threshold, (Double) oe.getParts()[1], 1.0e-10);
353 }
354 }
355
356 @Test
357 void testOutOfSyncCalls() {
358
359 final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
360 final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
361 final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231);
362 final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
363 FramesFactory.getEME2000(), initialDate,
364 Constants.EIGEN5C_EARTH_MU);
365
366 final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
367 Constants.WGS84_EARTH_FLATTENING,
368 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
369 final TopocentricFrame volgograd = new TopocentricFrame(earth,
370 new GeodeticPoint(FastMath.toRadians(48.7),
371 FastMath.toRadians(44.5),
372 24.0),
373 "Волгоград");
374 final AttitudesSequence attitudesSequence = new AttitudesSequence();
375 final double transitionTime = 250.0;
376 final AttitudeProvider nadirPointing = new NadirPointing(initialOrbit.getFrame(), earth);
377 final AttitudeProvider targetPointing = new TargetPointing(initialOrbit.getFrame(), volgograd.getPoint(), earth);
378 final ElevationDetector eventDetector = new ElevationDetector(volgograd).
379 withConstantElevation(FastMath.toRadians(5.0)).
380 withHandler(new ContinueOnEvent());
381 final Handler nadirToTarget = new Handler(nadirPointing, targetPointing);
382 attitudesSequence.addSwitchingCondition(nadirPointing, targetPointing, eventDetector,
383 true, false, transitionTime, AngularDerivativesFilter.USE_RR,
384 nadirToTarget);
385 final Handler targetToNadir = new Handler(targetPointing, nadirPointing);
386 attitudesSequence.addSwitchingCondition(targetPointing, nadirPointing, eventDetector,
387 false, true, transitionTime, AngularDerivativesFilter.USE_RR,
388 targetToNadir);
389 final double[][] tolerance = ToleranceProvider.getDefaultToleranceProvider(10.).getTolerances(initialOrbit, initialOrbit.getType());
390 final AdaptiveStepsizeIntegrator integrator = new DormandPrince853Integrator(0.001, 300.0, tolerance[0], tolerance[1]);
391 final NumericalPropagator propagator = new NumericalPropagator(integrator);
392 GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
393 propagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(),
394 GravityFieldFactory.getNormalizedProvider(8, 8)));
395 propagator.setInitialState(new SpacecraftState(initialOrbit,
396 nadirPointing.getAttitude(initialOrbit,
397 initialOrbit.getDate(),
398 initialOrbit.getFrame())));
399 propagator.setAttitudeProvider(attitudesSequence);
400 propagator.setStepHandler(10, state -> {
401
402 Attitude nadirAttitude = nadirPointing.getAttitude(state.getOrbit(), state.getDate(), state.getFrame());
403 Attitude targetAttitude = targetPointing.getAttitude(state.getOrbit(), state.getDate(), state.getFrame());
404 Attitude stateAttitude = state.getAttitude();
405
406 if (nadirToTarget.dates.isEmpty() || state.getDate().durationFrom(nadirToTarget.dates.get(0)) < 0) {
407
408 checkEqualAttitudes(nadirAttitude, stateAttitude);
409 } else if (state.getDate().durationFrom(nadirToTarget.dates.get(0)) <= transitionTime) {
410
411 checkBetweenAttitudes(nadirAttitude, targetAttitude, stateAttitude);
412 } else if (targetToNadir.dates.isEmpty() || state.getDate().durationFrom(targetToNadir.dates.get(0)) < 0) {
413
414 checkEqualAttitudes(targetAttitude, stateAttitude);
415 } else if (state.getDate().durationFrom(targetToNadir.dates.get(0)) <= transitionTime) {
416
417 checkBetweenAttitudes(targetAttitude, nadirAttitude, stateAttitude);
418 } else {
419
420 checkEqualAttitudes(nadirAttitude, stateAttitude);
421 }
422
423 });
424 propagator.propagate(initialDate.shiftedBy(6000));
425
426 }
427
428
429
430
431 @Test
432 void testResetDuringTransitionForward() {
433
434 final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
435 final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
436 final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231);
437 final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
438 FramesFactory.getEME2000(), initialDate,
439 Constants.EIGEN5C_EARTH_MU);
440
441 final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
442 Constants.WGS84_EARTH_FLATTENING,
443 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
444 final TopocentricFrame volgograd = new TopocentricFrame(earth,
445 new GeodeticPoint(FastMath.toRadians(48.7),
446 FastMath.toRadians(44.5),
447 24.0),
448 "Волгоград");
449 final AttitudesSequence attitudesSequence = new AttitudesSequence();
450 final double transitionTime = 250.0;
451 final AttitudeProvider nadirPointing = new NadirPointing(initialOrbit.getFrame(), earth);
452 final AttitudeProvider targetPointing = new TargetPointing(initialOrbit.getFrame(), volgograd.getPoint(), earth);
453 final ElevationDetector eventDetector = new ElevationDetector(volgograd).
454 withConstantElevation(FastMath.toRadians(5.0)).
455 withHandler(new ContinueOnEvent());
456 final List<AbsoluteDate> nadirToTarget = new ArrayList<>();
457 attitudesSequence.addSwitchingCondition(nadirPointing, targetPointing, eventDetector,
458 true, false, transitionTime, AngularDerivativesFilter.USE_RR,
459 (previous, next, state) -> nadirToTarget.add(state.getDate()));
460 final double[][] tolerance = ToleranceProvider.getDefaultToleranceProvider(10.).getTolerances(initialOrbit, initialOrbit.getType());
461 final AdaptiveStepsizeIntegrator integrator = new DormandPrince853Integrator(0.001, 300.0, tolerance[0], tolerance[1]);
462 final NumericalPropagator propagator = new NumericalPropagator(integrator);
463 GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
464 propagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(),
465 GravityFieldFactory.getNormalizedProvider(8, 8)));
466 propagator.setInitialState(new SpacecraftState(initialOrbit,
467 nadirPointing.getAttitude(initialOrbit,
468 initialOrbit.getDate(),
469 initialOrbit.getFrame())));
470 propagator.setAttitudeProvider(attitudesSequence);
471 propagator.propagate(initialDate.shiftedBy(6000));
472
473
474
475
476 final AbsoluteDate midTransition = nadirToTarget.get(0).shiftedBy(0.5 * transitionTime);
477 SpacecraftState state = propagator.propagate(midTransition.shiftedBy(-60), midTransition);
478 Rotation nadirR = nadirPointing.getAttitude(state.getOrbit(), state.getDate(), state.getFrame()).getRotation();
479 Rotation targetR = targetPointing.getAttitude(state.getOrbit(), state.getDate(), state.getFrame()).getRotation();
480 final double reorientationAngle = Rotation.distance(nadirR, targetR);
481 Assertions.assertEquals(0.5 * reorientationAngle, Rotation.distance(state.getAttitude().getRotation(), nadirR),
482 0.03 * reorientationAngle);
483
484
485
486
487
488 final AbsoluteDate afterTransition = midTransition.shiftedBy(transitionTime);
489 state = propagator.propagate(midTransition, afterTransition);
490 targetR = targetPointing.getAttitude(state.getOrbit(), state.getDate(), state.getFrame()).getRotation();
491
492 Assertions.assertEquals(targetR.getQ0(), state.getAttitude().getRotation().getQ0(), 1.0e-16);
493 Assertions.assertEquals(targetR.getQ1(), state.getAttitude().getRotation().getQ1(), 1.0e-16);
494 Assertions.assertEquals(targetR.getQ2(), state.getAttitude().getRotation().getQ2(), 1.0e-16);
495 Assertions.assertEquals(targetR.getQ3(), state.getAttitude().getRotation().getQ3(), 1.0e-16);
496 }
497
498 @Test
499 void testResetDuringTransitionBackward() {
500
501 final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC());
502 final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680);
503 final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231);
504 final AbsolutePVCoordinates initialPV = new AbsolutePVCoordinates(FramesFactory.getEME2000(), initialDate, new PVCoordinates(position, velocity));
505
506 final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
507 Constants.WGS84_EARTH_FLATTENING,
508 FramesFactory.getITRF(IERSConventions.IERS_2010, true));
509 final TopocentricFrame volgograd = new TopocentricFrame(earth,
510 new GeodeticPoint(FastMath.toRadians(48.7),
511 FastMath.toRadians(44.5),
512 24.0),
513 "Волгоград");
514 final AttitudesSequence attitudesSequence = new AttitudesSequence();
515 final double transitionTime = 250.0;
516 final AttitudeProvider nadirPointing = new NadirPointing(initialPV.getFrame(), earth);
517 final AttitudeProvider targetPointing = new TargetPointing(initialPV.getFrame(), volgograd.getPoint(), earth);
518 final ElevationDetector eventDetector = new ElevationDetector(volgograd).
519 withConstantElevation(FastMath.toRadians(5.0)).
520 withHandler(new ContinueOnEvent());
521 final List<AbsoluteDate> nadirToTarget = new ArrayList<>();
522 attitudesSequence.addSwitchingCondition(nadirPointing, targetPointing, eventDetector,
523 true, false, transitionTime, AngularDerivativesFilter.USE_RR,
524 (previous, next, state) -> nadirToTarget.add(state.getDate()));
525 final double[][] tolerance = ToleranceProvider.getDefaultToleranceProvider(10.).getTolerances(initialPV);
526 final AdaptiveStepsizeIntegrator integrator = new DormandPrince853Integrator(0.001, 300.0, tolerance[0], tolerance[1]);
527 final NumericalPropagator propagator = new NumericalPropagator(integrator);
528 GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("g007_eigen_05c_coef", false));
529 propagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(),
530 GravityFieldFactory.getNormalizedProvider(8, 8)));
531 propagator.setInitialState(new SpacecraftState(initialPV,
532 nadirPointing.getAttitude(initialPV,
533 initialPV.getDate(),
534 initialPV.getFrame())));
535 propagator.setAttitudeProvider(attitudesSequence);
536 propagator.setOrbitType(null);
537 propagator.propagate(initialDate.shiftedBy(6000));
538
539
540
541 final AbsoluteDate midTransition = nadirToTarget.get(0).shiftedBy(0.5 * transitionTime);
542 SpacecraftState state = propagator.propagate(midTransition.shiftedBy(+60), midTransition);
543 Rotation nadirR = nadirPointing.getAttitude(state.getAbsPVA(), state.getDate(), state.getFrame()).getRotation();
544 Rotation targetR = targetPointing.getAttitude(state.getAbsPVA(), state.getDate(), state.getFrame()).getRotation();
545 final double reorientationAngle = Rotation.distance(nadirR, targetR);
546 Assertions.assertEquals(0.5 * reorientationAngle, Rotation.distance(state.getAttitude().getRotation(), targetR),
547 0.08 * reorientationAngle);
548
549
550 }
551
552
553
554
555 @Test
556 void testAnalyticalPropagatorTransition() {
557
558 final AbsoluteDate initialDate = new AbsoluteDate(2017, 03, 27, 0, 0, 00.000, TimeScalesFactory.getUTC());
559 final Vector3D position = new Vector3D(-39098981.4866597, -15784239.3610601, 78908.2289853595);
560 final Vector3D velocity = new Vector3D(1151.00321021175, -2851.14864755189, -2.02133248357321);
561 final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity),
562 FramesFactory.getGCRF(), initialDate,
563 Constants.WGS84_EARTH_MU);
564
565
566 final AttitudeProvider before = new FrameAlignedProvider(new Rotation(0, 0, 0, 1, false));
567 final AttitudeProvider current = new FrameAlignedProvider(Rotation.IDENTITY);
568 final AttitudeProvider after = new FrameAlignedProvider(new Rotation(0, 0, 0, -1, false));
569
570
571 final AbsoluteDate forwardSwitchDate = initialDate.shiftedBy(600);
572 final AbsoluteDate backwardSwitchDate = initialDate.shiftedBy(-600);
573 final DateDetector forwardSwitchDetector = new DateDetector(forwardSwitchDate).withHandler(new ContinueOnEvent());
574 final DateDetector backwardSwitchDetector = new DateDetector(backwardSwitchDate).withHandler(new ContinueOnEvent());
575
576
577 final AttitudesSequence attitudeSequence = new AttitudesSequence();
578 attitudeSequence.resetActiveProvider(current);
579 attitudeSequence.addSwitchingCondition(before, current, backwardSwitchDetector, true, true, 60, AngularDerivativesFilter.USE_RR, null);
580 attitudeSequence.addSwitchingCondition(current, after, forwardSwitchDetector, true, true, 60, AngularDerivativesFilter.USE_RR, null);
581
582
583 Propagator propagator = new KeplerianPropagator(initialOrbit);
584 propagator.setAttitudeProvider(attitudeSequence);
585
586 SpacecraftState stateAfter = propagator.propagate(initialDate, initialDate.shiftedBy(1200));
587 SpacecraftState stateBefore = propagator.propagate(initialDate, initialDate.shiftedBy(-1200));
588
589
590 Assertions.assertEquals(1200, stateAfter.getDate().durationFrom(initialDate), 1.0E-3);
591 Assertions.assertEquals(-1200, stateBefore.getDate().durationFrom(initialDate), 1.0E-3);
592
593
594 Assertions.assertEquals(
595 before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ0(),
596 stateBefore.getAttitude().getRotation().getQ0(), 1.0E-16);
597 Assertions.assertEquals(
598 before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ1(),
599 stateBefore.getAttitude().getRotation().getQ1(), 1.0E-16);
600 Assertions.assertEquals(
601 before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ2(),
602 stateBefore.getAttitude().getRotation().getQ2(), 1.0E-16);
603 Assertions.assertEquals(
604 before.getAttitude(stateBefore.getOrbit(), stateBefore.getDate(), stateBefore.getFrame()).getRotation().getQ3(),
605 stateBefore.getAttitude().getRotation().getQ3(), 1.0E-16);
606
607 Assertions.assertEquals(
608 after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ0(),
609 stateAfter.getAttitude().getRotation().getQ0(), 1.0E-16);
610 Assertions.assertEquals(
611 after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ1(),
612 stateAfter.getAttitude().getRotation().getQ1(), 1.0E-16);
613 Assertions.assertEquals(
614 after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ2(),
615 stateAfter.getAttitude().getRotation().getQ2(), 1.0E-16);
616 Assertions.assertEquals(
617 after.getAttitude(stateAfter.getOrbit(), stateAfter.getDate(), stateAfter.getFrame()).getRotation().getQ3(),
618 stateAfter.getAttitude().getRotation().getQ3(), 1.0E-16);
619 }
620
621
622 @Test
623 void testGetSwitches() {
624
625 final double transitionTime = 1;
626 final AttitudeProvider mockPastAttitudeProvider = mock(AttitudeProvider.class);
627 final AttitudeProvider mockFutureAttitudeProvider = mock(AttitudeProvider.class);
628 final boolean switchOnIncrease = true;
629 final boolean switchOnDecrease = true;
630 final AngularDerivativesFilter filter = AngularDerivativesFilter.USE_R;
631 final EventDetector mockSwitchEvent = mock(EventDetector.class);
632 when(mockSwitchEvent.getThreshold()).thenReturn(transitionTime - 0.5);
633
634
635 final AttitudesSequence attitudesSequence = new AttitudesSequence();
636 attitudesSequence.addSwitchingCondition(mockPastAttitudeProvider, mockFutureAttitudeProvider, mockSwitchEvent,
637 switchOnIncrease, switchOnDecrease, transitionTime, filter, null);
638
639
640
641 final List<AttitudesSequence.Switch> switches1 = attitudesSequence.getSwitches();
642 final List<AttitudesSequence.Switch> switches2 = attitudesSequence.getSwitches();
643
644
645 Assertions.assertEquals(1, switches1.size());
646 Assertions.assertNotSame(switches1, switches2);
647 }
648
649 private static class Handler implements AttitudeSwitchHandler {
650
651 private final AttitudeProvider expectedPrevious;
652 private final AttitudeProvider expectedNext;
653 private final List<AbsoluteDate> dates;
654
655 public Handler(final AttitudeProvider expectedPrevious, final AttitudeProvider expectedNext) {
656 this.expectedPrevious = expectedPrevious;
657 this.expectedNext = expectedNext;
658 this.dates = new ArrayList<>();
659 }
660
661 @Override
662 public void switchOccurred(AttitudeProvider previous, AttitudeProvider next,
663 SpacecraftState state) {
664 Assertions.assertSame(previous, expectedPrevious);
665 Assertions.assertSame(next, expectedNext);
666 dates.add(state.getDate());
667 }
668
669 }
670
671 private void setInEclipse(AbsoluteDate lastChange, boolean inEclipse) {
672 this.lastChange = lastChange;
673 this.inEclipse = inEclipse;
674 }
675
676 private void checkEqualAttitudes(final Attitude expected, final Attitude tested) {
677 Assertions.assertEquals(0.0, Rotation.distance(expected.getRotation(), tested.getRotation()), 1.0e-14);
678 Assertions.assertEquals(0.0, Vector3D.distance(expected.getSpin(), tested.getSpin()), 1.0e-11);
679 Assertions.assertEquals(0.0,
680 Vector3D.distance(expected.getRotationAcceleration(), tested.getRotationAcceleration()), 1.0e-9);
681 }
682
683 private void checkBetweenAttitudes(final Attitude limit1, final Attitude limit2, final Attitude tested) {
684 final Rotation r1 = limit1.getRotation();
685 final Rotation r2 = limit2.getRotation();
686 final Rotation t = tested.getRotation();
687 final double reorientationAngle = Rotation.distance(r1, r2);
688 Assertions.assertTrue(Rotation.distance(t, r1) < reorientationAngle);
689 Assertions.assertTrue(Rotation.distance(t, r2) < reorientationAngle);
690 }
691
692 @BeforeEach
693 public void setUp() {
694 Utils.setDataRoot("regular-data:potential");
695 }
696
697 }
698