first commit

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

View File

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

View File

@@ -0,0 +1,57 @@
// AIWakeGroup.hxx -- Group of AI wake meshes for the computation of the induced
// wake.
//
// Written by Bertrand Coconnier, started April 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AIWAKEGROUP_HXX
#define _FG_AIWAKEGROUP_HXX
#include <simgear/props/propsfwd.hxx>
#include "FDM/AIWake/WakeMesh.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class FGAIAircraft;
class AIWakeGroup {
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
struct AIWakeData {
explicit AIWakeData(WakeMesh* m = nullptr) : mesh(m) {}
SGVec3d position {SGVec3d::zeros()};
SGQuatd Te2b {SGQuatd::unit()};
bool visited {false};
WakeMesh_ptr mesh;
};
std::map<int, AIWakeData> _aiWakeData;
SGPropertyNode_ptr _density_slugft;
public:
AIWakeGroup(void);
void AddAI(FGAIAircraft* ai);
SGVec3d getInducedVelocityAt(const SGVec3d& pt) const;
// Garbage collection
void gc(void);
};
#endif

View File

@@ -0,0 +1,82 @@
// AeroElement.cxx -- Mesh element for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <cmath>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include "AeroElement.hxx"
AeroElement::AeroElement(const SGVec3d& n1, const SGVec3d& n2,
const SGVec3d& n3, const SGVec3d& n4)
{
p1 = 0.75*n2 + 0.25*n1;
p2 = 0.75*n3 + 0.25*n4;
normal = normalize(cross(n3 - n1, n2 - n4));
collocationPt = 0.375*(n1 + n4) + 0.125*(n2 + n3);
}
SGVec3d AeroElement::vortexInducedVel(const SGVec3d& p, const SGVec3d& n1,
const SGVec3d& n2) const
{
SGVec3d r0 = n2-n1, r1 = p-n1, r2 = p-n2;
SGVec3d v = cross(r1, r2);
double vSqrNorm = dot(v, v);
double r1SqrNorm = dot(r1, r1);
double r2SqrNorm = dot(r2, r2);
if ((vSqrNorm < 1E-6) || (r1SqrNorm < 1E-6) || (r2SqrNorm < 1E-6))
return SGVec3d::zeros();
r1 /= sqrt(r1SqrNorm);
r2 /= sqrt(r2SqrNorm);
v *= dot(r0, r1-r2) / (4.0*M_PI*vSqrNorm);
return v;
}
SGVec3d AeroElement::semiInfVortexInducedVel(const SGVec3d& point,
const SGVec3d& vEnd,
const SGVec3d& vDir) const
{
SGVec3d r = point-vEnd;
double rSqrNorm = dot(r, r);
double denom = rSqrNorm - dot(r, vDir)*sqrt(rSqrNorm);
if (fabs(denom) < 1E-6)
return SGVec3d::zeros();
SGVec3d v = cross(r, vDir);
v /= 4*M_PI*denom;
return v;
}
SGVec3d AeroElement::getInducedVelocity(const SGVec3d& p) const
{
const SGVec3d w(-1.,0.,0.);
SGVec3d v = semiInfVortexInducedVel(p, p1, w);
v -= semiInfVortexInducedVel(p, p2, w);
v += vortexInducedVel(p, p1, p2);
return v;
}

View File

@@ -0,0 +1,48 @@
// AeroElement.hxx -- Mesh element for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AEROELEMENT_HXX
#define _FG_AEROELEMENT_HXX
#include <simgear/math/SGMathFwd.hxx>
class AeroElement : public SGReferenced {
public:
AeroElement(const SGVec3d& n1, const SGVec3d& n2, const SGVec3d& n3,
const SGVec3d& n4);
const SGVec3d& getNormal(void) const { return normal; }
const SGVec3d& getCollocationPoint(void) const { return collocationPt; }
SGVec3d getBoundVortex(void) const { return p2 - p1; }
SGVec3d getBoundVortexMidPoint(void) const { return 0.5*(p1+p2); }
SGVec3d getInducedVelocity(const SGVec3d& p) const;
private:
SGVec3d vortexInducedVel(const SGVec3d& p, const SGVec3d& n1,
const SGVec3d& n2) const;
SGVec3d semiInfVortexInducedVel(const SGVec3d& point, const SGVec3d& vEnd,
const SGVec3d& vDir) const;
SGVec3d p1, p2, normal, collocationPt;
};
typedef SGSharedPtr<AeroElement> AeroElement_ptr;
#endif

View File

