1 /*******************************************************************************
3 Header: FGInitialCondition.cpp
4 Author: Tony Peden, Bertrand Coconnier
7 ------------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) -------------
9 This program is free software; you can redistribute it and/or modify it under
10 the terms of the GNU Lesser General Public License as published by the Free Software
11 Foundation; either version 2 of the License, or (at your option) any later
14 This program is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19 You should have received a copy of the GNU Lesser General Public License along with
20 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21 Place - Suite 330, Boston, MA 02111-1307, USA.
23 Further information about the GNU Lesser General Public License can also be found on
24 the world wide web at http://www.gnu.org.
28 --------------------------------------------------------------------------------
30 11/25/10 BC Complete revision - Use minimal set of variables to prevent
31 inconsistent states. Wind is correctly handled.
34 FUNCTIONAL DESCRIPTION
35 --------------------------------------------------------------------------------
37 The purpose of this class is to take a set of initial conditions and provide
38 a kinematically consistent set of body axis velocity components, euler
39 angles, and altitude. This class does not attempt to trim the model i.e.
40 the sim will most likely start in a very dynamic state (unless, of course,
41 you have chosen your IC's wisely) even after setting it up with this class.
43 ********************************************************************************
45 *******************************************************************************/
47 #include "FGInitialCondition.h"
48 #include "FGFDMExec.h"
49 #include "math/FGQuaternion.h"
50 #include "models/FGInertial.h"
51 #include "models/FGAtmosphere.h"
52 #include "models/FGPropagate.h"
53 #include "models/FGPropulsion.h"
54 #include "input_output/FGPropertyManager.h"
55 #include "input_output/string_utilities.h"
64 static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.56 2011/01/23 12:13:44 bcoconni Exp $";
65 static const char *IdHdr = ID_INITIALCONDITION;
67 //******************************************************************************
69 FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec) : fdmex(FDMExec)
73 if(FDMExec != NULL ) {
74 PropertyManager=fdmex->GetPropertyManager();
79 cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl;
85 //******************************************************************************
87 FGInitialCondition::~FGInitialCondition()
92 //******************************************************************************
94 void FGInitialCondition::ResetIC(double u0, double v0, double w0,
95 double p0, double q0, double r0,
96 double alpha0, double beta0,
97 double phi0, double theta0, double psi0,
98 double latRad0, double lonRad0, double altAGLFt0,
101 double calpha = cos(alpha0), cbeta = cos(beta0);
102 double salpha = sin(alpha0), sbeta = sin(beta0);
106 p = p0; q = q0; r = r0;
107 alpha = alpha0; beta = beta0;
108 phi = phi0; theta = theta0; psi = psi0;
110 position.SetPosition(lonRad0, latRad0, altAGLFt0 + terrain_elevation + sea_level_radius);
112 FGQuaternion Quat(phi, theta, psi);
115 Tb2l = Quat.GetTInv();
117 vUVW_NED = Tb2l * FGColumnVector3(u0, v0, w0);
118 vt = vUVW_NED.Magnitude();
120 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
122 salpha*cbeta, -salpha*sbeta, calpha);
123 Tb2w = Tw2b.Transposed();
125 SetFlightPathAngleRadIC(gamma0);
128 //******************************************************************************
130 void FGInitialCondition::InitializeIC(void)
134 terrain_elevation = 0;
135 sea_level_radius = fdmex->GetInertial()->GetRefRadius();
136 position.SetPosition(0., 0., sea_level_radius);
137 position.SetEarthPositionAngle(fdmex->GetInertial()->GetEarthPositionAngle());
138 vUVW_NED.InitMatrix();
144 Tw2b.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
145 Tb2w.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
146 Tl2b.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
147 Tb2l.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
150 //******************************************************************************
152 void FGInitialCondition::WriteStateFile(int num)
154 if (Constructing) return;
156 string filename = fdmex->GetFullAircraftPath();
158 if (filename.empty())
159 filename = "initfile.xml";
161 filename.append("/initfile.xml");
163 ofstream outfile(filename.c_str());
164 FGPropagate* Propagate = fdmex->GetPropagate();
166 if (outfile.is_open()) {
167 outfile << "<?xml version=\"1.0\"?>" << endl;
168 outfile << "<initialize name=\"reset00\">" << endl;
169 outfile << " <ubody unit=\"FT/SEC\"> " << Propagate->GetUVW(eX) << " </ubody> " << endl;
170 outfile << " <vbody unit=\"FT/SEC\"> " << Propagate->GetUVW(eY) << " </vbody> " << endl;
171 outfile << " <wbody unit=\"FT/SEC\"> " << Propagate->GetUVW(eZ) << " </wbody> " << endl;
172 outfile << " <phi unit=\"DEG\"> " << Propagate->GetEuler(ePhi) << " </phi>" << endl;
173 outfile << " <theta unit=\"DEG\"> " << Propagate->GetEuler(eTht) << " </theta>" << endl;
174 outfile << " <psi unit=\"DEG\"> " << Propagate->GetEuler(ePsi) << " </psi>" << endl;
175 outfile << " <longitude unit=\"DEG\"> " << Propagate->GetLongitudeDeg() << " </longitude>" << endl;
176 outfile << " <latitude unit=\"DEG\"> " << Propagate->GetLatitudeDeg() << " </latitude>" << endl;
177 outfile << " <altitude unit=\"FT\"> " << Propagate->GetDistanceAGL() << " </altitude>" << endl;
178 outfile << "</initialize>" << endl;
181 cerr << "Could not open and/or write the state to the initial conditions file: " << filename << endl;
185 //******************************************************************************
187 void FGInitialCondition::SetVequivalentKtsIC(double ve)
189 double altitudeASL = position.GetRadius() - sea_level_radius;
190 double rho = fdmex->GetAtmosphere()->GetDensity(altitudeASL);
191 double rhoSL = fdmex->GetAtmosphere()->GetDensitySL();
192 SetVtrueFpsIC(ve*ktstofps/sqrt(rho/rhoSL));
193 lastSpeedSet = setve;
196 //******************************************************************************
198 void FGInitialCondition::SetMachIC(double mach)
200 double altitudeASL = position.GetRadius() - sea_level_radius;
201 double temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
202 double soundSpeed = sqrt(SHRatio*Reng*temperature);
203 SetVtrueFpsIC(mach*soundSpeed);
204 lastSpeedSet = setmach;
207 //******************************************************************************
209 void FGInitialCondition::SetVcalibratedKtsIC(double vcas)
211 double altitudeASL = position.GetRadius() - sea_level_radius;
212 double mach = getMachFromVcas(fabs(vcas)*ktstofps);
213 double temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
214 double soundSpeed = sqrt(SHRatio*Reng*temperature);
216 SetVtrueFpsIC(mach*soundSpeed);
217 lastSpeedSet = setvc;
220 //******************************************************************************
221 // Updates alpha and beta according to the aircraft true airspeed in the local
224 void FGInitialCondition::calcAeroAngles(const FGColumnVector3& _vt_NED)
226 FGColumnVector3 _vt_BODY = Tl2b * _vt_NED;
227 double ua = _vt_BODY(eX);
228 double va = _vt_BODY(eY);
229 double wa = _vt_BODY(eZ);
230 double uwa = sqrt(ua*ua + wa*wa);
231 double calpha, cbeta;
232 double salpha, sbeta;
235 calpha = cbeta = 1.0;
236 salpha = sbeta = 0.0;
239 alpha = atan2( wa, ua );
241 // alpha cannot be constrained without updating other informations like the
242 // true speed or the Euler angles. Otherwise we might end up with an inconsistent
243 // state of the aircraft.
244 /*alpha = Constrain(fdmex->GetAerodynamics()->GetAlphaCLMin(), alpha,
245 fdmex->GetAerodynamics()->GetAlphaCLMax());*/
248 beta = atan2( va, uwa );
260 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
262 salpha*cbeta, -salpha*sbeta, calpha);
263 Tb2w = Tw2b.Transposed();
266 //******************************************************************************
267 // Set the ground velocity. Caution it sets the vertical velocity to zero to
268 // keep backward compatibility.
270 void FGInitialCondition::SetVgroundFpsIC(double vg)
272 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
273 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
275 vUVW_NED(eU) = vg*cos(psi);
276 vUVW_NED(eV) = vg*sin(psi);
278 _vt_NED = vUVW_NED + _vWIND_NED;
279 vt = _vt_NED.Magnitude();
281 calcAeroAngles(_vt_NED);
283 lastSpeedSet = setvg;
286 //******************************************************************************
287 // Sets the true airspeed. The amplitude of the airspeed is modified but its
288 // direction is kept unchanged. If there is no wind, the same is true for the
289 // ground velocity. If there is some wind, the airspeed direction is unchanged
290 // but this may result in the ground velocity direction being altered. This is
291 // for backward compatibility.
293 void FGInitialCondition::SetVtrueFpsIC(double vtrue)
295 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
296 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
299 _vt_NED *= vtrue / vt;
301 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vtrue, 0., 0.);
304 vUVW_NED = _vt_NED - _vWIND_NED;
306 calcAeroAngles(_vt_NED);
308 lastSpeedSet = setvt;
311 //******************************************************************************
312 // When the climb rate is modified, we need to update the angles theta and beta
313 // to keep the true airspeed amplitude, the AoA and the heading unchanged.
314 // Beta will be modified if the aircraft roll angle is not null.
316 void FGInitialCondition::SetClimbRateFpsIC(double hdot)
318 if (fabs(hdot) > vt) {
319 cerr << "The climb rate cannot be higher than the true speed." << endl;
323 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
324 FGColumnVector3 _WIND_NED = _vt_NED - vUVW_NED;
325 double hdot0 = _vt_NED(eW);
327 if (fabs(hdot0) < vt) {
328 double scale = sqrt((vt*vt-hdot*hdot)/(vt*vt-hdot0*hdot0));
329 _vt_NED(eU) *= scale;
330 _vt_NED(eV) *= scale;
333 vUVW_NED = _vt_NED - _WIND_NED;
335 // The AoA is not modified here but the function SetAlphaRadIC is updating the
336 // same angles than SetClimbRateFpsIC needs to update.
337 // TODO : create a subroutine that only shares the relevant code.
338 SetAlphaRadIC(alpha);
341 //******************************************************************************
342 // When the AoA is modified, we need to update the angles theta and beta to
343 // keep the true airspeed amplitude, the climb rate and the heading unchanged.
344 // Beta will be modified if the aircraft roll angle is not null.
346 void FGInitialCondition::SetAlphaRadIC(double alfa)
348 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
350 double calpha = cos(alfa), salpha = sin(alfa);
351 double cpsi = cos(psi), spsi = sin(psi);
352 double cphi = cos(phi), sphi = sin(phi);
353 FGMatrix33 Tpsi( cpsi, spsi, 0.,
356 FGMatrix33 Tphi(1., 0., 0.,
359 FGMatrix33 Talpha( calpha, 0., salpha,
361 -salpha, 0., calpha);
363 FGColumnVector3 v0 = Tpsi * _vt_NED;
364 FGColumnVector3 n = (Talpha * Tphi).Transposed() * FGColumnVector3(0., 0., 1.);
365 FGColumnVector3 y = FGColumnVector3(0., 1., 0.);
366 FGColumnVector3 u = y - DotProduct(y, n) * n;
367 FGColumnVector3 p = y * n;
369 if (DotProduct(p, v0) < 0) p *= -1.0;
372 u *= DotProduct(v0, y) / DotProduct(u, y);
374 // There are situations where the desired alpha angle cannot be obtained. This
375 // is not a limitation of the algorithm but is due to the mathematical problem
376 // not having a solution. This can only be cured by limiting the alpha angle
377 // or by modifying an additional angle (psi ?). Since this is anticipated to
378 // be a pathological case (mainly when a high roll angle is required) this
379 // situation is not addressed below. However if there are complaints about the
380 // following error being raised too often, we might need to reconsider this
382 if (DotProduct(v0, v0) < DotProduct(u, u)) {
383 cerr << "Cannot modify angle 'alpha' from " << alpha << " to " << alfa << endl;
387 FGColumnVector3 v1 = u + sqrt(DotProduct(v0, v0) - DotProduct(u, u))*p;
389 FGColumnVector3 v0xz(v0(eU), 0., v0(eW));
390 FGColumnVector3 v1xz(v1(eU), 0., v1(eW));
393 double sinTheta = (v1xz * v0xz)(eY);
394 theta = asin(sinTheta);
396 FGQuaternion Quat(phi, theta, psi);
399 Tb2l = Quat.GetTInv();
401 FGColumnVector3 v2 = Talpha * Quat.GetT() * _vt_NED;
404 beta = atan2(v2(eV), v2(eU));
405 double cbeta=0.0, sbeta=0.0;
410 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
412 salpha*cbeta, -salpha*sbeta, calpha);
413 Tb2w = Tw2b.Transposed();
416 //******************************************************************************
417 // When the beta angle is modified, we need to update the angles theta and psi
418 // to keep the true airspeed (amplitude and direction - including the climb rate)
419 // and the alpha angle unchanged. This may result in the aircraft heading (psi)
420 // being altered especially if there is cross wind.
422 void FGInitialCondition::SetBetaRadIC(double bta)
424 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
427 double calpha = cos(alpha), salpha = sin(alpha);
428 double cbeta = cos(beta), sbeta = sin(beta);
430 Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
432 salpha*cbeta, -salpha*sbeta, calpha);
433 Tb2w = Tw2b.Transposed();
435 FGColumnVector3 vf = FGQuaternion(eX, phi).GetTInv() * Tw2b * FGColumnVector3(vt, 0., 0.);
436 FGColumnVector3 v0xy(_vt_NED(eX), _vt_NED(eY), 0.);
437 FGColumnVector3 v1xy(sqrt(v0xy(eX)*v0xy(eX)+v0xy(eY)*v0xy(eY)-vf(eY)*vf(eY)),vf(eY),0.);
441 if (vf(eX) < 0.) v0xy(eX) *= -1.0;
443 double sinPsi = (v1xy * v0xy)(eZ);
444 double cosPsi = DotProduct(v0xy, v1xy);
445 psi = atan2(sinPsi, cosPsi);
446 FGMatrix33 Tpsi( cosPsi, sinPsi, 0.,
450 FGColumnVector3 v2xz = Tpsi * _vt_NED;
451 FGColumnVector3 vfxz = vf;
452 v2xz(eV) = vfxz(eV) = 0.0;
455 double sinTheta = (v2xz * vfxz)(eY);
456 theta = -asin(sinTheta);
458 FGQuaternion Quat(phi, theta, psi);
461 Tb2l = Quat.GetTInv();
464 //******************************************************************************
465 // Modifies the body frame orientation (roll angle phi). The true airspeed in
466 // the local NED frame is kept unchanged. Hence the true airspeed in the body
467 // frame is modified.
469 void FGInitialCondition::SetPhiRadIC(double fi)
471 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
474 FGQuaternion Quat = FGQuaternion(phi, theta, psi);
477 Tb2l = Quat.GetTInv();
479 calcAeroAngles(_vt_NED);
482 //******************************************************************************
483 // Modifies the body frame orientation (pitch angle theta). The true airspeed in
484 // the local NED frame is kept unchanged. Hence the true airspeed in the body
485 // frame is modified.
487 void FGInitialCondition::SetThetaRadIC(double teta)
489 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
492 FGQuaternion Quat = FGQuaternion(phi, theta, psi);
495 Tb2l = Quat.GetTInv();
497 calcAeroAngles(_vt_NED);
500 //******************************************************************************
501 // Modifies the body frame orientation (yaw angle psi). The true airspeed in
502 // the local NED frame is kept unchanged. Hence the true airspeed in the body
503 // frame is modified.
505 void FGInitialCondition::SetPsiRadIC(double psy)
507 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
510 FGQuaternion Quat = FGQuaternion(phi, theta, psi);
513 Tb2l = Quat.GetTInv();
515 calcAeroAngles(_vt_NED);
518 //******************************************************************************
519 // Modifies an aircraft velocity component (eU, eV or eW) in the body frame. The
520 // true airspeed is modified accordingly. If there is some wind, the airspeed
521 // direction modification may differ from the body velocity modification.
523 void FGInitialCondition::SetBodyVelFpsIC(int idx, double vel)
525 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
526 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED;
527 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
529 _vUVW_BODY(idx) = vel;
530 vUVW_NED = Tb2l * _vUVW_BODY;
531 _vt_NED = vUVW_NED + _vWIND_NED;
532 vt = _vt_NED.Magnitude();
534 calcAeroAngles(_vt_NED);
536 lastSpeedSet = setuvw;
539 //******************************************************************************
540 // Modifies an aircraft velocity component (eX, eY or eZ) in the local NED frame.
541 // The true airspeed is modified accordingly. If there is some wind, the airspeed
542 // direction modification may differ from the local velocity modification.
544 void FGInitialCondition::SetNEDVelFpsIC(int idx, double vel)
546 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
547 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
550 _vt_NED = vUVW_NED + _vWIND_NED;
551 vt = _vt_NED.Magnitude();
553 calcAeroAngles(_vt_NED);
555 lastSpeedSet = setned;
558 //******************************************************************************
559 // Set wind amplitude and direction in the local NED frame. The aircraft velocity
560 // with respect to the ground is not changed but the true airspeed is.
562 void FGInitialCondition::SetWindNEDFpsIC(double wN, double wE, double wD )
564 FGColumnVector3 _vt_NED = vUVW_NED + FGColumnVector3(wN, wE, wD);
565 vt = _vt_NED.Magnitude();
567 calcAeroAngles(_vt_NED);
570 //******************************************************************************
571 // Set the cross wind velocity (in knots). Here, 'cross wind' means perpendicular
572 // to the aircraft heading and parallel to the ground. The aircraft velocity
573 // with respect to the ground is not changed but the true airspeed is.
575 void FGInitialCondition::SetCrossWindKtsIC(double cross)
577 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
578 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
579 FGColumnVector3 _vCROSS(-sin(psi), cos(psi), 0.);
581 // Gram-Schmidt process is used to remove the existing cross wind component
582 _vWIND_NED -= DotProduct(_vWIND_NED, _vCROSS) * _vCROSS;
583 // which is now replaced by the new value.
584 _vWIND_NED += cross * _vCROSS;
585 _vt_NED = vUVW_NED + _vWIND_NED;
586 vt = _vt_NED.Magnitude();
588 calcAeroAngles(_vt_NED);
591 //******************************************************************************
592 // Set the head wind velocity (in knots). Here, 'head wind' means parallel
593 // to the aircraft heading and to the ground. The aircraft velocity
594 // with respect to the ground is not changed but the true airspeed is.
596 void FGInitialCondition::SetHeadWindKtsIC(double head)
598 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
599 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
600 FGColumnVector3 _vHEAD(cos(psi), sin(psi), 0.);
602 // Gram-Schmidt process is used to remove the existing cross wind component
603 _vWIND_NED -= DotProduct(_vWIND_NED, _vHEAD) * _vHEAD;
604 // which is now replaced by the new value.
605 _vWIND_NED += head * _vHEAD;
606 _vt_NED = vUVW_NED + _vWIND_NED;
607 vt = _vt_NED.Magnitude();
609 calcAeroAngles(_vt_NED);
612 //******************************************************************************
613 // Set the vertical wind velocity (in knots). The 'vertical' is taken in the
614 // local NED frame. The aircraft velocity with respect to the ground is not
615 // changed but the true airspeed is.
617 void FGInitialCondition::SetWindDownKtsIC(double wD)
619 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
621 _vt_NED(eW) = vUVW_NED(eW) + wD;
622 vt = _vt_NED.Magnitude();
624 calcAeroAngles(_vt_NED);
627 //******************************************************************************
628 // Modifies the wind velocity (in knots) while keeping its direction unchanged.
629 // The vertical component (in local NED frame) is unmodified. The aircraft
630 // velocity with respect to the ground is not changed but the true airspeed is.
632 void FGInitialCondition::SetWindMagKtsIC(double mag)
634 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
635 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
636 FGColumnVector3 _vHEAD(_vWIND_NED(eU), _vWIND_NED(eV), 0.);
637 double windMag = _vHEAD.Magnitude();
640 _vHEAD *= mag / windMag;
642 _vHEAD = FGColumnVector3(mag, 0., 0.);
644 _vWIND_NED(eU) = _vHEAD(eU);
645 _vWIND_NED(eV) = _vHEAD(eV);
646 _vt_NED = vUVW_NED + _vWIND_NED;
647 vt = _vt_NED.Magnitude();
649 calcAeroAngles(_vt_NED);
652 //******************************************************************************
653 // Modifies the wind direction while keeping its velocity unchanged. The vertical
654 // component (in local NED frame) is unmodified. The aircraft velocity with
655 // respect to the ground is not changed but the true airspeed is.
657 void FGInitialCondition::SetWindDirDegIC(double dir)
659 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
660 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
661 double mag = _vWIND_NED.Magnitude(eU, eV);
662 FGColumnVector3 _vHEAD(mag*cos(dir*degtorad), mag*sin(dir*degtorad), 0.);
664 _vWIND_NED(eU) = _vHEAD(eU);
665 _vWIND_NED(eV) = _vHEAD(eV);
666 _vt_NED = vUVW_NED + _vWIND_NED;
667 vt = _vt_NED.Magnitude();
669 calcAeroAngles(_vt_NED);
672 //******************************************************************************
673 // Set the altitude SL. If the airspeed has been previously set with parameters
674 // that are atmosphere dependent (Mach, VCAS, VEAS) then the true airspeed is
675 // modified to keep the last set speed to its previous value.
677 void FGInitialCondition::SetAltitudeASLFtIC(double alt)
679 double altitudeASL = position.GetRadius() - sea_level_radius;
680 double temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
681 double soundSpeed = sqrt(SHRatio*Reng*temperature);
682 double rho = fdmex->GetAtmosphere()->GetDensity(altitudeASL);
683 double rhoSL = fdmex->GetAtmosphere()->GetDensitySL();
685 double mach0 = vt / soundSpeed;
686 double vc0 = calcVcas(mach0);
687 double ve0 = vt * sqrt(rho/rhoSL);
690 temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
691 soundSpeed = sqrt(SHRatio*Reng*temperature);
692 rho = fdmex->GetAtmosphere()->GetDensity(altitudeASL);
694 switch(lastSpeedSet) {
696 mach0 = getMachFromVcas(vc0);
698 SetVtrueFpsIC(mach0 * soundSpeed);
701 SetVtrueFpsIC(ve0 * sqrt(rho/rhoSL));
705 position.SetRadius(alt + sea_level_radius);
708 //******************************************************************************
709 // Calculate the VCAS. Uses the Rayleigh formula for supersonic speeds
710 // (See "Introduction to Aerodynamics of a Compressible Fluid - H.W. Liepmann,
711 // A.E. Puckett - Wiley & sons (1947)" §5.4 pp 75-80)
713 double FGInitialCondition::calcVcas(double Mach) const
715 double altitudeASL = position.GetRadius() - sea_level_radius;
716 double p=fdmex->GetAtmosphere()->GetPressure(altitudeASL);
717 double psl=fdmex->GetAtmosphere()->GetPressureSL();
718 double rhosl=fdmex->GetAtmosphere()->GetDensitySL();
721 if (Mach < 0) Mach=0;
722 if (Mach < 1) //calculate total pressure assuming isentropic flow
723 pt=p*pow((1 + 0.2*Mach*Mach),3.5);
725 // shock in front of pitot tube, we'll assume its normal and use
726 // the Rayleigh Pitot Tube Formula, i.e. the ratio of total
727 // pressure behind the shock to the static pressure in front of
728 // the normal shock assumption should not be a bad one -- most supersonic
729 // aircraft place the pitot probe out front so that it is the forward
730 // most point on the aircraft. The real shock would, of course, take
731 // on something like the shape of a rounded-off cone but, here again,
732 // the assumption should be good since the opening of the pitot probe
733 // is very small and, therefore, the effects of the shock curvature
734 // should be small as well. AFAIK, this approach is fairly well accepted
735 // within the aerospace community
737 // The denominator below is zero for Mach ~ 0.38, for which
738 // we'll never be here, so we're safe
740 pt = p*166.92158*pow(Mach,7.0)/pow(7*Mach*Mach-1,2.5);
743 A = pow(((pt-p)/psl+1),0.28571);
744 vcas = sqrt(7*psl/rhosl*(A-1));
745 //cout << "calcVcas: vcas= " << vcas*fpstokts << " mach= " << Mach << " pressure: " << pt << endl;
749 //******************************************************************************
750 // Reverse the VCAS formula to obtain the corresponding Mach number. For subsonic
751 // speeds, the reversed formula has a closed form. For supersonic speeds, the
752 // formula is reversed by the Newton-Raphson algorithm.
754 double FGInitialCondition::getMachFromVcas(double vcas)
756 double altitudeASL = position.GetRadius() - sea_level_radius;
757 double p=fdmex->GetAtmosphere()->GetPressure(altitudeASL);
758 double psl=fdmex->GetAtmosphere()->GetPressureSL();
759 double rhosl=fdmex->GetAtmosphere()->GetDensitySL();
761 double pt = p + psl*(pow(1+vcas*vcas*rhosl/(7.0*psl),3.5)-1);
764 return sqrt(5.0*(pow(pt/p, 0.2857143) -1)); // Mach < 1
767 double mach = sqrt(0.77666*pt/p); // Initial guess is based on a quadratic approximation of the Rayleigh formula
769 double target = pt/(166.92158*p);
772 // Find the root with Newton-Raphson. Since the differential is never zero,
773 // the function is monotonic and has only one root with a multiplicity of one.
774 // Convergence is certain.
775 while (delta > 1E-5 && iter < 10) {
776 double m2 = mach*mach; // Mach^2
777 double m6 = m2*m2*m2; // Mach^6
778 delta = mach*m6/pow(7.0*m2-1.0,2.5) - target;
779 double diff = 7.0*m6*(2.0*m2-1)/pow(7.0*m2-1.0,3.5); // Never zero when Mach >= 1
788 //******************************************************************************
790 double FGInitialCondition::GetWindDirDegIC(void) const
792 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
793 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
795 return _vWIND_NED(eV) == 0.0 ? 0.0
796 : atan2(_vWIND_NED(eV), _vWIND_NED(eU))*radtodeg;
799 //******************************************************************************
801 double FGInitialCondition::GetNEDWindFpsIC(int idx) const
803 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
804 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
806 return _vWIND_NED(idx);
809 //******************************************************************************
811 double FGInitialCondition::GetWindFpsIC(void) const
813 FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
814 FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
816 return _vWIND_NED.Magnitude(eU, eV);
819 //******************************************************************************
821 double FGInitialCondition::GetBodyWindFpsIC(int idx) const
823 FGColumnVector3 _vt_BODY = Tw2b * FGColumnVector3(vt, 0., 0.);
824 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED;
825 FGColumnVector3 _vWIND_BODY = _vt_BODY - _vUVW_BODY;
827 return _vWIND_BODY(idx);
830 //******************************************************************************
832 double FGInitialCondition::GetVcalibratedKtsIC(void) const
834 double altitudeASL = position.GetRadius() - sea_level_radius;
835 double temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
836 double soundSpeed = sqrt(SHRatio*Reng*temperature);
837 double mach = vt / soundSpeed;
838 return fpstokts * calcVcas(mach);
841 //******************************************************************************
843 double FGInitialCondition::GetVequivalentKtsIC(void) const
845 double altitudeASL = position.GetRadius() - sea_level_radius;
846 double rho = fdmex->GetAtmosphere()->GetDensity(altitudeASL);
847 double rhoSL = fdmex->GetAtmosphere()->GetDensitySL();
848 return fpstokts * vt * sqrt(rho/rhoSL);
851 //******************************************************************************
853 double FGInitialCondition::GetMachIC(void) const
855 double altitudeASL = position.GetRadius() - sea_level_radius;
856 double temperature = fdmex->GetAtmosphere()->GetTemperature(altitudeASL);
857 double soundSpeed = sqrt(SHRatio*Reng*temperature);
858 return vt / soundSpeed;
861 //******************************************************************************
863 double FGInitialCondition::GetBodyVelFpsIC(int idx) const
865 FGColumnVector3 _vUVW_BODY = Tl2b * vUVW_NED;
867 return _vUVW_BODY(idx);
870 //******************************************************************************
872 bool FGInitialCondition::Load(string rstfile, bool useStoredPath)
875 if( useStoredPath ) {
876 init_file_name = fdmex->GetFullAircraftPath() + sep + rstfile + ".xml";
878 init_file_name = rstfile;
881 document = LoadXMLDocument(init_file_name);
883 // Make sure that the document is valid
885 cerr << "File: " << init_file_name << " could not be read." << endl;
889 if (document->GetName() != string("initialize")) {
890 cerr << "File: " << init_file_name << " is not a reset file." << endl;
894 double version = document->GetAttributeValueAsNumber("version");
897 if (version == HUGE_VAL) {
898 result = Load_v1(); // Default to the old version
899 } else if (version >= 3.0) {
900 cerr << "Only initialization file formats 1 and 2 are currently supported" << endl;
902 } else if (version >= 2.0) {
904 } else if (version >= 1.0) {
908 // Check to see if any engines are specified to be initialized in a running state
909 FGPropulsion* propulsion = fdmex->GetPropulsion();
910 Element* running_elements = document->FindElement("running");
911 while (running_elements) {
912 int n = int(running_elements->GetDataAsNumber());
914 propulsion->InitRunning(n);
915 } catch (string str) {
919 running_elements = document->FindNextElement("running");
923 fdmex->GetPropagate()->DumpState();
928 //******************************************************************************
930 bool FGInitialCondition::Load_v1(void)
934 if (document->FindElement("latitude"))
935 position.SetLatitude(document->FindElementValueAsNumberConvertTo("latitude", "RAD"));
936 if (document->FindElement("longitude"))
937 position.SetLongitude(document->FindElementValueAsNumberConvertTo("longitude", "RAD"));
938 if (document->FindElement("elevation"))
939 terrain_elevation = document->FindElementValueAsNumberConvertTo("elevation", "FT");
941 if (document->FindElement("altitude")) // This is feet above ground level
942 position.SetRadius(document->FindElementValueAsNumberConvertTo("altitude", "FT") + terrain_elevation + sea_level_radius);
943 else if (document->FindElement("altitudeAGL")) // This is feet above ground level
944 position.SetRadius(document->FindElementValueAsNumberConvertTo("altitudeAGL", "FT") + terrain_elevation + sea_level_radius);
945 else if (document->FindElement("altitudeMSL")) // This is feet above sea level
946 position.SetRadius(document->FindElementValueAsNumberConvertTo("altitudeMSL", "FT") + sea_level_radius);
948 if (document->FindElement("phi"))
949 phi = document->FindElementValueAsNumberConvertTo("phi", "RAD");
950 if (document->FindElement("theta"))
951 theta = document->FindElementValueAsNumberConvertTo("theta", "RAD");
952 if (document->FindElement("psi"))
953 psi = document->FindElementValueAsNumberConvertTo("psi", "RAD");
955 FGQuaternion Quat(phi, theta, psi);
958 Tb2l = Quat.GetTInv();
960 if (document->FindElement("ubody"))
961 SetUBodyFpsIC(document->FindElementValueAsNumberConvertTo("ubody", "FT/SEC"));
962 if (document->FindElement("vbody"))
963 SetVBodyFpsIC(document->FindElementValueAsNumberConvertTo("vbody", "FT/SEC"));
964 if (document->FindElement("wbody"))
965 SetWBodyFpsIC(document->FindElementValueAsNumberConvertTo("wbody", "FT/SEC"));
966 if (document->FindElement("vnorth"))
967 SetVNorthFpsIC(document->FindElementValueAsNumberConvertTo("vnorth", "FT/SEC"));
968 if (document->FindElement("veast"))
969 SetVEastFpsIC(document->FindElementValueAsNumberConvertTo("veast", "FT/SEC"));
970 if (document->FindElement("vdown"))
971 SetVDownFpsIC(document->FindElementValueAsNumberConvertTo("vdown", "FT/SEC"));
972 if (document->FindElement("vc"))
973 SetVcalibratedKtsIC(document->FindElementValueAsNumberConvertTo("vc", "KTS"));
974 if (document->FindElement("vt"))
975 SetVtrueKtsIC(document->FindElementValueAsNumberConvertTo("vt", "KTS"));
976 if (document->FindElement("mach"))
977 SetMachIC(document->FindElementValueAsNumber("mach"));
978 if (document->FindElement("gamma"))
979 SetFlightPathAngleDegIC(document->FindElementValueAsNumberConvertTo("gamma", "DEG"));
980 if (document->FindElement("roc"))
981 SetClimbRateFpsIC(document->FindElementValueAsNumberConvertTo("roc", "FT/SEC"));
982 if (document->FindElement("vground"))
983 SetVgroundKtsIC(document->FindElementValueAsNumberConvertTo("vground", "KTS"));
984 if (document->FindElement("alpha"))
985 SetAlphaDegIC(document->FindElementValueAsNumberConvertTo("alpha", "DEG"));
986 if (document->FindElement("beta"))
987 SetBetaDegIC(document->FindElementValueAsNumberConvertTo("beta", "DEG"));
988 if (document->FindElement("vwind"))
989 SetWindMagKtsIC(document->FindElementValueAsNumberConvertTo("vwind", "KTS"));
990 if (document->FindElement("winddir"))
991 SetWindDirDegIC(document->FindElementValueAsNumberConvertTo("winddir", "DEG"));
992 if (document->FindElement("hwind"))
993 SetHeadWindKtsIC(document->FindElementValueAsNumberConvertTo("hwind", "KTS"));
994 if (document->FindElement("xwind"))
995 SetCrossWindKtsIC(document->FindElementValueAsNumberConvertTo("xwind", "KTS"));
996 if (document->FindElement("targetNlf"))
998 SetTargetNlfIC(document->FindElementValueAsNumber("targetNlf"));
1004 //******************************************************************************
1006 bool FGInitialCondition::Load_v2(void)
1008 FGColumnVector3 vOrient;
1010 FGColumnVector3 vOmegaEarth = FGColumnVector3(0.0, 0.0, fdmex->GetInertial()->omega());
1012 if (document->FindElement("earth_position_angle"))
1013 position.SetEarthPositionAngle(document->FindElementValueAsNumberConvertTo("earth_position_angle", "RAD"));
1015 if (document->FindElement("elevation"))
1016 terrain_elevation = document->FindElementValueAsNumberConvertTo("elevation", "FT");
1018 // Initialize vehicle position
1020 // Allowable frames:
1021 // - ECI (Earth Centered Inertial)
1022 // - ECEF (Earth Centered, Earth Fixed)
1024 Element* position_el = document->FindElement("position");
1026 string frame = position_el->GetAttributeValue("frame");
1027 frame = to_lower(frame);
1028 if (frame == "eci") { // Need to transform vLoc to ECEF for storage and use in FGLocation.
1029 position = position.GetTi2ec() * position_el->FindElementTripletConvertTo("FT");
1030 } else if (frame == "ecef") {
1031 if (!position_el->FindElement("x") && !position_el->FindElement("y") && !position_el->FindElement("z")) {
1032 if (position_el->FindElement("radius")) {
1033 position.SetRadius(position_el->FindElementValueAsNumberConvertTo("radius", "FT"));
1034 } else if (position_el->FindElement("altitudeAGL")) {
1035 position.SetRadius(sea_level_radius + terrain_elevation + position_el->FindElementValueAsNumberConvertTo("altitude", "FT"));
1036 } else if (position_el->FindElement("altitudeMSL")) {
1037 position.SetRadius(sea_level_radius + position_el->FindElementValueAsNumberConvertTo("altitudeMSL", "FT"));
1039 cerr << endl << " No altitude or radius initial condition is given." << endl;
1042 if (position_el->FindElement("longitude"))
1043 position.SetLongitude(position_el->FindElementValueAsNumberConvertTo("longitude", "RAD"));
1044 if (position_el->FindElement("latitude"))
1045 position.SetLatitude(position_el->FindElementValueAsNumberConvertTo("latitude", "RAD"));
1047 position = position_el->FindElementTripletConvertTo("FT");
1050 cerr << endl << " Neither ECI nor ECEF frame is specified for initial position." << endl;
1054 cerr << endl << " Initial position not specified in this initialization file." << endl;
1058 // End of position initialization
1060 // Initialize vehicle orientation
1062 // - ECI (Earth Centered Inertial)
1063 // - ECEF (Earth Centered, Earth Fixed)
1066 // Need to convert the provided orientation to a local orientation, using
1067 // the given orientation and knowledge of the Earth position angle.
1068 // This could be done using matrices (where in the subscript "b/a",
1069 // it is meant "b with respect to a", and where b=body frame,
1070 // i=inertial frame, and e=ecef frame) as:
1072 // C_b/l = C_b/e * C_e/l
1074 // Using quaternions (note reverse ordering compared to matrix representation):
1076 // Q_b/l = Q_e/l * Q_b/e
1078 // Use the specific matrices as needed. The above example of course is for the whole
1079 // body to local orientation.
1080 // The new orientation angles can be extracted from the matrix or the quaternion.
1081 // ToDo: Do we need to deal with normalization of the quaternions here?
1083 Element* orientation_el = document->FindElement("orientation");
1084 FGQuaternion QuatLocal2Body;
1085 if (orientation_el) {
1086 string frame = orientation_el->GetAttributeValue("frame");
1087 frame = to_lower(frame);
1088 vOrient = orientation_el->FindElementTripletConvertTo("RAD");
1089 if (frame == "eci") {
1091 // In this case, we are supplying the Euler angles for the vehicle with
1092 // respect to the inertial system, represented by the C_b/i Matrix.
1093 // We want the body orientation with respect to the local (NED frame):
1095 // C_b/l = C_b/i * C_i/l
1097 // Or, using quaternions (note reverse ordering compared to matrix representation):
1099 // Q_b/l = Q_e/l * Q_b/i
1101 FGQuaternion QuatI2Body = FGQuaternion(vOrient);
1102 QuatI2Body.Normalize();
1103 FGQuaternion QuatLocal2I = position.GetTl2i();
1104 QuatLocal2I.Normalize();
1105 QuatLocal2Body = QuatLocal2I * QuatI2Body;
1107 } else if (frame == "ecef") {
1109 // In this case we are given the Euler angles representing the orientation of
1110 // the body with respect to the ECEF system, represented by the C_b/e Matrix.
1111 // We want the body orientation with respect to the local (NED frame):
1113 // C_b/l = C_b/e * C_e/l
1115 // Using quaternions (note reverse ordering compared to matrix representation):
1117 // Q_b/l = Q_e/l * Q_b/e
1119 FGQuaternion QuatEC2Body(vOrient); // Store relationship of Body frame wrt ECEF frame, Q_b/e
1120 QuatEC2Body.Normalize();
1121 FGQuaternion QuatLocal2EC = position.GetTl2ec(); // Get Q_e/l from matrix
1122 QuatLocal2EC.Normalize();
1123 QuatLocal2Body = QuatLocal2EC * QuatEC2Body; // Q_b/l = Q_e/l * Q_b/e
1125 } else if (frame == "local") {
1127 QuatLocal2Body = FGQuaternion(vOrient);
1131 cerr << endl << fgred << " Orientation frame type: \"" << frame
1132 << "\" is not supported!" << reset << endl << endl;
1138 QuatLocal2Body.Normalize();
1139 phi = QuatLocal2Body.GetEuler(ePhi);
1140 theta = QuatLocal2Body.GetEuler(eTht);
1141 psi = QuatLocal2Body.GetEuler(ePsi);
1142 Tl2b = QuatLocal2Body.GetT();
1143 Tb2l = QuatLocal2Body.GetTInv();
1145 // Initialize vehicle velocity
1147 // - ECI (Earth Centered Inertial)
1148 // - ECEF (Earth Centered, Earth Fixed)
1151 // The vehicle will be defaulted to (0,0,0) in the Body frame if nothing is provided.
1153 Element* velocity_el = document->FindElement("velocity");
1154 FGColumnVector3 vInitVelocity = FGColumnVector3(0.0, 0.0, 0.0);
1155 FGMatrix33 mTec2l = position.GetTec2l();
1158 string frame = velocity_el->GetAttributeValue("frame");
1159 frame = to_lower(frame);
1160 FGColumnVector3 vInitVelocity = velocity_el->FindElementTripletConvertTo("FT/SEC");
1162 if (frame == "eci") {
1163 FGColumnVector3 omega_cross_r = vOmegaEarth * (position.GetTec2i() * position);
1164 vUVW_NED = mTec2l * (vInitVelocity - omega_cross_r);
1165 } else if (frame == "ecef") {
1166 vUVW_NED = mTec2l * vInitVelocity;
1167 } else if (frame == "local") {
1168 vUVW_NED = vInitVelocity;
1169 lastSpeedSet = setned;
1170 } else if (frame == "body") {
1171 vUVW_NED = Tb2l * vInitVelocity;
1172 lastSpeedSet = setuvw;
1175 cerr << endl << fgred << " Velocity frame type: \"" << frame
1176 << "\" is not supported!" << reset << endl << endl;
1183 vUVW_NED = Tb2l * vInitVelocity;
1187 vt = vUVW_NED.Magnitude();
1189 calcAeroAngles(vUVW_NED);
1191 // Initialize vehicle body rates
1193 // - ECI (Earth Centered Inertial)
1194 // - ECEF (Earth Centered, Earth Fixed)
1197 FGColumnVector3 vLocalRate;
1198 Element* attrate_el = document->FindElement("attitude_rate");
1199 double radInv = 1.0 / position.GetRadius();
1200 FGColumnVector3 vOmegaLocal = FGColumnVector3(
1201 radInv*vUVW_NED(eEast),
1202 -radInv*vUVW_NED(eNorth),
1203 -radInv*vUVW_NED(eEast)*position.GetTanLatitude() );
1207 string frame = attrate_el->GetAttributeValue("frame");
1208 frame = to_lower(frame);
1209 FGColumnVector3 vAttRate = attrate_el->FindElementTripletConvertTo("RAD/SEC");
1211 if (frame == "eci") {
1212 vLocalRate = Tl2b * (position.GetTi2l() * (vAttRate - vOmegaEarth) - vOmegaLocal);
1213 } else if (frame == "ecef") {
1214 vLocalRate = Tl2b * (position.GetTec2l() * vAttRate - vOmegaLocal);
1215 } else if (frame == "local") {
1216 vLocalRate = vAttRate;
1217 } else if (!frame.empty()) { // misspelling of frame
1219 cerr << endl << fgred << " Attitude rate frame type: \"" << frame
1220 << "\" is not supported!" << reset << endl << endl;
1223 } else if (frame.empty()) {
1227 } else { // Body frame attitude rate assumed 0 relative to local.
1228 vLocalRate.InitMatrix();
1238 //******************************************************************************
1240 void FGInitialCondition::bind(void)
1242 PropertyManager->Tie("ic/vc-kts", this,
1243 &FGInitialCondition::GetVcalibratedKtsIC,
1244 &FGInitialCondition::SetVcalibratedKtsIC,
1246 PropertyManager->Tie("ic/ve-kts", this,
1247 &FGInitialCondition::GetVequivalentKtsIC,
1248 &FGInitialCondition::SetVequivalentKtsIC,
1250 PropertyManager->Tie("ic/vg-kts", this,
1251 &FGInitialCondition::GetVgroundKtsIC,
1252 &FGInitialCondition::SetVgroundKtsIC,
1254 PropertyManager->Tie("ic/vt-kts", this,
1255 &FGInitialCondition::GetVtrueKtsIC,
1256 &FGInitialCondition::SetVtrueKtsIC,
1258 PropertyManager->Tie("ic/mach", this,
1259 &FGInitialCondition::GetMachIC,
1260 &FGInitialCondition::SetMachIC,
1262 PropertyManager->Tie("ic/roc-fpm", this,
1263 &FGInitialCondition::GetClimbRateFpmIC,
1264 &FGInitialCondition::SetClimbRateFpmIC,
1266 PropertyManager->Tie("ic/gamma-deg", this,
1267 &FGInitialCondition::GetFlightPathAngleDegIC,
1268 &FGInitialCondition::SetFlightPathAngleDegIC,
1270 PropertyManager->Tie("ic/alpha-deg", this,
1271 &FGInitialCondition::GetAlphaDegIC,
1272 &FGInitialCondition::SetAlphaDegIC,
1274 PropertyManager->Tie("ic/beta-deg", this,
1275 &FGInitialCondition::GetBetaDegIC,
1276 &FGInitialCondition::SetBetaDegIC,
1278 PropertyManager->Tie("ic/theta-deg", this,
1279 &FGInitialCondition::GetThetaDegIC,
1280 &FGInitialCondition::SetThetaDegIC,
1282 PropertyManager->Tie("ic/phi-deg", this,
1283 &FGInitialCondition::GetPhiDegIC,
1284 &FGInitialCondition::SetPhiDegIC,
1286 PropertyManager->Tie("ic/psi-true-deg", this,
1287 &FGInitialCondition::GetPsiDegIC );
1288 PropertyManager->Tie("ic/lat-gc-deg", this,
1289 &FGInitialCondition::GetLatitudeDegIC,
1290 &FGInitialCondition::SetLatitudeDegIC,
1292 PropertyManager->Tie("ic/long-gc-deg", this,
1293 &FGInitialCondition::GetLongitudeDegIC,
1294 &FGInitialCondition::SetLongitudeDegIC,
1296 PropertyManager->Tie("ic/h-sl-ft", this,
1297 &FGInitialCondition::GetAltitudeASLFtIC,
1298 &FGInitialCondition::SetAltitudeASLFtIC,
1300 PropertyManager->Tie("ic/h-agl-ft", this,
1301 &FGInitialCondition::GetAltitudeAGLFtIC,
1302 &FGInitialCondition::SetAltitudeAGLFtIC,
1304 PropertyManager->Tie("ic/sea-level-radius-ft", this,
1305 &FGInitialCondition::GetSeaLevelRadiusFtIC,
1306 &FGInitialCondition::SetSeaLevelRadiusFtIC,
1308 PropertyManager->Tie("ic/terrain-elevation-ft", this,
1309 &FGInitialCondition::GetTerrainElevationFtIC,
1310 &FGInitialCondition::SetTerrainElevationFtIC,
1312 PropertyManager->Tie("ic/vg-fps", this,
1313 &FGInitialCondition::GetVgroundFpsIC,
1314 &FGInitialCondition::SetVgroundFpsIC,
1316 PropertyManager->Tie("ic/vt-fps", this,
1317 &FGInitialCondition::GetVtrueFpsIC,
1318 &FGInitialCondition::SetVtrueFpsIC,
1320 PropertyManager->Tie("ic/vw-bx-fps", this,
1321 &FGInitialCondition::GetWindUFpsIC);
1322 PropertyManager->Tie("ic/vw-by-fps", this,
1323 &FGInitialCondition::GetWindVFpsIC);
1324 PropertyManager->Tie("ic/vw-bz-fps", this,
1325 &FGInitialCondition::GetWindWFpsIC);
1326 PropertyManager->Tie("ic/vw-north-fps", this,
1327 &FGInitialCondition::GetWindNFpsIC);
1328 PropertyManager->Tie("ic/vw-east-fps", this,
1329 &FGInitialCondition::GetWindEFpsIC);
1330 PropertyManager->Tie("ic/vw-down-fps", this,
1331 &FGInitialCondition::GetWindDFpsIC);
1332 PropertyManager->Tie("ic/vw-mag-fps", this,
1333 &FGInitialCondition::GetWindFpsIC);
1334 PropertyManager->Tie("ic/vw-dir-deg", this,
1335 &FGInitialCondition::GetWindDirDegIC,
1336 &FGInitialCondition::SetWindDirDegIC,
1339 PropertyManager->Tie("ic/roc-fps", this,
1340 &FGInitialCondition::GetClimbRateFpsIC,
1341 &FGInitialCondition::SetClimbRateFpsIC,
1343 PropertyManager->Tie("ic/u-fps", this,
1344 &FGInitialCondition::GetUBodyFpsIC,
1345 &FGInitialCondition::SetUBodyFpsIC,
1347 PropertyManager->Tie("ic/v-fps", this,
1348 &FGInitialCondition::GetVBodyFpsIC,
1349 &FGInitialCondition::SetVBodyFpsIC,
1351 PropertyManager->Tie("ic/w-fps", this,
1352 &FGInitialCondition::GetWBodyFpsIC,
1353 &FGInitialCondition::SetWBodyFpsIC,
1355 PropertyManager->Tie("ic/vn-fps", this,
1356 &FGInitialCondition::GetVNorthFpsIC,
1357 &FGInitialCondition::SetVNorthFpsIC,
1359 PropertyManager->Tie("ic/ve-fps", this,
1360 &FGInitialCondition::GetVEastFpsIC,
1361 &FGInitialCondition::SetVEastFpsIC,
1363 PropertyManager->Tie("ic/vd-fps", this,
1364 &FGInitialCondition::GetVDownFpsIC,
1365 &FGInitialCondition::SetVDownFpsIC,
1367 PropertyManager->Tie("ic/gamma-rad", this,
1368 &FGInitialCondition::GetFlightPathAngleRadIC,
1369 &FGInitialCondition::SetFlightPathAngleRadIC,
1371 PropertyManager->Tie("ic/alpha-rad", this,
1372 &FGInitialCondition::GetAlphaRadIC,
1373 &FGInitialCondition::SetAlphaRadIC,
1375 PropertyManager->Tie("ic/theta-rad", this,
1376 &FGInitialCondition::GetThetaRadIC,
1377 &FGInitialCondition::SetThetaRadIC,
1379 PropertyManager->Tie("ic/beta-rad", this,
1380 &FGInitialCondition::GetBetaRadIC,
1381 &FGInitialCondition::SetBetaRadIC,
1383 PropertyManager->Tie("ic/phi-rad", this,
1384 &FGInitialCondition::GetPhiRadIC,
1385 &FGInitialCondition::SetPhiRadIC,
1387 PropertyManager->Tie("ic/psi-true-rad", this,
1388 &FGInitialCondition::GetPsiRadIC);
1389 PropertyManager->Tie("ic/lat-gc-rad", this,
1390 &FGInitialCondition::GetLatitudeRadIC,
1391 &FGInitialCondition::SetLatitudeRadIC,
1393 PropertyManager->Tie("ic/long-gc-rad", this,
1394 &FGInitialCondition::GetLongitudeRadIC,
1395 &FGInitialCondition::SetLongitudeRadIC,
1397 PropertyManager->Tie("ic/p-rad_sec", this,
1398 &FGInitialCondition::GetPRadpsIC,
1399 &FGInitialCondition::SetPRadpsIC,
1401 PropertyManager->Tie("ic/q-rad_sec", this,
1402 &FGInitialCondition::GetQRadpsIC,
1403 &FGInitialCondition::SetQRadpsIC,
1405 PropertyManager->Tie("ic/r-rad_sec", this,
1406 &FGInitialCondition::GetRRadpsIC,
1407 &FGInitialCondition::SetRRadpsIC,
1410 typedef int (FGInitialCondition::*iPMF)(void) const;
1411 PropertyManager->Tie("simulation/write-state-file",
1414 &FGInitialCondition::WriteStateFile);
1418 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1419 // The bitmasked value choices are as follows:
1420 // unset: In this case (the default) JSBSim would only print
1421 // out the normally expected messages, essentially echoing
1422 // the config files as they are read. If the environment
1423 // variable is not set, debug_lvl is set to 1 internally
1424 // 0: This requests JSBSim not to output any messages
1426 // 1: This value explicity requests the normal JSBSim
1428 // 2: This value asks for a message to be printed out when
1429 // a class is instantiated
1430 // 4: When this value is set, a message is displayed when a
1431 // FGModel object executes its Run() method
1432 // 8: When this value is set, various runtime state variables
1433 // are printed out periodically
1434 // 16: When set various parameters are sanity checked and
1435 // a message is printed out when they go out of bounds
1437 void FGInitialCondition::Debug(int from)
1439 if (debug_lvl <= 0) return;
1441 if (debug_lvl & 1) { // Standard console startup message output
1443 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1444 if (from == 0) cout << "Instantiated: FGInitialCondition" << endl;
1445 if (from == 1) cout << "Destroyed: FGInitialCondition" << endl;
1447 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1449 if (debug_lvl & 8 ) { // Runtime state variables
1451 if (debug_lvl & 16) { // Sanity checking
1453 if (debug_lvl & 64) {
1454 if (from == 0) { // Constructor
1455 cout << IdSrc << endl;
1456 cout << IdHdr << endl;