C Attitude Math Library
Quaternion, Euler, and DCM math utilities for attitude control.
Loading...
Searching...
No Matches
dcm.c
Go to the documentation of this file.
1#include "attitude/dcm.h"
2#include <math.h>
3#include <stddef.h>
4
5int dcm_is_orthonormal(const double dcm[3][3], double tol) {
6 if (dcm == NULL || !isfinite(tol) || tol < 0.0) {
7 return 0;
8 }
9
10 for (int row = 0; row < 3; ++row) {
11 for (int column = 0; column < 3; ++column) {
12 if (!isfinite(dcm[row][column])) {
13 return 0;
14 }
15 }
16 }
17
18 for (int left = 0; left < 3; ++left) {
19 for (int right = 0; right < 3; ++right) {
20 double row_dot = 0.0;
21 double column_dot = 0.0;
22 for (int index = 0; index < 3; ++index) {
23 row_dot += dcm[left][index] * dcm[right][index];
24 column_dot += dcm[index][left] * dcm[index][right];
25 }
26 const double expected = left == right ? 1.0 : 0.0;
27 if (fabs(row_dot - expected) > tol || fabs(column_dot - expected) > tol) {
28 return 0;
29 }
30 }
31 }
32
33 const double determinant =
34 dcm[0][0] * (dcm[1][1] * dcm[2][2] - dcm[1][2] * dcm[2][1]) -
35 dcm[0][1] * (dcm[1][0] * dcm[2][2] - dcm[1][2] * dcm[2][0]) +
36 dcm[0][2] * (dcm[1][0] * dcm[2][1] - dcm[1][1] * dcm[2][0]);
37 if (fabs(determinant - 1.0) > tol) {
38 return 0;
39 }
40
41 return 1;
42}
43
44int dcm_to_euler_checked(const double dcm[3][3],
45 double *roll,
46 double *pitch,
47 double *yaw) {
48 if (roll == NULL || pitch == NULL || yaw == NULL ||
49 !dcm_is_orthonormal(dcm, 1e-9)) {
50 return 0;
51 }
52
53 const double horizontal = hypot(dcm[0][0], dcm[1][0]);
54 *pitch = atan2(-dcm[2][0], horizontal);
55 if (horizontal > 1e-12) {
56 *roll = atan2(dcm[2][1], dcm[2][2]);
57 *yaw = atan2(dcm[1][0], dcm[0][0]);
58 } else {
59 *roll = 0.0;
60 *yaw = atan2(-dcm[0][1], dcm[1][1]);
61 }
62 return 1;
63}
64
65void dcm_to_euler(const double dcm[3][3], double *roll, double *pitch, double *yaw) {
66 if (!dcm_to_euler_checked(dcm, roll, pitch, yaw)) {
67 if (roll != NULL) *roll = NAN;
68 if (pitch != NULL) *pitch = NAN;
69 if (yaw != NULL) *yaw = NAN;
70 }
71}
72
73int dcm_to_quaternion_checked(const double dcm[3][3], double q[4]) {
74 if (q == NULL || !dcm_is_orthonormal(dcm, 1e-9)) {
75 return 0;
76 }
77
78 const double trace = dcm[0][0] + dcm[1][1] + dcm[2][2];
79 if (trace > 0.0) {
80 const double scale = 2.0 * sqrt(trace + 1.0);
81 q[0] = 0.25 * scale;
82 q[1] = (dcm[2][1] - dcm[1][2]) / scale;
83 q[2] = (dcm[0][2] - dcm[2][0]) / scale;
84 q[3] = (dcm[1][0] - dcm[0][1]) / scale;
85 } else if (dcm[0][0] > dcm[1][1] && dcm[0][0] > dcm[2][2]) {
86 const double scale = 2.0 * sqrt(1.0 + dcm[0][0] - dcm[1][1] - dcm[2][2]);
87 q[0] = (dcm[2][1] - dcm[1][2]) / scale;
88 q[1] = 0.25 * scale;
89 q[2] = (dcm[0][1] + dcm[1][0]) / scale;
90 q[3] = (dcm[0][2] + dcm[2][0]) / scale;
91 } else if (dcm[1][1] > dcm[2][2]) {
92 const double scale = 2.0 * sqrt(1.0 + dcm[1][1] - dcm[0][0] - dcm[2][2]);
93 q[0] = (dcm[0][2] - dcm[2][0]) / scale;
94 q[1] = (dcm[0][1] + dcm[1][0]) / scale;
95 q[2] = 0.25 * scale;
96 q[3] = (dcm[1][2] + dcm[2][1]) / scale;
97 } else {
98 const double scale = 2.0 * sqrt(1.0 + dcm[2][2] - dcm[0][0] - dcm[1][1]);
99 q[0] = (dcm[1][0] - dcm[0][1]) / scale;
100 q[1] = (dcm[0][2] + dcm[2][0]) / scale;
101 q[2] = (dcm[1][2] + dcm[2][1]) / scale;
102 q[3] = 0.25 * scale;
103 }
104
105 const double norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
106 for (int index = 0; index < 4; ++index) {
107 q[index] /= norm;
108 }
109 if (q[0] < 0.0) {
110 for (int index = 0; index < 4; ++index) {
111 q[index] = -q[index];
112 }
113 }
114 return 1;
115}
116
117void dcm_to_quaternion(const double dcm[3][3], double q[4]) {
118 if (!dcm_to_quaternion_checked(dcm, q) && q != NULL) {
119 for (int index = 0; index < 4; ++index) {
120 q[index] = NAN;
121 }
122 }
123}
124
125void dcm_apply(const double dcm[3][3], const double vin[3], double vout[3]) {
126 vout[0] = dcm[0][0]*vin[0] + dcm[0][1]*vin[1] + dcm[0][2]*vin[2];
127 vout[1] = dcm[1][0]*vin[0] + dcm[1][1]*vin[1] + dcm[1][2]*vin[2];
128 vout[2] = dcm[2][0]*vin[0] + dcm[2][1]*vin[1] + dcm[2][2]*vin[2];
129}
void dcm_to_euler(const double dcm[3][3], double *roll, double *pitch, double *yaw)
Convert a direction cosine matrix to intrinsic ZYX Euler angles.
Definition dcm.c:65
int dcm_to_euler_checked(const double dcm[3][3], double *roll, double *pitch, double *yaw)
Checked DCM-to-Euler conversion.
Definition dcm.c:44
int dcm_to_quaternion_checked(const double dcm[3][3], double q[4])
Checked DCM-to-quaternion conversion.
Definition dcm.c:73
int dcm_is_orthonormal(const double dcm[3][3], double tol)
Check whether a DCM is orthonormal within a tolerance.
Definition dcm.c:5
void dcm_apply(const double dcm[3][3], const double vin[3], double vout[3])
Apply a DCM to a vector.
Definition dcm.c:125
void dcm_to_quaternion(const double dcm[3][3], double q[4])
Convert a DCM to a quaternion.
Definition dcm.c:117