JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGQuaternion.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGQuaternion.cpp
4  Author: Jon Berndt, Mathias Froehlich
5  Date started: 12/02/98
6 
7  ------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
8  ------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
9 
10  This program is free software; you can redistribute it and/or modify it under
11  the terms of the GNU Lesser General Public License as published by the Free Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  version.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  details.
19 
20  You should have received a copy of the GNU Lesser General Public License along with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26 
27 HISTORY
28 -------------------------------------------------------------------------------
29 12/02/98 JSB Created
30 15/01/04 Mathias Froehlich implemented a quaternion class from many places
31  in JSBSim.
32 
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 SENTRY
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
36 
37 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38  INCLUDES
39  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40 
41 #include <string>
42 #include <iostream>
43 #include <sstream>
44 #include <iomanip>
45 #include <cmath>
46 using std::cerr;
47 using std::cout;
48 using std::endl;
49 
50 #include "FGMatrix33.h"
51 #include "FGColumnVector3.h"
52 
53 #include "FGQuaternion.h"
54 
55 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56  DEFINITIONS
57  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58 
59 namespace JSBSim {
60 
61 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 
63 // Initialize from q
64 FGQuaternion::FGQuaternion(const FGQuaternion& q) : mCacheValid(q.mCacheValid)
65 {
66  data[0] = q(1);
67  data[1] = q(2);
68  data[2] = q(3);
69  data[3] = q(4);
70  if (mCacheValid) {
71  mT = q.mT;
72  mTInv = q.mTInv;
73  mEulerAngles = q.mEulerAngles;
74  mEulerSines = q.mEulerSines;
75  mEulerCosines = q.mEulerCosines;
76  }
77 }
78 
79 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80 
81 // Initialize with the three euler angles
82 FGQuaternion::FGQuaternion(double phi, double tht, double psi): mCacheValid(false)
83 {
84  InitializeFromEulerAngles(phi, tht, psi);
85 }
86 
87 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 
89 FGQuaternion::FGQuaternion(FGColumnVector3 vOrient): mCacheValid(false)
90 {
91  double phi = vOrient(ePhi);
92  double tht = vOrient(eTht);
93  double psi = vOrient(ePsi);
94 
95  InitializeFromEulerAngles(phi, tht, psi);
96 }
97 
98 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 //
100 // This function computes the quaternion that describes the relationship of the
101 // body frame with respect to another frame, such as the ECI or ECEF frames. The
102 // Euler angles used represent a 3-2-1 (psi, theta, phi) rotation sequence from
103 // the reference frame to the body frame. See "Quaternions and Rotation
104 // Sequences", Jack B. Kuipers, sections 9.2 and 7.6.
105 
106 void FGQuaternion::InitializeFromEulerAngles(double phi, double tht, double psi)
107 {
108  mEulerAngles(ePhi) = phi;
109  mEulerAngles(eTht) = tht;
110  mEulerAngles(ePsi) = psi;
111 
112  double thtd2 = 0.5*tht;
113  double psid2 = 0.5*psi;
114  double phid2 = 0.5*phi;
115 
116  double Sthtd2 = sin(thtd2);
117  double Spsid2 = sin(psid2);
118  double Sphid2 = sin(phid2);
119 
120  double Cthtd2 = cos(thtd2);
121  double Cpsid2 = cos(psid2);
122  double Cphid2 = cos(phid2);
123 
124  double Cphid2Cthtd2 = Cphid2*Cthtd2;
125  double Cphid2Sthtd2 = Cphid2*Sthtd2;
126  double Sphid2Sthtd2 = Sphid2*Sthtd2;
127  double Sphid2Cthtd2 = Sphid2*Cthtd2;
128 
129  data[0] = Cphid2Cthtd2*Cpsid2 + Sphid2Sthtd2*Spsid2;
130  data[1] = Sphid2Cthtd2*Cpsid2 - Cphid2Sthtd2*Spsid2;
131  data[2] = Cphid2Sthtd2*Cpsid2 + Sphid2Cthtd2*Spsid2;
132  data[3] = Cphid2Cthtd2*Spsid2 - Sphid2Sthtd2*Cpsid2;
133 
134  Normalize();
135 }
136 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137 // Initialize with a direction cosine (rotation) matrix
138 
139 FGQuaternion::FGQuaternion(const FGMatrix33& m) : mCacheValid(false)
140 {
141  data[0] = 0.50*sqrt(1.0 + m(1,1) + m(2,2) + m(3,3));
142  double t = 0.25/data[0];
143  data[1] = t*(m(2,3) - m(3,2));
144  data[2] = t*(m(3,1) - m(1,3));
145  data[3] = t*(m(1,2) - m(2,1));
146 
147  Normalize();
148 }
149 
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 
159 {
160  return FGQuaternion(
161  -0.5*( data[1]*PQR(eP) + data[2]*PQR(eQ) + data[3]*PQR(eR)),
162  0.5*( data[0]*PQR(eP) - data[3]*PQR(eQ) + data[2]*PQR(eR)),
163  0.5*( data[3]*PQR(eP) + data[0]*PQR(eQ) - data[1]*PQR(eR)),
164  0.5*(-data[2]*PQR(eP) + data[1]*PQR(eQ) + data[0]*PQR(eR))
165  );
166 }
167 
168 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169 
171 {
172  // Note: this does not touch the cache since it does not change the orientation
173  double norm = Magnitude();
174  if (norm == 0.0 || fabs(norm - 1.000) < 1e-10) return;
175 
176  double rnorm = 1.0/norm;
177 
178  data[0] *= rnorm;
179  data[1] *= rnorm;
180  data[2] *= rnorm;
181  data[3] *= rnorm;
182 }
183 
184 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185 
186 // Compute the derived values if required ...
187 void FGQuaternion::ComputeDerivedUnconditional(void) const
188 {
189  mCacheValid = true;
190 
191  double q0 = data[0]; // use some aliases/shorthand for the quat elements.
192  double q1 = data[1];
193  double q2 = data[2];
194  double q3 = data[3];
195 
196  // Now compute the transformation matrix.
197  double q0q0 = q0*q0;
198  double q1q1 = q1*q1;
199  double q2q2 = q2*q2;
200  double q3q3 = q3*q3;
201  double q0q1 = q0*q1;
202  double q0q2 = q0*q2;
203  double q0q3 = q0*q3;
204  double q1q2 = q1*q2;
205  double q1q3 = q1*q3;
206  double q2q3 = q2*q3;
207 
208  mT(1,1) = q0q0 + q1q1 - q2q2 - q3q3; // This is found from Eqn. 1.3-32 in
209  mT(1,2) = 2.0*(q1q2 + q0q3); // Stevens and Lewis
210  mT(1,3) = 2.0*(q1q3 - q0q2);
211  mT(2,1) = 2.0*(q1q2 - q0q3);
212  mT(2,2) = q0q0 - q1q1 + q2q2 - q3q3;
213  mT(2,3) = 2.0*(q2q3 + q0q1);
214  mT(3,1) = 2.0*(q1q3 + q0q2);
215  mT(3,2) = 2.0*(q2q3 - q0q1);
216  mT(3,3) = q0q0 - q1q1 - q2q2 + q3q3;
217 
218  // Since this is an orthogonal matrix, the inverse is simply the transpose.
219 
220  mTInv = mT;
221  mTInv.T();
222 
223  // Compute the Euler-angles
224 
225  mEulerAngles = mT.GetEuler();
226 
227  // FIXME: may be one can compute those values easier ???
228  mEulerSines(ePhi) = sin(mEulerAngles(ePhi));
229  // mEulerSines(eTht) = sin(mEulerAngles(eTht));
230  mEulerSines(eTht) = -mT(1,3);
231  mEulerSines(ePsi) = sin(mEulerAngles(ePsi));
232  mEulerCosines(ePhi) = cos(mEulerAngles(ePhi));
233  mEulerCosines(eTht) = cos(mEulerAngles(eTht));
234  mEulerCosines(ePsi) = cos(mEulerAngles(ePsi));
235 }
236 
237 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 
239 std::string FGQuaternion::Dump(const std::string& delimiter) const
240 {
241  std::ostringstream buffer;
242  buffer << std::setprecision(16) << data[0] << delimiter;
243  buffer << std::setprecision(16) << data[1] << delimiter;
244  buffer << std::setprecision(16) << data[2] << delimiter;
245  buffer << std::setprecision(16) << data[3];
246  return buffer.str();
247 }
248 
249 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 
251 std::ostream& operator<<(std::ostream& os, const FGQuaternion& q)
252 {
253  os << q(1) << " , " << q(2) << " , " << q(3) << " , " << q(4);
254  return os;
255 }
256 
257 } // namespace JSBSim
JSBSim::FGMatrix33::T
void T(void)
Transposes this matrix.
Definition: FGMatrix33.cpp:462
JSBSim::FGColumnVector3
This class implements a 3 element column vector.
Definition: FGColumnVector3.h:63
JSBSim::FGQuaternion::Normalize
void Normalize(void)
Normalize.
Definition: FGQuaternion.cpp:170
JSBSim::FGMatrix33
Handles matrix math operations.
Definition: FGMatrix33.h:69
JSBSim::FGQuaternion::Magnitude
double Magnitude(void) const
Length of the vector.
Definition: FGQuaternion.h:460
JSBSim::FGMatrix33::GetEuler
FGColumnVector3 GetEuler() const
Returns the Euler angle column vector associated with this matrix.
Definition: FGMatrix33.cpp:159
JSBSim::FGQuaternion::FGQuaternion
FGQuaternion()
Default initializer.
Definition: FGQuaternion.h:90
JSBSim::FGQuaternion::GetQDot
FGQuaternion GetQDot(const FGColumnVector3 &PQR) const
Quaternion derivative for given angular rates.
Definition: FGQuaternion.cpp:158
JSBSim::FGQuaternion
Models the Quaternion representation of rotations.
Definition: FGQuaternion.h:86