39#include "FGAerodynamics.h"
41#include "input_output/FGXMLElement.h"
42#include "input_output/FGLog.h"
55 Name =
"FGAerodynamics";
65 AxisIdx[
"NORMAL"] = 2;
71 forceAxisType = atNone;
72 momentAxisType = atNone;
74 AeroFunctions =
new AeroFunctionArray[6];
75 AeroFunctionsAtCG =
new AeroFunctionArray[6];
77 impending_stall = stall_hyst = 0.0;
78 alphaclmin = alphaclmax = 0.0;
79 alphaclmin0 = alphaclmax0 = 0.0;
80 alphahystmin = alphahystmax = 0.0;
83 bi2vel = ci2vel = 0.0;
85 vDeltaRP.InitMatrix();
99 for (j=0; j<AeroFunctions[i].size(); j++)
100 delete AeroFunctions[i][j];
102 for (j=0; j<AeroFunctionsAtCG[i].size(); j++)
103 delete AeroFunctionsAtCG[i][j];
105 delete[] AeroFunctions;
106 delete[] AeroFunctionsAtCG;
115bool FGAerodynamics::InitModel(
void)
117 if (!FGModel::InitModel())
return false;
119 impending_stall = stall_hyst = 0.0;
120 alphaclmin = alphaclmin0;
121 alphaclmax = alphaclmax0;
122 alphahystmin = alphahystmax = 0.0;
125 bi2vel = ci2vel = 0.0;
127 vDeltaRP.InitMatrix();
128 vForces.InitMatrix();
129 vMoments.InitMatrix();
138 if (Holding)
return false;
140 unsigned int axis_ctr;
141 const double twovel=2*in.Vt;
146 if ( in.Qbar > 1.0) {
150 clsq = vFw(eLift) / (in.Wingarea*in.Qbar);
159 bi2vel = in.Wingspan / twovel;
160 ci2vel = in.Wingchord / twovel;
162 alphaw = in.Alpha + in.Wingincidence;
163 qbar_area = in.Wingarea * in.Qbar;
165 if (alphaclmax != 0) {
166 if (in.Alpha > 0.85*alphaclmax) {
167 impending_stall = 10*(in.Alpha/alphaclmax - 0.85);
173 if (alphahystmax != 0.0 && alphahystmin != 0.0) {
174 if (in.Alpha > alphahystmax) {
176 }
else if (in.Alpha < alphahystmin) {
182 vFnative.InitMatrix();
183 vFnativeAtCG.InitMatrix();
185 BuildStabilityTransformMatrices();
187 for (axis_ctr = 0; axis_ctr < 3; ++axis_ctr) {
188 AeroFunctionArray::iterator f;
190 AeroFunctionArray* array = &AeroFunctions[axis_ctr];
191 for (f=array->begin(); f != array->end(); ++f) {
196 (*f)->cacheValue(
true);
197 vFnative(axis_ctr+1) += (*f)->GetValue();
200 array = &AeroFunctionsAtCG[axis_ctr];
201 for (f=array->begin(); f != array->end(); ++f) {
202 (*f)->cacheValue(
true);
203 vFnativeAtCG(axis_ctr+1) += (*f)->GetValue();
207 switch (forceAxisType) {
210 vForcesAtCG = vFnativeAtCG;
213 vFnative(eDrag)*=-1; vFnative(eLift)*=-1;
214 vForces = in.Tw2b*vFnative;
216 vFnativeAtCG(eDrag)*=-1; vFnativeAtCG(eLift)*=-1;
217 vForcesAtCG = in.Tw2b*vFnativeAtCG;
219 case atBodyAxialNormal:
220 vFnative(eX)*=-1; vFnative(eZ)*=-1;
223 vFnativeAtCG(eX)*=-1; vFnativeAtCG(eZ)*=-1;
224 vForcesAtCG = vFnativeAtCG;
227 vFnative(eDrag) *= -1; vFnative(eLift) *= -1;
228 vForces = Ts2b*vFnative;
230 vFnativeAtCG(eDrag) *= -1; vFnativeAtCG(eLift) *= -1;
231 vForcesAtCG = Ts2b*vFnativeAtCG;
236 err <<
"\n A proper axis type has NOT been selected. Check "
237 <<
"your aerodynamics definition.\n";
246 if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->
GetValue()*in.Wingchord;
248 vDXYZcg(eX) = in.RPBody(eX) - vDeltaRP(eX);
249 vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY);
250 vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ);
252 vMomentsMRC.InitMatrix();
254 for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
255 AeroFunctionArray* array = &AeroFunctions[axis_ctr+3];
256 for (AeroFunctionArray::iterator f=array->begin(); f != array->end(); ++f) {
261 (*f)->cacheValue(
true);
262 vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
268 vMomentsMRCBodyXYZ.InitMatrix();
269 switch (momentAxisType) {
271 vMomentsMRCBodyXYZ = vMomentsMRC;
274 vMomentsMRCBodyXYZ = Ts2b*vMomentsMRC;
277 vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
282 err <<
"\n A proper axis type has NOT been selected. Check "
283 <<
"your aerodynamics definition.\n";
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) {
378 ca_atCG.push_back(
new FGFunction(FDMExec, function_element));
380 ca.push_back(
new FGFunction(FDMExec, function_element));
384 log << LogFormat::RED <<
"\nError loading aerodynamic function in "
385 << current_func_name <<
":" << e.what() <<
" Aborting.\n" << LogFormat::RESET;
390 AeroFunctions[AxisIdx[axis]] = ca;
391 AeroFunctionsAtCG[AxisIdx[axis]] = ca_atCG;
395 PostLoad(document, FDMExec);
440void FGAerodynamics::DetermineAxisSystem(
Element* document)
444 while (axis_element) {
447 if (axis ==
"X" || axis ==
"Y" || axis ==
"Z") {
448 ProcessAxesNameAndFrame(forceAxisType, axis, frame, axis_element,
450 }
else if (axis ==
"ROLL" || axis ==
"PITCH" || axis ==
"YAW") {
451 ProcessAxesNameAndFrame(momentAxisType, axis, frame, axis_element,
453 }
else if (axis ==
"LIFT" || axis ==
"DRAG") {
454 if (forceAxisType == atNone) forceAxisType = atWind;
455 else if (forceAxisType != atWind) {
456 FGXMLLogging log(axis_element, LogLevel::WARN);
457 log <<
"\n Mixed aerodynamic axis systems have been used in the"
458 <<
" aircraft config file. (LIFT DRAG)\n";
460 }
else if (axis ==
"SIDE") {
461 if (forceAxisType != atNone && forceAxisType != atWind && forceAxisType != atBodyAxialNormal) {
462 FGXMLLogging log(axis_element, LogLevel::WARN);
463 log <<
"\n Mixed aerodynamic axis systems have been used in the"
464 <<
" aircraft config file. (SIDE)\n";
466 }
else if (axis ==
"AXIAL" || axis ==
"NORMAL") {
467 if (forceAxisType == atNone) forceAxisType = atBodyAxialNormal;
468 else if (forceAxisType != atBodyAxialNormal) {
469 FGXMLLogging log(axis_element, LogLevel::WARN);
470 log <<
"\n Mixed aerodynamic axis systems have been used in the"
471 <<
" aircraft config file. (NORMAL AXIAL)\n";
474 XMLLogException err(axis_element);
475 err <<
"\n An unknown axis type, " << axis <<
" has been specified"
476 <<
" in the aircraft configuration file.\n";
482 if (forceAxisType == atNone) {
483 forceAxisType = atWind;
484 FGLogging log(LogLevel::INFO);
485 log <<
"\n The aerodynamic axis system has been set by default"
486 <<
" to the Lift/Side/Drag system.\n";
488 if (momentAxisType == atNone) {
489 momentAxisType = atBodyXYZ;
490 FGLogging log(LogLevel::INFO);
491 log <<
"\n The aerodynamic moment axis system has been set by default"
492 <<
" to the bodyXYZ system.\n";
498void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType,
const string& name,
499 const string& frame, Element* el,
500 const string& validNames)
502 if (frame ==
"BODY" || frame.empty()) {
503 if (axisType == atNone) axisType = atBodyXYZ;
504 else if (axisType != atBodyXYZ) {
505 FGXMLLogging log(el, LogLevel::WARN);
506 log <<
"\n Mixed aerodynamic axis systems have been used in the "
507 <<
" aircraft config file." << validNames <<
" - BODY\n";
510 else if (frame ==
"STABILITY") {
511 if (axisType == atNone) axisType = atStability;
512 else if (axisType != atStability) {
513 FGXMLLogging log(el, LogLevel::WARN);
514 log <<
"\n Mixed aerodynamic axis systems have been used in the "
515 <<
" aircraft config file." << validNames <<
" - STABILITY\n";
518 else if (frame ==
"WIND") {
519 if (axisType == atNone) axisType = atWind;
520 else if (axisType != atWind){
521 FGXMLLogging log(el, LogLevel::WARN);
522 log <<
"\n Mixed aerodynamic axis systems have been used in the "
523 <<
" aircraft config file." << validNames <<
" - WIND\n";
527 XMLLogException err(el);
528 err <<
"\n Unknown axis frame type of - " << frame <<
"\n";
537 string AeroFunctionStrings =
"";
538 bool firstime =
true;
539 unsigned int axis, sd;
541 for (axis = 0; axis < 6; axis++) {
542 for (sd = 0; sd < AeroFunctions[axis].size(); sd++) {
546 AeroFunctionStrings += delimeter;
548 AeroFunctionStrings += AeroFunctions[axis][sd]->GetName();
554 if (!FunctionStrings.empty()) {
555 if (!AeroFunctionStrings.empty()) {
556 AeroFunctionStrings += delimeter + FunctionStrings;
558 AeroFunctionStrings = FunctionStrings;
562 return AeroFunctionStrings;
571 for (
unsigned int axis = 0; axis < 6; axis++) {
572 for (
unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) {
573 if (buf.tellp() > 0) buf << delimeter;
574 buf << AeroFunctions[axis][sd]->GetValue();
580 if (!FunctionValues.empty()) {
581 if (!buf.str().empty()) {
582 buf << delimeter << FunctionValues;
584 buf << FunctionValues;
593void FGAerodynamics::bind(
void)
615 PropertyManager->Tie(
"aero/qbar-area", &qbar_area);
616 PropertyManager->Tie(
"aero/alpha-max-rad",
this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax);
617 PropertyManager->Tie(
"aero/alpha-min-rad",
this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin);
618 PropertyManager->Tie(
"aero/bi2vel",
this, &FGAerodynamics::GetBI2Vel);
619 PropertyManager->Tie(
"aero/ci2vel",
this, &FGAerodynamics::GetCI2Vel);
620 PropertyManager->Tie(
"aero/alpha-wing-rad",
this, &FGAerodynamics::GetAlphaW);
621 PropertyManager->Tie(
"systems/stall-warn-norm",
this, &FGAerodynamics::GetStallWarn);
622 PropertyManager->Tie(
"aero/stall-hyst-norm",
this, &FGAerodynamics::GetHysteresisParm);
644void FGAerodynamics::BuildStabilityTransformMatrices(
void)
646 double ca = cos(in.Alpha);
647 double sa = sin(in.Alpha);
682void FGAerodynamics::Debug(
int from)
684 if (debug_lvl <= 0)
return;
688 FGLogging log(LogLevel::DEBUG);
689 switch (forceAxisType) {
691 log <<
"\n Aerodynamics (Lift|Side|Drag axes):\n\n";
693 case (atBodyAxialNormal):
694 log <<
"\n Aerodynamics (Axial|Side|Normal axes):\n\n";
697 log <<
"\n Aerodynamics (Body X|Y|Z axes):\n\n";
700 log <<
"\n Aerodynamics (Stability X|Y|Z axes):\n\n";
703 log <<
"\n Aerodynamics (undefined axes):\n\n";
708 if (debug_lvl & 2 ) {
709 FGLogging log(LogLevel::DEBUG);
710 if (from == 0) log <<
"Instantiated: FGAerodynamics\n";
711 if (from == 1) log <<
"Destroyed: FGAerodynamics\n";
713 if (debug_lvl & 4 ) {
715 if (debug_lvl & 8 ) {
717 if (debug_lvl & 16) {
719 if (debug_lvl & 64) {
Element * FindElement(const std::string &el="")
Searches for a specified element.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
double FindElementValueAsNumberConvertFromTo(const std::string &el, const std::string &supplied_units, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
std::string GetAeroFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of aero functions.
const FGColumnVector3 & GetMoments(void) const
Gets the total aerodynamic moment vector about the CG.
bool Load(Element *element) override
Loads the Aerodynamics model.
FGColumnVector3 GetForcesInStabilityAxes(void) const
Retrieves the aerodynamic forces in the stability axes.
FGColumnVector3 GetMomentsInWindAxes(void) const
Gets the total aerodynamic moment vector about the CG in the wind axes.
~FGAerodynamics() override
Destructor.
FGColumnVector3 GetMomentsInStabilityAxes(void) const
Gets the total aerodynamic moment vector about the CG in the stability axes.
double GetLoD(void) const
Retrieves the lift over drag ratio.
FGAerodynamics(FGFDMExec *Executive)
Constructor.
const FGColumnVector3 & GetvFw(void) const
Retrieves the aerodynamic forces in the wind axes.
bool Run(bool Holding) override
Runs the Aerodynamics model; called by the Executive Can pass in a value indicating if the executive ...
std::string GetAeroFunctionValues(const std::string &delimeter) const
Gets the aero function values.
double GetClSquared(void) const
Retrieves the square of the lift coefficient.
const FGColumnVector3 & GetForces(void) const
Gets the total aerodynamic force vector.
This class implements a 3 element column vector.
Encapsulates the JSBSim simulation executive.
Represents a mathematical function.
double GetValue(void) const override
Retrieves the value of the function object.
FGMatrix33 Transposed(void) const
Transposed matrix.
std::string GetFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of functions.
std::string GetFunctionValues(const std::string &delimeter) const
Gets the function values.
Base class for all scheduled JSBSim models.
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
bool Upload(Element *el, bool preLoad)
Uploads this model in memory.
Main namespace for the JSBSim Flight Dynamics Model.