NumCpp  2.17.0
A Templatized Header Only C++ Implementation of the Python NumPy Library
Loading...
Searching...
No Matches
Quaternion.hpp
Go to the documentation of this file.
1
28#pragma once
29
30#include <array>
31#include <cmath>
32#include <iostream>
33#include <string>
34
37#include "NumCpp/Core/Types.hpp"
44#include "NumCpp/Linalg/hat.hpp"
45#include "NumCpp/NdArray.hpp"
48#include "NumCpp/Utils/sqr.hpp"
50
51namespace nc::rotations
52{
53 //================================================================================
54 // Class Description:
57 {
58 public:
59 //============================================================================
60 // Method Description:
63 Quaternion() = default;
64
65 //============================================================================
66 // Method Description:
73 Quaternion(double roll, double pitch, double yaw) noexcept
74 {
75 eulerToQuat(roll, pitch, yaw);
76 }
77
78 //============================================================================
79 // Method Description:
87 Quaternion(double inI, double inJ, double inK, double inS) noexcept :
88 components_{ inI, inJ, inK, inS }
89 {
90 normalize();
91 }
92
93 //============================================================================
94 // Method Description:
99 Quaternion(const std::array<double, 4>& components) noexcept :
100 components_{ components }
101 {
102 normalize();
103 }
104
105 //============================================================================
106 // Method Description:
113 Quaternion(const NdArray<double>& inArray) :
114 components_{ 0., 0., 0., 0. }
115 {
116 if (inArray.size() == 3)
117 {
118 // euler angles
119 eulerToQuat(inArray[0], inArray[1], inArray[2]);
120 }
121 else if (inArray.size() == 4)
122 {
123 // quaternion i, j, k, s components
124 stl_algorithms::copy(inArray.cbegin(), inArray.cend(), components_.begin());
125 normalize();
126 }
127 else if (inArray.size() == 9)
128 {
129 // direction cosine matrix
130 dcmToQuat(inArray);
131 }
132 else
133 {
134 THROW_INVALID_ARGUMENT_ERROR("input array is not a valid size.");
135 }
136 }
137
138 //============================================================================
139 // Method Description:
145 Quaternion(const Vec3& inAxis, double inAngle) noexcept
146 {
147 // normalize the input vector
148 Vec3 normAxis = inAxis.normalize();
149
150 const double halfAngle = inAngle / 2.;
151 const double sinHalfAngle = std::sin(halfAngle);
152
153 components_[0] = normAxis.x * sinHalfAngle;
154 components_[1] = normAxis.y * sinHalfAngle;
155 components_[2] = normAxis.z * sinHalfAngle;
156 components_[3] = std::cos(halfAngle);
157 }
158
159 //============================================================================
160 // Method Description:
166 Quaternion(const NdArray<double>& inAxis, double inAngle) :
167 Quaternion(Vec3(inAxis), inAngle)
168 {
169 }
170
171 //============================================================================
172 // Method Description:
177 [[nodiscard]] double angleOfRotation() const noexcept
178 {
179 return 2. * std::acos(s());
180 }
181
182 //============================================================================
183 // Method Description:
192 static NdArray<double> angularVelocity(const Quaternion& inQuat1, const Quaternion& inQuat2, double inTime)
193 {
194 NdArray<double> q0 = inQuat1.toNdArray();
195 NdArray<double> q1 = inQuat2.toNdArray();
196
197 NdArray<double> qDot = q1 - q0;
198 qDot /= inTime;
199
200 NdArray<double> eyeTimesScalar(3);
201 eyeTimesScalar.zeros();
202 eyeTimesScalar(0, 0) = inQuat2.s();
203 eyeTimesScalar(1, 1) = inQuat2.s();
204 eyeTimesScalar(2, 2) = inQuat2.s();
205
206 NdArray<double> epsilonHat = linalg::hat<double>(inQuat2.i(), inQuat2.j(), inQuat2.k());
207 NdArray<double> q(4, 3);
208 q.put(Slice(0, 3), Slice(0, 3), eyeTimesScalar + epsilonHat);
209 q(3, 0) = -inQuat2.i();
210 q(3, 1) = -inQuat2.j();
211 q(3, 2) = -inQuat2.k();
212
213 NdArray<double> omega = q.transpose().dot(qDot.transpose());
214 return omega *= 2.;
215 }
216
217 //============================================================================
218 // Method Description:
226 [[nodiscard]] NdArray<double> angularVelocity(const Quaternion& inQuat2, double inTime) const
227 {
228 return angularVelocity(*this, inQuat2, inTime);
229 }
230
231 //============================================================================
232 // Method Description:
237 [[nodiscard]] Vec3 axisOfRotation() const noexcept
238 {
239 const auto halfAngle = angleOfRotation() / 2.;
240 const auto sinHalfAngle = std::sin(halfAngle);
241 auto axis = Vec3(i() / sinHalfAngle, j() / sinHalfAngle, k() / sinHalfAngle);
242
243 // shouldn't be necessary, but let's be pedantic
244 return axis.normalize();
245 }
246
247 //============================================================================
248 // Method Description:
253 [[nodiscard]] Quaternion conjugate() const noexcept
254 {
255 return { -i(), -j(), -k(), s() };
256 }
257
258 //============================================================================
259 // Method Description:
264 [[nodiscard]] double i() const noexcept
265 {
266 return components_[0];
267 }
268
269 //============================================================================
270 // Method Description:
275 static Quaternion identity() noexcept
276 {
277 return {};
278 }
279
280 //============================================================================
281 // Method Description:
286 [[nodiscard]] Quaternion inverse() const noexcept
287 {
289 return conjugate();
290 }
291
292 //============================================================================
293 // Method Description:
298 [[nodiscard]] double j() const noexcept
299 {
300 return components_[1];
301 }
302
303 //============================================================================
304 // Method Description:
309 [[nodiscard]] double k() const noexcept
310 {
311 return components_[2];
312 }
313
314 //============================================================================
315 // Method Description:
323 static Quaternion nlerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent)
324 {
325 if (inPercent < 0. || inPercent > 1.)
326 {
327 THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0,1].");
328 }
329
330 if (utils::essentiallyEqual(inPercent, 0.))
331 {
332 return inQuat1;
333 }
334 if (utils::essentiallyEqual(inPercent, 1.))
335 {
336 return inQuat2;
337 }
338
339 const double oneMinus = 1. - inPercent;
340 std::array<double, 4> newComponents{};
341
342 stl_algorithms::transform(inQuat1.components_.begin(),
343 inQuat1.components_.end(),
344 inQuat2.components_.begin(),
345 newComponents.begin(),
346 [inPercent, oneMinus](double component1, double component2) -> double
347 { return oneMinus * component1 + inPercent * component2; });
348
349 return { newComponents };
350 }
351
352 //============================================================================
353 // Method Description:
360 [[nodiscard]] Quaternion nlerp(const Quaternion& inQuat2, double inPercent) const
361 {
362 return nlerp(*this, inQuat2, inPercent);
363 }
364
365 //============================================================================
366 // Method Description:
371 [[nodiscard]] double pitch() const noexcept
372 {
373 return std::asin(2 * (s() * j() - k() * i()));
374 }
375
376 //============================================================================
377 // Method Description:
383 static Quaternion pitchRotation(double inAngle) noexcept
384 {
385 return { 0., inAngle, 0. };
386 }
387
388 //============================================================================
389 // Method Description:
392 void print() const
393 {
394 std::cout << *this;
395 }
396
397 //============================================================================
398 // Method Description:
404 void propagateBody(const Vec3& inAngularVelocity, double inDeltaT)
405 {
406 propagate(inAngularVelocity, inDeltaT, getBodyOmegaOperator(inAngularVelocity));
407 }
408
409 //============================================================================
410 // Method Description:
417 static Quaternion propagateBody(Quaternion inQuaternion, const Vec3& inAngularVelocity, double inDeltaT)
418 {
419 inQuaternion.propagateBody(inAngularVelocity, inDeltaT);
420 return inQuaternion;
421 }
422
423 //============================================================================
424 // Method Description:
430 void propagateBodyRoll(double rollRate, double inDeltaT)
431 {
432 propagateBody(Vec3{ rollRate, 0., 0. }, inDeltaT);
433 }
434
435 //============================================================================
436 // Method Description:
443 static Quaternion propagateBodyRoll(Quaternion inQuaternion, double rollRate, double inDeltaT)
444 {
445 inQuaternion.propagateBodyRoll(rollRate, inDeltaT);
446 return inQuaternion;
447 }
448
449 //============================================================================
450 // Method Description:
456 void propagateBodyPitch(double pitchRate, double inDeltaT)
457 {
458 propagateBody(Vec3{ 0., pitchRate, 0. }, inDeltaT);
459 }
460
461 //============================================================================
462 // Method Description:
469 static Quaternion propagateBodyPitch(Quaternion inQuaternion, double pitchRate, double inDeltaT)
470 {
471 inQuaternion.propagateBodyPitch(pitchRate, inDeltaT);
472 return inQuaternion;
473 }
474
475 //============================================================================
476 // Method Description:
482 void propagateBodyYaw(double yawRate, double inDeltaT)
483 {
484 propagateBody(Vec3{ 0., 0., yawRate }, inDeltaT);
485 }
486
487 //============================================================================
488 // Method Description:
495 static Quaternion propagateBodyYaw(Quaternion inQuaternion, double yawRate, double inDeltaT)
496 {
497 inQuaternion.propagateBodyYaw(yawRate, inDeltaT);
498 return inQuaternion;
499 }
500
501 //============================================================================
502 // Method Description:
508 void propagateInertial(const Vec3& inAngularVelocity, double inDeltaT)
509 {
510 propagate(inAngularVelocity, inDeltaT, getInertialOmegaOperator(inAngularVelocity));
511 }
512
513 //============================================================================
514 // Method Description:
521 static Quaternion propagateInertial(Quaternion inQuaternion, const Vec3& inAngularVelocity, double inDeltaT)
522 {
523 inQuaternion.propagateInertial(inAngularVelocity, inDeltaT);
524 return inQuaternion;
525 }
526
527 //============================================================================
528 // Method Description:
534 void propagateInertialRoll(double rollRate, double inDeltaT)
535 {
536 propagateInertial(Vec3{ rollRate, 0., 0. }, inDeltaT);
537 }
538
539 //============================================================================
540 // Method Description:
547 static Quaternion propagateInertialRoll(Quaternion inQuaternion, double rollRate, double inDeltaT)
548 {
549 inQuaternion.propagateInertialRoll(rollRate, inDeltaT);
550 return inQuaternion;
551 }
552
553 //============================================================================
554 // Method Description:
560 void propagateInertialPitch(double pitchRate, double inDeltaT)
561 {
562 propagateInertial(Vec3{ 0., pitchRate, 0. }, inDeltaT);
563 }
564
565 //============================================================================
566 // Method Description:
573 static Quaternion propagateInertialPitch(Quaternion inQuaternion, double pitchRate, double inDeltaT)
574 {
575 inQuaternion.propagateInertialPitch(pitchRate, inDeltaT);
576 return inQuaternion;
577 }
578
579 //============================================================================
580 // Method Description:
586 void propagateInertialYaw(double yawRate, double inDeltaT)
587 {
588 propagateInertial(Vec3{ 0., 0., yawRate }, inDeltaT);
589 }
590
591 //============================================================================
592 // Method Description:
599 static Quaternion propagateInertialYaw(Quaternion inQuaternion, double yawRate, double inDeltaT)
600 {
601 inQuaternion.propagateInertialYaw(yawRate, inDeltaT);
602 return inQuaternion;
603 }
604
605 //============================================================================
606 // Method Description:
611 [[nodiscard]] double roll() const noexcept
612 {
613 return std::atan2(2. * (s() * i() + j() * k()), 1. - 2. * (utils::sqr(i()) + utils::sqr(j())));
614 }
615
616 //============================================================================
617 // Method Description:
623 static Quaternion rollRotation(double inAngle) noexcept
624 {
625 return { inAngle, 0., 0. };
626 }
627
628 //============================================================================
629 // Method Description:
635 [[nodiscard]] NdArray<double> rotate(const NdArray<double>& inVector) const
636 {
637 if (inVector.size() != 3)
638 {
639 THROW_INVALID_ARGUMENT_ERROR("input inVector must be a cartesion vector of length = 3.");
640 }
641
642 return *this * inVector;
643 }
644
645 //============================================================================
646 // Method Description:
652 [[nodiscard]] Vec3 rotate(const Vec3& inVec3) const
653 {
654 return *this * inVec3;
655 }
656
657 //============================================================================
658 // Method Description:
663 [[nodiscard]] double s() const noexcept
664 {
665 return components_[3];
666 }
667
668 //============================================================================
669 // Method Description:
677 static Quaternion slerp(const Quaternion& inQuat1, const Quaternion& inQuat2, double inPercent)
678 {
679 if (inPercent < 0 || inPercent > 1)
680 {
681 THROW_INVALID_ARGUMENT_ERROR("input percent must be of the range [0, 1]");
682 }
683
684 if (utils::essentiallyEqual(inPercent, 0.))
685 {
686 return inQuat1;
687 }
688 if (utils::essentiallyEqual(inPercent, 1.))
689 {
690 return inQuat2;
691 }
692
693 double dotProduct = dot<double>(inQuat1.toNdArray(), inQuat2.toNdArray()).item();
694
695 // If the dot product is negative, the quaternions
696 // have opposite handed-ness and slerp won't take
697 // the shorter path. Fix by reversing one quaternion.
698 Quaternion quat1Copy(inQuat1);
699 if (dotProduct < 0.)
700 {
701 quat1Copy *= -1.;
702 dotProduct *= -1.;
703 }
704
705 constexpr double DOT_THRESHOLD = 0.9995;
706 if (dotProduct > DOT_THRESHOLD)
707 {
708 // If the inputs are too close for comfort, linearly interpolate
709 // and normalize the result.
710 return nlerp(inQuat1, inQuat2, inPercent);
711 }
712
713 dotProduct = clip(dotProduct, -1., 1.); // Robustness: Stay within domain of acos()
714 const double theta0 = std::acos(dotProduct); // angle between input vectors
715 const double theta = theta0 * inPercent; // angle between v0 and result
716
717 const double s0 = std::cos(theta) -
718 dotProduct * std::sin(theta) / std::sin(theta0); // == sin(theta_0 - theta) / sin(theta_0)
719 const double s1 = std::sin(theta) / std::sin(theta0);
720
721 NdArray<double> interpQuat = (quat1Copy.toNdArray() * s0) + (inQuat2.toNdArray() * s1);
722 return Quaternion(interpQuat); // NOLINT(modernize-return-braced-init-list)
723 }
724
725 //============================================================================
726 // Method Description:
733 [[nodiscard]] Quaternion slerp(const Quaternion& inQuat2, double inPercent) const
734 {
735 return slerp(*this, inQuat2, inPercent);
736 }
737
738 //============================================================================
739 // Method Description:
744 [[nodiscard]] std::string str() const
745 {
746 std::string output = "[" + utils::num2str(i()) + ", " + utils::num2str(j()) + ", " + utils::num2str(k()) +
747 ", " + utils::num2str(s()) + "]\n";
748
749 return output;
750 }
751
752 //============================================================================
753 // Method Description:
758 [[nodiscard]] NdArray<double> toDCM() const
759 {
760 NdArray<double> dcm(3);
761
762 const double q0 = i();
763 const double q1 = j();
764 const double q2 = k();
765 const double q3 = s();
766
767 const double q0sqr = utils::sqr(q0);
768 const double q1sqr = utils::sqr(q1);
769 const double q2sqr = utils::sqr(q2);
770 const double q3sqr = utils::sqr(q3);
771
772 dcm(0, 0) = q3sqr + q0sqr - q1sqr - q2sqr;
773 dcm(0, 1) = 2. * (q0 * q1 - q3 * q2);
774 dcm(0, 2) = 2. * (q0 * q2 + q3 * q1);
775 dcm(1, 0) = 2. * (q0 * q1 + q3 * q2);
776 dcm(1, 1) = q3sqr + q1sqr - q0sqr - q2sqr;
777 dcm(1, 2) = 2. * (q1 * q2 - q3 * q0);
778 dcm(2, 0) = 2. * (q0 * q2 - q3 * q1);
779 dcm(2, 1) = 2. * (q1 * q2 + q3 * q0);
780 dcm(2, 2) = q3sqr + q2sqr - q0sqr - q1sqr;
781
782 return dcm;
783 }
784
785 //============================================================================
786 // Method Description:
791 [[nodiscard]] NdArray<double> toNdArray() const
792 {
793 auto componentsCopy = components_;
794 return NdArray<double>(componentsCopy); // NOLINT(modernize-return-braced-init-list)
795 }
796
797 //============================================================================
798 // Method Description:
804 static Quaternion xRotation(double inAngle) noexcept
805 {
806 const Vec3 eulerAxis = { 1., 0., 0. };
807 return Quaternion(eulerAxis, inAngle); // NOLINT(modernize-return-braced-init-list)
808 }
809
810 //============================================================================
811 // Method Description:
816 [[nodiscard]] double yaw() const noexcept
817 {
818 return std::atan2(2. * (s() * k() + i() * j()), 1. - 2. * (utils::sqr(j()) + utils::sqr(k())));
819 }
820
821 //============================================================================
822 // Method Description:
828 static Quaternion yawRotation(double inAngle) noexcept
829 {
830 return { 0., 0., inAngle };
831 }
832
833 //============================================================================
834 // Method Description:
840 static Quaternion yRotation(double inAngle) noexcept
841 {
842 const Vec3 eulerAxis = { 0., 1., 0. };
843 return Quaternion(eulerAxis, inAngle); // NOLINT(modernize-return-braced-init-list)
844 }
845
846 //============================================================================
847 // Method Description:
853 static Quaternion zRotation(double inAngle) noexcept
854 {
855 const Vec3 eulerAxis = { 0., 0., 1. };
856 return Quaternion(eulerAxis, inAngle); // NOLINT(modernize-return-braced-init-list)
857 }
858
859 //============================================================================
860 // Method Description:
866 bool operator==(const Quaternion& inRhs) const noexcept
867 {
868 const auto comparitor = [](double value1, double value2) noexcept -> bool
869 { return utils::essentiallyEqual(value1, value2); };
870
871 return stl_algorithms::equal(components_.begin(), components_.end(), inRhs.components_.begin(), comparitor);
872 }
873
874 //============================================================================
875 // Method Description:
881 bool operator!=(const Quaternion& inRhs) const noexcept
882 {
883 return !(*this == inRhs);
884 }
885
886 //============================================================================
887 // Method Description:
893 Quaternion& operator+=(const Quaternion& inRhs) noexcept
894 {
895 stl_algorithms::transform(components_.begin(),
896 components_.end(),
897 inRhs.components_.begin(),
898 components_.begin(),
899 std::plus<double>()); // NOLINT(modernize-use-transparent-functors)
900
901 normalize();
902
903 return *this;
904 }
905
906 //============================================================================
907 // Method Description:
913 Quaternion operator+(const Quaternion& inRhs) const noexcept
914 {
915 return Quaternion(*this) += inRhs;
916 }
917
918 //============================================================================
919 // Method Description:
925 Quaternion& operator-=(const Quaternion& inRhs) noexcept
926 {
927 stl_algorithms::transform(components_.begin(),
928 components_.end(),
929 inRhs.components_.begin(),
930 components_.begin(),
931 std::minus<double>()); // NOLINT(modernize-use-transparent-functors)
932
933 normalize();
934
935 return *this;
936 }
937
938 //============================================================================
939 // Method Description:
945 Quaternion operator-(const Quaternion& inRhs) const noexcept
946 {
947 return Quaternion(*this) -= inRhs;
948 }
949
950 //============================================================================
951 // Method Description:
956 Quaternion operator-() const noexcept
957 {
958 return Quaternion(*this) *= -1.;
959 }
960
961 //============================================================================
962 // Method Description:
968 Quaternion& operator*=(const Quaternion& inRhs) noexcept
969 {
970 double q0 = inRhs.s() * i();
971 q0 += inRhs.i() * s();
972 q0 -= inRhs.j() * k();
973 q0 += inRhs.k() * j();
974
975 double q1 = inRhs.s() * j();
976 q1 += inRhs.i() * k();
977 q1 += inRhs.j() * s();
978 q1 -= inRhs.k() * i();
979
980 double q2 = inRhs.s() * k();
981 q2 -= inRhs.i() * j();
982 q2 += inRhs.j() * i();
983 q2 += inRhs.k() * s();
984
985 double q3 = inRhs.s() * s();
986 q3 -= inRhs.i() * i();
987 q3 -= inRhs.j() * j();
988 q3 -= inRhs.k() * k();
989
990 components_[0] = q0;
991 components_[1] = q1;
992 components_[2] = q2;
993 components_[3] = q3;
994
995 normalize();
996
997 return *this;
998 }
999
1000 //============================================================================
1001 // Method Description:
1008 Quaternion& operator*=(double inScalar) noexcept
1009 {
1010 stl_algorithms::for_each(components_.begin(),
1011 components_.end(),
1012 [inScalar](double& component) { component *= inScalar; });
1013
1014 normalize();
1015
1016 return *this;
1017 }
1018
1019 //============================================================================
1020 // Method Description:
1026 Quaternion operator*(const Quaternion& inRhs) const noexcept
1027 {
1028 return Quaternion(*this) *= inRhs;
1029 }
1030
1031 //============================================================================
1032 // Method Description:
1039 Quaternion operator*(double inScalar) const noexcept
1040 {
1041 return Quaternion(*this) *= inScalar;
1042 }
1043
1044 //============================================================================
1045 // Method Description:
1052 {
1053 if (inVec.size() != 3)
1054 {
1055 THROW_INVALID_ARGUMENT_ERROR("input vector must be a cartesion vector of length = 3.");
1056 }
1057
1058 const auto vecNorm = norm(inVec).item();
1059 if (utils::essentiallyEqual(vecNorm, 0.))
1060 {
1061 return inVec;
1062 }
1063
1064 const auto p = Quaternion(inVec[0], inVec[1], inVec[2], 0.);
1065 const auto pPrime = *this * p * this->inverse();
1066
1067 NdArray<double> rotatedVec = { pPrime.i(), pPrime.j(), pPrime.k() };
1068 rotatedVec *= vecNorm;
1069 return rotatedVec;
1070 }
1071
1072 //============================================================================
1073 // Method Description:
1079 Vec3 operator*(const Vec3& inVec3) const
1080 {
1081 return *this * inVec3.toNdArray();
1082 }
1083
1084 //============================================================================
1085 // Method Description:
1091 Quaternion& operator/=(const Quaternion& inRhs) noexcept
1092 {
1093 return *this *= inRhs.conjugate();
1094 }
1095
1096 //============================================================================
1097 // Method Description:
1103 Quaternion operator/(const Quaternion& inRhs) const noexcept
1104 {
1105 return Quaternion(*this) /= inRhs;
1106 }
1107
1108 //============================================================================
1109 // Method Description:
1116 friend std::ostream& operator<<(std::ostream& inOStream, const Quaternion& inQuat)
1117 {
1118 inOStream << inQuat.str();
1119 return inOStream;
1120 }
1121
1122 private:
1123 //====================================Attributes==============================
1124 std::array<double, 4> components_{ { 0., 0., 0., 1. } };
1125
1126 //============================================================================
1127 // Method Description:
1130 void normalize() noexcept
1131 {
1132 double sumOfSquares = 0.;
1133 std::for_each(components_.begin(),
1134 components_.end(),
1135 [&sumOfSquares](double component) noexcept -> void
1136 { sumOfSquares += utils::sqr(component); });
1137
1138 const double norm = std::sqrt(sumOfSquares);
1139 stl_algorithms::for_each(components_.begin(),
1140 components_.end(),
1141 [norm](double& component) noexcept -> void { component /= norm; });
1142 }
1143
1144 //============================================================================
1145 // Method Description:
1152 void eulerToQuat(double roll, double pitch, double yaw) noexcept
1153 {
1154 const auto halfPhi = roll / 2.;
1155 const auto halfTheta = pitch / 2.;
1156 const auto halfPsi = yaw / 2.;
1157
1158 const auto sinHalfPhi = std::sin(halfPhi);
1159 const auto cosHalfPhi = std::cos(halfPhi);
1160
1161 const auto sinHalfTheta = std::sin(halfTheta);
1162 const auto cosHalfTheta = std::cos(halfTheta);
1163
1164 const auto sinHalfPsi = std::sin(halfPsi);
1165 const auto cosHalfPsi = std::cos(halfPsi);
1166
1167 components_[0] = sinHalfPhi * cosHalfTheta * cosHalfPsi;
1168 components_[0] -= cosHalfPhi * sinHalfTheta * sinHalfPsi;
1169
1170 components_[1] = cosHalfPhi * sinHalfTheta * cosHalfPsi;
1171 components_[1] += sinHalfPhi * cosHalfTheta * sinHalfPsi;
1172
1173 components_[2] = cosHalfPhi * cosHalfTheta * sinHalfPsi;
1174 components_[2] -= sinHalfPhi * sinHalfTheta * cosHalfPsi;
1175
1176 components_[3] = cosHalfPhi * cosHalfTheta * cosHalfPsi;
1177 components_[3] += sinHalfPhi * sinHalfTheta * sinHalfPsi;
1178 }
1179
1180 //============================================================================
1181 // Method Description:
1186 void dcmToQuat(const NdArray<double>& dcm)
1187 {
1188 const Shape inShape = dcm.shape();
1189 if (!(inShape.rows == 3 && inShape.cols == 3))
1190 {
1191 THROW_INVALID_ARGUMENT_ERROR("input direction cosine matrix must have shape = (3,3).");
1192 }
1193
1194 NdArray<double> checks(1, 4);
1195 checks[0] = 1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2);
1196 checks[1] = 1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2);
1197 checks[2] = 1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2);
1198 checks[3] = 1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2);
1199
1200 const uint32 maxIdx = argmax(checks).item();
1201
1202 switch (maxIdx)
1203 {
1204 case 0:
1205 {
1206 components_[3] = 0.5 * std::sqrt(1 + dcm(0, 0) + dcm(1, 1) + dcm(2, 2));
1207 components_[0] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[3]);
1208 components_[1] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[3]);
1209 components_[2] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[3]);
1210
1211 break;
1212 }
1213 case 1:
1214 {
1215 components_[0] = 0.5 * std::sqrt(1 + dcm(0, 0) - dcm(1, 1) - dcm(2, 2));
1216 components_[1] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[0]);
1217 components_[2] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[0]);
1218 components_[3] = (dcm(2, 1) - dcm(1, 2)) / (4 * components_[0]);
1219
1220 break;
1221 }
1222 case 2:
1223 {
1224 components_[1] = 0.5 * std::sqrt(1 - dcm(0, 0) + dcm(1, 1) - dcm(2, 2));
1225 components_[0] = (dcm(1, 0) + dcm(0, 1)) / (4 * components_[1]);
1226 components_[2] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[1]);
1227 components_[3] = (dcm(0, 2) - dcm(2, 0)) / (4 * components_[1]);
1228
1229 break;
1230 }
1231 case 3:
1232 {
1233 components_[2] = 0.5 * std::sqrt(1 - dcm(0, 0) - dcm(1, 1) + dcm(2, 2));
1234 components_[0] = (dcm(2, 0) + dcm(0, 2)) / (4 * components_[2]);
1235 components_[1] = (dcm(2, 1) + dcm(1, 2)) / (4 * components_[2]);
1236 components_[3] = (dcm(1, 0) - dcm(0, 1)) / (4 * components_[2]);
1237
1238 break;
1239 }
1240 }
1241 }
1242
1243 //============================================================================
1244 // Method Description:
1251 void propagate(const Vec3& inAngularVelocity, double inDeltaT, const NdArray<double>& inOmegaOperator)
1252 {
1253 if (utils::essentiallyEqual(inDeltaT, 0.))
1254 {
1255 return;
1256 }
1257
1258 const auto angularVelocityNorm = inAngularVelocity.norm();
1259 if (utils::essentiallyEqual(angularVelocityNorm, 0.))
1260 {
1261 return;
1262 }
1263
1264 const auto halfDeltaT = inDeltaT / 2.;
1265 const auto halfAngle = angularVelocityNorm * halfDeltaT;
1266 const auto sinHalfAngle = std::sin(halfAngle) / angularVelocityNorm;
1267 const auto cosHalfAngle = std::cos(halfAngle);
1268
1269 const auto lhs = cosHalfAngle * eye<double>(4);
1270 const auto rhs = sinHalfAngle * inOmegaOperator;
1271
1272 const auto qDeltaT = (lhs + rhs).dot(toNdArray().transpose());
1273
1274 components_[0] = qDeltaT[0];
1275 components_[1] = qDeltaT[1];
1276 components_[2] = qDeltaT[2];
1277 components_[3] = qDeltaT[3];
1278
1279 normalize();
1280 }
1281
1282 //============================================================================
1283 // Method Description:
1288 NdArray<double> getBodyOmegaOperator(const Vec3& inAngularVelocity) const
1289 {
1290 return NdArray<double>({ { 0., inAngularVelocity.z, -inAngularVelocity.y, inAngularVelocity.x },
1291 { -inAngularVelocity.z, 0., inAngularVelocity.x, inAngularVelocity.y },
1292 { inAngularVelocity.y, -inAngularVelocity.x, 0., inAngularVelocity.z },
1293 { -inAngularVelocity.x, -inAngularVelocity.y, -inAngularVelocity.z, 0. } });
1294 }
1295
1296 //============================================================================
1297 // Method Description:
1302 NdArray<double> getInertialOmegaOperator(const Vec3& inAngularVelocity) const
1303 {
1304 return NdArray<double>({ { 0., -inAngularVelocity.z, inAngularVelocity.y, inAngularVelocity.x },
1305 { inAngularVelocity.z, 0., -inAngularVelocity.x, inAngularVelocity.y },
1306 { -inAngularVelocity.y, inAngularVelocity.x, 0., inAngularVelocity.z },
1307 { -inAngularVelocity.x, -inAngularVelocity.y, -inAngularVelocity.z, 0. } });
1308 }
1309 };
1310} // namespace nc::rotations
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition Error.hpp:37
Holds 1D and 2D arrays, the main work horse of the NumCpp library.
Definition NdArrayCore.hpp:139
size_type size() const noexcept
Definition NdArrayCore.hpp:4604
self_type & zeros() noexcept
Definition NdArrayCore.hpp:4981
const_iterator cbegin() const noexcept
Definition NdArrayCore.hpp:1365
self_type transpose() const
Definition NdArrayCore.hpp:4963
self_type dot(const self_type &inOtherArray) const
Definition NdArrayCore.hpp:2795
const_iterator cend() const noexcept
Definition NdArrayCore.hpp:1673
value_type item() const
Definition NdArrayCore.hpp:3102
self_type & put(index_type inIndex, const value_type &inValue)
Definition NdArrayCore.hpp:3773
A Class for slicing into NdArrays.
Definition Slice.hpp:45
Holds a 3D vector.
Definition Vec3.hpp:51
double z
Definition Vec3.hpp:56
Vec3 normalize() const noexcept
Definition Vec3.hpp:289
double x
Definition Vec3.hpp:54
double y
Definition Vec3.hpp:55
NdArray< double > toNdArray() const
Definition Vec3.hpp:337
void propagateInertialPitch(double pitchRate, double inDeltaT)
Definition Quaternion.hpp:560
double s() const noexcept
Definition Quaternion.hpp:663
std::string str() const
Definition Quaternion.hpp:744
double angleOfRotation() const noexcept
Definition Quaternion.hpp:177
friend std::ostream & operator<<(std::ostream &inOStream, const Quaternion &inQuat)
Definition Quaternion.hpp:1116
double roll() const noexcept
Definition Quaternion.hpp:611
static Quaternion xRotation(double inAngle) noexcept
Definition Quaternion.hpp:804
static Quaternion nlerp(const Quaternion &inQuat1, const Quaternion &inQuat2, double inPercent)
Definition Quaternion.hpp:323
static Quaternion propagateInertialPitch(Quaternion inQuaternion, double pitchRate, double inDeltaT)
Definition Quaternion.hpp:573
static Quaternion rollRotation(double inAngle) noexcept
Definition Quaternion.hpp:623
Vec3 rotate(const Vec3 &inVec3) const
Definition Quaternion.hpp:652
Quaternion(double inI, double inJ, double inK, double inS) noexcept
Definition Quaternion.hpp:87
NdArray< double > angularVelocity(const Quaternion &inQuat2, double inTime) const
Definition Quaternion.hpp:226
Quaternion operator-() const noexcept
Definition Quaternion.hpp:956
static Quaternion propagateBody(Quaternion inQuaternion, const Vec3 &inAngularVelocity, double inDeltaT)
Definition Quaternion.hpp:417
NdArray< double > operator*(const NdArray< double > &inVec) const
Definition Quaternion.hpp:1051
Quaternion(const std::array< double, 4 > &components) noexcept
Definition Quaternion.hpp:99
static Quaternion propagateBodyRoll(Quaternion inQuaternion, double rollRate, double inDeltaT)
Definition Quaternion.hpp:443
Quaternion operator+(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:913
void propagateBodyYaw(double yawRate, double inDeltaT)
Definition Quaternion.hpp:482
double i() const noexcept
Definition Quaternion.hpp:264
double yaw() const noexcept
Definition Quaternion.hpp:816
double pitch() const noexcept
Definition Quaternion.hpp:371
Quaternion slerp(const Quaternion &inQuat2, double inPercent) const
Definition Quaternion.hpp:733
NdArray< double > toNdArray() const
Definition Quaternion.hpp:791
Quaternion & operator/=(const Quaternion &inRhs) noexcept
Definition Quaternion.hpp:1091
void propagateInertialRoll(double rollRate, double inDeltaT)
Definition Quaternion.hpp:534
static Quaternion slerp(const Quaternion &inQuat1, const Quaternion &inQuat2, double inPercent)
Definition Quaternion.hpp:677
static Quaternion yawRotation(double inAngle) noexcept
Definition Quaternion.hpp:828
void print() const
Definition Quaternion.hpp:392
Quaternion(const NdArray< double > &inAxis, double inAngle)
Definition Quaternion.hpp:166
bool operator==(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:866
NdArray< double > rotate(const NdArray< double > &inVector) const
Definition Quaternion.hpp:635
static Quaternion propagateInertialRoll(Quaternion inQuaternion, double rollRate, double inDeltaT)
Definition Quaternion.hpp:547
Quaternion(double roll, double pitch, double yaw) noexcept
Definition Quaternion.hpp:73
static Quaternion propagateInertial(Quaternion inQuaternion, const Vec3 &inAngularVelocity, double inDeltaT)
Definition Quaternion.hpp:521
void propagateInertial(const Vec3 &inAngularVelocity, double inDeltaT)
Definition Quaternion.hpp:508
Vec3 operator*(const Vec3 &inVec3) const
Definition Quaternion.hpp:1079
Quaternion inverse() const noexcept
Definition Quaternion.hpp:286
void propagateBodyPitch(double pitchRate, double inDeltaT)
Definition Quaternion.hpp:456
double k() const noexcept
Definition Quaternion.hpp:309
static Quaternion zRotation(double inAngle) noexcept
Definition Quaternion.hpp:853
NdArray< double > toDCM() const
Definition Quaternion.hpp:758
Quaternion operator/(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:1103
Quaternion nlerp(const Quaternion &inQuat2, double inPercent) const
Definition Quaternion.hpp:360
static Quaternion propagateInertialYaw(Quaternion inQuaternion, double yawRate, double inDeltaT)
Definition Quaternion.hpp:599
void propagateBodyRoll(double rollRate, double inDeltaT)
Definition Quaternion.hpp:430
static Quaternion yRotation(double inAngle) noexcept
Definition Quaternion.hpp:840
void propagateBody(const Vec3 &inAngularVelocity, double inDeltaT)
Definition Quaternion.hpp:404
Quaternion(const Vec3 &inAxis, double inAngle) noexcept
Definition Quaternion.hpp:145
void propagateInertialYaw(double yawRate, double inDeltaT)
Definition Quaternion.hpp:586
Quaternion & operator*=(double inScalar) noexcept
Definition Quaternion.hpp:1008
double j() const noexcept
Definition Quaternion.hpp:298
Quaternion & operator+=(const Quaternion &inRhs) noexcept
Definition Quaternion.hpp:893
static Quaternion propagateBodyYaw(Quaternion inQuaternion, double yawRate, double inDeltaT)
Definition Quaternion.hpp:495
Quaternion operator*(double inScalar) const noexcept
Definition Quaternion.hpp:1039
Quaternion operator-(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:945
Quaternion operator*(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:1026
bool operator!=(const Quaternion &inRhs) const noexcept
Definition Quaternion.hpp:881
Quaternion(const NdArray< double > &inArray)
Definition Quaternion.hpp:113
Quaternion conjugate() const noexcept
Definition Quaternion.hpp:253
static Quaternion propagateBodyPitch(Quaternion inQuaternion, double pitchRate, double inDeltaT)
Definition Quaternion.hpp:469
static Quaternion identity() noexcept
Definition Quaternion.hpp:275
Vec3 axisOfRotation() const noexcept
Definition Quaternion.hpp:237
static NdArray< double > angularVelocity(const Quaternion &inQuat1, const Quaternion &inQuat2, double inTime)
Definition Quaternion.hpp:192
Quaternion & operator-=(const Quaternion &inRhs) noexcept
Definition Quaternion.hpp:925
Quaternion & operator*=(const Quaternion &inRhs) noexcept
Definition Quaternion.hpp:968
static Quaternion pitchRotation(double inAngle) noexcept
Definition Quaternion.hpp:383
NdArray< dtype > hat(dtype inX, dtype inY, dtype inZ)
Definition hat.hpp:49
Definition DCM.hpp:39
OutputIt transform(InputIt first, InputIt last, OutputIt destination, UnaryOperation unaryFunction)
Definition StlAlgorithms.hpp:776
void for_each(InputIt first, InputIt last, UnaryFunction f)
Definition StlAlgorithms.hpp:226
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) noexcept
Definition StlAlgorithms.hpp:141
OutputIt copy(InputIt first, InputIt last, OutputIt destination) noexcept
Definition StlAlgorithms.hpp:98
std::string num2str(dtype inNumber)
Definition num2str.hpp:44
bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept
Definition essentiallyEqual.hpp:49
constexpr dtype sqr(dtype inValue) noexcept
Definition sqr.hpp:42
NdArray< double > norm(const NdArray< dtype > &inArray, Axis inAxis=Axis::NONE)
Definition norm.hpp:51
NdArray< dtype > dot(const NdArray< dtype > &inArray1, const NdArray< dtype > &inArray2)
Definition dot.hpp:48
dtype clip(dtype inValue, dtype inMinValue, dtype inMaxValue)
Definition clip.hpp:50
NdArray< uint32 > argmax(const NdArray< dtype > &inArray, Axis inAxis=Axis::NONE)
Definition argmax.hpp:46
NdArray< dtype > eye(uint32 inN, uint32 inM, int32 inK=0)
Definition eye.hpp:51
NdArray< double > normalize(const NdArray< dtype > &inArray, Axis inAxis=Axis::NONE)
Definition normalize.hpp:52
std::uint32_t uint32
Definition Types.hpp:40
NdArray< dtype > transpose(const NdArray< dtype > &inArray)
Definition transpose.hpp:45