first commit
This commit is contained in:
102
src/FDM/AIWake/AIWakeGroup.cxx
Normal file
102
src/FDM/AIWake/AIWakeGroup.cxx
Normal 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;
|
||||
}
|
||||
57
src/FDM/AIWake/AIWakeGroup.hxx
Normal file
57
src/FDM/AIWake/AIWakeGroup.hxx
Normal 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
|
||||
82
src/FDM/AIWake/AeroElement.cxx
Normal file
82
src/FDM/AIWake/AeroElement.cxx
Normal 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;
|
||||
}
|
||||
48
src/FDM/AIWake/AeroElement.hxx
Normal file
48
src/FDM/AIWake/AeroElement.hxx
Normal 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
|
||||
96
src/FDM/AIWake/AircraftMesh.cxx
Normal file
96
src/FDM/AIWake/AircraftMesh.cxx
Normal 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;
|
||||
}
|
||||
50
src/FDM/AIWake/AircraftMesh.hxx
Normal file
50
src/FDM/AIWake/AircraftMesh.hxx
Normal 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
120
src/FDM/AIWake/WakeMesh.cxx
Normal 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;
|
||||
}
|
||||
51
src/FDM/AIWake/WakeMesh.hxx
Normal file
51
src/FDM/AIWake/WakeMesh.hxx
Normal 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
|
||||
186
src/FDM/CMakeLists.txt
Normal file
186
src/FDM/CMakeLists.txt
Normal file
@@ -0,0 +1,186 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
if(SP_FDMS)
|
||||
set(SP_FDM_SOURCES
|
||||
SP/ACMS.cxx
|
||||
SP/ADA.cxx
|
||||
SP/Balloon.cxx
|
||||
SP/BalloonSim.cpp
|
||||
SP/MagicCarpet.cxx
|
||||
SP/AISim.cpp
|
||||
)
|
||||
set (SP_FDM_HEADERS
|
||||
SP/ACMS.hxx
|
||||
SP/ADA.hxx
|
||||
SP/MagicCarpet.hxx
|
||||
SP/Balloon.h
|
||||
SP/BalloonSim.h
|
||||
SP/AISim.hpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(UIUC_SOURCES
|
||||
uiuc_1DdataFileReader.cpp
|
||||
uiuc_1Dinterpolation.cpp
|
||||
uiuc_2DdataFileReader.cpp
|
||||
uiuc_2Dinterpolation.cpp
|
||||
uiuc_3Dinterpolation.cpp
|
||||
uiuc_aerodeflections.cpp
|
||||
uiuc_alh_ap.cpp
|
||||
uiuc_auto_pilot.cpp
|
||||
uiuc_betaprobe.cpp
|
||||
uiuc_coef_drag.cpp
|
||||
uiuc_coef_lift.cpp
|
||||
uiuc_coef_pitch.cpp
|
||||
uiuc_coef_roll.cpp
|
||||
uiuc_coef_sideforce.cpp
|
||||
uiuc_coef_yaw.cpp
|
||||
uiuc_coefficients.cpp
|
||||
uiuc_controlInput.cpp
|
||||
uiuc_convert.cpp
|
||||
uiuc_engine.cpp
|
||||
uiuc_find_position.cpp
|
||||
uiuc_flapdata.cpp
|
||||
uiuc_fog.cpp
|
||||
uiuc_gear.cpp
|
||||
uiuc_get_flapper.cpp
|
||||
uiuc_getwind.cpp
|
||||
uiuc_hh_ap.cpp
|
||||
uiuc_ice.cpp
|
||||
uiuc_iceboot.cpp
|
||||
uiuc_iced_nonlin.cpp
|
||||
uiuc_icing_demo.cpp
|
||||
uiuc_initializemaps.cpp
|
||||
uiuc_map_CD.cpp
|
||||
uiuc_map_CL.cpp
|
||||
uiuc_map_CY.cpp
|
||||
uiuc_map_Cm.cpp
|
||||
uiuc_map_Cn.cpp
|
||||
uiuc_map_Croll.cpp
|
||||
uiuc_map_controlSurface.cpp
|
||||
uiuc_map_engine.cpp
|
||||
uiuc_map_fog.cpp
|
||||
uiuc_map_gear.cpp
|
||||
uiuc_map_geometry.cpp
|
||||
uiuc_map_ice.cpp
|
||||
uiuc_map_init.cpp
|
||||
uiuc_map_keyword.cpp
|
||||
uiuc_map_mass.cpp
|
||||
uiuc_map_misc.cpp
|
||||
uiuc_map_record1.cpp
|
||||
uiuc_map_record2.cpp
|
||||
uiuc_map_record3.cpp
|
||||
uiuc_map_record4.cpp
|
||||
uiuc_map_record5.cpp
|
||||
uiuc_map_record6.cpp
|
||||
uiuc_menu.cpp
|
||||
uiuc_menu_CD.cpp
|
||||
uiuc_menu_CL.cpp
|
||||
uiuc_menu_CY.cpp
|
||||
uiuc_menu_Cm.cpp
|
||||
uiuc_menu_Cn.cpp
|
||||
uiuc_menu_Croll.cpp
|
||||
uiuc_menu_controlSurface.cpp
|
||||
uiuc_menu_engine.cpp
|
||||
uiuc_menu_fog.cpp
|
||||
uiuc_menu_functions.cpp
|
||||
uiuc_menu_gear.cpp
|
||||
uiuc_menu_geometry.cpp
|
||||
uiuc_menu_ice.cpp
|
||||
uiuc_menu_init.cpp
|
||||
uiuc_menu_mass.cpp
|
||||
uiuc_menu_misc.cpp
|
||||
uiuc_menu_record.cpp
|
||||
uiuc_pah_ap.cpp
|
||||
uiuc_parsefile.cpp
|
||||
uiuc_rah_ap.cpp
|
||||
uiuc_recorder.cpp
|
||||
uiuc_warnings_errors.cpp
|
||||
uiuc_wrapper.cpp
|
||||
)
|
||||
|
||||
set(LARCSIM_SOURCES
|
||||
atmos_62.c
|
||||
basic_aero.c
|
||||
basic_engine.c
|
||||
basic_gear.c
|
||||
basic_init.c
|
||||
c172_aero.c
|
||||
c172_engine.c
|
||||
c172_gear.c
|
||||
c172_init.c
|
||||
cherokee_aero.c
|
||||
cherokee_engine.c
|
||||
cherokee_gear.c
|
||||
cherokee_init.c
|
||||
default_model_routines.c
|
||||
ls_accel.c
|
||||
ls_aux.c
|
||||
ls_geodesy.c
|
||||
ls_gravity.c
|
||||
ls_init.c
|
||||
ls_interface.c
|
||||
ls_model.c
|
||||
ls_step.c
|
||||
navion_aero.c
|
||||
navion_engine.c
|
||||
navion_gear.c
|
||||
navion_init.c
|
||||
uiuc_aero.c
|
||||
IO360.cxx
|
||||
LaRCsim.cxx
|
||||
LaRCsimIC.cxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
NullFDM.cxx
|
||||
UFO.cxx
|
||||
fdm_shell.cxx
|
||||
flight.cxx
|
||||
flightProperties.cxx
|
||||
TankProperties.cxx
|
||||
groundcache.cxx
|
||||
${SP_FDM_SOURCES}
|
||||
ExternalNet/ExternalNet.cxx
|
||||
ExternalPipe/ExternalPipe.cxx
|
||||
AIWake/AircraftMesh.cxx
|
||||
AIWake/WakeMesh.cxx
|
||||
AIWake/AeroElement.cxx
|
||||
AIWake/AIWakeGroup.cxx
|
||||
LaRCsim/ls_matrix.c
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
NullFDM.hxx
|
||||
TankProperties.hxx
|
||||
UFO.hxx
|
||||
fdm_shell.hxx
|
||||
flight.hxx
|
||||
flightProperties.hxx
|
||||
groundcache.hxx
|
||||
${SP_FDM_HEADERS}
|
||||
)
|
||||
|
||||
if(ENABLE_UIUC_MODEL)
|
||||
foreach(component ${UIUC_SOURCES})
|
||||
list(APPEND SOURCES "UIUCModel/${component}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(ENABLE_LARCSIM)
|
||||
foreach(component ${LARCSIM_SOURCES})
|
||||
list(APPEND SOURCES "LaRCsim/${component}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
flightgear_component(FDM "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_YASIM)
|
||||
add_subdirectory(YASim)
|
||||
endif()
|
||||
|
||||
|
||||
if(ENABLE_JSBSIM)
|
||||
add_subdirectory(JSBSim)
|
||||
endif()
|
||||
|
||||
243
src/FDM/ExternalNet/ExternalNet.cxx
Normal file
243
src/FDM/ExternalNet/ExternalNet.cxx
Normal file
@@ -0,0 +1,243 @@
|
||||
// ExternalNet.hxx -- an net interface to an external flight dynamics model
|
||||
//
|
||||
// Written by Curtis Olson, started November 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/lowlevel.hxx> // endian tests
|
||||
#include <simgear/io/sg_netBuffer.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Network/native_structs.hxx>
|
||||
#include <Network/native_ctrls.hxx>
|
||||
#include <Network/native_fdm.hxx>
|
||||
|
||||
#include "ExternalNet.hxx"
|
||||
|
||||
|
||||
class HTTPClient : public simgear::NetBufferChannel
|
||||
{
|
||||
bool done;
|
||||
SGTimeStamp start;
|
||||
simgear::NetChannelPoller poller;
|
||||
|
||||
public:
|
||||
HTTPClient(const char* host, int port, const char* path) : done(false)
|
||||
{
|
||||
open();
|
||||
connect(host, port);
|
||||
|
||||
char buffer[300];
|
||||
::snprintf(buffer, 299, "GET %s HTTP/1.0\r\n\r\n", path);
|
||||
buffer[299] = '\0';
|
||||
|
||||
bufferSend(buffer, strlen(buffer));
|
||||
|
||||
poller.addChannel(this);
|
||||
start.stamp();
|
||||
}
|
||||
|
||||
virtual void handleBufferRead(simgear::NetBuffer& buffer)
|
||||
{
|
||||
const char* s = buffer.getData();
|
||||
while (*s)
|
||||
fputc(*s++, stdout);
|
||||
|
||||
printf("done\n");
|
||||
|
||||
buffer.remove();
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
bool isDone() const { return done; }
|
||||
bool isDone(long usec) const
|
||||
{
|
||||
if (start + SGTimeStamp::fromUSec(usec) < SGTimeStamp::now()) {
|
||||
return true;
|
||||
} else {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
void poll(int timeout)
|
||||
{
|
||||
poller.poll(timeout);
|
||||
}
|
||||
};
|
||||
|
||||
FGExternalNet::FGExternalNet(double dt, string host, int dop, int dip, int cp)
|
||||
{
|
||||
// set_delta_t( dt );
|
||||
|
||||
valid = true;
|
||||
|
||||
data_in_port = dip;
|
||||
data_out_port = dop;
|
||||
cmd_port = cp;
|
||||
fdm_host = host;
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Setup client udp connection (sends data to remote fdm)
|
||||
|
||||
if (!data_client.open(false)) {
|
||||
SG_LOG(SG_FLIGHT, SG_ALERT, "Error opening client data channel");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// fire and forget
|
||||
data_client.setBlocking(false);
|
||||
|
||||
if (data_client.connect(fdm_host.c_str(), data_out_port) == -1) {
|
||||
printf("error connecting to %s:%d\n", fdm_host.c_str(), data_out_port);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Setup server udp connection (for receiving data)
|
||||
|
||||
if (!data_server.open(false)) {
|
||||
SG_LOG(SG_FLIGHT, SG_ALERT, "Error opening client server channel");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// disable blocking
|
||||
data_server.setBlocking(false);
|
||||
|
||||
// allowed to read from a broadcast addr
|
||||
// data_server.setBroadcast( true );
|
||||
|
||||
// if we bind to fdm_host = "" then we accept messages from
|
||||
// anyone.
|
||||
if (data_server.bind("", data_in_port) == -1) {
|
||||
printf("error binding to port %d\n", data_in_port);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FGExternalNet::~FGExternalNet()
|
||||
{
|
||||
data_client.close();
|
||||
data_server.close();
|
||||
}
|
||||
|
||||
|
||||
// Initialize the ExternalNet flight model, dt is the time increment
|
||||
// for each subsequent iteration through the EOM
|
||||
void FGExternalNet::init()
|
||||
{
|
||||
// cout << "FGExternalNet::init()" << endl;
|
||||
|
||||
// Explicitly call the superclass's
|
||||
// init method first.
|
||||
common_init();
|
||||
|
||||
double lon = fgGetDouble("/sim/presets/longitude-deg");
|
||||
double lat = fgGetDouble("/sim/presets/latitude-deg");
|
||||
double alt = fgGetDouble("/sim/presets/altitude-ft");
|
||||
double ground = get_Runway_altitude_m();
|
||||
double heading = fgGetDouble("/sim/presets/heading-deg");
|
||||
double speed = fgGetDouble("/sim/presets/airspeed-kt");
|
||||
|
||||
char cmd[256];
|
||||
|
||||
HTTPClient* http;
|
||||
sprintf(cmd, "/longitude-deg?value=%.8f", lon);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
sprintf(cmd, "/latitude-deg?value=%.8f", lat);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
sprintf(cmd, "/altitude-ft?value=%.8f", alt);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
sprintf(cmd, "/ground-m?value=%.8f", ground);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
sprintf(cmd, "/speed-kts?value=%.8f", speed);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
sprintf(cmd, "/heading-deg?value=%.8f", heading);
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
SG_LOG(SG_IO, SG_INFO, "before sending reset command.");
|
||||
|
||||
if (fgGetBool("/sim/presets/onground")) {
|
||||
sprintf(cmd, "/reset?value=ground");
|
||||
} else {
|
||||
sprintf(cmd, "/reset?value=air");
|
||||
}
|
||||
http = new HTTPClient(fdm_host.c_str(), cmd_port, cmd);
|
||||
while (!http->isDone(1000000)) http->poll(0);
|
||||
delete http;
|
||||
|
||||
SG_LOG(SG_IO, SG_INFO, "Remote FDM init() finished.");
|
||||
}
|
||||
|
||||
|
||||
// Run an iteration of the EOM.
|
||||
void FGExternalNet::update(double dt)
|
||||
{
|
||||
int length;
|
||||
int result;
|
||||
|
||||
if (is_suspended())
|
||||
return;
|
||||
|
||||
// Send control positions to remote fdm
|
||||
length = sizeof(ctrls);
|
||||
FGProps2Ctrls<FGNetCtrls>( globals->get_props(), &ctrls, true, true);
|
||||
if (data_client.send((char*)(&ctrls), length, 0) != length) {
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Error writing data.");
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_DEBUG, "wrote control data.");
|
||||
}
|
||||
|
||||
// Read next set of FDM data (blocking enabled to maintain 'sync')
|
||||
length = sizeof(fdm);
|
||||
while ((result = data_server.recv((char*)(&fdm), length, 0)) >= 0) {
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Success reading data.");
|
||||
FGFDM2Props<FGNetFDM>( globals->get_props(), &fdm);
|
||||
}
|
||||
}
|
||||
|
||||
// Register the subsystem.
|
||||
#if 0
|
||||
SGSubsystemMgr::Registrant<FGExternalNet> registrantFGExternalNet;
|
||||
#endif
|
||||
65
src/FDM/ExternalNet/ExternalNet.hxx
Normal file
65
src/FDM/ExternalNet/ExternalNet.hxx
Normal file
@@ -0,0 +1,65 @@
|
||||
// ExternalNet.hxx -- an net interface to an external flight dynamics model
|
||||
//
|
||||
// Written by Curtis Olson, started November 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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 _EXTERNAL_NET_HXX
|
||||
#define _EXTERNAL_NET_HXX
|
||||
|
||||
#include <simgear/timing/timestamp.hxx> // fine grained timing measurements
|
||||
#include <simgear/io/raw_socket.hxx>
|
||||
|
||||
#include <Network/net_ctrls.hxx>
|
||||
#include <Network/net_fdm.hxx>
|
||||
#include <FDM/flight.hxx>
|
||||
|
||||
|
||||
class FGExternalNet : public FGInterface
|
||||
{
|
||||
private:
|
||||
int data_in_port;
|
||||
int data_out_port;
|
||||
int cmd_port;
|
||||
std::string fdm_host;
|
||||
|
||||
simgear::Socket data_client;
|
||||
simgear::Socket data_server;
|
||||
|
||||
bool valid;
|
||||
|
||||
FGNetCtrls ctrls;
|
||||
FGNetFDM fdm;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
FGExternalNet( double dt, std::string host, int dop, int dip, int cp );
|
||||
|
||||
// Destructor
|
||||
~FGExternalNet();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "network"; }
|
||||
};
|
||||
|
||||
#endif // _EXTERNAL_NET_HXX
|
||||
589
src/FDM/ExternalPipe/ExternalPipe.cxx
Normal file
589
src/FDM/ExternalPipe/ExternalPipe.cxx
Normal file
@@ -0,0 +1,589 @@
|
||||
// ExternalPipe.cxx -- a "pipe" interface to an external flight dynamics model
|
||||
//
|
||||
// Written by Curtis Olson, started March 2003.
|
||||
//
|
||||
// Copyright (C) 2003 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MKFIFO
|
||||
# include <sys/types.h> // mkfifo() umask()
|
||||
# include <sys/stat.h> // mkfifo() umask()
|
||||
# include <errno.h> // perror()
|
||||
# include <unistd.h> // unlink()
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h> // FILE*, fopen(), fread(), fwrite(), et. al.
|
||||
#include <iostream> // for cout, endl
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/lowlevel.hxx> // endian tests
|
||||
#include <simgear/misc/strutils.hxx> // split()
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Network/native_structs.hxx>
|
||||
#include <Network/native_ctrls.hxx>
|
||||
#include <Network/native_fdm.hxx>
|
||||
#include <Scenery/scenery.hxx>
|
||||
|
||||
#include "ExternalPipe.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
static const int MAX_BUF = 32768;
|
||||
|
||||
FGExternalPipe::FGExternalPipe( double dt, string name, string protocol ) {
|
||||
valid = true;
|
||||
last_weight = 0.0;
|
||||
last_cg_offset = -9999.9;
|
||||
|
||||
buf = new char[MAX_BUF];
|
||||
|
||||
// clear property request list
|
||||
property_names.clear();
|
||||
nodes.clear();
|
||||
|
||||
#ifdef HAVE_MKFIFO
|
||||
fifo_name_1 = name + "1";
|
||||
fifo_name_2 = name + "2";
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT, "ExternalPipe Inited with " << name );
|
||||
|
||||
// Make the named pipe
|
||||
umask(0);
|
||||
int result;
|
||||
result = mkfifo( fifo_name_1.c_str(), 0644 );
|
||||
if ( result == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to create named pipe: "
|
||||
<< fifo_name_1 );
|
||||
perror( "ExternalPipe()" );
|
||||
}
|
||||
result = mkfifo( fifo_name_2.c_str(), 0644 );
|
||||
if ( result == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to create named pipe: "
|
||||
<< fifo_name_2 );
|
||||
perror( "ExternalPipe()" );
|
||||
}
|
||||
|
||||
pd1 = fopen( fifo_name_1.c_str(), "w" );
|
||||
if ( pd1 == NULL ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to open named pipe: " << fifo_name_1 );
|
||||
valid = false;
|
||||
}
|
||||
pd2 = fopen( fifo_name_2.c_str(), "r" );
|
||||
if ( pd2 == NULL ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to open named pipe: " << fifo_name_2 );
|
||||
valid = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
_protocol = protocol;
|
||||
|
||||
if ( _protocol != "binary" && _protocol != "property" ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Constructor(): Unknown ExternalPipe protocol."
|
||||
<< " Must be 'binary' or 'property'."
|
||||
<< " (assuming binary)" );
|
||||
_protocol = "binary";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FGExternalPipe::~FGExternalPipe() {
|
||||
delete [] buf;
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, "Closing up the ExternalPipe." );
|
||||
|
||||
#ifdef HAVE_MKFIFO
|
||||
// close
|
||||
int result;
|
||||
result = fclose( pd1 );
|
||||
if ( result ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to close named pipe: "
|
||||
<< fifo_name_1 );
|
||||
perror( "~FGExternalPipe()" );
|
||||
}
|
||||
result = fclose( pd2 );
|
||||
if ( result ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Unable to close named pipe: "
|
||||
<< fifo_name_2 );
|
||||
perror( "~FGExternalPipe()" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int write_binary( char cmd_type, FILE *pd, char *cmd, int len ) {
|
||||
#ifdef HAVE_MKFIFO
|
||||
char *buf = new char[len + 3];
|
||||
|
||||
// write 2 byte command length + command type + command
|
||||
unsigned char hi = (len + 1) / 256;
|
||||
unsigned char lo = (len + 1) - (hi * 256);
|
||||
|
||||
// cout << "len = " << len << " hi = " << (int)hi << " lo = "
|
||||
// << (int)lo << endl;
|
||||
|
||||
buf[0] = hi;
|
||||
buf[1] = lo;
|
||||
buf[2] = cmd_type;
|
||||
|
||||
memcpy( buf + 3, cmd, len );
|
||||
|
||||
if ( cmd_type == '1' ) {
|
||||
cout << "writing ";
|
||||
cout << (int)hi << " ";
|
||||
cout << (int)lo << " '";
|
||||
for ( int i = 2; i < len + 3; ++i ) {
|
||||
cout << buf[i];
|
||||
}
|
||||
cout << "' (" << cmd << ")" << endl;
|
||||
} else if ( cmd_type == '2' ) {
|
||||
// cout << "writing controls packet" << endl;
|
||||
} else {
|
||||
cout << "writing unknown command?" << endl;
|
||||
}
|
||||
|
||||
// for ( int i = 0; i < len + 3; ++i ) {
|
||||
// cout << " " << (int)buf[i];
|
||||
// }
|
||||
// cout << endl;
|
||||
|
||||
int result = fwrite( buf, len + 3, 1, pd );
|
||||
if ( result != 1 ) {
|
||||
perror( "write_binary()" );
|
||||
SG_LOG( SG_IO, SG_ALERT, "Write error to named pipe: " << pd );
|
||||
}
|
||||
// cout << "wrote " << len + 3 << " bytes." << endl;
|
||||
|
||||
delete [] buf;
|
||||
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int write_property( FILE *pd, char *cmd ) {
|
||||
|
||||
#ifdef HAVE_MKFIFO
|
||||
int len = strlen(cmd);
|
||||
char *buf = new char[len + 1];
|
||||
|
||||
memcpy( buf, cmd, len );
|
||||
buf[len] = '\n';
|
||||
|
||||
int result = fwrite( buf, len + 1, 1, pd );
|
||||
if ( result == len + 1 ) {
|
||||
perror( "write_property()" );
|
||||
SG_LOG( SG_IO, SG_ALERT, "Write error to named pipe: " << pd );
|
||||
}
|
||||
// cout << "wrote " << len + 1 << " bytes." << endl;
|
||||
|
||||
delete [] buf;
|
||||
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wrapper for the ExternalPipe flight model initialization. dt is
|
||||
// the time increment for each subsequent iteration through the EOM
|
||||
void FGExternalPipe::init() {
|
||||
// Explicitly call the superclass's
|
||||
// init method first.
|
||||
common_init();
|
||||
|
||||
if ( _protocol == "binary" ) {
|
||||
init_binary();
|
||||
} else if ( _protocol == "property" ) {
|
||||
init_property();
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Init(): Unknown ExternalPipe protocol."
|
||||
<< " Must be 'binary' or 'property'."
|
||||
<< " (assuming binary)" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize the ExternalPipe flight model using the binary protocol,
|
||||
// dt is the time increment for each subsequent iteration through the
|
||||
// EOM
|
||||
void FGExternalPipe::init_binary() {
|
||||
cout << "init_binary()" << endl;
|
||||
|
||||
double lon = fgGetDouble( "/sim/presets/longitude-deg" );
|
||||
double lat = fgGetDouble( "/sim/presets/latitude-deg" );
|
||||
double alt = fgGetDouble( "/sim/presets/altitude-ft" );
|
||||
double ground = get_Runway_altitude_m();
|
||||
double heading = fgGetDouble("/sim/presets/heading-deg");
|
||||
double speed = fgGetDouble( "/sim/presets/airspeed-kt" );
|
||||
double weight = fgGetDouble( "/sim/aircraft-weight-lbs" );
|
||||
double cg_offset = fgGetDouble( "/sim/aircraft-cg-offset-inches" );
|
||||
|
||||
char cmd[256];
|
||||
int result;
|
||||
|
||||
sprintf( cmd, "longitude-deg=%.8f", lon );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
sprintf( cmd, "latitude-deg=%.8f", lat );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
sprintf( cmd, "altitude-ft=%.8f", alt );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
sprintf( cmd, "ground-m=%.8f", ground );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
sprintf( cmd, "speed-kts=%.8f", speed );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
sprintf( cmd, "heading-deg=%.8f", heading );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
if ( weight > 1000.0 ) {
|
||||
sprintf( cmd, "aircraft-weight-lbs=%.2f", weight );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
}
|
||||
last_weight = weight;
|
||||
|
||||
if ( cg_offset > -5.0 || cg_offset < 5.0 ) {
|
||||
sprintf( cmd, "aircraft-cg-offset-inches=%.2f", cg_offset );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
}
|
||||
last_cg_offset = cg_offset;
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT, "before sending reset command." );
|
||||
|
||||
if( fgGetBool("/sim/presets/onground") ) {
|
||||
sprintf( cmd, "reset=ground" );
|
||||
} else {
|
||||
sprintf( cmd, "reset=air" );
|
||||
}
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
|
||||
fflush( pd1 );
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT, "Remote FDM init() finished." );
|
||||
|
||||
(void) result; // ignore result
|
||||
}
|
||||
|
||||
|
||||
// Initialize the ExternalPipe flight model using the property
|
||||
// protocol, dt is the time increment for each subsequent iteration
|
||||
// through the EOM
|
||||
void FGExternalPipe::init_property() {
|
||||
cout << "init_property()" << endl;
|
||||
|
||||
double lon = fgGetDouble( "/sim/presets/longitude-deg" );
|
||||
double lat = fgGetDouble( "/sim/presets/latitude-deg" );
|
||||
double alt = fgGetDouble( "/sim/presets/altitude-ft" );
|
||||
double ground = get_Runway_altitude_m();
|
||||
double heading = fgGetDouble("/sim/presets/heading-deg");
|
||||
double speed = fgGetDouble( "/sim/presets/airspeed-kt" );
|
||||
double weight = fgGetDouble( "/sim/aircraft-weight-lbs" );
|
||||
double cg_offset = fgGetDouble( "/sim/aircraft-cg-offset-inches" );
|
||||
|
||||
char cmd[256];
|
||||
int result;
|
||||
|
||||
sprintf( cmd, "init longitude-deg=%.8f", lon );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
sprintf( cmd, "init latitude-deg=%.8f", lat );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
sprintf( cmd, "init altitude-ft=%.8f", alt );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
sprintf( cmd, "init ground-m=%.8f", ground );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
sprintf( cmd, "init speed-kts=%.8f", speed );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
sprintf( cmd, "init heading-deg=%.8f", heading );
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
if ( weight > 1000.0 ) {
|
||||
sprintf( cmd, "init aircraft-weight-lbs=%.2f", weight );
|
||||
result = write_property( pd1, cmd );
|
||||
}
|
||||
last_weight = weight;
|
||||
|
||||
if ( cg_offset > -5.0 || cg_offset < 5.0 ) {
|
||||
sprintf( cmd, "init aircraft-cg-offset-inches=%.2f", cg_offset );
|
||||
result = write_property( pd1, cmd );
|
||||
}
|
||||
last_cg_offset = cg_offset;
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT, "before sending reset command." );
|
||||
|
||||
if( fgGetBool("/sim/presets/onground") ) {
|
||||
sprintf( cmd, "reset ground" );
|
||||
} else {
|
||||
sprintf( cmd, "reset air" );
|
||||
}
|
||||
result = write_property( pd1, cmd );
|
||||
|
||||
fflush( pd1 );
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT, "Remote FDM init() finished." );
|
||||
|
||||
(void) result; // ignore result
|
||||
}
|
||||
|
||||
|
||||
// Wrapper for the ExternalPipe update routines. dt is the time
|
||||
// increment for each subsequent iteration through the EOM
|
||||
void FGExternalPipe::update( double dt ) {
|
||||
if ( _protocol == "binary" ) {
|
||||
update_binary(dt);
|
||||
} else if ( _protocol == "property" ) {
|
||||
update_property(dt);
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Init(): Unknown ExternalPipe protocol."
|
||||
<< " Must be 'binary' or 'property'."
|
||||
<< " (assuming binary)" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Run an iteration of the EOM.
|
||||
void FGExternalPipe::update_binary( double dt ) {
|
||||
#ifdef HAVE_MKFIFO
|
||||
SG_LOG( SG_IO, SG_INFO, "Start FGExternalPipe::udpate_binary()" );
|
||||
|
||||
int length;
|
||||
int result;
|
||||
|
||||
if ( is_suspended() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int iterations = _calc_multiloop(dt);
|
||||
|
||||
double weight = fgGetDouble( "/sim/aircraft-weight-lbs" );
|
||||
static double last_weight = 0.0;
|
||||
if ( fabs( weight - last_weight ) > 0.01 ) {
|
||||
char cmd[256];
|
||||
sprintf( cmd, "aircraft-weight-lbs=%.2f", weight );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
}
|
||||
last_weight = weight;
|
||||
|
||||
double cg_offset = fgGetDouble( "/sim/aircraft-cg-offset-inches" );
|
||||
if ( fabs( cg_offset - last_cg_offset ) > 0.01 ) {
|
||||
char cmd[256];
|
||||
sprintf( cmd, "aircraft-cg-offset-inches=%.2f", cg_offset );
|
||||
result = write_binary( '1', pd1, cmd, strlen(cmd) );
|
||||
}
|
||||
last_cg_offset = cg_offset;
|
||||
|
||||
// Send control positions to remote fdm
|
||||
length = sizeof(ctrls);
|
||||
FGProps2Ctrls<FGNetCtrls>( globals->get_props(), &ctrls, true, false );
|
||||
char *ptr = buf;
|
||||
*((int *)ptr) = iterations;
|
||||
// cout << "iterations = " << iterations << endl;
|
||||
ptr += sizeof(int);
|
||||
memcpy( ptr, (char *)(&ctrls), length );
|
||||
// cout << "writing control structure, size = "
|
||||
// << length + sizeof(int) << endl;
|
||||
|
||||
result = write_binary( '2', pd1, buf, length + sizeof(int) );
|
||||
fflush( pd1 );
|
||||
|
||||
// Read fdm values
|
||||
length = sizeof(fdm);
|
||||
// cout << "about to read fdm data from remote fdm." << endl;
|
||||
result = fread( (char *)(& fdm), length, 1, pd2 );
|
||||
if ( result != 1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Read error from named pipe: "
|
||||
<< fifo_name_2 << " expected 1 item, but got " << result );
|
||||
} else {
|
||||
// cout << " read successful." << endl;
|
||||
FGFDM2Props<FGNetFDM>( globals->get_props(), &fdm, false );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Process remote FDM "set" commands
|
||||
|
||||
void FGExternalPipe::process_set_command( const string_list &tokens ) {
|
||||
if ( tokens[1] == "geodetic_position" ) {
|
||||
double lat_rad = atof( tokens[2].c_str() );
|
||||
double lon_rad = atof( tokens[3].c_str() );
|
||||
double alt_m = atof( tokens[4].c_str() );
|
||||
_updateGeodeticPosition( lat_rad, lon_rad,
|
||||
alt_m * SG_METER_TO_FEET );
|
||||
|
||||
double agl_m = alt_m - get_Runway_altitude_m();
|
||||
_set_Altitude_AGL( agl_m * SG_METER_TO_FEET );
|
||||
} else if ( tokens[1] == "euler_angles" ) {
|
||||
double phi_rad = atof( tokens[2].c_str() );
|
||||
double theta_rad = atof( tokens[3].c_str() );
|
||||
double psi_rad = atof( tokens[4].c_str() );
|
||||
_set_Euler_Angles( phi_rad, theta_rad, psi_rad );
|
||||
} else if ( tokens[1] == "euler_rates" ) {
|
||||
double phidot = atof( tokens[2].c_str() );
|
||||
double thetadot = atof( tokens[3].c_str() );
|
||||
double psidot = atof( tokens[4].c_str() );
|
||||
_set_Euler_Rates( phidot, thetadot, psidot );
|
||||
} else if ( tokens[1] == "ned" ) {
|
||||
double north_fps = atof( tokens[2].c_str() );
|
||||
double east_fps = atof( tokens[3].c_str() );
|
||||
double down_fps = atof( tokens[4].c_str() );
|
||||
_set_Velocities_Local( north_fps, east_fps, down_fps );
|
||||
} else if ( tokens[1] == "alpha" ) {
|
||||
_set_Alpha( atof(tokens[2].c_str()) );
|
||||
} else if ( tokens[1] == "beta" ) {
|
||||
_set_Beta( atof(tokens[2].c_str()) );
|
||||
|
||||
#if 0
|
||||
_set_V_calibrated_kts( net->vcas );
|
||||
_set_Climb_Rate( net->climb_rate );
|
||||
_set_Velocities_Local( net->v_north,
|
||||
net->v_east,
|
||||
net->v_down );
|
||||
_set_Velocities_Body( net->v_body_u,
|
||||
net->v_body_v,
|
||||
net->v_body_w );
|
||||
|
||||
_set_Accels_Pilot_Body( net->A_X_pilot,
|
||||
net->A_Y_pilot,
|
||||
net->A_Z_pilot );
|
||||
#endif
|
||||
} else {
|
||||
fgSetString( tokens[1].c_str(), tokens[2].c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Run an iteration of the EOM.
|
||||
void FGExternalPipe::update_property( double dt ) {
|
||||
// cout << "update_property()" << endl;
|
||||
|
||||
#ifdef HAVE_MKFIFO
|
||||
// SG_LOG( SG_IO, SG_INFO, "Start FGExternalPipe::udpate()" );
|
||||
|
||||
int result;
|
||||
char cmd[256];
|
||||
|
||||
if ( is_suspended() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int iterations = _calc_multiloop(dt);
|
||||
|
||||
double weight = fgGetDouble( "/sim/aircraft-weight-lbs" );
|
||||
static double last_weight = 0.0;
|
||||
if ( fabs( weight - last_weight ) > 0.01 ) {
|
||||
sprintf( cmd, "init aircraft-weight-lbs=%.2f", weight );
|
||||
result = write_property( pd1, cmd );
|
||||
}
|
||||
last_weight = weight;
|
||||
|
||||
double cg_offset = fgGetDouble( "/sim/aircraft-cg-offset-inches" );
|
||||
if ( fabs( cg_offset - last_cg_offset ) > 0.01 ) {
|
||||
sprintf( cmd, "init aircraft-cg-offset-inches=%.2f", cg_offset );
|
||||
result = write_property( pd1, cmd );
|
||||
}
|
||||
last_cg_offset = cg_offset;
|
||||
|
||||
// Send requested property values to fdm
|
||||
for ( unsigned int i = 0; i < nodes.size(); i++ ) {
|
||||
sprintf( cmd, "set %s %s", property_names[i].c_str(),
|
||||
nodes[i]->getStringValue().c_str() );
|
||||
// cout << " sending " << cmd << endl;
|
||||
result = write_property( pd1, cmd );
|
||||
}
|
||||
|
||||
sprintf( cmd, "update %d", iterations );
|
||||
write_property( pd1, cmd );
|
||||
|
||||
fflush( pd1 );
|
||||
|
||||
// Read FDM response
|
||||
// cout << "ready to read fdm response" << endl;
|
||||
bool done = false;
|
||||
while ( !done ) {
|
||||
if ( fgets( cmd, 256, pd2 ) == NULL ) {
|
||||
cout << "Error reading data" << endl;
|
||||
} else {
|
||||
// cout << " read " << strlen(cmd) << " bytes" << endl;
|
||||
// cout << cmd << endl;
|
||||
}
|
||||
|
||||
// chop trailing newline
|
||||
cmd[strlen(cmd)-1] = '\0';
|
||||
|
||||
// cout << cmd << endl;
|
||||
string_list tokens = simgear::strutils::split( cmd, " " );
|
||||
|
||||
if ( tokens[0] == "request" ) {
|
||||
// save the long form name
|
||||
property_names.push_back( tokens[1] );
|
||||
|
||||
// now do the property name lookup and cache the pointer
|
||||
SGPropertyNode *node = fgGetNode( tokens[1].c_str() );
|
||||
if ( node == NULL ) {
|
||||
// node doesn't exist so create with requested type
|
||||
node = fgGetNode( tokens[1].c_str(), true );
|
||||
if ( tokens[2] == "bool" ) {
|
||||
node->setBoolValue(false);
|
||||
} else if ( tokens[2] == "int" ) {
|
||||
node->setIntValue(0);
|
||||
} else if ( tokens[2] == "double" ) {
|
||||
node->setDoubleValue(0.0);
|
||||
} else if ( tokens[2] == "string" ) {
|
||||
node->setStringValue("");
|
||||
} else {
|
||||
cout << "Unknown data type: " << tokens[2]
|
||||
<< " for " << tokens[1] << endl;
|
||||
}
|
||||
}
|
||||
nodes.push_back( node );
|
||||
} else if ( tokens[0] == "set" ) {
|
||||
process_set_command( tokens );
|
||||
} else if ( tokens[0] == "update" ) {
|
||||
done = true;
|
||||
} else {
|
||||
cout << "unknown command = " << cmd << endl;
|
||||
}
|
||||
}
|
||||
|
||||
(void) result; // ignore result
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
#if 0
|
||||
SGSubsystemMgr::Registrant<FGExternalPipe> registrantFGExternalPipe;
|
||||
#endif
|
||||
81
src/FDM/ExternalPipe/ExternalPipe.hxx
Normal file
81
src/FDM/ExternalPipe/ExternalPipe.hxx
Normal file
@@ -0,0 +1,81 @@
|
||||
// ExternalPipe.hxx -- a "pipe" interface to an external flight dynamics model
|
||||
//
|
||||
// Written by Curtis Olson, started March 2003.
|
||||
//
|
||||
// Copyright (C) 2003 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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 _EXTERNAL_PIPE_HXX
|
||||
#define _EXTERNAL_PIPE_HXX
|
||||
|
||||
#include <stdio.h> // FILE*, fopen(), fread(), fwrite(), et. al.
|
||||
|
||||
#include <simgear/timing/timestamp.hxx> // fine grained timing measurements
|
||||
|
||||
#include <Network/net_ctrls.hxx>
|
||||
#include <Network/net_fdm.hxx>
|
||||
#include <FDM/flight.hxx>
|
||||
|
||||
|
||||
class FGExternalPipe : public FGInterface
|
||||
{
|
||||
private:
|
||||
bool valid;
|
||||
|
||||
std::string fifo_name_1;
|
||||
std::string fifo_name_2;
|
||||
FILE *pd1;
|
||||
FILE *pd2;
|
||||
std::string _protocol;
|
||||
|
||||
FGNetCtrls ctrls;
|
||||
FGNetFDM fdm;
|
||||
char *buf;
|
||||
|
||||
double last_weight;
|
||||
double last_cg_offset;
|
||||
|
||||
std::vector <std::string> property_names;
|
||||
std::vector <SGPropertyNode_ptr> nodes;
|
||||
|
||||
// Protocol specific init routines
|
||||
void init_binary();
|
||||
void init_property();
|
||||
|
||||
// Protocol specific update routines
|
||||
void update_binary( double dt );
|
||||
void update_property( double dt );
|
||||
|
||||
void process_set_command( const string_list &tokens );
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
FGExternalPipe( double dt, std::string fifo_name, std::string protocol );
|
||||
|
||||
// Destructor
|
||||
~FGExternalPipe();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "pipe"; }
|
||||
};
|
||||
|
||||
#endif // _EXTERNAL_PIPE_HXX
|
||||
56
src/FDM/JSBSim/AUTHORS
Normal file
56
src/FDM/JSBSim/AUTHORS
Normal file
@@ -0,0 +1,56 @@
|
||||
AUTHORS
|
||||
-------
|
||||
|
||||
Jon S. Berndt
|
||||
primary architect and coordinator
|
||||
jsb@hal-pc.org
|
||||
CVS access as developer (r/w)
|
||||
|
||||
|
||||
Tony Peden
|
||||
additional architecture support
|
||||
trimming and aerodynamics
|
||||
interface with flightgear
|
||||
apeden@earthlink.net
|
||||
CVS access as developer (r/w)
|
||||
|
||||
|
||||
Additional support (programming, bug fixes, aircraft models)
|
||||
by the FlightGear team and others as listed:
|
||||
|
||||
Curt Olson
|
||||
wrote initial interface with flightgear
|
||||
works flightgear/jsbsim issues
|
||||
http://www.flightgear.org/~curt
|
||||
|
||||
Norman Vine
|
||||
matrix math class optimization support
|
||||
general C++ suggestions, debugging, and support
|
||||
nhv@cape.com
|
||||
|
||||
Christian Mayer
|
||||
JSBSim compatibility with MSVC
|
||||
mail@ChristianMayer.de
|
||||
|
||||
Erik Hofman
|
||||
JSBSim compatibility with IRIX
|
||||
erik@ehofman.com
|
||||
|
||||
David Megginson
|
||||
ported Dave Luff's piston engine model initially to JSBSim
|
||||
creation of multi-engine C-310 model and testing/debugging
|
||||
general C++ suggestions, debugging, and support
|
||||
david@megginson.com
|
||||
|
||||
Ross Golder
|
||||
improvements to the build process and auto* files
|
||||
ross@golder.org
|
||||
|
||||
Dave Luff
|
||||
wrote the piston engine model used by LaRCSim and is ported to JSBSim
|
||||
eazdluf@nottingham.ac.uk
|
||||
|
||||
Norman Princen
|
||||
provided an improved gear model
|
||||
nprincen@hotmail.com
|
||||
|
||||
208
src/FDM/JSBSim/CMakeLists.txt
Normal file
208
src/FDM/JSBSim/CMakeLists.txt
Normal file
@@ -0,0 +1,208 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
FGFDMExec.h
|
||||
FGJSBBase.h
|
||||
JSBSim.hxx
|
||||
initialization/FGInitialCondition.h
|
||||
initialization/FGTrim.h
|
||||
initialization/FGTrimAxis.h
|
||||
input_output/FGXMLParse.h
|
||||
input_output/FGXMLFileRead.h
|
||||
input_output/FGPropertyReader.h
|
||||
input_output/FGPropertyManager.h
|
||||
input_output/FGScript.h
|
||||
input_output/FGfdmSocket.h
|
||||
input_output/string_utilities.h
|
||||
input_output/FGXMLElement.h
|
||||
input_output/net_fdm.hxx
|
||||
input_output/FGGroundCallback.h
|
||||
input_output/FGInputType.h
|
||||
input_output/FGInputSocket.h
|
||||
input_output/FGUDPInputSocket.h
|
||||
input_output/FGOutputFG.h
|
||||
input_output/FGOutputFile.h
|
||||
input_output/FGOutputSocket.h
|
||||
input_output/FGOutputTextFile.h
|
||||
input_output/FGOutputType.h
|
||||
input_output/FGModelLoader.h
|
||||
math/FGParameter.h
|
||||
math/LagrangeMultiplier.h
|
||||
math/FGColumnVector3.h
|
||||
math/FGCondition.h
|
||||
math/FGFunction.h
|
||||
math/FGLocation.h
|
||||
math/FGMatrix33.h
|
||||
math/FGModelFunctions.h
|
||||
math/FGPropertyValue.h
|
||||
math/FGQuaternion.h
|
||||
math/FGRealValue.h
|
||||
math/FGRungeKutta.h
|
||||
math/FGTable.h
|
||||
math/FGFunctionValue.h
|
||||
math/FGTemplateFunc.h
|
||||
models/FGAccelerations.h
|
||||
models/FGAerodynamics.h
|
||||
models/FGAircraft.h
|
||||
models/FGAtmosphere.h
|
||||
models/FGAuxiliary.h
|
||||
models/FGBuoyantForces.h
|
||||
models/FGExternalForce.h
|
||||
models/FGExternalReactions.h
|
||||
models/FGFCS.h
|
||||
models/FGGasCell.h
|
||||
models/FGSurface.h
|
||||
models/FGGroundReactions.h
|
||||
models/FGInertial.h
|
||||
models/FGInput.h
|
||||
models/FGLGear.h
|
||||
models/FGMassBalance.h
|
||||
models/FGModel.h
|
||||
models/FGOutput.h
|
||||
models/FGPropagate.h
|
||||
models/FGPropulsion.h
|
||||
models/atmosphere/FGStandardAtmosphere.h
|
||||
models/atmosphere/FGWinds.h
|
||||
models/flight_control/FGAccelerometer.h
|
||||
models/flight_control/FGActuator.h
|
||||
models/flight_control/FGAngles.h
|
||||
models/flight_control/FGDeadBand.h
|
||||
models/flight_control/FGFCSComponent.h
|
||||
models/flight_control/FGFCSFunction.h
|
||||
models/flight_control/FGFilter.h
|
||||
models/flight_control/FGGain.h
|
||||
models/flight_control/FGGyro.h
|
||||
models/flight_control/FGKinemat.h
|
||||
models/flight_control/FGMagnetometer.h
|
||||
models/flight_control/FGPID.h
|
||||
models/flight_control/FGSensor.h
|
||||
models/flight_control/FGSensorOrientation.h
|
||||
models/flight_control/FGSummer.h
|
||||
models/flight_control/FGSwitch.h
|
||||
models/flight_control/FGWaypoint.h
|
||||
models/flight_control/FGDistributor.h
|
||||
models/flight_control/FGLinearActuator.h
|
||||
models/propulsion/FGElectric.h
|
||||
models/propulsion/FGEngine.h
|
||||
models/propulsion/FGForce.h
|
||||
models/propulsion/FGNozzle.h
|
||||
models/propulsion/FGPiston.h
|
||||
models/propulsion/FGPropeller.h
|
||||
models/propulsion/FGRocket.h
|
||||
models/propulsion/FGRotor.h
|
||||
models/propulsion/FGTank.h
|
||||
models/propulsion/FGThruster.h
|
||||
models/propulsion/FGTransmission.h
|
||||
models/propulsion/FGTurbine.h
|
||||
models/propulsion/FGTurboProp.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
FGFDMExec.cpp
|
||||
FGJSBBase.cpp
|
||||
JSBSim.cxx
|
||||
initialization/FGInitialCondition.cpp
|
||||
initialization/FGTrim.cpp
|
||||
initialization/FGTrimAxis.cpp
|
||||
input_output/FGGroundCallback.cpp
|
||||
input_output/FGPropertyReader.cpp
|
||||
input_output/FGPropertyManager.cpp
|
||||
input_output/FGScript.cpp
|
||||
input_output/FGXMLElement.cpp
|
||||
input_output/FGXMLParse.cpp
|
||||
input_output/FGfdmSocket.cpp
|
||||
input_output/FGInputType.cpp
|
||||
input_output/FGInputSocket.cpp
|
||||
input_output/FGUDPInputSocket.cpp
|
||||
input_output/FGOutputFG.cpp
|
||||
input_output/FGOutputFile.cpp
|
||||
input_output/FGOutputSocket.cpp
|
||||
input_output/FGOutputTextFile.cpp
|
||||
input_output/FGOutputType.cpp
|
||||
input_output/FGModelLoader.cpp
|
||||
math/FGColumnVector3.cpp
|
||||
math/FGCondition.cpp
|
||||
math/FGFunction.cpp
|
||||
math/FGLocation.cpp
|
||||
math/FGMatrix33.cpp
|
||||
math/FGModelFunctions.cpp
|
||||
math/FGPropertyValue.cpp
|
||||
math/FGQuaternion.cpp
|
||||
math/FGRealValue.cpp
|
||||
math/FGRungeKutta.cpp
|
||||
math/FGTable.cpp
|
||||
math/FGTemplateFunc.cpp
|
||||
models/FGAccelerations.cpp
|
||||
models/FGAerodynamics.cpp
|
||||
models/FGAircraft.cpp
|
||||
models/FGAtmosphere.cpp
|
||||
models/FGAuxiliary.cpp
|
||||
models/FGBuoyantForces.cpp
|
||||
models/FGExternalForce.cpp
|
||||
models/FGExternalReactions.cpp
|
||||
models/FGFCS.cpp
|
||||
models/FGGasCell.cpp
|
||||
models/FGSurface.cpp
|
||||
models/FGGroundReactions.cpp
|
||||
models/FGInertial.cpp
|
||||
models/FGInput.cpp
|
||||
models/FGLGear.cpp
|
||||
models/FGMassBalance.cpp
|
||||
models/FGModel.cpp
|
||||
models/FGOutput.cpp
|
||||
models/FGPropagate.cpp
|
||||
models/FGPropulsion.cpp
|
||||
models/atmosphere/FGStandardAtmosphere.cpp
|
||||
models/atmosphere/FGWinds.cpp
|
||||
models/flight_control/FGAccelerometer.cpp
|
||||
models/flight_control/FGActuator.cpp
|
||||
models/flight_control/FGAngles.cpp
|
||||
models/flight_control/FGDeadBand.cpp
|
||||
models/flight_control/FGFCSComponent.cpp
|
||||
models/flight_control/FGFCSFunction.cpp
|
||||
models/flight_control/FGFilter.cpp
|
||||
models/flight_control/FGGain.cpp
|
||||
models/flight_control/FGGyro.cpp
|
||||
models/flight_control/FGKinemat.cpp
|
||||
models/flight_control/FGMagnetometer.cpp
|
||||
models/flight_control/FGPID.cpp
|
||||
models/flight_control/FGSensor.cpp
|
||||
models/flight_control/FGSummer.cpp
|
||||
models/flight_control/FGSwitch.cpp
|
||||
models/flight_control/FGWaypoint.cpp
|
||||
models/flight_control/FGDistributor.cpp
|
||||
models/flight_control/FGLinearActuator.cpp
|
||||
models/propulsion/FGElectric.cpp
|
||||
models/propulsion/FGEngine.cpp
|
||||
models/propulsion/FGForce.cpp
|
||||
models/propulsion/FGNozzle.cpp
|
||||
models/propulsion/FGPiston.cpp
|
||||
models/propulsion/FGPropeller.cpp
|
||||
models/propulsion/FGRocket.cpp
|
||||
models/propulsion/FGRotor.cpp
|
||||
models/propulsion/FGTank.cpp
|
||||
models/propulsion/FGThruster.cpp
|
||||
models/propulsion/FGTransmission.cpp
|
||||
models/propulsion/FGTurbine.cpp
|
||||
models/propulsion/FGTurboProp.cpp
|
||||
)
|
||||
|
||||
|
||||
add_library(JSBSim STATIC ${SOURCES} ${HEADERS})
|
||||
set(VERSION_MESSAGE "compiled from FlightGear ${FLIGHTGEAR_VERSION}")
|
||||
add_definitions("-DJSBSIM_VERSION=\"${VERSION_MESSAGE}\"")
|
||||
|
||||
target_link_libraries(JSBSim SimGearCore)
|
||||
target_include_directories(JSBSim PRIVATE ${CMAKE_SOURCE_DIR}/src/FDM/JSBSim)
|
||||
|
||||
add_executable(JSBsim_bin JSBSim.cpp )
|
||||
set_target_properties(JSBsim_bin PROPERTIES OUTPUT_NAME "JSBSim" )
|
||||
target_Link_libraries(JSBsim_bin JSBSim)
|
||||
target_include_directories(JSBsim_bin PRIVATE ${CMAKE_SOURCE_DIR}/src/FDM/JSBSim)
|
||||
|
||||
if (MSVC)
|
||||
set_target_properties(JSBsim_bin PROPERTIES DEBUG_POSTFIX d)
|
||||
endif ()
|
||||
install(TARGETS JSBsim_bin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# eof
|
||||
1243
src/FDM/JSBSim/FGFDMExec.cpp
Normal file
1243
src/FDM/JSBSim/FGFDMExec.cpp
Normal file
File diff suppressed because it is too large
Load Diff
713
src/FDM/JSBSim/FGFDMExec.h
Normal file
713
src/FDM/JSBSim/FGFDMExec.h
Normal file
@@ -0,0 +1,713 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
Header: FGFDMExec.h
|
||||
Author: Jon Berndt
|
||||
Date started: 11/17/98
|
||||
file The header file for the JSBSim executive.
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
11/17/98 JSB Created
|
||||
7/31/99 TP Added RunIC function that runs the sim so that every frame
|
||||
begins with the IC values from the given FGInitialCondition
|
||||
object and dt=0.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGFDMEXEC_HEADER_H
|
||||
#define FGFDMEXEC_HEADER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include "models/FGPropagate.h"
|
||||
#include "models/FGOutput.h"
|
||||
#include "math/FGTemplateFunc.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGScript;
|
||||
class FGTrim;
|
||||
class FGAerodynamics;
|
||||
class FGAircraft;
|
||||
class FGAtmosphere;
|
||||
class FGAccelerations;
|
||||
class FGWinds;
|
||||
class FGAuxiliary;
|
||||
class FGBuoyantForces;
|
||||
class FGExternalReactions;
|
||||
class FGGroundReactions;
|
||||
class FGFCS;
|
||||
class FGInertial;
|
||||
class FGInput;
|
||||
class FGPropulsion;
|
||||
class FGMassBalance;
|
||||
class FGTrim;
|
||||
|
||||
class TrimFailureException : public BaseException {
|
||||
public:
|
||||
TrimFailureException(const std::string& msg) : BaseException(msg) {}
|
||||
};
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates the JSBSim simulation executive.
|
||||
This class is the executive class through which all other simulation classes
|
||||
are instantiated, initialized, and run. When integrated with FlightGear (or
|
||||
other flight simulator) this class is typically instantiated by an interface
|
||||
class on the simulator side.
|
||||
|
||||
At the time of simulation initialization, the interface
|
||||
class creates an instance of this executive class. The
|
||||
executive is subsequently directed to load the chosen aircraft specification
|
||||
file:
|
||||
|
||||
@code{.cpp}
|
||||
fdmex = new FGFDMExec( ... );
|
||||
result = fdmex->LoadModel( ... );
|
||||
@endcode
|
||||
|
||||
When an aircraft model is loaded, the config file is parsed and for each of
|
||||
the sections of the config file (propulsion, flight control, etc.) the
|
||||
corresponding Load() method is called (e.g. FGFCS::Load()).
|
||||
|
||||
Subsequent to the creation of the executive and loading of the model,
|
||||
initialization is performed. Initialization involves copying control inputs
|
||||
into the appropriate JSBSim data storage locations, configuring it for the
|
||||
set of user supplied initial conditions, and then copying state variables
|
||||
from JSBSim. The state variables are used to drive the instrument displays
|
||||
and to place the vehicle model in world space for visual rendering:
|
||||
|
||||
@code{.cpp}
|
||||
copy_to_JSBsim(); // copy control inputs to JSBSim
|
||||
fdmex->RunIC(); // loop JSBSim once w/o integrating
|
||||
copy_from_JSBsim(); // update the bus
|
||||
@endcode
|
||||
|
||||
Once initialization is complete, cyclic execution proceeds:
|
||||
|
||||
@code{.cpp}
|
||||
copy_to_JSBsim(); // copy control inputs to JSBSim
|
||||
fdmex->Run(); // execute JSBSim
|
||||
copy_from_JSBsim(); // update the bus
|
||||
@endcode
|
||||
|
||||
JSBSim can be used in a standalone mode by creating a compact stub program
|
||||
that effectively performs the same progression of steps as outlined above
|
||||
for the integrated version, but with two exceptions. First, the
|
||||
copy_to_JSBSim() and copy_from_JSBSim() functions are not used because the
|
||||
control inputs are handled directly by the scripting facilities and outputs
|
||||
are handled by the output (data logging) class. Second, the name of a script
|
||||
file can be supplied to the stub program. Scripting (see FGScript) provides
|
||||
a way to supply command inputs to the simulation:
|
||||
|
||||
@code{.cpp}
|
||||
FDMExec = new JSBSim::FGFDMExec();
|
||||
FDMExec->LoadScript( ScriptName ); // the script loads the aircraft and ICs
|
||||
result = FDMExec->Run();
|
||||
while (result) { // cyclic execution
|
||||
result = FDMExec->Run(); // execute JSBSim
|
||||
}
|
||||
@endcode
|
||||
|
||||
The standalone mode has been useful for verifying changes before committing
|
||||
updates to the source code repository. It is also useful for running sets of
|
||||
tests that reveal some aspects of simulated aircraft performance, such as
|
||||
range, time-to-climb, takeoff distance, etc.
|
||||
|
||||
<h3>JSBSim Debugging Directives</h3>
|
||||
|
||||
This describes to any interested entity the debug level
|
||||
requested by setting the JSBSIM_DEBUG environment variable.
|
||||
The bitmasked value choices are as follows:
|
||||
- <b>unset</b>: In this case (the default) JSBSim would only print
|
||||
out the normally expected messages, essentially echoing
|
||||
the config files as they are read. If the environment
|
||||
variable is not set, debug_lvl is set to 1 internally
|
||||
- <b>0</b>: This requests JSBSim not to output any messages
|
||||
whatsoever
|
||||
- <b>1</b>: This value explicity requests the normal JSBSim
|
||||
startup messages
|
||||
- <b>2</b>: This value asks for a message to be printed out when
|
||||
a class is instantiated
|
||||
- <b>4</b>: When this value is set, a message is displayed when a
|
||||
FGModel object executes its Run() method
|
||||
- <b>8</b>: When this value is set, various runtime state variables
|
||||
are printed out periodically
|
||||
- <b>16</b>: When set various parameters are sanity checked and
|
||||
a message is printed out when they go out of bounds
|
||||
|
||||
<h3>Properties</h3>
|
||||
@property simulator/do_trim (write only) Can be set to the integer equivalent to one of
|
||||
tLongitudinal (0), tFull (1), tGround (2), tPullup (3),
|
||||
tCustom (4), tTurn (5). Setting this to a legal value
|
||||
(such as by a script) causes a trim to be performed. This
|
||||
property actually maps toa function call of DoTrim().
|
||||
|
||||
@author Jon S. Berndt
|
||||
@version $Revision: 1.106 $
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGFDMExec : public FGJSBBase
|
||||
{
|
||||
struct childData {
|
||||
FGFDMExec* exec;
|
||||
std::string info;
|
||||
FGColumnVector3 Loc;
|
||||
FGColumnVector3 Orient;
|
||||
bool mated;
|
||||
bool internal;
|
||||
|
||||
childData(void) {
|
||||
info = "";
|
||||
Loc = FGColumnVector3(0,0,0);
|
||||
Orient = FGColumnVector3(0,0,0);
|
||||
mated = true;
|
||||
internal = false;
|
||||
}
|
||||
|
||||
void Run(void) {exec->Run();}
|
||||
void AssignState(FGPropagate* source_prop) {
|
||||
exec->GetPropagate()->SetVState(source_prop->GetVState());
|
||||
}
|
||||
|
||||
~childData(void) {
|
||||
delete exec;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor
|
||||
FGFDMExec(FGPropertyManager* root = 0, unsigned int* fdmctr = 0);
|
||||
|
||||
/// Default destructor
|
||||
~FGFDMExec();
|
||||
|
||||
// This list of enums is very important! The order in which models are listed
|
||||
// here determines the order of execution of the models.
|
||||
//
|
||||
// There are some conditions that need to be met :
|
||||
// 1. FCS can request mass geometry changes via the inertia/pointmass-*
|
||||
// properties so it must be executed before MassBalance
|
||||
// 2. MassBalance must be executed before Propulsion, Aerodynamics,
|
||||
// GroundReactions, ExternalReactions and BuoyantForces to ensure that
|
||||
// their moments are computed with the updated CG position.
|
||||
enum eModels { ePropagate=0,
|
||||
eInput,
|
||||
eInertial,
|
||||
eAtmosphere,
|
||||
eWinds,
|
||||
eSystems,
|
||||
eMassBalance,
|
||||
eAuxiliary,
|
||||
ePropulsion,
|
||||
eAerodynamics,
|
||||
eGroundReactions,
|
||||
eExternalReactions,
|
||||
eBuoyantForces,
|
||||
eAircraft,
|
||||
eAccelerations,
|
||||
eOutput,
|
||||
eNumStandardModels };
|
||||
|
||||
/** Unbind all tied JSBSim properties. */
|
||||
void Unbind(void) {instance->Unbind();}
|
||||
|
||||
/** This function executes each scheduled model in succession.
|
||||
@return true if successful, false if sim should be ended */
|
||||
bool Run(void);
|
||||
|
||||
/** Initializes the sim from the initial condition object and executes
|
||||
each scheduled model without integrating i.e. dt=0.
|
||||
@return true if successful */
|
||||
bool RunIC(void);
|
||||
|
||||
/** Loads an aircraft model.
|
||||
@param AircraftPath path to the aircraft/ directory. For instance:
|
||||
"aircraft". Under aircraft, then, would be directories for various
|
||||
modeled aircraft such as C172/, x15/, etc.
|
||||
@param EnginePath path to the directory under which engine config
|
||||
files are kept, for instance "engine"
|
||||
@param SystemsPath path to the directory under which systems config
|
||||
files are kept, for instance "systems"
|
||||
@param model the name of the aircraft model itself. This file will
|
||||
be looked for in the directory specified in the AircraftPath variable,
|
||||
and in turn under the directory with the same name as the model. For
|
||||
instance: "aircraft/x15/x15.xml"
|
||||
@param addModelToPath set to true to add the model name to the
|
||||
AircraftPath, defaults to true
|
||||
@return true if successful */
|
||||
bool LoadModel(const SGPath& AircraftPath, const SGPath& EnginePath,
|
||||
const SGPath& SystemsPath, const std::string& model,
|
||||
bool addModelToPath = true);
|
||||
|
||||
/** Loads an aircraft model. The paths to the aircraft and engine
|
||||
config file directories must be set prior to calling this. See
|
||||
below.
|
||||
@param model the name of the aircraft model itself. This file will
|
||||
be looked for in the directory specified in the AircraftPath variable,
|
||||
and in turn under the directory with the same name as the model. For
|
||||
instance: "aircraft/x15/x15.xml"
|
||||
@param addModelToPath set to true to add the model name to the
|
||||
AircraftPath, defaults to true
|
||||
@return true if successful*/
|
||||
bool LoadModel(const std::string& model, bool addModelToPath = true);
|
||||
|
||||
/** Load a script
|
||||
@param Script The full path name and file name for the script to be loaded.
|
||||
@param deltaT The simulation integration step size, if given. If no value
|
||||
is supplied then 0.0 is used and the value is expected to be
|
||||
supplied in the script file itself.
|
||||
@param initfile The initialization file that will override the
|
||||
initialization file specified in the script file. If no
|
||||
file name is given on the command line, the file specified
|
||||
in the script will be used. If an initialization file is
|
||||
not given in either place, an error will result.
|
||||
@return true if successfully loads; false otherwise. */
|
||||
bool LoadScript(const SGPath& Script, double deltaT=0.0,
|
||||
const SGPath& initfile=SGPath());
|
||||
|
||||
/** Set the path to the engine config file directories.
|
||||
Relative paths are taken from the root directory.
|
||||
@param path path to the directory under which engine config files are
|
||||
kept, for instance "engine".
|
||||
@see SetRootDir
|
||||
@see GetEnginePath */
|
||||
bool SetEnginePath(const SGPath& path) {
|
||||
EnginePath = GetFullPath(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Set the path to the aircraft config file directories.
|
||||
Under this path, then, would be directories for various modeled aircraft
|
||||
such as C172/, x15/, etc.
|
||||
Relative paths are taken from the root directory.
|
||||
@param path path to the aircraft directory, for instance "aircraft".
|
||||
@see SetRootDir
|
||||
@see GetAircraftPath */
|
||||
bool SetAircraftPath(const SGPath& path) {
|
||||
AircraftPath = GetFullPath(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Set the path to the systems config file directories.
|
||||
Relative paths are taken from the root directory.
|
||||
@param path path to the directory under which systems config files are
|
||||
kept, for instance "systems"
|
||||
@see SetRootDir
|
||||
@see GetSystemsPath */
|
||||
bool SetSystemsPath(const SGPath& path) {
|
||||
SystemsPath = GetFullPath(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Set the directory where the output files will be written.
|
||||
Relative paths are taken from the root directory.
|
||||
@param path path to the directory under which the output files will be
|
||||
written.
|
||||
@see SetRootDir
|
||||
@see GetOutputPath */
|
||||
bool SetOutputPath(const SGPath& path) {
|
||||
OutputPath = GetFullPath(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @name Top-level executive State and Model retrieval mechanism
|
||||
///@{
|
||||
/// Returns the FGAtmosphere pointer.
|
||||
FGAtmosphere* GetAtmosphere(void) {return (FGAtmosphere*)Models[eAtmosphere];}
|
||||
/// Returns the FGAccelerations pointer.
|
||||
FGAccelerations* GetAccelerations(void) {return (FGAccelerations*)Models[eAccelerations];}
|
||||
/// Returns the FGWinds pointer.
|
||||
FGWinds* GetWinds(void) {return (FGWinds*)Models[eWinds];}
|
||||
/// Returns the FGFCS pointer.
|
||||
FGFCS* GetFCS(void) {return (FGFCS*)Models[eSystems];}
|
||||
/// Returns the FGPropulsion pointer.
|
||||
FGPropulsion* GetPropulsion(void) {return (FGPropulsion*)Models[ePropulsion];}
|
||||
/// Returns the FGAircraft pointer.
|
||||
FGMassBalance* GetMassBalance(void) {return (FGMassBalance*)Models[eMassBalance];}
|
||||
/// Returns the FGAerodynamics pointer
|
||||
FGAerodynamics* GetAerodynamics(void){return (FGAerodynamics*)Models[eAerodynamics];}
|
||||
/// Returns the FGInertial pointer.
|
||||
FGInertial* GetInertial(void) {return (FGInertial*)Models[eInertial];}
|
||||
/// Returns the FGGroundReactions pointer.
|
||||
FGGroundReactions* GetGroundReactions(void) {return (FGGroundReactions*)Models[eGroundReactions];}
|
||||
/// Returns the FGExternalReactions pointer.
|
||||
FGExternalReactions* GetExternalReactions(void) {return (FGExternalReactions*)Models[eExternalReactions];}
|
||||
/// Returns the FGBuoyantForces pointer.
|
||||
FGBuoyantForces* GetBuoyantForces(void) {return (FGBuoyantForces*)Models[eBuoyantForces];}
|
||||
/// Returns the FGAircraft pointer.
|
||||
FGAircraft* GetAircraft(void) {return (FGAircraft*)Models[eAircraft];}
|
||||
/// Returns the FGPropagate pointer.
|
||||
FGPropagate* GetPropagate(void) {return (FGPropagate*)Models[ePropagate];}
|
||||
/// Returns the FGAuxiliary pointer.
|
||||
FGAuxiliary* GetAuxiliary(void) {return (FGAuxiliary*)Models[eAuxiliary];}
|
||||
/// Returns the FGInput pointer.
|
||||
FGInput* GetInput(void) {return (FGInput*)Models[eInput];}
|
||||
/// Returns the FGOutput pointer.
|
||||
FGOutput* GetOutput(void) {return (FGOutput*)Models[eOutput];}
|
||||
/// Retrieves the script object
|
||||
FGScript* GetScript(void) {return Script;}
|
||||
/// Returns a pointer to the FGInitialCondition object
|
||||
FGInitialCondition* GetIC(void) {return IC;}
|
||||
/// Returns a pointer to the FGTrim object
|
||||
FGTrim* GetTrim(void);
|
||||
///@}
|
||||
|
||||
/// Retrieves the engine path.
|
||||
const SGPath& GetEnginePath(void) { return EnginePath; }
|
||||
/// Retrieves the aircraft path.
|
||||
const SGPath& GetAircraftPath(void) { return AircraftPath; }
|
||||
/// Retrieves the systems path.
|
||||
const SGPath& GetSystemsPath(void) { return SystemsPath; }
|
||||
/// Retrieves the full aircraft path name.
|
||||
const SGPath& GetFullAircraftPath(void) { return FullAircraftPath; }
|
||||
/// Retrieves the path to the output files.
|
||||
const SGPath& GetOutputPath(void) { return OutputPath; }
|
||||
|
||||
/** Retrieves the value of a property.
|
||||
@param property the name of the property
|
||||
@result the value of the specified property */
|
||||
inline double GetPropertyValue(const std::string& property)
|
||||
{ return instance->GetNode()->GetDouble(property); }
|
||||
|
||||
/** Sets a property value.
|
||||
@param property the property to be set
|
||||
@param value the value to set the property to */
|
||||
inline void SetPropertyValue(const std::string& property, double value) {
|
||||
instance->GetNode()->SetDouble(property, value);
|
||||
}
|
||||
|
||||
/// Returns the model name.
|
||||
const std::string& GetModelName(void) const { return modelName; }
|
||||
|
||||
/// Returns a pointer to the property manager object.
|
||||
FGPropertyManager* GetPropertyManager(void);
|
||||
/// Returns a vector of strings representing the names of all loaded models (future)
|
||||
std::vector <std::string> EnumerateFDMs(void);
|
||||
/// Gets the number of child FDMs.
|
||||
int GetFDMCount(void) const {return (int)ChildFDMList.size();}
|
||||
/// Gets a particular child FDM.
|
||||
childData* GetChildFDM(int i) const {return ChildFDMList[i];}
|
||||
/// Marks this instance of the Exec object as a "child" object.
|
||||
void SetChild(bool ch) {IsChild = ch;}
|
||||
|
||||
/** Sets the output (logging) mechanism for this run.
|
||||
Calling this function passes the name of an output directives file to
|
||||
the FGOutput object associated with this run. The call to this function
|
||||
should be made prior to loading an aircraft model. This call results in an
|
||||
FGOutput object being built as the first Output object in the FDMExec-managed
|
||||
list of Output objects that may be created for an aircraft model. If this call
|
||||
is made after an aircraft model is loaded, there is no effect. Any Output
|
||||
objects added by the aircraft model itself (in an <output> element) will be
|
||||
added after this one. Care should be taken not to refer to the same file
|
||||
name.
|
||||
An output directives file contains an <output> </output> element, within
|
||||
which should be specified the parameters or parameter groups that should
|
||||
be logged.
|
||||
@param fname the filename of an output directives file.
|
||||
*/
|
||||
bool SetOutputDirectives(const SGPath& fname)
|
||||
{ return Output->SetDirectivesFile(GetFullPath(fname)); }
|
||||
|
||||
/** Forces the specified output object to print its items once */
|
||||
void ForceOutput(int idx=0) { Output->ForceOutput(idx); }
|
||||
|
||||
/** Sets the logging rate in Hz for all output objects (if any). */
|
||||
void SetLoggingRate(double rate) { Output->SetRateHz(rate); }
|
||||
|
||||
/** Sets (or overrides) the output filename
|
||||
@param n index of file
|
||||
@param fname the name of the file to output data to
|
||||
@return true if successful, false if there is no output specified for the flight model */
|
||||
bool SetOutputFileName(const int n, const std::string& fname) { return Output->SetOutputName(n, fname); }
|
||||
|
||||
/** Retrieves the current output filename.
|
||||
@param n index of file
|
||||
@return the name of the output file for the output specified by the flight model.
|
||||
If none is specified, the empty string is returned. */
|
||||
std::string GetOutputFileName(int n) const { return Output->GetOutputName(n); }
|
||||
|
||||
/** Executes trimming in the selected mode.
|
||||
* @param mode Specifies how to trim:
|
||||
* - tLongitudinal=0
|
||||
* - tFull
|
||||
* - tGround
|
||||
* - tPullup
|
||||
* - tCustom
|
||||
* - tTurn
|
||||
* - tNone */
|
||||
void DoTrim(int mode);
|
||||
|
||||
/// Disables data logging to all outputs.
|
||||
void DisableOutput(void) { Output->Disable(); }
|
||||
/// Enables data logging to all outputs.
|
||||
void EnableOutput(void) { Output->Enable(); }
|
||||
/// Pauses execution by preventing time from incrementing.
|
||||
void Hold(void) {holding = true;}
|
||||
/// Turn on hold after increment
|
||||
void EnableIncrementThenHold(int Timesteps) {TimeStepsUntilHold = Timesteps; IncrementThenHolding = true;}
|
||||
/// Checks if required to hold afer increment
|
||||
void CheckIncrementalHold(void);
|
||||
/// Resumes execution from a "Hold".
|
||||
void Resume(void) {holding = false;}
|
||||
/// Returns true if the simulation is Holding (i.e. simulation time is not moving).
|
||||
bool Holding(void) {return holding;}
|
||||
/// Mode flags for ResetToInitialConditions
|
||||
static const int START_NEW_OUTPUT = 0x1;
|
||||
static const int DONT_EXECUTE_RUN_IC = 0x2;
|
||||
/** Resets the initial conditions object and prepares the simulation to run
|
||||
again. If the mode's first bit is set the output instances will take special actions
|
||||
such as closing the current output file and open a new one with a different name.
|
||||
If the second bit is set then RunIC() won't be executed, leaving it to the caller
|
||||
to call RunIC(), e.g. in case the caller wants to set some other state like control
|
||||
surface deflections which would've been reset.
|
||||
@param mode Sets the reset mode.*/
|
||||
void ResetToInitialConditions(int mode);
|
||||
/// Sets the debug level.
|
||||
void SetDebugLevel(int level) {debug_lvl = level;}
|
||||
|
||||
struct PropertyCatalogStructure {
|
||||
/// Name of the property.
|
||||
std::string base_string;
|
||||
/// The node for the property.
|
||||
FGPropertyNode_ptr node;
|
||||
};
|
||||
|
||||
/** Builds a catalog of properties.
|
||||
* This function descends the property tree and creates a list (an STL vector)
|
||||
* containing the name and node for all properties.
|
||||
* @param pcs The "root" property catalog structure pointer. */
|
||||
void BuildPropertyCatalog(struct PropertyCatalogStructure* pcs);
|
||||
|
||||
/** Retrieves property or properties matching the supplied string.
|
||||
* A string is returned that contains a carriage return delimited list of all
|
||||
* strings in the property catalog that matches the supplied check string.
|
||||
* @param check The string to search for in the property catalog.
|
||||
* @return the carriage-return-delimited string containing all matching strings
|
||||
* in the catalog. */
|
||||
std::string QueryPropertyCatalog(const std::string& check);
|
||||
|
||||
// Print the contents of the property catalog for the loaded aircraft.
|
||||
void PrintPropertyCatalog(void);
|
||||
|
||||
// Print the simulation configuration
|
||||
void PrintSimulationConfiguration(void) const;
|
||||
|
||||
std::vector<std::string>& GetPropertyCatalog(void) {return PropertyCatalog;}
|
||||
|
||||
void SetTrimStatus(bool status){ trim_status = status; }
|
||||
bool GetTrimStatus(void) const { return trim_status; }
|
||||
void SetTrimMode(int mode){ ta_mode = mode; }
|
||||
int GetTrimMode(void) const { return ta_mode; }
|
||||
|
||||
std::string GetPropulsionTankReport();
|
||||
|
||||
/// Returns the cumulative simulation time in seconds.
|
||||
double GetSimTime(void) const { return sim_time; }
|
||||
|
||||
/// Returns the simulation delta T.
|
||||
double GetDeltaT(void) const {return dT;}
|
||||
|
||||
/// Suspends the simulation and sets the delta T to zero.
|
||||
void SuspendIntegration(void) {saved_dT = dT; dT = 0.0;}
|
||||
|
||||
/// Resumes the simulation by resetting delta T to the correct value.
|
||||
void ResumeIntegration(void) {dT = saved_dT;}
|
||||
|
||||
/** Returns the simulation suspension state.
|
||||
@return true if suspended, false if executing */
|
||||
bool IntegrationSuspended(void) const {return dT == 0.0;}
|
||||
|
||||
/** Sets the current sim time.
|
||||
@param cur_time the current time
|
||||
@return the current simulation time. */
|
||||
double Setsim_time(double cur_time);
|
||||
|
||||
/** Sets the integration time step for the simulation executive.
|
||||
@param delta_t the time step in seconds. */
|
||||
void Setdt(double delta_t) { dT = delta_t; }
|
||||
|
||||
/** Set the root directory that is used to obtain absolute paths from
|
||||
relative paths.
|
||||
Aircraft, engine, systems and output paths are not updated by this
|
||||
method. You must call each methods (SetAircraftPath(), SetEnginePath(),
|
||||
etc.) individually if you need to update these paths as well.
|
||||
@param rootDir the path to the root directory.
|
||||
@see GetRootDir
|
||||
@see SetAircraftPath
|
||||
@see SetEnginePath
|
||||
@see SetSystemsPath
|
||||
@see SetOutputPath
|
||||
*/
|
||||
void SetRootDir(const SGPath& rootDir) {RootDir = rootDir;}
|
||||
|
||||
/** Retrieve the Root Directory.
|
||||
@return the path to the root (base) JSBSim directory.
|
||||
@see SetRootDir */
|
||||
const SGPath& GetRootDir(void) const {return RootDir;}
|
||||
|
||||
/** Increments the simulation time if not in Holding mode. The Frame counter
|
||||
is also incremented.
|
||||
@return the new simulation time. */
|
||||
double IncrTime(void);
|
||||
|
||||
/** Retrieves the current frame count. */
|
||||
unsigned int GetFrame(void) const {return Frame;}
|
||||
|
||||
/** Retrieves the current debug level setting. */
|
||||
int GetDebugLevel(void) const {return debug_lvl;};
|
||||
|
||||
/** Initializes the simulation with initial conditions
|
||||
@param FGIC The initial conditions that will be passed to the simulation. */
|
||||
void Initialize(FGInitialCondition *FGIC);
|
||||
|
||||
/** Sets the property forces/hold-down. This allows to do hard 'hold-down'
|
||||
such as for rockets on a launch pad with engines ignited.
|
||||
@param hd enables the 'hold-down' function if non-zero
|
||||
*/
|
||||
void SetHoldDown(bool hd);
|
||||
|
||||
/** Gets the value of the property forces/hold-down.
|
||||
@result zero if the 'hold-down' function is disabled, non-zero otherwise.
|
||||
*/
|
||||
bool GetHoldDown(void) const {return HoldDown;}
|
||||
|
||||
FGTemplateFunc* GetTemplateFunc(const std::string& name) {
|
||||
return TemplateFunctions.count(name) ? TemplateFunctions[name] : nullptr;
|
||||
}
|
||||
|
||||
void AddTemplateFunc(const std::string& name, Element* el) {
|
||||
TemplateFunctions[name] = new FGTemplateFunc(this, el);
|
||||
}
|
||||
|
||||
const std::shared_ptr<std::default_random_engine>& GetRandomEngine(void) const
|
||||
{ return RandomEngine; }
|
||||
|
||||
private:
|
||||
unsigned int Frame;
|
||||
unsigned int IdFDM;
|
||||
int disperse;
|
||||
unsigned short Terminate;
|
||||
double dT;
|
||||
double saved_dT;
|
||||
double sim_time;
|
||||
bool holding;
|
||||
bool IncrementThenHolding;
|
||||
int TimeStepsUntilHold;
|
||||
bool Constructing;
|
||||
bool modelLoaded;
|
||||
bool IsChild;
|
||||
std::string modelName;
|
||||
SGPath AircraftPath;
|
||||
SGPath FullAircraftPath;
|
||||
SGPath EnginePath;
|
||||
SGPath SystemsPath;
|
||||
SGPath OutputPath;
|
||||
std::string CFGVersion;
|
||||
std::string Release;
|
||||
SGPath RootDir;
|
||||
|
||||
// Standard Model pointers - shortcuts for internal executive use only.
|
||||
FGPropagate* Propagate;
|
||||
FGInertial* Inertial;
|
||||
FGAtmosphere* Atmosphere;
|
||||
FGWinds* Winds;
|
||||
FGAuxiliary* Auxiliary;
|
||||
FGFCS* FCS;
|
||||
FGPropulsion* Propulsion;
|
||||
FGAerodynamics* Aerodynamics;
|
||||
FGGroundReactions* GroundReactions;
|
||||
FGExternalReactions* ExternalReactions;
|
||||
FGBuoyantForces* BuoyantForces;
|
||||
FGMassBalance* MassBalance;
|
||||
FGAircraft* Aircraft;
|
||||
FGAccelerations* Accelerations;
|
||||
FGOutput* Output;
|
||||
|
||||
bool trim_status;
|
||||
int ta_mode;
|
||||
unsigned int ResetMode;
|
||||
int trim_completed;
|
||||
|
||||
FGScript* Script;
|
||||
FGInitialCondition* IC;
|
||||
FGTrim* Trim;
|
||||
|
||||
FGPropertyManager* Root;
|
||||
bool StandAlone;
|
||||
FGPropertyManager* instance;
|
||||
|
||||
bool HoldDown;
|
||||
|
||||
int RandomSeed;
|
||||
std::shared_ptr<std::default_random_engine> RandomEngine;
|
||||
|
||||
// The FDM counter is used to give each child FDM an unique ID. The root FDM
|
||||
// has the ID 0
|
||||
unsigned int* FDMctr;
|
||||
|
||||
std::vector <std::string> PropertyCatalog;
|
||||
std::vector <childData*> ChildFDMList;
|
||||
std::vector <FGModel*> Models;
|
||||
std::map<std::string, FGTemplateFunc_ptr> TemplateFunctions;
|
||||
|
||||
bool ReadFileHeader(Element*);
|
||||
bool ReadChild(Element*);
|
||||
bool ReadPrologue(Element*);
|
||||
void SRand(int sr);
|
||||
int SRand(void) const {return RandomSeed;}
|
||||
void LoadInputs(unsigned int idx);
|
||||
void LoadPlanetConstants(void);
|
||||
void LoadModelConstants(void);
|
||||
bool Allocate(void);
|
||||
bool DeAllocate(void);
|
||||
void InitializeModels(void);
|
||||
int GetDisperse(void) const {return disperse;}
|
||||
SGPath GetFullPath(const SGPath& name) {
|
||||
if (name.isRelative())
|
||||
return RootDir/name.utf8Str();
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
311
src/FDM/JSBSim/FGJSBBase.cpp
Normal file
311
src/FDM/JSBSim/FGJSBBase.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGJSBBase.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 07/01/01
|
||||
Purpose: Encapsulates the JSBBase object
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
07/01/01 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#define BASE
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "models/FGAtmosphere.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef _MSC_VER
|
||||
char FGJSBBase::highint[5] = {27, '[', '1', 'm', '\0' };
|
||||
char FGJSBBase::halfint[5] = {27, '[', '2', 'm', '\0' };
|
||||
char FGJSBBase::normint[6] = {27, '[', '2', '2', 'm', '\0' };
|
||||
char FGJSBBase::reset[5] = {27, '[', '0', 'm', '\0' };
|
||||
char FGJSBBase::underon[5] = {27, '[', '4', 'm', '\0' };
|
||||
char FGJSBBase::underoff[6] = {27, '[', '2', '4', 'm', '\0' };
|
||||
char FGJSBBase::fgblue[6] = {27, '[', '3', '4', 'm', '\0' };
|
||||
char FGJSBBase::fgcyan[6] = {27, '[', '3', '6', 'm', '\0' };
|
||||
char FGJSBBase::fgred[6] = {27, '[', '3', '1', 'm', '\0' };
|
||||
char FGJSBBase::fggreen[6] = {27, '[', '3', '2', 'm', '\0' };
|
||||
char FGJSBBase::fgdef[6] = {27, '[', '3', '9', 'm', '\0' };
|
||||
#else
|
||||
char FGJSBBase::highint[5] = {'\0' };
|
||||
char FGJSBBase::halfint[5] = {'\0' };
|
||||
char FGJSBBase::normint[6] = {'\0' };
|
||||
char FGJSBBase::reset[5] = {'\0' };
|
||||
char FGJSBBase::underon[5] = {'\0' };
|
||||
char FGJSBBase::underoff[6] = {'\0' };
|
||||
char FGJSBBase::fgblue[6] = {'\0' };
|
||||
char FGJSBBase::fgcyan[6] = {'\0' };
|
||||
char FGJSBBase::fgred[6] = {'\0' };
|
||||
char FGJSBBase::fggreen[6] = {'\0' };
|
||||
char FGJSBBase::fgdef[6] = {'\0' };
|
||||
#endif
|
||||
|
||||
const string FGJSBBase::needed_cfg_version = "2.0";
|
||||
const string FGJSBBase::JSBSim_version = JSBSIM_VERSION " " __DATE__ " " __TIME__ ;
|
||||
|
||||
queue <FGJSBBase::Message> FGJSBBase::Messages;
|
||||
FGJSBBase::Message FGJSBBase::localMsg;
|
||||
unsigned int FGJSBBase::messageId = 0;
|
||||
|
||||
int FGJSBBase::gaussian_random_number_phase = 0;
|
||||
|
||||
short FGJSBBase::debug_lvl = 1;
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::PutMessage(const Message& msg)
|
||||
{
|
||||
Messages.push(msg);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::PutMessage(const string& text)
|
||||
{
|
||||
Message msg;
|
||||
msg.text = text;
|
||||
msg.messageId = messageId++;
|
||||
msg.subsystem = "FDM";
|
||||
msg.type = Message::eText;
|
||||
Messages.push(msg);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::PutMessage(const string& text, bool bVal)
|
||||
{
|
||||
Message msg;
|
||||
msg.text = text;
|
||||
msg.messageId = messageId++;
|
||||
msg.subsystem = "FDM";
|
||||
msg.type = Message::eBool;
|
||||
msg.bVal = bVal;
|
||||
Messages.push(msg);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::PutMessage(const string& text, int iVal)
|
||||
{
|
||||
Message msg;
|
||||
msg.text = text;
|
||||
msg.messageId = messageId++;
|
||||
msg.subsystem = "FDM";
|
||||
msg.type = Message::eInteger;
|
||||
msg.iVal = iVal;
|
||||
Messages.push(msg);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::PutMessage(const string& text, double dVal)
|
||||
{
|
||||
Message msg;
|
||||
msg.text = text;
|
||||
msg.messageId = messageId++;
|
||||
msg.subsystem = "FDM";
|
||||
msg.type = Message::eDouble;
|
||||
msg.dVal = dVal;
|
||||
Messages.push(msg);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::ProcessMessage(void)
|
||||
{
|
||||
if (Messages.empty()) return;
|
||||
localMsg = Messages.front();
|
||||
|
||||
while (SomeMessages()) {
|
||||
switch (localMsg.type) {
|
||||
case JSBSim::FGJSBBase::Message::eText:
|
||||
cout << localMsg.messageId << ": " << localMsg.text << endl;
|
||||
break;
|
||||
case JSBSim::FGJSBBase::Message::eBool:
|
||||
cout << localMsg.messageId << ": " << localMsg.text << " " << localMsg.bVal << endl;
|
||||
break;
|
||||
case JSBSim::FGJSBBase::Message::eInteger:
|
||||
cout << localMsg.messageId << ": " << localMsg.text << " " << localMsg.iVal << endl;
|
||||
break;
|
||||
case JSBSim::FGJSBBase::Message::eDouble:
|
||||
cout << localMsg.messageId << ": " << localMsg.text << " " << localMsg.dVal << endl;
|
||||
break;
|
||||
default:
|
||||
cerr << "Unrecognized message type." << endl;
|
||||
break;
|
||||
}
|
||||
Messages.pop();
|
||||
if (SomeMessages()) localMsg = Messages.front();
|
||||
else break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGJSBBase::Message* FGJSBBase::ProcessNextMessage(void)
|
||||
{
|
||||
if (Messages.empty()) return NULL;
|
||||
localMsg = Messages.front();
|
||||
|
||||
Messages.pop();
|
||||
return &localMsg;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGJSBBase::disableHighLighting(void)
|
||||
{
|
||||
highint[0]='\0';
|
||||
halfint[0]='\0';
|
||||
normint[0]='\0';
|
||||
reset[0]='\0';
|
||||
underon[0]='\0';
|
||||
underoff[0]='\0';
|
||||
fgblue[0]='\0';
|
||||
fgcyan[0]='\0';
|
||||
fgred[0]='\0';
|
||||
fggreen[0]='\0';
|
||||
fgdef[0]='\0';
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGJSBBase::CreateIndexedPropertyName(const string& Property, int index)
|
||||
{
|
||||
ostringstream buf;
|
||||
buf << Property << '[' << index << ']';
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGJSBBase::GaussianRandomNumber(void)
|
||||
{
|
||||
static double V1, V2, S;
|
||||
double X;
|
||||
|
||||
if (gaussian_random_number_phase == 0) {
|
||||
V1 = V2 = S = X = 0.0;
|
||||
|
||||
do {
|
||||
double U1 = (double)rand() / RAND_MAX;
|
||||
double U2 = (double)rand() / RAND_MAX;
|
||||
|
||||
V1 = 2 * U1 - 1;
|
||||
V2 = 2 * U2 - 1;
|
||||
S = V1 * V1 + V2 * V2;
|
||||
} while(S >= 1 || S == 0);
|
||||
|
||||
X = V1 * sqrt(-2 * log(S) / S);
|
||||
} else
|
||||
X = V2 * sqrt(-2 * log(S) / S);
|
||||
|
||||
gaussian_random_number_phase = 1 - gaussian_random_number_phase;
|
||||
|
||||
return X;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGJSBBase::PitotTotalPressure(double mach, double p)
|
||||
{
|
||||
if (mach < 0) return p;
|
||||
if (mach < 1) //calculate total pressure assuming isentropic flow
|
||||
return p*pow((1 + 0.2*mach*mach),3.5);
|
||||
else {
|
||||
// shock in front of pitot tube, we'll assume its normal and use
|
||||
// the Rayleigh Pitot Tube Formula, i.e. the ratio of total
|
||||
// pressure behind the shock to the static pressure in front of
|
||||
// the normal shock assumption should not be a bad one -- most supersonic
|
||||
// aircraft place the pitot probe out front so that it is the forward
|
||||
// most point on the aircraft. The real shock would, of course, take
|
||||
// on something like the shape of a rounded-off cone but, here again,
|
||||
// the assumption should be good since the opening of the pitot probe
|
||||
// is very small and, therefore, the effects of the shock curvature
|
||||
// should be small as well. AFAIK, this approach is fairly well accepted
|
||||
// within the aerospace community
|
||||
|
||||
// The denominator below is zero for Mach ~ 0.38, for which
|
||||
// we'll never be here, so we're safe
|
||||
|
||||
return p*166.92158009316827*pow(mach,7.0)/pow(7*mach*mach-1,2.5);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// Based on the formulas in the US Air Force Aircraft Performance Flight Testing
|
||||
// Manual (AFFTC-TIH-99-01). In particular sections 4.6 to 4.8.
|
||||
|
||||
double FGJSBBase::MachFromImpactPressure(double qc, double p)
|
||||
{
|
||||
double A = qc / p + 1;
|
||||
double M = sqrt(5.0*(pow(A, 1. / 3.5) - 1)); // Equation (4.12)
|
||||
|
||||
if (M > 1.0)
|
||||
for (unsigned int i = 0; i<10; i++)
|
||||
M = 0.8812848543473311*sqrt(A*pow(1 - 1.0 / (7.0*M*M), 2.5)); // Equation (4.17)
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGJSBBase::VcalibratedFromMach(double mach, double p)
|
||||
{
|
||||
double asl = FGAtmosphere::StdDaySLsoundspeed;
|
||||
double psl = FGAtmosphere::StdDaySLpressure;
|
||||
double qc = PitotTotalPressure(mach, p) - p;
|
||||
|
||||
return asl * MachFromImpactPressure(qc, psl);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGJSBBase::MachFromVcalibrated(double vcas, double p)
|
||||
{
|
||||
double asl = FGAtmosphere::StdDaySLsoundspeed;
|
||||
double psl = FGAtmosphere::StdDaySLpressure;
|
||||
double qc = PitotTotalPressure(vcas / asl, psl) - psl;
|
||||
|
||||
return MachFromImpactPressure(qc, p);
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
403
src/FDM/JSBSim/FGJSBBase.h
Normal file
403
src/FDM/JSBSim/FGJSBBase.h
Normal file
@@ -0,0 +1,403 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGJSBBase.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 07/01/01
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
07/01/01 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGJSBBASE_H
|
||||
#define FGJSBBASE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <float.h>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "input_output/string_utilities.h"
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class BaseException : public std::runtime_error {
|
||||
public:
|
||||
BaseException(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** JSBSim Base class.
|
||||
* This class provides universal constants, utility functions, messaging
|
||||
* functions, and enumerated constants to JSBSim.
|
||||
@author Jon S. Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGJSBBase {
|
||||
public:
|
||||
/// Constructor for FGJSBBase.
|
||||
FGJSBBase() {};
|
||||
|
||||
/// Destructor for FGJSBBase.
|
||||
virtual ~FGJSBBase() {};
|
||||
|
||||
/// JSBSim Message structure
|
||||
struct Message {
|
||||
unsigned int fdmId;
|
||||
unsigned int messageId;
|
||||
std::string text;
|
||||
std::string subsystem;
|
||||
enum mType {eText, eInteger, eDouble, eBool} type;
|
||||
bool bVal;
|
||||
int iVal;
|
||||
double dVal;
|
||||
};
|
||||
|
||||
/// First order, (low pass / lag) filter
|
||||
class Filter {
|
||||
double prev_in;
|
||||
double prev_out;
|
||||
double ca;
|
||||
double cb;
|
||||
public:
|
||||
Filter(void) {}
|
||||
Filter(double coeff, double dt) {
|
||||
prev_in = prev_out = 0.0;
|
||||
double denom = 2.0 + coeff*dt;
|
||||
ca = coeff*dt/denom;
|
||||
cb = (2.0 - coeff*dt)/denom;
|
||||
}
|
||||
double execute(double in) {
|
||||
double out = (in + prev_in)*ca + prev_out*cb;
|
||||
prev_in = in;
|
||||
prev_out = out;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
///@name JSBSim console output highlighting terms.
|
||||
//@{
|
||||
/// highlights text
|
||||
static char highint[5];
|
||||
/// low intensity text
|
||||
static char halfint[5];
|
||||
/// normal intensity text
|
||||
static char normint[6];
|
||||
/// resets text properties
|
||||
static char reset[5];
|
||||
/// underlines text
|
||||
static char underon[5];
|
||||
/// underline off
|
||||
static char underoff[6];
|
||||
/// blue text
|
||||
static char fgblue[6];
|
||||
/// cyan text
|
||||
static char fgcyan[6];
|
||||
/// red text
|
||||
static char fgred[6];
|
||||
/// green text
|
||||
static char fggreen[6];
|
||||
/// default text
|
||||
static char fgdef[6];
|
||||
//@}
|
||||
|
||||
///@name JSBSim Messaging functions
|
||||
//@{
|
||||
/** Places a Message structure on the Message queue.
|
||||
@param msg pointer to a Message structure
|
||||
@return pointer to a Message structure */
|
||||
void PutMessage(const Message& msg);
|
||||
/** Creates a message with the given text and places it on the queue.
|
||||
@param text message text
|
||||
@return pointer to a Message structure */
|
||||
void PutMessage(const std::string& text);
|
||||
/** Creates a message with the given text and boolean value and places it on the queue.
|
||||
@param text message text
|
||||
@param bVal boolean value associated with the message
|
||||
@return pointer to a Message structure */
|
||||
void PutMessage(const std::string& text, bool bVal);
|
||||
/** Creates a message with the given text and integer value and places it on the queue.
|
||||
@param text message text
|
||||
@param iVal integer value associated with the message
|
||||
@return pointer to a Message structure */
|
||||
void PutMessage(const std::string& text, int iVal);
|
||||
/** Creates a message with the given text and double value and places it on the queue.
|
||||
@param text message text
|
||||
@param dVal double value associated with the message
|
||||
@return pointer to a Message structure */
|
||||
void PutMessage(const std::string& text, double dVal);
|
||||
/** Reads the message on the queue (but does not delete it).
|
||||
@return 1 if some messages */
|
||||
int SomeMessages(void) const { return !Messages.empty(); }
|
||||
/** Reads the message on the queue and removes it from the queue.
|
||||
This function also prints out the message.*/
|
||||
void ProcessMessage(void);
|
||||
/** Reads the next message on the queue and removes it from the queue.
|
||||
This function also prints out the message.
|
||||
@return a pointer to the message, or NULL if there are no messages.*/
|
||||
Message* ProcessNextMessage(void);
|
||||
//@}
|
||||
|
||||
/** Returns the version number of JSBSim.
|
||||
* @return The version number of JSBSim. */
|
||||
static const std::string& GetVersion(void) {return JSBSim_version;}
|
||||
|
||||
/// Disables highlighting in the console output.
|
||||
void disableHighLighting(void);
|
||||
|
||||
static short debug_lvl;
|
||||
|
||||
/** Converts from degrees Kelvin to degrees Fahrenheit.
|
||||
* @param kelvin The temperature in degrees Kelvin.
|
||||
* @return The temperature in Fahrenheit. */
|
||||
static constexpr double KelvinToFahrenheit (double kelvin) {
|
||||
return 1.8*kelvin - 459.4;
|
||||
}
|
||||
|
||||
/** Converts from degrees Celsius to degrees Rankine.
|
||||
* @param celsius The temperature in degrees Celsius.
|
||||
* @return The temperature in Rankine. */
|
||||
static constexpr double CelsiusToRankine (double celsius) {
|
||||
return celsius * 1.8 + 491.67;
|
||||
}
|
||||
|
||||
/** Converts from degrees Rankine to degrees Celsius.
|
||||
* @param rankine The temperature in degrees Rankine.
|
||||
* @return The temperature in Celsius. */
|
||||
static constexpr double RankineToCelsius (double rankine) {
|
||||
return (rankine - 491.67)/1.8;
|
||||
}
|
||||
|
||||
/** Converts from degrees Kelvin to degrees Rankine.
|
||||
* @param kelvin The temperature in degrees Kelvin.
|
||||
* @return The temperature in Rankine. */
|
||||
static constexpr double KelvinToRankine (double kelvin) {
|
||||
return kelvin * 1.8;
|
||||
}
|
||||
|
||||
/** Converts from degrees Rankine to degrees Kelvin.
|
||||
* @param rankine The temperature in degrees Rankine.
|
||||
* @return The temperature in Kelvin. */
|
||||
static constexpr double RankineToKelvin (double rankine) {
|
||||
return rankine/1.8;
|
||||
}
|
||||
|
||||
/** Converts from degrees Fahrenheit to degrees Celsius.
|
||||
* @param fahrenheit The temperature in degrees Fahrenheit.
|
||||
* @return The temperature in Celsius. */
|
||||
static constexpr double FahrenheitToCelsius (double fahrenheit) {
|
||||
return (fahrenheit - 32.0)/1.8;
|
||||
}
|
||||
|
||||
/** Converts from degrees Celsius to degrees Fahrenheit.
|
||||
* @param celsius The temperature in degrees Celsius.
|
||||
* @return The temperature in Fahrenheit. */
|
||||
static constexpr double CelsiusToFahrenheit (double celsius) {
|
||||
return celsius * 1.8 + 32.0;
|
||||
}
|
||||
|
||||
/** Converts from degrees Celsius to degrees Kelvin
|
||||
* @param celsius The temperature in degrees Celsius.
|
||||
* @return The temperature in Kelvin. */
|
||||
static constexpr double CelsiusToKelvin (double celsius) {
|
||||
return celsius + 273.15;
|
||||
}
|
||||
|
||||
/** Converts from degrees Kelvin to degrees Celsius
|
||||
* @param celsius The temperature in degrees Kelvin.
|
||||
* @return The temperature in Celsius. */
|
||||
static constexpr double KelvinToCelsius (double kelvin) {
|
||||
return kelvin - 273.15;
|
||||
}
|
||||
|
||||
/** Converts from feet to meters
|
||||
* @param measure The length in feet.
|
||||
* @return The length in meters. */
|
||||
static constexpr double FeetToMeters (double measure) {
|
||||
return measure*0.3048;
|
||||
}
|
||||
|
||||
/** Compute the total pressure in front of the Pitot tube. It uses the
|
||||
* Rayleigh formula for supersonic speeds (See "Introduction to Aerodynamics
|
||||
* of a Compressible Fluid - H.W. Liepmann, A.E. Puckett - Wiley & sons
|
||||
* (1947)" §5.4 pp 75-80)
|
||||
* @param mach The Mach number
|
||||
* @param p Pressure in psf
|
||||
* @return The total pressure in front of the Pitot tube in psf */
|
||||
static double PitotTotalPressure(double mach, double p);
|
||||
|
||||
/** Compute the Mach number from the differential pressure (qc) and the
|
||||
* static pressure. Based on the formulas in the US Air Force Aircraft
|
||||
* Performance Flight Testing Manual (AFFTC-TIH-99-01).
|
||||
* @param qc The differential/impact pressure
|
||||
* @param p Pressure in psf
|
||||
* @return The Mach number */
|
||||
static double MachFromImpactPressure(double qc, double p);
|
||||
|
||||
/** Calculate the calibrated airspeed from the Mach number. Based on the
|
||||
* formulas in the US Air Force Aircraft Performance Flight Testing
|
||||
* Manual (AFFTC-TIH-99-01).
|
||||
* @param mach The Mach number
|
||||
* @param p Pressure in psf
|
||||
* @return The calibrated airspeed (CAS) in ft/s
|
||||
* */
|
||||
static double VcalibratedFromMach(double mach, double p);
|
||||
|
||||
/** Calculate the Mach number from the calibrated airspeed.Based on the
|
||||
* formulas in the US Air Force Aircraft Performance Flight Testing
|
||||
* Manual (AFFTC-TIH-99-01).
|
||||
* @param vcas The calibrated airspeed (CAS) in ft/s
|
||||
* @param p Pressure in psf
|
||||
* @return The Mach number
|
||||
* */
|
||||
static double MachFromVcalibrated(double vcas, double p);
|
||||
|
||||
/** Finite precision comparison.
|
||||
@param a first value to compare
|
||||
@param b second value to compare
|
||||
@return if the two values can be considered equal up to roundoff */
|
||||
static bool EqualToRoundoff(double a, double b) {
|
||||
double eps = 2.0*DBL_EPSILON;
|
||||
return std::fabs(a - b) <= eps * std::max<double>(std::fabs(a), std::fabs(b));
|
||||
}
|
||||
|
||||
/** Finite precision comparison.
|
||||
@param a first value to compare
|
||||
@param b second value to compare
|
||||
@return if the two values can be considered equal up to roundoff */
|
||||
static bool EqualToRoundoff(float a, float b) {
|
||||
float eps = 2.0*FLT_EPSILON;
|
||||
return std::fabs(a - b) <= eps * std::max<double>(std::fabs(a), std::fabs(b));
|
||||
}
|
||||
|
||||
/** Finite precision comparison.
|
||||
@param a first value to compare
|
||||
@param b second value to compare
|
||||
@return if the two values can be considered equal up to roundoff */
|
||||
static bool EqualToRoundoff(float a, double b) {
|
||||
return EqualToRoundoff(a, (float)b);
|
||||
}
|
||||
|
||||
/** Finite precision comparison.
|
||||
@param a first value to compare
|
||||
@param b second value to compare
|
||||
@return if the two values can be considered equal up to roundoff */
|
||||
static bool EqualToRoundoff(double a, float b) {
|
||||
return EqualToRoundoff((float)a, b);
|
||||
}
|
||||
|
||||
/** Constrain a value between a minimum and a maximum value.
|
||||
*/
|
||||
static constexpr double Constrain(double min, double value, double max) {
|
||||
return value<min?(min):(value>max?(max):(value));
|
||||
}
|
||||
|
||||
static constexpr double sign(double num) {return num>=0.0?1.0:-1.0;}
|
||||
|
||||
static double GaussianRandomNumber(void);
|
||||
|
||||
protected:
|
||||
static Message localMsg;
|
||||
|
||||
static std::queue <Message> Messages;
|
||||
|
||||
static unsigned int messageId;
|
||||
|
||||
static constexpr double radtodeg = 180. / M_PI;
|
||||
static constexpr double degtorad = M_PI / 180.;
|
||||
static constexpr double hptoftlbssec = 550.0;
|
||||
static constexpr double psftoinhg = 0.014138;
|
||||
static constexpr double psftopa = 47.88;
|
||||
static constexpr double ktstofps = 1.68781;
|
||||
static constexpr double fpstokts = 1.0 / ktstofps;
|
||||
static constexpr double inchtoft = 1.0/12.0;
|
||||
static constexpr double fttom = 0.3048;
|
||||
static constexpr double m3toft3 = 1.0/(fttom*fttom*fttom);
|
||||
static constexpr double in3tom3 = inchtoft*inchtoft*inchtoft/m3toft3;
|
||||
static constexpr double inhgtopa = 3386.38;
|
||||
/** Note that definition of lbtoslug by the inverse of slugtolb and not to a
|
||||
different constant you can also get from some tables will make
|
||||
lbtoslug*slugtolb == 1 up to the magnitude of roundoff. So converting from
|
||||
slug to lb and back will yield to the original value you started with up
|
||||
to the magnitude of roundoff.
|
||||
Taken from units gnu commandline tool */
|
||||
static constexpr double slugtolb = 32.174049;
|
||||
static constexpr double lbtoslug = 1.0/slugtolb;
|
||||
static constexpr double kgtolb = 2.20462;
|
||||
static constexpr double kgtoslug = 0.06852168;
|
||||
static const std::string needed_cfg_version;
|
||||
static const std::string JSBSim_version;
|
||||
|
||||
static std::string CreateIndexedPropertyName(const std::string& Property, int index);
|
||||
|
||||
static int gaussian_random_number_phase;
|
||||
|
||||
public:
|
||||
/// Moments L, M, N
|
||||
enum {eL = 1, eM, eN };
|
||||
/// Rates P, Q, R
|
||||
enum {eP = 1, eQ, eR };
|
||||
/// Velocities U, V, W
|
||||
enum {eU = 1, eV, eW };
|
||||
/// Positions X, Y, Z
|
||||
enum {eX = 1, eY, eZ };
|
||||
/// Euler angles Phi, Theta, Psi
|
||||
enum {ePhi = 1, eTht, ePsi };
|
||||
/// Stability axis forces, Drag, Side force, Lift
|
||||
enum {eDrag = 1, eSide, eLift };
|
||||
/// Local frame orientation Roll, Pitch, Yaw
|
||||
enum {eRoll = 1, ePitch, eYaw };
|
||||
/// Local frame position North, East, Down
|
||||
enum {eNorth = 1, eEast, eDown };
|
||||
/// Locations Radius, Latitude, Longitude
|
||||
enum {eLat = 1, eLong, eRad };
|
||||
/// Conversion specifiers
|
||||
enum {inNone = 0, inDegrees, inRadians, inMeters, inFeet };
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
772
src/FDM/JSBSim/JSBSim.cpp
Normal file
772
src/FDM/JSBSim/JSBSim.cpp
Normal file
@@ -0,0 +1,772 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: JSBSim.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 08/17/99
|
||||
Purpose: Standalone version of JSBSim.
|
||||
Called by: The USER.
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This class implements the JSBSim standalone application. It is set up for compilation
|
||||
under gnu C++, MSVC++, or other compiler.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
08/17/99 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "initialization/FGTrim.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "input_output/FGXMLFileRead.h"
|
||||
|
||||
#if !defined(__GNUC__) && !defined(sgi) && !defined(_MSC_VER)
|
||||
# include <time>
|
||||
#else
|
||||
# include <time.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <float.h>
|
||||
#elif defined(__GNUC__) && !defined(sgi)
|
||||
# include <fenv.h>
|
||||
#endif
|
||||
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <mmsystem.h>
|
||||
# include <regstr.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/timeb.h>
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
using JSBSim::FGXMLFileRead;
|
||||
using JSBSim::Element;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
GLOBAL DATA
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
SGPath RootDir;
|
||||
SGPath ScriptName;
|
||||
string AircraftName;
|
||||
SGPath ResetName;
|
||||
vector <string> LogOutputName;
|
||||
vector <SGPath> LogDirectiveName;
|
||||
vector <string> CommandLineProperties;
|
||||
vector <double> CommandLinePropertyValues;
|
||||
JSBSim::FGFDMExec* FDMExec;
|
||||
JSBSim::FGTrim* trimmer;
|
||||
|
||||
bool realtime;
|
||||
bool play_nice;
|
||||
bool suspend;
|
||||
bool catalog;
|
||||
bool nohighlight;
|
||||
|
||||
double end_time = 1e99;
|
||||
double simulation_rate = 1./120.;
|
||||
bool override_sim_rate = false;
|
||||
double sleep_period=0.01;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
bool options(int, char**);
|
||||
int real_main(int argc, char* argv[]);
|
||||
void PrintHelp(void);
|
||||
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
|
||||
double getcurrentseconds(void)
|
||||
{
|
||||
struct timeb tm_ptr;
|
||||
ftime(&tm_ptr);
|
||||
return tm_ptr.time + tm_ptr.millitm*0.001;
|
||||
}
|
||||
#else
|
||||
double getcurrentseconds(void)
|
||||
{
|
||||
struct timeval tval;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tval, &tz);
|
||||
return (tval.tv_sec + tval.tv_usec*1e-6);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
|
||||
void sim_nsleep(long nanosec)
|
||||
{
|
||||
Sleep((DWORD)(nanosec*1e-6)); // convert nanoseconds (passed in) to milliseconds for Win32.
|
||||
}
|
||||
#else
|
||||
void sim_nsleep(long nanosec)
|
||||
{
|
||||
struct timespec ts, ts1;
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = nanosec;
|
||||
nanosleep(&ts, &ts1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** This class is solely for the purpose of determining what type
|
||||
of file is given on the command line */
|
||||
class XMLFile : public FGXMLFileRead {
|
||||
public:
|
||||
bool IsScriptFile(const SGPath& filename) {
|
||||
bool result=false;
|
||||
Element *document = LoadXMLDocument(filename, false);
|
||||
if (document && document->GetName() == "runscript") result = true;
|
||||
ResetParser();
|
||||
return result;
|
||||
}
|
||||
bool IsLogDirectiveFile(const SGPath& filename) {
|
||||
bool result=false;
|
||||
Element *document = LoadXMLDocument(filename, false);
|
||||
if (document && document->GetName() == "output") result = true;
|
||||
ResetParser();
|
||||
return result;
|
||||
}
|
||||
bool IsAircraftFile(const SGPath& filename) {
|
||||
bool result=false;
|
||||
Element* document = LoadXMLDocument(filename, false);
|
||||
if (document && document->GetName() == "fdm_config") result = true;
|
||||
ResetParser();
|
||||
return result;
|
||||
}
|
||||
bool IsInitFile(const SGPath& filename) {
|
||||
bool result=false;
|
||||
Element *document = LoadXMLDocument(filename, false);
|
||||
if (document && document->GetName() == "initialize") result = true;
|
||||
ResetParser();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** \mainpage JSBSim
|
||||
* An Open Source, Object-Oriented, Cross-Platform Flight Dynamics Model in C++
|
||||
|
||||
* \section intro Introduction
|
||||
*
|
||||
* JSBSim is an open source, multi-platform, object-oriented flight dynamics
|
||||
* model (FDM) framework written in the C++ programming language. It is
|
||||
* designed to support simulation modeling of any aerospace craft without the
|
||||
* need for specific compiled and linked program code, instead relying on a
|
||||
* versatile and powerful specification written in an XML format. The format is
|
||||
* formally known as JSBSim-ML (JSBSim Markup Language).
|
||||
*
|
||||
* JSBSim (www.jsbsim.org) was created initially for the open source FlightGear
|
||||
* flight simulator (www.flightgear.org). JSBSim maintains the ability to run
|
||||
* as a standalone executable in soft real-time, or batch mode. This is useful
|
||||
* for running tests or sets of tests automatically using the internal scripting
|
||||
* capability.
|
||||
*
|
||||
* JSBSim does not model specific aircraft in program code. The aircraft itself
|
||||
* is defined in a file written in an XML-based format
|
||||
* where the aircraft mass and geometric properties are specified. Additional
|
||||
* statements define such characteristics as:
|
||||
*
|
||||
* - Landing gear location and properties.
|
||||
* - Pilot eyepoint
|
||||
* - Additional point masses (passengers, cargo, etc.)
|
||||
* - Propulsion system (engines, fuel tanks, and "thrusters")
|
||||
* - Flight control system
|
||||
* - Autopilot
|
||||
* - Aerodynamic stability derivatives and coefficients
|
||||
*
|
||||
* The configuration file format is set up to be easily comprehensible, for
|
||||
* instance featuring textbook-like coefficients, which enables newcomers to
|
||||
* become immediately fluent in describing vehicles, and requiring only prior
|
||||
* basic theoretical aero knowledge.
|
||||
*
|
||||
* One of the more unique features of JSBSim is its method of modeling aircraft
|
||||
* systems such as a flight control system, autopilot, electrical, etc.
|
||||
* These are modeled by assembling strings of components that represent filters,
|
||||
* switches, summers, gains, sensors, and so on.
|
||||
*
|
||||
* Another unique feature is displayed in the use of "properties". Properties
|
||||
* essentially expose chosen variables as nodes in a tree, in a directory-like
|
||||
* hierarchy. This approach facilitates plugging in different FDMs (Flight Dynamics
|
||||
* Model) into FlightGear, but it also is a fundamental tool in allowing a wide
|
||||
* range of aircraft to be modeled, each having its own unique control system,
|
||||
* aerosurfaces, and flight deck instrument panel. The use of properties allows
|
||||
* all these items for a craft to be modeled and integrated without the need for
|
||||
* specific and unique program source code.
|
||||
*
|
||||
* The equations of motion are modeled essentially as they are presented in
|
||||
* aerospace textbooks for the benefit of student users, but quaternions are
|
||||
* used to track orientation, avoiding "gimbal lock". JSBSim can model the
|
||||
* atmospheric flight of an aircraft, or the motion of a spacecraft in orbit.
|
||||
* Coriolis and centripetal accelerations are incorporated into the EOM.
|
||||
*
|
||||
* JSBSim can output (log) data in a configurable way. Sets of data that are
|
||||
* logically related can be selected to be output at a chosen rate, and
|
||||
* individual properties can be selected for output. The output can be streamed
|
||||
* to the console, and/or to a file (or files), and/or can be transmitted through a
|
||||
* socket or sockets, or any combination of the aforementioned methods.
|
||||
*
|
||||
* JSBSim has been used in a variety of ways:
|
||||
*
|
||||
* - For developing control laws for a sounding rocket
|
||||
* - For crafting an aircraft autopilot as part of a thesis project
|
||||
* - As a flight model for FlightGear
|
||||
* - As an FDM that drives motion base simulators for some
|
||||
* commercial/entertainment simulators
|
||||
*
|
||||
* \section Supported Platforms:
|
||||
* JSBSim has been built on the following platforms:
|
||||
*
|
||||
* - Linux (x86)
|
||||
* - Windows (MSVC, Cygwin, Mingwin)
|
||||
* - Mac OS X
|
||||
* - FreeBSD
|
||||
*
|
||||
* \section depends Dependencies
|
||||
*
|
||||
* JSBSim has no external dependencies at present. No code is autogenerated.
|
||||
*
|
||||
* \section license Licensing
|
||||
*
|
||||
* JSBSim is licensed under the terms of the Lesser GPL (LGPL)
|
||||
*
|
||||
* \section website Website
|
||||
*
|
||||
* For more information, see the JSBSim web site: www.jsbsim.org.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
_clearfp();
|
||||
_controlfp(_controlfp(0, 0) & ~(_EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW),
|
||||
_MCW_EM);
|
||||
#elif defined(__GNUC__) && !defined(sgi) && !defined(__APPLE__)
|
||||
feenableexcept(FE_DIVBYZERO | FE_INVALID);
|
||||
#endif
|
||||
|
||||
try {
|
||||
real_main(argc, argv);
|
||||
} catch (string& msg) {
|
||||
std::cerr << "FATAL ERROR: JSBSim terminated with an exception."
|
||||
<< std::endl << "The message was: " << msg << std::endl;
|
||||
return 1;
|
||||
} catch (...) {
|
||||
std::cerr << "FATAL ERROR: JSBSim terminated with an unknown exception."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int real_main(int argc, char* argv[])
|
||||
{
|
||||
// *** INITIALIZATIONS *** //
|
||||
|
||||
ScriptName = "";
|
||||
AircraftName = "";
|
||||
ResetName = "";
|
||||
LogOutputName.clear();
|
||||
LogDirectiveName.clear();
|
||||
bool result = false, success;
|
||||
bool was_paused = false;
|
||||
|
||||
double frame_duration;
|
||||
|
||||
double new_five_second_value = 0.0;
|
||||
double actual_elapsed_time = 0;
|
||||
double initial_seconds = 0;
|
||||
double current_seconds = 0.0;
|
||||
double paused_seconds = 0.0;
|
||||
double sim_lag_time = 0;
|
||||
double cycle_duration = 0.0;
|
||||
double override_sim_rate_value = 0.0;
|
||||
long sleep_nseconds = 0;
|
||||
|
||||
realtime = false;
|
||||
play_nice = false;
|
||||
suspend = false;
|
||||
catalog = false;
|
||||
nohighlight = false;
|
||||
|
||||
// *** PARSE OPTIONS PASSED INTO THIS SPECIFIC APPLICATION: JSBSim *** //
|
||||
success = options(argc, argv);
|
||||
if (!success) {
|
||||
PrintHelp();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *** SET UP JSBSIM *** //
|
||||
FDMExec = new JSBSim::FGFDMExec();
|
||||
FDMExec->SetRootDir(RootDir);
|
||||
FDMExec->SetAircraftPath(SGPath("aircraft"));
|
||||
FDMExec->SetEnginePath(SGPath("engine"));
|
||||
FDMExec->SetSystemsPath(SGPath("systems"));
|
||||
FDMExec->GetPropertyManager()->Tie("simulation/frame_start_time", &actual_elapsed_time);
|
||||
FDMExec->GetPropertyManager()->Tie("simulation/cycle_duration", &cycle_duration);
|
||||
|
||||
if (nohighlight) FDMExec->disableHighLighting();
|
||||
|
||||
if (simulation_rate < 1.0 )
|
||||
FDMExec->Setdt(simulation_rate);
|
||||
else
|
||||
FDMExec->Setdt(1.0/simulation_rate);
|
||||
|
||||
if (override_sim_rate) override_sim_rate_value = FDMExec->GetDeltaT();
|
||||
|
||||
// SET PROPERTY VALUES THAT ARE GIVEN ON THE COMMAND LINE and which are for the simulation only.
|
||||
|
||||
for (unsigned int i=0; i<CommandLineProperties.size(); i++) {
|
||||
|
||||
if (CommandLineProperties[i].find("simulation") != std::string::npos) {
|
||||
if (FDMExec->GetPropertyManager()->GetNode(CommandLineProperties[i])) {
|
||||
FDMExec->SetPropertyValue(CommandLineProperties[i], CommandLinePropertyValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *** OPTION A: LOAD A SCRIPT, WHICH LOADS EVERYTHING ELSE *** //
|
||||
if (!ScriptName.isNull()) {
|
||||
|
||||
result = FDMExec->LoadScript(ScriptName, override_sim_rate_value, ResetName);
|
||||
|
||||
if (!result) {
|
||||
cerr << "Script file " << ScriptName << " was not successfully loaded" << endl;
|
||||
delete FDMExec;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *** OPTION B: LOAD AN AIRCRAFT AND A SET OF INITIAL CONDITIONS *** //
|
||||
} else if (!AircraftName.empty() || !ResetName.isNull()) {
|
||||
|
||||
if (catalog) FDMExec->SetDebugLevel(0);
|
||||
|
||||
if ( ! FDMExec->LoadModel(SGPath("aircraft"),
|
||||
SGPath("engine"),
|
||||
SGPath("systems"),
|
||||
AircraftName)) {
|
||||
cerr << " JSBSim could not be started" << endl << endl;
|
||||
delete FDMExec;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (catalog) {
|
||||
FDMExec->PrintPropertyCatalog();
|
||||
delete FDMExec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSBSim::FGInitialCondition *IC = FDMExec->GetIC();
|
||||
if ( ! IC->Load(ResetName)) {
|
||||
delete FDMExec;
|
||||
cerr << "Initialization unsuccessful" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
} else {
|
||||
cout << " No Aircraft, Script, or Reset information given" << endl << endl;
|
||||
delete FDMExec;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Load output directives file[s], if given
|
||||
for (unsigned int i=0; i<LogDirectiveName.size(); i++) {
|
||||
if (!LogDirectiveName[i].isNull()) {
|
||||
if (!FDMExec->SetOutputDirectives(LogDirectiveName[i])) {
|
||||
cout << "Output directives not properly set in file " << LogDirectiveName[i] << endl;
|
||||
delete FDMExec;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OVERRIDE OUTPUT FILE NAME. THIS IS USEFUL FOR CASES WHERE MULTIPLE
|
||||
// RUNS ARE BEING MADE (SUCH AS IN A MONTE CARLO STUDY) AND THE OUTPUT FILE
|
||||
// NAME MUST BE SET EACH TIME TO AVOID THE PREVIOUS RUN DATA FROM BEING OVER-
|
||||
// WRITTEN.
|
||||
for (unsigned int i=0; i<LogOutputName.size(); i++) {
|
||||
string old_filename = FDMExec->GetOutputFileName(i);
|
||||
if (!FDMExec->SetOutputFileName(i, LogOutputName[i])) {
|
||||
cout << "Output filename could not be set" << endl;
|
||||
} else {
|
||||
cout << "Output filename change from " << old_filename << " from aircraft"
|
||||
" configuration file to " << LogOutputName[i] << " specified on"
|
||||
" command line" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// SET PROPERTY VALUES THAT ARE GIVEN ON THE COMMAND LINE
|
||||
|
||||
for (unsigned int i=0; i<CommandLineProperties.size(); i++) {
|
||||
|
||||
if (!FDMExec->GetPropertyManager()->GetNode(CommandLineProperties[i])) {
|
||||
cerr << endl << " No property by the name " << CommandLineProperties[i] << endl;
|
||||
delete FDMExec;
|
||||
exit(-1);
|
||||
} else {
|
||||
FDMExec->SetPropertyValue(CommandLineProperties[i], CommandLinePropertyValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FDMExec->RunIC();
|
||||
|
||||
// PRINT SIMULATION CONFIGURATION
|
||||
FDMExec->PrintSimulationConfiguration();
|
||||
|
||||
// Dump the simulation state (position, orientation, etc.)
|
||||
FDMExec->GetPropagate()->DumpState();
|
||||
|
||||
// Perform trim if requested via the initialization file
|
||||
JSBSim::TrimMode icTrimRequested = (JSBSim::TrimMode)FDMExec->GetIC()->TrimRequested();
|
||||
if (icTrimRequested != JSBSim::TrimMode::tNone) {
|
||||
trimmer = new JSBSim::FGTrim( FDMExec, icTrimRequested );
|
||||
try {
|
||||
trimmer->DoTrim();
|
||||
|
||||
if (FDMExec->GetDebugLevel() > 0)
|
||||
trimmer->Report();
|
||||
|
||||
delete trimmer;
|
||||
} catch (string& msg) {
|
||||
cerr << endl << msg << endl << endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
cout << endl << JSBSim::FGFDMExec::fggreen << JSBSim::FGFDMExec::highint
|
||||
<< "---- JSBSim Execution beginning ... --------------------------------------------"
|
||||
<< JSBSim::FGFDMExec::reset << endl << endl;
|
||||
|
||||
result = FDMExec->Run(); // MAKE AN INITIAL RUN
|
||||
|
||||
if (suspend) FDMExec->Hold();
|
||||
|
||||
// Print actual time at start
|
||||
char s[100];
|
||||
time_t tod;
|
||||
time(&tod);
|
||||
strftime(s, 99, "%A %B %d %Y %X", localtime(&tod));
|
||||
cout << "Start: " << s << " (HH:MM:SS)" << endl;
|
||||
|
||||
frame_duration = FDMExec->GetDeltaT();
|
||||
if (realtime) sleep_nseconds = (long)(frame_duration*1e9);
|
||||
else sleep_nseconds = (sleep_period )*1e9; // 0.01 seconds
|
||||
|
||||
tzset();
|
||||
current_seconds = initial_seconds = getcurrentseconds();
|
||||
|
||||
// *** CYCLIC EXECUTION LOOP, AND MESSAGE READING *** //
|
||||
while (result && FDMExec->GetSimTime() <= end_time) {
|
||||
|
||||
FDMExec->ProcessMessage(); // Process messages, if any.
|
||||
|
||||
// Check if increment then hold is on and take appropriate actions if it is
|
||||
// Iterate is not supported in realtime - only in batch and playnice modes
|
||||
FDMExec->CheckIncrementalHold();
|
||||
|
||||
// if running realtime, throttle the execution, else just run flat-out fast
|
||||
// unless "playing nice", in which case sleep for a while (0.01 seconds) each frame.
|
||||
// If suspended, then don't increment cumulative realtime "stopwatch".
|
||||
|
||||
if ( ! FDMExec->Holding()) {
|
||||
if ( ! realtime ) { // ------------ RUNNING IN BATCH MODE
|
||||
|
||||
result = FDMExec->Run();
|
||||
|
||||
if (play_nice) sim_nsleep(sleep_nseconds);
|
||||
|
||||
} else { // ------------ RUNNING IN REALTIME MODE
|
||||
|
||||
// "was_paused" will be true if entering this "run" loop from a paused state.
|
||||
if (was_paused) {
|
||||
initial_seconds += paused_seconds;
|
||||
was_paused = false;
|
||||
}
|
||||
current_seconds = getcurrentseconds(); // Seconds since 1 Jan 1970
|
||||
actual_elapsed_time = current_seconds - initial_seconds; // Real world elapsed seconds since start
|
||||
sim_lag_time = actual_elapsed_time - FDMExec->GetSimTime(); // How far behind sim-time is from actual
|
||||
// elapsed time.
|
||||
for (int i=0; i<(int)(sim_lag_time/frame_duration); i++) { // catch up sim time to actual elapsed time.
|
||||
result = FDMExec->Run();
|
||||
cycle_duration = getcurrentseconds() - current_seconds; // Calculate cycle duration
|
||||
current_seconds = getcurrentseconds(); // Get new current_seconds
|
||||
if (FDMExec->Holding()) break;
|
||||
}
|
||||
|
||||
if (play_nice) sim_nsleep(sleep_nseconds);
|
||||
|
||||
if (FDMExec->GetSimTime() >= new_five_second_value) { // Print out elapsed time every five seconds.
|
||||
cout << "Simulation elapsed time: " << FDMExec->GetSimTime() << endl;
|
||||
new_five_second_value += 5.0;
|
||||
}
|
||||
}
|
||||
} else { // Suspended
|
||||
was_paused = true;
|
||||
paused_seconds = getcurrentseconds() - current_seconds;
|
||||
sim_nsleep(sleep_nseconds);
|
||||
result = FDMExec->Run();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PRINT ENDING CLOCK TIME
|
||||
time(&tod);
|
||||
strftime(s, 99, "%A %B %d %Y %X", localtime(&tod));
|
||||
cout << "End: " << s << " (HH:MM:SS)" << endl;
|
||||
|
||||
// CLEAN UP
|
||||
delete FDMExec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
#define gripe cerr << "Option '" << keyword \
|
||||
<< "' requires a value, as in '" \
|
||||
<< keyword << "=something'" << endl << endl;/**/
|
||||
|
||||
bool options(int count, char **arg)
|
||||
{
|
||||
int i;
|
||||
bool result = true;
|
||||
|
||||
if (count == 1) {
|
||||
PrintHelp();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
cout.setf(ios_base::fixed);
|
||||
|
||||
for (i=1; i<count; i++) {
|
||||
string argument = string(arg[i]);
|
||||
string keyword(argument);
|
||||
string value("");
|
||||
string::size_type n=argument.find("=");
|
||||
|
||||
if (n != string::npos && n > 0) {
|
||||
keyword = argument.substr(0, n);
|
||||
value = argument.substr(n+1);
|
||||
}
|
||||
|
||||
if (keyword == "--help") {
|
||||
PrintHelp();
|
||||
exit(0);
|
||||
} else if (keyword == "--version") {
|
||||
cout << endl << " JSBSim Version: " << FDMExec->GetVersion() << endl << endl;
|
||||
exit (0);
|
||||
} else if (keyword == "--realtime") {
|
||||
realtime = true;
|
||||
} else if (keyword == "--nice") {
|
||||
play_nice = true;
|
||||
if (n != string::npos) {
|
||||
try {
|
||||
sleep_period = atof( value.c_str() );
|
||||
} catch (...) {
|
||||
cerr << endl << " Invalid sleep period given!" << endl << endl;
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
sleep_period = 0.01;
|
||||
}
|
||||
} else if (keyword == "--suspend") {
|
||||
suspend = true;
|
||||
} else if (keyword == "--nohighlight") {
|
||||
nohighlight = true;
|
||||
} else if (keyword == "--outputlogfile") {
|
||||
if (n != string::npos) {
|
||||
LogOutputName.push_back(value);
|
||||
}
|
||||
} else if (keyword == "--logdirectivefile") {
|
||||
if (n != string::npos) {
|
||||
LogDirectiveName.push_back(SGPath::fromLocal8Bit(value.c_str()));
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
} else if (keyword == "--root") {
|
||||
if (n != string::npos) {
|
||||
RootDir = SGPath::fromLocal8Bit(value.c_str());
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
} else if (keyword == "--aircraft") {
|
||||
if (n != string::npos) {
|
||||
AircraftName = value;
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
} else if (keyword == "--script") {
|
||||
if (n != string::npos) {
|
||||
ScriptName = SGPath::fromLocal8Bit(value.c_str());
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
} else if (keyword == "--initfile") {
|
||||
if (n != string::npos) {
|
||||
ResetName = SGPath::fromLocal8Bit(value.c_str());
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} else if (keyword == "--property") {
|
||||
if (n != string::npos) {
|
||||
string propName = value.substr(0,value.find("="));
|
||||
string propValueString = value.substr(value.find("=")+1);
|
||||
double propValue = atof(propValueString.c_str());
|
||||
CommandLineProperties.push_back(propName);
|
||||
CommandLinePropertyValues.push_back(propValue);
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} else if (keyword.substr(0,5) == "--end") {
|
||||
if (n != string::npos) {
|
||||
try {
|
||||
end_time = atof( value.c_str() );
|
||||
} catch (...) {
|
||||
cerr << endl << " Invalid end time given!" << endl << endl;
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} else if (keyword == "--simulation-rate") {
|
||||
if (n != string::npos) {
|
||||
try {
|
||||
simulation_rate = atof( value.c_str() );
|
||||
override_sim_rate = true;
|
||||
} catch (...) {
|
||||
cerr << endl << " Invalid simulation rate given!" << endl << endl;
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
gripe;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} else if (keyword == "--catalog") {
|
||||
catalog = true;
|
||||
if (value.size() > 0) AircraftName=value;
|
||||
} else if (keyword.substr(0,2) != "--" && value.empty() ) {
|
||||
// See what kind of files we are specifying on the command line
|
||||
|
||||
XMLFile xmlFile;
|
||||
SGPath path = SGPath::fromLocal8Bit(keyword.c_str());
|
||||
|
||||
if (xmlFile.IsScriptFile(path)) ScriptName = path;
|
||||
else if (xmlFile.IsLogDirectiveFile(path)) LogDirectiveName.push_back(path);
|
||||
else if (xmlFile.IsAircraftFile(SGPath("aircraft")/keyword/keyword)) AircraftName = keyword;
|
||||
else if (xmlFile.IsInitFile(path)) ResetName = path;
|
||||
else if (xmlFile.IsInitFile(SGPath("aircraft")/AircraftName/keyword)) ResetName = SGPath("aircraft")/AircraftName/keyword;
|
||||
else {
|
||||
cerr << "The argument \"" << keyword << "\" cannot be interpreted as a file name or option." << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
else //Unknown keyword so print the help file, the bad keyword and abort
|
||||
{
|
||||
PrintHelp();
|
||||
cerr << "The argument \"" << keyword << "\" cannot be interpreted as a file name or option." << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Post-processing for script options. check for incompatible options.
|
||||
|
||||
if (catalog && !ScriptName.isNull()) {
|
||||
cerr << "Cannot specify catalog with script option" << endl << endl;
|
||||
result = false;
|
||||
}
|
||||
if (!AircraftName.empty() && ResetName.isNull() && !catalog) {
|
||||
cerr << "You must specify an initialization file with the aircraft name." << endl << endl;
|
||||
result = false;
|
||||
}
|
||||
if (!ScriptName.isNull() && !AircraftName.empty()) {
|
||||
cerr << "You cannot specify an aircraft file with a script." << endl;
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void PrintHelp(void)
|
||||
{
|
||||
cout << endl << " JSBSim version " << FDMExec->GetVersion() << endl << endl;
|
||||
cout << " Usage: jsbsim [script file name] [output file names] <options>" << endl << endl;
|
||||
cout << " options:" << endl;
|
||||
cout << " --help returns this message" << endl;
|
||||
cout << " --version returns the version number" << endl;
|
||||
cout << " --outputlogfile=<filename> sets (overrides) the name of a data output file" << endl;
|
||||
cout << " --logdirectivefile=<filename> specifies the name of a data logging directives file" << endl;
|
||||
cout << " (can appear multiple times)" << endl;
|
||||
cout << " --root=<path> specifies the JSBSim root directory (where aircraft/, engine/, etc. reside)" << endl;
|
||||
cout << " --aircraft=<filename> specifies the name of the aircraft to be modeled" << endl;
|
||||
cout << " --script=<filename> specifies a script to run" << endl;
|
||||
cout << " --realtime specifies to run in actual real world time" << endl;
|
||||
cout << " --nice specifies to run at lower CPU usage" << endl;
|
||||
cout << " --nohighlight specifies that console output should be pure text only (no color)" << endl;
|
||||
cout << " --suspend specifies to suspend the simulation after initialization" << endl;
|
||||
cout << " --initfile=<filename> specifies an initilization file" << endl;
|
||||
cout << " --catalog specifies that all properties for this aircraft model should be printed" << endl;
|
||||
cout << " (catalog=aircraftname is an optional format)" << endl;
|
||||
cout << " --property=<name=value> e.g. --property=simulation/integrator/rate/rotational=1" << endl;
|
||||
cout << " --simulation-rate=<rate (double)> specifies the sim dT time or frequency" << endl;
|
||||
cout << " If rate specified is less than 1, it is interpreted as" << endl;
|
||||
cout << " a time step size, otherwise it is assumed to be a rate in Hertz." << endl;
|
||||
cout << " --end=<time (double)> specifies the sim end time" << endl << endl;
|
||||
|
||||
cout << " NOTE: There can be no spaces around the = sign when" << endl;
|
||||
cout << " an option is followed by a filename" << endl << endl;
|
||||
}
|
||||
1661
src/FDM/JSBSim/JSBSim.cxx
Normal file
1661
src/FDM/JSBSim/JSBSim.cxx
Normal file
File diff suppressed because it is too large
Load Diff
323
src/FDM/JSBSim/JSBSim.hxx
Normal file
323
src/FDM/JSBSim/JSBSim.hxx
Normal file
@@ -0,0 +1,323 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: JSBSim.hxx
|
||||
Author: Curtis L. Olson
|
||||
Maintained by: Tony Peden, Curt Olson
|
||||
Date started: 02/01/1999
|
||||
|
||||
------ Copyright (C) 1999 - 2000 Curtis L. Olson (curt@flightgear.org) ------
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
02/01/1999 CLO Created
|
||||
Additional log messages stored in CVS
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef _JSBSIM_HXX
|
||||
#define _JSBSIM_HXX
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#undef MAX_ENGINES
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#define ID_JSBSIMXX "$Header JSBSim.hxx,v 1.4 2000/10/22 14:02:16 jsb Exp $"
|
||||
|
||||
#define METERS_TO_FEET 3.2808398950
|
||||
#define RADTODEG 57.2957795
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <FDM/JSBSim/FGFDMExec.h>
|
||||
#include "FDM/AIWake/AircraftMesh.hxx"
|
||||
|
||||
namespace JSBSim {
|
||||
class FGAtmosphere;
|
||||
class FGWinds;
|
||||
class FGFCS;
|
||||
class FGPropulsion;
|
||||
class FGMassBalance;
|
||||
class FGAerodynamics;
|
||||
class FGInertial;
|
||||
class FGAircraft;
|
||||
class FGPropagate;
|
||||
class FGAuxiliary;
|
||||
class FGOutput;
|
||||
class FGInitialCondition;
|
||||
class FGLocation;
|
||||
class FGAccelerations;
|
||||
class FGPropertyManager;
|
||||
class FGColumnVector3;
|
||||
}
|
||||
|
||||
// Adding it here will cause a namespace clash in FlightGear -EMH-
|
||||
// using namespace JSBSim;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** FGFS / JSBSim interface (aka "The Bus").
|
||||
This class provides for an interface between FlightGear and its data
|
||||
structures and JSBSim and its data structures. This is the class which is
|
||||
used to command JSBSim when integrated with FlightGear. See the
|
||||
documentation for main for direction on running JSBSim apart from FlightGear.
|
||||
@author Curtis L. Olson (original)
|
||||
@author Tony Peden (Maintained and refined)
|
||||
@version $Id: FlightGear.hxx,v 1.16 2014/01/28 09:42:21 ehofman Exp $
|
||||
@see main in file JSBSim.cpp (use main() wrapper for standalone usage)
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGJSBsim : public FGInterface
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FGJSBsim( double dt );
|
||||
|
||||
/// Destructor
|
||||
~FGJSBsim();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void resume() override;
|
||||
void suspend() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "jsb"; }
|
||||
|
||||
/// copy FDM state to LaRCsim structures
|
||||
bool copy_to_JSBsim();
|
||||
|
||||
/// copy FDM state from LaRCsim structures
|
||||
bool copy_from_JSBsim();
|
||||
|
||||
/// @name Position Parameter Set
|
||||
//@{
|
||||
/** Set geocentric latitude
|
||||
@param lat latitude in radians measured from the 0 meridian where
|
||||
the westerly direction is positive and east is negative */
|
||||
void set_Latitude(double lat); // geocentric
|
||||
|
||||
/** Set longitude
|
||||
@param lon longitude in radians measured from the equator where
|
||||
the northerly direction is positive and south is negative */
|
||||
void set_Longitude(double lon);
|
||||
|
||||
/** Set altitude
|
||||
Note: this triggers a recalculation of AGL altitude
|
||||
@param alt altitude in feet */
|
||||
void set_Altitude(double alt); // triggers re-calc of AGL altitude
|
||||
//@}
|
||||
|
||||
//void set_AltitudeAGL(double altagl); // and vice-versa
|
||||
|
||||
/// @name Velocity Parameter Set
|
||||
//@{
|
||||
/** Sets calibrated airspeed
|
||||
Setting this will trigger a recalc of the other velocity terms.
|
||||
@param vc Calibrated airspeed in ft/sec */
|
||||
void set_V_calibrated_kts(double vc);
|
||||
|
||||
/** Sets Mach number.
|
||||
Setting this will trigger a recalc of the other velocity terms.
|
||||
@param mach Mach number */
|
||||
void set_Mach_number(double mach);
|
||||
|
||||
/** Sets velocity in N-E-D coordinates.
|
||||
Setting this will trigger a recalc of the other velocity terms.
|
||||
@param north velocity northward in ft/sec
|
||||
@param east velocity eastward in ft/sec
|
||||
@param down velocity downward in ft/sec */
|
||||
void set_Velocities_Local( double north, double east, double down );
|
||||
|
||||
/** Sets aircraft velocity in stability frame.
|
||||
Setting this will trigger a recalc of the other velocity terms.
|
||||
@param u X velocity in ft/sec
|
||||
@param v Y velocity in ft/sec
|
||||
@param w Z velocity in ft/sec */
|
||||
void set_Velocities_Body( double u, double v, double w);
|
||||
//@}
|
||||
|
||||
/** Euler Angle Parameter Set
|
||||
@param phi roll angle in radians
|
||||
@param theta pitch angle in radians
|
||||
@param psi heading angle in radians */
|
||||
void set_Euler_Angles( double phi, double theta, double psi );
|
||||
|
||||
/// @name Flight Path Parameter Set
|
||||
//@{
|
||||
/** Sets rate of climb
|
||||
@param roc Rate of climb in ft/sec */
|
||||
void set_Climb_Rate( double roc);
|
||||
|
||||
/** Sets the flight path angle in radians
|
||||
@param gamma flight path angle in radians. */
|
||||
void set_Gamma_vert_rad( double gamma);
|
||||
//@}
|
||||
|
||||
|
||||
/// @name Atmospheric Parameter Set
|
||||
//@{
|
||||
/** Sets the atmospheric static pressure
|
||||
@param p pressure in psf */
|
||||
// void set_Static_pressure(double p);
|
||||
|
||||
/** Sets the atmospheric temperature
|
||||
@param T temperature in degrees rankine */
|
||||
// void set_Static_temperature(double T);
|
||||
|
||||
/** Sets the atmospheric density.
|
||||
@param rho air density slugs/cubic foot */
|
||||
// void set_Density(double rho);
|
||||
|
||||
/** Sets the velocity of the local airmass for wind modeling.
|
||||
@param wnorth velocity north in fps
|
||||
@param weast velocity east in fps
|
||||
@param wdown velocity down in fps*/
|
||||
/// @name Position Parameter Update
|
||||
//@{
|
||||
|
||||
bool ToggleDataLogging(bool state);
|
||||
bool ToggleDataLogging(void);
|
||||
|
||||
double get_agl_ft(double t, const JSBSim::FGColumnVector3& loc,
|
||||
double alt_off, double contact[3], double normal[3],
|
||||
double vel[3], double angularVel[3]);
|
||||
|
||||
private:
|
||||
JSBSim::FGFDMExec *fdmex;
|
||||
JSBSim::FGInitialCondition *fgic;
|
||||
bool needTrim;
|
||||
|
||||
JSBSim::FGAtmosphere* Atmosphere;
|
||||
JSBSim::FGWinds* Winds;
|
||||
JSBSim::FGFCS* FCS;
|
||||
JSBSim::FGPropulsion* Propulsion;
|
||||
JSBSim::FGMassBalance* MassBalance;
|
||||
JSBSim::FGAircraft* Aircraft;
|
||||
JSBSim::FGPropagate* Propagate;
|
||||
JSBSim::FGAuxiliary* Auxiliary;
|
||||
JSBSim::FGAerodynamics* Aerodynamics;
|
||||
JSBSim::FGGroundReactions* GroundReactions;
|
||||
JSBSim::FGInertial* Inertial;
|
||||
JSBSim::FGAccelerations* Accelerations;
|
||||
JSBSim::FGPropertyManager* PropertyManager;
|
||||
|
||||
// disabling unused members
|
||||
/*
|
||||
int runcount;
|
||||
double trim_elev;
|
||||
double trim_throttle;
|
||||
*/
|
||||
|
||||
SGPropertyNode_ptr startup_trim;
|
||||
SGPropertyNode_ptr trimmed;
|
||||
SGPropertyNode_ptr pitch_trim;
|
||||
SGPropertyNode_ptr throttle_trim;
|
||||
SGPropertyNode_ptr aileron_trim;
|
||||
SGPropertyNode_ptr rudder_trim;
|
||||
SGPropertyNode_ptr stall_warning;
|
||||
|
||||
/* SGPropertyNode_ptr elevator_pos_deg;
|
||||
SGPropertyNode_ptr left_aileron_pos_deg;
|
||||
SGPropertyNode_ptr right_aileron_pos_deg;
|
||||
SGPropertyNode_ptr rudder_pos_deg;
|
||||
SGPropertyNode_ptr flap_pos_deg; */
|
||||
|
||||
|
||||
SGPropertyNode_ptr elevator_pos_pct;
|
||||
SGPropertyNode_ptr left_aileron_pos_pct;
|
||||
SGPropertyNode_ptr right_aileron_pos_pct;
|
||||
SGPropertyNode_ptr rudder_pos_pct;
|
||||
SGPropertyNode_ptr flap_pos_pct;
|
||||
SGPropertyNode_ptr speedbrake_pos_pct;
|
||||
SGPropertyNode_ptr spoilers_pos_pct;
|
||||
|
||||
SGPropertyNode_ptr override_fg_brake_prop;
|
||||
SGPropertyNode_ptr ab_brake_engaged;
|
||||
SGPropertyNode_ptr ab_brake_left_pct;
|
||||
SGPropertyNode_ptr ab_brake_right_pct;
|
||||
|
||||
SGPropertyNode_ptr gear_pos_pct;
|
||||
SGPropertyNode_ptr wing_fold_pos_pct;
|
||||
SGPropertyNode_ptr tailhook_pos_pct;
|
||||
|
||||
SGConstPropertyNode_ptr altitude;
|
||||
SGPropertyNode_ptr precipitation;
|
||||
SGPropertyNode_ptr temperature;
|
||||
SGPropertyNode_ptr pressure;
|
||||
SGPropertyNode_ptr pressureSL;
|
||||
SGPropertyNode_ptr dew_point;
|
||||
SGPropertyNode_ptr ground_wind;
|
||||
SGPropertyNode_ptr turbulence_gain;
|
||||
SGPropertyNode_ptr turbulence_rate;
|
||||
SGPropertyNode_ptr turbulence_model;
|
||||
|
||||
SGPropertyNode_ptr wind_from_north;
|
||||
SGPropertyNode_ptr wind_from_east;
|
||||
SGPropertyNode_ptr wind_from_down;
|
||||
|
||||
SGPropertyNode_ptr arrestor_wire_engaged_hook;
|
||||
SGPropertyNode_ptr release_hook;
|
||||
|
||||
SGPropertyNode_ptr slaved;
|
||||
|
||||
SGPropertyNode_ptr terrain;
|
||||
|
||||
static std::map<std::string,int> TURBULENCE_TYPE_NAMES;
|
||||
|
||||
double last_hook_tip[3];
|
||||
double last_hook_root[3];
|
||||
JSBSim::FGColumnVector3 hook_root_struct;
|
||||
double hook_length;
|
||||
|
||||
bool crashed;
|
||||
|
||||
AircraftMesh_ptr mesh;
|
||||
SGPropertyNode_ptr _ai_wake_enabled;
|
||||
SGPropertyNode_ptr _fmag{nullptr}, _mmag{nullptr};
|
||||
SGPropertyNode_ptr _fbx{nullptr}, _fby{nullptr}, _fbz{nullptr};
|
||||
SGPropertyNode_ptr _mbx{nullptr}, _mby{nullptr}, _mbz{nullptr};
|
||||
|
||||
void do_trim(void);
|
||||
|
||||
bool update_ground_cache(const JSBSim::FGLocation& cart, double dt);
|
||||
void init_gear(void);
|
||||
void update_gear(void);
|
||||
|
||||
void update_external_forces(double t_off);
|
||||
};
|
||||
|
||||
#endif // _JSBSIM_HXX
|
||||
1630
src/FDM/JSBSim/initialization/FGInitialCondition.cpp
Normal file
1630
src/FDM/JSBSim/initialization/FGInitialCondition.cpp
Normal file
File diff suppressed because it is too large
Load Diff
730
src/FDM/JSBSim/initialization/FGInitialCondition.h
Normal file
730
src/FDM/JSBSim/initialization/FGInitialCondition.h
Normal file
@@ -0,0 +1,730 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGInitialCondition.h
|
||||
Author: Tony Peden
|
||||
Date started: 7/1/99
|
||||
|
||||
--------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ---------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
7/1/99 TP Created
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
The purpose of this class is to take a set of initial conditions and provide a
|
||||
kinematically consistent set of body axis velocity components, euler angles, and
|
||||
altitude. This class does not attempt to trim the model i.e. the sim will most
|
||||
likely start in a very dynamic state (unless, of course, you have chosen your
|
||||
IC's wisely) even after setting it up with this class.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGINITIALCONDITION_H
|
||||
#define FGINITIALCONDITION_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "math/FGLocation.h"
|
||||
#include "math/FGQuaternion.h"
|
||||
#include "simgear/misc/sg_path.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGFDMExec;
|
||||
class FGMatrix33;
|
||||
class FGColumnVector3;
|
||||
class FGAtmosphere;
|
||||
class FGAircraft;
|
||||
class FGPropertyManager;
|
||||
class Element;
|
||||
|
||||
typedef enum { setvt, setvc, setve, setmach, setuvw, setned, setvg } speedset;
|
||||
typedef enum { setasl, setagl } altitudeset;
|
||||
typedef enum { setgeoc, setgeod } latitudeset;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Initializes the simulation run.
|
||||
Takes a set of initial conditions (IC) and provide a kinematically
|
||||
consistent set of body axis velocity components, euler angles, and altitude.
|
||||
This class does not attempt to trim the model i.e. the sim will most likely
|
||||
start in a very dynamic state (unless, of course, you have chosen your IC's
|
||||
wisely, or started on the ground) even after setting it up with this class.
|
||||
|
||||
<h3>Usage Notes</h3>
|
||||
|
||||
With a valid object of FGFDMExec and an aircraft model loaded:
|
||||
|
||||
@code
|
||||
FGInitialCondition* fgic = FDMExec->GetIC();
|
||||
|
||||
// Reset the initial conditions and set VCAS and altitude
|
||||
fgic->InitializeIC();
|
||||
fgic->SetVcalibratedKtsIC(vcas);
|
||||
fgic->SetAltitudeAGLFtIC(altitude);
|
||||
|
||||
// directly into Run
|
||||
FDMExec->GetPropagate()->SetInitialState(fgic);
|
||||
delete fgic;
|
||||
FDMExec->Run();
|
||||
|
||||
//or to loop the sim w/o integrating
|
||||
FDMExec->RunIC();
|
||||
@endcode
|
||||
|
||||
Alternatively, you can load initial conditions from an XML file:
|
||||
|
||||
@code
|
||||
FGInitialCondition* fgic = FDMExec->GetIC();
|
||||
fgic->Load(IC_file);
|
||||
@endcode
|
||||
|
||||
<h3>Speed</h3>
|
||||
|
||||
Since vc, ve, vt, and mach all represent speed, the remaining
|
||||
three are recalculated each time one of them is set (using the
|
||||
current altitude). The most recent speed set is remembered so
|
||||
that if and when altitude is reset, the last set speed is used
|
||||
to recalculate the remaining three. Setting any of the body
|
||||
components forces a recalculation of vt and vt then becomes the
|
||||
most recent speed set.
|
||||
|
||||
<h3>Alpha,Gamma, and Theta</h3>
|
||||
|
||||
This class assumes that it will be used to set up the sim for a
|
||||
steady, zero pitch rate condition. Since any two of those angles
|
||||
specifies the third gamma (flight path angle) is favored when setting
|
||||
alpha and theta and alpha is favored when setting gamma. i.e.
|
||||
|
||||
- set alpha : recalculate theta using gamma as currently set
|
||||
- set theta : recalculate alpha using gamma as currently set
|
||||
- set gamma : recalculate theta using alpha as currently set
|
||||
|
||||
The idea being that gamma is most interesting to pilots (since it
|
||||
is indicative of climb rate).
|
||||
|
||||
Setting climb rate is, for the purpose of this discussion,
|
||||
considered equivalent to setting gamma.
|
||||
|
||||
These are the items that can be set in an initialization file:
|
||||
|
||||
- ubody (velocity, ft/sec)
|
||||
- vbody (velocity, ft/sec)
|
||||
- wbody (velocity, ft/sec)
|
||||
- vnorth (velocity, ft/sec)
|
||||
- veast (velocity, ft/sec)
|
||||
- vdown (velocity, ft/sec)
|
||||
- latitude (position, degrees)
|
||||
- longitude (position, degrees)
|
||||
- phi (orientation, degrees)
|
||||
- theta (orientation, degrees)
|
||||
- psi (orientation, degrees)
|
||||
- alpha (angle, degrees)
|
||||
- beta (angle, degrees)
|
||||
- gamma (angle, degrees)
|
||||
- roc (vertical velocity, ft/sec)
|
||||
- elevation (local terrain elevation, ft)
|
||||
- altitude (altitude AGL, ft)
|
||||
- altitudeAGL (altitude AGL, ft)
|
||||
- altitudeMSL (altitude MSL, ft)
|
||||
- winddir (wind from-angle, degrees)
|
||||
- vwind (magnitude wind speed, ft/sec)
|
||||
- hwind (headwind speed, knots)
|
||||
- xwind (crosswind speed, knots)
|
||||
- vc (calibrated airspeed, ft/sec)
|
||||
- mach (mach)
|
||||
- vground (ground speed, ft/sec)
|
||||
- trim (0 for no trim, 1 for ground trim, 'Longitudinal', 'Full', 'Ground', 'Pullup', 'Custom', 'Turn')
|
||||
- running (-1 for all engines, 0 ... n-1 for specific engines)
|
||||
|
||||
<h3>Properties</h3>
|
||||
@property ic/vc-kts (read/write) Calibrated airspeed initial condition in knots
|
||||
@property ic/ve-kts (read/write) Knots equivalent airspeed initial condition
|
||||
@property ic/vg-kts (read/write) Ground speed initial condition in knots
|
||||
@property ic/vt-kts (read/write) True airspeed initial condition in knots
|
||||
@property ic/mach (read/write) Mach initial condition
|
||||
@property ic/roc-fpm (read/write) Rate of climb initial condition in feet/minute
|
||||
@property ic/gamma-deg (read/write) Flightpath angle initial condition in degrees
|
||||
@property ic/alpha-deg (read/write) Angle of attack initial condition in degrees
|
||||
@property ic/beta-deg (read/write) Angle of sideslip initial condition in degrees
|
||||
@property ic/theta-deg (read/write) Pitch angle initial condition in degrees
|
||||
@property ic/phi-deg (read/write) Roll angle initial condition in degrees
|
||||
@property ic/psi-true-deg (read/write) Heading angle initial condition in degrees
|
||||
@property ic/lat-gc-deg (read/write) Latitude initial condition in degrees
|
||||
@property ic/long-gc-deg (read/write) Longitude initial condition in degrees
|
||||
@property ic/h-sl-ft (read/write) Height above sea level initial condition in feet
|
||||
@property ic/h-agl-ft (read/write) Height above ground level initial condition in feet
|
||||
@property ic/sea-level-radius-ft (read/write) Radius of planet at sea level in feet
|
||||
@property ic/terrain-elevation-ft (read/write) Terrain elevation above sea level in feet
|
||||
@property ic/vg-fps (read/write) Ground speed initial condition in feet/second
|
||||
@property ic/vt-fps (read/write) True airspeed initial condition in feet/second
|
||||
@property ic/vw-bx-fps (read/write) Wind velocity initial condition in Body X frame in feet/second
|
||||
@property ic/vw-by-fps (read/write) Wind velocity initial condition in Body Y frame in feet/second
|
||||
@property ic/vw-bz-fps (read/write) Wind velocity initial condition in Body Z frame in feet/second
|
||||
@property ic/vw-north-fps (read/write) Wind northward velocity initial condition in feet/second
|
||||
@property ic/vw-east-fps (read/write) Wind eastward velocity initial condition in feet/second
|
||||
@property ic/vw-down-fps (read/write) Wind downward velocity initial condition in feet/second
|
||||
@property ic/vw-mag-fps (read/write) Wind velocity magnitude initial condition in feet/sec.
|
||||
@property ic/vw-dir-deg (read/write) Wind direction initial condition, in degrees from north
|
||||
@property ic/roc-fps (read/write) Rate of climb initial condition, in feet/second
|
||||
@property ic/u-fps (read/write) Body frame x-axis velocity initial condition in feet/second
|
||||
@property ic/v-fps (read/write) Body frame y-axis velocity initial condition in feet/second
|
||||
@property ic/w-fps (read/write) Body frame z-axis velocity initial condition in feet/second
|
||||
@property ic/vn-fps (read/write) Local frame x-axis (north) velocity initial condition in feet/second
|
||||
@property ic/ve-fps (read/write) Local frame y-axis (east) velocity initial condition in feet/second
|
||||
@property ic/vd-fps (read/write) Local frame z-axis (down) velocity initial condition in feet/second
|
||||
@property ic/gamma-rad (read/write) Flight path angle initial condition in radians
|
||||
@property ic/alpha-rad (read/write) Angle of attack initial condition in radians
|
||||
@property ic/theta-rad (read/write) Pitch angle initial condition in radians
|
||||
@property ic/beta-rad (read/write) Angle of sideslip initial condition in radians
|
||||
@property ic/phi-rad (read/write) Roll angle initial condition in radians
|
||||
@property ic/psi-true-rad (read/write) Heading angle initial condition in radians
|
||||
@property ic/lat-gc-rad (read/write) Geocentric latitude initial condition in radians
|
||||
@property ic/long-gc-rad (read/write) Longitude initial condition in radians
|
||||
@property ic/p-rad_sec (read/write) Roll rate initial condition in radians/second
|
||||
@property ic/q-rad_sec (read/write) Pitch rate initial condition in radians/second
|
||||
@property ic/r-rad_sec (read/write) Yaw rate initial condition in radians/second
|
||||
|
||||
@author Tony Peden
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGInitialCondition : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
explicit FGInitialCondition(FGFDMExec *fdmex);
|
||||
/// Destructor
|
||||
~FGInitialCondition();
|
||||
|
||||
/** Set calibrated airspeed initial condition in knots.
|
||||
@param vc Calibrated airspeed in knots */
|
||||
void SetVcalibratedKtsIC(double vc);
|
||||
|
||||
/** Set equivalent airspeed initial condition in knots.
|
||||
@param ve Equivalent airspeed in knots */
|
||||
void SetVequivalentKtsIC(double ve);
|
||||
|
||||
/** Set true airspeed initial condition in knots.
|
||||
@param vtrue True airspeed in knots */
|
||||
void SetVtrueKtsIC(double vtrue) { SetVtrueFpsIC(vtrue*ktstofps); }
|
||||
|
||||
/** Set ground speed initial condition in knots.
|
||||
@param vg Ground speed in knots */
|
||||
void SetVgroundKtsIC(double vg) { SetVgroundFpsIC(vg*ktstofps); }
|
||||
|
||||
/** Set mach initial condition.
|
||||
@param mach Mach number */
|
||||
void SetMachIC(double mach);
|
||||
|
||||
/** Sets angle of attack initial condition in degrees.
|
||||
@param a Alpha in degrees */
|
||||
void SetAlphaDegIC(double a) { SetAlphaRadIC(a*degtorad); }
|
||||
|
||||
/** Sets angle of sideslip initial condition in degrees.
|
||||
@param B Beta in degrees */
|
||||
void SetBetaDegIC(double b) { SetBetaRadIC(b*degtorad);}
|
||||
|
||||
/** Sets pitch angle initial condition in degrees.
|
||||
@param theta Theta (pitch) angle in degrees */
|
||||
void SetThetaDegIC(double theta) { SetThetaRadIC(theta*degtorad); }
|
||||
|
||||
/** Resets the IC data structure to new values
|
||||
@param u, v, w, ... **/
|
||||
void ResetIC(double u0, double v0, double w0, double p0, double q0, double r0,
|
||||
double alpha0, double beta0, double phi0, double theta0, double psi0,
|
||||
double latitudeRad0, double longitudeRad0, double altitudeAGL0,
|
||||
double gamma0);
|
||||
|
||||
/** Sets the roll angle initial condition in degrees.
|
||||
@param phi roll angle in degrees */
|
||||
void SetPhiDegIC(double phi) { SetPhiRadIC(phi*degtorad);}
|
||||
|
||||
/** Sets the heading angle initial condition in degrees.
|
||||
@param psi Heading angle in degrees */
|
||||
void SetPsiDegIC(double psi){ SetPsiRadIC(psi*degtorad); }
|
||||
|
||||
/** Sets the climb rate initial condition in feet/minute.
|
||||
@param roc Rate of Climb in feet/minute */
|
||||
void SetClimbRateFpmIC(double roc) { SetClimbRateFpsIC(roc/60.0); }
|
||||
|
||||
/** Sets the flight path angle initial condition in degrees.
|
||||
@param gamma Flight path angle in degrees */
|
||||
void SetFlightPathAngleDegIC(double gamma)
|
||||
{ SetClimbRateFpsIC(vt*sin(gamma*degtorad)); }
|
||||
|
||||
/** Sets the altitude above sea level initial condition in feet.
|
||||
@param altitudeASL Altitude above sea level in feet */
|
||||
void SetAltitudeASLFtIC(double altitudeASL);
|
||||
|
||||
/** Sets the initial Altitude above ground level.
|
||||
@param agl Altitude above ground level in feet */
|
||||
void SetAltitudeAGLFtIC(double agl);
|
||||
|
||||
/** Sets the initial terrain elevation.
|
||||
@param elev Initial terrain elevation in feet */
|
||||
void SetTerrainElevationFtIC(double elev);
|
||||
|
||||
/** Sets the initial latitude.
|
||||
@param lat Initial latitude in degrees */
|
||||
void SetLatitudeDegIC(double lat) { SetLatitudeRadIC(lat*degtorad); }
|
||||
|
||||
/** Sets the initial geodetic latitude.
|
||||
This method modifies the geodetic altitude in order to keep the altitude
|
||||
above sea level unchanged.
|
||||
@param glat Initial geodetic latitude in degrees */
|
||||
void SetGeodLatitudeDegIC(double glat)
|
||||
{ SetGeodLatitudeRadIC(glat*degtorad); }
|
||||
|
||||
/** Sets the initial longitude.
|
||||
@param lon Initial longitude in degrees */
|
||||
void SetLongitudeDegIC(double lon) { SetLongitudeRadIC(lon*degtorad); }
|
||||
|
||||
/** Gets the initial calibrated airspeed.
|
||||
@return Initial calibrated airspeed in knots */
|
||||
double GetVcalibratedKtsIC(void) const;
|
||||
|
||||
/** Gets the initial equivalent airspeed.
|
||||
@return Initial equivalent airspeed in knots */
|
||||
double GetVequivalentKtsIC(void) const;
|
||||
|
||||
/** Gets the initial ground speed.
|
||||
@return Initial ground speed in knots */
|
||||
double GetVgroundKtsIC(void) const { return GetVgroundFpsIC() * fpstokts; }
|
||||
|
||||
/** Gets the initial true velocity.
|
||||
@return Initial true airspeed in knots. */
|
||||
double GetVtrueKtsIC(void) const { return vt*fpstokts; }
|
||||
|
||||
/** Gets the initial mach.
|
||||
@return Initial mach number */
|
||||
double GetMachIC(void) const;
|
||||
|
||||
/** Gets the initial climb rate.
|
||||
@return Initial climb rate in feet/minute */
|
||||
double GetClimbRateFpmIC(void) const
|
||||
{ return GetClimbRateFpsIC()*60; }
|
||||
|
||||
/** Gets the initial flight path angle.
|
||||
@return Initial flight path angle in degrees */
|
||||
double GetFlightPathAngleDegIC(void) const
|
||||
{ return GetFlightPathAngleRadIC()*radtodeg; }
|
||||
|
||||
/** Gets the initial angle of attack.
|
||||
@return Initial alpha in degrees */
|
||||
double GetAlphaDegIC(void) const { return alpha*radtodeg; }
|
||||
|
||||
/** Gets the initial sideslip angle.
|
||||
@return Initial beta in degrees */
|
||||
double GetBetaDegIC(void) const { return beta*radtodeg; }
|
||||
|
||||
/** Gets the initial pitch angle.
|
||||
@return Initial pitch angle in degrees */
|
||||
double GetThetaDegIC(void) const { return orientation.GetEulerDeg(eTht); }
|
||||
|
||||
/** Gets the initial roll angle.
|
||||
@return Initial phi in degrees */
|
||||
double GetPhiDegIC(void) const { return orientation.GetEulerDeg(ePhi); }
|
||||
|
||||
/** Gets the initial heading angle.
|
||||
@return Initial psi in degrees */
|
||||
double GetPsiDegIC(void) const { return orientation.GetEulerDeg(ePsi); }
|
||||
|
||||
/** Gets the initial latitude.
|
||||
@return Initial geocentric latitude in degrees */
|
||||
double GetLatitudeDegIC(void) const { return position.GetLatitudeDeg(); }
|
||||
|
||||
/** Gets the initial geodetic latitude.
|
||||
@return Initial geodetic latitude in degrees */
|
||||
double GetGeodLatitudeDegIC(void) const
|
||||
{ return position.GetGeodLatitudeDeg(); }
|
||||
|
||||
/** Gets the initial longitude.
|
||||
@return Initial longitude in degrees */
|
||||
double GetLongitudeDegIC(void) const { return position.GetLongitudeDeg(); }
|
||||
|
||||
/** Gets the initial altitude above sea level.
|
||||
@return Initial altitude in feet. */
|
||||
double GetAltitudeASLFtIC(void) const;
|
||||
|
||||
/** Gets the initial altitude above ground level.
|
||||
@return Initial altitude AGL in feet */
|
||||
double GetAltitudeAGLFtIC(void) const;
|
||||
|
||||
/** Gets the initial terrain elevation.
|
||||
@return Initial terrain elevation in feet */
|
||||
double GetTerrainElevationFtIC(void) const;
|
||||
|
||||
/** Gets the initial Earth position angle.
|
||||
@return Initial Earth position angle in radians. */
|
||||
double GetEarthPositionAngleIC(void) const { return epa; }
|
||||
|
||||
/** Sets the initial ground speed.
|
||||
@param vg Initial ground speed in feet/second */
|
||||
void SetVgroundFpsIC(double vg);
|
||||
|
||||
/** Sets the initial true airspeed.
|
||||
@param vt Initial true airspeed in feet/second */
|
||||
void SetVtrueFpsIC(double vt);
|
||||
|
||||
/** Sets the initial body axis X velocity.
|
||||
@param ubody Initial X velocity in feet/second */
|
||||
void SetUBodyFpsIC(double ubody) { SetBodyVelFpsIC(eU, ubody); }
|
||||
|
||||
/** Sets the initial body axis Y velocity.
|
||||
@param vbody Initial Y velocity in feet/second */
|
||||
void SetVBodyFpsIC(double vbody) { SetBodyVelFpsIC(eV, vbody); }
|
||||
|
||||
/** Sets the initial body axis Z velocity.
|
||||
@param wbody Initial Z velocity in feet/second */
|
||||
void SetWBodyFpsIC(double wbody) { SetBodyVelFpsIC(eW, wbody); }
|
||||
|
||||
/** Sets the initial local axis north velocity.
|
||||
@param vn Initial north velocity in feet/second */
|
||||
void SetVNorthFpsIC(double vn) { SetNEDVelFpsIC(eU, vn); }
|
||||
|
||||
/** Sets the initial local axis east velocity.
|
||||
@param ve Initial east velocity in feet/second */
|
||||
void SetVEastFpsIC(double ve) { SetNEDVelFpsIC(eV, ve); }
|
||||
|
||||
/** Sets the initial local axis down velocity.
|
||||
@param vd Initial down velocity in feet/second */
|
||||
void SetVDownFpsIC(double vd) { SetNEDVelFpsIC(eW, vd); }
|
||||
|
||||
/** Sets the initial body axis roll rate.
|
||||
@param P Initial roll rate in radians/second */
|
||||
void SetPRadpsIC(double P) { vPQR_body(eP) = P; }
|
||||
|
||||
/** Sets the initial body axis pitch rate.
|
||||
@param Q Initial pitch rate in radians/second */
|
||||
void SetQRadpsIC(double Q) { vPQR_body(eQ) = Q; }
|
||||
|
||||
/** Sets the initial body axis yaw rate.
|
||||
@param R initial yaw rate in radians/second */
|
||||
void SetRRadpsIC(double R) { vPQR_body(eR) = R; }
|
||||
|
||||
/** Sets the initial wind velocity.
|
||||
@param wN Initial wind velocity in local north direction, feet/second
|
||||
@param wE Initial wind velocity in local east direction, feet/second
|
||||
@param wD Initial wind velocity in local down direction, feet/second */
|
||||
void SetWindNEDFpsIC(double wN, double wE, double wD);
|
||||
|
||||
/** Sets the initial total wind speed.
|
||||
@param mag Initial wind velocity magnitude in knots */
|
||||
void SetWindMagKtsIC(double mag);
|
||||
|
||||
/** Sets the initial wind direction.
|
||||
@param dir Initial direction wind is coming from in degrees */
|
||||
void SetWindDirDegIC(double dir);
|
||||
|
||||
/** Sets the initial headwind velocity.
|
||||
@param head Initial headwind speed in knots */
|
||||
void SetHeadWindKtsIC(double head);
|
||||
|
||||
/** Sets the initial crosswind speed.
|
||||
@param cross Initial crosswind speed, positive from left to right */
|
||||
void SetCrossWindKtsIC(double cross);
|
||||
|
||||
/** Sets the initial wind downward speed.
|
||||
@param wD Initial downward wind speed in knots*/
|
||||
void SetWindDownKtsIC(double wD);
|
||||
|
||||
/** Sets the initial climb rate.
|
||||
@param roc Initial Rate of climb in feet/second */
|
||||
void SetClimbRateFpsIC(double roc);
|
||||
|
||||
/** Gets the initial ground velocity.
|
||||
@return Initial ground velocity in feet/second */
|
||||
double GetVgroundFpsIC(void) const { return vUVW_NED.Magnitude(eU, eV); }
|
||||
|
||||
/** Gets the initial true velocity.
|
||||
@return Initial true velocity in feet/second */
|
||||
double GetVtrueFpsIC(void) const { return vt; }
|
||||
|
||||
/** Gets the initial body axis X wind velocity.
|
||||
@return Initial body axis X wind velocity in feet/second */
|
||||
double GetWindUFpsIC(void) const { return GetBodyWindFpsIC(eU); }
|
||||
|
||||
/** Gets the initial body axis Y wind velocity.
|
||||
@return Initial body axis Y wind velocity in feet/second */
|
||||
double GetWindVFpsIC(void) const { return GetBodyWindFpsIC(eV); }
|
||||
|
||||
/** Gets the initial body axis Z wind velocity.
|
||||
@return Initial body axis Z wind velocity in feet/second */
|
||||
double GetWindWFpsIC(void) const { return GetBodyWindFpsIC(eW); }
|
||||
|
||||
/** Gets the initial wind velocity in the NED local frame
|
||||
@return Initial wind velocity in NED frame in feet/second */
|
||||
const FGColumnVector3 GetWindNEDFpsIC(void) const {
|
||||
const FGMatrix33& Tb2l = orientation.GetTInv();
|
||||
FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
|
||||
return _vt_NED - vUVW_NED;
|
||||
}
|
||||
|
||||
/** Gets the initial wind velocity in local frame.
|
||||
@return Initial wind velocity toward north in feet/second */
|
||||
double GetWindNFpsIC(void) const { return GetNEDWindFpsIC(eX); }
|
||||
|
||||
/** Gets the initial wind velocity in local frame.
|
||||
@return Initial wind velocity eastwards in feet/second */
|
||||
double GetWindEFpsIC(void) const { return GetNEDWindFpsIC(eY); }
|
||||
|
||||
/** Gets the initial wind velocity in local frame.
|
||||
@return Initial wind velocity downwards in feet/second */
|
||||
double GetWindDFpsIC(void) const { return GetNEDWindFpsIC(eZ); }
|
||||
|
||||
/** Gets the initial total wind velocity in feet/sec.
|
||||
@return Initial wind velocity in feet/second */
|
||||
double GetWindFpsIC(void) const;
|
||||
|
||||
/** Gets the initial wind direction.
|
||||
@return Initial wind direction in feet/second */
|
||||
double GetWindDirDegIC(void) const;
|
||||
|
||||
/** Gets the initial climb rate.
|
||||
@return Initial rate of climb in feet/second */
|
||||
double GetClimbRateFpsIC(void) const
|
||||
{
|
||||
const FGMatrix33& Tb2l = orientation.GetTInv();
|
||||
FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
|
||||
return -_vt_NED(eW);
|
||||
}
|
||||
|
||||
/** Gets the initial body velocity
|
||||
@return Initial body velocity in feet/second. */
|
||||
const FGColumnVector3 GetUVWFpsIC(void) const {
|
||||
const FGMatrix33& Tl2b = orientation.GetT();
|
||||
return Tl2b * vUVW_NED;
|
||||
}
|
||||
|
||||
/** Gets the initial body axis X velocity.
|
||||
@return Initial body axis X velocity in feet/second. */
|
||||
double GetUBodyFpsIC(void) const { return GetBodyVelFpsIC(eU); }
|
||||
|
||||
/** Gets the initial body axis Y velocity.
|
||||
@return Initial body axis Y velocity in feet/second. */
|
||||
double GetVBodyFpsIC(void) const { return GetBodyVelFpsIC(eV); }
|
||||
|
||||
/** Gets the initial body axis Z velocity.
|
||||
@return Initial body axis Z velocity in feet/second. */
|
||||
double GetWBodyFpsIC(void) const { return GetBodyVelFpsIC(eW); }
|
||||
|
||||
/** Gets the initial local frame X (North) velocity.
|
||||
@return Initial local frame X (North) axis velocity in feet/second. */
|
||||
double GetVNorthFpsIC(void) const { return vUVW_NED(eU); }
|
||||
|
||||
/** Gets the initial local frame Y (East) velocity.
|
||||
@return Initial local frame Y (East) axis velocity in feet/second. */
|
||||
double GetVEastFpsIC(void) const { return vUVW_NED(eV); }
|
||||
|
||||
/** Gets the initial local frame Z (Down) velocity.
|
||||
@return Initial local frame Z (Down) axis velocity in feet/second. */
|
||||
double GetVDownFpsIC(void) const { return vUVW_NED(eW); }
|
||||
|
||||
/** Gets the initial body rotation rate
|
||||
@return Initial body rotation rate in radians/second */
|
||||
const FGColumnVector3 GetPQRRadpsIC(void) const { return vPQR_body; }
|
||||
|
||||
/** Gets the initial body axis roll rate.
|
||||
@return Initial body axis roll rate in radians/second */
|
||||
double GetPRadpsIC() const { return vPQR_body(eP); }
|
||||
|
||||
/** Gets the initial body axis pitch rate.
|
||||
@return Initial body axis pitch rate in radians/second */
|
||||
double GetQRadpsIC() const { return vPQR_body(eQ); }
|
||||
|
||||
/** Gets the initial body axis yaw rate.
|
||||
@return Initial body axis yaw rate in radians/second */
|
||||
double GetRRadpsIC() const { return vPQR_body(eR); }
|
||||
|
||||
/** Sets the initial flight path angle.
|
||||
@param gamma Initial flight path angle in radians */
|
||||
void SetFlightPathAngleRadIC(double gamma)
|
||||
{ SetClimbRateFpsIC(vt*sin(gamma)); }
|
||||
|
||||
/** Sets the initial angle of attack.
|
||||
@param alpha Initial angle of attack in radians */
|
||||
void SetAlphaRadIC(double alpha);
|
||||
|
||||
/** Sets the initial sideslip angle.
|
||||
@param beta Initial angle of sideslip in radians. */
|
||||
void SetBetaRadIC(double beta);
|
||||
|
||||
/** Sets the initial roll angle.
|
||||
@param phi Initial roll angle in radians */
|
||||
void SetPhiRadIC(double phi) { SetEulerAngleRadIC(ePhi, phi); }
|
||||
|
||||
/** Sets the initial pitch angle.
|
||||
@param theta Initial pitch angle in radians */
|
||||
void SetThetaRadIC(double theta) { SetEulerAngleRadIC(eTht, theta); }
|
||||
|
||||
/** Sets the initial heading angle.
|
||||
@param psi Initial heading angle in radians */
|
||||
void SetPsiRadIC(double psi) { SetEulerAngleRadIC(ePsi, psi); }
|
||||
|
||||
/** Sets the initial latitude.
|
||||
@param lat Initial latitude in radians */
|
||||
void SetLatitudeRadIC(double lat);
|
||||
|
||||
/** Sets the initial geodetic latitude.
|
||||
This method modifies the geodetic altitude in order to keep the altitude
|
||||
above sea level unchanged.
|
||||
@param glat Initial geodetic latitude in radians */
|
||||
void SetGeodLatitudeRadIC(double glat);
|
||||
|
||||
/** Sets the initial longitude.
|
||||
@param lon Initial longitude in radians */
|
||||
void SetLongitudeRadIC(double lon);
|
||||
|
||||
/** Sets the target normal load factor.
|
||||
@param nlf Normal load factor*/
|
||||
void SetTargetNlfIC(double nlf) { targetNlfIC=nlf; }
|
||||
|
||||
/** Gets the initial flight path angle.
|
||||
If total velocity is zero, this function returns zero.
|
||||
@return Initial flight path angle in radians */
|
||||
double GetFlightPathAngleRadIC(void) const
|
||||
{ return (vt == 0.0)?0.0:asin(GetClimbRateFpsIC() / vt); }
|
||||
|
||||
/** Gets the initial angle of attack.
|
||||
@return Initial alpha in radians */
|
||||
double GetAlphaRadIC(void) const { return alpha; }
|
||||
|
||||
/** Gets the initial angle of sideslip.
|
||||
@return Initial sideslip angle in radians */
|
||||
double GetBetaRadIC(void) const { return beta; }
|
||||
|
||||
/** Gets the initial position
|
||||
@return Initial location */
|
||||
const FGLocation& GetPosition(void) const { return position; }
|
||||
|
||||
/** Gets the initial latitude.
|
||||
@return Initial latitude in radians */
|
||||
double GetLatitudeRadIC(void) const { return position.GetLatitude(); }
|
||||
|
||||
/** Gets the initial geodetic latitude.
|
||||
@return Initial geodetic latitude in radians */
|
||||
double GetGeodLatitudeRadIC(void) const
|
||||
{ return position.GetGeodLatitudeRad(); }
|
||||
|
||||
/** Gets the initial longitude.
|
||||
@return Initial longitude in radians */
|
||||
double GetLongitudeRadIC(void) const { return position.GetLongitude(); }
|
||||
|
||||
/** Gets the initial orientation
|
||||
@return Initial orientation */
|
||||
const FGQuaternion& GetOrientation(void) const { return orientation; }
|
||||
|
||||
/** Gets the initial roll angle.
|
||||
@return Initial roll angle in radians */
|
||||
double GetPhiRadIC(void) const { return orientation.GetEuler(ePhi); }
|
||||
|
||||
/** Gets the initial pitch angle.
|
||||
@return Initial pitch angle in radians */
|
||||
double GetThetaRadIC(void) const { return orientation.GetEuler(eTht); }
|
||||
|
||||
/** Gets the initial heading angle.
|
||||
@return Initial heading angle in radians */
|
||||
double GetPsiRadIC(void) const { return orientation.GetEuler(ePsi); }
|
||||
|
||||
/** Gets the initial speedset.
|
||||
@return Initial speedset */
|
||||
speedset GetSpeedSet(void) const { return lastSpeedSet; }
|
||||
|
||||
/** Gets the target normal load factor set from IC.
|
||||
@return target normal load factor set from IC*/
|
||||
double GetTargetNlfIC(void) const { return targetNlfIC; }
|
||||
|
||||
/** Loads the initial conditions.
|
||||
@param rstname The name of an initial conditions file
|
||||
@param useStoredPath true if the stored path to the IC file should be used
|
||||
@return true if successful */
|
||||
bool Load(const SGPath& rstname, bool useStoredPath = true );
|
||||
|
||||
/** Is an engine running ?
|
||||
@param index of the engine to be checked
|
||||
@return true if the engine is running. */
|
||||
bool IsEngineRunning(unsigned int n) const { return (enginesRunning & (1 << n)) != 0; }
|
||||
|
||||
/** Does initialization file call for trim ?
|
||||
@return Trim type, if any requested (version 1). */
|
||||
int TrimRequested(void) const { return trimRequested; }
|
||||
|
||||
/** Initialize the initial conditions to default values */
|
||||
void InitializeIC(void);
|
||||
|
||||
void bind(FGPropertyManager* pm);
|
||||
|
||||
private:
|
||||
FGColumnVector3 vUVW_NED;
|
||||
FGColumnVector3 vPQR_body;
|
||||
FGLocation position;
|
||||
FGQuaternion orientation;
|
||||
double vt;
|
||||
|
||||
double targetNlfIC;
|
||||
|
||||
FGMatrix33 Tw2b, Tb2w;
|
||||
double alpha, beta;
|
||||
double epa;
|
||||
|
||||
speedset lastSpeedSet;
|
||||
altitudeset lastAltitudeSet;
|
||||
latitudeset lastLatitudeSet;
|
||||
unsigned int enginesRunning;
|
||||
int trimRequested;
|
||||
|
||||
FGFDMExec *fdmex;
|
||||
FGAtmosphere* Atmosphere;
|
||||
FGAircraft* Aircraft;
|
||||
|
||||
bool Load_v1(Element* document);
|
||||
bool Load_v2(Element* document);
|
||||
|
||||
void SetEulerAngleRadIC(int idx, double angle);
|
||||
void SetBodyVelFpsIC(int idx, double vel);
|
||||
void SetNEDVelFpsIC(int idx, double vel);
|
||||
double GetBodyWindFpsIC(int idx) const;
|
||||
double GetNEDWindFpsIC(int idx) const;
|
||||
double GetBodyVelFpsIC(int idx) const;
|
||||
void calcAeroAngles(const FGColumnVector3& _vt_BODY);
|
||||
void calcThetaBeta(double alfa, const FGColumnVector3& _vt_NED);
|
||||
double ComputeGeodAltitude(double geodLatitude);
|
||||
bool LoadLatitude(Element* position_el);
|
||||
void SetTrimRequest(std::string trim);
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
856
src/FDM/JSBSim/initialization/FGTrim.cpp
Normal file
856
src/FDM/JSBSim/initialization/FGTrim.cpp
Normal file
@@ -0,0 +1,856 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTrim.cpp
|
||||
Author: Tony Peden
|
||||
Date started: 9/8/99
|
||||
|
||||
--------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ---------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
9/8/99 TP Created
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This class takes the given set of IC's and finds the angle of attack, elevator,
|
||||
and throttle setting required to fly steady level. This is currently for in-air
|
||||
conditions only. It is implemented using an iterative, one-axis-at-a-time
|
||||
scheme. */
|
||||
|
||||
// !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iomanip>
|
||||
#include "FGTrim.h"
|
||||
#include "models/FGInertial.h"
|
||||
#include "models/FGAccelerations.h"
|
||||
#include "models/FGMassBalance.h"
|
||||
#include "models/FGFCS.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#pragma warning (disable : 4786 4788)
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTrim::FGTrim(FGFDMExec *FDMExec,TrimMode tt)
|
||||
: fgic(FDMExec)
|
||||
{
|
||||
|
||||
Nsub=0;
|
||||
max_iterations=60;
|
||||
max_sub_iterations=100;
|
||||
Tolerance=1E-3;
|
||||
A_Tolerance = Tolerance / 10;
|
||||
|
||||
Debug=0;DebugLevel=0;
|
||||
fdmex=FDMExec;
|
||||
fgic = *fdmex->GetIC();
|
||||
total_its=0;
|
||||
gamma_fallback=false;
|
||||
mode=tt;
|
||||
xlo=xhi=alo=ahi=0.0;
|
||||
targetNlf=fgic.GetTargetNlfIC();
|
||||
debug_axis=tAll;
|
||||
SetMode(tt);
|
||||
if (debug_lvl & 2) cout << "Instantiated: FGTrim" << endl;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTrim::~FGTrim(void) {
|
||||
if (debug_lvl & 2) cout << "Destroyed: FGTrim" << endl;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::TrimStats() {
|
||||
int run_sum=0;
|
||||
cout << endl << " Trim Statistics: " << endl;
|
||||
cout << " Total Iterations: " << total_its << endl;
|
||||
if( total_its > 0) {
|
||||
cout << " Sub-iterations:" << endl;
|
||||
for (unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
|
||||
run_sum += TrimAxes[current_axis].GetRunCount();
|
||||
cout << " " << setw(5) << TrimAxes[current_axis].GetStateName().c_str()
|
||||
<< ": " << setprecision(3) << sub_iterations[current_axis]
|
||||
<< " average: " << setprecision(5) << sub_iterations[current_axis]/double(total_its)
|
||||
<< " successful: " << setprecision(3) << successful[current_axis]
|
||||
<< " stability: " << setprecision(5) << TrimAxes[current_axis].GetAvgStability()
|
||||
<< endl;
|
||||
}
|
||||
cout << " Run Count: " << run_sum << endl;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::Report(void) {
|
||||
cout << " Trim Results: " << endl;
|
||||
for(unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++)
|
||||
TrimAxes[current_axis].AxisReport();
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::ClearStates(void) {
|
||||
mode=tCustom;
|
||||
TrimAxes.clear();
|
||||
//cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGTrim::AddState( State state, Control control ) {
|
||||
mode = tCustom;
|
||||
vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
|
||||
for (; iAxes != TrimAxes.end(); ++iAxes) {
|
||||
if (iAxes->GetStateType() == state)
|
||||
return false;
|
||||
}
|
||||
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,state,control));
|
||||
sub_iterations.resize(TrimAxes.size());
|
||||
successful.resize(TrimAxes.size());
|
||||
solution.resize(TrimAxes.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGTrim::RemoveState( State state ) {
|
||||
bool result=false;
|
||||
|
||||
mode = tCustom;
|
||||
vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
|
||||
while (iAxes != TrimAxes.end()) {
|
||||
if( iAxes->GetStateType() == state ) {
|
||||
iAxes = TrimAxes.erase(iAxes);
|
||||
result=true;
|
||||
continue;
|
||||
}
|
||||
++iAxes;
|
||||
}
|
||||
if(result) {
|
||||
sub_iterations.resize(TrimAxes.size());
|
||||
successful.resize(TrimAxes.size());
|
||||
solution.resize(TrimAxes.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGTrim::EditState( State state, Control new_control ){
|
||||
mode = tCustom;
|
||||
vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
|
||||
while (iAxes != TrimAxes.end()) {
|
||||
if( iAxes->GetStateType() == state ) {
|
||||
*iAxes = FGTrimAxis(fdmex,&fgic,state,new_control);
|
||||
return true;
|
||||
}
|
||||
++iAxes;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGTrim::DoTrim(void) {
|
||||
bool trim_failed=false;
|
||||
unsigned int N = 0;
|
||||
unsigned int axis_count = 0;
|
||||
FGFCS *FCS = fdmex->GetFCS();
|
||||
vector<double> throttle0 = FCS->GetThrottleCmd();
|
||||
double elevator0 = FCS->GetDeCmd();
|
||||
double aileron0 = FCS->GetDaCmd();
|
||||
double rudder0 = FCS->GetDrCmd();
|
||||
double PitchTrim0 = FCS->GetPitchTrimCmd();
|
||||
|
||||
for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++)
|
||||
fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false);
|
||||
|
||||
fdmex->SetTrimStatus(true);
|
||||
fdmex->SuspendIntegration();
|
||||
|
||||
fgic.SetPRadpsIC(0.0);
|
||||
fgic.SetQRadpsIC(0.0);
|
||||
fgic.SetRRadpsIC(0.0);
|
||||
|
||||
if (mode == tGround) {
|
||||
fdmex->Initialize(&fgic);
|
||||
fdmex->Run();
|
||||
trimOnGround();
|
||||
double theta = fgic.GetThetaRadIC();
|
||||
double phi = fgic.GetPhiRadIC();
|
||||
// Take opportunity of the first approx. found by trimOnGround() to
|
||||
// refine the control limits.
|
||||
TrimAxes[0].SetControlLimits(0., fgic.GetAltitudeAGLFtIC());
|
||||
TrimAxes[1].SetControlLimits(theta - 5.0 * degtorad, theta + 5.0 * degtorad);
|
||||
TrimAxes[2].SetControlLimits(phi - 30.0 * degtorad, phi + 30.0 * degtorad);
|
||||
}
|
||||
|
||||
//clear the sub iterations counts & zero out the controls
|
||||
for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
|
||||
//cout << current_axis << " " << TrimAxes[current_axis]->GetStateName()
|
||||
//<< " " << TrimAxes[current_axis]->GetControlName()<< endl;
|
||||
xlo=TrimAxes[current_axis].GetControlMin();
|
||||
xhi=TrimAxes[current_axis].GetControlMax();
|
||||
TrimAxes[current_axis].SetControl((xlo+xhi)/2);
|
||||
TrimAxes[current_axis].Run();
|
||||
//TrimAxes[current_axis].AxisReport();
|
||||
sub_iterations[current_axis]=0;
|
||||
successful[current_axis]=0;
|
||||
solution[current_axis]=false;
|
||||
}
|
||||
|
||||
if(mode == tPullup ) {
|
||||
cout << "Setting pitch rate and nlf... " << endl;
|
||||
setupPullup();
|
||||
cout << "pitch rate done ... " << endl;
|
||||
TrimAxes[0].SetStateTarget(targetNlf);
|
||||
cout << "nlf done" << endl;
|
||||
} else if (mode == tTurn) {
|
||||
setupTurn();
|
||||
//TrimAxes[0].SetStateTarget(targetNlf);
|
||||
}
|
||||
|
||||
do {
|
||||
axis_count=0;
|
||||
for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
|
||||
setDebug(TrimAxes[current_axis]);
|
||||
updateRates();
|
||||
Nsub=0;
|
||||
if(!solution[current_axis]) {
|
||||
if(checkLimits(TrimAxes[current_axis])) {
|
||||
solution[current_axis]=true;
|
||||
solve(TrimAxes[current_axis]);
|
||||
}
|
||||
} else if(findInterval(TrimAxes[current_axis])) {
|
||||
solve(TrimAxes[current_axis]);
|
||||
} else {
|
||||
solution[current_axis]=false;
|
||||
}
|
||||
sub_iterations[current_axis]+=Nsub;
|
||||
}
|
||||
for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
|
||||
//these checks need to be done after all the axes have run
|
||||
if(Debug > 0) TrimAxes[current_axis].AxisReport();
|
||||
if(TrimAxes[current_axis].InTolerance()) {
|
||||
axis_count++;
|
||||
successful[current_axis]++;
|
||||
}
|
||||
}
|
||||
|
||||
if((axis_count == TrimAxes.size()-1) && (TrimAxes.size() > 1)) {
|
||||
//cout << TrimAxes.size()-1 << " out of " << TrimAxes.size() << "!" << endl;
|
||||
//At this point we can check the input limits of the failed axis
|
||||
//and declare the trim failed if there is no sign change. If there
|
||||
//is, keep going until success or max iteration count
|
||||
|
||||
//Oh, well: two out of three ain't bad
|
||||
for(unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
|
||||
//these checks need to be done after all the axes have run
|
||||
if(!TrimAxes[current_axis].InTolerance()) {
|
||||
if(!checkLimits(TrimAxes[current_axis])) {
|
||||
// special case this for now -- if other cases arise proper
|
||||
// support can be added to FGTrimAxis
|
||||
if( (gamma_fallback) &&
|
||||
(TrimAxes[current_axis].GetStateType() == tUdot) &&
|
||||
(TrimAxes[current_axis].GetControlType() == tThrottle)) {
|
||||
cout << " Can't trim udot with throttle, trying flight"
|
||||
<< " path angle. (" << N << ")" << endl;
|
||||
if(TrimAxes[current_axis].GetState() > 0)
|
||||
TrimAxes[current_axis].SetControlToMin();
|
||||
else
|
||||
TrimAxes[current_axis].SetControlToMax();
|
||||
TrimAxes[current_axis].Run();
|
||||
TrimAxes[current_axis]=FGTrimAxis(fdmex,&fgic,tUdot,tGamma);
|
||||
} else {
|
||||
cout << " Sorry, " << TrimAxes[current_axis].GetStateName()
|
||||
<< " doesn't appear to be trimmable" << endl;
|
||||
//total_its=k;
|
||||
trim_failed=true; //force the trim to fail
|
||||
} //gamma_fallback
|
||||
}
|
||||
} //solution check
|
||||
} //for loop
|
||||
} //all-but-one check
|
||||
N++;
|
||||
if(N > max_iterations)
|
||||
trim_failed=true;
|
||||
} while((axis_count < TrimAxes.size()) && (!trim_failed));
|
||||
|
||||
if((!trim_failed) && (axis_count >= TrimAxes.size())) {
|
||||
total_its=N;
|
||||
if (debug_lvl > 0)
|
||||
cout << endl << " Trim successful" << endl;
|
||||
} else { // The trim has failed
|
||||
total_its=N;
|
||||
|
||||
// Restore the aircraft parameters to their initial values
|
||||
fgic = *fdmex->GetIC();
|
||||
FCS->SetDeCmd(elevator0);
|
||||
FCS->SetDaCmd(aileron0);
|
||||
FCS->SetDrCmd(rudder0);
|
||||
FCS->SetPitchTrimCmd(PitchTrim0);
|
||||
for (unsigned int i=0; i < throttle0.size(); i++)
|
||||
FCS->SetThrottleCmd(i, throttle0[i]);
|
||||
|
||||
fdmex->Initialize(&fgic);
|
||||
fdmex->Run();
|
||||
|
||||
// If WOW is true we must make sure there are no gears into the ground.
|
||||
if (fdmex->GetGroundReactions()->GetWOW())
|
||||
trimOnGround();
|
||||
|
||||
if (debug_lvl > 0)
|
||||
cout << endl << " Trim failed" << endl;
|
||||
}
|
||||
|
||||
fdmex->GetPropagate()->InitializeDerivatives();
|
||||
fdmex->ResumeIntegration();
|
||||
fdmex->SetTrimStatus(false);
|
||||
|
||||
for(int i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++)
|
||||
fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true);
|
||||
|
||||
return !trim_failed;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Trim the aircraft on the ground. The algorithm is looking for a stable
|
||||
// position of the aicraft. Assuming the aircaft is a rigid body and the ground
|
||||
// a plane: we need to find the translations and rotations of the aircraft that
|
||||
// will move 3 non-colinear points in contact with the ground.
|
||||
// The algorithm proceeds in three stages (one for each point):
|
||||
// 1. Look for the contact point closer to or deeper into the ground. Move the
|
||||
// aircraft along the vertical direction so that only this contact point
|
||||
// remains in contact with the ground.
|
||||
// 2. The forces applied on the aircraft (most likely the gravity) will generate
|
||||
// a moment on the aircraft around the point in contact. The rotation axis is
|
||||
// therefore the moment axis. The 2nd stage thus consists in determining the
|
||||
// minimum rotation angle around the first point in contact that will place a
|
||||
// second contact point on the ground.
|
||||
// 3. At this stage, 2 points are in contact with the ground: the rotation axis
|
||||
// is therefore the vector generated by the 2 points. Like stage #2, the
|
||||
// rotation direction will be driven by the moment around the axis formed by
|
||||
// the 2 points in contact. The rotation angle is obtained similarly to stage
|
||||
// #2: it is the minimum angle that will place a third contact point on the
|
||||
// ground.
|
||||
// The calculations below do not account for the compression of the landing
|
||||
// gears meaning that the position found is close to the real position but not
|
||||
// strictly equal to it.
|
||||
|
||||
void FGTrim::trimOnGround(void)
|
||||
{
|
||||
FGGroundReactions* GroundReactions = fdmex->GetGroundReactions();
|
||||
FGPropagate* Propagate = fdmex->GetPropagate();
|
||||
FGMassBalance* MassBalance = fdmex->GetMassBalance();
|
||||
FGAccelerations* Accelerations = fdmex->GetAccelerations();
|
||||
vector<ContactPoints> contacts;
|
||||
FGLocation CGLocation = Propagate->GetLocation();
|
||||
FGMatrix33 Tec2b = Propagate->GetTec2b();
|
||||
FGMatrix33 Tb2l = Propagate->GetTb2l();
|
||||
double hmin = 1E+10;
|
||||
int contactRef = -1;
|
||||
|
||||
// Build the list of the aircraft contact points and take opportunity of the
|
||||
// loop to find which one is closer to (or deeper into) the ground.
|
||||
for (int i = 0; i < GroundReactions->GetNumGearUnits(); ++i) {
|
||||
ContactPoints c;
|
||||
FGLGear* gear = GroundReactions->GetGearUnit(i);
|
||||
|
||||
// Skip the retracted landing gears
|
||||
if (!gear->GetGearUnitDown())
|
||||
continue;
|
||||
|
||||
c.location = gear->GetBodyLocation();
|
||||
FGLocation gearLoc = CGLocation.LocalToLocation(Tb2l * c.location);
|
||||
|
||||
FGColumnVector3 normal, vDummy;
|
||||
FGLocation lDummy;
|
||||
double height = fdmex->GetInertial()->GetContactPoint(gearLoc, lDummy,
|
||||
normal, vDummy,
|
||||
vDummy);
|
||||
|
||||
if (gear->IsBogey() && !GroundReactions->GetSolid())
|
||||
continue;
|
||||
|
||||
c.normal = Tec2b * normal;
|
||||
contacts.push_back(c);
|
||||
|
||||
if (height < hmin) {
|
||||
hmin = height;
|
||||
contactRef = contacts.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (contacts.size() < 3)
|
||||
return;
|
||||
|
||||
// Remove the contact point that is closest to the ground from the list:
|
||||
// the rotation axis will be going thru this point so we need to remove it
|
||||
// to avoid divisions by zero that could result from the computation of
|
||||
// the rotations.
|
||||
FGColumnVector3 contact0 = contacts[contactRef].location;
|
||||
contacts.erase(contacts.begin() + contactRef);
|
||||
|
||||
// Update the initial conditions: this should remove the forces generated
|
||||
// by overcompressed landing gears
|
||||
fgic.SetAltitudeASLFtIC(fgic.GetAltitudeASLFtIC() - hmin);
|
||||
fdmex->Initialize(&fgic);
|
||||
fdmex->Run();
|
||||
|
||||
// Compute the rotation axis: it is obtained from the direction of the
|
||||
// moment measured at the contact point 'contact0'
|
||||
FGColumnVector3 force = MassBalance->GetMass() * Accelerations->GetUVWdot();
|
||||
FGColumnVector3 moment = MassBalance->GetJ() * Accelerations->GetPQRdot()
|
||||
+ force * contact0;
|
||||
FGColumnVector3 rotationAxis = moment.Normalize();
|
||||
|
||||
// Compute the rotation parameters: angle and the first point to come into
|
||||
// contact with the ground when the rotation is applied.
|
||||
RotationParameters rParam = calcRotation(contacts, rotationAxis, contact0);
|
||||
FGQuaternion q0(rParam.angleMin, rotationAxis);
|
||||
|
||||
// Apply the computed rotation to all the contact points
|
||||
FGMatrix33 rot = q0.GetTInv();
|
||||
vector<ContactPoints>::iterator iter;
|
||||
for (iter = contacts.begin(); iter != contacts.end(); ++iter)
|
||||
iter->location = contact0 + rot * (iter->location - contact0);
|
||||
|
||||
// Remove the second point to come in contact with the ground from the list.
|
||||
// The reason is the same than above: avoid divisions by zero when the next
|
||||
// rotation will be computed.
|
||||
FGColumnVector3 contact1 = rParam.contactRef->location;
|
||||
contacts.erase(rParam.contactRef);
|
||||
|
||||
// Compute the rotation axis: now there are 2 points in contact with the
|
||||
// ground so the only option for the aircraft is to rotate around the axis
|
||||
// generated by these 2 points.
|
||||
rotationAxis = contact1 - contact0;
|
||||
// Make sure that the rotation orientation is consistent with the moment.
|
||||
if (DotProduct(rotationAxis, moment) < 0.0)
|
||||
rotationAxis = contact0 - contact1;
|
||||
|
||||
rotationAxis.Normalize();
|
||||
|
||||
// Compute the rotation parameters
|
||||
rParam = calcRotation(contacts, rotationAxis, contact0);
|
||||
FGQuaternion q1(rParam.angleMin, rotationAxis);
|
||||
|
||||
// Update the aircraft orientation
|
||||
FGColumnVector3 euler = (fgic.GetOrientation() * q0 * q1).GetEuler();
|
||||
|
||||
fgic.SetPhiRadIC(euler(1));
|
||||
fgic.SetThetaRadIC(euler(2));
|
||||
fgic.SetPsiRadIC(euler(3));
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Given a set of points and a rotation axis, this routine computes for each
|
||||
// point the rotation angle that would drive the point in contact with the
|
||||
// plane. It returns the minimum angle as well as the point with which this
|
||||
// angle has been obtained.
|
||||
// The rotation axis is defined by a vector 'u' and a point 'M0' on the axis.
|
||||
// Since we are in the body frame, the position if 'M0' is measured from the CG
|
||||
// hence the name 'GM0'.
|
||||
|
||||
FGTrim::RotationParameters FGTrim::calcRotation(vector<ContactPoints>& contacts,
|
||||
const FGColumnVector3& u,
|
||||
const FGColumnVector3& GM0)
|
||||
{
|
||||
RotationParameters rParam;
|
||||
vector<ContactPoints>::iterator iter;
|
||||
|
||||
rParam.angleMin = 3.0 * M_PI;
|
||||
|
||||
for (iter = contacts.begin(); iter != contacts.end(); ++iter) {
|
||||
// Below the processed contact point is named 'M'
|
||||
// Construct an orthonormal basis (u, v, t). The ground normal is obtained
|
||||
// from iter->normal.
|
||||
FGColumnVector3 t = u * iter->normal;
|
||||
double length = t.Magnitude();
|
||||
t /= length; // Normalize the tangent
|
||||
FGColumnVector3 v = t * u;
|
||||
FGColumnVector3 MM0 = GM0 - iter->location;
|
||||
// d0 is the distance from the circle center 'C' to the reference point 'M0'
|
||||
double d0 = DotProduct(MM0, u);
|
||||
// Compute the square of the circle radius i.e. the square of the distance
|
||||
// between 'C' and 'M'.
|
||||
double sqrRadius = DotProduct(MM0, MM0) - d0 * d0;
|
||||
// Compute the distance from the circle center 'C' to the line made by the
|
||||
// intersection between the ground and the plane that contains the circle.
|
||||
double DistPlane = d0 * DotProduct(u, iter->normal) / length;
|
||||
// The coordinate of the point of intersection 'P' between the circle and
|
||||
// the ground is (0, DistPlane, alpha) in the basis (u, v, t)
|
||||
double mag = sqrRadius - DistPlane * DistPlane;
|
||||
if (mag < 0) {
|
||||
cout << "FGTrim::calcRotation DistPlane^2 larger than sqrRadius" << endl;
|
||||
}
|
||||
double alpha = sqrt(max(mag, 0.0));
|
||||
FGColumnVector3 CP = alpha * t + DistPlane * v;
|
||||
// The transformation is now constructed: we can extract the angle using the
|
||||
// classical formulas (cosine is obtained from the dot product and sine from
|
||||
// the cross product).
|
||||
double cosine = -DotProduct(MM0, CP) / sqrRadius;
|
||||
double sine = DotProduct(MM0 * u, CP) / sqrRadius;
|
||||
double angle = atan2(sine, cosine);
|
||||
if (angle < 0.0) angle += 2.0 * M_PI;
|
||||
if (angle < rParam.angleMin) {
|
||||
rParam.angleMin = angle;
|
||||
rParam.contactRef = iter;
|
||||
}
|
||||
}
|
||||
|
||||
return rParam;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGTrim::solve(FGTrimAxis& axis) {
|
||||
|
||||
double x1,x2,x3,f1,f2,f3,d,d0;
|
||||
const double relax =0.9;
|
||||
double eps=axis.GetSolverEps();
|
||||
|
||||
x1=x2=x3=0;
|
||||
d=1;
|
||||
bool success=false;
|
||||
//initializations
|
||||
if( solutionDomain != 0) {
|
||||
/* if(ahi > alo) { */
|
||||
x1=xlo;f1=alo;
|
||||
x3=xhi;f3=ahi;
|
||||
/* } else {
|
||||
x1=xhi;f1=ahi;
|
||||
x3=xlo;f3=alo;
|
||||
} */
|
||||
d0=fabs(x3-x1);
|
||||
//iterations
|
||||
//max_sub_iterations=axis.GetIterationLimit();
|
||||
while ( (axis.InTolerance() == false )
|
||||
&& (fabs(d) > eps) && (Nsub < max_sub_iterations)) {
|
||||
Nsub++;
|
||||
d=(x3-x1)/d0;
|
||||
x2=x1-d*d0*f1/(f3-f1);
|
||||
axis.SetControl(x2);
|
||||
axis.Run();
|
||||
f2=axis.GetState();
|
||||
if(Debug > 1) {
|
||||
cout << "FGTrim::solve Nsub,x1,x2,x3: " << Nsub << ", " << x1
|
||||
<< ", " << x2 << ", " << x3 << endl;
|
||||
cout << " " << f1 << ", " << f2 << ", " << f3 << endl;
|
||||
}
|
||||
if(f1*f2 <= 0.0) {
|
||||
x3=x2;
|
||||
f3=f2;
|
||||
f1=relax*f1;
|
||||
//cout << "Solution is between x1 and x2" << endl;
|
||||
}
|
||||
else if(f2*f3 <= 0.0) {
|
||||
x1=x2;
|
||||
f1=f2;
|
||||
f3=relax*f3;
|
||||
//cout << "Solution is between x2 and x3" << endl;
|
||||
|
||||
}
|
||||
//cout << i << endl;
|
||||
|
||||
|
||||
}//end while
|
||||
if(Nsub < max_sub_iterations) success=true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
/*
|
||||
produces an interval (xlo..xhi) on one side or the other of the current
|
||||
control value in which a solution exists. This domain is, hopefully,
|
||||
smaller than xmin..0 or 0..xmax and the solver will require fewer iterations
|
||||
to find the solution. This is, hopefully, more efficient than having the
|
||||
solver start from scratch every time. Maybe it isn't though...
|
||||
This tries to take advantage of the idea that the changes from iteration to
|
||||
iteration will be small after the first one or two top-level iterations.
|
||||
|
||||
assumes that changing the control will a produce significant change in the
|
||||
accel i.e. checkLimits() has already been called.
|
||||
|
||||
if a solution is found above the current control, the function returns true
|
||||
and xlo is set to the current control, xhi to the interval max it found, and
|
||||
solutionDomain is set to 1.
|
||||
if the solution lies below the current control, then the function returns
|
||||
true and xlo is set to the interval min it found and xmax to the current
|
||||
control. if no solution is found, then the function returns false.
|
||||
|
||||
|
||||
in all cases, alo=accel(xlo) and ahi=accel(xhi) after the function exits.
|
||||
no assumptions about the state of the sim after this function has run
|
||||
can be made.
|
||||
*/
|
||||
bool FGTrim::findInterval(FGTrimAxis& axis) {
|
||||
bool found=false;
|
||||
double step;
|
||||
double current_control=axis.GetControl();
|
||||
double current_accel=axis.GetState();;
|
||||
double xmin=axis.GetControlMin();
|
||||
double xmax=axis.GetControlMax();
|
||||
double lastxlo,lastxhi,lastalo,lastahi;
|
||||
|
||||
step=0.025*fabs(xmax);
|
||||
xlo=xhi=current_control;
|
||||
alo=ahi=current_accel;
|
||||
lastxlo=xlo;lastxhi=xhi;
|
||||
lastalo=alo;lastahi=ahi;
|
||||
do {
|
||||
|
||||
Nsub++;
|
||||
step*=2;
|
||||
xlo-=step;
|
||||
if(xlo < xmin) xlo=xmin;
|
||||
xhi+=step;
|
||||
if(xhi > xmax) xhi=xmax;
|
||||
axis.SetControl(xlo);
|
||||
axis.Run();
|
||||
alo=axis.GetState();
|
||||
axis.SetControl(xhi);
|
||||
axis.Run();
|
||||
ahi=axis.GetState();
|
||||
if(fabs(ahi-alo) <= axis.GetTolerance()) continue;
|
||||
if(alo*ahi <=0) { //found interval with root
|
||||
found=true;
|
||||
if(alo*current_accel <= 0) { //narrow interval down a bit
|
||||
solutionDomain=-1;
|
||||
xhi=lastxlo;
|
||||
ahi=lastalo;
|
||||
//xhi=current_control;
|
||||
//ahi=current_accel;
|
||||
} else {
|
||||
solutionDomain=1;
|
||||
xlo=lastxhi;
|
||||
alo=lastahi;
|
||||
//xlo=current_control;
|
||||
//alo=current_accel;
|
||||
}
|
||||
}
|
||||
lastxlo=xlo;lastxhi=xhi;
|
||||
lastalo=alo;lastahi=ahi;
|
||||
if( !found && xlo==xmin && xhi==xmax ) continue;
|
||||
if(Debug > 1)
|
||||
cout << "FGTrim::findInterval: Nsub=" << Nsub << " Lo= " << xlo
|
||||
<< " Hi= " << xhi << " alo*ahi: " << alo*ahi << endl;
|
||||
} while(!found && (Nsub <= max_sub_iterations) );
|
||||
return found;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
//checks to see which side of the current control value the solution is on
|
||||
//and sets solutionDomain accordingly:
|
||||
// 1 if solution is between the current and max
|
||||
// -1 if solution is between the min and current
|
||||
// 0 if there is no solution
|
||||
//
|
||||
//if changing the control produces no significant change in the accel then
|
||||
//solutionDomain is set to zero and the function returns false
|
||||
//if a solution is found, then xlo and xhi are set so that they bracket
|
||||
//the solution, alo is set to accel(xlo), and ahi is set to accel(xhi)
|
||||
//if there is no change or no solution then xlo=xmin, alo=accel(xmin) and
|
||||
//xhi=xmax and ahi=accel(xmax)
|
||||
//in all cases the sim is left such that the control=xmax and accel=ahi
|
||||
|
||||
bool FGTrim::checkLimits(FGTrimAxis& axis)
|
||||
{
|
||||
bool solutionExists;
|
||||
double current_control=axis.GetControl();
|
||||
double current_accel=axis.GetState();
|
||||
xlo=axis.GetControlMin();
|
||||
xhi=axis.GetControlMax();
|
||||
|
||||
axis.SetControl(xlo);
|
||||
axis.Run();
|
||||
alo=axis.GetState();
|
||||
axis.SetControl(xhi);
|
||||
axis.Run();
|
||||
ahi=axis.GetState();
|
||||
if(Debug > 1)
|
||||
cout << "checkLimits() xlo,xhi,alo,ahi: " << xlo << ", " << xhi << ", "
|
||||
<< alo << ", " << ahi << endl;
|
||||
solutionDomain=0;
|
||||
solutionExists=false;
|
||||
if(fabs(ahi-alo) > axis.GetTolerance()) {
|
||||
if(alo*current_accel <= 0) {
|
||||
solutionExists=true;
|
||||
solutionDomain=-1;
|
||||
xhi=current_control;
|
||||
ahi=current_accel;
|
||||
} else if(current_accel*ahi < 0){
|
||||
solutionExists=true;
|
||||
solutionDomain=1;
|
||||
xlo=current_control;
|
||||
alo=current_accel;
|
||||
}
|
||||
}
|
||||
axis.SetControl(current_control);
|
||||
axis.Run();
|
||||
return solutionExists;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::setupPullup() {
|
||||
double g,q,cgamma;
|
||||
g=fdmex->GetInertial()->GetGravity().Magnitude();
|
||||
cgamma=cos(fgic.GetFlightPathAngleRadIC());
|
||||
cout << "setPitchRateInPullup(): " << g << ", " << cgamma << ", "
|
||||
<< fgic.GetVtrueFpsIC() << endl;
|
||||
q=g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
|
||||
cout << targetNlf << ", " << q << endl;
|
||||
fgic.SetQRadpsIC(q);
|
||||
cout << "setPitchRateInPullup() complete" << endl;
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::setupTurn(void){
|
||||
double g,phi;
|
||||
phi = fgic.GetPhiRadIC();
|
||||
if( fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
|
||||
targetNlf = 1 / cos(phi);
|
||||
g = fdmex->GetInertial()->GetGravity().Magnitude();
|
||||
psidot = g*tan(phi) / fgic.GetUBodyFpsIC();
|
||||
cout << targetNlf << ", " << psidot << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::updateRates(void){
|
||||
if( mode == tTurn ) {
|
||||
double phi = fgic.GetPhiRadIC();
|
||||
double g = fdmex->GetInertial()->GetGravity().Magnitude();
|
||||
double p,q,r,theta;
|
||||
if(fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
|
||||
theta=fgic.GetThetaRadIC();
|
||||
phi=fgic.GetPhiRadIC();
|
||||
psidot = g*tan(phi) / fgic.GetUBodyFpsIC();
|
||||
p=-psidot*sin(theta);
|
||||
q=psidot*cos(theta)*sin(phi);
|
||||
r=psidot*cos(theta)*cos(phi);
|
||||
} else {
|
||||
p=q=r=0;
|
||||
}
|
||||
fgic.SetPRadpsIC(p);
|
||||
fgic.SetQRadpsIC(q);
|
||||
fgic.SetRRadpsIC(r);
|
||||
} else if( mode == tPullup && fabs(targetNlf-1) > 0.01) {
|
||||
double g,q,cgamma;
|
||||
g=fdmex->GetInertial()->GetGravity().Magnitude();
|
||||
cgamma=cos(fgic.GetFlightPathAngleRadIC());
|
||||
q=g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
|
||||
fgic.SetQRadpsIC(q);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::setDebug(FGTrimAxis& axis) {
|
||||
if(debug_axis == tAll ||
|
||||
axis.GetStateType() == debug_axis ) {
|
||||
Debug=DebugLevel;
|
||||
return;
|
||||
} else {
|
||||
Debug=0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTrim::SetMode(TrimMode tt) {
|
||||
ClearStates();
|
||||
mode=tt;
|
||||
switch(tt) {
|
||||
case tFull:
|
||||
if (debug_lvl > 0)
|
||||
cout << " Full Trim" << endl;
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
|
||||
//TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
|
||||
break;
|
||||
case tLongitudinal:
|
||||
if (debug_lvl > 0)
|
||||
cout << " Longitudinal Trim" << endl;
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
|
||||
break;
|
||||
case tGround:
|
||||
if (debug_lvl > 0)
|
||||
cout << " Ground Trim" << endl;
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAltAGL ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tTheta ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tPhi ));
|
||||
break;
|
||||
case tPullup:
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tNlf,tAlpha ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
|
||||
break;
|
||||
case tTurn:
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tVdot,tBeta ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
|
||||
TrimAxes.push_back(FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
|
||||
break;
|
||||
case tCustom:
|
||||
case tNone:
|
||||
break;
|
||||
}
|
||||
//cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
|
||||
sub_iterations.resize(TrimAxes.size());
|
||||
successful.resize(TrimAxes.size());
|
||||
solution.resize(TrimAxes.size());
|
||||
}
|
||||
//YOU WERE WARNED, BUT YOU DID IT ANYWAY.
|
||||
}
|
||||
294
src/FDM/JSBSim/initialization/FGTrim.h
Normal file
294
src/FDM/JSBSim/initialization/FGTrim.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTrim.h
|
||||
Author: Tony Peden
|
||||
Date started: 7/1/99
|
||||
|
||||
------------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
9/8/99 TP Created
|
||||
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This class takes the given set of IC's and finds the aircraft state required to
|
||||
maintain a specified flight condition. This flight condition can be
|
||||
steady-level with non-zero sideslip, a steady turn, a pull-up or pushover.
|
||||
On-ground conditions can be trimmed as well, but this is currently limited to
|
||||
adjusting altitude and pitch angle only. It is implemented using an iterative,
|
||||
one-axis-at-a-time scheme.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGTRIM_H
|
||||
#define FGTRIM_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGTrimAxis.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
typedef enum { tLongitudinal=0, tFull, tGround, tPullup,
|
||||
tCustom, tTurn, tNone } TrimMode;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** The trimming routine for JSBSim.
|
||||
FGTrim finds the aircraft attitude and control settings needed to maintain
|
||||
the steady state described by the FGInitialCondition object . It does this
|
||||
iteratively by assigning a control to each state and adjusting that control
|
||||
until the state is within a specified tolerance of zero. States include the
|
||||
recti-linear accelerations udot, vdot, and wdot, the angular accelerations
|
||||
qdot, pdot, and rdot, and the difference between heading and ground track.
|
||||
Controls include the usual flight deck controls available to the pilot plus
|
||||
angle of attack (alpha), sideslip angle(beta), flight path angle (gamma),
|
||||
pitch attitude(theta), roll attitude(phi), and altitude above ground. The
|
||||
last three are used for on-ground trimming. The state-control pairs used in
|
||||
a given trim are completely user configurable and several pre-defined modes
|
||||
are provided as well. They are:
|
||||
- tLongitudinal: Trim wdot with alpha, udot with thrust, qdot with elevator
|
||||
- tFull: tLongitudinal + vdot with phi, pdot with aileron, rdot with rudder
|
||||
and heading minus ground track (hmgt) with beta
|
||||
- tPullup: tLongitudinal but adjust alpha to achieve load factor input
|
||||
with SetTargetNlf()
|
||||
- tGround: wdot with altitude, qdot with theta, and pdot with phi
|
||||
|
||||
The remaining modes include <b>tCustom</b>, which is completely user defined and
|
||||
<b>tNone</b>.
|
||||
|
||||
Note that trims can (and do) fail for reasons that are completely outside
|
||||
the control of the trimming routine itself. The most common problem is the
|
||||
initial conditions: is the model capable of steady state flight
|
||||
at those conditions? Check the speed, altitude, configuration (flaps,
|
||||
gear, etc.), weight, cg, and anything else that may be relevant.
|
||||
|
||||
Example usage:
|
||||
@code
|
||||
FGFDMExec* FDMExec = new FGFDMExec();
|
||||
|
||||
FGInitialCondition* fgic = new FGInitialCondition(FDMExec);
|
||||
FGTrim fgt(FDMExec, fgic, tFull);
|
||||
fgic->SetVcaibratedKtsIC(100);
|
||||
fgic->SetAltitudeFtIC(1000);
|
||||
fgic->SetClimbRate(500);
|
||||
if( !fgt.DoTrim() ) {
|
||||
cout << "Trim Failed" << endl;
|
||||
}
|
||||
fgt.Report();
|
||||
@endcode
|
||||
|
||||
@author Tony Peden
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGTrim : public FGJSBBase
|
||||
{
|
||||
private:
|
||||
|
||||
std::vector<FGTrimAxis> TrimAxes;
|
||||
unsigned int Nsub;
|
||||
TrimMode mode;
|
||||
int DebugLevel, Debug;
|
||||
double Tolerance, A_Tolerance;
|
||||
std::vector<double> sub_iterations, successful;
|
||||
std::vector<bool> solution;
|
||||
unsigned int max_sub_iterations;
|
||||
unsigned int max_iterations;
|
||||
unsigned int total_its;
|
||||
bool gamma_fallback;
|
||||
int solutionDomain;
|
||||
double xlo,xhi,alo,ahi;
|
||||
double targetNlf;
|
||||
int debug_axis;
|
||||
|
||||
double psidot;
|
||||
|
||||
FGFDMExec* fdmex;
|
||||
FGInitialCondition fgic;
|
||||
|
||||
bool solve(FGTrimAxis& axis);
|
||||
|
||||
/** @return false if there is no change in the current axis accel
|
||||
between accel(control_min) and accel(control_max). If there is a
|
||||
change, sets solutionDomain to:
|
||||
0 for no sign change,
|
||||
-1 if sign change between accel(control_min) and accel(0)
|
||||
1 if sign between accel(0) and accel(control_max)
|
||||
*/
|
||||
bool findInterval(FGTrimAxis& axis);
|
||||
|
||||
bool checkLimits(FGTrimAxis& axis);
|
||||
|
||||
void setupPullup(void);
|
||||
void setupTurn(void);
|
||||
|
||||
void updateRates(void);
|
||||
void setDebug(FGTrimAxis& axis);
|
||||
|
||||
struct ContactPoints {
|
||||
FGColumnVector3 location;
|
||||
FGColumnVector3 normal;
|
||||
};
|
||||
|
||||
struct RotationParameters {
|
||||
double angleMin;
|
||||
std::vector<ContactPoints>::iterator contactRef;
|
||||
};
|
||||
|
||||
void trimOnGround(void);
|
||||
RotationParameters calcRotation(std::vector<ContactPoints>& contacts,
|
||||
const FGColumnVector3& rotationAxis,
|
||||
const FGColumnVector3& contact0);
|
||||
|
||||
public:
|
||||
/** Initializes the trimming class
|
||||
@param FDMExec pointer to a JSBSim executive object.
|
||||
@param tm trim mode
|
||||
*/
|
||||
FGTrim(FGFDMExec *FDMExec, TrimMode tm=tGround );
|
||||
|
||||
~FGTrim(void);
|
||||
|
||||
/** Execute the trim
|
||||
*/
|
||||
bool DoTrim(void);
|
||||
|
||||
/** Print the results of the trim. For each axis trimmed, this
|
||||
includes the final state value, control value, and tolerance
|
||||
used.
|
||||
@return true if trim succeeds
|
||||
*/
|
||||
void Report(void);
|
||||
|
||||
/** Iteration statistics
|
||||
*/
|
||||
void TrimStats();
|
||||
|
||||
/** Clear all state-control pairs and set a predefined trim mode
|
||||
@param tm the set of axes to trim. Can be:
|
||||
tLongitudinal, tFull, tGround, tCustom, or tNone
|
||||
*/
|
||||
void SetMode(TrimMode tm);
|
||||
|
||||
/** Clear all state-control pairs from the current configuration.
|
||||
The trimming routine must have at least one state-control pair
|
||||
configured to be useful
|
||||
*/
|
||||
void ClearStates(void);
|
||||
|
||||
/** Add a state-control pair to the current configuration. See the enums
|
||||
State and Control in FGTrimAxis.h for the available options.
|
||||
Will fail if the given state is already configured.
|
||||
@param state the accel or other condition to zero
|
||||
@param control the control used to zero the state
|
||||
@return true if add is successful
|
||||
*/
|
||||
bool AddState( State state, Control control );
|
||||
|
||||
/** Remove a specific state-control pair from the current configuration
|
||||
@param state the state to remove
|
||||
@return true if removal is successful
|
||||
*/
|
||||
bool RemoveState( State state );
|
||||
|
||||
/** Change the control used to zero a state previously configured
|
||||
@param state the accel or other condition to zero
|
||||
@param new_control the control used to zero the state
|
||||
*/
|
||||
bool EditState( State state, Control new_control );
|
||||
|
||||
/** automatically switch to trimming longitudinal acceleration with
|
||||
flight path angle (gamma) once it becomes apparent that there
|
||||
is not enough/too much thrust.
|
||||
@param bb true to enable fallback
|
||||
*/
|
||||
inline void SetGammaFallback(bool bb) { gamma_fallback=bb; }
|
||||
|
||||
/** query the fallback state
|
||||
@return true if fallback is enabled.
|
||||
*/
|
||||
inline bool GetGammaFallback(void) { return gamma_fallback; }
|
||||
|
||||
/** Set the iteration limit. DoTrim() will return false if limit
|
||||
iterations are reached before trim is achieved. The default
|
||||
is 60. This does not ordinarily need to be changed.
|
||||
@param ii integer iteration limit
|
||||
*/
|
||||
inline void SetMaxCycles(int ii) { max_iterations = ii; }
|
||||
|
||||
/** Set the per-axis iteration limit. Attempt to zero each state
|
||||
by iterating limit times before moving on to the next. The
|
||||
default limit is 100 and also does not ordinarily need to
|
||||
be changed.
|
||||
@param ii integer iteration limit
|
||||
*/
|
||||
inline void SetMaxCyclesPerAxis(int ii) { max_sub_iterations = ii; }
|
||||
|
||||
/** Set the tolerance for declaring a state trimmed. Angular accels are
|
||||
held to a tolerance of 1/10th of the given. The default is
|
||||
0.001 for the recti-linear accelerations and 0.0001 for the angular.
|
||||
*/
|
||||
inline void SetTolerance(double tt) {
|
||||
Tolerance = tt;
|
||||
A_Tolerance = tt / 10;
|
||||
}
|
||||
|
||||
/**
|
||||
Debug level 1 shows results of each top-level iteration
|
||||
Debug level 2 shows level 1 & results of each per-axis iteration
|
||||
*/
|
||||
inline void SetDebug(int level) { DebugLevel = level; }
|
||||
inline void ClearDebug(void) { DebugLevel = 0; }
|
||||
|
||||
/**
|
||||
Output debug data for one of the axes
|
||||
The State enum is defined in FGTrimAxis.h
|
||||
*/
|
||||
inline void DebugState(State state) { debug_axis=state; }
|
||||
|
||||
inline void SetTargetNlf(double nlf) { targetNlf=nlf; }
|
||||
inline double GetTargetNlf(void) { return targetNlf; }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
364
src/FDM/JSBSim/initialization/FGTrimAxis.cpp
Normal file
364
src/FDM/JSBSim/initialization/FGTrimAxis.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTrimAxis.cpp
|
||||
Author: Tony Peden
|
||||
Date started: 7/3/00
|
||||
|
||||
--------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) ---------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
7/3/00 TP Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning (disable : 4786)
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include "FGFDMExec.h"
|
||||
#include "models/FGAtmosphere.h"
|
||||
#include "FGInitialCondition.h"
|
||||
#include "FGTrimAxis.h"
|
||||
#include "models/FGPropulsion.h"
|
||||
#include "models/FGAerodynamics.h"
|
||||
#include "models/FGFCS.h"
|
||||
#include "models/propulsion/FGEngine.h"
|
||||
#include "models/FGAuxiliary.h"
|
||||
#include "models/FGGroundReactions.h"
|
||||
#include "models/FGPropagate.h"
|
||||
#include "models/FGAccelerations.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
FGTrimAxis::FGTrimAxis(FGFDMExec* fdex, FGInitialCondition* ic, State st,
|
||||
Control ctrl) {
|
||||
|
||||
fdmex=fdex;
|
||||
fgic=ic;
|
||||
state=st;
|
||||
control=ctrl;
|
||||
max_iterations=10;
|
||||
control_value=0;
|
||||
its_to_stable_value=0;
|
||||
total_iterations=0;
|
||||
total_stability_iterations=0;
|
||||
state_convert=1.0;
|
||||
control_convert=1.0;
|
||||
state_value=0;
|
||||
state_target=0;
|
||||
switch(state) {
|
||||
case tUdot: tolerance = DEFAULT_TOLERANCE; break;
|
||||
case tVdot: tolerance = DEFAULT_TOLERANCE; break;
|
||||
case tWdot: tolerance = DEFAULT_TOLERANCE; break;
|
||||
case tQdot: tolerance = DEFAULT_TOLERANCE / 10; break;
|
||||
case tPdot: tolerance = DEFAULT_TOLERANCE / 10; break;
|
||||
case tRdot: tolerance = DEFAULT_TOLERANCE / 10; break;
|
||||
case tHmgt: tolerance = 0.01; break;
|
||||
case tNlf: state_target=1.0; tolerance = 1E-5; break;
|
||||
case tAll: break;
|
||||
}
|
||||
|
||||
solver_eps=tolerance;
|
||||
switch(control) {
|
||||
case tThrottle:
|
||||
control_min=0;
|
||||
control_max=1;
|
||||
control_value=0.5;
|
||||
break;
|
||||
case tBeta:
|
||||
control_min=-30*degtorad;
|
||||
control_max=30*degtorad;
|
||||
control_convert=radtodeg;
|
||||
break;
|
||||
case tAlpha:
|
||||
control_min=fdmex->GetAerodynamics()->GetAlphaCLMin();
|
||||
control_max=fdmex->GetAerodynamics()->GetAlphaCLMax();
|
||||
if(control_max <= control_min) {
|
||||
control_max=20*degtorad;
|
||||
control_min=-5*degtorad;
|
||||
}
|
||||
control_value= (control_min+control_max)/2;
|
||||
control_convert=radtodeg;
|
||||
solver_eps=tolerance/100;
|
||||
break;
|
||||
case tPitchTrim:
|
||||
case tElevator:
|
||||
case tRollTrim:
|
||||
case tAileron:
|
||||
case tYawTrim:
|
||||
case tRudder:
|
||||
control_min=-1;
|
||||
control_max=1;
|
||||
state_convert=radtodeg;
|
||||
solver_eps=tolerance/100;
|
||||
break;
|
||||
case tAltAGL:
|
||||
control_min=0;
|
||||
control_max=30;
|
||||
control_value=ic->GetAltitudeAGLFtIC();
|
||||
solver_eps=tolerance/100;
|
||||
break;
|
||||
case tTheta:
|
||||
control_min=ic->GetThetaRadIC() - 5*degtorad;
|
||||
control_max=ic->GetThetaRadIC() + 5*degtorad;
|
||||
state_convert=radtodeg;
|
||||
break;
|
||||
case tPhi:
|
||||
control_min=ic->GetPhiRadIC() - 30*degtorad;
|
||||
control_max=ic->GetPhiRadIC() + 30*degtorad;
|
||||
state_convert=radtodeg;
|
||||
control_convert=radtodeg;
|
||||
break;
|
||||
case tGamma:
|
||||
solver_eps=tolerance/100;
|
||||
control_min=-80*degtorad;
|
||||
control_max=80*degtorad;
|
||||
control_convert=radtodeg;
|
||||
break;
|
||||
case tHeading:
|
||||
control_min=ic->GetPsiRadIC() - 30*degtorad;
|
||||
control_max=ic->GetPsiRadIC() + 30*degtorad;
|
||||
state_convert=radtodeg;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
FGTrimAxis::~FGTrimAxis(void)
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void FGTrimAxis::getState(void) {
|
||||
switch(state) {
|
||||
case tUdot: state_value=fdmex->GetAccelerations()->GetUVWdot(1)-state_target; break;
|
||||
case tVdot: state_value=fdmex->GetAccelerations()->GetUVWdot(2)-state_target; break;
|
||||
case tWdot: state_value=fdmex->GetAccelerations()->GetUVWdot(3)-state_target; break;
|
||||
case tQdot: state_value=fdmex->GetAccelerations()->GetPQRdot(2)-state_target;break;
|
||||
case tPdot: state_value=fdmex->GetAccelerations()->GetPQRdot(1)-state_target; break;
|
||||
case tRdot: state_value=fdmex->GetAccelerations()->GetPQRdot(3)-state_target; break;
|
||||
case tHmgt: state_value=computeHmgt()-state_target; break;
|
||||
case tNlf: state_value=fdmex->GetAuxiliary()->GetNlf()-state_target; break;
|
||||
case tAll: break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
//States are not settable
|
||||
|
||||
void FGTrimAxis::getControl(void) {
|
||||
switch(control) {
|
||||
case tThrottle: control_value=fdmex->GetFCS()->GetThrottleCmd(0); break;
|
||||
case tBeta: control_value=fdmex->GetAuxiliary()->Getbeta(); break;
|
||||
case tAlpha: control_value=fdmex->GetAuxiliary()->Getalpha(); break;
|
||||
case tPitchTrim: control_value=fdmex->GetFCS() -> GetPitchTrimCmd(); break;
|
||||
case tElevator: control_value=fdmex->GetFCS() -> GetDeCmd(); break;
|
||||
case tRollTrim:
|
||||
case tAileron: control_value=fdmex->GetFCS() -> GetDaCmd(); break;
|
||||
case tYawTrim:
|
||||
case tRudder: control_value=fdmex->GetFCS() -> GetDrCmd(); break;
|
||||
case tAltAGL: control_value=fdmex->GetPropagate()->GetDistanceAGL();break;
|
||||
case tTheta: control_value=fdmex->GetPropagate()->GetEuler(eTht); break;
|
||||
case tPhi: control_value=fdmex->GetPropagate()->GetEuler(ePhi); break;
|
||||
case tGamma: control_value=fdmex->GetAuxiliary()->GetGamma();break;
|
||||
case tHeading: control_value=fdmex->GetPropagate()->GetEuler(ePsi); break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
double FGTrimAxis::computeHmgt(void) {
|
||||
double diff;
|
||||
|
||||
diff = fdmex->GetPropagate()->GetEuler(ePsi) -
|
||||
fdmex->GetAuxiliary()->GetGroundTrack();
|
||||
|
||||
if( diff < -M_PI ) {
|
||||
return (diff + 2*M_PI);
|
||||
} else if( diff > M_PI ) {
|
||||
return (diff - 2*M_PI);
|
||||
} else {
|
||||
return diff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
void FGTrimAxis::setControl(void) {
|
||||
switch(control) {
|
||||
case tThrottle: setThrottlesPct(); break;
|
||||
case tBeta: fgic->SetBetaRadIC(control_value); break;
|
||||
case tAlpha: fgic->SetAlphaRadIC(control_value); break;
|
||||
case tPitchTrim: fdmex->GetFCS()->SetPitchTrimCmd(control_value); break;
|
||||
case tElevator: fdmex->GetFCS()->SetDeCmd(control_value); break;
|
||||
case tRollTrim:
|
||||
case tAileron: fdmex->GetFCS()->SetDaCmd(control_value); break;
|
||||
case tYawTrim:
|
||||
case tRudder: fdmex->GetFCS()->SetDrCmd(control_value); break;
|
||||
case tAltAGL: fgic->SetAltitudeAGLFtIC(control_value); break;
|
||||
case tTheta: fgic->SetThetaRadIC(control_value); break;
|
||||
case tPhi: fgic->SetPhiRadIC(control_value); break;
|
||||
case tGamma: fgic->SetFlightPathAngleRadIC(control_value); break;
|
||||
case tHeading: fgic->SetPsiRadIC(control_value); break;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void FGTrimAxis::Run(void) {
|
||||
|
||||
double last_state_value;
|
||||
int i;
|
||||
setControl();
|
||||
//cout << "FGTrimAxis::Run: " << control_value << endl;
|
||||
i=0;
|
||||
bool stable=false;
|
||||
while(!stable) {
|
||||
i++;
|
||||
last_state_value=state_value;
|
||||
fdmex->Initialize(fgic);
|
||||
fdmex->Run();
|
||||
getState();
|
||||
if(i > 1) {
|
||||
if((fabs(last_state_value - state_value) < tolerance) || (i >= 100) )
|
||||
stable=true;
|
||||
}
|
||||
}
|
||||
|
||||
its_to_stable_value=i;
|
||||
total_stability_iterations+=its_to_stable_value;
|
||||
total_iterations++;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void FGTrimAxis::setThrottlesPct(void) {
|
||||
double tMin,tMax;
|
||||
for(unsigned i=0;i<fdmex->GetPropulsion()->GetNumEngines();i++) {
|
||||
tMin=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMin();
|
||||
tMax=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMax();
|
||||
|
||||
// Both the main throttle setting in FGFCS and the copy of the position
|
||||
// in the Propulsion::Inputs structure need to be set at this time.
|
||||
fdmex->GetFCS()->SetThrottleCmd(i,tMin+control_value*(tMax-tMin));
|
||||
fdmex->GetPropulsion()->in.ThrottlePos[i] = tMin +control_value*(tMax - tMin);
|
||||
|
||||
fdmex->Initialize(fgic);
|
||||
fdmex->Run(); //apply throttle change
|
||||
fdmex->GetPropulsion()->GetSteadyState();
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void FGTrimAxis::AxisReport(void) {
|
||||
// Save original cout format characteristics
|
||||
std::ios_base::fmtflags originalFormat = cout.flags();
|
||||
std::streamsize originalPrecision = cout.precision();
|
||||
std::streamsize originalWidth = cout.width();
|
||||
cout << " " << setw(20) << GetControlName() << ": ";
|
||||
cout << setw(6) << setprecision(2) << GetControl()*control_convert << ' ';
|
||||
cout << setw(5) << GetStateName() << ": ";
|
||||
cout << setw(9) << setprecision(2) << scientific << GetState()+state_target;
|
||||
cout << " Tolerance: " << setw(3) << setprecision(0) << scientific << GetTolerance();
|
||||
|
||||
if( fabs(GetState()+state_target) < fabs(GetTolerance()) )
|
||||
cout << " Passed" << endl;
|
||||
else
|
||||
cout << " Failed" << endl;
|
||||
// Restore original cout format characteristics
|
||||
cout.flags(originalFormat);
|
||||
cout.precision(originalPrecision);
|
||||
cout.width(originalWidth);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
double FGTrimAxis::GetAvgStability( void ) {
|
||||
if(total_iterations > 0) {
|
||||
return double(total_stability_iterations)/double(total_iterations);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGTrimAxis::Debug(int from)
|
||||
{
|
||||
|
||||
if (debug_lvl <= 0) return;
|
||||
if (debug_lvl & 1 ) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGTrimAxis" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGTrimAxis" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
187
src/FDM/JSBSim/initialization/FGTrimAxis.h
Normal file
187
src/FDM/JSBSim/initialization/FGTrimAxis.h
Normal file
@@ -0,0 +1,187 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTrimAxis.h
|
||||
Author: Tony Peden
|
||||
Date started: 7/3/00
|
||||
|
||||
------------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
7/3/00 TP Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGTRIMAXIS_H
|
||||
#define FGTRIMAXIS_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGInitialCondition.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#define DEFAULT_TOLERANCE 0.001
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
const std::string StateNames[] = { "all","udot","vdot","wdot","qdot","pdot",
|
||||
"rdot","hmgt","nlf"
|
||||
};
|
||||
const std::string ControlNames[] = { "Throttle","Sideslip","Angle of Attack",
|
||||
"Elevator","Ailerons","Rudder",
|
||||
"Altitude AGL", "Pitch Angle",
|
||||
"Roll Angle", "Flight Path Angle",
|
||||
"Pitch Trim", "Roll Trim", "Yaw Trim",
|
||||
"Heading"
|
||||
};
|
||||
|
||||
class FGInitialCondition;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Models an aircraft axis for purposes of trimming.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
enum State { tAll,tUdot,tVdot,tWdot,tQdot,tPdot,tRdot,tHmgt,tNlf };
|
||||
enum Control { tThrottle, tBeta, tAlpha, tElevator, tAileron, tRudder, tAltAGL,
|
||||
tTheta, tPhi, tGamma, tPitchTrim, tRollTrim, tYawTrim, tHeading };
|
||||
|
||||
class FGTrimAxis : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/** Constructor for Trim Axis class.
|
||||
@param fdmex FGFDMExec pointer
|
||||
@param IC pointer to initial conditions instance
|
||||
@param state a State type (enum)
|
||||
@param control a Control type (enum) */
|
||||
FGTrimAxis(FGFDMExec* fdmex,
|
||||
FGInitialCondition *IC,
|
||||
State state,
|
||||
Control control );
|
||||
/// Destructor
|
||||
~FGTrimAxis();
|
||||
|
||||
/** This function iterates through a call to the FGFDMExec::RunIC()
|
||||
function until the desired trimming condition falls inside a tolerance.*/
|
||||
void Run(void);
|
||||
|
||||
double GetState(void) { getState(); return state_value; }
|
||||
//Accels are not settable
|
||||
inline void SetControl(double value ) { control_value=value; }
|
||||
inline double GetControl(void) { return control_value; }
|
||||
|
||||
inline State GetStateType(void) { return state; }
|
||||
inline Control GetControlType(void) { return control; }
|
||||
|
||||
inline std::string GetStateName(void) { return StateNames[state]; }
|
||||
inline std::string GetControlName(void) { return ControlNames[control]; }
|
||||
|
||||
inline double GetControlMin(void) { return control_min; }
|
||||
inline double GetControlMax(void) { return control_max; }
|
||||
|
||||
inline void SetControlToMin(void) { control_value=control_min; }
|
||||
inline void SetControlToMax(void) { control_value=control_max; }
|
||||
|
||||
inline void SetControlLimits(double min, double max) {
|
||||
control_min=min;
|
||||
control_max=max;
|
||||
}
|
||||
|
||||
inline void SetTolerance(double ff) { tolerance=ff;}
|
||||
inline double GetTolerance(void) { return tolerance; }
|
||||
|
||||
inline double GetSolverEps(void) { return solver_eps; }
|
||||
inline void SetSolverEps(double ff) { solver_eps=ff; }
|
||||
|
||||
inline int GetIterationLimit(void) { return max_iterations; }
|
||||
inline void SetIterationLimit(int ii) { max_iterations=ii; }
|
||||
|
||||
inline int GetStability(void) { return its_to_stable_value; }
|
||||
inline int GetRunCount(void) { return total_stability_iterations; }
|
||||
double GetAvgStability( void );
|
||||
|
||||
inline void SetStateTarget(double target) { state_target=target; }
|
||||
inline double GetStateTarget(void) { return state_target; }
|
||||
|
||||
void AxisReport(void);
|
||||
|
||||
bool InTolerance(void) { getState(); return (fabs(state_value) <= tolerance); }
|
||||
|
||||
private:
|
||||
FGFDMExec *fdmex;
|
||||
FGInitialCondition *fgic;
|
||||
|
||||
State state;
|
||||
Control control;
|
||||
|
||||
double state_target;
|
||||
|
||||
double state_value;
|
||||
double control_value;
|
||||
|
||||
double control_min;
|
||||
double control_max;
|
||||
|
||||
double tolerance;
|
||||
|
||||
double solver_eps;
|
||||
|
||||
double state_convert;
|
||||
double control_convert;
|
||||
|
||||
int max_iterations;
|
||||
|
||||
int its_to_stable_value;
|
||||
int total_stability_iterations;
|
||||
int total_iterations;
|
||||
|
||||
void setThrottlesPct(void);
|
||||
|
||||
void getState(void);
|
||||
void getControl(void);
|
||||
void setControl(void);
|
||||
|
||||
double computeHmgt(void);
|
||||
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
61
src/FDM/JSBSim/input_output/FGGroundCallback.cpp
Normal file
61
src/FDM/JSBSim/input_output/FGGroundCallback.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGGroundCallback.cpp
|
||||
Author: Mathias Froehlich
|
||||
Date started: 05/21/04
|
||||
|
||||
------ Copyright (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) -------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
-------------------------------------------------------------------------------
|
||||
05/21/00 MF Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "math/FGLocation.h"
|
||||
#include "FGGroundCallback.h"
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGDefaultGroundCallback::GetAGLevel(double t, const FGLocation& loc,
|
||||
FGLocation& contact, FGColumnVector3& normal,
|
||||
FGColumnVector3& vel, FGColumnVector3& angularVel) const
|
||||
{
|
||||
vel.InitMatrix();
|
||||
angularVel.InitMatrix();
|
||||
FGLocation l = loc;
|
||||
l.SetEllipse(a,b);
|
||||
double latitude = l.GetGeodLatitudeRad();
|
||||
double cosLat = cos(latitude);
|
||||
double longitude = l.GetLongitude();
|
||||
normal = FGColumnVector3(cosLat*cos(longitude), cosLat*sin(longitude),
|
||||
sin(latitude));
|
||||
contact.SetEllipse(a, b);
|
||||
contact.SetPositionGeodetic(longitude, latitude, mTerrainElevation);
|
||||
return l.GetGeodAltitude() - mTerrainElevation;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
} // namespace JSBSim
|
||||
149
src/FDM/JSBSim/input_output/FGGroundCallback.h
Normal file
149
src/FDM/JSBSim/input_output/FGGroundCallback.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGGroundCallback.h
|
||||
Author: Mathias Froehlich
|
||||
Date started: 05/21/04
|
||||
|
||||
------ Copyright (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) -------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
-------------------------------------------------------------------------------
|
||||
05/21/00 MF Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGGROUNDCALLBACK_H
|
||||
#define FGGROUNDCALLBACK_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGLocation;
|
||||
class FGColumnVector3;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** This class provides callback slots to get ground specific data.
|
||||
|
||||
The default implementation returns values for a
|
||||
ball formed earth with an adjustable terrain elevation.
|
||||
|
||||
@author Mathias Froehlich
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGGroundCallback
|
||||
{
|
||||
public:
|
||||
|
||||
FGGroundCallback() : time(0.0) {}
|
||||
virtual ~FGGroundCallback() {}
|
||||
|
||||
/** Compute the altitude above ground.
|
||||
The altitude depends on time t and location l.
|
||||
@param t simulation time
|
||||
@param l location
|
||||
@param contact Contact point location below the location l
|
||||
@param normal Normal vector at the contact point
|
||||
@param v Linear velocity at the contact point
|
||||
@param w Angular velocity at the contact point
|
||||
@return altitude above ground
|
||||
*/
|
||||
virtual double GetAGLevel(double t, const FGLocation& location,
|
||||
FGLocation& contact,
|
||||
FGColumnVector3& normal, FGColumnVector3& v,
|
||||
FGColumnVector3& w) const = 0;
|
||||
|
||||
/** Compute the altitude above ground.
|
||||
The altitude depends on location l.
|
||||
@param l location
|
||||
@param contact Contact point location below the location l
|
||||
@param normal Normal vector at the contact point
|
||||
@param v Linear velocity at the contact point
|
||||
@param w Angular velocity at the contact point
|
||||
@return altitude above ground
|
||||
*/
|
||||
virtual double GetAGLevel(const FGLocation& location, FGLocation& contact,
|
||||
FGColumnVector3& normal, FGColumnVector3& v,
|
||||
FGColumnVector3& w) const
|
||||
{ return GetAGLevel(time, location, contact, normal, v, w); }
|
||||
|
||||
/** Set the terrain elevation.
|
||||
Only needs to be implemented if JSBSim should be allowed
|
||||
to modify the local terrain radius (see the default implementation)
|
||||
*/
|
||||
virtual void SetTerrainElevation(double h) {}
|
||||
|
||||
/** Set the planet semimajor and semiminor axes.
|
||||
Only needs to be implemented if JSBSim should be allowed to modify
|
||||
the planet dimensions.
|
||||
*/
|
||||
virtual void SetEllipse(double semimajor, double semiminor) {}
|
||||
|
||||
/** Set the simulation time.
|
||||
The elapsed time can be used by the ground callbck to assess the planet
|
||||
rotation or the movement of objects.
|
||||
@param _time elapsed time in seconds since the simulation started.
|
||||
*/
|
||||
void SetTime(double _time) { time = _time; }
|
||||
|
||||
protected:
|
||||
double time;
|
||||
};
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The default sphere earth implementation:
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
class FGDefaultGroundCallback : public FGGroundCallback
|
||||
{
|
||||
public:
|
||||
explicit FGDefaultGroundCallback(double semiMajor, double semiMinor) :
|
||||
a(semiMajor), b(semiMinor) {}
|
||||
|
||||
double GetAGLevel(double t, const FGLocation& location,
|
||||
FGLocation& contact,
|
||||
FGColumnVector3& normal, FGColumnVector3& v,
|
||||
FGColumnVector3& w) const override;
|
||||
|
||||
void SetTerrainElevation(double h) override
|
||||
{ mTerrainElevation = h; }
|
||||
|
||||
void SetEllipse(double semimajor, double semiminor) override
|
||||
{ a = semimajor; b = semiminor; }
|
||||
|
||||
private:
|
||||
double a, b;
|
||||
double mTerrainElevation = 0.0;
|
||||
};
|
||||
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
269
src/FDM/JSBSim/input_output/FGInputSocket.cpp
Normal file
269
src/FDM/JSBSim/input_output/FGInputSocket.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGInputSocket.cpp
|
||||
Author: Paul Chavent
|
||||
Date started: 01/20/15
|
||||
Purpose: Manage input of sim parameters to a socket
|
||||
Called by: FGInput
|
||||
|
||||
------------- Copyright (C) 2015 Paul Chavent -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create input routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
01/20/15 PC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "FGInputSocket.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "models/FGAircraft.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGInputSocket::FGInputSocket(FGFDMExec* fdmex) :
|
||||
FGInputType(fdmex), socket(0), SockProtocol(FGfdmSocket::ptTCP),
|
||||
BlockingInput(false)
|
||||
{
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGInputSocket::~FGInputSocket()
|
||||
{
|
||||
delete socket;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGInputSocket::Load(Element* el)
|
||||
{
|
||||
if (!FGInputType::Load(el))
|
||||
return false;
|
||||
|
||||
SockPort = atoi(el->GetAttributeValue("port").c_str());
|
||||
|
||||
if (SockPort == 0) {
|
||||
cerr << endl << "No port assigned in input element" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
string action = el->GetAttributeValue("action");
|
||||
if (to_upper(action) == "BLOCKING_INPUT")
|
||||
BlockingInput = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGInputSocket::InitModel(void)
|
||||
{
|
||||
if (FGInputType::InitModel()) {
|
||||
delete socket;
|
||||
socket = new FGfdmSocket(SockPort, SockProtocol);
|
||||
|
||||
if (socket == 0) return false;
|
||||
if (!socket->GetConnectStatus()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGInputSocket::Read(bool Holding)
|
||||
{
|
||||
string line, token;
|
||||
size_t start=0, string_start=0, string_end=0;
|
||||
double value=0;
|
||||
FGPropertyNode* node=0;
|
||||
|
||||
if (socket == 0) return;
|
||||
if (!socket->GetConnectStatus()) return;
|
||||
|
||||
if (BlockingInput)
|
||||
socket->WaitUntilReadable(); // block until a transmission is received
|
||||
data = socket->Receive(); // read data
|
||||
|
||||
if (data.size() > 0) {
|
||||
// parse lines
|
||||
while (1) {
|
||||
string_start = data.find_first_not_of("\r\n", start);
|
||||
if (string_start == string::npos) break;
|
||||
string_end = data.find_first_of("\r\n", string_start);
|
||||
if (string_end == string::npos) break;
|
||||
line = data.substr(string_start, string_end-string_start);
|
||||
if (line.size() == 0) break;
|
||||
|
||||
// now parse individual line
|
||||
vector <string> tokens = split(line,' ');
|
||||
|
||||
string command="", argument="", str_value="";
|
||||
if (tokens.size() > 0) {
|
||||
command = to_lower(tokens[0]);
|
||||
if (tokens.size() > 1) {
|
||||
argument = trim(tokens[1]);
|
||||
if (tokens.size() > 2) {
|
||||
str_value = trim(tokens[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "set") { // SET PROPERTY
|
||||
|
||||
if (argument.size() == 0) {
|
||||
socket->Reply("No property argument supplied.\n");
|
||||
break;
|
||||
}
|
||||
try {
|
||||
node = PropertyManager->GetNode(argument);
|
||||
} catch(...) {
|
||||
socket->Reply("Badly formed property query\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == 0) {
|
||||
socket->Reply("Unknown property\n");
|
||||
break;
|
||||
} else if (!node->hasValue()) {
|
||||
socket->Reply("Not a leaf property\n");
|
||||
break;
|
||||
} else {
|
||||
value = atof(str_value.c_str());
|
||||
node->setDoubleValue(value);
|
||||
}
|
||||
socket->Reply("set successful\n");
|
||||
|
||||
} else if (command == "get") { // GET PROPERTY
|
||||
|
||||
if (argument.size() == 0) {
|
||||
socket->Reply("No property argument supplied.\n");
|
||||
break;
|
||||
}
|
||||
try {
|
||||
node = PropertyManager->GetNode(argument);
|
||||
} catch(...) {
|
||||
socket->Reply("Badly formed property query\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (node == 0) {
|
||||
socket->Reply("Unknown property\n");
|
||||
break;
|
||||
} else if (!node->hasValue()) {
|
||||
if (Holding) { // if holding can query property list
|
||||
string query = FDMExec->QueryPropertyCatalog(argument);
|
||||
socket->Reply(query);
|
||||
} else {
|
||||
socket->Reply("Must be in HOLD to search properties\n");
|
||||
}
|
||||
} else {
|
||||
ostringstream buf;
|
||||
buf << argument << " = " << setw(12) << setprecision(6) << node->getDoubleValue() << endl;
|
||||
socket->Reply(buf.str());
|
||||
}
|
||||
|
||||
} else if (command == "hold") { // PAUSE
|
||||
|
||||
FDMExec->Hold();
|
||||
socket->Reply("Holding\n");
|
||||
|
||||
} else if (command == "resume") { // RESUME
|
||||
|
||||
FDMExec->Resume();
|
||||
socket->Reply("Resuming\n");
|
||||
|
||||
} else if (command == "iterate") { // ITERATE
|
||||
|
||||
int argumentInt;
|
||||
istringstream (argument) >> argumentInt;
|
||||
if (argument.size() == 0) {
|
||||
socket->Reply("No argument supplied for number of iterations.\n");
|
||||
break;
|
||||
}
|
||||
if ( !(argumentInt > 0) ){
|
||||
socket->Reply("Required argument must be a positive Integer.\n");
|
||||
break;
|
||||
}
|
||||
FDMExec->EnableIncrementThenHold( argumentInt );
|
||||
FDMExec->Resume();
|
||||
socket->Reply("Iterations performed\n");
|
||||
|
||||
} else if (command == "quit") { // QUIT
|
||||
|
||||
// close the socket connection
|
||||
socket->Reply("Closing connection\n");
|
||||
socket->Close();
|
||||
|
||||
} else if (command == "info") { // INFO
|
||||
|
||||
// get info about the sim run and/or aircraft, etc.
|
||||
ostringstream info;
|
||||
info << "JSBSim version: " << JSBSim_version << endl;
|
||||
info << "Config File version: " << needed_cfg_version << endl;
|
||||
info << "Aircraft simulated: " << FDMExec->GetAircraft()->GetAircraftName() << endl;
|
||||
info << "Simulation time: " << setw(8) << setprecision(3) << FDMExec->GetSimTime() << endl;
|
||||
socket->Reply(info.str());
|
||||
|
||||
} else if (command == "help") { // HELP
|
||||
|
||||
socket->Reply(
|
||||
" JSBSim Server commands:\n\n"
|
||||
" get {property name}\n"
|
||||
" set {property name} {value}\n"
|
||||
" hold\n"
|
||||
" resume\n"
|
||||
" iterate {value}\n"
|
||||
" help\n"
|
||||
" quit\n"
|
||||
" info\n\n");
|
||||
|
||||
} else {
|
||||
socket->Reply(string("Unknown command: ") + token + string("\n"));
|
||||
}
|
||||
|
||||
start = string_end;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
95
src/FDM/JSBSim/input_output/FGInputSocket.h
Normal file
95
src/FDM/JSBSim/input_output/FGInputSocket.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGInputSocket.h
|
||||
Author: Paul Chavent
|
||||
Date started: 01/20/15
|
||||
|
||||
------------- Copyright (C) 2015 Paul Chavent -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
20/01/15 PC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGINPUTSOCKET_H
|
||||
#define FGINPUTSOCKET_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGInputType.h"
|
||||
#include "input_output/FGfdmSocket.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Implements the input from a socket. This class inputs data from a telnet
|
||||
session. This is a leaf class.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGInputSocket : public FGInputType
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
FGInputSocket(FGFDMExec* fdmex);
|
||||
|
||||
/** Destructor. */
|
||||
~FGInputSocket() override;
|
||||
|
||||
/** Init the input directives from an XML file.
|
||||
@param element XML Element that is pointing to the input directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/** Initializes the instance. This method basically opens the socket to which
|
||||
inputs will be directed.
|
||||
@result true if the execution succeeded.
|
||||
*/
|
||||
bool InitModel(void) override;
|
||||
|
||||
/// Generates the input.
|
||||
void Read(bool Holding) override;
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int SockPort;
|
||||
FGfdmSocket* socket;
|
||||
FGfdmSocket::ProtocolType SockProtocol;
|
||||
std::string data;
|
||||
bool BlockingInput;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
157
src/FDM/JSBSim/input_output/FGInputType.cpp
Normal file
157
src/FDM/JSBSim/input_output/FGInputType.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGInputType.cpp
|
||||
Author: Paul Chavent
|
||||
Date started: 01/20/15
|
||||
Purpose: Manage input of sim parameters to file or stdout
|
||||
|
||||
------------- Copyright (C) 2015 Paul Chavent -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create input routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
01/20/15 PC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGInputType.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGInputType::FGInputType(FGFDMExec* fdmex) :
|
||||
FGModel(fdmex), enabled(true)
|
||||
{
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGInputType::~FGInputType()
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGInputType::SetIdx(unsigned int idx)
|
||||
{
|
||||
InputIdx = idx;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGInputType::Load(Element* element)
|
||||
{
|
||||
// Perform base class Load.
|
||||
if(!FGModel::Upload(element, true))
|
||||
return false;
|
||||
|
||||
// no common attributes yet (see FGOutputType for example
|
||||
|
||||
// FIXME : PostLoad should be called in the most derived class ?
|
||||
PostLoad(element, FDMExec);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGInputType::InitModel(void)
|
||||
{
|
||||
bool ret = FGModel::InitModel();
|
||||
|
||||
Debug(2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGInputType::Run(bool Holding)
|
||||
{
|
||||
if (FGModel::Run(Holding)) return true;
|
||||
if (!enabled) return true;
|
||||
|
||||
RunPreFunctions();
|
||||
Read(Holding);
|
||||
RunPostFunctions();
|
||||
|
||||
Debug(4);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to input any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGInputType::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message input
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
if (from == 2) {
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGInputType" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGInputType" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
src/FDM/JSBSim/input_output/FGInputType.h
Normal file
139
src/FDM/JSBSim/input_output/FGInputType.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGInputType.h
|
||||
Author: Paul Chavent
|
||||
Date started: 01/20/15
|
||||
|
||||
------------- Copyright (C) 2015 Paul Chavent -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
01/20/15 PC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGINPUTTYPE_H
|
||||
#define FGINPUTTYPE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "models/FGModel.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Abstract class to provide functions generic to all the input directives.
|
||||
This class is used by the input manager FGInput to manage a list of
|
||||
different input classes without needing to know the details of each one of
|
||||
them. It also provides the functions that are common to all the input
|
||||
classes.
|
||||
|
||||
The class inherits from FGModelFunctions so it is possible to define
|
||||
functions that execute before or after the input is generated. Such
|
||||
functions need to be tagged with a "pre" or "post" type attribute to denote
|
||||
the sequence in which they should be executed.
|
||||
|
||||
The class mimics some functionalities of FGModel (methods InitModel(),
|
||||
Run() and SetRate()). However it does not inherit from FGModel since it is
|
||||
conceptually different from the model paradigm.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGInputType : public FGModel
|
||||
{
|
||||
public:
|
||||
/** Constructor (implement the FGModel interface).
|
||||
@param fdmex a pointer to the parent executive object
|
||||
*/
|
||||
FGInputType(FGFDMExec* fdmex);
|
||||
|
||||
/// Destructor
|
||||
~FGInputType() override;
|
||||
|
||||
/** Set the idx for this input instance
|
||||
@param idx ID of the input instance that is constructed
|
||||
*/
|
||||
void SetIdx(unsigned int idx);
|
||||
|
||||
/** Init the input directives from an XML file (implement the FGModel interface).
|
||||
@param element XML Element that is pointing to the input directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/// Init the input model according to its configitation.
|
||||
bool InitModel(void) override;
|
||||
|
||||
/** Executes the input directives (implement the FGModel interface).
|
||||
This method checks that the current time step matches the input
|
||||
rate and calls the registered "pre" functions, the input
|
||||
generation and finally the "post" functions.
|
||||
@result false if no error.
|
||||
*/
|
||||
bool Run(bool Holding) override;
|
||||
|
||||
/** Generate the input. This is a pure method so it must be implemented by
|
||||
the classes that inherits from FGInputType. The Read name may not be
|
||||
relevant to all inputs but it has been kept for backward compatibility.
|
||||
*/
|
||||
virtual void Read(bool Holding) = 0;
|
||||
|
||||
/// Enables the input generation.
|
||||
void Enable(void) { enabled = true; }
|
||||
/// Disables the input generation.
|
||||
void Disable(void) { enabled = false; }
|
||||
/** Toggles the input generation.
|
||||
@result the input generation status i.e. true if the input has been
|
||||
enabled, false if the input has been disabled. */
|
||||
bool Toggle(void) {enabled = !enabled; return enabled;}
|
||||
|
||||
/** Overwrites the name identifier under which the input will be read.
|
||||
This method is taken into account if it is called before
|
||||
FGFDMExec::RunIC() otherwise it is ignored until the next call to
|
||||
SetStartNewInput().
|
||||
@param name new name */
|
||||
virtual void SetInputName(const std::string& name) { Name = name; }
|
||||
|
||||
/** Get the name identifier to which the input will be directed.
|
||||
@result the name identifier.*/
|
||||
virtual const std::string& GetInputName(void) const { return Name; }
|
||||
|
||||
protected:
|
||||
unsigned int InputIdx;
|
||||
bool enabled;
|
||||
|
||||
void Debug(int from) override;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
94
src/FDM/JSBSim/input_output/FGModelLoader.cpp
Normal file
94
src/FDM/JSBSim/input_output/FGModelLoader.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGModelLoader.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 12/14/13
|
||||
Purpose: Read and manage XML data for models definition
|
||||
|
||||
------------- Copyright (C) 2013 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where the XML data is loaded in memory for an access during
|
||||
the models initialization.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/14/13 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGModelLoader.h"
|
||||
#include "FGXMLFileRead.h"
|
||||
#include "models/FGModel.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
Element_ptr FGModelLoader::Open(Element *el)
|
||||
{
|
||||
Element_ptr document = el;
|
||||
string fname = el->GetAttributeValue("file");
|
||||
|
||||
if (!fname.empty()) {
|
||||
FGXMLFileRead XMLFileRead;
|
||||
SGPath path(SGPath::fromUtf8(fname.c_str()));
|
||||
|
||||
if (path.isRelative())
|
||||
path = model->FindFullPathName(path);
|
||||
|
||||
if (CachedFiles.find(path.utf8Str()) != CachedFiles.end())
|
||||
document = CachedFiles[path.utf8Str()];
|
||||
else {
|
||||
document = XMLFileRead.LoadXMLDocument(path);
|
||||
if (document == 0L) {
|
||||
cerr << endl << el->ReadFrom()
|
||||
<< "Could not open file: " << fname << endl;
|
||||
return NULL;
|
||||
}
|
||||
CachedFiles[path.utf8Str()] = document;
|
||||
}
|
||||
|
||||
if (document->GetName() != el->GetName()) {
|
||||
document->SetParent(el);
|
||||
el->AddChildElement(document);
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
SGPath CheckPathName(const SGPath& path, const SGPath& filename) {
|
||||
SGPath fullName = path/filename.utf8Str();
|
||||
|
||||
if (fullName.extension() != "xml")
|
||||
fullName.concat(".xml");
|
||||
|
||||
return fullName.exists() ? fullName : SGPath();
|
||||
}
|
||||
}
|
||||
76
src/FDM/JSBSim/input_output/FGModelLoader.h
Normal file
76
src/FDM/JSBSim/input_output/FGModelLoader.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGModelLoader.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 12/14/13
|
||||
|
||||
------------- Copyright (C) 2013 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/14/13 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGMODELLOADER_H
|
||||
#define FGMODELLOADER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "FGXMLElement.h"
|
||||
#include "simgear/misc/sg_path.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGModel;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGModelLoader
|
||||
{
|
||||
public:
|
||||
FGModelLoader(const FGModel* _model) : model(_model) {}
|
||||
Element_ptr Open(Element *el);
|
||||
|
||||
private:
|
||||
const FGModel* model;
|
||||
std::map<std::string, Element_ptr> CachedFiles;
|
||||
};
|
||||
|
||||
SGPath CheckPathName(const SGPath& path, const SGPath& filename);
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
388
src/FDM/JSBSim/input_output/FGOutputFG.cpp
Normal file
388
src/FDM/JSBSim/input_output/FGOutputFG.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGOutputFG.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
Purpose: Manage output of sim parameters to FlightGear
|
||||
Called by: FGOutput
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create output routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
11/09/07 HDW Added FlightGear Socket Interface
|
||||
09/10/11 BC Moved the FlightGear socket in a separate class
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "FGOutputFG.h"
|
||||
#include "FGXMLElement.h"
|
||||
#include "models/FGAuxiliary.h"
|
||||
#include "models/FGPropulsion.h"
|
||||
#include "models/FGFCS.h"
|
||||
#include "models/propulsion/FGPiston.h"
|
||||
#include "models/propulsion/FGElectric.h"
|
||||
#include "models/propulsion/FGTank.h"
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <netinet/in.h> // htonl() ntohl()
|
||||
#endif
|
||||
|
||||
#if !defined (min)
|
||||
# define min(X,Y) X<Y?X:Y
|
||||
#endif
|
||||
|
||||
static const int endianTest = 1;
|
||||
#define isLittleEndian (*((char *) &endianTest ) != 0)
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
// (stolen from FGFS native_fdm.cxx)
|
||||
// The function htond is defined this way due to the way some
|
||||
// processors and OSes treat floating point values. Some will raise
|
||||
// an exception whenever a "bad" floating point value is loaded into a
|
||||
// floating point register. Solaris is notorious for this, but then
|
||||
// so is LynxOS on the PowerPC. By translating the data in place,
|
||||
// there is no need to load a FP register with the "corruped" floating
|
||||
// point value. By doing the BIG_ENDIAN test, I can optimize the
|
||||
// routine for big-endian processors so it can be as efficient as
|
||||
// possible
|
||||
static void htond (double &x)
|
||||
{
|
||||
if ( isLittleEndian ) {
|
||||
int *Double_Overlay;
|
||||
int Holding_Buffer;
|
||||
|
||||
Double_Overlay = (int *) &x;
|
||||
Holding_Buffer = Double_Overlay [0];
|
||||
|
||||
Double_Overlay [0] = htonl (Double_Overlay [1]);
|
||||
Double_Overlay [1] = htonl (Holding_Buffer);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Float version
|
||||
static void htonf (float &x)
|
||||
{
|
||||
if ( isLittleEndian ) {
|
||||
int *Float_Overlay;
|
||||
int Holding_Buffer;
|
||||
|
||||
Float_Overlay = (int *) &x;
|
||||
Holding_Buffer = Float_Overlay [0];
|
||||
|
||||
Float_Overlay [0] = htonl (Holding_Buffer);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGOutputFG::FGOutputFG(FGFDMExec* fdmex) :
|
||||
FGOutputSocket(fdmex), outputOptions{false, 1e6}
|
||||
{
|
||||
memset(&fgSockBuf, 0x0, sizeof(fgSockBuf));
|
||||
|
||||
if (fdmex->GetDebugLevel() > 0) {
|
||||
// Engine status
|
||||
if (Propulsion->GetNumEngines() > FGNetFDM::FG_MAX_ENGINES)
|
||||
cerr << "This vehicle has " << Propulsion->GetNumEngines() << " engines, but the current " << endl
|
||||
<< "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_ENGINES << " engines." << endl
|
||||
<< "Only the first " << FGNetFDM::FG_MAX_ENGINES << " engines will be used." << endl;
|
||||
|
||||
// Consumables
|
||||
if (Propulsion->GetNumTanks() > FGNetFDM::FG_MAX_TANKS)
|
||||
cerr << "This vehicle has " << Propulsion->GetNumTanks() << " tanks, but the current " << endl
|
||||
<< "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_TANKS << " tanks." << endl
|
||||
<< "Only the first " << FGNetFDM::FG_MAX_TANKS << " tanks will be used." << endl;
|
||||
|
||||
// Gear status
|
||||
if (GroundReactions->GetNumGearUnits() > FGNetFDM::FG_MAX_WHEELS)
|
||||
cerr << "This vehicle has " << GroundReactions->GetNumGearUnits() << " bogeys, but the current " << endl
|
||||
<< "version of FlightGear's FGNetFDM only supports " << FGNetFDM::FG_MAX_WHEELS << " bogeys." << endl
|
||||
<< "Only the first " << FGNetFDM::FG_MAX_WHEELS << " bogeys will be used." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputFG::Load(Element* el)
|
||||
{
|
||||
if (!FGOutputSocket::Load(el)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there is a <time> element
|
||||
Element* time_el = el->FindElement("time");
|
||||
if (time_el) {
|
||||
// Check if the attribute "type" is specified and is set to "simulation"
|
||||
if (time_el->HasAttribute("type") && time_el->GetAttributeValue("type") == "simulation") {
|
||||
outputOptions.useSimTime = true;
|
||||
}
|
||||
|
||||
// Check if the attribute "resolution" is specified and set to a valid value
|
||||
if (time_el->HasAttribute("resolution")) {
|
||||
if (time_el->GetAttributeValueAsNumber("resolution") <= 1 &&
|
||||
time_el->GetAttributeValueAsNumber("resolution") >= 1e-9) {
|
||||
outputOptions.timeFactor = 1./time_el->GetAttributeValueAsNumber("resolution");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputFG::SocketDataFill(FGNetFDM* net)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
// Version
|
||||
net->version = FG_NET_FDM_VERSION;
|
||||
|
||||
// Positions
|
||||
net->longitude = Propagate->GetLongitude(); // longitude (radians)
|
||||
net->latitude = Propagate->GetGeodLatitudeRad(); // geodetic (radians)
|
||||
net->altitude = Propagate->GetAltitudeASL()*0.3048; // altitude, above sea level (meters)
|
||||
net->agl = (float)(Propagate->GetDistanceAGL()*0.3048); // altitude, above ground level (meters)
|
||||
|
||||
net->phi = (float)(Propagate->GetEuler(ePhi)); // roll (radians)
|
||||
net->theta = (float)(Propagate->GetEuler(eTht)); // pitch (radians)
|
||||
net->psi = (float)(Propagate->GetEuler(ePsi)); // yaw or true heading (radians)
|
||||
|
||||
net->alpha = (float)(Auxiliary->Getalpha()); // angle of attack (radians)
|
||||
net->beta = (float)(Auxiliary->Getbeta()); // side slip angle (radians)
|
||||
|
||||
// Velocities
|
||||
net->phidot = (float)(Auxiliary->GetEulerRates(ePhi)); // roll rate (radians/sec)
|
||||
net->thetadot = (float)(Auxiliary->GetEulerRates(eTht)); // pitch rate (radians/sec)
|
||||
net->psidot = (float)(Auxiliary->GetEulerRates(ePsi)); // yaw rate (radians/sec)
|
||||
net->vcas = (float)(Auxiliary->GetVcalibratedKTS()); // VCAS, knots
|
||||
net->climb_rate = (float)(Propagate->Gethdot()); // altitude rate, ft/sec
|
||||
net->v_north = (float)(Propagate->GetVel(eNorth)); // north vel in NED frame, fps
|
||||
net->v_east = (float)(Propagate->GetVel(eEast)); // east vel in NED frame, fps
|
||||
net->v_down = (float)(Propagate->GetVel(eDown)); // down vel in NED frame, fps
|
||||
//---ADD METHOD TO CALCULATE THESE TERMS---
|
||||
net->v_body_u = (float)(Propagate->GetUVW(1)); // ECEF speed in body axis
|
||||
net->v_body_v = (float)(Propagate->GetUVW(2)); // ECEF speed in body axis
|
||||
net->v_body_w = (float)(Propagate->GetUVW(3)); // ECEF speed in body axis
|
||||
|
||||
// Accelerations
|
||||
net->A_X_pilot = (float)(Auxiliary->GetPilotAccel(1)); // X body accel, ft/s/s
|
||||
net->A_Y_pilot = (float)(Auxiliary->GetPilotAccel(2)); // Y body accel, ft/s/s
|
||||
net->A_Z_pilot = (float)(Auxiliary->GetPilotAccel(3)); // Z body accel, ft/s/s
|
||||
|
||||
// Stall
|
||||
net->stall_warning = 0.0; // 0.0 - 1.0 indicating the amount of stall
|
||||
net->slip_deg = (float)(Auxiliary->Getbeta(inDegrees)); // slip ball deflection, deg
|
||||
|
||||
net->num_engines = min(FGNetFDM::FG_MAX_ENGINES,Propulsion->GetNumEngines()); // Number of valid engines
|
||||
|
||||
for (i=0; i<net->num_engines; i++) {
|
||||
FGEngine* engine = Propulsion->GetEngine(i);
|
||||
if (engine->GetRunning())
|
||||
net->eng_state[i] = 2; // Engine state running
|
||||
else if (engine->GetCranking())
|
||||
net->eng_state[i] = 1; // Engine state cranking
|
||||
else
|
||||
net->eng_state[i] = 0; // Engine state off
|
||||
|
||||
switch (engine->GetType()) {
|
||||
case (FGEngine::etRocket):
|
||||
break;
|
||||
case (FGEngine::etPiston):
|
||||
{
|
||||
FGPiston* piston_engine = static_cast<FGPiston*>(engine);
|
||||
net->rpm[i] = (float)(piston_engine->getRPM());
|
||||
net->fuel_flow[i] = (float)(piston_engine->getFuelFlow_gph());
|
||||
net->fuel_px[i] = 0; // Fuel pressure, psi (N/A in current model)
|
||||
net->egt[i] = (float)(piston_engine->GetEGT());
|
||||
net->cht[i] = (float)(piston_engine->getCylinderHeadTemp_degF());
|
||||
net->mp_osi[i] = (float)(piston_engine->getManifoldPressure_inHg());
|
||||
net->oil_temp[i] = (float)(piston_engine->getOilTemp_degF());
|
||||
net->oil_px[i] = (float)(piston_engine->getOilPressure_psi());
|
||||
net->tit[i] = 0; // Turbine Inlet Temperature (N/A for piston)
|
||||
}
|
||||
break;
|
||||
case (FGEngine::etTurbine):
|
||||
break;
|
||||
case (FGEngine::etTurboprop):
|
||||
break;
|
||||
case (FGEngine::etElectric):
|
||||
net->rpm[i] = static_cast<float>(static_cast<FGElectric*>(engine)->getRPM());
|
||||
break;
|
||||
case (FGEngine::etUnknown):
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
net->num_tanks = min(FGNetFDM::FG_MAX_TANKS, Propulsion->GetNumTanks()); // Max number of fuel tanks
|
||||
|
||||
for (i=0; i<net->num_tanks; i++) {
|
||||
net->fuel_quantity[i] = (float)(((FGTank *)Propulsion->GetTank(i))->GetContents());
|
||||
}
|
||||
|
||||
net->num_wheels = min(FGNetFDM::FG_MAX_WHEELS, GroundReactions->GetNumGearUnits());
|
||||
|
||||
for (i=0; i<net->num_wheels; i++) {
|
||||
net->wow[i] = GroundReactions->GetGearUnit(i)->GetWOW();
|
||||
if (GroundReactions->GetGearUnit(i)->GetGearUnitDown())
|
||||
net->gear_pos[i] = 1; //gear down, using FCS convention
|
||||
else
|
||||
net->gear_pos[i] = 0; //gear up, using FCS convention
|
||||
net->gear_steer[i] = (float)(GroundReactions->GetGearUnit(i)->GetSteerNorm());
|
||||
net->gear_compression[i] = (float)(GroundReactions->GetGearUnit(i)->GetCompLen());
|
||||
}
|
||||
|
||||
// Environment
|
||||
if (outputOptions.useSimTime) {
|
||||
// Send simulation time with specified resolution
|
||||
net->cur_time = static_cast<uint32_t>(FDMExec->GetSimTime()*outputOptions.timeFactor);
|
||||
} else {
|
||||
// Default to sending constant dummy value to ensure backwards-compatibility
|
||||
net->cur_time = 1234567890u;
|
||||
}
|
||||
|
||||
net->warp = 0; // offset in seconds to unix time
|
||||
net->visibility = 25000.0; // visibility in meters (for env. effects)
|
||||
|
||||
// Control surface positions (normalized values)
|
||||
net->elevator = (float)(FCS->GetDePos(ofNorm)); // Norm Elevator Pos, --
|
||||
net->elevator_trim_tab = (float)(FCS->GetPitchTrimCmd()); // Norm Elev Trim Tab Pos, --
|
||||
net->left_flap = (float)(FCS->GetDfPos(ofNorm)); // Norm Flap Pos, --
|
||||
net->right_flap = (float)(FCS->GetDfPos(ofNorm)); // Norm Flap Pos, --
|
||||
net->left_aileron = (float)(FCS->GetDaLPos(ofNorm)); // Norm L Aileron Pos, --
|
||||
net->right_aileron = (float)(FCS->GetDaRPos(ofNorm)); // Norm R Aileron Pos, --
|
||||
net->rudder = (float)(FCS->GetDrPos(ofNorm)); // Norm Rudder Pos, --
|
||||
net->nose_wheel = (float)(FCS->GetDrPos(ofNorm)); // *** FIX *** Using Rudder Pos for NWS, --
|
||||
net->speedbrake = (float)(FCS->GetDsbPos(ofNorm)); // Norm Speedbrake Pos, --
|
||||
net->spoilers = (float)(FCS->GetDspPos(ofNorm)); // Norm Spoiler Pos, --
|
||||
|
||||
// Convert the net buffer to network format
|
||||
if ( isLittleEndian ) {
|
||||
net->version = htonl(net->version);
|
||||
|
||||
htond(net->longitude);
|
||||
htond(net->latitude);
|
||||
htond(net->altitude);
|
||||
htonf(net->agl);
|
||||
htonf(net->phi);
|
||||
htonf(net->theta);
|
||||
htonf(net->psi);
|
||||
htonf(net->alpha);
|
||||
htonf(net->beta);
|
||||
|
||||
htonf(net->phidot);
|
||||
htonf(net->thetadot);
|
||||
htonf(net->psidot);
|
||||
htonf(net->vcas);
|
||||
htonf(net->climb_rate);
|
||||
htonf(net->v_north);
|
||||
htonf(net->v_east);
|
||||
htonf(net->v_down);
|
||||
htonf(net->v_body_u);
|
||||
htonf(net->v_body_v);
|
||||
htonf(net->v_body_w);
|
||||
|
||||
htonf(net->A_X_pilot);
|
||||
htonf(net->A_Y_pilot);
|
||||
htonf(net->A_Z_pilot);
|
||||
|
||||
htonf(net->stall_warning);
|
||||
htonf(net->slip_deg);
|
||||
|
||||
for (i=0; i<net->num_engines; ++i ) {
|
||||
net->eng_state[i] = htonl(net->eng_state[i]);
|
||||
htonf(net->rpm[i]);
|
||||
htonf(net->fuel_flow[i]);
|
||||
htonf(net->fuel_px[i]);
|
||||
htonf(net->egt[i]);
|
||||
htonf(net->cht[i]);
|
||||
htonf(net->mp_osi[i]);
|
||||
htonf(net->tit[i]);
|
||||
htonf(net->oil_temp[i]);
|
||||
htonf(net->oil_px[i]);
|
||||
}
|
||||
net->num_engines = htonl(net->num_engines);
|
||||
|
||||
for (i=0; i<net->num_tanks; ++i ) {
|
||||
htonf(net->fuel_quantity[i]);
|
||||
}
|
||||
net->num_tanks = htonl(net->num_tanks);
|
||||
|
||||
for (i=0; i<net->num_wheels; ++i ) {
|
||||
net->wow[i] = htonl(net->wow[i]);
|
||||
htonf(net->gear_pos[i]);
|
||||
htonf(net->gear_steer[i]);
|
||||
htonf(net->gear_compression[i]);
|
||||
}
|
||||
net->num_wheels = htonl(net->num_wheels);
|
||||
|
||||
net->cur_time = htonl( net->cur_time );
|
||||
net->warp = htonl( net->warp );
|
||||
htonf(net->visibility);
|
||||
|
||||
htonf(net->elevator);
|
||||
htonf(net->elevator_trim_tab);
|
||||
htonf(net->left_flap);
|
||||
htonf(net->right_flap);
|
||||
htonf(net->left_aileron);
|
||||
htonf(net->right_aileron);
|
||||
htonf(net->rudder);
|
||||
htonf(net->nose_wheel);
|
||||
htonf(net->speedbrake);
|
||||
htonf(net->spoilers);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputFG::Print(void)
|
||||
{
|
||||
int length = sizeof(fgSockBuf);
|
||||
|
||||
if (socket == 0) return;
|
||||
if (!socket->GetConnectStatus()) return;
|
||||
|
||||
SocketDataFill(&fgSockBuf);
|
||||
socket->Send((char *)&fgSockBuf, length);
|
||||
}
|
||||
}
|
||||
91
src/FDM/JSBSim/input_output/FGOutputFG.h
Normal file
91
src/FDM/JSBSim/input_output/FGOutputFG.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGOutputFG.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
11/09/07 HDW Added FlightGear Socket Interface
|
||||
09/10/11 BC Moved the FlightGear socket in a separate class
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGOUTPUTFG_H
|
||||
#define FGOUTPUTFG_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGOutputSocket.h"
|
||||
#include "input_output/net_fdm.hxx"
|
||||
#include "input_output/FGfdmSocket.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Implements the output to a FlightGear socket.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGOutputFG : public FGOutputSocket
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FGOutputFG(FGFDMExec* fdmex);
|
||||
|
||||
void Print(void) override;
|
||||
|
||||
/** Evaluate the output directives from an XML file.
|
||||
@param element XML Element that is pointing to the output directives
|
||||
*/
|
||||
bool Load(Element*) override;
|
||||
|
||||
protected:
|
||||
void PrintHeaders(void) override {};
|
||||
|
||||
private:
|
||||
|
||||
struct {
|
||||
bool useSimTime;
|
||||
double timeFactor;
|
||||
} outputOptions;
|
||||
|
||||
FGNetFDM fgSockBuf;
|
||||
void SocketDataFill(FGNetFDM* net);
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
105
src/FDM/JSBSim/input_output/FGOutputFile.cpp
Normal file
105
src/FDM/JSBSim/input_output/FGOutputFile.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGOutputFile.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
Purpose: Manage output of sim parameters to a file
|
||||
Called by: FGOutput
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create output routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "FGOutputFile.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGOutputFile::FGOutputFile(FGFDMExec* fdmex) :
|
||||
FGOutputType(fdmex),
|
||||
runID_postfix(-1)
|
||||
{
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputFile::InitModel(void)
|
||||
{
|
||||
if (FGOutputType::InitModel()) {
|
||||
if (Filename.isNull()) {
|
||||
Filename = SGPath(Name);
|
||||
runID_postfix = 0;
|
||||
}
|
||||
return OpenFile();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputFile::SetStartNewOutput(void)
|
||||
{
|
||||
if (runID_postfix >= 0) {
|
||||
ostringstream buf;
|
||||
string::size_type dot = Name.find_last_of('.');
|
||||
if (dot != string::npos) {
|
||||
buf << Name.substr(0, dot) << '_' << runID_postfix++ << Name.substr(dot);
|
||||
} else {
|
||||
buf << Name << '_' << runID_postfix++;
|
||||
}
|
||||
Filename = SGPath(buf.str());
|
||||
}
|
||||
|
||||
CloseFile();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputFile::Load(Element* el)
|
||||
{
|
||||
if (!FGOutputType::Load(el))
|
||||
return false;
|
||||
|
||||
SetOutputName(el->GetAttributeValue("name"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
125
src/FDM/JSBSim/input_output/FGOutputFile.h
Normal file
125
src/FDM/JSBSim/input_output/FGOutputFile.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGOutputFile.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGOUTPUTFILE_H
|
||||
#define FGOUTPUTFILE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGOutputType.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Abstract class that provide functions that are generic to all the outputs
|
||||
that are directed to a file. A new class derived from FGOutputFile should
|
||||
be created for each file format that JSBSim is able to output.
|
||||
|
||||
This class provides all the machinery necessary to manage the file naming
|
||||
including the sequence in which the file should be opened then closed. The
|
||||
logic of SetStartNewOutput() is also managed in this class. Derived class
|
||||
should normally not need to reimplement this method. In most cases, derived
|
||||
classes only need to implement the methods OpenFile(), CloseFile() and
|
||||
Print().
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGOutputFile : public FGOutputType
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FGOutputFile(FGFDMExec* fdmex);
|
||||
|
||||
/// Destructor : closes the file.
|
||||
~FGOutputFile() override { CloseFile(); }
|
||||
|
||||
/** Init the output directives from an XML file.
|
||||
@param element XML Element that is pointing to the output directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/** Initializes the instance. This method basically opens the file to which
|
||||
outputs will be directed.
|
||||
@result true if the execution succeeded.
|
||||
*/
|
||||
bool InitModel(void) override;
|
||||
/** Reset the output prior to a restart of the simulation. This method should
|
||||
be called when the simulation is restarted with, for example, new initial
|
||||
conditions. The current file is closed and reopened with a new name. The
|
||||
new name is contructed from the base file name set by the class
|
||||
constructor or SetOutputName() and is appended with an underscore _ and
|
||||
an ID that is incremented at each call to this method.
|
||||
*/
|
||||
void SetStartNewOutput(void) override;
|
||||
/** Overwrites the name identifier under which the output will be logged.
|
||||
For this method to take effect, it must be called prior to
|
||||
FGFDMExec::RunIC(). If it is called after, it will not take effect before
|
||||
the next call to SetStartNewOutput().
|
||||
@param name new name */
|
||||
void SetOutputName(const std::string& fname) override {
|
||||
Name = (FDMExec->GetOutputPath()/fname).utf8Str();
|
||||
runID_postfix = -1;
|
||||
Filename = SGPath();
|
||||
}
|
||||
/** Generate the output. This is a pure method so it must be implemented by
|
||||
the classes that inherits from FGOutputFile.
|
||||
*/
|
||||
void Print(void) override = 0;
|
||||
|
||||
protected:
|
||||
SGPath Filename;
|
||||
|
||||
/// Opens the file
|
||||
virtual bool OpenFile(void) = 0;
|
||||
/// Closes the file
|
||||
virtual void CloseFile(void) {}
|
||||
|
||||
private:
|
||||
int runID_postfix;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
403
src/FDM/JSBSim/input_output/FGOutputSocket.cpp
Normal file
403
src/FDM/JSBSim/input_output/FGOutputSocket.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGOutputSocket.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
Purpose: Manage output of sim parameters to a socket
|
||||
Called by: FGOutput
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create output routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "FGOutputSocket.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "models/FGAerodynamics.h"
|
||||
#include "models/FGAccelerations.h"
|
||||
#include "models/FGAircraft.h"
|
||||
#include "models/FGAtmosphere.h"
|
||||
#include "models/FGAuxiliary.h"
|
||||
#include "models/FGPropulsion.h"
|
||||
#include "models/FGMassBalance.h"
|
||||
#include "models/FGPropagate.h"
|
||||
#include "models/FGGroundReactions.h"
|
||||
#include "models/FGFCS.h"
|
||||
#include "models/atmosphere/FGWinds.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
#include "math/FGPropertyValue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGOutputSocket::FGOutputSocket(FGFDMExec* fdmex) :
|
||||
FGOutputType(fdmex),
|
||||
socket(0)
|
||||
{
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGOutputSocket::~FGOutputSocket()
|
||||
{
|
||||
delete socket;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputSocket::SetOutputName(const string& fname)
|
||||
{
|
||||
// tokenize the output name
|
||||
size_t dot_pos = fname.find(':', 0);
|
||||
size_t slash_pos = fname.find('/', 0);
|
||||
|
||||
string name = fname.substr(0, dot_pos);
|
||||
|
||||
string proto = "TCP";
|
||||
if(dot_pos + 1 < slash_pos)
|
||||
proto = fname.substr(dot_pos + 1, slash_pos - dot_pos - 1);
|
||||
|
||||
string port = "1138";
|
||||
if(slash_pos < string::npos)
|
||||
port = fname.substr(slash_pos + 1, string::npos);
|
||||
|
||||
// set the model name
|
||||
Name = name + ":" + port + "/" + proto;
|
||||
|
||||
// set the socket params
|
||||
SockName = name;
|
||||
|
||||
SockPort = atoi(port.c_str());
|
||||
|
||||
if (to_upper(proto) == "UDP")
|
||||
SockProtocol = FGfdmSocket::ptUDP;
|
||||
else // Default to TCP
|
||||
SockProtocol = FGfdmSocket::ptTCP;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputSocket::Load(Element* el)
|
||||
{
|
||||
if (!FGOutputType::Load(el))
|
||||
return false;
|
||||
|
||||
SetOutputName(el->GetAttributeValue("name") + ":" +
|
||||
el->GetAttributeValue("protocol") + "/" +
|
||||
el->GetAttributeValue("port"));
|
||||
|
||||
// Check if output precision for doubles has been specified, default to 7 if not
|
||||
if(el->HasAttribute("precision"))
|
||||
precision = (int)el->GetAttributeValueAsNumber("precision");
|
||||
else
|
||||
precision = 7;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputSocket::InitModel(void)
|
||||
{
|
||||
if (FGOutputType::InitModel()) {
|
||||
delete socket;
|
||||
socket = new FGfdmSocket(SockName, SockPort, SockProtocol, precision);
|
||||
|
||||
if (socket == 0) return false;
|
||||
if (!socket->GetConnectStatus()) return false;
|
||||
|
||||
PrintHeaders();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputSocket::PrintHeaders(void)
|
||||
{
|
||||
string scratch;
|
||||
|
||||
socket->Clear();
|
||||
socket->Clear("<LABELS>");
|
||||
socket->Append("Time");
|
||||
|
||||
if (SubSystems & ssAerosurfaces) {
|
||||
socket->Append("Aileron Command");
|
||||
socket->Append("Elevator Command");
|
||||
socket->Append("Rudder Command");
|
||||
socket->Append("Flap Command");
|
||||
socket->Append("Left Aileron Position");
|
||||
socket->Append("Right Aileron Position");
|
||||
socket->Append("Elevator Position");
|
||||
socket->Append("Rudder Position");
|
||||
socket->Append("Flap Position");
|
||||
}
|
||||
|
||||
if (SubSystems & ssRates) {
|
||||
socket->Append("P");
|
||||
socket->Append("Q");
|
||||
socket->Append("R");
|
||||
socket->Append("PDot");
|
||||
socket->Append("QDot");
|
||||
socket->Append("RDot");
|
||||
}
|
||||
|
||||
if (SubSystems & ssVelocities) {
|
||||
socket->Append("QBar");
|
||||
socket->Append("Vtotal");
|
||||
socket->Append("UBody");
|
||||
socket->Append("VBody");
|
||||
socket->Append("WBody");
|
||||
socket->Append("UAero");
|
||||
socket->Append("VAero");
|
||||
socket->Append("WAero");
|
||||
socket->Append("Vn");
|
||||
socket->Append("Ve");
|
||||
socket->Append("Vd");
|
||||
}
|
||||
|
||||
if (SubSystems & ssForces) {
|
||||
socket->Append("F_Drag");
|
||||
socket->Append("F_Side");
|
||||
socket->Append("F_Lift");
|
||||
socket->Append("LoD");
|
||||
socket->Append("Fx");
|
||||
socket->Append("Fy");
|
||||
socket->Append("Fz");
|
||||
}
|
||||
|
||||
if (SubSystems & ssMoments) {
|
||||
socket->Append("L");
|
||||
socket->Append("M");
|
||||
socket->Append("N");
|
||||
}
|
||||
|
||||
if (SubSystems & ssAtmosphere) {
|
||||
socket->Append("Rho");
|
||||
socket->Append("SL pressure");
|
||||
socket->Append("Ambient pressure");
|
||||
socket->Append("Turbulence Magnitude");
|
||||
socket->Append("Turbulence Direction");
|
||||
socket->Append("NWind");
|
||||
socket->Append("EWind");
|
||||
socket->Append("DWind");
|
||||
}
|
||||
|
||||
if (SubSystems & ssMassProps) {
|
||||
socket->Append("Ixx");
|
||||
socket->Append("Ixy");
|
||||
socket->Append("Ixz");
|
||||
socket->Append("Iyx");
|
||||
socket->Append("Iyy");
|
||||
socket->Append("Iyz");
|
||||
socket->Append("Izx");
|
||||
socket->Append("Izy");
|
||||
socket->Append("Izz");
|
||||
socket->Append("Mass");
|
||||
socket->Append("Xcg");
|
||||
socket->Append("Ycg");
|
||||
socket->Append("Zcg");
|
||||
}
|
||||
|
||||
if (SubSystems & ssPropagate) {
|
||||
socket->Append("Altitude");
|
||||
socket->Append("Phi (deg)");
|
||||
socket->Append("Tht (deg)");
|
||||
socket->Append("Psi (deg)");
|
||||
socket->Append("Alpha (deg)");
|
||||
socket->Append("Beta (deg)");
|
||||
socket->Append("Latitude (deg)");
|
||||
socket->Append("Longitude (deg)");
|
||||
}
|
||||
|
||||
if (SubSystems & ssAeroFunctions) {
|
||||
scratch = Aerodynamics->GetAeroFunctionStrings(",");
|
||||
if (scratch.length() != 0) socket->Append(scratch);
|
||||
}
|
||||
|
||||
if (SubSystems & ssFCS) {
|
||||
scratch = FCS->GetComponentStrings(",");
|
||||
if (scratch.length() != 0) socket->Append(scratch);
|
||||
}
|
||||
|
||||
if (SubSystems & ssGroundReactions)
|
||||
socket->Append(GroundReactions->GetGroundReactionStrings(","));
|
||||
|
||||
if (SubSystems & ssPropulsion && Propulsion->GetNumEngines() > 0)
|
||||
socket->Append(Propulsion->GetPropulsionStrings(","));
|
||||
|
||||
for (unsigned int i=0;i<OutputParameters.size();++i) {
|
||||
if (!OutputCaptions[i].empty())
|
||||
socket->Append(OutputCaptions[i]);
|
||||
else
|
||||
socket->Append(OutputParameters[i]->GetPrintableName());
|
||||
}
|
||||
|
||||
socket->Send();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputSocket::Print(void)
|
||||
{
|
||||
string asciiData, scratch;
|
||||
|
||||
if (socket == 0) return;
|
||||
if (!socket->GetConnectStatus()) return;
|
||||
|
||||
socket->Clear();
|
||||
socket->Append(FDMExec->GetSimTime());
|
||||
|
||||
if (SubSystems & ssAerosurfaces) {
|
||||
socket->Append(FCS->GetDaCmd());
|
||||
socket->Append(FCS->GetDeCmd());
|
||||
socket->Append(FCS->GetDrCmd());
|
||||
socket->Append(FCS->GetDfCmd());
|
||||
socket->Append(FCS->GetDaLPos());
|
||||
socket->Append(FCS->GetDaRPos());
|
||||
socket->Append(FCS->GetDePos());
|
||||
socket->Append(FCS->GetDrPos());
|
||||
socket->Append(FCS->GetDfPos());
|
||||
}
|
||||
if (SubSystems & ssRates) {
|
||||
socket->Append(radtodeg*Propagate->GetPQR(eP));
|
||||
socket->Append(radtodeg*Propagate->GetPQR(eQ));
|
||||
socket->Append(radtodeg*Propagate->GetPQR(eR));
|
||||
socket->Append(radtodeg*Accelerations->GetPQRdot(eP));
|
||||
socket->Append(radtodeg*Accelerations->GetPQRdot(eQ));
|
||||
socket->Append(radtodeg*Accelerations->GetPQRdot(eR));
|
||||
}
|
||||
if (SubSystems & ssVelocities) {
|
||||
socket->Append(Auxiliary->Getqbar());
|
||||
socket->Append(Auxiliary->GetVt());
|
||||
socket->Append(Propagate->GetUVW(eU));
|
||||
socket->Append(Propagate->GetUVW(eV));
|
||||
socket->Append(Propagate->GetUVW(eW));
|
||||
socket->Append(Auxiliary->GetAeroUVW(eU));
|
||||
socket->Append(Auxiliary->GetAeroUVW(eV));
|
||||
socket->Append(Auxiliary->GetAeroUVW(eW));
|
||||
socket->Append(Propagate->GetVel(eNorth));
|
||||
socket->Append(Propagate->GetVel(eEast));
|
||||
socket->Append(Propagate->GetVel(eDown));
|
||||
}
|
||||
if (SubSystems & ssForces) {
|
||||
socket->Append(Aerodynamics->GetvFw()(eDrag));
|
||||
socket->Append(Aerodynamics->GetvFw()(eSide));
|
||||
socket->Append(Aerodynamics->GetvFw()(eLift));
|
||||
socket->Append(Aerodynamics->GetLoD());
|
||||
socket->Append(Aircraft->GetForces(eX));
|
||||
socket->Append(Aircraft->GetForces(eY));
|
||||
socket->Append(Aircraft->GetForces(eZ));
|
||||
}
|
||||
if (SubSystems & ssMoments) {
|
||||
socket->Append(Aircraft->GetMoments(eL));
|
||||
socket->Append(Aircraft->GetMoments(eM));
|
||||
socket->Append(Aircraft->GetMoments(eN));
|
||||
}
|
||||
if (SubSystems & ssAtmosphere) {
|
||||
socket->Append(Atmosphere->GetDensity());
|
||||
socket->Append(Atmosphere->GetPressureSL());
|
||||
socket->Append(Atmosphere->GetPressure());
|
||||
socket->Append(Winds->GetTurbMagnitude());
|
||||
socket->Append(Winds->GetTurbDirection());
|
||||
socket->Append(Winds->GetTotalWindNED().Dump(","));
|
||||
}
|
||||
if (SubSystems & ssMassProps) {
|
||||
socket->Append(MassBalance->GetJ()(1,1));
|
||||
socket->Append(MassBalance->GetJ()(1,2));
|
||||
socket->Append(MassBalance->GetJ()(1,3));
|
||||
socket->Append(MassBalance->GetJ()(2,1));
|
||||
socket->Append(MassBalance->GetJ()(2,2));
|
||||
socket->Append(MassBalance->GetJ()(2,3));
|
||||
socket->Append(MassBalance->GetJ()(3,1));
|
||||
socket->Append(MassBalance->GetJ()(3,2));
|
||||
socket->Append(MassBalance->GetJ()(3,3));
|
||||
socket->Append(MassBalance->GetMass());
|
||||
socket->Append(MassBalance->GetXYZcg()(eX));
|
||||
socket->Append(MassBalance->GetXYZcg()(eY));
|
||||
socket->Append(MassBalance->GetXYZcg()(eZ));
|
||||
}
|
||||
if (SubSystems & ssPropagate) {
|
||||
socket->Append(Propagate->GetAltitudeASL());
|
||||
socket->Append(radtodeg*Propagate->GetEuler(ePhi));
|
||||
socket->Append(radtodeg*Propagate->GetEuler(eTht));
|
||||
socket->Append(radtodeg*Propagate->GetEuler(ePsi));
|
||||
socket->Append(Auxiliary->Getalpha(inDegrees));
|
||||
socket->Append(Auxiliary->Getbeta(inDegrees));
|
||||
socket->Append(Propagate->GetLocation().GetLatitudeDeg());
|
||||
socket->Append(Propagate->GetLocation().GetLongitudeDeg());
|
||||
}
|
||||
if (SubSystems & ssAeroFunctions) {
|
||||
scratch = Aerodynamics->GetAeroFunctionValues(",");
|
||||
if (scratch.length() != 0) socket->Append(scratch);
|
||||
}
|
||||
if (SubSystems & ssFCS) {
|
||||
scratch = FCS->GetComponentValues(",");
|
||||
if (scratch.length() != 0) socket->Append(scratch);
|
||||
}
|
||||
if (SubSystems & ssGroundReactions) {
|
||||
socket->Append(GroundReactions->GetGroundReactionValues(","));
|
||||
}
|
||||
if (SubSystems & ssPropulsion && Propulsion->GetNumEngines() > 0) {
|
||||
socket->Append(Propulsion->GetPropulsionValues(","));
|
||||
}
|
||||
|
||||
for (unsigned int i=0;i<OutputParameters.size();++i) {
|
||||
socket->Append(OutputParameters[i]->GetValue());
|
||||
}
|
||||
|
||||
socket->Send();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputSocket::SocketStatusOutput(const string& out_str)
|
||||
{
|
||||
string asciiData;
|
||||
|
||||
if (socket == 0) return;
|
||||
|
||||
socket->Clear();
|
||||
asciiData = string("<STATUS>") + out_str;
|
||||
socket->Append(asciiData.c_str());
|
||||
socket->Send();
|
||||
}
|
||||
}
|
||||
115
src/FDM/JSBSim/input_output/FGOutputSocket.h
Normal file
115
src/FDM/JSBSim/input_output/FGOutputSocket.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGOutputSocket.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGOUTPUTSOCKET_H
|
||||
#define FGOUTPUTSOCKET_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGOutputType.h"
|
||||
#include "input_output/net_fdm.hxx"
|
||||
#include "input_output/FGfdmSocket.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Implements the output to a socket. This class outputs data to a socket
|
||||
according to the JSBSim format. It can be inherited as a generic class that
|
||||
provides services for socket outputs. For instance FGOutputFG inherits
|
||||
FGOutputSocket for the socket management but outputs data with a format
|
||||
different than FGOutputSocket.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGOutputSocket : public FGOutputType
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
FGOutputSocket(FGFDMExec* fdmex);
|
||||
|
||||
/** Destructor. */
|
||||
~FGOutputSocket() override;
|
||||
|
||||
/** Overwrites the name identifier under which the output will be logged.
|
||||
This method is taken into account if it is called before
|
||||
FGFDMExec::RunIC() otherwise it is ignored until the next call to
|
||||
SetStartNewOutput().
|
||||
@param name new name in the form "hostname:port/proto"
|
||||
hostname could be an ip, port a numerical value and
|
||||
proto should be UDP or TCP (the default if omitted)
|
||||
*/
|
||||
void SetOutputName(const std::string& name) override;
|
||||
|
||||
/** Init the output directives from an XML file.
|
||||
@param element XML Element that is pointing to the output directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/** Initializes the instance. This method basically opens the socket to which
|
||||
outputs will be directed.
|
||||
@result true if the execution succeeded.
|
||||
*/
|
||||
bool InitModel(void) override;
|
||||
/// Generates the output.
|
||||
void Print(void) override;
|
||||
|
||||
/** Outputs a status thru the socket. This method issues a message prepended
|
||||
by the string "<STATUS>" to the socket.
|
||||
@param out_str status message
|
||||
*/
|
||||
void SocketStatusOutput(const std::string& out_str);
|
||||
|
||||
protected:
|
||||
virtual void PrintHeaders(void);
|
||||
|
||||
std::string SockName;
|
||||
unsigned int SockPort;
|
||||
FGfdmSocket::ProtocolType SockProtocol;
|
||||
FGfdmSocket* socket;
|
||||
int precision;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
394
src/FDM/JSBSim/input_output/FGOutputTextFile.cpp
Normal file
394
src/FDM/JSBSim/input_output/FGOutputTextFile.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGOutputTextFile.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/17/11
|
||||
Purpose: Manage output of sim parameters to a text file
|
||||
Called by: FGOutput
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create output routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/17/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "FGOutputTextFile.h"
|
||||
#include "models/FGAerodynamics.h"
|
||||
#include "models/FGAccelerations.h"
|
||||
#include "models/FGAtmosphere.h"
|
||||
#include "models/FGAuxiliary.h"
|
||||
#include "models/FGPropulsion.h"
|
||||
#include "models/FGMassBalance.h"
|
||||
#include "models/FGExternalReactions.h"
|
||||
#include "models/FGBuoyantForces.h"
|
||||
#include "models/FGFCS.h"
|
||||
#include "models/atmosphere/FGWinds.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
bool FGOutputTextFile::Load(Element* el)
|
||||
{
|
||||
if(!FGOutputFile::Load(el))
|
||||
return false;
|
||||
|
||||
string type = el->GetAttributeValue("type");
|
||||
string delim;
|
||||
if (type == "TABULAR") {
|
||||
delim = "\t";
|
||||
} else {
|
||||
delim = ",";
|
||||
}
|
||||
|
||||
SetDelimiter(delim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputTextFile::OpenFile(void)
|
||||
{
|
||||
datafile.clear();
|
||||
datafile.open(Filename);
|
||||
if (!datafile) {
|
||||
cerr << endl << fgred << highint << "ERROR: unable to open the file "
|
||||
<< reset << Filename.c_str() << endl
|
||||
<< fgred << highint << " => Output to this file is disabled."
|
||||
<< reset << endl << endl;
|
||||
Disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
string scratch = "";
|
||||
streambuf* buffer = datafile.rdbuf();
|
||||
ostream outstream(buffer);
|
||||
|
||||
outstream.precision(10);
|
||||
|
||||
outstream << "Time";
|
||||
if (SubSystems & ssSimulation) {
|
||||
// Nothing here, yet
|
||||
}
|
||||
if (SubSystems & ssAerosurfaces) {
|
||||
outstream << delimeter;
|
||||
outstream << "Aileron Command (norm)" + delimeter;
|
||||
outstream << "Elevator Command (norm)" + delimeter;
|
||||
outstream << "Rudder Command (norm)" + delimeter;
|
||||
outstream << "Flap Command (norm)" + delimeter;
|
||||
outstream << "Left Aileron Position (deg)" + delimeter;
|
||||
outstream << "Right Aileron Position (deg)" + delimeter;
|
||||
outstream << "Elevator Position (deg)" + delimeter;
|
||||
outstream << "Rudder Position (deg)" + delimeter;
|
||||
outstream << "Flap Position (deg)";
|
||||
}
|
||||
if (SubSystems & ssRates) {
|
||||
outstream << delimeter;
|
||||
outstream << "P (deg/s)" + delimeter + "Q (deg/s)" + delimeter + "R (deg/s)" + delimeter;
|
||||
outstream << "P dot (deg/s^2)" + delimeter + "Q dot (deg/s^2)" + delimeter + "R dot (deg/s^2)" + delimeter;
|
||||
outstream << "P_{inertial} (deg/s)" + delimeter + "Q_{inertial} (deg/s)" + delimeter + "R_{inertial} (deg/s)";
|
||||
}
|
||||
if (SubSystems & ssVelocities) {
|
||||
outstream << delimeter;
|
||||
outstream << "q bar (psf)" + delimeter;
|
||||
outstream << "Reynolds Number" + delimeter;
|
||||
outstream << "V_{Total} (ft/s)" + delimeter;
|
||||
outstream << "V_{Inertial} (ft/s)" + delimeter;
|
||||
outstream << "UBody" + delimeter + "VBody" + delimeter + "WBody" + delimeter;
|
||||
outstream << "UdotBody" + delimeter + "VdotBody" + delimeter + "WdotBody" + delimeter;
|
||||
outstream << "UdotBody_i" + delimeter + "VdotBody_i" + delimeter + "WdotBody_i" + delimeter;
|
||||
outstream << "BodyAccel_X" + delimeter + "BodyAccel_Y" + delimeter + "BodyAccel_Z" + delimeter;
|
||||
outstream << "Aero V_{X Body} (ft/s)" + delimeter + "Aero V_{Y Body} (ft/s)" + delimeter + "Aero V_{Z Body} (ft/s)" + delimeter;
|
||||
outstream << "V_{X_{inertial}} (ft/s)" + delimeter + "V_{Y_{inertial}} (ft/s)" + delimeter + "V_{Z_{inertial}} (ft/s)" + delimeter;
|
||||
outstream << "V_{X_{ecef}} (ft/s)" + delimeter + "V_{Y_{ecef}} (ft/s)" + delimeter + "V_{Z_{ecef}} (ft/s)" + delimeter;
|
||||
outstream << "V_{North} (ft/s)" + delimeter + "V_{East} (ft/s)" + delimeter + "V_{Down} (ft/s)";
|
||||
}
|
||||
if (SubSystems & ssForces) {
|
||||
outstream << delimeter;
|
||||
outstream << "F_{Drag} (lbs)" + delimeter + "F_{Side} (lbs)" + delimeter + "F_{Lift} (lbs)" + delimeter;
|
||||
outstream << "L/D" + delimeter;
|
||||
outstream << "F_{Aero x} (lbs)" + delimeter + "F_{Aero y} (lbs)" + delimeter + "F_{Aero z} (lbs)" + delimeter;
|
||||
outstream << "F_{Prop x} (lbs)" + delimeter + "F_{Prop y} (lbs)" + delimeter + "F_{Prop z} (lbs)" + delimeter;
|
||||
outstream << "F_{Gear x} (lbs)" + delimeter + "F_{Gear y} (lbs)" + delimeter + "F_{Gear z} (lbs)" + delimeter;
|
||||
outstream << "F_{Ext x} (lbs)" + delimeter + "F_{Ext y} (lbs)" + delimeter + "F_{Ext z} (lbs)" + delimeter;
|
||||
outstream << "F_{Buoyant x} (lbs)" + delimeter + "F_{Buoyant y} (lbs)" + delimeter + "F_{Buoyant z} (lbs)" + delimeter;
|
||||
outstream << "F_{Weight x} (lbs)" + delimeter + "F_{Weight y} (lbs)" + delimeter + "F_{Weight z} (lbs)" + delimeter;
|
||||
outstream << "F_{Total x} (lbs)" + delimeter + "F_{Total y} (lbs)" + delimeter + "F_{Total z} (lbs)";
|
||||
}
|
||||
if (SubSystems & ssMoments) {
|
||||
outstream << delimeter;
|
||||
outstream << "L_{Aero} (ft-lbs)" + delimeter + "M_{Aero} (ft-lbs)" + delimeter + "N_{Aero} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{Aero MRC} (ft-lbs)" + delimeter + "M_{Aero MRC} (ft-lbs)" + delimeter + "N_{Aero MRC} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{Prop} (ft-lbs)" + delimeter + "M_{Prop} (ft-lbs)" + delimeter + "N_{Prop} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{Gear} (ft-lbs)" + delimeter + "M_{Gear} (ft-lbs)" + delimeter + "N_{Gear} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{ext} (ft-lbs)" + delimeter + "M_{ext} (ft-lbs)" + delimeter + "N_{ext} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{Buoyant} (ft-lbs)" + delimeter + "M_{Buoyant} (ft-lbs)" + delimeter + "N_{Buoyant} (ft-lbs)" + delimeter;
|
||||
outstream << "L_{Total} (ft-lbs)" + delimeter + "M_{Total} (ft-lbs)" + delimeter + "N_{Total} (ft-lbs)";
|
||||
}
|
||||
if (SubSystems & ssAtmosphere) {
|
||||
outstream << delimeter;
|
||||
outstream << "Rho (slugs/ft^3)" + delimeter;
|
||||
outstream << "Absolute Viscosity" + delimeter;
|
||||
outstream << "Kinematic Viscosity" + delimeter;
|
||||
outstream << "Temperature (R)" + delimeter;
|
||||
outstream << "P_{SL} (psf)" + delimeter;
|
||||
outstream << "P_{Ambient} (psf)" + delimeter;
|
||||
outstream << "Turbulence Magnitude (ft/sec)" + delimeter;
|
||||
outstream << "Turbulence X Direction (deg)" + delimeter;
|
||||
outstream << "Wind V_{North} (ft/s)" + delimeter + "Wind V_{East} (ft/s)" + delimeter + "Wind V_{Down} (ft/s)" + delimeter;
|
||||
outstream << "Roll Turbulence (deg/sec)" + delimeter + "Pitch Turbulence (deg/sec)" + delimeter + "Yaw Turbulence (deg/sec)";
|
||||
}
|
||||
if (SubSystems & ssMassProps) {
|
||||
outstream << delimeter;
|
||||
outstream << "I_{xx}" + delimeter;
|
||||
outstream << "I_{xy}" + delimeter;
|
||||
outstream << "I_{xz}" + delimeter;
|
||||
outstream << "I_{yx}" + delimeter;
|
||||
outstream << "I_{yy}" + delimeter;
|
||||
outstream << "I_{yz}" + delimeter;
|
||||
outstream << "I_{zx}" + delimeter;
|
||||
outstream << "I_{zy}" + delimeter;
|
||||
outstream << "I_{zz}" + delimeter;
|
||||
outstream << "Mass" + delimeter;
|
||||
outstream << "Weight" + delimeter;
|
||||
outstream << "X_{cg}" + delimeter + "Y_{cg}" + delimeter + "Z_{cg}";
|
||||
}
|
||||
if (SubSystems & ssPropagate) {
|
||||
outstream << delimeter;
|
||||
outstream << "Altitude ASL (ft)" + delimeter;
|
||||
outstream << "Altitude AGL (ft)" + delimeter;
|
||||
outstream << "Phi (deg)" + delimeter + "Theta (deg)" + delimeter + "Psi (deg)" + delimeter;
|
||||
outstream << "Q(1)_{LOCAL}" + delimeter + "Q(2)_{LOCAL}" + delimeter + "Q(3)_{LOCAL}" + delimeter + "Q(4)_{LOCAL}" + delimeter;
|
||||
outstream << "Q(1)_{ECEF}" + delimeter + "Q(2)_{ECEF}" + delimeter + "Q(3)_{ECEF}" + delimeter + "Q(4)_{ECEF}" + delimeter;
|
||||
outstream << "Q(1)_{ECI}" + delimeter + "Q(2)_{ECI}" + delimeter + "Q(3)_{ECI}" + delimeter + "Q(4)_{ECI}" + delimeter;
|
||||
outstream << "Alpha (deg)" + delimeter;
|
||||
outstream << "Beta (deg)" + delimeter;
|
||||
outstream << "Latitude (deg)" + delimeter;
|
||||
outstream << "Latitude Geodetic (deg)" + delimeter;
|
||||
outstream << "Longitude (deg)" + delimeter;
|
||||
outstream << "X_{ECI} (ft)" + delimeter + "Y_{ECI} (ft)" + delimeter + "Z_{ECI} (ft)" + delimeter;
|
||||
outstream << "X_{ECEF} (ft)" + delimeter + "Y_{ECEF} (ft)" + delimeter + "Z_{ECEF} (ft)" + delimeter;
|
||||
outstream << "Earth Position Angle (deg)" + delimeter;
|
||||
outstream << "Distance AGL (ft)" + delimeter;
|
||||
outstream << "Terrain Elevation (ft)";
|
||||
}
|
||||
if (SubSystems & ssAeroFunctions) {
|
||||
scratch = Aerodynamics->GetAeroFunctionStrings(delimeter);
|
||||
if (scratch.length() != 0) outstream << delimeter << scratch;
|
||||
}
|
||||
if (SubSystems & ssFCS) {
|
||||
scratch = FCS->GetComponentStrings(delimeter);
|
||||
if (scratch.length() != 0) outstream << delimeter << scratch;
|
||||
}
|
||||
if (SubSystems & ssGroundReactions) {
|
||||
outstream << delimeter;
|
||||
outstream << GroundReactions->GetGroundReactionStrings(delimeter);
|
||||
}
|
||||
if (SubSystems & ssPropulsion && Propulsion->GetNumEngines() > 0) {
|
||||
outstream << delimeter;
|
||||
outstream << Propulsion->GetPropulsionStrings(delimeter);
|
||||
}
|
||||
|
||||
for (unsigned int i=0;i<OutputParameters.size();++i) {
|
||||
if (!OutputCaptions[i].empty())
|
||||
outstream << delimeter << OutputCaptions[i];
|
||||
else
|
||||
outstream << delimeter << OutputParameters[i]->GetFullyQualifiedName();
|
||||
}
|
||||
|
||||
if (PreFunctions.size() > 0) {
|
||||
for (unsigned int i=0;i<PreFunctions.size();i++) {
|
||||
outstream << delimeter << PreFunctions[i]->GetName();
|
||||
}
|
||||
}
|
||||
|
||||
outstream << endl;
|
||||
outstream.flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputTextFile::Print(void)
|
||||
{
|
||||
streambuf* buffer;
|
||||
string scratch = Filename.utf8Str();
|
||||
|
||||
if (to_upper(scratch) == "COUT") {
|
||||
buffer = cout.rdbuf();
|
||||
} else {
|
||||
buffer = datafile.rdbuf();
|
||||
}
|
||||
|
||||
ostream outstream(buffer);
|
||||
|
||||
outstream.precision(10);
|
||||
|
||||
outstream << FDMExec->GetSimTime();
|
||||
if (SubSystems & ssSimulation) {
|
||||
}
|
||||
if (SubSystems & ssAerosurfaces) {
|
||||
outstream << delimeter;
|
||||
outstream << FCS->GetDaCmd() << delimeter;
|
||||
outstream << FCS->GetDeCmd() << delimeter;
|
||||
outstream << FCS->GetDrCmd() << delimeter;
|
||||
outstream << FCS->GetDfCmd() << delimeter;
|
||||
outstream << FCS->GetDaLPos(ofDeg) << delimeter;
|
||||
outstream << FCS->GetDaRPos(ofDeg) << delimeter;
|
||||
outstream << FCS->GetDePos(ofDeg) << delimeter;
|
||||
outstream << FCS->GetDrPos(ofDeg) << delimeter;
|
||||
outstream << FCS->GetDfPos(ofDeg);
|
||||
}
|
||||
if (SubSystems & ssRates) {
|
||||
outstream << delimeter;
|
||||
outstream << (radtodeg*Propagate->GetPQR()).Dump(delimeter) << delimeter;
|
||||
outstream << (radtodeg*Accelerations->GetPQRdot()).Dump(delimeter) << delimeter;
|
||||
outstream << (radtodeg*Propagate->GetPQRi()).Dump(delimeter);
|
||||
}
|
||||
if (SubSystems & ssVelocities) {
|
||||
outstream << delimeter;
|
||||
outstream << Auxiliary->Getqbar() << delimeter;
|
||||
outstream << Auxiliary->GetReynoldsNumber() << delimeter;
|
||||
outstream << setprecision(12) << Auxiliary->GetVt() << delimeter;
|
||||
outstream << Propagate->GetInertialVelocityMagnitude() << delimeter;
|
||||
outstream << setprecision(12) << Propagate->GetUVW().Dump(delimeter) << delimeter;
|
||||
outstream << setprecision(12) << Accelerations->GetUVWdot().Dump(delimeter) << delimeter;
|
||||
outstream << setprecision(12) << Accelerations->GetUVWidot().Dump(delimeter) << delimeter;
|
||||
outstream << setprecision(12) << Accelerations->GetBodyAccel().Dump(delimeter) << delimeter;
|
||||
outstream << Auxiliary->GetAeroUVW().Dump(delimeter) << delimeter;
|
||||
outstream << Propagate->GetInertialVelocity().Dump(delimeter) << delimeter;
|
||||
outstream << Propagate->GetECEFVelocity().Dump(delimeter) << delimeter;
|
||||
outstream << Propagate->GetVel().Dump(delimeter);
|
||||
outstream.precision(10);
|
||||
}
|
||||
if (SubSystems & ssForces) {
|
||||
outstream << delimeter;
|
||||
outstream << Aerodynamics->GetvFw().Dump(delimeter) << delimeter;
|
||||
outstream << Aerodynamics->GetLoD() << delimeter;
|
||||
outstream << Aerodynamics->GetForces().Dump(delimeter) << delimeter;
|
||||
outstream << Propulsion->GetForces().Dump(delimeter) << delimeter;
|
||||
outstream << Accelerations->GetGroundForces().Dump(delimeter) << delimeter;
|
||||
outstream << ExternalReactions->GetForces().Dump(delimeter) << delimeter;
|
||||
outstream << BuoyantForces->GetForces().Dump(delimeter) << delimeter;
|
||||
outstream << Accelerations->GetWeight().Dump(delimeter) << delimeter;
|
||||
outstream << Accelerations->GetForces().Dump(delimeter);
|
||||
}
|
||||
if (SubSystems & ssMoments) {
|
||||
outstream << delimeter;
|
||||
outstream << Aerodynamics->GetMoments().Dump(delimeter) << delimeter;
|
||||
outstream << Aerodynamics->GetMomentsMRC().Dump(delimeter) << delimeter;
|
||||
outstream << Propulsion->GetMoments().Dump(delimeter) << delimeter;
|
||||
outstream << Accelerations->GetGroundMoments().Dump(delimeter) << delimeter;
|
||||
outstream << ExternalReactions->GetMoments().Dump(delimeter) << delimeter;
|
||||
outstream << BuoyantForces->GetMoments().Dump(delimeter) << delimeter;
|
||||
outstream << Accelerations->GetMoments().Dump(delimeter);
|
||||
}
|
||||
if (SubSystems & ssAtmosphere) {
|
||||
outstream << delimeter;
|
||||
outstream << Atmosphere->GetDensity() << delimeter;
|
||||
outstream << Atmosphere->GetAbsoluteViscosity() << delimeter;
|
||||
outstream << Atmosphere->GetKinematicViscosity() << delimeter;
|
||||
outstream << Atmosphere->GetTemperature() << delimeter;
|
||||
outstream << Atmosphere->GetPressureSL() << delimeter;
|
||||
outstream << Atmosphere->GetPressure() << delimeter;
|
||||
outstream << Winds->GetTurbMagnitude() << delimeter;
|
||||
outstream << Winds->GetTurbDirection() << delimeter;
|
||||
outstream << Winds->GetTotalWindNED().Dump(delimeter) << delimeter;
|
||||
outstream << (Winds->GetTurbPQR()*radtodeg).Dump(delimeter);
|
||||
}
|
||||
if (SubSystems & ssMassProps) {
|
||||
outstream << delimeter;
|
||||
outstream << MassBalance->GetJ().Dump(delimeter) << delimeter;
|
||||
outstream << MassBalance->GetMass() << delimeter;
|
||||
outstream << MassBalance->GetWeight() << delimeter;
|
||||
outstream << MassBalance->GetXYZcg().Dump(delimeter);
|
||||
}
|
||||
if (SubSystems & ssPropagate) {
|
||||
outstream.precision(14);
|
||||
outstream << delimeter;
|
||||
outstream << Propagate->GetAltitudeASL() << delimeter;
|
||||
outstream << Propagate->GetDistanceAGL() << delimeter;
|
||||
outstream << (radtodeg*Propagate->GetEuler()).Dump(delimeter) << delimeter;
|
||||
outstream << Propagate->GetQuaternion().Dump(delimeter) << delimeter;
|
||||
FGQuaternion Qec = Propagate->GetQuaternionECEF();
|
||||
outstream << Qec.Dump(delimeter) << delimeter;
|
||||
outstream << Propagate->GetQuaternionECI().Dump(delimeter) << delimeter;
|
||||
outstream << Auxiliary->Getalpha(inDegrees) << delimeter;
|
||||
outstream << Auxiliary->Getbeta(inDegrees) << delimeter;
|
||||
outstream << Propagate->GetLatitudeDeg() << delimeter;
|
||||
outstream << Propagate->GetGeodLatitudeDeg() << delimeter;
|
||||
outstream << Propagate->GetLongitudeDeg() << delimeter;
|
||||
outstream.precision(18);
|
||||
outstream << ((FGColumnVector3)Propagate->GetInertialPosition()).Dump(delimeter) << delimeter;
|
||||
outstream << ((FGColumnVector3)Propagate->GetLocation()).Dump(delimeter) << delimeter;
|
||||
outstream.precision(14);
|
||||
outstream << Propagate->GetEarthPositionAngleDeg() << delimeter;
|
||||
outstream << Propagate->GetDistanceAGL() << delimeter;
|
||||
outstream << Propagate->GetTerrainElevation();
|
||||
outstream.precision(10);
|
||||
}
|
||||
if (SubSystems & ssAeroFunctions) {
|
||||
scratch = Aerodynamics->GetAeroFunctionValues(delimeter);
|
||||
if (scratch.length() != 0) outstream << delimeter << scratch;
|
||||
}
|
||||
if (SubSystems & ssFCS) {
|
||||
scratch = FCS->GetComponentValues(delimeter);
|
||||
if (scratch.length() != 0) outstream << delimeter << scratch;
|
||||
}
|
||||
if (SubSystems & ssGroundReactions) {
|
||||
outstream << delimeter;
|
||||
outstream << GroundReactions->GetGroundReactionValues(delimeter);
|
||||
}
|
||||
if (SubSystems & ssPropulsion && Propulsion->GetNumEngines() > 0) {
|
||||
outstream << delimeter;
|
||||
outstream << Propulsion->GetPropulsionValues(delimeter);
|
||||
}
|
||||
|
||||
outstream.precision(18);
|
||||
for (unsigned int i=0;i<OutputParameters.size();++i) {
|
||||
outstream << delimeter << OutputParameters[i]->GetValue();
|
||||
}
|
||||
for (unsigned int i=0;i<PreFunctions.size();i++) {
|
||||
outstream << delimeter << PreFunctions[i]->getDoubleValue();
|
||||
}
|
||||
outstream.precision(10);
|
||||
|
||||
outstream << endl;
|
||||
outstream.flush();
|
||||
}
|
||||
}
|
||||
94
src/FDM/JSBSim/input_output/FGOutputTextFile.h
Normal file
94
src/FDM/JSBSim/input_output/FGOutputTextFile.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGOutputTextFile.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/17/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/17/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGOUTPUTTEXTFILE_H
|
||||
#define FGOUTPUTTEXTFILE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "FGOutputFile.h"
|
||||
#include "simgear/io/iostreams/sgstream.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Implements the output to a human readable text file. This class uses the
|
||||
standard C++ library to open and close a file to which output values are
|
||||
comma-separated (CSV) or tabulated (TAB).
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGOutputTextFile : public FGOutputFile
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
FGOutputTextFile(FGFDMExec* fdmex) : FGOutputFile(fdmex), delimeter(",") {}
|
||||
|
||||
/** Set the delimiter.
|
||||
@param delim delimiter of the output values (most likely a comma or a
|
||||
tab)
|
||||
*/
|
||||
void SetDelimiter(const std::string& delim) { delimeter = delim; }
|
||||
|
||||
/** Init the output directives from an XML file.
|
||||
@param element XML Element that is pointing to the output directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/// Generates the output to the text file.
|
||||
void Print(void) override;
|
||||
|
||||
protected:
|
||||
std::string delimeter;
|
||||
sg_ofstream datafile;
|
||||
|
||||
bool OpenFile(void) override;
|
||||
void CloseFile(void) override { if (datafile.is_open()) datafile.close(); }
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
292
src/FDM/JSBSim/input_output/FGOutputType.cpp
Normal file
292
src/FDM/JSBSim/input_output/FGOutputType.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGOutputType.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
Purpose: Manage output of sim parameters to file or stdout
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This is the place where you create output routines to dump data for perusal
|
||||
later.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGOutputType.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
#include "math/FGTemplateFunc.h"
|
||||
#include "math/FGFunctionValue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGOutputType::FGOutputType(FGFDMExec* fdmex) :
|
||||
FGModel(fdmex),
|
||||
SubSystems(0),
|
||||
enabled(true)
|
||||
{
|
||||
Aerodynamics = FDMExec->GetAerodynamics();
|
||||
Auxiliary = FDMExec->GetAuxiliary();
|
||||
Aircraft = FDMExec->GetAircraft();
|
||||
Atmosphere = FDMExec->GetAtmosphere();
|
||||
Winds = FDMExec->GetWinds();
|
||||
Propulsion = FDMExec->GetPropulsion();
|
||||
MassBalance = FDMExec->GetMassBalance();
|
||||
Propagate = FDMExec->GetPropagate();
|
||||
Accelerations = FDMExec->GetAccelerations();
|
||||
FCS = FDMExec->GetFCS();
|
||||
GroundReactions = FDMExec->GetGroundReactions();
|
||||
ExternalReactions = FDMExec->GetExternalReactions();
|
||||
BuoyantForces = FDMExec->GetBuoyantForces();
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGOutputType::~FGOutputType()
|
||||
{
|
||||
for (auto param: OutputParameters)
|
||||
delete param;
|
||||
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputType::SetIdx(unsigned int idx)
|
||||
{
|
||||
string outputProp = CreateIndexedPropertyName("simulation/output", idx);
|
||||
|
||||
PropertyManager->Tie(outputProp + "/log_rate_hz", this, &FGOutputType::GetRateHz, &FGOutputType::SetRateHz);
|
||||
PropertyManager->Tie(outputProp + "/enabled", &enabled);
|
||||
OutputIdx = idx;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputType::Load(Element* element)
|
||||
{
|
||||
if (element->FindElementValue("simulation") == string("ON"))
|
||||
SubSystems += ssSimulation;
|
||||
if (element->FindElementValue("aerosurfaces") == string("ON"))
|
||||
SubSystems += ssAerosurfaces;
|
||||
if (element->FindElementValue("rates") == string("ON"))
|
||||
SubSystems += ssRates;
|
||||
if (element->FindElementValue("velocities") == string("ON"))
|
||||
SubSystems += ssVelocities;
|
||||
if (element->FindElementValue("forces") == string("ON"))
|
||||
SubSystems += ssForces;
|
||||
if (element->FindElementValue("moments") == string("ON"))
|
||||
SubSystems += ssMoments;
|
||||
if (element->FindElementValue("atmosphere") == string("ON"))
|
||||
SubSystems += ssAtmosphere;
|
||||
if (element->FindElementValue("massprops") == string("ON"))
|
||||
SubSystems += ssMassProps;
|
||||
if (element->FindElementValue("position") == string("ON"))
|
||||
SubSystems += ssPropagate;
|
||||
if (element->FindElementValue("coefficients") == string("ON") || element->FindElementValue("aerodynamics") == string("ON"))
|
||||
SubSystems += ssAeroFunctions;
|
||||
if (element->FindElementValue("ground_reactions") == string("ON"))
|
||||
SubSystems += ssGroundReactions;
|
||||
if (element->FindElementValue("fcs") == string("ON"))
|
||||
SubSystems += ssFCS;
|
||||
if (element->FindElementValue("propulsion") == string("ON"))
|
||||
SubSystems += ssPropulsion;
|
||||
|
||||
Element *property_element = element->FindElement("property");
|
||||
|
||||
while (property_element) {
|
||||
string property_str = property_element->GetDataLine();
|
||||
FGPropertyNode* node = PropertyManager->GetNode(property_str);
|
||||
if (!node) {
|
||||
cerr << property_element->ReadFrom()
|
||||
<< fgred << highint << endl << " No property by the name "
|
||||
<< property_str << " has been defined. This property will " << endl
|
||||
<< " not be logged. You should check your configuration file."
|
||||
<< reset << endl;
|
||||
} else {
|
||||
if (property_element->HasAttribute("apply")) {
|
||||
string function_str = property_element->GetAttributeValue("apply");
|
||||
FGTemplateFunc* f = FDMExec->GetTemplateFunc(function_str);
|
||||
if (f)
|
||||
OutputParameters.push_back(new FGFunctionValue(node, f));
|
||||
else {
|
||||
cerr << property_element->ReadFrom()
|
||||
<< fgred << highint << " No function by the name "
|
||||
<< function_str << " has been defined. This property will "
|
||||
<< "not be logged. You should check your configuration file."
|
||||
<< reset << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
OutputParameters.push_back(new FGPropertyValue(node));
|
||||
|
||||
if (property_element->HasAttribute("caption"))
|
||||
OutputCaptions.push_back(property_element->GetAttributeValue("caption"));
|
||||
else
|
||||
OutputCaptions.push_back("");
|
||||
}
|
||||
property_element = element->FindNextElement("property");
|
||||
}
|
||||
|
||||
double outRate = 1.0;
|
||||
if (element->HasAttribute("rate"))
|
||||
outRate = element->GetAttributeValueAsNumber("rate");
|
||||
|
||||
SetRateHz(outRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputType::InitModel(void)
|
||||
{
|
||||
bool ret = FGModel::InitModel();
|
||||
|
||||
Debug(2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGOutputType::Run(void)
|
||||
{
|
||||
if (FGModel::Run(false)) return true;
|
||||
if (!enabled) return true;
|
||||
|
||||
RunPreFunctions();
|
||||
Print();
|
||||
RunPostFunctions();
|
||||
|
||||
Debug(4);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputType::SetRateHz(double rtHz)
|
||||
{
|
||||
rtHz = rtHz>1000?1000:(rtHz<0?0:rtHz);
|
||||
if (rtHz > 0) {
|
||||
SetRate(0.5 + 1.0/(FDMExec->GetDeltaT()*rtHz));
|
||||
Enable();
|
||||
} else {
|
||||
SetRate(1);
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGOutputType::GetRateHz(void) const
|
||||
{
|
||||
return 1.0 / (rate * FDMExec->GetDeltaT());
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGOutputType::SetOutputProperties(vector<FGPropertyNode_ptr> & outputProperties)
|
||||
{
|
||||
for (auto prop: outputProperties)
|
||||
OutputParameters.push_back(new FGPropertyValue(prop));
|
||||
}
|
||||
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGOutputType::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
if (from == 2) {
|
||||
if (SubSystems & ssSimulation) cout << " Simulation parameters logged" << endl;
|
||||
if (SubSystems & ssAerosurfaces) cout << " Aerosurface parameters logged" << endl;
|
||||
if (SubSystems & ssRates) cout << " Rate parameters logged" << endl;
|
||||
if (SubSystems & ssVelocities) cout << " Velocity parameters logged" << endl;
|
||||
if (SubSystems & ssForces) cout << " Force parameters logged" << endl;
|
||||
if (SubSystems & ssMoments) cout << " Moments parameters logged" << endl;
|
||||
if (SubSystems & ssAtmosphere) cout << " Atmosphere parameters logged" << endl;
|
||||
if (SubSystems & ssMassProps) cout << " Mass parameters logged" << endl;
|
||||
if (SubSystems & ssAeroFunctions) cout << " Coefficient parameters logged" << endl;
|
||||
if (SubSystems & ssPropagate) cout << " Propagate parameters logged" << endl;
|
||||
if (SubSystems & ssGroundReactions) cout << " Ground parameters logged" << endl;
|
||||
if (SubSystems & ssFCS) cout << " FCS parameters logged" << endl;
|
||||
if (SubSystems & ssPropulsion) cout << " Propulsion parameters logged" << endl;
|
||||
if (!OutputParameters.empty()) cout << " Properties logged:" << endl;
|
||||
for (auto param: OutputParameters)
|
||||
cout << " - " << param->GetName() << endl;
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGOutputType" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGOutputType" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
216
src/FDM/JSBSim/input_output/FGOutputType.h
Normal file
216
src/FDM/JSBSim/input_output/FGOutputType.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGOutputType.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 09/10/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/10/11 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGOUTPUTTYPE_H
|
||||
#define FGOUTPUTTYPE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "models/FGModel.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGFDMExec;
|
||||
class Element;
|
||||
class FGAerodynamics;
|
||||
class FGAuxiliary;
|
||||
class FGAircraft;
|
||||
class FGAtmosphere;
|
||||
class FGWinds;
|
||||
class FGPropulsion;
|
||||
class FGMassBalance;
|
||||
class FGPropagate;
|
||||
class FGAccelerations;
|
||||
class FGFCS;
|
||||
class FGGroundReactions;
|
||||
class FGExternalReactions;
|
||||
class FGBuoyantForces;
|
||||
class FGPropertyValue;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Abstract class to provide functions generic to all the output directives.
|
||||
This class is used by the output manager FGOutput to manage a list of
|
||||
different output classes without needing to know the details of each one of
|
||||
them. It also provides the functions that are common to all the output
|
||||
classes.
|
||||
|
||||
The class inherits from FGModelFunctions so it is possible to define
|
||||
functions that execute before or after the output is generated. Such
|
||||
functions need to be tagged with a "pre" or "post" type attribute to denote
|
||||
the sequence in which they should be executed.
|
||||
|
||||
The class mimics some functionalities of FGModel (methods InitModel(),
|
||||
Run() and SetRate()). However it does not inherit from FGModel since it is
|
||||
conceptually different from the model paradigm.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGOutputType : public FGModel
|
||||
{
|
||||
public:
|
||||
/** Constructor (implement the FGModel interface).
|
||||
@param fdmex a pointer to the parent executive object
|
||||
*/
|
||||
FGOutputType(FGFDMExec* fdmex);
|
||||
|
||||
/// Destructor
|
||||
~FGOutputType() override;
|
||||
|
||||
/** Set the idx for this output instance
|
||||
@param idx ID of the output instance that is constructed
|
||||
*/
|
||||
void SetIdx(unsigned int idx);
|
||||
|
||||
/** Set the output rate for this output instances.
|
||||
@param rtHz new output rate in Hz */
|
||||
void SetRateHz(double rtHz);
|
||||
|
||||
/// Get the output rate in Hz for this output.
|
||||
double GetRateHz(void) const;
|
||||
|
||||
/** Set the activated subsystems for this output instance.
|
||||
@param subSystems bitfield that describes the activated subsystems
|
||||
@param outputProperties list of properties that should be output
|
||||
*/
|
||||
void SetSubSystems(int subSystems) { SubSystems = subSystems; }
|
||||
|
||||
/** Set the list of properties that should be output for this output instance.
|
||||
@param outputProperties list of properties that should be output
|
||||
*/
|
||||
void SetOutputProperties(std::vector<FGPropertyNode_ptr> & outputProperties);
|
||||
|
||||
/** Overwrites the name identifier under which the output will be logged.
|
||||
This method is taken into account if it is called before
|
||||
FGFDMExec::RunIC() otherwise it is ignored until the next call to
|
||||
SetStartNewOutput().
|
||||
@param name new name */
|
||||
virtual void SetOutputName(const std::string& name) { Name = name; }
|
||||
|
||||
/** Get the name identifier to which the output will be directed.
|
||||
@result the name identifier.*/
|
||||
virtual const std::string& GetOutputName(void) const { return Name; }
|
||||
|
||||
/** Init the output directives from an XML file (implement the FGModel interface).
|
||||
@param element XML Element that is pointing to the output directives
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/// Init the output model according to its configitation.
|
||||
bool InitModel(void) override;
|
||||
|
||||
/** Executes the output directives (implement the FGModel interface).
|
||||
This method checks that the current time step matches the output
|
||||
rate and calls the registered "pre" functions, the output
|
||||
generation and finally the "post" functions.
|
||||
@result false if no error.
|
||||
*/
|
||||
bool Run(void);
|
||||
|
||||
/** Generate the output. This is a pure method so it must be implemented by
|
||||
the classes that inherits from FGOutputType. The Print name may not be
|
||||
relevant to all outputs but it has been kept for backward compatibility.
|
||||
*/
|
||||
virtual void Print(void) = 0;
|
||||
|
||||
/** Reset the output prior to a restart of the simulation. This method should
|
||||
be called when the simulation is restarted with, for example, new initial
|
||||
conditions. When this method is executed the output instance can take
|
||||
special actions such as closing the current output file and open a new
|
||||
one with a different name. */
|
||||
|
||||
virtual void SetStartNewOutput(void) {}
|
||||
|
||||
/// Enables the output generation.
|
||||
void Enable(void) { enabled = true; }
|
||||
/// Disables the output generation.
|
||||
void Disable(void) { enabled = false; }
|
||||
/** Toggles the output generation.
|
||||
@result the output generation status i.e. true if the output has been
|
||||
enabled, false if the output has been disabled. */
|
||||
bool Toggle(void) {enabled = !enabled; return enabled;}
|
||||
|
||||
/// Subsystem types for specifying which will be output in the FDM data logging
|
||||
enum eSubSystems {
|
||||
/** Subsystem: Simulation (= 1) */ ssSimulation = 1,
|
||||
/** Subsystem: Aerosurfaces (= 2) */ ssAerosurfaces = 2,
|
||||
/** Subsystem: Body rates (= 4) */ ssRates = 4,
|
||||
/** Subsystem: Velocities (= 8) */ ssVelocities = 8,
|
||||
/** Subsystem: Forces (= 16) */ ssForces = 16,
|
||||
/** Subsystem: Moments (= 32) */ ssMoments = 32,
|
||||
/** Subsystem: Atmosphere (= 64) */ ssAtmosphere = 64,
|
||||
/** Subsystem: Mass Properties (= 128) */ ssMassProps = 128,
|
||||
/** Subsystem: Coefficients (= 256) */ ssAeroFunctions = 256,
|
||||
/** Subsystem: Propagate (= 512) */ ssPropagate = 512,
|
||||
/** Subsystem: Ground Reactions (= 1024) */ ssGroundReactions = 1024,
|
||||
/** Subsystem: FCS (= 2048) */ ssFCS = 2048,
|
||||
/** Subsystem: Propulsion (= 4096) */ ssPropulsion = 4096
|
||||
} subsystems;
|
||||
|
||||
protected:
|
||||
unsigned int OutputIdx;
|
||||
int SubSystems;
|
||||
std::vector <FGPropertyValue*> OutputParameters;
|
||||
std::vector <std::string> OutputCaptions;
|
||||
bool enabled;
|
||||
|
||||
FGAerodynamics* Aerodynamics;
|
||||
FGAuxiliary* Auxiliary;
|
||||
FGAircraft* Aircraft;
|
||||
FGAtmosphere* Atmosphere;
|
||||
FGWinds* Winds;
|
||||
FGPropulsion* Propulsion;
|
||||
FGMassBalance* MassBalance;
|
||||
FGPropagate* Propagate;
|
||||
FGAccelerations* Accelerations;
|
||||
FGFCS* FCS;
|
||||
FGGroundReactions* GroundReactions;
|
||||
FGExternalReactions* ExternalReactions;
|
||||
FGBuoyantForces* BuoyantForces;
|
||||
|
||||
void Debug(int from) override;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
329
src/FDM/JSBSim/input_output/FGPropertyManager.cpp
Normal file
329
src/FDM/JSBSim/input_output/FGPropertyManager.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGPropertyManager.cpp
|
||||
Author: Tony Peden
|
||||
Based on work originally by David Megginson
|
||||
Date: 2/2002
|
||||
|
||||
------------- Copyright (C) 2002 -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "FGPropertyManager.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES [use "class documentation" below for API docs]
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyManager::Unbind(void)
|
||||
{
|
||||
for(auto& prop: tied_properties)
|
||||
prop->untie();
|
||||
|
||||
tied_properties.clear();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGPropertyManager::mkPropertyName(string name, bool lowercase) {
|
||||
|
||||
/* do this two pass to avoid problems with characters getting skipped
|
||||
because the index changed */
|
||||
unsigned i;
|
||||
for(i=0;i<name.length();i++) {
|
||||
if( lowercase && isupper(name[i]) )
|
||||
name[i]=tolower(name[i]);
|
||||
else if( isspace(name[i]) )
|
||||
name[i]='-';
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGPropertyNode*
|
||||
FGPropertyNode::GetNode (const string &path, bool create)
|
||||
{
|
||||
SGPropertyNode* node = getNode(path.c_str(), create);
|
||||
if (node == 0) {
|
||||
cerr << "FGPropertyManager::GetNode() No node found for " << path << endl;
|
||||
}
|
||||
return (FGPropertyNode*)node;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGPropertyNode*
|
||||
FGPropertyNode::GetNode (const string &relpath, int index, bool create)
|
||||
{
|
||||
SGPropertyNode* node = getNode(relpath.c_str(), index, create);
|
||||
if (node == 0) {
|
||||
cerr << "FGPropertyManager::GetNode() No node found for " << relpath
|
||||
<< "[" << index << "]" << endl;
|
||||
}
|
||||
return (FGPropertyNode*)node;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::HasNode (const string &path)
|
||||
{
|
||||
const SGPropertyNode* node = getNode(path.c_str(), false);
|
||||
return (node != 0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGPropertyNode::GetPrintableName( void ) const
|
||||
{
|
||||
string temp_string(getNameString());
|
||||
size_t initial_location=0;
|
||||
size_t found_location;
|
||||
|
||||
found_location = temp_string.rfind("/");
|
||||
if (found_location != string::npos)
|
||||
temp_string = temp_string.substr(found_location);
|
||||
|
||||
found_location = temp_string.find('_',initial_location);
|
||||
while (found_location != string::npos) {
|
||||
temp_string.replace(found_location,1," ");
|
||||
initial_location = found_location+1;
|
||||
found_location = temp_string.find('_',initial_location);
|
||||
}
|
||||
return temp_string;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGPropertyNode::GetFullyQualifiedName(void) const
|
||||
{
|
||||
vector<string> stack;
|
||||
stack.push_back( getDisplayName(true) );
|
||||
const SGPropertyNode* tmpn=getParent();
|
||||
bool atroot=false;
|
||||
while( !atroot ) {
|
||||
stack.push_back( tmpn->getDisplayName(true) );
|
||||
if( !tmpn->getParent() )
|
||||
atroot=true;
|
||||
else
|
||||
tmpn=tmpn->getParent();
|
||||
}
|
||||
|
||||
string fqname="";
|
||||
for(size_t i=stack.size()-1;i>0;i--) {
|
||||
fqname+= stack[i];
|
||||
fqname+= "/";
|
||||
}
|
||||
fqname+= stack[0];
|
||||
return fqname;
|
||||
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGPropertyNode::GetRelativeName( const string &path ) const
|
||||
{
|
||||
string temp_string = GetFullyQualifiedName();
|
||||
size_t len = path.length();
|
||||
if ( (len > 0) && (temp_string.substr(0,len) == path) ) {
|
||||
temp_string = temp_string.erase(0,len);
|
||||
}
|
||||
return temp_string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::GetBool (const string &name, bool defaultValue) const
|
||||
{
|
||||
return getBoolValue(name.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
int FGPropertyNode::GetInt (const string &name, int defaultValue ) const
|
||||
{
|
||||
return getIntValue(name.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
int FGPropertyNode::GetLong (const string &name, long defaultValue ) const
|
||||
{
|
||||
return getLongValue(name.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
float FGPropertyNode::GetFloat (const string &name, float defaultValue ) const
|
||||
{
|
||||
return getFloatValue(name.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGPropertyNode::GetDouble (const string &name, double defaultValue ) const
|
||||
{
|
||||
return getDoubleValue(name.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGPropertyNode::GetString (const string &name, string defaultValue ) const
|
||||
{
|
||||
return string(getStringValue(name.c_str(), defaultValue.c_str()));
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetBool (const string &name, bool val)
|
||||
{
|
||||
return setBoolValue(name.c_str(), val);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetInt (const string &name, int val)
|
||||
{
|
||||
return setIntValue(name.c_str(), val);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetLong (const string &name, long val)
|
||||
{
|
||||
return setLongValue(name.c_str(), val);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetFloat (const string &name, float val)
|
||||
{
|
||||
return setFloatValue(name.c_str(), val);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetDouble (const string &name, double val)
|
||||
{
|
||||
return setDoubleValue(name.c_str(), val);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyNode::SetString (const string &name, const string &val)
|
||||
{
|
||||
return setStringValue(name.c_str(), val.c_str());
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyNode::SetArchivable (const string &name, bool state )
|
||||
{
|
||||
SGPropertyNode * node = getNode(name.c_str());
|
||||
if (node == 0)
|
||||
cerr <<
|
||||
"Attempt to set archive flag for non-existent property "
|
||||
<< name << endl;
|
||||
else
|
||||
node->setAttribute(SGPropertyNode::ARCHIVE, state);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyNode::SetReadable (const string &name, bool state )
|
||||
{
|
||||
SGPropertyNode * node = getNode(name.c_str());
|
||||
if (node == 0)
|
||||
cerr <<
|
||||
"Attempt to set read flag for non-existant property "
|
||||
<< name << endl;
|
||||
else
|
||||
node->setAttribute(SGPropertyNode::READ, state);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyNode::SetWritable (const string &name, bool state )
|
||||
{
|
||||
SGPropertyNode * node = getNode(name.c_str());
|
||||
if (node == 0)
|
||||
cerr <<
|
||||
"Attempt to set write flag for non-existant property "
|
||||
<< name << endl;
|
||||
else
|
||||
node->setAttribute(SGPropertyNode::WRITE, state);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyManager::Untie(const string &name)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str());
|
||||
if (!property) {
|
||||
cerr << "Attempt to untie a non-existant property." << name << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Untie(property);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyManager::Untie(SGPropertyNode *property)
|
||||
{
|
||||
const string& name = property->getNameString();
|
||||
|
||||
assert(property->isTied());
|
||||
|
||||
for (auto it = tied_properties.begin(); it != tied_properties.end(); ++it) {
|
||||
if (*it == property) {
|
||||
property->untie();
|
||||
tied_properties.erase(it);
|
||||
if (FGJSBBase::debug_lvl & 0x20) cout << "Untied " << name << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cerr << "Failed to untie property " << name << endl
|
||||
<< "JSBSim is not the owner of this property." << endl;
|
||||
}
|
||||
} // namespace JSBSim
|
||||
620
src/FDM/JSBSim/input_output/FGPropertyManager.h
Normal file
620
src/FDM/JSBSim/input_output/FGPropertyManager.h
Normal file
@@ -0,0 +1,620 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGPropertyManager.h
|
||||
Author: Tony Peden
|
||||
Based on work originally by David Megginson
|
||||
Date: 2/2002
|
||||
|
||||
------------- Copyright (C) 2002 -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGPROPERTYMANAGER_H
|
||||
#define FGPROPERTYMANAGER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// This is needed by MSVC9 when included in FlightGear because of
|
||||
// the new Vec4d class in props.hxx
|
||||
#if defined( HAVE_CONFIG_H )
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include "simgear/props/propertyObject.hxx"
|
||||
#if !PROPS_STANDALONE
|
||||
# include "simgear/math/SGMath.hxx"
|
||||
#endif
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Class wrapper for property handling.
|
||||
@author David Megginson, Tony Peden
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGPropertyNode : public SGPropertyNode
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
virtual ~FGPropertyNode(void) {}
|
||||
|
||||
/**
|
||||
* Get a property node.
|
||||
*
|
||||
* @param path The path of the node, relative to root.
|
||||
* @param create true to create the node if it doesn't exist.
|
||||
* @return The node, or 0 if none exists and none was created.
|
||||
*/
|
||||
FGPropertyNode*
|
||||
GetNode (const std::string &path, bool create = false);
|
||||
|
||||
FGPropertyNode*
|
||||
GetNode (const std::string &relpath, int index, bool create = false);
|
||||
|
||||
/**
|
||||
* Test whether a given node exists.
|
||||
*
|
||||
* @param path The path of the node, relative to root.
|
||||
* @return true if the node exists, false otherwise.
|
||||
*/
|
||||
bool HasNode (const std::string &path);
|
||||
|
||||
/**
|
||||
* Get the name of a node
|
||||
*/
|
||||
const std::string& GetName( void ) const { return getNameString(); }
|
||||
|
||||
/**
|
||||
* Get the name of a node without underscores, etc.
|
||||
*/
|
||||
std::string GetPrintableName( void ) const;
|
||||
|
||||
/**
|
||||
* Get the fully qualified name of a node
|
||||
* This function is very slow, so is probably useful for debugging only.
|
||||
*/
|
||||
std::string GetFullyQualifiedName(void) const;
|
||||
|
||||
/**
|
||||
* Get the qualified name of a node relative to given base path,
|
||||
* otherwise the fully qualified name.
|
||||
* This function is very slow, so is probably useful for debugging only.
|
||||
*
|
||||
* @param path The path to strip off, if found.
|
||||
*/
|
||||
std::string GetRelativeName( const std::string &path = "/fdm/jsbsim/" ) const;
|
||||
|
||||
/**
|
||||
* Get a bool value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getBoolValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as a bool, or the default value provided.
|
||||
*/
|
||||
bool GetBool (const std::string &name, bool defaultValue = false) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get an int value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getIntValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as an int, or the default value provided.
|
||||
*/
|
||||
int GetInt (const std::string &name, int defaultValue = 0) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get a long value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getLongValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as a long, or the default value provided.
|
||||
*/
|
||||
int GetLong (const std::string &name, long defaultValue = 0L) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get a float value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getFloatValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as a float, or the default value provided.
|
||||
*/
|
||||
float GetFloat (const std::string &name, float defaultValue = 0.0) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get a double value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getDoubleValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as a double, or the default value provided.
|
||||
*/
|
||||
double GetDouble (const std::string &name, double defaultValue = 0.0) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get a string value for a property.
|
||||
*
|
||||
* This method is convenient but inefficient. It should be used
|
||||
* infrequently (i.e. for initializing, loading, saving, etc.),
|
||||
* not in the main loop. If you need to get a value frequently,
|
||||
* it is better to look up the node itself using GetNode and then
|
||||
* use the node's getStringValue() method, to avoid the lookup overhead.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param defaultValue The default value to return if the property
|
||||
* does not exist.
|
||||
* @return The property's value as a string, or the default value provided.
|
||||
*/
|
||||
std::string GetString (const std::string &name, std::string defaultValue = "") const;
|
||||
|
||||
|
||||
/**
|
||||
* Set a bool value for a property.
|
||||
*
|
||||
* Assign a bool value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* BOOL; if it has a type of UNKNOWN, the type will also be set to
|
||||
* BOOL; otherwise, the value type will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetBool (const std::string &name, bool val);
|
||||
|
||||
|
||||
/**
|
||||
* Set an int value for a property.
|
||||
*
|
||||
* Assign an int value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* INT; if it has a type of UNKNOWN, the type will also be set to
|
||||
* INT; otherwise, the value type will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetInt (const std::string &name, int val);
|
||||
|
||||
|
||||
/**
|
||||
* Set a long value for a property.
|
||||
*
|
||||
* Assign a long value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* LONG; if it has a type of UNKNOWN, the type will also be set to
|
||||
* LONG; otherwise, the value type will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetLong (const std::string &name, long val);
|
||||
|
||||
|
||||
/**
|
||||
* Set a float value for a property.
|
||||
*
|
||||
* Assign a float value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* FLOAT; if it has a type of UNKNOWN, the type will also be set to
|
||||
* FLOAT; otherwise, the value type will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetFloat (const std::string &name, float val);
|
||||
|
||||
|
||||
/**
|
||||
* Set a double value for a property.
|
||||
*
|
||||
* Assign a double value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* DOUBLE; if it has a type of UNKNOWN, the type will also be set to
|
||||
* DOUBLE; otherwise, the double value will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetDouble (const std::string &name, double val);
|
||||
|
||||
|
||||
/**
|
||||
* Set a string value for a property.
|
||||
*
|
||||
* Assign a string value to a property. If the property does not
|
||||
* yet exist, it will be created and its type will be set to
|
||||
* STRING; if it has a type of UNKNOWN, the type will also be set to
|
||||
* STRING; otherwise, the string value will be converted to the property's
|
||||
* type.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param val The new value for the property.
|
||||
* @return true if the assignment succeeded, false otherwise.
|
||||
*/
|
||||
bool SetString (const std::string &name, const std::string &val);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Convenience functions for setting property attributes.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* Set the state of the archive attribute for a property.
|
||||
*
|
||||
* If the archive attribute is true, the property will be written
|
||||
* when a flight is saved; if it is false, the property will be
|
||||
* skipped.
|
||||
*
|
||||
* A warning message will be printed if the property does not exist.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param state The state of the archive attribute (defaults to true).
|
||||
*/
|
||||
void SetArchivable (const std::string &name, bool state = true);
|
||||
|
||||
|
||||
/**
|
||||
* Set the state of the read attribute for a property.
|
||||
*
|
||||
* If the read attribute is true, the property value will be readable;
|
||||
* if it is false, the property value will always be the default value
|
||||
* for its type.
|
||||
*
|
||||
* A warning message will be printed if the property does not exist.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param state The state of the read attribute (defaults to true).
|
||||
*/
|
||||
void SetReadable (const std::string &name, bool state = true);
|
||||
|
||||
|
||||
/**
|
||||
* Set the state of the write attribute for a property.
|
||||
*
|
||||
* If the write attribute is true, the property value may be modified
|
||||
* (depending on how it is tied); if the write attribute is false, the
|
||||
* property value may not be modified.
|
||||
*
|
||||
* A warning message will be printed if the property does not exist.
|
||||
*
|
||||
* @param name The property name.
|
||||
* @param state The state of the write attribute (defaults to true).
|
||||
*/
|
||||
void SetWritable (const std::string &name, bool state = true);
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<FGPropertyNode> FGPropertyNode_ptr;
|
||||
typedef SGSharedPtr<const FGPropertyNode> FGConstPropertyNode_ptr;
|
||||
|
||||
class FGPropertyManager
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
FGPropertyManager(void) { root = new FGPropertyNode; }
|
||||
|
||||
/// Constructor
|
||||
explicit FGPropertyManager(FGPropertyNode* _root) : root(_root) {};
|
||||
|
||||
/// Destructor
|
||||
virtual ~FGPropertyManager(void) { Unbind(); }
|
||||
|
||||
FGPropertyNode* GetNode(void) const { return root; }
|
||||
FGPropertyNode* GetNode(const std::string &path, bool create = false)
|
||||
{ return root->GetNode(path, create); }
|
||||
FGPropertyNode* GetNode(const std::string &relpath, int index, bool create = false)
|
||||
{ return root->GetNode(relpath, index, create); }
|
||||
bool HasNode(const std::string& path) const
|
||||
{
|
||||
std::string newPath = path;
|
||||
if (newPath[0] == '-') newPath.erase(0,1);
|
||||
return root->HasNode(newPath);
|
||||
}
|
||||
|
||||
/** Property-ify a name
|
||||
* replaces spaces with '-' and, optionally, makes name all lower case
|
||||
* @param name string to change
|
||||
* @param lowercase true to change all upper case chars to lower
|
||||
* NOTE: this function changes its argument and thus relies
|
||||
* on pass by value
|
||||
*/
|
||||
std::string mkPropertyName(std::string name, bool lowercase);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Convenience functions for tying properties, with logging.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* Untie a property from an external data source.
|
||||
*
|
||||
* Classes should use this function to release control of any
|
||||
* properties they are managing.
|
||||
*
|
||||
* @param name The property name to untie (full path).
|
||||
*/
|
||||
void Untie (const std::string &name);
|
||||
|
||||
/**
|
||||
* Untie a property from an external data source.
|
||||
*
|
||||
* Classes should use this function to release control of any
|
||||
* properties they are managing.
|
||||
*
|
||||
* @param property A pointer to the property to untie.
|
||||
*/
|
||||
void Untie (SGPropertyNode* property);
|
||||
|
||||
/**
|
||||
* Unbind all properties bound by this manager to an external data source.
|
||||
*
|
||||
* Classes should use this function to release control of any
|
||||
* properties they have bound using this property manager.
|
||||
*/
|
||||
void Unbind (void);
|
||||
|
||||
/**
|
||||
* Tie a property to an external variable.
|
||||
*
|
||||
* The property's value will automatically mirror the variable's
|
||||
* value, and vice-versa, until the property is untied.
|
||||
*
|
||||
* @param name The property name to tie (full path).
|
||||
* @param pointer A pointer to the variable.
|
||||
*/
|
||||
template <typename T> void
|
||||
Tie (const std::string &name, T *pointer)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str(), true);
|
||||
if (!property) {
|
||||
cerr << "Could not get or create property " << name << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property->tie(SGRawValuePointer<T>(pointer), false))
|
||||
cerr << "Failed to tie property " << name << " to a pointer" << endl;
|
||||
else {
|
||||
tied_properties.push_back(property);
|
||||
if (FGJSBBase::debug_lvl & 0x20) cout << name << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie a property to a pair of simple functions.
|
||||
*
|
||||
* Every time the property value is queried, the getter (if any) will
|
||||
* be invoked; every time the property value is modified, the setter
|
||||
* (if any) will be invoked. The getter can be 0 to make the property
|
||||
* unreadable, and the setter can be 0 to make the property
|
||||
* unmodifiable.
|
||||
*
|
||||
* @param name The property name to tie (full path).
|
||||
* @param getter The getter function, or 0 if the value is unreadable.
|
||||
* @param setter The setter function, or 0 if the value is unmodifiable.
|
||||
*/
|
||||
|
||||
template <typename T> void
|
||||
Tie (const std::string &name, T (*getter)(), void (*setter)(T) = nullptr)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str(), true);
|
||||
if (!property) {
|
||||
std::cerr << "Could not get or create property " << name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property->tie(SGRawValueFunctions<T>(getter, setter), false))
|
||||
std::cerr << "Failed to tie property " << name << " to functions"
|
||||
<< std::endl;
|
||||
else {
|
||||
if (!setter) property->setAttribute(SGPropertyNode::WRITE, false);
|
||||
if (!getter) property->setAttribute(SGPropertyNode::READ, false);
|
||||
tied_properties.push_back(property);
|
||||
if (FGJSBBase::debug_lvl & 0x20) std::cout << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie a property to a pair of indexed functions.
|
||||
*
|
||||
* Every time the property value is queried, the getter (if any) will
|
||||
* be invoked with the index provided; every time the property value
|
||||
* is modified, the setter (if any) will be invoked with the index
|
||||
* provided. The getter can be 0 to make the property unreadable, and
|
||||
* the setter can be 0 to make the property unmodifiable.
|
||||
*
|
||||
* @param name The property name to tie (full path).
|
||||
* @param index The integer argument to pass to the getter and
|
||||
* setter functions.
|
||||
* @param getter The getter function, or 0 if the value is unreadable.
|
||||
* @param setter The setter function, or 0 if the value is unmodifiable.
|
||||
*/
|
||||
template <typename T> void
|
||||
Tie (const std::string &name, int index, T (*getter)(int),
|
||||
void (*setter)(int, T) = nullptr)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str(), true);
|
||||
if (!property) {
|
||||
std::cerr << "Could not get or create property " << name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property->tie(SGRawValueFunctionsIndexed<T>(index, getter, setter),
|
||||
false))
|
||||
std::cerr << "Failed to tie property " << name << " to indexed functions"
|
||||
<< std::endl;
|
||||
else {
|
||||
if (!setter) property->setAttribute(SGPropertyNode::WRITE, false);
|
||||
if (!getter) property->setAttribute(SGPropertyNode::READ, false);
|
||||
tied_properties.push_back(property);
|
||||
if (FGJSBBase::debug_lvl & 0x20) std::cout << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie a property to a pair of object methods.
|
||||
*
|
||||
* Every time the property value is queried, the getter (if any) will
|
||||
* be invoked; every time the property value is modified, the setter
|
||||
* (if any) will be invoked. The getter can be 0 to make the property
|
||||
* unreadable, and the setter can be 0 to make the property
|
||||
* unmodifiable.
|
||||
*
|
||||
* @param name The property name to tie (full path).
|
||||
* @param obj The object whose methods should be invoked.
|
||||
* @param getter The object's getter method, or 0 if the value is
|
||||
* unreadable.
|
||||
* @param setter The object's setter method, or 0 if the value is
|
||||
* unmodifiable.
|
||||
*/
|
||||
template <class T, class V> void
|
||||
Tie (const std::string &name, T * obj, V (T::*getter)() const,
|
||||
void (T::*setter)(V) = nullptr)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str(), true);
|
||||
if (!property) {
|
||||
std::cerr << "Could not get or create property " << name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property->tie(SGRawValueMethods<T,V>(*obj, getter, setter), false))
|
||||
std::cerr << "Failed to tie property " << name << " to object methods"
|
||||
<< std::endl;
|
||||
else {
|
||||
if (!setter) property->setAttribute(SGPropertyNode::WRITE, false);
|
||||
if (!getter) property->setAttribute(SGPropertyNode::READ, false);
|
||||
tied_properties.push_back(property);
|
||||
if (FGJSBBase::debug_lvl & 0x20) std::cout << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie a property to a pair of indexed object methods.
|
||||
*
|
||||
* Every time the property value is queried, the getter (if any) will
|
||||
* be invoked with the index provided; every time the property value
|
||||
* is modified, the setter (if any) will be invoked with the index
|
||||
* provided. The getter can be 0 to make the property unreadable, and
|
||||
* the setter can be 0 to make the property unmodifiable.
|
||||
*
|
||||
* @param name The property name to tie (full path).
|
||||
* @param obj The object whose methods should be invoked.
|
||||
* @param index The integer argument to pass to the getter and
|
||||
* setter methods.
|
||||
* @param getter The getter method, or 0 if the value is unreadable.
|
||||
* @param setter The setter method, or 0 if the value is unmodifiable.
|
||||
*/
|
||||
template <class T, class V> void
|
||||
Tie (const std::string &name, T * obj, int index, V (T::*getter)(int) const,
|
||||
void (T::*setter)(int, V) = nullptr)
|
||||
{
|
||||
SGPropertyNode* property = root->getNode(name.c_str(), true);
|
||||
if (!property) {
|
||||
std::cerr << "Could not get or create property " << name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!property->tie(SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter),
|
||||
false))
|
||||
std::cerr << "Failed to tie property " << name
|
||||
<< " to indexed object methods" << std::endl;
|
||||
else {
|
||||
if (!setter) property->setAttribute(SGPropertyNode::WRITE, false);
|
||||
if (!getter) property->setAttribute(SGPropertyNode::READ, false);
|
||||
tied_properties.push_back(property);
|
||||
if (FGJSBBase::debug_lvl & 0x20) std::cout << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> simgear::PropertyObject<T>
|
||||
CreatePropertyObject(const std::string &path)
|
||||
{ return simgear::PropertyObject<T>(root->GetNode(path, true)); }
|
||||
|
||||
private:
|
||||
std::vector<SGPropertyNode_ptr> tied_properties;
|
||||
FGPropertyNode_ptr root;
|
||||
};
|
||||
}
|
||||
#endif // FGPROPERTYMANAGER_H
|
||||
136
src/FDM/JSBSim/input_output/FGPropertyReader.cpp
Normal file
136
src/FDM/JSBSim/input_output/FGPropertyReader.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGPropertyReader.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 12/30/13
|
||||
Purpose: Read and manage properties from XML data
|
||||
|
||||
------------- Copyright (C) 2013 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This class reads and manages properties defined in XML data
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/30/13 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGPropertyReader.h"
|
||||
#include "FGPropertyManager.h"
|
||||
#include "FGXMLElement.h"
|
||||
#include "FGJSBBase.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGPropertyReader::ResetToIC(void)
|
||||
{
|
||||
for (auto v: interface_prop_initial_value) {
|
||||
SGPropertyNode* node = v.first;
|
||||
if (!node->getAttribute(SGPropertyNode::PRESERVE))
|
||||
node->setDoubleValue(v.second);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyReader::Load(Element* el, FGPropertyManager* PM, bool override)
|
||||
{
|
||||
Element *property_element = el->FindElement("property");
|
||||
if (property_element && FGJSBBase::debug_lvl > 0) {
|
||||
cout << endl << " ";
|
||||
if (override)
|
||||
cout << "Overriding";
|
||||
else
|
||||
cout << "Declared";
|
||||
cout << " properties" << endl << endl;
|
||||
}
|
||||
|
||||
while (property_element) {
|
||||
SGPropertyNode* node = nullptr;
|
||||
double value=0.0;
|
||||
if ( ! property_element->GetAttributeValue("value").empty())
|
||||
value = property_element->GetAttributeValueAsNumber("value");
|
||||
|
||||
string interface_property_string = property_element->GetDataLine();
|
||||
if (PM->HasNode(interface_property_string)) {
|
||||
if (override) {
|
||||
node = PM->GetNode(interface_property_string);
|
||||
|
||||
if (FGJSBBase::debug_lvl > 0) {
|
||||
if (interface_prop_initial_value.find(node) == interface_prop_initial_value.end()) {
|
||||
cout << property_element->ReadFrom()
|
||||
<< " The following property will be overridden but it has not been" << endl
|
||||
<< " defined in the current model '" << el->GetName() << "'" << endl;
|
||||
}
|
||||
|
||||
cout << " " << "Overriding value for property " << interface_property_string << endl
|
||||
<< " (old value: " << node->getDoubleValue() << " new value: " << value << ")"
|
||||
<< endl << endl;
|
||||
}
|
||||
|
||||
node->setDoubleValue(value);
|
||||
}
|
||||
else {
|
||||
cerr << property_element->ReadFrom()
|
||||
<< " Property " << interface_property_string
|
||||
<< " is already defined." << endl;
|
||||
property_element = el->FindNextElement("property");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
node = PM->GetNode(interface_property_string, true);
|
||||
if (node) {
|
||||
node->setDoubleValue(value);
|
||||
|
||||
if (FGJSBBase::debug_lvl > 0)
|
||||
cout << " " << interface_property_string << " (initial value: "
|
||||
<< value << ")" << endl << endl;
|
||||
}
|
||||
else {
|
||||
cerr << "Could not create property " << interface_property_string
|
||||
<< endl;
|
||||
property_element = el->FindNextElement("property");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
interface_prop_initial_value[node] = value;
|
||||
if (property_element->GetAttributeValue("persistent") == string("true"))
|
||||
node->setAttribute(SGPropertyNode::PRESERVE, true);
|
||||
|
||||
property_element = el->FindNextElement("property");
|
||||
}
|
||||
|
||||
// End of interface property loading logic
|
||||
}
|
||||
}
|
||||
92
src/FDM/JSBSim/input_output/FGPropertyReader.h
Normal file
92
src/FDM/JSBSim/input_output/FGPropertyReader.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGPropertyReader.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 12/30/13
|
||||
|
||||
------------- Copyright (C) 2013 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/30/13 BC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGPROPERTYREADER_H
|
||||
#define FGPROPERTYREADER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "simgear/props/props.hxx"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class Element;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGPropertyReader
|
||||
{
|
||||
public:
|
||||
void Load(Element* el, FGPropertyManager* PropertyManager, bool override);
|
||||
bool ResetToIC(void);
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
public:
|
||||
explicit const_iterator(const std::map<SGPropertyNode_ptr, double>::const_iterator &it) : prop_it(it) {}
|
||||
const_iterator& operator++() { ++prop_it; return *this; }
|
||||
bool operator!=(const const_iterator& it) const { return prop_it != it.prop_it; }
|
||||
FGPropertyNode* operator*() {
|
||||
SGPropertyNode* node = prop_it->first;
|
||||
return static_cast<FGPropertyNode*>(node);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<SGPropertyNode_ptr, double>::const_iterator prop_it;
|
||||
};
|
||||
|
||||
const_iterator begin(void) const { return const_iterator(interface_prop_initial_value.begin()); }
|
||||
const_iterator end(void) const { return const_iterator(interface_prop_initial_value.end()); }
|
||||
bool empty(void) const { return interface_prop_initial_value.empty(); }
|
||||
|
||||
private:
|
||||
std::map<SGPropertyNode_ptr, double> interface_prop_initial_value;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
701
src/FDM/JSBSim/input_output/FGScript.cpp
Normal file
701
src/FDM/JSBSim/input_output/FGScript.cpp
Normal file
@@ -0,0 +1,701 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGScript.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 12/21/01
|
||||
Purpose: Loads and runs JSBSim scripts.
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This class wraps up the simulation scripting routines.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/21/01 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "FGScript.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "input_output/FGXMLFileRead.h"
|
||||
#include "initialization/FGInitialCondition.h"
|
||||
#include "models/FGInput.h"
|
||||
#include "math/FGCondition.h"
|
||||
#include "math/FGFunctionValue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// Constructor
|
||||
|
||||
FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex)
|
||||
{
|
||||
PropertyManager=FDMExec->GetPropertyManager();
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGScript::~FGScript()
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i=0; i<Events.size(); i++) {
|
||||
delete Events[i].Condition;
|
||||
for (j=0; j<Events[i].Functions.size(); j++)
|
||||
delete Events[i].Functions[j];
|
||||
for (j=0; j<Events[i].NotifyProperties.size(); j++)
|
||||
delete Events[i].NotifyProperties[j];
|
||||
}
|
||||
Events.clear();
|
||||
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGScript::LoadScript(const SGPath& script, double default_dT,
|
||||
const SGPath& initfile)
|
||||
{
|
||||
SGPath initialize;
|
||||
string aircraft="", prop_name="";
|
||||
string notifyPropertyName="";
|
||||
Element *element=0, *run_element=0, *event_element=0;
|
||||
Element *set_element=0;
|
||||
Element *notify_element = 0L, *notify_property_element = 0L;
|
||||
double dt = 0.0, value = 0.0;
|
||||
FGCondition *newCondition;
|
||||
|
||||
FGXMLFileRead XMLFileRead;
|
||||
Element* document = XMLFileRead.LoadXMLDocument(script);
|
||||
|
||||
if (!document) {
|
||||
cerr << "File: " << script << " could not be loaded." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document->GetName() != string("runscript")) {
|
||||
cerr << "File: " << script << " is not a script file" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptName = document->GetAttributeValue("name");
|
||||
|
||||
// First, find "run" element and set delta T
|
||||
|
||||
run_element = document->FindElement("run");
|
||||
|
||||
if (!run_element) {
|
||||
cerr << "No \"run\" element found in script." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set sim timing
|
||||
|
||||
if (run_element->HasAttribute("start"))
|
||||
StartTime = run_element->GetAttributeValueAsNumber("start");
|
||||
else
|
||||
StartTime = 0.0;
|
||||
FDMExec->Setsim_time(StartTime);
|
||||
if (run_element->HasAttribute("end")) {
|
||||
EndTime = run_element->GetAttributeValueAsNumber("end");
|
||||
} else {
|
||||
cerr << "An end time (duration) for the script must be specified in the script <run> element." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (default_dT == 0.0)
|
||||
dt = run_element->GetAttributeValueAsNumber("dt");
|
||||
else {
|
||||
dt = default_dT;
|
||||
cout << endl << "Overriding simulation step size from the command line. New step size is: "
|
||||
<< default_dT << " seconds (" << 1/default_dT << " Hz)" << endl << endl;
|
||||
}
|
||||
|
||||
FDMExec->Setdt(dt);
|
||||
|
||||
// Make sure that the desired time is reached and executed.
|
||||
EndTime += 0.99*FDMExec->GetDeltaT();
|
||||
|
||||
// read aircraft and initialization files
|
||||
|
||||
element = document->FindElement("use");
|
||||
if (element) {
|
||||
aircraft = element->GetAttributeValue("aircraft");
|
||||
if (!aircraft.empty()) {
|
||||
if (!FDMExec->LoadModel(aircraft))
|
||||
return false;
|
||||
} else {
|
||||
cerr << "Aircraft must be specified in use element." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
initialize = SGPath::fromLocal8Bit(element->GetAttributeValue("initialize").c_str());
|
||||
if (initfile.isNull()) {
|
||||
if (initialize.isNull()) {
|
||||
cerr << "Initialization file must be specified in use element." << endl;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cout << endl << "The initialization file specified in the script file ("
|
||||
<< initialize << ") has been overridden with a specified file ("
|
||||
<< initfile << ")." << endl;
|
||||
initialize = initfile;
|
||||
}
|
||||
|
||||
} else {
|
||||
cerr << "No \"use\" directives in the script file." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
FGInitialCondition *IC=FDMExec->GetIC();
|
||||
if ( ! IC->Load( initialize )) {
|
||||
cerr << "Initialization unsuccessful" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, read input spec if given.
|
||||
element = document->FindElement("input");
|
||||
while (element) {
|
||||
if (!FDMExec->GetInput()->Load(element))
|
||||
return false;
|
||||
|
||||
element = document->FindNextElement("input");
|
||||
}
|
||||
|
||||
// Now, read output spec if given.
|
||||
element = document->FindElement("output");
|
||||
SGPath scriptDir = SGPath(script.dir());
|
||||
if (scriptDir.isNull())
|
||||
scriptDir = SGPath(".");
|
||||
|
||||
while (element) {
|
||||
if (!FDMExec->GetOutput()->Load(element, scriptDir))
|
||||
return false;
|
||||
|
||||
element = document->FindNextElement("output");
|
||||
}
|
||||
|
||||
// Read local property/value declarations
|
||||
int saved_debug_lvl = debug_lvl;
|
||||
debug_lvl = 0; // Disable messages
|
||||
LocalProperties.Load(run_element, PropertyManager, true);
|
||||
debug_lvl = saved_debug_lvl;
|
||||
|
||||
// Read "events" from script
|
||||
|
||||
event_element = run_element->FindElement("event");
|
||||
while (event_element) { // event processing
|
||||
|
||||
// Create the event structure
|
||||
struct event *newEvent = new struct event();
|
||||
|
||||
// Retrieve the event name if given
|
||||
newEvent->Name = event_element->GetAttributeValue("name");
|
||||
|
||||
// Is this event persistent? That is, does it execute every time the
|
||||
// condition triggers to true, or does it execute as a one-shot event, only?
|
||||
if (event_element->GetAttributeValue("persistent") == string("true")) {
|
||||
newEvent->Persistent = true;
|
||||
}
|
||||
|
||||
// Does this event execute continuously when triggered to true?
|
||||
if (event_element->GetAttributeValue("continuous") == string("true")) {
|
||||
newEvent->Continuous = true;
|
||||
}
|
||||
|
||||
// Process the conditions
|
||||
Element* condition_element = event_element->FindElement("condition");
|
||||
if (condition_element != 0) {
|
||||
try {
|
||||
newCondition = new FGCondition(condition_element, PropertyManager);
|
||||
} catch(string& str) {
|
||||
cout << endl << fgred << str << reset << endl << endl;
|
||||
delete newEvent;
|
||||
return false;
|
||||
}
|
||||
newEvent->Condition = newCondition;
|
||||
} else {
|
||||
cerr << "No condition specified in script event " << newEvent->Name
|
||||
<< endl;
|
||||
delete newEvent;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is there a delay between the time this event is triggered, and when the
|
||||
// event actions are executed?
|
||||
|
||||
Element* delay_element = event_element->FindElement("delay");
|
||||
if (delay_element)
|
||||
newEvent->Delay = event_element->FindElementValueAsNumber("delay");
|
||||
else
|
||||
newEvent->Delay = 0.0;
|
||||
|
||||
// Notify about when this event is triggered?
|
||||
if ((notify_element = event_element->FindElement("notify")) != 0) {
|
||||
if (notify_element->HasAttribute("format")) {
|
||||
if (notify_element->GetAttributeValue("format") == "kml") newEvent->NotifyKML = true;
|
||||
}
|
||||
newEvent->Notify = true;
|
||||
// Check here for new <description> tag that gets echoed
|
||||
string notify_description = notify_element->FindElementValue("description");
|
||||
if (!notify_description.empty()) {
|
||||
newEvent->Description = notify_description;
|
||||
}
|
||||
notify_property_element = notify_element->FindElement("property");
|
||||
while (notify_property_element) {
|
||||
notifyPropertyName = notify_property_element->GetDataLine();
|
||||
|
||||
if (notify_property_element->HasAttribute("apply")) {
|
||||
string function_str = notify_property_element->GetAttributeValue("apply");
|
||||
FGTemplateFunc* f = FDMExec->GetTemplateFunc(function_str);
|
||||
if (f)
|
||||
newEvent->NotifyProperties.push_back(new FGFunctionValue(notifyPropertyName, PropertyManager, f));
|
||||
else {
|
||||
cerr << notify_property_element->ReadFrom()
|
||||
<< fgred << highint << " No function by the name "
|
||||
<< function_str << " has been defined. This property will "
|
||||
<< "not be logged. You should check your configuration file."
|
||||
<< reset << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
newEvent->NotifyProperties.push_back(new FGPropertyValue(notifyPropertyName, PropertyManager));
|
||||
|
||||
string caption_attribute = notify_property_element->GetAttributeValue("caption");
|
||||
if (caption_attribute.empty()) {
|
||||
newEvent->DisplayString.push_back(notifyPropertyName);
|
||||
} else {
|
||||
newEvent->DisplayString.push_back(caption_attribute);
|
||||
}
|
||||
|
||||
notify_property_element = notify_element->FindNextElement("property");
|
||||
}
|
||||
}
|
||||
|
||||
// Read set definitions (these define the actions to be taken when the event
|
||||
// is triggered).
|
||||
set_element = event_element->FindElement("set");
|
||||
while (set_element) {
|
||||
prop_name = set_element->GetAttributeValue("name");
|
||||
if (PropertyManager->HasNode(prop_name)) {
|
||||
newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
|
||||
} else {
|
||||
newEvent->SetParam.push_back( 0L );
|
||||
}
|
||||
newEvent->SetParamName.push_back( prop_name );
|
||||
|
||||
// Todo - should probably do some safety checking here to make sure one or
|
||||
// the other of value or function is specified.
|
||||
if (!set_element->GetAttributeValue("value").empty()) {
|
||||
value = set_element->GetAttributeValueAsNumber("value");
|
||||
newEvent->Functions.push_back(nullptr);
|
||||
} else if (set_element->FindElement("function")) {
|
||||
value = 0.0;
|
||||
newEvent->Functions.push_back(new FGFunction(FDMExec, set_element->FindElement("function")));
|
||||
}
|
||||
newEvent->SetValue.push_back(value);
|
||||
newEvent->OriginalValue.push_back(0.0);
|
||||
newEvent->newValue.push_back(0.0);
|
||||
newEvent->ValueSpan.push_back(0.0);
|
||||
string tempCompare = set_element->GetAttributeValue("type");
|
||||
if (to_lower(tempCompare).find("delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
|
||||
else if (to_lower(tempCompare).find("bool") != string::npos) newEvent->Type.push_back(FG_BOOL);
|
||||
else if (to_lower(tempCompare).find("value") != string::npos) newEvent->Type.push_back(FG_VALUE);
|
||||
else newEvent->Type.push_back(FG_VALUE); // DEFAULT
|
||||
tempCompare = set_element->GetAttributeValue("action");
|
||||
if (to_lower(tempCompare).find("ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
|
||||
else if (to_lower(tempCompare).find("step") != string::npos) newEvent->Action.push_back(FG_STEP);
|
||||
else if (to_lower(tempCompare).find("exp") != string::npos) newEvent->Action.push_back(FG_EXP);
|
||||
else newEvent->Action.push_back(FG_STEP); // DEFAULT
|
||||
|
||||
if (!set_element->GetAttributeValue("tc").empty())
|
||||
newEvent->TC.push_back(set_element->GetAttributeValueAsNumber("tc"));
|
||||
else
|
||||
newEvent->TC.push_back(1.0); // DEFAULT
|
||||
|
||||
newEvent->Transiting.push_back(false);
|
||||
|
||||
set_element = event_element->FindNextElement("set");
|
||||
}
|
||||
Events.push_back(*newEvent);
|
||||
delete newEvent;
|
||||
|
||||
event_element = run_element->FindNextElement("event");
|
||||
}
|
||||
|
||||
Debug(4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGScript::ResetEvents(void)
|
||||
{
|
||||
LocalProperties.ResetToIC();
|
||||
FDMExec->Setsim_time(StartTime);
|
||||
|
||||
for (unsigned int i=0; i<Events.size(); i++)
|
||||
Events[i].reset();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGScript::RunScript(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
unsigned event_ctr = 0;
|
||||
|
||||
double currentTime = FDMExec->GetSimTime();
|
||||
double newSetValue = 0;
|
||||
|
||||
if (currentTime > EndTime) return false;
|
||||
|
||||
// Iterate over all events.
|
||||
for (unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
|
||||
|
||||
struct event &thisEvent = Events[ev_ctr];
|
||||
|
||||
// Determine whether the set of conditional tests for this condition equate
|
||||
// to true and should cause the event to execute. If the conditions evaluate
|
||||
// to true, then the event is triggered. If the event is not persistent,
|
||||
// then this trigger will remain set true. If the event is persistent, the
|
||||
// trigger will reset to false when the condition evaluates to false.
|
||||
if (thisEvent.Condition->Evaluate()) {
|
||||
if (!thisEvent.Triggered) {
|
||||
|
||||
// The conditions are true, do the setting of the desired Event
|
||||
// parameters
|
||||
for (i=0; i<thisEvent.SetValue.size(); i++) {
|
||||
if (thisEvent.SetParam[i] == 0L) { // Late bind property if necessary
|
||||
if (PropertyManager->HasNode(thisEvent.SetParamName[i])) {
|
||||
thisEvent.SetParam[i] = PropertyManager->GetNode(thisEvent.SetParamName[i]);
|
||||
} else {
|
||||
throw("No property, \""+thisEvent.SetParamName[i]+"\" is defined.");
|
||||
}
|
||||
}
|
||||
thisEvent.OriginalValue[i] = thisEvent.SetParam[i]->getDoubleValue();
|
||||
if (thisEvent.Functions[i] != 0) { // Parameter should be set to a function value
|
||||
try {
|
||||
thisEvent.SetValue[i] = thisEvent.Functions[i]->GetValue();
|
||||
} catch (string& msg) {
|
||||
std::cerr << std::endl << "A problem occurred in the execution of the script. " << msg << endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
switch (thisEvent.Type[i]) {
|
||||
case FG_VALUE:
|
||||
case FG_BOOL:
|
||||
thisEvent.newValue[i] = thisEvent.SetValue[i];
|
||||
break;
|
||||
case FG_DELTA:
|
||||
thisEvent.newValue[i] = thisEvent.OriginalValue[i] + thisEvent.SetValue[i];
|
||||
break;
|
||||
default:
|
||||
cerr << "Invalid Type specified" << endl;
|
||||
break;
|
||||
}
|
||||
thisEvent.StartTime = currentTime + thisEvent.Delay;
|
||||
thisEvent.ValueSpan[i] = thisEvent.newValue[i] - thisEvent.OriginalValue[i];
|
||||
thisEvent.Transiting[i] = true;
|
||||
}
|
||||
}
|
||||
thisEvent.Triggered = true;
|
||||
|
||||
} else if (thisEvent.Persistent) { // If the event is persistent, reset the trigger.
|
||||
thisEvent.Triggered = false; // Reset the trigger for persistent events
|
||||
thisEvent.Notified = false; // Also reset the notification flag
|
||||
} else if (thisEvent.Continuous) { // If the event is continuous, reset the trigger.
|
||||
thisEvent.Triggered = false; // Reset the trigger for persistent events
|
||||
thisEvent.Notified = false; // Also reset the notification flag
|
||||
}
|
||||
|
||||
if ((currentTime >= thisEvent.StartTime) && thisEvent.Triggered) {
|
||||
|
||||
for (i=0; i<thisEvent.SetValue.size(); i++) {
|
||||
if (thisEvent.Transiting[i]) {
|
||||
thisEvent.TimeSpan = currentTime - thisEvent.StartTime;
|
||||
switch (thisEvent.Action[i]) {
|
||||
case FG_RAMP:
|
||||
if (thisEvent.TimeSpan <= thisEvent.TC[i]) {
|
||||
newSetValue = thisEvent.TimeSpan/thisEvent.TC[i] * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
|
||||
} else {
|
||||
newSetValue = thisEvent.newValue[i];
|
||||
if (thisEvent.Continuous != true) thisEvent.Transiting[i] = false;
|
||||
}
|
||||
break;
|
||||
case FG_STEP:
|
||||
newSetValue = thisEvent.newValue[i];
|
||||
|
||||
// If this is not a continuous event, reset the transiting flag.
|
||||
// Otherwise, it is known that the event is a continuous event.
|
||||
// Furthermore, if the event is to be determined by a function,
|
||||
// then the function will be continuously calculated.
|
||||
if (thisEvent.Continuous != true)
|
||||
thisEvent.Transiting[i] = false;
|
||||
else if (thisEvent.Functions[i] != 0)
|
||||
newSetValue = thisEvent.Functions[i]->GetValue();
|
||||
|
||||
break;
|
||||
case FG_EXP:
|
||||
newSetValue = (1 - exp( -thisEvent.TimeSpan/thisEvent.TC[i] )) * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
|
||||
break;
|
||||
default:
|
||||
cerr << "Invalid Action specified" << endl;
|
||||
break;
|
||||
}
|
||||
thisEvent.SetParam[i]->setDoubleValue(newSetValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Print notification values after setting them
|
||||
if (thisEvent.Notify && !thisEvent.Notified) {
|
||||
if (thisEvent.NotifyKML) {
|
||||
cout << endl << "<Placemark>" << endl;
|
||||
cout << " <name> " << currentTime << " seconds" << " </name>"
|
||||
<< endl;
|
||||
cout << " <description>" << endl;
|
||||
cout << " <![CDATA[" << endl;
|
||||
cout << " <b>" << thisEvent.Name << " (Event " << event_ctr << ")"
|
||||
<< " executed at time: " << currentTime << "</b><br/>" << endl;
|
||||
} else {
|
||||
cout << endl << underon
|
||||
<< highint << thisEvent.Name << normint << underoff
|
||||
<< " (Event " << event_ctr << ")"
|
||||
<< " executed at time: " << highint << currentTime << normint
|
||||
<< endl;
|
||||
}
|
||||
if (!thisEvent.Description.empty()) {
|
||||
cout << " " << thisEvent.Description << endl;
|
||||
}
|
||||
for (j=0; j<thisEvent.NotifyProperties.size();j++) {
|
||||
cout << " " << thisEvent.DisplayString[j] << " = "
|
||||
<< thisEvent.NotifyProperties[j]->getDoubleValue();
|
||||
if (thisEvent.NotifyKML) cout << " <br/>";
|
||||
cout << endl;
|
||||
}
|
||||
if (thisEvent.NotifyKML) {
|
||||
cout << " ]]>" << endl;
|
||||
cout << " </description>" << endl;
|
||||
cout << " <Point>" << endl;
|
||||
cout << " <altitudeMode> absolute </altitudeMode>" << endl;
|
||||
cout << " <extrude> 1 </extrude>" << endl;
|
||||
cout << " <coordinates>"
|
||||
<< FDMExec->GetPropagate()->GetLongitudeDeg() << ","
|
||||
<< FDMExec->GetPropagate()->GetGeodLatitudeDeg() << ","
|
||||
<< FDMExec->GetPropagate()->GetAltitudeASLmeters()
|
||||
<< "</coordinates>" << endl;
|
||||
cout << " </Point>" << endl;
|
||||
cout << "</Placemark>" << endl;
|
||||
}
|
||||
cout << endl;
|
||||
thisEvent.Notified = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
event_ctr++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGScript::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
} else if (from == 3) {
|
||||
} else if (from == 4) { // print out script data
|
||||
cout << endl;
|
||||
cout << "Script: \"" << ScriptName << "\"" << endl;
|
||||
cout << " begins at " << StartTime << " seconds and runs to " << EndTime
|
||||
<< " seconds with dt = " << setprecision(6) << FDMExec->GetDeltaT()
|
||||
<< " (" << ceil(1.0/FDMExec->GetDeltaT()) << " Hz)" << endl;
|
||||
cout << endl;
|
||||
|
||||
for (auto node: LocalProperties) {
|
||||
cout << "Local property: " << node->GetName()
|
||||
<< " = " << node->getDoubleValue()
|
||||
<< endl;
|
||||
}
|
||||
|
||||
if (LocalProperties.empty()) cout << endl;
|
||||
|
||||
for (unsigned i=0; i<Events.size(); i++) {
|
||||
cout << "Event " << i;
|
||||
if (!Events[i].Name.empty()) cout << " (" << Events[i].Name << ")";
|
||||
cout << ":" << endl;
|
||||
|
||||
if (Events[i].Persistent)
|
||||
cout << " " << "Whenever triggered, executes once";
|
||||
else if (Events[i].Continuous)
|
||||
cout << " " << "While true, always executes";
|
||||
else
|
||||
cout << " " << "When first triggered, executes once";
|
||||
|
||||
Events[i].Condition->PrintCondition();
|
||||
|
||||
cout << endl << " Actions taken";
|
||||
if (Events[i].Delay > 0.0)
|
||||
cout << " (after a delay of " << Events[i].Delay << " secs)";
|
||||
cout << ":" << endl << " {";
|
||||
for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
|
||||
if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
|
||||
if (Events[i].SetParam[j] == 0) {
|
||||
if (Events[i].SetParamName[j].empty()) {
|
||||
stringstream s;
|
||||
s << " An attempt has been made to access a non-existent property" << endl
|
||||
<< " in this event. Please check the property names used, spelling, etc.";
|
||||
cerr << fgred << highint << endl << s.str() << reset << endl;
|
||||
throw BaseException(s.str());
|
||||
} else {
|
||||
cout << endl << " set " << Events[i].SetParamName[j]
|
||||
<< " to function value (Late Bound)";
|
||||
}
|
||||
} else {
|
||||
cout << endl << " set "
|
||||
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
|
||||
<< " to function value";
|
||||
}
|
||||
} else {
|
||||
if (Events[i].SetParam[j] == 0) {
|
||||
if (Events[i].SetParamName[j].empty()) {
|
||||
stringstream s;
|
||||
s << " An attempt has been made to access a non-existent property" << endl
|
||||
<< " in this event. Please check the property names used, spelling, etc.";
|
||||
cerr << fgred << highint << endl << s.str() << reset << endl;
|
||||
throw BaseException(s.str());
|
||||
} else {
|
||||
cout << endl << " set " << Events[i].SetParamName[j]
|
||||
<< " to function value (Late Bound)";
|
||||
}
|
||||
} else {
|
||||
cout << endl << " set "
|
||||
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
|
||||
<< " to " << Events[i].SetValue[j];
|
||||
}
|
||||
}
|
||||
|
||||
switch (Events[i].Type[j]) {
|
||||
case FG_VALUE:
|
||||
case FG_BOOL:
|
||||
cout << " (constant";
|
||||
break;
|
||||
case FG_DELTA:
|
||||
cout << " (delta";
|
||||
break;
|
||||
default:
|
||||
cout << " (unspecified type";
|
||||
}
|
||||
|
||||
switch (Events[i].Action[j]) {
|
||||
case FG_RAMP:
|
||||
cout << " via ramp";
|
||||
break;
|
||||
case FG_STEP:
|
||||
cout << " via step)";
|
||||
break;
|
||||
case FG_EXP:
|
||||
cout << " via exponential approach";
|
||||
break;
|
||||
default:
|
||||
cout << " via unspecified action)";
|
||||
}
|
||||
|
||||
if (Events[i].Action[j] == FG_RAMP || Events[i].Action[j] == FG_EXP)
|
||||
cout << " with time constant " << Events[i].TC[j] << ")";
|
||||
}
|
||||
cout << endl << " }" << endl;
|
||||
|
||||
// Print notifications
|
||||
if (Events[i].Notify) {
|
||||
if (Events[i].NotifyProperties.size() > 0) {
|
||||
if (Events[i].NotifyKML) {
|
||||
cout << " Notifications (KML Format):" << endl << " {"
|
||||
<< endl;
|
||||
} else {
|
||||
cout << " Notifications:" << endl << " {" << endl;
|
||||
}
|
||||
for (unsigned j=0; j<Events[i].NotifyProperties.size();j++) {
|
||||
cout << " "
|
||||
<< Events[i].NotifyProperties[j]->GetPrintableName()
|
||||
<< endl;
|
||||
}
|
||||
cout << " }" << endl;
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGScript" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGScript" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
267
src/FDM/JSBSim/input_output/FGScript.h
Normal file
267
src/FDM/JSBSim/input_output/FGScript.h
Normal file
@@ -0,0 +1,267 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
Header: FGScript.h
|
||||
Author: Jon Berndt
|
||||
Date started: 12/21/2001
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/21/01 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGSCRIPT_HEADER_H
|
||||
#define FGSCRIPT_HEADER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGPropertyReader.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
#include "simgear/misc/sg_path.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGFDMExec;
|
||||
class FGCondition;
|
||||
class FGFunction;
|
||||
class FGPropertyValue;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates the JSBSim scripting capability.
|
||||
<h4>Scripting support provided via FGScript.</h4>
|
||||
|
||||
<p>There is support for scripting provided in the FGScript
|
||||
class. Commands are specified using the <em>Scripting
|
||||
Directives for JSBSim</em>. The script file is in XML
|
||||
format. A test condition (or conditions) can be set up in an event in a
|
||||
script and when the condition evaluates to true, the specified
|
||||
action[s] is/are taken. An event can be <em>persistent</em>,
|
||||
meaning that at every time the test condition first evaluates to true
|
||||
(toggling from false to true) then the specified <em>set</em> actions take
|
||||
place. An event can also be defined to execute or evaluate continuously
|
||||
while the condition is true. When the set of tests evaluates to true for a given
|
||||
condition, an item may be set to another value. This value may
|
||||
be a value, or a delta value, and the change from the
|
||||
current value to the new value can be either via a step action,
|
||||
a ramp, or an exponential approach. The speed of a ramp or exponential
|
||||
approach is specified via the time constant. Here is an example
|
||||
illustrating the format of the script file:
|
||||
|
||||
@code
|
||||
<?xml version="1.0"?>
|
||||
<runscript name="C172-01A takeoff run">
|
||||
<!--
|
||||
This run is for testing the C172 altitude hold autopilot
|
||||
-->
|
||||
|
||||
<use aircraft="c172x"/>
|
||||
<use initialize="reset00"/>
|
||||
<run start="0.0" end="3000" dt="0.0083333">
|
||||
|
||||
<event name="engine start">
|
||||
<notify/>
|
||||
<condition>
|
||||
sim-time-sec >= 0.25
|
||||
</condition>
|
||||
<set name="fcs/throttle-cmd-norm" value="1.0" action="FG_RAMP" tc ="0.5"/>
|
||||
<set name="fcs/mixture-cmd-norm" value="0.87" action="FG_RAMP" tc ="0.5"/>
|
||||
<set name="propulsion/magneto_cmd" value="3"/>
|
||||
<set name="propulsion/starter_cmd" value="1"/>
|
||||
</event>
|
||||
|
||||
<event name="set heading hold">
|
||||
<!-- Set Heading when reach 5 ft -->
|
||||
<notify/>
|
||||
<condition>
|
||||
position/h-agl-ft >= 5
|
||||
</condition>
|
||||
<set name="ap/heading_setpoint" value="200"/>
|
||||
<set name="ap/attitude_hold" value="0"/>
|
||||
<set name="ap/heading_hold" value="1"/>
|
||||
</event>
|
||||
|
||||
<event name="set autopilot">
|
||||
<!-- Set Autopilot for 20 ft -->
|
||||
<notify/>
|
||||
<condition>
|
||||
aero/qbar-psf >= 4
|
||||
</condition>
|
||||
<set name="ap/altitude_setpoint" value="100.0" action="FG_EXP" tc ="2.0"/>
|
||||
<set name="ap/altitude_hold" value="1"/>
|
||||
<set name="fcs/flap-cmd-norm" value=".33"/>
|
||||
</event>
|
||||
|
||||
<event name="set autopilot 2" persistent="true">
|
||||
<!-- Set Autopilot for 6000 ft -->
|
||||
<notify/>
|
||||
<condition>
|
||||
aero/qbar-psf > 5
|
||||
</condition>
|
||||
<set name="ap/altitude_setpoint" value="6000.0"/>
|
||||
</event>
|
||||
|
||||
<event name="Time Notify">
|
||||
<notify/>
|
||||
<condition> sim-time-sec >= 500 </condition>
|
||||
</event>
|
||||
|
||||
<event name="Time Notify">
|
||||
<notify/>
|
||||
<condition> sim-time-sec >= 1000 </condition>
|
||||
</event>
|
||||
|
||||
</run>
|
||||
|
||||
</runscript>
|
||||
@endcode
|
||||
|
||||
The first line must always be present - it identifies the file
|
||||
as an XML format file. The second line
|
||||
identifies this file as a script file, and gives a descriptive
|
||||
name to the script file. Comments are next, delineated by the
|
||||
<!-- and --> symbols. The aircraft and initialization files
|
||||
to be used are specified in the "use" lines. Next,
|
||||
comes the "run" section, where the conditions are
|
||||
described in "event" clauses.</p>
|
||||
@author Jon S. Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGScript : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
FGScript(FGFDMExec* exec);
|
||||
|
||||
/// Default destructor
|
||||
~FGScript();
|
||||
|
||||
/** Loads a script to drive JSBSim (usually in standalone mode).
|
||||
The language is the Script Directives for JSBSim. If a simulation step
|
||||
size has been supplied on the command line, it will override the script
|
||||
specified simulation step size.
|
||||
@param script the filename (including path name, if any) for the script.
|
||||
@param default_dT the default simulation step size if no value is specified
|
||||
in the script
|
||||
@param initfile An optional initialization file name passed in, empty by
|
||||
default. If a file name is passed in, it will override the
|
||||
one present in the script.
|
||||
@return true if successful */
|
||||
bool LoadScript(const SGPath& script, double default_dT,
|
||||
const SGPath& initfile);
|
||||
|
||||
/** This function is called each pass through the executive Run() method IF
|
||||
scripting is enabled.
|
||||
@return false if script should exit (i.e. if time limits are violated */
|
||||
bool RunScript(void);
|
||||
|
||||
void ResetEvents(void);
|
||||
|
||||
private:
|
||||
enum eAction {
|
||||
FG_RAMP = 1,
|
||||
FG_STEP = 2,
|
||||
FG_EXP = 3
|
||||
};
|
||||
|
||||
enum eType {
|
||||
FG_VALUE = 1,
|
||||
FG_DELTA = 2,
|
||||
FG_BOOL = 3
|
||||
};
|
||||
|
||||
struct event {
|
||||
FGCondition *Condition;
|
||||
bool Persistent;
|
||||
bool Continuous;
|
||||
bool Triggered;
|
||||
bool Notify;
|
||||
bool NotifyKML;
|
||||
bool Notified;
|
||||
double Delay;
|
||||
double StartTime;
|
||||
double TimeSpan;
|
||||
std::string Name;
|
||||
std::string Description;
|
||||
std::vector <FGPropertyNode_ptr> SetParam;
|
||||
std::vector <std::string> SetParamName;
|
||||
std::vector <FGPropertyValue*> NotifyProperties;
|
||||
std::vector <std::string> DisplayString;
|
||||
std::vector <eAction> Action;
|
||||
std::vector <eType> Type;
|
||||
std::vector <double> SetValue;
|
||||
std::vector <double> TC;
|
||||
std::vector <double> newValue;
|
||||
std::vector <double> OriginalValue;
|
||||
std::vector <double> ValueSpan;
|
||||
std::vector <bool> Transiting;
|
||||
std::vector <FGFunction*> Functions;
|
||||
|
||||
event() {
|
||||
Triggered = false;
|
||||
Persistent = false;
|
||||
Continuous = false;
|
||||
Delay = 0.0;
|
||||
Notify = Notified = NotifyKML = false;
|
||||
Name = "";
|
||||
StartTime = 0.0;
|
||||
TimeSpan = 0.0;
|
||||
}
|
||||
|
||||
void reset(void) {
|
||||
Triggered = false;
|
||||
Notified = false;
|
||||
StartTime = 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
std::string ScriptName;
|
||||
double StartTime;
|
||||
double EndTime;
|
||||
std::vector <struct event> Events;
|
||||
|
||||
FGPropertyReader LocalProperties;
|
||||
|
||||
FGFDMExec* FDMExec;
|
||||
FGPropertyManager* PropertyManager;
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
131
src/FDM/JSBSim/input_output/FGUDPInputSocket.cpp
Normal file
131
src/FDM/JSBSim/input_output/FGUDPInputSocket.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGUDPInputSocket.cpp
|
||||
Author: Dave Culp
|
||||
Date started: 02/19/15
|
||||
Purpose: Manage input of data from a UDP socket
|
||||
Called by: FGInput
|
||||
|
||||
------------- Copyright (C) 2015 Dave Culp --------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This class establishes a UDP socket and reads data from it.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
02/19/15 DC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include "FGUDPInputSocket.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGUDPInputSocket::FGUDPInputSocket(FGFDMExec* fdmex) :
|
||||
FGInputSocket(fdmex), rate(20), oldTimeStamp(0.0)
|
||||
{
|
||||
SockPort = 5139;
|
||||
SockProtocol = FGfdmSocket::ptUDP;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGUDPInputSocket::Load(Element* el)
|
||||
{
|
||||
if (!FGInputSocket::Load(el))
|
||||
return false;
|
||||
|
||||
rate = atoi(el->GetAttributeValue("rate").c_str());
|
||||
SetRate(0.5 + 1.0/(FDMExec->GetDeltaT()*rate));
|
||||
|
||||
Element *property_element = el->FindElement("property");
|
||||
|
||||
while (property_element) {
|
||||
string property_str = property_element->GetDataLine();
|
||||
FGPropertyNode* node = PropertyManager->GetNode(property_str);
|
||||
if (!node) {
|
||||
cerr << fgred << highint << endl << " No property by the name "
|
||||
<< property_str << " can be found." << reset << endl;
|
||||
} else {
|
||||
InputProperties.push_back(node);
|
||||
}
|
||||
property_element = el->FindNextElement("property");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGUDPInputSocket::Read(bool Holding)
|
||||
{
|
||||
if (socket == 0) return;
|
||||
|
||||
data = socket->Receive();
|
||||
|
||||
if (data.size() > 0) {
|
||||
|
||||
vector<string> tokens;
|
||||
stringstream ss(data);
|
||||
string temp;
|
||||
while (getline(ss, temp, ',')) {
|
||||
tokens.push_back(temp);
|
||||
}
|
||||
|
||||
vector<double> values;
|
||||
|
||||
for (unsigned int i=0; i<tokens.size(); i++) {
|
||||
values.push_back( atof(tokens[i].c_str()) );
|
||||
}
|
||||
|
||||
if (values[0] < oldTimeStamp) {
|
||||
return;
|
||||
} else {
|
||||
oldTimeStamp = values[0];
|
||||
}
|
||||
|
||||
// the zeroeth value is the time stamp
|
||||
if ((values.size() - 1) != InputProperties.size()) {
|
||||
cerr << endl << "Mismatch between UDP input property and value counts." << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i=1; i<values.size(); i++) {
|
||||
InputProperties[i-1]->setDoubleValue(values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
82
src/FDM/JSBSim/input_output/FGUDPInputSocket.h
Normal file
82
src/FDM/JSBSim/input_output/FGUDPInputSocket.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGUDPInputSocket.h
|
||||
Author: Dave Culp
|
||||
Date started: 02/19/15
|
||||
|
||||
------------- Copyright (C) 2015 David Culp -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
02/19/15 DC Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGUDPINPUTSOCKET_H
|
||||
#define FGUDPINPUTSOCKET_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGInputSocket.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Implements a UDP input socket.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGUDPInputSocket : public FGInputSocket
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
FGUDPInputSocket(FGFDMExec* fdmex);
|
||||
|
||||
/** Reads the property names from an XML file.
|
||||
@param element The root XML Element of the input file.
|
||||
*/
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/// Reads the socket and updates properties accordingly.
|
||||
void Read(bool Holding) override;
|
||||
|
||||
protected:
|
||||
|
||||
int rate;
|
||||
double oldTimeStamp;
|
||||
std::vector<FGPropertyNode_ptr> InputProperties;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
768
src/FDM/JSBSim/input_output/FGXMLElement.cpp
Normal file
768
src/FDM/JSBSim/input_output/FGXMLElement.cpp
Normal file
@@ -0,0 +1,768 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Author: Jon Berndt
|
||||
Date started: 09/28/2004
|
||||
Purpose: XML element class
|
||||
Called by: FGXMLParse
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <sstream> // for assembling the error messages / what of exceptions.
|
||||
#include <stdexcept> // using domain_error, invalid_argument, and length_error.
|
||||
#include "FGXMLElement.h"
|
||||
#include "FGJSBBase.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
bool Element::converterIsInitialized = false;
|
||||
map <string, map <string, double> > Element::convert;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
Element::Element(const string& nm)
|
||||
{
|
||||
name = nm;
|
||||
parent = 0L;
|
||||
element_index = 0;
|
||||
line_number = -1;
|
||||
|
||||
if (!converterIsInitialized) {
|
||||
converterIsInitialized = true;
|
||||
// convert ["from"]["to"] = factor, so: from * factor = to
|
||||
// Length
|
||||
convert["M"]["FT"] = 3.2808399;
|
||||
convert["FT"]["M"] = 1.0/convert["M"]["FT"];
|
||||
convert["CM"]["FT"] = 0.032808399;
|
||||
convert["FT"]["CM"] = 1.0/convert["CM"]["FT"];
|
||||
convert["KM"]["FT"] = 3280.8399;
|
||||
convert["FT"]["KM"] = 1.0/convert["KM"]["FT"];
|
||||
convert["FT"]["IN"] = 12.0;
|
||||
convert["IN"]["FT"] = 1.0/convert["FT"]["IN"];
|
||||
convert["IN"]["M"] = convert["IN"]["FT"] * convert["FT"]["M"];
|
||||
convert["M"]["IN"] = convert["M"]["FT"] * convert["FT"]["IN"];
|
||||
// Area
|
||||
convert["M2"]["FT2"] = convert["M"]["FT"]*convert["M"]["FT"];
|
||||
convert["FT2"]["M2"] = 1.0/convert["M2"]["FT2"];
|
||||
convert["CM2"]["FT2"] = convert["CM"]["FT"]*convert["CM"]["FT"];
|
||||
convert["FT2"]["CM2"] = 1.0/convert["CM2"]["FT2"];
|
||||
convert["M2"]["IN2"] = convert["M"]["IN"]*convert["M"]["IN"];
|
||||
convert["IN2"]["M2"] = 1.0/convert["M2"]["IN2"];
|
||||
convert["FT2"]["IN2"] = 144.0;
|
||||
convert["IN2"]["FT2"] = 1.0/convert["FT2"]["IN2"];
|
||||
// Volume
|
||||
convert["IN3"]["CC"] = 16.387064;
|
||||
convert["CC"]["IN3"] = 1.0/convert["IN3"]["CC"];
|
||||
convert["FT3"]["IN3"] = 1728.0;
|
||||
convert["IN3"]["FT3"] = 1.0/convert["FT3"]["IN3"];
|
||||
convert["M3"]["FT3"] = 35.3146667;
|
||||
convert["FT3"]["M3"] = 1.0/convert["M3"]["FT3"];
|
||||
convert["LTR"]["IN3"] = 61.0237441;
|
||||
convert["IN3"]["LTR"] = 1.0/convert["LTR"]["IN3"];
|
||||
convert["GAL"]["FT3"] = 0.133681;
|
||||
convert["FT3"]["GAL"] = 1.0/convert["GAL"]["FT3"];
|
||||
convert["IN3"]["GAL"] = convert["IN3"]["FT3"]*convert["FT3"]["GAL"];
|
||||
convert["LTR"]["GAL"] = convert["LTR"]["IN3"]*convert["IN3"]["GAL"];
|
||||
convert["M3"]["GAL"] = 1000.*convert["LTR"]["GAL"];
|
||||
convert["CC"]["GAL"] = convert["CC"]["IN3"]*convert["IN3"]["GAL"];
|
||||
// Mass & Weight
|
||||
convert["LBS"]["KG"] = 0.45359237;
|
||||
convert["KG"]["LBS"] = 1.0/convert["LBS"]["KG"];
|
||||
convert["SLUG"]["KG"] = 14.59390;
|
||||
convert["KG"]["SLUG"] = 1.0/convert["SLUG"]["KG"];
|
||||
// Moments of Inertia
|
||||
convert["SLUG*FT2"]["KG*M2"] = 1.35594;
|
||||
convert["KG*M2"]["SLUG*FT2"] = 1.0/convert["SLUG*FT2"]["KG*M2"];
|
||||
// Angles
|
||||
convert["RAD"]["DEG"] = 180.0/M_PI;
|
||||
convert["DEG"]["RAD"] = 1.0/convert["RAD"]["DEG"];
|
||||
// Angular rates
|
||||
convert["RAD/SEC"]["DEG/SEC"] = convert["RAD"]["DEG"];
|
||||
convert["DEG/SEC"]["RAD/SEC"] = 1.0/convert["RAD/SEC"]["DEG/SEC"];
|
||||
// Spring force
|
||||
convert["LBS/FT"]["N/M"] = 14.5939;
|
||||
convert["N/M"]["LBS/FT"] = 1.0/convert["LBS/FT"]["N/M"];
|
||||
// Damping force
|
||||
convert["LBS/FT/SEC"]["N/M/SEC"] = 14.5939;
|
||||
convert["N/M/SEC"]["LBS/FT/SEC"] = 1.0/convert["LBS/FT/SEC"]["N/M/SEC"];
|
||||
// Damping force (Square Law)
|
||||
convert["LBS/FT2/SEC2"]["N/M2/SEC2"] = 47.880259;
|
||||
convert["N/M2/SEC2"]["LBS/FT2/SEC2"] = 1.0/convert["LBS/FT2/SEC2"]["N/M2/SEC2"];
|
||||
// Power
|
||||
convert["WATTS"]["HP"] = 0.001341022;
|
||||
convert["HP"]["WATTS"] = 1.0/convert["WATTS"]["HP"];
|
||||
// Force
|
||||
convert["N"]["LBS"] = 0.22482;
|
||||
convert["LBS"]["N"] = 1.0/convert["N"]["LBS"];
|
||||
// Velocity
|
||||
convert["KTS"]["FT/SEC"] = 1.68781;
|
||||
convert["FT/SEC"]["KTS"] = 1.0/convert["KTS"]["FT/SEC"];
|
||||
convert["M/S"]["FT/S"] = 3.2808399;
|
||||
convert["M/S"]["KTS"] = convert["M/S"]["FT/S"]/convert["KTS"]["FT/SEC"];
|
||||
convert["M/SEC"]["FT/SEC"] = 3.2808399;
|
||||
convert["FT/S"]["M/S"] = 1.0/convert["M/S"]["FT/S"];
|
||||
convert["M/SEC"]["FT/SEC"] = 3.2808399;
|
||||
convert["FT/SEC"]["M/SEC"] = 1.0/convert["M/SEC"]["FT/SEC"];
|
||||
convert["KM/SEC"]["FT/SEC"] = 3280.8399;
|
||||
convert["FT/SEC"]["KM/SEC"] = 1.0/convert["KM/SEC"]["FT/SEC"];
|
||||
// Torque
|
||||
convert["FT*LBS"]["N*M"] = 1.35581795;
|
||||
convert["N*M"]["FT*LBS"] = 1/convert["FT*LBS"]["N*M"];
|
||||
// Valve
|
||||
convert["M4*SEC/KG"]["FT4*SEC/SLUG"] = convert["M"]["FT"]*convert["M"]["FT"]*
|
||||
convert["M"]["FT"]*convert["M"]["FT"]/convert["KG"]["SLUG"];
|
||||
convert["FT4*SEC/SLUG"]["M4*SEC/KG"] =
|
||||
1.0/convert["M4*SEC/KG"]["FT4*SEC/SLUG"];
|
||||
// Pressure
|
||||
convert["INHG"]["PSF"] = 70.7180803;
|
||||
convert["PSF"]["INHG"] = 1.0/convert["INHG"]["PSF"];
|
||||
convert["ATM"]["INHG"] = 29.9246899;
|
||||
convert["INHG"]["ATM"] = 1.0/convert["ATM"]["INHG"];
|
||||
convert["PSI"]["INHG"] = 2.03625437;
|
||||
convert["INHG"]["PSI"] = 1.0/convert["PSI"]["INHG"];
|
||||
convert["INHG"]["PA"] = 3386.0; // inches Mercury to pascals
|
||||
convert["PA"]["INHG"] = 1.0/convert["INHG"]["PA"];
|
||||
convert["LBS/FT2"]["N/M2"] = 14.5939/convert["FT"]["M"];
|
||||
convert["N/M2"]["LBS/FT2"] = 1.0/convert["LBS/FT2"]["N/M2"];
|
||||
convert["LBS/FT2"]["PA"] = convert["LBS/FT2"]["N/M2"];
|
||||
convert["PA"]["LBS/FT2"] = 1.0/convert["LBS/FT2"]["PA"];
|
||||
// Mass flow
|
||||
convert["KG/MIN"]["LBS/MIN"] = convert["KG"]["LBS"];
|
||||
convert["KG/SEC"]["LBS/SEC"] = convert["KG"]["LBS"];
|
||||
convert ["N/SEC"]["LBS/SEC"] = 0.224808943;
|
||||
convert ["LBS/SEC"]["N/SEC"] = 1.0/convert ["N/SEC"]["LBS/SEC"];
|
||||
// Fuel Consumption
|
||||
convert["LBS/HP*HR"]["KG/KW*HR"] = 0.6083;
|
||||
convert["KG/KW*HR"]["LBS/HP*HR"] = 1.0/convert["LBS/HP*HR"]["KG/KW*HR"];
|
||||
// Density
|
||||
convert["KG/L"]["LBS/GAL"] = 8.3454045;
|
||||
convert["LBS/GAL"]["KG/L"] = 1.0/convert["KG/L"]["LBS/GAL"];
|
||||
// Gravitational
|
||||
convert["FT3/SEC2"]["M3/SEC2"] = convert["FT3"]["M3"];
|
||||
convert["M3/SEC2"]["FT3/SEC2"] = convert["M3"]["FT3"];
|
||||
|
||||
// Length
|
||||
convert["M"]["M"] = 1.00;
|
||||
convert["KM"]["KM"] = 1.00;
|
||||
convert["FT"]["FT"] = 1.00;
|
||||
convert["IN"]["IN"] = 1.00;
|
||||
// Area
|
||||
convert["M2"]["M2"] = 1.00;
|
||||
convert["FT2"]["FT2"] = 1.00;
|
||||
// Volume
|
||||
convert["IN3"]["IN3"] = 1.00;
|
||||
convert["CC"]["CC"] = 1.0;
|
||||
convert["M3"]["M3"] = 1.0;
|
||||
convert["FT3"]["FT3"] = 1.0;
|
||||
convert["LTR"]["LTR"] = 1.0;
|
||||
convert["GAL"]["GAL"] = 1.0;
|
||||
// Mass & Weight
|
||||
convert["KG"]["KG"] = 1.00;
|
||||
convert["LBS"]["LBS"] = 1.00;
|
||||
// Moments of Inertia
|
||||
convert["KG*M2"]["KG*M2"] = 1.00;
|
||||
convert["SLUG*FT2"]["SLUG*FT2"] = 1.00;
|
||||
// Angles
|
||||
convert["DEG"]["DEG"] = 1.00;
|
||||
convert["RAD"]["RAD"] = 1.00;
|
||||
// Angular rates
|
||||
convert["DEG/SEC"]["DEG/SEC"] = 1.00;
|
||||
convert["RAD/SEC"]["RAD/SEC"] = 1.00;
|
||||
// Spring force
|
||||
convert["LBS/FT"]["LBS/FT"] = 1.00;
|
||||
convert["N/M"]["N/M"] = 1.00;
|
||||
// Damping force
|
||||
convert["LBS/FT/SEC"]["LBS/FT/SEC"] = 1.00;
|
||||
convert["N/M/SEC"]["N/M/SEC"] = 1.00;
|
||||
// Damping force (Square law)
|
||||
convert["LBS/FT2/SEC2"]["LBS/FT2/SEC2"] = 1.00;
|
||||
convert["N/M2/SEC2"]["N/M2/SEC2"] = 1.00;
|
||||
// Power
|
||||
convert["HP"]["HP"] = 1.00;
|
||||
convert["WATTS"]["WATTS"] = 1.00;
|
||||
// Force
|
||||
convert["N"]["N"] = 1.00;
|
||||
// Velocity
|
||||
convert["FT/SEC"]["FT/SEC"] = 1.00;
|
||||
convert["KTS"]["KTS"] = 1.00;
|
||||
convert["M/S"]["M/S"] = 1.0;
|
||||
convert["M/SEC"]["M/SEC"] = 1.0;
|
||||
convert["KM/SEC"]["KM/SEC"] = 1.0;
|
||||
// Torque
|
||||
convert["FT*LBS"]["FT*LBS"] = 1.00;
|
||||
convert["N*M"]["N*M"] = 1.00;
|
||||
// Valve
|
||||
convert["M4*SEC/KG"]["M4*SEC/KG"] = 1.0;
|
||||
convert["FT4*SEC/SLUG"]["FT4*SEC/SLUG"] = 1.0;
|
||||
// Pressure
|
||||
convert["PSI"]["PSI"] = 1.00;
|
||||
convert["PSF"]["PSF"] = 1.00;
|
||||
convert["INHG"]["INHG"] = 1.00;
|
||||
convert["ATM"]["ATM"] = 1.0;
|
||||
convert["PA"]["PA"] = 1.0;
|
||||
convert["N/M2"]["N/M2"] = 1.00;
|
||||
convert["LBS/FT2"]["LBS/FT2"] = 1.00;
|
||||
// Mass flow
|
||||
convert["LBS/SEC"]["LBS/SEC"] = 1.00;
|
||||
convert["KG/MIN"]["KG/MIN"] = 1.0;
|
||||
convert["LBS/MIN"]["LBS/MIN"] = 1.0;
|
||||
convert["N/SEC"]["N/SEC"] = 1.0;
|
||||
// Fuel Consumption
|
||||
convert["LBS/HP*HR"]["LBS/HP*HR"] = 1.0;
|
||||
convert["KG/KW*HR"]["KG/KW*HR"] = 1.0;
|
||||
// Density
|
||||
convert["KG/L"]["KG/L"] = 1.0;
|
||||
convert["LBS/GAL"]["LBS/GAL"] = 1.0;
|
||||
// Gravitational
|
||||
convert["FT3/SEC2"]["FT3/SEC2"] = 1.0;
|
||||
convert["M3/SEC2"]["M3/SEC2"] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Element::~Element(void)
|
||||
{
|
||||
for (unsigned int i = 0; i < children.size(); ++i)
|
||||
children[i]->SetParent(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string Element::GetAttributeValue(const string& attr)
|
||||
{
|
||||
if (HasAttribute(attr)) return attributes[attr];
|
||||
else return ("");
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool Element::SetAttributeValue(const std::string& key, const std::string& value)
|
||||
{
|
||||
bool ret = HasAttribute(key);
|
||||
if (ret)
|
||||
attributes[key] = value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::GetAttributeValueAsNumber(const string& attr)
|
||||
{
|
||||
string attribute = GetAttributeValue(attr);
|
||||
|
||||
if (attribute.empty()) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Expecting numeric attribute value, but got no data";
|
||||
cerr << s.str() << endl;
|
||||
throw length_error(s.str());
|
||||
}
|
||||
else {
|
||||
double number=0;
|
||||
if (is_number(trim(attribute)))
|
||||
number = atof(attribute.c_str());
|
||||
else {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Expecting numeric attribute value, but got: " << attribute;
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
|
||||
return (number);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Element* Element::GetElement(unsigned int el)
|
||||
{
|
||||
if (children.size() > el) {
|
||||
element_index = el;
|
||||
return children[el];
|
||||
}
|
||||
else {
|
||||
element_index = 0;
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Element* Element::GetNextElement(void)
|
||||
{
|
||||
if (children.size() > element_index+1) {
|
||||
element_index++;
|
||||
return children[element_index];
|
||||
} else {
|
||||
element_index = 0;
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string Element::GetDataLine(unsigned int i)
|
||||
{
|
||||
if (data_lines.size() > 0) return data_lines[i];
|
||||
else return string("");
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::GetDataAsNumber(void)
|
||||
{
|
||||
if (data_lines.size() == 1) {
|
||||
double number=0;
|
||||
if (is_number(trim(data_lines[0])))
|
||||
number = atof(data_lines[0].c_str());
|
||||
else {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Expected numeric value, but got: " << data_lines[0];
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
|
||||
return number;
|
||||
} else if (data_lines.size() == 0) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Expected numeric value, but got no data";
|
||||
cerr << s.str() << endl;
|
||||
throw length_error(s.str());
|
||||
} else {
|
||||
cerr << ReadFrom() << "Attempting to get single data value in element "
|
||||
<< "<" << name << ">" << endl
|
||||
<< " from multiple lines:" << endl;
|
||||
for(unsigned int i=0; i<data_lines.size(); ++i)
|
||||
cerr << data_lines[i] << endl;
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Attempting to get single data value in element "
|
||||
<< "<" << name << ">"
|
||||
<< " from multiple lines (" << data_lines.size() << ").";
|
||||
throw length_error(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
unsigned int Element::GetNumElements(const string& element_name)
|
||||
{
|
||||
unsigned int number_of_elements=0;
|
||||
Element* el=FindElement(element_name);
|
||||
while (el) {
|
||||
number_of_elements++;
|
||||
el=FindNextElement(element_name);
|
||||
}
|
||||
return number_of_elements;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Element* Element::FindElement(const string& el)
|
||||
{
|
||||
if (el.empty() && children.size() >= 1) {
|
||||
element_index = 1;
|
||||
return children[0];
|
||||
}
|
||||
for (unsigned int i=0; i<children.size(); i++) {
|
||||
if (el == children[i]->GetName()) {
|
||||
element_index = i+1;
|
||||
return children[i];
|
||||
}
|
||||
}
|
||||
element_index = 0;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Element* Element::FindNextElement(const string& el)
|
||||
{
|
||||
if (el.empty()) {
|
||||
if (element_index < children.size()) {
|
||||
return children[element_index++];
|
||||
} else {
|
||||
element_index = 0;
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
for (unsigned int i=element_index; i<children.size(); i++) {
|
||||
if (el == children[i]->GetName()) {
|
||||
element_index = i+1;
|
||||
return children[i];
|
||||
}
|
||||
}
|
||||
element_index = 0;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::FindElementValueAsNumber(const string& el)
|
||||
{
|
||||
Element* element = FindElement(el);
|
||||
if (element) {
|
||||
double value = element->GetDataAsNumber();
|
||||
value = DisperseValue(element, value);
|
||||
return value;
|
||||
} else {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Attempting to get non-existent element " << el;
|
||||
cerr << s.str() << endl;
|
||||
throw length_error(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool Element::FindElementValueAsBoolean(const string& el)
|
||||
{
|
||||
Element* element = FindElement(el);
|
||||
if (element) {
|
||||
// check value as an ordinary number
|
||||
double value = element->GetDataAsNumber();
|
||||
|
||||
// now check how it should return data
|
||||
if (value == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
cerr << ReadFrom() << "Attempting to get non-existent element " << el << " ;returning false"
|
||||
<< endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string Element::FindElementValue(const string& el)
|
||||
{
|
||||
Element* element = FindElement(el);
|
||||
if (element) {
|
||||
return element->GetDataLine();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::FindElementValueAsNumberConvertTo(const string& el, const string& target_units)
|
||||
{
|
||||
Element* element = FindElement(el);
|
||||
|
||||
if (!element) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Attempting to get non-existent element " << el;
|
||||
cerr << s.str() << endl;
|
||||
throw length_error(s.str());
|
||||
}
|
||||
|
||||
string supplied_units = element->GetAttributeValue("unit");
|
||||
|
||||
if (!supplied_units.empty()) {
|
||||
if (convert.find(supplied_units) == convert.end()) {
|
||||
std::stringstream s;
|
||||
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" does not exist (typo?).";
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
|
||||
std::stringstream s;
|
||||
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" cannot be converted to " << target_units;
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
double value = element->GetDataAsNumber();
|
||||
|
||||
// Sanity check for angular values
|
||||
if ((supplied_units == "RAD") && (fabs(value) > 2 * M_PI)) {
|
||||
cerr << element->ReadFrom() << element->GetName() << " value "
|
||||
<< value << " RAD is outside the range [ -2*M_PI RAD ; +2*M_PI RAD ]"
|
||||
<< endl;
|
||||
}
|
||||
if ((supplied_units == "DEG") && (fabs(value) > 360.0)) {
|
||||
cerr << element->ReadFrom() << element->GetName() << " value "
|
||||
<< value << " DEG is outside the range [ -360 DEG ; +360 DEG ]"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
||||
if (!supplied_units.empty()) {
|
||||
value *= convert[supplied_units][target_units];
|
||||
}
|
||||
|
||||
if ((target_units == "RAD") && (fabs(value) > 2 * M_PI)) {
|
||||
cerr << element->ReadFrom() << element->GetName() << " value "
|
||||
<< value << " RAD is outside the range [ -2*M_PI RAD ; +2*M_PI RAD ]"
|
||||
<< endl;
|
||||
}
|
||||
if ((target_units == "DEG") && (fabs(value) > 360.0)) {
|
||||
cerr << element->ReadFrom() << element->GetName() << " value "
|
||||
<< value << " DEG is outside the range [ -360 DEG ; +360 DEG ]"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
value = DisperseValue(element, value, supplied_units, target_units);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::FindElementValueAsNumberConvertFromTo( const string& el,
|
||||
const string& supplied_units,
|
||||
const string& target_units)
|
||||
{
|
||||
Element* element = FindElement(el);
|
||||
|
||||
if (!element) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Attempting to get non-existent element " << el;
|
||||
cerr << s.str() << endl;
|
||||
throw length_error(s.str());
|
||||
}
|
||||
|
||||
if (!supplied_units.empty()) {
|
||||
if (convert.find(supplied_units) == convert.end()) {
|
||||
std::stringstream s;
|
||||
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" does not exist (typo?).";
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
|
||||
std::stringstream s;
|
||||
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" cannot be converted to " << target_units;
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
double value = element->GetDataAsNumber();
|
||||
if (!supplied_units.empty()) {
|
||||
value *= convert[supplied_units][target_units];
|
||||
}
|
||||
|
||||
value = DisperseValue(element, value, supplied_units, target_units);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3 Element::FindElementTripletConvertTo( const string& target_units)
|
||||
{
|
||||
FGColumnVector3 triplet;
|
||||
Element* item;
|
||||
double value=0.0;
|
||||
string supplied_units = GetAttributeValue("unit");
|
||||
|
||||
if (!supplied_units.empty()) {
|
||||
if (convert.find(supplied_units) == convert.end()) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" does not exist (typo?).";
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Supplied unit: \"" << supplied_units
|
||||
<< "\" cannot be converted to " << target_units;
|
||||
cerr << s.str() << endl;
|
||||
throw invalid_argument(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
item = FindElement("x");
|
||||
if (!item) item = FindElement("roll");
|
||||
if (item) {
|
||||
value = item->GetDataAsNumber();
|
||||
if (!supplied_units.empty()) value *= convert[supplied_units][target_units];
|
||||
triplet(1) = DisperseValue(item, value, supplied_units, target_units);
|
||||
} else {
|
||||
triplet(1) = 0.0;
|
||||
}
|
||||
|
||||
|
||||
item = FindElement("y");
|
||||
if (!item) item = FindElement("pitch");
|
||||
if (item) {
|
||||
value = item->GetDataAsNumber();
|
||||
if (!supplied_units.empty()) value *= convert[supplied_units][target_units];
|
||||
triplet(2) = DisperseValue(item, value, supplied_units, target_units);
|
||||
} else {
|
||||
triplet(2) = 0.0;
|
||||
}
|
||||
|
||||
item = FindElement("z");
|
||||
if (!item) item = FindElement("yaw");
|
||||
if (item) {
|
||||
value = item->GetDataAsNumber();
|
||||
if (!supplied_units.empty()) value *= convert[supplied_units][target_units];
|
||||
triplet(3) = DisperseValue(item, value, supplied_units, target_units);
|
||||
} else {
|
||||
triplet(3) = 0.0;
|
||||
}
|
||||
|
||||
return triplet;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double Element::DisperseValue(Element *e, double val, const std::string& supplied_units,
|
||||
const std::string& target_units)
|
||||
{
|
||||
double value=val;
|
||||
|
||||
bool disperse = false;
|
||||
try {
|
||||
char* num = getenv("JSBSIM_DISPERSE");
|
||||
if (num) {
|
||||
disperse = (atoi(num) == 1); // set dispersions
|
||||
}
|
||||
} catch (...) { // if error set to false
|
||||
disperse = false;
|
||||
std::cerr << "Could not process JSBSIM_DISPERSE environment variable: Assumed NO dispersions." << endl;
|
||||
}
|
||||
|
||||
if (e->HasAttribute("dispersion") && disperse) {
|
||||
double disp = e->GetAttributeValueAsNumber("dispersion");
|
||||
if (!supplied_units.empty()) disp *= convert[supplied_units][target_units];
|
||||
string attType = e->GetAttributeValue("type");
|
||||
if (attType == "gaussian" || attType == "gaussiansigned") {
|
||||
double grn = FGJSBBase::GaussianRandomNumber();
|
||||
if (attType == "gaussian") {
|
||||
value = val + disp*grn;
|
||||
} else { // Assume gaussiansigned
|
||||
value = (val + disp*grn)*(fabs(grn)/grn);
|
||||
}
|
||||
} else if (attType == "uniform" || attType == "uniformsigned") {
|
||||
double urn = ((((double)rand()/RAND_MAX)-0.5)*2.0);
|
||||
if (attType == "uniform") {
|
||||
value = val + disp * urn;
|
||||
} else { // Assume uniformsigned
|
||||
value = (val + disp * urn)*(fabs(urn)/urn);
|
||||
}
|
||||
} else {
|
||||
std::stringstream s;
|
||||
s << ReadFrom() << "Unknown dispersion type" << attType;
|
||||
cerr << s.str() << endl;
|
||||
throw domain_error(s.str());
|
||||
}
|
||||
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void Element::Print(unsigned int level)
|
||||
{
|
||||
unsigned int i, spaces;
|
||||
|
||||
level+=2;
|
||||
for (spaces=0; spaces<=level; spaces++) cout << " "; // format output
|
||||
cout << "Element Name: " << name;
|
||||
|
||||
map<string, string>::iterator it;
|
||||
for (it = attributes.begin(); it != attributes.end(); ++it)
|
||||
cout << " " << it->first << " = " << it->second;
|
||||
|
||||
cout << endl;
|
||||
for (i=0; i<data_lines.size(); i++) {
|
||||
for (spaces=0; spaces<=level; spaces++) cout << " "; // format output
|
||||
cout << data_lines[i] << endl;
|
||||
}
|
||||
for (i=0; i<children.size(); i++) {
|
||||
children[i]->Print(level);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void Element::AddAttribute(const string& name, const string& value)
|
||||
{
|
||||
attributes[name] = value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void Element::AddData(string d)
|
||||
{
|
||||
string::size_type string_start = d.find_first_not_of(" \t");
|
||||
if (string_start != string::npos && string_start > 0) {
|
||||
d.erase(0,string_start);
|
||||
}
|
||||
data_lines.push_back(d);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string Element::ReadFrom(void) const
|
||||
{
|
||||
ostringstream message;
|
||||
|
||||
message << endl
|
||||
<< "In file " << GetFileName() << ": line " << GetLineNumber()
|
||||
<< endl;
|
||||
|
||||
return message.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void Element::MergeAttributes(Element* el)
|
||||
{
|
||||
map<string, string>::iterator it;
|
||||
|
||||
for (it=el->attributes.begin(); it != el->attributes.end(); ++it) {
|
||||
if (attributes.find(it->first) == attributes.end())
|
||||
attributes[it->first] = it->second;
|
||||
else {
|
||||
if (FGJSBBase::debug_lvl > 0 && (attributes[it->first] != it->second))
|
||||
cout << el->ReadFrom() << " Attribute '" << it->first << "' is overridden in file "
|
||||
<< GetFileName() << ": line " << GetLineNumber() << endl
|
||||
<< " The value '" << attributes[it->first] << "' will be used instead of '"
|
||||
<< it->second << "'." << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace JSBSim
|
||||
405
src/FDM/JSBSim/input_output/FGXMLElement.h
Normal file
405
src/FDM/JSBSim/input_output/FGXMLElement.h
Normal file
@@ -0,0 +1,405 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
File: FGXMLElement.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 9/28/04
|
||||
|
||||
------------- Copyright (C) 2004 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef XMLELEMENT_H
|
||||
#define XMLELEMENT_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "simgear/structure/SGSharedPtr.hxx"
|
||||
#include "math/FGColumnVector3.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates an XML element.
|
||||
This class handles the creation, storage, and manipulation of XML elements.
|
||||
This class also can convert supplied values as follows:
|
||||
|
||||
convert ["from"]["to"] = factor, so: from * factor = to
|
||||
- convert["M"]["FT"] = 3.2808399;
|
||||
- convert["FT"]["M"] = 1.0/convert["M"]["FT"];
|
||||
- convert["M2"]["FT2"] = convert["M"]["FT"]*convert["M"]["FT"];
|
||||
- convert["FT2"]["M2"] = 1.0/convert["M2"]["FT2"];
|
||||
- convert["FT"]["IN"] = 12.0;
|
||||
- convert["IN"]["FT"] = 1.0/convert["FT"]["IN"];
|
||||
- convert["LBS"]["KG"] = 0.45359237;
|
||||
- convert["KG"]["LBS"] = 1.0/convert["LBS"]["KG"];
|
||||
- convert["SLUG*FT2"]["KG*M2"] = 1.35594;
|
||||
- convert["KG*M2"]["SLUG*FT2"] = 1.0/convert["SLUG*FT2"]["KG*M2"];
|
||||
- convert["RAD"]["DEG"] = 360.0/(2.0*3.1415926);
|
||||
- convert["DEG"]["RAD"] = 1.0/convert["RAD"]["DEG"];
|
||||
- convert["LBS/FT"]["N/M"] = 14.5939;
|
||||
- convert["LBS/FT/SEC"]["N/M/SEC"] = 14.5939;
|
||||
- convert["N/M"]["LBS/FT"] = 1.0/convert["LBS/FT"]["N/M"];
|
||||
- convert["N/M/SEC"]["LBS/FT/SEC"] = 1.0/convert["LBS/FT/SEC"]["N/M/SEC"];
|
||||
- convert["WATTS"]["HP"] = 0.001341022;
|
||||
- convert["HP"]["WATTS"] = 1.0/convert["WATTS"]["HP"];
|
||||
- convert["N"]["LBS"] = 0.22482;
|
||||
- convert["LBS"]["N"] = 1.0/convert["N"]["LBS"];
|
||||
- convert["KTS"]["FT/SEC"] = ktstofps;
|
||||
- convert["KG/MIN"]["LBS/MIN"] = convert["KG"]["LBS"];
|
||||
- convert["LBS/HP*HR"]["KG/KW*HR"] = 0.6083;
|
||||
- convert["KG/KW*HR"]["LBS/HP*HR"] = 1/convert["LBS/HP*HR"]["KG/KW*HR"];
|
||||
- convert["KG/L"]["LBS/GAL"] = 8.3454045;
|
||||
|
||||
- convert["M"]["M"] = 1.00;
|
||||
- convert["FT"]["FT"] = 1.00;
|
||||
- convert["IN"]["IN"] = 1.00;
|
||||
- convert["DEG"]["DEG"] = 1.00;
|
||||
- convert["RAD"]["RAD"] = 1.00;
|
||||
- convert["M2"]["M2"] = 1.00;
|
||||
- convert["FT2"]["FT2"] = 1.00;
|
||||
- convert["KG*M2"]["KG*M2"] = 1.00;
|
||||
- convert["SLUG*FT2"]["SLUG*FT2"] = 1.00;
|
||||
- convert["KG"]["KG"] = 1.00;
|
||||
- convert["LBS"]["LBS"] = 1.00;
|
||||
- convert["LBS/FT"]["LBS/FT"] = 1.00;
|
||||
- convert["N/M"]["N/M"] = 1.00;
|
||||
- convert["LBS/FT/SEC"]["LBS/FT/SEC"] = 1.00;
|
||||
- convert["N/M/SEC"]["N/M/SEC"] = 1.00;
|
||||
- convert["PSI"]["PSI"] = 1.00;
|
||||
- convert["INHG"]["INHG"] = 1.00;
|
||||
- convert["HP"]["HP"] = 1.00;
|
||||
- convert["N"]["N"] = 1.00;
|
||||
- convert["WATTS"]["WATTS"] = 1.00;
|
||||
- convert["KTS"]["KTS"] = 1.0;
|
||||
- convert["FT/SEC"]["FT/SEC"] = 1.0;
|
||||
- convert["KG/MIN"]["KG/MIN"] = 1.0;
|
||||
- convert["LBS/MIN"]["LBS/MIN"] = 1.0;
|
||||
- convert["LBS/HP*HR"]["LBS/HP*HR"] = 1.0;
|
||||
- convert["KG/KW*HR"]["KG/KW*HR"] = 1.0;
|
||||
- convert["KG/L"]["KG/L"] = 1.0;
|
||||
- convert["LBS/GAL"]["LBS/GAL"] = 1.0;
|
||||
|
||||
Where:
|
||||
- N = newtons
|
||||
- M = meters
|
||||
- M2 = meters squared
|
||||
- KG = kilograms
|
||||
- LBS = pounds force
|
||||
- FT = feet
|
||||
- FT2 = feet squared
|
||||
- SEC = seconds
|
||||
- MIN = minutes
|
||||
- SLUG = slug
|
||||
- DEG = degrees
|
||||
- RAD = radians
|
||||
- WATTS = watts
|
||||
- HP = horsepower
|
||||
- HR = hour
|
||||
- L = liter
|
||||
- GAL = gallon (U.S. liquid)
|
||||
|
||||
@author Jon S. Berndt
|
||||
*/
|
||||
|
||||
class Element;
|
||||
typedef SGSharedPtr<Element> Element_ptr;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class Element : public SGReferenced {
|
||||
public:
|
||||
/** Constructor
|
||||
@param nm the name of this element (if given)
|
||||
*/
|
||||
Element(const std::string& nm);
|
||||
/// Destructor
|
||||
~Element(void);
|
||||
|
||||
/** Determines if an element has the supplied attribute.
|
||||
@param key specifies the attribute key to retrieve the value of.
|
||||
@return true or false. */
|
||||
bool HasAttribute(const std::string& key) {return attributes.find(key) != attributes.end();}
|
||||
|
||||
/** Retrieves an attribute.
|
||||
@param key specifies the attribute key to retrieve the value of.
|
||||
@return the key value (as a string), or the empty string if no such
|
||||
attribute exists. */
|
||||
std::string GetAttributeValue(const std::string& key);
|
||||
|
||||
/** Modifies an attribute.
|
||||
@param key specifies the attribute key to modify the value of.
|
||||
@param value new key value (as a string).
|
||||
@return false if it did not find any attribute with the requested key,
|
||||
true otherwise.
|
||||
*/
|
||||
bool SetAttributeValue(const std::string& key, const std::string& value);
|
||||
|
||||
/** Retrieves an attribute value as a double precision real number.
|
||||
@param key specifies the attribute key to retrieve the value of.
|
||||
@return the key value (as a number), or the HUGE_VAL if no such
|
||||
attribute exists. */
|
||||
double GetAttributeValueAsNumber(const std::string& key);
|
||||
|
||||
/** Retrieves the element name.
|
||||
@return the element name, or the empty string if no name has been set.*/
|
||||
const std::string& GetName(void) const {return name;}
|
||||
void ChangeName(const std::string& _name) { name = _name; }
|
||||
|
||||
/** Gets a line of data belonging to an element.
|
||||
@param i the index of the data line to return (0 by default).
|
||||
@return a string representing the data line requested, or the empty string
|
||||
if none exists.*/
|
||||
std::string GetDataLine(unsigned int i=0);
|
||||
|
||||
/// Returns the number of lines of data stored
|
||||
unsigned int GetNumDataLines(void) {return (unsigned int)data_lines.size();}
|
||||
|
||||
/// Returns the number of child elements for this element.
|
||||
unsigned int GetNumElements(void) {return (unsigned int)children.size();}
|
||||
|
||||
/// Returns the number of named child elements for this element.
|
||||
unsigned int GetNumElements(const std::string& element_name);
|
||||
|
||||
/** Converts the element data to a number.
|
||||
This function attempts to convert the first (and presumably only) line of
|
||||
data "owned" by the element into a real number. If there is not exactly one
|
||||
line of data owned by the element, then HUGE_VAL is returned.
|
||||
@return the numeric value of the data owned by the element.*/
|
||||
double GetDataAsNumber(void);
|
||||
|
||||
/** Returns a pointer to the element requested by index.
|
||||
This function also resets an internal counter to the index, so that
|
||||
subsequent calls to GetNextElement() will return the following
|
||||
elements sequentially, until the last element is reached. At that point,
|
||||
GetNextElement() will return NULL.
|
||||
@param el the index of the requested element (0 by default)
|
||||
@return a pointer to the Element, or 0 if no valid element exists. */
|
||||
Element* GetElement(unsigned int el=0);
|
||||
|
||||
/** Returns a pointer to the next element in the list.
|
||||
The function GetElement() must be called first to be sure that this
|
||||
function will return the correct element. The call to GetElement() resets
|
||||
the internal counter to zero. Subsequent calls to GetNextElement() return
|
||||
a pointer to subsequent elements in the list. When the final element is
|
||||
reached, 0 is returned.
|
||||
@return a pointer to the next Element in the sequence, or 0 if no valid
|
||||
Element is present. */
|
||||
Element* GetNextElement(void);
|
||||
|
||||
/** Returns a pointer to the parent of an element.
|
||||
@return a pointer to the parent Element, or 0 if this is the top level Element. */
|
||||
Element* GetParent(void) {return parent;}
|
||||
|
||||
/** Returns the line number at which the element has been defined.
|
||||
@return the line number
|
||||
*/
|
||||
int GetLineNumber(void) const { return line_number; }
|
||||
|
||||
/** Returns the name of the file in which the element has been read.
|
||||
@return the file name
|
||||
*/
|
||||
const std::string& GetFileName(void) const { return file_name; }
|
||||
|
||||
/** Searches for a specified element.
|
||||
Finds the first element that matches the supplied string, or simply the first
|
||||
element if no search string is supplied. This function call resets the internal
|
||||
element counter to the first element.
|
||||
@param el the search string (empty string by default).
|
||||
@return a pointer to the first element that matches the supplied search string. */
|
||||
Element* FindElement(const std::string& el="");
|
||||
|
||||
/** Searches for the next element as specified.
|
||||
This function would be called after FindElement() is first called (in order to
|
||||
reset the internal counter). If no argument is supplied (or the empty string)
|
||||
a pointer to the very next element is returned. Otherwise, the next occurence
|
||||
of the named element is returned. If the end of the list is reached, 0 is
|
||||
returned.
|
||||
@param el the name of the next element to find.
|
||||
@return the pointer to the found element, or 0 if no appropriate element us
|
||||
found.*/
|
||||
Element* FindNextElement(const std::string& el="");
|
||||
|
||||
/** Searches for the named element and returns the string data belonging to it.
|
||||
This function allows the data belonging to a named element to be returned
|
||||
as a string. If no element is found, the empty string is returned. If no
|
||||
argument is supplied, the data string for the first element is returned.
|
||||
@param el the name of the element being searched for (the empty string by
|
||||
default)
|
||||
@return the data value for the named element as a string, or the empty
|
||||
string if the element cannot be found. */
|
||||
std::string FindElementValue(const std::string& el="");
|
||||
|
||||
/** Searches for the named element and returns the data belonging to it as a number.
|
||||
This function allows the data belonging to a named element to be returned
|
||||
as a double. If no element is found, HUGE_VAL is returned. If no
|
||||
argument is supplied, the data for the first element is returned.
|
||||
@param el the name of the element being searched for (the empty string by
|
||||
default)
|
||||
@return the data value for the named element as a double, or HUGE_VAL if the
|
||||
data is missing. */
|
||||
double FindElementValueAsNumber(const std::string& el="");
|
||||
|
||||
/** Searches for the named element and returns the data belonging to it as a bool.
|
||||
This function allows the data belonging to a named element to be returned
|
||||
as a bool. If no element is found, false is returned. If no
|
||||
argument is supplied, the data for the first element is returned.
|
||||
@param el the name of the element being searched for (the empty string by
|
||||
default)
|
||||
@return the data value for the named element as a bool, or false if the
|
||||
data is missing. Zero will be false, while any other number will be true. */
|
||||
bool FindElementValueAsBoolean(const std::string& el="");
|
||||
|
||||
/** Searches for the named element and converts and returns the data belonging to it.
|
||||
This function allows the data belonging to a named element to be returned
|
||||
as a double. If no element is found, HUGE_VAL is returned. If no
|
||||
argument is supplied, the data for the first element is returned. Additionally,
|
||||
this function converts the value from the units specified in the config file (via
|
||||
the UNITS="" attribute in the element definition) to the native units used by
|
||||
JSBSim itself, as specified by the target_units parameter. The currently
|
||||
allowable unit conversions are seen in the source file FGXMLElement.cpp.
|
||||
Also, see above in the main documentation for this class.
|
||||
@param el the name of the element being searched for (the empty string by
|
||||
default)
|
||||
@param target_units the string representing the native units used by JSBSim
|
||||
to which the value returned will be converted.
|
||||
@return the unit-converted data value for the named element as a double,
|
||||
or HUGE_VAL if the data is missing. */
|
||||
double FindElementValueAsNumberConvertTo(const std::string& el, const std::string& target_units);
|
||||
|
||||
/** Searches for the named element and converts and returns the data belonging to it.
|
||||
This function allows the data belonging to a named element to be returned
|
||||
as a double. If no element is found, HUGE_VAL is returned. If no
|
||||
argument is supplied, the data for the first element is returned. Additionally,
|
||||
this function converts the value from the units specified in the supplied_units
|
||||
parameter to the units specified in the target_units parameter. JSBSim itself,
|
||||
as specified by the target_units parameter. The currently allowable unit
|
||||
conversions are seen in the source file FGXMLElement.cpp. Also, see above
|
||||
in the main documentation for this class.
|
||||
@param el the name of the element being searched for (the empty string by
|
||||
default)
|
||||
@param supplied_units the string representing the units of the value as
|
||||
supplied by the config file.
|
||||
@param target_units the string representing the native units used by JSBSim
|
||||
to which the value returned will be converted.
|
||||
@return the unit-converted data value for the named element as a double,
|
||||
or HUGE_VAL if the data is missing. */
|
||||
double FindElementValueAsNumberConvertFromTo( const std::string& el,
|
||||
const std::string& supplied_units,
|
||||
const std::string& target_units);
|
||||
|
||||
/** Composes a 3-element column vector for the supplied location or orientation.
|
||||
This function processes a LOCATION or ORIENTATION construct, returning a
|
||||
filled-out 3-element column vector containing the X, Y, Z or ROLL, PITCH,
|
||||
YAW elements found in the supplied element. If one of the mentioned components
|
||||
is not found, that component is set to zero and a warning message is printed.
|
||||
All three elements should be supplied.
|
||||
@param target_units the string representing the native units used by JSBSim
|
||||
to which the value returned will be converted.
|
||||
@return a column vector object built from the LOCATION or ORIENT components. */
|
||||
FGColumnVector3 FindElementTripletConvertTo( const std::string& target_units);
|
||||
|
||||
double DisperseValue(Element *e, double val, const std::string& supplied_units="",
|
||||
const std::string& target_units="");
|
||||
|
||||
/** This function sets the value of the parent class attribute to the supplied
|
||||
Element pointer.
|
||||
@param p pointer to the parent Element. */
|
||||
void SetParent(Element* p) {parent = p;}
|
||||
|
||||
/** Adds a child element to the list of children stored for this element.
|
||||
* @param el Child element to add. */
|
||||
void AddChildElement(Element* el) {children.push_back(el);}
|
||||
|
||||
/** Stores an attribute belonging to this element.
|
||||
* @param name The string name of the attribute.
|
||||
* @param value The string value of the attribute. */
|
||||
void AddAttribute(const std::string& name, const std::string& value);
|
||||
|
||||
/** Stores data belonging to this element.
|
||||
* @param d the data to store. */
|
||||
void AddData(std::string d);
|
||||
|
||||
/** Prints the element.
|
||||
* Prints this element and calls the Print routine for child elements.
|
||||
* @param d The tab level. A level corresponds to a single space. */
|
||||
void Print(unsigned int level=0);
|
||||
|
||||
/** Set the line number at which the element has been read.
|
||||
* @param line line number.
|
||||
*/
|
||||
void SetLineNumber(int line) { line_number = line; }
|
||||
|
||||
/** Set the name of the file in which the element has been read.
|
||||
* @param name file name
|
||||
*/
|
||||
void SetFileName(const std::string& name) { file_name = name; }
|
||||
|
||||
/** Return a string that contains a description of the location where the
|
||||
* current XML element was read from.
|
||||
* @return a string describing the file name and line number where the
|
||||
* element was read.
|
||||
*/
|
||||
std::string ReadFrom(void) const;
|
||||
|
||||
/** Merges the attributes of the current element with another element. The
|
||||
* attributes from the current element override the element that is passed
|
||||
* as a parameter. In other words if the two elements have an attribute with
|
||||
* the same name, the attribute from the current element is kept and the
|
||||
* corresponding attribute of the other element is ignored.
|
||||
* @param el element with which the current element will merge its attributes.
|
||||
*/
|
||||
void MergeAttributes(Element* el);
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::map <std::string, std::string> attributes;
|
||||
std::vector <std::string> data_lines;
|
||||
std::vector <Element_ptr> children;
|
||||
Element *parent;
|
||||
unsigned int element_index;
|
||||
std::string file_name;
|
||||
int line_number;
|
||||
typedef std::map <std::string, std::map <std::string, double> > tMapConvert;
|
||||
static tMapConvert convert;
|
||||
static bool converterIsInitialized;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
#endif
|
||||
93
src/FDM/JSBSim/input_output/FGXMLFileRead.h
Normal file
93
src/FDM/JSBSim/input_output/FGXMLFileRead.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGXMLFileRead.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 02/04/07
|
||||
Purpose: Shared base class that wraps the XML file reading logic
|
||||
|
||||
------------- Copyright (C) 2007 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGXMLFILEREAD_HEADER_H
|
||||
#define FGXMLFILEREAD_HEADER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "input_output/FGXMLParse.h"
|
||||
#include "simgear/misc/sg_path.hxx"
|
||||
#include "simgear/io/iostreams/sgstream.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGXMLFileRead {
|
||||
public:
|
||||
FGXMLFileRead(void) {}
|
||||
~FGXMLFileRead(void) {}
|
||||
|
||||
Element* LoadXMLDocument(const SGPath& XML_filename, bool verbose=true)
|
||||
{
|
||||
return LoadXMLDocument(XML_filename, file_parser, verbose);
|
||||
}
|
||||
|
||||
Element* LoadXMLDocument(const SGPath& XML_filename, FGXMLParse& fparse, bool verbose=true)
|
||||
{
|
||||
sg_ifstream infile;
|
||||
SGPath filename(XML_filename);
|
||||
|
||||
if (!filename.isNull()) {
|
||||
if (filename.extension().empty())
|
||||
filename.concat(".xml");
|
||||
|
||||
infile.open(filename);
|
||||
if ( !infile.is_open()) {
|
||||
if (verbose) std::cerr << "Could not open file: " << filename << std::endl;
|
||||
return 0L;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "No filename given." << std::endl;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
readXML(infile, fparse, filename.utf8Str());
|
||||
Element* document = fparse.GetDocument();
|
||||
infile.close();
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
void ResetParser(void) {file_parser.reset();}
|
||||
|
||||
private:
|
||||
FGXMLParse file_parser;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
115
src/FDM/JSBSim/input_output/FGXMLParse.cpp
Normal file
115
src/FDM/JSBSim/input_output/FGXMLParse.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGXMLParse.cpp
|
||||
Author: Jon Berndt
|
||||
Date started: 08/20/2004
|
||||
Purpose: Config file read-in class and XML parser
|
||||
Called by: Various
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGXMLParse.h"
|
||||
#include "input_output/string_utilities.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
void FGXMLParse::reset(void)
|
||||
{
|
||||
current_element = document = nullptr;
|
||||
working_string.erase();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGXMLParse::dumpDataLines(void)
|
||||
{
|
||||
if (!working_string.empty()) {
|
||||
for (auto s: split(working_string, '\n'))
|
||||
current_element->AddData(s);
|
||||
}
|
||||
working_string.erase();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGXMLParse::startElement (const char * name, const XMLAttributes &atts)
|
||||
{
|
||||
if (!document) {
|
||||
document = new Element(name);
|
||||
current_element = document;
|
||||
} else {
|
||||
dumpDataLines();
|
||||
|
||||
Element* temp_element = new Element(name);
|
||||
if (temp_element) {
|
||||
temp_element->SetParent(current_element);
|
||||
current_element->AddChildElement(temp_element);
|
||||
}
|
||||
current_element = temp_element;
|
||||
}
|
||||
|
||||
if (!current_element) {
|
||||
cerr << "In file " << getPath() << ": line " << getLine() << endl
|
||||
<< "No current element read (running out of memory?)" << endl;
|
||||
throw("Fatal error");
|
||||
}
|
||||
|
||||
current_element->SetLineNumber(getLine());
|
||||
current_element->SetFileName(getPath());
|
||||
|
||||
for (int i=0; i<atts.size();i++) {
|
||||
current_element->AddAttribute(atts.getName(i), atts.getValue(i));
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGXMLParse::endElement (const char * name)
|
||||
{
|
||||
dumpDataLines();
|
||||
current_element = current_element->GetParent();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGXMLParse::data (const char * s, int length)
|
||||
{
|
||||
working_string += string(s, length);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGXMLParse::warning (const char * message, int line, int column)
|
||||
{
|
||||
cerr << "Warning: " << message << " line: " << line << " column: " << column
|
||||
<< endl;
|
||||
}
|
||||
|
||||
} // end namespace JSBSim
|
||||
92
src/FDM/JSBSim/input_output/FGXMLParse.h
Normal file
92
src/FDM/JSBSim/input_output/FGXMLParse.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGXMLParse.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 8/20/04
|
||||
|
||||
------------- Copyright (C) 2004 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGXMLPARSE_H
|
||||
#define FGXMLPARSE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "simgear/xml/easyxml.hxx"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#define VALID_CHARS """`!@#$%^&*()_+`1234567890-={}[];':,.<>/?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class Element;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates an XML parser based on the EasyXML parser from the SimGear
|
||||
library.
|
||||
@author Jon S. Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGXMLParse : public XMLVisitor
|
||||
{
|
||||
public:
|
||||
FGXMLParse(void) : current_element(nullptr) {}
|
||||
|
||||
Element* GetDocument(void) {return document;}
|
||||
|
||||
void startElement (const char * name, const XMLAttributes &atts) override;
|
||||
void endElement (const char * name) override;
|
||||
void data (const char * s, int length) override;
|
||||
void warning (const char * message, int line, int column) override;
|
||||
void reset(void);
|
||||
|
||||
private:
|
||||
void dumpDataLines(void);
|
||||
|
||||
std::string working_string;
|
||||
Element_ptr document;
|
||||
Element *current_element;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
#endif
|
||||
439
src/FDM/JSBSim/input_output/FGfdmSocket.cpp
Normal file
439
src/FDM/JSBSim/input_output/FGfdmSocket.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGfdmSocket.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 11/08/99
|
||||
Purpose: Encapsulates a socket
|
||||
Called by: FGOutput, et. al.
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This class excapsulates a socket for simple data writing
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
11/08/99 JSB Created
|
||||
11/08/07 HDW Added Generic Socket Send
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#include <ws2tcpip.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
#include "FGfdmSocket.h"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
static bool LoadWinSockDLL(int debug_lvl)
|
||||
{
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
|
||||
cerr << "Winsock DLL not initialized ..." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debug_lvl > 0)
|
||||
cout << "Winsock DLL loaded ..." << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol, int precision)
|
||||
{
|
||||
sckt = sckt_in = 0;
|
||||
Protocol = (ProtocolType)protocol;
|
||||
connected = false;
|
||||
struct addrinfo *addr = nullptr;
|
||||
this->precision = precision;
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
if (!LoadWinSockDLL(debug_lvl)) return;
|
||||
#endif
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET;
|
||||
if (protocol == ptUDP)
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
else
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
if (!is_number(address))
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
else
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
int failure = getaddrinfo(address.c_str(), NULL, &hints, &addr);
|
||||
if (failure || !addr) {
|
||||
cerr << "Could not get host net address " << address;
|
||||
|
||||
if (hints.ai_flags == AI_NUMERICHOST)
|
||||
cerr << " by number..." << endl;
|
||||
else
|
||||
cerr << " by name..." << endl;
|
||||
|
||||
cerr << gai_strerror(failure) << endl;
|
||||
|
||||
freeaddrinfo(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
sckt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
|
||||
if (debug_lvl > 0) {
|
||||
if (protocol == ptUDP) //use udp protocol
|
||||
cout << "Creating UDP socket on port " << port << endl;
|
||||
else //use tcp protocol
|
||||
cout << "Creating TCP socket on port " << port << endl;
|
||||
}
|
||||
|
||||
if (sckt >= 0) { // successful
|
||||
int len = sizeof(struct sockaddr_in);
|
||||
memcpy(&scktName, addr->ai_addr, len);
|
||||
scktName.sin_port = htons(port);
|
||||
|
||||
if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
|
||||
if (debug_lvl > 0)
|
||||
cout << "Successfully connected to socket for output ..." << endl;
|
||||
connected = true;
|
||||
} else // unsuccessful
|
||||
cerr << "Could not connect to socket for output ..." << endl;
|
||||
} else // unsuccessful
|
||||
cerr << "Could not create socket for FDM output, error = " << errno << endl;
|
||||
|
||||
freeaddrinfo(addr);
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// assumes TCP or UDP socket on localhost, for inbound datagrams
|
||||
FGfdmSocket::FGfdmSocket(int port, int protocol, int precision)
|
||||
{
|
||||
sckt = -1;
|
||||
connected = false;
|
||||
Protocol = (ProtocolType)protocol;
|
||||
string ProtocolName;
|
||||
this->precision = precision;
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
if (!LoadWinSockDLL(debug_lvl)) return;
|
||||
#endif
|
||||
|
||||
if (Protocol == ptUDP) { //use udp protocol
|
||||
ProtocolName = "UDP";
|
||||
sckt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
u_long NonBlock = 1; // True
|
||||
ioctlsocket(sckt, FIONBIO, &NonBlock);
|
||||
#else
|
||||
fcntl(sckt, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
ProtocolName = "TCP";
|
||||
sckt = socket(AF_INET, SOCK_STREAM, 0);
|
||||
}
|
||||
|
||||
if (debug_lvl > 0)
|
||||
cout << "Creating input " << ProtocolName << " socket on port " << port
|
||||
<< endl;
|
||||
|
||||
if (sckt != -1) {
|
||||
memset(&scktName, 0, sizeof(struct sockaddr_in));
|
||||
scktName.sin_family = AF_INET;
|
||||
scktName.sin_port = htons(port);
|
||||
|
||||
if (Protocol == ptUDP)
|
||||
scktName.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
int len = sizeof(struct sockaddr_in);
|
||||
if (bind(sckt, (struct sockaddr*)&scktName, len) != -1) {
|
||||
if (debug_lvl > 0)
|
||||
cout << "Successfully bound to " << ProtocolName
|
||||
<< " input socket on port " << port << endl << endl;
|
||||
|
||||
if (Protocol == ptTCP) {
|
||||
if (listen(sckt, 5) >= 0) { // successful listen()
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
u_long NoBlock = 1;
|
||||
ioctlsocket(sckt, FIONBIO, &NoBlock);
|
||||
sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
|
||||
#else
|
||||
int flags = fcntl(sckt, F_GETFL, 0);
|
||||
fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
|
||||
sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
|
||||
#endif
|
||||
connected = true;
|
||||
} else
|
||||
cerr << "Could not listen ..." << endl;
|
||||
} else
|
||||
connected = true;
|
||||
} else // unsuccessful
|
||||
cerr << "Could not bind to " << ProtocolName << " input socket, error = "
|
||||
<< errno << endl;
|
||||
} else // unsuccessful
|
||||
cerr << "Could not create " << ProtocolName << " socket for input, error = "
|
||||
<< errno << endl;
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGfdmSocket::~FGfdmSocket()
|
||||
{
|
||||
if (sckt) shutdown(sckt,2);
|
||||
if (sckt_in) shutdown(sckt_in,2);
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGfdmSocket::Receive(void)
|
||||
{
|
||||
char buf[1024];
|
||||
int len = sizeof(struct sockaddr_in);
|
||||
int num_chars=0;
|
||||
string data; // todo: should allocate this with a standard size as a
|
||||
// class attribute and pass as a reference?
|
||||
|
||||
if (sckt_in <= 0 && Protocol == ptTCP) {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
|
||||
#else
|
||||
sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
|
||||
#endif
|
||||
if (sckt_in > 0) {
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
u_long NoBlock = 1;
|
||||
ioctlsocket(sckt_in, FIONBIO, &NoBlock);
|
||||
#else
|
||||
int flags = fcntl(sckt_in, F_GETFL, 0);
|
||||
fcntl(sckt_in, F_SETFL, flags | O_NONBLOCK);
|
||||
#endif
|
||||
send(sckt_in, "Connected to JSBSim server\nJSBSim> ", 35, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (sckt_in > 0) {
|
||||
while ((num_chars = recv(sckt_in, buf, sizeof buf, 0)) > 0) {
|
||||
data.append(buf, num_chars);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// when nothing received and the error isn't "would block"
|
||||
// then assume that the client has closed the socket.
|
||||
if (num_chars == 0) {
|
||||
DWORD err = WSAGetLastError ();
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
printf ("Socket Closed. back to listening\n");
|
||||
closesocket (sckt_in);
|
||||
sckt_in = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is for FGUDPInputSocket
|
||||
if (sckt >= 0 && Protocol == ptUDP) {
|
||||
struct sockaddr addr;
|
||||
socklen_t fromlen = sizeof addr;
|
||||
num_chars = recvfrom(sckt, buf, sizeof buf, 0, (struct sockaddr*)&addr, &fromlen);
|
||||
if (num_chars != -1) data.append(buf, num_chars);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
int FGfdmSocket::Reply(const string& text)
|
||||
{
|
||||
int num_chars_sent=0;
|
||||
|
||||
if (sckt_in >= 0) {
|
||||
num_chars_sent = send(sckt_in, text.c_str(), text.size(), 0);
|
||||
send(sckt_in, "JSBSim> ", 8, 0);
|
||||
} else {
|
||||
cerr << "Socket reply must be to a valid socket" << endl;
|
||||
return -1;
|
||||
}
|
||||
return num_chars_sent;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Close(void)
|
||||
{
|
||||
close(sckt_in);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Clear(void)
|
||||
{
|
||||
buffer.str(string());
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Clear(const string& s)
|
||||
{
|
||||
Clear();
|
||||
buffer << s << ' ';
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Append(const char* item)
|
||||
{
|
||||
if (buffer.tellp() > 0) buffer << ',';
|
||||
buffer << item;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Append(double item)
|
||||
{
|
||||
if (buffer.tellp() > 0) buffer << ',';
|
||||
buffer << std::setw(12) << std::setprecision(precision) << item;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Append(long item)
|
||||
{
|
||||
if (buffer.tellp() > 0) buffer << ',';
|
||||
buffer << std::setw(12) << item;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Send(void)
|
||||
{
|
||||
buffer << '\n';
|
||||
string str = buffer.str();
|
||||
if ((send(sckt,str.c_str(),str.size(),0)) <= 0) {
|
||||
perror("send");
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::Send(const char *data, int length)
|
||||
{
|
||||
if ((send(sckt,data,length,0)) <= 0) {
|
||||
perror("send");
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGfdmSocket::WaitUntilReadable(void)
|
||||
{
|
||||
if (sckt_in <= 0)
|
||||
return;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sckt_in, &fds);
|
||||
select(sckt_in+1, &fds, NULL, NULL, NULL);
|
||||
|
||||
/*
|
||||
If you want to check select return status:
|
||||
|
||||
int recVal = select(sckt_in+1, &fds, NULL, NULL, NULL);
|
||||
recVal: received value
|
||||
0, if socket timeout
|
||||
-1, if socket error
|
||||
anithing else, if socket is readable
|
||||
*/
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGfdmSocket::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGfdmSocket" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGfdmSocket" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
src/FDM/JSBSim/input_output/FGfdmSocket.h
Normal file
110
src/FDM/JSBSim/input_output/FGfdmSocket.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGfdmSocket.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 11/08/99
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
11/08/99 JSB Created
|
||||
11/08/07 HDW Added Generic Socket Send
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGfdmSocket_H
|
||||
#define FGfdmSocket_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#include <winsock.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates an object that enables JSBSim to communicate via socket (input
|
||||
and/or output).
|
||||
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGfdmSocket : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
FGfdmSocket(const std::string& address, int port, int protocol, int precision = 7);
|
||||
FGfdmSocket(int port, int protocol, int precision = 7);
|
||||
~FGfdmSocket();
|
||||
void Send(void);
|
||||
void Send(const char *data, int length);
|
||||
|
||||
std::string Receive(void);
|
||||
int Reply(const std::string& text);
|
||||
void Append(const std::string& s) {Append(s.c_str());}
|
||||
void Append(const char*);
|
||||
void Append(double);
|
||||
void Append(long);
|
||||
void Clear(void);
|
||||
void Clear(const std::string& s);
|
||||
void Close(void);
|
||||
bool GetConnectStatus(void) {return connected;}
|
||||
void WaitUntilReadable(void);
|
||||
|
||||
enum ProtocolType {ptUDP, ptTCP};
|
||||
|
||||
private:
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
SOCKET sckt;
|
||||
SOCKET sckt_in;
|
||||
#else
|
||||
int sckt;
|
||||
int sckt_in;
|
||||
#endif
|
||||
ProtocolType Protocol;
|
||||
struct sockaddr_in scktName;
|
||||
struct hostent *host;
|
||||
std::ostringstream buffer;
|
||||
int precision;
|
||||
bool connected;
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
123
src/FDM/JSBSim/input_output/net_fdm.hxx
Normal file
123
src/FDM/JSBSim/input_output/net_fdm.hxx
Normal file
@@ -0,0 +1,123 @@
|
||||
// net_fdm.hxx -- defines a common net I/O interface to the flight
|
||||
// dynamics model
|
||||
//
|
||||
// Written by Curtis Olson - http://www.flightgear.org/~curt
|
||||
// Started September 2001.
|
||||
//
|
||||
// This file is in the Public Domain, and comes with no warranty.
|
||||
//
|
||||
// $Id: net_fdm.hxx,v 1.6 2013/11/09 14:06:36 bcoconni Exp $
|
||||
|
||||
|
||||
#ifndef _NET_FDM_HXX
|
||||
#define _NET_FDM_HXX
|
||||
|
||||
|
||||
#include <time.h> // time_t
|
||||
#include <simgear/misc/stdint.hxx>
|
||||
|
||||
// NOTE: this file defines an external interface structure. Due to
|
||||
// variability between platforms and architectures, we only used fixed
|
||||
// length types here. Specifically, integer types can vary in length.
|
||||
// I am not aware of any platforms that don't use 4 bytes for float
|
||||
// and 8 bytes for double.
|
||||
|
||||
const uint32_t FG_NET_FDM_VERSION = 24;
|
||||
|
||||
|
||||
// Define a structure containing the top level flight dynamics model
|
||||
// parameters
|
||||
|
||||
class FGNetFDM {
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
FG_MAX_ENGINES = 4,
|
||||
FG_MAX_WHEELS = 3,
|
||||
FG_MAX_TANKS = 4
|
||||
};
|
||||
|
||||
uint32_t version; // increment when data values change
|
||||
uint32_t padding; // padding
|
||||
|
||||
// Positions
|
||||
double longitude; // geodetic (radians)
|
||||
double latitude; // geodetic (radians)
|
||||
double altitude; // above sea level (meters)
|
||||
float agl; // above ground level (meters)
|
||||
float phi; // roll (radians)
|
||||
float theta; // pitch (radians)
|
||||
float psi; // yaw or true heading (radians)
|
||||
float alpha; // angle of attack (radians)
|
||||
float beta; // side slip angle (radians)
|
||||
|
||||
// Velocities
|
||||
float phidot; // roll rate (radians/sec)
|
||||
float thetadot; // pitch rate (radians/sec)
|
||||
float psidot; // yaw rate (radians/sec)
|
||||
float vcas; // calibrated airspeed
|
||||
float climb_rate; // feet per second
|
||||
float v_north; // north velocity in local/body frame, fps
|
||||
float v_east; // east velocity in local/body frame, fps
|
||||
float v_down; // down/vertical velocity in local/body frame, fps
|
||||
float v_body_u; // ECEF velocity in body axis
|
||||
float v_body_v; // ECEF velocity in body axis
|
||||
float v_body_w; // ECEF velocity in body axis
|
||||
|
||||
// Accelerations
|
||||
float A_X_pilot; // X accel in body frame ft/sec^2
|
||||
float A_Y_pilot; // Y accel in body frame ft/sec^2
|
||||
float A_Z_pilot; // Z accel in body frame ft/sec^2
|
||||
|
||||
// Stall
|
||||
float stall_warning; // 0.0 - 1.0 indicating the amount of stall
|
||||
float slip_deg; // slip ball deflection
|
||||
|
||||
// Pressure
|
||||
|
||||
// Engine status
|
||||
uint32_t num_engines; // Number of valid engines
|
||||
uint32_t eng_state[FG_MAX_ENGINES];// Engine state (off, cranking, running)
|
||||
float rpm[FG_MAX_ENGINES]; // Engine RPM rev/min
|
||||
float fuel_flow[FG_MAX_ENGINES]; // Fuel flow gallons/hr
|
||||
float fuel_px[FG_MAX_ENGINES]; // Fuel pressure psi
|
||||
float egt[FG_MAX_ENGINES]; // Exhuast gas temp deg F
|
||||
float cht[FG_MAX_ENGINES]; // Cylinder head temp deg F
|
||||
float mp_osi[FG_MAX_ENGINES]; // Manifold pressure
|
||||
float tit[FG_MAX_ENGINES]; // Turbine Inlet Temperature
|
||||
float oil_temp[FG_MAX_ENGINES]; // Oil temp deg F
|
||||
float oil_px[FG_MAX_ENGINES]; // Oil pressure psi
|
||||
|
||||
// Consumables
|
||||
uint32_t num_tanks; // Max number of fuel tanks
|
||||
float fuel_quantity[FG_MAX_TANKS];
|
||||
|
||||
// Gear status
|
||||
uint32_t num_wheels;
|
||||
uint32_t wow[FG_MAX_WHEELS];
|
||||
float gear_pos[FG_MAX_WHEELS];
|
||||
float gear_steer[FG_MAX_WHEELS];
|
||||
float gear_compression[FG_MAX_WHEELS];
|
||||
|
||||
// Environment
|
||||
uint32_t cur_time; // current unix time
|
||||
// FIXME: make this uint64_t before 2038
|
||||
int32_t warp; // offset in seconds to unix time
|
||||
float visibility; // visibility in meters (for env. effects)
|
||||
|
||||
// Control surface positions (normalized values)
|
||||
float elevator;
|
||||
float elevator_trim_tab;
|
||||
float left_flap;
|
||||
float right_flap;
|
||||
float left_aileron;
|
||||
float right_aileron;
|
||||
float rudder;
|
||||
float nose_wheel;
|
||||
float speedbrake;
|
||||
float spoilers;
|
||||
};
|
||||
|
||||
|
||||
#endif // _NET_FDM_HXX
|
||||
156
src/FDM/JSBSim/input_output/string_utilities.h
Normal file
156
src/FDM/JSBSim/input_output/string_utilities.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: string_utilities.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 06/01/09
|
||||
|
||||
------------- Copyright (C) 2009 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
06/01/09 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef STRINGUTILS_H
|
||||
#define STRINGUTILS_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#if !defined(BASE)
|
||||
extern std::string& trim_left(std::string& str);
|
||||
extern std::string& trim_right(std::string& str);
|
||||
extern std::string& trim(std::string& str);
|
||||
extern std::string& trim_all_space(std::string& str);
|
||||
extern std::string& to_upper(std::string& str);
|
||||
extern std::string& to_lower(std::string& str);
|
||||
extern bool is_number(const std::string& str);
|
||||
std::vector <std::string> split(std::string str, char d);
|
||||
|
||||
extern std::string replace(std::string str, const std::string& old, const std::string& newstr);
|
||||
#else
|
||||
#include <cctype>
|
||||
|
||||
std::string& trim_left(std::string& str)
|
||||
{
|
||||
while (!str.empty() && isspace((unsigned char)str[0])) {
|
||||
str = str.erase(0,1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string& trim_right(std::string& str)
|
||||
{
|
||||
while (!str.empty() && isspace((unsigned char)str[str.size()-1])) {
|
||||
str = str.erase(str.size()-1,1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string& trim(std::string& str)
|
||||
{
|
||||
if (str.empty()) return str;
|
||||
std::string temp_str = trim_right(str);
|
||||
return str = trim_left(temp_str);
|
||||
}
|
||||
|
||||
std::string& trim_all_space(std::string& str)
|
||||
{
|
||||
for (size_t i=0; i<str.size(); i++) {
|
||||
if (isspace((unsigned char)str[i])) {
|
||||
str = str.erase(i,1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string& to_upper(std::string& str)
|
||||
{
|
||||
for (size_t i=0; i<str.size(); i++) str[i] = toupper(str[i]);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string& to_lower(std::string& str)
|
||||
{
|
||||
for (size_t i=0; i<str.size(); i++) str[i] = tolower(str[i]);
|
||||
return str;
|
||||
}
|
||||
|
||||
bool is_number(const std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return false;
|
||||
else
|
||||
return (str.find_first_not_of("+-.0123456789Ee") == std::string::npos);
|
||||
}
|
||||
|
||||
std::vector <std::string> split(std::string str, char d)
|
||||
{
|
||||
std::vector <std::string> str_array;
|
||||
size_t index=0;
|
||||
std::string temp = "";
|
||||
|
||||
trim(str);
|
||||
index = str.find(d);
|
||||
while (index != std::string::npos) {
|
||||
temp = str.substr(0,index);
|
||||
trim(temp);
|
||||
if (!temp.empty()) str_array.push_back(temp);
|
||||
str = str.erase(0,index+1);
|
||||
index = str.find(d);
|
||||
}
|
||||
if (!str.empty()) {
|
||||
temp = trim(str);
|
||||
if (!temp.empty()) str_array.push_back(temp);
|
||||
}
|
||||
|
||||
return str_array;
|
||||
}
|
||||
|
||||
std::string replace(std::string str, const std::string& oldstr, const std::string& newstr)
|
||||
{
|
||||
std::string temp = str;
|
||||
size_t old_idx = str.find(oldstr);
|
||||
if (old_idx != std::string::npos) {
|
||||
temp = str.replace(old_idx, 1, newstr);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
#endif
|
||||
133
src/FDM/JSBSim/math/FGColumnVector3.cpp
Normal file
133
src/FDM/JSBSim/math/FGColumnVector3.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGColumnVector3.cpp
|
||||
Author: Originally by Tony Peden [formatted here by JSB]
|
||||
Date started: 1998
|
||||
Purpose: FGColumnVector3 class
|
||||
Called by: Various
|
||||
|
||||
------------- Copyright (C) 1998 Tony Peden and 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
??/??/?? TP Created
|
||||
03/16/2000 JSB Added exception throwing
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGColumnVector3.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGColumnVector3::FGColumnVector3(void)
|
||||
{
|
||||
data[0] = data[1] = data[2] = 0.0;
|
||||
// Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGColumnVector3::Dump(const string& delimiter) const
|
||||
{
|
||||
ostringstream buffer;
|
||||
buffer << std::setprecision(16) << data[0] << delimiter;
|
||||
buffer << std::setprecision(16) << data[1] << delimiter;
|
||||
buffer << std::setprecision(16) << data[2];
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
ostream& operator<<(ostream& os, const FGColumnVector3& col)
|
||||
{
|
||||
os << col(1) << " , " << col(2) << " , " << col(3);
|
||||
return os;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3 FGColumnVector3::operator/(const double scalar) const
|
||||
{
|
||||
if (scalar != 0.0)
|
||||
return operator*( 1.0/scalar );
|
||||
|
||||
cerr << "Attempt to divide by zero in method \
|
||||
FGColumnVector3::operator/(const double scalar), \
|
||||
object " << data[0] << " , " << data[1] << " , " << data[2] << endl;
|
||||
return FGColumnVector3();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3& FGColumnVector3::operator/=(const double scalar)
|
||||
{
|
||||
if (scalar != 0.0)
|
||||
operator*=( 1.0/scalar );
|
||||
else
|
||||
cerr << "Attempt to divide by zero in method \
|
||||
FGColumnVector3::operator/=(const double scalar), \
|
||||
object " << data[0] << " , " << data[1] << " , " << data[2] << endl;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGColumnVector3::Magnitude(void) const
|
||||
{
|
||||
return sqrt( data[0]*data[0] + data[1]*data[1] + data[2]*data[2] );
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3& FGColumnVector3::Normalize(void)
|
||||
{
|
||||
double Mag = Magnitude();
|
||||
|
||||
if (Mag != 0.0)
|
||||
operator*=( 1.0/Mag );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGColumnVector3::Magnitude(const int idx1, const int idx2) const {
|
||||
return sqrt( data[idx1-1]*data[idx1-1] + data[idx2-1]*data[idx2-1] );
|
||||
}
|
||||
|
||||
|
||||
} // namespace JSBSim
|
||||
279
src/FDM/JSBSim/math/FGColumnVector3.h
Normal file
279
src/FDM/JSBSim/math/FGColumnVector3.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGColumnVector3.h
|
||||
Author: Originally by Tony Peden [formatted and adapted here by Jon Berndt]
|
||||
Date started: Unknown
|
||||
|
||||
------ Copyright (C) 2001 by Tony Peden and 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
??/??/???? ?? Initial version and more.
|
||||
03/06/2004 MF Rework, document and do much inlineing.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGCOLUMNVECTOR3_H
|
||||
#define FGCOLUMNVECTOR3_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** This class implements a 3 element column vector.
|
||||
@author Jon S. Berndt, Tony Peden, et. al.
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGColumnVector3
|
||||
{
|
||||
public:
|
||||
/** Default initializer.
|
||||
Create a zero vector. */
|
||||
FGColumnVector3(void);
|
||||
|
||||
/** Initialization by given values.
|
||||
@param X value of the x-conponent.
|
||||
@param Y value of the y-conponent.
|
||||
@param Z value of the z-conponent.
|
||||
Create a vector from the doubles given in the arguments. */
|
||||
FGColumnVector3(const double X, const double Y, const double Z) {
|
||||
data[0] = X;
|
||||
data[1] = Y;
|
||||
data[2] = Z;
|
||||
}
|
||||
|
||||
/** Copy constructor.
|
||||
@param v Vector which is used for initialization.
|
||||
Create copy of the vector given in the argument. */
|
||||
FGColumnVector3(const FGColumnVector3& v) {
|
||||
data[0] = v.data[0];
|
||||
data[1] = v.data[1];
|
||||
data[2] = v.data[2];
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
~FGColumnVector3(void) { }
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
@param idx the component index.
|
||||
Return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double operator()(const unsigned int idx) const { return data[idx-1]; }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
@param idx the component index.
|
||||
Return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double& operator()(const unsigned int idx) { return data[idx-1]; }
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
@param idx the component index.
|
||||
Return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
This function is just a shortcut for the <tt>double
|
||||
operator()(unsigned int idx) const</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double Entry(const unsigned int idx) const { return data[idx-1]; }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
@param idx the component index.
|
||||
Return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
This function is just a shortcut for the <tt>double&
|
||||
operator()(unsigned int idx)</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double& Entry(const unsigned int idx) { return data[idx-1]; }
|
||||
|
||||
/** Prints the contents of the vector
|
||||
@param delimeter the item separator (tab or comma)
|
||||
@return a string with the delimeter-separated contents of the vector */
|
||||
std::string Dump(const std::string& delimeter) const;
|
||||
|
||||
/** Assignment operator.
|
||||
@param b source vector.
|
||||
Copy the content of the vector given in the argument into *this. */
|
||||
FGColumnVector3& operator=(const FGColumnVector3& b) {
|
||||
data[0] = b.data[0];
|
||||
data[1] = b.data[1];
|
||||
data[2] = b.data[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assignment operator.
|
||||
@param lv initializer list of at most 3 values (i.e. {x, y, Z})
|
||||
Copy the content of the list into *this. */
|
||||
FGColumnVector3& operator=(std::initializer_list<double> lv) {
|
||||
double *v = data;
|
||||
for(auto &x : lv)
|
||||
*(v++) = x;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Comparison operator.
|
||||
@param b other vector.
|
||||
Returns true if both vectors are exactly the same. */
|
||||
bool operator==(const FGColumnVector3& b) const {
|
||||
return data[0] == b.data[0] && data[1] == b.data[1] && data[2] == b.data[2];
|
||||
}
|
||||
|
||||
/** Comparison operator.
|
||||
@param b other vector.
|
||||
Returns false if both vectors are exactly the same. */
|
||||
bool operator!=(const FGColumnVector3& b) const { return ! operator==(b); }
|
||||
|
||||
/** Multiplication by a scalar.
|
||||
@param scalar scalar value to multiply the vector with.
|
||||
@return The resulting vector from the multiplication with that scalar.
|
||||
Multiply the vector with the scalar given in the argument. */
|
||||
FGColumnVector3 operator*(const double scalar) const {
|
||||
return FGColumnVector3(scalar*data[0], scalar*data[1], scalar*data[2]);
|
||||
}
|
||||
|
||||
/** Multiply by 1/scalar.
|
||||
@param scalar scalar value to devide the vector through.
|
||||
@return The resulting vector from the division through that scalar.
|
||||
Multiply the vector with the 1/scalar given in the argument. */
|
||||
FGColumnVector3 operator/(const double scalar) const;
|
||||
|
||||
/** Cross product multiplication.
|
||||
@param V vector to multiply with.
|
||||
@return The resulting vector from the cross product multiplication.
|
||||
Compute and return the cross product of the current vector with
|
||||
the given argument. */
|
||||
FGColumnVector3 operator*(const FGColumnVector3& V) const {
|
||||
return FGColumnVector3( data[1] * V.data[2] - data[2] * V.data[1],
|
||||
data[2] * V.data[0] - data[0] * V.data[2],
|
||||
data[0] * V.data[1] - data[1] * V.data[0] );
|
||||
}
|
||||
|
||||
/// Addition operator.
|
||||
FGColumnVector3 operator+(const FGColumnVector3& B) const {
|
||||
return FGColumnVector3( data[0] + B.data[0], data[1] + B.data[1],
|
||||
data[2] + B.data[2] );
|
||||
}
|
||||
|
||||
/// Subtraction operator.
|
||||
FGColumnVector3 operator-(const FGColumnVector3& B) const {
|
||||
return FGColumnVector3( data[0] - B.data[0], data[1] - B.data[1],
|
||||
data[2] - B.data[2] );
|
||||
}
|
||||
|
||||
/// Subtract an other vector.
|
||||
FGColumnVector3& operator-=(const FGColumnVector3 &B) {
|
||||
data[0] -= B.data[0];
|
||||
data[1] -= B.data[1];
|
||||
data[2] -= B.data[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add an other vector.
|
||||
FGColumnVector3& operator+=(const FGColumnVector3 &B) {
|
||||
data[0] += B.data[0];
|
||||
data[1] += B.data[1];
|
||||
data[2] += B.data[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by a scalar.
|
||||
FGColumnVector3& operator*=(const double scalar) {
|
||||
data[0] *= scalar;
|
||||
data[1] *= scalar;
|
||||
data[2] *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by a 1/scalar.
|
||||
FGColumnVector3& operator/=(const double scalar);
|
||||
|
||||
void InitMatrix(void) { data[0] = data[1] = data[2] = 0.0; }
|
||||
void InitMatrix(const double a) { data[0] = data[1] = data[2] = a; }
|
||||
void InitMatrix(const double a, const double b, const double c) {
|
||||
data[0]=a; data[1]=b; data[2]=c;
|
||||
}
|
||||
|
||||
/** Length of the vector.
|
||||
Compute and return the euclidean norm of this vector. */
|
||||
double Magnitude(void) const;
|
||||
|
||||
/** Length of the vector in a coordinate axis plane.
|
||||
Compute and return the euclidean norm of this vector projected into
|
||||
the coordinate axis plane idx1-idx2. */
|
||||
double Magnitude(const int idx1, const int idx2) const;
|
||||
|
||||
/** Normalize.
|
||||
Normalize the vector to have the Magnitude() == 1.0. If the vector
|
||||
is equal to zero it is left untouched. */
|
||||
FGColumnVector3& Normalize(void);
|
||||
|
||||
private:
|
||||
double data[3];
|
||||
};
|
||||
|
||||
/** Dot product of two vectors
|
||||
Compute and return the euclidean dot (or scalar) product of two vectors
|
||||
v1 and v2 */
|
||||
inline double DotProduct(const FGColumnVector3& v1, const FGColumnVector3& v2) {
|
||||
return v1(1)*v2(1) + v1(2)*v2(2) + v1(3)*v2(3);
|
||||
}
|
||||
|
||||
/** Scalar multiplication.
|
||||
@param scalar scalar value to multiply with.
|
||||
@param A Vector to multiply.
|
||||
Multiply the Vector with a scalar value. Note: At this time, this
|
||||
operator MUST be inlined, or a multiple definition link error will occur.*/
|
||||
inline FGColumnVector3 operator*(double scalar, const FGColumnVector3& A) {
|
||||
// use already defined operation.
|
||||
return A*scalar;
|
||||
}
|
||||
|
||||
/** Write vector to a stream.
|
||||
@param os Stream to write to.
|
||||
@param col vector to write.
|
||||
Write the vector to a stream.*/
|
||||
std::ostream& operator<<(std::ostream& os, const FGColumnVector3& col);
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
305
src/FDM/JSBSim/math/FGCondition.cpp
Normal file
305
src/FDM/JSBSim/math/FGCondition.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGCondition.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 1/2/2003
|
||||
|
||||
-------------- Copyright (C) 2003 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FGCondition.h"
|
||||
#include "FGPropertyValue.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
#include "FGParameterValue.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// This constructor is called when tests are inside an element
|
||||
FGCondition::FGCondition(Element* element, FGPropertyManager* PropertyManager)
|
||||
: Logic(elUndef), TestParam1(nullptr), TestParam2(nullptr),
|
||||
Comparison(ecUndef)
|
||||
{
|
||||
InitializeConditionals();
|
||||
|
||||
string logic = element->GetAttributeValue("logic");
|
||||
if (!logic.empty()) {
|
||||
if (logic == "OR") Logic = eOR;
|
||||
else if (logic == "AND") Logic = eAND;
|
||||
else { // error
|
||||
cerr << element->ReadFrom()
|
||||
<< "Unrecognized LOGIC token " << logic << endl;
|
||||
throw std::invalid_argument("JSBSim FGCondition: unrecognized logic value:'" + logic + "'");
|
||||
}
|
||||
} else {
|
||||
Logic = eAND; // default
|
||||
}
|
||||
|
||||
for (unsigned int i=0; i<element->GetNumDataLines(); i++) {
|
||||
string data = element->GetDataLine(i);
|
||||
conditions.push_back(new FGCondition(data, PropertyManager, element));
|
||||
}
|
||||
|
||||
Element* condition_element = element->GetElement();
|
||||
const string& elName = element->GetName();
|
||||
|
||||
while (condition_element) {
|
||||
string tagName = condition_element->GetName();
|
||||
|
||||
if (tagName != elName) {
|
||||
cerr << condition_element->ReadFrom()
|
||||
<< "Unrecognized tag <" << tagName << "> in the condition statement."
|
||||
<< endl;
|
||||
throw std::invalid_argument("JSBSim FGCondition: unrecognized tag:'" + tagName + "'");
|
||||
}
|
||||
|
||||
conditions.push_back(new FGCondition(condition_element, PropertyManager));
|
||||
condition_element = element->GetNextElement();
|
||||
}
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// This constructor is called when there are no nested test groups inside the
|
||||
// condition
|
||||
|
||||
FGCondition::FGCondition(const string& test, FGPropertyManager* PropertyManager,
|
||||
Element* el)
|
||||
: Logic(elUndef), TestParam1(nullptr), TestParam2(nullptr),
|
||||
Comparison(ecUndef)
|
||||
{
|
||||
InitializeConditionals();
|
||||
|
||||
vector<string> test_strings = split(test, ' ');
|
||||
|
||||
if (test_strings.size() == 3) {
|
||||
TestParam1 = new FGPropertyValue(test_strings[0], PropertyManager);
|
||||
conditional = test_strings[1];
|
||||
TestParam2 = new FGParameterValue(test_strings[2], PropertyManager);
|
||||
} else {
|
||||
cerr << el->ReadFrom()
|
||||
<< " Conditional test is invalid: \"" << test
|
||||
<< "\" has " << test_strings.size() << " elements in the "
|
||||
<< "test condition." << endl;
|
||||
throw std::invalid_argument("JSBSim FGCondition: incorrect numner of test elments:" + std::to_string(test_strings.size()));
|
||||
}
|
||||
|
||||
Comparison = mComparison[conditional];
|
||||
if (Comparison == ecUndef) {
|
||||
throw std::invalid_argument("JSBSim FGCondition: Comparison operator: \""+conditional
|
||||
+"\" does not exist. Please check the conditional.");
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGCondition::InitializeConditionals(void)
|
||||
{
|
||||
mComparison["EQ"] = eEQ;
|
||||
mComparison["NE"] = eNE;
|
||||
mComparison["GT"] = eGT;
|
||||
mComparison["GE"] = eGE;
|
||||
mComparison["LT"] = eLT;
|
||||
mComparison["LE"] = eLE;
|
||||
mComparison["eq"] = eEQ;
|
||||
mComparison["ne"] = eNE;
|
||||
mComparison["gt"] = eGT;
|
||||
mComparison["ge"] = eGE;
|
||||
mComparison["lt"] = eLT;
|
||||
mComparison["le"] = eLE;
|
||||
mComparison["=="] = eEQ;
|
||||
mComparison["!="] = eNE;
|
||||
mComparison[">"] = eGT;
|
||||
mComparison[">="] = eGE;
|
||||
mComparison["<"] = eLT;
|
||||
mComparison["<="] = eLE;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGCondition::~FGCondition(void)
|
||||
{
|
||||
for (auto cond: conditions) delete cond;
|
||||
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGCondition::Evaluate(void )
|
||||
{
|
||||
bool pass = false;
|
||||
|
||||
if (!TestParam1) {
|
||||
|
||||
if (Logic == eAND) {
|
||||
|
||||
pass = true;
|
||||
for (auto cond: conditions) {
|
||||
if (!cond->Evaluate()) pass = false;
|
||||
}
|
||||
|
||||
} else { // Logic must be eOR
|
||||
|
||||
pass = false;
|
||||
for (auto cond: conditions) {
|
||||
if (cond->Evaluate()) pass = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
double compareValue = TestParam2->GetValue();
|
||||
|
||||
switch (Comparison) {
|
||||
case ecUndef:
|
||||
cerr << "Undefined comparison operator." << endl;
|
||||
break;
|
||||
case eEQ:
|
||||
pass = TestParam1->getDoubleValue() == compareValue;
|
||||
break;
|
||||
case eNE:
|
||||
pass = TestParam1->getDoubleValue() != compareValue;
|
||||
break;
|
||||
case eGT:
|
||||
pass = TestParam1->getDoubleValue() > compareValue;
|
||||
break;
|
||||
case eGE:
|
||||
pass = TestParam1->getDoubleValue() >= compareValue;
|
||||
break;
|
||||
case eLT:
|
||||
pass = TestParam1->getDoubleValue() < compareValue;
|
||||
break;
|
||||
case eLE:
|
||||
pass = TestParam1->getDoubleValue() <= compareValue;
|
||||
break;
|
||||
default:
|
||||
cerr << "Unknown comparison operator." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGCondition::PrintCondition(string indent)
|
||||
{
|
||||
string scratch;
|
||||
|
||||
if (!conditions.empty()) {
|
||||
|
||||
switch(Logic) {
|
||||
case (elUndef):
|
||||
scratch = " UNSET";
|
||||
cerr << "unset logic for test condition" << endl;
|
||||
break;
|
||||
case (eAND):
|
||||
scratch = indent + "if all of the following are true: {";
|
||||
break;
|
||||
case (eOR):
|
||||
scratch = indent + "if any of the following are true: {";
|
||||
break;
|
||||
default:
|
||||
scratch = " UNKNOWN";
|
||||
cerr << "Unknown logic for test condition" << endl;
|
||||
}
|
||||
cout << scratch << endl;
|
||||
|
||||
for (auto cond: conditions) {
|
||||
cond->PrintCondition(indent + " ");
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
cout << indent << "}";
|
||||
|
||||
} else {
|
||||
cout << indent << TestParam1->GetName() << " " << conditional
|
||||
<< " " << TestParam2->GetName();
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGCondition::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGCondition" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGCondition" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace JSBSim
|
||||
95
src/FDM/JSBSim/math/FGCondition.h
Normal file
95
src/FDM/JSBSim/math/FGCondition.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGCondition.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 1/02/2003
|
||||
|
||||
------------- Copyright (C) -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGCONDITION_H
|
||||
#define FGCONDITION_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "math/FGPropertyValue.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGPropertyManager;
|
||||
class FGPropertyValue;
|
||||
class Element;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates a condition, which is used in parts of JSBSim including switches
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGCondition : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
FGCondition(Element* element, FGPropertyManager* PropertyManager);
|
||||
~FGCondition(void);
|
||||
|
||||
bool Evaluate(void);
|
||||
void PrintCondition(std::string indent=" ");
|
||||
|
||||
private:
|
||||
FGCondition(const std::string& test, FGPropertyManager* PropertyManager,
|
||||
Element* el);
|
||||
|
||||
enum eComparison {ecUndef=0, eEQ, eNE, eGT, eGE, eLT, eLE};
|
||||
enum eLogic {elUndef=0, eAND, eOR};
|
||||
std::map <std::string, eComparison> mComparison;
|
||||
eLogic Logic;
|
||||
|
||||
FGPropertyValue_ptr TestParam1;
|
||||
FGParameter_ptr TestParam2;
|
||||
eComparison Comparison;
|
||||
std::string conditional;
|
||||
|
||||
std::vector <FGCondition*> conditions;
|
||||
void InitializeConditionals(void);
|
||||
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
1037
src/FDM/JSBSim/math/FGFunction.cpp
Normal file
1037
src/FDM/JSBSim/math/FGFunction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
840
src/FDM/JSBSim/math/FGFunction.h
Normal file
840
src/FDM/JSBSim/math/FGFunction.h
Normal file
@@ -0,0 +1,840 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGFunction.h
|
||||
Author: Jon Berndt
|
||||
Date started: August 25 2004
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGFUNCTION_H
|
||||
#define FGFUNCTION_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGParameter.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class Element;
|
||||
class FGPropertyValue;
|
||||
class FGFDMExec;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents a mathematical function.
|
||||
The FGFunction class is a powerful and versatile resource that allows
|
||||
algebraic functions to be defined in a JSBSim configuration file. It is
|
||||
similar in concept to MathML (Mathematical Markup Language, www.w3.org/Math/),
|
||||
but simpler and more terse.
|
||||
A function definition consists of an operation, a value, a table, or a property
|
||||
(which evaluates to a value). The currently supported operations are:
|
||||
- sum (takes n args)
|
||||
- difference (takes n args)
|
||||
- product (takes n args)
|
||||
- quotient (takes 2 args)
|
||||
- pow (takes 2 args)
|
||||
- sqrt (takes one argument)
|
||||
- toradians (takes one argument)
|
||||
- todegrees (takes one argument)
|
||||
- exp (takes 2 args)
|
||||
- log2 (takes 1 arg)
|
||||
- ln (takes 1 arg)
|
||||
- log10 (takes 1 arg)
|
||||
- abs (takes 1 arg)
|
||||
- sin (takes 1 arg)
|
||||
- cos (takes 1 arg)
|
||||
- tan (takes 1 arg)
|
||||
- asin (takes 1 arg)
|
||||
- acos (takes 1 arg)
|
||||
- atan (takes 1 arg)
|
||||
- atan2 (takes 2 args)
|
||||
- min (takes n args)
|
||||
- max (takes n args)
|
||||
- avg (takes n args)
|
||||
- fraction
|
||||
- mod
|
||||
- floor (takes 1 arg)
|
||||
- ceil (takes 1 arg)
|
||||
- fmod (takes 2 args)
|
||||
- lt (less than, takes 2 args)
|
||||
- le (less equal, takes 2 args)
|
||||
- gt (greater than, takes 2 args)
|
||||
- ge (greater than, takes 2 args)
|
||||
- eq (equal, takes 2 args)
|
||||
- nq (not equal, takes 2 args)
|
||||
- and (takes n args)
|
||||
- or (takes n args)
|
||||
- not (takes 1 args)
|
||||
- if-then (takes 2-3 args)
|
||||
- switch (takes 2 or more args)
|
||||
- random (Gaussian distribution random number)
|
||||
- urandom (Uniform random number between -1 and +1)
|
||||
- pi
|
||||
- integer
|
||||
- interpolate 1-dimensional (takes a minimum of five arguments, odd number)
|
||||
|
||||
An operation is defined in the configuration file as in the following example:
|
||||
|
||||
@code
|
||||
<sum>
|
||||
<value> 3.14159 </value>
|
||||
<property> velocities/qbar </property>
|
||||
<product>
|
||||
<value> 0.125 </value>
|
||||
<property> metrics/wingarea </property>
|
||||
</product>
|
||||
</sum>
|
||||
@endcode
|
||||
|
||||
A full function definition, such as is used in the aerodynamics section of a
|
||||
configuration file includes the function element, and other elements. It should
|
||||
be noted that there can be only one non-optional (non-documentation) element -
|
||||
that is, one operation element - in the top-level function definition.
|
||||
Multiple value and/or property elements cannot be immediate child
|
||||
members of the function element. Almost always, the first operation within the
|
||||
function element will be a product or sum. For example:
|
||||
|
||||
@code
|
||||
<function name="aero/moment/Clr">
|
||||
<description>Roll moment due to yaw rate</description>
|
||||
<product>
|
||||
<property>aero/qbar-area</property>
|
||||
<property>metrics/bw-ft</property>
|
||||
<property>aero/bi2vel</property>
|
||||
<property>velocities/r-aero-rad_sec</property>
|
||||
<table>
|
||||
<independentVar>aero/alpha-rad</independentVar>
|
||||
<tableData>
|
||||
0.000 0.08
|
||||
0.094 0.19
|
||||
</tableData>
|
||||
</table>
|
||||
</product>
|
||||
</function>
|
||||
@endcode
|
||||
|
||||
The "lowest level" in a function is always a value or a property, which cannot
|
||||
itself contain another element. As shown, operations can contain values,
|
||||
properties, tables, or other operations. In the first above example, the sum
|
||||
element contains all three. What is evaluated is written algebraically as:
|
||||
|
||||
@code 3.14159 + qbar + (0.125 * wingarea) @endcode
|
||||
|
||||
Some operations can take only a single argument. That argument, however, can be
|
||||
an operation (such as sum) which can contain other items. The point to keep in
|
||||
mind is that it evaluates to a single value - which is just what the
|
||||
trigonometric functions require (except atan2, which takes two arguments).
|
||||
|
||||
<h2>Specific Function Definitions</h2>
|
||||
|
||||
Note: In the definitions below, a "property" refers to a single property
|
||||
specified within either the <property></property> tag or the shortcut tag,
|
||||
\<p>\</p>. The keyword "value" refers to a single numeric value specified either
|
||||
within the \<value>\</value> tag or the shortcut <v></v> tag. The keyword
|
||||
"table" refers to a single table specified either within the \<table>\</table>
|
||||
tag or the shortcut <t></t> tag. The plural form of any of the three words
|
||||
refers to one or more instances of a property, value, or table.
|
||||
|
||||
- @b sum, sums the values of all immediate child elements:
|
||||
@code
|
||||
<sum>
|
||||
{properties, values, tables, or other function elements}
|
||||
</sum>
|
||||
|
||||
Example: Mach + 0.01
|
||||
|
||||
<sum>
|
||||
<p> velocities/mach </p>
|
||||
<v> 0.01 </v>
|
||||
</sum>
|
||||
@endcode
|
||||
- @b difference, subtracts the values of all immediate child elements from the
|
||||
value of the first child element:
|
||||
@code
|
||||
<difference>
|
||||
{properties, values, tables, or other function elements}
|
||||
</difference>
|
||||
|
||||
Example: Mach - 0.01
|
||||
|
||||
<difference>
|
||||
<p> velocities/mach </p>
|
||||
<v> 0.01 </v>
|
||||
</difference>
|
||||
@endcode
|
||||
- @b product multiplies together the values of all immediate child elements:
|
||||
@code
|
||||
<product>
|
||||
{properties, values, tables, or other function elements}
|
||||
</product>
|
||||
|
||||
Example: qbar*S*beta*CY_beta
|
||||
|
||||
<product>
|
||||
<property> aero/qbar-psf </property>
|
||||
<property> metrics/Sw-sqft </property>
|
||||
<property> aero/beta-rad </property>
|
||||
<property> aero/coefficient/CY_beta </property>
|
||||
</product>
|
||||
@endcode
|
||||
- @b quotient, divides the value of the first immediate child element by the
|
||||
second immediate child element:
|
||||
@code
|
||||
<quotient>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</quotient>
|
||||
|
||||
Example: (2*GM)/R
|
||||
|
||||
<quotient>
|
||||
<product>
|
||||
<v> 2.0 </v>
|
||||
<p> guidance/executive/gm </p>
|
||||
</product>
|
||||
<p> position/radius-to-vehicle-ft </p>
|
||||
</quotient>
|
||||
@endcode
|
||||
- @b pow, raises the value of the first immediate child element to the power of
|
||||
the value of the second immediate child element:
|
||||
@code
|
||||
<pow>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</pow>
|
||||
|
||||
Example: Mach^2
|
||||
|
||||
<pow>
|
||||
<p> velocities/mach </p>
|
||||
<v> 2.0 </v>
|
||||
</pow>
|
||||
@endcode
|
||||
- @b sqrt, takes the square root of the value of the immediate child element:
|
||||
@code
|
||||
<sqrt>
|
||||
{property, value, table, or other function element}
|
||||
</sqrt>
|
||||
|
||||
Example: square root of 25
|
||||
|
||||
<sqrt> <v> 25.0 </v> </sqrt>
|
||||
@endcode
|
||||
- @b toradians, converts a presumed argument in degrees to radians by
|
||||
multiplying the value of the immediate child element by pi/180:
|
||||
@code
|
||||
<toradians>
|
||||
{property, value, table, or other function element}
|
||||
</toradians>
|
||||
|
||||
Example: convert 45 degrees to radians
|
||||
|
||||
<toradians> <v> 45 </v> </toradians>
|
||||
@endcode
|
||||
- @b todegrees, converts a presumed argument in radians to degrees by
|
||||
multiplying the value of the immediate child element by 180/pi:
|
||||
@code
|
||||
<todegrees>
|
||||
{property, value, table, or other function element}
|
||||
</todegrees>
|
||||
|
||||
Example: convert 0.5*pi radians to degrees
|
||||
|
||||
<todegrees>
|
||||
<product> <v> 0.5 </v> <pi/> </product>
|
||||
</todegrees>
|
||||
@endcode
|
||||
- @b exp, raises "e" to the power of the immediate child element:
|
||||
@code
|
||||
<exp>
|
||||
{property, value, table, or other function element}
|
||||
</exp>
|
||||
|
||||
Example: raise "e" to the 1.5 power, e^1.5
|
||||
|
||||
<exp> <v> 1.5 </v> </exp>
|
||||
@endcode
|
||||
- @b log2, calculates the log base 2 value of the immediate child element:
|
||||
@code
|
||||
<log2>
|
||||
{property, value, table, or other function element}
|
||||
</log2>
|
||||
|
||||
Example:
|
||||
<log2> <v> 128 </v> </log2>
|
||||
@endcode
|
||||
- @b ln, calculates the natural logarithm of the value of the immediate child
|
||||
element:
|
||||
@code
|
||||
<ln>
|
||||
{property, value, table, or other function element}
|
||||
</ln>
|
||||
|
||||
Example: ln(128)
|
||||
|
||||
<ln> <v> 200 </v> </ln>
|
||||
@endcode
|
||||
- @b log10, calculates the base 10 logarithm of the value of the immediate child
|
||||
element
|
||||
@code
|
||||
<log10>
|
||||
{property, value, table, or other function element}
|
||||
</log10>
|
||||
|
||||
Example log(Mach)
|
||||
|
||||
<log10> <p> velocities/mach </p> </log10>
|
||||
@endcode
|
||||
- @b abs calculates the absolute value of the immediate child element
|
||||
@code
|
||||
<abs>
|
||||
{property, value, table, or other function element}
|
||||
</abs>
|
||||
|
||||
Example:
|
||||
|
||||
<abs> <p> flight-path/gamma-rad </p> </abs>
|
||||
@endcode
|
||||
- @b sin, calculates the sine of the value of the immediate child element (the
|
||||
argument is expected to be in radians)
|
||||
@code
|
||||
<sin>
|
||||
{property, value, table, or other function element}
|
||||
</sin>
|
||||
|
||||
Example:
|
||||
|
||||
<sin> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </sin>
|
||||
@endcode
|
||||
- @b cos, calculates the cosine of the value of the immediate child element (the
|
||||
argument is expected to be in radians)
|
||||
@code
|
||||
<cos>
|
||||
{property, value, table, or other function element}
|
||||
</cos>
|
||||
|
||||
Example:
|
||||
|
||||
<cos> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </cos>
|
||||
@endcode
|
||||
- @b tan, calculates the tangent of the value of the immediate child element
|
||||
(the argument is expected to be in radians)
|
||||
@code
|
||||
<tan>
|
||||
{property, value, table, or other function element}
|
||||
</tan>
|
||||
|
||||
Example:
|
||||
|
||||
<tan> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </tan>
|
||||
@endcode
|
||||
- @b asin, calculates the arcsine (inverse sine) of the value of the immediate
|
||||
child element. The value provided should be in the range from -1 to
|
||||
+1. The value returned will be expressed in radians, and will be in
|
||||
the range from -pi/2 to +pi/2.
|
||||
@code
|
||||
<asin>
|
||||
{property, value, table, or other function element}
|
||||
</asin>
|
||||
|
||||
Example:
|
||||
|
||||
<asin> <v> 0.5 </v> </asin>
|
||||
@endcode
|
||||
- @b acos, calculates the arccosine (inverse cosine) of the value of the
|
||||
immediate child element. The value provided should be in the range
|
||||
from -1 to +1. The value returned will be expressed in radians, and
|
||||
will be in the range from 0 to pi.
|
||||
@code
|
||||
<acos>
|
||||
{property, value, table, or other function element}
|
||||
</acos>
|
||||
|
||||
Example:
|
||||
|
||||
<acos> <v> 0.5 </v> </acos>
|
||||
@endcode
|
||||
- @b atan, calculates the inverse tangent of the value of the immediate child
|
||||
element. The value returned will be expressed in radians, and will
|
||||
be in the range from -pi/2 to +pi/2.
|
||||
@code
|
||||
<atan>
|
||||
{property, value, table, or other function element}
|
||||
</atan>
|
||||
|
||||
Example:
|
||||
|
||||
<atan> <v> 0.5 </v> </atan>
|
||||
@endcode
|
||||
- @b atan2 calculates the inverse tangent of the value of the immediate child
|
||||
elements, Y/X (in that order). It even works for X values near zero.
|
||||
The value returned will be expressed in radians, and in the range
|
||||
-pi to +pi.
|
||||
@code
|
||||
<atan2>
|
||||
{property, value, table, or other function element} {property, value, table, or other function element}
|
||||
</atan2>
|
||||
|
||||
Example: inverse tangent of 0.5/0.25, evaluates to: 1.107 radians
|
||||
|
||||
<atan2> <v> 0.5 </<v> <v> 0.25 </v> </atan2>
|
||||
@endcode
|
||||
- @b min returns the smallest value from all the immediate child elements
|
||||
@code
|
||||
<min>
|
||||
{properties, values, tables, or other function elements}
|
||||
</min>
|
||||
|
||||
Example: returns the lesser of velocity and 2500
|
||||
|
||||
<min>
|
||||
<p> velocities/eci-velocity-mag-fps </p>
|
||||
<v> 2500.0 </v>
|
||||
</min>
|
||||
@endcode
|
||||
- @b max returns the largest value from all the immediate child elements
|
||||
@code
|
||||
<max>
|
||||
{properties, values, tables, or other function elements}
|
||||
</max>
|
||||
|
||||
Example: returns the greater of velocity and 15000
|
||||
|
||||
<max>
|
||||
<p> velocities/eci-velocity-mag-fps </p>
|
||||
<v> 15000.0 </v>
|
||||
</max>
|
||||
@endcode
|
||||
- @b avg returns the average value of all the immediate child elements
|
||||
@code
|
||||
<avg>
|
||||
{properties, values, tables, or other function elements}
|
||||
</avg>
|
||||
|
||||
Example: returns the average of the four numbers below, evaluates to 0.50.
|
||||
|
||||
<avg>
|
||||
<v> 0.25 </v>
|
||||
<v> 0.50 </v>
|
||||
<v> 0.75 </v>
|
||||
<v> 0.50 </v>
|
||||
</avg>
|
||||
@endcode
|
||||
- @b fraction, returns the fractional part of the value of the immediate child
|
||||
element
|
||||
@code
|
||||
<fraction>
|
||||
{property, value, table, or other function element}
|
||||
</fraction>
|
||||
|
||||
Example: returns the fractional part of pi - or, roughly, 0.1415926...
|
||||
|
||||
<fraction> <pi/> </fraction>
|
||||
@endcode
|
||||
- @b integer, returns the integer portion of the value of the immediate child
|
||||
element
|
||||
@code
|
||||
<integer>
|
||||
{property, value, table, or other function element}
|
||||
</integer>
|
||||
@endcode
|
||||
- @b mod returns the remainder from the integer division of the value of the
|
||||
first immediate child element by the second immediate child element,
|
||||
X/Y (X modulo Y). The value returned is the value X-I*Y, for the
|
||||
largest integer I such that if Y is nonzero, the result has the
|
||||
same sign as X and magnitude less than the magnitude of Y. For
|
||||
instance, the expression "5 mod 2" would evaluate to 1 because 5
|
||||
divided by 2 leaves a quotient of 2 and a remainder of 1, while
|
||||
"9 mod 3" would evaluate to 0 because the division of 9 by 3 has a
|
||||
quotient of 3 and leaves a remainder of 0.
|
||||
@code
|
||||
<mod>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</mod>
|
||||
|
||||
Example: 5 mod 2, evaluates to 1
|
||||
|
||||
<mod> <v> 5 </v> <v> 2 </v> </mod>
|
||||
@endcode
|
||||
- @b floor returns the largest integral value that is not greater than X.
|
||||
@code
|
||||
<floor>
|
||||
{property, value, table, or other function element}
|
||||
</floor>
|
||||
@endcode
|
||||
Examples: floor(2.3) evaluates to 2.0 while floor(-2.3) evaluates to -3.0
|
||||
- @b ceil returns the smallest integral value that is not less than X.
|
||||
@code
|
||||
<ceil>
|
||||
{property, value, table, or other function element}
|
||||
</ceil>
|
||||
@endcode
|
||||
Examples: ceil(2.3) evaluates to 3.0 while ceil(-2.3) evaluates to -2.0
|
||||
- @b fmod returns the floating-point remainder of X/Y (rounded towards zero)
|
||||
@code
|
||||
<fmod>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</fmod>
|
||||
@endcode
|
||||
Example: fmod(18.5, 4.2) evaluates to 1.7
|
||||
- @b lt returns a 1 if the value of the first immediate child element is less
|
||||
than the value of the second immediate child element, returns 0
|
||||
otherwise
|
||||
@code
|
||||
<lt>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</lt>
|
||||
|
||||
Example: returns 1 if thrust is less than 10,000, returns 0 otherwise
|
||||
|
||||
<lt>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 10000.0 </v>
|
||||
</lt>
|
||||
@endcode
|
||||
- @b le, returns a 1 if the value of the first immediate child element is less
|
||||
than or equal to the value of the second immediate child element,
|
||||
returns 0 otherwise
|
||||
@code
|
||||
<le>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</le>
|
||||
|
||||
Example: returns 1 if thrust is less than or equal to 10,000, returns 0 otherwise
|
||||
|
||||
<le>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 10000.0 </v>
|
||||
</le>
|
||||
@endcode
|
||||
- @b gt returns a 1 if the value of the first immediate child element is greater
|
||||
than the value of the second immediate child element, returns 0
|
||||
otherwise
|
||||
@code
|
||||
<gt>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</gt>
|
||||
|
||||
Example: returns 1 if thrust is greater than 10,000, returns 0 otherwise
|
||||
|
||||
<gt>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 10000.0 </v>
|
||||
</gt>
|
||||
@endcode
|
||||
- @b ge, returns a 1 if the value of the first immediate child element is
|
||||
greater than or equal to the value of the second immediate child
|
||||
element, returns 0 otherwise
|
||||
@code
|
||||
<ge>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</ge>
|
||||
|
||||
Example: returns 1 if thrust is greater than or equal to 10,000, returns 0
|
||||
otherwise
|
||||
|
||||
<ge>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 10000.0 </v>
|
||||
</ge>
|
||||
@endcode
|
||||
- @b eq returns a 1 if the value of the first immediate child element is
|
||||
equal to the second immediate child element, returns 0
|
||||
otherwise
|
||||
@code
|
||||
<eq>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</eq>
|
||||
|
||||
Example: returns 1 if thrust is equal to 10,000, returns 0 otherwise
|
||||
|
||||
<eq>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 10000.0 </v>
|
||||
</eq>
|
||||
@endcode
|
||||
- @b nq returns a 1 if the value of the first immediate child element is not
|
||||
equal to the value of the second immediate child element, returns 0
|
||||
otherwise
|
||||
@code
|
||||
<nq>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</nq>
|
||||
|
||||
Example: returns 1 if thrust is not 0, returns 0 otherwise
|
||||
|
||||
<nq>
|
||||
<p> propulsion/engine[2]/thrust-lbs </p>
|
||||
<v> 0.0 </v>
|
||||
</nq>
|
||||
@endcode
|
||||
- @b and returns a 1 if the values of the immediate child elements are all 1,
|
||||
returns 0 otherwise. Values provided are expected to be either 1 or 0
|
||||
within machine precision.
|
||||
@code
|
||||
<and>
|
||||
{properties, values, tables, or other function elements}
|
||||
</and>
|
||||
|
||||
Example: returns 1 if the specified flags are all 1
|
||||
|
||||
<and>
|
||||
<p> guidance/first-stage-flight-flag </p>
|
||||
<p> control/engines-running-flag </p>
|
||||
</and>
|
||||
@endcode
|
||||
- @b or returns a 1 if the values of any of the immediate child elements 1,
|
||||
returns 0 otherwise. Values provided are expected to be either 1 or 0
|
||||
within machine precision.
|
||||
@code
|
||||
<or>
|
||||
{properties, values, tables, or other function elements}
|
||||
</or>
|
||||
|
||||
Example: returns 1 if any of the specified flags are 1
|
||||
|
||||
<or>
|
||||
<p> guidance/first-stage-flight-flag </p>
|
||||
<p> control/engines-running-flag </p>
|
||||
</or>
|
||||
@endcode
|
||||
- @b not, returns the inverse of the value of the supplied immediate child
|
||||
element (e.g., returns 1 if supplied a 0)
|
||||
@code
|
||||
<not>
|
||||
{property, value, table, or other function element}
|
||||
</not>
|
||||
|
||||
Example: returns 0 if the value of the supplied flag is 1
|
||||
|
||||
<not> <p> guidance/first-stage-flight-flag </p> </not>
|
||||
@endcode
|
||||
- @b ifthen if the value of the first immediate child element is 1, then the
|
||||
value of the second immediate child element is returned, otherwise
|
||||
the value of the third child element is returned
|
||||
@code
|
||||
<ifthen>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
</ifthen>
|
||||
|
||||
Example: if flight-mode is greater than 2, then a value of 0.00 is
|
||||
returned, otherwise the value of the property control/pitch-lag is
|
||||
returned.
|
||||
|
||||
<ifthen>
|
||||
<gt> <p> executive/flight-mode </p> <v> 2 </v> </gt>
|
||||
<v> 0.00 </v>
|
||||
<p> control/pitch-lag </p>
|
||||
</ifthen>
|
||||
@endcode
|
||||
- @b switch uses the integer value of the first immediate child element as an
|
||||
index to select one of the subsequent immediate child elements to
|
||||
return the value of
|
||||
@code
|
||||
<switch>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element}
|
||||
...
|
||||
</switch>
|
||||
|
||||
Example: if flight-mode is 2, the switch function returns 0.50
|
||||
<switch>
|
||||
<p> executive/flight-mode </p>
|
||||
<v> 0.25 </v>
|
||||
<v> 0.50 </v>
|
||||
<v> 0.75 </v>
|
||||
<v> 1.00 </v>
|
||||
</switch>
|
||||
@endcode
|
||||
- @b random Returns a normal distributed random number.
|
||||
The function, without parameters, returns a normal distributed
|
||||
random value with a distribution defined by the parameters
|
||||
mean = 0.0 and standard deviation (stddev) = 1.0
|
||||
The Mean of the distribution (its expected value, μ).
|
||||
Which coincides with the location of its peak.
|
||||
Standard deviation (σ): The square root of variance,
|
||||
representing the dispersion of values from the distribution mean.
|
||||
This shall be a positive value (σ>0).
|
||||
@code
|
||||
<random/>
|
||||
<random seed="1234"/>
|
||||
<random seed="time_now"/>
|
||||
<random seed="time_now" mean="0.0" stddev="1.0"/>
|
||||
@endcode
|
||||
- @b urandom Returns a uniformly distributed random number.
|
||||
The function, without parameters, returns a random value
|
||||
between the minimum value -1.0 and the maximum value of 1.0
|
||||
The two maximum and minimum values can be modified using the
|
||||
lower and upper parameters.
|
||||
@code
|
||||
<urandom/>
|
||||
<random seed="1234"/>
|
||||
<random seed="time_now"/>
|
||||
<random seed="time_now" lower="-1.0" upper="1.0"/>
|
||||
@endcode
|
||||
- @b pi Takes no argument and returns the value of Pi
|
||||
@code<pi/>@endcode
|
||||
- @b interpolate1d returns the result from a 1-dimensional interpolation of the
|
||||
supplied values, with the value of the first immediate child
|
||||
element representing the lookup value into the table, and the
|
||||
following pairs of values representing the independent and
|
||||
dependent values. The first provided child element is
|
||||
expected to be a property. The interpolation does not
|
||||
extrapolate, but holds the highest value if the provided
|
||||
lookup value goes outside of the provided range.
|
||||
@code
|
||||
<interpolate1d>
|
||||
{property, value, table, or other function element}
|
||||
{property, value, table, or other function element} {property, value, table, or other function element}
|
||||
...
|
||||
</interpolate1d>
|
||||
|
||||
Example: If mach is 0.4, the interpolation will return 0.375. If mach is
|
||||
1.5, the interpolation will return 0.60.
|
||||
|
||||
<interpolate1d>
|
||||
<p> velocities/mach </p>
|
||||
<v> 0.00 </v> <v> 0.25 </v>
|
||||
<v> 0.80 </v> <v> 0.50 </v>
|
||||
<v> 0.90 </v> <v> 0.60 </v>
|
||||
</interpolate1d>
|
||||
@endcode
|
||||
@author Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGFunction
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// Todo: Does this class need a copy constructor, like FGLGear?
|
||||
|
||||
class FGFunction : public FGParameter, public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/// Default constructor.
|
||||
FGFunction()
|
||||
: cached(false), cachedValue(-HUGE_VAL), PropertyManager(nullptr),
|
||||
pNode(nullptr), pCopyTo(nullptr) {}
|
||||
|
||||
explicit FGFunction(FGPropertyManager* pm)
|
||||
: FGFunction()
|
||||
{ PropertyManager = pm; }
|
||||
|
||||
/** Constructor.
|
||||
When this constructor is called, the XML element pointed to in memory by the
|
||||
element argument is traversed. If other FGParameter-derived objects (values,
|
||||
functions, properties, or tables) are encountered, this instance of the
|
||||
FGFunction object will store a pointer to the found object and pass the
|
||||
relevant Element pointer to the constructor for the new object. In other
|
||||
words, each FGFunction object maintains a list of "child"
|
||||
FGParameter-derived objects which in turn may each contain its own list, and
|
||||
so on. At runtime, each object evaluates its child parameters, which each
|
||||
may have its own child parameters to evaluate.
|
||||
|
||||
@param PropertyManager a pointer to the property manager instance.
|
||||
@param element a pointer to the Element object containing the function
|
||||
definition.
|
||||
@param prefix an optional prefix to prepend to the name given to the
|
||||
property that represents this function (if given).
|
||||
*/
|
||||
FGFunction(FGFDMExec* fdmex, Element* element, const std::string& prefix="",
|
||||
FGPropertyValue* var=0L);
|
||||
|
||||
/** Destructor
|
||||
Make sure the function is untied before destruction.
|
||||
*/
|
||||
~FGFunction(void) override;
|
||||
|
||||
/** Retrieves the value of the function object.
|
||||
@return the total value of the function. */
|
||||
double GetValue(void) const override;
|
||||
|
||||
/** The value that the function evaluates to, as a string.
|
||||
@return the value of the function as a string. */
|
||||
std::string GetValueAsString(void) const;
|
||||
|
||||
/// Retrieves the name of the function.
|
||||
std::string GetName(void) const override {return Name;}
|
||||
|
||||
/** Does the function always return the same result (i.e. does it apply to
|
||||
constant parameters) ? */
|
||||
bool IsConstant(void) const override;
|
||||
|
||||
/** Specifies whether to cache the value of the function, so it is calculated
|
||||
only once per frame.
|
||||
If shouldCache is true, then the value of the function is calculated, and a
|
||||
flag is set so further calculations done this frame will use the cached
|
||||
value. In order to turn off caching, cacheValue must be called with a false
|
||||
argument.
|
||||
@param shouldCache specifies whether the function should cache the computed
|
||||
value. */
|
||||
void cacheValue(bool shouldCache);
|
||||
|
||||
enum class OddEven {Either, Odd, Even};
|
||||
|
||||
protected:
|
||||
bool cached;
|
||||
double cachedValue;
|
||||
std::vector <FGParameter_ptr> Parameters;
|
||||
FGPropertyManager* PropertyManager;
|
||||
FGPropertyNode_ptr pNode;
|
||||
|
||||
void Load(Element* element, FGPropertyValue* var, FGFDMExec* fdmex,
|
||||
const std::string& prefix="");
|
||||
virtual void bind(Element*, const std::string&);
|
||||
void CheckMinArguments(Element* el, unsigned int _min);
|
||||
void CheckMaxArguments(Element* el, unsigned int _max);
|
||||
void CheckOddOrEvenArguments(Element* el, OddEven odd_even);
|
||||
std::string CreateOutputNode(Element* el, const std::string& Prefix);
|
||||
|
||||
private:
|
||||
std::string Name;
|
||||
FGPropertyNode_ptr pCopyTo; // Property node for CopyTo property string
|
||||
|
||||
void Debug(int from);
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
88
src/FDM/JSBSim/math/FGFunctionValue.h
Normal file
88
src/FDM/JSBSim/math/FGFunctionValue.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGFunctionValue.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: March 10 2018
|
||||
|
||||
--------- Copyright (C) 2018 B. Coconnier (bcoconni@users.sf.net) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGFUNCTIONVALUE_H
|
||||
#define FGFUNCTIONVALUE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "math/FGPropertyValue.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents a property value on which a function is applied
|
||||
@author Bertrand Coconnier
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGFunctionValue
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGFunctionValue : public FGPropertyValue
|
||||
{
|
||||
public:
|
||||
|
||||
FGFunctionValue(FGPropertyNode* propNode, FGTemplateFunc* f)
|
||||
:FGPropertyValue(propNode), function(f) {}
|
||||
FGFunctionValue(std::string propName, FGPropertyManager* propertyManager,
|
||||
FGTemplateFunc* f)
|
||||
:FGPropertyValue(propName, propertyManager), function(f) {}
|
||||
|
||||
double GetValue(void) const override { return function->GetValue(GetNode()); }
|
||||
|
||||
std::string GetName(void) const override {
|
||||
return function->GetName() + "(" + FGPropertyValue::GetName() + ")";
|
||||
}
|
||||
std::string GetNameWithSign(void) const override {
|
||||
return function->GetName() + "(" + FGPropertyValue::GetNameWithSign() + ")";
|
||||
}
|
||||
std::string GetPrintableName(void) const override {
|
||||
return function->GetName() + "(" + FGPropertyValue::GetPrintableName() + ")";
|
||||
}
|
||||
std::string GetFullyQualifiedName(void) const override {
|
||||
return function->GetName() + "(" + FGPropertyValue::GetFullyQualifiedName() + ")";
|
||||
}
|
||||
|
||||
private:
|
||||
FGTemplateFunc_ptr function;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
439
src/FDM/JSBSim/math/FGLocation.cpp
Normal file
439
src/FDM/JSBSim/math/FGLocation.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGLocation.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 04/04/2004
|
||||
Purpose: Store an arbitrary location on the globe
|
||||
|
||||
------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
|
||||
------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
|
||||
------- (C) 2011 Ola Røer Thorsen (ola@silentwings.no) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
------------------------------------------------------------------------------
|
||||
This class encapsulates an arbitrary position in the globe with its accessors.
|
||||
It has vector properties, so you can add multiply ....
|
||||
|
||||
HISTORY
|
||||
------------------------------------------------------------------------------
|
||||
04/04/2004 MF Created
|
||||
11/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
|
||||
it from FGFDMExec.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "FGLocation.h"
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGLocation::FGLocation(void)
|
||||
: mECLoc(1.0, 0.0, 0.0), mCacheValid(false)
|
||||
{
|
||||
e2 = c = 0.0;
|
||||
a = ec = ec2 = 1.0;
|
||||
|
||||
mLon = mLat = mRadius = 0.0;
|
||||
mGeodLat = GeodeticAltitude = 0.0;
|
||||
|
||||
mTl2ec.InitMatrix();
|
||||
mTec2l.InitMatrix();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGLocation::FGLocation(double lon, double lat, double radius)
|
||||
: mCacheValid(false)
|
||||
{
|
||||
e2 = c = 0.0;
|
||||
a = ec = ec2 = 1.0;
|
||||
|
||||
mLon = mLat = mRadius = 0.0;
|
||||
mGeodLat = GeodeticAltitude = 0.0;
|
||||
|
||||
mTl2ec.InitMatrix();
|
||||
mTec2l.InitMatrix();
|
||||
|
||||
double sinLat = sin(lat);
|
||||
double cosLat = cos(lat);
|
||||
double sinLon = sin(lon);
|
||||
double cosLon = cos(lon);
|
||||
mECLoc = { radius*cosLat*cosLon,
|
||||
radius*cosLat*sinLon,
|
||||
radius*sinLat };
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGLocation::FGLocation(const FGColumnVector3& lv)
|
||||
: mECLoc(lv), mCacheValid(false)
|
||||
{
|
||||
e2 = c = 0.0;
|
||||
a = ec = ec2 = 1.0;
|
||||
|
||||
mLon = mLat = mRadius = 0.0;
|
||||
mGeodLat = GeodeticAltitude = 0.0;
|
||||
|
||||
mTl2ec.InitMatrix();
|
||||
mTec2l.InitMatrix();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGLocation::FGLocation(const FGLocation& l)
|
||||
: mECLoc(l.mECLoc), mCacheValid(l.mCacheValid)
|
||||
{
|
||||
a = l.a;
|
||||
e2 = l.e2;
|
||||
c = l.c;
|
||||
ec = l.ec;
|
||||
ec2 = l.ec2;
|
||||
mEllipseSet = l.mEllipseSet;
|
||||
|
||||
/*ag
|
||||
* if the cache is not valid, all of the following values are unset.
|
||||
* They will be calculated once ComputeDerivedUnconditional is called.
|
||||
* If unset, they may possibly contain NaN and could thus trigger floating
|
||||
* point exceptions.
|
||||
*/
|
||||
if (!mCacheValid) return;
|
||||
|
||||
mLon = l.mLon;
|
||||
mLat = l.mLat;
|
||||
mRadius = l.mRadius;
|
||||
|
||||
mTl2ec = l.mTl2ec;
|
||||
mTec2l = l.mTec2l;
|
||||
|
||||
mGeodLat = l.mGeodLat;
|
||||
GeodeticAltitude = l.GeodeticAltitude;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGLocation& FGLocation::operator=(const FGLocation& l)
|
||||
{
|
||||
mECLoc = l.mECLoc;
|
||||
mCacheValid = l.mCacheValid;
|
||||
mEllipseSet = l.mEllipseSet;
|
||||
|
||||
a = l.a;
|
||||
e2 = l.e2;
|
||||
c = l.c;
|
||||
ec = l.ec;
|
||||
ec2 = l.ec2;
|
||||
|
||||
//ag See comment in constructor above
|
||||
if (!mCacheValid) return *this;
|
||||
|
||||
mLon = l.mLon;
|
||||
mLat = l.mLat;
|
||||
mRadius = l.mRadius;
|
||||
|
||||
mTl2ec = l.mTl2ec;
|
||||
mTec2l = l.mTec2l;
|
||||
|
||||
mGeodLat = l.mGeodLat;
|
||||
GeodeticAltitude = l.GeodeticAltitude;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetLongitude(double longitude)
|
||||
{
|
||||
double rtmp = mECLoc.Magnitude(eX, eY);
|
||||
// Check if we have zero radius.
|
||||
// If so set it to 1, so that we can set a position
|
||||
if (0.0 == mECLoc.Magnitude())
|
||||
rtmp = 1.0;
|
||||
|
||||
// Fast return if we are on the north or south pole ...
|
||||
if (rtmp == 0.0)
|
||||
return;
|
||||
|
||||
mCacheValid = false;
|
||||
|
||||
mECLoc(eX) = rtmp*cos(longitude);
|
||||
mECLoc(eY) = rtmp*sin(longitude);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetLatitude(double latitude)
|
||||
{
|
||||
mCacheValid = false;
|
||||
|
||||
double r = mECLoc.Magnitude();
|
||||
if (r == 0.0) {
|
||||
mECLoc(eX) = 1.0;
|
||||
r = 1.0;
|
||||
}
|
||||
|
||||
double rtmp = mECLoc.Magnitude(eX, eY);
|
||||
if (rtmp != 0.0) {
|
||||
double fac = r/rtmp*cos(latitude);
|
||||
mECLoc(eX) *= fac;
|
||||
mECLoc(eY) *= fac;
|
||||
} else {
|
||||
mECLoc(eX) = r*cos(latitude);
|
||||
mECLoc(eY) = 0.0;
|
||||
}
|
||||
mECLoc(eZ) = r*sin(latitude);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetRadius(double radius)
|
||||
{
|
||||
mCacheValid = false;
|
||||
|
||||
double rold = mECLoc.Magnitude();
|
||||
if (rold == 0.0)
|
||||
mECLoc(eX) = radius;
|
||||
else
|
||||
mECLoc *= radius/rold;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetPosition(double lon, double lat, double radius)
|
||||
{
|
||||
mCacheValid = false;
|
||||
|
||||
double sinLat = sin(lat);
|
||||
double cosLat = cos(lat);
|
||||
double sinLon = sin(lon);
|
||||
double cosLon = cos(lon);
|
||||
|
||||
mECLoc = { radius*cosLat*cosLon,
|
||||
radius*cosLat*sinLon,
|
||||
radius*sinLat };
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
|
||||
{
|
||||
assert(mEllipseSet);
|
||||
mCacheValid = false;
|
||||
|
||||
double slat = sin(lat);
|
||||
double clat = cos(lat);
|
||||
double RN = a / sqrt(1.0 - e2*slat*slat);
|
||||
|
||||
mECLoc(eX) = (RN + height)*clat*cos(lon);
|
||||
mECLoc(eY) = (RN + height)*clat*sin(lon);
|
||||
mECLoc(eZ) = ((1 - e2)*RN + height)*slat;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::SetEllipse(double semimajor, double semiminor)
|
||||
{
|
||||
mCacheValid = false;
|
||||
mEllipseSet = true;
|
||||
|
||||
a = semimajor;
|
||||
ec = semiminor/a;
|
||||
ec2 = ec * ec;
|
||||
e2 = 1.0 - ec2;
|
||||
c = a * e2;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGLocation::GetSeaLevelRadius(void) const
|
||||
{
|
||||
assert(mEllipseSet);
|
||||
ComputeDerived();
|
||||
double cosLat = cos(mLat);
|
||||
return a*ec/sqrt(1.0-e2*cosLat*cosLat);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGLocation::ComputeDerivedUnconditional(void) const
|
||||
{
|
||||
// The radius is just the Euclidean norm of the vector.
|
||||
mRadius = mECLoc.Magnitude();
|
||||
|
||||
// The distance of the location to the Z-axis, which is the axis
|
||||
// through the poles.
|
||||
double rxy = mECLoc.Magnitude(eX, eY);
|
||||
|
||||
// Compute the longitude and its sin/cos values.
|
||||
double sinLon, cosLon;
|
||||
if (rxy == 0.0) {
|
||||
sinLon = 0.0;
|
||||
cosLon = 1.0;
|
||||
mLon = 0.0;
|
||||
} else {
|
||||
sinLon = mECLoc(eY)/rxy;
|
||||
cosLon = mECLoc(eX)/rxy;
|
||||
mLon = atan2(mECLoc(eY), mECLoc(eX));
|
||||
}
|
||||
|
||||
// Compute the geocentric & geodetic latitudes.
|
||||
double sinLat, cosLat;
|
||||
if (mRadius == 0.0) {
|
||||
mLat = 0.0;
|
||||
sinLat = 0.0;
|
||||
cosLat = 1.0;
|
||||
if (mEllipseSet) {
|
||||
mGeodLat = 0.0;
|
||||
GeodeticAltitude = -a;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mLat = atan2( mECLoc(eZ), rxy );
|
||||
|
||||
// Calculate the geodetic latitude based on "Transformation from Cartesian to
|
||||
// geodetic coordinates accelerated by Halley's method", Fukushima T. (2006)
|
||||
// Journal of Geodesy, Vol. 79, pp. 689-693
|
||||
// Unlike I. Sofair's method which uses a closed form solution, Fukushima's
|
||||
// method is an iterative method whose convergence is so fast that only one
|
||||
// iteration suffices. In addition, Fukushima's method has a much better
|
||||
// numerical stability over Sofair's method at the North and South poles and
|
||||
// it also gives the correct result for a spherical Earth.
|
||||
if (mEllipseSet) {
|
||||
double s0 = fabs(mECLoc(eZ));
|
||||
double zc = ec * s0;
|
||||
double c0 = ec * rxy;
|
||||
double c02 = c0 * c0;
|
||||
double s02 = s0 * s0;
|
||||
double a02 = c02 + s02;
|
||||
double a0 = sqrt(a02);
|
||||
double a03 = a02 * a0;
|
||||
double s1 = zc*a03 + c*s02*s0;
|
||||
double c1 = rxy*a03 - c*c02*c0;
|
||||
double cs0c0 = c*c0*s0;
|
||||
double b0 = 1.5*cs0c0*((rxy*s0-zc*c0)*a0-cs0c0);
|
||||
s1 = s1*a03-b0*s0;
|
||||
double cc = ec*(c1*a03-b0*c0);
|
||||
mGeodLat = sign(mECLoc(eZ))*atan(s1 / cc);
|
||||
double s12 = s1 * s1;
|
||||
double cc2 = cc * cc;
|
||||
double norm = sqrt(s12 + cc2);
|
||||
cosLat = cc / norm;
|
||||
sinLat = sign(mECLoc(eZ)) * s1 / norm;
|
||||
GeodeticAltitude = (rxy*cc + s0*s1 - a*sqrt(ec2*s12 + cc2)) / norm;
|
||||
}
|
||||
else {
|
||||
sinLat = mECLoc(eZ)/mRadius;
|
||||
cosLat = rxy/mRadius;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the transform matrices from and to the earth centered frame.
|
||||
// See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
|
||||
// Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
|
||||
// orientation of the navigation (local) frame relative to the ECEF frame,
|
||||
// and a transformation from ECEF to nav (local) frame.
|
||||
|
||||
mTec2l = { -cosLon*sinLat, -sinLon*sinLat, cosLat,
|
||||
-sinLon , cosLon , 0.0 ,
|
||||
-cosLon*cosLat, -sinLon*cosLat, -sinLat };
|
||||
|
||||
// In Stevens and Lewis notation, this is C_e/n - the
|
||||
// orientation of the ECEF frame relative to the nav (local) frame,
|
||||
// and a transformation from nav (local) to ECEF frame.
|
||||
|
||||
mTl2ec = mTec2l.Transposed();
|
||||
|
||||
// Mark the cached values as valid
|
||||
mCacheValid = true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The calculations, below, implement the Haversine formulas to calculate
|
||||
// heading and distance to a set of lat/long coordinates from the current
|
||||
// position.
|
||||
//
|
||||
// The basic equations are (lat1, long1 are source positions; lat2
|
||||
// long2 are target positions):
|
||||
//
|
||||
// R = earth’s radius
|
||||
// Δlat = lat2 − lat1
|
||||
// Δlong = long2 − long1
|
||||
//
|
||||
// For the waypoint distance calculation:
|
||||
//
|
||||
// a = sin²(Δlat/2) + cos(lat1)∙cos(lat2)∙sin²(Δlong/2)
|
||||
// c = 2∙atan2(√a, √(1−a))
|
||||
// d = R∙c
|
||||
|
||||
double FGLocation::GetDistanceTo(double target_longitude,
|
||||
double target_latitude) const
|
||||
{
|
||||
ComputeDerived();
|
||||
double delta_lat_rad = target_latitude - mLat;
|
||||
double delta_lon_rad = target_longitude - mLon;
|
||||
|
||||
double distance_a = pow(sin(0.5*delta_lat_rad), 2.0)
|
||||
+ (cos(mLat) * cos(target_latitude)
|
||||
* (pow(sin(0.5*delta_lon_rad), 2.0)));
|
||||
|
||||
return 2.0 * GetRadius() * atan2(sqrt(distance_a), sqrt(1.0 - distance_a));
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The calculations, below, implement the Haversine formulas to calculate
|
||||
// heading and distance to a set of lat/long coordinates from the current
|
||||
// position.
|
||||
//
|
||||
// The basic equations are (lat1, long1 are source positions; lat2
|
||||
// long2 are target positions):
|
||||
//
|
||||
// R = earth’s radius
|
||||
// Δlat = lat2 − lat1
|
||||
// Δlong = long2 − long1
|
||||
//
|
||||
// For the heading angle calculation:
|
||||
//
|
||||
// θ = atan2(sin(Δlong)∙cos(lat2), cos(lat1)∙sin(lat2) − sin(lat1)∙cos(lat2)∙cos(Δlong))
|
||||
|
||||
double FGLocation::GetHeadingTo(double target_longitude,
|
||||
double target_latitude) const
|
||||
{
|
||||
ComputeDerived();
|
||||
double delta_lon_rad = target_longitude - mLon;
|
||||
|
||||
double Y = sin(delta_lon_rad) * cos(target_latitude);
|
||||
double X = cos(mLat) * sin(target_latitude)
|
||||
- sin(mLat) * cos(target_latitude) * cos(delta_lon_rad);
|
||||
|
||||
double heading_to_waypoint_rad = atan2(Y, X);
|
||||
if (heading_to_waypoint_rad < 0) heading_to_waypoint_rad += 2.0*M_PI;
|
||||
|
||||
return heading_to_waypoint_rad;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
} // namespace JSBSim
|
||||
550
src/FDM/JSBSim/math/FGLocation.h
Normal file
550
src/FDM/JSBSim/math/FGLocation.h
Normal file
@@ -0,0 +1,550 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGLocation.h
|
||||
Author: Jon S. Berndt, Mathias Froehlich
|
||||
Date started: 04/04/2004
|
||||
|
||||
------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
|
||||
------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
|
||||
------- (C) 2011 Ola Røer Thorsen (ola@silentwings.no) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
-------------------------------------------------------------------------------
|
||||
04/04/2004 MF Created from code previously in the old positions class.
|
||||
11/01/2011 ORT Encapsulated ground callback code in FGLocation and removed
|
||||
it from FGFDMExec.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGLOCATION_H
|
||||
#define FGLOCATION_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGColumnVector3.h"
|
||||
#include "FGMatrix33.h"
|
||||
|
||||
/* Setting the -ffast-math compilation flag is highly discouraged */
|
||||
#ifdef __FAST_MATH__
|
||||
#error Usage of -ffast-math is strongly discouraged
|
||||
#endif
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** FGLocation holds an arbitrary location in the Earth centered Earth fixed
|
||||
reference frame (ECEF). The coordinate frame ECEF has its center in the
|
||||
middle of the earth. The X-axis points from the center of the Earth towards
|
||||
a location with zero latitude and longitude on the Earth surface. The Y-axis
|
||||
points from the center of the Earth towards a location with zero latitude
|
||||
and 90 deg East longitude on the Earth surface. The Z-axis points from the
|
||||
Earth center to the geographic north pole.
|
||||
|
||||
This class provides access functions to set and get the location as either
|
||||
the simple X, Y and Z values in ft or longitude/latitude and the radial
|
||||
distance of the location from the Earth center.
|
||||
|
||||
It is common to associate a parent frame with a location. This frame is
|
||||
usually called the local horizontal frame or simply the local frame. It is
|
||||
also called the NED frame (North, East, Down), as well as the Navigation
|
||||
frame. This frame has its X/Y plane parallel to the surface of the Earth.
|
||||
The X-axis points towards north, the Y-axis points east and the Z-axis
|
||||
is normal to the reference spheroid (WGS84 for Earth).
|
||||
|
||||
Since the local frame is determined by the location (and NOT by the
|
||||
orientation of the vehicle IN any frame), this class also provides the
|
||||
rotation matrices required to transform from the Earth centered (ECEF) frame
|
||||
to the local horizontal frame and back. This class "owns" the
|
||||
transformations that go from the ECEF frame to and from the local frame.
|
||||
Again, this is because the ECEF, and local frames do not involve the actual
|
||||
orientation of the vehicle - only the location on the Earth surface. There
|
||||
are conversion functions for conversion of position vectors given in the one
|
||||
frame to positions in the other frame.
|
||||
|
||||
The Earth centered reference frame is NOT an inertial frame since it rotates
|
||||
with the Earth.
|
||||
|
||||
The cartesian coordinates (X,Y,Z) in the Earth centered frame are the master
|
||||
values. All other values are computed from these master values and are
|
||||
cached as long as the location is changed by access through a non-const
|
||||
member function. Values are cached to improve performance. It is best
|
||||
practice to work with a natural set of master values. Other parameters that
|
||||
are derived from these master values are calculated only when needed, and IF
|
||||
they are needed and calculated, then they are cached (stored and remembered)
|
||||
so they do not need to be re-calculated until the master values they are
|
||||
derived from are themselves changed (and become stale).
|
||||
|
||||
Accuracy and round off
|
||||
|
||||
Given,
|
||||
|
||||
- that we model a vehicle near the Earth
|
||||
- that the Earth surface average radius is about 2*10^7, ft
|
||||
- that we use double values for the representation of the location
|
||||
|
||||
we have an accuracy of about
|
||||
|
||||
1e-16*2e7ft/1 = 2e-9 ft
|
||||
|
||||
left. This should be sufficient for our needs. Note that this is the same
|
||||
relative accuracy we would have when we compute directly with
|
||||
lon/lat/radius. For the radius value this is clear. For the lon/lat pair
|
||||
this is easy to see. Take for example KSFO located at about 37.61 deg north
|
||||
122.35 deg west, which corresponds to 0.65642 rad north and 2.13541 rad
|
||||
west. Both values are of magnitude of about 1. But 1 ft corresponds to about
|
||||
1/(2e7*2*pi) = 7.9577e-09 rad. So the left accuracy with this representation
|
||||
is also about 1*1e-16/7.9577e-09 = 1.2566e-08 which is of the same magnitude
|
||||
as the representation chosen here.
|
||||
|
||||
The advantage of this representation is that it is a linear space without
|
||||
singularities. The singularities are the north and south pole and most
|
||||
notably the non-steady jump at -pi to pi. It is harder to track this jump
|
||||
correctly especially when we need to work with error norms and derivatives
|
||||
of the equations of motion within the time-stepping code. Also, the rate of
|
||||
change is of the same magnitude for all components in this representation
|
||||
which is an advantage for numerical stability in implicit time-stepping.
|
||||
|
||||
Note: Both GEOCENTRIC and GEODETIC latitudes can be used. In order to get
|
||||
best matching relative to a map, geodetic latitude must be used.
|
||||
|
||||
@see Stevens and Lewis, "Aircraft Control and Simulation", Second edition
|
||||
@see W. C. Durham "Aircraft Dynamics & Control", section 2.2
|
||||
|
||||
@author Mathias Froehlich
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGLocation : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/** Default constructor. */
|
||||
FGLocation(void);
|
||||
|
||||
/** Constructor to set the longitude, latitude and the distance
|
||||
from the center of the earth.
|
||||
@param lon longitude
|
||||
@param lat GEOCENTRIC latitude
|
||||
@param radius distance from center of earth to vehicle in feet*/
|
||||
FGLocation(double lon, double lat, double radius);
|
||||
|
||||
/** Constructor to initialize the location with the cartesian coordinates
|
||||
(X,Y,Z) contained in the input FGColumnVector3. Distances are in feet,
|
||||
the position is expressed in the ECEF frame.
|
||||
@param lv vector that contain the cartesian coordinates*/
|
||||
FGLocation(const FGColumnVector3& lv);
|
||||
|
||||
/** Copy constructor. */
|
||||
FGLocation(const FGLocation& l);
|
||||
|
||||
/** Set the longitude.
|
||||
@param longitude Longitude in rad to set.
|
||||
Sets the longitude of the location represented with this class instance to
|
||||
the value of the given argument. The value is meant to be in rad. The
|
||||
latitude and the radius value are preserved with this call with the
|
||||
exception of radius being equal to zero. If the radius is previously set
|
||||
to zero it is changed to be equal to 1.0 past this call.
|
||||
Longitude is positive east and negative west.
|
||||
The arguments should be within the bounds of -pi <= lon <= pi.
|
||||
The behavior of this function with arguments outside this range is left as
|
||||
an exercise to the gentle reader ... */
|
||||
void SetLongitude(double longitude);
|
||||
|
||||
/** Set the GEOCENTRIC latitude.
|
||||
@param latitude GEOCENTRIC latitude in rad to set.
|
||||
Sets the latitude of the location represented with this class instance to
|
||||
the value of the given argument. The value is meant to be in rad. The
|
||||
longitude and the radius value are preserved with this call with the
|
||||
exception of radius being equal to zero. If the radius is previously set
|
||||
to zero it is changed to be equal to 1.0 past this call.
|
||||
Latitude is positive north and negative south.
|
||||
The arguments should be within the bounds of -pi/2 <= lat <= pi/2.
|
||||
The behavior of this function with arguments outside this range is left as
|
||||
an exercise to the gentle reader ... */
|
||||
void SetLatitude(double latitude);
|
||||
|
||||
/** Set the distance from the center of the earth.
|
||||
@param radius Radius in ft to set.
|
||||
Sets the radius of the location represented with this class instance to
|
||||
the value of the given argument. The value is meant to be in ft. The
|
||||
latitude and longitude values are preserved with this call with the
|
||||
exception of radius being equal to zero. If the radius is previously set
|
||||
to zero, latitude and longitude is set equal to zero past this call.
|
||||
The argument should be positive.
|
||||
The behavior of this function called with a negative argument is left as
|
||||
an exercise to the gentle reader ... */
|
||||
void SetRadius(double radius);
|
||||
|
||||
/** Sets the longitude, latitude and the distance from the center of the earth.
|
||||
@param lon longitude in radians
|
||||
@param lat GEOCENTRIC latitude in radians
|
||||
@param radius distance from center of earth to vehicle in feet*/
|
||||
void SetPosition(double lon, double lat, double radius);
|
||||
|
||||
/** Sets the longitude, latitude and the distance above the reference spheroid.
|
||||
@param lon longitude in radians
|
||||
@param lat GEODETIC latitude in radians
|
||||
@param height distance above the reference ellipsoid to vehicle in feet*/
|
||||
void SetPositionGeodetic(double lon, double lat, double height);
|
||||
|
||||
/** Sets the semimajor and semiminor axis lengths for this planet.
|
||||
The eccentricity and flattening are calculated from the semimajor
|
||||
and semiminor axis lengths.
|
||||
@param semimajor planet semi-major axis in ft.
|
||||
@param semiminor planet semi-minor axis in ft.*/
|
||||
void SetEllipse(double semimajor, double semiminor);
|
||||
|
||||
/** Get the longitude.
|
||||
@return the longitude in rad of the location represented with this
|
||||
class instance. The returned values are in the range between
|
||||
-pi <= lon <= pi. Longitude is positive east and negative west. */
|
||||
double GetLongitude() const { ComputeDerived(); return mLon; }
|
||||
|
||||
/** Get the longitude.
|
||||
@return the longitude in deg of the location represented with this
|
||||
class instance. The returned values are in the range between
|
||||
-180 <= lon <= 180. Longitude is positive east and negative west. */
|
||||
double GetLongitudeDeg() const { ComputeDerived(); return radtodeg*mLon; }
|
||||
|
||||
/** Get the sine of Longitude. */
|
||||
double GetSinLongitude() const { ComputeDerived(); return -mTec2l(2,1); }
|
||||
|
||||
/** Get the cosine of Longitude. */
|
||||
double GetCosLongitude() const { ComputeDerived(); return mTec2l(2,2); }
|
||||
|
||||
/** Get the GEOCENTRIC latitude in radians.
|
||||
@return the geocentric latitude in rad of the location represented with
|
||||
this class instance. The returned values are in the range between
|
||||
-pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
|
||||
double GetLatitude() const { ComputeDerived(); return mLat; }
|
||||
|
||||
/** Get the GEODETIC latitude in radians.
|
||||
@return the geodetic latitude in rad of the location represented with this
|
||||
class instance. The returned values are in the range between
|
||||
-pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
|
||||
double GetGeodLatitudeRad(void) const {
|
||||
assert(mEllipseSet);
|
||||
ComputeDerived(); return mGeodLat;
|
||||
}
|
||||
|
||||
/** Get the GEOCENTRIC latitude in degrees.
|
||||
@return the geocentric latitude in deg of the location represented with
|
||||
this class instance. The returned value is in the range between
|
||||
-90 <= lon <= 90. Latitude is positive north and negative south. */
|
||||
double GetLatitudeDeg() const { ComputeDerived(); return radtodeg*mLat; }
|
||||
|
||||
/** Get the GEODETIC latitude in degrees.
|
||||
@return the geodetic latitude in degrees of the location represented by
|
||||
this class instance. The returned value is in the range between
|
||||
-90 <= lon <= 90. Latitude is positive north and negative south. */
|
||||
double GetGeodLatitudeDeg(void) const {
|
||||
assert(mEllipseSet);
|
||||
ComputeDerived(); return radtodeg*mGeodLat;
|
||||
}
|
||||
|
||||
/** Gets the geodetic altitude in feet. */
|
||||
double GetGeodAltitude(void) const {
|
||||
assert(mEllipseSet);
|
||||
ComputeDerived(); return GeodeticAltitude;
|
||||
}
|
||||
|
||||
/** Get the sea level radius in feet below the current location. */
|
||||
double GetSeaLevelRadius(void) const;
|
||||
|
||||
/** Get the distance from the center of the earth in feet.
|
||||
@return the distance of the location represented with this class
|
||||
instance to the center of the earth in ft. The radius value is
|
||||
always positive. */
|
||||
double GetRadius() const { ComputeDerived(); return mRadius; }
|
||||
|
||||
/** Transform matrix from local horizontal to earth centered frame.
|
||||
@return a const reference to the rotation matrix of the transform from
|
||||
the local horizontal frame to the earth centered frame. */
|
||||
const FGMatrix33& GetTl2ec(void) const { ComputeDerived(); return mTl2ec; }
|
||||
|
||||
/** Transform matrix from the earth centered to local horizontal frame.
|
||||
@return a const reference to the rotation matrix of the transform from
|
||||
the earth centered frame to the local horizontal frame. */
|
||||
const FGMatrix33& GetTec2l(void) const { ComputeDerived(); return mTec2l; }
|
||||
|
||||
/** Get the geodetic distance between the current location and a given
|
||||
location. This corresponds to the shortest distance between the two
|
||||
locations. Earth curvature is taken into account.
|
||||
@param target_longitude the target longitude in radians
|
||||
@param target_latitude the target latitude in radians
|
||||
@return The geodetic distance between the two locations */
|
||||
double GetDistanceTo(double target_longitude, double target_latitude) const;
|
||||
|
||||
/** Get the heading that should be followed from the current location to
|
||||
a given location along the shortest path. Earth curvature is taken into
|
||||
account.
|
||||
@param target_longitude the target longitude in radians
|
||||
@param target_latitude the target latitude in radians
|
||||
@return The heading that should be followed to reach the targeted
|
||||
location along the shortest path */
|
||||
double GetHeadingTo(double target_longitude, double target_latitude) const;
|
||||
|
||||
/** Conversion from Local frame coordinates to a location in the
|
||||
earth centered and fixed frame.
|
||||
This function calculates the FGLocation of an object which position
|
||||
relative to the vehicle is given as in input.
|
||||
@param lvec Vector in the local horizontal coordinate frame
|
||||
@return The location in the earth centered and fixed frame */
|
||||
FGLocation LocalToLocation(const FGColumnVector3& lvec) const {
|
||||
ComputeDerived(); return mTl2ec*lvec + mECLoc;
|
||||
}
|
||||
|
||||
/** Conversion from a location in the earth centered and fixed frame
|
||||
to local horizontal frame coordinates.
|
||||
This function calculates the relative position between the vehicle and
|
||||
the input vector and returns the result expressed in the local frame.
|
||||
@param ecvec Vector in the earth centered and fixed frame
|
||||
@return The vector in the local horizontal coordinate frame */
|
||||
FGColumnVector3 LocationToLocal(const FGColumnVector3& ecvec) const {
|
||||
ComputeDerived(); return mTec2l*(ecvec - mECLoc);
|
||||
}
|
||||
|
||||
// For time-stepping, locations have vector properties...
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
@param idx the component index.
|
||||
Return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double operator()(unsigned int idx) const { return mECLoc.Entry(idx); }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
@param idx the component index.
|
||||
@return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double& operator()(unsigned int idx) { mCacheValid = false; return mECLoc.Entry(idx); }
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
@param idx the component index.
|
||||
@return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
This function is just a shortcut for the <tt>double
|
||||
operator()(unsigned int idx) const</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double Entry(unsigned int idx) const { return mECLoc.Entry(idx); }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
@param idx the component index.
|
||||
@return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
This function is just a shortcut for the double&
|
||||
operator()(unsigned int idx) function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
Note that the index given in the argument is unchecked. */
|
||||
double& Entry(unsigned int idx) {
|
||||
mCacheValid = false; return mECLoc.Entry(idx);
|
||||
}
|
||||
|
||||
/** Sets this location via the supplied vector.
|
||||
The location can be set by an Earth-centered, Earth-fixed (ECEF) frame
|
||||
position vector. The cache is marked as invalid, so any future requests
|
||||
for selected important data will cause the parameters to be calculated.
|
||||
@param v the ECEF column vector in feet.
|
||||
@return a reference to the FGLocation object. */
|
||||
const FGLocation& operator=(const FGColumnVector3& v)
|
||||
{
|
||||
mECLoc(eX) = v(eX);
|
||||
mECLoc(eY) = v(eY);
|
||||
mECLoc(eZ) = v(eZ);
|
||||
mCacheValid = false;
|
||||
//ComputeDerived();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Sets this location via the supplied location object.
|
||||
@param l A location object reference.
|
||||
@return a reference to the FGLocation object. */
|
||||
FGLocation& operator=(const FGLocation& l);
|
||||
|
||||
/** This operator returns true if the ECEF location vectors for the two
|
||||
location objects are equal. */
|
||||
bool operator==(const FGLocation& l) const {
|
||||
return mECLoc == l.mECLoc;
|
||||
}
|
||||
|
||||
/** This operator returns true if the ECEF location vectors for the two
|
||||
location objects are not equal. */
|
||||
bool operator!=(const FGLocation& l) const { return ! operator==(l); }
|
||||
|
||||
/** This operator adds the ECEF position vectors.
|
||||
The cartesian coordinates of the supplied vector (right side) are added to
|
||||
the ECEF position vector on the left side of the equality, and a reference
|
||||
to this object is returned. */
|
||||
const FGLocation& operator+=(const FGLocation &l) {
|
||||
mCacheValid = false;
|
||||
mECLoc += l.mECLoc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This operator substracts the ECEF position vectors.
|
||||
The cartesian coordinates of the supplied vector (right side) are
|
||||
substracted from the ECEF position vector on the left side of the
|
||||
equality, and a reference to this object is returned. */
|
||||
const FGLocation& operator-=(const FGLocation &l) {
|
||||
mCacheValid = false;
|
||||
mECLoc -= l.mECLoc;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This operator scales the ECEF position vector.
|
||||
The cartesian coordinates of the ECEF position vector on the left side of
|
||||
the equality are scaled by the supplied value (right side), and a
|
||||
reference to this object is returned. */
|
||||
const FGLocation& operator*=(double scalar) {
|
||||
mCacheValid = false;
|
||||
mECLoc *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** This operator scales the ECEF position vector.
|
||||
The cartesian coordinates of the ECEF position vector on the left side of
|
||||
the equality are scaled by the inverse of the supplied value (right side),
|
||||
and a reference to this object is returned. */
|
||||
const FGLocation& operator/=(double scalar) {
|
||||
return operator*=(1.0/scalar);
|
||||
}
|
||||
|
||||
/** This operator adds two ECEF position vectors.
|
||||
A new object is returned that defines a position which is the sum of the
|
||||
cartesian coordinates of the two positions provided. */
|
||||
FGLocation operator+(const FGLocation& l) const {
|
||||
FGLocation result(mECLoc + l.mECLoc);
|
||||
if (mEllipseSet) result.SetEllipse(a, ec*a);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** This operator substracts two ECEF position vectors.
|
||||
A new object is returned that defines a position which is the difference
|
||||
of the cartesian coordinates of the two positions provided. */
|
||||
FGLocation operator-(const FGLocation& l) const {
|
||||
FGLocation result(mECLoc - l.mECLoc);
|
||||
if (mEllipseSet) result.SetEllipse(a, ec*a);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** This operator scales an ECEF position vector.
|
||||
A new object is returned that defines a position made of the cartesian
|
||||
coordinates of the provided ECEF position scaled by the supplied scalar
|
||||
value. */
|
||||
FGLocation operator*(double scalar) const {
|
||||
FGLocation result(scalar*mECLoc);
|
||||
if (mEllipseSet) result.SetEllipse(a, ec*a);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Cast to a simple 3d vector */
|
||||
operator const FGColumnVector3&() const {
|
||||
return mECLoc;
|
||||
}
|
||||
|
||||
private:
|
||||
/** Computation of derived values.
|
||||
This function re-computes the derived values like lat/lon and
|
||||
transformation matrices. It does this unconditionally. */
|
||||
void ComputeDerivedUnconditional(void) const;
|
||||
|
||||
/** Computation of derived values.
|
||||
This function checks if the derived values like lat/lon and
|
||||
transformation matrices are already computed. If so, it
|
||||
returns. If they need to be computed this is done here. */
|
||||
void ComputeDerived(void) const {
|
||||
if (!mCacheValid)
|
||||
ComputeDerivedUnconditional();
|
||||
}
|
||||
|
||||
/** The coordinates in the earth centered frame. This is the master copy.
|
||||
The coordinate frame has its center in the middle of the earth.
|
||||
Its x-axis points from the center of the earth towards a
|
||||
location with zero latitude and longitude on the earths
|
||||
surface. The y-axis points from the center of the earth towards a
|
||||
location with zero latitude and 90deg longitude on the earths
|
||||
surface. The z-axis points from the earths center to the
|
||||
geographic north pole.
|
||||
@see W. C. Durham "Aircraft Dynamics & Control", section 2.2 */
|
||||
FGColumnVector3 mECLoc;
|
||||
|
||||
/** The cached lon/lat/radius values. */
|
||||
mutable double mLon;
|
||||
mutable double mLat;
|
||||
mutable double mRadius;
|
||||
mutable double mGeodLat;
|
||||
mutable double GeodeticAltitude;
|
||||
|
||||
/** The cached rotation matrices from and to the associated frames. */
|
||||
mutable FGMatrix33 mTl2ec;
|
||||
mutable FGMatrix33 mTec2l;
|
||||
|
||||
/* Terms for geodetic latitude calculation. Values are from WGS84 model */
|
||||
double a; // Earth semimajor axis in feet
|
||||
double e2; // Earth eccentricity squared
|
||||
double c;
|
||||
double ec;
|
||||
double ec2;
|
||||
|
||||
/** A data validity flag.
|
||||
This class implements caching of the derived values like the
|
||||
orthogonal rotation matrices or the lon/lat/radius values. For caching we
|
||||
carry a flag which signals if the values are valid or not.
|
||||
The C++ keyword "mutable" tells the compiler that the data member is
|
||||
allowed to change during a const member function. */
|
||||
mutable bool mCacheValid;
|
||||
// Flag that checks that geodetic methods are called after SetEllipse() has
|
||||
// been called.
|
||||
bool mEllipseSet = false;
|
||||
};
|
||||
|
||||
/** Scalar multiplication.
|
||||
|
||||
@param scalar scalar value to multiply with.
|
||||
@param l Vector to multiply.
|
||||
|
||||
Multiply the Vector with a scalar value. */
|
||||
inline FGLocation operator*(double scalar, const FGLocation& l)
|
||||
{
|
||||
return l.operator*(scalar);
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
502
src/FDM/JSBSim/math/FGMatrix33.cpp
Normal file
502
src/FDM/JSBSim/math/FGMatrix33.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGMatrix33.cpp
|
||||
Author: Tony Peden, Jon Berndt, Mathias Frolich
|
||||
Date started: 1998
|
||||
Purpose: FGMatrix33 class
|
||||
Called by: Various
|
||||
|
||||
------------- Copyright (C) 1998 by the authors above -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
??/??/?? TP Created
|
||||
03/16/2000 JSB Added exception throwing
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGMatrix33.h"
|
||||
#include "FGColumnVector3.h"
|
||||
#include "FGQuaternion.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33::FGMatrix33(void)
|
||||
{
|
||||
data[0] = data[1] = data[2] = data[3] = data[4] = data[5] =
|
||||
data[6] = data[7] = data[8] = 0.0;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGMatrix33::Dump(const string& delimiter) const
|
||||
{
|
||||
ostringstream buffer;
|
||||
buffer << setw(12) << setprecision(10) << data[0] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[3] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[6] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[1] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[4] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[7] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[2] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[5] << delimiter;
|
||||
buffer << setw(12) << setprecision(10) << data[8];
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGMatrix33::Dump(const string& delimiter, const string& prefix) const
|
||||
{
|
||||
ostringstream buffer;
|
||||
|
||||
buffer << prefix << right << fixed << setw(9) << setprecision(6) << data[0] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[3] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[6] << endl;
|
||||
|
||||
buffer << prefix << right << fixed << setw(9) << setprecision(6) << data[1] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[4] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[7] << endl;
|
||||
|
||||
buffer << prefix << right << fixed << setw(9) << setprecision(6) << data[2] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[5] << delimiter;
|
||||
buffer << right << fixed << setw(9) << setprecision(6) << data[8];
|
||||
|
||||
buffer << setw(0) << left;
|
||||
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGQuaternion FGMatrix33::GetQuaternion(void) const
|
||||
{
|
||||
FGQuaternion Q;
|
||||
|
||||
double tempQ[4];
|
||||
int idx;
|
||||
|
||||
tempQ[0] = 1.0 + data[0] + data[4] + data[8];
|
||||
tempQ[1] = 1.0 + data[0] - data[4] - data[8];
|
||||
tempQ[2] = 1.0 - data[0] + data[4] - data[8];
|
||||
tempQ[3] = 1.0 - data[0] - data[4] + data[8];
|
||||
|
||||
// Find largest of the above
|
||||
idx = 0;
|
||||
for (int i=1; i<4; i++) if (tempQ[i] > tempQ[idx]) idx = i;
|
||||
|
||||
switch(idx) {
|
||||
case 0:
|
||||
Q(1) = 0.50*sqrt(tempQ[0]);
|
||||
Q(2) = 0.25*(data[7] - data[5])/Q(1);
|
||||
Q(3) = 0.25*(data[2] - data[6])/Q(1);
|
||||
Q(4) = 0.25*(data[3] - data[1])/Q(1);
|
||||
break;
|
||||
case 1:
|
||||
Q(2) = 0.50*sqrt(tempQ[1]);
|
||||
Q(1) = 0.25*(data[7] - data[5])/Q(2);
|
||||
Q(3) = 0.25*(data[3] + data[1])/Q(2);
|
||||
Q(4) = 0.25*(data[2] + data[6])/Q(2);
|
||||
break;
|
||||
case 2:
|
||||
Q(3) = 0.50*sqrt(tempQ[2]);
|
||||
Q(1) = 0.25*(data[2] - data[6])/Q(3);
|
||||
Q(2) = 0.25*(data[3] + data[1])/Q(3);
|
||||
Q(4) = 0.25*(data[7] + data[5])/Q(3);
|
||||
break;
|
||||
case 3:
|
||||
Q(4) = 0.50*sqrt(tempQ[3]);
|
||||
Q(1) = 0.25*(data[3] - data[1])/Q(4);
|
||||
Q(2) = 0.25*(data[6] + data[2])/Q(4);
|
||||
Q(3) = 0.25*(data[7] + data[5])/Q(4);
|
||||
break;
|
||||
default:
|
||||
//error
|
||||
break;
|
||||
}
|
||||
|
||||
return (Q);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Compute the Euler-angles
|
||||
// Also see Jack Kuipers, "Quaternions and Rotation Sequences", section 7.8..
|
||||
|
||||
FGColumnVector3 FGMatrix33::GetEuler(void) const
|
||||
{
|
||||
FGColumnVector3 mEulerAngles;
|
||||
bool GimbalLock = false;
|
||||
|
||||
if (data[6] <= -1.0) {
|
||||
mEulerAngles(2) = 0.5*M_PI;
|
||||
GimbalLock = true;
|
||||
}
|
||||
else if (1.0 <= data[6]) {
|
||||
mEulerAngles(2) = -0.5*M_PI;
|
||||
GimbalLock = true;
|
||||
}
|
||||
else
|
||||
mEulerAngles(2) = asin(-data[6]);
|
||||
|
||||
if (GimbalLock)
|
||||
mEulerAngles(1) = atan2(-data[5], data[4]);
|
||||
else
|
||||
mEulerAngles(1) = atan2(data[7], data[8]);
|
||||
|
||||
if (GimbalLock)
|
||||
mEulerAngles(3) = 0.0;
|
||||
else {
|
||||
double psi = atan2(data[3], data[0]);
|
||||
if (psi < 0.0)
|
||||
psi += 2*M_PI;
|
||||
mEulerAngles(3) = psi;
|
||||
}
|
||||
|
||||
return mEulerAngles;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
ostream& operator<<(ostream& os, const FGMatrix33& M)
|
||||
{
|
||||
for (unsigned int i=1; i<=M.Rows(); i++) {
|
||||
for (unsigned int j=1; j<=M.Cols(); j++) {
|
||||
if (i == M.Rows() && j == M.Cols())
|
||||
os << M(i,j);
|
||||
else
|
||||
os << M(i,j) << ", ";
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
istream& operator>>(istream& is, FGMatrix33& M)
|
||||
{
|
||||
for (unsigned int i=1; i<=M.Rows(); i++) {
|
||||
for (unsigned int j=1; j<=M.Cols(); j++) {
|
||||
is >> M(i,j);
|
||||
}
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGMatrix33::Determinant(void) const {
|
||||
return data[0]*data[4]*data[8] + data[3]*data[7]*data[2]
|
||||
+ data[6]*data[1]*data[5] - data[6]*data[4]*data[2]
|
||||
- data[3]*data[1]*data[8] - data[7]*data[5]*data[0];
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33 FGMatrix33::Inverse(void) const {
|
||||
// Compute the inverse of a general matrix using Cramers rule.
|
||||
// I guess googling for cramers rule gives tons of references
|
||||
// for this. :)
|
||||
|
||||
if (Determinant() != 0.0) {
|
||||
double rdet = 1.0/Determinant();
|
||||
|
||||
double i11 = rdet*(data[4]*data[8]-data[7]*data[5]);
|
||||
double i21 = rdet*(data[7]*data[2]-data[1]*data[8]);
|
||||
double i31 = rdet*(data[1]*data[5]-data[4]*data[2]);
|
||||
double i12 = rdet*(data[6]*data[5]-data[3]*data[8]);
|
||||
double i22 = rdet*(data[0]*data[8]-data[6]*data[2]);
|
||||
double i32 = rdet*(data[3]*data[2]-data[0]*data[5]);
|
||||
double i13 = rdet*(data[3]*data[7]-data[6]*data[4]);
|
||||
double i23 = rdet*(data[6]*data[1]-data[0]*data[7]);
|
||||
double i33 = rdet*(data[0]*data[4]-data[3]*data[1]);
|
||||
|
||||
return FGMatrix33( i11, i12, i13,
|
||||
i21, i22, i23,
|
||||
i31, i32, i33 );
|
||||
} else {
|
||||
return FGMatrix33( 0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGMatrix33::InitMatrix(void)
|
||||
{
|
||||
data[0] = data[1] = data[2] = data[3] = data[4] = data[5] =
|
||||
data[6] = data[7] = data[8] = 0.0;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// binary operators ************************************************************
|
||||
// *****************************************************************************
|
||||
|
||||
FGMatrix33 FGMatrix33::operator-(const FGMatrix33& M) const
|
||||
{
|
||||
return FGMatrix33( data[0] - M.data[0],
|
||||
data[3] - M.data[3],
|
||||
data[6] - M.data[6],
|
||||
data[1] - M.data[1],
|
||||
data[4] - M.data[4],
|
||||
data[7] - M.data[7],
|
||||
data[2] - M.data[2],
|
||||
data[5] - M.data[5],
|
||||
data[8] - M.data[8] );
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33& FGMatrix33::operator-=(const FGMatrix33 &M)
|
||||
{
|
||||
data[0] -= M.data[0];
|
||||
data[1] -= M.data[1];
|
||||
data[2] -= M.data[2];
|
||||
data[3] -= M.data[3];
|
||||
data[4] -= M.data[4];
|
||||
data[5] -= M.data[5];
|
||||
data[6] -= M.data[6];
|
||||
data[7] -= M.data[7];
|
||||
data[8] -= M.data[8];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33 FGMatrix33::operator+(const FGMatrix33& M) const
|
||||
{
|
||||
return FGMatrix33( data[0] + M.data[0],
|
||||
data[3] + M.data[3],
|
||||
data[6] + M.data[6],
|
||||
data[1] + M.data[1],
|
||||
data[4] + M.data[4],
|
||||
data[7] + M.data[7],
|
||||
data[2] + M.data[2],
|
||||
data[5] + M.data[5],
|
||||
data[8] + M.data[8] );
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33& FGMatrix33::operator+=(const FGMatrix33 &M)
|
||||
{
|
||||
data[0] += M.data[0];
|
||||
data[3] += M.data[3];
|
||||
data[6] += M.data[6];
|
||||
data[1] += M.data[1];
|
||||
data[4] += M.data[4];
|
||||
data[7] += M.data[7];
|
||||
data[2] += M.data[2];
|
||||
data[5] += M.data[5];
|
||||
data[8] += M.data[8];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33 FGMatrix33::operator*(const double scalar) const
|
||||
{
|
||||
return FGMatrix33( scalar * data[0],
|
||||
scalar * data[3],
|
||||
scalar * data[6],
|
||||
scalar * data[1],
|
||||
scalar * data[4],
|
||||
scalar * data[7],
|
||||
scalar * data[2],
|
||||
scalar * data[5],
|
||||
scalar * data[8] );
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
/*
|
||||
FGMatrix33 operator*(double scalar, FGMatrix33 &M)
|
||||
{
|
||||
return FGMatrix33( scalar * M(1,1),
|
||||
scalar * M(1,2),
|
||||
scalar * M(1,3),
|
||||
scalar * M(2,1),
|
||||
scalar * M(2,2),
|
||||
scalar * M(2,3),
|
||||
scalar * M(3,1),
|
||||
scalar * M(3,2),
|
||||
scalar * M(3,3) );
|
||||
}
|
||||
*/
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33& FGMatrix33::operator*=(const double scalar)
|
||||
{
|
||||
data[0] *= scalar;
|
||||
data[3] *= scalar;
|
||||
data[6] *= scalar;
|
||||
data[1] *= scalar;
|
||||
data[4] *= scalar;
|
||||
data[7] *= scalar;
|
||||
data[2] *= scalar;
|
||||
data[5] *= scalar;
|
||||
data[8] *= scalar;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33 FGMatrix33::operator*(const FGMatrix33& M) const
|
||||
{
|
||||
FGMatrix33 Product;
|
||||
|
||||
Product.data[0] = data[0]*M.data[0] + data[3]*M.data[1] + data[6]*M.data[2];
|
||||
Product.data[3] = data[0]*M.data[3] + data[3]*M.data[4] + data[6]*M.data[5];
|
||||
Product.data[6] = data[0]*M.data[6] + data[3]*M.data[7] + data[6]*M.data[8];
|
||||
Product.data[1] = data[1]*M.data[0] + data[4]*M.data[1] + data[7]*M.data[2];
|
||||
Product.data[4] = data[1]*M.data[3] + data[4]*M.data[4] + data[7]*M.data[5];
|
||||
Product.data[7] = data[1]*M.data[6] + data[4]*M.data[7] + data[7]*M.data[8];
|
||||
Product.data[2] = data[2]*M.data[0] + data[5]*M.data[1] + data[8]*M.data[2];
|
||||
Product.data[5] = data[2]*M.data[3] + data[5]*M.data[4] + data[8]*M.data[5];
|
||||
Product.data[8] = data[2]*M.data[6] + data[5]*M.data[7] + data[8]*M.data[8];
|
||||
|
||||
return Product;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33& FGMatrix33::operator*=(const FGMatrix33& M)
|
||||
{
|
||||
// FIXME: Make compiler friendlier
|
||||
double a,b,c;
|
||||
|
||||
a = data[0]; b=data[3]; c=data[6];
|
||||
data[0] = a*M.data[0] + b*M.data[1] + c*M.data[2];
|
||||
data[3] = a*M.data[3] + b*M.data[4] + c*M.data[5];
|
||||
data[6] = a*M.data[6] + b*M.data[7] + c*M.data[8];
|
||||
|
||||
a = data[1]; b=data[4]; c=data[7];
|
||||
data[1] = a*M.data[0] + b*M.data[1] + c*M.data[2];
|
||||
data[4] = a*M.data[3] + b*M.data[4] + c*M.data[5];
|
||||
data[7] = a*M.data[6] + b*M.data[7] + c*M.data[8];
|
||||
|
||||
a = data[2]; b=data[5]; c=data[8];
|
||||
data[2] = a*M.data[0] + b*M.data[1] + c*M.data[2];
|
||||
data[5] = a*M.data[3] + b*M.data[4] + c*M.data[5];
|
||||
data[8] = a*M.data[6] + b*M.data[7] + c*M.data[8];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33 FGMatrix33::operator/(const double scalar) const
|
||||
{
|
||||
FGMatrix33 Quot;
|
||||
|
||||
double tmp = 1.0/scalar;
|
||||
Quot.data[0] = data[0] * tmp;
|
||||
Quot.data[3] = data[3] * tmp;
|
||||
Quot.data[6] = data[6] * tmp;
|
||||
Quot.data[1] = data[1] * tmp;
|
||||
Quot.data[4] = data[4] * tmp;
|
||||
Quot.data[7] = data[7] * tmp;
|
||||
Quot.data[2] = data[2] * tmp;
|
||||
Quot.data[5] = data[5] * tmp;
|
||||
Quot.data[8] = data[8] * tmp;
|
||||
|
||||
return Quot;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGMatrix33& FGMatrix33::operator/=(const double scalar)
|
||||
{
|
||||
double tmp = 1.0/scalar;
|
||||
data[0] *= tmp;
|
||||
data[3] *= tmp;
|
||||
data[6] *= tmp;
|
||||
data[1] *= tmp;
|
||||
data[4] *= tmp;
|
||||
data[7] *= tmp;
|
||||
data[2] *= tmp;
|
||||
data[5] *= tmp;
|
||||
data[8] *= tmp;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGMatrix33::T(void)
|
||||
{
|
||||
double tmp;
|
||||
|
||||
tmp = data[3];
|
||||
data[3] = data[1];
|
||||
data[1] = tmp;
|
||||
|
||||
tmp = data[6];
|
||||
data[6] = data[2];
|
||||
data[2] = tmp;
|
||||
|
||||
tmp = data[7];
|
||||
data[7] = data[5];
|
||||
data[5] = tmp;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3 FGMatrix33::operator*(const FGColumnVector3& v) const
|
||||
{
|
||||
double v1 = v(1);
|
||||
double v2 = v(2);
|
||||
double v3 = v(3);
|
||||
|
||||
double tmp1 = v1*data[0]; //[(col-1)*eRows+row-1]
|
||||
double tmp2 = v1*data[1];
|
||||
double tmp3 = v1*data[2];
|
||||
|
||||
tmp1 += v2*data[3];
|
||||
tmp2 += v2*data[4];
|
||||
tmp3 += v2*data[5];
|
||||
|
||||
tmp1 += v3*data[6];
|
||||
tmp2 += v3*data[7];
|
||||
tmp3 += v3*data[8];
|
||||
|
||||
return FGColumnVector3( tmp1, tmp2, tmp3 );
|
||||
}
|
||||
|
||||
}
|
||||
468
src/FDM/JSBSim/math/FGMatrix33.h
Normal file
468
src/FDM/JSBSim/math/FGMatrix33.h
Normal file
@@ -0,0 +1,468 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGMatrix33.h
|
||||
Author: Tony Peden, Jon Berndt, Mathias Frolich
|
||||
Date started: Unknown
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
??/??/?? TP Created
|
||||
03/16/2000 JSB Added exception throwing
|
||||
03/06/2004 MF Rework of the code to make it a bit compiler friendlier
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGMATRIX33_H
|
||||
#define FGMATRIX33_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGColumnVector3.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGQuaternion;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Handles matrix math operations.
|
||||
@author Tony Peden, Jon Berndt, Mathias Froelich
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGMatrix33
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGMatrix33
|
||||
{
|
||||
public:
|
||||
|
||||
enum {
|
||||
eRows = 3,
|
||||
eColumns = 3
|
||||
};
|
||||
|
||||
/** Default initializer.
|
||||
|
||||
Create a zero matrix.
|
||||
*/
|
||||
FGMatrix33(void);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param M Matrix which is used for initialization.
|
||||
|
||||
Create copy of the matrix given in the argument.
|
||||
*/
|
||||
FGMatrix33(const FGMatrix33& M)
|
||||
{
|
||||
data[0] = M.data[0];
|
||||
data[1] = M.data[1];
|
||||
data[2] = M.data[2];
|
||||
data[3] = M.data[3];
|
||||
data[4] = M.data[4];
|
||||
data[5] = M.data[5];
|
||||
data[6] = M.data[6];
|
||||
data[7] = M.data[7];
|
||||
data[8] = M.data[8];
|
||||
}
|
||||
|
||||
/** Initialization by given values.
|
||||
|
||||
@param m11 value of the 1,1 Matrix element.
|
||||
@param m12 value of the 1,2 Matrix element.
|
||||
@param m13 value of the 1,3 Matrix element.
|
||||
@param m21 value of the 2,1 Matrix element.
|
||||
@param m22 value of the 2,2 Matrix element.
|
||||
@param m23 value of the 2,3 Matrix element.
|
||||
@param m31 value of the 3,1 Matrix element.
|
||||
@param m32 value of the 3,2 Matrix element.
|
||||
@param m33 value of the 3,3 Matrix element.
|
||||
|
||||
Create a matrix from the doubles given in the arguments.
|
||||
*/
|
||||
FGMatrix33(const double m11, const double m12, const double m13,
|
||||
const double m21, const double m22, const double m23,
|
||||
const double m31, const double m32, const double m33)
|
||||
{
|
||||
data[0] = m11;
|
||||
data[1] = m21;
|
||||
data[2] = m31;
|
||||
data[3] = m12;
|
||||
data[4] = m22;
|
||||
data[5] = m32;
|
||||
data[6] = m13;
|
||||
data[7] = m23;
|
||||
data[8] = m33;
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
*/
|
||||
~FGMatrix33(void) {}
|
||||
|
||||
/** Prints the contents of the matrix.
|
||||
@param delimeter the item separator (tab or comma)
|
||||
@return a string with the delimeter-separated contents of the matrix */
|
||||
std::string Dump(const std::string& delimeter) const;
|
||||
|
||||
/** Prints the contents of the matrix.
|
||||
@param delimeter the item separator (tab or comma, etc.)
|
||||
@param prefix an additional prefix that is used to indent the 3X3 matrix
|
||||
printout
|
||||
@return a string with the delimeter-separated contents of the matrix */
|
||||
std::string Dump(const std::string& delimiter, const std::string& prefix) const;
|
||||
|
||||
/** Read access the entries of the matrix.
|
||||
@param row Row index.
|
||||
@param col Column index.
|
||||
|
||||
@return the value of the matrix entry at the given row and
|
||||
column indices. Indices are counted starting with 1.
|
||||
*/
|
||||
double operator()(unsigned int row, unsigned int col) const {
|
||||
return data[(col-1)*eRows+row-1];
|
||||
}
|
||||
|
||||
/** Write access the entries of the matrix.
|
||||
Note that the indices given in the arguments are unchecked.
|
||||
|
||||
@param row Row index.
|
||||
@param col Column index.
|
||||
|
||||
@return a reference to the matrix entry at the given row and
|
||||
column indices. Indices are counted starting with 1.
|
||||
*/
|
||||
double& operator()(unsigned int row, unsigned int col) {
|
||||
return data[(col-1)*eRows+row-1];
|
||||
}
|
||||
|
||||
/** Read access the entries of the matrix.
|
||||
This function is just a shortcut for the <tt>double&
|
||||
operator()(unsigned int row, unsigned int col)</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
|
||||
Note that the indices given in the arguments are unchecked.
|
||||
|
||||
@param row Row index.
|
||||
@param col Column index.
|
||||
|
||||
@return the value of the matrix entry at the given row and
|
||||
column indices. Indices are counted starting with 1.
|
||||
*/
|
||||
double Entry(unsigned int row, unsigned int col) const {
|
||||
return data[(col-1)*eRows+row-1];
|
||||
}
|
||||
|
||||
/** Write access the entries of the matrix.
|
||||
This function is just a shortcut for the <tt>double&
|
||||
operator()(unsigned int row, unsigned int col)</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
|
||||
Note that the indices given in the arguments are unchecked.
|
||||
|
||||
@param row Row index.
|
||||
@param col Column index.
|
||||
|
||||
@return a reference to the matrix entry at the given row and
|
||||
column indices. Indices are counted starting with 1.
|
||||
*/
|
||||
double& Entry(unsigned int row, unsigned int col) {
|
||||
return data[(col-1)*eRows+row-1];
|
||||
}
|
||||
|
||||
/** Number of rows in the matrix.
|
||||
@return the number of rows in the matrix.
|
||||
*/
|
||||
unsigned int Rows(void) const { return eRows; }
|
||||
|
||||
/** Number of cloumns in the matrix.
|
||||
@return the number of columns in the matrix.
|
||||
*/
|
||||
unsigned int Cols(void) const { return eColumns; }
|
||||
|
||||
/** Transposed matrix.
|
||||
This function only returns the transpose of this matrix. This matrix
|
||||
itself remains unchanged.
|
||||
@return the transposed matrix.
|
||||
*/
|
||||
FGMatrix33 Transposed(void) const {
|
||||
return FGMatrix33( data[0], data[1], data[2],
|
||||
data[3], data[4], data[5],
|
||||
data[6], data[7], data[8] );
|
||||
}
|
||||
|
||||
/** Transposes this matrix.
|
||||
This function only transposes this matrix. Nothing is returned.
|
||||
*/
|
||||
void T(void);
|
||||
|
||||
/** Initialize the matrix.
|
||||
This function initializes a matrix to all 0.0.
|
||||
*/
|
||||
void InitMatrix(void);
|
||||
|
||||
/** Initialize the matrix.
|
||||
This function initializes a matrix to user specified values.
|
||||
*/
|
||||
void InitMatrix(const double m11, const double m12, const double m13,
|
||||
const double m21, const double m22, const double m23,
|
||||
const double m31, const double m32, const double m33)
|
||||
{
|
||||
data[0] = m11;
|
||||
data[1] = m21;
|
||||
data[2] = m31;
|
||||
data[3] = m12;
|
||||
data[4] = m22;
|
||||
data[5] = m32;
|
||||
data[6] = m13;
|
||||
data[7] = m23;
|
||||
data[8] = m33;
|
||||
}
|
||||
|
||||
/** Returns the quaternion associated with this direction cosine (rotation) matrix.
|
||||
*/
|
||||
FGQuaternion GetQuaternion(void) const;
|
||||
|
||||
/** Returns the Euler angle column vector associated with this matrix.
|
||||
*/
|
||||
FGColumnVector3 GetEuler() const;
|
||||
|
||||
/** Determinant of the matrix.
|
||||
@return the determinant of the matrix.
|
||||
*/
|
||||
double Determinant(void) const;
|
||||
|
||||
/** Return if the matrix is invertible.
|
||||
Checks and returns if the matrix is nonsingular and thus
|
||||
invertible. This is done by simply computing the determinant and
|
||||
check if it is zero. Note that this test does not cover any
|
||||
instabilities caused by nearly singular matirces using finite
|
||||
arithmetics. It only checks exact singularity.
|
||||
*/
|
||||
bool Invertible(void) const { return 0.0 != Determinant(); }
|
||||
|
||||
/** Return the inverse of the matrix.
|
||||
Computes and returns if the inverse of the matrix. It is computed
|
||||
by Cramers Rule. Also there are no checks performed if the matrix
|
||||
is invertible. If you are not sure that it really is check this
|
||||
with the @ref Invertible() call before.
|
||||
*/
|
||||
FGMatrix33 Inverse(void) const;
|
||||
|
||||
/** Assignment operator.
|
||||
|
||||
@param A source matrix.
|
||||
|
||||
Copy the content of the matrix given in the argument into *this.
|
||||
*/
|
||||
FGMatrix33& operator=(const FGMatrix33& A)
|
||||
{
|
||||
data[0] = A.data[0];
|
||||
data[1] = A.data[1];
|
||||
data[2] = A.data[2];
|
||||
data[3] = A.data[3];
|
||||
data[4] = A.data[4];
|
||||
data[5] = A.data[5];
|
||||
data[6] = A.data[6];
|
||||
data[7] = A.data[7];
|
||||
data[8] = A.data[8];
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Assignment operator.
|
||||
|
||||
@param lv initializer list of at most 9 values.
|
||||
|
||||
Copy the content of the list into *this. */
|
||||
FGMatrix33& operator=(std::initializer_list<double> lv)
|
||||
{
|
||||
double *v = data;
|
||||
for(auto& x: lv) {
|
||||
*v = x;
|
||||
v += 3;
|
||||
if (v-data > 8)
|
||||
v -= 8;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Matrix vector multiplication.
|
||||
|
||||
@param v vector to multiply with.
|
||||
@return matric vector product.
|
||||
|
||||
Compute and return the product of the current matrix with the
|
||||
vector given in the argument.
|
||||
*/
|
||||
FGColumnVector3 operator*(const FGColumnVector3& v) const;
|
||||
|
||||
/** Matrix subtraction.
|
||||
|
||||
@param B matrix to add to.
|
||||
@return difference of the matrices.
|
||||
|
||||
Compute and return the sum of the current matrix and the matrix
|
||||
B given in the argument.
|
||||
*/
|
||||
FGMatrix33 operator-(const FGMatrix33& B) const;
|
||||
|
||||
/** Matrix addition.
|
||||
|
||||
@param B matrix to add to.
|
||||
@return sum of the matrices.
|
||||
|
||||
Compute and return the sum of the current matrix and the matrix
|
||||
B given in the argument.
|
||||
*/
|
||||
FGMatrix33 operator+(const FGMatrix33& B) const;
|
||||
|
||||
/** Matrix product.
|
||||
|
||||
@param B matrix to add to.
|
||||
@return product of the matrices.
|
||||
|
||||
Compute and return the product of the current matrix and the matrix
|
||||
B given in the argument.
|
||||
*/
|
||||
FGMatrix33 operator*(const FGMatrix33& B) const;
|
||||
|
||||
/** Multiply the matrix with a scalar.
|
||||
|
||||
@param scalar scalar factor to multiply with.
|
||||
@return scaled matrix.
|
||||
|
||||
Compute and return the product of the current matrix with the
|
||||
scalar value scalar given in the argument.
|
||||
*/
|
||||
FGMatrix33 operator*(const double scalar) const;
|
||||
|
||||
/** Multiply the matrix with 1.0/scalar.
|
||||
|
||||
@param scalar scalar factor to divide through.
|
||||
@return scaled matrix.
|
||||
|
||||
Compute and return the product of the current matrix with the
|
||||
scalar value 1.0/scalar, where scalar is given in the argument.
|
||||
*/
|
||||
FGMatrix33 operator/(const double scalar) const;
|
||||
|
||||
/** In place matrix subtraction.
|
||||
|
||||
@param B matrix to subtract.
|
||||
@return reference to the current matrix.
|
||||
|
||||
Compute the diffence from the current matrix and the matrix B
|
||||
given in the argument.
|
||||
*/
|
||||
FGMatrix33& operator-=(const FGMatrix33 &B);
|
||||
|
||||
/** In place matrix addition.
|
||||
|
||||
@param B matrix to add.
|
||||
@return reference to the current matrix.
|
||||
|
||||
Compute the sum of the current matrix and the matrix B
|
||||
given in the argument.
|
||||
*/
|
||||
FGMatrix33& operator+=(const FGMatrix33 &B);
|
||||
|
||||
/** In place matrix multiplication.
|
||||
|
||||
@param B matrix to multiply with.
|
||||
@return reference to the current matrix.
|
||||
|
||||
Compute the product of the current matrix and the matrix B
|
||||
given in the argument.
|
||||
*/
|
||||
FGMatrix33& operator*=(const FGMatrix33 &B);
|
||||
|
||||
/** In place matrix scale.
|
||||
|
||||
@param scalar scalar value to multiply with.
|
||||
@return reference to the current matrix.
|
||||
|
||||
Compute the product of the current matrix and the scalar value scalar
|
||||
given in the argument.
|
||||
*/
|
||||
FGMatrix33& operator*=(const double scalar);
|
||||
|
||||
/** In place matrix scale.
|
||||
|
||||
@param scalar scalar value to divide through.
|
||||
@return reference to the current matrix.
|
||||
|
||||
Compute the product of the current matrix and the scalar value
|
||||
1.0/scalar, where scalar is given in the argument.
|
||||
*/
|
||||
FGMatrix33& operator/=(const double scalar);
|
||||
|
||||
private:
|
||||
double data[eRows*eColumns];
|
||||
};
|
||||
|
||||
/** Scalar multiplication.
|
||||
|
||||
@param scalar scalar value to multiply with.
|
||||
@param A Matrix to multiply.
|
||||
|
||||
Multiply the Matrix with a scalar value.
|
||||
*/
|
||||
inline FGMatrix33 operator*(double scalar, const FGMatrix33& A) {
|
||||
// use already defined operation.
|
||||
return A*scalar;
|
||||
}
|
||||
|
||||
/** Write matrix to a stream.
|
||||
|
||||
@param os Stream to write to.
|
||||
@param M Matrix to write.
|
||||
|
||||
Write the matrix to a stream.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, const FGMatrix33& M);
|
||||
|
||||
/** Read matrix from a stream.
|
||||
|
||||
@param os Stream to read from.
|
||||
@param M Matrix to initialize with the values from the stream.
|
||||
|
||||
Read matrix from a stream.
|
||||
*/
|
||||
std::istream& operator>>(std::istream& is, FGMatrix33& M);
|
||||
|
||||
} // namespace JSBSim
|
||||
#endif
|
||||
195
src/FDM/JSBSim/math/FGModelFunctions.cpp
Normal file
195
src/FDM/JSBSim/math/FGModelFunctions.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGModelFunctions.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: August 2010
|
||||
|
||||
------- Copyright (C) 2010 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGModelFunctions.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGModelFunctions::~FGModelFunctions()
|
||||
{
|
||||
for (auto prefunc: PreFunctions) delete prefunc;
|
||||
for (auto postfunc: PostFunctions) delete postfunc;
|
||||
|
||||
if (debug_lvl & 2) cout << "Destroyed: FGModelFunctions" << endl;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGModelFunctions::InitModel(void)
|
||||
{
|
||||
LocalProperties.ResetToIC();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGModelFunctions::Load(Element* el, FGFDMExec* fdmex, string prefix)
|
||||
{
|
||||
LocalProperties.Load(el, fdmex->GetPropertyManager(), false);
|
||||
PreLoad(el, fdmex, prefix);
|
||||
|
||||
return true; // TODO: Need to make this value mean something.
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGModelFunctions::PreLoad(Element* el, FGFDMExec* fdmex, string prefix)
|
||||
{
|
||||
// Load model post-functions, if any
|
||||
|
||||
Element *function = el->FindElement("function");
|
||||
|
||||
while (function) {
|
||||
string fType = function->GetAttributeValue("type");
|
||||
if (fType.empty() || fType == "pre")
|
||||
PreFunctions.push_back(new FGFunction(fdmex, function, prefix));
|
||||
else if (fType == "template") {
|
||||
string name = function->GetAttributeValue("name");
|
||||
fdmex->AddTemplateFunc(name, function);
|
||||
}
|
||||
|
||||
function = el->FindNextElement("function");
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGModelFunctions::PostLoad(Element* el, FGFDMExec* fdmex, string prefix)
|
||||
{
|
||||
// Load model post-functions, if any
|
||||
|
||||
Element *function = el->FindElement("function");
|
||||
while (function) {
|
||||
if (function->GetAttributeValue("type") == "post") {
|
||||
PostFunctions.push_back(new FGFunction(fdmex, function, prefix));
|
||||
}
|
||||
function = el->FindNextElement("function");
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Tell the Functions to cache values, so when the function values
|
||||
// are being used in the model, the functions do not get
|
||||
// calculated each time, but instead use the values that have already
|
||||
// been calculated for this frame.
|
||||
|
||||
void FGModelFunctions::RunPreFunctions(void)
|
||||
{
|
||||
for (auto prefunc: PreFunctions)
|
||||
prefunc->cacheValue(true);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Tell the Functions to cache values, so when the function values
|
||||
// are being used in the model, the functions do not get
|
||||
// calculated each time, but instead use the values that have already
|
||||
// been calculated for this frame.
|
||||
|
||||
void FGModelFunctions::RunPostFunctions(void)
|
||||
{
|
||||
for (auto postfunc: PostFunctions)
|
||||
postfunc->cacheValue(true);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGFunction* FGModelFunctions::GetPreFunction(const std::string& name)
|
||||
{
|
||||
for (auto prefunc: PreFunctions) {
|
||||
if (prefunc->GetName() == name)
|
||||
return prefunc;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGModelFunctions::GetFunctionStrings(const string& delimeter) const
|
||||
{
|
||||
string FunctionStrings;
|
||||
|
||||
for (auto prefunc: PreFunctions) {
|
||||
if (!FunctionStrings.empty())
|
||||
FunctionStrings += delimeter;
|
||||
|
||||
FunctionStrings += prefunc->GetName();
|
||||
}
|
||||
|
||||
for (auto postfunc: PostFunctions) {
|
||||
if (!FunctionStrings.empty())
|
||||
FunctionStrings += delimeter;
|
||||
|
||||
FunctionStrings += postfunc->GetName();
|
||||
}
|
||||
|
||||
return FunctionStrings;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGModelFunctions::GetFunctionValues(const string& delimeter) const
|
||||
{
|
||||
ostringstream buf;
|
||||
|
||||
for (auto prefunc: PreFunctions) {
|
||||
if (buf.tellp() > 0) buf << delimeter;
|
||||
buf << prefunc->GetValue();
|
||||
}
|
||||
|
||||
for (auto postfunc: PostFunctions) {
|
||||
if (buf.tellp() > 0) buf << delimeter;
|
||||
buf << postfunc->GetValue();
|
||||
}
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
}
|
||||
109
src/FDM/JSBSim/math/FGModelFunctions.h
Normal file
109
src/FDM/JSBSim/math/FGModelFunctions.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGModelFunctions.h
|
||||
Author: Jon Berndt
|
||||
Date started: August 2010
|
||||
|
||||
------------- Copyright (C) 2010 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGMODELFUNCTIONS_H
|
||||
#define FGMODELFUNCTIONS_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "input_output/FGPropertyReader.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGFunction;
|
||||
class Element;
|
||||
class FGPropertyManager;
|
||||
class FGFDMExec;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** The model functions class provides the capability for loading, storing, and
|
||||
executing arbitrary functions.
|
||||
For certain classes, such as the engine, aerodynamics, ground reactions,
|
||||
mass balance, etc., it can be useful to incorporate special functions that
|
||||
can operate on the local model parameters before and/or after the model
|
||||
executes. For example, there is no inherent chamber pressure calculation
|
||||
done in the rocket engine model. However, an arbitrary function can be added
|
||||
to a specific rocket engine XML configuration file. It would be tagged with
|
||||
a "pre" or "post" type attribute to denote whether the function is to be
|
||||
executed before or after the standard model algorithm.
|
||||
@author Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGModelFunctions
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGModelFunctions : public FGJSBBase
|
||||
{
|
||||
public:
|
||||
virtual ~FGModelFunctions();
|
||||
void RunPreFunctions(void);
|
||||
void RunPostFunctions(void);
|
||||
bool Load(Element* el, FGFDMExec* fdmex, std::string prefix="");
|
||||
void PreLoad(Element* el, FGFDMExec* fdmex, std::string prefix="");
|
||||
void PostLoad(Element* el, FGFDMExec* fdmex, std::string prefix="");
|
||||
|
||||
/** Gets the strings for the current set of functions.
|
||||
@param delimeter either a tab or comma string depending on output type
|
||||
@return a string containing the descriptive names for all functions */
|
||||
std::string GetFunctionStrings(const std::string& delimeter) const;
|
||||
|
||||
/** Gets the function values.
|
||||
@param delimeter either a tab or comma string depending on output type
|
||||
@return a string containing the numeric values for the current set of
|
||||
functions */
|
||||
std::string GetFunctionValues(const std::string& delimeter) const;
|
||||
|
||||
/** Get one of the "pre" function
|
||||
@param name the name of the requested function.
|
||||
@return a pointer to the function (NULL if not found)
|
||||
*/
|
||||
FGFunction* GetPreFunction(const std::string& name);
|
||||
|
||||
protected:
|
||||
std::vector <FGFunction*> PreFunctions;
|
||||
std::vector <FGFunction*> PostFunctions;
|
||||
FGPropertyReader LocalProperties;
|
||||
|
||||
virtual bool InitModel(void);
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
82
src/FDM/JSBSim/math/FGParameter.h
Normal file
82
src/FDM/JSBSim/math/FGParameter.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGParameter.h
|
||||
Author: Jon Berndt
|
||||
Date started: August 25 2004
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGPARAMETER_H
|
||||
#define FGPARAMETER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include "simgear/structure/SGSharedPtr.hxx"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents various types of parameters.
|
||||
@author Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGParameter
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGParameter : public SGReferenced
|
||||
{
|
||||
public:
|
||||
virtual ~FGParameter(void) {};
|
||||
virtual double GetValue(void) const = 0;
|
||||
virtual std::string GetName(void) const = 0;
|
||||
virtual bool IsConstant(void) const { return false; }
|
||||
|
||||
// SGPropertyNode impersonation.
|
||||
double getDoubleValue(void) const { return GetValue(); }
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<FGParameter> FGParameter_ptr;
|
||||
|
||||
inline double operator*(double v, const FGParameter_ptr& p) {
|
||||
return v*p->GetValue();
|
||||
}
|
||||
|
||||
inline double operator*(const FGParameter_ptr& p, double v) {
|
||||
return p->GetValue()*v;
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
116
src/FDM/JSBSim/math/FGParameterValue.h
Normal file
116
src/FDM/JSBSim/math/FGParameterValue.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGParameterValue.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: December 09 2018
|
||||
|
||||
--------- Copyright (C) 2018 B. Coconnier (bcoconni@users.sf.net) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGPARAMETERVALUE_H
|
||||
#define FGPARAMETERVALUE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "math/FGRealValue.h"
|
||||
#include "math/FGPropertyValue.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGPropertyManager;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents a either a real value or a property value
|
||||
@author Bertrand Coconnier
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGParameterValue
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGParameterValue : public FGParameter
|
||||
{
|
||||
public:
|
||||
FGParameterValue(Element* el, FGPropertyManager* pm) {
|
||||
string value = el->GetDataLine();
|
||||
|
||||
if (el->GetNumDataLines() != 1 || value.empty()) {
|
||||
cerr << el->ReadFrom()
|
||||
<< "The element <" << el->GetName()
|
||||
<< "> must either contain a value number or a property name."
|
||||
<< endl;
|
||||
throw invalid_argument("FGParameterValue: Illegal argument defining: " + el->GetName());
|
||||
}
|
||||
|
||||
Construct(value, pm);
|
||||
}
|
||||
|
||||
FGParameterValue(const std::string& value, FGPropertyManager* pm) {
|
||||
Construct(value, pm);
|
||||
}
|
||||
|
||||
double GetValue(void) const override { return param->GetValue(); }
|
||||
bool IsConstant(void) const override { return param->IsConstant(); }
|
||||
|
||||
std::string GetName(void) const override {
|
||||
FGPropertyValue* v = dynamic_cast<FGPropertyValue*>(param.ptr());
|
||||
if (v)
|
||||
return v->GetNameWithSign();
|
||||
else
|
||||
return to_string(param->GetValue());
|
||||
}
|
||||
|
||||
bool IsLateBound(void) const {
|
||||
FGPropertyValue* v = dynamic_cast<FGPropertyValue*>(param.ptr());
|
||||
return v != nullptr && v->IsLateBound();
|
||||
}
|
||||
private:
|
||||
FGParameter_ptr param;
|
||||
|
||||
void Construct(const std::string& value, FGPropertyManager* pm) {
|
||||
if (is_number(value)) {
|
||||
param = new FGRealValue(atof(value.c_str()));
|
||||
} else {
|
||||
// "value" must be a property if execution passes to here.
|
||||
param = new FGPropertyValue(value, pm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<FGParameterValue> FGParameterValue_ptr;
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
133
src/FDM/JSBSim/math/FGPropertyValue.cpp
Normal file
133
src/FDM/JSBSim/math/FGPropertyValue.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGPropertyValue.cpp
|
||||
Author: Jon Berndt
|
||||
Date started: 12/10/2004
|
||||
Purpose: Stores property values
|
||||
|
||||
------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) -------------
|
||||
------ Copyright (C) 2010 - 2011 Anders Gidenstam (anders(at)gidenstam.org) -
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "FGPropertyValue.h"
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGPropertyValue::FGPropertyValue(const std::string& propName,
|
||||
FGPropertyManager* propertyManager)
|
||||
: PropertyManager(propertyManager), PropertyNode(nullptr),
|
||||
PropertyName(propName), Sign(1.0)
|
||||
{
|
||||
if (PropertyName[0] == '-') {
|
||||
PropertyName.erase(0,1);
|
||||
Sign = -1.0;
|
||||
}
|
||||
|
||||
if (PropertyManager->HasNode(PropertyName))
|
||||
PropertyNode = PropertyManager->GetNode(PropertyName);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGPropertyNode* FGPropertyValue::GetNode(void) const
|
||||
{
|
||||
if (!PropertyNode) {
|
||||
FGPropertyNode* node = PropertyManager->GetNode(PropertyName);
|
||||
|
||||
if (!node)
|
||||
throw(std::string("FGPropertyValue::GetValue() The property " +
|
||||
PropertyName + " does not exist."));
|
||||
|
||||
PropertyNode = node;
|
||||
}
|
||||
|
||||
return PropertyNode;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGPropertyValue::GetValue(void) const
|
||||
{
|
||||
return GetNode()->getDoubleValue()*Sign;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGPropertyValue::SetValue(double value)
|
||||
{
|
||||
// SetValue() ignores the Sign flag. So make sure it is never called with a
|
||||
// negative sign.
|
||||
assert(Sign == 1);
|
||||
GetNode()->setDoubleValue(value);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::string FGPropertyValue::GetName(void) const
|
||||
{
|
||||
if (PropertyNode)
|
||||
return PropertyNode->GetName();
|
||||
else
|
||||
return PropertyName;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::string FGPropertyValue::GetNameWithSign(void) const
|
||||
{
|
||||
string name;
|
||||
|
||||
if (Sign < 0.0) name ="-";
|
||||
|
||||
name += GetName();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::string FGPropertyValue::GetFullyQualifiedName(void) const
|
||||
{
|
||||
if (PropertyNode)
|
||||
return PropertyNode->GetFullyQualifiedName();
|
||||
else
|
||||
return PropertyName;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::string FGPropertyValue::GetPrintableName(void) const
|
||||
{
|
||||
if (PropertyNode)
|
||||
return PropertyNode->GetPrintableName();
|
||||
else
|
||||
return PropertyName;
|
||||
}
|
||||
|
||||
}
|
||||
96
src/FDM/JSBSim/math/FGPropertyValue.h
Normal file
96
src/FDM/JSBSim/math/FGPropertyValue.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGPropertyValue.h
|
||||
Author: Jon Berndt
|
||||
Date started: December 10 2004
|
||||
|
||||
------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) -------------
|
||||
------ Copyright (C) 2010 - 2011 Anders Gidenstam (anders(at)gidenstam.org) -
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGPROPERTYVALUE_H
|
||||
#define FGPROPERTYVALUE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGParameter.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents a property value which can use late binding.
|
||||
@author Jon Berndt, Anders Gidenstam
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGPropertyValue
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGPropertyValue : public FGParameter
|
||||
{
|
||||
public:
|
||||
|
||||
explicit FGPropertyValue(FGPropertyNode* propNode)
|
||||
: PropertyManager(nullptr), PropertyNode(propNode), Sign(1.0) {}
|
||||
FGPropertyValue(const std::string& propName,
|
||||
FGPropertyManager* propertyManager);
|
||||
|
||||
double GetValue(void) const override;
|
||||
bool IsConstant(void) const override {
|
||||
return PropertyNode && (!PropertyNode->isTied()
|
||||
&& !PropertyNode->getAttribute(SGPropertyNode::WRITE));
|
||||
}
|
||||
void SetNode(FGPropertyNode* node) {PropertyNode = node;}
|
||||
void SetValue(double value);
|
||||
bool IsLateBound(void) const { return PropertyNode == nullptr; }
|
||||
|
||||
std::string GetName(void) const override;
|
||||
virtual std::string GetNameWithSign(void) const;
|
||||
virtual std::string GetFullyQualifiedName(void) const;
|
||||
virtual std::string GetPrintableName(void) const;
|
||||
|
||||
protected:
|
||||
FGPropertyNode* GetNode(void) const;
|
||||
|
||||
private:
|
||||
FGPropertyManager* PropertyManager; // Property root used to do late binding.
|
||||
mutable FGPropertyNode_ptr PropertyNode;
|
||||
std::string PropertyName;
|
||||
double Sign;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<FGPropertyValue> FGPropertyValue_ptr;
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
257
src/FDM/JSBSim/math/FGQuaternion.cpp
Normal file
257
src/FDM/JSBSim/math/FGQuaternion.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGQuaternion.cpp
|
||||
Author: Jon Berndt, Mathias Froehlich
|
||||
Date started: 12/02/98
|
||||
|
||||
------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
|
||||
------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
-------------------------------------------------------------------------------
|
||||
12/02/98 JSB Created
|
||||
15/01/04 Mathias Froehlich implemented a quaternion class from many places
|
||||
in JSBSim.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#include "FGMatrix33.h"
|
||||
#include "FGColumnVector3.h"
|
||||
|
||||
#include "FGQuaternion.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// Initialize from q
|
||||
FGQuaternion::FGQuaternion(const FGQuaternion& q) : mCacheValid(q.mCacheValid)
|
||||
{
|
||||
data[0] = q(1);
|
||||
data[1] = q(2);
|
||||
data[2] = q(3);
|
||||
data[3] = q(4);
|
||||
if (mCacheValid) {
|
||||
mT = q.mT;
|
||||
mTInv = q.mTInv;
|
||||
mEulerAngles = q.mEulerAngles;
|
||||
mEulerSines = q.mEulerSines;
|
||||
mEulerCosines = q.mEulerCosines;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// Initialize with the three euler angles
|
||||
FGQuaternion::FGQuaternion(double phi, double tht, double psi): mCacheValid(false)
|
||||
{
|
||||
InitializeFromEulerAngles(phi, tht, psi);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGQuaternion::FGQuaternion(FGColumnVector3 vOrient): mCacheValid(false)
|
||||
{
|
||||
double phi = vOrient(ePhi);
|
||||
double tht = vOrient(eTht);
|
||||
double psi = vOrient(ePsi);
|
||||
|
||||
InitializeFromEulerAngles(phi, tht, psi);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
//
|
||||
// This function computes the quaternion that describes the relationship of the
|
||||
// body frame with respect to another frame, such as the ECI or ECEF frames. The
|
||||
// Euler angles used represent a 3-2-1 (psi, theta, phi) rotation sequence from
|
||||
// the reference frame to the body frame. See "Quaternions and Rotation
|
||||
// Sequences", Jack B. Kuipers, sections 9.2 and 7.6.
|
||||
|
||||
void FGQuaternion::InitializeFromEulerAngles(double phi, double tht, double psi)
|
||||
{
|
||||
mEulerAngles(ePhi) = phi;
|
||||
mEulerAngles(eTht) = tht;
|
||||
mEulerAngles(ePsi) = psi;
|
||||
|
||||
double thtd2 = 0.5*tht;
|
||||
double psid2 = 0.5*psi;
|
||||
double phid2 = 0.5*phi;
|
||||
|
||||
double Sthtd2 = sin(thtd2);
|
||||
double Spsid2 = sin(psid2);
|
||||
double Sphid2 = sin(phid2);
|
||||
|
||||
double Cthtd2 = cos(thtd2);
|
||||
double Cpsid2 = cos(psid2);
|
||||
double Cphid2 = cos(phid2);
|
||||
|
||||
double Cphid2Cthtd2 = Cphid2*Cthtd2;
|
||||
double Cphid2Sthtd2 = Cphid2*Sthtd2;
|
||||
double Sphid2Sthtd2 = Sphid2*Sthtd2;
|
||||
double Sphid2Cthtd2 = Sphid2*Cthtd2;
|
||||
|
||||
data[0] = Cphid2Cthtd2*Cpsid2 + Sphid2Sthtd2*Spsid2;
|
||||
data[1] = Sphid2Cthtd2*Cpsid2 - Cphid2Sthtd2*Spsid2;
|
||||
data[2] = Cphid2Sthtd2*Cpsid2 + Sphid2Cthtd2*Spsid2;
|
||||
data[3] = Cphid2Cthtd2*Spsid2 - Sphid2Sthtd2*Cpsid2;
|
||||
|
||||
Normalize();
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Initialize with a direction cosine (rotation) matrix
|
||||
|
||||
FGQuaternion::FGQuaternion(const FGMatrix33& m) : mCacheValid(false)
|
||||
{
|
||||
data[0] = 0.50*sqrt(1.0 + m(1,1) + m(2,2) + m(3,3));
|
||||
double t = 0.25/data[0];
|
||||
data[1] = t*(m(2,3) - m(3,2));
|
||||
data[2] = t*(m(3,1) - m(1,3));
|
||||
data[3] = t*(m(1,2) - m(2,1));
|
||||
|
||||
Normalize();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
/** Returns the derivative of the quaternion corresponding to the
|
||||
angular velocities PQR.
|
||||
See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
|
||||
Equation 1.3-36.
|
||||
Also see Jack Kuipers, "Quaternions and Rotation Sequences", Equation 11.12.
|
||||
*/
|
||||
FGQuaternion FGQuaternion::GetQDot(const FGColumnVector3& PQR) const
|
||||
{
|
||||
return FGQuaternion(
|
||||
-0.5*( data[1]*PQR(eP) + data[2]*PQR(eQ) + data[3]*PQR(eR)),
|
||||
0.5*( data[0]*PQR(eP) - data[3]*PQR(eQ) + data[2]*PQR(eR)),
|
||||
0.5*( data[3]*PQR(eP) + data[0]*PQR(eQ) - data[1]*PQR(eR)),
|
||||
0.5*(-data[2]*PQR(eP) + data[1]*PQR(eQ) + data[0]*PQR(eR))
|
||||
);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGQuaternion::Normalize()
|
||||
{
|
||||
// Note: this does not touch the cache since it does not change the orientation
|
||||
double norm = Magnitude();
|
||||
if (norm == 0.0 || fabs(norm - 1.000) < 1e-10) return;
|
||||
|
||||
double rnorm = 1.0/norm;
|
||||
|
||||
data[0] *= rnorm;
|
||||
data[1] *= rnorm;
|
||||
data[2] *= rnorm;
|
||||
data[3] *= rnorm;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// Compute the derived values if required ...
|
||||
void FGQuaternion::ComputeDerivedUnconditional(void) const
|
||||
{
|
||||
mCacheValid = true;
|
||||
|
||||
double q0 = data[0]; // use some aliases/shorthand for the quat elements.
|
||||
double q1 = data[1];
|
||||
double q2 = data[2];
|
||||
double q3 = data[3];
|
||||
|
||||
// Now compute the transformation matrix.
|
||||
double q0q0 = q0*q0;
|
||||
double q1q1 = q1*q1;
|
||||
double q2q2 = q2*q2;
|
||||
double q3q3 = q3*q3;
|
||||
double q0q1 = q0*q1;
|
||||
double q0q2 = q0*q2;
|
||||
double q0q3 = q0*q3;
|
||||
double q1q2 = q1*q2;
|
||||
double q1q3 = q1*q3;
|
||||
double q2q3 = q2*q3;
|
||||
|
||||
mT(1,1) = q0q0 + q1q1 - q2q2 - q3q3; // This is found from Eqn. 1.3-32 in
|
||||
mT(1,2) = 2.0*(q1q2 + q0q3); // Stevens and Lewis
|
||||
mT(1,3) = 2.0*(q1q3 - q0q2);
|
||||
mT(2,1) = 2.0*(q1q2 - q0q3);
|
||||
mT(2,2) = q0q0 - q1q1 + q2q2 - q3q3;
|
||||
mT(2,3) = 2.0*(q2q3 + q0q1);
|
||||
mT(3,1) = 2.0*(q1q3 + q0q2);
|
||||
mT(3,2) = 2.0*(q2q3 - q0q1);
|
||||
mT(3,3) = q0q0 - q1q1 - q2q2 + q3q3;
|
||||
|
||||
// Since this is an orthogonal matrix, the inverse is simply the transpose.
|
||||
|
||||
mTInv = mT;
|
||||
mTInv.T();
|
||||
|
||||
// Compute the Euler-angles
|
||||
|
||||
mEulerAngles = mT.GetEuler();
|
||||
|
||||
// FIXME: may be one can compute those values easier ???
|
||||
mEulerSines(ePhi) = sin(mEulerAngles(ePhi));
|
||||
// mEulerSines(eTht) = sin(mEulerAngles(eTht));
|
||||
mEulerSines(eTht) = -mT(1,3);
|
||||
mEulerSines(ePsi) = sin(mEulerAngles(ePsi));
|
||||
mEulerCosines(ePhi) = cos(mEulerAngles(ePhi));
|
||||
mEulerCosines(eTht) = cos(mEulerAngles(eTht));
|
||||
mEulerCosines(ePsi) = cos(mEulerAngles(ePsi));
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::string FGQuaternion::Dump(const std::string& delimiter) const
|
||||
{
|
||||
std::ostringstream buffer;
|
||||
buffer << std::setprecision(16) << data[0] << delimiter;
|
||||
buffer << std::setprecision(16) << data[1] << delimiter;
|
||||
buffer << std::setprecision(16) << data[2] << delimiter;
|
||||
buffer << std::setprecision(16) << data[3];
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const FGQuaternion& q)
|
||||
{
|
||||
os << q(1) << " , " << q(2) << " , " << q(3) << " , " << q(4);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
568
src/FDM/JSBSim/math/FGQuaternion.h
Normal file
568
src/FDM/JSBSim/math/FGQuaternion.h
Normal file
@@ -0,0 +1,568 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGQuaternion.h
|
||||
Author: Jon Berndt, Mathis Froehlich
|
||||
Date started: 12/02/98
|
||||
|
||||
------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) ------------------
|
||||
------- (C) 2004 Mathias Froehlich (Mathias.Froehlich@web.de) ----
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
-------------------------------------------------------------------------------
|
||||
12/02/98 JSB Created
|
||||
15/01/04 MF Quaternion class from old FGColumnVector4
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGQUATERNION_H
|
||||
#define FGQUATERNION_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGColumnVector3.h"
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGMatrix33;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Models the Quaternion representation of rotations.
|
||||
FGQuaternion is a representation of an arbitrary rotation through a
|
||||
quaternion. It has vector properties. This class also contains access
|
||||
functions to the euler angle representation of rotations and access to
|
||||
transformation matrices for 3D vectors. Transformations and euler angles are
|
||||
therefore computed once they are requested for the first time. Then they are
|
||||
cached for later usage as long as the class is not accessed trough
|
||||
a nonconst member function.
|
||||
|
||||
Note: The order of rotations used in this class corresponds to a 3-2-1 sequence,
|
||||
or Y-P-R, or Z-Y-X, if you prefer.
|
||||
|
||||
@see Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
|
||||
Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
|
||||
School, January 1994
|
||||
@see D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
|
||||
JSC 12960, July 1977
|
||||
@see Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
|
||||
NASA-Ames", NASA CR-2497, January 1975
|
||||
@see Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
|
||||
Wiley & Sons, 1979 ISBN 0-471-03032-5
|
||||
@see Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
|
||||
1982 ISBN 0-471-08936-2
|
||||
@author Mathias Froehlich, extended FGColumnVector4 originally by Tony Peden
|
||||
and Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGQuaternion : public FGJSBBase {
|
||||
public:
|
||||
/** Default initializer.
|
||||
Default initializer, initializes the class with the identity rotation. */
|
||||
FGQuaternion() : mCacheValid(false) {
|
||||
data[0] = 1.0;
|
||||
data[1] = data[2] = data[3] = 0.0;
|
||||
}
|
||||
|
||||
/** Copy constructor.
|
||||
Copy constructor, initializes the quaternion.
|
||||
@param q a constant reference to another FGQuaternion instance */
|
||||
FGQuaternion(const FGQuaternion& q);
|
||||
|
||||
/** Initializer by euler angles.
|
||||
Initialize the quaternion with the euler angles.
|
||||
@param phi The euler X axis (roll) angle in radians
|
||||
@param tht The euler Y axis (attitude) angle in radians
|
||||
@param psi The euler Z axis (heading) angle in radians */
|
||||
FGQuaternion(double phi, double tht, double psi);
|
||||
|
||||
/** Initializer by euler angle vector.
|
||||
Initialize the quaternion with the euler angle vector.
|
||||
@param vOrient The euler axis angle vector in radians (phi, tht, psi) */
|
||||
FGQuaternion(FGColumnVector3 vOrient);
|
||||
|
||||
/** Initializer by one euler angle.
|
||||
Initialize the quaternion with the single euler angle where its index
|
||||
is given in the first argument.
|
||||
@param idx Index of the euler angle to initialize
|
||||
@param angle The euler angle in radians */
|
||||
FGQuaternion(int idx, double angle)
|
||||
: mCacheValid(false) {
|
||||
|
||||
double angle2 = 0.5*angle;
|
||||
|
||||
double Sangle2 = sin(angle2);
|
||||
double Cangle2 = cos(angle2);
|
||||
|
||||
if (idx == ePhi) {
|
||||
data[0] = Cangle2;
|
||||
data[1] = Sangle2;
|
||||
data[2] = 0.0;
|
||||
data[3] = 0.0;
|
||||
|
||||
} else if (idx == eTht) {
|
||||
data[0] = Cangle2;
|
||||
data[1] = 0.0;
|
||||
data[2] = Sangle2;
|
||||
data[3] = 0.0;
|
||||
|
||||
} else {
|
||||
data[0] = Cangle2;
|
||||
data[1] = 0.0;
|
||||
data[2] = 0.0;
|
||||
data[3] = Sangle2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializer by a rotation axis and an angle.
|
||||
Initialize the quaternion to represent the rotation around a given
|
||||
angle and an arbitrary axis.
|
||||
@param angle The angle in radians
|
||||
@param axis The rotation axis
|
||||
*/
|
||||
FGQuaternion(double angle, const FGColumnVector3& axis)
|
||||
: mCacheValid(false) {
|
||||
|
||||
double angle2 = 0.5 * angle;
|
||||
|
||||
double length = axis.Magnitude();
|
||||
double Sangle2 = sin(angle2) / length;
|
||||
double Cangle2 = cos(angle2);
|
||||
|
||||
data[0] = Cangle2;
|
||||
data[1] = Sangle2 * axis(1);
|
||||
data[2] = Sangle2 * axis(2);
|
||||
data[3] = Sangle2 * axis(3);
|
||||
}
|
||||
|
||||
/** Initializer by matrix.
|
||||
Initialize the quaternion with the matrix representing a transform from one frame
|
||||
to another using the standard aerospace sequence, Yaw-Pitch-Roll (3-2-1).
|
||||
@param m the rotation matrix */
|
||||
FGQuaternion(const FGMatrix33& m);
|
||||
|
||||
/// Destructor.
|
||||
~FGQuaternion() {}
|
||||
|
||||
/** Quaternion derivative for given angular rates.
|
||||
Computes the quaternion derivative which results from the given
|
||||
angular velocities
|
||||
@param PQR a constant reference to a rotation rate vector
|
||||
@return the quaternion derivative
|
||||
@see Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
|
||||
Equation 1.3-36. */
|
||||
FGQuaternion GetQDot(const FGColumnVector3& PQR) const;
|
||||
|
||||
/** Transformation matrix.
|
||||
@return a reference to the transformation/rotation matrix
|
||||
corresponding to this quaternion rotation. */
|
||||
const FGMatrix33& GetT(void) const { ComputeDerived(); return mT; }
|
||||
|
||||
/** Backward transformation matrix.
|
||||
@return a reference to the inverse transformation/rotation matrix
|
||||
corresponding to this quaternion rotation. */
|
||||
const FGMatrix33& GetTInv(void) const { ComputeDerived(); return mTInv; }
|
||||
|
||||
/** Retrieves the Euler angles.
|
||||
@return a reference to the triad of Euler angles corresponding
|
||||
to this quaternion rotation.
|
||||
units radians */
|
||||
const FGColumnVector3& GetEuler(void) const {
|
||||
ComputeDerived();
|
||||
return mEulerAngles;
|
||||
}
|
||||
|
||||
/** Retrieves the Euler angles.
|
||||
@param i the Euler angle index.
|
||||
units radians.
|
||||
@return a reference to the i-th euler angles corresponding
|
||||
to this quaternion rotation.
|
||||
*/
|
||||
double GetEuler(int i) const {
|
||||
ComputeDerived();
|
||||
return mEulerAngles(i);
|
||||
}
|
||||
|
||||
/** Retrieves the Euler angles.
|
||||
@param i the Euler angle index.
|
||||
@return a reference to the i-th euler angles corresponding
|
||||
to this quaternion rotation.
|
||||
units degrees */
|
||||
double GetEulerDeg(int i) const {
|
||||
ComputeDerived();
|
||||
return radtodeg*mEulerAngles(i);
|
||||
}
|
||||
|
||||
/** Retrieves the Euler angle vector.
|
||||
@return an Euler angle column vector corresponding
|
||||
to this quaternion rotation.
|
||||
units degrees */
|
||||
FGColumnVector3 const GetEulerDeg(void) const {
|
||||
ComputeDerived();
|
||||
return radtodeg*mEulerAngles;
|
||||
}
|
||||
|
||||
/** Retrieves sine of the given euler angle.
|
||||
@return the sine of the Euler angle theta (pitch attitude) corresponding
|
||||
to this quaternion rotation. */
|
||||
double GetSinEuler(int i) const {
|
||||
ComputeDerived();
|
||||
return mEulerSines(i);
|
||||
}
|
||||
|
||||
/** Retrieves cosine of the given euler angle.
|
||||
@return the sine of the Euler angle theta (pitch attitude) corresponding
|
||||
to this quaternion rotation. */
|
||||
double GetCosEuler(int i) const {
|
||||
ComputeDerived();
|
||||
return mEulerCosines(i);
|
||||
}
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
|
||||
@param idx the component index.
|
||||
|
||||
Return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
|
||||
Note that the index given in the argument is unchecked.
|
||||
*/
|
||||
double operator()(unsigned int idx) const { return data[idx-1]; }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
|
||||
@param idx the component index.
|
||||
|
||||
Return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
|
||||
Note that the index given in the argument is unchecked.
|
||||
*/
|
||||
double& operator()(unsigned int idx) { mCacheValid = false; return data[idx-1]; }
|
||||
|
||||
/** Read access the entries of the vector.
|
||||
|
||||
@param idx the component index.
|
||||
|
||||
Return the value of the matrix entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
|
||||
This function is just a shortcut for the <tt>double
|
||||
operator()(unsigned int idx) const</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
|
||||
Note that the index given in the argument is unchecked.
|
||||
*/
|
||||
double Entry(unsigned int idx) const { return data[idx-1]; }
|
||||
|
||||
/** Write access the entries of the vector.
|
||||
|
||||
@param idx the component index.
|
||||
|
||||
Return a reference to the vector entry at the given index.
|
||||
Indices are counted starting with 1.
|
||||
|
||||
This function is just a shortcut for the <tt>double&
|
||||
operator()(unsigned int idx)</tt> function. It is
|
||||
used internally to access the elements in a more convenient way.
|
||||
|
||||
Note that the index given in the argument is unchecked.
|
||||
*/
|
||||
double& Entry(unsigned int idx) {
|
||||
mCacheValid = false;
|
||||
return data[idx-1];
|
||||
}
|
||||
|
||||
/** Assignment operator "=".
|
||||
Assign the value of q to the current object. Cached values are
|
||||
conserved.
|
||||
@param q reference to an FGQuaternion instance
|
||||
@return reference to a quaternion object */
|
||||
const FGQuaternion& operator=(const FGQuaternion& q) {
|
||||
// Copy the master values ...
|
||||
data[0] = q.data[0];
|
||||
data[1] = q.data[1];
|
||||
data[2] = q.data[2];
|
||||
data[3] = q.data[3];
|
||||
ComputeDerived();
|
||||
// .. and copy the derived values if they are valid
|
||||
mCacheValid = q.mCacheValid;
|
||||
if (mCacheValid) {
|
||||
mT = q.mT;
|
||||
mTInv = q.mTInv;
|
||||
mEulerAngles = q.mEulerAngles;
|
||||
mEulerSines = q.mEulerSines;
|
||||
mEulerCosines = q.mEulerCosines;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Conversion from Quat to Matrix
|
||||
operator FGMatrix33() const { return GetT(); }
|
||||
|
||||
/** Comparison operator "==".
|
||||
@param q a quaternion reference
|
||||
@return true if both quaternions represent the same rotation. */
|
||||
bool operator==(const FGQuaternion& q) const {
|
||||
return data[0] == q.data[0] && data[1] == q.data[1]
|
||||
&& data[2] == q.data[2] && data[3] == q.data[3];
|
||||
}
|
||||
|
||||
/** Comparison operator "!=".
|
||||
@param q a quaternion reference
|
||||
@return true if both quaternions do not represent the same rotation. */
|
||||
bool operator!=(const FGQuaternion& q) const { return ! operator==(q); }
|
||||
const FGQuaternion& operator+=(const FGQuaternion& q) {
|
||||
// Copy the master values ...
|
||||
data[0] += q.data[0];
|
||||
data[1] += q.data[1];
|
||||
data[2] += q.data[2];
|
||||
data[3] += q.data[3];
|
||||
mCacheValid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Arithmetic operator "-=".
|
||||
@param q a quaternion reference.
|
||||
@return a quaternion reference representing Q, where Q = Q - q. */
|
||||
const FGQuaternion& operator-=(const FGQuaternion& q) {
|
||||
// Copy the master values ...
|
||||
data[0] -= q.data[0];
|
||||
data[1] -= q.data[1];
|
||||
data[2] -= q.data[2];
|
||||
data[3] -= q.data[3];
|
||||
mCacheValid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Arithmetic operator "*=".
|
||||
@param scalar a multiplicative value.
|
||||
@return a quaternion reference representing Q, where Q = Q * scalar. */
|
||||
const FGQuaternion& operator*=(double scalar) {
|
||||
data[0] *= scalar;
|
||||
data[1] *= scalar;
|
||||
data[2] *= scalar;
|
||||
data[3] *= scalar;
|
||||
mCacheValid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Arithmetic operator "/=".
|
||||
@param scalar a divisor value.
|
||||
@return a quaternion reference representing Q, where Q = Q / scalar. */
|
||||
const FGQuaternion& operator/=(double scalar) {
|
||||
return operator*=(1.0/scalar);
|
||||
}
|
||||
|
||||
/** Arithmetic operator "+".
|
||||
@param q a quaternion to be summed.
|
||||
@return a quaternion representing Q, where Q = Q + q. */
|
||||
FGQuaternion operator+(const FGQuaternion& q) const {
|
||||
return FGQuaternion(data[0]+q.data[0], data[1]+q.data[1],
|
||||
data[2]+q.data[2], data[3]+q.data[3]);
|
||||
}
|
||||
|
||||
/** Arithmetic operator "-".
|
||||
@param q a quaternion to be subtracted.
|
||||
@return a quaternion representing Q, where Q = Q - q. */
|
||||
FGQuaternion operator-(const FGQuaternion& q) const {
|
||||
return FGQuaternion(data[0]-q.data[0], data[1]-q.data[1],
|
||||
data[2]-q.data[2], data[3]-q.data[3]);
|
||||
}
|
||||
|
||||
/** Arithmetic operator "*".
|
||||
Multiplication of two quaternions is like performing successive rotations.
|
||||
@param q a quaternion to be multiplied.
|
||||
@return a quaternion representing Q, where Q = Q * q. */
|
||||
FGQuaternion operator*(const FGQuaternion& q) const {
|
||||
return FGQuaternion(data[0]*q.data[0]-data[1]*q.data[1]-data[2]*q.data[2]-data[3]*q.data[3],
|
||||
data[0]*q.data[1]+data[1]*q.data[0]+data[2]*q.data[3]-data[3]*q.data[2],
|
||||
data[0]*q.data[2]-data[1]*q.data[3]+data[2]*q.data[0]+data[3]*q.data[1],
|
||||
data[0]*q.data[3]+data[1]*q.data[2]-data[2]*q.data[1]+data[3]*q.data[0]);
|
||||
}
|
||||
|
||||
/** Arithmetic operator "*=".
|
||||
Multiplication of two quaternions is like performing successive rotations.
|
||||
@param q a quaternion to be multiplied.
|
||||
@return a quaternion reference representing Q, where Q = Q * q. */
|
||||
const FGQuaternion& operator*=(const FGQuaternion& q) {
|
||||
double q0 = data[0]*q.data[0]-data[1]*q.data[1]-data[2]*q.data[2]-data[3]*q.data[3];
|
||||
double q1 = data[0]*q.data[1]+data[1]*q.data[0]+data[2]*q.data[3]-data[3]*q.data[2];
|
||||
double q2 = data[0]*q.data[2]-data[1]*q.data[3]+data[2]*q.data[0]+data[3]*q.data[1];
|
||||
double q3 = data[0]*q.data[3]+data[1]*q.data[2]-data[2]*q.data[1]+data[3]*q.data[0];
|
||||
data[0] = q0;
|
||||
data[1] = q1;
|
||||
data[2] = q2;
|
||||
data[3] = q3;
|
||||
mCacheValid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Inverse of the quaternion.
|
||||
|
||||
Compute and return the inverse of the quaternion so that the orientation
|
||||
represented with *this multiplied with the returned value is equal to
|
||||
the identity orientation.
|
||||
*/
|
||||
FGQuaternion Inverse(void) const {
|
||||
double norm = SqrMagnitude();
|
||||
if (norm == 0.0)
|
||||
return *this;
|
||||
double rNorm = 1.0/norm;
|
||||
return FGQuaternion( data[0]*rNorm, -data[1]*rNorm,
|
||||
-data[2]*rNorm, -data[3]*rNorm );
|
||||
}
|
||||
|
||||
/** Conjugate of the quaternion.
|
||||
|
||||
Compute and return the conjugate of the quaternion. This one is equal
|
||||
to the inverse iff the quaternion is normalized.
|
||||
*/
|
||||
FGQuaternion Conjugate(void) const {
|
||||
return FGQuaternion( data[0], -data[1], -data[2], -data[3] );
|
||||
}
|
||||
|
||||
friend FGQuaternion operator*(double, const FGQuaternion&);
|
||||
|
||||
/** Length of the vector.
|
||||
|
||||
Compute and return the euclidean norm of this vector.
|
||||
*/
|
||||
double Magnitude(void) const { return sqrt(SqrMagnitude()); }
|
||||
|
||||
/** Square of the length of the vector.
|
||||
|
||||
Compute and return the square of the euclidean norm of this vector.
|
||||
*/
|
||||
double SqrMagnitude(void) const {
|
||||
return data[0]*data[0] + data[1]*data[1]
|
||||
+ data[2]*data[2] + data[3]*data[3];
|
||||
}
|
||||
|
||||
/** Normalize.
|
||||
|
||||
Normalize the vector to have the Magnitude() == 1.0. If the vector
|
||||
is equal to zero it is left untouched.
|
||||
*/
|
||||
void Normalize(void);
|
||||
|
||||
/** Zero quaternion vector. Does not represent any orientation.
|
||||
Useful for initialization of increments */
|
||||
static FGQuaternion zero(void) { return FGQuaternion( 0.0, 0.0, 0.0, 0.0 ); }
|
||||
|
||||
std::string Dump(const std::string& delimiter) const;
|
||||
|
||||
friend FGQuaternion QExp(const FGColumnVector3& omega);
|
||||
|
||||
private:
|
||||
/** Copying by assigning the vector valued components. */
|
||||
FGQuaternion(double q1, double q2, double q3, double q4) : mCacheValid(false)
|
||||
{ data[0] = q1; data[1] = q2; data[2] = q3; data[3] = q4; }
|
||||
|
||||
/** Computation of derived values.
|
||||
This function recomputes the derived values like euler angles and
|
||||
transformation matrices. It does this unconditionally. */
|
||||
void ComputeDerivedUnconditional(void) const;
|
||||
|
||||
/** Computation of derived values.
|
||||
This function checks if the derived values like euler angles and
|
||||
transformation matrices are already computed. If so, it
|
||||
returns. If they need to be computed the real worker routine
|
||||
FGQuaternion::ComputeDerivedUnconditional(void) const
|
||||
is called. */
|
||||
void ComputeDerived(void) const {
|
||||
if (!mCacheValid)
|
||||
ComputeDerivedUnconditional();
|
||||
}
|
||||
|
||||
/** The quaternion values itself. This is the master copy. */
|
||||
double data[4];
|
||||
|
||||
/** A data validity flag.
|
||||
This class implements caching of the derived values like the
|
||||
orthogonal rotation matrices or the Euler angles. For caching we
|
||||
carry a flag which signals if the values are valid or not.
|
||||
The C++ keyword "mutable" tells the compiler that the data member is
|
||||
allowed to change during a const member function. */
|
||||
mutable bool mCacheValid;
|
||||
|
||||
/** This stores the transformation matrices. */
|
||||
mutable FGMatrix33 mT;
|
||||
mutable FGMatrix33 mTInv;
|
||||
|
||||
/** The cached euler angles. */
|
||||
mutable FGColumnVector3 mEulerAngles;
|
||||
|
||||
/** The cached sines and cosines of the euler angles. */
|
||||
mutable FGColumnVector3 mEulerSines;
|
||||
mutable FGColumnVector3 mEulerCosines;
|
||||
|
||||
void InitializeFromEulerAngles(double phi, double tht, double psi);
|
||||
};
|
||||
|
||||
/** Scalar multiplication.
|
||||
|
||||
@param scalar scalar value to multiply with.
|
||||
@param q Vector to multiply.
|
||||
|
||||
Multiply the Vector with a scalar value.
|
||||
*/
|
||||
inline FGQuaternion operator*(double scalar, const FGQuaternion& q) {
|
||||
return FGQuaternion(scalar*q.data[0], scalar*q.data[1], scalar*q.data[2], scalar*q.data[3]);
|
||||
}
|
||||
|
||||
/** Quaternion exponential
|
||||
@param omega rotation velocity
|
||||
Calculate the unit quaternion which is the result of the exponentiation of
|
||||
the vector 'omega'.
|
||||
*/
|
||||
inline FGQuaternion QExp(const FGColumnVector3& omega) {
|
||||
FGQuaternion qexp;
|
||||
double angle = omega.Magnitude();
|
||||
double sina_a = angle > 0.0 ? sin(angle)/angle : 1.0;
|
||||
|
||||
qexp.data[0] = cos(angle);
|
||||
qexp.data[1] = omega(1) * sina_a;
|
||||
qexp.data[2] = omega(2) * sina_a;
|
||||
qexp.data[3] = omega(3) * sina_a;
|
||||
|
||||
return qexp;
|
||||
}
|
||||
|
||||
/** Write quaternion to a stream.
|
||||
@param os Stream to write to.
|
||||
@param q Quaternion to write.
|
||||
Write the quaternion to a stream.*/
|
||||
std::ostream& operator<<(std::ostream& os, const FGQuaternion& q);
|
||||
|
||||
} // namespace JSBSim
|
||||
#endif
|
||||
48
src/FDM/JSBSim/math/FGRealValue.cpp
Normal file
48
src/FDM/JSBSim/math/FGRealValue.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGRealValue.cpp
|
||||
Author: Jon Berndt
|
||||
Date started: 12/10/2004
|
||||
Purpose: Stores real values
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGRealValue.h"
|
||||
#include "FGJSBBase.h"
|
||||
#include "input_output/string_utilities.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
std::string FGRealValue::GetName(void) const
|
||||
{
|
||||
return std::string("constant value ") + to_string(Value);
|
||||
}
|
||||
|
||||
}
|
||||
73
src/FDM/JSBSim/math/FGRealValue.h
Normal file
73
src/FDM/JSBSim/math/FGRealValue.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGRealValue.h
|
||||
Author: Jon Berndt
|
||||
Date started: December 10 2004
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGREALVALUE_H
|
||||
#define FGREALVALUE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGParameter.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Represents a real value
|
||||
@author Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGRealValue
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGRealValue : public FGParameter
|
||||
{
|
||||
public:
|
||||
|
||||
explicit FGRealValue(double val) : Value(val) {}
|
||||
|
||||
double GetValue(void) const override { return Value; };
|
||||
std::string GetName(void) const override;
|
||||
bool IsConstant(void) const override { return true; }
|
||||
|
||||
private:
|
||||
const double Value;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
227
src/FDM/JSBSim/math/FGRungeKutta.cpp
Normal file
227
src/FDM/JSBSim/math/FGRungeKutta.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGRungeKutta.cpp
|
||||
Author: Thomas Kreitler
|
||||
Date started: 04/9/2010
|
||||
|
||||
------------- Copyright (C) -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include "FGJSBBase.h"
|
||||
#include "FGRungeKutta.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DEFINITIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
const double FGRungeKutta::RealLimit = 1e30;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGRungeKutta::~FGRungeKutta() { };
|
||||
|
||||
int FGRungeKutta::init(double x_start, double x_end, int intervals)
|
||||
{
|
||||
x0 = x_start;
|
||||
x1 = x_end;
|
||||
h = (x_end - x_start)/intervals;
|
||||
safer_x1 = x1 - h*1e-6; // avoid 'intervals*h < x1'
|
||||
h05 = h*0.5;
|
||||
err = 0.0;
|
||||
|
||||
if (x0>=x1) {
|
||||
status &= eFaultyInit;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
/*
|
||||
Make sure that a numerical result is within +/-RealLimit.
|
||||
This is a hapless try to be portable.
|
||||
(There will be at least one architecture/compiler combination
|
||||
where this will fail.)
|
||||
*/
|
||||
|
||||
bool FGRungeKutta::sane_val(double x)
|
||||
{
|
||||
// assuming +/- inf behave as expected and 'nan' comparisons yield to false
|
||||
if ( x < RealLimit && x > -RealLimit ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGRungeKutta::evolve(double y_0, FGRungeKuttaProblem *pf)
|
||||
{
|
||||
double x = x0;
|
||||
double y = y_0;
|
||||
pfo = pf;
|
||||
|
||||
iterations = 0;
|
||||
|
||||
if (!trace_values) {
|
||||
while (x<safer_x1) {
|
||||
y = approximate(x,y);
|
||||
if (!sane_val(y)) { status &= eMathError; }
|
||||
x += h;
|
||||
iterations++;
|
||||
}
|
||||
} else {
|
||||
while (x<safer_x1) {
|
||||
cout << x << " " << y << endl;
|
||||
y = approximate(x,y);
|
||||
if (!sane_val(y)) { status &= eMathError; }
|
||||
x += h;
|
||||
iterations++;
|
||||
}
|
||||
cout << x << " " << y << endl;
|
||||
}
|
||||
|
||||
x_end = x; // twimc, store the last x used.
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGRK4::~FGRK4() { };
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGRK4::approximate(double x, double y)
|
||||
{
|
||||
double k1,k2,k3,k4;
|
||||
|
||||
k1 = pfo->pFunc(x , y );
|
||||
k2 = pfo->pFunc(x + h05, y + h05*k1);
|
||||
k3 = pfo->pFunc(x + h05, y + h05*k2);
|
||||
k4 = pfo->pFunc(x + h , y + h *k3);
|
||||
|
||||
y += h/6.0 * ( k1 + 2.0*k2 + 2.0*k3 + k4 );
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// Butcher tableau
|
||||
const double FGRKFehlberg::A2[] = { 0.0, 1.0/4.0 };
|
||||
const double FGRKFehlberg::A3[] = { 0.0, 3.0/32.0, 9.0/32.0 };
|
||||
const double FGRKFehlberg::A4[] = { 0.0, 1932.0/2197.0, -7200.0/2197.0, 7296.0/2197.0 };
|
||||
const double FGRKFehlberg::A5[] = { 0.0, 439.0/216.0, -8.0, 3680.0/513.0, -845.0/4104.0 };
|
||||
const double FGRKFehlberg::A6[] = { 0.0, -8.0/27.0, 2.0, -3544.0/2565.0, 1859.0/4104.0, -11.0/40.0 };
|
||||
|
||||
const double FGRKFehlberg::C[] = { 0.0, 0.0, 1.0/4.0, 3.0/8.0, 12.0/13.0, 1.0, 1.0/2.0 };
|
||||
|
||||
const double FGRKFehlberg::B[] = { 0.0, 16.0/135.0, 0.0, 6656.0/12825.0, 28561.0/56430.0, -9.0/50.0, 2.0/55.0 };
|
||||
const double FGRKFehlberg::Bs[] = { 0.0, 25.0/216.0, 0.0, 1408.0/2565.0, 2197.0/4104.0, -1.0/5.0, 0.0 };
|
||||
|
||||
// use this if truncation is an issue
|
||||
// const double Ee[] = { 0.0, 1.0/360.0, 0.0, -128.0/4275.0, -2197.0/75240.0, 1.0/50.0, 2.0/55.0 };
|
||||
|
||||
FGRKFehlberg::~FGRKFehlberg() { };
|
||||
|
||||
double FGRKFehlberg::approximate(double x, double y)
|
||||
{
|
||||
|
||||
double k1,k2,k3,k4,k5,k6, as;
|
||||
|
||||
double y4_val;
|
||||
double y5_val;
|
||||
double abs_err;
|
||||
double est_step;
|
||||
int done = 0;
|
||||
|
||||
|
||||
while (!done) {
|
||||
|
||||
err = h*h*h*h*h; // h might change
|
||||
|
||||
k1 = pfo->pFunc(x , y );
|
||||
|
||||
as = h*A2[1]*k1;
|
||||
k2 = pfo->pFunc(x + C[2]*h , y + as );
|
||||
|
||||
as = h*(A3[1]*k1 + A3[2]*k2);
|
||||
k3 = pfo->pFunc(x + C[3]*h , y + as );
|
||||
|
||||
as = h*(A4[1]*k1 + A4[2]*k2 + A4[3]*k3);
|
||||
k4 = pfo->pFunc(x + C[4]*h , y + as );
|
||||
|
||||
as = h*(A5[1]*k1 + A5[2]*k2 + A5[3]*k3 + A5[4]*k4);
|
||||
k5 = pfo->pFunc(x + C[5]*h , y + as );
|
||||
|
||||
as = h*(A6[1]*k1 + A6[2]*k2 + A6[3]*k3 + A6[4]*k4 + A6[5]*k5);
|
||||
k6 = pfo->pFunc(x + C[6]*h , y + as );
|
||||
|
||||
/* B[2]*k2 and Bs[2]*k2 are zero */
|
||||
y5_val = y + h * ( B[1]*k1 + B[3]*k3 + B[4]*k4 + B[5]*k5 + B[6]*k6);
|
||||
y4_val = y + h * (Bs[1]*k1 + Bs[3]*k3 + Bs[4]*k4 + Bs[5]*k5);
|
||||
|
||||
abs_err = fabs(y4_val-y5_val);
|
||||
// same in green
|
||||
// abs_err = h * (Ee[1] * k1 + Ee[3] * k3 + Ee[4] * k4 + Ee[5] * k5 + Ee[6] * k6);
|
||||
|
||||
// estimate step size
|
||||
if (abs_err > epsilon) {
|
||||
est_step = sqrt(sqrt(epsilon*h/abs_err));
|
||||
} else {
|
||||
est_step=2.0*h; // cheat
|
||||
}
|
||||
|
||||
// check if a smaller step size is proposed
|
||||
|
||||
if (shrink_avail>0 && est_step<h) {
|
||||
h/=2.0;
|
||||
shrink_avail--;
|
||||
} else {
|
||||
done = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return y4_val;
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
181
src/FDM/JSBSim/math/FGRungeKutta.h
Normal file
181
src/FDM/JSBSim/math/FGRungeKutta.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGRungeKutta.h
|
||||
Author: Thomas Kreitler
|
||||
Date started: 04/9/2010
|
||||
|
||||
------------- Copyright (C) -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGRUNGEKUTTA_H
|
||||
#define FGRUNGEKUTTA_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
|
||||
/**
|
||||
Minimalistic implementation of some Runge-Kutta methods. Runge-Kutta methods
|
||||
are a standard for solving ordinary differential equation (ODE) initial
|
||||
value problems. The code follows closely the description given on
|
||||
Wikipedia, see http://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods.
|
||||
|
||||
For more powerfull routines see GNU Scientific Library (GSL)
|
||||
or GNU Plotutils 'ode'.
|
||||
*/
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGRungeKuttaProblem
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/**
|
||||
Abstract base for the function to solve.
|
||||
*/
|
||||
class FGRungeKuttaProblem {
|
||||
public:
|
||||
virtual double pFunc(double x, double y) = 0;
|
||||
};
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGRungeKutta
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
/**
|
||||
Abstract base.
|
||||
*/
|
||||
|
||||
class FGRungeKutta {
|
||||
|
||||
public:
|
||||
|
||||
enum eStates { eNoError=0, eMathError=1, eFaultyInit=2, eEvolve=4, eUnknown=8} ;
|
||||
|
||||
int init(double x_start, double x_end, int intervals = 4);
|
||||
|
||||
double evolve(double y_0, FGRungeKuttaProblem *pf);
|
||||
|
||||
double getXEnd() { return x_end; }
|
||||
double getError() { return err; }
|
||||
|
||||
int getStatus() { return status; }
|
||||
int getIterations() { return iterations; }
|
||||
void clearStatus() { status = eNoError; }
|
||||
void setTrace(bool t) { trace_values = t; }
|
||||
|
||||
protected:
|
||||
// avoid accidents
|
||||
FGRungeKutta(): status(eNoError), trace_values(false), iterations(0) {};
|
||||
virtual ~FGRungeKutta();
|
||||
|
||||
FGRungeKuttaProblem *pfo;
|
||||
|
||||
double h;
|
||||
double h05; // h*0.5, halfwidth
|
||||
double err;
|
||||
|
||||
private:
|
||||
|
||||
virtual double approximate(double x, double y) = 0;
|
||||
|
||||
bool sane_val(double x);
|
||||
|
||||
static const double RealLimit;
|
||||
|
||||
double x0, x1;
|
||||
double safer_x1;
|
||||
double x_end;
|
||||
|
||||
int status;
|
||||
bool trace_values;
|
||||
int iterations;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGRK4
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
/**
|
||||
Classical RK4.
|
||||
*/
|
||||
|
||||
class FGRK4 : public FGRungeKutta {
|
||||
virtual ~FGRK4();
|
||||
private:
|
||||
double approximate(double x, double y);
|
||||
};
|
||||
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGRKFehlberg
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/**
|
||||
Runge-Kutta-Fehlberg method.
|
||||
This is a semi adaptive implementation of rkf - the interval only
|
||||
shrinks. As a result interval calculations remain trivial, but
|
||||
sometimes too many calculations are performed.
|
||||
Rationale: this code is not meant to be a universal pain-reliever
|
||||
for ode's. Rather it provides some safety if the number of
|
||||
intervals is set too low, or the problem function behaves a bit
|
||||
nasty in rare conditions.
|
||||
*/
|
||||
|
||||
|
||||
class FGRKFehlberg : public FGRungeKutta {
|
||||
|
||||
public:
|
||||
FGRKFehlberg() : shrink_avail(4), epsilon(1e-12) { };
|
||||
virtual ~FGRKFehlberg();
|
||||
double getEpsilon() { return epsilon; }
|
||||
int getShrinkAvail() { return shrink_avail; }
|
||||
void setEpsilon(double e) { epsilon = e; }
|
||||
void setShrinkAvail(int s) { shrink_avail = s; }
|
||||
|
||||
private:
|
||||
|
||||
double approximate(double x, double y);
|
||||
|
||||
int shrink_avail;
|
||||
double epsilon;
|
||||
|
||||
static const double A2[], A3[], A4[], A5[], A6[];
|
||||
static const double B[], Bs[], C[];
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
740
src/FDM/JSBSim/math/FGTable.cpp
Normal file
740
src/FDM/JSBSim/math/FGTable.cpp
Normal file
@@ -0,0 +1,740 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGTable.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 1/9/2001
|
||||
Purpose: Models a lookup table
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
Models a lookup table
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
JSB 1/9/00 Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "FGTable.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGTable::FGTable(int NRows)
|
||||
: nRows(NRows), nCols(1), PropertyManager(nullptr)
|
||||
{
|
||||
Type = tt1D;
|
||||
colCounter = 0;
|
||||
rowCounter = 1;
|
||||
nTables = 0;
|
||||
|
||||
Data = Allocate();
|
||||
Debug(0);
|
||||
lastRowIndex=lastColumnIndex=2;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTable::FGTable(int NRows, int NCols)
|
||||
: nRows(NRows), nCols(NCols), PropertyManager(nullptr)
|
||||
{
|
||||
Type = tt2D;
|
||||
colCounter = 1;
|
||||
rowCounter = 0;
|
||||
nTables = 0;
|
||||
|
||||
Data = Allocate();
|
||||
Debug(0);
|
||||
lastRowIndex=lastColumnIndex=2;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTable::FGTable(const FGTable& t) : PropertyManager(t.PropertyManager)
|
||||
{
|
||||
Type = t.Type;
|
||||
colCounter = t.colCounter;
|
||||
rowCounter = t.rowCounter;
|
||||
tableCounter = t.tableCounter;
|
||||
nRows = t.nRows;
|
||||
nCols = t.nCols;
|
||||
nTables = t.nTables;
|
||||
dimension = t.dimension;
|
||||
internal = t.internal;
|
||||
Name = t.Name;
|
||||
lookupProperty[0] = t.lookupProperty[0];
|
||||
lookupProperty[1] = t.lookupProperty[1];
|
||||
lookupProperty[2] = t.lookupProperty[2];
|
||||
|
||||
Tables = t.Tables;
|
||||
Data = Allocate();
|
||||
for (unsigned int r=0; r<=nRows; r++) {
|
||||
for (unsigned int c=0; c<=nCols; c++) {
|
||||
Data[r][c] = t.Data[r][c];
|
||||
}
|
||||
}
|
||||
lastRowIndex = t.lastRowIndex;
|
||||
lastColumnIndex = t.lastColumnIndex;
|
||||
lastTableIndex = t.lastTableIndex;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
unsigned int FindNumColumns(const string& test_line)
|
||||
{
|
||||
// determine number of data columns in table (first column is row lookup - don't count)
|
||||
size_t position=0;
|
||||
unsigned int nCols=0;
|
||||
while ((position = test_line.find_first_not_of(" \t", position)) != string::npos) {
|
||||
nCols++;
|
||||
position = test_line.find_first_of(" \t", position);
|
||||
}
|
||||
return nCols;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTable::FGTable(FGPropertyManager* propMan, Element* el,
|
||||
const std::string& Prefix)
|
||||
: PropertyManager(propMan)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
stringstream buf;
|
||||
string brkpt_string;
|
||||
Element *tableData = nullptr;
|
||||
string operation_types = "function, product, sum, difference, quotient,"
|
||||
"pow, abs, sin, cos, asin, acos, tan, atan, table";
|
||||
|
||||
nTables = 0;
|
||||
|
||||
// Is this an internal lookup table?
|
||||
|
||||
internal = false;
|
||||
Name = el->GetAttributeValue("name"); // Allow this table to be named with a property
|
||||
string call_type = el->GetAttributeValue("type");
|
||||
if (call_type == string("internal")) {
|
||||
Element* parent_element = el->GetParent();
|
||||
string parent_type = parent_element->GetName();
|
||||
if (operation_types.find(parent_type) == string::npos) {
|
||||
internal = true;
|
||||
} else {
|
||||
// internal table is a child element of a restricted type
|
||||
std::cerr << el->ReadFrom()
|
||||
<< " An internal table cannot be nested within another type,"
|
||||
<< " such as a function. The 'internal' keyword of table "
|
||||
<< Name << "is ignored." << endl;
|
||||
}
|
||||
} else if (!call_type.empty()) {
|
||||
std::cerr << el->ReadFrom()
|
||||
<<" An unknown table type attribute is listed: " << call_type
|
||||
<< endl;
|
||||
throw BaseException("Unknown table type.");
|
||||
}
|
||||
|
||||
// Determine and store the lookup properties for this table unless this table
|
||||
// is part of a 3D table, in which case its independentVar property indexes
|
||||
// will be set by a call from the owning table during creation
|
||||
|
||||
dimension = 0;
|
||||
|
||||
Element* axisElement = el->FindElement("independentVar");
|
||||
if (axisElement) {
|
||||
|
||||
// The 'internal' attribute of the table element cannot be specified
|
||||
// at the same time that independentVars are specified.
|
||||
if (internal) {
|
||||
cerr << el->ReadFrom()
|
||||
<< fgred << " This table specifies both 'internal' call type" << endl
|
||||
<< " and specific lookup properties via the 'independentVar' element." << endl
|
||||
<< " These are mutually exclusive specifications. The 'internal'" << endl
|
||||
<< " attribute will be ignored." << fgdef << endl << endl;
|
||||
internal = false;
|
||||
}
|
||||
|
||||
while (axisElement) {
|
||||
string property_string = axisElement->GetDataLine();
|
||||
if (property_string.find("#") != string::npos) {
|
||||
if (is_number(Prefix)) {
|
||||
property_string = replace(property_string,"#",Prefix);
|
||||
}
|
||||
}
|
||||
|
||||
FGPropertyValue_ptr node = new FGPropertyValue(property_string,
|
||||
PropertyManager);
|
||||
string lookup_axis = axisElement->GetAttributeValue("lookup");
|
||||
if (lookup_axis == string("row")) {
|
||||
lookupProperty[eRow] = node;
|
||||
} else if (lookup_axis == string("column")) {
|
||||
lookupProperty[eColumn] = node;
|
||||
} else if (lookup_axis == string("table")) {
|
||||
lookupProperty[eTable] = node;
|
||||
} else if (!lookup_axis.empty()) {
|
||||
throw BaseException("Lookup table axis specification not understood: " + lookup_axis);
|
||||
} else { // assumed single dimension table; row lookup
|
||||
lookupProperty[eRow] = node;
|
||||
}
|
||||
dimension++;
|
||||
axisElement = el->FindNextElement("independentVar");
|
||||
}
|
||||
|
||||
} else if (internal) { // This table is an internal table
|
||||
|
||||
// determine how many rows, columns, and tables in this table (dimension).
|
||||
|
||||
if (el->GetNumElements("tableData") > 1) {
|
||||
dimension = 3; // this is a 3D table
|
||||
} else {
|
||||
tableData = el->FindElement("tableData");
|
||||
string test_line = tableData->GetDataLine(1); // examine second line in table for dimension
|
||||
if (FindNumColumns(test_line) == 2) dimension = 1; // 1D table
|
||||
else if (FindNumColumns(test_line) > 2) dimension = 2; // 2D table
|
||||
else {
|
||||
std::cerr << tableData->ReadFrom()
|
||||
<< "Invalid number of columns in table" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
brkpt_string = el->GetAttributeValue("breakPoint");
|
||||
if (brkpt_string.empty()) {
|
||||
// no independentVars found, and table is not marked as internal, nor is it
|
||||
// a 3D table
|
||||
std::cerr << el->ReadFrom()
|
||||
<< "No independentVars found, and table is not marked as internal,"
|
||||
<< " nor is it a 3D table." << endl;
|
||||
throw BaseException("No independent variable found for table.");
|
||||
}
|
||||
}
|
||||
// end lookup property code
|
||||
|
||||
if (brkpt_string.empty()) { // Not a 3D table "table element"
|
||||
tableData = el->FindElement("tableData");
|
||||
} else { // This is a table in a 3D table
|
||||
tableData = el;
|
||||
dimension = 2; // Currently, infers 2D table
|
||||
}
|
||||
|
||||
for (i=0; i<tableData->GetNumDataLines(); i++) {
|
||||
string line = tableData->GetDataLine(i);
|
||||
if (line.find_first_not_of("0123456789.-+eE \t\n") != string::npos) {
|
||||
cerr << " In file " << tableData->GetFileName() << endl
|
||||
<< " Illegal character found in line "
|
||||
<< tableData->GetLineNumber() + i + 1 << ": " << endl << line << endl;
|
||||
throw BaseException("Illegal character");
|
||||
}
|
||||
buf << line << " ";
|
||||
}
|
||||
|
||||
switch (dimension) {
|
||||
case 1:
|
||||
nRows = tableData->GetNumDataLines();
|
||||
nCols = 1;
|
||||
Type = tt1D;
|
||||
colCounter = 0;
|
||||
rowCounter = 1;
|
||||
Data = Allocate();
|
||||
Debug(0);
|
||||
lastRowIndex = lastColumnIndex = 2;
|
||||
*this << buf;
|
||||
break;
|
||||
case 2:
|
||||
nRows = tableData->GetNumDataLines()-1;
|
||||
|
||||
if (nRows >= 2) {
|
||||
nCols = FindNumColumns(tableData->GetDataLine(0));
|
||||
if (nCols < 2) {
|
||||
std::cerr << tableData->ReadFrom()
|
||||
<< "Not enough columns in table data" << endl;
|
||||
throw BaseException("Not enough columns in table data.");
|
||||
}
|
||||
} else {
|
||||
std::cerr << tableData->ReadFrom()
|
||||
<< "Not enough rows in table data" << endl;
|
||||
throw BaseException("Not enough rows in the table data.");
|
||||
}
|
||||
|
||||
Type = tt2D;
|
||||
colCounter = 1;
|
||||
rowCounter = 0;
|
||||
|
||||
Data = Allocate();
|
||||
lastRowIndex = lastColumnIndex = 2;
|
||||
*this << buf;
|
||||
break;
|
||||
case 3:
|
||||
nTables = el->GetNumElements("tableData");
|
||||
nRows = nTables;
|
||||
nCols = 1;
|
||||
Type = tt3D;
|
||||
colCounter = 1;
|
||||
rowCounter = 1;
|
||||
lastRowIndex = lastColumnIndex = 2;
|
||||
|
||||
Data = Allocate(); // this data array will contain the keys for the associated tables
|
||||
Tables.reserve(nTables); // necessary?
|
||||
tableData = el->FindElement("tableData");
|
||||
for (i=0; i<nTables; i++) {
|
||||
Tables.push_back(new FGTable(PropertyManager, tableData));
|
||||
Data[i+1][1] = tableData->GetAttributeValueAsNumber("breakPoint");
|
||||
Tables[i]->lookupProperty[eRow] = lookupProperty[eRow];
|
||||
Tables[i]->lookupProperty[eColumn] = lookupProperty[eColumn];
|
||||
tableData = el->FindNextElement("tableData");
|
||||
}
|
||||
|
||||
Debug(0);
|
||||
break;
|
||||
default:
|
||||
cout << "No dimension given" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
// Sanity checks: lookup indices must be increasing monotonically
|
||||
unsigned int r,c,b;
|
||||
|
||||
// find next xml element containing a name attribute
|
||||
// to indicate where the error occured
|
||||
Element* nameel = el;
|
||||
while (nameel != 0 && nameel->GetAttributeValue("name") == "")
|
||||
nameel=nameel->GetParent();
|
||||
|
||||
// check breakpoints, if applicable
|
||||
if (dimension > 2) {
|
||||
for (b=2; b<=nTables; ++b) {
|
||||
if (Data[b][1] <= Data[b-1][1]) {
|
||||
std::cerr << el->ReadFrom()
|
||||
<< fgred << highint
|
||||
<< " FGTable: breakpoint lookup is not monotonically increasing" << endl
|
||||
<< " in breakpoint " << b;
|
||||
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
|
||||
std::cerr << ":" << reset << endl
|
||||
<< " " << Data[b][1] << "<=" << Data[b-1][1] << endl;
|
||||
throw BaseException("Breakpoint lookup is not monotonically increasing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check columns, if applicable
|
||||
if (dimension > 1) {
|
||||
for (c=2; c<=nCols; ++c) {
|
||||
if (Data[0][c] <= Data[0][c-1]) {
|
||||
std::cerr << el->ReadFrom()
|
||||
<< fgred << highint
|
||||
<< " FGTable: column lookup is not monotonically increasing" << endl
|
||||
<< " in column " << c;
|
||||
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
|
||||
std::cerr << ":" << reset << endl
|
||||
<< " " << Data[0][c] << "<=" << Data[0][c-1] << endl;
|
||||
throw BaseException("FGTable: column lookup is not monotonically increasing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check rows
|
||||
if (dimension < 3) { // in 3D tables, check only rows of subtables
|
||||
for (r=2; r<=nRows; ++r) {
|
||||
if (Data[r][0]<=Data[r-1][0]) {
|
||||
std::cerr << el->ReadFrom()
|
||||
<< fgred << highint
|
||||
<< " FGTable: row lookup is not monotonically increasing" << endl
|
||||
<< " in row " << r;
|
||||
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
|
||||
std::cerr << ":" << reset << endl
|
||||
<< " " << Data[r][0] << "<=" << Data[r-1][0] << endl;
|
||||
throw BaseException("FGTable: row lookup is not monotonically increasing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bind(el, Prefix);
|
||||
|
||||
if (debug_lvl & 1) Print();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double** FGTable::Allocate(void)
|
||||
{
|
||||
Data = new double*[nRows+1];
|
||||
for (unsigned int r=0; r<=nRows; r++) {
|
||||
Data[r] = new double[nCols+1];
|
||||
for (unsigned int c=0; c<=nCols; c++) {
|
||||
Data[r][c] = 0.0;
|
||||
}
|
||||
}
|
||||
return Data;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTable::~FGTable()
|
||||
{
|
||||
// Untie the bound property so that it makes no further reference to this
|
||||
// instance of FGTable after the destruction is completed.
|
||||
if (!Name.empty() && !internal) {
|
||||
string tmp = PropertyManager->mkPropertyName(Name, false);
|
||||
FGPropertyNode* node = PropertyManager->GetNode(tmp);
|
||||
if (node && node->isTied())
|
||||
PropertyManager->Untie(node);
|
||||
}
|
||||
|
||||
if (nTables > 0) {
|
||||
for (unsigned int i=0; i<nTables; i++) delete Tables[i];
|
||||
Tables.clear();
|
||||
}
|
||||
for (unsigned int r=0; r<=nRows; r++) delete[] Data[r];
|
||||
delete[] Data;
|
||||
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGTable::GetValue(void) const
|
||||
{
|
||||
double temp = 0;
|
||||
double temp2 = 0;
|
||||
|
||||
switch (Type) {
|
||||
case tt1D:
|
||||
assert(lookupProperty[eRow]);
|
||||
temp = lookupProperty[eRow]->getDoubleValue();
|
||||
temp2 = GetValue(temp);
|
||||
return temp2;
|
||||
case tt2D:
|
||||
assert(lookupProperty[eRow]);
|
||||
assert(lookupProperty[eColumn]);
|
||||
return GetValue(lookupProperty[eRow]->getDoubleValue(),
|
||||
lookupProperty[eColumn]->getDoubleValue());
|
||||
case tt3D:
|
||||
assert(lookupProperty[eRow]);
|
||||
assert(lookupProperty[eColumn]);
|
||||
assert(lookupProperty[eTable]);
|
||||
return GetValue(lookupProperty[eRow]->getDoubleValue(),
|
||||
lookupProperty[eColumn]->getDoubleValue(),
|
||||
lookupProperty[eTable]->getDoubleValue());
|
||||
default:
|
||||
cerr << "Attempted to GetValue() for invalid/unknown table type" << endl;
|
||||
throw(string("Attempted to GetValue() for invalid/unknown table type"));
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGTable::GetValue(double key) const
|
||||
{
|
||||
double Factor, Value, Span;
|
||||
unsigned int r = lastRowIndex;
|
||||
|
||||
//if the key is off the end of the table, just return the
|
||||
//end-of-table value, do not extrapolate
|
||||
if( key <= Data[1][0] ) {
|
||||
lastRowIndex=2;
|
||||
//cout << "Key underneath table: " << key << endl;
|
||||
return Data[1][1];
|
||||
} else if ( key >= Data[nRows][0] ) {
|
||||
lastRowIndex=nRows;
|
||||
//cout << "Key over table: " << key << endl;
|
||||
return Data[nRows][1];
|
||||
}
|
||||
|
||||
// the key is somewhere in the middle, search for the right breakpoint
|
||||
// The search is particularly efficient if
|
||||
// the correct breakpoint has not changed since last frame or
|
||||
// has only changed very little
|
||||
|
||||
while (r > 2 && Data[r-1][0] > key) { r--; }
|
||||
while (r < nRows && Data[r][0] < key) { r++; }
|
||||
|
||||
lastRowIndex=r;
|
||||
// make sure denominator below does not go to zero.
|
||||
|
||||
Span = Data[r][0] - Data[r-1][0];
|
||||
if (Span != 0.0) {
|
||||
Factor = (key - Data[r-1][0]) / Span;
|
||||
if (Factor > 1.0) Factor = 1.0;
|
||||
} else {
|
||||
Factor = 1.0;
|
||||
}
|
||||
|
||||
Value = Factor*(Data[r][1] - Data[r-1][1]) + Data[r-1][1];
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGTable::GetValue(double rowKey, double colKey) const
|
||||
{
|
||||
double rFactor, cFactor, col1temp, col2temp, Value;
|
||||
unsigned int r = lastRowIndex;
|
||||
unsigned int c = lastColumnIndex;
|
||||
|
||||
while(r > 2 && Data[r-1][0] > rowKey) { r--; }
|
||||
while(r < nRows && Data[r] [0] < rowKey) { r++; }
|
||||
|
||||
while(c > 2 && Data[0][c-1] > colKey) { c--; }
|
||||
while(c < nCols && Data[0][c] < colKey) { c++; }
|
||||
|
||||
lastRowIndex=r;
|
||||
lastColumnIndex=c;
|
||||
|
||||
rFactor = (rowKey - Data[r-1][0]) / (Data[r][0] - Data[r-1][0]);
|
||||
cFactor = (colKey - Data[0][c-1]) / (Data[0][c] - Data[0][c-1]);
|
||||
|
||||
if (rFactor > 1.0) rFactor = 1.0;
|
||||
else if (rFactor < 0.0) rFactor = 0.0;
|
||||
|
||||
if (cFactor > 1.0) cFactor = 1.0;
|
||||
else if (cFactor < 0.0) cFactor = 0.0;
|
||||
|
||||
col1temp = rFactor*(Data[r][c-1] - Data[r-1][c-1]) + Data[r-1][c-1];
|
||||
col2temp = rFactor*(Data[r][c] - Data[r-1][c]) + Data[r-1][c];
|
||||
|
||||
Value = col1temp + cFactor*(col2temp - col1temp);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGTable::GetValue(double rowKey, double colKey, double tableKey) const
|
||||
{
|
||||
double Factor, Value, Span;
|
||||
unsigned int r = lastRowIndex;
|
||||
|
||||
//if the key is off the end (or before the beginning) of the table,
|
||||
// just return the boundary-table value, do not extrapolate
|
||||
|
||||
if( tableKey <= Data[1][1] ) {
|
||||
lastRowIndex=2;
|
||||
return Tables[0]->GetValue(rowKey, colKey);
|
||||
} else if ( tableKey >= Data[nRows][1] ) {
|
||||
lastRowIndex=nRows;
|
||||
return Tables[nRows-1]->GetValue(rowKey, colKey);
|
||||
}
|
||||
|
||||
// the key is somewhere in the middle, search for the right breakpoint
|
||||
// The search is particularly efficient if
|
||||
// the correct breakpoint has not changed since last frame or
|
||||
// has only changed very little
|
||||
|
||||
while(r > 2 && Data[r-1][1] > tableKey) { r--; }
|
||||
while(r < nRows && Data[r] [1] < tableKey) { r++; }
|
||||
|
||||
lastRowIndex=r;
|
||||
// make sure denominator below does not go to zero.
|
||||
|
||||
Span = Data[r][1] - Data[r-1][1];
|
||||
if (Span != 0.0) {
|
||||
Factor = (tableKey - Data[r-1][1]) / Span;
|
||||
if (Factor > 1.0) Factor = 1.0;
|
||||
} else {
|
||||
Factor = 1.0;
|
||||
}
|
||||
|
||||
Value = Factor*(Tables[r-1]->GetValue(rowKey, colKey) - Tables[r-2]->GetValue(rowKey, colKey))
|
||||
+ Tables[r-2]->GetValue(rowKey, colKey);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTable::operator<<(istream& in_stream)
|
||||
{
|
||||
int startRow=0;
|
||||
int startCol=0;
|
||||
|
||||
// In 1D table, no pseudo-row of column-headers (i.e. keys):
|
||||
if (Type == tt1D) startRow = 1;
|
||||
|
||||
for (unsigned int r=startRow; r<=nRows; r++) {
|
||||
for (unsigned int c=startCol; c<=nCols; c++) {
|
||||
if (r != 0 || c != 0) {
|
||||
in_stream >> Data[r][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// Put some error handling in here if trying to access out of range row, col.
|
||||
|
||||
FGTable& FGTable::operator<<(const double n)
|
||||
{
|
||||
Data[rowCounter][colCounter] = n;
|
||||
if (colCounter == (int)nCols) {
|
||||
colCounter = 0;
|
||||
rowCounter++;
|
||||
} else {
|
||||
colCounter++;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGTable& FGTable::operator<<(const int n)
|
||||
{
|
||||
*this << (double)n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTable::Print(void)
|
||||
{
|
||||
int startRow=0;
|
||||
int startCol=0;
|
||||
|
||||
if (Type == tt1D || Type == tt3D) startRow = 1;
|
||||
if (Type == tt3D) startCol = 1;
|
||||
|
||||
#if defined (sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
|
||||
unsigned long flags = cout.setf(ios::fixed);
|
||||
#else
|
||||
ios::fmtflags flags = cout.setf(ios::fixed); // set up output stream
|
||||
#endif
|
||||
|
||||
switch(Type) {
|
||||
case tt1D:
|
||||
cout << " 1 dimensional table with " << nRows << " rows." << endl;
|
||||
break;
|
||||
case tt2D:
|
||||
cout << " 2 dimensional table with " << nRows << " rows, " << nCols << " columns." << endl;
|
||||
break;
|
||||
case tt3D:
|
||||
cout << " 3 dimensional table with " << nRows << " rows, "
|
||||
<< nCols << " columns "
|
||||
<< nTables << " tables." << endl;
|
||||
break;
|
||||
}
|
||||
cout.precision(4);
|
||||
for (unsigned int r=startRow; r<=nRows; r++) {
|
||||
cout << " ";
|
||||
for (unsigned int c=startCol; c<=nCols; c++) {
|
||||
if (r == 0 && c == 0) {
|
||||
cout << " ";
|
||||
} else {
|
||||
cout << Data[r][c] << " ";
|
||||
if (Type == tt3D) {
|
||||
cout << endl;
|
||||
Tables[r-1]->Print();
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
cout.setf(flags); // reset
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGTable::bind(Element* el, const string& Prefix)
|
||||
{
|
||||
typedef double (FGTable::*PMF)(void) const;
|
||||
|
||||
if ( !Name.empty() && !internal) {
|
||||
if (!Prefix.empty()) {
|
||||
if (is_number(Prefix)) {
|
||||
if (Name.find("#") != string::npos) { // if "#" is found
|
||||
Name = replace(Name, "#", Prefix);
|
||||
} else {
|
||||
cerr << el->ReadFrom()
|
||||
<< "Malformed table name with number: " << Prefix
|
||||
<< " and property name: " << Name
|
||||
<< " but no \"#\" sign for substitution." << endl;
|
||||
}
|
||||
} else {
|
||||
Name = Prefix + "/" + Name;
|
||||
}
|
||||
}
|
||||
string tmp = PropertyManager->mkPropertyName(Name, false);
|
||||
|
||||
if (PropertyManager->HasNode(tmp)) {
|
||||
FGPropertyNode* _property = PropertyManager->GetNode(tmp);
|
||||
if (_property->isTied()) {
|
||||
cerr << el->ReadFrom()
|
||||
<< "Property " << tmp << " has already been successfully bound (late)." << endl;
|
||||
throw("Failed to bind the property to an existing already tied node.");
|
||||
}
|
||||
}
|
||||
|
||||
PropertyManager->Tie(tmp, this, (PMF)&FGTable::GetValue);
|
||||
}
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGTable::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGTable" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGTable" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
312
src/FDM/JSBSim/math/FGTable.h
Normal file
312
src/FDM/JSBSim/math/FGTable.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTable.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 1/9/2001
|
||||
|
||||
------------- Copyright (C) 2001 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
JSB 1/9/00 Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGTABLE_H
|
||||
#define FGTABLE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGParameter.h"
|
||||
#include "math/FGPropertyValue.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class Element;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Lookup table class.
|
||||
Models a one, two, or three dimensional lookup table for use in aerodynamics
|
||||
and function definitions.
|
||||
|
||||
For a single "vector" lookup table, the format is as follows:
|
||||
|
||||
@code
|
||||
<table name="property_name">
|
||||
<independentVar lookup="row"> property_name </independentVar>
|
||||
<tableData>
|
||||
key_1 value_1
|
||||
key_2 value_2
|
||||
... ...
|
||||
key_n value_n
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
The lookup="row" attribute in the independentVar element is option in this case;
|
||||
it is assumed that the independentVar is a row variable.
|
||||
|
||||
A "real life" example is as shown here:
|
||||
|
||||
@code
|
||||
<table>
|
||||
<independentVar lookup="row"> aero/alpha-rad </independentVar>
|
||||
<tableData>
|
||||
-1.57 1.500
|
||||
-0.26 0.033
|
||||
0.00 0.025
|
||||
0.26 0.033
|
||||
1.57 1.500
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
The first column in the data table represents the lookup index (or "key"). In
|
||||
this case, the lookup index is aero/alpha-rad (angle of attack in radians).
|
||||
If alpha is 0.26 radians, the value returned from the lookup table
|
||||
would be 0.033.
|
||||
|
||||
The definition for a 2D table, is as follows:
|
||||
|
||||
@code
|
||||
<table name="property_name">
|
||||
<independentVar lookup="row"> property_name </independentVar>
|
||||
<independentVar lookup="column"> property_name </independentVar>
|
||||
<tableData>
|
||||
{col_1_key col_2_key ... col_n_key }
|
||||
{row_1_key} {col_1_data col_2_data ... col_n_data}
|
||||
{row_2_key} {... ... ... ... }
|
||||
{ ... } {... ... ... ... }
|
||||
{row_n_key} {... ... ... ... }
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
The data is in a gridded format.
|
||||
|
||||
A "real life" example is as shown below. Alpha in radians is the row lookup (alpha
|
||||
breakpoints are arranged in the first column) and flap position in degrees is
|
||||
|
||||
@code
|
||||
<table>
|
||||
<independentVar lookup="row">aero/alpha-rad</independentVar>
|
||||
<independentVar lookup="column">fcs/flap-pos-deg</independentVar>
|
||||
<tableData>
|
||||
0.0 10.0 20.0 30.0
|
||||
-0.0523599 8.96747e-05 0.00231942 0.0059252 0.00835082
|
||||
-0.0349066 0.000313268 0.00567451 0.0108461 0.0140545
|
||||
-0.0174533 0.00201318 0.0105059 0.0172432 0.0212346
|
||||
0.0 0.0051894 0.0168137 0.0251167 0.0298909
|
||||
0.0174533 0.00993967 0.0247521 0.0346492 0.0402205
|
||||
0.0349066 0.0162201 0.0342207 0.0457119 0.0520802
|
||||
0.0523599 0.0240308 0.0452195 0.0583047 0.0654701
|
||||
0.0698132 0.0333717 0.0577485 0.0724278 0.0803902
|
||||
0.0872664 0.0442427 0.0718077 0.088081 0.0968405
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
The definition for a 3D table in a coefficient would be (for example):
|
||||
|
||||
@code
|
||||
<table name="property_name">
|
||||
<independentVar lookup="row"> property_name </independentVar>
|
||||
<independentVar lookup="column"> property_name </independentVar>
|
||||
<tableData breakpoint="table_1_key">
|
||||
{col_1_key col_2_key ... col_n_key }
|
||||
{row_1_key} {col_1_data col_2_data ... col_n_data}
|
||||
{row_2_key} {... ... ... ... }
|
||||
{ ... } {... ... ... ... }
|
||||
{row_n_key} {... ... ... ... }
|
||||
</tableData>
|
||||
<tableData breakpoint="table_2_key">
|
||||
{col_1_key col_2_key ... col_n_key }
|
||||
{row_1_key} {col_1_data col_2_data ... col_n_data}
|
||||
{row_2_key} {... ... ... ... }
|
||||
{ ... } {... ... ... ... }
|
||||
{row_n_key} {... ... ... ... }
|
||||
</tableData>
|
||||
...
|
||||
<tableData breakpoint="table_n_key">
|
||||
{col_1_key col_2_key ... col_n_key }
|
||||
{row_1_key} {col_1_data col_2_data ... col_n_data}
|
||||
{row_2_key} {... ... ... ... }
|
||||
{ ... } {... ... ... ... }
|
||||
{row_n_key} {... ... ... ... }
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
[Note the "breakpoint" attribute in the tableData element, above.]
|
||||
|
||||
Here's an example:
|
||||
|
||||
@code
|
||||
<table>
|
||||
<independentVar lookup="row">fcs/row-value</independentVar>
|
||||
<independentVar lookup="column">fcs/column-value</independentVar>
|
||||
<independentVar lookup="table">fcs/table-value</independentVar>
|
||||
<tableData breakPoint="-1.0">
|
||||
-1.0 1.0
|
||||
0.0 1.0000 2.0000
|
||||
1.0 3.0000 4.0000
|
||||
</tableData>
|
||||
<tableData breakPoint="0.0000">
|
||||
0.0 10.0
|
||||
2.0 1.0000 2.0000
|
||||
3.0 3.0000 4.0000
|
||||
</tableData>
|
||||
<tableData breakPoint="1.0">
|
||||
0.0 10.0 20.0
|
||||
2.0 1.0000 2.0000 3.0000
|
||||
3.0 4.0000 5.0000 6.0000
|
||||
10.0 7.0000 8.0000 9.0000
|
||||
</tableData>
|
||||
</table>
|
||||
@endcode
|
||||
|
||||
In addition to using a Table for something like a coefficient, where all the
|
||||
row and column elements are read in from a file, a Table could be created
|
||||
and populated completely within program code:
|
||||
|
||||
@code
|
||||
// First column is thi, second is neta (combustion efficiency)
|
||||
Lookup_Combustion_Efficiency = new FGTable(12);
|
||||
|
||||
*Lookup_Combustion_Efficiency << 0.00 << 0.980;
|
||||
*Lookup_Combustion_Efficiency << 0.90 << 0.980;
|
||||
*Lookup_Combustion_Efficiency << 1.00 << 0.970;
|
||||
*Lookup_Combustion_Efficiency << 1.05 << 0.950;
|
||||
*Lookup_Combustion_Efficiency << 1.10 << 0.900;
|
||||
*Lookup_Combustion_Efficiency << 1.15 << 0.850;
|
||||
*Lookup_Combustion_Efficiency << 1.20 << 0.790;
|
||||
*Lookup_Combustion_Efficiency << 1.30 << 0.700;
|
||||
*Lookup_Combustion_Efficiency << 1.40 << 0.630;
|
||||
*Lookup_Combustion_Efficiency << 1.50 << 0.570;
|
||||
*Lookup_Combustion_Efficiency << 1.60 << 0.525;
|
||||
*Lookup_Combustion_Efficiency << 2.00 << 0.345;
|
||||
@endcode
|
||||
|
||||
The first column in the table, above, is thi (the lookup index, or key). The
|
||||
second column is the output data - in this case, "neta" (the Greek letter
|
||||
referring to combustion efficiency). Later on, the table is used like this:
|
||||
|
||||
@code
|
||||
combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio);
|
||||
@endcode
|
||||
|
||||
@author Jon S. Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGTable : public FGParameter, public FGJSBBase
|
||||
{
|
||||
public:
|
||||
/// Destructor
|
||||
~FGTable();
|
||||
|
||||
/** This is the very important copy constructor.
|
||||
@param table a const reference to a table.*/
|
||||
FGTable(const FGTable& table);
|
||||
|
||||
/// The constructor for a table
|
||||
FGTable (FGPropertyManager* propMan, Element* el, const std::string& prefix="");
|
||||
FGTable (int );
|
||||
FGTable (int, int);
|
||||
double GetValue(void) const;
|
||||
double GetValue(double key) const;
|
||||
double GetValue(double rowKey, double colKey) const;
|
||||
double GetValue(double rowKey, double colKey, double TableKey) const;
|
||||
/** Read the table in.
|
||||
Data in the config file should be in matrix format with the row
|
||||
independents as the first column and the column independents in
|
||||
the first row. The implication of this layout is that there should
|
||||
be no value in the upper left corner of the matrix e.g:
|
||||
<pre>
|
||||
0 10 20 30 ...
|
||||
-5 1 2 3 4 ...
|
||||
...
|
||||
</pre>
|
||||
|
||||
For multiple-table (i.e. 3D) data sets there is an additional number
|
||||
key in the table definition. For example:
|
||||
|
||||
<pre>
|
||||
0.0
|
||||
0 10 20 30 ...
|
||||
-5 1 2 3 4 ...
|
||||
...
|
||||
</pre>
|
||||
*/
|
||||
|
||||
void operator<<(std::istream&);
|
||||
FGTable& operator<<(const double n);
|
||||
FGTable& operator<<(const int n);
|
||||
|
||||
inline double GetElement(int r, int c) const {return Data[r][c];}
|
||||
|
||||
double operator()(unsigned int r, unsigned int c) const
|
||||
{ return GetElement(r, c); }
|
||||
|
||||
void SetRowIndexProperty(FGPropertyNode *node)
|
||||
{ lookupProperty[eRow] = new FGPropertyValue(node); }
|
||||
void SetColumnIndexProperty(FGPropertyNode *node)
|
||||
{ lookupProperty[eColumn] = new FGPropertyValue(node); }
|
||||
|
||||
unsigned int GetNumRows() const {return nRows;}
|
||||
|
||||
void Print(void);
|
||||
|
||||
std::string GetName(void) const {return Name;}
|
||||
|
||||
private:
|
||||
enum type {tt1D, tt2D, tt3D} Type;
|
||||
enum axis {eRow=0, eColumn, eTable};
|
||||
bool internal;
|
||||
FGPropertyValue_ptr lookupProperty[3];
|
||||
double** Data;
|
||||
std::vector <FGTable*> Tables;
|
||||
unsigned int nRows, nCols, nTables, dimension;
|
||||
int colCounter, rowCounter, tableCounter;
|
||||
mutable int lastRowIndex, lastColumnIndex, lastTableIndex;
|
||||
double** Allocate(void);
|
||||
FGPropertyManager* const PropertyManager;
|
||||
std::string Name;
|
||||
void bind(Element* el, const std::string& Prefix);
|
||||
void Debug(int from);
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
#endif
|
||||
61
src/FDM/JSBSim/math/FGTemplateFunc.cpp
Normal file
61
src/FDM/JSBSim/math/FGTemplateFunc.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGTemplateFunc.cpp
|
||||
Author: Bertrand Coconnier
|
||||
Date started: December 27, 2018
|
||||
|
||||
--------- Copyright (C) 2018 B. Coconnier (bcoconni@users.sf.net) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGTemplateFunc::FGTemplateFunc(FGFDMExec* fdmex, Element* element)
|
||||
: FGFunction(fdmex->GetPropertyManager())
|
||||
{
|
||||
var = new FGPropertyValue(nullptr);
|
||||
Load(element, var, fdmex);
|
||||
CheckMinArguments(element, 1);
|
||||
CheckMaxArguments(element, 1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
}
|
||||
78
src/FDM/JSBSim/math/FGTemplateFunc.h
Normal file
78
src/FDM/JSBSim/math/FGTemplateFunc.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGTemplateFunc.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: March 10 2018
|
||||
|
||||
--------- Copyright (C) 2018 B. Coconnier (bcoconni@users.sf.net) -----------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGTEMPLATEFUNC_H
|
||||
#define FGTEMPLATEFUNC_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "math/FGFunction.h"
|
||||
#include "math/FGPropertyValue.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
class FGFDMExec;
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
DECLARATION: FGTemplateFunc
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGTemplateFunc : public FGFunction
|
||||
{
|
||||
public:
|
||||
|
||||
FGTemplateFunc(FGFDMExec* fdmex, Element* element);
|
||||
|
||||
double GetValue(FGPropertyNode* node) {
|
||||
var->SetNode(node);
|
||||
return FGFunction::GetValue();
|
||||
}
|
||||
|
||||
private:
|
||||
/** FGTemplateFunc must not be bound to the property manager. The bind method
|
||||
is therefore made private and overloaded as a no-op */
|
||||
void bind(Element*, const std::string&) override {}
|
||||
FGPropertyValue_ptr var;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<FGTemplateFunc> FGTemplateFunc_ptr;
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
#endif
|
||||
61
src/FDM/JSBSim/math/LagrangeMultiplier.h
Normal file
61
src/FDM/JSBSim/math/LagrangeMultiplier.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: LaGrangeMultiplier.h
|
||||
Author: Bertrand Coconnier
|
||||
Date started: 07/01/11
|
||||
|
||||
------------- Copyright (C) 2011 Bertrand Coconnier -------------
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be found on
|
||||
the world wide web at http://www.gnu.org.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef LAGRANGEMULTIPLIER_H
|
||||
#define LAGRANGEMULTIPLIER_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
struct LagrangeMultiplier {
|
||||
FGColumnVector3 ForceJacobian;
|
||||
FGColumnVector3 LeverArm;
|
||||
double Min;
|
||||
double Max;
|
||||
double value;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
420
src/FDM/JSBSim/models/FGAccelerations.cpp
Normal file
420
src/FDM/JSBSim/models/FGAccelerations.cpp
Normal file
@@ -0,0 +1,420 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGAccelerations.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 07/12/11
|
||||
Purpose: Calculates derivatives of rotational and translational rates, and
|
||||
of the attitude quaternion.
|
||||
Called by: FGFDMExec
|
||||
|
||||
------------- Copyright (C) 2011 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This class encapsulates the calculation of the derivatives of the state vectors
|
||||
UVW and PQR - the translational and rotational rates relative to the planet
|
||||
fixed frame. The derivatives relative to the inertial frame are also calculated
|
||||
as a side effect. Also, the derivative of the attitude quaterion is also
|
||||
calculated.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
07/12/11 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
[1] Stevens and Lewis, "Aircraft Control and Simulation", Second edition (2004)
|
||||
Wiley
|
||||
[2] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
|
||||
NASA-Ames", NASA CR-2497, January 1975
|
||||
[3] Erin Catto, "Iterative Dynamics with Temporal Coherence", February 22, 2005
|
||||
[4] Mark Harris and Robert Lyle, "Spacecraft Gravitational Torques",
|
||||
NASA SP-8024, May 1969
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGAccelerations.h"
|
||||
#include "FGFDMExec.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGAccelerations::FGAccelerations(FGFDMExec* fdmex)
|
||||
: FGModel(fdmex)
|
||||
{
|
||||
Debug(0);
|
||||
Name = "FGAccelerations";
|
||||
gravTorque = false;
|
||||
|
||||
vPQRidot.InitMatrix();
|
||||
vUVWidot.InitMatrix();
|
||||
vUVWdot.InitMatrix();
|
||||
vBodyAccel.InitMatrix();
|
||||
|
||||
bind();
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGAccelerations::~FGAccelerations(void)
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAccelerations::InitModel(void)
|
||||
{
|
||||
if (!FGModel::InitModel()) return false;
|
||||
|
||||
vPQRidot.InitMatrix();
|
||||
vUVWidot.InitMatrix();
|
||||
vUVWdot.InitMatrix();
|
||||
vBodyAccel.InitMatrix();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
/*
|
||||
Purpose: Called on a schedule to calculate derivatives.
|
||||
*/
|
||||
|
||||
bool FGAccelerations::Run(bool Holding)
|
||||
{
|
||||
if (FGModel::Run(Holding)) return true; // Fast return if we have nothing to do ...
|
||||
if (Holding) return false;
|
||||
|
||||
CalculatePQRdot(); // Angular rate derivative
|
||||
CalculateUVWdot(); // Translational rate derivative
|
||||
|
||||
if (!FDMExec->GetHoldDown())
|
||||
CalculateFrictionForces(in.DeltaT * rate); // Update rate derivatives with friction forces
|
||||
|
||||
Debug(2);
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Compute body frame rotational accelerations based on the current body moments
|
||||
//
|
||||
// vPQRdot is the derivative of the absolute angular velocity of the vehicle
|
||||
// (body rate with respect to the ECEF frame), expressed in the body frame,
|
||||
// where the derivative is taken in the body frame.
|
||||
// J is the inertia matrix
|
||||
// Jinv is the inverse inertia matrix
|
||||
// vMoments is the moment vector in the body frame
|
||||
// in.vPQRi is the total inertial angular velocity of the vehicle
|
||||
// expressed in the body frame.
|
||||
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
|
||||
// Second edition (2004), eqn 1.5-16e (page 50)
|
||||
|
||||
void FGAccelerations::CalculatePQRdot(void)
|
||||
{
|
||||
if (gravTorque) {
|
||||
// Compute the gravitational torque
|
||||
// Reference: See Harris and Lyle "Spacecraft Gravitational Torques",
|
||||
// NASA SP-8024 (1969) eqn (2) (page 7)
|
||||
FGColumnVector3 R = in.Ti2b * in.vInertialPosition;
|
||||
double invRadius = 1.0 / R.Magnitude();
|
||||
R *= invRadius;
|
||||
in.Moment += (3.0 * in.vGravAccel.Magnitude() * invRadius) * (R * (in.J * R));
|
||||
}
|
||||
|
||||
// Compute body frame rotational accelerations based on the current body
|
||||
// moments and the total inertial angular velocity expressed in the body
|
||||
// frame.
|
||||
// if (HoldDown && !FDMExec->GetTrimStatus()) {
|
||||
if (FDMExec->GetHoldDown()) {
|
||||
// The rotational acceleration in ECI is calculated so that the rotational
|
||||
// acceleration is zero in the body frame.
|
||||
vPQRdot.InitMatrix();
|
||||
vPQRidot = in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
|
||||
}
|
||||
else {
|
||||
vPQRidot = in.Jinv * (in.Moment - in.vPQRi * (in.J * in.vPQRi));
|
||||
vPQRdot = vPQRidot - in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// This set of calculations results in the body and inertial frame accelerations
|
||||
// being computed.
|
||||
// Compute body and inertial frames accelerations based on the current body
|
||||
// forces including centripetal and Coriolis accelerations for the former.
|
||||
// in.vOmegaPlanet is the Earth angular rate - expressed in the inertial frame -
|
||||
// so it has to be transformed to the body frame. More completely,
|
||||
// in.vOmegaPlanet is the rate of the ECEF frame relative to the Inertial
|
||||
// frame (ECI), expressed in the Inertial frame.
|
||||
// in.Force is the total force on the vehicle in the body frame.
|
||||
// in.vPQR is the vehicle body rate relative to the ECEF frame, expressed
|
||||
// in the body frame.
|
||||
// in.vUVW is the vehicle velocity relative to the ECEF frame, expressed
|
||||
// in the body frame.
|
||||
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
|
||||
// Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
|
||||
|
||||
void FGAccelerations::CalculateUVWdot(void)
|
||||
{
|
||||
if (FDMExec->GetHoldDown() && !FDMExec->GetTrimStatus())
|
||||
vBodyAccel.InitMatrix();
|
||||
else
|
||||
vBodyAccel = in.Force / in.Mass;
|
||||
|
||||
vUVWdot = vBodyAccel - (in.vPQR + 2.0 * (in.Ti2b * in.vOmegaPlanet)) * in.vUVW;
|
||||
|
||||
// Include Centripetal acceleration.
|
||||
vUVWdot -= in.Ti2b * (in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition));
|
||||
|
||||
if (FDMExec->GetHoldDown()) {
|
||||
// The acceleration in ECI is calculated so that the acceleration is zero
|
||||
// in the body frame.
|
||||
vUVWidot = in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition);
|
||||
vUVWdot.InitMatrix();
|
||||
}
|
||||
else {
|
||||
vUVWdot += in.Tec2b * in.vGravAccel;
|
||||
vUVWidot = in.Tb2i * vBodyAccel + in.Tec2i * in.vGravAccel;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAccelerations::SetHoldDown(bool hd)
|
||||
{
|
||||
if (hd) {
|
||||
vUVWidot = in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition);
|
||||
vUVWdot.InitMatrix();
|
||||
vPQRidot = in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
|
||||
vPQRdot.InitMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Computes the contact forces just before integrating the EOM.
|
||||
// This routine is using Lagrange multipliers and the projected Gauss-Seidel
|
||||
// (PGS) method.
|
||||
// Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
|
||||
// February 22, 2005
|
||||
// In JSBSim there is only one rigid body (the aircraft) and there can be
|
||||
// multiple points of contact between the aircraft and the ground. As a
|
||||
// consequence our matrix Jac*M^-1*Jac^T is not sparse and the algorithm
|
||||
// described in Catto's paper has been adapted accordingly.
|
||||
// The friction forces are resolved in the body frame relative to the origin
|
||||
// (Earth center).
|
||||
|
||||
void FGAccelerations::CalculateFrictionForces(double dt)
|
||||
{
|
||||
vector<LagrangeMultiplier*>& multipliers = *in.MultipliersList;
|
||||
size_t n = multipliers.size();
|
||||
|
||||
vFrictionForces.InitMatrix();
|
||||
vFrictionMoments.InitMatrix();
|
||||
|
||||
// If no gears are in contact with the ground then return
|
||||
if (!n) return;
|
||||
|
||||
vector<double> a(n*n); // Will contain Jac*M^-1*Jac^T
|
||||
vector<double> rhs(n);
|
||||
|
||||
// Assemble the linear system of equations
|
||||
for (unsigned int i=0; i < n; i++) {
|
||||
FGColumnVector3 U = multipliers[i]->ForceJacobian;
|
||||
FGColumnVector3 r = multipliers[i]->LeverArm;
|
||||
FGColumnVector3 v1 = U / in.Mass;
|
||||
FGColumnVector3 v2 = in.Jinv * (r*U); // Should be J^-T but J is symmetric and so is J^-1
|
||||
|
||||
for (unsigned int j=0; j < i; j++)
|
||||
a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of Jac^T*M^-1*Jac
|
||||
|
||||
for (unsigned int j=i; j < n; j++) {
|
||||
U = multipliers[j]->ForceJacobian;
|
||||
r = multipliers[j]->LeverArm;
|
||||
a[i*n+j] = DotProduct(U, v1 + v2*r);
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble the RHS member
|
||||
|
||||
// Translation
|
||||
FGColumnVector3 vdot = vUVWdot;
|
||||
if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground
|
||||
vdot += (in.vUVW - in.Tec2b * in.TerrainVelocity) / dt;
|
||||
|
||||
// Rotation
|
||||
FGColumnVector3 wdot = vPQRdot;
|
||||
if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground
|
||||
wdot += (in.vPQR - in.Tec2b * in.TerrainAngularVel) / dt;
|
||||
|
||||
// Prepare the linear system for the Gauss-Seidel algorithm :
|
||||
// 1. Compute the right hand side member 'rhs'
|
||||
// 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
|
||||
// a division computation at each iteration of Gauss-Seidel.
|
||||
for (unsigned int i=0; i < n; i++) {
|
||||
double d = a[i*n+i];
|
||||
FGColumnVector3 U = multipliers[i]->ForceJacobian;
|
||||
FGColumnVector3 r = multipliers[i]->LeverArm;
|
||||
|
||||
rhs[i] = -DotProduct(U, vdot + wdot*r)/d;
|
||||
|
||||
for (unsigned int j=0; j < n; j++)
|
||||
a[i*n+j] /= d;
|
||||
}
|
||||
|
||||
// Resolve the Lagrange multipliers with the projected Gauss-Seidel method
|
||||
for (int iter=0; iter < 50; iter++) {
|
||||
double norm = 0.;
|
||||
|
||||
for (unsigned int i=0; i < n; i++) {
|
||||
double lambda0 = multipliers[i]->value;
|
||||
double dlambda = rhs[i];
|
||||
|
||||
for (unsigned int j=0; j < n; j++)
|
||||
dlambda -= a[i*n+j]*multipliers[j]->value;
|
||||
|
||||
multipliers[i]->value = Constrain(multipliers[i]->Min, lambda0+dlambda, multipliers[i]->Max);
|
||||
dlambda = multipliers[i]->value - lambda0;
|
||||
|
||||
norm += fabs(dlambda);
|
||||
}
|
||||
|
||||
if (norm < 1E-5) break;
|
||||
}
|
||||
|
||||
// Calculate the total friction forces and moments
|
||||
|
||||
for (unsigned int i=0; i< n; i++) {
|
||||
double lambda = multipliers[i]->value;
|
||||
FGColumnVector3 U = multipliers[i]->ForceJacobian;
|
||||
FGColumnVector3 r = multipliers[i]->LeverArm;
|
||||
|
||||
FGColumnVector3 F = lambda * U;
|
||||
vFrictionForces += F;
|
||||
vFrictionMoments += r * F;
|
||||
}
|
||||
|
||||
FGColumnVector3 accel = vFrictionForces / in.Mass;
|
||||
FGColumnVector3 omegadot = in.Jinv * vFrictionMoments;
|
||||
|
||||
vBodyAccel += accel;
|
||||
vUVWdot += accel;
|
||||
vUVWidot += in.Tb2i * accel;
|
||||
vPQRdot += omegadot;
|
||||
vPQRidot += omegadot;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAccelerations::InitializeDerivatives(void)
|
||||
{
|
||||
// Make an initial run and set past values
|
||||
CalculatePQRdot(); // Angular rate derivative
|
||||
CalculateUVWdot(); // Translational rate derivative
|
||||
CalculateFrictionForces(0.); // Update rate derivatives with friction forces
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAccelerations::bind(void)
|
||||
{
|
||||
typedef double (FGAccelerations::*PMF)(int) const;
|
||||
|
||||
PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGAccelerations::GetPQRdot);
|
||||
PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGAccelerations::GetPQRdot);
|
||||
PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGAccelerations::GetPQRdot);
|
||||
|
||||
PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGAccelerations::GetUVWdot);
|
||||
PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGAccelerations::GetUVWdot);
|
||||
PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGAccelerations::GetUVWdot);
|
||||
|
||||
PropertyManager->Tie("accelerations/gravity-ft_sec2", this, &FGAccelerations::GetGravAccelMagnitude);
|
||||
PropertyManager->Tie("simulation/gravitational-torque", &gravTorque);
|
||||
PropertyManager->Tie("forces/fbx-weight-lbs", this, eX, (PMF)&FGAccelerations::GetWeight);
|
||||
PropertyManager->Tie("forces/fby-weight-lbs", this, eY, (PMF)&FGAccelerations::GetWeight);
|
||||
PropertyManager->Tie("forces/fbz-weight-lbs", this, eZ, (PMF)&FGAccelerations::GetWeight);
|
||||
|
||||
PropertyManager->Tie("forces/fbx-total-lbs", this, eX, (PMF)&FGAccelerations::GetForces);
|
||||
PropertyManager->Tie("forces/fby-total-lbs", this, eY, (PMF)&FGAccelerations::GetForces);
|
||||
PropertyManager->Tie("forces/fbz-total-lbs", this, eZ, (PMF)&FGAccelerations::GetForces);
|
||||
PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAccelerations::GetMoments);
|
||||
PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAccelerations::GetMoments);
|
||||
PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAccelerations::GetMoments);
|
||||
|
||||
PropertyManager->Tie("moments/l-gear-lbsft", this, eL, (PMF)&FGAccelerations::GetGroundMoments);
|
||||
PropertyManager->Tie("moments/m-gear-lbsft", this, eM, (PMF)&FGAccelerations::GetGroundMoments);
|
||||
PropertyManager->Tie("moments/n-gear-lbsft", this, eN, (PMF)&FGAccelerations::GetGroundMoments);
|
||||
PropertyManager->Tie("forces/fbx-gear-lbs", this, eX, (PMF)&FGAccelerations::GetGroundForces);
|
||||
PropertyManager->Tie("forces/fby-gear-lbs", this, eY, (PMF)&FGAccelerations::GetGroundForces);
|
||||
PropertyManager->Tie("forces/fbz-gear-lbs", this, eZ, (PMF)&FGAccelerations::GetGroundForces);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGAccelerations::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGAccelerations" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGAccelerations" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 && from == 2) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
383
src/FDM/JSBSim/models/FGAccelerations.h
Normal file
383
src/FDM/JSBSim/models/FGAccelerations.h
Normal file
@@ -0,0 +1,383 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGAccelerations.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 07/12/11
|
||||
|
||||
------------- Copyright (C) 2011 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
07/12/11 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGACCELERATIONS_H
|
||||
#define FGACCELERATIONS_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "models/FGModel.h"
|
||||
#include "math/FGColumnVector3.h"
|
||||
#include "math/LagrangeMultiplier.h"
|
||||
#include "math/FGMatrix33.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Handles the calculation of accelerations.
|
||||
|
||||
- Calculate the angular accelerations
|
||||
- Calculate the translational accelerations
|
||||
|
||||
This class is collecting all the forces and the moments acting on the body
|
||||
to calculate the corresponding accelerations according to Newton's second
|
||||
law. This is also where the friction forces related to the ground reactions
|
||||
are evaluated.
|
||||
|
||||
JSBSim provides several ways to calculate the influence of the gravity on
|
||||
the vehicle. The different options can be selected via the following
|
||||
properties :
|
||||
@property simulation/gravity-model (read/write) Selects the gravity model.
|
||||
Two options are available : 0 (Standard gravity assuming the Earth
|
||||
is spherical) or 1 (WGS84 gravity taking the Earth oblateness into
|
||||
account). WGS84 gravity is the default.
|
||||
@property simulation/gravitational-torque (read/write) Enables/disables the
|
||||
calculations of the gravitational torque on the vehicle. This is
|
||||
mainly relevant for spacecrafts that are orbiting at low altitudes.
|
||||
Gravitational torque calculations are disabled by default.
|
||||
|
||||
Special care is taken in the calculations to obtain maximum fidelity in
|
||||
JSBSim results. In FGAccelerations, this is obtained by avoiding as much as
|
||||
possible the transformations from one frame to another. As a consequence,
|
||||
the frames in which the accelerations are primarily evaluated are dictated
|
||||
by the frames in which FGPropagate resolves the equations of movement (the
|
||||
ECI frame for the translations and the body frame for the rotations).
|
||||
|
||||
@see Mark Harris and Robert Lyle, "Spacecraft Gravitational Torques",
|
||||
NASA SP-8024, May 1969
|
||||
|
||||
@author Jon S. Berndt, Mathias Froehlich, Bertrand Coconnier
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGAccelerations : public FGModel {
|
||||
public:
|
||||
/** Constructor.
|
||||
@param Executive a pointer to the parent executive object */
|
||||
explicit FGAccelerations(FGFDMExec* Executive);
|
||||
|
||||
/// Destructor
|
||||
~FGAccelerations();
|
||||
|
||||
/** Initializes the FGAccelerations class after instantiation and prior to first execution.
|
||||
The base class FGModel::InitModel is called first, initializing pointers to the
|
||||
other FGModel objects (and others). */
|
||||
bool InitModel(void) override;
|
||||
|
||||
/** Runs the state propagation model; called by the Executive
|
||||
Can pass in a value indicating if the executive is directing the simulation to Hold.
|
||||
@param Holding if true, the executive has been directed to hold the sim from
|
||||
advancing time. Some models may ignore this flag, such as the Input
|
||||
model, which may need to be active to listen on a socket for the
|
||||
"Resume" command to be given.
|
||||
@return false if no error */
|
||||
bool Run(bool Holding) override;
|
||||
|
||||
/** Retrieves the body axis acceleration.
|
||||
Retrieves the computed body axis accelerations based on the
|
||||
applied forces and accounting for a rotating body frame.
|
||||
The vector returned is represented by an FGColumnVector3 reference. The vector
|
||||
for the acceleration in Body frame is organized (Ax, Ay, Az). The vector
|
||||
is 1-based, so that the first element can be retrieved using the "()" operator.
|
||||
In other words, vUVWdot(1) is Ax. Various convenience enumerators are defined
|
||||
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
|
||||
eX=1, eY=2, eZ=3.
|
||||
units ft/sec^2
|
||||
@return Body axis translational acceleration in ft/sec^2.
|
||||
*/
|
||||
const FGColumnVector3& GetUVWdot(void) const { return vUVWdot; }
|
||||
|
||||
/** Retrieves the body axis acceleration in the ECI frame.
|
||||
Retrieves the computed body axis accelerations based on the applied forces.
|
||||
The ECI frame being an inertial frame this vector does not contain the
|
||||
Coriolis and centripetal accelerations. The vector is expressed in the
|
||||
Body frame.
|
||||
The vector returned is represented by an FGColumnVector3 reference. The
|
||||
vector for the acceleration in Body frame is organized (Aix, Aiy, Aiz). The
|
||||
vector is 1-based, so that the first element can be retrieved using the
|
||||
"()" operator. In other words, vUVWidot(1) is Aix. Various convenience
|
||||
enumerators are defined in FGJSBBase. The relevant enumerators for the
|
||||
vector returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units ft/sec^2
|
||||
@return Body axis translational acceleration in ft/sec^2.
|
||||
*/
|
||||
const FGColumnVector3& GetUVWidot(void) const { return vUVWidot; }
|
||||
|
||||
/** Retrieves the body axis angular acceleration vector.
|
||||
Retrieves the body axis angular acceleration vector in rad/sec^2. The
|
||||
angular acceleration vector is determined from the applied moments and
|
||||
accounts for a rotating frame.
|
||||
The vector returned is represented by an FGColumnVector3 reference. The vector
|
||||
for the angular acceleration in Body frame is organized (Pdot, Qdot, Rdot). The vector
|
||||
is 1-based, so that the first element can be retrieved using the "()" operator.
|
||||
In other words, vPQRdot(1) is Pdot. Various convenience enumerators are defined
|
||||
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
|
||||
eP=1, eQ=2, eR=3.
|
||||
units rad/sec^2
|
||||
@return The angular acceleration vector.
|
||||
*/
|
||||
const FGColumnVector3& GetPQRdot(void) const {return vPQRdot;}
|
||||
|
||||
/** Retrieves the axis angular acceleration vector in the ECI frame.
|
||||
Retrieves the body axis angular acceleration vector measured in the ECI
|
||||
frame and expressed in the body frame. The angular acceleration vector is
|
||||
determined from the applied moments.
|
||||
The vector returned is represented by an FGColumnVector3 reference. The
|
||||
vector for the angular acceleration in Body frame is organized (Pidot,
|
||||
Qidot, Ridot). The vector is 1-based, so that the first element can be
|
||||
retrieved using the "()" operator. In other words, vPQRidot(1) is Pidot.
|
||||
Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the vector returned by this call are, eP=1, eQ=2, eR=3.
|
||||
units rad/sec^2
|
||||
@return The angular acceleration vector.
|
||||
*/
|
||||
const FGColumnVector3& GetPQRidot(void) const {return vPQRidot;}
|
||||
|
||||
/** Retrieves a body frame acceleration component.
|
||||
Retrieves a body frame acceleration component. The acceleration returned
|
||||
is extracted from the vUVWdot vector (an FGColumnVector3). The vector for
|
||||
the acceleration in Body frame is organized (Ax, Ay, Az). The vector is
|
||||
1-based. In other words, GetUVWdot(1) returns Ax. Various convenience
|
||||
enumerators are defined in FGJSBBase. The relevant enumerators for the
|
||||
acceleration returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units ft/sec^2
|
||||
@param idx the index of the acceleration component desired (1-based).
|
||||
@return The body frame acceleration component.
|
||||
*/
|
||||
double GetUVWdot(int idx) const { return vUVWdot(idx); }
|
||||
|
||||
/** Retrieves the acceleration resulting from the applied forces.
|
||||
Retrieves the ratio of the sum of all forces applied on the craft to its
|
||||
mass. This does include the friction forces but not the gravity.
|
||||
The vector returned is represented by an FGColumnVector3 reference. The
|
||||
vector for the acceleration in Body frame is organized (Ax, Ay, Az). The
|
||||
vector is 1-based, so that the first element can be retrieved using the
|
||||
"()" operator. In other words, vBodyAccel(1) is Ax. Various convenience
|
||||
enumerators are defined in FGJSBBase. The relevant enumerators for the
|
||||
vector returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units ft/sec^2
|
||||
@return The acceleration resulting from the applied forces.
|
||||
*/
|
||||
const FGColumnVector3& GetBodyAccel(void) const { return vBodyAccel; }
|
||||
|
||||
double GetGravAccelMagnitude(void) const { return in.vGravAccel.Magnitude(); }
|
||||
|
||||
/** Retrieves a component of the acceleration resulting from the applied forces.
|
||||
Retrieves a component of the ratio between the sum of all forces applied
|
||||
on the craft to its mass. The value returned is extracted from the vBodyAccel
|
||||
vector (an FGColumnVector3). The vector for the acceleration in Body frame
|
||||
is organized (Ax, Ay, Az). The vector is 1-based. In other words,
|
||||
GetBodyAccel(1) returns Ax. Various convenience enumerators are defined
|
||||
in FGJSBBase. The relevant enumerators for the vector returned by this
|
||||
call are, eX=1, eY=2, eZ=3.
|
||||
units ft/sec^2
|
||||
@param idx the index of the acceleration component desired (1-based).
|
||||
@return The component of the acceleration resulting from the applied forces.
|
||||
*/
|
||||
double GetBodyAccel(int idx) const { return vBodyAccel(idx); }
|
||||
|
||||
/** Retrieves a body frame angular acceleration component.
|
||||
Retrieves a body frame angular acceleration component. The angular
|
||||
acceleration returned is extracted from the vPQRdot vector (an
|
||||
FGColumnVector3). The vector for the angular acceleration in Body frame
|
||||
is organized (Pdot, Qdot, Rdot). The vector is 1-based. In other words,
|
||||
GetPQRdot(1) returns Pdot (roll acceleration). Various convenience
|
||||
enumerators are defined in FGJSBBase. The relevant enumerators for the
|
||||
angular acceleration returned by this call are, eP=1, eQ=2, eR=3.
|
||||
units rad/sec^2
|
||||
@param axis the index of the angular acceleration component desired (1-based).
|
||||
@return The body frame angular acceleration component.
|
||||
*/
|
||||
double GetPQRdot(int axis) const {return vPQRdot(axis);}
|
||||
|
||||
/** Retrieves a component of the total moments applied on the body.
|
||||
Retrieves a component of the total moments applied on the body. This does
|
||||
include the moments generated by friction forces and the gravitational
|
||||
torque (if the property \e simulation/gravitational-torque is set to true).
|
||||
The vector for the total moments in the body frame is organized (Mx, My
|
||||
, Mz). The vector is 1-based. In other words, GetMoments(1) returns Mx.
|
||||
Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the moments returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units lbs*ft
|
||||
@param idx the index of the moments component desired (1-based).
|
||||
@return The total moments applied on the body.
|
||||
*/
|
||||
double GetMoments(int idx) const { return in.Moment(idx) + vFrictionMoments(idx); }
|
||||
FGColumnVector3 GetMoments(void) const { return in.Moment + vFrictionMoments; }
|
||||
|
||||
/** Retrieves the total forces applied on the body.
|
||||
Retrieves the total forces applied on the body. This does include the
|
||||
friction forces but not the gravity.
|
||||
The vector for the total forces in the body frame is organized (Fx, Fy
|
||||
, Fz). The vector is 1-based. In other words, GetForces(1) returns Fx.
|
||||
Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the forces returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units lbs
|
||||
@param idx the index of the forces component desired (1-based).
|
||||
@return The total forces applied on the body.
|
||||
*/
|
||||
double GetForces(int idx) const { return in.Force(idx) + vFrictionForces(idx); }
|
||||
FGColumnVector3 GetForces(void) const { return in.Force + vFrictionForces; }
|
||||
|
||||
/** Retrieves the ground moments applied on the body.
|
||||
Retrieves the ground moments applied on the body. This does include the
|
||||
ground normal reaction and friction moments.
|
||||
The vector for the ground moments in the body frame is organized (Mx, My
|
||||
, Mz). The vector is 1-based. In other words, GetGroundMoments(1) returns
|
||||
Mx. Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the moments returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units lbs*ft
|
||||
@param idx the index of the moments component desired (1-based).
|
||||
@return The ground moments applied on the body.
|
||||
*/
|
||||
double GetGroundMoments(int idx) const { return in.GroundMoment(idx) + vFrictionMoments(idx); }
|
||||
FGColumnVector3 GetGroundMoments(void) const { return in.GroundMoment + vFrictionMoments; }
|
||||
|
||||
/** Retrieves the ground forces applied on the body.
|
||||
Retrieves the ground forces applied on the body. This does include the
|
||||
ground normal reaction and friction forces.
|
||||
The vector for the ground forces in the body frame is organized (Fx, Fy
|
||||
, Fz). The vector is 1-based. In other words, GetGroundForces(1) returns
|
||||
Fx. Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the forces returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units lbs.
|
||||
@param idx the index of the forces component desired (1-based).
|
||||
@return The ground forces applied on the body.
|
||||
*/
|
||||
double GetGroundForces(int idx) const { return in.GroundForce(idx) + vFrictionForces(idx); }
|
||||
FGColumnVector3 GetGroundForces(void) const { return in.GroundForce + vFrictionForces; }
|
||||
|
||||
/** Retrieves the weight applied on the body.
|
||||
Retrieves the weight applied on the body i.e. the force that results from
|
||||
the gravity applied to the body mass.
|
||||
The vector for the weight forces in the body frame is organized (Fx, Fy ,
|
||||
Fz). The vector is 1-based. In other words, GetWeight(1) returns
|
||||
Fx. Various convenience enumerators are defined in FGJSBBase. The relevant
|
||||
enumerators for the forces returned by this call are, eX=1, eY=2, eZ=3.
|
||||
units lbs.
|
||||
@param idx the index of the forces component desired (1-based).
|
||||
@return The ground forces applied on the body.
|
||||
*/
|
||||
double GetWeight(int idx) const { return in.Mass * (in.Tec2b * in.vGravAccel)(idx); }
|
||||
FGColumnVector3 GetWeight(void) const { return in.Mass * in.Tec2b * in.vGravAccel; }
|
||||
|
||||
/** Initializes the FGAccelerations class prior to a new execution.
|
||||
Initializes the class prior to a new execution when the input data stored
|
||||
in the Inputs structure have been set to their initial values.
|
||||
*/
|
||||
void InitializeDerivatives(void);
|
||||
|
||||
/** Sets the property forces/hold-down. This allows to do hard 'hold-down'
|
||||
such as for rockets on a launch pad with engines ignited.
|
||||
@param hd enables the 'hold-down' function if non-zero
|
||||
*/
|
||||
void SetHoldDown(bool hd);
|
||||
|
||||
struct Inputs {
|
||||
/// The body inertia matrix expressed in the body frame
|
||||
FGMatrix33 J;
|
||||
/// The inverse of the inertia matrix J
|
||||
FGMatrix33 Jinv;
|
||||
/// Transformation matrix from the ECI to the Body frame
|
||||
FGMatrix33 Ti2b;
|
||||
/// Transformation matrix from the Body to the ECI frame
|
||||
FGMatrix33 Tb2i;
|
||||
/// Transformation matrix from the ECEF to the Body frame
|
||||
FGMatrix33 Tec2b;
|
||||
/// Transformation matrix from the ECEF to the ECI frame
|
||||
FGMatrix33 Tec2i;
|
||||
/// Total moments applied to the body except friction and gravity (expressed in the body frame)
|
||||
FGColumnVector3 Moment;
|
||||
/// Moments generated by the ground normal reactions expressed in the body frame. Does not account for friction.
|
||||
FGColumnVector3 GroundMoment;
|
||||
/// Total forces applied to the body except friction and gravity (expressed in the body frame)
|
||||
FGColumnVector3 Force;
|
||||
/// Forces generated by the ground normal reactions expressed in the body frame. Does not account for friction.
|
||||
FGColumnVector3 GroundForce;
|
||||
/// Gravity intensity vector (expressed in the ECEF frame).
|
||||
FGColumnVector3 vGravAccel;
|
||||
/// Angular velocities of the body with respect to the ECI frame (expressed in the body frame).
|
||||
FGColumnVector3 vPQRi;
|
||||
/// Angular velocities of the body with respect to the local frame (expressed in the body frame).
|
||||
FGColumnVector3 vPQR;
|
||||
/// Velocities of the body with respect to the local frame (expressed in the body frame).
|
||||
FGColumnVector3 vUVW;
|
||||
/// Body position (X,Y,Z) measured in the ECI frame.
|
||||
FGColumnVector3 vInertialPosition;
|
||||
/// Earth rotating vector (expressed in the ECI frame).
|
||||
FGColumnVector3 vOmegaPlanet;
|
||||
/// Terrain velocities with respect to the local frame (expressed in the ECEF frame).
|
||||
FGColumnVector3 TerrainVelocity;
|
||||
/// Terrain angular velocities with respect to the local frame (expressed in the ECEF frame).
|
||||
FGColumnVector3 TerrainAngularVel;
|
||||
/// Time step
|
||||
double DeltaT;
|
||||
/// Body mass
|
||||
double Mass;
|
||||
/// List of Lagrange multipliers set by FGLGear for friction forces calculations.
|
||||
std::vector<LagrangeMultiplier*> *MultipliersList;
|
||||
} in;
|
||||
|
||||
private:
|
||||
|
||||
FGColumnVector3 vPQRdot, vPQRidot;
|
||||
FGColumnVector3 vUVWdot, vUVWidot;
|
||||
FGColumnVector3 vBodyAccel;
|
||||
FGColumnVector3 vFrictionForces;
|
||||
FGColumnVector3 vFrictionMoments;
|
||||
|
||||
bool gravTorque;
|
||||
|
||||
void CalculatePQRdot(void);
|
||||
void CalculateUVWdot(void);
|
||||
|
||||
void CalculateFrictionForces(double dt);
|
||||
|
||||
void bind(void);
|
||||
void Debug(int from) override;
|
||||
};
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
707
src/FDM/JSBSim/models/FGAerodynamics.cpp
Normal file
707
src/FDM/JSBSim/models/FGAerodynamics.cpp
Normal file
@@ -0,0 +1,707 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGAerodynamics.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 09/13/00
|
||||
Purpose: Encapsulates the aerodynamic forces
|
||||
|
||||
------------- 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/13/00 JSB Created
|
||||
04/22/01 JSB Moved code into here from FGAircraft
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGAerodynamics.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
|
||||
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["AXIAL"] = 0;
|
||||
AxisIdx["NORMAL"] = 2;
|
||||
|
||||
AxisIdx["X"] = 0;
|
||||
AxisIdx["Y"] = 1;
|
||||
AxisIdx["Z"] = 2;
|
||||
|
||||
forceAxisType = atNone;
|
||||
momentAxisType = atNone;
|
||||
|
||||
AeroFunctions = new AeroFunctionArray[6];
|
||||
AeroFunctionsAtCG = new AeroFunctionArray[6];
|
||||
|
||||
impending_stall = stall_hyst = 0.0;
|
||||
alphaclmin = alphaclmax = 0.0;
|
||||
alphaclmin0 = alphaclmax0 = 0.0;
|
||||
alphahystmin = alphahystmax = 0.0;
|
||||
clsq = lod = 0.0;
|
||||
alphaw = 0.0;
|
||||
bi2vel = ci2vel = 0.0;
|
||||
AeroRPShift = 0;
|
||||
vDeltaRP.InitMatrix();
|
||||
|
||||
bind();
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGAerodynamics::~FGAerodynamics()
|
||||
{
|
||||
unsigned int i,j;
|
||||
|
||||
for (i=0; i<6; i++)
|
||||
for (j=0; j<AeroFunctions[i].size(); j++)
|
||||
delete AeroFunctions[i][j];
|
||||
for (i=0; i<6; i++)
|
||||
for (j=0; j<AeroFunctionsAtCG[i].size(); j++)
|
||||
delete AeroFunctionsAtCG[i][j];
|
||||
|
||||
delete[] AeroFunctions;
|
||||
delete[] AeroFunctionsAtCG;
|
||||
|
||||
delete AeroRPShift;
|
||||
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAerodynamics::InitModel(void)
|
||||
{
|
||||
if (!FGModel::InitModel()) return false;
|
||||
|
||||
impending_stall = stall_hyst = 0.0;
|
||||
alphaclmin = alphaclmin0;
|
||||
alphaclmax = alphaclmax0;
|
||||
alphahystmin = alphahystmax = 0.0;
|
||||
clsq = lod = 0.0;
|
||||
alphaw = 0.0;
|
||||
bi2vel = ci2vel = 0.0;
|
||||
AeroRPShift = 0;
|
||||
vDeltaRP.InitMatrix();
|
||||
vForces.InitMatrix();
|
||||
vMoments.InitMatrix();
|
||||
return true;
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAerodynamics::Run(bool Holding)
|
||||
{
|
||||
|
||||
if (FGModel::Run(Holding)) return true;
|
||||
if (Holding) return false; // if paused don't execute
|
||||
|
||||
unsigned int axis_ctr;
|
||||
const double twovel=2*in.Vt;
|
||||
|
||||
// The lift coefficient squared (property aero/cl-squared) is computed before
|
||||
// the aero functions are called to make sure that they use the same value for
|
||||
// qbar.
|
||||
if ( in.Qbar > 1.0) {
|
||||
// Skip the computation if qbar is close to zero to avoid huge values for
|
||||
// aero/cl-squared when a non-null lift coincides with a very small aero
|
||||
// velocity (i.e. when qbar is close to zero).
|
||||
clsq = vFw(eLift) / (in.Wingarea*in.Qbar);
|
||||
clsq *= clsq;
|
||||
}
|
||||
|
||||
RunPreFunctions();
|
||||
|
||||
// calculate some oft-used quantities for speed
|
||||
|
||||
if (twovel != 0) {
|
||||
bi2vel = in.Wingspan / twovel;
|
||||
ci2vel = in.Wingchord / twovel;
|
||||
}
|
||||
alphaw = in.Alpha + in.Wingincidence;
|
||||
qbar_area = in.Wingarea * in.Qbar;
|
||||
|
||||
if (alphaclmax != 0) {
|
||||
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 (in.Alpha > alphahystmax) {
|
||||
stall_hyst = 1;
|
||||
} else if (in.Alpha < alphahystmin) {
|
||||
stall_hyst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vFw.InitMatrix();
|
||||
vFnative.InitMatrix();
|
||||
vFnativeAtCG.InitMatrix();
|
||||
|
||||
BuildStabilityTransformMatrices();
|
||||
|
||||
for (axis_ctr = 0; axis_ctr < 3; ++axis_ctr) {
|
||||
AeroFunctionArray::iterator f;
|
||||
|
||||
AeroFunctionArray* array = &AeroFunctions[axis_ctr];
|
||||
for (f=array->begin(); f != array->end(); ++f) {
|
||||
// Tell the Functions to cache values, so when the function values are
|
||||
// being requested for output, the functions do not get calculated again
|
||||
// in a context that might have changed, but instead use the values that
|
||||
// have already been calculated for this frame.
|
||||
(*f)->cacheValue(true);
|
||||
vFnative(axis_ctr+1) += (*f)->GetValue();
|
||||
}
|
||||
|
||||
array = &AeroFunctionsAtCG[axis_ctr];
|
||||
for (f=array->begin(); f != array->end(); ++f) {
|
||||
(*f)->cacheValue(true); // Same as above
|
||||
vFnativeAtCG(axis_ctr+1) += (*f)->GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
switch (forceAxisType) {
|
||||
case atBodyXYZ: // Forces already in body axes; no manipulation needed
|
||||
vForces = vFnative;
|
||||
vForcesAtCG = vFnativeAtCG;
|
||||
break;
|
||||
case atWind: // Copy forces into wind axes
|
||||
vFnative(eDrag)*=-1; vFnative(eLift)*=-1;
|
||||
vForces = in.Tw2b*vFnative;
|
||||
|
||||
vFnativeAtCG(eDrag)*=-1; vFnativeAtCG(eLift)*=-1;
|
||||
vForcesAtCG = in.Tw2b*vFnativeAtCG;
|
||||
break;
|
||||
case atBodyAxialNormal: // Convert native forces into Axial|Normal|Side system
|
||||
vFnative(eX)*=-1; vFnative(eZ)*=-1;
|
||||
vForces = vFnative;
|
||||
|
||||
vFnativeAtCG(eX)*=-1; vFnativeAtCG(eZ)*=-1;
|
||||
vForcesAtCG = vFnativeAtCG;
|
||||
break;
|
||||
case atStability: // Convert from stability axes to both body and wind axes
|
||||
vFnative(eDrag) *= -1; vFnative(eLift) *= -1;
|
||||
vForces = Ts2b*vFnative;
|
||||
|
||||
vFnativeAtCG(eDrag) *= -1; vFnativeAtCG(eLift) *= -1;
|
||||
vForcesAtCG = Ts2b*vFnativeAtCG;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
stringstream s;
|
||||
s << " A proper axis type has NOT been selected. Check "
|
||||
<< "your aerodynamics definition.";
|
||||
cerr << endl << s.str() << endl;
|
||||
throw BaseException(s.str());
|
||||
}
|
||||
}
|
||||
// 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(eX) = in.RPBody(eX) - vDeltaRP(eX); // vDeltaRP is given in the
|
||||
vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY); // structural frame.
|
||||
vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ);
|
||||
|
||||
vMomentsMRC.InitMatrix();
|
||||
|
||||
for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
|
||||
AeroFunctionArray* array = &AeroFunctions[axis_ctr+3];
|
||||
for (AeroFunctionArray::iterator f=array->begin(); f != array->end(); ++f) {
|
||||
// Tell the Functions to cache values, so when the function values are
|
||||
// being requested for output, the functions do not get calculated again
|
||||
// in a context that might have changed, but instead use the values that
|
||||
// have already been calculated for this frame.
|
||||
(*f)->cacheValue(true);
|
||||
vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
// Transform moments to bodyXYZ if the moments are specified in stability or
|
||||
// wind axes
|
||||
vMomentsMRCBodyXYZ.InitMatrix();
|
||||
switch (momentAxisType) {
|
||||
case atBodyXYZ:
|
||||
vMomentsMRCBodyXYZ = vMomentsMRC;
|
||||
break;
|
||||
case atStability:
|
||||
vMomentsMRCBodyXYZ = Ts2b*vMomentsMRC;
|
||||
break;
|
||||
case atWind:
|
||||
vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
stringstream s;
|
||||
s << " A proper axis type has NOT been selected. Check "
|
||||
<< "your aerodynamics definition.";
|
||||
cerr << endl << s.str() << endl;
|
||||
throw BaseException(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
vMoments = vMomentsMRCBodyXYZ + vDXYZcg*vForces; // M = r X F
|
||||
|
||||
// Now add the "at CG" values to base forces - after the moments have been
|
||||
// transferred.
|
||||
vForces += vForcesAtCG;
|
||||
|
||||
// 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.
|
||||
//
|
||||
// JSB 4/27/12 - After use, convert wind axes to produce normal lift and drag
|
||||
// values - not negative ones!
|
||||
//
|
||||
// As a clarification, JSBSim assumes that drag and lift values are defined in
|
||||
// wind axes - BUT with a 180 rotation about the Y axis. That is, lift and
|
||||
// drag will be positive up and aft, respectively, so that they are reported
|
||||
// as positive numbers. However, the wind axes themselves assume that the X
|
||||
// and Z forces are positive forward and down. Same applies to the stability
|
||||
// axes.
|
||||
vFw = in.Tb2w * vForces;
|
||||
vFw(eDrag) *= -1; vFw(eLift) *= -1;
|
||||
|
||||
// Calculate Lift over Drag
|
||||
if ( fabs(vFw(eDrag)) > 0.0)
|
||||
lod = fabs( vFw(eLift) / vFw(eDrag));
|
||||
|
||||
RunPostFunctions();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGColumnVector3 FGAerodynamics::GetForcesInStabilityAxes(void) const
|
||||
{
|
||||
FGColumnVector3 vFs = Tb2s*vForces;
|
||||
// Need sign flips since drag is positive and lift is positive in stability axes
|
||||
vFs(eDrag) *= -1; vFs(eLift) *= -1;
|
||||
|
||||
return vFs;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAerodynamics::Load(Element *document)
|
||||
{
|
||||
string axis;
|
||||
string scratch_unit="";
|
||||
Element *temp_element, *axis_element, *function_element;
|
||||
|
||||
Name = "Aerodynamics Model: " + document->GetAttributeValue("name");
|
||||
|
||||
// Perform base class Pre-Load
|
||||
if (!FGModel::Upload(document, true))
|
||||
return false;
|
||||
|
||||
DetermineAxisSystem(document); // Determine if Lift/Side/Drag, etc. is used.
|
||||
|
||||
Debug(2);
|
||||
|
||||
if ((temp_element = document->FindElement("alphalimits"))) {
|
||||
scratch_unit = temp_element->GetAttributeValue("unit");
|
||||
if (scratch_unit.empty()) scratch_unit = "RAD";
|
||||
alphaclmin0 = temp_element->FindElementValueAsNumberConvertFromTo("min", scratch_unit, "RAD");
|
||||
alphaclmax0 = temp_element->FindElementValueAsNumberConvertFromTo("max", scratch_unit, "RAD");
|
||||
alphaclmin = alphaclmin0;
|
||||
alphaclmax = alphaclmax0;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if ((temp_element = document->FindElement("aero_ref_pt_shift_x"))) {
|
||||
function_element = temp_element->FindElement("function");
|
||||
AeroRPShift = new FGFunction(FDMExec, function_element);
|
||||
}
|
||||
|
||||
axis_element = document->FindElement("axis");
|
||||
while (axis_element) {
|
||||
AeroFunctionArray ca;
|
||||
AeroFunctionArray ca_atCG;
|
||||
axis = axis_element->GetAttributeValue("name");
|
||||
function_element = axis_element->FindElement("function");
|
||||
while (function_element) {
|
||||
string current_func_name = function_element->GetAttributeValue("name");
|
||||
bool apply_at_cg = false;
|
||||
if (function_element->HasAttribute("apply_at_cg")) {
|
||||
if (function_element->GetAttributeValue("apply_at_cg") == "true") apply_at_cg = true;
|
||||
}
|
||||
if (!apply_at_cg) {
|
||||
try {
|
||||
ca.push_back( new FGFunction(FDMExec, function_element) );
|
||||
} catch (const string& str) {
|
||||
cerr << endl << axis_element->ReadFrom()
|
||||
<< endl << fgred << "Error loading aerodynamic function in "
|
||||
<< current_func_name << ":" << str << " Aborting." << reset << endl;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
ca_atCG.push_back( new FGFunction(FDMExec, function_element) );
|
||||
} catch (const string& str) {
|
||||
cerr << endl << axis_element->ReadFrom()
|
||||
<< endl << fgred << "Error loading aerodynamic function in "
|
||||
<< current_func_name << ":" << str << " Aborting." << reset << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function_element = axis_element->FindNextElement("function");
|
||||
}
|
||||
AeroFunctions[AxisIdx[axis]] = ca;
|
||||
AeroFunctionsAtCG[AxisIdx[axis]] = ca_atCG;
|
||||
axis_element = document->FindNextElement("axis");
|
||||
}
|
||||
|
||||
PostLoad(document, FDMExec); // 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.
|
||||
// Alternatively the axis name 'X|Y|Z or ROLL|PITCH|YAW' can be specified in
|
||||
// conjunction with a frame 'BODY|STABILITY|WIND', for example:
|
||||
// <axis name="X" frame="STABILITY"/>
|
||||
|
||||
void FGAerodynamics::DetermineAxisSystem(Element* document)
|
||||
{
|
||||
Element* axis_element = document->FindElement("axis");
|
||||
string axis;
|
||||
while (axis_element) {
|
||||
axis = axis_element->GetAttributeValue("name");
|
||||
string frame = axis_element->GetAttributeValue("frame");
|
||||
if (axis == "X" || axis == "Y" || axis == "Z") {
|
||||
ProcessAxesNameAndFrame(forceAxisType, axis, frame, axis_element,
|
||||
"(X Y Z)");
|
||||
} else if (axis == "ROLL" || axis == "PITCH" || axis == "YAW") {
|
||||
ProcessAxesNameAndFrame(momentAxisType, axis, frame, axis_element,
|
||||
"(ROLL PITCH YAW)");
|
||||
} else if (axis == "LIFT" || axis == "DRAG") {
|
||||
if (forceAxisType == atNone) forceAxisType = atWind;
|
||||
else if (forceAxisType != atWind) {
|
||||
cerr << endl << axis_element->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the"
|
||||
<< " aircraft config file. (LIFT DRAG)" << endl;
|
||||
}
|
||||
} else if (axis == "SIDE") {
|
||||
if (forceAxisType != atNone && forceAxisType != atWind && forceAxisType != atBodyAxialNormal) {
|
||||
cerr << endl << axis_element->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the"
|
||||
<< " aircraft config file. (SIDE)" << endl;
|
||||
}
|
||||
} else if (axis == "AXIAL" || axis == "NORMAL") {
|
||||
if (forceAxisType == atNone) forceAxisType = atBodyAxialNormal;
|
||||
else if (forceAxisType != atBodyAxialNormal) {
|
||||
cerr << endl << axis_element->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the"
|
||||
<< " aircraft config file. (NORMAL AXIAL)" << endl;
|
||||
}
|
||||
} else { // error
|
||||
stringstream s;
|
||||
s << axis_element->ReadFrom()
|
||||
<< endl << " An unknown axis type, " << axis << " has been specified"
|
||||
<< " in the aircraft configuration file.";
|
||||
cerr << endl << s.str() << endl;
|
||||
throw BaseException(s.str());
|
||||
}
|
||||
axis_element = document->FindNextElement("axis");
|
||||
}
|
||||
|
||||
if (forceAxisType == atNone) {
|
||||
forceAxisType = atWind;
|
||||
cerr << endl << " The aerodynamic axis system has been set by default"
|
||||
<< " to the Lift/Side/Drag system." << endl;
|
||||
}
|
||||
if (momentAxisType == atNone) {
|
||||
momentAxisType = atBodyXYZ;
|
||||
cerr << endl << " The aerodynamic moment axis system has been set by default"
|
||||
<< " to the bodyXYZ system." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType, const string& name,
|
||||
const string& frame, Element* el,
|
||||
const string& validNames)
|
||||
{
|
||||
if (frame == "BODY" || frame.empty()) {
|
||||
if (axisType == atNone) axisType = atBodyXYZ;
|
||||
else if (axisType != atBodyXYZ)
|
||||
cerr << endl << el->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the "
|
||||
<< " aircraft config file." << validNames << " - BODY" << endl;
|
||||
}
|
||||
else if (frame == "STABILITY") {
|
||||
if (axisType == atNone) axisType = atStability;
|
||||
else if (axisType != atStability)
|
||||
cerr << endl << el->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the "
|
||||
<< " aircraft config file." << validNames << " - STABILITY" << endl;
|
||||
}
|
||||
else if (frame == "WIND") {
|
||||
if (axisType == atNone) axisType = atWind;
|
||||
else if (axisType != atWind)
|
||||
cerr << endl << el->ReadFrom()
|
||||
<< endl << " Mixed aerodynamic axis systems have been used in the "
|
||||
<< " aircraft config file." << validNames << " - WIND" << endl;
|
||||
}
|
||||
else {
|
||||
stringstream s;
|
||||
s << " Unknown axis frame type of - " << frame;
|
||||
cerr << endl << s.str() << endl;
|
||||
throw BaseException(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGAerodynamics::GetAeroFunctionStrings(const string& delimeter) const
|
||||
{
|
||||
string AeroFunctionStrings = "";
|
||||
bool firstime = true;
|
||||
unsigned int axis, sd;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
string FunctionStrings = FGModelFunctions::GetFunctionStrings(delimeter);
|
||||
|
||||
if (FunctionStrings.size() > 0) {
|
||||
if (AeroFunctionStrings.size() > 0) {
|
||||
AeroFunctionStrings += delimeter + FunctionStrings;
|
||||
} else {
|
||||
AeroFunctionStrings = FunctionStrings;
|
||||
}
|
||||
}
|
||||
|
||||
return AeroFunctionStrings;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
string FGAerodynamics::GetAeroFunctionValues(const string& delimeter) const
|
||||
{
|
||||
ostringstream buf;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
string FunctionValues = FGModelFunctions::GetFunctionValues(delimeter);
|
||||
|
||||
if (FunctionValues.size() > 0) {
|
||||
if (buf.str().size() > 0) {
|
||||
buf << delimeter << FunctionValues;
|
||||
} else {
|
||||
buf << FunctionValues;
|
||||
}
|
||||
}
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAerodynamics::bind(void)
|
||||
{
|
||||
typedef double (FGAerodynamics::*PMF)(int) const;
|
||||
|
||||
PropertyManager->Tie("forces/fbx-aero-lbs", this, eX, (PMF)&FGAerodynamics::GetForces);
|
||||
PropertyManager->Tie("forces/fby-aero-lbs", this, eY, (PMF)&FGAerodynamics::GetForces);
|
||||
PropertyManager->Tie("forces/fbz-aero-lbs", this, eZ, (PMF)&FGAerodynamics::GetForces);
|
||||
PropertyManager->Tie("moments/l-aero-lbsft", this, eL, (PMF)&FGAerodynamics::GetMoments);
|
||||
PropertyManager->Tie("moments/m-aero-lbsft", this, eM, (PMF)&FGAerodynamics::GetMoments);
|
||||
PropertyManager->Tie("moments/n-aero-lbsft", this, eN, (PMF)&FGAerodynamics::GetMoments);
|
||||
PropertyManager->Tie("forces/fwx-aero-lbs", this, eDrag, (PMF)&FGAerodynamics::GetvFw);
|
||||
PropertyManager->Tie("forces/fwy-aero-lbs", this, eSide, (PMF)&FGAerodynamics::GetvFw);
|
||||
PropertyManager->Tie("forces/fwz-aero-lbs", this, eLift, (PMF)&FGAerodynamics::GetvFw);
|
||||
PropertyManager->Tie("forces/fsx-aero-lbs", this, eX, (PMF)&FGAerodynamics::GetForcesInStabilityAxes);
|
||||
PropertyManager->Tie("forces/fsy-aero-lbs", this, eY, (PMF)&FGAerodynamics::GetForcesInStabilityAxes);
|
||||
PropertyManager->Tie("forces/fsz-aero-lbs", this, eZ, (PMF)&FGAerodynamics::GetForcesInStabilityAxes);
|
||||
PropertyManager->Tie("moments/roll-stab-aero-lbsft", this, eRoll, (PMF)&FGAerodynamics::GetMomentsInStabilityAxes);
|
||||
PropertyManager->Tie("moments/pitch-stab-aero-lbsft", this, ePitch, (PMF)&FGAerodynamics::GetMomentsInStabilityAxes);
|
||||
PropertyManager->Tie("moments/yaw-stab-aero-lbsft", this, eYaw, (PMF)&FGAerodynamics::GetMomentsInStabilityAxes);
|
||||
PropertyManager->Tie("moments/roll-wind-aero-lbsft", this, eRoll, (PMF)&FGAerodynamics::GetMomentsInWindAxes);
|
||||
PropertyManager->Tie("moments/pitch-wind-aero-lbsft", this, ePitch, (PMF)&FGAerodynamics::GetMomentsInWindAxes);
|
||||
PropertyManager->Tie("moments/yaw-wind-aero-lbsft", this, eYaw, (PMF)&FGAerodynamics::GetMomentsInWindAxes);
|
||||
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-rad", this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax);
|
||||
PropertyManager->Tie("aero/alpha-min-rad", this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin);
|
||||
PropertyManager->Tie("aero/bi2vel", this, &FGAerodynamics::GetBI2Vel);
|
||||
PropertyManager->Tie("aero/ci2vel", this, &FGAerodynamics::GetCI2Vel);
|
||||
PropertyManager->Tie("aero/alpha-wing-rad", this, &FGAerodynamics::GetAlphaW);
|
||||
PropertyManager->Tie("systems/stall-warn-norm", this, &FGAerodynamics::GetStallWarn);
|
||||
PropertyManager->Tie("aero/stall-hyst-norm", this, &FGAerodynamics::GetHysteresisParm);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
//
|
||||
// Build transformation matrices for transforming from stability axes to
|
||||
// body axes and to wind axes. Where "a" is alpha and "B" is beta:
|
||||
//
|
||||
// The transform from body to stability axes is:
|
||||
//
|
||||
// cos(a) 0 sin(a)
|
||||
// 0 1 0
|
||||
// -sin(a) 0 cos(a)
|
||||
//
|
||||
// The transform from stability to body axes is:
|
||||
//
|
||||
// cos(a) 0 -sin(a)
|
||||
// 0 1 0
|
||||
// sin(a) 0 cos(a)
|
||||
//
|
||||
//
|
||||
|
||||
void FGAerodynamics::BuildStabilityTransformMatrices(void)
|
||||
{
|
||||
double ca = cos(in.Alpha);
|
||||
double sa = sin(in.Alpha);
|
||||
|
||||
// Stability-to-body
|
||||
Ts2b(1, 1) = ca;
|
||||
Ts2b(1, 2) = 0.0;
|
||||
Ts2b(1, 3) = -sa;
|
||||
Ts2b(2, 1) = 0.0;
|
||||
Ts2b(2, 2) = 1.0;
|
||||
Ts2b(2, 3) = 0.0;
|
||||
Ts2b(3, 1) = sa;
|
||||
Ts2b(3, 2) = 0.0;
|
||||
Ts2b(3, 3) = ca;
|
||||
|
||||
Tb2s = Ts2b.Transposed();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGAerodynamics::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 2) { // Loader
|
||||
switch (forceAxisType) {
|
||||
case (atWind):
|
||||
cout << endl << " Aerodynamics (Lift|Side|Drag axes):" << endl << endl;
|
||||
break;
|
||||
case (atBodyAxialNormal):
|
||||
cout << endl << " Aerodynamics (Axial|Side|Normal axes):" << endl << endl;
|
||||
break;
|
||||
case (atBodyXYZ):
|
||||
cout << endl << " Aerodynamics (Body X|Y|Z axes):" << endl << endl;
|
||||
break;
|
||||
case (atStability):
|
||||
cout << endl << " Aerodynamics (Stability X|Y|Z axes):" << endl << endl;
|
||||
break;
|
||||
case (atNone):
|
||||
cout << endl << " Aerodynamics (undefined axes):" << endl << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGAerodynamics" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGAerodynamics" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
292
src/FDM/JSBSim/models/FGAerodynamics.h
Normal file
292
src/FDM/JSBSim/models/FGAerodynamics.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGAerodynamics.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 09/13/00
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
09/13/00 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGAERODYNAMICS_H
|
||||
#define FGAERODYNAMICS_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "FGModel.h"
|
||||
#include "math/FGFunction.h"
|
||||
#include "math/FGColumnVector3.h"
|
||||
#include "math/FGMatrix33.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates the aerodynamic calculations.
|
||||
This class owns and contains the list of force/coefficients that define the
|
||||
aerodynamic properties of an aircraft. Here also, such unique phenomena
|
||||
as ground effect, aerodynamic reference point shift, and maximum lift curve
|
||||
tailoff are handled.
|
||||
|
||||
<h3>Configuration File Format for \<aerodynamics> Section:</h3>
|
||||
@code{.xml}
|
||||
<aerodynamics>
|
||||
<alphalimits unit="{RAD | DEG}">
|
||||
<min> {number} </min>
|
||||
<max> {number} </max>
|
||||
</alphalimits>
|
||||
<hysteresis_limits unit="{RAD | DEG}">
|
||||
<min> {number} </min>
|
||||
<max> {number} </max>
|
||||
</hysteresis_limits>
|
||||
<aero_ref_pt_shift_x>
|
||||
<function>
|
||||
{function contents}
|
||||
</function>
|
||||
</aero_ref_pt_shift_x>
|
||||
<function>
|
||||
{function contents}
|
||||
</function>
|
||||
<axis name="{LIFT | DRAG | SIDE | ROLL | PITCH | YAW}">
|
||||
{force or moment definitions}
|
||||
</axis>
|
||||
{additional axis definitions}
|
||||
</aerodynamics>
|
||||
@endcode
|
||||
|
||||
Optionally two other coordinate systems may be used.<br><br>
|
||||
1) Body coordinate system:
|
||||
@code{.xml}
|
||||
<axis name="{X | Y | Z}">
|
||||
@endcode
|
||||
<br>
|
||||
2) Axial-Normal coordinate system:
|
||||
@code{.xml}
|
||||
<axis name="{AXIAL | NORMAL | SIDE}">
|
||||
@endcode
|
||||
<br>
|
||||
Systems may NOT be combined, or a load error will occur.
|
||||
|
||||
@author Jon S. Berndt, Tony Peden
|
||||
@version $Revision: 1.30 $
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGAerodynamics : public FGModel
|
||||
{
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
@param Executive a pointer to the parent executive object */
|
||||
FGAerodynamics(FGFDMExec* Executive);
|
||||
/// Destructor
|
||||
~FGAerodynamics() override;
|
||||
|
||||
bool InitModel(void) override;
|
||||
|
||||
/** Runs the Aerodynamics model; called by the Executive
|
||||
Can pass in a value indicating if the executive is directing the simulation to Hold.
|
||||
@param Holding if true, the executive has been directed to hold the sim from
|
||||
advancing time. Some models may ignore this flag, such as the Input
|
||||
model, which may need to be active to listen on a socket for the
|
||||
"Resume" command to be given.
|
||||
@return false if no error */
|
||||
bool Run(bool Holding) override;
|
||||
|
||||
/** Loads the Aerodynamics model.
|
||||
The Load function for this class expects the XML parser to
|
||||
have found the aerodynamics keyword in the configuration file.
|
||||
@param element pointer to the current XML element for aerodynamics parameters.
|
||||
@return true if successful */
|
||||
bool Load(Element* element) override;
|
||||
|
||||
/** Gets the total aerodynamic force vector.
|
||||
@return a force vector reference. */
|
||||
const FGColumnVector3& GetForces(void) const {return vForces;}
|
||||
|
||||
/** Gets the aerodynamic force for an axis.
|
||||
@param n Axis index. This could be 0, 1, or 2, or one of the
|
||||
axis enums: eX, eY, eZ.
|
||||
@return the force acting on an axis */
|
||||
double GetForces(int n) const {return vForces(n);}
|
||||
|
||||
/** Gets the total aerodynamic moment vector about the CG.
|
||||
@return a moment vector reference. */
|
||||
const FGColumnVector3& GetMoments(void) const {return vMoments;}
|
||||
|
||||
/** Gets the aerodynamic moment about the CG for an axis.
|
||||
@return the moment about a single axis (as described also in the
|
||||
similar call to GetForces(int n).*/
|
||||
double GetMoments(int n) const {return vMoments(n);}
|
||||
|
||||
/** Gets the total aerodynamic moment vector about the Moment Reference Center.
|
||||
@return a moment vector reference. */
|
||||
const FGColumnVector3& GetMomentsMRC(void) const {return vMomentsMRC;}
|
||||
|
||||
/** Gets the aerodynamic moment about the Moment Reference Center for an axis.
|
||||
@return the moment about a single axis (as described also in the
|
||||
similar call to GetForces(int n).*/
|
||||
double GetMomentsMRC(int n) const {return vMomentsMRC(n);}
|
||||
|
||||
/** Retrieves the aerodynamic forces in the wind axes.
|
||||
@return a reference to a column vector containing the wind axis forces. */
|
||||
const FGColumnVector3& GetvFw(void) const { return vFw; }
|
||||
|
||||
/** Retrieves the aerodynamic forces in the wind axes, given an axis.
|
||||
@param axis the axis to return the force for (eX, eY, eZ).
|
||||
@return a reference to a column vector containing the requested wind
|
||||
axis force. */
|
||||
double GetvFw(int axis) const { return vFw(axis); }
|
||||
|
||||
/** Retrieves the aerodynamic forces in the stability axes.
|
||||
@return a reference to a column vector containing the stability axis forces. */
|
||||
FGColumnVector3 GetForcesInStabilityAxes(void) const;
|
||||
|
||||
/** Retrieves the aerodynamic forces in the stability axes, given an axis.
|
||||
@param axis the axis to return the force for (eX, eY, eZ).
|
||||
@return a reference to a column vector containing the requested stability
|
||||
axis force. */
|
||||
double GetForcesInStabilityAxes(int n) const { return GetForcesInStabilityAxes()(n); }
|
||||
|
||||
/** Gets the total aerodynamic moment vector about the CG in the stability axes.
|
||||
@return a moment vector reference. */
|
||||
FGColumnVector3 GetMomentsInStabilityAxes(void) const { return Tb2s*vMoments; }
|
||||
|
||||
/** Gets the aerodynamic moment about the CG for an axis.
|
||||
@return the moment about a single axis (as described also in the
|
||||
similar call to GetForces(int n).*/
|
||||
double GetMomentsInStabilityAxes(int n) const { return GetMomentsInStabilityAxes()(n); }
|
||||
|
||||
/** Gets the total aerodynamic moment vector about the CG in the wind axes.
|
||||
@return a moment vector reference. */
|
||||
FGColumnVector3 GetMomentsInWindAxes(void) const { return in.Tb2w*vMoments; }
|
||||
|
||||
/** Gets the aerodynamic moment about the CG for an axis.
|
||||
@return the moment about a single axis (as described also in the
|
||||
similar call to GetForces(int n).*/
|
||||
double GetMomentsInWindAxes(int n) const { return GetMomentsInWindAxes()(n); }
|
||||
|
||||
/** Retrieves the lift over drag ratio */
|
||||
double GetLoD(void) const { return lod; }
|
||||
|
||||
/** Retrieves the square of the lift coefficient. */
|
||||
double GetClSquared(void) const { return clsq; }
|
||||
double GetAlphaCLMax(void) const { return alphaclmax; }
|
||||
double GetAlphaCLMin(void) const { return alphaclmin; }
|
||||
|
||||
double GetHysteresisParm(void) const { return stall_hyst; }
|
||||
double GetStallWarn(void) const { return impending_stall; }
|
||||
double GetAlphaW(void) const { return alphaw; }
|
||||
|
||||
double GetBI2Vel(void) const { return bi2vel; }
|
||||
double GetCI2Vel(void) const { return ci2vel; }
|
||||
|
||||
void SetAlphaCLMax(double tt) { alphaclmax=tt; }
|
||||
void SetAlphaCLMin(double tt) { alphaclmin=tt; }
|
||||
|
||||
/** Gets the strings for the current set of aero functions.
|
||||
@param delimeter either a tab or comma string depending on output type
|
||||
@return a string containing the descriptive names for all aero functions */
|
||||
std::string GetAeroFunctionStrings(const std::string& delimeter) const;
|
||||
|
||||
/** Gets the aero function values.
|
||||
@param delimeter either a tab or comma string depending on output type
|
||||
@return a string containing the numeric values for the current set of
|
||||
aero functions */
|
||||
std::string GetAeroFunctionValues(const std::string& delimeter) const;
|
||||
|
||||
std::vector <FGFunction*> * GetAeroFunctions(void) const { return AeroFunctions; }
|
||||
|
||||
struct Inputs {
|
||||
double Alpha;
|
||||
double Beta;
|
||||
double Vt;
|
||||
double Qbar;
|
||||
double Wingarea;
|
||||
double Wingspan;
|
||||
double Wingchord;
|
||||
double Wingincidence;
|
||||
FGColumnVector3 RPBody;
|
||||
FGMatrix33 Tb2w;
|
||||
FGMatrix33 Tw2b;
|
||||
} in;
|
||||
|
||||
private:
|
||||
enum eAxisType {atNone, atWind, atBodyAxialNormal, atBodyXYZ, atStability} forceAxisType, momentAxisType;
|
||||
typedef std::map<std::string,int> AxisIndex;
|
||||
AxisIndex AxisIdx;
|
||||
FGFunction* AeroRPShift;
|
||||
typedef std::vector <FGFunction*> AeroFunctionArray;
|
||||
AeroFunctionArray* AeroFunctions;
|
||||
FGMatrix33 Ts2b, Tb2s;
|
||||
FGColumnVector3 vFnative;
|
||||
FGColumnVector3 vFw;
|
||||
FGColumnVector3 vForces;
|
||||
AeroFunctionArray* AeroFunctionsAtCG;
|
||||
FGColumnVector3 vFnativeAtCG;
|
||||
FGColumnVector3 vForcesAtCG;
|
||||
FGColumnVector3 vMoments;
|
||||
FGColumnVector3 vMomentsMRC;
|
||||
FGColumnVector3 vMomentsMRCBodyXYZ;
|
||||
FGColumnVector3 vDXYZcg;
|
||||
FGColumnVector3 vDeltaRP;
|
||||
double alphaclmax, alphaclmin;
|
||||
double alphaclmax0, alphaclmin0;
|
||||
double alphahystmax, alphahystmin;
|
||||
double impending_stall, stall_hyst;
|
||||
double bi2vel, ci2vel,alphaw;
|
||||
double clsq, lod, qbar_area;
|
||||
|
||||
typedef double (FGAerodynamics::*PMF)(int) const;
|
||||
void DetermineAxisSystem(Element* document);
|
||||
void ProcessAxesNameAndFrame(FGAerodynamics::eAxisType& axisType,
|
||||
const string& name, const string& frame,
|
||||
Element* el, const string& validNames);
|
||||
void bind(void);
|
||||
void BuildStabilityTransformMatrices(void);
|
||||
|
||||
void Debug(int from) override;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
255
src/FDM/JSBSim/models/FGAircraft.cpp
Normal file
255
src/FDM/JSBSim/models/FGAircraft.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGAircraft.cpp
|
||||
Author: Jon S. Berndt
|
||||
Date started: 12/12/98
|
||||
Purpose: Encapsulates an aircraft
|
||||
Called by: FGFDMExec
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
Models the aircraft reactions and forces. This class is instantiated by the
|
||||
FGFDMExec class and scheduled as an FDM entry.
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGAircraft.h"
|
||||
#include "input_output/FGXMLElement.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
|
||||
{
|
||||
Name = "FGAircraft";
|
||||
WingSpan = 0.0;
|
||||
WingArea = 0.0;
|
||||
cbar = 0.0;
|
||||
HTailArea = VTailArea = 0.0;
|
||||
HTailArm = VTailArm = 0.0;
|
||||
lbarh = lbarv = 0.0;
|
||||
vbarh = vbarv = 0.0;
|
||||
WingIncidence = 0.0;
|
||||
|
||||
bind();
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGAircraft::~FGAircraft()
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAircraft::InitModel(void)
|
||||
{
|
||||
if (!FGModel::InitModel()) return false;
|
||||
|
||||
vForces.InitMatrix();
|
||||
vMoments.InitMatrix();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAircraft::Run(bool Holding)
|
||||
{
|
||||
if (FGModel::Run(Holding)) return true;
|
||||
if (Holding) return false;
|
||||
|
||||
RunPreFunctions();
|
||||
|
||||
vForces = in.AeroForce;
|
||||
vForces += in.PropForce;
|
||||
vForces += in.GroundForce;
|
||||
vForces += in.ExternalForce;
|
||||
vForces += in.BuoyantForce;
|
||||
|
||||
vMoments = in.AeroMoment;
|
||||
vMoments += in.PropMoment;
|
||||
vMoments += in.GroundMoment;
|
||||
vMoments += in.ExternalMoment;
|
||||
vMoments += in.BuoyantMoment;
|
||||
|
||||
RunPostFunctions();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAircraft::Load(Element* el)
|
||||
{
|
||||
string element_name;
|
||||
Element* element;
|
||||
|
||||
if (!FGModel::Upload(el, true)) return false;
|
||||
|
||||
if (el->FindElement("wingarea"))
|
||||
WingArea = el->FindElementValueAsNumberConvertTo("wingarea", "FT2");
|
||||
if (el->FindElement("wingspan"))
|
||||
WingSpan = el->FindElementValueAsNumberConvertTo("wingspan", "FT");
|
||||
if (el->FindElement("chord"))
|
||||
cbar = el->FindElementValueAsNumberConvertTo("chord", "FT");
|
||||
if (el->FindElement("wing_incidence"))
|
||||
WingIncidence = el->FindElementValueAsNumberConvertTo("wing_incidence", "RAD");
|
||||
if (el->FindElement("htailarea"))
|
||||
HTailArea = el->FindElementValueAsNumberConvertTo("htailarea", "FT2");
|
||||
if (el->FindElement("htailarm"))
|
||||
HTailArm = el->FindElementValueAsNumberConvertTo("htailarm", "FT");
|
||||
if (el->FindElement("vtailarea"))
|
||||
VTailArea = el->FindElementValueAsNumberConvertTo("vtailarea", "FT2");
|
||||
if (el->FindElement("vtailarm"))
|
||||
VTailArm = el->FindElementValueAsNumberConvertTo("vtailarm", "FT");
|
||||
|
||||
// Find all LOCATION elements that descend from this METRICS branch of the
|
||||
// config file. This would be CG location, eyepoint, etc.
|
||||
|
||||
element = el->FindElement("location");
|
||||
while (element) {
|
||||
element_name = element->GetAttributeValue("name");
|
||||
|
||||
if (element_name == "AERORP") vXYZrp = element->FindElementTripletConvertTo("IN");
|
||||
else if (element_name == "EYEPOINT") vXYZep = element->FindElementTripletConvertTo("IN");
|
||||
else if (element_name == "VRP") vXYZvrp = element->FindElementTripletConvertTo("IN");
|
||||
|
||||
element = el->FindNextElement("location");
|
||||
}
|
||||
|
||||
// calculate some derived parameters
|
||||
if (cbar != 0.0) {
|
||||
lbarh = HTailArm/cbar;
|
||||
lbarv = VTailArm/cbar;
|
||||
if (WingArea != 0.0) {
|
||||
vbarh = HTailArm*HTailArea / (cbar*WingArea);
|
||||
vbarv = VTailArm*VTailArea / (WingSpan*WingArea);
|
||||
}
|
||||
}
|
||||
|
||||
PostLoad(el, FDMExec);
|
||||
|
||||
Debug(2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAircraft::bind(void)
|
||||
{
|
||||
typedef double (FGAircraft::*PMF)(int) const;
|
||||
PropertyManager->Tie("metrics/Sw-sqft", this, &FGAircraft::GetWingArea, &FGAircraft::SetWingArea);
|
||||
PropertyManager->Tie("metrics/bw-ft", this, &FGAircraft::GetWingSpan);
|
||||
PropertyManager->Tie("metrics/cbarw-ft", this, &FGAircraft::Getcbar);
|
||||
PropertyManager->Tie("metrics/iw-rad", this, &FGAircraft::GetWingIncidence);
|
||||
PropertyManager->Tie("metrics/iw-deg", this, &FGAircraft::GetWingIncidenceDeg);
|
||||
PropertyManager->Tie("metrics/Sh-sqft", this, &FGAircraft::GetHTailArea);
|
||||
PropertyManager->Tie("metrics/lh-ft", this, &FGAircraft::GetHTailArm);
|
||||
PropertyManager->Tie("metrics/Sv-sqft", this, &FGAircraft::GetVTailArea);
|
||||
PropertyManager->Tie("metrics/lv-ft", this, &FGAircraft::GetVTailArm);
|
||||
PropertyManager->Tie("metrics/lh-norm", this, &FGAircraft::Getlbarh);
|
||||
PropertyManager->Tie("metrics/lv-norm", this, &FGAircraft::Getlbarv);
|
||||
PropertyManager->Tie("metrics/vbarh-norm", this, &FGAircraft::Getvbarh);
|
||||
PropertyManager->Tie("metrics/vbarv-norm", this, &FGAircraft::Getvbarv);
|
||||
PropertyManager->Tie("metrics/aero-rp-x-in", this, eX, (PMF)&FGAircraft::GetXYZrp, &FGAircraft::SetXYZrp);
|
||||
PropertyManager->Tie("metrics/aero-rp-y-in", this, eY, (PMF)&FGAircraft::GetXYZrp, &FGAircraft::SetXYZrp);
|
||||
PropertyManager->Tie("metrics/aero-rp-z-in", this, eZ, (PMF)&FGAircraft::GetXYZrp, &FGAircraft::SetXYZrp);
|
||||
PropertyManager->Tie("metrics/eyepoint-x-in", this, eX, (PMF)&FGAircraft::GetXYZep);
|
||||
PropertyManager->Tie("metrics/eyepoint-y-in", this, eY,(PMF)&FGAircraft::GetXYZep);
|
||||
PropertyManager->Tie("metrics/eyepoint-z-in", this, eZ, (PMF)&FGAircraft::GetXYZep);
|
||||
PropertyManager->Tie("metrics/visualrefpoint-x-in", this, eX, (PMF)&FGAircraft::GetXYZvrp);
|
||||
PropertyManager->Tie("metrics/visualrefpoint-y-in", this, eY, (PMF)&FGAircraft::GetXYZvrp);
|
||||
PropertyManager->Tie("metrics/visualrefpoint-z-in", this, eZ, (PMF)&FGAircraft::GetXYZvrp);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGAircraft::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 2) { // Loading
|
||||
cout << endl << " Aircraft Metrics:" << endl;
|
||||
cout << " WingArea: " << WingArea << endl;
|
||||
cout << " WingSpan: " << WingSpan << endl;
|
||||
cout << " Incidence: " << WingIncidence << endl;
|
||||
cout << " Chord: " << cbar << endl;
|
||||
cout << " H. Tail Area: " << HTailArea << endl;
|
||||
cout << " H. Tail Arm: " << HTailArm << endl;
|
||||
cout << " V. Tail Area: " << VTailArea << endl;
|
||||
cout << " V. Tail Arm: " << VTailArm << endl;
|
||||
cout << " Eyepoint (x, y, z): " << vXYZep << endl;
|
||||
cout << " Ref Pt (x, y, z): " << vXYZrp << endl;
|
||||
cout << " Visual Ref Pt (x, y, z): " << vXYZvrp << endl;
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGAircraft" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGAircraft" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
200
src/FDM/JSBSim/models/FGAircraft.h
Normal file
200
src/FDM/JSBSim/models/FGAircraft.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGAircraft.h
|
||||
Author: Jon S. Berndt
|
||||
Date started: 12/12/98
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
12/12/98 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGAIRCRAFT_H
|
||||
#define FGAIRCRAFT_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "FGModel.h"
|
||||
#include "math/FGMatrix33.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Encapsulates an Aircraft and its systems.
|
||||
<p> Owns all the parts (other classes) which make up this aircraft. This includes
|
||||
the Engines, Tanks, Propellers, Nozzles, Aerodynamic and Mass properties,
|
||||
landing gear, etc. These constituent parts may actually run as separate
|
||||
JSBSim models themselves, but the responsibility for initializing them and
|
||||
for retrieving their force and moment contributions falls to FGAircraft.
|
||||
<p> The \<metrics> section of the aircraft configuration file is read here, and
|
||||
the metrical information is held by this class.
|
||||
<h3>Configuration File Format for \<metrics> Section:</h3>
|
||||
@code{.xml}
|
||||
<metrics>
|
||||
<wingarea unit="{FT2 | M2}"> {number} </wingarea>
|
||||
<wingspan unit="{FT | M}"> {number} </wingspan>
|
||||
<chord unit="{FT | M}"> {number} </chord>
|
||||
<htailarea unit="{FT2 | M2}"> {number} </htailarea>
|
||||
<htailarm unit="{FT | M}"> {number} </htailarm>
|
||||
<vtailarea unit="{FT2 | M}"> {number} </vtailarea>
|
||||
<vtailarm unit="{FT | M}"> {number} </vtailarm>
|
||||
<wing_incidence unit="{RAD | DEG}"> {number} </wing_incidence>
|
||||
<location name="{AERORP | EYEPOINT | VRP}" unit="{IN | M}">
|
||||
<x> {number} </x>
|
||||
<y> {number} </y>
|
||||
<z> {number} </z>
|
||||
</location>
|
||||
{other location blocks}
|
||||
</metrics>
|
||||
@endcode
|
||||
|
||||
@author Jon S. Berndt
|
||||
@see Cooke, Zyda, Pratt, and McGhee, "NPSNET: Flight Simulation Dynamic Modeling
|
||||
Using Quaternions", Presence, Vol. 1, No. 4, pp. 404-420 Naval Postgraduate
|
||||
School, January 1994
|
||||
@see D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
|
||||
JSC 12960, July 1977
|
||||
@see Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
|
||||
NASA-Ames", NASA CR-2497, January 1975
|
||||
@see Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
|
||||
Wiley & Sons, 1979 ISBN 0-471-03032-5
|
||||
@see Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
|
||||
1982 ISBN 0-471-08936-2
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGAircraft : public FGModel {
|
||||
public:
|
||||
/** Constructor
|
||||
@param Executive a pointer to the parent executive object */
|
||||
FGAircraft(FGFDMExec *Executive);
|
||||
|
||||
/// Destructor
|
||||
~FGAircraft() override;
|
||||
|
||||
/** Runs the Aircraft model; called by the Executive
|
||||
Can pass in a value indicating if the executive is directing the simulation to Hold.
|
||||
@param Holding if true, the executive has been directed to hold the sim from
|
||||
advancing time. Some models may ignore this flag, such as the Input
|
||||
model, which may need to be active to listen on a socket for the
|
||||
"Resume" command to be given.
|
||||
@see JSBSim.cpp documentation
|
||||
@return false if no error */
|
||||
bool Run(bool Holding) override;
|
||||
|
||||
bool InitModel(void) override;
|
||||
|
||||
/** Loads the aircraft.
|
||||
The executive calls this method to load the aircraft into JSBSim.
|
||||
@param el a pointer to the element tree
|
||||
@return true if successful */
|
||||
bool Load(Element* el) override;
|
||||
|
||||
/** Gets the aircraft name
|
||||
@return the name of the aircraft as a string type */
|
||||
const std::string& GetAircraftName(void) const { return AircraftName; }
|
||||
|
||||
/// Gets the wing area
|
||||
double GetWingArea(void) const { return WingArea; }
|
||||
/// Gets the wing span
|
||||
double GetWingSpan(void) const { return WingSpan; }
|
||||
/// Gets the average wing chord
|
||||
double Getcbar(void) const { return cbar; }
|
||||
double GetWingIncidence(void) const { return WingIncidence; }
|
||||
double GetWingIncidenceDeg(void) const { return WingIncidence*radtodeg; }
|
||||
double GetHTailArea(void) const { return HTailArea; }
|
||||
double GetHTailArm(void) const { return HTailArm; }
|
||||
double GetVTailArea(void) const { return VTailArea; }
|
||||
double GetVTailArm(void) const { return VTailArm; }
|
||||
double Getlbarh(void) const { return lbarh; } // HTailArm / cbar
|
||||
double Getlbarv(void) const { return lbarv; } // VTailArm / cbar
|
||||
double Getvbarh(void) const { return vbarh; } // H. Tail Volume
|
||||
double Getvbarv(void) const { return vbarv; } // V. Tail Volume
|
||||
const FGColumnVector3& GetMoments(void) const { return vMoments; }
|
||||
double GetMoments(int idx) const { return vMoments(idx); }
|
||||
const FGColumnVector3& GetForces(void) const { return vForces; }
|
||||
double GetForces(int idx) const { return vForces(idx); }
|
||||
/** Gets the the aero reference point (RP) coordinates.
|
||||
@return a vector containing the RP coordinates in the structural frame. */
|
||||
const FGColumnVector3& GetXYZrp(void) const { return vXYZrp; }
|
||||
const FGColumnVector3& GetXYZvrp(void) const { return vXYZvrp; }
|
||||
const FGColumnVector3& GetXYZep(void) const { return vXYZep; }
|
||||
double GetXYZrp(int idx) const { return vXYZrp(idx); }
|
||||
double GetXYZvrp(int idx) const { return vXYZvrp(idx); }
|
||||
double GetXYZep(int idx) const { return vXYZep(idx); }
|
||||
void SetAircraftName(const std::string& name) {AircraftName = name;}
|
||||
|
||||
void SetXYZrp(int idx, double value) {vXYZrp(idx) = value;}
|
||||
|
||||
void SetWingArea(double S) {WingArea = S;}
|
||||
|
||||
struct Inputs {
|
||||
FGColumnVector3 AeroForce;
|
||||
FGColumnVector3 PropForce;
|
||||
FGColumnVector3 GroundForce;
|
||||
FGColumnVector3 ExternalForce;
|
||||
FGColumnVector3 BuoyantForce;
|
||||
FGColumnVector3 AeroMoment;
|
||||
FGColumnVector3 PropMoment;
|
||||
FGColumnVector3 GroundMoment;
|
||||
FGColumnVector3 ExternalMoment;
|
||||
FGColumnVector3 BuoyantMoment;
|
||||
} in;
|
||||
|
||||
private:
|
||||
FGColumnVector3 vMoments;
|
||||
FGColumnVector3 vForces;
|
||||
FGColumnVector3 vXYZrp;
|
||||
FGColumnVector3 vXYZvrp;
|
||||
FGColumnVector3 vXYZep;
|
||||
FGColumnVector3 vDXYZcg;
|
||||
|
||||
double WingArea, WingSpan, cbar, WingIncidence;
|
||||
double HTailArea, VTailArea, HTailArm, VTailArm;
|
||||
double lbarh,lbarv,vbarh,vbarv;
|
||||
std::string AircraftName;
|
||||
|
||||
void bind(void);
|
||||
void unbind(void);
|
||||
void Debug(int from) override;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
333
src/FDM/JSBSim/models/FGAtmosphere.cpp
Normal file
333
src/FDM/JSBSim/models/FGAtmosphere.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGAtmosphere.cpp
|
||||
Author: Jon Berndt, Tony Peden
|
||||
Date started: 6/2011
|
||||
Purpose: Models an atmosphere interface class
|
||||
Called by: FGFDMExec
|
||||
|
||||
------------- Copyright (C) 2011 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This models a base atmosphere class to serve as a common interface to any
|
||||
derived atmosphere models.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
6/18/2011 Started Jon S. Berndt
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
COMMENTS, REFERENCES, and NOTES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "FGFDMExec.h"
|
||||
#include "FGAtmosphere.h"
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
// Atmosphere constants in British units converted from the SI values specified in the
|
||||
// ISA document - https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770009539.pdf
|
||||
double FGAtmosphere::Reng = Rstar / Mair;
|
||||
|
||||
const double FGAtmosphere::StdDaySLsoundspeed = sqrt(SHRatio*Reng*StdDaySLtemperature);
|
||||
|
||||
FGAtmosphere::FGAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex),
|
||||
PressureAltitude(0.0), // ft
|
||||
DensityAltitude(0.0) // ft
|
||||
{
|
||||
Name = "FGAtmosphere";
|
||||
|
||||
bind();
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGAtmosphere::~FGAtmosphere()
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAtmosphere::InitModel(void)
|
||||
{
|
||||
if (!FGModel::InitModel()) return false;
|
||||
|
||||
Calculate(0.0);
|
||||
SLtemperature = Temperature = StdDaySLtemperature;
|
||||
SLpressure = Pressure = StdDaySLpressure;
|
||||
SLdensity = Density = Pressure/(Reng*Temperature);
|
||||
SLsoundspeed = Soundspeed = StdDaySLsoundspeed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAtmosphere::Run(bool Holding)
|
||||
{
|
||||
if (FGModel::Run(Holding)) return true;
|
||||
if (Holding) return false;
|
||||
|
||||
Calculate(in.altitudeASL);
|
||||
|
||||
Debug(2);
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAtmosphere::Calculate(double altitude)
|
||||
{
|
||||
FGPropertyNode* node = PropertyManager->GetNode();
|
||||
if (!PropertyManager->HasNode("atmosphere/override/temperature"))
|
||||
Temperature = GetTemperature(altitude);
|
||||
else
|
||||
Temperature = node->GetDouble("atmosphere/override/temperature");
|
||||
|
||||
if (!PropertyManager->HasNode("atmosphere/override/pressure"))
|
||||
Pressure = GetPressure(altitude);
|
||||
else
|
||||
Pressure = node->GetDouble("atmosphere/override/pressure");
|
||||
|
||||
if (!PropertyManager->HasNode("atmosphere/override/density"))
|
||||
Density = GetDensity(altitude);
|
||||
else
|
||||
Density = node->GetDouble("atmosphere/override/density");
|
||||
|
||||
Soundspeed = sqrt(SHRatio*Reng*Temperature);
|
||||
PressureAltitude = CalculatePressureAltitude(Pressure, altitude);
|
||||
DensityAltitude = CalculateDensityAltitude(Density, altitude);
|
||||
|
||||
Viscosity = Beta * pow(Temperature, 1.5) / (SutherlandConstant + Temperature);
|
||||
KinematicViscosity = Viscosity / Density;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAtmosphere::SetPressureSL(ePressure unit, double pressure)
|
||||
{
|
||||
double press = ConvertToPSF(pressure, unit);
|
||||
|
||||
SLpressure = press;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Get the modeled density at a specified altitude
|
||||
|
||||
double FGAtmosphere::GetDensity(double altitude) const
|
||||
{
|
||||
return GetPressure(altitude)/(Reng * GetTemperature(altitude));
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// Get the sound speed at a specified altitude
|
||||
|
||||
double FGAtmosphere::GetSoundSpeed(double altitude) const
|
||||
{
|
||||
return sqrt(SHRatio * Reng * GetTemperature(altitude));
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// This function sets the sea level temperature.
|
||||
// Internally, the Rankine scale is used for calculations, so any temperature
|
||||
// supplied must be converted to that unit.
|
||||
|
||||
void FGAtmosphere::SetTemperatureSL(double t, eTemperature unit)
|
||||
{
|
||||
SLtemperature = ConvertToRankine(t, unit);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAtmosphere::ConvertToRankine(double t, eTemperature unit) const
|
||||
{
|
||||
double targetTemp=0; // in degrees Rankine
|
||||
|
||||
switch(unit) {
|
||||
case eFahrenheit:
|
||||
targetTemp = t + 459.67;
|
||||
break;
|
||||
case eCelsius:
|
||||
targetTemp = (t + 273.15) * 1.8;
|
||||
break;
|
||||
case eRankine:
|
||||
targetTemp = t;
|
||||
break;
|
||||
case eKelvin:
|
||||
targetTemp = t*1.8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return targetTemp;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAtmosphere::ConvertFromRankine(double t, eTemperature unit) const
|
||||
{
|
||||
double targetTemp=0;
|
||||
|
||||
switch(unit) {
|
||||
case eFahrenheit:
|
||||
targetTemp = t - 459.67;
|
||||
break;
|
||||
case eCelsius:
|
||||
targetTemp = t/1.8 - 273.15;
|
||||
break;
|
||||
case eRankine:
|
||||
targetTemp = t;
|
||||
break;
|
||||
case eKelvin:
|
||||
targetTemp = t/1.8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return targetTemp;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAtmosphere::ConvertToPSF(double p, ePressure unit) const
|
||||
{
|
||||
double targetPressure=0; // Pressure in PSF
|
||||
|
||||
switch(unit) {
|
||||
case ePSF:
|
||||
targetPressure = p;
|
||||
break;
|
||||
case eMillibars:
|
||||
targetPressure = p*2.08854342;
|
||||
break;
|
||||
case ePascals:
|
||||
targetPressure = p*0.0208854342;
|
||||
break;
|
||||
case eInchesHg:
|
||||
targetPressure = p*70.7180803;
|
||||
break;
|
||||
default:
|
||||
throw("Undefined pressure unit given");
|
||||
}
|
||||
|
||||
return targetPressure;
|
||||
}
|
||||
|
||||
double FGAtmosphere::ConvertFromPSF(double p, ePressure unit) const
|
||||
{
|
||||
double targetPressure=0; // Pressure
|
||||
|
||||
switch(unit) {
|
||||
case ePSF:
|
||||
targetPressure = p;
|
||||
break;
|
||||
case eMillibars:
|
||||
targetPressure = p/2.08854342;
|
||||
break;
|
||||
case ePascals:
|
||||
targetPressure = p/0.0208854342;
|
||||
break;
|
||||
case eInchesHg:
|
||||
targetPressure = p/70.7180803;
|
||||
break;
|
||||
default:
|
||||
throw("Undefined pressure unit given");
|
||||
}
|
||||
|
||||
return targetPressure;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAtmosphere::bind(void)
|
||||
{
|
||||
PropertyManager->Tie("atmosphere/T-R", this, &FGAtmosphere::GetTemperature);
|
||||
PropertyManager->Tie("atmosphere/rho-slugs_ft3", this, &FGAtmosphere::GetDensity);
|
||||
PropertyManager->Tie("atmosphere/P-psf", this, &FGAtmosphere::GetPressure);
|
||||
PropertyManager->Tie("atmosphere/a-fps", this, &FGAtmosphere::GetSoundSpeed);
|
||||
PropertyManager->Tie("atmosphere/T-sl-R", this, &FGAtmosphere::GetTemperatureSL);
|
||||
PropertyManager->Tie("atmosphere/rho-sl-slugs_ft3", this, &FGAtmosphere::GetDensitySL);
|
||||
PropertyManager->Tie("atmosphere/a-sl-fps", this, &FGAtmosphere::GetSoundSpeedSL);
|
||||
PropertyManager->Tie("atmosphere/theta", this, &FGAtmosphere::GetTemperatureRatio);
|
||||
PropertyManager->Tie("atmosphere/sigma", this, &FGAtmosphere::GetDensityRatio);
|
||||
PropertyManager->Tie("atmosphere/delta", this, &FGAtmosphere::GetPressureRatio);
|
||||
PropertyManager->Tie("atmosphere/a-ratio", this, &FGAtmosphere::GetSoundSpeedRatio);
|
||||
PropertyManager->Tie("atmosphere/density-altitude", this, &FGAtmosphere::GetDensityAltitude);
|
||||
PropertyManager->Tie("atmosphere/pressure-altitude", this, &FGAtmosphere::GetPressureAltitude);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGAtmosphere::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) std::cout << "Instantiated: FGAtmosphere" << std::endl;
|
||||
if (from == 1) std::cout << "Destroyed: FGAtmosphere" << std::endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
}
|
||||
if (debug_lvl & 128) { //
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
282
src/FDM/JSBSim/models/FGAtmosphere.h
Normal file
282
src/FDM/JSBSim/models/FGAtmosphere.h
Normal file
@@ -0,0 +1,282 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Header: FGAtmosphere.h
|
||||
Author: Jon Berndt
|
||||
Date started: 6/2011
|
||||
|
||||
------------- Copyright (C) 2011 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
5/2011 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
SENTRY
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#ifndef FGATMOSPHERE_H
|
||||
#define FGATMOSPHERE_H
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include "models/FGModel.h"
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
FORWARD DECLARATIONS
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DOCUMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
/** Models an empty, abstract base atmosphere class.
|
||||
|
||||
<h2> Properties </h2>
|
||||
@property atmosphere/T-R The current modeled temperature in degrees Rankine.
|
||||
@property atmosphere/rho-slugs_ft3
|
||||
@property atmosphere/P-psf
|
||||
@property atmosphere/a-fps
|
||||
@property atmosphere/T-sl-R
|
||||
@property atmosphere/rho-sl-slugs_ft3
|
||||
@property atmosphere/P-sl-psf
|
||||
@property atmosphere/a-sl-fps
|
||||
@property atmosphere/theta
|
||||
@property atmosphere/sigma
|
||||
@property atmosphere/delta
|
||||
@property atmosphere/a-ratio
|
||||
|
||||
@author Jon Berndt
|
||||
*/
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS DECLARATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
class FGAtmosphere : public FGModel {
|
||||
public:
|
||||
|
||||
/// Enums for specifying temperature units.
|
||||
enum eTemperature {eNoTempUnit=0, eFahrenheit, eCelsius, eRankine, eKelvin};
|
||||
|
||||
/// Enums for specifying pressure units.
|
||||
enum ePressure {eNoPressUnit=0, ePSF, eMillibars, ePascals, eInchesHg};
|
||||
|
||||
/// Constructor
|
||||
FGAtmosphere(FGFDMExec*);
|
||||
|
||||
/// Destructor
|
||||
virtual ~FGAtmosphere();
|
||||
|
||||
/** Runs the atmosphere forces model; called by the Executive.
|
||||
Can pass in a value indicating if the executive is directing the simulation to Hold.
|
||||
@param Holding if true, the executive has been directed to hold the sim from
|
||||
advancing time. Some models may ignore this flag, such as the Input
|
||||
model, which may need to be active to listen on a socket for the
|
||||
"Resume" command to be given.
|
||||
@return false if no error */
|
||||
bool Run(bool Holding) override;
|
||||
|
||||
bool InitModel(void) override;
|
||||
|
||||
// *************************************************************************
|
||||
/// @name Temperature access functions.
|
||||
/// There are several ways to get the temperature, and several modeled temperature
|
||||
/// values that can be retrieved.
|
||||
// @{
|
||||
/// Returns the actual, modeled temperature at the current altitude in degrees Rankine.
|
||||
/// @return Modeled temperature in degrees Rankine.
|
||||
virtual double GetTemperature() const {return Temperature;}
|
||||
|
||||
/// Returns the actual modeled temperature in degrees Rankine at a specified altitude.
|
||||
/// @param altitude The altitude above sea level (ASL) in feet.
|
||||
/// @return Modeled temperature in degrees Rankine at the specified altitude.
|
||||
virtual double GetTemperature(double altitude) const = 0;
|
||||
|
||||
/// Returns the actual, modeled sea level temperature in degrees Rankine.
|
||||
/// @return The modeled temperature in degrees Rankine at sea level.
|
||||
virtual double GetTemperatureSL() const { return SLtemperature; }
|
||||
|
||||
/// Returns the ratio of the at-current-altitude temperature as modeled
|
||||
/// over the sea level value.
|
||||
virtual double GetTemperatureRatio() const { return GetTemperature()/SLtemperature; }
|
||||
|
||||
/// Returns the ratio of the temperature as modeled at the supplied altitude
|
||||
/// over the sea level value.
|
||||
virtual double GetTemperatureRatio(double h) const { return GetTemperature(h)/SLtemperature; }
|
||||
|
||||
/// Sets the Sea Level temperature.
|
||||
/// @param t the temperature value in the unit provided.
|
||||
/// @param unit the unit of the temperature.
|
||||
virtual void SetTemperatureSL(double t, eTemperature unit=eFahrenheit);
|
||||
|
||||
/// Sets the temperature at the supplied altitude.
|
||||
/// @param t The temperature value in the unit provided.
|
||||
/// @param h The altitude in feet above sea level.
|
||||
/// @param unit The unit of the temperature.
|
||||
virtual void SetTemperature(double t, double h, eTemperature unit=eFahrenheit) = 0;
|
||||
//@}
|
||||
|
||||
// *************************************************************************
|
||||
/// @name Pressure access functions.
|
||||
//@{
|
||||
/// Returns the pressure in psf.
|
||||
virtual double GetPressure(void) const {return Pressure;}
|
||||
|
||||
/// Returns the pressure at a specified altitude in psf.
|
||||
virtual double GetPressure(double altitude) const = 0;
|
||||
|
||||
// Returns the sea level pressure in target units, default in psf.
|
||||
virtual double GetPressureSL(ePressure to=ePSF) const { return ConvertFromPSF(SLpressure, to); }
|
||||
|
||||
/// Returns the ratio of at-altitude pressure over the sea level value.
|
||||
virtual double GetPressureRatio(void) const { return Pressure/SLpressure; }
|
||||
|
||||
/** Sets the sea level pressure for modeling.
|
||||
@param pressure The pressure in the units specified.
|
||||
@param unit the unit of measure that the specified pressure is
|
||||
supplied in.*/
|
||||
virtual void SetPressureSL(ePressure unit, double pressure);
|
||||
//@}
|
||||
|
||||
// *************************************************************************
|
||||
/// @name Density access functions.
|
||||
//@{
|
||||
/** Returns the density in slugs/ft^3.
|
||||
This function may only be used if Run() is called first. */
|
||||
virtual double GetDensity(void) const {return Density;}
|
||||
|
||||
/** Returns the density in slugs/ft^3 at a given altitude in ft. */
|
||||
virtual double GetDensity(double altitude) const;
|
||||
|
||||
/// Returns the sea level density in slugs/ft^3
|
||||
virtual double GetDensitySL(void) const { return SLdensity; }
|
||||
|
||||
/// Returns the ratio of at-altitude density over the sea level value.
|
||||
virtual double GetDensityRatio(void) const { return Density/SLdensity; }
|
||||
//@}
|
||||
|
||||
// *************************************************************************
|
||||
/// @name Speed of sound access functions.
|
||||
//@{
|
||||
/// Returns the speed of sound in ft/sec.
|
||||
virtual double GetSoundSpeed(void) const {return Soundspeed;}
|
||||
|
||||
/// Returns the speed of sound in ft/sec at a given altitude in ft.
|
||||
virtual double GetSoundSpeed(double altitude) const;
|
||||
|
||||
/// Returns the sea level speed of sound in ft/sec.
|
||||
virtual double GetSoundSpeedSL(void) const { return SLsoundspeed; }
|
||||
|
||||
/// Returns the ratio of at-altitude sound speed over the sea level value.
|
||||
virtual double GetSoundSpeedRatio(void) const { return Soundspeed/SLsoundspeed; }
|
||||
//@}
|
||||
|
||||
// *************************************************************************
|
||||
/// @name Viscosity access functions.
|
||||
//@{
|
||||
/// Returns the absolute viscosity.
|
||||
virtual double GetAbsoluteViscosity(void) const {return Viscosity;}
|
||||
|
||||
/// Returns the kinematic viscosity.
|
||||
virtual double GetKinematicViscosity(void) const {return KinematicViscosity;}
|
||||
//@}
|
||||
|
||||
virtual double GetDensityAltitude() const {return DensityAltitude;}
|
||||
|
||||
virtual double GetPressureAltitude() const {return PressureAltitude;}
|
||||
|
||||
struct Inputs {
|
||||
double altitudeASL;
|
||||
} in;
|
||||
|
||||
static constexpr double StdDaySLtemperature = 518.67;
|
||||
static constexpr double StdDaySLpressure = 2116.228;
|
||||
static const double StdDaySLsoundspeed;
|
||||
|
||||
protected:
|
||||
double SLtemperature, SLdensity, SLpressure, SLsoundspeed; // Sea level conditions
|
||||
double Temperature, Density, Pressure, Soundspeed; // Current actual conditions at altitude
|
||||
|
||||
double PressureAltitude;
|
||||
double DensityAltitude;
|
||||
|
||||
static constexpr double SutherlandConstant = 198.72; // deg Rankine
|
||||
static constexpr double Beta = 2.269690E-08; // slug/(sec ft R^0.5)
|
||||
double Viscosity, KinematicViscosity;
|
||||
|
||||
/// Calculate the atmosphere for the given altitude.
|
||||
virtual void Calculate(double altitude);
|
||||
|
||||
/// Calculates the density altitude given any temperature or pressure bias.
|
||||
/// Calculated density for the specified geometric altitude given any temperature
|
||||
/// or pressure biases is passed in.
|
||||
/// @param density
|
||||
/// @param geometricAlt
|
||||
virtual double CalculateDensityAltitude(double density, double geometricAlt) { return geometricAlt; }
|
||||
|
||||
/// Calculates the pressure altitude given any temperature or pressure bias.
|
||||
/// Calculated pressure for the specified geometric altitude given any temperature
|
||||
/// or pressure biases is passed in.
|
||||
/// @param pressure
|
||||
/// @param geometricAlt
|
||||
virtual double CalculatePressureAltitude(double pressure, double geometricAlt) { return geometricAlt; }
|
||||
|
||||
/// Converts to Rankine from one of several unit systems.
|
||||
double ConvertToRankine(double t, eTemperature unit) const;
|
||||
|
||||
/// Converts from Rankine to one of several unit systems.
|
||||
double ConvertFromRankine(double t, eTemperature unit) const;
|
||||
|
||||
/// Converts to PSF (pounds per square foot) from one of several unit systems.
|
||||
double ConvertToPSF(double t, ePressure unit=ePSF) const;
|
||||
|
||||
/// Converts from PSF (pounds per square foot) to one of several unit systems.
|
||||
double ConvertFromPSF(double t, ePressure unit=ePSF) const;
|
||||
|
||||
/// @name ISA constants
|
||||
//@{
|
||||
/// Universal gas constant - ft*lbf/R/mol
|
||||
static constexpr double Rstar = 8.31432 * kgtoslug / KelvinToRankine(fttom * fttom);
|
||||
/// Mean molecular weight for air - slug/mol
|
||||
static constexpr double Mair = 28.9645 * kgtoslug / 1000.0;
|
||||
/** Sea-level acceleration of gravity - ft/s^2.
|
||||
This constant is defined to compute the International Standard Atmosphere.
|
||||
It is by definition the sea level gravity at a latitude of 45deg. This
|
||||
value is fixed whichever gravity model is used by FGInertial.
|
||||
*/
|
||||
static constexpr double g0 = 9.80665 / fttom;
|
||||
/// Specific gas constant for air - ft*lbf/slug/R
|
||||
static double Reng;
|
||||
//@}
|
||||
|
||||
static constexpr double SHRatio = 1.4;
|
||||
|
||||
virtual void bind(void);
|
||||
void Debug(int from) override;
|
||||
};
|
||||
|
||||
} // namespace JSBSim
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
#endif
|
||||
425
src/FDM/JSBSim/models/FGAuxiliary.cpp
Normal file
425
src/FDM/JSBSim/models/FGAuxiliary.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
Module: FGAuxiliary.cpp
|
||||
Author: Tony Peden, Jon Berndt
|
||||
Date started: 01/26/99
|
||||
Purpose: Calculates additional parameters needed by the visual system, etc.
|
||||
Called by: FGFDMExec
|
||||
|
||||
------------- Copyright (C) 1999 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 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 Lesser General Public License for more
|
||||
details.
|
||||
|
||||
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 Lesser General Public License can also be
|
||||
found on the world wide web at http://www.gnu.org.
|
||||
|
||||
FUNCTIONAL DESCRIPTION
|
||||
--------------------------------------------------------------------------------
|
||||
This class calculates various auxiliary parameters.
|
||||
|
||||
REFERENCES
|
||||
Anderson, John D. "Introduction to Flight", 3rd Edition, McGraw-Hill, 1989
|
||||
pgs. 112-126
|
||||
HISTORY
|
||||
--------------------------------------------------------------------------------
|
||||
01/26/99 JSB Created
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
INCLUDES
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "FGAuxiliary.h"
|
||||
#include "initialization/FGInitialCondition.h"
|
||||
#include "FGFDMExec.h"
|
||||
#include "input_output/FGPropertyManager.h"
|
||||
#include "FGInertial.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace JSBSim {
|
||||
|
||||
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
CLASS IMPLEMENTATION
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
|
||||
|
||||
|
||||
FGAuxiliary::FGAuxiliary(FGFDMExec* fdmex) : FGModel(fdmex)
|
||||
{
|
||||
Name = "FGAuxiliary";
|
||||
pt = 2116.23; // ISA SL pressure
|
||||
tatc = 15.0; // ISA SL temperature
|
||||
tat = 518.67;
|
||||
|
||||
vcas = veas = 0.0;
|
||||
qbar = qbarUW = qbarUV = 0.0;
|
||||
Mach = MachU = 0.0;
|
||||
alpha = beta = 0.0;
|
||||
adot = bdot = 0.0;
|
||||
gamma = Vt = Vground = 0.0;
|
||||
psigt = 0.0;
|
||||
day_of_year = 1;
|
||||
seconds_in_day = 0.0;
|
||||
hoverbmac = hoverbcg = 0.0;
|
||||
Re = 0.0;
|
||||
Nx = Ny = Nz = 0.0;
|
||||
|
||||
vPilotAccel.InitMatrix();
|
||||
vPilotAccelN.InitMatrix();
|
||||
vAeroUVW.InitMatrix();
|
||||
vAeroPQR.InitMatrix();
|
||||
vMachUVW.InitMatrix();
|
||||
vEulerRates.InitMatrix();
|
||||
|
||||
bind();
|
||||
|
||||
Debug(0);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAuxiliary::InitModel(void)
|
||||
{
|
||||
if (!FGModel::InitModel()) return false;
|
||||
|
||||
pt = in.Pressure;
|
||||
tat = in.Temperature;
|
||||
tatc = RankineToCelsius(tat);
|
||||
|
||||
vcas = veas = 0.0;
|
||||
qbar = qbarUW = qbarUV = 0.0;
|
||||
Mach = MachU = 0.0;
|
||||
alpha = beta = 0.0;
|
||||
adot = bdot = 0.0;
|
||||
gamma = Vt = Vground = 0.0;
|
||||
psigt = 0.0;
|
||||
day_of_year = 1;
|
||||
seconds_in_day = 0.0;
|
||||
hoverbmac = hoverbcg = 0.0;
|
||||
Re = 0.0;
|
||||
Nz = Ny = 0.0;
|
||||
|
||||
vPilotAccel.InitMatrix();
|
||||
vPilotAccelN.InitMatrix();
|
||||
vAeroUVW.InitMatrix();
|
||||
vAeroPQR.InitMatrix();
|
||||
vMachUVW.InitMatrix();
|
||||
vEulerRates.InitMatrix();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
FGAuxiliary::~FGAuxiliary()
|
||||
{
|
||||
Debug(1);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
bool FGAuxiliary::Run(bool Holding)
|
||||
{
|
||||
if (FGModel::Run(Holding)) return true; // return true if error returned from base class
|
||||
if (Holding) return false;
|
||||
|
||||
// Rotation
|
||||
|
||||
vEulerRates(eTht) = in.vPQR(eQ)*in.CosPhi - in.vPQR(eR)*in.SinPhi;
|
||||
if (in.CosTht != 0.0) {
|
||||
vEulerRates(ePsi) = (in.vPQR(eQ)*in.SinPhi + in.vPQR(eR)*in.CosPhi)/in.CosTht;
|
||||
vEulerRates(ePhi) = in.vPQR(eP) + vEulerRates(ePsi)*in.SinTht;
|
||||
}
|
||||
|
||||
// Combine the wind speed with aircraft speed to obtain wind relative speed
|
||||
vAeroPQR = in.vPQR - in.TurbPQR;
|
||||
vAeroUVW = in.vUVW - in.Tl2b * in.TotalWindNED;
|
||||
|
||||
alpha = beta = adot = bdot = 0;
|
||||
double AeroU2 = vAeroUVW(eU)*vAeroUVW(eU);
|
||||
double AeroV2 = vAeroUVW(eV)*vAeroUVW(eV);
|
||||
double AeroW2 = vAeroUVW(eW)*vAeroUVW(eW);
|
||||
double mUW = AeroU2 + AeroW2;
|
||||
|
||||
double Vt2 = mUW + AeroV2;
|
||||
Vt = sqrt(Vt2);
|
||||
|
||||
if ( Vt > 0.001 ) {
|
||||
beta = atan2(vAeroUVW(eV), sqrt(mUW));
|
||||
|
||||
if ( mUW >= 1E-6 ) {
|
||||
alpha = atan2(vAeroUVW(eW), vAeroUVW(eU));
|
||||
double Vtdot = (vAeroUVW(eU)*in.vUVWdot(eU) + vAeroUVW(eV)*in.vUVWdot(eV) + vAeroUVW(eW)*in.vUVWdot(eW))/Vt;
|
||||
adot = (vAeroUVW(eU)*in.vUVWdot(eW) - vAeroUVW(eW)*in.vUVWdot(eU))/mUW;
|
||||
bdot = (in.vUVWdot(eV)*Vt - vAeroUVW(eV)*Vtdot)/(Vt*sqrt(mUW));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateWindMatrices();
|
||||
|
||||
Re = Vt * in.Wingchord / in.KinematicViscosity;
|
||||
|
||||
double densityD2 = 0.5*in.Density;
|
||||
|
||||
qbar = densityD2 * Vt2;
|
||||
qbarUW = densityD2 * (mUW);
|
||||
qbarUV = densityD2 * (AeroU2 + AeroV2);
|
||||
Mach = Vt / in.SoundSpeed;
|
||||
MachU = vMachUVW(eU) = vAeroUVW(eU) / in.SoundSpeed;
|
||||
vMachUVW(eV) = vAeroUVW(eV) / in.SoundSpeed;
|
||||
vMachUVW(eW) = vAeroUVW(eW) / in.SoundSpeed;
|
||||
|
||||
// Position
|
||||
|
||||
Vground = sqrt( in.vVel(eNorth)*in.vVel(eNorth) + in.vVel(eEast)*in.vVel(eEast) );
|
||||
|
||||
psigt = atan2(in.vVel(eEast), in.vVel(eNorth));
|
||||
if (psigt < 0.0) psigt += 2*M_PI;
|
||||
gamma = atan2(-in.vVel(eDown), Vground);
|
||||
|
||||
tat = in.Temperature*(1 + 0.2*Mach*Mach); // Total Temperature, isentropic flow
|
||||
tatc = RankineToCelsius(tat);
|
||||
|
||||
pt = PitotTotalPressure(Mach, in.Pressure);
|
||||
|
||||
if (abs(Mach) > 0.0) {
|
||||
vcas = VcalibratedFromMach(Mach, in.Pressure);
|
||||
veas = sqrt(2 * qbar / in.DensitySL);
|
||||
}
|
||||
else
|
||||
vcas = veas = 0.0;
|
||||
|
||||
vPilotAccel.InitMatrix();
|
||||
vNcg = in.vBodyAccel/in.StandardGravity;
|
||||
// Nz is Acceleration in "g's", along normal axis (-Z body axis)
|
||||
Nz = -vNcg(eZ);
|
||||
Ny = vNcg(eY);
|
||||
Nx = vNcg(eX);
|
||||
vPilotAccel = in.vBodyAccel + in.vPQRidot * in.ToEyePt;
|
||||
vPilotAccel += in.vPQRi * (in.vPQRi * in.ToEyePt);
|
||||
|
||||
vNwcg = mTb2w * vNcg;
|
||||
vNwcg(eZ) = 1.0 - vNwcg(eZ);
|
||||
|
||||
vPilotAccelN = vPilotAccel / in.StandardGravity;
|
||||
|
||||
// VRP computation
|
||||
vLocationVRP = in.vLocation.LocalToLocation( in.Tb2l * in.VRPBody );
|
||||
|
||||
// Recompute some derived values now that we know the dependent parameters values ...
|
||||
hoverbcg = in.DistanceAGL / in.Wingspan;
|
||||
|
||||
FGColumnVector3 vMac = in.Tb2l * in.RPBody;
|
||||
hoverbmac = (in.DistanceAGL - vMac(3)) / in.Wingspan;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
//
|
||||
// From Stevens and Lewis, "Aircraft Control and Simulation", 3rd Ed., the
|
||||
// transformation from body to wind axes is defined (where "a" is alpha and "B"
|
||||
// is beta):
|
||||
//
|
||||
// cos(a)*cos(B) sin(B) sin(a)*cos(B)
|
||||
// -cos(a)*sin(B) cos(B) -sin(a)*sin(B)
|
||||
// -sin(a) 0 cos(a)
|
||||
//
|
||||
// The transform from wind to body axes is then,
|
||||
//
|
||||
// cos(a)*cos(B) -cos(a)*sin(B) -sin(a)
|
||||
// sin(B) cos(B) 0
|
||||
// sin(a)*cos(B) -sin(a)*sin(B) cos(a)
|
||||
|
||||
void FGAuxiliary::UpdateWindMatrices(void)
|
||||
{
|
||||
double ca, cb, sa, sb;
|
||||
|
||||
ca = cos(alpha);
|
||||
sa = sin(alpha);
|
||||
cb = cos(beta);
|
||||
sb = sin(beta);
|
||||
|
||||
mTw2b(1,1) = ca*cb;
|
||||
mTw2b(1,2) = -ca*sb;
|
||||
mTw2b(1,3) = -sa;
|
||||
mTw2b(2,1) = sb;
|
||||
mTw2b(2,2) = cb;
|
||||
mTw2b(2,3) = 0.0;
|
||||
mTw2b(3,1) = sa*cb;
|
||||
mTw2b(3,2) = -sa*sb;
|
||||
mTw2b(3,3) = ca;
|
||||
|
||||
mTb2w = mTw2b.Transposed();
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAuxiliary::GetNlf(void) const
|
||||
{
|
||||
if (in.Mass != 0)
|
||||
return (in.vFw(3))/(in.Mass*slugtolb);
|
||||
else
|
||||
return 0.;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAuxiliary::GetLongitudeRelativePosition(void) const
|
||||
{
|
||||
return in.vLocation.GetDistanceTo(FDMExec->GetIC()->GetLongitudeRadIC(),
|
||||
in.vLocation.GetLatitude())* fttom;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAuxiliary::GetLatitudeRelativePosition(void) const
|
||||
{
|
||||
return in.vLocation.GetDistanceTo(in.vLocation.GetLongitude(),
|
||||
FDMExec->GetIC()->GetLatitudeRadIC())* fttom;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAuxiliary::GetDistanceRelativePosition(void) const
|
||||
{
|
||||
FGInitialCondition *ic = FDMExec->GetIC();
|
||||
return in.vLocation.GetDistanceTo(ic->GetLongitudeRadIC(),
|
||||
ic->GetLatitudeRadIC())* fttom;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
void FGAuxiliary::bind(void)
|
||||
{
|
||||
typedef double (FGAuxiliary::*PMF)(int) const;
|
||||
typedef double (FGAuxiliary::*PF)(void) const;
|
||||
PropertyManager->Tie("propulsion/tat-r", this, &FGAuxiliary::GetTotalTemperature);
|
||||
PropertyManager->Tie("propulsion/tat-c", this, &FGAuxiliary::GetTAT_C);
|
||||
PropertyManager->Tie("propulsion/pt-lbs_sqft", this, &FGAuxiliary::GetTotalPressure);
|
||||
PropertyManager->Tie("velocities/vc-fps", this, &FGAuxiliary::GetVcalibratedFPS);
|
||||
PropertyManager->Tie("velocities/vc-kts", this, &FGAuxiliary::GetVcalibratedKTS);
|
||||
PropertyManager->Tie("velocities/ve-fps", this, &FGAuxiliary::GetVequivalentFPS);
|
||||
PropertyManager->Tie("velocities/ve-kts", this, &FGAuxiliary::GetVequivalentKTS);
|
||||
PropertyManager->Tie("velocities/vtrue-fps", this, &FGAuxiliary::GetVtrueFPS);
|
||||
PropertyManager->Tie("velocities/vtrue-kts", this, &FGAuxiliary::GetVtrueKTS);
|
||||
PropertyManager->Tie("velocities/machU", this, &FGAuxiliary::GetMachU);
|
||||
PropertyManager->Tie("velocities/p-aero-rad_sec", this, eX, (PMF)&FGAuxiliary::GetAeroPQR);
|
||||
PropertyManager->Tie("velocities/q-aero-rad_sec", this, eY, (PMF)&FGAuxiliary::GetAeroPQR);
|
||||
PropertyManager->Tie("velocities/r-aero-rad_sec", this, eZ, (PMF)&FGAuxiliary::GetAeroPQR);
|
||||
PropertyManager->Tie("velocities/phidot-rad_sec", this, ePhi, (PMF)&FGAuxiliary::GetEulerRates);
|
||||
PropertyManager->Tie("velocities/thetadot-rad_sec", this, eTht, (PMF)&FGAuxiliary::GetEulerRates);
|
||||
PropertyManager->Tie("velocities/psidot-rad_sec", this, ePsi, (PMF)&FGAuxiliary::GetEulerRates);
|
||||
PropertyManager->Tie("velocities/u-aero-fps", this, eU, (PMF)&FGAuxiliary::GetAeroUVW);
|
||||
PropertyManager->Tie("velocities/v-aero-fps", this, eV, (PMF)&FGAuxiliary::GetAeroUVW);
|
||||
PropertyManager->Tie("velocities/w-aero-fps", this, eW, (PMF)&FGAuxiliary::GetAeroUVW);
|
||||
PropertyManager->Tie("velocities/vt-fps", this, &FGAuxiliary::GetVt);
|
||||
PropertyManager->Tie("velocities/mach", this, &FGAuxiliary::GetMach);
|
||||
PropertyManager->Tie("velocities/vg-fps", this, &FGAuxiliary::GetVground);
|
||||
PropertyManager->Tie("accelerations/a-pilot-x-ft_sec2", this, eX, (PMF)&FGAuxiliary::GetPilotAccel);
|
||||
PropertyManager->Tie("accelerations/a-pilot-y-ft_sec2", this, eY, (PMF)&FGAuxiliary::GetPilotAccel);
|
||||
PropertyManager->Tie("accelerations/a-pilot-z-ft_sec2", this, eZ, (PMF)&FGAuxiliary::GetPilotAccel);
|
||||
PropertyManager->Tie("accelerations/n-pilot-x-norm", this, eX, (PMF)&FGAuxiliary::GetNpilot);
|
||||
PropertyManager->Tie("accelerations/n-pilot-y-norm", this, eY, (PMF)&FGAuxiliary::GetNpilot);
|
||||
PropertyManager->Tie("accelerations/n-pilot-z-norm", this, eZ, (PMF)&FGAuxiliary::GetNpilot);
|
||||
PropertyManager->Tie("accelerations/Nx", this, &FGAuxiliary::GetNx);
|
||||
PropertyManager->Tie("accelerations/Ny", this, &FGAuxiliary::GetNy);
|
||||
PropertyManager->Tie("accelerations/Nz", this, &FGAuxiliary::GetNz);
|
||||
PropertyManager->Tie("forces/load-factor", this, &FGAuxiliary::GetNlf);
|
||||
PropertyManager->Tie("aero/alpha-rad", this, (PF)&FGAuxiliary::Getalpha);
|
||||
PropertyManager->Tie("aero/beta-rad", this, (PF)&FGAuxiliary::Getbeta);
|
||||
PropertyManager->Tie("aero/mag-beta-rad", this, (PF)&FGAuxiliary::GetMagBeta);
|
||||
PropertyManager->Tie("aero/alpha-deg", this, inDegrees, (PMF)&FGAuxiliary::Getalpha);
|
||||
PropertyManager->Tie("aero/beta-deg", this, inDegrees, (PMF)&FGAuxiliary::Getbeta);
|
||||
PropertyManager->Tie("aero/mag-beta-deg", this, inDegrees, (PMF)&FGAuxiliary::GetMagBeta);
|
||||
PropertyManager->Tie("aero/Re", this, &FGAuxiliary::GetReynoldsNumber);
|
||||
PropertyManager->Tie("aero/qbar-psf", this, &FGAuxiliary::Getqbar);
|
||||
PropertyManager->Tie("aero/qbarUW-psf", this, &FGAuxiliary::GetqbarUW);
|
||||
PropertyManager->Tie("aero/qbarUV-psf", this, &FGAuxiliary::GetqbarUV);
|
||||
PropertyManager->Tie("aero/alphadot-rad_sec", this, (PF)&FGAuxiliary::Getadot);
|
||||
PropertyManager->Tie("aero/betadot-rad_sec", this, (PF)&FGAuxiliary::Getbdot);
|
||||
PropertyManager->Tie("aero/alphadot-deg_sec", this, inDegrees, (PMF)&FGAuxiliary::Getadot);
|
||||
PropertyManager->Tie("aero/betadot-deg_sec", this, inDegrees, (PMF)&FGAuxiliary::Getbdot);
|
||||
PropertyManager->Tie("aero/h_b-cg-ft", this, &FGAuxiliary::GetHOverBCG);
|
||||
PropertyManager->Tie("aero/h_b-mac-ft", this, &FGAuxiliary::GetHOverBMAC);
|
||||
PropertyManager->Tie("flight-path/gamma-rad", this, &FGAuxiliary::GetGamma);
|
||||
PropertyManager->Tie("flight-path/gamma-deg", this, inDegrees, (PMF)&FGAuxiliary::GetGamma);
|
||||
PropertyManager->Tie("flight-path/psi-gt-rad", this, &FGAuxiliary::GetGroundTrack);
|
||||
|
||||
PropertyManager->Tie("position/distance-from-start-lon-mt", this, &FGAuxiliary::GetLongitudeRelativePosition);
|
||||
PropertyManager->Tie("position/distance-from-start-lat-mt", this, &FGAuxiliary::GetLatitudeRelativePosition);
|
||||
PropertyManager->Tie("position/distance-from-start-mag-mt", this, &FGAuxiliary::GetDistanceRelativePosition);
|
||||
PropertyManager->Tie("position/vrp-gc-latitude_deg", &vLocationVRP, &FGLocation::GetLatitudeDeg);
|
||||
PropertyManager->Tie("position/vrp-longitude_deg", &vLocationVRP, &FGLocation::GetLongitudeDeg);
|
||||
PropertyManager->Tie("position/vrp-radius-ft", &vLocationVRP, &FGLocation::GetRadius);
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
double FGAuxiliary::BadUnits(void) const
|
||||
{
|
||||
cerr << "Bad units" << endl; return 0.0;
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
// The bitmasked value choices are as follows:
|
||||
// unset: In this case (the default) JSBSim would only print
|
||||
// out the normally expected messages, essentially echoing
|
||||
// the config files as they are read. If the environment
|
||||
// variable is not set, debug_lvl is set to 1 internally
|
||||
// 0: This requests JSBSim not to output any messages
|
||||
// whatsoever.
|
||||
// 1: This value explicity requests the normal JSBSim
|
||||
// startup messages
|
||||
// 2: This value asks for a message to be printed out when
|
||||
// a class is instantiated
|
||||
// 4: When this value is set, a message is displayed when a
|
||||
// FGModel object executes its Run() method
|
||||
// 8: When this value is set, various runtime state variables
|
||||
// are printed out periodically
|
||||
// 16: When set various parameters are sanity checked and
|
||||
// a message is printed out when they go out of bounds
|
||||
|
||||
void FGAuxiliary::Debug(int from)
|
||||
{
|
||||
if (debug_lvl <= 0) return;
|
||||
|
||||
if (debug_lvl & 1) { // Standard console startup message output
|
||||
if (from == 0) { // Constructor
|
||||
|
||||
}
|
||||
}
|
||||
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
|
||||
if (from == 0) cout << "Instantiated: FGAuxiliary" << endl;
|
||||
if (from == 1) cout << "Destroyed: FGAuxiliary" << endl;
|
||||
}
|
||||
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
|
||||
}
|
||||
if (debug_lvl & 8 ) { // Runtime state variables
|
||||
}
|
||||
if (debug_lvl & 16) { // Sanity checking
|
||||
if (Mach > 100 || Mach < 0.00)
|
||||
cout << "FGPropagate::Mach is out of bounds: " << Mach << endl;
|
||||
if (qbar > 1e6 || qbar < 0.00)
|
||||
cout << "FGPropagate::qbar is out of bounds: " << qbar << endl;
|
||||
}
|
||||
if (debug_lvl & 64) {
|
||||
if (from == 0) { // Constructor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace JSBSim
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user