first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
// AIWakeGroup.hxx -- Group of AI wake meshes for the computation of the induced
// wake.
//
// Written by Bertrand Coconnier, started April 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <cmath>
#include <vector>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include <simgear/math/SGGeod.hxx>
#include <simgear/math/SGGeoc.hxx>
#include <simgear/math/SGQuat.hxx>
#include "Main/globals.hxx"
#include "AIModel/performancedata.hxx"
#include "AIModel/AIAircraft.hxx"
#include "FDM/AIWake/AIWakeGroup.hxx"
AIWakeGroup::AIWakeGroup(void)
{
SGPropertyNode* _props = globals->get_props();
_density_slugft = _props->getNode("environment/density-slugft3", true);
}
void AIWakeGroup::AddAI(FGAIAircraft* ai)
{
int id = ai->getID();
PerformanceData* perfData = ai->getPerformance();
if (_aiWakeData.find(id) == _aiWakeData.end()) {
double span = perfData->wingSpan();
double chord = perfData->wingChord();
_aiWakeData[id] = AIWakeData(new WakeMesh(span, chord,
std::string("AI:") + ai->_getName()));
SG_LOG(SG_FLIGHT, SG_DEV_ALERT,
"Created mesh for " << ai->_getName() << " ID: #" << id);
}
AIWakeData& data = _aiWakeData[id];
data.visited = true;
data.position = ai->getCartPos() * SG_METER_TO_FEET;
SGGeoc geoc = SGGeoc::fromCart(data.position);
SGQuatd Te2l = SGQuatd::fromLonLatRad(geoc.getLongitudeRad(),
geoc.getLatitudeRad());
data.Te2b = Te2l * SGQuatd::fromYawPitchRollDeg(ai->_getHeading(),
ai->getPitch(), 0.0);
double hVel = ai->getSpeed()*SG_KT_TO_FPS;
double vVel = ai->getVerticalSpeedFPM()/60;
double gamma = atan2(vVel, hVel);
double vel = sqrt(hVel*hVel + vVel*vVel);
double weight = perfData->weight();
_aiWakeData[id].mesh->computeAoA(vel, _density_slugft->getDoubleValue(),
weight*cos(gamma));
}
SGVec3d AIWakeGroup::getInducedVelocityAt(const SGVec3d& pt) const
{
SGVec3d vi(0.,0.,0.);
for (auto item : _aiWakeData) {
AIWakeData& data = item.second;
if (!data.visited) continue;
SGVec3d at = data.Te2b.transform(pt - data.position);
vi += data.Te2b.backTransform(data.mesh->getInducedVelocityAt(at));
}
return vi;
}
void AIWakeGroup::gc(void)
{
for (auto it=_aiWakeData.begin(); it != _aiWakeData.end(); ++it) {
if (!(*it).second.visited) {
SG_LOG(SG_FLIGHT, SG_DEV_ALERT, "Deleted mesh for aircraft #"
<< (*it).first);
_aiWakeData.erase(it);
break; // erase has invalidated the iterator, other dead meshes (if
// any) will be erased at the next time steps.
}
}
for (auto &item : _aiWakeData) item.second.visited = false;
}

View File

@@ -0,0 +1,57 @@
// AIWakeGroup.hxx -- Group of AI wake meshes for the computation of the induced
// wake.
//
// Written by Bertrand Coconnier, started April 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AIWAKEGROUP_HXX
#define _FG_AIWAKEGROUP_HXX
#include <simgear/props/propsfwd.hxx>
#include "FDM/AIWake/WakeMesh.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class FGAIAircraft;
class AIWakeGroup {
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
struct AIWakeData {
explicit AIWakeData(WakeMesh* m = nullptr) : mesh(m) {}
SGVec3d position {SGVec3d::zeros()};
SGQuatd Te2b {SGQuatd::unit()};
bool visited {false};
WakeMesh_ptr mesh;
};
std::map<int, AIWakeData> _aiWakeData;
SGPropertyNode_ptr _density_slugft;
public:
AIWakeGroup(void);
void AddAI(FGAIAircraft* ai);
SGVec3d getInducedVelocityAt(const SGVec3d& pt) const;
// Garbage collection
void gc(void);
};
#endif

View File

