JSBSim Flight Dynamics Model  1.0 (02 March 2017)
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 IDENT(IdSrc,"$Id: FGQuaternion.cpp,v 1.24 2014/01/13 10:46:03 ehofman Exp $");
62 IDENT(IdHdr,ID_QUATERNION);
63 
64 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 
66 // Initialize from q
67 FGQuaternion::FGQuaternion(const FGQuaternion& q) : mCacheValid(q.mCacheValid)
68 {
69  data[0] = q(1);
70  data[1] = q(2);
71  data[2] = q(3);
72  data[3] = q(4);
73  if (mCacheValid) {
74  mT = q.mT;
75  mTInv = q.mTInv;
76  mEulerAngles = q.mEulerAngles;
77  mEulerSines = q.mEulerSines;
78  mEulerCosines = q.mEulerCosines;
79  }
80 }
81 
82 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 
84 // Initialize with the three euler angles
85 FGQuaternion::FGQuaternion(double phi, double tht, double psi): mCacheValid(false)
86 {
87  InitializeFromEulerAngles(phi, tht, psi);
88 }
89 
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 
92 FGQuaternion::FGQuaternion(FGColumnVector3 vOrient): mCacheValid(false)
93 {
94  double phi = vOrient(ePhi);
95  double tht = vOrient(eTht);
96  double psi = vOrient(ePsi);
97 
98  InitializeFromEulerAngles(phi, tht, psi);
99 }
100 
101 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 //
103 // This function computes the quaternion that describes the relationship of the
104 // body frame with respect to another frame, such as the ECI or ECEF frames. The
105 // Euler angles used represent a 3-2-1 (psi, theta, phi) rotation sequence from
106 // the reference frame to the body frame. See "Quaternions and Rotation
107 // Sequences", Jack B. Kuipers, sections 9.2 and 7.6.
108 
109 void FGQuaternion::InitializeFromEulerAngles(double phi, double tht, double psi)
110 {
111  mEulerAngles(ePhi) = phi;
112  mEulerAngles(eTht) = tht;
113  mEulerAngles(ePsi) = psi;
114 
115  double thtd2 = 0.5*tht;
116  double psid2 = 0.5*psi;
117  double phid2 = 0.5*phi;
118 
119  double Sthtd2 = sin(thtd2);
120  double Spsid2 = sin(psid2);
121  double Sphid2 = sin(phid2);
122 
123  double Cthtd2 = cos(thtd2);
124  double Cpsid2 = cos(psid2);
125  double Cphid2 = cos(phid2);
126 
127  double Cphid2Cthtd2 = Cphid2*Cthtd2;
128  double Cphid2Sthtd2 = Cphid2*Sthtd2;
129  double Sphid2Sthtd2 = Sphid2*Sthtd2;
130  double Sphid2Cthtd2 = Sphid2*Cthtd2;
131 
132  data[0] = Cphid2Cthtd2*Cpsid2 + Sphid2Sthtd2*Spsid2;
133  data[1] = Sphid2Cthtd2*Cpsid2 - Cphid2Sthtd2*Spsid2;
134  data[2] = Cphid2Sthtd2*Cpsid2 + Sphid2Cthtd2*Spsid2;
135  data[3] = Cphid2Cthtd2*Spsid2 - Sphid2Sthtd2*Cpsid2;
136 
137  Normalize();
138 }
139 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 // Initialize with a direction cosine (rotation) matrix
141 
142 FGQuaternion::FGQuaternion(const FGMatrix33& m) : mCacheValid(false)
143 {
144  data[0] = 0.50*sqrt(1.0 + m(1,1) + m(2,2) + m(3,3));
145  double t = 0.25/data[0];
146  data[1] = t*(m(2,3) - m(3,2));
147  data[2] = t*(m(3,1) - m(1,3));
148  data[3] = t*(m(1,2) - m(2,1));
149 
150  Normalize();
151 }
152 
153 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 
162 {
163  return FGQuaternion(
164  -0.5*( data[1]*PQR(eP) + data[2]*PQR(eQ) + data[3]*PQR(eR)),
165  0.5*( data[0]*PQR(eP) - data[3]*PQR(eQ) + data[2]*PQR(eR)),
166  0.5*( data[3]*PQR(eP) + data[0]*PQR(eQ) - data[1]*PQR(eR)),
167  0.5*(-data[2]*PQR(eP) + data[1]*PQR(eQ) + data[0]*PQR(eR))
168  );
169 }
170 
171 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172 
174 {
175  // Note: this does not touch the cache since it does not change the orientation
176  double norm = Magnitude();
177  if (norm == 0.0 || fabs(norm - 1.000) < 1e-10) return;
178 
179  double rnorm = 1.0/norm;
180 
181  data[0] *= rnorm;
182  data[1] *= rnorm;
183  data[2] *= rnorm;
184  data[3] *= rnorm;
185 }
186 
187 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188 
189 // Compute the derived values if required ...
190 void FGQuaternion::ComputeDerivedUnconditional(void) const
191 {
192  mCacheValid = true;
193 
194  double q0 = data[0]; // use some aliases/shorthand for the quat elements.
195  double q1 = data[1];
196  double q2 = data[2];
197  double q3 = data[3];
198 
199  // Now compute the transformation matrix.
200  double q0q0 = q0*q0;
201  double q1q1 = q1*q1;
202  double q2q2 = q2*q2;
203  double q3q3 = q3*q3;
204  double q0q1 = q0*q1;
205  double q0q2 = q0*q2;
206  double q0q3 = q0*q3;
207  double q1q2 = q1*q2;
208  double q1q3 = q1*q3;
209  double q2q3 = q2*q3;
210 
211  mT(1,1) = q0q0 + q1q1 - q2q2 - q3q3; // This is found from Eqn. 1.3-32 in
212  mT(1,2) = 2.0*(q1q2 + q0q3); // Stevens and Lewis
213  mT(1,3) = 2.0*(q1q3 - q0q2);
214  mT(2,1) = 2.0*(q1q2 - q0q3);
215  mT(2,2) = q0q0 - q1q1 + q2q2 - q3q3;
216  mT(2,3) = 2.0*(q2q3 + q0q1);
217  mT(3,1) = 2.0*(q1q3 + q0q2);
218  mT(3,2) = 2.0*(q2q3 - q0q1);
219  mT(3,3) = q0q0 - q1q1 - q2q2 + q3q3;
220 
221  // Since this is an orthogonal matrix, the inverse is simply the transpose.
222 
223  mTInv = mT;
224  mTInv.T();
225 
226  // Compute the Euler-angles
227 
228  mEulerAngles = mT.GetEuler();
229 
230  // FIXME: may be one can compute those values easier ???
231  mEulerSines(ePhi) = sin(mEulerAngles(ePhi));
232  // mEulerSines(eTht) = sin(mEulerAngles(eTht));
233  mEulerSines(eTht) = -mT(1,3);
234  mEulerSines(ePsi) = sin(mEulerAngles(ePsi));
235  mEulerCosines(ePhi) = cos(mEulerAngles(ePhi));
236  mEulerCosines(eTht) = cos(mEulerAngles(eTht));
237  mEulerCosines(ePsi) = cos(mEulerAngles(ePsi));
238 }
239 
240 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241 
242 std::string FGQuaternion::Dump(const std::string& delimiter) const
243 {
244  std::ostringstream buffer;
245  buffer << std::setprecision(16) << data[0] << delimiter;
246  buffer << std::setprecision(16) << data[1] << delimiter;
247  buffer << std::setprecision(16) << data[2] << delimiter;
248  buffer << std::setprecision(16) << data[3];
249  return buffer.str();
250 }
251 
252 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 
254 std::ostream& operator<<(std::ostream& os, const FGQuaternion& q)
255 {
256  os << q(1) << " , " << q(2) << " , " << q(3) << " , " << q(4);
257  return os;
258 }
259 
260 } // namespace JSBSim
Models the Quaternion representation of rotations.
Definition: FGQuaternion.h:92
FGQuaternion GetQDot(const FGColumnVector3 &PQR) const
Quaternion derivative for given angular rates.
FGColumnVector3 GetEuler() const
Returns the Euler angle column vector associated with this matrix.
Definition: FGMatrix33.cpp:162
double Magnitude(void) const
Length of the vector.
Definition: FGQuaternion.h:466
FGQuaternion()
Default initializer.
Definition: FGQuaternion.h:96
This class implements a 3 element column vector.
void T(void)
Transposes this matrix.
Definition: FGMatrix33.cpp:470
Handles matrix math operations.
Definition: FGMatrix33.h:92
void Normalize(void)
Normalize.