![]() |
JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++
|
00001 /******************************************************************************* 00002 00003 Header: FGInitialCondition.cpp 00004 Author: Tony Peden, Bertrand Coconnier 00005 Date started: 7/1/99 00006 00007 ------------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ------------- 00008 00009 This program is free software; you can redistribute it and/or modify it under 00010 the terms of the GNU Lesser General Public License as published by the Free Software 00011 Foundation; either version 2 of the License, or (at your option) any later 00012 version. 00013 00014 This program is distributed in the hope that it will be useful, but WITHOUT 00015 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00016 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 00017 details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA. 00022 00023 Further information about the GNU Lesser General Public License can also be found on 00024 the world wide web at http://www.gnu.org. 00025 00026 00027 HISTORY 00028 -------------------------------------------------------------------------------- 00029 7/1/99 TP Created 00030 11/25/10 BC Complete revision - Use minimal set of variables to prevent 00031 inconsistent states. Wind is correctly handled. 00032 00033 00034 FUNCTIONAL DESCRIPTION 00035 -------------------------------------------------------------------------------- 00036 00037 The purpose of this class is to take a set of initial conditions and provide 00038 a kinematically consistent set of body axis velocity components, euler 00039 angles, and altitude. This class does not attempt to trim the model i.e. 00040 the sim will most likely start in a very dynamic state (unless, of course, 00041 you have chosen your IC's wisely) even after setting it up with this class. 00042 00043 ******************************************************************************** 00044 INCLUDES 00045 *******************************************************************************/ 00046 00047 #include <iostream> 00048 #include <fstream> 00049 #include <cstdlib> 00050 00051 #include "FGInitialCondition.h" 00052 #include "FGFDMExec.h" 00053 #include "math/FGQuaternion.h" 00054 #include "models/FGInertial.h" 00055 #include "models/FGAtmosphere.h" 00056 #include "models/FGPropagate.h" 00057 #include "models/FGPropulsion.h" 00058 #include "models/FGFCS.h" 00059 #include "input_output/FGPropertyManager.h" 00060 #include "input_output/string_utilities.h" 00061 00062 using namespace std; 00063 00064 namespace JSBSim { 00065 00066 static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.87 2013/01/19 14:19:43 bcoconni Exp $"; 00067 static const char *IdHdr = ID_INITIALCONDITION; 00068 00069 //****************************************************************************** 00070 00071 FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec) : fdmex(FDMExec) 00072 { 00073 InitializeIC(); 00074 00075 if(FDMExec != NULL ) { 00076 PropertyManager=fdmex->GetPropertyManager(); 00077 Atmosphere=fdmex->GetAtmosphere(); 00078 bind(); 00079 } else { 00080 cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl; 00081 } 00082 00083 Debug(0); 00084 } 00085 00086 //****************************************************************************** 00087 00088 FGInitialCondition::~FGInitialCondition() 00089 { 00090 Debug(1); 00091 } 00092 00093 //****************************************************************************** 00094 00095 void FGInitialCondition::ResetIC(double u0, double v0, double w0, 00096 double p0, double q0, double r0, 00097 double alpha0, double beta0, 00098 double phi0, double theta0, double psi0, 00099 double latRad0, double lonRad0, double altAGLFt0, 00100 double gamma0) 00101 { 00102 double calpha = cos(alpha0), cbeta = cos(beta0); 00103 double salpha = sin(alpha0), sbeta = sin(beta0); 00104 00105 InitializeIC(); 00106 00107 vPQR_body = FGColumnVector3(p0, q0, r0); 00108 alpha = alpha0; beta = beta0; 00109 00110 position.SetLongitude(lonRad0); 00111 position.SetLatitude(latRad0); 00112 position.SetAltitudeAGL(altAGLFt0, fdmex->GetSimTime()); 00113 00114 orientation = FGQuaternion(phi0, theta0, psi0); 00115 const FGMatrix33& Tb2l = orientation.GetTInv(); 00116 00117 vUVW_NED = Tb2l * FGColumnVector3(u0, v0, w0); 00118 vt = vUVW_NED.Magnitude(); 00119 lastSpeedSet = setuvw; 00120 00121 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha, 00122 sbeta, cbeta, 0.0, 00123 salpha*cbeta, -salpha*sbeta, calpha); 00124 Tb2w = Tw2b.Transposed(); 00125 00126 SetFlightPathAngleRadIC(gamma0); 00127 } 00128 00129 //****************************************************************************** 00130 00131 void FGInitialCondition::InitializeIC(void) 00132 { 00133 alpha=beta=0; 00134 00135 position.SetEllipse(fdmex->GetInertial()->GetSemimajor(), fdmex->GetInertial()->GetSemiminor()); 00136 00137 position.SetPositionGeodetic(0.0, 0.0, 0.0); 00138 position.SetEarthPositionAngle(fdmex->GetPropagate()->GetEarthPositionAngle()); 00139 00140 orientation = FGQuaternion(0.0, 0.0, 0.0); 00141 vUVW_NED.InitMatrix(); 00142 vPQR_body.InitMatrix(); 00143 vt=0; 00144 00145 targetNlfIC = 1.0; 00146 00147 Tw2b.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.); 00148 Tb2w.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.); 00149 00150 lastSpeedSet = setvt; 00151 lastAltitudeSet = setasl; 00152 enginesRunning.clear(); 00153 } 00154 00155 //****************************************************************************** 00156 00157 void FGInitialCondition::SetVequivalentKtsIC(double ve) 00158 { 00159 double altitudeASL = position.GetAltitudeASL(); 00160 double rho = Atmosphere->GetDensity(altitudeASL); 00161 double rhoSL = Atmosphere->GetDensitySL(); 00162 SetVtrueFpsIC(ve*ktstofps*sqrt(rhoSL/rho)); 00163 lastSpeedSet = setve; 00164 } 00165 00166 //****************************************************************************** 00167 00168 void FGInitialCondition::SetMachIC(double mach) 00169 { 00170 double altitudeASL = position.GetAltitudeASL(); 00171 double temperature = Atmosphere->GetTemperature(altitudeASL); 00172 double soundSpeed = sqrt(SHRatio*Reng*temperature); 00173 SetVtrueFpsIC(mach*soundSpeed); 00174 lastSpeedSet = setmach; 00175 } 00176 00177 //****************************************************************************** 00178 00179 void FGInitialCondition::SetVcalibratedKtsIC(double vcas) 00180 { 00181 double altitudeASL = position.GetAltitudeASL(); 00182 double pressure = Atmosphere->GetPressure(altitudeASL); 00183 double pressureSL = Atmosphere->GetPressureSL(); 00184 double rhoSL = Atmosphere->GetDensitySL(); 00185 double mach = MachFromVcalibrated(fabs(vcas)*ktstofps, pressure, pressureSL, rhoSL); 00186 double temperature = Atmosphere->GetTemperature(altitudeASL); 00187 double soundSpeed = sqrt(SHRatio*Reng*temperature); 00188 00189 SetVtrueFpsIC(mach*soundSpeed); 00190 lastSpeedSet = setvc; 00191 } 00192 00193 //****************************************************************************** 00194 // Updates alpha and beta according to the aircraft true airspeed in the local 00195 // NED frame. 00196 00197 void FGInitialCondition::calcAeroAngles(const FGColumnVector3& _vt_NED) 00198 { 00199 const FGMatrix33& Tl2b = orientation.GetT(); 00200 FGColumnVector3 _vt_BODY = Tl2b * _vt_NED; 00201 double ua = _vt_BODY(eX); 00202 double va = _vt_BODY(eY); 00203 double wa = _vt_BODY(eZ); 00204 double uwa = sqrt(ua*ua + wa*wa); 00205 double calpha, cbeta; 00206 double salpha, sbeta; 00207 00208 alpha = beta = 0.0; 00209 calpha = cbeta = 1.0; 00210 salpha = sbeta = 0.0; 00211 00212 if( wa != 0 ) 00213 alpha = atan2( wa, ua ); 00214 00215 // alpha cannot be constrained without updating other informations like the 00216 // true speed or the Euler angles. Otherwise we might end up with an inconsistent 00217 // state of the aircraft. 00218 /*alpha = Constrain(fdmex->GetAerodynamics()->GetAlphaCLMin(), alpha, 00219 fdmex->GetAerodynamics()->GetAlphaCLMax());*/ 00220 00221 if( va != 0 ) 00222 beta = atan2( va, uwa ); 00223 00224 if (uwa != 0) { 00225 calpha = ua / uwa; 00226 salpha = wa / uwa; 00227 } 00228 00229 if (vt != 0) { 00230 cbeta = uwa / vt; 00231 sbeta = va / vt; 00232 } 00233 00234 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha, 00235 sbeta, cbeta, 0.0, 00236 salpha*cbeta, -salpha*sbeta, calpha); 00237 Tb2w = Tw2b.Transposed(); 00238 } 00239 00240 //****************************************************************************** 00241 // Set the ground velocity. Caution it sets the vertical velocity to zero to 00242 // keep backward compatibility. 00243 00244 void FGInitialCondition::SetVgroundFpsIC(double vg) 00245 { 00246 const FGMatrix33& Tb2l = orientation.GetTInv(); 00247 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00248 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00249 00250 vUVW_NED(eU) = vg * orientation.GetCosEuler(ePsi); 00251 vUVW_NED(eV) = vg * orientation.GetSinEuler(ePsi); 00252 vUVW_NED(eW) = 0.; 00253 _vt_NED = vUVW_NED + _vWIND_NED; 00254 vt = _vt_NED.Magnitude(); 00255 00256 calcAeroAngles(_vt_NED); 00257 00258 lastSpeedSet = setvg; 00259 } 00260 00261 //****************************************************************************** 00262 // Sets the true airspeed. The amplitude of the airspeed is modified but its 00263 // direction is kept unchanged. If there is no wind, the same is true for the 00264 // ground velocity. If there is some wind, the airspeed direction is unchanged 00265 // but this may result in the ground velocity direction being altered. This is 00266 // for backward compatibility. 00267 00268 void FGInitialCondition::SetVtrueFpsIC(double vtrue) 00269 { 00270 const FGMatrix33& Tb2l = orientation.GetTInv(); 00271 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00272 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00273 00274 if (vt > 0.1) 00275 _vt_NED *= vtrue / vt; 00276 else 00277 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vtrue, 0., 0.); 00278 00279 vt = vtrue; 00280 vUVW_NED = _vt_NED - _vWIND_NED; 00281 00282 calcAeroAngles(_vt_NED); 00283 00284 lastSpeedSet = setvt; 00285 } 00286 00287 //****************************************************************************** 00288 // When the climb rate is modified, we need to update the angles theta and beta 00289 // to keep the true airspeed amplitude, the AoA and the heading unchanged. 00290 // Beta will be modified if the aircraft roll angle is not null. 00291 00292 void FGInitialCondition::SetClimbRateFpsIC(double hdot) 00293 { 00294 if (fabs(hdot) > vt) { 00295 cerr << "The climb rate cannot be higher than the true speed." << endl; 00296 return; 00297 } 00298 00299 const FGMatrix33& Tb2l = orientation.GetTInv(); 00300 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00301 FGColumnVector3 _WIND_NED = _vt_NED - vUVW_NED; 00302 double hdot0 = -_vt_NED(eW); 00303 00304 if (fabs(hdot0) < vt) { // Is this check really needed ? 00305 double scale = sqrt((vt*vt-hdot*hdot)/(vt*vt-hdot0*hdot0)); 00306 _vt_NED(eU) *= scale; 00307 _vt_NED(eV) *= scale; 00308 } 00309 _vt_NED(eW) = -hdot; 00310 vUVW_NED = _vt_NED - _WIND_NED; 00311 00312 // Updating the angles theta and beta to keep the true airspeed amplitude 00313 calcThetaBeta(alpha, _vt_NED); 00314 } 00315 00316 //****************************************************************************** 00317 // When the AoA is modified, we need to update the angles theta and beta to 00318 // keep the true airspeed amplitude, the climb rate and the heading unchanged. 00319 // Beta will be modified if the aircraft roll angle is not null. 00320 00321 void FGInitialCondition::SetAlphaRadIC(double alfa) 00322 { 00323 const FGMatrix33& Tb2l = orientation.GetTInv(); 00324 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00325 calcThetaBeta(alfa, _vt_NED); 00326 } 00327 00328 //****************************************************************************** 00329 // When the AoA is modified, we need to update the angles theta and beta to 00330 // keep the true airspeed amplitude, the climb rate and the heading unchanged. 00331 // Beta will be modified if the aircraft roll angle is not null. 00332 00333 void FGInitialCondition::calcThetaBeta(double alfa, const FGColumnVector3& _vt_NED) 00334 { 00335 FGColumnVector3 vOrient = orientation.GetEuler(); 00336 double calpha = cos(alfa), salpha = sin(alfa); 00337 double cpsi = orientation.GetCosEuler(ePsi), spsi = orientation.GetSinEuler(ePsi); 00338 double cphi = orientation.GetCosEuler(ePhi), sphi = orientation.GetSinEuler(ePhi); 00339 FGMatrix33 Tpsi( cpsi, spsi, 0., 00340 -spsi, cpsi, 0., 00341 0., 0., 1.); 00342 FGMatrix33 Tphi(1., 0., 0., 00343 0., cphi, sphi, 00344 0.,-sphi, cphi); 00345 FGMatrix33 Talpha( calpha, 0., salpha, 00346 0., 1., 0., 00347 -salpha, 0., calpha); 00348 00349 FGColumnVector3 v0 = Tpsi * _vt_NED; 00350 FGColumnVector3 n = (Talpha * Tphi).Transposed() * FGColumnVector3(0., 0., 1.); 00351 FGColumnVector3 y = FGColumnVector3(0., 1., 0.); 00352 FGColumnVector3 u = y - DotProduct(y, n) * n; 00353 FGColumnVector3 p = y * n; 00354 00355 if (DotProduct(p, v0) < 0) p *= -1.0; 00356 p.Normalize(); 00357 00358 u *= DotProduct(v0, y) / DotProduct(u, y); 00359 00360 // There are situations where the desired alpha angle cannot be obtained. This 00361 // is not a limitation of the algorithm but is due to the mathematical problem 00362 // not having a solution. This can only be cured by limiting the alpha angle 00363 // or by modifying an additional angle (psi ?). Since this is anticipated to 00364 // be a pathological case (mainly when a high roll angle is required) this 00365 // situation is not addressed below. However if there are complaints about the 00366 // following error being raised too often, we might need to reconsider this 00367 // position. 00368 if (DotProduct(v0, v0) < DotProduct(u, u)) { 00369 cerr << "Cannot modify angle 'alpha' from " << alpha << " to " << alfa << endl; 00370 return; 00371 } 00372 00373 FGColumnVector3 v1 = u + sqrt(DotProduct(v0, v0) - DotProduct(u, u))*p; 00374 00375 FGColumnVector3 v0xz(v0(eU), 0., v0(eW)); 00376 FGColumnVector3 v1xz(v1(eU), 0., v1(eW)); 00377 v0xz.Normalize(); 00378 v1xz.Normalize(); 00379 double sinTheta = (v1xz * v0xz)(eY); 00380 vOrient(eTht) = asin(sinTheta); 00381 00382 orientation = FGQuaternion(vOrient); 00383 00384 const FGMatrix33& Tl2b = orientation.GetT(); 00385 FGColumnVector3 v2 = Talpha * Tl2b * _vt_NED; 00386 00387 alpha = alfa; 00388 beta = atan2(v2(eV), v2(eU)); 00389 double cbeta=1.0, sbeta=0.0; 00390 if (vt != 0.0) { 00391 cbeta = v2(eU) / vt; 00392 sbeta = v2(eV) / vt; 00393 } 00394 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha, 00395 sbeta, cbeta, 0.0, 00396 salpha*cbeta, -salpha*sbeta, calpha); 00397 Tb2w = Tw2b.Transposed(); 00398 } 00399 00400 //****************************************************************************** 00401 // When the beta angle is modified, we need to update the angles theta and psi 00402 // to keep the true airspeed (amplitude and direction - including the climb rate) 00403 // and the alpha angle unchanged. This may result in the aircraft heading (psi) 00404 // being altered especially if there is cross wind. 00405 00406 void FGInitialCondition::SetBetaRadIC(double bta) 00407 { 00408 const FGMatrix33& Tb2l = orientation.GetTInv(); 00409 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00410 FGColumnVector3 vOrient = orientation.GetEuler(); 00411 00412 beta = bta; 00413 double calpha = cos(alpha), salpha = sin(alpha); 00414 double cbeta = cos(beta), sbeta = sin(beta); 00415 double cphi = orientation.GetCosEuler(ePhi), sphi = orientation.GetSinEuler(ePhi); 00416 FGMatrix33 TphiInv(1., 0., 0., 00417 0., cphi,-sphi, 00418 0., sphi, cphi); 00419 00420 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha, 00421 sbeta, cbeta, 0.0, 00422 salpha*cbeta, -salpha*sbeta, calpha); 00423 Tb2w = Tw2b.Transposed(); 00424 00425 FGColumnVector3 vf = TphiInv * Tw2b * FGColumnVector3(vt, 0., 0.); 00426 FGColumnVector3 v0xy(_vt_NED(eX), _vt_NED(eY), 0.); 00427 FGColumnVector3 v1xy(sqrt(v0xy(eX)*v0xy(eX)+v0xy(eY)*v0xy(eY)-vf(eY)*vf(eY)),vf(eY),0.); 00428 v0xy.Normalize(); 00429 v1xy.Normalize(); 00430 00431 if (vf(eX) < 0.) v0xy(eX) *= -1.0; 00432 00433 double sinPsi = (v1xy * v0xy)(eZ); 00434 double cosPsi = DotProduct(v0xy, v1xy); 00435 vOrient(ePsi) = atan2(sinPsi, cosPsi); 00436 FGMatrix33 Tpsi( cosPsi, sinPsi, 0., 00437 -sinPsi, cosPsi, 0., 00438 0., 0., 1.); 00439 00440 FGColumnVector3 v2xz = Tpsi * _vt_NED; 00441 FGColumnVector3 vfxz = vf; 00442 v2xz(eV) = vfxz(eV) = 0.0; 00443 v2xz.Normalize(); 00444 vfxz.Normalize(); 00445 double sinTheta = (v2xz * vfxz)(eY); 00446 vOrient(eTht) = -asin(sinTheta); 00447 00448 orientation = FGQuaternion(vOrient); 00449 } 00450 00451 //****************************************************************************** 00452 // Modifies the body frame orientation. 00453 00454 void FGInitialCondition::SetEulerAngleRadIC(int idx, double angle) 00455 { 00456 const FGMatrix33& Tb2l = orientation.GetTInv(); 00457 const FGMatrix33& Tl2b = orientation.GetT(); 00458 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00459 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00460 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED; 00461 FGColumnVector3 vOrient = orientation.GetEuler(); 00462 00463 vOrient(idx) = angle; 00464 orientation = FGQuaternion(vOrient); 00465 00466 if ((lastSpeedSet != setned) && (lastSpeedSet != setvg)) { 00467 const FGMatrix33& newTb2l = orientation.GetTInv(); 00468 vUVW_NED = newTb2l * _vUVW_BODY; 00469 _vt_NED = vUVW_NED + _vWIND_NED; 00470 vt = _vt_NED.Magnitude(); 00471 } 00472 00473 calcAeroAngles(_vt_NED); 00474 } 00475 00476 //****************************************************************************** 00477 // Modifies an aircraft velocity component (eU, eV or eW) in the body frame. The 00478 // true airspeed is modified accordingly. If there is some wind, the airspeed 00479 // direction modification may differ from the body velocity modification. 00480 00481 void FGInitialCondition::SetBodyVelFpsIC(int idx, double vel) 00482 { 00483 const FGMatrix33& Tb2l = orientation.GetTInv(); 00484 const FGMatrix33& Tl2b = orientation.GetT(); 00485 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00486 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED; 00487 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00488 00489 _vUVW_BODY(idx) = vel; 00490 vUVW_NED = Tb2l * _vUVW_BODY; 00491 _vt_NED = vUVW_NED + _vWIND_NED; 00492 vt = _vt_NED.Magnitude(); 00493 00494 calcAeroAngles(_vt_NED); 00495 00496 lastSpeedSet = setuvw; 00497 } 00498 00499 //****************************************************************************** 00500 // Modifies an aircraft velocity component (eX, eY or eZ) in the local NED frame. 00501 // The true airspeed is modified accordingly. If there is some wind, the airspeed 00502 // direction modification may differ from the local velocity modification. 00503 00504 void FGInitialCondition::SetNEDVelFpsIC(int idx, double vel) 00505 { 00506 const FGMatrix33& Tb2l = orientation.GetTInv(); 00507 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00508 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00509 00510 vUVW_NED(idx) = vel; 00511 _vt_NED = vUVW_NED + _vWIND_NED; 00512 vt = _vt_NED.Magnitude(); 00513 00514 calcAeroAngles(_vt_NED); 00515 00516 lastSpeedSet = setned; 00517 } 00518 00519 //****************************************************************************** 00520 // Set wind amplitude and direction in the local NED frame. The aircraft velocity 00521 // with respect to the ground is not changed but the true airspeed is. 00522 00523 void FGInitialCondition::SetWindNEDFpsIC(double wN, double wE, double wD ) 00524 { 00525 FGColumnVector3 _vt_NED = vUVW_NED + FGColumnVector3(wN, wE, wD); 00526 vt = _vt_NED.Magnitude(); 00527 00528 calcAeroAngles(_vt_NED); 00529 } 00530 00531 //****************************************************************************** 00532 // Set the cross wind velocity (in knots). Here, 'cross wind' means perpendicular 00533 // to the aircraft heading and parallel to the ground. The aircraft velocity 00534 // with respect to the ground is not changed but the true airspeed is. 00535 00536 void FGInitialCondition::SetCrossWindKtsIC(double cross) 00537 { 00538 const FGMatrix33& Tb2l = orientation.GetTInv(); 00539 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00540 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00541 FGColumnVector3 _vCROSS(-orientation.GetSinEuler(ePsi), orientation.GetCosEuler(ePsi), 0.); 00542 00543 // Gram-Schmidt process is used to remove the existing cross wind component 00544 _vWIND_NED -= DotProduct(_vWIND_NED, _vCROSS) * _vCROSS; 00545 // Which is now replaced by the new value. The input cross wind is expected 00546 // in knots, so first convert to fps, which is the internal unit used. 00547 _vWIND_NED += (cross * ktstofps) * _vCROSS; 00548 _vt_NED = vUVW_NED + _vWIND_NED; 00549 vt = _vt_NED.Magnitude(); 00550 00551 calcAeroAngles(_vt_NED); 00552 } 00553 00554 //****************************************************************************** 00555 // Set the head wind velocity (in knots). Here, 'head wind' means parallel 00556 // to the aircraft heading and to the ground. The aircraft velocity 00557 // with respect to the ground is not changed but the true airspeed is. 00558 00559 void FGInitialCondition::SetHeadWindKtsIC(double head) 00560 { 00561 const FGMatrix33& Tb2l = orientation.GetTInv(); 00562 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00563 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00564 // This is a head wind, so the direction vector for the wind 00565 // needs to be set opposite to the heading the aircraft 00566 // is taking. So, the cos and sin of the heading (psi) 00567 // are negated in the line below. 00568 FGColumnVector3 _vHEAD(-orientation.GetCosEuler(ePsi), -orientation.GetSinEuler(ePsi), 0.); 00569 00570 // Gram-Schmidt process is used to remove the existing head wind component 00571 _vWIND_NED -= DotProduct(_vWIND_NED, _vHEAD) * _vHEAD; 00572 // Which is now replaced by the new value. The input head wind is expected 00573 // in knots, so first convert to fps, which is the internal unit used. 00574 _vWIND_NED += (head * ktstofps) * _vHEAD; 00575 _vt_NED = vUVW_NED + _vWIND_NED; 00576 00577 vt = _vt_NED.Magnitude(); 00578 00579 calcAeroAngles(_vt_NED); 00580 } 00581 00582 //****************************************************************************** 00583 // Set the vertical wind velocity (in knots). The 'vertical' is taken in the 00584 // local NED frame. The aircraft velocity with respect to the ground is not 00585 // changed but the true airspeed is. 00586 00587 void FGInitialCondition::SetWindDownKtsIC(double wD) 00588 { 00589 const FGMatrix33& Tb2l = orientation.GetTInv(); 00590 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00591 00592 _vt_NED(eW) = vUVW_NED(eW) + wD; 00593 vt = _vt_NED.Magnitude(); 00594 00595 calcAeroAngles(_vt_NED); 00596 } 00597 00598 //****************************************************************************** 00599 // Modifies the wind velocity (in knots) while keeping its direction unchanged. 00600 // The vertical component (in local NED frame) is unmodified. The aircraft 00601 // velocity with respect to the ground is not changed but the true airspeed is. 00602 00603 void FGInitialCondition::SetWindMagKtsIC(double mag) 00604 { 00605 const FGMatrix33& Tb2l = orientation.GetTInv(); 00606 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00607 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00608 FGColumnVector3 _vHEAD(_vWIND_NED(eU), _vWIND_NED(eV), 0.); 00609 double windMag = _vHEAD.Magnitude(); 00610 00611 if (windMag > 0.001) 00612 _vHEAD *= (mag*ktstofps) / windMag; 00613 else 00614 _vHEAD = FGColumnVector3((mag*ktstofps), 0., 0.); 00615 00616 _vWIND_NED(eU) = _vHEAD(eU); 00617 _vWIND_NED(eV) = _vHEAD(eV); 00618 _vt_NED = vUVW_NED + _vWIND_NED; 00619 vt = _vt_NED.Magnitude(); 00620 00621 calcAeroAngles(_vt_NED); 00622 } 00623 00624 //****************************************************************************** 00625 // Modifies the wind direction while keeping its velocity unchanged. The vertical 00626 // component (in local NED frame) is unmodified. The aircraft velocity with 00627 // respect to the ground is not changed but the true airspeed is. 00628 00629 void FGInitialCondition::SetWindDirDegIC(double dir) 00630 { 00631 const FGMatrix33& Tb2l = orientation.GetTInv(); 00632 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00633 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00634 double mag = _vWIND_NED.Magnitude(eU, eV); 00635 FGColumnVector3 _vHEAD(mag*cos(dir*degtorad), mag*sin(dir*degtorad), 0.); 00636 00637 _vWIND_NED(eU) = _vHEAD(eU); 00638 _vWIND_NED(eV) = _vHEAD(eV); 00639 _vt_NED = vUVW_NED + _vWIND_NED; 00640 vt = _vt_NED.Magnitude(); 00641 00642 calcAeroAngles(_vt_NED); 00643 } 00644 00645 //****************************************************************************** 00646 00647 void FGInitialCondition::SetSeaLevelRadiusFtIC(double slr) 00648 { 00649 fdmex->GetGroundCallback()->SetSeaLevelRadius(slr); 00650 } 00651 00652 //****************************************************************************** 00653 00654 void FGInitialCondition::SetTerrainElevationFtIC(double elev) 00655 { 00656 double agl = GetAltitudeAGLFtIC(); 00657 00658 fdmex->GetGroundCallback()->SetTerrainGeoCentRadius(elev + position.GetSeaLevelRadius()); 00659 00660 if (lastAltitudeSet == setagl) 00661 SetAltitudeAGLFtIC(agl); 00662 } 00663 00664 //****************************************************************************** 00665 00666 double FGInitialCondition::GetAltitudeAGLFtIC(void) const 00667 { 00668 return position.GetAltitudeAGL(fdmex->GetSimTime()); 00669 } 00670 00671 //****************************************************************************** 00672 00673 double FGInitialCondition::GetTerrainElevationFtIC(void) const 00674 { 00675 return position.GetTerrainRadius(fdmex->GetSimTime()) 00676 - position.GetSeaLevelRadius(); 00677 } 00678 00679 //****************************************************************************** 00680 00681 void FGInitialCondition::SetAltitudeAGLFtIC(double agl) 00682 { 00683 double terrainElevation = position.GetTerrainRadius(fdmex->GetSimTime()) - position.GetSeaLevelRadius(); 00684 SetAltitudeASLFtIC(agl + terrainElevation); 00685 lastAltitudeSet = setagl; 00686 } 00687 00688 //****************************************************************************** 00689 // Set the altitude SL. If the airspeed has been previously set with parameters 00690 // that are atmosphere dependent (Mach, VCAS, VEAS) then the true airspeed is 00691 // modified to keep the last set speed to its previous value. 00692 00693 void FGInitialCondition::SetAltitudeASLFtIC(double alt) 00694 { 00695 double altitudeASL = position.GetAltitudeASL(); 00696 double temperature = Atmosphere->GetTemperature(altitudeASL); 00697 double pressure = Atmosphere->GetPressure(altitudeASL); 00698 double pressureSL = Atmosphere->GetPressureSL(); 00699 double soundSpeed = sqrt(SHRatio*Reng*temperature); 00700 double rho = Atmosphere->GetDensity(altitudeASL); 00701 double rhoSL = Atmosphere->GetDensitySL(); 00702 00703 double mach0 = vt / soundSpeed; 00704 double vc0 = VcalibratedFromMach(mach0, pressure, pressureSL, rhoSL); 00705 double ve0 = vt * sqrt(rho/rhoSL); 00706 00707 altitudeASL=alt; 00708 position.SetAltitudeASL(alt); 00709 00710 temperature = Atmosphere->GetTemperature(altitudeASL); 00711 soundSpeed = sqrt(SHRatio*Reng*temperature); 00712 rho = Atmosphere->GetDensity(altitudeASL); 00713 pressure = Atmosphere->GetPressure(altitudeASL); 00714 00715 switch(lastSpeedSet) { 00716 case setvc: 00717 mach0 = MachFromVcalibrated(vc0, pressure, pressureSL, rhoSL); 00718 SetVtrueFpsIC(mach0 * soundSpeed); 00719 break; 00720 case setmach: 00721 SetVtrueFpsIC(mach0 * soundSpeed); 00722 break; 00723 case setve: 00724 SetVtrueFpsIC(ve0 * sqrt(rhoSL/rho)); 00725 break; 00726 default: // Make the compiler stop complaining about missing enums 00727 break; 00728 } 00729 00730 lastAltitudeSet = setasl; 00731 } 00732 00733 //****************************************************************************** 00734 00735 void FGInitialCondition::SetLatitudeRadIC(double lat) 00736 { 00737 double altitude; 00738 00739 switch(lastAltitudeSet) { 00740 case setagl: 00741 altitude = GetAltitudeAGLFtIC(); 00742 position.SetLatitude(lat); 00743 SetAltitudeAGLFtIC(altitude); 00744 break; 00745 default: 00746 altitude = position.GetAltitudeASL(); 00747 position.SetLatitude(lat); 00748 position.SetAltitudeASL(altitude); 00749 } 00750 } 00751 00752 //****************************************************************************** 00753 00754 void FGInitialCondition::SetLongitudeRadIC(double lon) 00755 { 00756 double altitude; 00757 00758 switch(lastAltitudeSet) { 00759 case setagl: 00760 altitude = GetAltitudeAGLFtIC(); 00761 position.SetLongitude(lon); 00762 SetAltitudeAGLFtIC(altitude); 00763 break; 00764 default: 00765 altitude = position.GetAltitudeASL(); 00766 position.SetLongitude(lon); 00767 position.SetAltitudeASL(altitude); 00768 break; 00769 } 00770 } 00771 00772 //****************************************************************************** 00773 00774 double FGInitialCondition::GetWindDirDegIC(void) const 00775 { 00776 const FGMatrix33& Tb2l = orientation.GetTInv(); 00777 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00778 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00779 00780 return _vWIND_NED(eV) == 0.0 ? 0.0 00781 : atan2(_vWIND_NED(eV), _vWIND_NED(eU))*radtodeg; 00782 } 00783 00784 //****************************************************************************** 00785 00786 double FGInitialCondition::GetNEDWindFpsIC(int idx) const 00787 { 00788 const FGMatrix33& Tb2l = orientation.GetTInv(); 00789 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00790 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00791 00792 return _vWIND_NED(idx); 00793 } 00794 00795 //****************************************************************************** 00796 00797 double FGInitialCondition::GetWindFpsIC(void) const 00798 { 00799 const FGMatrix33& Tb2l = orientation.GetTInv(); 00800 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.); 00801 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED; 00802 00803 return _vWIND_NED.Magnitude(eU, eV); 00804 } 00805 00806 //****************************************************************************** 00807 00808 double FGInitialCondition::GetBodyWindFpsIC(int idx) const 00809 { 00810 const FGMatrix33& Tl2b = orientation.GetT(); 00811 FGColumnVector3 _vt_BODY = Tw2b * FGColumnVector3(vt, 0., 0.); 00812 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED; 00813 FGColumnVector3 _vWIND_BODY = _vt_BODY - _vUVW_BODY; 00814 00815 return _vWIND_BODY(idx); 00816 } 00817 00818 //****************************************************************************** 00819 00820 double FGInitialCondition::GetVcalibratedKtsIC(void) const 00821 { 00822 double altitudeASL = position.GetAltitudeASL(); 00823 double temperature = Atmosphere->GetTemperature(altitudeASL); 00824 double pressure = Atmosphere->GetPressure(altitudeASL); 00825 double pressureSL = Atmosphere->GetPressureSL(); 00826 double rhoSL = Atmosphere->GetDensitySL(); 00827 double soundSpeed = sqrt(SHRatio*Reng*temperature); 00828 double mach = vt / soundSpeed; 00829 return fpstokts * VcalibratedFromMach(mach, pressure, pressureSL, rhoSL); 00830 } 00831 00832 //****************************************************************************** 00833 00834 double FGInitialCondition::GetVequivalentKtsIC(void) const 00835 { 00836 double altitudeASL = position.GetAltitudeASL(); 00837 double rho = Atmosphere->GetDensity(altitudeASL); 00838 double rhoSL = Atmosphere->GetDensitySL(); 00839 return fpstokts * vt * sqrt(rho/rhoSL); 00840 } 00841 00842 //****************************************************************************** 00843 00844 double FGInitialCondition::GetMachIC(void) const 00845 { 00846 double altitudeASL = position.GetAltitudeASL(); 00847 double temperature = Atmosphere->GetTemperature(altitudeASL); 00848 double soundSpeed = sqrt(SHRatio*Reng*temperature); 00849 return vt / soundSpeed; 00850 } 00851 00852 //****************************************************************************** 00853 00854 double FGInitialCondition::GetBodyVelFpsIC(int idx) const 00855 { 00856 const FGMatrix33& Tl2b = orientation.GetT(); 00857 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED; 00858 00859 return _vUVW_BODY(idx); 00860 } 00861 00862 //****************************************************************************** 00863 00864 bool FGInitialCondition::Load(string rstfile, bool useStoredPath) 00865 { 00866 string init_file_name; 00867 if( useStoredPath ) { 00868 init_file_name = fdmex->GetFullAircraftPath() + "/" + rstfile + ".xml"; 00869 } else { 00870 init_file_name = rstfile; 00871 } 00872 00873 document = LoadXMLDocument(init_file_name); 00874 00875 // Make sure that the document is valid 00876 if (!document) { 00877 cerr << "File: " << init_file_name << " could not be read." << endl; 00878 exit(-1); 00879 } 00880 00881 if (document->GetName() != string("initialize")) { 00882 cerr << "File: " << init_file_name << " is not a reset file." << endl; 00883 exit(-1); 00884 } 00885 00886 double version = document->GetAttributeValueAsNumber("version"); 00887 bool result = false; 00888 00889 if (version == HUGE_VAL) { 00890 result = Load_v1(); // Default to the old version 00891 } else if (version >= 3.0) { 00892 cerr << "Only initialization file formats 1 and 2 are currently supported" << endl; 00893 exit (-1); 00894 } else if (version >= 2.0) { 00895 result = Load_v2(); 00896 } else if (version >= 1.0) { 00897 result = Load_v1(); 00898 } 00899 00900 // Check to see if any engines are specified to be initialized in a running state 00901 Element* running_elements = document->FindElement("running"); 00902 while (running_elements) { 00903 enginesRunning.push_back(int(running_elements->GetDataAsNumber())); 00904 running_elements = document->FindNextElement("running"); 00905 } 00906 00907 return result; 00908 } 00909 00910 //****************************************************************************** 00911 00912 bool FGInitialCondition::Load_v1(void) 00913 { 00914 bool result = true; 00915 00916 if (document->FindElement("latitude")) 00917 SetLatitudeRadIC(document->FindElementValueAsNumberConvertTo("latitude", "RAD")); 00918 if (document->FindElement("longitude")) 00919 SetLongitudeRadIC(document->FindElementValueAsNumberConvertTo("longitude", "RAD")); 00920 if (document->FindElement("elevation")) 00921 SetTerrainElevationFtIC(document->FindElementValueAsNumberConvertTo("elevation", "FT")); 00922 00923 if (document->FindElement("altitude")) // This is feet above ground level 00924 SetAltitudeAGLFtIC(document->FindElementValueAsNumberConvertTo("altitude", "FT")); 00925 else if (document->FindElement("altitudeAGL")) // This is feet above ground level 00926 SetAltitudeAGLFtIC(document->FindElementValueAsNumberConvertTo("altitudeAGL", "FT")); 00927 else if (document->FindElement("altitudeMSL")) // This is feet above sea level 00928 SetAltitudeASLFtIC(document->FindElementValueAsNumberConvertTo("altitudeMSL", "FT")); 00929 00930 FGColumnVector3 vOrient = orientation.GetEuler(); 00931 00932 if (document->FindElement("phi")) 00933 vOrient(ePhi) = document->FindElementValueAsNumberConvertTo("phi", "RAD"); 00934 if (document->FindElement("theta")) 00935 vOrient(eTht) = document->FindElementValueAsNumberConvertTo("theta", "RAD"); 00936 if (document->FindElement("psi")) 00937 vOrient(ePsi) = document->FindElementValueAsNumberConvertTo("psi", "RAD"); 00938 00939 orientation = FGQuaternion(vOrient); 00940 00941 if (document->FindElement("ubody")) 00942 SetUBodyFpsIC(document->FindElementValueAsNumberConvertTo("ubody", "FT/SEC")); 00943 if (document->FindElement("vbody")) 00944 SetVBodyFpsIC(document->FindElementValueAsNumberConvertTo("vbody", "FT/SEC")); 00945 if (document->FindElement("wbody")) 00946 SetWBodyFpsIC(document->FindElementValueAsNumberConvertTo("wbody", "FT/SEC")); 00947 if (document->FindElement("vnorth")) 00948 SetVNorthFpsIC(document->FindElementValueAsNumberConvertTo("vnorth", "FT/SEC")); 00949 if (document->FindElement("veast")) 00950 SetVEastFpsIC(document->FindElementValueAsNumberConvertTo("veast", "FT/SEC")); 00951 if (document->FindElement("vdown")) 00952 SetVDownFpsIC(document->FindElementValueAsNumberConvertTo("vdown", "FT/SEC")); 00953 if (document->FindElement("vc")) 00954 SetVcalibratedKtsIC(document->FindElementValueAsNumberConvertTo("vc", "KTS")); 00955 if (document->FindElement("vt")) 00956 SetVtrueKtsIC(document->FindElementValueAsNumberConvertTo("vt", "KTS")); 00957 if (document->FindElement("mach")) 00958 SetMachIC(document->FindElementValueAsNumber("mach")); 00959 if (document->FindElement("gamma")) 00960 SetFlightPathAngleDegIC(document->FindElementValueAsNumberConvertTo("gamma", "DEG")); 00961 if (document->FindElement("roc")) 00962 SetClimbRateFpsIC(document->FindElementValueAsNumberConvertTo("roc", "FT/SEC")); 00963 if (document->FindElement("vground")) 00964 SetVgroundKtsIC(document->FindElementValueAsNumberConvertTo("vground", "KTS")); 00965 if (document->FindElement("alpha")) 00966 SetAlphaDegIC(document->FindElementValueAsNumberConvertTo("alpha", "DEG")); 00967 if (document->FindElement("beta")) 00968 SetBetaDegIC(document->FindElementValueAsNumberConvertTo("beta", "DEG")); 00969 if (document->FindElement("vwind")) 00970 SetWindMagKtsIC(document->FindElementValueAsNumberConvertTo("vwind", "KTS")); 00971 if (document->FindElement("winddir")) 00972 SetWindDirDegIC(document->FindElementValueAsNumberConvertTo("winddir", "DEG")); 00973 if (document->FindElement("hwind")) 00974 SetHeadWindKtsIC(document->FindElementValueAsNumberConvertTo("hwind", "KTS")); 00975 if (document->FindElement("xwind")) 00976 SetCrossWindKtsIC(document->FindElementValueAsNumberConvertTo("xwind", "KTS")); 00977 if (document->FindElement("targetNlf")) 00978 { 00979 SetTargetNlfIC(document->FindElementValueAsNumber("targetNlf")); 00980 } 00981 00982 // Refer to Stevens and Lewis, 1.5-14a, pg. 49. 00983 // This is the rotation rate of the "Local" frame, expressed in the local frame. 00984 const FGMatrix33& Tl2b = orientation.GetT(); 00985 double radInv = 1.0 / position.GetRadius(); 00986 FGColumnVector3 vOmegaLocal = FGColumnVector3( 00987 radInv*vUVW_NED(eEast), 00988 -radInv*vUVW_NED(eNorth), 00989 -radInv*vUVW_NED(eEast)*position.GetTanLatitude() ); 00990 00991 vPQR_body = Tl2b * vOmegaLocal; 00992 00993 return result; 00994 } 00995 00996 //****************************************************************************** 00997 00998 bool FGInitialCondition::Load_v2(void) 00999 { 01000 FGColumnVector3 vOrient; 01001 bool result = true; 01002 FGColumnVector3 vOmegaEarth = fdmex->GetInertial()->GetOmegaPlanet(); 01003 01004 if (document->FindElement("earth_position_angle")) 01005 position.SetEarthPositionAngle(document->FindElementValueAsNumberConvertTo("earth_position_angle", "RAD")); 01006 01007 // Initialize vehicle position 01008 // 01009 // Allowable frames: 01010 // - ECI (Earth Centered Inertial) 01011 // - ECEF (Earth Centered, Earth Fixed) 01012 01013 Element* position_el = document->FindElement("position"); 01014 if (position_el) { 01015 string frame = position_el->GetAttributeValue("frame"); 01016 frame = to_lower(frame); 01017 if (frame == "eci") { // Need to transform vLoc to ECEF for storage and use in FGLocation. 01018 position = position.GetTi2ec() * position_el->FindElementTripletConvertTo("FT"); 01019 } else if (frame == "ecef") { 01020 if (!position_el->FindElement("x") && !position_el->FindElement("y") && !position_el->FindElement("z")) { 01021 01022 if (position_el->FindElement("longitude")) 01023 position.SetLongitude(position_el->FindElementValueAsNumberConvertTo("longitude", "RAD")); 01024 01025 if (position_el->FindElement("radius")) { 01026 position.SetRadius(position_el->FindElementValueAsNumberConvertTo("radius", "FT")); 01027 } else if (position_el->FindElement("altitudeAGL")) { 01028 position.SetAltitudeAGL(position_el->FindElementValueAsNumberConvertTo("altitudeAGL", "FT"), 01029 fdmex->GetSimTime()); 01030 } else if (position_el->FindElement("altitudeMSL")) { 01031 position.SetAltitudeASL(position_el->FindElementValueAsNumberConvertTo("altitudeMSL", "FT")); 01032 } else { 01033 cerr << endl << " No altitude or radius initial condition is given." << endl; 01034 result = false; 01035 } 01036 01037 Element* latitude_el = position_el->FindElement("latitude"); 01038 if (latitude_el) { 01039 string lat_type = latitude_el->GetAttributeValue("type"); 01040 double latitude = position_el->FindElementValueAsNumberConvertTo("latitude", "RAD"); 01041 if (lat_type == "geod" || lat_type == "geodetic") { 01042 double longitude = position.GetLongitude(); 01043 double altitude = position.GetAltitudeASL(); // SetPositionGeodetic() assumes altitude 01044 position.SetPositionGeodetic(longitude, latitude, altitude); // is geodetic, but it's close enough for now. 01045 position.SetAltitudeAGL(altitude, fdmex->GetSimTime()); 01046 } else { 01047 position.SetLatitude(latitude); 01048 } 01049 } 01050 } else { 01051 position = position_el->FindElementTripletConvertTo("FT"); 01052 } 01053 } else { 01054 cerr << endl << " Neither ECI nor ECEF frame is specified for initial position." << endl; 01055 result = false; 01056 } 01057 } else { 01058 cerr << endl << " Initial position not specified in this initialization file." << endl; 01059 result = false; 01060 } 01061 01062 if (document->FindElement("elevation")) 01063 fdmex->GetGroundCallback()->SetTerrainGeoCentRadius(document->FindElementValueAsNumberConvertTo("elevation", "FT")+position.GetSeaLevelRadius()); 01064 01065 // End of position initialization 01066 01067 // Initialize vehicle orientation 01068 // Allowable frames 01069 // - ECI (Earth Centered Inertial) 01070 // - ECEF (Earth Centered, Earth Fixed) 01071 // - Local 01072 // 01073 // Need to convert the provided orientation to a local orientation, using 01074 // the given orientation and knowledge of the Earth position angle. 01075 // This could be done using matrices (where in the subscript "b/a", 01076 // it is meant "b with respect to a", and where b=body frame, 01077 // i=inertial frame, l=local NED frame and e=ecef frame) as: 01078 // 01079 // C_b/l = C_b/e * C_e/l 01080 // 01081 // Using quaternions (note reverse ordering compared to matrix representation): 01082 // 01083 // Q_b/l = Q_e/l * Q_b/e 01084 // 01085 // Use the specific matrices as needed. The above example of course is for the whole 01086 // body to local orientation. 01087 // The new orientation angles can be extracted from the matrix or the quaternion. 01088 // ToDo: Do we need to deal with normalization of the quaternions here? 01089 01090 Element* orientation_el = document->FindElement("orientation"); 01091 if (orientation_el) { 01092 string frame = orientation_el->GetAttributeValue("frame"); 01093 frame = to_lower(frame); 01094 vOrient = orientation_el->FindElementTripletConvertTo("RAD"); 01095 if (frame == "eci") { 01096 01097 // In this case, we are supplying the Euler angles for the vehicle with 01098 // respect to the inertial system, represented by the C_b/i Matrix. 01099 // We want the body orientation with respect to the local (NED frame): 01100 // 01101 // C_b/l = C_b/i * C_i/l 01102 // 01103 // Or, using quaternions (note reverse ordering compared to matrix representation): 01104 // 01105 // Q_b/l = Q_i/l * Q_b/i 01106 01107 FGQuaternion QuatI2Body = FGQuaternion(vOrient); 01108 QuatI2Body.Normalize(); 01109 FGQuaternion QuatLocal2I = position.GetTl2i(); 01110 QuatLocal2I.Normalize(); 01111 orientation = QuatLocal2I * QuatI2Body; 01112 01113 } else if (frame == "ecef") { 01114 01115 // In this case we are given the Euler angles representing the orientation of 01116 // the body with respect to the ECEF system, represented by the C_b/e Matrix. 01117 // We want the body orientation with respect to the local (NED frame): 01118 // 01119 // C_b/l = C_b/e * C_e/l 01120 // 01121 // Using quaternions (note reverse ordering compared to matrix representation): 01122 // 01123 // Q_b/l = Q_e/l * Q_b/e 01124 01125 FGQuaternion QuatEC2Body(vOrient); // Store relationship of Body frame wrt ECEF frame, Q_b/e 01126 QuatEC2Body.Normalize(); 01127 FGQuaternion QuatLocal2EC = position.GetTl2ec(); // Get Q_e/l from matrix 01128 QuatLocal2EC.Normalize(); 01129 orientation = QuatLocal2EC * QuatEC2Body; // Q_b/l = Q_e/l * Q_b/e 01130 01131 } else if (frame == "local") { 01132 01133 orientation = FGQuaternion(vOrient); 01134 01135 } else { 01136 01137 cerr << endl << fgred << " Orientation frame type: \"" << frame 01138 << "\" is not supported!" << reset << endl << endl; 01139 result = false; 01140 01141 } 01142 } 01143 01144 // Initialize vehicle velocity 01145 // Allowable frames 01146 // - ECI (Earth Centered Inertial) 01147 // - ECEF (Earth Centered, Earth Fixed) 01148 // - Local 01149 // - Body 01150 // The vehicle will be defaulted to (0,0,0) in the Body frame if nothing is provided. 01151 01152 Element* velocity_el = document->FindElement("velocity"); 01153 FGColumnVector3 vInitVelocity = FGColumnVector3(0.0, 0.0, 0.0); 01154 FGMatrix33 mTec2l = position.GetTec2l(); 01155 const FGMatrix33& Tb2l = orientation.GetTInv(); 01156 01157 if (velocity_el) { 01158 01159 string frame = velocity_el->GetAttributeValue("frame"); 01160 frame = to_lower(frame); 01161 FGColumnVector3 vInitVelocity = velocity_el->FindElementTripletConvertTo("FT/SEC"); 01162 01163 if (frame == "eci") { 01164 FGColumnVector3 omega_cross_r = vOmegaEarth * (position.GetTec2i() * position); 01165 vUVW_NED = mTec2l * (vInitVelocity - omega_cross_r); 01166 lastSpeedSet = setned; 01167 } else if (frame == "ecef") { 01168 vUVW_NED = mTec2l * vInitVelocity; 01169 lastSpeedSet = setned; 01170 } else if (frame == "local") { 01171 vUVW_NED = vInitVelocity; 01172 lastSpeedSet = setned; 01173 } else if (frame == "body") { 01174 vUVW_NED = Tb2l * vInitVelocity; 01175 lastSpeedSet = setuvw; 01176 } else { 01177 01178 cerr << endl << fgred << " Velocity frame type: \"" << frame 01179 << "\" is not supported!" << reset << endl << endl; 01180 result = false; 01181 01182 } 01183 01184 } else { 01185 01186 vUVW_NED = Tb2l * vInitVelocity; 01187 01188 } 01189 01190 vt = vUVW_NED.Magnitude(); 01191 01192 calcAeroAngles(vUVW_NED); 01193 01194 // Initialize vehicle body rates 01195 // Allowable frames 01196 // - ECI (Earth Centered Inertial) 01197 // - ECEF (Earth Centered, Earth Fixed) 01198 // - Body 01199 01200 Element* attrate_el = document->FindElement("attitude_rate"); 01201 const FGMatrix33& Tl2b = orientation.GetT(); 01202 01203 // Refer to Stevens and Lewis, 1.5-14a, pg. 49. 01204 // This is the rotation rate of the "Local" frame, expressed in the local frame. 01205 double radInv = 1.0 / position.GetRadius(); 01206 FGColumnVector3 vOmegaLocal = FGColumnVector3( 01207 radInv*vUVW_NED(eEast), 01208 -radInv*vUVW_NED(eNorth), 01209 -radInv*vUVW_NED(eEast)*position.GetTanLatitude() ); 01210 01211 if (attrate_el) { 01212 01213 string frame = attrate_el->GetAttributeValue("frame"); 01214 frame = to_lower(frame); 01215 FGColumnVector3 vAttRate = attrate_el->FindElementTripletConvertTo("RAD/SEC"); 01216 01217 if (frame == "eci") { 01218 vPQR_body = Tl2b * position.GetTi2l() * (vAttRate - vOmegaEarth); 01219 } else if (frame == "ecef") { 01220 vPQR_body = Tl2b * position.GetTec2l() * vAttRate; 01221 } else if (frame == "local") { 01222 vPQR_body = Tl2b * (vAttRate + vOmegaLocal); 01223 } else if (frame == "body") { 01224 vPQR_body = vAttRate; 01225 } else if (!frame.empty()) { // misspelling of frame 01226 01227 cerr << endl << fgred << " Attitude rate frame type: \"" << frame 01228 << "\" is not supported!" << reset << endl << endl; 01229 result = false; 01230 01231 } else if (frame.empty()) { 01232 vPQR_body = Tl2b * vOmegaLocal; 01233 } 01234 01235 } else { // Body frame attitude rate assumed 0 relative to local. 01236 vPQR_body = Tl2b * vOmegaLocal; 01237 } 01238 01239 return result; 01240 } 01241 01242 //****************************************************************************** 01243 01244 void FGInitialCondition::bind(void) 01245 { 01246 PropertyManager->Tie("ic/vc-kts", this, 01247 &FGInitialCondition::GetVcalibratedKtsIC, 01248 &FGInitialCondition::SetVcalibratedKtsIC, 01249 true); 01250 PropertyManager->Tie("ic/ve-kts", this, 01251 &FGInitialCondition::GetVequivalentKtsIC, 01252 &FGInitialCondition::SetVequivalentKtsIC, 01253 true); 01254 PropertyManager->Tie("ic/vg-kts", this, 01255 &FGInitialCondition::GetVgroundKtsIC, 01256 &FGInitialCondition::SetVgroundKtsIC, 01257 true); 01258 PropertyManager->Tie("ic/vt-kts", this, 01259 &FGInitialCondition::GetVtrueKtsIC, 01260 &FGInitialCondition::SetVtrueKtsIC, 01261 true); 01262 PropertyManager->Tie("ic/mach", this, 01263 &FGInitialCondition::GetMachIC, 01264 &FGInitialCondition::SetMachIC, 01265 true); 01266 PropertyManager->Tie("ic/roc-fpm", this, 01267 &FGInitialCondition::GetClimbRateFpmIC, 01268 &FGInitialCondition::SetClimbRateFpmIC, 01269 true); 01270 PropertyManager->Tie("ic/gamma-deg", this, 01271 &FGInitialCondition::GetFlightPathAngleDegIC, 01272 &FGInitialCondition::SetFlightPathAngleDegIC, 01273 true); 01274 PropertyManager->Tie("ic/alpha-deg", this, 01275 &FGInitialCondition::GetAlphaDegIC, 01276 &FGInitialCondition::SetAlphaDegIC, 01277 true); 01278 PropertyManager->Tie("ic/beta-deg", this, 01279 &FGInitialCondition::GetBetaDegIC, 01280 &FGInitialCondition::SetBetaDegIC, 01281 true); 01282 PropertyManager->Tie("ic/theta-deg", this, 01283 &FGInitialCondition::GetThetaDegIC, 01284 &FGInitialCondition::SetThetaDegIC, 01285 true); 01286 PropertyManager->Tie("ic/phi-deg", this, 01287 &FGInitialCondition::GetPhiDegIC, 01288 &FGInitialCondition::SetPhiDegIC, 01289 true); 01290 PropertyManager->Tie("ic/psi-true-deg", this, 01291 &FGInitialCondition::GetPsiDegIC, 01292 &FGInitialCondition::SetPsiDegIC, 01293 true); 01294 PropertyManager->Tie("ic/lat-gc-deg", this, 01295 &FGInitialCondition::GetLatitudeDegIC, 01296 &FGInitialCondition::SetLatitudeDegIC, 01297 true); 01298 PropertyManager->Tie("ic/long-gc-deg", this, 01299 &FGInitialCondition::GetLongitudeDegIC, 01300 &FGInitialCondition::SetLongitudeDegIC, 01301 true); 01302 PropertyManager->Tie("ic/h-sl-ft", this, 01303 &FGInitialCondition::GetAltitudeASLFtIC, 01304 &FGInitialCondition::SetAltitudeASLFtIC, 01305 true); 01306 PropertyManager->Tie("ic/h-agl-ft", this, 01307 &FGInitialCondition::GetAltitudeAGLFtIC, 01308 &FGInitialCondition::SetAltitudeAGLFtIC, 01309 true); 01310 PropertyManager->Tie("ic/terrain-elevation-ft", this, 01311 &FGInitialCondition::GetTerrainElevationFtIC, 01312 &FGInitialCondition::SetTerrainElevationFtIC, 01313 true); 01314 PropertyManager->Tie("ic/vg-fps", this, 01315 &FGInitialCondition::GetVgroundFpsIC, 01316 &FGInitialCondition::SetVgroundFpsIC, 01317 true); 01318 PropertyManager->Tie("ic/vt-fps", this, 01319 &FGInitialCondition::GetVtrueFpsIC, 01320 &FGInitialCondition::SetVtrueFpsIC, 01321 true); 01322 PropertyManager->Tie("ic/vw-bx-fps", this, 01323 &FGInitialCondition::GetWindUFpsIC); 01324 PropertyManager->Tie("ic/vw-by-fps", this, 01325 &FGInitialCondition::GetWindVFpsIC); 01326 PropertyManager->Tie("ic/vw-bz-fps", this, 01327 &FGInitialCondition::GetWindWFpsIC); 01328 PropertyManager->Tie("ic/vw-north-fps", this, 01329 &FGInitialCondition::GetWindNFpsIC); 01330 PropertyManager->Tie("ic/vw-east-fps", this, 01331 &FGInitialCondition::GetWindEFpsIC); 01332 PropertyManager->Tie("ic/vw-down-fps", this, 01333 &FGInitialCondition::GetWindDFpsIC); 01334 PropertyManager->Tie("ic/vw-mag-fps", this, 01335 &FGInitialCondition::GetWindFpsIC); 01336 PropertyManager->Tie("ic/vw-dir-deg", this, 01337 &FGInitialCondition::GetWindDirDegIC, 01338 &FGInitialCondition::SetWindDirDegIC, 01339 true); 01340 01341 PropertyManager->Tie("ic/roc-fps", this, 01342 &FGInitialCondition::GetClimbRateFpsIC, 01343 &FGInitialCondition::SetClimbRateFpsIC, 01344 true); 01345 PropertyManager->Tie("ic/u-fps", this, 01346 &FGInitialCondition::GetUBodyFpsIC, 01347 &FGInitialCondition::SetUBodyFpsIC, 01348 true); 01349 PropertyManager->Tie("ic/v-fps", this, 01350 &FGInitialCondition::GetVBodyFpsIC, 01351 &FGInitialCondition::SetVBodyFpsIC, 01352 true); 01353 PropertyManager->Tie("ic/w-fps", this, 01354 &FGInitialCondition::GetWBodyFpsIC, 01355 &FGInitialCondition::SetWBodyFpsIC, 01356 true); 01357 PropertyManager->Tie("ic/vn-fps", this, 01358 &FGInitialCondition::GetVNorthFpsIC, 01359 &FGInitialCondition::SetVNorthFpsIC, 01360 true); 01361 PropertyManager->Tie("ic/ve-fps", this, 01362 &FGInitialCondition::GetVEastFpsIC, 01363 &FGInitialCondition::SetVEastFpsIC, 01364 true); 01365 PropertyManager->Tie("ic/vd-fps", this, 01366 &FGInitialCondition::GetVDownFpsIC, 01367 &FGInitialCondition::SetVDownFpsIC, 01368 true); 01369 PropertyManager->Tie("ic/gamma-rad", this, 01370 &FGInitialCondition::GetFlightPathAngleRadIC, 01371 &FGInitialCondition::SetFlightPathAngleRadIC, 01372 true); 01373 PropertyManager->Tie("ic/alpha-rad", this, 01374 &FGInitialCondition::GetAlphaRadIC, 01375 &FGInitialCondition::SetAlphaRadIC, 01376 true); 01377 PropertyManager->Tie("ic/theta-rad", this, 01378 &FGInitialCondition::GetThetaRadIC, 01379 &FGInitialCondition::SetThetaRadIC, 01380 true); 01381 PropertyManager->Tie("ic/beta-rad", this, 01382 &FGInitialCondition::GetBetaRadIC, 01383 &FGInitialCondition::SetBetaRadIC, 01384 true); 01385 PropertyManager->Tie("ic/phi-rad", this, 01386 &FGInitialCondition::GetPhiRadIC, 01387 &FGInitialCondition::SetPhiRadIC, 01388 true); 01389 PropertyManager->Tie("ic/psi-true-rad", this, 01390 &FGInitialCondition::GetPsiRadIC, 01391 &FGInitialCondition::SetPsiRadIC, 01392 true); 01393 PropertyManager->Tie("ic/lat-gc-rad", this, 01394 &FGInitialCondition::GetLatitudeRadIC, 01395 &FGInitialCondition::SetLatitudeRadIC, 01396 true); 01397 PropertyManager->Tie("ic/long-gc-rad", this, 01398 &FGInitialCondition::GetLongitudeRadIC, 01399 &FGInitialCondition::SetLongitudeRadIC, 01400 true); 01401 PropertyManager->Tie("ic/p-rad_sec", this, 01402 &FGInitialCondition::GetPRadpsIC, 01403 &FGInitialCondition::SetPRadpsIC, 01404 true); 01405 PropertyManager->Tie("ic/q-rad_sec", this, 01406 &FGInitialCondition::GetQRadpsIC, 01407 &FGInitialCondition::SetQRadpsIC, 01408 true); 01409 PropertyManager->Tie("ic/r-rad_sec", this, 01410 &FGInitialCondition::GetRRadpsIC, 01411 &FGInitialCondition::SetRRadpsIC, 01412 true); 01413 } 01414 01415 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01416 // The bitmasked value choices are as follows: 01417 // unset: In this case (the default) JSBSim would only print 01418 // out the normally expected messages, essentially echoing 01419 // the config files as they are read. If the environment 01420 // variable is not set, debug_lvl is set to 1 internally 01421 // 0: This requests JSBSim not to output any messages 01422 // whatsoever. 01423 // 1: This value explicity requests the normal JSBSim 01424 // startup messages 01425 // 2: This value asks for a message to be printed out when 01426 // a class is instantiated 01427 // 4: When this value is set, a message is displayed when a 01428 // FGModel object executes its Run() method 01429 // 8: When this value is set, various runtime state variables 01430 // are printed out periodically 01431 // 16: When set various parameters are sanity checked and 01432 // a message is printed out when they go out of bounds 01433 01434 void FGInitialCondition::Debug(int from) 01435 { 01436 if (debug_lvl <= 0) return; 01437 01438 if (debug_lvl & 1) { // Standard console startup message output 01439 } 01440 if (debug_lvl & 2 ) { // Instantiation/Destruction notification 01441 if (from == 0) cout << "Instantiated: FGInitialCondition" << endl; 01442 if (from == 1) cout << "Destroyed: FGInitialCondition" << endl; 01443 } 01444 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects 01445 } 01446 if (debug_lvl & 8 ) { // Runtime state variables 01447 } 01448 if (debug_lvl & 16) { // Sanity checking 01449 } 01450 if (debug_lvl & 64) { 01451 if (from == 0) { // Constructor 01452 cout << IdSrc << endl; 01453 cout << IdHdr << endl; 01454 } 01455 } 01456 } 01457 }