1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.utils;
18
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
23
24 import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
25 import org.apache.commons.math3.exception.MathArithmeticException;
26 import org.apache.commons.math3.exception.MathIllegalArgumentException;
27 import org.apache.commons.math3.exception.NumberIsTooLargeException;
28 import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
29 import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
30 import org.apache.commons.math3.geometry.euclidean.threed.RotationConvention;
31 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
32 import org.apache.commons.math3.linear.DecompositionSolver;
33 import org.apache.commons.math3.linear.MatrixUtils;
34 import org.apache.commons.math3.linear.QRDecomposition;
35 import org.apache.commons.math3.linear.RealMatrix;
36 import org.apache.commons.math3.linear.RealVector;
37 import org.apache.commons.math3.linear.SingularMatrixException;
38 import org.apache.commons.math3.util.FastMath;
39 import org.apache.commons.math3.util.MathArrays;
40 import org.apache.commons.math3.util.Pair;
41 import org.orekit.errors.OrekitException;
42 import org.orekit.errors.OrekitMessages;
43 import org.orekit.time.AbsoluteDate;
44 import org.orekit.time.TimeShiftable;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class AngularCoordinates implements TimeShiftable<AngularCoordinates>, Serializable {
60
61
62
63
64 public static final AngularCoordinates IDENTITY =
65 new AngularCoordinates(Rotation.IDENTITY, Vector3D.ZERO, Vector3D.ZERO);
66
67
68 private static final long serialVersionUID = 20140414L;
69
70
71 private final Rotation rotation;
72
73
74 private final Vector3D rotationRate;
75
76
77 private final Vector3D rotationAcceleration;
78
79
80
81
82 public AngularCoordinates() {
83 this(Rotation.IDENTITY, Vector3D.ZERO, Vector3D.ZERO);
84 }
85
86
87
88
89
90 public AngularCoordinates(final Rotation rotation, final Vector3D rotationRate) {
91 this(rotation, rotationRate, Vector3D.ZERO);
92 }
93
94
95
96
97
98
99 public AngularCoordinates(final Rotation rotation,
100 final Vector3D rotationRate, final Vector3D rotationAcceleration) {
101 this.rotation = rotation;
102 this.rotationRate = rotationRate;
103 this.rotationAcceleration = rotationAcceleration;
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 public AngularCoordinates(final PVCoordinates u1, final PVCoordinates u2,
129 final PVCoordinates v1, final PVCoordinates v2,
130 final double tolerance)
131 throws OrekitException {
132
133 try {
134
135 rotation = new Rotation(u1.getPosition(), u2.getPosition(),
136 v1.getPosition(), v2.getPosition());
137
138
139
140
141 final Vector3D ru1Dot = rotation.applyTo(u1.getVelocity());
142 final Vector3D ru2Dot = rotation.applyTo(u2.getVelocity());
143 rotationRate = inverseCrossProducts(v1.getPosition(), ru1Dot.subtract(v1.getVelocity()),
144 v2.getPosition(), ru2Dot.subtract(v2.getVelocity()),
145 tolerance);
146
147
148
149
150 final Vector3D ru1DotDot = rotation.applyTo(u1.getAcceleration());
151 final Vector3D oDotv1 = Vector3D.crossProduct(rotationRate, v1.getVelocity());
152 final Vector3D oov1 = Vector3D.crossProduct(rotationRate, Vector3D.crossProduct(rotationRate, v1.getPosition()));
153 final Vector3D c1 = new Vector3D(1, ru1DotDot, -2, oDotv1, -1, oov1, -1, v1.getAcceleration());
154 final Vector3D ru2DotDot = rotation.applyTo(u2.getAcceleration());
155 final Vector3D oDotv2 = Vector3D.crossProduct(rotationRate, v2.getVelocity());
156 final Vector3D oov2 = Vector3D.crossProduct(rotationRate, Vector3D.crossProduct(rotationRate, v2.getPosition()));
157 final Vector3D c2 = new Vector3D(1, ru2DotDot, -2, oDotv2, -1, oov2, -1, v2.getAcceleration());
158 rotationAcceleration = inverseCrossProducts(v1.getPosition(), c1, v2.getPosition(), c2, tolerance);
159
160 } catch (MathIllegalArgumentException miae) {
161 throw new OrekitException(miae);
162 } catch (MathArithmeticException mae) {
163 throw new OrekitException(mae);
164 }
165
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public AngularCoordinates(final PVCoordinates u, final PVCoordinates v) throws OrekitException {
183 this(new FieldRotation<DerivativeStructure>(u.toDerivativeStructureVector(2),
184 v.toDerivativeStructureVector(2)));
185 }
186
187
188
189
190
191
192
193
194 public AngularCoordinates(final FieldRotation<DerivativeStructure> r) {
195
196 final double q0 = r.getQ0().getReal();
197 final double q1 = r.getQ1().getReal();
198 final double q2 = r.getQ2().getReal();
199 final double q3 = r.getQ3().getReal();
200
201 rotation = new Rotation(q0, q1, q2, q3, false);
202 if (r.getQ0().getOrder() >= 1) {
203 final double q0Dot = r.getQ0().getPartialDerivative(1);
204 final double q1Dot = r.getQ1().getPartialDerivative(1);
205 final double q2Dot = r.getQ2().getPartialDerivative(1);
206 final double q3Dot = r.getQ3().getPartialDerivative(1);
207 rotationRate =
208 new Vector3D(2 * MathArrays.linearCombination(-q1, q0Dot, q0, q1Dot, q3, q2Dot, -q2, q3Dot),
209 2 * MathArrays.linearCombination(-q2, q0Dot, -q3, q1Dot, q0, q2Dot, q1, q3Dot),
210 2 * MathArrays.linearCombination(-q3, q0Dot, q2, q1Dot, -q1, q2Dot, q0, q3Dot));
211 if (r.getQ0().getOrder() >= 2) {
212 final double q0DotDot = r.getQ0().getPartialDerivative(2);
213 final double q1DotDot = r.getQ1().getPartialDerivative(2);
214 final double q2DotDot = r.getQ2().getPartialDerivative(2);
215 final double q3DotDot = r.getQ3().getPartialDerivative(2);
216 rotationAcceleration =
217 new Vector3D(2 * MathArrays.linearCombination(-q1, q0DotDot, q0, q1DotDot, q3, q2DotDot, -q2, q3DotDot),
218 2 * MathArrays.linearCombination(-q2, q0DotDot, -q3, q1DotDot, q0, q2DotDot, q1, q3DotDot),
219 2 * MathArrays.linearCombination(-q3, q0DotDot, q2, q1DotDot, -q1, q2DotDot, q0, q3DotDot));
220 } else {
221 rotationAcceleration = Vector3D.ZERO;
222 }
223 } else {
224 rotationRate = Vector3D.ZERO;
225 rotationAcceleration = Vector3D.ZERO;
226 }
227
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247 private static Vector3D inverseCrossProducts(final Vector3D v1, final Vector3D c1,
248 final Vector3D v2, final Vector3D c2,
249 final double tolerance)
250 throws MathIllegalArgumentException {
251
252 final double v12 = v1.getNormSq();
253 final double v1n = FastMath.sqrt(v12);
254 final double v22 = v2.getNormSq();
255 final double v2n = FastMath.sqrt(v22);
256 final double threshold = tolerance * FastMath.max(v1n, v2n);
257
258 Vector3D omega;
259
260 try {
261
262 final RealMatrix m = MatrixUtils.createRealMatrix(6, 3);
263 m.setEntry(0, 1, v1.getZ());
264 m.setEntry(0, 2, -v1.getY());
265 m.setEntry(1, 0, -v1.getZ());
266 m.setEntry(1, 2, v1.getX());
267 m.setEntry(2, 0, v1.getY());
268 m.setEntry(2, 1, -v1.getX());
269 m.setEntry(3, 1, v2.getZ());
270 m.setEntry(3, 2, -v2.getY());
271 m.setEntry(4, 0, -v2.getZ());
272 m.setEntry(4, 2, v2.getX());
273 m.setEntry(5, 0, v2.getY());
274 m.setEntry(5, 1, -v2.getX());
275
276 final RealVector rhs = MatrixUtils.createRealVector(new double[] {
277 c1.getX(), c1.getY(), c1.getZ(),
278 c2.getX(), c2.getY(), c2.getZ()
279 });
280
281
282 final DecompositionSolver solver = new QRDecomposition(m, threshold).getSolver();
283 final RealVector v = solver.solve(rhs);
284 omega = new Vector3D(v.getEntry(0), v.getEntry(1), v.getEntry(2));
285
286 } catch (SingularMatrixException sme) {
287
288
289 final double c12 = c1.getNormSq();
290 final double c1n = FastMath.sqrt(c12);
291 final double c22 = c2.getNormSq();
292 final double c2n = FastMath.sqrt(c22);
293
294 if (c1n <= threshold && c2n <= threshold) {
295
296 return Vector3D.ZERO;
297 } else if (v1n <= threshold && c1n >= threshold) {
298
299 throw new NumberIsTooLargeException(c1n, 0, true);
300 } else if (v2n <= threshold && c2n >= threshold) {
301
302 throw new NumberIsTooLargeException(c2n, 0, true);
303 } else if (Vector3D.crossProduct(v1, v2).getNorm() <= threshold && v12 > threshold) {
304
305
306 omega = new Vector3D(1.0 / v12, Vector3D.crossProduct(v1, c1));
307 } else {
308 throw sme;
309 }
310
311 }
312
313
314 final double d1 = Vector3D.distance(Vector3D.crossProduct(omega, v1), c1);
315 if (d1 > threshold) {
316 throw new NumberIsTooLargeException(d1, 0, true);
317 }
318
319 final double d2 = Vector3D.distance(Vector3D.crossProduct(omega, v2), c2);
320 if (d2 > threshold) {
321 throw new NumberIsTooLargeException(d2, 0, true);
322 }
323
324 return omega;
325
326 }
327
328
329
330
331
332
333
334
335
336
337 public FieldRotation<DerivativeStructure> toDerivativeStructureRotation(final int order)
338 throws OrekitException {
339
340
341 final double q0 = rotation.getQ0();
342 final double q1 = rotation.getQ1();
343 final double q2 = rotation.getQ2();
344 final double q3 = rotation.getQ3();
345
346
347 final double oX = rotationRate.getX();
348 final double oY = rotationRate.getY();
349 final double oZ = rotationRate.getZ();
350 final double q0Dot = 0.5 * MathArrays.linearCombination(-q1, oX, -q2, oY, -q3, oZ);
351 final double q1Dot = 0.5 * MathArrays.linearCombination( q0, oX, -q3, oY, q2, oZ);
352 final double q2Dot = 0.5 * MathArrays.linearCombination( q3, oX, q0, oY, -q1, oZ);
353 final double q3Dot = 0.5 * MathArrays.linearCombination(-q2, oX, q1, oY, q0, oZ);
354
355
356 final double oXDot = rotationAcceleration.getX();
357 final double oYDot = rotationAcceleration.getY();
358 final double oZDot = rotationAcceleration.getZ();
359 final double q0DotDot = -0.5 * MathArrays.linearCombination(new double[] {
360 q1, q2, q3, q1Dot, q2Dot, q3Dot
361 }, new double[] {
362 oXDot, oYDot, oZDot, oX, oY, oZ
363 });
364 final double q1DotDot = 0.5 * MathArrays.linearCombination(new double[] {
365 q0, q2, -q3, q0Dot, q2Dot, -q3Dot
366 }, new double[] {
367 oXDot, oZDot, oYDot, oX, oZ, oY
368 });
369 final double q2DotDot = 0.5 * MathArrays.linearCombination(new double[] {
370 q0, q3, -q1, q0Dot, q3Dot, -q1Dot
371 }, new double[] {
372 oYDot, oXDot, oZDot, oY, oX, oZ
373 });
374 final double q3DotDot = 0.5 * MathArrays.linearCombination(new double[] {
375 q0, q1, -q2, q0Dot, q1Dot, -q2Dot
376 }, new double[] {
377 oZDot, oYDot, oXDot, oZ, oY, oX
378 });
379
380 final DerivativeStructure q0DS;
381 final DerivativeStructure q1DS;
382 final DerivativeStructure q2DS;
383 final DerivativeStructure q3DS;
384 switch(order) {
385 case 0 :
386 q0DS = new DerivativeStructure(1, 0, q0);
387 q1DS = new DerivativeStructure(1, 0, q1);
388 q2DS = new DerivativeStructure(1, 0, q2);
389 q3DS = new DerivativeStructure(1, 0, q3);
390 break;
391 case 1 :
392 q0DS = new DerivativeStructure(1, 1, q0, q0Dot);
393 q1DS = new DerivativeStructure(1, 1, q1, q1Dot);
394 q2DS = new DerivativeStructure(1, 1, q2, q2Dot);
395 q3DS = new DerivativeStructure(1, 1, q3, q3Dot);
396 break;
397 case 2 :
398 q0DS = new DerivativeStructure(1, 2, q0, q0Dot, q0DotDot);
399 q1DS = new DerivativeStructure(1, 2, q1, q1Dot, q1DotDot);
400 q2DS = new DerivativeStructure(1, 2, q2, q2Dot, q2DotDot);
401 q3DS = new DerivativeStructure(1, 2, q3, q3Dot, q3DotDot);
402 break;
403 default :
404 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DERIVATION_ORDER, order);
405 }
406
407 return new FieldRotation<DerivativeStructure>(q0DS, q1DS, q2DS, q3DS, false);
408
409 }
410
411
412
413
414
415
416
417
418
419 public static Vector3D estimateRate(final Rotation start, final Rotation end, final double dt) {
420 final Rotation evolution = start.compose(end.revert(), RotationConvention.VECTOR_OPERATOR);
421 return new Vector3D(evolution.getAngle() / dt, evolution.getAxis(RotationConvention.VECTOR_OPERATOR));
422 }
423
424
425
426
427
428
429 public AngularCoordinates revert() {
430 return new AngularCoordinates(rotation.revert(),
431 rotation.applyInverseTo(rotationRate).negate(),
432 rotation.applyInverseTo(rotationAcceleration).negate());
433 }
434
435
436
437
438
439
440
441
442
443
444
445 public AngularCoordinates shiftedBy(final double dt) {
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461 final double rate = rotationRate.getNorm();
462 final Rotation rateContribution = (rate == 0.0) ?
463 Rotation.IDENTITY :
464 new Rotation(rotationRate, rate * dt, RotationConvention.FRAME_TRANSFORM);
465
466
467 final AngularCoordinates linearPart =
468 new AngularCoordinates(rateContribution.compose(rotation, RotationConvention.VECTOR_OPERATOR), rotationRate);
469
470 final double acc = rotationAcceleration.getNorm();
471 if (acc == 0.0) {
472
473 return linearPart;
474 }
475
476
477
478
479
480 final AngularCoordinates quadraticContribution =
481 new AngularCoordinates(new Rotation(rotationAcceleration,
482 0.5 * acc * dt * dt,
483 RotationConvention.FRAME_TRANSFORM),
484 new Vector3D(dt, rotationAcceleration),
485 rotationAcceleration);
486
487
488
489
490
491 return quadraticContribution.addOffset(linearPart);
492
493 }
494
495
496
497
498 public Rotation getRotation() {
499 return rotation;
500 }
501
502
503
504
505 public Vector3D getRotationRate() {
506 return rotationRate;
507 }
508
509
510
511
512 public Vector3D getRotationAcceleration() {
513 return rotationAcceleration;
514 }
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534 public AngularCoordinates addOffset(final AngularCoordinates offset) {
535 final Vector3D rOmega = rotation.applyTo(offset.rotationRate);
536 final Vector3D rOmegaDot = rotation.applyTo(offset.rotationAcceleration);
537 return new AngularCoordinates(rotation.compose(offset.rotation, RotationConvention.VECTOR_OPERATOR),
538 rotationRate.add(rOmega),
539 new Vector3D( 1.0, rotationAcceleration,
540 1.0, rOmegaDot,
541 -1.0, Vector3D.crossProduct(rotationRate, rOmega)));
542 }
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562 public AngularCoordinates subtractOffset(final AngularCoordinates offset) {
563 return addOffset(offset.revert());
564 }
565
566
567
568
569
570 public PVCoordinates applyTo(final PVCoordinates pv) {
571
572 final Vector3D transformedP = rotation.applyTo(pv.getPosition());
573 final Vector3D crossP = Vector3D.crossProduct(rotationRate, transformedP);
574 final Vector3D transformedV = rotation.applyTo(pv.getVelocity()).subtract(crossP);
575 final Vector3D crossV = Vector3D.crossProduct(rotationRate, transformedV);
576 final Vector3D crossCrossP = Vector3D.crossProduct(rotationRate, crossP);
577 final Vector3D crossDotP = Vector3D.crossProduct(rotationAcceleration, transformedP);
578 final Vector3D transformedA = new Vector3D( 1, rotation.applyTo(pv.getAcceleration()),
579 -2, crossV,
580 -1, crossCrossP,
581 -1, crossDotP);
582
583 return new PVCoordinates(transformedP, transformedV, transformedA);
584
585 }
586
587
588
589
590
591 public TimeStampedPVCoordinates applyTo(final TimeStampedPVCoordinates pv) {
592
593 final Vector3D transformedP = getRotation().applyTo(pv.getPosition());
594 final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), transformedP);
595 final Vector3D transformedV = getRotation().applyTo(pv.getVelocity()).subtract(crossP);
596 final Vector3D crossV = Vector3D.crossProduct(getRotationRate(), transformedV);
597 final Vector3D crossCrossP = Vector3D.crossProduct(getRotationRate(), crossP);
598 final Vector3D crossDotP = Vector3D.crossProduct(getRotationAcceleration(), transformedP);
599 final Vector3D transformedA = new Vector3D( 1, getRotation().applyTo(pv.getAcceleration()),
600 -2, crossV,
601 -1, crossCrossP,
602 -1, crossDotP);
603
604 return new TimeStampedPVCoordinates(pv.getDate(), transformedP, transformedV, transformedA);
605
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640 @Deprecated
641 public static AngularCoordinates interpolate(final AbsoluteDate date, final boolean useRotationRates,
642 final Collection<Pair<AbsoluteDate, AngularCoordinates>> sample)
643 throws OrekitException {
644 final List<TimeStampedAngularCoordinates> list = new ArrayList<TimeStampedAngularCoordinates>(sample.size());
645 for (final Pair<AbsoluteDate, AngularCoordinates> pair : sample) {
646 list.add(new TimeStampedAngularCoordinates(pair.getFirst(),
647 pair.getSecond().getRotation(),
648 pair.getSecond().getRotationRate(),
649 pair.getSecond().getRotationAcceleration()));
650 }
651 return TimeStampedAngularCoordinates.interpolate(date,
652 useRotationRates ? AngularDerivativesFilter.USE_RR : AngularDerivativesFilter.USE_R,
653 list);
654 }
655
656
657
658
659
660
661
662
663
664
665
666 public double[][] getModifiedRodrigues(final double sign) {
667
668 final double q0 = sign * getRotation().getQ0();
669 final double q1 = sign * getRotation().getQ1();
670 final double q2 = sign * getRotation().getQ2();
671 final double q3 = sign * getRotation().getQ3();
672 final double oX = getRotationRate().getX();
673 final double oY = getRotationRate().getY();
674 final double oZ = getRotationRate().getZ();
675 final double oXDot = getRotationAcceleration().getX();
676 final double oYDot = getRotationAcceleration().getY();
677 final double oZDot = getRotationAcceleration().getZ();
678
679
680 final double q0Dot = 0.5 * MathArrays.linearCombination(-q1, oX, -q2, oY, -q3, oZ);
681 final double q1Dot = 0.5 * MathArrays.linearCombination( q0, oX, -q3, oY, q2, oZ);
682 final double q2Dot = 0.5 * MathArrays.linearCombination( q3, oX, q0, oY, -q1, oZ);
683 final double q3Dot = 0.5 * MathArrays.linearCombination(-q2, oX, q1, oY, q0, oZ);
684
685
686 final double q0DotDot = -0.5 * MathArrays.linearCombination(new double[] {
687 q1, q2, q3, q1Dot, q2Dot, q3Dot
688 }, new double[] {
689 oXDot, oYDot, oZDot, oX, oY, oZ
690 });
691 final double q1DotDot = 0.5 * MathArrays.linearCombination(new double[] {
692 q0, q2, -q3, q0Dot, q2Dot, -q3Dot
693 }, new double[] {
694 oXDot, oZDot, oYDot, oX, oZ, oY
695 });
696 final double q2DotDot = 0.5 * MathArrays.linearCombination(new double[] {
697 q0, q3, -q1, q0Dot, q3Dot, -q1Dot
698 }, new double[] {
699 oYDot, oXDot, oZDot, oY, oX, oZ
700 });
701 final double q3DotDot = 0.5 * MathArrays.linearCombination(new double[] {
702 q0, q1, -q2, q0Dot, q1Dot, -q2Dot
703 }, new double[] {
704 oZDot, oYDot, oXDot, oZ, oY, oX
705 });
706
707
708
709
710
711 final double inv = 1.0 / (1.0 + q0);
712 final double mTwoInvQ0Dot = -2 * inv * q0Dot;
713
714 final double r1 = inv * q1;
715 final double r2 = inv * q2;
716 final double r3 = inv * q3;
717
718 final double mInvR1 = -inv * r1;
719 final double mInvR2 = -inv * r2;
720 final double mInvR3 = -inv * r3;
721
722 final double r1Dot = MathArrays.linearCombination(inv, q1Dot, mInvR1, q0Dot);
723 final double r2Dot = MathArrays.linearCombination(inv, q2Dot, mInvR2, q0Dot);
724 final double r3Dot = MathArrays.linearCombination(inv, q3Dot, mInvR3, q0Dot);
725
726 final double r1DotDot = MathArrays.linearCombination(inv, q1DotDot, mTwoInvQ0Dot, r1Dot, mInvR1, q0DotDot);
727 final double r2DotDot = MathArrays.linearCombination(inv, q2DotDot, mTwoInvQ0Dot, r2Dot, mInvR2, q0DotDot);
728 final double r3DotDot = MathArrays.linearCombination(inv, q3DotDot, mTwoInvQ0Dot, r3Dot, mInvR3, q0DotDot);
729
730 return new double[][] {
731 {
732 r1, r2, r3
733 }, {
734 r1Dot, r2Dot, r3Dot
735 }, {
736 r1DotDot, r2DotDot, r3DotDot
737 }
738 };
739
740 }
741
742
743
744
745
746
747 public static AngularCoordinates createFromModifiedRodrigues(final double[][] r) {
748
749
750 final double rSquared = r[0][0] * r[0][0] + r[0][1] * r[0][1] + r[0][2] * r[0][2];
751 final double oPQ0 = 2 / (1 + rSquared);
752 final double q0 = oPQ0 - 1;
753 final double q1 = oPQ0 * r[0][0];
754 final double q2 = oPQ0 * r[0][1];
755 final double q3 = oPQ0 * r[0][2];
756
757
758 final double oPQ02 = oPQ0 * oPQ0;
759 final double q0Dot = -oPQ02 * MathArrays.linearCombination(r[0][0], r[1][0], r[0][1], r[1][1], r[0][2], r[1][2]);
760 final double q1Dot = oPQ0 * r[1][0] + r[0][0] * q0Dot;
761 final double q2Dot = oPQ0 * r[1][1] + r[0][1] * q0Dot;
762 final double q3Dot = oPQ0 * r[1][2] + r[0][2] * q0Dot;
763 final double oX = 2 * MathArrays.linearCombination(-q1, q0Dot, q0, q1Dot, q3, q2Dot, -q2, q3Dot);
764 final double oY = 2 * MathArrays.linearCombination(-q2, q0Dot, -q3, q1Dot, q0, q2Dot, q1, q3Dot);
765 final double oZ = 2 * MathArrays.linearCombination(-q3, q0Dot, q2, q1Dot, -q1, q2Dot, q0, q3Dot);
766
767
768 final double q0DotDot = (1 - q0) / oPQ0 * q0Dot * q0Dot -
769 oPQ02 * MathArrays.linearCombination(r[0][0], r[2][0], r[0][1], r[2][1], r[0][2], r[2][2]) -
770 (q1Dot * q1Dot + q2Dot * q2Dot + q3Dot * q3Dot);
771 final double q1DotDot = MathArrays.linearCombination(oPQ0, r[2][0], 2 * r[1][0], q0Dot, r[0][0], q0DotDot);
772 final double q2DotDot = MathArrays.linearCombination(oPQ0, r[2][1], 2 * r[1][1], q0Dot, r[0][1], q0DotDot);
773 final double q3DotDot = MathArrays.linearCombination(oPQ0, r[2][2], 2 * r[1][2], q0Dot, r[0][2], q0DotDot);
774 final double oXDot = 2 * MathArrays.linearCombination(-q1, q0DotDot, q0, q1DotDot, q3, q2DotDot, -q2, q3DotDot);
775 final double oYDot = 2 * MathArrays.linearCombination(-q2, q0DotDot, -q3, q1DotDot, q0, q2DotDot, q1, q3DotDot);
776 final double oZDot = 2 * MathArrays.linearCombination(-q3, q0DotDot, q2, q1DotDot, -q1, q2DotDot, q0, q3DotDot);
777
778 return new AngularCoordinates(new Rotation(q0, q1, q2, q3, false),
779 new Vector3D(oX, oY, oZ),
780 new Vector3D(oXDot, oYDot, oZDot));
781
782 }
783
784 }