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"
58 static const char *IdSrc = "$Id$";
59 static const char *IdHdr = ID_INITIALCONDITION;
61 extern short debug_lvl;
63 //******************************************************************************
65 FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec){
77 sea_level_radius = EARTHRAD;
78 radius_to_vehicle = EARTHRAD;
81 salpha=sbeta=stheta=sphi=spsi=sgamma=0;
82 calpha=cbeta=ctheta=cphi=cpsi=cgamma=1;
84 if(FDMExec != NULL ) {
86 fdmex->GetPosition()->Seth(altitude);
87 fdmex->GetAtmosphere()->Run();
89 cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl;
92 if (debug_lvl & 2) cout << "Instantiated: FGInitialCondition" << endl;
95 //******************************************************************************
97 FGInitialCondition::~FGInitialCondition()
99 if (debug_lvl & 2) cout << "Destroyed: FGInitialCondition" << endl;
102 //******************************************************************************
104 void FGInitialCondition::SetVcalibratedKtsIC(float tt) {
106 if(getMachFromVcas(&mach,tt*jsbKTSTOFPS)) {
107 //cout << "Mach: " << mach << endl;
110 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
111 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
112 //cout << "Vt: " << vt*jsbFPSTOKTS << " Vc: " << vc*jsbFPSTOKTS << endl;
115 cout << "Failed to get Mach number for given Vc and altitude, Vc unchanged." << endl;
116 cout << "Please mail the set of initial conditions used to apeden@earthlink.net" << endl;
120 //******************************************************************************
122 void FGInitialCondition::SetVequivalentKtsIC(float tt) {
125 vt=ve*1/sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
126 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
130 //******************************************************************************
132 void FGInitialCondition::SetVgroundFpsIC(float tt) {
136 //cout << "FGInitialCondition::SetVgroundFpsIC" << endl;
139 vnorth = vg*cos(psi); veast = vg*sin(psi); vdown = 0;
141 //cout << "\tu,v,w: " << u << ", " << v << ", " << w << endl;
143 //cout << "\tuw,vw,ww: " << uw << ", " << vw << ", " << ww << endl;
144 u = -uw; v = -vw; w = -ww;
145 //ua = u - uw; va = v - vw; wa = w - ww;
146 //cout << "\tua,va,wa: " << ua << ", " << va << ", " << wa << endl;
147 vt = sqrt( u*u + v*v + w*w );
149 vxz = sqrt( u*u + w*w );
150 if( w != 0 ) alpha = atan2( w, u );
151 if( vxz != 0 ) beta = atan2( v, vxz );
152 //cout << "\tvt,alpha,beta: " << vt << ", " << alpha*RADTODEG << ", "
153 // << beta*RADTODEG << endl;
154 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
156 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
159 //******************************************************************************
161 void FGInitialCondition::SetVtrueFpsIC(float tt) {
164 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
166 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
169 //******************************************************************************
171 void FGInitialCondition::SetMachIC(float tt) {
173 lastSpeedSet=setmach;
174 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
176 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
177 //cout << "Vt: " << vt*jsbFPSTOKTS << " Vc: " << vc*jsbFPSTOKTS << endl;
180 //******************************************************************************
182 void FGInitialCondition::SetClimbRateFpmIC(float tt) {
183 SetClimbRateFpsIC(tt/60.0);
186 //******************************************************************************
188 void FGInitialCondition::SetClimbRateFpsIC(float tt) {
193 sgamma=sin(gamma); cgamma=cos(gamma);
197 //******************************************************************************
199 void FGInitialCondition::SetFlightPathAngleRadIC(float tt) {
201 sgamma=sin(gamma); cgamma=cos(gamma);
206 //******************************************************************************
208 void FGInitialCondition::SetAlphaRadIC(float tt) {
210 salpha=sin(alpha); calpha=cos(alpha);
214 //******************************************************************************
216 void FGInitialCondition::SetPitchAngleRadIC(float tt) {
218 stheta=sin(theta); ctheta=cos(theta);
223 //******************************************************************************
225 void FGInitialCondition::SetBetaRadIC(float tt) {
227 sbeta=sin(beta); cbeta=cos(beta);
231 //******************************************************************************
233 void FGInitialCondition::SetRollAngleRadIC(float tt) {
235 sphi=sin(phi); cphi=cos(phi);
239 //******************************************************************************
241 void FGInitialCondition::SetTrueHeadingRadIC(float tt) {
243 spsi=sin(psi); cpsi=cos(psi);
247 //******************************************************************************
249 void FGInitialCondition::SetUBodyFpsIC(float tt) {
251 vt=sqrt(u*u + v*v + w*w);
255 //******************************************************************************
257 void FGInitialCondition::SetVBodyFpsIC(float tt) {
259 vt=sqrt(u*u + v*v + w*w);
263 //******************************************************************************
265 void FGInitialCondition::SetWBodyFpsIC(float tt) {
267 vt=sqrt( u*u + v*v + w*w );
271 //******************************************************************************
273 float FGInitialCondition::GetUBodyFpsIC(void) {
274 if(lastSpeedSet == setvg )
277 return vt*calpha*cbeta;
280 //******************************************************************************
282 float FGInitialCondition::GetVBodyFpsIC(void) {
283 if( lastSpeedSet == setvg )
289 //******************************************************************************
291 float FGInitialCondition::GetWBodyFpsIC(void) {
292 if( lastSpeedSet == setvg )
295 return vt*salpha*cbeta;
299 //******************************************************************************
301 void FGInitialCondition::SetWindNEDFpsIC(float wN, float wE, float wD ) {
302 wnorth = wN; weast = wE; wdown = wD;
303 if(lastSpeedSet == setvg)
309 //******************************************************************************
311 void FGInitialCondition::calcWindUVW(void) {
312 if(lastSpeedSet == setvg ) {
314 uw=wnorth*ctheta*cpsi +
317 vw=wnorth*( sphi*stheta*cpsi - cphi*spsi ) +
318 weast*( sphi*stheta*spsi + cphi*cpsi ) +
320 ww=wnorth*(cphi*stheta*cpsi + sphi*spsi) +
321 weast*(cphi*stheta*spsi - sphi*cpsi) +
323 /* cout << "FGInitialCondition::calcWindUVW: wnorth, weast, wdown "
324 << wnorth << ", " << weast << ", " << wdown << endl;
325 cout << "FGInitialCondition::calcWindUVW: theta, phi, psi "
326 << theta << ", " << phi << ", " << psi << endl;
327 cout << "FGInitialCondition::calcWindUVW: uw, vw, ww "
328 << uw << ", " << vw << ", " << ww << endl; */
335 //******************************************************************************
337 void FGInitialCondition::SetAltitudeFtIC(float tt) {
339 fdmex->GetPosition()->Seth(altitude);
340 fdmex->GetAtmosphere()->Run();
342 //lets try to make sure the user gets what they intended
344 switch(lastSpeedSet) {
348 SetVtrueKtsIC(vt*jsbFPSTOKTS);
351 SetVcalibratedKtsIC(vc*jsbFPSTOKTS);
354 SetVequivalentKtsIC(ve*jsbFPSTOKTS);
365 //******************************************************************************
367 void FGInitialCondition::SetAltitudeAGLFtIC(float tt) {
368 fdmex->GetPosition()->SetDistanceAGL(tt);
369 altitude=fdmex->GetPosition()->Geth();
370 SetAltitudeFtIC(altitude);
373 //******************************************************************************
375 void FGInitialCondition::SetSeaLevelRadiusFtIC(double tt) {
376 sea_level_radius = tt;
379 //******************************************************************************
381 void FGInitialCondition::SetTerrainAltitudeFtIC(double tt) {
383 fdmex->GetPosition()->SetDistanceAGL(altitude-terrain_altitude);
384 fdmex->GetPosition()->SetRunwayRadius(sea_level_radius + terrain_altitude);
387 //******************************************************************************
389 void FGInitialCondition::calcUVWfromNED(void) {
390 u=vnorth*ctheta*cpsi +
393 v=vnorth*( sphi*stheta*cpsi - cphi*spsi ) +
394 veast*( sphi*stheta*spsi + cphi*cpsi ) +
396 w=vnorth*( cphi*stheta*cpsi + sphi*spsi ) +
397 veast*( cphi*stheta*spsi - sphi*cpsi ) +
401 //******************************************************************************
403 void FGInitialCondition::SetVnorthFpsIC(float tt) {
406 vt=sqrt(u*u + v*v + w*w);
410 //******************************************************************************
412 void FGInitialCondition::SetVeastFpsIC(float tt) {
415 vt=sqrt(u*u + v*v + w*w);
419 //******************************************************************************
421 void FGInitialCondition::SetVdownFpsIC(float tt) {
424 vt=sqrt(u*u + v*v + w*w);
425 SetClimbRateFpsIC(-1*vdown);
429 //******************************************************************************
431 bool FGInitialCondition::getMachFromVcas(float *Mach,float vcas) {
437 sfunc=&FGInitialCondition::calcVcas;
438 if(findInterval(vcas,guess)) {
439 if(solve(&mach,vcas))
445 //******************************************************************************
447 bool FGInitialCondition::getAlpha(void) {
449 float guess=theta-gamma;
451 xmin=fdmex->GetAircraft()->GetAlphaCLMin();
452 xmax=fdmex->GetAircraft()->GetAlphaCLMax();
453 sfunc=&FGInitialCondition::GammaEqOfAlpha;
454 if(findInterval(0,guess)){
464 //******************************************************************************
466 bool FGInitialCondition::getTheta(void) {
468 float guess=alpha+gamma;
471 sfunc=&FGInitialCondition::GammaEqOfTheta;
472 if(findInterval(0,guess)){
482 //******************************************************************************
484 float FGInitialCondition::GammaEqOfTheta(float Theta) {
488 //theta=Theta; stheta=sin(theta); ctheta=cos(theta);
489 sTheta=sin(Theta); cTheta=cos(Theta);
491 a=wdown + vt*calpha*cbeta + uw;
492 b=vt*sphi*sbeta + vw*sphi;
493 c=vt*cphi*salpha*cbeta + ww*cphi;
494 return vt*sgamma - ( a*sTheta - (b+c)*cTheta);
497 //******************************************************************************
499 float FGInitialCondition::GammaEqOfAlpha(float Alpha) {
503 sAlpha=sin(Alpha); cAlpha=cos(Alpha);
504 a=wdown + vt*cAlpha*cbeta + uw;
505 b=vt*sphi*sbeta + vw*sphi;
506 c=vt*cphi*sAlpha*cbeta + ww*cphi;
508 return vt*sgamma - ( a*stheta - (b+c)*ctheta );
511 //******************************************************************************
513 float FGInitialCondition::calcVcas(float Mach) {
515 float p=fdmex->GetAtmosphere()->GetPressure();
516 float psl=fdmex->GetAtmosphere()->GetPressureSL();
517 float rhosl=fdmex->GetAtmosphere()->GetDensitySL();
520 if(Mach < 1) //calculate total pressure assuming isentropic flow
521 pt=p*pow((1 + 0.2*Mach*Mach),3.5);
523 // shock in front of pitot tube, we'll assume its normal and use
524 // the Rayleigh Pitot Tube Formula, i.e. the ratio of total
525 // pressure behind the shock to the static pressure in front
528 //the normal shock assumption should not be a bad one -- most supersonic
529 //aircraft place the pitot probe out front so that it is the forward
530 //most point on the aircraft. The real shock would, of course, take
531 //on something like the shape of a rounded-off cone but, here again,
532 //the assumption should be good since the opening of the pitot probe
533 //is very small and, therefore, the effects of the shock curvature
534 //should be small as well. AFAIK, this approach is fairly well accepted
535 //within the aerospace community
537 B = 5.76*Mach*Mach/(5.6*Mach*Mach - 0.8);
539 // The denominator above is zero for Mach ~ 0.38, for which
540 // we'll never be here, so we're safe
542 D = (2.8*Mach*Mach-0.4)*0.4167;
546 A = pow(((pt-p)/psl+1),0.28571);
547 vcas = sqrt(7*psl/rhosl*(A-1));
548 //cout << "calcVcas: vcas= " << vcas*jsbFPSTOKTS << " mach= " << Mach << " pressure: " << pt << endl;
552 //******************************************************************************
554 bool FGInitialCondition::findInterval(float x,float guess) {
555 //void find_interval(inter_params &ip,eqfunc f,float y,float constant, int &flag){
559 float flo,fhi,fguess;
562 fguess=(this->*sfunc)(guess)-x;
568 if(lo < xmin) lo=xmin;
569 if(hi > xmax) hi=xmax;
571 flo=(this->*sfunc)(lo)-x;
572 fhi=(this->*sfunc)(hi)-x;
573 if(flo*fhi <=0) { //found interval with root
575 if(flo*fguess <= 0) { //narrow interval down a bit
576 hi=lo+step; //to pass solver interval that is as
579 else if(fhi*fguess <= 0) {
583 //cout << "findInterval: i=" << i << " Lo= " << lo << " Hi= " << hi << endl;
585 while((found == 0) && (i <= 100));
591 //******************************************************************************
593 bool FGInitialCondition::solve(float *y,float x) {
594 float x1,x2,x3,f1,f2,f3,d,d0;
596 float const relax =0.9;
604 f1=(this->*sfunc)(x1)-x;
605 f3=(this->*sfunc)(x3)-x;
610 while ((fabs(d) > eps) && (i < 100)) {
612 x2=x1-d*d0*f1/(f3-f1);
614 f2=(this->*sfunc)(x2)-x;
615 //cout << "solve x1,x2,x3: " << x1 << "," << x2 << "," << x3 << endl;
616 //cout << " " << f1 << "," << f2 << "," << f3 << endl;
618 if(fabs(f2) <= 0.001) {
620 } else if(f1*f2 <= 0.0) {
624 } else if(f2*f3 <= 0) {
637 //cout << "Success= " << success << " Vcas: " << vcas*jsbFPSTOKTS << " Mach: " << x2 << endl;
641 //******************************************************************************
643 void FGInitialCondition::Debug(void)
645 //TODO: Add your source code here