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 = "$Header$";
59 static const char *IdHdr = ID_INITIALCONDITION;
62 FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec) {
72 sea_level_radius = EARTHRAD;
73 radius_to_vehicle = EARTHRAD;
75 if(FDMExec != NULL ) {
77 fdmex->GetPosition()->Seth(altitude);
78 fdmex->GetAtmosphere()->Run();
80 cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl;
86 FGInitialCondition::~FGInitialCondition(void) {}
89 void FGInitialCondition::SetVcalibratedKtsIC(float tt) {
91 if(getMachFromVcas(&mach,tt*jsbKTSTOFPS)) {
92 //cout << "Mach: " << mach << endl;
95 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
96 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
97 //cout << "Vt: " << vt*jsbFPSTOKTS << " Vc: " << vc*jsbFPSTOKTS << endl;
100 cout << "Failed to get Mach number for given Vc and altitude, Vc unchanged." << endl;
101 cout << "Please mail the set of initial conditions used to apeden@earthlink.net" << endl;
105 void FGInitialCondition::SetVequivalentKtsIC(float tt) {
108 vt=ve*1/sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
109 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
113 void FGInitialCondition::SetVtrueKtsIC(float tt) {
116 mach=vt/fdmex->GetAtmosphere()->GetSoundSpeed();
118 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
121 void FGInitialCondition::SetMachIC(float tt) {
123 lastSpeedSet=setmach;
124 vt=mach*fdmex->GetAtmosphere()->GetSoundSpeed();
126 ve=vt*sqrt(fdmex->GetAtmosphere()->GetDensityRatio());
127 //cout << "Vt: " << vt*jsbFPSTOKTS << " Vc: " << vc*jsbFPSTOKTS << endl;
130 void FGInitialCondition::SetClimbRateFpmIC(float tt) {
131 SetClimbRateFpsIC(tt/60.0);
134 void FGInitialCondition::SetClimbRateFpsIC(float tt) {
142 void FGInitialCondition::SetFlightPathAngleRadIC(float tt) {
149 void FGInitialCondition::SetUBodyFpsIC(float tt) {
151 vt=sqrt(u*u+v*v+w*w);
156 void FGInitialCondition::SetVBodyFpsIC(float tt) {
158 vt=sqrt(u*u+v*v+w*w);
162 void FGInitialCondition::SetWBodyFpsIC(float tt) {
164 vt=sqrt(u*u+v*v+w*w);
169 void FGInitialCondition::SetAltitudeFtIC(float tt) {
171 fdmex->GetPosition()->Seth(altitude);
172 fdmex->GetAtmosphere()->Run();
174 //lets try to make sure the user gets what they intended
176 switch(lastSpeedSet) {
180 SetVtrueKtsIC(vt*jsbFPSTOKTS);
183 SetVcalibratedKtsIC(vc*jsbFPSTOKTS);
186 SetVequivalentKtsIC(ve*jsbFPSTOKTS);
194 void FGInitialCondition::SetAltitudeAGLFtIC(float tt) {
195 fdmex->GetPosition()->SetDistanceAGL(tt);
196 altitude=fdmex->GetPosition()->Geth();
197 SetAltitudeFtIC(altitude);
199 void FGInitialCondition::SetSeaLevelRadiusFtIC(double tt) {
200 sea_level_radius = tt;
203 void FGInitialCondition::SetTerrainAltitudeFtIC(double tt) {
205 fdmex->GetPosition()->SetDistanceAGL(altitude-terrain_altitude);
206 fdmex->GetPosition()->SetRunwayRadius(sea_level_radius + terrain_altitude);
209 void FGInitialCondition::calcUVWfromNED(void) {
210 u=vnorth*cos(theta)*cos(psi) +
211 veast*cos(theta)*sin(psi) -
213 v=vnorth*(sin(phi)*sin(theta)*cos(psi) - cos(phi)*sin(psi)) +
214 veast*(sin(phi)*sin(theta)*sin(psi) + cos(phi)*cos(psi)) +
215 vdown*sin(phi)*cos(theta);
216 w=vnorth*(cos(phi)*sin(theta)*cos(psi) + sin(phi)*sin(psi)) +
217 veast*(cos(phi)*sin(theta)*sin(psi) - sin(phi)*cos(psi)) +
218 vdown*cos(phi)*cos(theta);
221 void FGInitialCondition::SetVnorthFpsIC(float tt) {
224 vt=sqrt(u*u + v*v + w*w);
228 void FGInitialCondition::SetVeastFpsIC(float tt) {
231 vt=sqrt(u*u + v*v + w*w);
235 void FGInitialCondition::SetVdownFpsIC(float tt) {
238 vt=sqrt(u*u + v*v + w*w);
239 SetClimbRateFpsIC(-1*vdown);
243 bool FGInitialCondition::getMachFromVcas(float *Mach,float vcas) {
249 sfunc=&FGInitialCondition::calcVcas;
250 if(findInterval(vcas,guess)) {
251 if(solve(&mach,vcas))
257 bool FGInitialCondition::getAlpha(void) {
259 float guess=theta-gamma;
261 xmin=fdmex->GetAircraft()->GetAlphaCLMin();
262 xmax=fdmex->GetAircraft()->GetAlphaCLMax();
263 sfunc=&FGInitialCondition::GammaEqOfAlpha;
264 if(findInterval(0,guess)){
272 bool FGInitialCondition::getTheta(void) {
274 float guess=alpha+gamma;
277 sfunc=&FGInitialCondition::GammaEqOfTheta;
278 if(findInterval(0,guess)){
288 float FGInitialCondition::GammaEqOfTheta(float Theta) {
291 a=cos(alpha)*cos(beta)*sin(Theta);
292 b=sin(beta)*sin(phi);
293 c=sin(alpha)*cos(beta)*cos(phi);
294 return sin(gamma)-a+(b+c)*cos(Theta);
297 float FGInitialCondition::GammaEqOfAlpha(float Alpha) {
300 a=cos(Alpha)*cos(beta)*sin(theta);
301 b=sin(beta)*sin(phi);
302 c=sin(Alpha)*cos(beta)*cos(phi);
303 return sin(gamma)-a+(b+c)*cos(theta);
307 float FGInitialCondition::calcVcas(float Mach) {
309 float p=fdmex->GetAtmosphere()->GetPressure();
310 float psl=fdmex->GetAtmosphere()->GetPressureSL();
311 float rhosl=fdmex->GetAtmosphere()->GetDensitySL();
314 if(Mach < 1) //calculate total pressure assuming isentropic flow
315 pt=p*pow((1 + 0.2*Mach*Mach),3.5);
317 // shock in front of pitot tube, we'll assume its normal and use
318 // the Rayleigh Pitot Tube Formula, i.e. the ratio of total
319 // pressure behind the shock to the static pressure in front
322 //the normal shock assumption should not be a bad one -- most supersonic
323 //aircraft place the pitot probe out front so that it is the forward
324 //most point on the aircraft. The real shock would, of course, take
325 //on something like the shape of a rounded-off cone but, here again,
326 //the assumption should be good since the opening of the pitot probe
327 //is very small and, therefore, the effects of the shock curvature
328 //should be small as well. AFAIK, this approach is fairly well accepted
329 //within the aerospace community
331 B = 5.76*Mach*Mach/(5.6*Mach*Mach - 0.8);
333 // The denominator above is zero for Mach ~ 0.38, for which
334 // we'll never be here, so we're safe
336 D = (2.8*Mach*Mach-0.4)*0.4167;
340 A = pow(((pt-p)/psl+1),0.28571);
341 vcas = sqrt(7*psl/rhosl*(A-1));
342 //cout << "calcVcas: vcas= " << vcas*jsbFPSTOKTS << " mach= " << Mach << " pressure: " << pt << endl;
346 bool FGInitialCondition::findInterval(float x,float guess) {
347 //void find_interval(inter_params &ip,eqfunc f,float y,float constant, int &flag){
351 float flo,fhi,fguess;
354 fguess=(this->*sfunc)(guess)-x;
360 if(lo < xmin) lo=xmin;
361 if(hi > xmax) hi=xmax;
363 flo=(this->*sfunc)(lo)-x;
364 fhi=(this->*sfunc)(hi)-x;
365 if(flo*fhi <=0) { //found interval with root
367 if(flo*fguess <= 0) { //narrow interval down a bit
368 hi=lo+step; //to pass solver interval that is as
371 else if(fhi*fguess <= 0) {
375 //cout << "findInterval: i=" << i << " Lo= " << lo << " Hi= " << hi << endl;
377 while((found == 0) && (i <= 100));
386 bool FGInitialCondition::solve(float *y,float x) {
387 float x1,x2,x3,f1,f2,f3,d,d0;
389 float const relax =0.9;
397 f1=(this->*sfunc)(x1)-x;
398 f3=(this->*sfunc)(x3)-x;
403 while ((fabs(d) > eps) && (i < 100)) {
405 x2=x1-d*d0*f1/(f3-f1);
407 f2=(this->*sfunc)(x2)-x;
408 //cout << "solve x1,x2,x3: " << x1 << "," << x2 << "," << x3 << endl;
409 //cout << " " << f1 << "," << f2 << "," << f3 << endl;
411 if(fabs(f2) <= 0.001) {
413 } else if(f1*f2 <= 0.0) {
417 } else if(f2*f3 <= 0) {
430 //cout << "Success= " << success << " Vcas: " << vcas*jsbFPSTOKTS << " Mach: " << x2 << endl;