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