39#include "FGAerodynamics.h"
41#include "input_output/FGXMLElement.h"
42#include "input_output/FGLog.h"
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;
113bool 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 err <<
"\n A proper axis type has NOT been selected. Check "
235 <<
"your aerodynamics definition.\n";
244 if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->
GetValue()*in.Wingchord;
246 vDXYZcg(eX) = in.RPBody(eX) - vDeltaRP(eX);
247 vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY);
248 vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ);
250 vMomentsMRC.InitMatrix();
252 for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
253 AeroFunctionArray* array = &AeroFunctions[axis_ctr+3];
254 for (AeroFunctionArray::iterator f=array->begin(); f != array->end(); ++f) {
259 (*f)->cacheValue(
true);
260 vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
266 vMomentsMRCBodyXYZ.InitMatrix();
267 switch (momentAxisType) {
269 vMomentsMRCBodyXYZ = vMomentsMRC;
272 vMomentsMRCBodyXYZ = Ts2b*vMomentsMRC;
275 vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
280 err <<
"\n A proper axis type has NOT been selected. Check "
281 <<
"your aerodynamics definition.\n";
286 vMoments = vMomentsMRCBodyXYZ + vDXYZcg*vForces;
290 vForces += vForcesAtCG;
304 vFw = in.Tb2w * vForces;
305 vFw(eDrag) *= -1; vFw(eLift) *= -1;
308 if ( fabs(vFw(eDrag)) > 0.0)
309 lod = fabs( vFw(eLift) / vFw(eDrag));
322 vFs(eDrag) *= -1; vFs(eLift) *= -1;
332 string scratch_unit=
"";
333 Element *temp_element, *axis_element, *function_element;
341 DetermineAxisSystem(document);
345 if ((temp_element = document->
FindElement(
"alphalimits"))) {
347 if (scratch_unit.empty()) scratch_unit =
"RAD";
350 alphaclmin = alphaclmin0;
351 alphaclmax = alphaclmax0;
354 if ((temp_element = document->
FindElement(
"hysteresis_limits"))) {
356 if (scratch_unit.empty()) scratch_unit =
"RAD";
361 if ((temp_element = document->
FindElement(
"aero_ref_pt_shift_x"))) {
362 function_element = temp_element->
FindElement(
"function");
363 AeroRPShift =
new FGFunction(FDMExec, function_element);
367 while (axis_element) {
368 AeroFunctionArray ca;
369 AeroFunctionArray ca_atCG;
371 function_element = axis_element->
FindElement(
"function");
372 while (function_element) {
376 ca_atCG.push_back(
new FGFunction(FDMExec, function_element));
378 ca.push_back(
new FGFunction(FDMExec, function_element));
382 log << LogFormat::RED <<
"\nError loading aerodynamic function in "
383 << current_func_name <<
":" << e.what() <<
" Aborting.\n" << LogFormat::RESET;
388 AeroFunctions[AxisIdx[axis]] = ca;
389 AeroFunctionsAtCG[AxisIdx[axis]] = ca_atCG;
393 PostLoad(document, FDMExec);
438void FGAerodynamics::DetermineAxisSystem(
Element* document)
442 while (axis_element) {
445 if (axis ==
"X" || axis ==
"Y" || axis ==
"Z") {
446 ProcessAxesNameAndFrame(forceAxisType, axis, frame, axis_element,
448 }
else if (axis ==
"ROLL" || axis ==
"PITCH" || axis ==
"YAW") {
449 ProcessAxesNameAndFrame(momentAxisType, axis, frame, axis_element,
451 }
else if (axis ==
"LIFT" || axis ==
"DRAG") {
452 if (forceAxisType == atNone) forceAxisType = atWind;
453 else if (forceAxisType != atWind) {
454 FGXMLLogging log(axis_element, LogLevel::WARN);
455 log <<
"\n Mixed aerodynamic axis systems have been used in the"
456 <<
" aircraft config file. (LIFT DRAG)\n";
458 }
else if (axis ==
"SIDE") {
459 if (forceAxisType != atNone && forceAxisType != atWind && forceAxisType != atBodyAxialNormal) {
460 FGXMLLogging log(axis_element, LogLevel::WARN);
461 log <<
"\n Mixed aerodynamic axis systems have been used in the"
462 <<
" aircraft config file. (SIDE)\n";
464 }
else if (axis ==
"AXIAL" || axis ==
"NORMAL") {
465 if (forceAxisType == atNone) forceAxisType = atBodyAxialNormal;
466 else if (forceAxisType != atBodyAxialNormal) {
467 FGXMLLogging log(axis_element, LogLevel::WARN);
468 log <<
"\n Mixed aerodynamic axis systems have been used in the"
469 <<
" aircraft config file. (NORMAL AXIAL)\n";
472 XMLLogException err(axis_element);
473 err <<
"\n An unknown axis type, " << axis <<
" has been specified"
474 <<
" in the aircraft configuration file.\n";
480 if (forceAxisType == atNone) {
481 forceAxisType = atWind;
482 FGLogging log(LogLevel::INFO);
483 log <<
"\n The aerodynamic axis system has been set by default"
484 <<
" to the Lift/Side/Drag system.\n";
486 if (momentAxisType == atNone) {
487 momentAxisType = atBodyXYZ;
488 FGLogging log(LogLevel::INFO);
489 log <<
"\n The aerodynamic moment axis system has been set by default"
490 <<
" to the bodyXYZ system.\n";
496void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType,
const string& name,
497 const string& frame, Element* el,
498 const string& validNames)
500 if (frame ==
"BODY" || frame.empty()) {
501 if (axisType == atNone) axisType = atBodyXYZ;
502 else if (axisType != atBodyXYZ) {
503 FGXMLLogging log(el, LogLevel::WARN);
504 log <<
"\n Mixed aerodynamic axis systems have been used in the "
505 <<
" aircraft config file." << validNames <<
" - BODY\n";
508 else if (frame ==
"STABILITY") {
509 if (axisType == atNone) axisType = atStability;
510 else if (axisType != atStability) {
511 FGXMLLogging log(el, LogLevel::WARN);
512 log <<
"\n Mixed aerodynamic axis systems have been used in the "
513 <<
" aircraft config file." << validNames <<
" - STABILITY\n";
516 else if (frame ==
"WIND") {
517 if (axisType == atNone) axisType = atWind;
518 else if (axisType != atWind){
519 FGXMLLogging log(el, LogLevel::WARN);
520 log <<
"\n Mixed aerodynamic axis systems have been used in the "
521 <<
" aircraft config file." << validNames <<
" - WIND\n";
525 XMLLogException err(el);
526 err <<
"\n Unknown axis frame type of - " << frame <<
"\n";
535 string AeroFunctionStrings =
"";
536 bool firstime =
true;
537 unsigned int axis, sd;
539 for (axis = 0; axis < 6; axis++) {
540 for (sd = 0; sd < AeroFunctions[axis].size(); sd++) {
544 AeroFunctionStrings += delimeter;
546 AeroFunctionStrings += AeroFunctions[axis][sd]->GetName();
552 if (!FunctionStrings.empty()) {
553 if (!AeroFunctionStrings.empty()) {
554 AeroFunctionStrings += delimeter + FunctionStrings;
556 AeroFunctionStrings = FunctionStrings;
560 return AeroFunctionStrings;
569 for (
unsigned int axis = 0; axis < 6; axis++) {
570 for (
unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) {
571 if (buf.tellp() > 0) buf << delimeter;
572 buf << AeroFunctions[axis][sd]->GetValue();
578 if (!FunctionValues.empty()) {
579 if (!buf.str().empty()) {
580 buf << delimeter << FunctionValues;
582 buf << FunctionValues;
591void FGAerodynamics::bind(
void)
613 PropertyManager->Tie(
"aero/qbar-area", &qbar_area);
614 PropertyManager->Tie(
"aero/alpha-max-rad",
this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax);
615 PropertyManager->Tie(
"aero/alpha-min-rad",
this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin);
616 PropertyManager->Tie(
"aero/bi2vel",
this, &FGAerodynamics::GetBI2Vel);
617 PropertyManager->Tie(
"aero/ci2vel",
this, &FGAerodynamics::GetCI2Vel);
618 PropertyManager->Tie(
"aero/alpha-wing-rad",
this, &FGAerodynamics::GetAlphaW);
619 PropertyManager->Tie(
"systems/stall-warn-norm",
this, &FGAerodynamics::GetStallWarn);
620 PropertyManager->Tie(
"aero/stall-hyst-norm",
this, &FGAerodynamics::GetHysteresisParm);
642void FGAerodynamics::BuildStabilityTransformMatrices(
void)
644 double ca = cos(in.Alpha);
645 double sa = sin(in.Alpha);
680void FGAerodynamics::Debug(
int from)
682 if (debug_lvl <= 0)
return;
686 FGLogging log(LogLevel::DEBUG);
687 switch (forceAxisType) {
689 log <<
"\n Aerodynamics (Lift|Side|Drag axes):\n\n";
691 case (atBodyAxialNormal):
692 log <<
"\n Aerodynamics (Axial|Side|Normal axes):\n\n";
695 log <<
"\n Aerodynamics (Body X|Y|Z axes):\n\n";
698 log <<
"\n Aerodynamics (Stability X|Y|Z axes):\n\n";
701 log <<
"\n Aerodynamics (undefined axes):\n\n";
706 if (debug_lvl & 2 ) {
707 FGLogging log(LogLevel::DEBUG);
708 if (from == 0) log <<
"Instantiated: FGAerodynamics\n";
709 if (from == 1) log <<
"Destroyed: FGAerodynamics\n";
711 if (debug_lvl & 4 ) {
713 if (debug_lvl & 8 ) {
715 if (debug_lvl & 16) {
717 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.