JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++

FGInitialCondition.cpp

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 }