@@ -0,0 +1,82 @@
// AeroElement.cxx -- Mesh element for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <cmath>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include "AeroElement.hxx"
AeroElement::AeroElement(const SGVec3d& n1, const SGVec3d& n2,
const SGVec3d& n3, const SGVec3d& n4)
{
p1 = 0.75*n2 + 0.25*n1;
p2 = 0.75*n3 + 0.25*n4;
normal = normalize(cross(n3 - n1, n2 - n4));
collocationPt = 0.375*(n1 + n4) + 0.125*(n2 + n3);
}
SGVec3d AeroElement::vortexInducedVel(const SGVec3d& p, const SGVec3d& n1,
const SGVec3d& n2) const
{
SGVec3d r0 = n2-n1, r1 = p-n1, r2 = p-n2;
SGVec3d v = cross(r1, r2);
double vSqrNorm = dot(v, v);
double r1SqrNorm = dot(r1, r1);
double r2SqrNorm = dot(r2, r2);
if ((vSqrNorm < 1E-6) || (r1SqrNorm < 1E-6) || (r2SqrNorm < 1E-6))
return SGVec3d::zeros();
r1 /= sqrt(r1SqrNorm);
r2 /= sqrt(r2SqrNorm);
v *= dot(r0, r1-r2) / (4.0*M_PI*vSqrNorm);
return v;
}
SGVec3d AeroElement::semiInfVortexInducedVel(const SGVec3d& point,
const SGVec3d& vEnd,
const SGVec3d& vDir) const
{
SGVec3d r = point-vEnd;
double rSqrNorm = dot(r, r);
double denom = rSqrNorm - dot(r, vDir)*sqrt(rSqrNorm);
if (fabs(denom) < 1E-6)
return SGVec3d::zeros();
SGVec3d v = cross(r, vDir);
v /= 4*M_PI*denom;
return v;
}
SGVec3d AeroElement::getInducedVelocity(const SGVec3d& p) const
{
const SGVec3d w(-1.,0.,0.);
SGVec3d v = semiInfVortexInducedVel(p, p1, w);
v -= semiInfVortexInducedVel(p, p2, w);
v += vortexInducedVel(p, p1, p2);
return v;
}

View File

@@ -0,0 +1,48 @@
// AeroElement.hxx -- Mesh element for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AEROELEMENT_HXX
#define _FG_AEROELEMENT_HXX
#include <simgear/math/SGMathFwd.hxx>
class AeroElement : public SGReferenced {
public:
AeroElement(const SGVec3d& n1, const SGVec3d& n2, const SGVec3d& n3,
const SGVec3d& n4);
const SGVec3d& getNormal(void) const { return normal; }
const SGVec3d& getCollocationPoint(void) const { return collocationPt; }
SGVec3d getBoundVortex(void) const { return p2 - p1; }
SGVec3d getBoundVortexMidPoint(void) const { return 0.5*(p1+p2); }
SGVec3d getInducedVelocity(const SGVec3d& p) const;
private:
SGVec3d vortexInducedVel(const SGVec3d& p, const SGVec3d& n1,
const SGVec3d& n2) const;
SGVec3d semiInfVortexInducedVel(const SGVec3d& point, const SGVec3d& vEnd,
const SGVec3d& vDir) const;
SGVec3d p1, p2, normal, collocationPt;
};
typedef SGSharedPtr<AeroElement> AeroElement_ptr;
#endif

View File

@@ -0,0 +1,96 @@
// AircraftMesh.cxx -- Mesh for the computation of the wake induced force and
// moment on an aircraft.
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <vector>
#include <cmath>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include <simgear/math/SGGeoc.hxx>
#include <simgear/math/SGGeod.hxx>
#include <simgear/math/SGQuat.hxx>
#include "AircraftMesh.hxx"
#include <FDM/flight.hxx>
#include "AIWakeGroup.hxx"
#include "AIModel/AIAircraft.hxx"
extern "C" {
#include "../LaRCsim/ls_matrix.h"
}
AircraftMesh::AircraftMesh(double _span, double _chord, const std::string& name)
: WakeMesh(_span, _chord, name)
{
collPt.resize(nelm, SGVec3d::zeros());
midPt.resize(nelm, SGVec3d::zeros());
}
void AircraftMesh::setPosition(const SGVec3d& _pos, const SGQuatd& orient)
{
SGVec3d pos = _pos * SG_METER_TO_FEET;
SGGeoc geoc = SGGeoc::fromCart(pos);
SGQuatd Te2l = SGQuatd::fromLonLatRad(geoc.getLongitudeRad(),
geoc.getLatitudeRad());
Te2b = Te2l * orient;
for (int i=0; i<nelm; ++i) {
SGVec3d pt = elements[i]->getCollocationPoint();
collPt[i] = pos + Te2b.backTransform(pt);
pt = elements[i]->getBoundVortexMidPoint();
midPt[i] = pos + Te2b.backTransform(pt);
}
}
SGVec3d AircraftMesh::GetForce(const AIWakeGroup& wg, const SGVec3d& vel,
double rho)
{
std::vector<double> rhs;
rhs.resize(nelm, 0.0);
for (int i=0; i<nelm; ++i)
rhs[i] = dot(elements[i]->getNormal(),
Te2b.transform(wg.getInducedVelocityAt(collPt[i])));
for (int i=1; i<=nelm; ++i) {
Gamma[i][1] = 0.0;
for (int k=1; k<=nelm; ++k)
Gamma[i][1] += influenceMtx[i][k]*rhs[k-1];
}
SGVec3d f(0.,0.,0.);
moment = SGVec3d::zeros();
for (int i=0; i<nelm; ++i) {
SGVec3d mp = elements[i]->getBoundVortexMidPoint();
SGVec3d v = Te2b.transform(wg.getInducedVelocityAt(midPt[i]));
v += getInducedVelocityAt(mp);
// The minus sign before vel to transform the aircraft velocity from the
// body frame to wind frame.
SGVec3d Fi = rho*Gamma[i+1][1]*cross(v-vel,
elements[i]->getBoundVortex());
f += Fi;
moment += cross(mp, Fi);
}
return f;
}