@@ -0,0 +1,96 @@
// AircraftMesh.cxx -- Mesh for the computation of the wake induced force and
// moment on an aircraft.
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <vector>
#include <cmath>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGVec3.hxx>
#include <simgear/math/SGGeoc.hxx>
#include <simgear/math/SGGeod.hxx>
#include <simgear/math/SGQuat.hxx>
#include "AircraftMesh.hxx"
#include <FDM/flight.hxx>
#include "AIWakeGroup.hxx"
#include "AIModel/AIAircraft.hxx"
extern "C" {
#include "../LaRCsim/ls_matrix.h"
}
AircraftMesh::AircraftMesh(double _span, double _chord, const std::string& name)
: WakeMesh(_span, _chord, name)
{
collPt.resize(nelm, SGVec3d::zeros());
midPt.resize(nelm, SGVec3d::zeros());
}
void AircraftMesh::setPosition(const SGVec3d& _pos, const SGQuatd& orient)
{
SGVec3d pos = _pos * SG_METER_TO_FEET;
SGGeoc geoc = SGGeoc::fromCart(pos);
SGQuatd Te2l = SGQuatd::fromLonLatRad(geoc.getLongitudeRad(),
geoc.getLatitudeRad());
Te2b = Te2l * orient;
for (int i=0; i<nelm; ++i) {
SGVec3d pt = elements[i]->getCollocationPoint();
collPt[i] = pos + Te2b.backTransform(pt);
pt = elements[i]->getBoundVortexMidPoint();
midPt[i] = pos + Te2b.backTransform(pt);
}
}
SGVec3d AircraftMesh::GetForce(const AIWakeGroup& wg, const SGVec3d& vel,
double rho)
{
std::vector<double> rhs;
rhs.resize(nelm, 0.0);
for (int i=0; i<nelm; ++i)
rhs[i] = dot(elements[i]->getNormal(),
Te2b.transform(wg.getInducedVelocityAt(collPt[i])));
for (int i=1; i<=nelm; ++i) {
Gamma[i][1] = 0.0;
for (int k=1; k<=nelm; ++k)
Gamma[i][1] += influenceMtx[i][k]*rhs[k-1];
}
SGVec3d f(0.,0.,0.);
moment = SGVec3d::zeros();
for (int i=0; i<nelm; ++i) {
SGVec3d mp = elements[i]->getBoundVortexMidPoint();
SGVec3d v = Te2b.transform(wg.getInducedVelocityAt(midPt[i]));
v += getInducedVelocityAt(mp);
// The minus sign before vel to transform the aircraft velocity from the
// body frame to wind frame.
SGVec3d Fi = rho*Gamma[i+1][1]*cross(v-vel,
elements[i]->getBoundVortex());
f += Fi;
moment += cross(mp, Fi);
}
return f;
}

View File

@@ -0,0 +1,50 @@
// AircraftMesh.hxx -- Mesh for the computation of the wake induced force and
// moment on an aircraft.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_AEROMESH_HXX
#define _FG_AEROMESH_HXX
#include "WakeMesh.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class FGAIAircraft;
class AIWakeGroup;
class AircraftMesh : public WakeMesh {
public:
AircraftMesh(double _span, double _chord, const std::string& name);
void setPosition(const SGVec3d& pos, const SGQuatd& orient);
SGVec3d GetForce(const AIWakeGroup& wg, const SGVec3d& vel, double rho);
const SGVec3d& GetMoment(void) const { return moment; };
private:
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
std::vector<SGVec3d> collPt, midPt;
SGQuatd Te2b;
SGVec3d moment;
};
typedef SGSharedPtr<AircraftMesh> AircraftMesh_ptr;
#endif

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

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

View File

@@ -0,0 +1,51 @@
// WakeMesh.hxx -- Mesh for the computation of a wing wake.
//
// Written by Bertrand Coconnier, started March 2017.
//
// Copyright (C) 2017 Bertrand Coconnier - bcoconni@users.sf.net
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FG_WAKEMESH_HXX
#define _FG_WAKEMESH_HXX
#include <string>
#include "AeroElement.hxx"
namespace FGTestApi { namespace PrivateAccessor { namespace FDM { class Accessor; } } }
class WakeMesh : public SGReferenced {
public:
WakeMesh(double _span, double _chord, const std::string& aircraft_name);
virtual ~WakeMesh();
double computeAoA(double vel, double rho, double weight);
SGVec3d getInducedVelocityAt(const SGVec3d& at) const;
protected:
friend class FGTestApi::PrivateAccessor::FDM::Accessor;
int nelm;
double span, chord;
std::vector<AeroElement_ptr> elements;
double **influenceMtx, **Gamma;
};
typedef SGSharedPtr<WakeMesh> WakeMesh_ptr;
#endif

186
src/FDM/CMakeLists.txt Normal file
View 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()

View 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

View 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

View 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

View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

713
src/FDM/JSBSim/FGFDMExec.h Normal file
View 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 &lt;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 &lt;output> &lt;/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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

323
src/FDM/JSBSim/JSBSim.hxx Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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

View 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.
}

View 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

View 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
}
}
}
}

View 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

View 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

View 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

View 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;
}
}
}
}

View 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

View 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
}
}
}
}

View 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

View 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();
}
}

View 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

View 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);
}
}

View 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

View 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;
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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
}
}
}
}

View 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

View 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

View 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

View 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
}
}

View 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

View 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
}
}
}
}

View 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
&lt;!-- and --&gt; symbols. The aircraft and initialization files
to be used are specified in the &quot;use&quot; lines. Next,
comes the &quot;run&quot; section, where the conditions are
described in &quot;event&quot; 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

View 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]);
}
}
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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
}
}
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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 = earths 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, √(1a))
// 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 = earths 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

View 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

View 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 );
}
}

View 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

View 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();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}

View 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

View 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

View 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

View 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;
}
}

View 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

View 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

View 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

View 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);
}
}

View 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

View 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

View 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

View 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
}
}
}
}

View 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

View 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);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}

View 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

View 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

View 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
}
}
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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