TestSuite: Migration of the AeroMesh tests into the CppUnit infrastructure.
This commit is contained in:
parent
7aa034bc80
commit
acb3307a1a
17 changed files with 403 additions and 264 deletions
|
@ -26,7 +26,10 @@ PerformanceData::PerformanceData() :
|
|||
_vDescent(300.0),
|
||||
_vApproach(170.0),
|
||||
_vTouchdown(150.0),
|
||||
_vTaxi(15.0)
|
||||
_vTaxi(15.0),
|
||||
_wingSpan(100.0),
|
||||
_wingChord(12.0),
|
||||
_weight(90000.0)
|
||||
{
|
||||
_rollrate = 9.0; // degrees per second
|
||||
_maxbank = 30.0; // passenger friendly bank angle
|
||||
|
@ -69,9 +72,9 @@ void PerformanceData::initFromProps(SGPropertyNode *db_node)
|
|||
_vApproach = db_node->getDoubleValue("approach-speed-kts", _vApproach);
|
||||
_vTouchdown = db_node->getDoubleValue("touchdown-speed-kts", _vTouchdown);
|
||||
_vTaxi = db_node->getDoubleValue("taxi-speed-kts", _vTaxi);
|
||||
_wingSpan = db_node->getDoubleValue("geometry/wing/span-ft", 100.);
|
||||
_wingChord = db_node->getDoubleValue("geometry/wing/chord-ft", 12.);
|
||||
_weight = db_node->getDoubleValue("geometry/weight-lbs", 90000.);
|
||||
_wingSpan = db_node->getDoubleValue("geometry/wing/span-ft", _wingSpan);
|
||||
_wingChord = db_node->getDoubleValue("geometry/wing/chord-ft", _wingChord);
|
||||
_weight = db_node->getDoubleValue("geometry/weight-lbs", _weight);
|
||||
}
|
||||
|
||||
double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool maxBrakes) {
|
||||
|
|
|
@ -28,17 +28,9 @@
|
|||
#include <simgear/math/SGGeod.hxx>
|
||||
#include <simgear/math/SGGeoc.hxx>
|
||||
#include <simgear/math/SGQuat.hxx>
|
||||
#ifdef FG_TESTLIB
|
||||
#include <map>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include "tests/fakeAIAircraft.hxx"
|
||||
Globals g;
|
||||
Globals *globals = &g;
|
||||
#else
|
||||
#include "Main/globals.hxx"
|
||||
#include "AIModel/performancedata.hxx"
|
||||
#include "AIModel/AIAircraft.hxx"
|
||||
#endif
|
||||
#include "FDM/AIWake/AIWakeGroup.hxx"
|
||||
|
||||
AIWakeGroup::AIWakeGroup(void)
|
||||
|
|
|
@ -24,14 +24,16 @@
|
|||
#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 {
|
||||
#ifdef FG_TESTLIB
|
||||
public:
|
||||
#endif
|
||||
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
|
||||
|
||||
struct AIWakeData {
|
||||
explicit AIWakeData(WakeMesh* m = nullptr) : mesh(m) {}
|
||||
|
||||
|
|
|
@ -32,11 +32,7 @@
|
|||
#include "AircraftMesh.hxx"
|
||||
#include <FDM/flight.hxx>
|
||||
#include "AIWakeGroup.hxx"
|
||||
#ifndef FG_TESTLIB
|
||||
#include "AIModel/AIAircraft.hxx"
|
||||
#else
|
||||
#include "fakeAIAircraft.hxx"
|
||||
#endif
|
||||
extern "C" {
|
||||
#include "../LaRCsim/ls_matrix.h"
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "WakeMesh.hxx"
|
||||
|
||||
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
|
||||
class FGAIAircraft;
|
||||
class AIWakeGroup;
|
||||
|
||||
|
@ -36,9 +37,9 @@ public:
|
|||
SGVec3d GetForce(const AIWakeGroup& wg, const SGVec3d& vel, double rho);
|
||||
const SGVec3d& GetMoment(void) const { return moment; };
|
||||
|
||||
#ifndef FG_TESTLIB
|
||||
private:
|
||||
#endif
|
||||
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
|
||||
|
||||
std::vector<SGVec3d> collPt, midPt;
|
||||
SGQuatd Te2b;
|
||||
SGVec3d moment;
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include "AeroElement.hxx"
|
||||
|
||||
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
|
||||
|
||||
|
||||
class WakeMesh : public SGReferenced {
|
||||
public:
|
||||
WakeMesh(double _span, double _chord);
|
||||
|
@ -32,9 +35,9 @@ public:
|
|||
double computeAoA(double vel, double rho, double weight);
|
||||
SGVec3d getInducedVelocityAt(const SGVec3d& at) const;
|
||||
|
||||
#ifndef FG_TESTLIB
|
||||
protected:
|
||||
#endif
|
||||
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
|
||||
|
||||
int nelm;
|
||||
double span, chord;
|
||||
std::vector<AeroElement_ptr> elements;
|
||||
|
|
|
@ -61,6 +61,7 @@ endif()
|
|||
# Set up all test suites as CTests.
|
||||
|
||||
# System test suites.
|
||||
add_test(AeroMeshSystemTests ${TESTSUITE_OUTPUT_DIR}/run_test_suite --ctest -s AeroMeshTests)
|
||||
|
||||
# Unit test suites.
|
||||
add_test(AddonManagementUnitTests ${TESTSUITE_OUTPUT_DIR}/run_test_suite --ctest -u AddonManagementTests)
|
||||
|
|
|
@ -1,2 +1,47 @@
|
|||
#include "PrivateAccessorFDM.hxx"
|
||||
|
||||
#include <FDM/AIWake/AIWakeGroup.hxx>
|
||||
#include <FDM/AIWake/AircraftMesh.hxx>
|
||||
#include <FDM/AIWake/WakeMesh.hxx>
|
||||
|
||||
|
||||
// Access variables from src/FDM/AIWake/AIWakeGroup.hxx.
|
||||
WakeMesh*
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AIWakeGroup_aiWakeData(AIWakeGroup* instance, int i)
|
||||
{
|
||||
return instance->_aiWakeData[i].mesh;
|
||||
}
|
||||
|
||||
|
||||
// Access variables from src/FDM/AIWake/AircraftMesh.hxx.
|
||||
const std::vector<SGVec3d>
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AircraftMesh_collPt(AircraftMesh* instance) const
|
||||
{
|
||||
return instance->collPt;
|
||||
}
|
||||
|
||||
const std::vector<SGVec3d>
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_AircraftMesh_midPt(AircraftMesh* instance) const
|
||||
{
|
||||
return instance->midPt;
|
||||
}
|
||||
|
||||
|
||||
// Access variables from src/FDM/AIWake/WakeMesh.hxx.
|
||||
const std::vector<AeroElement_ptr>
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_elements(WakeMesh* instance) const
|
||||
{
|
||||
return instance->elements;
|
||||
}
|
||||
|
||||
int
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_nelm(WakeMesh* instance) const
|
||||
{
|
||||
return instance->nelm;
|
||||
}
|
||||
|
||||
double **
|
||||
FGTestApi::PrivateAccessor::FDM::Accessor::read_FDM_AIWake_WakeMesh_Gamma(WakeMesh* instance) const
|
||||
{
|
||||
return instance->Gamma;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
#ifndef _FG_PRIVATE_ACCESSOR_FDM_HXX
|
||||
#define _FG_PRIVATE_ACCESSOR_FDM_HXX
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
// Forward declarations for src/FDM/AIWake.
|
||||
class AIWakeData;
|
||||
class AIWakeGroup;
|
||||
class AircraftMesh;
|
||||
class WakeMesh;
|
||||
class AeroElement;
|
||||
typedef SGSharedPtr<AeroElement> AeroElement_ptr;
|
||||
|
||||
|
||||
namespace FGTestApi {
|
||||
namespace PrivateAccessor {
|
||||
namespace FDM {
|
||||
|
@ -8,6 +23,17 @@ namespace FDM {
|
|||
class Accessor
|
||||
{
|
||||
public:
|
||||
// Access variables from src/FDM/AIWake/AIWakeGroup.hxx.
|
||||
WakeMesh* read_FDM_AIWake_AIWakeGroup_aiWakeData(AIWakeGroup* instance, int i);
|
||||
|
||||
// Access variables from src/FDM/AIWake/AircraftMesh.hxx.
|
||||
const std::vector<SGVec3d> read_FDM_AIWake_AircraftMesh_collPt(AircraftMesh* instance) const;
|
||||
const std::vector<SGVec3d> read_FDM_AIWake_AircraftMesh_midPt(AircraftMesh* instance) const;
|
||||
|
||||
// Access variables from src/FDM/AIWake/WakeMesh.hxx.
|
||||
const std::vector<AeroElement_ptr> read_FDM_AIWake_WakeMesh_elements(WakeMesh* instance) const;
|
||||
int read_FDM_AIWake_WakeMesh_nelm(WakeMesh* instance) const;
|
||||
double **read_FDM_AIWake_WakeMesh_Gamma(WakeMesh* instance) const;
|
||||
};
|
||||
|
||||
} // End of namespace FDM.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Add each system test category.
|
||||
foreach( system_test_category
|
||||
FDM
|
||||
)
|
||||
|
||||
add_subdirectory(${system_test_category})
|
||||
|
|
12
test_suite/system_tests/FDM/CMakeLists.txt
Normal file
12
test_suite/system_tests/FDM/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
set(TESTSUITE_SOURCES
|
||||
${TESTSUITE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testAeroMesh.cxx
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
set(TESTSUITE_HEADERS
|
||||
${TESTSUITE_HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/testAeroMesh.hxx
|
||||
PARENT_SCOPE
|
||||
)
|
24
test_suite/system_tests/FDM/TestSuite.cxx
Normal file
24
test_suite/system_tests/FDM/TestSuite.cxx
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* This file is part of the program FlightGear.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "testAeroMesh.hxx"
|
||||
|
||||
|
||||
// Set up the unit tests.
|
||||
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AeroMeshTests, "System tests");
|
222
test_suite/system_tests/FDM/testAeroMesh.cxx
Normal file
222
test_suite/system_tests/FDM/testAeroMesh.cxx
Normal file
|
@ -0,0 +1,222 @@
|
|||
#include "testAeroMesh.hxx"
|
||||
|
||||
#include "test_suite/FGTestApi/globals.hxx"
|
||||
#include "test_suite/FGTestApi/PrivateAccessorFDM.hxx"
|
||||
#include "test_suite/FGTestApi/scene_graph.hxx"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/math/SGVec3.hxx>
|
||||
#include <simgear/math/SGGeod.hxx>
|
||||
#include <simgear/math/SGQuat.hxx>
|
||||
#include <simgear/math/SGGeoc.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <AIModel/AIAircraft.hxx>
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/performancedata.hxx>
|
||||
#include <AIModel/performancedb.hxx>
|
||||
#include "FDM/AIWake/AircraftMesh.hxx"
|
||||
#include "FDM/AIWake/AIWakeGroup.hxx"
|
||||
extern "C" {
|
||||
#include "src/FDM/LaRCsim/ls_matrix.h"
|
||||
}
|
||||
|
||||
#include "FDM/JSBSim/math/FGLocation.h"
|
||||
#include "FDM/JSBSim/math/FGQuaternion.h"
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace JSBSim;
|
||||
|
||||
double rho = 2.0E-3;
|
||||
|
||||
|
||||
// Set up function for each test.
|
||||
void AeroMeshTests::setUp()
|
||||
{
|
||||
FGTestApi::setUp::initTestGlobals("aeromesh");
|
||||
FGTestApi::setUp::initScenery();
|
||||
globals->get_props()->getNode("environment/density-slugft3", true)
|
||||
->setDoubleValue(rho);
|
||||
}
|
||||
|
||||
|
||||
// Clean up after each test.
|
||||
void AeroMeshTests::tearDown()
|
||||
{
|
||||
FGTestApi::tearDown::shutdownTestGlobals();
|
||||
}
|
||||
|
||||
|
||||
void AeroMeshTests::testLiftComputation()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
AircraftMesh_ptr mesh = new AircraftMesh(b, c);
|
||||
SGGeod geodPos = SGGeod::fromDeg(0.0, 0.0);
|
||||
SGVec3d pos;
|
||||
double vel = 100.;
|
||||
double weight = 50.;
|
||||
|
||||
SGGeodesy::SGGeodToCart(geodPos, pos);
|
||||
mesh->setPosition(pos, SGQuatd::unit());
|
||||
|
||||
SGPropertyNode* props = globals->get_props()->getNode("ai/models", true);
|
||||
props->setDoubleValue("acceleration-kts-hour", 0.0);
|
||||
props->setDoubleValue("deceleration-kts-hour", 0.0);
|
||||
props->setDoubleValue("climbrate-fpm", 0.0);
|
||||
props->setDoubleValue("decentrate-fpm", 0.0);
|
||||
props->setDoubleValue("rotate-speed-kts", 0.0);
|
||||
props->setDoubleValue("takeoff-speed-kts", 0.0);
|
||||
props->setDoubleValue("climb-speed-kts", 0.0);
|
||||
props->setDoubleValue("cruise-speed-kts", 0.0);
|
||||
props->setDoubleValue("decent-speed-kts", 0.0);
|
||||
props->setDoubleValue("approach-speed-kts", 0.0);
|
||||
props->setDoubleValue("touchdown-speed-kts", 0.0);
|
||||
props->setDoubleValue("taxi-speed-kts", 0.0);
|
||||
props->setDoubleValue("geometry/wing/span-ft", b);
|
||||
props->setDoubleValue("geometry/wing/chord-ft", c);
|
||||
props->setDoubleValue("geometry/weight-lbs", weight);
|
||||
|
||||
globals->add_new_subsystem<PerformanceDB>(SGSubsystemMgr::POST_FDM);
|
||||
globals->get_subsystem<PerformanceDB>()->bind();
|
||||
globals->get_subsystem<PerformanceDB>()->init();
|
||||
|
||||
FGAIManager *aiManager = new FGAIManager;
|
||||
FGAIAircraft *ai = new FGAIAircraft;
|
||||
ai->setGeodPos(geodPos);
|
||||
ai->setSpeed(vel * SG_FPS_TO_KT);
|
||||
ai->setPerformance("", "jet_transport");
|
||||
ai->getPerformance()->initFromProps(props);
|
||||
aiManager->attach(ai);
|
||||
|
||||
AIWakeGroup wg;
|
||||
wg.AddAI(ai);
|
||||
|
||||
SGVec3d force = mesh->GetForce(wg, SGVec3d(vel, 0., 0.), rho);
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(force[1], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(force[2], -weight, 1e-9);
|
||||
|
||||
SGVec3d moment = mesh->GetMoment();
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[0], 0.0, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[1], -0.5*weight, 1e-9);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(moment[2], 0.0, 1e-9);
|
||||
|
||||
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
|
||||
|
||||
for (int i=1; i<= accessor.read_FDM_AIWake_WakeMesh_nelm(mesh); ++i)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_WakeMesh_Gamma(accessor.read_FDM_AIWake_AIWakeGroup_aiWakeData(&wg, 1))[i][1],
|
||||
accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1], 1e-9);
|
||||
}
|
||||
|
||||
|
||||
void AeroMeshTests::testFourierLiftingLine()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
double vel = 100.;
|
||||
double weight = 50.;
|
||||
|
||||
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
|
||||
|
||||
WakeMesh_ptr mesh = new WakeMesh(b, c);
|
||||
int N = accessor.read_FDM_AIWake_WakeMesh_nelm(mesh);
|
||||
double **mtx = nr_matrix(1, N, 1, N);
|
||||
double **coef = nr_matrix(1, N, 1, 1);
|
||||
|
||||
mesh->computeAoA(vel, rho, weight);
|
||||
|
||||
for (int m=1; m<=N; ++m) {
|
||||
double vm = M_PI*m/(N+1);
|
||||
double sm = sin(vm);
|
||||
coef[m][1] = c*M_PI/(2*b);
|
||||
for (int n=1; n<=N; ++n)
|
||||
mtx[m][n] = sin(n*vm)*(1.0+coef[m][1]*n/sm);
|
||||
}
|
||||
|
||||
nr_gaussj(mtx, N, coef, 1);
|
||||
|
||||
double S = b*c;
|
||||
double AR = b*b/S;
|
||||
double lift = 0.5*rho*S*vel*vel*coef[1][1]*M_PI*AR;
|
||||
double sinAlpha = weight / lift;
|
||||
lift *= sinAlpha;
|
||||
|
||||
cout << "y, Lift (Fourier), Lift (VLM), Corrected lift (VLM)" << endl;
|
||||
|
||||
for (int i=1; i<=N; ++i) {
|
||||
double y = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i-1]->getBoundVortexMidPoint()[1];
|
||||
double theta = acos(2.0*y/b);
|
||||
double gamma = 0.0;
|
||||
for (int n=1; n<=N; ++n)
|
||||
gamma += coef[n][1]*sin(n*theta);
|
||||
|
||||
gamma *= 2.0*b*vel*sinAlpha;
|
||||
|
||||
cout << y << ", " << gamma << ", " << accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1] << ", "
|
||||
<< accessor.read_FDM_AIWake_WakeMesh_Gamma(mesh)[i][1] / gamma - 1.0 << endl;
|
||||
}
|
||||
|
||||
nr_free_matrix(mtx, 1, N, 1, N);
|
||||
nr_free_matrix(coef, 1, N, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
void AeroMeshTests::testFrameTransformations()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
double yaw = 80. * SGD_DEGREES_TO_RADIANS;
|
||||
double pitch = 5. * SGD_DEGREES_TO_RADIANS;
|
||||
double roll = -10. * SGD_DEGREES_TO_RADIANS;
|
||||
SGQuatd orient = SGQuatd::fromYawPitchRoll(yaw, pitch, roll);
|
||||
SGGeod geodPos = SGGeod::fromDeg(45.0, 10.0);
|
||||
SGVec3d pos;
|
||||
|
||||
SGGeodesy::SGGeodToCart(geodPos, pos);
|
||||
SGGeoc geoc = SGGeoc::fromCart(pos);
|
||||
|
||||
FGLocation loc(geoc.getLongitudeRad(),
|
||||
geoc.getLatitudeRad(),
|
||||
geoc.getRadiusFt());
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[0] * SG_METER_TO_FEET, loc(1), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[1] * SG_METER_TO_FEET, loc(2), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(pos[2] * SG_METER_TO_FEET, loc(3), 1e-7);
|
||||
|
||||
AircraftMesh_ptr mesh = new AircraftMesh(b, c);
|
||||
mesh->setPosition(pos, orient);
|
||||
|
||||
FGQuaternion qJ(roll, pitch, yaw);
|
||||
FGMatrix33 Tb2l = qJ.GetTInv();
|
||||
FGColumnVector3 refPos = loc.GetTec2l() * loc;
|
||||
|
||||
for (int i=0; i < 4; ++i)
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(orient(i), qJ((i+1) % 4 + 1), 1e-9);
|
||||
|
||||
auto accessor = FGTestApi::PrivateAccessor::FDM::Accessor();
|
||||
|
||||
for (int i=0; i < accessor.read_FDM_AIWake_WakeMesh_nelm(mesh); ++i) {
|
||||
SGVec3d pt = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i]->getBoundVortexMidPoint();
|
||||
FGColumnVector3 ptJ(pt[0], pt[1], pt[2]);
|
||||
FGColumnVector3 p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][0], p(1), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][1], p(2), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_midPt(mesh)[i][2], p(3), 1e-7);
|
||||
|
||||
pt = accessor.read_FDM_AIWake_WakeMesh_elements(mesh)[i]->getCollocationPoint();
|
||||
ptJ.InitMatrix(pt[0], pt[1], pt[2]);
|
||||
p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][0], p(1), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][1], p(2), 1e-7);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(accessor.read_FDM_AIWake_AircraftMesh_collPt(mesh)[i][2], p(3), 1e-7);
|
||||
}
|
||||
}
|
52
test_suite/system_tests/FDM/testAeroMesh.hxx
Normal file
52
test_suite/system_tests/FDM/testAeroMesh.hxx
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Edward d'Auvergne
|
||||
*
|
||||
* This file is part of the program FlightGear.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_AERO_MESH_SYSTEM_TESTS_HXX
|
||||
#define _FG_AERO_MESH_SYSTEM_TESTS_HXX
|
||||
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
|
||||
// The system tests.
|
||||
class AeroMeshTests : public CppUnit::TestFixture
|
||||
{
|
||||
// Set up the test suite.
|
||||
CPPUNIT_TEST_SUITE(AeroMeshTests);
|
||||
CPPUNIT_TEST(testFourierLiftingLine);
|
||||
CPPUNIT_TEST(testFrameTransformations);
|
||||
CPPUNIT_TEST(testLiftComputation);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
// Set up function for each test.
|
||||
void setUp();
|
||||
|
||||
// Clean up after each test.
|
||||
void tearDown();
|
||||
|
||||
// The tests.
|
||||
void testFourierLiftingLine();
|
||||
void testFrameTransformations();
|
||||
void testLiftComputation();
|
||||
};
|
||||
|
||||
#endif // _FG_AERO_MESH_SYSTEM_TESTS_HXX
|
|
@ -115,16 +115,3 @@ macro(flightgear_test name sources)
|
|||
target_link_libraries(${name} fgtestlib)
|
||||
add_test(${name} ${EXECUTABLE_OUTPUT_PATH}/${name})
|
||||
endmacro()
|
||||
|
||||
add_executable(testAeroMesh testAeroMesh.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/AIWake/AeroElement.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/AIWake/AircraftMesh.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/AIWake/WakeMesh.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/AIWake/AIWakeGroup.cxx
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/LaRCsim/ls_matrix.c
|
||||
)
|
||||
set_target_properties (testAeroMesh PROPERTIES COMPILE_DEFINITIONS "FG_TESTLIB")
|
||||
target_include_directories(testAeroMesh PRIVATE ${CMAKE_SOURCE_DIR}/tests
|
||||
${CMAKE_SOURCE_DIR}/src/FDM/JSBSim)
|
||||
target_link_libraries(testAeroMesh SimGearCore JSBSim)
|
||||
add_test(testAeroMesh ${EXECUTABLE_OUTPUT_PATH}/testAeroMesh)
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
#ifndef FAKEFGAIAIRCRAFT
|
||||
#define FAKEFGAIAIRCRAFT
|
||||
|
||||
struct Globals {
|
||||
Globals(void) { root.getNode("environment/density-slugft3", true); }
|
||||
SGPropertyNode* get_props(void) { return &root; }
|
||||
SGPropertyNode root;
|
||||
};
|
||||
|
||||
struct PerformanceData {
|
||||
double _span, _chord, _weight;
|
||||
PerformanceData(double s, double c, double w)
|
||||
: _span(s), _chord(c), _weight(w) {}
|
||||
double wingSpan(void) const { return _span; }
|
||||
double wingChord(void) const { return _chord; }
|
||||
double weight(void) const { return _weight; }
|
||||
};
|
||||
|
||||
class FGAIAircraft {
|
||||
public:
|
||||
explicit FGAIAircraft(int id)
|
||||
: _id(id), _pos(SGVec3d::zeros()), _heading(0.0), _pitch(0.0),
|
||||
_vel(0.0), _perf(0.0, 0.0, 0.0) {}
|
||||
void setPosition(const SGVec3d& pos) { _pos = pos; }
|
||||
void setOrientation(double heading, double pitch)
|
||||
{
|
||||
_heading = heading;
|
||||
_pitch = pitch;
|
||||
}
|
||||
void setGeom(double s, double c, double w)
|
||||
{
|
||||
_perf._span = s;
|
||||
_perf._chord = c;
|
||||
_perf._weight = w;
|
||||
}
|
||||
void setVelocity(double v) { _vel = v; }
|
||||
const SGVec3d& getCartPos(void) const { return _pos; }
|
||||
double _getHeading(void) const { return _heading; }
|
||||
double getPitch(void) const { return _pitch; }
|
||||
int getID(void) const { return _id; }
|
||||
PerformanceData* getPerformance(void) { return &_perf; }
|
||||
std::string getAcType(void) const { return "The AC type"; }
|
||||
std::string _getName(void) const { return "The AC name"; }
|
||||
double getSpeed(void) const { return _vel * SG_FPS_TO_KT; }
|
||||
double getVerticalSpeed(void) const { return 0.0; }
|
||||
double getAltitude(void) const { return 3000.; }
|
||||
double getRoll(void) const { return 0.0; }
|
||||
|
||||
private:
|
||||
int _id;
|
||||
SGVec3d _pos;
|
||||
double _heading, _pitch, _vel;
|
||||
PerformanceData _perf;
|
||||
};
|
||||
|
||||
extern Globals* globals;
|
||||
|
||||
#endif
|
|
@ -1,170 +0,0 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/math/SGVec3.hxx>
|
||||
#include <simgear/math/SGGeod.hxx>
|
||||
#include <simgear/math/SGQuat.hxx>
|
||||
#include <simgear/math/SGGeoc.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include "fakeAIAircraft.hxx"
|
||||
#include "FDM/AIWake/AircraftMesh.hxx"
|
||||
#include "FDM/AIWake/AIWakeGroup.hxx"
|
||||
extern "C" {
|
||||
#include "src/FDM/LaRCsim/ls_matrix.h"
|
||||
}
|
||||
|
||||
#include "FDM/JSBSim/math/FGLocation.h"
|
||||
#include "FDM/JSBSim/math/FGQuaternion.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace JSBSim;
|
||||
|
||||
double rho = 2.0E-3;
|
||||
|
||||
void testLiftComputation()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
AircraftMesh_ptr mesh = new AircraftMesh(b, c);
|
||||
SGGeod geodPos = SGGeod::fromDeg(0.0, 0.0);
|
||||
SGVec3d pos;
|
||||
double vel = 100.;
|
||||
double weight = 50.;
|
||||
|
||||
SGGeodesy::SGGeodToCart(geodPos, pos);
|
||||
mesh->setPosition(pos, SGQuatd::unit());
|
||||
|
||||
FGAIAircraft ai(1);
|
||||
ai.setPosition(pos);
|
||||
ai.setGeom(b, c, weight);
|
||||
ai.setVelocity(vel);
|
||||
|
||||
AIWakeGroup wg;
|
||||
wg.AddAI(&ai);
|
||||
|
||||
SGVec3d force = mesh->GetForce(wg, SGVec3d(vel, 0., 0.), rho);
|
||||
|
||||
SG_CHECK_EQUAL_EP(force[1], 0.0);
|
||||
SG_CHECK_EQUAL_EP(force[2], -weight);
|
||||
|
||||
SGVec3d moment = mesh->GetMoment();
|
||||
SG_CHECK_EQUAL_EP(moment[0], 0.0);
|
||||
SG_CHECK_EQUAL_EP(moment[1], -0.5*weight);
|
||||
SG_CHECK_EQUAL_EP(moment[2], 0.0);
|
||||
|
||||
for (int i=1; i<= mesh->nelm; ++i)
|
||||
SG_CHECK_EQUAL_EP(wg._aiWakeData[1].mesh->Gamma[i][1],
|
||||
mesh->Gamma[i][1]);
|
||||
}
|
||||
|
||||
void testFourierLiftingLine()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
double vel = 100.;
|
||||
double weight = 50.;
|
||||
|
||||
WakeMesh_ptr mesh = new WakeMesh(b, c);
|
||||
int N = mesh->nelm;
|
||||
double **mtx = nr_matrix(1, N, 1, N);
|
||||
double **coef = nr_matrix(1, N, 1, 1);
|
||||
|
||||
mesh->computeAoA(vel, rho, weight);
|
||||
|
||||
for (int m=1; m<=N; ++m) {
|
||||
double vm = M_PI*m/(N+1);
|
||||
double sm = sin(vm);
|
||||
coef[m][1] = c*M_PI/(2*b);
|
||||
for (int n=1; n<=N; ++n)
|
||||
mtx[m][n] = sin(n*vm)*(1.0+coef[m][1]*n/sm);
|
||||
}
|
||||
|
||||
nr_gaussj(mtx, N, coef, 1);
|
||||
|
||||
double S = b*c;
|
||||
double AR = b*b/S;
|
||||
double lift = 0.5*rho*S*vel*vel*coef[1][1]*M_PI*AR;
|
||||
double sinAlpha = weight / lift;
|
||||
lift *= sinAlpha;
|
||||
|
||||
cout << "y, Lift (Fourier), Lift (VLM), Corrected lift (VLM)" << endl;
|
||||
|
||||
for (int i=1; i<=N; ++i) {
|
||||
double y = mesh->elements[i-1]->getBoundVortexMidPoint()[1];
|
||||
double theta = acos(2.0*y/b);
|
||||
double gamma = 0.0;
|
||||
for (int n=1; n<=N; ++n)
|
||||
gamma += coef[n][1]*sin(n*theta);
|
||||
|
||||
gamma *= 2.0*b*vel*sinAlpha;
|
||||
|
||||
cout << y << ", " << gamma << ", " << mesh->Gamma[i][1] << ", "
|
||||
<< mesh->Gamma[i][1] / gamma - 1.0 << endl;
|
||||
}
|
||||
|
||||
nr_free_matrix(mtx, 1, N, 1, N);
|
||||
nr_free_matrix(coef, 1, N, 1, 1);
|
||||
}
|
||||
|
||||
void testFrameTransformations()
|
||||
{
|
||||
double b = 10.0;
|
||||
double c = 2.0;
|
||||
double yaw = 80. * SGD_DEGREES_TO_RADIANS;
|
||||
double pitch = 5. * SGD_DEGREES_TO_RADIANS;
|
||||
double roll = -10. * SGD_DEGREES_TO_RADIANS;
|
||||
SGQuatd orient = SGQuatd::fromYawPitchRoll(yaw, pitch, roll);
|
||||
SGGeod geodPos = SGGeod::fromDeg(45.0, 10.0);
|
||||
SGVec3d pos;
|
||||
|
||||
SGGeodesy::SGGeodToCart(geodPos, pos);
|
||||
SGGeoc geoc = SGGeoc::fromCart(pos);
|
||||
|
||||
FGLocation loc(geoc.getLongitudeRad(),
|
||||
geoc.getLatitudeRad(),
|
||||
geoc.getRadiusFt());
|
||||
|
||||
SG_CHECK_EQUAL_EP(pos[0] * SG_METER_TO_FEET, loc(1));
|
||||
SG_CHECK_EQUAL_EP(pos[1] * SG_METER_TO_FEET, loc(2));
|
||||
SG_CHECK_EQUAL_EP(pos[2] * SG_METER_TO_FEET, loc(3));
|
||||
|
||||
AircraftMesh_ptr mesh = new AircraftMesh(b, c);
|
||||
mesh->setPosition(pos, orient);
|
||||
|
||||
FGQuaternion qJ(roll, pitch, yaw);
|
||||
FGMatrix33 Tb2l = qJ.GetTInv();
|
||||
FGColumnVector3 refPos = loc.GetTec2l() * loc;
|
||||
|
||||
for (int i=0; i < 4; ++i)
|
||||
SG_CHECK_EQUAL_EP(orient(i), qJ((i+1) % 4 + 1));
|
||||
|
||||
for (int i=0; i < mesh->nelm; ++i) {
|
||||
SGVec3d pt = mesh->elements[i]->getBoundVortexMidPoint();
|
||||
FGColumnVector3 ptJ(pt[0], pt[1], pt[2]);
|
||||
FGColumnVector3 p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
|
||||
SG_CHECK_EQUAL_EP(mesh->midPt[i][0], p(1));
|
||||
SG_CHECK_EQUAL_EP(mesh->midPt[i][1], p(2));
|
||||
SG_CHECK_EQUAL_EP(mesh->midPt[i][2], p(3));
|
||||
|
||||
pt = mesh->elements[i]->getCollocationPoint();
|
||||
ptJ.InitMatrix(pt[0], pt[1], pt[2]);
|
||||
p = loc.GetTl2ec() * (refPos + Tb2l * ptJ);
|
||||
SG_CHECK_EQUAL_EP(mesh->collPt[i][0], p(1));
|
||||
SG_CHECK_EQUAL_EP(mesh->collPt[i][1], p(2));
|
||||
SG_CHECK_EQUAL_EP(mesh->collPt[i][2], p(3));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
globals->get_props()->getNode("environment/density-slugft3", false)
|
||||
->setDoubleValue(rho);
|
||||
|
||||
testFourierLiftingLine();
|
||||
testLiftComputation();
|
||||
testFrameTransformations();
|
||||
}
|
Loading…
Reference in a new issue