X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FFDM%2FJSBSim%2Fmodels%2FFGAerodynamics.cpp;h=ed1d639fce787d7fdc4a93f61ff00e41c7026342;hb=024ef128e3395e8c0e32b360abe19b4d345e4f80;hp=63f00754d2335fb68f5acd541690bdaca14050f8;hpb=932b38a87e2870d23f9be9461b551f1e1fe556ba;p=flightgear.git diff --git a/src/FDM/JSBSim/models/FGAerodynamics.cpp b/src/FDM/JSBSim/models/FGAerodynamics.cpp index 63f00754d..ed1d639fc 100644 --- a/src/FDM/JSBSim/models/FGAerodynamics.cpp +++ b/src/FDM/JSBSim/models/FGAerodynamics.cpp @@ -5,23 +5,23 @@ Date started: 09/13/00 Purpose: Encapsulates the aerodynamic forces - ------------- Copyright (C) 2000 Jon S. Berndt (jsb@hal-pc.org) ------------- + ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) ------------- This program is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software + the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - You should have received a copy of the GNU General Public License along with + You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - Further information about the GNU General Public License can also be found on + Further information about the GNU Lesser General Public License can also be found on the world wide web at http://www.gnu.org. FUNCTIONAL DESCRIPTION @@ -36,22 +36,21 @@ HISTORY INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include +#include +#include +#include +#include "FGFDMExec.h" #include "FGAerodynamics.h" -#include "FGPropagate.h" -#include "FGAircraft.h" -#include "FGState.h" -#include "FGMassBalance.h" -#include +#include "input_output/FGPropertyManager.h" + +using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id$"; +static const char *IdSrc = "$Id: FGAerodynamics.cpp,v 1.45 2012/04/13 13:25:52 jberndt Exp $"; static const char *IdHdr = ID_AERODYNAMICS; -const unsigned NAxes=6; -const char* AxisNames[] = { "drag", "side-force", "lift", "rolling-moment", - "pitching-moment","yawing-moment" }; - /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ @@ -61,14 +60,23 @@ FGAerodynamics::FGAerodynamics(FGFDMExec* FDMExec) : FGModel(FDMExec) { Name = "FGAerodynamics"; - AxisIdx["DRAG"] = 0; - AxisIdx["SIDE"] = 1; - AxisIdx["LIFT"] = 2; - AxisIdx["ROLL"] = 3; - AxisIdx["PITCH"] = 4; - AxisIdx["YAW"] = 5; + AxisIdx["DRAG"] = 0; + AxisIdx["SIDE"] = 1; + AxisIdx["LIFT"] = 2; + AxisIdx["ROLL"] = 3; + AxisIdx["PITCH"] = 4; + AxisIdx["YAW"] = 5; + + AxisIdx["AXIAL"] = 0; + AxisIdx["NORMAL"] = 2; + + AxisIdx["X"] = 0; + AxisIdx["Y"] = 1; + AxisIdx["Z"] = 2; - Coeff = new CoeffArray[6]; + axisType = atNone; + + AeroFunctions = new AeroFunctionArray[6]; impending_stall = stall_hyst = 0.0; alphaclmin = alphaclmax = 0.0; @@ -76,6 +84,9 @@ FGAerodynamics::FGAerodynamics(FGFDMExec* FDMExec) : FGModel(FDMExec) clsq = lod = 0.0; alphaw = 0.0; bi2vel = ci2vel = 0.0; + AeroRPShift = 0; + vDeltaRP.InitMatrix(); + bind(); Debug(0); @@ -87,99 +98,136 @@ FGAerodynamics::~FGAerodynamics() { unsigned int i,j; - unbind(); - for (i=0; i<6; i++) - for (j=0; jHolding()) return false; // if paused don't execute + if (FGModel::Run(Holding)) return true; + if (Holding) return false; // if paused don't execute + + unsigned int axis_ctr, ctr; + const double twovel=2*in.Vt; + + RunPreFunctions(); // calculate some oft-used quantities for speed - twovel = 2*Auxiliary->GetVt(); if (twovel != 0) { - bi2vel = Aircraft->GetWingSpan() / twovel; - ci2vel = Aircraft->Getcbar() / twovel; + bi2vel = in.Wingspan / twovel; + ci2vel = in.Wingchord / twovel; } - alphaw = Auxiliary->Getalpha() + Aircraft->GetWingIncidence(); - alpha = Auxiliary->Getalpha(); - qbar_area = Aircraft->GetWingArea() * Auxiliary->Getqbar(); + alphaw = in.Alpha + in.Wingincidence; + qbar_area = in.Wingarea * in.Qbar; if (alphaclmax != 0) { - if (alpha > 0.85*alphaclmax) { - impending_stall = 10*(alpha/alphaclmax - 0.85); + if (in.Alpha > 0.85*alphaclmax) { + impending_stall = 10*(in.Alpha/alphaclmax - 0.85); } else { impending_stall = 0; } } if (alphahystmax != 0.0 && alphahystmin != 0.0) { - if (alpha > alphahystmax) { + if (in.Alpha > alphahystmax) { stall_hyst = 1; - } else if (alpha < alphahystmin) { + } else if (in.Alpha < alphahystmin) { stall_hyst = 0; } } - vLastFs = vFs; - vFs.InitMatrix(); - - // Tell the variable functions to cache their values, so while the aerodynamic - // functions are being calculated for each axis, these functions do not get - // calculated each time, but instead use the values that have already - // been calculated for this frame. - for (i=0; icacheValue(true); + vFw.InitMatrix(); + vFnative.InitMatrix(); for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) { - for (ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) { - vFs(axis_ctr+1) += Coeff[axis_ctr][ctr]->GetValue(); + for (ctr=0; ctr < AeroFunctions[axis_ctr].size(); ctr++) { + vFnative(axis_ctr+1) += AeroFunctions[axis_ctr][ctr]->GetValue(); } } - // calculate lift coefficient squared - if ( Auxiliary->Getqbar() > 0) { - clsq = vFs(eLift) / (Aircraft->GetWingArea()*Auxiliary->Getqbar()); - clsq *= clsq; + // Note that we still need to convert to wind axes here, because it is + // used in the L/D calculation, and we still may want to look at Lift + // and Drag. + + switch (axisType) { + case atBodyXYZ: // Forces already in body axes; no manipulation needed + vFw = in.Tb2w*vFnative; + vForces = vFnative; + break; + case atLiftDrag: // Copy forces into wind axes + vFw = vFnative; + vFw(eDrag)*=-1; vFw(eLift)*=-1; + vForces = in.Tw2b*vFw; + break; + case atAxialNormal: // Convert native forces into Axial|Normal|Side system + vFw = in.Tb2w*vFnative; + vFnative(eX)*=-1; vFnative(eZ)*=-1; + vForces = vFnative; + break; + default: + cerr << endl << " A proper axis type has NOT been selected. Check " + << "your aerodynamics definition." << endl; + exit(-1); } - if ( vFs(eDrag) > 0) { - lod = vFs(eLift) / vFs(eDrag); + // Calculate lift coefficient squared + if ( in.Qbar > 0) { + clsq = vFw(eLift) / (in.Wingarea*in.Qbar); + clsq *= clsq; } - //correct signs of drag and lift to wind axes convention - //positive forward, right, down - vFs(eDrag)*=-1; vFs(eLift)*=-1; + // Calculate lift Lift over Drag + if ( fabs(vFw(eDrag)) > 0.0) lod = fabs( vFw(eLift) / vFw(eDrag) ); - // transform stability axis forces into body axes - vForces = State->GetTs2b()*vFs; + // Calculate aerodynamic reference point shift, if any. The shift + // takes place in the structual axis. That is, if the shift is positive, + // it is towards the back (tail) of the vehicle. The AeroRPShift + // function should be non-dimensionalized by the wing chord. The + // calculated vDeltaRP will be in feet. + if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->GetValue()*in.Wingchord; - vDXYZcg = MassBalance->StructuralToBody(Aircraft->GetXYZrp()); + vDXYZcg(eX) = in.RPBody(eX) - vDeltaRP(eX); // vDeltaRP is given in the structural frame + vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY); + vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ); vMoments = vDXYZcg*vForces; // M = r X F for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) { - for (ctr = 0; ctr < Coeff[axis_ctr+3].size(); ctr++) { - vMoments(axis_ctr+1) += Coeff[axis_ctr+3][ctr]->GetValue(); + for (ctr = 0; ctr < AeroFunctions[axis_ctr+3].size(); ctr++) { + vMoments(axis_ctr+1) += AeroFunctions[axis_ctr+3][ctr]->GetValue(); } } + RunPostFunctions(); + return false; } @@ -188,93 +236,180 @@ bool FGAerodynamics::Run(void) bool FGAerodynamics::Load(Element *element) { string parameter, axis, scratch; + string scratch_unit=""; + string fname="", file=""; Element *temp_element, *axis_element, *function_element; + string separator = "/"; + + fname = element->GetAttributeValue("file"); + if (!fname.empty()) { + file = FDMExec->GetFullAircraftPath() + separator + fname; + document = LoadXMLDocument(file); + if (document == 0L) return false; + } else { + document = element; + } + + FGModel::Load(document); // Perform base class Pre-Load + + DetermineAxisSystem(); // Detemine if Lift/Side/Drag, etc. is used. + Debug(2); - if (temp_element = element->FindElement("alphalimits")) { - alphaclmin = temp_element->FindElementValueAsNumberConvertTo("min", "DEG"); - alphaclmax = temp_element->FindElementValueAsNumberConvertTo("max", "DEG"); + if ((temp_element = document->FindElement("alphalimits"))) { + scratch_unit = temp_element->GetAttributeValue("unit"); + if (scratch_unit.empty()) scratch_unit = "RAD"; + alphaclmin = temp_element->FindElementValueAsNumberConvertFromTo("min", scratch_unit, "RAD"); + alphaclmax = temp_element->FindElementValueAsNumberConvertFromTo("max", scratch_unit, "RAD"); } - if (temp_element = element->FindElement("hysteresis_limits")) { - alphahystmin = temp_element->FindElementValueAsNumberConvertTo("min", "DEG"); - alphahystmax = temp_element->FindElementValueAsNumberConvertTo("max", "DEG"); + if ((temp_element = document->FindElement("hysteresis_limits"))) { + scratch_unit = temp_element->GetAttributeValue("unit"); + if (scratch_unit.empty()) scratch_unit = "RAD"; + alphahystmin = temp_element->FindElementValueAsNumberConvertFromTo("min", scratch_unit, "RAD"); + alphahystmax = temp_element->FindElementValueAsNumberConvertFromTo("max", scratch_unit, "RAD"); } - function_element = element->FindElement("function"); - while (function_element) { - variables.push_back( new FGFunction(PropertyManager, function_element) ); - function_element = element->FindNextElement("function"); + if ((temp_element = document->FindElement("aero_ref_pt_shift_x"))) { + function_element = temp_element->FindElement("function"); + AeroRPShift = new FGFunction(PropertyManager, function_element); } - axis_element = element->FindElement("axis"); + axis_element = document->FindElement("axis"); while (axis_element) { - CoeffArray ca; + AeroFunctionArray ca; axis = axis_element->GetAttributeValue("name"); function_element = axis_element->FindElement("function"); while (function_element) { - ca.push_back( new FGFunction(PropertyManager, function_element) ); + string current_func_name = function_element->GetAttributeValue("name"); + try { + ca.push_back( new FGFunction(PropertyManager, function_element) ); + } catch (string const str) { + cerr << endl << fgred << "Error loading aerodynamic function in " + << current_func_name << ":" << str << " Aborting." << reset << endl; + return false; + } function_element = axis_element->FindNextElement("function"); } - Coeff[AxisIdx[axis]] = ca; - axis_element = element->FindNextElement("axis"); + AeroFunctions[AxisIdx[axis]] = ca; + axis_element = document->FindNextElement("axis"); } + PostLoad(document, PropertyManager); // Perform base class Post-Load + return true; } +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// +// This private class function checks to verify consistency in the choice of +// aerodynamic axes used in the config file. One set of LIFT|DRAG|SIDE, or +// X|Y|Z, or AXIAL|NORMAL|SIDE must be chosen; mixed system axes are not allowed. +// Note that if the "SIDE" axis specifier is entered first in a config file, +// a warning message will be given IF the AXIAL|NORMAL specifiers are also given. +// This is OK, and the warning is due to the SIDE specifier used for both +// the Lift/Drag and Axial/Normal axis systems. + +void FGAerodynamics::DetermineAxisSystem() +{ + Element* axis_element = document->FindElement("axis"); + string axis; + while (axis_element) { + axis = axis_element->GetAttributeValue("name"); + if (axis == "LIFT" || axis == "DRAG") { + if (axisType == atNone) axisType = atLiftDrag; + else if (axisType != atLiftDrag) { + cerr << endl << " Mixed aerodynamic axis systems have been used in the" + << " aircraft config file. (LIFT DRAG)" << endl; + } + } else if (axis == "SIDE") { + if (axisType != atNone && axisType != atLiftDrag && axisType != atAxialNormal) { + cerr << endl << " Mixed aerodynamic axis systems have been used in the" + << " aircraft config file. (SIDE)" << endl; + } + } else if (axis == "AXIAL" || axis == "NORMAL") { + if (axisType == atNone) axisType = atAxialNormal; + else if (axisType != atAxialNormal) { + cerr << endl << " Mixed aerodynamic axis systems have been used in the" + << " aircraft config file. (NORMAL AXIAL)" << endl; + } + } else if (axis == "X" || axis == "Y" || axis == "Z") { + if (axisType == atNone) axisType = atBodyXYZ; + else if (axisType != atBodyXYZ) { + cerr << endl << " Mixed aerodynamic axis systems have been used in the" + << " aircraft config file. (XYZ)" << endl; + } + } else if (axis != "ROLL" && axis != "PITCH" && axis != "YAW") { // error + cerr << endl << " An unknown axis type, " << axis << " has been specified" + << " in the aircraft configuration file." << endl; + exit(-1); + } + axis_element = document->FindNextElement("axis"); + } + if (axisType == atNone) { + axisType = atLiftDrag; + cerr << endl << " The aerodynamic axis system has been set by default" + << " to the Lift/Side/Drag system." << endl; + } +} + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -string FGAerodynamics::GetCoefficientStrings(string delimeter) +string FGAerodynamics::GetAeroFunctionStrings(const string& delimeter) const { - string CoeffStrings = ""; + string AeroFunctionStrings = ""; bool firstime = true; unsigned int axis, sd; - for (sd = 0; sd < variables.size(); sd++) { - if (firstime) { - firstime = false; - } else { - CoeffStrings += delimeter; + for (axis = 0; axis < 6; axis++) { + for (sd = 0; sd < AeroFunctions[axis].size(); sd++) { + if (firstime) { + firstime = false; + } else { + AeroFunctionStrings += delimeter; + } + AeroFunctionStrings += AeroFunctions[axis][sd]->GetName(); } - CoeffStrings += variables[sd]->GetName(); } - for (axis = 0; axis < 6; axis++) { - for (sd = 0; sd < Coeff[axis].size(); sd++) { - CoeffStrings += delimeter; - CoeffStrings += Coeff[axis][sd]->GetName(); + string FunctionStrings = FGModelFunctions::GetFunctionStrings(delimeter); + + if (FunctionStrings.size() > 0) { + if (AeroFunctionStrings.size() > 0) { + AeroFunctionStrings += delimeter + FunctionStrings; + } else { + AeroFunctionStrings = FunctionStrings; } } - return CoeffStrings; + + return AeroFunctionStrings; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -string FGAerodynamics::GetCoefficientValues(string delimeter) +string FGAerodynamics::GetAeroFunctionValues(const string& delimeter) const { - string SDValues = ""; - bool firstime = true; - unsigned int sd; + ostringstream buf; - for (sd = 0; sd < variables.size(); sd++) { - if (firstime) { - firstime = false; - } else { - SDValues += delimeter; + for (unsigned int axis = 0; axis < 6; axis++) { + for (unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) { + if (buf.tellp() > 0) buf << delimeter; + buf << AeroFunctions[axis][sd]->GetValue(); } - SDValues += variables[sd]->GetValueAsString(); } - for (unsigned int axis = 0; axis < 6; axis++) { - for (unsigned int sd = 0; sd < Coeff[axis].size(); sd++) { - SDValues += delimeter; - SDValues += Coeff[axis][sd]->GetValueAsString(); + string FunctionValues = FGModelFunctions::GetFunctionValues(delimeter); + + if (FunctionValues.size() > 0) { + if (buf.str().size() > 0) { + buf << delimeter << FunctionValues; + } else { + buf << FunctionValues; } } - return SDValues; + return buf.str(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -296,21 +431,21 @@ void FGAerodynamics::bind(void) PropertyManager->Tie("moments/n-aero-lbsft", this,3, (PMF)&FGAerodynamics::GetMoments); PropertyManager->Tie("forces/fwx-aero-lbs", this,1, - (PMF)&FGAerodynamics::GetvFs); + (PMF)&FGAerodynamics::GetvFw); PropertyManager->Tie("forces/fwy-aero-lbs", this,2, - (PMF)&FGAerodynamics::GetvFs); + (PMF)&FGAerodynamics::GetvFw); PropertyManager->Tie("forces/fwz-aero-lbs", this,3, - (PMF)&FGAerodynamics::GetvFs); + (PMF)&FGAerodynamics::GetvFw); PropertyManager->Tie("forces/lod-norm", this, &FGAerodynamics::GetLoD); PropertyManager->Tie("aero/cl-squared", this, &FGAerodynamics::GetClSquared); PropertyManager->Tie("aero/qbar-area", &qbar_area); - PropertyManager->Tie("aero/alpha-max-deg", this, + PropertyManager->Tie("aero/alpha-max-rad", this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax, true); - PropertyManager->Tie("aero/alpha-min-deg", this, + PropertyManager->Tie("aero/alpha-min-rad", this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin, true); @@ -326,34 +461,6 @@ void FGAerodynamics::bind(void) &FGAerodynamics::GetHysteresisParm); } -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -void FGAerodynamics::unbind(void) -{ - unsigned i,j; - - PropertyManager->Untie("forces/fbx-aero-lbs"); - PropertyManager->Untie("forces/fby-aero-lbs"); - PropertyManager->Untie("forces/fbz-aero-lbs"); - PropertyManager->Untie("moments/l-aero-lbsft"); - PropertyManager->Untie("moments/m-aero-lbsft"); - PropertyManager->Untie("moments/n-aero-lbsft"); - PropertyManager->Untie("forces/fwx-aero-lbs"); - PropertyManager->Untie("forces/fwy-aero-lbs"); - PropertyManager->Untie("forces/fwz-aero-lbs"); - PropertyManager->Untie("forces/lod-norm"); - PropertyManager->Untie("aero/cl-squared"); - PropertyManager->Untie("aero/qbar-area"); - PropertyManager->Untie("aero/alpha-max-deg"); - PropertyManager->Untie("aero/alpha-min-deg"); - PropertyManager->Untie("aero/bi2vel"); - PropertyManager->Untie("aero/ci2vel"); - PropertyManager->Untie("aero/alpha-wing-rad"); - PropertyManager->Untie("aero/stall-hyst-norm"); - PropertyManager->Untie("systems/stall-warn-norm"); - -} - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // The bitmasked value choices are as follows: // unset: In this case (the default) JSBSim would only print @@ -379,7 +486,20 @@ void FGAerodynamics::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 2) { // Loader - cout << endl << " Aerodynamics: " << endl; + switch (axisType) { + case (atLiftDrag): + cout << endl << " Aerodynamics (Lift|Side|Drag axes):" << endl << endl; + break; + case (atAxialNormal): + cout << endl << " Aerodynamics (Axial|Side|Normal axes):" << endl << endl; + break; + case (atBodyXYZ): + cout << endl << " Aerodynamics (X|Y|Z axes):" << endl << endl; + break; + case (atNone): + cout << endl << " Aerodynamics (undefined axes):" << endl << endl; + break; + } } } if (debug_lvl & 2 ) { // Instantiation/Destruction notification