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;
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;
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;
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;
310 if ( fabs(vFw(
eDrag)) > 0.0)
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");
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;
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;
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;
424void 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;
482void 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)
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)
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)
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;
575void FGAerodynamics::bind(
void)
628void FGAerodynamics::BuildStabilityTransformMatrices(
void)
666void FGAerodynamics::Debug(
int from)
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;
692 if (from == 0) cout <<
"Instantiated: FGAerodynamics" << endl;
693 if (from == 1) cout <<
"Destroyed: FGAerodynamics" << endl;
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
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.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindElement(const std::string &el="")
Searches for a specified element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
Encapsulates the aerodynamic calculations.
void SetAlphaCLMin(double tt)
FGAerodynamics(FGFDMExec *Executive)
Constructor.
bool InitModel(void) override
~FGAerodynamics() override
Destructor.
const FGColumnVector3 & GetMoments(void) const
Gets the total aerodynamic moment vector about the CG.
FGColumnVector3 GetForcesInStabilityAxes(void) const
Retrieves the aerodynamic forces in the stability axes.
double GetAlphaCLMin(void) const
const FGColumnVector3 & GetvFw(void) const
Retrieves the aerodynamic forces in the wind axes.
double GetStallWarn(void) const
bool Load(Element *element) override
Loads the Aerodynamics model.
double GetAlphaW(void) const
void SetAlphaCLMax(double tt)
double GetClSquared(void) const
Retrieves the square of the lift coefficient.
double GetHysteresisParm(void) const
bool Run(bool Holding) override
Runs the Aerodynamics model; called by the Executive Can pass in a value indicating if the executive ...
double GetLoD(void) const
Retrieves the lift over drag ratio.
std::string GetAeroFunctionValues(const std::string &delimeter) const
Gets the aero function values.
FGColumnVector3 GetMomentsInWindAxes(void) const
Gets the total aerodynamic moment vector about the CG in the wind axes.
const FGColumnVector3 & GetForces(void) const
Gets the total aerodynamic force vector.
double GetCI2Vel(void) const
double GetAlphaCLMax(void) const
struct JSBSim::FGAerodynamics::Inputs in
double GetBI2Vel(void) const
std::string GetAeroFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of aero functions.
FGColumnVector3 GetMomentsInStabilityAxes(void) const
Gets the total aerodynamic moment vector about the CG in the stability axes.
This class implements a 3 element column vector.
Represents a mathematical function.
static char reset[5]
resets text properties
static char fgred[6]
red text
FGMatrix33 Transposed(void) const
Transposed matrix.
std::string GetFunctionValues(const std::string &delimeter) const
Gets the function values.
std::string GetFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of functions.
void RunPostFunctions(void)
void PostLoad(Element *el, FGFDMExec *fdmex, std::string prefix="")
void RunPreFunctions(void)
FGPropertyManager * PropertyManager
bool InitModel(void) override
FGModel(FGFDMExec *)
Constructor.
bool Upload(Element *el, bool preLoad)
Uploads this model in memory.
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.