1 /*******************************************************************************
3 Header: FGInitialCondition.cpp
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 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 General Public License for more
19 You should have received a copy of the GNU 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 General Public License can also be found on
24 the world wide web at http://www.gnu.org.
28 --------------------------------------------------------------------------------
32 FUNCTIONAL DESCRIPTION
33 --------------------------------------------------------------------------------
35 The purpose of this class is to take a set of initial conditions and provide
36 a kinematically consistent set of body axis velocity components, euler
37 angles, and altitude. This class does not attempt to trim the model i.e.
38 the sim will most likely start in a very dynamic state (unless, of course,
39 you have chosen your IC's wisely) even after setting it up with this class.
41 ********************************************************************************
43 *******************************************************************************/
45 #include "FGInitialCondition.h"
46 #include "FGFDMExec.h"
48 #include "FGAtmosphere.h"
50 #include "FGAircraft.h"
51 #include "FGTranslation.h"
52 #include "FGRotation.h"
53 #include "FGPosition.h"
54 #include "FGAuxiliary.h"
56 #include "FGConfigFile.h"
58 static const char *IdSrc = "$Id$";
59 static const char *IdHdr = ID_INITIALCONDITION;
61 //******************************************************************************
63 FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec)
79 sea_level_radius = FDMExec->GetInertial()->RefRadius();
80 radius_to_vehicle = FDMExec->GetInertial()->RefRadius();
83 salpha=sbeta=stheta=sphi=spsi=sgamma=0;
84 calpha=cbeta=ctheta=cphi=cpsi=cgamma=1;
86 if(FDMExec != NULL ) {
88 fdmex->GetPosition()->Seth(altitude);
89 fdmex->GetAtmosphere()->Run();
91 cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl;
94 if (debug_lvl & 2) cout << "Instantiated: FGInitialCondition" << endl;
97 //******************************************************************************
99 FGInitialCondition::~FGInitialCondition()
101 if (debug_lvl & 2) cout << "Destroyed: FGInitialCondition" << endl;
104 //******************************************************************************
106 void FGInitialCondition::SetVcalibratedKtsIC(double tt) {
108 if(getMachFromVcas(&mach,tt*ktstofps)) {
109 //cout << "Mach: " << mach << endl;
112 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
113 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
114 //cout << "Vt: " << vt*fpstokts << " Vc: " << vc*fpstokts << endl;
117 cout << "Failed to get Mach number for given Vc and altitude, Vc unchanged." << endl;
118 cout << "Please mail the set of initial conditions used to apeden@earthlink.net" << endl;
122 //******************************************************************************
124 void FGInitialCondition::SetVequivalentKtsIC(double tt) {
127 vt=ve*1/sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
128 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
132 //******************************************************************************
134 void FGInitialCondition::SetVgroundFpsIC(double tt) {
140 vnorth = vg*cos(psi); veast = vg*sin(psi); vdown = 0;
142 ua = u + uw; va = v + vw; wa = w + ww;
143 vt = sqrt( ua*ua + va*va + wa*wa );
145 vxz = sqrt( u*u + w*w );
146 if( w != 0 ) alpha = atan2( w, u );
147 if( vxz != 0 ) beta = atan2( v, vxz );
148 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
150 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
153 //******************************************************************************
155 void FGInitialCondition::SetVtrueFpsIC(double tt) {
158 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
160 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
163 //******************************************************************************
165 void FGInitialCondition::SetMachIC(double tt) {
167 lastSpeedSet=setmach;
168 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
170 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
171 //cout << "Vt: " << vt*fpstokts << " Vc: " << vc*fpstokts << endl;
174 //******************************************************************************
176 void FGInitialCondition::SetClimbRateFpmIC(double tt) {
177 SetClimbRateFpsIC(tt/60.0);
180 //******************************************************************************
182 void FGInitialCondition::SetClimbRateFpsIC(double tt) {
187 sgamma=sin(gamma); cgamma=cos(gamma);
191 //******************************************************************************
193 void FGInitialCondition::SetFlightPathAngleRadIC(double tt) {
195 sgamma=sin(gamma); cgamma=cos(gamma);
200 //******************************************************************************
202 void FGInitialCondition::SetAlphaRadIC(double tt) {
204 salpha=sin(alpha); calpha=cos(alpha);
208 //******************************************************************************
210 void FGInitialCondition::SetPitchAngleRadIC(double tt) {
212 stheta=sin(theta); ctheta=cos(theta);
216 //******************************************************************************
218 void FGInitialCondition::SetBetaRadIC(double tt) {
220 sbeta=sin(beta); cbeta=cos(beta);
225 //******************************************************************************
227 void FGInitialCondition::SetRollAngleRadIC(double tt) {
229 sphi=sin(phi); cphi=cos(phi);
233 //******************************************************************************
235 void FGInitialCondition::SetTrueHeadingRadIC(double tt) {
237 spsi=sin(psi); cpsi=cos(psi);
241 //******************************************************************************
243 void FGInitialCondition::SetUBodyFpsIC(double tt) {
245 vt=sqrt(u*u + v*v + w*w);
249 //******************************************************************************
251 void FGInitialCondition::SetVBodyFpsIC(double tt) {
253 vt=sqrt(u*u + v*v + w*w);
257 //******************************************************************************
259 void FGInitialCondition::SetWBodyFpsIC(double tt) {
261 vt=sqrt( u*u + v*v + w*w );
265 //******************************************************************************
267 double FGInitialCondition::GetUBodyFpsIC(void) {
268 if(lastSpeedSet == setvg )
271 return vt*calpha*cbeta - uw;
274 //******************************************************************************
276 double FGInitialCondition::GetVBodyFpsIC(void) {
277 if( lastSpeedSet == setvg )
280 return vt*sbeta - vw;
284 //******************************************************************************
286 double FGInitialCondition::GetWBodyFpsIC(void) {
287 if( lastSpeedSet == setvg )
290 return vt*salpha*cbeta -ww;
293 //******************************************************************************
295 void FGInitialCondition::SetWindNEDFpsIC(double wN, double wE, double wD ) {
296 wnorth = wN; weast = wE; wdown = wD;
297 lastWindSet = setwned;
299 if(lastSpeedSet == setvg)
303 //******************************************************************************
305 // positive from left
306 void FGInitialCondition::SetHeadWindKtsIC(double head){
310 if(lastSpeedSet == setvg)
315 //******************************************************************************
317 void FGInitialCondition::SetCrossWindKtsIC(double cross){
318 wcross=cross*ktstofps;
321 if(lastSpeedSet == setvg)
326 //******************************************************************************
328 void FGInitialCondition::SetWindDownKtsIC(double wD) {
331 if(lastSpeedSet == setvg)
335 //******************************************************************************
337 void FGInitialCondition::SetWindMagKtsIC(double mag) {
341 if(lastSpeedSet == setvg)
345 //******************************************************************************
347 void FGInitialCondition::SetWindDirDegIC(double dir) {
351 if(lastSpeedSet == setvg)
356 //******************************************************************************
358 void FGInitialCondition::calcWindUVW(void) {
360 switch(lastWindSet) {
362 wnorth=wmag*cos(wdir);
363 weast=wmag*sin(wdir);
366 wnorth=whead*cos(psi) + wcross*cos(psi+M_PI/2);
367 weast=whead*sin(psi) + wcross*sin(psi+M_PI/2);
370 uw=wnorth*ctheta*cpsi +
373 vw=wnorth*( sphi*stheta*cpsi - cphi*spsi ) +
374 weast*( sphi*stheta*spsi + cphi*cpsi ) +
376 ww=wnorth*(cphi*stheta*cpsi + sphi*spsi) +
377 weast*(cphi*stheta*spsi - sphi*cpsi) +
381 /* cout << "FGInitialCondition::calcWindUVW: wnorth, weast, wdown "
382 << wnorth << ", " << weast << ", " << wdown << endl;
383 cout << "FGInitialCondition::calcWindUVW: theta, phi, psi "
384 << theta << ", " << phi << ", " << psi << endl;
385 cout << "FGInitialCondition::calcWindUVW: uw, vw, ww "
386 << uw << ", " << vw << ", " << ww << endl; */
390 //******************************************************************************
392 void FGInitialCondition::SetAltitudeFtIC(double tt) {
394 fdmex->GetPosition()->Seth(altitude);
395 fdmex->GetAtmosphere()->Run();
396 //lets try to make sure the user gets what they intended
398 switch(lastSpeedSet) {
402 SetVtrueKtsIC(vt*fpstokts);
405 SetVcalibratedKtsIC(vc*fpstokts);
408 SetVequivalentKtsIC(ve*fpstokts);
419 //******************************************************************************
421 void FGInitialCondition::SetAltitudeAGLFtIC(double tt) {
422 fdmex->GetPosition()->SetDistanceAGL(tt);
423 altitude=fdmex->GetPosition()->Geth();
424 SetAltitudeFtIC(altitude);
427 //******************************************************************************
429 void FGInitialCondition::SetSeaLevelRadiusFtIC(double tt) {
430 sea_level_radius = tt;
433 //******************************************************************************
435 void FGInitialCondition::SetTerrainAltitudeFtIC(double tt) {
439 //******************************************************************************
441 void FGInitialCondition::calcUVWfromNED(void) {
442 u=vnorth*ctheta*cpsi +
445 v=vnorth*( sphi*stheta*cpsi - cphi*spsi ) +
446 veast*( sphi*stheta*spsi + cphi*cpsi ) +
448 w=vnorth*( cphi*stheta*cpsi + sphi*spsi ) +
449 veast*( cphi*stheta*spsi - sphi*cpsi ) +
453 //******************************************************************************
455 void FGInitialCondition::SetVnorthFpsIC(double tt) {
458 vt=sqrt(u*u + v*v + w*w);
462 //******************************************************************************
464 void FGInitialCondition::SetVeastFpsIC(double tt) {
467 vt=sqrt(u*u + v*v + w*w);
471 //******************************************************************************
473 void FGInitialCondition::SetVdownFpsIC(double tt) {
476 vt=sqrt(u*u + v*v + w*w);
477 SetClimbRateFpsIC(-1*vdown);
481 //******************************************************************************
483 bool FGInitialCondition::getMachFromVcas(double *Mach,double vcas) {
489 sfunc=&FGInitialCondition::calcVcas;
490 if(findInterval(vcas,guess)) {
491 if(solve(&mach,vcas))
497 //******************************************************************************
499 bool FGInitialCondition::getAlpha(void) {
501 double guess=theta-gamma;
503 xmin=fdmex->GetAircraft()->GetAlphaCLMin();
504 xmax=fdmex->GetAircraft()->GetAlphaCLMax();
505 sfunc=&FGInitialCondition::GammaEqOfAlpha;
506 if(findInterval(0,guess)){
517 //******************************************************************************
519 bool FGInitialCondition::getTheta(void) {
521 double guess=alpha+gamma;
524 sfunc=&FGInitialCondition::GammaEqOfTheta;
525 if(findInterval(0,guess)){
536 //******************************************************************************
538 double FGInitialCondition::GammaEqOfTheta(double Theta) {
540 double sTheta,cTheta;
542 //theta=Theta; stheta=sin(theta); ctheta=cos(theta);
543 sTheta=sin(Theta); cTheta=cos(Theta);
545 a=wdown + vt*calpha*cbeta + uw;
546 b=vt*sphi*sbeta + vw*sphi;
547 c=vt*cphi*salpha*cbeta + ww*cphi;
548 return vt*sgamma - ( a*sTheta - (b+c)*cTheta);
551 //******************************************************************************
553 double FGInitialCondition::GammaEqOfAlpha(double Alpha) {
555 double sAlpha,cAlpha;
557 sAlpha=sin(Alpha); cAlpha=cos(Alpha);
558 a=wdown + vt*cAlpha*cbeta + uw;
559 b=vt*sphi*sbeta + vw*sphi;
560 c=vt*cphi*sAlpha*cbeta + ww*cphi;
562 return vt*sgamma - ( a*stheta - (b+c)*ctheta );
565 //******************************************************************************
567 double FGInitialCondition::calcVcas(double Mach) {
569 double p=fdmex->GetAtmosphere()->GetPressure();
570 double psl=fdmex->GetAtmosphere()->GetPressureSL();
571 double rhosl=fdmex->GetAtmosphere()->GetDensitySL();
572 double pt,A,B,D,vcas;
574 if(Mach < 1) //calculate total pressure assuming isentropic flow
575 pt=p*pow((1 + 0.2*Mach*Mach),3.5);
577 // shock in front of pitot tube, we'll assume its normal and use
578 // the Rayleigh Pitot Tube Formula, i.e. the ratio of total
579 // pressure behind the shock to the static pressure in front
582 //the normal shock assumption should not be a bad one -- most supersonic
583 //aircraft place the pitot probe out front so that it is the forward
584 //most point on the aircraft. The real shock would, of course, take
585 //on something like the shape of a rounded-off cone but, here again,
586 //the assumption should be good since the opening of the pitot probe
587 //is very small and, therefore, the effects of the shock curvature
588 //should be small as well. AFAIK, this approach is fairly well accepted
589 //within the aerospace community
591 B = 5.76*Mach*Mach/(5.6*Mach*Mach - 0.8);
593 // The denominator above is zero for Mach ~ 0.38, for which
594 // we'll never be here, so we're safe
596 D = (2.8*Mach*Mach-0.4)*0.4167;
600 A = pow(((pt-p)/psl+1),0.28571);
601 vcas = sqrt(7*psl/rhosl*(A-1));
602 //cout << "calcVcas: vcas= " << vcas*fpstokts << " mach= " << Mach << " pressure: " << pt << endl;
606 //******************************************************************************
608 bool FGInitialCondition::findInterval(double x,double guess) {
609 //void find_interval(inter_params &ip,eqfunc f,double y,double constant, int &flag){
613 double flo,fhi,fguess;
616 fguess=(this->*sfunc)(guess)-x;
622 if(lo < xmin) lo=xmin;
623 if(hi > xmax) hi=xmax;
625 flo=(this->*sfunc)(lo)-x;
626 fhi=(this->*sfunc)(hi)-x;
627 if(flo*fhi <=0) { //found interval with root
629 if(flo*fguess <= 0) { //narrow interval down a bit
630 hi=lo+step; //to pass solver interval that is as
633 else if(fhi*fguess <= 0) {
637 //cout << "findInterval: i=" << i << " Lo= " << lo << " Hi= " << hi << endl;
639 while((found == 0) && (i <= 100));
645 //******************************************************************************
647 bool FGInitialCondition::solve(double *y,double x)
649 double x1,x2,x3,f1,f2,f3,d,d0;
651 double const relax =0.9;
659 f1=(this->*sfunc)(x1)-x;
660 f3=(this->*sfunc)(x3)-x;
665 while ((fabs(d) > eps) && (i < 100)) {
667 if (f3-f1 != 0.0) x2 = x1-d*d0*f1/(f3-f1);
668 else x2 = x1-d*d0*f1/0.01; // TONY: What is correct? This is a WAG.
670 f2=(this->*sfunc)(x2)-x;
671 //cout << "solve x1,x2,x3: " << x1 << "," << x2 << "," << x3 << endl;
672 //cout << " " << f1 << "," << f2 << "," << f3 << endl;
674 if(fabs(f2) <= 0.001) {
676 } else if(f1*f2 <= 0.0) {
680 } else if(f2*f3 <= 0) {
693 //cout << "Success= " << success << " Vcas: " << vcas*fpstokts << " Mach: " << x2 << endl;
697 //******************************************************************************
699 double FGInitialCondition::GetWindDirDegIC(void) {
701 return atan2(weast,wnorth)*radtodeg;
708 //******************************************************************************
710 bool FGInitialCondition::Load(string acpath, string acname, string rstfile)
718 resetDef = acpath + "/" + acname + "/" + rstfile + ".xml";
720 resetDef = acpath + ";" + acname + ";" + rstfile + ".xml";
723 FGConfigFile resetfile(resetDef);
724 if (!resetfile.IsOpen()) return false;
726 resetfile.GetNextConfigLine();
727 token = resetfile.GetValue();
728 if (token != string("initialize")) {
729 cerr << "The reset file " << resetDef
730 << " does not appear to be a reset file" << endl;
734 resetfile.GetNextConfigLine();
736 while (token != string("/initialize") && token != string("EOF")) {
737 if (token == "UBODY" ) { resetfile >> temp; SetUBodyFpsIC(temp); }
738 if (token == "VBODY" ) { resetfile >> temp; SetVBodyFpsIC(temp); }
739 if (token == "WBODY" ) { resetfile >> temp; SetWBodyFpsIC(temp); }
740 if (token == "LATITUDE" ) { resetfile >> temp; SetLatitudeDegIC(temp); }
741 if (token == "LONGITUDE" ) { resetfile >> temp; SetLongitudeDegIC(temp); }
742 if (token == "PHI" ) { resetfile >> temp; SetRollAngleDegIC(temp); }
743 if (token == "THETA" ) { resetfile >> temp; SetPitchAngleDegIC(temp); }
744 if (token == "PSI" ) { resetfile >> temp; SetTrueHeadingDegIC(temp); }
745 if (token == "ALPHA" ) { resetfile >> temp; SetAlphaDegIC(temp); }
746 if (token == "BETA" ) { resetfile >> temp; SetBetaDegIC(temp); }
747 if (token == "GAMMA" ) { resetfile >> temp; SetFlightPathAngleDegIC(temp); }
748 if (token == "ROC" ) { resetfile >> temp; SetClimbRateFpmIC(temp); }
749 if (token == "ALTITUDE" ) { resetfile >> temp; SetAltitudeFtIC(temp); }
750 if (token == "WINDDIR" ) { resetfile >> temp; SetWindDirDegIC(temp); }
751 if (token == "VWIND" ) { resetfile >> temp; SetWindMagKtsIC(temp); }
752 if (token == "HWIND" ) { resetfile >> temp; SetHeadWindKtsIC(temp); }
753 if (token == "XWIND" ) { resetfile >> temp; SetCrossWindKtsIC(temp); }
754 if (token == "VC" ) { resetfile >> temp; SetVcalibratedKtsIC(temp); }
755 if (token == "MACH" ) { resetfile >> temp; SetMachIC(temp); }
756 if (token == "VGROUND" ) { resetfile >> temp; SetVgroundKtsIC(temp); }