View File

@@ -0,0 +1,50 @@
// AircraftMesh.hxx -- Mesh for the computation of the wake induced force and
// moment on an aircraft.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AEROMESH_HXX
#define _FG_AEROMESH_HXX
#include "WakeMesh.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class FGAIAircraft;
class AIWakeGroup;
class AircraftMesh : public WakeMesh {
public:
AircraftMesh(double _span, double _chord, const std::string& name);
void setPosition(const SGVec3d& pos, const SGQuatd& orient);
SGVec3d GetForce(const AIWakeGroup& wg, const SGVec3d& vel, double rho);
const SGVec3d& GetMoment(void) const { return moment; };
private:
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
std::vector<SGVec3d> collPt, midPt;
SGQuatd Te2b;
SGVec3d moment;
};
typedef SGSharedPtr<AircraftMesh> AircraftMesh_ptr;
#endif

120
src/FDM/AIWake/WakeMesh.cxx Normal file
View File

@@ -0,0 +1,120 @@
// WakeMesh.cxx -- Mesh for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <vector>
#include <cmath>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include <simgear/debug/logstream.hxx>
#include "WakeMesh.hxx"
extern "C" {
#include "../LaRCsim/ls_matrix.h"
}
WakeMesh::WakeMesh(double _span, double _chord, const std::string& aircraft_name)
: nelm(10), span(_span), chord(_chord)
{
double y1 = -0.5*span;
double ds = span / nelm;
for(int i=0; i<nelm; i++) {
double y2 = y1 + ds;
elements.push_back(new AeroElement(SGVec3d(-chord, y1, 0.),
SGVec3d(0., y1, 0.),
SGVec3d(0., y2, 0.),
SGVec3d(-chord, y2, 0.)));
y1 = y2;
}
influenceMtx = nr_matrix(1, nelm, 1, nelm);
Gamma = nr_matrix(1, nelm, 1, 1);
for (int i=0; i < nelm; ++i) {
SGVec3d normal = elements[i]->getNormal();
SGVec3d collPt = elements[i]->getCollocationPoint();
for (int j=0; j < nelm; ++j)
influenceMtx[i+1][j+1] = dot(elements[j]->getInducedVelocity(collPt),
normal);
}
// Compute the inverse matrix with the Gauss-Jordan algorithm
int ret = nr_gaussj(influenceMtx, nelm, nullptr, 0);
if (ret) {
// Something went wrong with the matrix inversion.
// 1. Nullify the influence matrix to disable the current aircraft wake.
for (int i=0; i < nelm; ++i) {
for (int j=0; j < nelm; ++j)
influenceMtx[i+1][j+1] = 0.0;
}
// 2. Report the issue in the log.
SG_LOG(SG_FLIGHT, SG_WARN,
"Failed to build wake mesh. " << aircraft_name << " ( span:"
<< _span << ", chord:" << _chord << ") wake will be ignored.");
}
}
WakeMesh::~WakeMesh()
{
nr_free_matrix(influenceMtx, 1, nelm, 1, nelm);
nr_free_matrix(Gamma, 1, nelm, 1, 1);
}
double WakeMesh::computeAoA(double vel, double rho, double weight)
{
for (int i=1; i<=nelm; ++i) {
Gamma[i][1] = 0.0;
for (int k=1; k<=nelm; ++k)
Gamma[i][1] -= influenceMtx[i][k];
Gamma[i][1] *= vel;
}
// Compute the lift only. Velocities in the z direction are discarded
// because they only produce drag. This include the vertical component
// vel*sin(alpha) and the induced velocities on the bound vortex.
// This assumption is only valid for small angles.
SGVec3d f(0.,0.,0.);
SGVec3d v(-vel, 0.0, 0.0);
for (int i=0; i<nelm; ++i)
f += rho*Gamma[i+1][1]*cross(v, elements[i]->getBoundVortex());
double sinAlpha = -weight/f[2];
for (int i=1; i<=nelm; ++i)
Gamma[i][1] *= sinAlpha;
return asin(sinAlpha);
}
SGVec3d WakeMesh::getInducedVelocityAt(const SGVec3d& at) const
{
SGVec3d v(0., 0., 0.);
for (int i=0; i<nelm; ++i)
v += Gamma[i+1][1] * elements[i]->getInducedVelocity(at);
return v;
}

View File

@@ -0,0 +1,51 @@
// WakeMesh.hxx -- Mesh for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// 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
// 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
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_WAKEMESH_HXX
#define _FG_WAKEMESH_HXX
#include <string>
#include "AeroElement.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class WakeMesh : public SGReferenced {
public:
WakeMesh(double _span, double _chord, const std::string& aircraft_name);
virtual ~WakeMesh();
double computeAoA(double vel, double rho, double weight);
SGVec3d getInducedVelocityAt(const SGVec3d& at) const;
protected:
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
int nelm;
double span, chord;
std::vector<AeroElement_ptr> elements;
double **influenceMtx, **Gamma;
};
typedef SGSharedPtr<WakeMesh> WakeMesh_ptr;
#endif