C Attitude Math Library
Quaternion, Euler, and DCM math utilities for attitude control.
Loading...
Searching...
No Matches
dcm.h File Reference

Go to the source code of this file.

Functions

void dcm_apply (const double dcm[3][3], const double vin[3], double vout[3])
 Apply a DCM to a vector.
 
int dcm_is_orthonormal (const double dcm[3][3], double tol)
 Check whether a DCM is orthonormal within a tolerance.
 
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.
 
int dcm_to_euler_checked (const double dcm[3][3], double *roll, double *pitch, double *yaw)
 Checked DCM-to-Euler conversion.
 
void dcm_to_quaternion (const double dcm[3][3], double q[4])
 Convert a DCM to a quaternion.
 
int dcm_to_quaternion_checked (const double dcm[3][3], double q[4])
 Checked DCM-to-quaternion conversion.
 

Function Documentation

◆ dcm_apply()

void dcm_apply ( const double  dcm[3][3],
const double  vin[3],
double  vout[3] 
)

Apply a DCM to a vector.

Computes \( v_{\text{out}} = \mathbf{C} \, v_{\text{in}} \).

Parameters
dcmRotation matrix.
vinInput vector.
voutOutput rotated vector.

Definition at line 125 of file dcm.c.

125 {
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}

◆ dcm_is_orthonormal()

int dcm_is_orthonormal ( const double  dcm[3][3],
double  tol 
)

Check whether a DCM is orthonormal within a tolerance.

Parameters
dcmInput rotation matrix.
tolAcceptable deviation from orthonormality.
Returns
1 if the matrix is valid, 0 otherwise.

Definition at line 5 of file dcm.c.

5 {
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}

◆ dcm_to_euler()

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.

Output angles are in radians using the aerospace yaw→pitch→roll convention.

Parameters
dcmInput \(3\times3\) rotation matrix (row-major).
rollOutput rotation about the x-axis (rad).
pitchOutput rotation about the y-axis (rad).
yawOutput rotation about the z-axis (rad).

Definition at line 65 of file dcm.c.

65 {
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}
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

◆ dcm_to_euler_checked()

int dcm_to_euler_checked ( const double  dcm[3][3],
double *  roll,
double *  pitch,
double *  yaw 
)

Checked DCM-to-Euler conversion.

Gimbal-lock inputs use the canonical solution roll = 0 while preserving the represented orientation.

Returns
1 on success, 0 for null pointers or a non-orthonormal matrix.

Definition at line 44 of file dcm.c.

47 {
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}
int dcm_is_orthonormal(const double dcm[3][3], double tol)
Check whether a DCM is orthonormal within a tolerance.
Definition dcm.c:5

◆ dcm_to_quaternion()

void dcm_to_quaternion ( const double  dcm[3][3],
double  q[4] 
)

Convert a DCM to a quaternion.

Parameters
dcmInput rotation matrix.
qOutput quaternion in \([w, x, y, z]\) order.

Definition at line 117 of file dcm.c.

117 {
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}
int dcm_to_quaternion_checked(const double dcm[3][3], double q[4])
Checked DCM-to-quaternion conversion.
Definition dcm.c:73

◆ dcm_to_quaternion_checked()

int dcm_to_quaternion_checked ( const double  dcm[3][3],
double  q[4] 
)

Checked DCM-to-quaternion conversion.

Returns
1 on success, 0 for null pointers or a non-orthonormal matrix.

Definition at line 73 of file dcm.c.

73 {
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}