1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.rugged.utils;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.stream.Collectors;
23
24 import org.hipparchus.geometry.euclidean.threed.Rotation;
25 import org.hipparchus.geometry.euclidean.threed.Vector3D;
26 import org.hipparchus.util.FastMath;
27 import org.orekit.frames.FactoryManagedFrame;
28 import org.orekit.frames.Frame;
29 import org.orekit.frames.FramesFactory;
30 import org.orekit.frames.Predefined;
31 import org.orekit.frames.Transform;
32 import org.orekit.rugged.errors.DumpManager;
33 import org.orekit.rugged.errors.RuggedException;
34 import org.orekit.rugged.errors.RuggedMessages;
35 import org.orekit.time.AbsoluteDate;
36 import org.orekit.time.TimeInterpolator;
37 import org.orekit.time.TimeOffset;
38 import org.orekit.utils.AngularCoordinates;
39 import org.orekit.utils.AngularDerivativesFilter;
40 import org.orekit.utils.CartesianDerivativesFilter;
41 import org.orekit.utils.ImmutableTimeStampedCache;
42 import org.orekit.utils.PVCoordinates;
43 import org.orekit.utils.TimeStampedAngularCoordinates;
44 import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator;
45 import org.orekit.utils.TimeStampedCache;
46 import org.orekit.utils.TimeStampedPVCoordinates;
47 import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator;
48
49
50
51
52
53 public class SpacecraftToObservedBody implements Serializable {
54
55
56 private static final long serialVersionUID = 20250427L;
57
58
59 private final Frame inertialFrame;
60
61
62 private final Frame bodyFrame;
63
64
65 private final AbsoluteDate minDate;
66
67
68 private final AbsoluteDate maxDate;
69
70
71 private final double tStep;
72
73
74 private final double overshootTolerance;
75
76
77 private final List<Transform> bodyToInertial;
78
79
80 private final List<Transform> inertialToBody;
81
82
83 private final List<Transform> scToInertial;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public SpacecraftToObservedBody(final Frame inertialFrame, final Frame bodyFrame,
101 final AbsoluteDate minDate, final AbsoluteDate maxDate, final double tStep,
102 final double overshootTolerance,
103 final List<TimeStampedPVCoordinates> positionsVelocities, final int pvInterpolationNumber,
104 final CartesianDerivativesFilter pvFilter,
105 final List<TimeStampedAngularCoordinates> quaternions, final int aInterpolationNumber,
106 final AngularDerivativesFilter aFilter) {
107
108 this.inertialFrame = inertialFrame;
109 this.bodyFrame = bodyFrame;
110 this.minDate = minDate;
111 this.maxDate = maxDate;
112 this.overshootTolerance = overshootTolerance;
113
114
115 final AbsoluteDate minPVDate = positionsVelocities.get(0).getDate();
116 final AbsoluteDate maxPVDate = positionsVelocities.get(positionsVelocities.size() - 1).getDate();
117 if (minPVDate.durationFrom(minDate) > overshootTolerance) {
118 throw new RuggedException(RuggedMessages.OUT_OF_TIME_RANGE, minDate, minPVDate, maxPVDate);
119 }
120 if (maxDate.durationFrom(maxPVDate) > overshootTolerance) {
121 throw new RuggedException(RuggedMessages.OUT_OF_TIME_RANGE, maxDate, minPVDate, maxPVDate);
122 }
123
124 final AbsoluteDate minQDate = quaternions.get(0).getDate();
125 final AbsoluteDate maxQDate = quaternions.get(quaternions.size() - 1).getDate();
126 if (minQDate.durationFrom(minDate) > overshootTolerance) {
127 throw new RuggedException(RuggedMessages.OUT_OF_TIME_RANGE, minDate, minQDate, maxQDate);
128 }
129 if (maxDate.durationFrom(maxQDate) > overshootTolerance) {
130 throw new RuggedException(RuggedMessages.OUT_OF_TIME_RANGE, maxDate, minQDate, maxQDate);
131 }
132
133
134 final TimeStampedCache<TimeStampedPVCoordinates> pvCache =
135 new ImmutableTimeStampedCache<>(pvInterpolationNumber, positionsVelocities);
136
137
138 final TimeInterpolator<TimeStampedPVCoordinates> pvInterpolator =
139 new TimeStampedPVCoordinatesHermiteInterpolator(pvInterpolationNumber, pvFilter);
140
141
142 final TimeStampedCache<TimeStampedAngularCoordinates> aCache =
143 new ImmutableTimeStampedCache<>(aInterpolationNumber, quaternions);
144
145
146 final TimeInterpolator<TimeStampedAngularCoordinates> angularInterpolator =
147 new TimeStampedAngularCoordinatesHermiteInterpolator(aInterpolationNumber, aFilter);
148
149 final int n = (int) FastMath.ceil(maxDate.durationFrom(minDate) / tStep);
150 this.tStep = tStep;
151 this.bodyToInertial = new ArrayList<>(n);
152 this.inertialToBody = new ArrayList<>(n);
153 this.scToInertial = new ArrayList<>(n);
154 for (AbsoluteDate date = minDate; bodyToInertial.size() < n; date = date.shiftedBy(tStep)) {
155
156
157 final AbsoluteDate pvInterpolationDate;
158 if (date.compareTo(pvCache.getEarliest().getDate()) < 0) {
159 pvInterpolationDate = pvCache.getEarliest().getDate();
160 } else if (date.compareTo(pvCache.getLatest().getDate()) > 0) {
161 pvInterpolationDate = pvCache.getLatest().getDate();
162 } else {
163 pvInterpolationDate = date;
164 }
165 final TimeStampedPVCoordinates interpolatedPV =
166 pvInterpolator.interpolate(pvInterpolationDate,
167 pvCache.getNeighbors(pvInterpolationDate));
168 final TimeStampedPVCoordinates pv = interpolatedPV.shiftedBy(date.durationFrom(pvInterpolationDate));
169
170
171 final AbsoluteDate aInterpolationDate;
172 if (date.compareTo(aCache.getEarliest().getDate()) < 0) {
173 aInterpolationDate = aCache.getEarliest().getDate();
174 } else if (date.compareTo(aCache.getLatest().getDate()) > 0) {
175 aInterpolationDate = aCache.getLatest().getDate();
176 } else {
177 aInterpolationDate = date;
178 }
179 final TimeStampedAngularCoordinates interpolatedQuaternion =
180 angularInterpolator.interpolate(aInterpolationDate,
181 aCache.getNeighbors(aInterpolationDate).collect(Collectors.toList()));
182 final TimeStampedAngularCoordinates quaternion = interpolatedQuaternion.shiftedBy(date.durationFrom(aInterpolationDate));
183
184
185 scToInertial.add(new Transform(date,
186 new Transform(date, quaternion.revert()),
187 new Transform(date, pv)));
188
189
190 final Transform b2i = bodyFrame.getTransformTo(inertialFrame, date);
191 bodyToInertial.add(b2i);
192 inertialToBody.add(b2i.getInverse());
193
194 }
195 }
196
197
198
199
200
201
202
203
204
205
206
207
208 public SpacecraftToObservedBody(final Frame inertialFrame, final Frame bodyFrame,
209 final AbsoluteDate minDate, final AbsoluteDate maxDate, final double tStep,
210 final double overshootTolerance,
211 final List<Transform> bodyToInertial, final List<Transform> scToInertial) {
212
213 this.inertialFrame = inertialFrame;
214 this.bodyFrame = bodyFrame;
215 this.minDate = minDate;
216 this.maxDate = maxDate;
217 this.tStep = tStep;
218 this.overshootTolerance = overshootTolerance;
219 this.bodyToInertial = bodyToInertial;
220 this.scToInertial = scToInertial;
221
222 this.inertialToBody = new ArrayList<>(bodyToInertial.size());
223 for (final Transform b2i : bodyToInertial) {
224 inertialToBody.add(b2i.getInverse());
225 }
226
227 }
228
229
230
231
232 public Frame getInertialFrame() {
233 return inertialFrame;
234 }
235
236
237
238
239 public Frame getBodyFrame() {
240 return bodyFrame;
241 }
242
243
244
245
246 public AbsoluteDate getMinDate() {
247 return minDate;
248 }
249
250
251
252
253 public AbsoluteDate getMaxDate() {
254 return maxDate;
255 }
256
257
258
259
260 public double getTStep() {
261 return tStep;
262 }
263
264
265
266
267 public double getOvershootTolerance() {
268 return overshootTolerance;
269 }
270
271
272
273
274
275 public Transform getScToInertial(final AbsoluteDate date) {
276 return interpolate(date, scToInertial);
277 }
278
279
280
281
282
283 public Transform getInertialToBody(final AbsoluteDate date) {
284 return interpolate(date, inertialToBody);
285 }
286
287
288
289
290
291 public Transform getBodyToInertial(final AbsoluteDate date) {
292 return interpolate(date, bodyToInertial);
293 }
294
295
296
297
298
299
300 private Transform interpolate(final AbsoluteDate date, final List<Transform> list) {
301
302
303 if (!isInRange(date)) {
304 throw new RuggedException(RuggedMessages.OUT_OF_TIME_RANGE, date, minDate, maxDate);
305 }
306
307 final double s = date.durationFrom(list.get(0).getDate()) / tStep;
308 final int index = FastMath.max(0, FastMath.min(list.size() - 1, (int) FastMath.rint(s)));
309
310 DumpManager.dumpTransform(this, index, bodyToInertial.get(index), scToInertial.get(index));
311
312 final Transform close = list.get(index);
313 return close.shiftedBy(date.durationFrom(close.getDate()));
314
315 }
316
317
318
319
320
321 public boolean isInRange(final AbsoluteDate date) {
322 return minDate.durationFrom(date) <= overshootTolerance &&
323 date.durationFrom(maxDate) <= overshootTolerance;
324 }
325
326
327
328
329 private Object writeReplace() {
330 return new DataTransferObject(((FactoryManagedFrame) inertialFrame).getFactoryKey(),
331 ((FactoryManagedFrame) bodyFrame).getFactoryKey(),
332 minDate, maxDate, tStep, overshootTolerance,
333 extractTimeOffsets(bodyToInertial),
334 extractCoordinates(bodyToInertial),
335 extractTimeOffsets(scToInertial),
336 extractCoordinates(scToInertial));
337 }
338
339
340
341
342
343
344 private long[] extractTimeOffsets(final List<Transform> transforms) {
345
346 final long[] offsets = new long[2 * transforms.size()];
347
348 for (int i = 0; i < transforms.size(); ++i) {
349 final Transform ti = transforms.get(i);
350 offsets[2 * i] = ti.getDate().getSeconds();
351 offsets[2 * i + 1] = ti.getDate().getAttoSeconds();
352 }
353
354 return offsets;
355
356 }
357
358
359
360
361
362
363 private double[] extractCoordinates(final List<Transform> transforms) {
364
365 final double[] coordinates = new double[19 * transforms.size()];
366
367 for (int i = 0; i < transforms.size(); ++i) {
368
369 final PVCoordinates pv = transforms.get(i).getCartesian();
370 final AngularCoordinates ag = transforms.get(i).getAngular();
371
372 coordinates[19 * i] = pv.getPosition().getX();
373 coordinates[19 * i + 1] = pv.getPosition().getY();
374 coordinates[19 * i + 2] = pv.getPosition().getZ();
375
376 coordinates[19 * i + 3] = pv.getVelocity().getX();
377 coordinates[19 * i + 4] = pv.getVelocity().getY();
378 coordinates[19 * i + 5] = pv.getVelocity().getZ();
379
380 coordinates[19 * i + 6] = pv.getAcceleration().getX();
381 coordinates[19 * i + 7] = pv.getAcceleration().getY();
382 coordinates[19 * i + 8] = pv.getAcceleration().getZ();
383
384 coordinates[19 * i + 9] = ag.getRotation().getQ0();
385 coordinates[19 * i + 10] = ag.getRotation().getQ1();
386 coordinates[19 * i + 11] = ag.getRotation().getQ2();
387 coordinates[19 * i + 12] = ag.getRotation().getQ3();
388
389 coordinates[19 * i + 13] = ag.getRotationRate().getX();
390 coordinates[19 * i + 14] = ag.getRotationRate().getY();
391 coordinates[19 * i + 15] = ag.getRotationRate().getZ();
392
393 coordinates[19 * i + 16] = ag.getRotationAcceleration().getX();
394 coordinates[19 * i + 17] = ag.getRotationAcceleration().getY();
395 coordinates[19 * i + 18] = ag.getRotationAcceleration().getZ();
396
397 }
398
399 return coordinates;
400
401 }
402
403
404
405
406 private static class DataTransferObject implements Serializable {
407
408
409 private static final long serialVersionUID = 20250427L;
410
411
412 private final Predefined inertialFrame;
413
414
415 private final Predefined bodyFrame;
416
417
418 private final AbsoluteDate minDate;
419
420
421 private final AbsoluteDate maxDate;
422
423
424 private final double tStep;
425
426
427 private final double overshootTolerance;
428
429
430 private final long[] bodyToInertialTimeOffset;
431
432
433 private final double[] bodyToInertialCoordinates;
434
435
436 private final long[] scToInertialTimOffset;
437
438
439 private final double[] scToInertialCoordinates;
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454 DataTransferObject(final Predefined inertialFrame, final Predefined bodyFrame,
455 final AbsoluteDate minDate, final AbsoluteDate maxDate, final double tStep,
456 final double overshootTolerance,
457 final long[] bodyToInertialTimeOffset, final double[] bodyToInertialCoordinates,
458 final long[] scToInertialTimOffset, final double[] scToInertialCoordinates) {
459 this.inertialFrame = inertialFrame;
460 this.bodyFrame = bodyFrame;
461 this.minDate = minDate;
462 this.maxDate = maxDate;
463 this.tStep = tStep;
464 this.overshootTolerance = overshootTolerance;
465 this.bodyToInertialTimeOffset = bodyToInertialTimeOffset;
466 this.bodyToInertialCoordinates = bodyToInertialCoordinates;
467 this.scToInertialTimOffset = scToInertialTimOffset;
468 this.scToInertialCoordinates = scToInertialCoordinates;
469 }
470
471
472
473
474
475
476
477 private Transform createTransform(final int i,
478 final long[] timeOffsets,
479 final double[] coordinates) {
480 final AbsoluteDate date = new AbsoluteDate(new TimeOffset(timeOffsets[2 * i],
481 timeOffsets[2 * i + 1]));
482 final PVCoordinates pv = new PVCoordinates(new Vector3D(coordinates[19 * i],
483 coordinates[19 * i + 1],
484 coordinates[19 * i + 2]),
485 new Vector3D(coordinates[19 * i + 3],
486 coordinates[19 * i + 4],
487 coordinates[19 * i + 5]),
488 new Vector3D(coordinates[19 * i + 6],
489 coordinates[19 * i + 7],
490 coordinates[19 * i + 8]));
491 final AngularCoordinates ag = new AngularCoordinates(new Rotation(coordinates[19 * i + 9],
492 coordinates[19 * i + 10],
493 coordinates[19 * i + 11],
494 coordinates[19 * i + 12],
495 false),
496 new Vector3D(coordinates[19 * i + 13],
497 coordinates[19 * i + 14],
498 coordinates[19 * i + 15]),
499 new Vector3D(coordinates[19 * i + 16],
500 coordinates[19 * i + 17],
501 coordinates[19 * i + 18]));
502 return new Transform(date, pv, ag);
503 }
504
505
506
507
508
509
510 private List<Transform> createAllTransforms(final long[] timeOffsets,
511 final double[] coordinates) {
512 final List<Transform> transforms = new ArrayList<>(timeOffsets.length / 2);
513 for (int i = 0; i < timeOffsets.length / 2; ++i) {
514 transforms.add(createTransform(i, timeOffsets, coordinates));
515 }
516 return transforms;
517 }
518
519
520
521
522
523 private Object readResolve() {
524 return new SpacecraftToObservedBody(FramesFactory.getFrame(inertialFrame),
525 FramesFactory.getFrame(bodyFrame),
526 minDate, maxDate, tStep, overshootTolerance,
527 createAllTransforms(bodyToInertialTimeOffset,
528 bodyToInertialCoordinates),
529 createAllTransforms(scToInertialTimOffset,
530 scToInertialCoordinates));
531 }
532
533 }
534
535 }