39 #include "FGAerodynamics.h"
40 #include "input_output/FGXMLElement.h"
53 Name =
"FGAerodynamics";
63 AxisIdx[
"NORMAL"] = 2;
69 forceAxisType = atNone;
70 momentAxisType = atNone;
72 AeroFunctions =
new AeroFunctionArray[6];
73 AeroFunctionsAtCG =
new AeroFunctionArray[6];
75 impending_stall = stall_hyst = 0.0;
76 alphaclmin = alphaclmax = 0.0;
77 alphaclmin0 = alphaclmax0 = 0.0;
78 alphahystmin = alphahystmax = 0.0;
81 bi2vel = ci2vel = 0.0;
83 vDeltaRP.InitMatrix();
97 for (j=0; j<AeroFunctions[i].size(); j++)
98 delete AeroFunctions[i][j];
100 for (j=0; j<AeroFunctionsAtCG[i].size(); j++)
101 delete AeroFunctionsAtCG[i][j];
103 delete[] AeroFunctions;
104 delete[] AeroFunctionsAtCG;
113 bool FGAerodynamics::InitModel(
void)
115 if (!FGModel::InitModel())
return false;
117 impending_stall = stall_hyst = 0.0;
118 alphaclmin = alphaclmin0;
119 alphaclmax = alphaclmax0;
120 alphahystmin = alphahystmax = 0.0;
123 bi2vel = ci2vel = 0.0;
125 vDeltaRP.InitMatrix();
126 vForces.InitMatrix();
127 vMoments.InitMatrix();
136 if (Holding)
return false;
138 unsigned int axis_ctr;
139 const double twovel=2*in.Vt;
144 if ( in.Qbar > 1.0) {
148 clsq = vFw(eLift) / (in.Wingarea*in.Qbar);
157 bi2vel = in.Wingspan / twovel;
158 ci2vel = in.Wingchord / twovel;
160 alphaw = in.Alpha + in.Wingincidence;
161 qbar_area = in.Wingarea * in.Qbar;
163 if (alphaclmax != 0) {
164 if (in.Alpha > 0.85*alphaclmax) {
165 impending_stall = 10*(in.Alpha/alphaclmax - 0.85);
171 if (alphahystmax != 0.0 && alphahystmin != 0.0) {
172 if (in.Alpha > alphahystmax) {
174 }
else if (in.Alpha < alphahystmin) {
180 vFnative.InitMatrix();
181 vFnativeAtCG.InitMatrix();
183 BuildStabilityTransformMatrices();
185 for (axis_ctr = 0; axis_ctr < 3; ++axis_ctr) {
186 AeroFunctionArray::iterator f;
188 AeroFunctionArray* array = &AeroFunctions[axis_ctr];
189 for (f=array->begin(); f != array->end(); ++f) {
194 (*f)->cacheValue(
true);
195 vFnative(axis_ctr+1) += (*f)->GetValue();
198 array = &AeroFunctionsAtCG[axis_ctr];
199 for (f=array->begin(); f != array->end(); ++f) {
200 (*f)->cacheValue(
true);
201 vFnativeAtCG(axis_ctr+1) += (*f)->GetValue();
205 switch (forceAxisType) {
208 vForcesAtCG = vFnativeAtCG;
211 vFnative(eDrag)*=-1; vFnative(eLift)*=-1;
212 vForces = in.Tw2b*vFnative;
214 vFnativeAtCG(eDrag)*=-1; vFnativeAtCG(eLift)*=-1;
215 vForcesAtCG = in.Tw2b*vFnativeAtCG;
217 case atBodyAxialNormal:
218 vFnative(eX)*=-1; vFnative(eZ)*=-1;
221 vFnativeAtCG(eX)*=-1; vFnativeAtCG(eZ)*=-1;
222 vForcesAtCG = vFnativeAtCG;
225 vFnative(eDrag) *= -1; vFnative(eLift) *= -1;
226 vForces = Ts2b*vFnative;
228 vFnativeAtCG(eDrag) *= -1; vFnativeAtCG(eLift) *= -1;
229 vForcesAtCG = Ts2b*vFnativeAtCG;
234 s <<
" A proper axis type has NOT been selected. Check "
235 <<
"your aerodynamics definition.";
236 cerr << endl << s.str() << endl;
245 if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->
GetValue()*in.Wingchord;
247 vDXYZcg(eX) = in.RPBody(eX) - vDeltaRP(eX);
248 vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY);
249 vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ);
251 vMomentsMRC.InitMatrix();
253 for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
254 AeroFunctionArray* array = &AeroFunctions[axis_ctr+3];
255 for (AeroFunctionArray::iterator f=array->begin(); f != array->end(); ++f) {
260 (*f)->cacheValue(
true);
261 vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
267 vMomentsMRCBodyXYZ.InitMatrix();
268 switch (momentAxisType) {
270 vMomentsMRCBodyXYZ = vMomentsMRC;
273 vMomentsMRCBodyXYZ = Ts2b*vMomentsMRC;
276 vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
281 s <<
" A proper axis type has NOT been selected. Check "
282 <<
"your aerodynamics definition.";
283 cerr << endl << s.str() << endl;
288 vMoments = vMomentsMRCBodyXYZ + vDXYZcg*vForces;
292 vForces += vForcesAtCG;
306 vFw = in.Tb2w * vForces;
307 vFw(eDrag) *= -1; vFw(eLift) *= -1;
310 if ( fabs(vFw(eDrag)) > 0.0)
311 lod = fabs( vFw(eLift) / vFw(eDrag));
324 vFs(eDrag) *= -1; vFs(eLift) *= -1;
334 string scratch_unit=
"";
335 Element *temp_element, *axis_element, *function_element;
343 DetermineAxisSystem(document);
347 if ((temp_element = document->
FindElement(
"alphalimits"))) {
349 if (scratch_unit.empty()) scratch_unit =
"RAD";
352 alphaclmin = alphaclmin0;
353 alphaclmax = alphaclmax0;
356 if ((temp_element = document->
FindElement(
"hysteresis_limits"))) {
358 if (scratch_unit.empty()) scratch_unit =
"RAD";
363 if ((temp_element = document->
FindElement(
"aero_ref_pt_shift_x"))) {
364 function_element = temp_element->
FindElement(
"function");
365 AeroRPShift =
new FGFunction(FDMExec, function_element);
369 while (axis_element) {
370 AeroFunctionArray ca;
371 AeroFunctionArray ca_atCG;
373 function_element = axis_element->
FindElement(
"function");
374 while (function_element) {
376 bool apply_at_cg =
false;
378 if (function_element->
GetAttributeValue(
"apply_at_cg") ==
"true") apply_at_cg =
true;
382 ca.push_back(
new FGFunction(FDMExec, function_element) );
383 }
catch (
const string& str) {
384 cerr << endl << axis_element->
ReadFrom()
385 << endl <<
fgred <<
"Error loading aerodynamic function in "
386 << current_func_name <<
":" << str <<
" Aborting." <<
reset << endl;
391 ca_atCG.push_back(
new FGFunction(FDMExec, function_element) );
392 }
catch (
const string& str) {
393 cerr << endl << axis_element->
ReadFrom()
394 << endl <<
fgred <<
"Error loading aerodynamic function in "
395 << current_func_name <<
":" << str <<
" Aborting." <<
reset << endl;
401 AeroFunctions[AxisIdx[axis]] = ca;
402 AeroFunctionsAtCG[AxisIdx[axis]] = ca_atCG;
406 PostLoad(document, FDMExec);
424 void FGAerodynamics::DetermineAxisSystem(
Element* document)
428 while (axis_element) {
431 if (axis ==
"X" || axis ==
"Y" || axis ==
"Z") {
432 ProcessAxesNameAndFrame(forceAxisType, axis, frame, axis_element,
434 }
else if (axis ==
"ROLL" || axis ==
"PITCH" || axis ==
"YAW") {
435 ProcessAxesNameAndFrame(momentAxisType, axis, frame, axis_element,
437 }
else if (axis ==
"LIFT" || axis ==
"DRAG") {
438 if (forceAxisType == atNone) forceAxisType = atWind;
439 else if (forceAxisType != atWind) {
440 cerr << endl << axis_element->
ReadFrom()
441 << endl <<
" Mixed aerodynamic axis systems have been used in the"
442 <<
" aircraft config file. (LIFT DRAG)" << endl;
444 }
else if (axis ==
"SIDE") {
445 if (forceAxisType != atNone && forceAxisType != atWind && forceAxisType != atBodyAxialNormal) {
446 cerr << endl << axis_element->
ReadFrom()
447 << endl <<
" Mixed aerodynamic axis systems have been used in the"
448 <<
" aircraft config file. (SIDE)" << endl;
450 }
else if (axis ==
"AXIAL" || axis ==
"NORMAL") {
451 if (forceAxisType == atNone) forceAxisType = atBodyAxialNormal;
452 else if (forceAxisType != atBodyAxialNormal) {
453 cerr << endl << axis_element->
ReadFrom()
454 << endl <<
" Mixed aerodynamic axis systems have been used in the"
455 <<
" aircraft config file. (NORMAL AXIAL)" << endl;
460 << endl <<
" An unknown axis type, " << axis <<
" has been specified"
461 <<
" in the aircraft configuration file.";
462 cerr << endl << s.str() << endl;
463 throw BaseException(s.str());
468 if (forceAxisType == atNone) {
469 forceAxisType = atWind;
470 cerr << endl <<
" The aerodynamic axis system has been set by default"
471 <<
" to the Lift/Side/Drag system." << endl;
473 if (momentAxisType == atNone) {
474 momentAxisType = atBodyXYZ;
475 cerr << endl <<
" The aerodynamic moment axis system has been set by default"
476 <<
" to the bodyXYZ system." << endl;
482 void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType,
const string& name,
483 const string& frame, Element* el,
484 const string& validNames)
486 if (frame ==
"BODY" || frame.empty()) {
487 if (axisType == atNone) axisType = atBodyXYZ;
488 else if (axisType != atBodyXYZ)
489 cerr << endl << el->ReadFrom()
490 << endl <<
" Mixed aerodynamic axis systems have been used in the "
491 <<
" aircraft config file." << validNames <<
" - BODY" << endl;
493 else if (frame ==
"STABILITY") {
494 if (axisType == atNone) axisType = atStability;
495 else if (axisType != atStability)
496 cerr << endl << el->ReadFrom()
497 << endl <<
" Mixed aerodynamic axis systems have been used in the "
498 <<
" aircraft config file." << validNames <<
" - STABILITY" << endl;
500 else if (frame ==
"WIND") {
501 if (axisType == atNone) axisType = atWind;
502 else if (axisType != atWind)
503 cerr << endl << el->ReadFrom()
504 << endl <<
" Mixed aerodynamic axis systems have been used in the "
505 <<
" aircraft config file." << validNames <<
" - WIND" << endl;
509 s <<
" Unknown axis frame type of - " << frame;
510 cerr << endl << s.str() << endl;
511 throw BaseException(s.str());
519 string AeroFunctionStrings =
"";
520 bool firstime =
true;
521 unsigned int axis, sd;
523 for (axis = 0; axis < 6; axis++) {
524 for (sd = 0; sd < AeroFunctions[axis].size(); sd++) {
528 AeroFunctionStrings += delimeter;
530 AeroFunctionStrings += AeroFunctions[axis][sd]->GetName();
536 if (FunctionStrings.size() > 0) {
537 if (AeroFunctionStrings.size() > 0) {
538 AeroFunctionStrings += delimeter + FunctionStrings;
540 AeroFunctionStrings = FunctionStrings;
544 return AeroFunctionStrings;
553 for (
unsigned int axis = 0; axis < 6; axis++) {
554 for (
unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) {
555 if (buf.tellp() > 0) buf << delimeter;
556 buf << AeroFunctions[axis][sd]->GetValue();
562 if (FunctionValues.size() > 0) {
563 if (buf.str().size() > 0) {
564 buf << delimeter << FunctionValues;
566 buf << FunctionValues;
575 void FGAerodynamics::bind(
void)
599 PropertyManager->
Tie(
"aero/qbar-area", &qbar_area);
600 PropertyManager->
Tie(
"aero/alpha-max-rad",
this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax);
601 PropertyManager->
Tie(
"aero/alpha-min-rad",
this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin);
602 PropertyManager->
Tie(
"aero/bi2vel",
this, &FGAerodynamics::GetBI2Vel);
603 PropertyManager->
Tie(
"aero/ci2vel",
this, &FGAerodynamics::GetCI2Vel);
604 PropertyManager->
Tie(
"aero/alpha-wing-rad",
this, &FGAerodynamics::GetAlphaW);
605 PropertyManager->
Tie(
"systems/stall-warn-norm",
this, &FGAerodynamics::GetStallWarn);
606 PropertyManager->
Tie(
"aero/stall-hyst-norm",
this, &FGAerodynamics::GetHysteresisParm);
628 void FGAerodynamics::BuildStabilityTransformMatrices(
void)
630 double ca = cos(in.Alpha);
631 double sa = sin(in.Alpha);
666 void FGAerodynamics::Debug(
int from)
668 if (debug_lvl <= 0)
return;
672 switch (forceAxisType) {
674 cout << endl <<
" Aerodynamics (Lift|Side|Drag axes):" << endl << endl;
676 case (atBodyAxialNormal):
677 cout << endl <<
" Aerodynamics (Axial|Side|Normal axes):" << endl << endl;
680 cout << endl <<
" Aerodynamics (Body X|Y|Z axes):" << endl << endl;
683 cout << endl <<
" Aerodynamics (Stability X|Y|Z axes):" << endl << endl;
686 cout << endl <<
" Aerodynamics (undefined axes):" << endl << endl;
691 if (debug_lvl & 2 ) {
692 if (from == 0) cout <<
"Instantiated: FGAerodynamics" << endl;
693 if (from == 1) cout <<
"Destroyed: FGAerodynamics" << endl;
695 if (debug_lvl & 4 ) {
697 if (debug_lvl & 8 ) {
699 if (debug_lvl & 16) {
701 if (debug_lvl & 64) {