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;
97 //******************************************************************************
99 FGInitialCondition::~FGInitialCondition()
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);
372 uw=wnorth*ctheta*cpsi +
375 vw=wnorth*( sphi*stheta*cpsi - cphi*spsi ) +
376 weast*( sphi*stheta*spsi + cphi*cpsi ) +
378 ww=wnorth*(cphi*stheta*cpsi + sphi*spsi) +
379 weast*(cphi*stheta*spsi - sphi*cpsi) +
383 /* cout << "FGInitialCondition::calcWindUVW: wnorth, weast, wdown "
384 << wnorth << ", " << weast << ", " << wdown << endl;
385 cout << "FGInitialCondition::calcWindUVW: theta, phi, psi "
386 << theta << ", " << phi << ", " << psi << endl;
387 cout << "FGInitialCondition::calcWindUVW: uw, vw, ww "
388 << uw << ", " << vw << ", " << ww << endl; */
392 //******************************************************************************
394 void FGInitialCondition::SetAltitudeFtIC(double tt) {
396 fdmex->GetPosition()->Seth(altitude);
397 fdmex->GetAtmosphere()->Run();
398 //lets try to make sure the user gets what they intended
400 switch(lastSpeedSet) {
404 SetVtrueKtsIC(vt*fpstokts);
407 SetVcalibratedKtsIC(vc*fpstokts);
410 SetVequivalentKtsIC(ve*fpstokts);
421 //******************************************************************************
423 void FGInitialCondition::SetAltitudeAGLFtIC(double tt) {
424 fdmex->GetPosition()->SetDistanceAGL(tt);
425 altitude=fdmex->GetPosition()->Geth();
426 SetAltitudeFtIC(altitude);
429 //******************************************************************************
431 void FGInitialCondition::SetSeaLevelRadiusFtIC(double tt) {
432 sea_level_radius = tt;
435 //******************************************************************************
437 void FGInitialCondition::SetTerrainAltitudeFtIC(double tt) {
441 //******************************************************************************
443 void FGInitialCondition::calcUVWfromNED(void) {
444 u=vnorth*ctheta*cpsi +
447 v=vnorth*( sphi*stheta*cpsi - cphi*spsi ) +
448 veast*( sphi*stheta*spsi + cphi*cpsi ) +
450 w=vnorth*( cphi*stheta*cpsi + sphi*spsi ) +
451 veast*( cphi*stheta*spsi - sphi*cpsi ) +
455 //******************************************************************************
457 void FGInitialCondition::SetVnorthFpsIC(double tt) {
460 vt=sqrt(u*u + v*v + w*w);
464 //******************************************************************************
466 void FGInitialCondition::SetVeastFpsIC(double tt) {
469 vt=sqrt(u*u + v*v + w*w);
473 //******************************************************************************
475 void FGInitialCondition::SetVdownFpsIC(double tt) {
478 vt=sqrt(u*u + v*v + w*w);
479 SetClimbRateFpsIC(-1*vdown);
483 //******************************************************************************
485 bool FGInitialCondition::getMachFromVcas(double *Mach,double vcas) {
491 sfunc=&FGInitialCondition::calcVcas;
492 if(findInterval(vcas,guess)) {
493 if(solve(&mach,vcas))
499 //******************************************************************************
501 bool FGInitialCondition::getAlpha(void) {
503 double guess=theta-gamma;
505 if(vt < 0.01) return 0;
508 xmin=fdmex->GetAircraft()->GetAlphaCLMin();
509 xmax=fdmex->GetAircraft()->GetAlphaCLMax();
510 sfunc=&FGInitialCondition::GammaEqOfAlpha;
511 if(findInterval(0,guess)){
522 //******************************************************************************
524 bool FGInitialCondition::getTheta(void) {
526 double guess=alpha+gamma;
528 if(vt < 0.01) return 0;
532 sfunc=&FGInitialCondition::GammaEqOfTheta;
533 if(findInterval(0,guess)){
544 //******************************************************************************
546 double FGInitialCondition::GammaEqOfTheta(double Theta) {
548 double sTheta,cTheta;
550 //theta=Theta; stheta=sin(theta); ctheta=cos(theta);
551 sTheta=sin(Theta); cTheta=cos(Theta);
553 a=wdown + vt*calpha*cbeta + uw;
554 b=vt*sphi*sbeta + vw*sphi;
555 c=vt*cphi*salpha*cbeta + ww*cphi;
556 return vt*sgamma - ( a*sTheta - (b+c)*cTheta);
559 //******************************************************************************
561 double FGInitialCondition::GammaEqOfAlpha(double Alpha) {
563 double sAlpha,cAlpha;
564 sAlpha=sin(Alpha); cAlpha=cos(Alpha);
565 a=wdown + vt*cAlpha*cbeta + uw;
566 b=vt*sphi*sbeta + vw*sphi;
567 c=vt*cphi*sAlpha*cbeta + ww*cphi;
569 return vt*sgamma - ( a*stheta - (b+c)*ctheta );
572 //******************************************************************************
574 double FGInitialCondition::calcVcas(double Mach) {
576 double p=fdmex->GetAtmosphere()->GetPressure();
577 double psl=fdmex->GetAtmosphere()->GetPressureSL();
578 double rhosl=fdmex->GetAtmosphere()->GetDensitySL();
579 double pt,A,B,D,vcas;
581 if(Mach < 1) //calculate total pressure assuming isentropic flow
582 pt=p*pow((1 + 0.2*Mach*Mach),3.5);
584 // shock in front of pitot tube, we'll assume its normal and use
585 // the Rayleigh Pitot Tube Formula, i.e. the ratio of total
586 // pressure behind the shock to the static pressure in front
589 //the normal shock assumption should not be a bad one -- most supersonic
590 //aircraft place the pitot probe out front so that it is the forward
591 //most point on the aircraft. The real shock would, of course, take
592 //on something like the shape of a rounded-off cone but, here again,
593 //the assumption should be good since the opening of the pitot probe
594 //is very small and, therefore, the effects of the shock curvature
595 //should be small as well. AFAIK, this approach is fairly well accepted
596 //within the aerospace community
598 B = 5.76*Mach*Mach/(5.6*Mach*Mach - 0.8);
600 // The denominator above is zero for Mach ~ 0.38, for which
601 // we'll never be here, so we're safe
603 D = (2.8*Mach*Mach-0.4)*0.4167;
607 A = pow(((pt-p)/psl+1),0.28571);
608 vcas = sqrt(7*psl/rhosl*(A-1));
609 //cout << "calcVcas: vcas= " << vcas*fpstokts << " mach= " << Mach << " pressure: " << pt << endl;
613 //******************************************************************************
615 bool FGInitialCondition::findInterval(double x,double guess) {
616 //void find_interval(inter_params &ip,eqfunc f,double y,double constant, int &flag){
620 double flo,fhi,fguess;
623 fguess=(this->*sfunc)(guess)-x;
629 if(lo < xmin) lo=xmin;
630 if(hi > xmax) hi=xmax;
632 flo=(this->*sfunc)(lo)-x;
633 fhi=(this->*sfunc)(hi)-x;
634 if(flo*fhi <=0) { //found interval with root
636 if(flo*fguess <= 0) { //narrow interval down a bit
637 hi=lo+step; //to pass solver interval that is as
640 else if(fhi*fguess <= 0) {
644 //cout << "findInterval: i=" << i << " Lo= " << lo << " Hi= " << hi << endl;
646 while((found == 0) && (i <= 100));
652 //******************************************************************************
654 bool FGInitialCondition::solve(double *y,double x)
656 double x1,x2,x3,f1,f2,f3,d,d0;
658 double const relax =0.9;
666 f1=(this->*sfunc)(x1)-x;
667 f3=(this->*sfunc)(x3)-x;
672 while ((fabs(d) > eps) && (i < 100)) {
674 x2 = x1-d*d0*f1/(f3-f1);
676 f2=(this->*sfunc)(x2)-x;
677 //cout << "solve x1,x2,x3: " << x1 << "," << x2 << "," << x3 << endl;
678 //cout << " " << f1 << "," << f2 << "," << f3 << endl;
680 if(fabs(f2) <= 0.001) {
682 } else if(f1*f2 <= 0.0) {
686 } else if(f2*f3 <= 0) {
699 //cout << "Success= " << success << " Vcas: " << vcas*fpstokts << " Mach: " << x2 << endl;
703 //******************************************************************************
705 double FGInitialCondition::GetWindDirDegIC(void) {
707 return atan2(weast,wnorth)*radtodeg;
714 //******************************************************************************
716 bool FGInitialCondition::Load(string acpath, string acname, string rstfile)
724 resetDef = acpath + "/" + acname + "/" + rstfile + ".xml";
726 resetDef = acpath + ";" + acname + ";" + rstfile + ".xml";
729 FGConfigFile resetfile(resetDef);
730 if (!resetfile.IsOpen()) {
731 cerr << "Failed to open reset file: " << resetDef << endl;
735 resetfile.GetNextConfigLine();
736 token = resetfile.GetValue();
737 if (token != string("initialize")) {
738 cerr << "The reset file " << resetDef
739 << " does not appear to be a reset file" << endl;
743 resetfile.GetNextConfigLine();
745 while (token != string("/initialize") && token != string("EOF")) {
746 if (token == "UBODY" ) { resetfile >> temp; SetUBodyFpsIC(temp); }
747 if (token == "VBODY" ) { resetfile >> temp; SetVBodyFpsIC(temp); }
748 if (token == "WBODY" ) { resetfile >> temp; SetWBodyFpsIC(temp); }
749 if (token == "LATITUDE" ) { resetfile >> temp; SetLatitudeDegIC(temp); }
750 if (token == "LONGITUDE" ) { resetfile >> temp; SetLongitudeDegIC(temp); }
751 if (token == "PHI" ) { resetfile >> temp; SetRollAngleDegIC(temp); }
752 if (token == "THETA" ) { resetfile >> temp; SetPitchAngleDegIC(temp); }
753 if (token == "PSI" ) { resetfile >> temp; SetTrueHeadingDegIC(temp); }
754 if (token == "ALPHA" ) { resetfile >> temp; SetAlphaDegIC(temp); }
755 if (token == "BETA" ) { resetfile >> temp; SetBetaDegIC(temp); }
756 if (token == "GAMMA" ) { resetfile >> temp; SetFlightPathAngleDegIC(temp); }
757 if (token == "ROC" ) { resetfile >> temp; SetClimbRateFpmIC(temp); }
758 if (token == "ALTITUDE" ) { resetfile >> temp; SetAltitudeFtIC(temp); }
759 if (token == "WINDDIR" ) { resetfile >> temp; SetWindDirDegIC(temp); }
760 if (token == "VWIND" ) { resetfile >> temp; SetWindMagKtsIC(temp); }
761 if (token == "HWIND" ) { resetfile >> temp; SetHeadWindKtsIC(temp); }
762 if (token == "XWIND" ) { resetfile >> temp; SetCrossWindKtsIC(temp); }
763 if (token == "VC" ) { resetfile >> temp; SetVcalibratedKtsIC(temp); }
764 if (token == "MACH" ) { resetfile >> temp; SetMachIC(temp); }
765 if (token == "VGROUND" ) { resetfile >> temp; SetVgroundKtsIC(temp); }
774 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
775 // The bitmasked value choices are as follows:
776 // unset: In this case (the default) JSBSim would only print
777 // out the normally expected messages, essentially echoing
778 // the config files as they are read. If the environment
779 // variable is not set, debug_lvl is set to 1 internally
780 // 0: This requests JSBSim not to output any messages
782 // 1: This value explicity requests the normal JSBSim
784 // 2: This value asks for a message to be printed out when
785 // a class is instantiated
786 // 4: When this value is set, a message is displayed when a
787 // FGModel object executes its Run() method
788 // 8: When this value is set, various runtime state variables
789 // are printed out periodically
790 // 16: When set various parameters are sanity checked and
791 // a message is printed out when they go out of bounds
793 void FGInitialCondition::Debug(int from)
795 if (debug_lvl <= 0) return;
797 if (debug_lvl & 1) { // Standard console startup message output
799 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
800 if (from == 0) cout << "Instantiated: FGInitialCondition" << endl;
801 if (from == 1) cout << "Destroyed: FGInitialCondition" << endl;
803 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
805 if (debug_lvl & 8 ) { // Runtime state variables
807 if (debug_lvl & 16) { // Sanity checking
809 if (debug_lvl & 64) {
810 if (from == 0) { // Constructor
811 cout << IdSrc << endl;
812 cout << IdHdr << endl;