1
0
Fork 0

Sync w. JSBSim CVS

This commit is contained in:
ehofman 2004-06-14 11:40:45 +00:00
parent 56f085b6f1
commit 8e1482f746
86 changed files with 5305 additions and 4950 deletions

View file

@ -37,7 +37,7 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGAerodynamics.h"
#include "FGTranslation.h"
#include "FGPropagate.h"
#include "FGAircraft.h"
#include "FGState.h"
#include "FGMassBalance.h"
@ -109,15 +109,15 @@ bool FGAerodynamics::Run(void)
if (!FGModel::Run()) {
twovel = 2*Translation->GetVt();
twovel = 2*Auxiliary->GetVt();
if (twovel != 0) {
bi2vel = Aircraft->GetWingSpan() / twovel;
ci2vel = Aircraft->Getcbar() / twovel;
}
alphaw = Translation->Getalpha() + Aircraft->GetWingIncidence();
alphaw = Auxiliary->Getalpha() + Aircraft->GetWingIncidence();
alpha = Translation->Getalpha();
alpha = Auxiliary->Getalpha();
if (alphaclmax != 0) {
if (alpha > 0.85*alphaclmax) {
@ -146,8 +146,8 @@ bool FGAerodynamics::Run(void)
//correct signs of drag and lift to wind axes convention
//positive forward, right, down
if ( Translation->Getqbar() > 0) {
clsq = vFs(eLift) / (Aircraft->GetWingArea()*Translation->Getqbar());
if ( Auxiliary->Getqbar() > 0) {
clsq = vFs(eLift) / (Aircraft->GetWingArea()*Auxiliary->Getqbar());
clsq *= clsq;
}
if ( vFs(eDrag) > 0) {

View file

@ -75,7 +75,7 @@ INCLUDES
#include "FGAerodynamics.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGPropertyManager.h"
namespace JSBSim {
@ -125,7 +125,6 @@ bool FGAircraft::Run(void)
if (!FGModel::Run()) { // if false then execute this Run()
vForces.InitMatrix();
vForces += Aerodynamics->GetForces();
vForces += Inertial->GetForces();
vForces += Propulsion->GetForces();
vForces += GroundReactions->GetForces();
@ -229,7 +228,6 @@ bool FGAircraft::Load(FGConfigFile* AC_cfg)
} else if (parameter == "AC_VRP") {
*AC_cfg >> vXYZvrp(eX) >> vXYZvrp(eY) >> vXYZvrp(eZ);
if (debug_lvl > 0) cout << " Visual Ref Pt (x, y, z): " << vXYZvrp << endl;
Position->SetVRP(vXYZvrp);
} else if (parameter == "AC_POINTMASS") {
*AC_cfg >> pmWt >> pmX >> pmY >> pmZ;
MassBalance->AddPointMass(pmWt, pmX, pmY, pmZ);

View file

@ -35,7 +35,7 @@ HISTORY
--------------------------------------------------------------------------------
11/24/98 JSB Created
07/23/99 TP Added implementation of 1959 Standard Atmosphere
Moved calculation of Mach number to FGTranslation
Moved calculation of Mach number to FGPropagate
Later updated to '76 model
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
@ -51,7 +51,7 @@ INCLUDES
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGInertial.h"
#include "FGPropertyManager.h"
@ -130,7 +130,7 @@ bool FGAtmosphere::Run(void)
if (!FGModel::Run()) { // if false then execute this Run()
//do temp, pressure, and density first
if (!useExternal) {
h = Position->Geth();
h = Propagate->Geth();
Calculate(h);
}
@ -250,10 +250,20 @@ void FGAtmosphere::Calculate(double altitude)
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Return the pressure at an arbitrary altitude and then restore the internal state
double FGAtmosphere::GetPressure(double alt) {
Calculate(alt);
double p = *pressure;
// Reset the internal atmospheric state
Run();
return(p);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// square a value, but preserve the original sign
static inline double
square_signed (double value)
static inline double square_signed (double value)
{
if (value < 0)
return value * value * -1;
@ -261,6 +271,8 @@ square_signed (double value)
return value * value;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::Turbulence(void)
{
switch (turbType) {
@ -292,13 +304,13 @@ void FGAtmosphere::Turbulence(void)
// Diminish turbulence within three wingspans
// of the ground
vTurbulence = TurbGain * Magnitude * vDirection;
double HOverBMAC = Position->GetHOverBMAC();
double HOverBMAC = Auxiliary->GetHOverBMAC();
if (HOverBMAC < 3.0)
vTurbulence *= (HOverBMAC / 3.0) * (HOverBMAC / 3.0);
vTurbulenceGrad = TurbGain*MagnitudeAccel * vDirection;
vBodyTurbGrad = State->GetTl2b()*vTurbulenceGrad;
vBodyTurbGrad = Propagate->GetTl2b()*vTurbulenceGrad;
if (Aircraft->GetWingSpan() > 0) {
vTurbPQR(eP) = vBodyTurbGrad(eY)/Aircraft->GetWingSpan();
@ -341,7 +353,7 @@ void FGAtmosphere::Turbulence(void)
// Diminish z-vector within two wingspans
// of the ground
double HOverBMAC = Position->GetHOverBMAC();
double HOverBMAC = Auxiliary->GetHOverBMAC();
if (HOverBMAC < 2.0)
vDirection(eZ) *= HOverBMAC / 2.0;
@ -350,7 +362,7 @@ void FGAtmosphere::Turbulence(void)
vTurbulence = TurbGain*Magnitude * vDirection;
vTurbulenceGrad = TurbGain*MagnitudeAccel * vDirection;
vBodyTurbGrad = State->GetTl2b()*vTurbulenceGrad;
vBodyTurbGrad = Propagate->GetTl2b()*vTurbulenceGrad;
vTurbPQR(eP) = vBodyTurbGrad(eY)/Aircraft->GetWingSpan();
if (Aircraft->GetHTailArm() > 0)
vTurbPQR(eQ) = vBodyTurbGrad(eZ)/Aircraft->GetHTailArm();
@ -387,7 +399,6 @@ void FGAtmosphere::UseInternal(void) {
useExternal=false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::bind(void)
@ -397,8 +408,8 @@ void FGAtmosphere::bind(void)
&FGAtmosphere::GetTemperature);
PropertyManager->Tie("atmosphere/rho-slugs_ft3", this,
&FGAtmosphere::GetDensity);
PropertyManager->Tie("atmosphere/P-psf", this,
&FGAtmosphere::GetPressure);
// PropertyManager->Tie("atmosphere/P-psf", this,
// &FGAtmosphere::GetPressure);
PropertyManager->Tie("atmosphere/a-fps", this,
&FGAtmosphere::GetSoundSpeed);
PropertyManager->Tie("atmosphere/T-sl-R", this,
@ -433,7 +444,7 @@ void FGAtmosphere::unbind(void)
{
PropertyManager->Untie("atmosphere/T-R");
PropertyManager->Untie("atmosphere/rho-slugs_ft3");
PropertyManager->Untie("atmosphere/P-psf");
// PropertyManager->Untie("atmosphere/P-psf");
PropertyManager->Untie("atmosphere/a-fps");
PropertyManager->Untie("atmosphere/T-sl-R");
PropertyManager->Untie("atmosphere/rho-sl-slugs_ft3");

View file

@ -28,7 +28,7 @@ HISTORY
--------------------------------------------------------------------------------
11/24/98 JSB Created
07/23/99 TP Added implementation of 1959 Standard Atmosphere
Moved calculation of Mach number to FGTranslation
Moved calculation of Mach number to FGPropagate
Updated to '76 model
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -92,6 +92,8 @@ public:
inline double GetDensity(void) const {return *density;}
/// Returns the pressure in psf.
inline double GetPressure(void) const {return *pressure;}
/// Returns the pressure at an arbitrary altitude in psf
double GetPressure(double alt);
/// Returns the speed of sound in ft/sec.
inline double GetSoundSpeed(void) const {return soundspeed;}

View file

@ -42,8 +42,7 @@ INCLUDES
#include "FGAuxiliary.h"
#include "FGAerodynamics.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPropagate.h"
#include "FGAtmosphere.h"
#include "FGState.h"
#include "FGFDMExec.h"
@ -64,11 +63,26 @@ CLASS IMPLEMENTATION
FGAuxiliary::FGAuxiliary(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGAuxiliary";
vcas = veas = mach = qbar = pt = tat = 0;
vcas = veas = pt = tat = 0;
psl = rhosl = 1;
earthPosAngle = 0.0;
qbar = 0;
qbarUW = 0.0;
qbarUV = 0.0;
Mach = 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;
vPilotAccel.InitMatrix();
vPilotAccelN.InitMatrix();
vToEyePt.InitMatrix();
vAeroPQR.InitMatrix();
vEulerRates.InitMatrix();
bind();
@ -87,93 +101,136 @@ FGAuxiliary::~FGAuxiliary()
bool FGAuxiliary::Run()
{
double A,B,D;
double A,B,D, hdot_Vt;
const FGColumnVector3& vPQR = Propagate->GetPQR();
const FGColumnVector3& vUVW = Propagate->GetUVW();
const FGColumnVector3& vUVWdot = Propagate->GetUVWdot();
const FGColumnVector3& vVel = Propagate->GetVel();
if (!FGModel::Run()) {
GetState();
if (!FGModel::Run())
{
p = Atmosphere->GetPressure();
rhosl = Atmosphere->GetDensitySL();
psl = Atmosphere->GetPressureSL();
sat = Atmosphere->GetTemperature();
//calculate total temperature assuming isentropic flow
tat=sat*(1 + 0.2*mach*mach);
tatc=RankineToCelsius(tat);
// Rotation
if (mach < 1) { //calculate total pressure assuming isentropic flow
pt = p*pow((1 + 0.2*machU*machU),3.5);
double cTht = Propagate->GetCostht();
double cPhi = Propagate->GetCosphi();
double sPhi = Propagate->GetSinphi();
vEulerRates(eTht) = vPQR(eQ)*cPhi - vPQR(eR)*sPhi;
if (cTht != 0.0) {
vEulerRates(ePsi) = (vPQR(eQ)*sPhi + vPQR(eR)*cPhi)/cTht;
vEulerRates(ePhi) = vPQR(eP) + vEulerRates(ePsi)*sPhi;
}
vAeroPQR = vPQR + Atmosphere->GetTurbPQR();
// Translation
vAeroUVW = vUVW + Propagate->GetTl2b()*Atmosphere->GetWindNED();
Vt = vAeroUVW.Magnitude();
if ( Vt > 0.05) {
if (vAeroUVW(eW) != 0.0)
alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
if (vAeroUVW(eV) != 0.0)
beta = vAeroUVW(eU)*vAeroUVW(eU)+vAeroUVW(eW)*vAeroUVW(eW) > 0.0 ? atan2(vAeroUVW(eV),
sqrt(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW))) : 0.0;
double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
double signU=1;
if (vAeroUVW(eU) != 0.0)
signU = vAeroUVW(eU)/fabs(vAeroUVW(eU));
if ( (mUW == 0.0) || (Vt == 0.0) ) {
adot = 0.0;
bdot = 0.0;
} else {
adot = (vAeroUVW(eU)*vUVWdot(eW) - vAeroUVW(eW)*vUVWdot(eU))/mUW;
bdot = (signU*mUW*vUVWdot(eV) - vAeroUVW(eV)*(vAeroUVW(eU)*vUVWdot(eU)
+ vAeroUVW(eW)*vUVWdot(eW)))/(Vt*Vt*sqrt(mUW));
}
} 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
alpha = beta = adot = bdot = 0;
}
B = 5.76*machU*machU/(5.6*machU*machU - 0.8);
qbar = 0.5*Atmosphere->GetDensity()*Vt*Vt;
qbarUW = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
qbarUV = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eV)*vAeroUVW(eV));
Mach = Vt / Atmosphere->GetSoundSpeed();
MachU = vMachUVW(eU) = vAeroUVW(eU) / Atmosphere->GetSoundSpeed();
vMachUVW(eV) = vAeroUVW(eV) / Atmosphere->GetSoundSpeed();
vMachUVW(eW) = vAeroUVW(eW) / Atmosphere->GetSoundSpeed();
// The denominator above is zero for Mach ~ 0.38, for which
// we'll never be here, so we're safe
// Position
D = (2.8*machU*machU-0.4)*0.4167;
Vground = sqrt( vVel(eNorth)*vVel(eNorth) + vVel(eEast)*vVel(eEast) );
if (vVel(eNorth) == 0) psigt = 0;
else psigt = atan2(vVel(eEast), vVel(eNorth));
if (psigt < 0.0) psigt += 2*M_PI;
if (Vt != 0) {
hdot_Vt = -vVel(eDown)/Vt;
if (fabs(hdot_Vt) <= 1) gamma = asin(hdot_Vt);
} else {
gamma = 0.0;
}
tat = sat*(1 + 0.2*Mach*Mach); // Total Temperature, isentropic flow
tatc = RankineToCelsius(tat);
if (MachU < 1) { // Calculate total pressure assuming isentropic flow
pt = p*pow((1 + 0.2*MachU*MachU),3.5);
} else {
// Use Rayleigh pitot tube formula for normal shock in front of pitot tube
B = 5.76*MachU*MachU/(5.6*MachU*MachU - 0.8);
D = (2.8*MachU*MachU-0.4)*0.4167;
pt = p*pow(B,3.5)*D;
}
A = pow(((pt-p)/psl+1),0.28571);
if (machU > 0.0) {
if (MachU > 0.0) {
vcas = sqrt(7*psl/rhosl*(A-1));
veas = sqrt(2*qbar/rhosl);
} else {
vcas = veas = 0.0;
}
// Pilot sensed accelerations are calculated here. This is used
// for the coordinated turn ball instrument. Motion base platforms sometimes
// use the derivative of pilot sensed accelerations as the driving parameter,
// rather than straight accelerations.
//
// The theory behind pilot-sensed calculations is presented:
//
// For purposes of discussion and calculation, assume for a minute that the
// pilot is in space and motionless in inertial space. She will feel
// no accelerations. If the aircraft begins to accelerate along any axis or
// axes (without rotating), the pilot will sense those accelerations. If
// any rotational moment is applied, the pilot will sense an acceleration
// due to that motion in the amount:
//
// [wdot X R] + [w X (w X R)]
// Term I Term II
//
// where:
//
// wdot = omegadot, the rotational acceleration rate vector
// w = omega, the rotational rate vector
// R = the vector from the aircraft CG to the pilot eyepoint
//
// The sum total of these two terms plus the acceleration of the aircraft
// body axis gives the acceleration the pilot senses in inertial space.
// In the presence of a large body such as a planet, a gravity field also
// provides an accelerating attraction. This acceleration can be transformed
// from the reference frame of the planet so as to be expressed in the frame
// of reference of the aircraft. This gravity field accelerating attraction
// is felt by the pilot as a force on her tushie as she sits in her aircraft
// on the runway awaiting takeoff clearance.
//
// In JSBSim the acceleration of the body frame in inertial space is given
// by the F = ma relation. If the vForces vector is divided by the aircraft
// mass, the acceleration vector is calculated. The term wdot is equivalent
// to the JSBSim vPQRdot vector, and the w parameter is equivalent to vPQR.
// The radius R is calculated below in the vector vToEyePt.
vPilotAccel.InitMatrix();
if ( Translation->GetVt() > 1 ) {
if ( Vt > 1.0 ) {
vPilotAccel = Aerodynamics->GetForces()
+ Propulsion->GetForces()
+ GroundReactions->GetForces();
vPilotAccel /= MassBalance->GetMass();
vToEyePt = MassBalance->StructuralToBody(Aircraft->GetXYZep());
vPilotAccel += Rotation->GetPQRdot() * vToEyePt;
vPilotAccel += Rotation->GetPQR() * (Rotation->GetPQR() * vToEyePt);
vPilotAccel += Propagate->GetPQRdot() * vToEyePt;
vPilotAccel += vPQR * (vPQR * vToEyePt);
} else {
vPilotAccel = -1*( State->GetTl2b() * Inertial->GetGravity() );
vPilotAccel = Propagate->GetTl2b() * FGColumnVector3( 0.0, 0.0, Inertial->gravity() );
}
vPilotAccelN = vPilotAccel/Inertial->gravity();
earthPosAngle += State->Getdt()*Inertial->omega();
// VRP computation
const FGLocation& vLocation = Propagate->GetLocation();
FGColumnVector3 vrpStructural = Aircraft->GetXYZvrp();
FGColumnVector3 vrpBody = MassBalance->StructuralToBody( vrpStructural );
FGColumnVector3 vrpLocal = Propagate->GetTb2l() * vrpBody;
vLocationVRP = vLocation.LocalToLocation( vrpLocal );
// Recompute some derived values now that we know the dependent parameters values ...
hoverbcg = Propagate->GetDistanceAGL() / Aircraft->GetWingSpan();
FGColumnVector3 vMac = Propagate->GetTb2l()*MassBalance->StructuralToBody(Aircraft->GetXYZrp());
hoverbmac = (Propagate->GetDistanceAGL() + vMac(3)) / Aircraft->GetWingSpan();
return false;
} else {
return true;
@ -184,26 +241,24 @@ bool FGAuxiliary::Run()
double FGAuxiliary::GetHeadWind(void)
{
double psiw,vw,psi;
double psiw,vw;
psiw = Atmosphere->GetWindPsi();
psi = Rotation->Getpsi();
vw = Atmosphere->GetWindNED().Magnitude();
return vw*cos(psiw - psi);
return vw*cos(psiw - Propagate->Getpsi());
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::GetCrossWind(void)
{
double psiw,vw,psi;
double psiw,vw;
psiw = Atmosphere->GetWindPsi();
psi = Rotation->Getpsi();
vw = Atmosphere->GetWindNED().Magnitude();
return vw*sin(psiw - psi);
return vw*sin(psiw - Propagate->Getpsi());
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -211,43 +266,53 @@ double FGAuxiliary::GetCrossWind(void)
void FGAuxiliary::bind(void)
{
typedef double (FGAuxiliary::*PMF)(int) const;
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/machU", this,
&FGAuxiliary::GetMachU);
PropertyManager->Tie("velocities/tat-r", this,
&FGAuxiliary::GetTotalTemperature);
PropertyManager->Tie("velocities/tat-c", this,
&FGAuxiliary::GetTAT_C);
PropertyManager->Tie("velocities/pt-lbs_sqft", this,
&FGAuxiliary::GetTotalPressure);
PropertyManager->Tie("accelerations/a-pilot-x-ft_sec2", this,1,
(PMF)&FGAuxiliary::GetPilotAccel);
PropertyManager->Tie("accelerations/a-pilot-y-ft_sec2", this,2,
(PMF)&FGAuxiliary::GetPilotAccel);
PropertyManager->Tie("accelerations/a-pilot-z-ft_sec2", this,3,
(PMF)&FGAuxiliary::GetPilotAccel);
PropertyManager->Tie("accelerations/n-pilot-x-norm", this,1,
(PMF)&FGAuxiliary::GetNpilot);
PropertyManager->Tie("accelerations/n-pilot-y-norm", this,2,
(PMF)&FGAuxiliary::GetNpilot);
PropertyManager->Tie("accelerations/n-pilot-z-norm", this,3,
(PMF)&FGAuxiliary::GetNpilot);
PropertyManager->Tie("position/epa-rad", this,
&FGAuxiliary::GetEarthPositionAngle);
/* PropertyManager->Tie("atmosphere/headwind-fps", this,
&FGAuxiliary::GetHeadWind,
true);
PropertyManager->Tie("atmosphere/crosswind-fps", this,
&FGAuxiliary::GetCrossWind,
true); */
typedef double (FGAuxiliary::*PF)(void) const;
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/machU", this, &FGAuxiliary::GetMachU);
PropertyManager->Tie("velocities/tat-r", this, &FGAuxiliary::GetTotalTemperature);
PropertyManager->Tie("velocities/tat-c", this, &FGAuxiliary::GetTAT_C);
PropertyManager->Tie("velocities/pt-lbs_sqft", this, &FGAuxiliary::GetTotalPressure);
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, &FGAuxiliary::SetVt, true);
PropertyManager->Tie("velocities/mach-norm", this, &FGAuxiliary::GetMach, &FGAuxiliary::SetMach, true);
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("position/epa-rad", this, &FGAuxiliary::GetEarthPositionAngle);
/* PropertyManager->Tie("atmosphere/headwind-fps", this, &FGAuxiliary::GetHeadWind, true);
PropertyManager->Tie("atmosphere/crosswind-fps", this, &FGAuxiliary::GetCrossWind, true); */
PropertyManager->Tie("aero/alpha-rad", this, (PF)&FGAuxiliary::Getalpha, &FGAuxiliary::Setalpha, true);
PropertyManager->Tie("aero/beta-rad", this, (PF)&FGAuxiliary::Getbeta, &FGAuxiliary::Setbeta, true);
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/qbar-psf", this, &FGAuxiliary::Getqbar, &FGAuxiliary::Setqbar, true);
PropertyManager->Tie("aero/qbarUW-psf", this, &FGAuxiliary::GetqbarUW, &FGAuxiliary::SetqbarUW, true);
PropertyManager->Tie("aero/qbarUV-psf", this, &FGAuxiliary::GetqbarUV, &FGAuxiliary::SetqbarUV, true);
PropertyManager->Tie("aero/alphadot-rad_sec", this, (PF)&FGAuxiliary::Getadot, &FGAuxiliary::Setadot, true);
PropertyManager->Tie("aero/betadot-rad_sec", this, (PF)&FGAuxiliary::Getbdot, &FGAuxiliary::Setbdot, true);
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, &FGAuxiliary::SetGamma);
PropertyManager->Tie("flight-path/psi-gt-rad", this, &FGAuxiliary::GetGroundTrack);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -261,6 +326,19 @@ void FGAuxiliary::unbind(void)
PropertyManager->Untie("velocities/machU");
PropertyManager->Untie("velocities/tat-r");
PropertyManager->Untie("velocities/tat-c");
PropertyManager->Untie("velocities/p-aero-rad_sec");
PropertyManager->Untie("velocities/q-aero-rad_sec");
PropertyManager->Untie("velocities/r-aero-rad_sec");
PropertyManager->Untie("velocities/pt-lbs_sqft");
PropertyManager->Untie("velocities/phidot-rad_sec");
PropertyManager->Untie("velocities/thetadot-rad_sec");
PropertyManager->Untie("velocities/psidot-rad_sec");
PropertyManager->Untie("velocities/u-aero-fps");
PropertyManager->Untie("velocities/v-aero-fps");
PropertyManager->Untie("velocities/w-aero-fps");
PropertyManager->Untie("velocities/vt-fps");
PropertyManager->Untie("velocities/mach-norm");
PropertyManager->Untie("velocities/vg-fps");
PropertyManager->Untie("accelerations/a-pilot-x-ft_sec2");
PropertyManager->Untie("accelerations/a-pilot-y-ft_sec2");
PropertyManager->Untie("accelerations/a-pilot-z-ft_sec2");
@ -270,20 +348,23 @@ void FGAuxiliary::unbind(void)
PropertyManager->Untie("position/epa-rad");
/* PropertyManager->Untie("atmosphere/headwind-fps");
PropertyManager->Untie("atmosphere/crosswind-fps"); */
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAuxiliary::GetState(void)
{
qbar = Translation->Getqbar();
mach = Translation->GetMach();
machU= Translation->GetMachU();
p = Atmosphere->GetPressure();
rhosl = Atmosphere->GetDensitySL();
psl = Atmosphere->GetPressureSL();
sat = Atmosphere->GetTemperature();
PropertyManager->Untie("aero/qbar-psf");
PropertyManager->Untie("aero/qbarUW-psf");
PropertyManager->Untie("aero/qbarUV-psf");
PropertyManager->Untie("aero/alpha-rad");
PropertyManager->Untie("aero/beta-rad");
PropertyManager->Untie("aero/alpha-deg");
PropertyManager->Untie("aero/beta-deg");
PropertyManager->Untie("aero/alphadot-rad_sec");
PropertyManager->Untie("aero/betadot-rad_sec");
PropertyManager->Untie("aero/mag-beta-rad");
PropertyManager->Untie("aero/alphadot-deg_sec");
PropertyManager->Untie("aero/betadot-deg_sec");
PropertyManager->Untie("aero/mag-beta-deg");
PropertyManager->Untie("aero/h_b-cg-ft");
PropertyManager->Untie("aero/h_b-mac-ft");
PropertyManager->Untie("flight-path/gamma-rad");
PropertyManager->Untie("flight-path/psi-gt-rad");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -323,6 +404,10 @@ void FGAuxiliary::Debug(int from)
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

View file

@ -41,6 +41,8 @@ INCLUDES
#include "FGModel.h"
#include "FGColumnVector3.h"
#include "FGLocation.h"
#include "FGPropagate.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
@ -59,6 +61,44 @@ CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Encapsulates various uncategorized scheduled functions.
Pilot sensed accelerations are calculated here. This is used
for the coordinated turn ball instrument. Motion base platforms sometimes
use the derivative of pilot sensed accelerations as the driving parameter,
rather than straight accelerations.
The theory behind pilot-sensed calculations is presented:
For purposes of discussion and calculation, assume for a minute that the
pilot is in space and motionless in inertial space. She will feel
no accelerations. If the aircraft begins to accelerate along any axis or
axes (without rotating), the pilot will sense those accelerations. If
any rotational moment is applied, the pilot will sense an acceleration
due to that motion in the amount:
[wdot X R] + [w X (w X R)]
Term I Term II
where:
wdot = omegadot, the rotational acceleration rate vector
w = omega, the rotational rate vector
R = the vector from the aircraft CG to the pilot eyepoint
The sum total of these two terms plus the acceleration of the aircraft
body axis gives the acceleration the pilot senses in inertial space.
In the presence of a large body such as a planet, a gravity field also
provides an accelerating attraction. This acceleration can be transformed
from the reference frame of the planet so as to be expressed in the frame
of reference of the aircraft. This gravity field accelerating attraction
is felt by the pilot as a force on her tushie as she sits in her aircraft
on the runway awaiting takeoff clearance.
In JSBSim the acceleration of the body frame in inertial space is given
by the F = ma relation. If the vForces vector is divided by the aircraft
mass, the acceleration vector is calculated. The term wdot is equivalent
to the JSBSim vPQRdot vector, and the w parameter is equivalent to vPQR.
The radius R is calculated below in the vector vToEyePt.
@author Tony Peden, Jon Berndt
@version $Id$
*/
@ -72,6 +112,7 @@ public:
/** Constructor
@param Executive a pointer to the parent executive object */
FGAuxiliary(FGFDMExec* Executive);
/// Destructor
~FGAuxiliary();
@ -79,49 +120,122 @@ public:
@return false if no error */
bool Run(void);
// Use FGInitialCondition to set these speeds
inline double GetVcalibratedFPS(void) const { return vcas; }
inline double GetVcalibratedKTS(void) const { return vcas*fpstokts; }
inline double GetVequivalentFPS(void) const { return veas; }
inline double GetVequivalentKTS(void) const { return veas*fpstokts; }
inline double GetMachU(void) const { return machU; }
// GET functions
inline double GetTotalTemperature(void) const { return tat; }
inline double GetTAT_C(void) const { return tatc; }
// Atmospheric parameters GET functions
double GetVcalibratedFPS(void) const { return vcas; }
double GetVcalibratedKTS(void) const { return vcas*fpstokts; }
double GetVequivalentFPS(void) const { return veas; }
double GetVequivalentKTS(void) const { return veas*fpstokts; }
// total pressure above is freestream total pressure for subsonic only
// for supersonic it is the 1D total pressure behind a normal shock
inline double GetTotalPressure(void) const { return pt; }
double GetTotalPressure(void) const { return pt; }
double GetTotalTemperature(void) const { return tat; }
double GetTAT_C(void) const { return tatc; }
inline FGColumnVector3& GetPilotAccel(void) { return vPilotAccel; }
inline double GetPilotAccel(int idx) const { return vPilotAccel(idx); }
FGColumnVector3 GetNpilot(void) const { return vPilotAccelN; }
double GetNpilot(int idx) const { return vPilotAccelN(idx); }
double GetPilotAccel(int idx) const { return vPilotAccel(idx); }
double GetNpilot(int idx) const { return vPilotAccelN(idx); }
double GetAeroPQR(int axis) const { return vAeroPQR(axis); }
double GetEulerRates(int axis) const { return vEulerRates(axis); }
inline double GetEarthPositionAngle(void) const { return earthPosAngle; }
const FGColumnVector3& GetPilotAccel (void) const { return vPilotAccel; }
const FGColumnVector3& GetNpilot (void) const { return vPilotAccelN; }
const FGColumnVector3& GetAeroPQR (void) const { return vAeroPQR; }
const FGColumnVector3& GetEulerRates (void) const { return vEulerRates; }
const FGColumnVector3& GetAeroUVW (void) const { return vAeroUVW; }
const FGLocation& GetLocationVRP(void) const { return vLocationVRP; }
double GethVRP(void) const { return vLocationVRP.GetRadius() - Propagate->GetSeaLevelRadius(); }
double GetAeroUVW (int idx) const { return vAeroUVW(idx); }
double Getalpha (void) const { return alpha; }
double Getbeta (void) const { return beta; }
double Getadot (void) const { return adot; }
double Getbdot (void) const { return bdot; }
double GetMagBeta (void) const { return fabs(beta); }
double Getalpha (int unit) const { if (unit == inDegrees) return alpha*radtodeg;
else cerr << "Bad units" << endl; return 0.0;}
double Getbeta (int unit) const { if (unit == inDegrees) return beta*radtodeg;
else cerr << "Bad units" << endl; return 0.0;}
double Getadot (int unit) const { if (unit == inDegrees) return adot*radtodeg;
else cerr << "Bad units" << endl; return 0.0;}
double Getbdot (int unit) const { if (unit == inDegrees) return bdot*radtodeg;
else cerr << "Bad units" << endl; return 0.0;}
double GetMagBeta (int unit) const { if (unit == inDegrees) return fabs(beta)*radtodeg;
else cerr << "Bad units" << endl; return 0.0;}
double Getqbar (void) const { return qbar; }
double GetqbarUW (void) const { return qbarUW; }
double GetqbarUV (void) const { return qbarUV; }
double GetVt (void) const { return Vt; }
double GetVground (void) const { return Vground; }
double GetMach (void) const { return Mach; }
double GetMachU (void) const { return MachU; }
double GetHOverBCG(void) const { return hoverbcg; }
double GetHOverBMAC(void) const { return hoverbmac; }
double GetGamma(void) const { return gamma; }
double GetGroundTrack(void) const { return psigt; }
double GetEarthPositionAngle(void) const { return earthPosAngle; }
double GetHeadWind(void);
double GetCrossWind(void);
// SET functions
void SetAeroUVW(FGColumnVector3 tt) { vAeroUVW = tt; }
void Setalpha (double tt) { alpha = tt; }
void Setbeta (double tt) { beta = tt; }
void Setqbar (double tt) { qbar = tt; }
void SetqbarUW (double tt) { qbarUW = tt; }
void SetqbarUV (double tt) { qbarUV = tt; }
void SetVt (double tt) { Vt = tt; }
void SetMach (double tt) { Mach=tt; }
void Setadot (double tt) { adot = tt; }
void Setbdot (double tt) { bdot = tt; }
void SetAB (double t1, double t2) { alpha=t1; beta=t2; }
void SetGamma (double tt) { gamma = tt; }
// Time routines, SET and GET functions
void SetDayOfYear (int doy) { day_of_year = doy; }
void SetSecondsInDay (double sid) { seconds_in_day = sid; }
int GetDayOfYear (void) const { return day_of_year; }
double GetSecondsInDay (void) const { return seconds_in_day; }
void bind(void);
void unbind(void);
private:
double vcas;
double veas;
double mach;
double machU;
double qbar,rhosl,rho,p,psl,pt,tat,sat,tatc;
// Don't add a getter for pt!
double vcas, veas;
double rhosl, rho, p, psl, pt, tat, sat, tatc; // Don't add a getter for pt!
FGColumnVector3 vPilotAccel;
FGColumnVector3 vPilotAccelN;
FGColumnVector3 vToEyePt;
FGColumnVector3 vAeroPQR;
FGColumnVector3 vAeroUVW;
FGColumnVector3 vEuler;
FGColumnVector3 vEulerRates;
FGColumnVector3 vMachUVW;
FGLocation vLocationVRP;
double Vt, Vground, Mach, MachU;
double qbar, qbarUW, qbarUV;
double alpha, beta;
double adot,bdot;
double psigt, gamma;
double seconds_in_day; // seconds since current GMT day began
int day_of_year; // GMT day, 1 .. 366
double earthPosAngle;
double hoverbcg, hoverbmac;
void GetState(void);
void Debug(int from);
};
@ -129,4 +243,3 @@ private:
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -81,7 +81,7 @@ FGCoefficient::FGCoefficient( FGFDMExec* fdex )
Table = (FGTable*)0L;
LookupR = LookupC = 0;
numInstances = 0;
rows = columns = 0;
rows = columns = tables = 0;
StaticValue = 0.0;
totalValue = 0.0;
@ -122,31 +122,37 @@ bool FGCoefficient::Load(FGConfigFile *AC_cfg)
*AC_cfg >> description;
if (method == "EQUATION") type = EQUATION;
else if (method == "TABLE") type = TABLE;
else if (method == "TABLE3D") type = TABLE3D;
else if (method == "VECTOR") type = VECTOR;
else if (method == "VALUE") type = VALUE;
else type = UNKNOWN;
if (type == VECTOR || type == TABLE) {
*AC_cfg >> rows;
if (type == TABLE) {
*AC_cfg >> columns;
if (type == VECTOR || type == TABLE || type == TABLE3D) {
if (type == TABLE3D) {
*AC_cfg >> rows >> columns >> tables;
Table = new FGTable(rows, columns, tables);
*AC_cfg >> multparmsRow >> multparmsCol >> multparmsTable;
LookupR = PropertyManager->GetNode( multparmsRow );
LookupC = PropertyManager->GetNode( multparmsCol );
LookupT = PropertyManager->GetNode( multparmsTable );
} else if (type == TABLE) {
*AC_cfg >> rows >> columns;
Table = new FGTable(rows, columns);
*AC_cfg >> multparmsRow >> multparmsCol;
LookupR = PropertyManager->GetNode( multparmsRow );
LookupC = PropertyManager->GetNode( multparmsCol );
} else {
*AC_cfg >> rows;
Table = new FGTable(rows);
*AC_cfg >> multparmsRow;
LookupR = PropertyManager->GetNode( multparmsRow );
}
*AC_cfg >> multparmsRow;
LookupR = PropertyManager->GetNode( multparmsRow );
}
if (type == TABLE) {
*AC_cfg >> multparmsCol;
LookupC = PropertyManager->GetNode( multparmsCol );
}
// Here, read in the line of the form (e.g.) FG_MACH|FG_QBAR|FG_ALPHA
// where each non-dimensionalizing parameter for this coefficient is
// Here, read in the line of the form:
// {property1} | {property2} | {property3}
// where each non-dimensionalizing property for this coefficient is
// separated by a | character
string line=AC_cfg->GetCurrentLine();
@ -159,9 +165,9 @@ bool FGCoefficient::Load(FGConfigFile *AC_cfg)
}
}
tmp[j]='\0'; multparms=tmp;
end = multparms.length();
end = multparms.length();
n = multparms.find("|");
n = multparms.find("|");
start = 0;
if (multparms != string("none")) {
while (n < end && n >= 0) {
@ -176,9 +182,10 @@ bool FGCoefficient::Load(FGConfigFile *AC_cfg)
// End of non-dimensionalizing parameter read-in
}
AC_cfg->GetNextConfigLine();
if (type == VALUE) {
*AC_cfg >> StaticValue;
} else if (type == VECTOR || type == TABLE) {
} else if (type == VECTOR || type == TABLE || type == TABLE3D) {
*Table << *AC_cfg;
} else {
cerr << "Unimplemented coefficient type: " << type << endl;
@ -195,6 +202,21 @@ bool FGCoefficient::Load(FGConfigFile *AC_cfg)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGCoefficient::Value(double rVal, double cVal, double tVal)
{
double Value;
unsigned int midx;
SD = Value = gain*Table->GetValue(rVal, cVal, tVal) + bias;
for (midx=0; midx < multipliers.size(); midx++) {
Value *= multipliers[midx]->getDoubleValue();
}
return Value;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGCoefficient::Value(double rVal, double cVal)
@ -228,7 +250,7 @@ double FGCoefficient::Value(double Val)
double FGCoefficient::Value(void)
{
double Value;
double Value;
SD = Value = gain*StaticValue + bias;
@ -258,7 +280,13 @@ double FGCoefficient::TotalValue(void)
case TABLE:
totalValue = Value( LookupR->getDoubleValue(),
LookupC->getDoubleValue() );
LookupC->getDoubleValue() );
break;
case TABLE3D:
totalValue = Value( LookupR->getDoubleValue(),
LookupC->getDoubleValue(),
LookupT->getDoubleValue() );
break;
case EQUATION:
@ -289,7 +317,7 @@ void FGCoefficient::DisplayCoeffFactors(void)
string FGCoefficient::GetSDstring(void)
{
char buffer[16];
char buffer[20];
string value;
sprintf(buffer,"%9.6f",SD);
@ -385,22 +413,17 @@ void FGCoefficient::Debug(int from)
cout << " " << description << endl;
cout << " " << method << endl;
if (type == VECTOR || type == TABLE) {
cout << " Rows: " << rows << " ";
if (type == TABLE) {
cout << "Cols: " << columns;
if (type == VECTOR || type == TABLE || type == TABLE3D) {
cout << " Rows: " << rows << " indexed by: " << LookupR->getName() << endl;
if (type == TABLE || type == TABLE3D) {
cout << " Cols: " << columns << " indexed by: " << LookupC->getName() << endl;
if (type == TABLE3D) {
cout << " Tables: " << tables << " indexed by: " << LookupT->getName() << endl;
}
}
cout << endl << " Row indexing parameter: " << LookupR->getName() << endl;
}
if (type == TABLE) {
cout << " Column indexing parameter: " << LookupC->getName() << endl;
}
if (type == VALUE) {
cout << " Value = " << StaticValue << endl;
} else if (type == VECTOR || type == TABLE) {
Table->Print();
} else if (type == VALUE) {
cout << " Value = " << StaticValue << endl;
}
DisplayCoeffFactors();

View file

@ -68,9 +68,7 @@ class FGState;
class FGAtmosphere;
class FGFCS;
class FGAircraft;
class FGTranslation;
class FGRotation;
class FGPosition;
class FGPropagate;
class FGAuxiliary;
class FGOutput;
@ -107,7 +105,7 @@ public:
typedef vector <FGPropertyManager*> MultVec;
enum Type {UNKNOWN, VALUE, VECTOR, TABLE, EQUATION};
enum Type {UNKNOWN, VALUE, VECTOR, TABLE, TABLE3D, EQUATION};
/** Returns the value for this coefficient.
Each instance of FGCoefficient stores a value for the "type" of coefficient
@ -165,18 +163,20 @@ private:
string multparms;
string multparmsRow;
string multparmsCol;
string multparmsTable;
double Value(double, double, double);
double Value(double, double);
double Value(double);
double Value(void);
double StaticValue;
double totalValue;
double bias,gain;
FGPropertyManager *LookupR, *LookupC;
FGPropertyManager *LookupR, *LookupC, *LookupT;
FGPropertyManager *node; // must be private!!
MultVec multipliers;
int rows, columns;
int rows, columns, tables;
Type type;
double SD; // Actual stability derivative (or other coefficient) value
FGTable *Table;
@ -185,9 +185,7 @@ private:
FGAtmosphere* Atmosphere;
FGFCS* FCS;
FGAircraft* Aircraft;
FGTranslation* Translation;
FGRotation* Rotation;
FGPosition* Position;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGOutput* Output;
FGPropertyManager* PropertyManager;

View file

@ -66,7 +66,7 @@ FGColumnVector3& FGColumnVector3::operator/=(const double scalar)
double FGColumnVector3::Magnitude(void) const
{
if (data[1] == 0.0 && data[2] == 0.0 && data[3] == 0.0)
if (Entry(1) == 0.0 && Entry(2) == 0.0 && Entry(3) == 0.0)
return 0.0;
else
return sqrt( Entry(1)*Entry(1) + Entry(2)*Entry(2) + Entry(3)*Entry(3) );

View file

@ -192,6 +192,24 @@ public:
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.
@ -281,7 +299,7 @@ public:
*/
double Magnitude(void) const;
/** Normialze.
/** Normalize.
Normalize the vector to have the Magnitude() == 1.0. If the vector
is equal to zero it is left untouched.

View file

@ -1,317 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGMatrix33.cpp
Author: Originally by Tony Peden [formatted here (and broken??) by JSB]
Date started: 1998
Purpose: FGMatrix33 class
Called by: Various
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
HISTORY
--------------------------------------------------------------------------------
??/??/?? TP Created
03/16/2000 JSB Added exception throwing
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGColumnVector4.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_COLUMNVECTOR4;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGColumnVector4::FGColumnVector4(void)
{
rowCtr = 1;
data[1]=0;data[2]=0;data[3]=0;data[4]=0;
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4::FGColumnVector4(double A, double B, double C, double D)
{
rowCtr = 1;
data[1]=A;
data[2]=B;
data[3]=C;
data[4]=D;
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4::~FGColumnVector4(void)
{
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4::FGColumnVector4(const FGColumnVector4& b)
{
data[1] = b.data[1];
data[2] = b.data[2];
data[3] = b.data[3];
data[4] = b.data[4];
rowCtr = 1;
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::operator=(const FGColumnVector4& b)
{
data[1] = b.data[1];
data[2] = b.data[2];
data[3] = b.data[3];
data[4] = b.data[4];
rowCtr = 1;
return *this;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::operator+(const FGColumnVector4& C)
{
FGColumnVector4 Sum;
Sum(1) = C(1) + data[1];
Sum(2) = C(2) + data[2];
Sum(3) = C(3) + data[3];
Sum(4) = C(4) + data[4];
return Sum;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGColumnVector4::operator+=(const FGColumnVector4& C)
{
data[1] += C(1);
data[2] += C(2);
data[3] += C(3);
data[4] += C(4);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::operator*(const double scalar)
{
FGColumnVector4 Product;
Product(1) = scalar * data[1];
Product(2) = scalar * data[2];
Product(3) = scalar * data[3];
Product(4) = scalar * data[4];
return Product;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGColumnVector4::operator*=(const double scalar)
{
data[1] *= scalar;
data[2] *= scalar;
data[3] *= scalar;
data[4] *= scalar;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::operator-(const FGColumnVector4& V)
{
FGColumnVector4 Diff;
Diff(1) = data[1] - V(1);
Diff(2) = data[2] - V(2);
Diff(3) = data[3] - V(3);
Diff(4) = data[4] - V(4);
return Diff;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGColumnVector4::operator-=(const FGColumnVector4& V)
{
data[1] -= V(1);
data[2] -= V(2);
data[3] -= V(3);
data[4] -= V(4);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::operator/(const double scalar)
{
FGColumnVector4 Quotient;
if (scalar != 0) {
double tmp = 1.0/scalar;
Quotient(1) = data[1] * tmp;
Quotient(2) = data[2] * tmp;
Quotient(3) = data[3] * tmp;
Quotient(4) = data[4] * tmp;
} else {
cerr << "Attempt to divide by zero in method FGColumnVector4::operator/(const double scalar), object " << this << endl;
}
return Quotient;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGColumnVector4::operator/=(const double scalar)
{
FGColumnVector4 Quotient;
if (scalar != 0) {
double tmp = 1.0/scalar;
data[1] *= tmp;
data[2] *= tmp;
data[3] *= tmp;
data[4] *= tmp;
} else {
cerr << "Attempt to divide by zero in method FGColumnVector4::operator/=(const double scalar), object " << this << endl;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 operator*(const double scalar, const FGColumnVector4& C)
{
FGColumnVector4 Product;
Product(1) = scalar * C(1);
Product(2) = scalar * C(2);
Product(3) = scalar * C(3);
Product(4) = scalar * C(4);
return Product;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGColumnVector4::Magnitude(void)
{
double num;
if ((data[1] == 0.00) &&
(data[2] == 0.00) &&
(data[3] == 0.00) &&
(data[4] == 0.00))
{
return 0.00;
} else {
num = data[1]*data[1];
num += data[2]*data[2];
num += data[3]*data[3];
num += data[4]*data[4];
return sqrt(num);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::Normalize(void)
{
double Mag = Magnitude();
if (Mag != 0) {
Mag = 1.0/Mag;
data[1] *= Mag;
data[2] *= Mag;
data[3] *= Mag;
data[4] *= Mag;
}
return *this;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4 FGColumnVector4::multElementWise(const FGColumnVector4& V)
{
FGColumnVector4 Product;
Product(1) = data[1] * V(1);
Product(2) = data[2] * V(2);
Product(3) = data[3] * V(3);
Product(4) = data[4] * V(4);
return Product;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ostream& operator<<(ostream& os, FGColumnVector4& col)
{
os << col(1) << " , " << col(2) << " , " << col(3) << " , " << col(4);
return os;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector4& FGColumnVector4::operator<<(const double ff)
{
data[rowCtr] = ff;
if (++rowCtr > 4) rowCtr = 1;
return *this;
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
// 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 FGColumnVector4::Debug(int from)
{
if (debug_lvl <= 0) return;
if (debug_lvl & 1) { // Standard console startup message output
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGColumnVector4" << endl;
if (from == 1) cout << "Destroyed: FGColumnVector4" << 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) { // Sanity checking
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -1,132 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGColumnVector4.h
Author: Originally by Tony Peden [formatted and adapted here by Jon Berndt]
Date started: Unknown
HISTORY
--------------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGCOLUMNVECTOR4_H
#define FGCOLUMNVECTOR4_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <stdlib.h>
#ifdef FGFS
# include <math.h>
# include <simgear/compiler.h>
# include STL_STRING
# include STL_FSTREAM
# include STL_IOSTREAM
SG_USING_STD(string);
SG_USING_STD(ostream);
SG_USING_STD(istream);
SG_USING_STD(cerr);
SG_USING_STD(cout);
SG_USING_STD(endl);
// SG_USING_STD(sqrt);
#else
# include <string>
# if defined (sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
# include <fstream.h>
# include <iostream.h>
# include <math.h>
# else
# include <fstream>
# if defined (sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
# include <iostream>
using std::ostream;
using std::istream;
using std::cerr;
using std::cout;
using std::endl;
using std::sqrt;
# endif
using std::string;
#endif
#include "FGJSBBase.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_COLUMNVECTOR4 "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** This class implements a 4 dimensional vector.
@author Jon S. Berndt, Tony Peden, et. al.
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGColumnVector4 : public FGJSBBase
{
public:
FGColumnVector4(void);
FGColumnVector4(double A, double B, double C, double D);
FGColumnVector4(const FGColumnVector4& b);
~FGColumnVector4(void);
FGColumnVector4 operator=(const FGColumnVector4& b);
FGColumnVector4 operator*(const double scalar);
FGColumnVector4 operator/(const double scalar);
FGColumnVector4 operator+(const FGColumnVector4& B); // must not return reference
FGColumnVector4 operator-(const FGColumnVector4& B);
void operator-=(const FGColumnVector4 &B);
void operator+=(const FGColumnVector4 &B);
void operator*=(const double scalar);
void operator/=(const double scalar);
inline double operator()(int m) const { return data[m]; }
inline double& operator()(int m) { return data[m]; }
FGColumnVector4& operator<<(const double ff);
inline void InitMatrix(void) { data[1]=0; data[2]=0; data[3]=0; data[4]=0; }
inline void InitMatrix(double ff) { data[1]=ff; data[2]=ff; data[3]=ff; data[4]=ff;}
double Magnitude(void);
FGColumnVector4 Normalize(void);
friend FGColumnVector4 operator*(const double scalar, const FGColumnVector4& A);
friend ostream& operator<<(ostream& os, FGColumnVector4& col);
FGColumnVector4 multElementWise(const FGColumnVector4& V);
private:
double data[5];
int rowCtr;
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -1,131 +0,0 @@
/*******************************************************************************
Header: FGDefs.h
Author: Jon S. Berndt
Date started: 02/01/99
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
02/01/99 JSB Created
********************************************************************************
SENTRY
*******************************************************************************/
#ifndef FGDEFS_H
#define FGDEFS_H
#define GRAVITY 32.174
#define INVGRAVITY 0.031081
#define EARTHRAD 20925650.00 // feet, equatorial
#define EARTHRADSQRD 437882827922500.0
#define ONESECOND 4.848136811E-6
#define Reng 1716 //Specific Gas Constant,ft^2/(sec^2*R)
#define SHRATIO 1.4 //Specific Heat Ratio
#define RADTODEG 57.29578
#define DEGTORAD 1.745329E-2
#define KTSTOFPS 1.68781
#define FPSTOKTS 0.592484
#define INCHTOFT 0.08333333
#define OMEGA_EARTH .00007272205217
#define NEEDED_CFG_VERSION "1.50"
#define JSBSIM_VERSION "0.9.0"
#define HPTOFTLBSSEC 550
#define METERS_TO_FEET 3.2808
#if defined ( sgi ) && !defined( __GNUC__ )
#define __STL_FUNCTION_TMPL_PARTIAL_ORDER
#endif
enum eParam {
FG_UNDEF = 0,
FG_TIME,
FG_QBAR,
FG_WINGAREA,
FG_WINGSPAN,
FG_CBAR,
FG_ALPHA,
FG_ALPHADOT,
FG_BETA,
FG_BETADOT,
FG_PHI,
FG_THT,
FG_PSI,
FG_PITCHRATE,
FG_ROLLRATE,
FG_YAWRATE,
FG_CL_SQRD,
FG_MACH,
FG_ALTITUDE,
FG_BI2VEL,
FG_CI2VEL,
FG_ELEVATOR_POS,
FG_AILERON_POS,
FG_RUDDER_POS,
FG_SPDBRAKE_POS,
FG_SPOILERS_POS,
FG_FLAPS_POS,
FG_ELEVATOR_CMD,
FG_AILERON_CMD,
FG_RUDDER_CMD,
FG_SPDBRAKE_CMD,
FG_SPOILERS_CMD,
FG_FLAPS_CMD,
FG_THROTTLE_CMD,
FG_THROTTLE_POS,
FG_MIXTURE_CMD,
FG_MIXTURE_POS,
FG_MAGNETO_CMD,
FG_STARTER_CMD,
FG_ACTIVE_ENGINE,
FG_HOVERB,
FG_PITCH_TRIM_CMD,
FG_LEFT_BRAKE_CMD,
FG_CENTER_BRAKE_CMD,
FG_RIGHT_BRAKE_CMD,
FG_SET_LOGGING,
FG_ALPHAH,
FG_ALPHAW,
FG_LBARH, //normalized horizontal tail arm
FG_LBARV, //normalized vertical tail arm
FG_HTAILAREA,
FG_VTAILAREA,
FG_VBARH, //horizontal tail volume
FG_VBARV //vertical tail volume
};
enum eAction {
FG_RAMP = 1,
FG_STEP = 2,
FG_EXP = 3
};
enum eType {
FG_VALUE = 1,
FG_DELTA = 2,
FG_BOOL = 3
};
/******************************************************************************/
#endif

View file

@ -1,12 +1,11 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGUtility.cpp
Author: Jon Berndt
Date started: 01/09/99
Purpose: Contains utility classes for the FG FDM
Called by: FGPosition, et. al.
Module: FGElectric.cpp
Author: David Culp
Date started: 04/07/2004
Purpose: This module models an electric motor
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
--------- Copyright (C) 2004 David Culp (davidculp2@comcast.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
@ -27,62 +26,90 @@
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class is a container for all utility classes used by the flight dynamics
model.
This class descends from the FGEngine class and models an electric motor based on
parameters given in the engine config file for this class
HISTORY
--------------------------------------------------------------------------------
01/09/99 JSB Created
04/07/2004 DPC Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# ifdef SG_HAVE_STD_INCLUDES
# include <cmath>
# else
# include <math.h>
# endif
#else
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
#endif
#include "FGUtility.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGElectric.h"
#include "FGPropulsion.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_UTILITY;
static const char *IdHdr = ID_ELECTRIC;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGUtility::FGUtility()
FGElectric::FGElectric(FGFDMExec* exec, FGConfigFile* Eng_cfg) : FGEngine(exec)
{
Debug(0);
string token;
Type = etElectric;
EngineNumber = 0;
PowerWatts = 745.7;
hptowatts = 745.7;
dt = State->Getdt();
Name = Eng_cfg->GetValue("NAME");
Eng_cfg->GetNextConfigLine();
while (Eng_cfg->GetValue() != string("/FG_ELECTRIC")) {
*Eng_cfg >> token;
if (token == "POWER_WATTS") *Eng_cfg >> PowerWatts;
else cerr << "Unhandled token in Engine config file: " << token << endl;
}
Debug(0); // Call Debug() routine from constructor if needed
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGUtility::~FGUtility()
FGElectric::~FGElectric()
{
Debug(1);
Debug(1); // Call Debug() routine from constructor if needed
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGElectric::Calculate(void)
{
Throttle = FCS->GetThrottlePos(EngineNumber);
RPM = Thruster->GetRPM() * Thruster->GetGearRatio();
HP = PowerWatts * Throttle / hptowatts;
PowerAvailable = (HP * hptoftlbssec) - Thruster->GetPowerRequired();
return Thruster->Calculate(PowerAvailable);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGElectric::GetEngineLabels(void)
{
return ""; // currently no labels are returned for this engine
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGElectric::GetEngineValues(void)
{
return ""; // currently no values are returned for this engine
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
// out the normally expected messages, essentially echoing
@ -101,18 +128,21 @@ FGUtility::~FGUtility()
// 16: When set various parameters are sanity checked and
// a message is printed out when they go out of bounds
void FGUtility::Debug(int from)
void FGElectric::Debug(int from)
{
if (debug_lvl <= 0) return;
if (debug_lvl & 1) { // Standard console startup message output
if (from == 0) { // Constructor
cout << "\n Engine Name: " << Name << endl;
cout << " Power Watts: " << PowerWatts << endl;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGUtility" << endl;
if (from == 1) cout << "Destroyed: FGUtility" << endl;
if (from == 0) cout << "Instantiated: FGElectric" << endl;
if (from == 1) cout << "Destroyed: FGElectric" << endl;
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
@ -127,4 +157,11 @@ void FGUtility::Debug(int from)
}
}
}
double
FGElectric::CalcFuelNeed(void)
{
return 0;
}
} // namespace JSBSim

View file

@ -1,10 +1,10 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGUtility.h
Author: Jon Berndt
Date started: 01/09/99
Header: FGElectric.h
Author: David Culp
Date started: 04/07/2004
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
----- Copyright (C) 2004 David P. Culp (davidculp2@comcast.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
@ -25,26 +25,27 @@
HISTORY
--------------------------------------------------------------------------------
01/09/99 JSB Created
04/07/2004 DPC Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGUTILITY_H
#define FGUTILITY_H
#ifndef FGELECTRIC_H
#define FGELECTRIC_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGJSBBase.h"
#include "FGEngine.h"
#include "FGConfigFile.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_UTILITY "$Id$"
#define ID_ELECTRIC "$Id$";
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -56,22 +57,49 @@ namespace JSBSim {
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models and electric motor.
FGElectric models an electric motor based on the configuration file
POWER_WATTS parameter. The throttle controls motor output linearly from
zero to POWER_WATTS. This power value (converted internally to horsepower)
is then used by FGPropeller to apply torque to the propeller.
@author David Culp
@version "$Id$"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGFDMExec;
class FGState;
class FGUtility : public FGJSBBase
class FGElectric : public FGEngine
{
public:
FGUtility(void);
~FGUtility();
/// Constructor
FGElectric(FGFDMExec* exec, FGConfigFile* Eng_cfg);
/// Destructor
~FGElectric();
double Calculate(void);
double GetPowerAvailable(void) {return PowerAvailable;}
double CalcFuelNeed(void);
double getRPM(void) {return RPM;}
string GetEngineLabels(void);
string GetEngineValues(void);
private:
FGState* State;
FGFDMExec* FDMExec;
double BrakeHorsePower;
double PowerAvailable;
// timestep
double dt;
// constants
double hptowatts;
double PowerWatts; // maximum engine power
double RPM; // revolutions per minute
double HP;
void Debug(int from);
};
}

View file

@ -55,6 +55,8 @@ INCLUDES
#include "FGEngine.h"
#include "FGTank.h"
#include "FGPropeller.h"
#include "FGNozzle.h"
namespace JSBSim {
@ -93,9 +95,7 @@ FGEngine::FGEngine(FGFDMExec* exec)
FCS = FDMExec->GetFCS();
Propulsion = FDMExec->GetPropulsion();
Aircraft = FDMExec->GetAircraft();
Translation = FDMExec->GetTranslation();
Rotation = FDMExec->GetRotation();
Position = FDMExec->GetPosition();
Propagate = FDMExec->GetPropagate();
Auxiliary = FDMExec->GetAuxiliary();
Output = FDMExec->GetOutput();
@ -119,17 +119,27 @@ FGEngine::~FGEngine()
void FGEngine::ConsumeFuel(void)
{
double Fshortage, Oshortage;
double Fshortage, Oshortage, TanksWithFuel;
FGTank* Tank;
if (TrimMode) return;
Fshortage = Oshortage = 0.0;
Fshortage = Oshortage = TanksWithFuel = 0.0;
// count how many assigned tanks have fuel
for (unsigned int i=0; i<SourceTanks.size(); i++) {
Tank = Propulsion->GetTank(SourceTanks[i]);
if (Tank->GetContents() > 0.0) {
++TanksWithFuel;
}
}
if (!TanksWithFuel) return;
for (unsigned int i=0; i<SourceTanks.size(); i++) {
Tank = Propulsion->GetTank(SourceTanks[i]);
if (Tank->GetType() == FGTank::ttFUEL) {
Fshortage += Tank->Reduce(CalcFuelNeed()/Propulsion->GetnumSelectedFuelTanks());
Fshortage += Tank->Drain(CalcFuelNeed()/TanksWithFuel);
} else {
Oshortage += Tank->Reduce(CalcOxidizerNeed()/Propulsion->GetnumSelectedOxiTanks());
Oshortage += Tank->Drain(CalcOxidizerNeed()/TanksWithFuel);
}
}
@ -171,6 +181,102 @@ void FGEngine::AddFeedTank(int tkID)
SourceTanks.push_back(tkID);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3& FGEngine::GetBodyForces(void)
{
return Thruster->GetBodyForces();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3& FGEngine::GetMoments(void)
{
return Thruster->GetMoments();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGEngine::LoadThruster(FGConfigFile* AC_cfg)
{
string token, fullpath, localpath;
string thrusterFileName, thrType, engineFileName;
FGConfigFile* Cfg_ptr = 0;
double xLoc, yLoc, zLoc, Pitch, Yaw;
double P_Factor = 0, Sense = 0.0;
string enginePath = FDMExec->GetEnginePath();
string aircraftPath = FDMExec->GetAircraftPath();
thrusterFileName = AC_cfg->GetValue("FILE");
# ifndef macintosh
fullpath = enginePath + "/";
localpath = aircraftPath + "/" + "/Engines/";
# else
fullpath = enginePath + ";";
localpath = aircraftPath + ";" + ";Engines;";
# endif
// Look in the Aircraft/Engines directory first
FGConfigFile Local_Thruster_cfg(localpath + thrusterFileName + ".xml");
FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
if (Local_Thruster_cfg.IsOpen()) {
Cfg_ptr = &Local_Thruster_cfg;
if (debug_lvl > 0) cout << "\n Reading thruster from file: " << localpath
+ thrusterFileName + ".xml"<< endl;
} else {
if (Thruster_cfg.IsOpen()) {
Cfg_ptr = &Thruster_cfg;
if (debug_lvl > 0) cout << "\n Reading thruster from file: " << fullpath
+ thrusterFileName + ".xml"<< endl;
}
}
if (Cfg_ptr) {
Cfg_ptr->GetNextConfigLine();
thrType = Cfg_ptr->GetValue();
if (thrType == "FG_PROPELLER") {
Thruster = new FGPropeller(FDMExec, Cfg_ptr);
} else if (thrType == "FG_NOZZLE") {
Thruster = new FGNozzle(FDMExec, Cfg_ptr);
} else if (thrType == "FG_DIRECT") {
Thruster = new FGThruster( FDMExec, Cfg_ptr);
}
AC_cfg->GetNextConfigLine();
while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
*AC_cfg >> token;
if (token == "XLOC") *AC_cfg >> xLoc;
else if (token == "YLOC") *AC_cfg >> yLoc;
else if (token == "ZLOC") *AC_cfg >> zLoc;
else if (token == "PITCH") *AC_cfg >> Pitch;
else if (token == "YAW") *AC_cfg >> Yaw;
else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
else if (token == "SENSE") *AC_cfg >> Sense;
else cerr << "Unknown identifier: " << token << " in engine file: "
<< engineFileName << endl;
}
Thruster->SetLocation(xLoc, yLoc, zLoc);
Thruster->SetAnglesToBody(0, Pitch, Yaw);
if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
((FGPropeller*)Thruster)->SetPFactor(P_Factor);
if (debug_lvl > 0) cout << " P-Factor: " << P_Factor << endl;
((FGPropeller*)Thruster)->SetSense(fabs(Sense)/Sense);
if (debug_lvl > 0) cout << " Sense: " << Sense << endl;
}
Thruster->SetdeltaT(State->Getdt() * Propulsion->GetRate());
return true;
} else {
cerr << "Could not read thruster config file: " << fullpath
+ thrusterFileName + ".xml" << endl;
return false;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -59,6 +59,7 @@ INCLUDES
#endif
#include "FGJSBBase.h"
#include "FGThruster.h"
#include "FGPropertyManager.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -81,12 +82,12 @@ class FGState;
class FGAtmosphere;
class FGFCS;
class FGAircraft;
class FGTranslation;
class FGRotation;
class FGPropagate;
class FGPropulsion;
class FGPosition;
class FGAuxiliary;
class FGOutput;
class FGThruster;
class FGConfigFile;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -109,7 +110,7 @@ public:
FGEngine(FGFDMExec* exec);
virtual ~FGEngine();
enum EngineType {etUnknown, etRocket, etPiston, etTurbine, etSimTurbine};
enum EngineType {etUnknown, etRocket, etPiston, etTurbine, etElectric};
EngineType GetType(void) { return Type; }
virtual string GetName(void) { return Name; }
@ -138,11 +139,8 @@ public:
virtual void SetStarter(bool s) { Starter = s; }
/** Calculates the thrust of the engine, and other engine functions.
@param PowerRequired this is the power required to run the thrusting device
such as a propeller. This resisting effect must be provided to the
engine model.
@return Thrust in pounds */
virtual double Calculate(double PowerRequired) {return 0.0;};
virtual double Calculate(void) {return 0.0;}
/** Reduces the fuel in the active tanks by the amount required.
This function should be called from within the
@ -174,6 +172,15 @@ public:
virtual bool GetTrimMode(void) {return TrimMode;}
virtual void SetTrimMode(bool state) {TrimMode = state;}
virtual FGColumnVector3& GetBodyForces(void);
virtual FGColumnVector3& GetMoments(void);
bool LoadThruster(FGConfigFile* AC_cfg);
FGThruster* GetThruster(void) {return Thruster;}
virtual string GetEngineLabels(void) = 0;
virtual string GetEngineValues(void) = 0;
protected:
FGPropertyManager* PropertyManager;
string Name;
@ -208,11 +215,10 @@ protected:
FGFCS* FCS;
FGPropulsion* Propulsion;
FGAircraft* Aircraft;
FGTranslation* Translation;
FGRotation* Rotation;
FGPosition* Position;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGOutput* Output;
FGThruster* Thruster;
vector <int> SourceTanks;
void Debug(int from);
@ -223,12 +229,12 @@ protected:
#include "FGAtmosphere.h"
#include "FGFCS.h"
#include "FGAircraft.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPropagate.h"
#include "FGPropulsion.h"
#include "FGPosition.h"
#include "FGAuxiliary.h"
#include "FGOutput.h"
#include "FGThruster.h"
#include "FGConfigFile.h"
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -75,8 +75,6 @@ FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
APAttitudeSetPt = APAltitudeSetPt = APHeadingSetPt = APAirspeedSetPt = 0.0;
DoNormalize=true;
eMode = mNone;
bind();
for (i=0;i<=NForms;i++) {
DePos[i] = DaLPos[i] = DaRPos[i] = DrPos[i] = 0.0;
@ -117,26 +115,18 @@ bool FGFCS::Run(void)
{
unsigned int i;
if (!FGModel::Run()) {
for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
for (i=0; i<APComponents.size(); i++) {
eMode = mAP;
APComponents[i]->Run();
eMode = mNone;
}
for (i=0; i<FCSComponents.size(); i++) {
eMode = mFCS;
FCSComponents[i]->Run();
eMode = mNone;
}
if (DoNormalize) Normalize();
if (FGModel::Run()) return true; // fast exit if nothing to do
return false;
} else {
return true;
}
for (i=0; i<ThrottlePos.size(); i++) ThrottlePos[i] = ThrottleCmd[i];
for (i=0; i<MixturePos.size(); i++) MixturePos[i] = MixtureCmd[i];
for (i=0; i<PropAdvance.size(); i++) PropAdvance[i] = PropAdvanceCmd[i];
for (i=0; i<APComponents.size(); i++) APComponents[i]->Run(); // cycle AP components
for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->Run(); // cycle FCS components
if (DoNormalize) Normalize();
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -297,9 +287,11 @@ bool FGFCS::Load(FGConfigFile* AC_cfg)
}
# ifndef macintosh
file = "control/" + fname + ".xml";
// file = "control/" + fname + ".xml";
file = FDMExec->GetAircraftPath() + "/" + FDMExec->GetModelName() + "/" + fname + ".xml";
# else
file = "control;" + fname + ".xml";
// file = "control;" + fname + ".xml";
file = FDMExec->GetAircraftPath() + ";" + FDMExec->GetModelName() + ";" + fname + ".xml";
# endif
if (name.empty()) {
@ -322,11 +314,9 @@ bool FGFCS::Load(FGConfigFile* AC_cfg)
if (delimiter == "AUTOPILOT") {
Components = &APComponents;
eMode = mAP;
Name = "Autopilot: " + name;
} else if (delimiter == "FLIGHT_CONTROL") {
Components = &FCSComponents;
eMode = mFCS;
Name = "FCS: " + name;
} else {
cerr << endl << "Unknown FCS delimiter" << endl << endl;
@ -400,45 +390,11 @@ bool FGFCS::Load(FGConfigFile* AC_cfg)
if (delimiter == "FLIGHT_CONTROL") bindModel();
eMode = mNone;
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGFCS::GetComponentOutput(int idx)
{
switch (eMode) {
case mFCS:
return FCSComponents[idx]->GetOutput();
case mAP:
return APComponents[idx]->GetOutput();
case mNone:
cerr << "Unknown FCS mode" << endl;
break;
}
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGFCS::GetComponentName(int idx)
{
switch (eMode) {
case mFCS:
return FCSComponents[idx]->GetName();
case mAP:
return APComponents[idx]->GetName();
case mNone:
cerr << "Unknown FCS mode" << endl;
break;
}
return string("");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
{
switch (bg) {
@ -484,7 +440,7 @@ string FGFCS::GetComponentValues(void)
{
unsigned int comp;
string CompValues = "";
char buffer[10];
char buffer[12];
bool firstime = true;
for (comp = 0; comp < FCSComponents.size(); comp++) {

View file

@ -83,40 +83,24 @@ CLASS DOCUMENTATION
the configuration file. For instance, for the X-15:
<pre>
&lt FLIGHT_CONTROL NAME="X-15 SAS"&gt
\<FLIGHT_CONTROL NAME="X-15 SAS">
&lt COMPONENT NAME="Pitch Trim Sum" TYPE="SUMMER"&gt
ID 0
INPUT FG_ELEVATOR_CMD
INPUT FG_PITCH_TRIM_CMD
CLIPTO -1 1
&lt/COMPONENT&gt
\<COMPONENT NAME="Pitch Trim Sum" TYPE="SUMMER">
INPUT fcs/elevator-cmd-norm
INPUT fcs/pitch-trim-cmd-norm
CLIPTO -1 1
\</COMPONENT>
&lt COMPONENT NAME="Pitch Command Scale" TYPE="AEROSURFACE_SCALE"&gt
ID 1
INPUT 0
\<COMPONENT NAME="Pitch Command Scale" TYPE="AEROSURFACE_SCALE">
INPUT fcs/pitch-trim-sum
MIN -50
MAX 50
&lt/COMPONENT&gt
\</COMPONENT>
&lt COMPONENT NAME="Pitch Gain 1" TYPE="PURE_GAIN"&gt
ID 2
INPUT 1
\<COMPONENT NAME="Pitch Gain 1" TYPE="PURE_GAIN">
INPUT fcs/pitch-command-scale
GAIN -0.36
&lt/COMPONENT&gt
&lt COMPONENT NAME="Pitch Scheduled Gain 1" TYPE="SCHEDULED_GAIN"&gt
ID 3
INPUT 2
GAIN 0.017
SCHEDULED_BY FG_ELEVATOR_POS
-0.35 -6.0
-0.17 -3.0
0.00 -2.0
0.09 -3.0
0.17 -5.0
0.60 -12.0
&lt/COMPONENT&gt
\</COMPONENT>
... etc.
</pre>
@ -125,17 +109,30 @@ CLASS DOCUMENTATION
defined. The input to the first component, as can be seen in the "Pitch trim
sum" component, is really the sum of two parameters: elevator command (from
the stick - a pilot input), and pitch trim. The type of this component is
"Summer". Its ID is 0 - the ID is used by other components to reference it.
"Summer".
The next component created is an aerosurface scale component - a type of
gain (see the LoadFCS() method for insight on how the various types of
components map into the actual component classes). You can see the input of
the "Pitch Command Scale" component takes "0" as input. When a number is
specified as an input, it refers to the ID of another FCS component. In this
case, ID 0 refers to the previously defined and discussed "Pitch Trim Sum"
component. This continues until the final component for an axis when the
components map into the actual component classes). This continues until the
final component for an axis when the
OUTPUT keyword specifies where the output is supposed to go. See the
individual components for more information on how they are mechanized.
Another option for the flight controls portion of the config file is that in
addition to using the "NAME" attribute in,
<pre>
\<FLIGHT_CONTROL NAME="X-15 SAS">
</pre>
one can also supply a filename:
<pre>
\<FLIGHT_CONTROL NAME="X-15 SAS" FILE="X15.xml">
\</FLIGHT_CONTROL>
</pre>
In this case, the FCS would be read in from another file.
@author Jon S. Berndt
@version $Id$
@see FGFCSComponent
@ -420,16 +417,6 @@ public:
@return pointer to the State object */
inline FGState* GetState(void) { return State; }
/** Retrieves a components output value
@param idx the index of the component (the component ID)
@return output value from the component */
double GetComponentOutput(int idx);
/** Retrieves the component name
@param idx the index of the component (the component ID)
@return name of the component */
string GetComponentName(int idx);
/** Retrieves all component names for inclusion in output stream */
string GetComponentStrings(void);
@ -622,8 +609,6 @@ private:
double LeftBrake, RightBrake, CenterBrake; // Brake settings
double GearCmd,GearPos;
enum Mode {mAP, mFCS, mNone} eMode;
double APAttitudeSetPt, APAltitudeSetPt, APHeadingSetPt, APAirspeedSetPt;
bool APAcquireAttitude, APAcquireAltitude, APAcquireHeading, APAcquireAirspeed;
bool APAttitudeHold, APAltitudeHold, APHeadingHold, APAirspeedHold, APWingsLevelHold;

View file

@ -64,9 +64,7 @@ INCLUDES
#include "FGAerodynamics.h"
#include "FGInertial.h"
#include "FGAircraft.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGAuxiliary.h"
#include "FGOutput.h"
#include "FGConfigFile.h"
@ -122,9 +120,7 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root)
Inertial = 0;
GroundReactions = 0;
Aircraft = 0;
Translation = 0;
Rotation = 0;
Position = 0;
Propagate = 0;
Auxiliary = 0;
Output = 0;
IC = 0;
@ -194,9 +190,7 @@ bool FGFDMExec::Allocate(void)
Inertial = new FGInertial(this);
GroundReactions = new FGGroundReactions(this);
Aircraft = new FGAircraft(this);
Translation = new FGTranslation(this);
Rotation = new FGRotation(this);
Position = new FGPosition(this);
Propagate = new FGPropagate(this);
Auxiliary = new FGAuxiliary(this);
Output = new FGOutput(this);
@ -230,15 +224,9 @@ bool FGFDMExec::Allocate(void)
if (!Aircraft->InitModel()) {
cerr << fgred << "Aircraft model init failed" << fgdef << endl;
Error+=128;}
if (!Translation->InitModel()) {
cerr << fgred << "Translation model init failed" << fgdef << endl;
Error+=256;}
if (!Rotation->InitModel()) {
cerr << fgred << "Rotation model init failed" << fgdef << endl;
if (!Propagate->InitModel()) {
cerr << fgred << "Propagate model init failed" << fgdef << endl;
Error+=512;}
if (!Position->InitModel()) {
cerr << fgred << "Position model init failed" << fgdef << endl;
Error+=1024;}
if (!Auxiliary->InitModel()) {
cerr << fgred << "Auxiliary model init failed" << fgdef << endl;
Error+=2058;}
@ -263,9 +251,7 @@ bool FGFDMExec::Allocate(void)
Schedule(Inertial, 1);
Schedule(GroundReactions, 1);
Schedule(Aircraft, 1);
Schedule(Rotation, 1);
Schedule(Translation, 1);
Schedule(Position, 1);
Schedule(Propagate, 1);
Schedule(Auxiliary, 1);
Schedule(Output, 1);
@ -286,9 +272,7 @@ bool FGFDMExec::DeAllocate(void)
delete Inertial;
delete GroundReactions;
delete Aircraft;
delete Translation;
delete Rotation;
delete Position;
delete Propagate;
delete Auxiliary;
delete Output;
delete State;
@ -308,9 +292,7 @@ bool FGFDMExec::DeAllocate(void)
Inertial = 0;
GroundReactions = 0;
Aircraft = 0;
Translation = 0;
Rotation = 0;
Position = 0;
Propagate = 0;
Auxiliary = 0;
Output = 0;
@ -425,9 +407,9 @@ bool FGFDMExec::LoadModel(string model)
}
# ifndef macintosh
aircraftCfgFileName = AircraftPath + "/" + model + "/" + model + ".xml";
aircraftCfgFileName = AircraftPath + "/" + model + ".xml";
# else
aircraftCfgFileName = AircraftPath + ";" + model + ";" + model + ".xml";
aircraftCfgFileName = AircraftPath + ";" + model + ".xml";
# endif
FGConfigFile AC_cfg(aircraftCfgFileName);

View file

@ -27,7 +27,7 @@ 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.
object and dt=0.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
@ -79,24 +79,23 @@ CLASS DOCUMENTATION
This describes to any interested entity the debug level
requested by setting the JSBSIM_DEBUG environment variable.
The bitmasked value choices are as follows:<ol>
<li><b>unset</b>: In this case (the default) JSBSim would only print
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</li>
<li><b>0</b>: This requests JSBSim not to output any messages
whatsoever.</li>
<li><b>1</b>: This value explicity requests the normal JSBSim
startup messages</li>
<li><b>2</b>: This value asks for a message to be printed out when
a class is instantiated</li>
<li><b>4</b>: When this value is set, a message is displayed when a
FGModel object executes its Run() method</li>
<li><b>8</b>: When this value is set, various runtime state variables
are printed out periodically</li>
<li><b>16</b>: When set various parameters are sanity checked and
a message is printed out when they go out of bounds</li>
</ol>
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
@author Jon S. Berndt
@version $Id$
@ -185,7 +184,7 @@ public:
@param path path to the control directory. For instance:
"control".
*/
bool SetControlPath(string path) { ControlPath = path; return true; }
// bool SetControlPath(string path) { ControlPath = path; return true; }
/// @name Top-level executive State and Model retrieval mechanism
@ -208,12 +207,8 @@ public:
inline FGGroundReactions* GetGroundReactions(void) {return GroundReactions;}
/// Returns the FGAircraft pointer.
inline FGAircraft* GetAircraft(void) {return Aircraft;}
/// Returns the FGTranslation pointer.
inline FGTranslation* GetTranslation(void) {return Translation;}
/// Returns the FGRotation pointer.
inline FGRotation* GetRotation(void) {return Rotation;}
/// Returns the FGPosition pointer.
inline FGPosition* GetPosition(void) {return Position;}
/// Returns the FGPropagate pointer.
inline FGPropagate* GetPropagate(void) {return Propagate;}
/// Returns the FGAuxiliary pointer.
inline FGAuxiliary* GetAuxiliary(void) {return Auxiliary;}
/// Returns the FGOutput pointer.
@ -228,8 +223,8 @@ public:
inline string GetEnginePath(void) {return EnginePath;}
/// Retrieves the aircraft path.
inline string GetAircraftPath(void) {return AircraftPath;}
/// Retrieves the control path.
inline string GetControlPath(void) {return ControlPath;}
// /// Retrieves the control path.
// inline string GetControlPath(void) {return ControlPath;}
string GetModelName(void) { return modelName; }
@ -273,7 +268,7 @@ private:
string AircraftPath;
string EnginePath;
string ControlPath;
// string ControlPath;
string CFGVersion;
string Release;
@ -287,9 +282,7 @@ private:
FGInertial* Inertial;
FGGroundReactions* GroundReactions;
FGAircraft* Aircraft;
FGTranslation* Translation;
FGRotation* Rotation;
FGPosition* Position;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGOutput* Output;

View file

@ -69,9 +69,7 @@ class FGState;
class FGAtmosphere;
class FGFCS;
class FGAircraft;
class FGTranslation;
class FGRotation;
class FGPosition;
class FGPropagate;
class FGAuxiliary;
class FGOutput;

View file

@ -42,7 +42,7 @@ and the cg.
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGTranslation.h"
#include "FGPropagate.h"
#include "FGMassBalance.h"
#include "FGState.h"
#include "FGForce.h"
@ -97,7 +97,7 @@ FGMatrix33 FGForce::Transform(void)
case tWindBody:
return fdmex->GetState()->GetTs2b();
case tLocalBody:
return fdmex->GetState()->GetTl2b();
return fdmex->GetPropagate()->GetTl2b();
case tCustom:
case tNone:
return mT;

View file

@ -331,7 +331,7 @@ private:
FGMatrix33 mT;
virtual void Debug(int from);
void Debug(int from);
};
}
#endif

View file

@ -35,6 +35,9 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include <iomanip>
#include "FGGroundReactions.h"
#include "FGPropertyManager.h"
@ -43,15 +46,6 @@ namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_GROUNDREACTIONS;
#if defined (__APPLE__)
/* Not all systems have the gcvt function */
inline char* gcvt (double value, int ndigits, char *buf) {
/* note that this is not exactly what gcvt is supposed to do! */
snprintf (buf, ndigits+1, "%f", value);
return buf;
}
#endif
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -84,7 +78,7 @@ bool FGGroundReactions::Run(void)
vMoments.InitMatrix();
// Only execute gear force code below 300 feet
if ( Position->GetDistanceAGL() < 300.0 ) {
if ( Propagate->GetDistanceAGL() < 300.0 ) {
vector <FGLGear>::iterator iGear = lGear.begin();
// Sum forces and moments for all gear, here.
// Some optimizations may be made here - or rather in the gear code itself.
@ -126,70 +120,62 @@ bool FGGroundReactions::Load(FGConfigFile* AC_cfg)
string FGGroundReactions::GetGroundReactionStrings(void)
{
string GroundReactionStrings = "";
bool firstime = true;
std::ostringstream buf;
for (unsigned int i=0;i<lGear.size();i++) {
if (!firstime) GroundReactionStrings += ", ";
GroundReactionStrings += (lGear[i].GetName() + "_WOW, ");
GroundReactionStrings += (lGear[i].GetName() + "_stroke, ");
GroundReactionStrings += (lGear[i].GetName() + "_strokeVel, ");
GroundReactionStrings += (lGear[i].GetName() + "_CompressForce, ");
GroundReactionStrings += (lGear[i].GetName() + "_WhlSideForce, ");
GroundReactionStrings += (lGear[i].GetName() + "_WhlVelVecX, ");
GroundReactionStrings += (lGear[i].GetName() + "_WhlVelVecY, ");
GroundReactionStrings += (lGear[i].GetName() + "_WhlRollForce, ");
GroundReactionStrings += (lGear[i].GetName() + "_BodyXForce, ");
GroundReactionStrings += (lGear[i].GetName() + "_BodyYForce, ");
GroundReactionStrings += (lGear[i].GetName() + "_WhlSlipDegrees");
firstime = false;
string name = lGear[i].GetName();
buf << name << "_WOW, "
<< name << "_stroke, "
<< name << "_strokeVel, "
<< name << "_CompressForce, "
<< name << "_WhlSideForce, "
<< name << "_WhlVelVecX, "
<< name << "_WhlVelVecY, "
<< name << "_WhlRollForce, "
<< name << "_BodyXForce, "
<< name << "_BodyYForce, "
<< name << "_WhlSlipDegrees, ";
}
GroundReactionStrings += ", TotalGearForce_X, ";
GroundReactionStrings += "TotalGearForce_Y, ";
GroundReactionStrings += "TotalGearForce_Z, ";
GroundReactionStrings += "TotalGearMoment_L, ";
GroundReactionStrings += "TotalGearMoment_M, ";
GroundReactionStrings += "TotalGearMoment_N";
buf << "TotalGearForce_X, "
<< "TotalGearForce_Y, "
<< "TotalGearForce_Z, "
<< "TotalGearMoment_L, "
<< "TotalGearMoment_M, "
<< "TotalGearMoment_N";
return GroundReactionStrings;
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGGroundReactions::GetGroundReactionValues(void)
{
char buff[20];
string GroundReactionValues = "";
bool firstime = true;
std::ostringstream buf;
for (unsigned int i=0;i<lGear.size();i++) {
if (!firstime) GroundReactionValues += ", ";
GroundReactionValues += string( lGear[i].GetWOW()?"1":"0" ) + ", ";
GroundReactionValues += (string(gcvt(lGear[i].GetCompLen(), 5, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetCompVel(), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetCompForce(), 10, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetWheelVel(eX), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetWheelVel(eY), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetWheelSideForce(), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetWheelRollForce(), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetBodyXForce(), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetBodyYForce(), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(lGear[i].GetWheelSlipAngle(), 6, buff)));
firstime = false;
FGLGear& gear = lGear[i];
buf << (gear.GetWOW() ? "1, " : "0, ")
<< setprecision(5) << gear.GetCompLen() << ", "
<< setprecision(6) << gear.GetCompVel() << ", "
<< setprecision(10) << gear.GetCompForce() << ", "
<< setprecision(6) << gear.GetWheelVel(eX) << ", "
<< gear.GetWheelVel(eY) << ", "
<< gear.GetWheelSideForce() << ", "
<< gear.GetWheelRollForce() << ", "
<< gear.GetBodyXForce() << ", "
<< gear.GetBodyYForce() << ", "
<< gear.GetWheelSlipAngle() << ", ";
}
GroundReactionValues += (", " + string(gcvt(vForces(eX), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(vForces(eY), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(vForces(eZ), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(vMoments(eX), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(vMoments(eY), 6, buff)) + ", ");
GroundReactionValues += (string(gcvt(vMoments(eZ), 6, buff)));
buf << vForces(eX) << ", "
<< vForces(eY) << ", "
<< vForces(eZ) << ", "
<< vMoments(eX) << ", "
<< vMoments(eY) << ", "
<< vMoments(eZ);
return GroundReactionValues;
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -36,7 +36,7 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGInertial.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGState.h"
#include "FGMassBalance.h"
@ -60,12 +60,6 @@ FGInertial::FGInertial(FGFDMExec* fgex) : FGModel(fgex)
RadiusReference = 20925650.00;
gAccelReference = GM/(RadiusReference*RadiusReference);
gAccel = GM/(RadiusReference*RadiusReference);
vRadius.InitMatrix();
vCoriolis.InitMatrix();
vCentrifugal.InitMatrix();
vGravity.InitMatrix();
bind();
Debug(0);
}
@ -74,7 +68,6 @@ FGInertial::FGInertial(FGFDMExec* fgex) : FGModel(fgex)
FGInertial::~FGInertial(void)
{
unbind();
Debug(1);
}
@ -82,42 +75,14 @@ FGInertial::~FGInertial(void)
bool FGInertial::Run(void)
{
if (!FGModel::Run()) {
// Fast return if we have nothing to do ...
if (FGModel::Run()) return true;
gAccel = GM / (Position->GetRadius()*Position->GetRadius());
// Gravitation accel
double r = Propagate->GetRadius();
gAccel = GetGAccel(r);
vGravity(eDown) = gAccel;
// The following equation for vOmegaLocal terms shows the angular velocity
// calculation _for_the_local_frame_ given the earth's rotation (first set)
// at the current latitude, and also the component due to the aircraft
// motion over the curved surface of the earth (second set).
vOmegaLocal(eX) = omega() * cos(Position->GetLatitude());
vOmegaLocal(eY) = 0.0;
vOmegaLocal(eZ) = omega() * -sin(Position->GetLatitude());
vOmegaLocal(eX) += Position->GetVe() / Position->GetRadius();
vOmegaLocal(eY) += -Position->GetVn() / Position->GetRadius();
vOmegaLocal(eZ) += 0.00;
// Coriolis acceleration is normally written: -2w*dr/dt, but due to the axis
// conventions used here the sign is reversed: 2w*dr/dt. The same is true for
// Centrifugal acceleration.
vCoriolis(eEast) = 2.0*omega() * (Position->GetVd()*cos(Position->GetLatitude()) +
Position->GetVn()*sin(Position->GetLatitude()));
vRadius(eDown) = Position->GetRadius();
vCentrifugal(eDown) = -vOmegaLocal.Magnitude() * vOmegaLocal.Magnitude() * vRadius(eDown);
// vForces = State->GetTl2b() * MassBalance->GetMass() * (vCoriolis + vCentrifugal + vGravity);
vForces = State->GetTl2b() * MassBalance->GetMass() * vGravity;
return false;
} else {
return true;
}
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -129,27 +94,6 @@ bool FGInertial::LoadInertial(FGConfigFile* AC_cfg)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGInertial::bind(void)
{
typedef double (FGInertial::*PMF)(int) const;
PropertyManager->Tie("forces/fbx-inertial-lbs", this,1,
(PMF)&FGInertial::GetForces);
PropertyManager->Tie("forces/fby-inertial-lbs", this,2,
(PMF)&FGInertial::GetForces);
PropertyManager->Tie("forces/fbz-inertial-lbs", this,3,
(PMF)&FGInertial::GetForces);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGInertial::unbind(void)
{
PropertyManager->Untie("forces/fbx-inertial-lbs");
PropertyManager->Untie("forces/fby-inertial-lbs");
PropertyManager->Untie("forces/fbz-inertial-lbs");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
// out the normally expected messages, essentially echoing

View file

@ -83,27 +83,14 @@ public:
~FGInertial(void);
bool Run(void);
FGColumnVector3& GetForces(void) {return vForces;}
FGColumnVector3& GetGravity(void) {return vGravity;}
FGColumnVector3& GetCoriolis(void) {return vCoriolis;}
FGColumnVector3& GetCentrifugal(void) {return vCentrifugal;}
double GetForces(int n) const {return vForces(n);}
bool LoadInertial(FGConfigFile* AC_cfg);
double SLgravity(void) const {return gAccelReference;}
double gravity(void) const {return gAccel;}
double omega(void) const {return RotationRate;}
double GetGAccel(double r) const { return GM/(r*r); }
double RefRadius(void) const {return RadiusReference;}
void bind(void);
void unbind(void);
private:
FGColumnVector3 vOmegaLocal;
FGColumnVector3 vForces;
FGColumnVector3 vRadius;
FGColumnVector3 vGravity;
FGColumnVector3 vCoriolis;
FGColumnVector3 vCentrifugal;
double gAccel;
double gAccelReference;
double RadiusReference;
@ -114,4 +101,3 @@ private:
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -47,7 +47,7 @@ INCLUDES
#include "FGInertial.h"
#include "FGAtmosphere.h"
#include "FGAerodynamics.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGConfigFile.h"
#include "FGPropertyManager.h"
@ -67,6 +67,7 @@ FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec)
altitude=hdot=0;
latitude=longitude=0;
u=v=w=0;
p=q=r=0;
uw=vw=ww=0;
vnorth=veast=vdown=0;
wnorth=weast=wdown=0;
@ -83,7 +84,7 @@ FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec)
if(FDMExec != NULL ) {
fdmex=FDMExec;
fdmex->GetPosition()->Seth(altitude);
fdmex->GetPropagate()->Seth(altitude);
fdmex->GetAtmosphere()->Run();
PropertyManager=fdmex->GetPropertyManager();
bind();
@ -265,7 +266,7 @@ void FGInitialCondition::SetWBodyFpsIC(double tt) {
//******************************************************************************
double FGInitialCondition::GetUBodyFpsIC(void) {
double FGInitialCondition::GetUBodyFpsIC(void) const {
if(lastSpeedSet == setvg )
return u;
else
@ -274,7 +275,7 @@ double FGInitialCondition::GetUBodyFpsIC(void) {
//******************************************************************************
double FGInitialCondition::GetVBodyFpsIC(void) {
double FGInitialCondition::GetVBodyFpsIC(void) const {
if( lastSpeedSet == setvg )
return v;
else {
@ -284,7 +285,7 @@ double FGInitialCondition::GetVBodyFpsIC(void) {
//******************************************************************************
double FGInitialCondition::GetWBodyFpsIC(void) {
double FGInitialCondition::GetWBodyFpsIC(void) const {
if( lastSpeedSet == setvg )
return w;
else
@ -394,7 +395,7 @@ void FGInitialCondition::calcWindUVW(void) {
void FGInitialCondition::SetAltitudeFtIC(double tt) {
altitude=tt;
fdmex->GetPosition()->Seth(altitude);
fdmex->GetPropagate()->Seth(altitude);
fdmex->GetAtmosphere()->Run();
//lets try to make sure the user gets what they intended
@ -422,8 +423,8 @@ void FGInitialCondition::SetAltitudeFtIC(double tt) {
//******************************************************************************
void FGInitialCondition::SetAltitudeAGLFtIC(double tt) {
fdmex->GetPosition()->SetDistanceAGL(tt);
altitude=fdmex->GetPosition()->Geth();
fdmex->GetPropagate()->SetDistanceAGL(tt);
altitude=fdmex->GetPropagate()->Geth();
SetAltitudeFtIC(altitude);
}
@ -925,6 +926,19 @@ void FGInitialCondition::bind(void){
&FGInitialCondition::GetLongitudeRadIC,
&FGInitialCondition::SetLongitudeRadIC,
true);
PropertyManager->Tie("ic/p-rad_sec", this,
&FGInitialCondition::GetPRadpsIC,
&FGInitialCondition::SetPRadpsIC,
true);
PropertyManager->Tie("ic/q-rad_sec", this,
&FGInitialCondition::GetQRadpsIC,
&FGInitialCondition::SetQRadpsIC,
true);
PropertyManager->Tie("ic/r-rad_sec", this,
&FGInitialCondition::GetRRadpsIC,
&FGInitialCondition::SetRRadpsIC,
true);
}
//******************************************************************************
@ -973,6 +987,10 @@ void FGInitialCondition::unbind(void){
PropertyManager->Untie("ic/psi-true-rad");
PropertyManager->Untie("ic/lat-gc-rad");
PropertyManager->Untie("ic/long-gc-rad");
PropertyManager->Untie("ic/p-rad_sec");
PropertyManager->Untie("ic/q-rad_sec");
PropertyManager->Untie("ic/r-rad_sec");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -192,6 +192,9 @@ public:
void SetVnorthFpsIC(double tt);
void SetVeastFpsIC(double tt);
void SetVdownFpsIC(double tt);
void SetPRadpsIC(double tt) { p = tt; }
void SetQRadpsIC(double tt) { q = tt; }
void SetRRadpsIC(double tt) { r = tt; }
void SetWindNEDFpsIC(double wN, double wE, double wD);
@ -215,9 +218,12 @@ public:
inline double GetWindFpsIC(void) const { return sqrt(wnorth*wnorth + weast*weast); }
double GetWindDirDegIC(void);
inline double GetClimbRateFpsIC(void) const { return hdot; }
double GetUBodyFpsIC(void);
double GetVBodyFpsIC(void);
double GetWBodyFpsIC(void);
double GetUBodyFpsIC(void) const;
double GetVBodyFpsIC(void) const;
double GetWBodyFpsIC(void) const;
double GetPRadpsIC() const { return p; }
double GetQRadpsIC() const { return q; }
double GetRRadpsIC() const { return r; }
void SetFlightPathAngleRadIC(double tt);
void SetAlphaRadIC(double tt);
void SetPitchAngleRadIC(double tt);
@ -253,6 +259,7 @@ private:
double altitude,hdot;
double latitude,longitude;
double u,v,w;
double p,q,r;
double uw,vw,ww;
double vnorth,veast,vdown;
double wnorth,weast,wdown;

View file

@ -78,7 +78,7 @@ const double FGJSBBase::SHRatio = 1.40;
const double FGJSBBase::slugtolb = 32.174049;
const double FGJSBBase::lbtoslug = 1.0/slugtolb;
const string FGJSBBase::needed_cfg_version = "1.61";
const string FGJSBBase::needed_cfg_version = "1.65";
const string FGJSBBase::JSBSim_version = "0.9.5";
std::queue <FGJSBBase::Message*> FGJSBBase::Messages;
@ -90,12 +90,6 @@ short FGJSBBase::debug_lvl = 1;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::FGJSBBase()
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::PutMessage(Message* msg)
{
Messages.push(msg);
@ -104,7 +98,7 @@ FGJSBBase::Message* FGJSBBase::PutMessage(Message* msg)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::PutMessage(string text)
FGJSBBase::Message* FGJSBBase::PutMessage(const string& text)
{
Message *msg = new Message();
msg->text = text;
@ -117,7 +111,7 @@ FGJSBBase::Message* FGJSBBase::PutMessage(string text)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::PutMessage(string text, bool bVal)
FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, bool bVal)
{
Message *msg = new Message();
msg->text = text;
@ -131,7 +125,7 @@ FGJSBBase::Message* FGJSBBase::PutMessage(string text, bool bVal)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::PutMessage(string text, int iVal)
FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, int iVal)
{
Message *msg = new Message();
msg->text = text;
@ -145,7 +139,7 @@ FGJSBBase::Message* FGJSBBase::PutMessage(string text, int iVal)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGJSBBase::Message* FGJSBBase::PutMessage(string text, double dVal)
FGJSBBase::Message* FGJSBBase::PutMessage(const string& text, double dVal)
{
Message *msg = new Message();
msg->text = text;

View file

@ -38,6 +38,8 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <limits>
#ifdef FGFS
# include <simgear/compiler.h>
# include <math.h>
@ -73,17 +75,6 @@ using std::string;
using std::max;
#endif
#ifdef __FreeBSD__ // define gcvt on FreeBSD
#include <stdio.h>
static char *gcvt(double number, size_t ndigit, char *buf)
{
sprintf(buf, "%f", number);
return buf;
}
#endif
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -112,10 +103,10 @@ CLASS DECLARATION
class FGJSBBase {
public:
/// Constructor for FGJSBBase.
FGJSBBase();
FGJSBBase() {};
/// Destructor for FGJSBBase.
virtual ~FGJSBBase() {};
~FGJSBBase() {};
/// JSBSim Message structure
typedef struct Msg {
@ -129,26 +120,6 @@ public:
double dVal;
} Message;
///@name JSBSim Enums.
//@{
/// 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 };
//@}
///@name JSBSim console output highlighting terms.
//@{
/// highlights text
@ -184,22 +155,22 @@ public:
/** Creates a message with the given text and places it on the queue.
@param text message text
@return pointer to a Message structure */
Message* PutMessage(string text);
Message* PutMessage(const 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 */
Message* PutMessage(string text, bool bVal);
Message* PutMessage(const 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 */
Message* PutMessage(string text, int iVal);
Message* PutMessage(const 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 */
Message* PutMessage(string text, double dVal);
Message* PutMessage(const string& text, double dVal);
/** Reads the message on the queue (but does not delete it).
@return pointer to a Message structure (or NULL if no mesage) */
Message* ReadMessage(void);
@ -212,20 +183,63 @@ public:
void disableHighLighting(void);
static short debug_lvl;
double KelvinToFahrenheit (double kelvin) {
static double KelvinToFahrenheit (double kelvin) {
return 1.8*kelvin - 459.4;
}
double RankineToCelsius (double rankine) {
static double RankineToCelsius (double rankine) {
return (rankine - 491.67)/1.8;
}
static double FahrenheitToCelsius (double fahrenheit) {
return (fahrenheit - 32.0)/1.8;
}
static double CelsiusToFahrenheit (double celsius) {
return celsius * 1.8 + 32.0;
}
/** 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*std::numeric_limits<double>::epsilon();
return fabs(a - b) <= eps*max(fabs(a), 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*std::numeric_limits<float>::epsilon();
return fabs(a - b) <= eps*max(fabs(a), 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);
}
protected:
static Message localMsg;
static std::queue <Message*> Messages;
virtual void Debug(int from) {};
void Debug(int from) {};
static unsigned int frame;
static unsigned int messageId;
@ -245,6 +259,28 @@ protected:
static const string needed_cfg_version;
static const string JSBSim_version;
};
/// 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

View file

@ -100,8 +100,8 @@ FGLGear::FGLGear(FGConfigFile* AC_cfg, FGFDMExec* fdmex) : Exec(fdmex)
State = Exec->GetState();
Aircraft = Exec->GetAircraft();
Position = Exec->GetPosition();
Rotation = Exec->GetRotation();
Propagate = Exec->GetPropagate();
Auxiliary = Exec->GetAuxiliary();
FCS = Exec->GetFCS();
MassBalance = Exec->GetMassBalance();
@ -117,7 +117,7 @@ FGLGear::FGLGear(FGConfigFile* AC_cfg, FGFDMExec* fdmex) : Exec(fdmex)
vWhlBodyVec = MassBalance->StructuralToBody(vXYZ);
vLocalGear = State->GetTb2l() * vWhlBodyVec;
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
compressLength = 0.0;
compressSpeed = 0.0;
@ -142,8 +142,8 @@ FGLGear::FGLGear(const FGLGear& lgear)
{
State = lgear.State;
Aircraft = lgear.Aircraft;
Position = lgear.Position;
Rotation = lgear.Rotation;
Propagate = lgear.Propagate;
Auxiliary = lgear.Auxiliary;
Exec = lgear.Exec;
FCS = lgear.FCS;
MassBalance = lgear.MassBalance;
@ -206,7 +206,6 @@ FGLGear::~FGLGear()
FGColumnVector3& FGLGear::Force(void)
{
double SteerGain = 0;
double SinWheel, CosWheel;
double deltaSlip;
double deltaT = State->Getdt()*Aircraft->GetRate();
@ -236,11 +235,11 @@ FGColumnVector3& FGLGear::Force(void)
// vWhlBodyVec now stores the vector from the cg to this wheel
vLocalGear = State->GetTb2l() * vWhlBodyVec;
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
// vLocalGear now stores the vector from the cg to the wheel in local coords.
compressLength = vLocalGear(eZ) - Position->GetDistanceAGL();
compressLength = vLocalGear(eZ) - Propagate->GetDistanceAGL();
// The compression length is currently measured in the Z-axis, only, at this time.
// It should be measured along the strut axis. If the local-frame gear position
@ -262,8 +261,8 @@ FGColumnVector3& FGLGear::Force(void)
// (used for calculating damping force) is found by taking the Z-component of the
// wheel velocity.
vWhlVelVec = State->GetTb2l() * (Rotation->GetPQR() * vWhlBodyVec);
vWhlVelVec += Position->GetVel();
vWhlVelVec = Propagate->GetTb2l() * (Propagate->GetPQR() * vWhlBodyVec);
vWhlVelVec += Propagate->GetVel();
compressSpeed = vWhlVelVec(eZ);
// If this is the first time the wheel has made contact, remember some values
@ -272,13 +271,13 @@ FGColumnVector3& FGLGear::Force(void)
if (!FirstContact) {
FirstContact = true;
SinkRate = compressSpeed;
GroundSpeed = Position->GetVel().Magnitude();
GroundSpeed = Propagate->GetVel().Magnitude();
TakeoffReported = false;
}
// If the takeoff run is starting, initialize.
if ((Position->GetVel().Magnitude() > 0.1) &&
if ((Propagate->GetVel().Magnitude() > 0.1) &&
(FCS->GetBrake(bgLeft) == 0) &&
(FCS->GetBrake(bgRight) == 0) &&
(FCS->GetThrottlePos(0) == 1) && !StartedGroundRun)
@ -344,8 +343,8 @@ FGColumnVector3& FGLGear::Force(void)
// For now, steering angle is assumed to happen in the Local Z axis,
// not the strut axis as it should be. Will fix this later.
SinWheel = sin(Rotation->Getpsi() + SteerAngle);
CosWheel = cos(Rotation->Getpsi() + SteerAngle);
SinWheel = sin(Propagate->Getpsi() + SteerAngle);
CosWheel = cos(Propagate->Getpsi() + SteerAngle);
RollingWhlVel = vWhlVelVec(eX)*CosWheel + vWhlVelVec(eY)*SinWheel;
SideWhlVel = vWhlVelVec(eY)*CosWheel - vWhlVelVec(eX)*SinWheel;
@ -388,7 +387,7 @@ FGColumnVector3& FGLGear::Force(void)
// Compute the sideforce coefficients using similar assumptions to LaRCSim for now.
// Allow a maximum of 10 degrees tire slip angle before wheel slides. At that point,
// transition from static to dynamic friction. There are more complicated formulations
// of this that avoid the discrete jump. Will fix this later.
// of this that avoid the discrete jump (similar to Pacejka). Will fix this later.
if (fabs(WheelSlip) <= 20.0) {
FCoeff = staticFCoeff*WheelSlip/20.0;
@ -441,14 +440,14 @@ FGColumnVector3& FGLGear::Force(void)
// Transform the forces back to the body frame and compute the moment.
vForce = State->GetTl2b() * vLocalForce;
vForce = Propagate->GetTl2b() * vLocalForce;
vMoment = vWhlBodyVec * vForce;
} else { // Gear is NOT compressed
WOW = false;
if (Position->GetDistanceAGL() > 200.0) {
if (Propagate->GetDistanceAGL() > 200.0) {
FirstContact = false;
StartedGroundRun = false;
LandingReported = false;
@ -459,19 +458,19 @@ FGColumnVector3& FGLGear::Force(void)
compressLength = 0.0; // reset compressLength to zero for data output validity
}
if (FirstContact) LandingDistanceTraveled += Position->GetVground()*deltaT;
if (FirstContact) LandingDistanceTraveled += Auxiliary->GetVground()*deltaT;
if (StartedGroundRun) {
TakeoffDistanceTraveled50ft += Position->GetVground()*deltaT;
if (WOW) TakeoffDistanceTraveled += Position->GetVground()*deltaT;
TakeoffDistanceTraveled50ft += Auxiliary->GetVground()*deltaT;
if (WOW) TakeoffDistanceTraveled += Auxiliary->GetVground()*deltaT;
}
if (ReportEnable && Position->GetVground() <= 0.05 && !LandingReported) {
if (ReportEnable && Auxiliary->GetVground() <= 0.05 && !LandingReported) {
if (debug_lvl > 0) Report(erLand);
}
if (ReportEnable && !TakeoffReported &&
(vLocalGear(eZ) - Position->GetDistanceAGL()) < -50.0)
(vLocalGear(eZ) - Propagate->GetDistanceAGL()) < -50.0)
{
if (debug_lvl > 0) Report(erTakeoff);
}

View file

@ -44,9 +44,9 @@ INCLUDES
#include "FGJSBBase.h"
#include "FGFDMExec.h"
#include <string>
#include "FGConfigFile.h"
#include "FGColumnVector3.h"
#include <string>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
@ -61,11 +61,11 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGAircraft;
class FGPosition;
class FGRotation;
class FGPropagate;
class FGFCS;
class FGState;
class FGMassBalance;
class FGAuxiliary;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -160,9 +160,9 @@ CLASS DOCUMENTATION
@author Jon S. Berndt
@version $Id$
@see Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
NASA-Ames", NASA CR-2497, January 1975
@see Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
Wiley & Sons, 1979 ISBN 0-471-03032-5
Wiley & Sons, 1979 ISBN 0-471-03032-5
@see W. A. Ragsdale, "A Generic Landing Gear Dynamics Model for LASRS++",
AIAA-2000-4303
*/
@ -293,12 +293,12 @@ private:
SteerType eSteerType;
double maxSteerAngle;
FGFDMExec* Exec;
FGState* State;
FGAircraft* Aircraft;
FGPosition* Position;
FGRotation* Rotation;
FGFCS* FCS;
FGFDMExec* Exec;
FGState* State;
FGAircraft* Aircraft;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGFCS* FCS;
FGMassBalance* MassBalance;
void Report(ReportType rt);
@ -306,13 +306,12 @@ private:
};
}
#include "FGAircraft.h"
#include "FGPosition.h"
#include "FGRotation.h"
#include "FGPropagate.h"
#include "FGAuxiliary.h"
#include "FGFCS.h"
#include "FGMassBalance.h"
#include "FGState.h"
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#include "FGState.h"
#endif

View file

@ -0,0 +1,216 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 (jsb@hal-pc.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 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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU 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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# ifdef SG_HAVE_STD_INCLUDES
# include <cmath>
# else
# include <math.h>
# endif
#else
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
#endif
#include "FGLocation.h"
#include "FGPropertyManager.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_LOCATION;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGLocation::FGLocation(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 = FGColumnVector3( radius*cosLat*cosLon,
radius*cosLat*sinLon,
radius*sinLat );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLocation::SetLongitude(double longitude)
{
double rtmp = sqrt(mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY));
// Fast return if we are on the north or south pole ...
if (rtmp == 0.0)
return;
mCacheValid = false;
mECLoc(eX) = rtmp*sin(longitude);
mECLoc(eY) = rtmp*cos(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 = sqrt(mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(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::ComputeDerivedUnconditional(void) const
{
// The radius is just the Euclidean norm of the vector.
mRadius = mECLoc.Magnitude();
// The distance of the location to the y-axis, which is the axis
// through the poles.
double rxy = sqrt(mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY));
// Compute the sin/cos values of the longitude
double sinLon, cosLon;
if (rxy == 0.0) {
sinLon = 0.0;
cosLon = 1.0;
} else {
sinLon = mECLoc(eY)/rxy;
cosLon = mECLoc(eX)/rxy;
}
// Compute the sin/cos values of the latitude
double sinLat, cosLat;
if (mRadius == 0.0) {
sinLat = 0.0;
cosLat = 1.0;
} else {
sinLat = mECLoc(eZ)/mRadius;
cosLat = rxy/mRadius;
}
// Compute the longitude and latitude itself
if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
mLon = 0.0;
else
mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
mLat = 0.0;
else
mLat = atan2( mECLoc(eZ), rxy );
// Compute the transform matrices from and to the earth centered frame.
// see Durham Chapter 4, problem 1, page 52
mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
-sinLon , cosLon , 0.0 ,
-cosLon*cosLat, -sinLon*cosLat, -sinLat );
mTl2ec = mTec2l.Transposed();
// Mark the cached values as valid
mCacheValid = true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLocation::bind(FGPropertyManager* PropertyManager, const string& prefix) const
{
PropertyManager->Tie(prefix + "lat-gc-rad", (FGLocation*)this,
&FGLocation::GetLatitude);
PropertyManager->Tie(prefix + "lat-gc-deg", (FGLocation*)this,
&FGLocation::GetLatitudeDeg);
PropertyManager->Tie(prefix + "long-gc-rad", (FGLocation*)this,
&FGLocation::GetLongitude);
PropertyManager->Tie(prefix + "long-gc-deg", (FGLocation*)this,
&FGLocation::GetLongitudeDeg);
PropertyManager->Tie(prefix + "radius-ft", (FGLocation*)this,
&FGLocation::GetRadius);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLocation::unbind(FGPropertyManager* PropertyManager, const string& prefix) const
{
PropertyManager->Untie(prefix + "lat-gc-rad");
PropertyManager->Untie(prefix + "lat-gc-deg");
PropertyManager->Untie(prefix + "long-gc-rad");
PropertyManager->Untie(prefix + "long-gc-deg");
PropertyManager->Untie(prefix + "radius-ft");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
} // namespace JSBSim

437
src/FDM/JSBSim/FGLocation.h Normal file
View file

@ -0,0 +1,437 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGLocation.h
Author: Jon S. Berndt, Mathias Froehlich
Date started: 04/04/2004
------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.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 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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU 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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGLOCATION_H
#define FGLOCATION_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGJSBBase.h"
#include "FGPropertyManager.h"
#include "FGColumnVector3.h"
#include "FGMatrix33.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_LOCATION "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Holds an arbitrary location in the earth centered reference frame.
This 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.
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. This frame has its x/y plane parallel to the surface of the earth
(with the assumption of a spherical earth). The x-axis points
towards north, the y-axis points towards east and the z-axis
points to the center of the earth.
Since this frame is determined by the location, this class also
provides the rotation matrices required to transform from the
earth centered frame to the local horizontal frame and back. There
are also 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 coordinates 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, the earths surface
radius is about 2*10^7, ft and that we use double values for the
representation of the location, we have an accuracy of about
1e-16*2e7ft/1=2e-9ft 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.61deg north 122.35deg west, which
corresponds to 0.65642rad north and 2.13541rad west. Both values
are of magnitude of about 1. But 1ft corresponds to about
1/(2e7*2*pi)=7.9577e-09rad. 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 too.
@see W. C. Durham "Aircraft Dynamics & Control", section 2.2
@author Mathias Froehlich
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGLocation : virtual FGJSBBase
{
public:
/** Default constructor. */
FGLocation() { mCacheValid = false; }
/** Constructor to set the longitude, latitude and the distance
from the center of the earth. */
FGLocation(double lon, double lat, double radius);
/** Copy constructor. */
FGLocation(const FGColumnVector3& lv)
: mECLoc(lv), mCacheValid(false) {}
/** Copy constructor. */
FGLocation(const FGLocation& l)
: mECLoc(l.mECLoc), mCacheValid(l.mCacheValid) {
if (!mCacheValid)
return;
mLon = l.mLon;
mLat = l.mLat;
mRadius = l.mRadius;
mTl2ec = l.mTl2ec;
mTec2l = l.mTec2l;
}
/** 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; }
/** 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. */
void SetLongitude(double longitude);
/** 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 latitude.
@return the 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 latitude.
@return the latitude in deg of the location represented with this
class instance. The returned values are in the range between
-90 <= lon <= 90. Latitude is positive north and negative south. */
double GetLatitudeDeg() const { ComputeDerived(); return radtodeg*mLat; }
/** Set the latitude.
@param latitude 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);
/** Get the sine of Latitude. */
double GetSinLatitude() const { ComputeDerived(); return -mTec2l(3,3); }
/** Get the cosine of Latitude. */
double GetCosLatitude() const { ComputeDerived(); return mTec2l(1,3); }
/** Get the cosine of Latitude. */
double GetTanLatitude() const {
ComputeDerived();
double cLat = mTec2l(1,3);
if (cLat == 0.0)
return 0.0;
else
return -mTec2l(3,3)/cLat;
}
/** Get the distance from the center of the earth.
@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; }
/** 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);
/** Transform matrix from local horizontal to earth centered frame.
Returns 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.
Returns 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; }
/** Conversion from Local frame coordinates to a location in the
earth centered and fixed frame.
@parm 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.
@parm 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 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) { return 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 @ref double
operator()(unsigned int idx) const 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);
}
const FGLocation& operator=(const FGLocation& l) {
mECLoc = l.mECLoc;
mCacheValid = l.mCacheValid;
if (!mCacheValid)
return *this;
mLon = l.mLon;
mLat = l.mLat;
mRadius = l.mRadius;
mTl2ec = l.mTl2ec;
mTec2l = l.mTec2l;
return *this;
}
bool operator==(const FGLocation& l) const {
return mECLoc == l.mECLoc;
}
bool operator!=(const FGLocation& l) const { return ! operator==(l); }
const FGLocation& operator+=(const FGLocation &l) {
mCacheValid = false;
mECLoc += l.mECLoc;
return *this;
}
const FGLocation& operator-=(const FGLocation &l) {
mCacheValid = false;
mECLoc -= l.mECLoc;
return *this;
}
const FGLocation& operator*=(double scalar) {
mCacheValid = false;
mECLoc *= scalar;
return *this;
}
const FGLocation& operator/=(double scalar) {
return operator*=(1.0/scalar);
}
FGLocation operator+(const FGLocation& l) const {
return FGLocation(mECLoc + l.mECLoc);
}
FGLocation operator-(const FGLocation& l) const {
return FGLocation(mECLoc - l.mECLoc);
}
FGLocation operator*(double scalar) const {
return FGLocation(scalar*mECLoc);
}
/** Cast to a simple 3d vector */
operator const FGColumnVector3&() const {
return mECLoc;
}
/** Ties into the property tree.
Ties the variables represented by this class into the property tree. */
void bind(FGPropertyManager*, const string&) const;
/** Remove from property tree.
Unties the variables represented by this class into the property tree. */
void unbind(FGPropertyManager*, const string&) const;
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;
/** The cached rotation matrices from and to the associated frames. */
mutable FGMatrix33 mTl2ec;
mutable FGMatrix33 mTec2l;
/** 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;
};
/** 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

@ -27,7 +27,7 @@
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This base class for the FGAero, FGRotational, etc. classes defines methods
This base class for the FGAerodynamics, FGPropagate, etc. classes defines methods
common to all models.
HISTORY
@ -49,9 +49,7 @@ INCLUDES
#include "FGInertial.h"
#include "FGGroundReactions.h"
#include "FGAircraft.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGAuxiliary.h"
#include "FGOutput.h"
@ -82,9 +80,7 @@ FGModel::FGModel(FGFDMExec* fdmex)
Inertial = 0;
GroundReactions = 0;
Aircraft = 0;
Translation = 0;
Rotation = 0;
Position = 0;
Propagate = 0;
Auxiliary = 0;
Output = 0;
@ -119,9 +115,7 @@ bool FGModel::InitModel(void)
Inertial = FDMExec->GetInertial();
GroundReactions = FDMExec->GetGroundReactions();
Aircraft = FDMExec->GetAircraft();
Translation = FDMExec->GetTranslation();
Rotation = FDMExec->GetRotation();
Position = FDMExec->GetPosition();
Propagate = FDMExec->GetPropagate();
Auxiliary = FDMExec->GetAuxiliary();
Output = FDMExec->GetOutput();
@ -134,9 +128,7 @@ bool FGModel::InitModel(void)
!Inertial ||
!GroundReactions ||
!Aircraft ||
!Translation ||
!Rotation ||
!Position ||
!Propagate ||
!Auxiliary ||
!Output) return(false);
else return(true);

View file

@ -82,9 +82,7 @@ class FGAerodynamics;
class FGInertial;
class FGGroundReactions;
class FGAircraft;
class FGTranslation;
class FGRotation;
class FGPosition;
class FGPropagate;
class FGAuxiliary;
class FGOutput;
class FGConfigFile;
@ -144,9 +142,7 @@ protected:
FGInertial* Inertial;
FGGroundReactions* GroundReactions;
FGAircraft* Aircraft;
FGTranslation* Translation;
FGRotation* Rotation;
FGPosition* Position;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGOutput* Output;
FGPropertyManager* PropertyManager;

View file

@ -35,12 +35,7 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# include STL_ALGORITHM
#else
# include <algorithm>
#endif
#include <sstream>
#include "FGNozzle.h"
#include "FGAtmosphere.h"
@ -104,6 +99,28 @@ double FGNozzle::GetPowerRequired(void)
return PE;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGNozzle::GetThrusterLabels(int id)
{
std::ostringstream buf;
buf << Name << "_Thrust[" << id << ']';
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGNozzle::GetThrusterValues(int id)
{
std::ostringstream buf;
buf << Thrust;
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -75,6 +75,8 @@ public:
double Calculate(double CfPc);
double GetPowerRequired(void);
string GetThrusterLabels(int id);
string GetThrusterValues(int id);
private:
double ReverserAngle;

View file

@ -47,12 +47,12 @@ INCLUDES
#include "FGGroundReactions.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGAuxiliary.h"
#include "FGInertial.h"
#include <iomanip>
namespace JSBSim {
static const char *IdSrc = "$Id$";
@ -91,22 +91,17 @@ FGOutput::~FGOutput()
bool FGOutput::Run(void)
{
if (enabled) {
if (!FGModel::Run()) {
if (Type == otSocket) {
SocketOutput();
} else if (Type == otCSV) {
DelimitedOutput(Filename);
} else if (Type == otTerminal) {
// Not done yet
} else if (Type == otNone) {
// Do nothing
} else {
// Not a valid type of output
}
return false;
if (FGModel::Run()) return true;
if (Type == otSocket) {
SocketOutput();
} else if (Type == otCSV) {
DelimitedOutput(Filename);
} else if (Type == otTerminal) {
// Not done yet
} else if (Type == otNone) {
// Do nothing
} else {
return true;
// Not a valid type of output
}
}
return false;
@ -180,10 +175,7 @@ void FGOutput::DelimitedOutput(string fname)
outstream << ", ";
outstream << "Drag, Side, Lift, ";
outstream << "L/D, ";
outstream << "Xforce, Yforce, Zforce, ";
outstream << "xGravity, yGravity, zGravity, ";
outstream << "xCoriolis, yCoriolis, zCoriolis, ";
outstream << "xCentrifugal, yCentrifugal, zCentrifugal";
outstream << "Xforce, Yforce, Zforce";
}
if (SubSystems & ssMoments) {
outstream << ", ";
@ -208,14 +200,14 @@ void FGOutput::DelimitedOutput(string fname)
outstream << "Mass, ";
outstream << "Xcg, Ycg, Zcg";
}
if (SubSystems & ssPosition) {
if (SubSystems & ssPropagate) {
outstream << ", ";
outstream << "Altitude, ";
outstream << "Phi, Tht, Psi, ";
outstream << "Alpha, ";
outstream << "Beta, ";
outstream << "Latitude, ";
outstream << "Longitude, ";
outstream << "Latitude (Deg), ";
outstream << "Longitude (Deg), ";
outstream << "Distance AGL, ";
outstream << "Runway Radius";
}
@ -262,25 +254,22 @@ void FGOutput::DelimitedOutput(string fname)
}
if (SubSystems & ssRates) {
outstream << ", ";
outstream << Rotation->GetPQR() << ", ";
outstream << Rotation->GetPQRdot();
outstream << Propagate->GetPQR() << ", ";
outstream << Propagate->GetPQRdot();
}
if (SubSystems & ssVelocities) {
outstream << ", ";
outstream << Translation->Getqbar() << ", ";
outstream << Translation->GetVt() << ", ";
outstream << Translation->GetUVW() << ", ";
outstream << Translation->GetAeroUVW() << ", ";
outstream << Position->GetVel();
outstream << Auxiliary->Getqbar() << ", ";
outstream << setprecision(12) << Auxiliary->GetVt() << ", ";
outstream << setprecision(12) << Propagate->GetUVW() << ", ";
outstream << Auxiliary->GetAeroUVW() << ", ";
outstream << Propagate->GetVel();
}
if (SubSystems & ssForces) {
outstream << ", ";
outstream << Aerodynamics->GetvFs() << ", ";
outstream << Aerodynamics->GetLoD() << ", ";
outstream << Aircraft->GetForces() << ", ";
outstream << Inertial->GetGravity() << ", ";
outstream << Inertial->GetCoriolis() << ", ";
outstream << Inertial->GetCentrifugal();
outstream << Aircraft->GetForces();
}
if (SubSystems & ssMoments) {
outstream << ", ";
@ -297,16 +286,16 @@ void FGOutput::DelimitedOutput(string fname)
outstream << MassBalance->GetMass() << ", ";
outstream << MassBalance->GetXYZcg();
}
if (SubSystems & ssPosition) {
if (SubSystems & ssPropagate) {
outstream << ", ";
outstream << Position->Geth() << ", ";
outstream << Rotation->GetEuler() << ", ";
outstream << Translation->Getalpha() << ", ";
outstream << Translation->Getbeta() << ", ";
outstream << Position->GetLatitude() << ", ";
outstream << Position->GetLongitude() << ", ";
outstream << Position->GetDistanceAGL() << ", ";
outstream << Position->GetRunwayRadius();
outstream << Propagate->Geth() << ", ";
outstream << Propagate->GetEuler() << ", ";
outstream << Auxiliary->Getalpha(inDegrees) << ", ";
outstream << Auxiliary->Getbeta(inDegrees) << ", ";
outstream << Propagate->GetLocation().GetLatitudeDeg() << ", ";
outstream << Propagate->GetLocation().GetLongitudeDeg() << ", ";
outstream << Propagate->GetDistanceAGL() << ", ";
outstream << Propagate->GetRunwayRadius();
}
if (SubSystems & ssCoefficients) {
scratch = Aerodynamics->GetCoefficientValues();
@ -373,8 +362,8 @@ void FGOutput::SocketOutput(void)
socket->Append("Fx");
socket->Append("Fy");
socket->Append("Fz");
socket->Append("Latitude");
socket->Append("Longitude");
socket->Append("Latitude (Deg)");
socket->Append("Longitude (Deg)");
socket->Append("QBar");
socket->Append("Alpha");
socket->Append("L");
@ -391,37 +380,37 @@ void FGOutput::SocketOutput(void)
socket->Clear();
socket->Append(State->Getsim_time());
socket->Append(Position->Geth());
socket->Append(Rotation->Getphi());
socket->Append(Rotation->Gettht());
socket->Append(Rotation->Getpsi());
socket->Append(Propagate->Geth());
socket->Append(Propagate->Getphi());
socket->Append(Propagate->Gettht());
socket->Append(Propagate->Getpsi());
socket->Append(Atmosphere->GetDensity());
socket->Append(Translation->GetVt());
socket->Append(Translation->GetUVW(eU));
socket->Append(Translation->GetUVW(eV));
socket->Append(Translation->GetUVW(eW));
socket->Append(Translation->GetAeroUVW(eU));
socket->Append(Translation->GetAeroUVW(eV));
socket->Append(Translation->GetAeroUVW(eW));
socket->Append(Position->GetVn());
socket->Append(Position->GetVe());
socket->Append(Position->GetVd());
socket->Append(Translation->GetUVWdot(eU));
socket->Append(Translation->GetUVWdot(eV));
socket->Append(Translation->GetUVWdot(eW));
socket->Append(Rotation->GetPQR(eP));
socket->Append(Rotation->GetPQR(eQ));
socket->Append(Rotation->GetPQR(eR));
socket->Append(Rotation->GetPQRdot(eP));
socket->Append(Rotation->GetPQRdot(eQ));
socket->Append(Rotation->GetPQRdot(eR));
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));
socket->Append(Propagate->GetUVWdot(eU));
socket->Append(Propagate->GetUVWdot(eV));
socket->Append(Propagate->GetUVWdot(eW));
socket->Append(Propagate->GetPQR(eP));
socket->Append(Propagate->GetPQR(eQ));
socket->Append(Propagate->GetPQR(eR));
socket->Append(Propagate->GetPQRdot(eP));
socket->Append(Propagate->GetPQRdot(eQ));
socket->Append(Propagate->GetPQRdot(eR));
socket->Append(Aircraft->GetForces(eX));
socket->Append(Aircraft->GetForces(eY));
socket->Append(Aircraft->GetForces(eZ));
socket->Append(Position->GetLatitude());
socket->Append(Position->GetLongitude());
socket->Append(Translation->Getqbar());
socket->Append(Translation->Getalpha());
socket->Append(Propagate->GetLocation().GetLatitudeDeg());
socket->Append(Propagate->GetLocation().GetLongitudeDeg());
socket->Append(Auxiliary->Getqbar());
socket->Append(Auxiliary->Getalpha(inDegrees));
socket->Append(Aircraft->GetMoments(eL));
socket->Append(Aircraft->GetMoments(eM));
socket->Append(Aircraft->GetMoments(eN));
@ -456,6 +445,7 @@ bool FGOutput::Load(FGConfigFile* AC_cfg)
int OutRate = 0;
FGConfigFile* Output_cfg;
string property;
unsigned int port;
# ifndef macintosh
separator = "/";
@ -466,13 +456,13 @@ bool FGOutput::Load(FGConfigFile* AC_cfg)
name = AC_cfg->GetValue("NAME");
fname = AC_cfg->GetValue("FILE");
token = AC_cfg->GetValue("TYPE");
port = atoi(AC_cfg->GetValue("PORT").c_str());
Output->SetType(token);
#if defined( FG_WITH_JSBSIM_SOCKET ) || !defined( FGFS )
if (token == "SOCKET") {
socket = new FGfdmSocket("localhost",1138);
socket = new FGfdmSocket(name,port);
}
#endif
if (!fname.empty()) {
outputInFileName = FDMExec->GetAircraftPath() + separator
@ -526,7 +516,7 @@ bool FGOutput::Load(FGConfigFile* AC_cfg)
}
if (parameter == "POSITION") {
*Output_cfg >> parameter;
if (parameter == "ON") SubSystems += ssPosition;
if (parameter == "ON") SubSystems += ssPropagate;
}
if (parameter == "COEFFICIENTS") {
*Output_cfg >> parameter;
@ -618,10 +608,14 @@ void FGOutput::Debug(int from)
if (SubSystems & ssAtmosphere) cout << " Atmosphere parameters logged" << endl;
if (SubSystems & ssMassProps) cout << " Mass parameters logged" << endl;
if (SubSystems & ssCoefficients) cout << " Coefficient parameters logged" << endl;
if (SubSystems & ssPosition) cout << " Position 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 (OutputProperties.size() > 0) cout << " Properties logged:" << endl;
for (unsigned int i=0;i<OutputProperties.size();i++) {
cout << " - " << OutputProperties[i]->GetName() << endl;
}
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification

View file

@ -73,7 +73,50 @@ CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Handles simulation output.
*/
OUTPUT section definition
The following specifies the way that JSBSim writes out data.
NAME is the filename you want the output to go to
TYPE can be:
CSV Comma separated data. If a filename is supplied then the data
goes to that file. If COUT or cout is specified, the data goes
to stdout. If the filename is a null filename the data goes to
stdout, as well.
SOCKET Will eventually send data to a socket output, where NAME
would then be the IP address of the machine the data should be
sent to. DON'T USE THIS YET!
TABULAR Columnar data. NOT IMPLEMENTED YET!
TERMINAL Output to terminal. NOT IMPLEMENTED YET!
NONE Specifies to do nothing. THis setting makes it easy to turn on and
off the data output without having to mess with anything else.
The arguments that can be supplied, currently, are
RATE_IN_HZ An integer rate in times-per-second that the data is output. This
value may not be *exactly* what you want, due to the dependence
on dt, the cycle rate for the FDM.
The following parameters tell which subsystems of data to output:
SIMULATION ON|OFF
ATMOSPHERE ON|OFF
MASSPROPS ON|OFF
AEROSURFACES ON|OFF
RATES ON|OFF
VELOCITIES ON|OFF
FORCES ON|OFF
MOMENTS ON|OFF
POSITION ON|OFF
COEFFICIENTS ON|OFF
GROUND_REACTIONS ON|OFF
FCS ON|OFF
PROPULSION ON|OFF
NOTE that Time is always output with the data.
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
@ -109,7 +152,7 @@ public:
/** Subsystem: Atmosphere (= 64) */ ssAtmosphere = 64,
/** Subsystem: Mass Properties (= 128) */ ssMassProps = 128,
/** Subsystem: Coefficients (= 256) */ ssCoefficients = 256,
/** Subsystem: Position (= 512) */ ssPosition = 512,
/** Subsystem: Propagate (= 512) */ ssPropagate = 512,
/** Subsystem: Ground Reactions (= 1024) */ ssGroundReactions = 1024,
/** Subsystem: FCS (= 2048) */ ssFCS = 2048,
/** Subsystem: Propulsion (= 4096) */ ssPropulsion = 4096

View file

@ -39,8 +39,11 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include "FGPiston.h"
#include "FGPropulsion.h"
#include "FGPropeller.h"
namespace JSBSim {
@ -67,6 +70,9 @@ FGPiston::FGPiston(FGFDMExec* exec, FGConfigFile* Eng_cfg) : FGEngine(exec),
MinManifoldPressure_inHg = 6.5;
MaxManifoldPressure_inHg = 28.5;
ManifoldPressure_inHg = Atmosphere->GetPressure() * psftoinhg; // psf to in Hg
minMAP = 21950;
maxMAP = 96250;
MAP = Atmosphere->GetPressure() * 47.88; // psf to Pa
CylinderHeadTemp_degK = 0.0;
Displacement = 360;
MaxHP = 200;
@ -78,6 +84,25 @@ FGPiston::FGPiston(FGFDMExec* exec, FGConfigFile* Eng_cfg) : FGEngine(exec),
dt = State->Getdt();
// Supercharging
BoostSpeeds = 0; // Default to no supercharging
BoostSpeed = 0;
Boosted = false;
BoostOverride = 0;
bBoostOverride = false;
bTakeoffBoost = false;
TakeoffBoost = 0.0; // Default to no extra takeoff-boost
int i;
for(i=0; i<FG_MAX_BOOST_SPEEDS; ++i) {
RatedBoost[i] = 0.0;
RatedPower[i] = 0.0;
RatedAltitude[i] = 0.0;
BoostMul[i] = 1.0;
RatedMAP[i] = 100000;
RatedRPM[i] = 2500;
TakeoffMAP[i] = 100000;
}
// Initialisation
volumetric_efficiency = 0.8; // Actually f(speed, load) but this will get us running
@ -123,9 +148,79 @@ FGPiston::FGPiston(FGFDMExec* exec, FGConfigFile* Eng_cfg) : FGEngine(exec),
else if (token == "IDLERPM") *Eng_cfg >> IdleRPM;
else if (token == "MAXTHROTTLE") *Eng_cfg >> MaxThrottle;
else if (token == "MINTHROTTLE") *Eng_cfg >> MinThrottle;
else if (token == "NUMBOOSTSPEEDS") *Eng_cfg >> BoostSpeeds;
else if (token == "BOOSTOVERRIDE") *Eng_cfg >> BoostOverride;
else if (token == "TAKEOFFBOOST") *Eng_cfg >> TakeoffBoost;
else if (token == "RATEDBOOST1") *Eng_cfg >> RatedBoost[0];
else if (token == "RATEDBOOST2") *Eng_cfg >> RatedBoost[1];
else if (token == "RATEDBOOST3") *Eng_cfg >> RatedBoost[2];
else if (token == "RATEDPOWER1") *Eng_cfg >> RatedPower[0];
else if (token == "RATEDPOWER2") *Eng_cfg >> RatedPower[1];
else if (token == "RATEDPOWER3") *Eng_cfg >> RatedPower[2];
else if (token == "RATEDRPM1") *Eng_cfg >> RatedRPM[0];
else if (token == "RATEDRPM2") *Eng_cfg >> RatedRPM[1];
else if (token == "RATEDRPM3") *Eng_cfg >> RatedRPM[2];
else if (token == "RATEDALTITUDE1") *Eng_cfg >> RatedAltitude[0];
else if (token == "RATEDALTITUDE2") *Eng_cfg >> RatedAltitude[1];
else if (token == "RATEDALTITUDE3") *Eng_cfg >> RatedAltitude[2];
else cerr << "Unhandled token in Engine config file: " << token << endl;
}
minMAP = MinManifoldPressure_inHg * 3376.85; // inHg to Pa
maxMAP = MaxManifoldPressure_inHg * 3376.85;
// Set up and sanity-check the turbo/supercharging configuration based on the input values.
if(TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true;
for(i=0; i<BoostSpeeds; ++i) {
bool bad = false;
if(RatedBoost[i] <= 0.0) bad = true;
if(RatedPower[i] <= 0.0) bad = true;
if(RatedAltitude[i] < 0.0) bad = true; // 0.0 is deliberately allowed - this corresponds to unregulated supercharging.
if(i > 0 && RatedAltitude[i] < RatedAltitude[i - 1]) bad = true;
if(bad) {
// We can't recover from the above - don't use this supercharger speed.
BoostSpeeds--;
// TODO - put out a massive error message!
break;
}
// Now sanity-check stuff that is recoverable.
if(i < BoostSpeeds - 1) {
if(BoostSwitchAltitude[i] < RatedAltitude[i]) {
// TODO - put out an error message
// But we can also make a reasonable estimate, as below.
BoostSwitchAltitude[i] = RatedAltitude[i] + 1000;
}
BoostSwitchPressure[i] = Atmosphere->GetPressure(BoostSwitchAltitude[i]) * 47.88;
//cout << "BoostSwitchAlt = " << BoostSwitchAltitude[i] << ", pressure = " << BoostSwitchPressure[i] << '\n';
// Assume there is some hysteresis on the supercharger gear switch, and guess the value for now
BoostSwitchHysteresis = 1000;
}
// Now work out the supercharger pressure multiplier of this speed from the rated boost and altitude.
RatedMAP[i] = Atmosphere->GetPressureSL() * 47.88 + RatedBoost[i] * 6895; // psf*47.88 = Pa, psi*6895 = Pa.
// Sometimes a separate BCV setting for takeoff or extra power is fitted.
if(TakeoffBoost > RatedBoost[0]) {
// Assume that the effect on the BCV is the same whichever speed is in use.
TakeoffMAP[i] = RatedMAP[i] + ((TakeoffBoost - RatedBoost[0]) * 6895);
bTakeoffBoost = true;
} else {
TakeoffMAP[i] = RatedMAP[i];
bTakeoffBoost = false;
}
BoostMul[i] = RatedMAP[i] / (Atmosphere->GetPressure(RatedAltitude[i]) * 47.88);
// TODO - get rid of the debugging output before sending it to Jon
//cout << "Speed " << i+1 << '\n';
//cout << "BoostMul = " << BoostMul[i] << ", RatedMAP = " << RatedMAP[i] << ", TakeoffMAP = " << TakeoffMAP[i] << '\n';
}
if(BoostSpeeds > 0) {
Boosted = true;
BoostSpeed = 0;
}
bBoostOverride = (BoostOverride == 1 ? true : false);
//cout << "Engine is " << (Boosted ? "supercharged" : "naturally aspirated") << '\n';
Debug(0); // Call Debug() routine from constructor if needed
}
@ -138,7 +233,7 @@ FGPiston::~FGPiston()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPiston::Calculate(double PowerRequired)
double FGPiston::Calculate(void)
{
if (FuelFlow_gph > 0.0) ConsumeFuel();
@ -149,19 +244,19 @@ double FGPiston::Calculate(double PowerRequired)
// Input values.
//
p_amb = Atmosphere->GetPressure() * 48; // convert from lbs/ft2 to Pa
p_amb_sea_level = Atmosphere->GetPressureSL() * 48;
p_amb = Atmosphere->GetPressure() * 47.88; // convert from lbs/ft2 to Pa
p_amb_sea_level = Atmosphere->GetPressureSL() * 47.88;
T_amb = Atmosphere->GetTemperature() * (5.0 / 9.0); // convert from Rankine to Kelvin
RPM = Propulsion->GetThruster(EngineNumber)->GetRPM() *
Propulsion->GetThruster(EngineNumber)->GetGearRatio();
RPM = Thruster->GetRPM() * Thruster->GetGearRatio();
IAS = Auxiliary->GetVcalibratedKTS();
doEngineStartup();
doManifoldPressure();
doAirFlow();
doFuelFlow();
doEngineStartup();
if(Boosted) doBoostControl();
doMAP();
doAirFlow();
doFuelFlow();
//Now that the fuel flow is done check if the mixture is too lean to run the engine
//Assume lean limit at 22 AFR for now - thats a thi of 0.668
@ -171,13 +266,18 @@ double FGPiston::Calculate(double PowerRequired)
Running = false;
doEnginePower();
doEGT();
doCHT();
doOilTemperature();
doOilPressure();
doEGT();
doCHT();
doOilTemperature();
doOilPressure();
PowerAvailable = (HP * hptoftlbssec) - PowerRequired;
return PowerAvailable;
if (Thruster->GetType() == FGThruster::ttPropeller) {
((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber));
}
PowerAvailable = (HP * hptoftlbssec) - Thruster->GetPowerRequired();
return Thruster->Calculate(PowerAvailable);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -250,50 +350,119 @@ void FGPiston::doEngineStartup(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/**
* Calculate the nominal manifold pressure in inches hg
* Calculate the Current Boost Speed
*
* This function calculates nominal manifold pressure directly
* from the throttle position, and does not adjust it for the
* difference between the pressure at sea level and the pressure
* at the current altitude (that adjustment takes place in
* {@link #doEnginePower}).
* This function calculates the current turbo/supercharger boost speed
* based on altitude and the (automatic) boost-speed control valve configuration.
*
* Inputs: p_amb, BoostSwitchPressure, BoostSwitchHysteresis
*
* Outputs: BoostSpeed
*/
void FGPiston::doBoostControl(void)
{
if(BoostSpeed < BoostSpeeds - 1) {
// Check if we need to change to a higher boost speed
if(p_amb < BoostSwitchPressure[BoostSpeed] - BoostSwitchHysteresis) {
BoostSpeed++;
}
} else if(BoostSpeed > 0) {
// Check if we need to change to a lower boost speed
if(p_amb > BoostSwitchPressure[BoostSpeed - 1] + BoostSwitchHysteresis) {
BoostSpeed--;
}
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/**
* Calculate the manifold absolute pressure (MAP) in inches hg
*
* This function calculates manifold absolute pressure (MAP)
* from the throttle position, turbo/supercharger boost control
* system, engine speed and local ambient air density.
*
* TODO: changes in MP should not be instantaneous -- introduce
* a lag between throttle changes and MP changes, to allow pressure
* to build up or disperse.
*
* Inputs: MinManifoldPressure_inHg, MaxManifoldPressure_inHg, Throttle
* Inputs: minMAP, maxMAP, p_amb, Throttle
*
* Outputs: ManifoldPressure_inHg
* Outputs: MAP, ManifoldPressure_inHg
*/
void FGPiston::doManifoldPressure(void)
void FGPiston::doMAP(void)
{
if (Running || Cranking) {
ManifoldPressure_inHg = MinManifoldPressure_inHg +
(Throttle * (MaxManifoldPressure_inHg - MinManifoldPressure_inHg));
if(RPM > 10) {
// Naturally aspirated
MAP = minMAP + (Throttle * (maxMAP - minMAP));
MAP *= p_amb / p_amb_sea_level;
if(Boosted) {
// If takeoff boost is fitted, we currently assume the following throttle map:
// (In throttle % - actual input is 0 -> 1)
// 99 / 100 - Takeoff boost
// 96 / 97 / 98 - Rated boost
// 0 - 95 - Idle to Rated boost (MinManifoldPressure to MaxManifoldPressure)
// In real life, most planes would be fitted with a mechanical 'gate' between
// the rated boost and takeoff boost positions.
double T = Throttle; // processed throttle value.
bool bTakeoffPos = false;
if(bTakeoffBoost) {
if(Throttle > 0.98) {
//cout << "Takeoff Boost!!!!\n";
bTakeoffPos = true;
} else if(Throttle <= 0.95) {
bTakeoffPos = false;
T *= 1.0 / 0.95;
} else {
bTakeoffPos = false;
//cout << "Rated Boost!!\n";
T = 1.0;
}
}
// Boost the manifold pressure.
MAP *= BoostMul[BoostSpeed];
// Now clip the manifold pressure to BCV or Wastegate setting.
if(bTakeoffPos) {
if(MAP > TakeoffMAP[BoostSpeed]) {
MAP = TakeoffMAP[BoostSpeed];
}
} else {
if(MAP > RatedMAP[BoostSpeed]) {
MAP = RatedMAP[BoostSpeed];
}
}
}
} else {
ManifoldPressure_inHg = Atmosphere->GetPressure() * psftoinhg; // psf to in Hg
// rpm < 10 - effectively stopped.
// TODO - add a better variation of MAP with engine speed
MAP = Atmosphere->GetPressure() * 47.88; // psf to Pa
}
// And set the value in American units as well
ManifoldPressure_inHg = MAP / 3376.85;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/**
* Calculate the air flow through the engine.
* Also calculates ambient air density
* (used in CHT calculation for air-cooled engines).
*
* At this point, ManifoldPressure_inHg still represents the sea-level
* MP, not adjusted for altitude.
*
* Inputs: p_amb, R_air, T_amb, ManifoldPressure_inHg, Displacement,
* Inputs: p_amb, R_air, T_amb, MAP, Displacement,
* RPM, volumetric_efficiency
*
* TODO: Model inlet manifold air temperature.
*
* Outputs: rho_air, m_dot_air
*/
void FGPiston::doAirFlow(void)
{
rho_air = p_amb / (R_air * T_amb);
double rho_air_manifold = rho_air * ManifoldPressure_inHg / 29.6;
double rho_air_manifold = MAP / (R_air * T_amb);
double displacement_SI = Displacement * in3tom3;
double swept_volume = (displacement_SI * (RPM/60)) / 2;
double v_dot_air = swept_volume * volumetric_efficiency;
@ -337,26 +506,44 @@ void FGPiston::doFuelFlow(void)
void FGPiston::doEnginePower(void)
{
ManifoldPressure_inHg *= p_amb / p_amb_sea_level;
if (Running) {
double ManXRPM = ManifoldPressure_inHg * RPM;
double T_amb_degF = KelvinToFahrenheit(T_amb);
double T_amb_sea_lev_degF = KelvinToFahrenheit(288);
// FIXME: this needs to be generalized
Percentage_Power = (6e-9 * ManXRPM * ManXRPM) + (8e-4 * ManXRPM) - 1.0;
Percentage_Power += ((T_amb_sea_lev_degF - T_amb_degF) * 7 /120);
double ManXRPM; // Convienience term for use in the calculations
if(Boosted) {
// Currently a simple linear fit.
// The zero crossing is moved up the speed-load range to reduce the idling power.
// This will change!
double zeroOffset = (minMAP / 2.0) * (IdleRPM / 2.0);
ManXRPM = MAP * (RPM > RatedRPM[BoostSpeed] ? RatedRPM[BoostSpeed] : RPM);
// The speed clip in the line above is deliberate.
Percentage_Power = ((ManXRPM - zeroOffset) / ((RatedMAP[BoostSpeed] * RatedRPM[BoostSpeed]) - zeroOffset)) * 107.0;
Percentage_Power -= 7.0; // Another idle power reduction offset - see line above with 107.
if (Percentage_Power < 0.0) Percentage_Power = 0.0;
// Note that %power is allowed to go over 100 for boosted powerplants
// such as for the BCV-override or takeoff power settings.
// TODO - currently no altitude effect (temperature & exhaust back-pressure) modelled
// for boosted engines.
} else {
ManXRPM = ManifoldPressure_inHg * RPM; // Note that inHg must be used for the following correlation.
Percentage_Power = (6e-9 * ManXRPM * ManXRPM) + (8e-4 * ManXRPM) - 1.0;
Percentage_Power += ((T_amb_sea_lev_degF - T_amb_degF) * 7 /120);
if (Percentage_Power < 0.0) Percentage_Power = 0.0;
else if (Percentage_Power > 100.0) Percentage_Power = 100.0;
}
double Percentage_of_best_power_mixture_power =
Power_Mixture_Correlation->GetValue(14.7 / equivalence_ratio);
Percentage_Power *= Percentage_of_best_power_mixture_power / 100.0;
if (Percentage_Power < 0.0) Percentage_Power = 0.0;
else if (Percentage_Power > 100.0) Percentage_Power = 100.0;
HP = Percentage_Power * MaxHP / 100.0;
if(Boosted) {
HP = Percentage_Power * RatedPower[BoostSpeed] / 100.0;
} else {
HP = Percentage_Power * MaxHP / 100.0;
}
} else {
@ -380,6 +567,7 @@ void FGPiston::doEnginePower(void)
HP = 0.0;
}
}
//cout << "Power = " << HP << '\n';
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -508,6 +696,29 @@ void FGPiston::doOilPressure(void)
OilPressure_psi += (Design_Oil_Temp - OilTemp_degK) * Oil_Viscosity_Index;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPiston::GetEngineLabels(void)
{
std::ostringstream buf;
buf << Name << "_PwrAvail[" << EngineNumber << "], "
<< Thruster->GetThrusterLabels(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPiston::GetEngineValues(void)
{
std::ostringstream buf;
buf << PowerAvailable << ", " << Thruster->GetThrusterValues(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// The bitmasked value choices are as follows:

View file

@ -48,6 +48,7 @@ DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_PISTON "$Id$";
#define FG_MAX_BOOST_SPEEDS 3
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -59,11 +60,78 @@ namespace JSBSim {
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models Dave Luff's engine model as ported into JSBSim by David Megginson.
/** Models Dave Luff's Turbo/Supercharged Piston engine model.
Additional elements are required for a supercharged engine. These can be
left off a non-supercharged engine, ie. the changes are all backward
compatible at present.
- NUMBOOSTSPEEDS - zero (or not present) for a naturally-aspirated engine,
either 1, 2 or 3 for a boosted engine. This corresponds to the number of
supercharger speeds. Merlin XII had 1 speed, Merlin 61 had 2, a late
Griffon engine apparently had 3. No known engine more than 3, although
some German engines apparently had a continuously variable-speed
supercharger.
- BOOSTOVERRIDE - whether the boost pressure control system (either a boost
control valve for superchargers or wastegate for turbochargers) can be
overriden by the pilot. During wartime this was commonly possible, and
known as "War Emergency Power" by the Brits. 1 or 0 in the config file.
This isn't implemented in the model yet though, there would need to be
some way of getting the boost control cutout lever position (on or off)
from FlightGear first.
- The next items are all appended with either 1, 2 or 3 depending on which
boost speed they refer to, eg RATEDBOOST1. The rated values seems to have
been a common convention at the time to express the maximum continuously
available power, and the conditions to attain that power.
- RATEDBOOST[123] - the absolute rated boost above sea level ambient for a
given boost speed, in psi. Eg the Merlin XII had a rated boost of 9psi,
giving approximately 42inHg manifold pressure up to the rated altitude.
- RATEDALTITUDE[123] - The altitude up to which rated boost can be
maintained. Up to this altitude the boost is maintained constant for a
given throttle position by the BCV or wastegate. Beyond this altitude the
manifold pressure must drop, since the supercharger is now at maximum
unregulated output. The actual pressure multiplier of the supercharger
system is calculated at initialisation from this value.
- RATEDPOWER[123] - The power developed at rated boost at rated altitude at
rated rpm.
- RATEDRPM[123] - The rpm at which rated power is developed.
- TAKEOFFBOOST - Takeoff boost in psi above ambient. Many aircraft had an
extra boost setting beyond rated boost, but not totally uncontrolled as in
the already mentioned boost-control-cutout, typically attained by pushing
the throttle past a mechanical 'gate' preventing its inadvertant use. This
was typically used for takeoff, and emergency situations, generally for
not more than five minutes. This is a change in the boost control
setting, not the actual supercharger speed, and so would only give extra
power below the rated altitude. When TAKEOFFBOOST is specified in the
config file (and is above RATEDBOOST1), then the throttle position is
interpreted as:
- 0 to 0.95 : idle manifold pressure to rated boost (where attainable)
- 0.96, 0.97, 0.98 : rated boost (where attainable).
- 0.99, 1.0 : takeoff boost (where attainable).
A typical takeoff boost for an earlyish Merlin was about 12psi, compared
with a rated boost of 9psi.
It is quite possible that other boost control settings could have been used
on some aircraft, or that takeoff/extra boost could have activated by other
means than pushing the throttle full forward through a gate, but this will
suffice for now.
Note that MAXMP is still the non-boosted max manifold pressure even for
boosted engines - effectively this is simply a measure of the pressure drop
through the fully open throttle.
@author Jon S. Berndt (Engine framework code and framework-related mods)
@author Dave Luff (engine operational code)
@author David Megginson (porting and additional code)
@version "$Id$"
@author David Megginson (initial porting and additional code)
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -78,7 +146,10 @@ public:
/// Destructor
~FGPiston();
double Calculate(double PowerRequired);
string GetEngineLabels(void);
string GetEngineValues(void);
double Calculate(void);
double GetPowerAvailable(void) {return PowerAvailable;}
double CalcFuelNeed(void);
@ -107,7 +178,8 @@ private:
double dt;
void doEngineStartup(void);
void doManifoldPressure(void);
void doBoostControl(void);
void doMAP(void);
void doAirFlow(void);
void doFuelFlow(void);
void doEnginePower(void);
@ -138,6 +210,31 @@ private:
double MaxHP; // horsepower
double Cycles; // cycles/power stroke
double IdleRPM; // revolutions per minute
int BoostSpeeds; // Number of super/turbocharger boost speeds - zero implies no turbo/supercharging.
int BoostSpeed; // The current boost-speed (zero-based).
bool Boosted; // Set true for boosted engine.
int BoostOverride; // The raw value read in from the config file - should be 1 or 0 - see description below.
bool bBoostOverride; // Set true if pilot override of the boost regulator was fitted.
// (Typically called 'war emergency power').
bool bTakeoffBoost; // Set true if extra takeoff / emergency boost above rated boost could be attained.
// (Typically by extra throttle movement past a mechanical 'gate').
double TakeoffBoost; // Sea-level takeoff boost in psi. (if fitted).
double RatedBoost[FG_MAX_BOOST_SPEEDS]; // Sea-level rated boost in psi.
double RatedAltitude[FG_MAX_BOOST_SPEEDS]; // Altitude at which full boost is reached (boost regulation ends)
// and at which power starts to fall with altitude [ft].
double RatedRPM[FG_MAX_BOOST_SPEEDS]; // Engine speed at which the rated power for each boost speed is delivered [rpm].
double RatedPower[FG_MAX_BOOST_SPEEDS]; // Power at rated throttle position at rated altitude [HP].
double BoostSwitchAltitude[FG_MAX_BOOST_SPEEDS - 1]; // Altitude at which switchover (currently assumed automatic)
// from one boost speed to next occurs [ft].
double BoostSwitchPressure[FG_MAX_BOOST_SPEEDS - 1]; // Pressure at which boost speed switchover occurs [Pa]
double BoostMul[FG_MAX_BOOST_SPEEDS]; // Pressure multipier of unregulated supercharger
double RatedMAP[FG_MAX_BOOST_SPEEDS]; // Rated manifold absolute pressure [Pa] (BCV clamp)
double TakeoffMAP[FG_MAX_BOOST_SPEEDS]; // Takeoff setting manifold absolute pressure [Pa] (BCV clamp)
double BoostSwitchHysteresis; // Pa.
double minMAP; // Pa
double maxMAP; // Pa
double MAP; // Pa
//
// Inputs (in addition to those in FGEngine).

View file

@ -1,367 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGPosition.cpp
Author: Jon S. Berndt
Date started: 01/05/99
Purpose: Integrate the EOM to determine instantaneous position
Called by: FGFDMExec
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class encapsulates the integration of rates and accelerations to get the
current position of the aircraft.
HISTORY
--------------------------------------------------------------------------------
01/05/99 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] 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
[2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
JSC 12960, July 1977
[3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
[4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
Wiley & Sons, 1979 ISBN 0-471-03032-5
[5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
1982 ISBN 0-471-08936-2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# ifdef SG_HAVE_STD_INCLUDES
# include <cmath>
# include <iomanip>
# else
# include <math.h>
# include <iomanip.h>
# endif
#else
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# if (_COMPILER_VERSION < 740)
# include <iomanip.h>
# else
# include <iomanip>
# endif
# else
# include <cmath>
# include <iomanip>
# endif
#endif
#include "FGPosition.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGInertial.h"
#include "FGPropertyManager.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_POSITION;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGPosition::FGPosition(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGPosition";
LongitudeDot = LatitudeDot = RadiusDot = 0.0;
for (int i=0;i<4;i++) {
LatitudeDot_prev[i] = 0.0;
LongitudeDot_prev[i] = 0.0;
RadiusDot_prev[i] = 0.0;
}
vVRPoffset.InitMatrix();
Longitude = Latitude = 0.0;
LongitudeVRP = LatitudeVRP = 0.0;
gamma = Vt = Vground = 0.0;
hoverbmac = hoverbcg = 0.0;
psigt = 0.0;
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGPosition::~FGPosition(void)
{
unbind();
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGPosition::InitModel(void)
{
FGModel::InitModel();
h = 3.0; // Est. height of aircraft cg off runway
SeaLevelRadius = Inertial->RefRadius(); // For initialization ONLY
Radius = SeaLevelRadius + h;
RunwayRadius = SeaLevelRadius;
DistanceAGL = Radius - RunwayRadius; // Geocentric
vRunwayNormal(3) = -1.0; // Initialized for standalone mode
b = 1;
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/*
Purpose: Called on a schedule to perform Positioning algorithms
Notes: [TP] Make sure that -Vt <= hdot <= Vt, which, of course, should always
be the case
[JB] Run in standalone mode, SeaLevelRadius will be reference radius.
In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
*/
bool FGPosition::Run(void)
{
double cosLat;
double hdot_Vt;
if (!FGModel::Run()) {
GetState();
Vground = sqrt( vVel(eNorth)*vVel(eNorth) + vVel(eEast)*vVel(eEast) );
if (vVel(eNorth) == 0) psigt = 0;
else psigt = atan2(vVel(eEast), vVel(eNorth));
if (psigt < 0.0) psigt += 2*M_PI;
Radius = h + SeaLevelRadius;
cosLat = cos(Latitude);
if (cosLat != 0) LongitudeDot = vVel(eEast) / (Radius * cosLat);
LatitudeDot = vVel(eNorth) / Radius;
RadiusDot = -vVel(eDown);
Longitude += State->Integrate(FGState::TRAPZ, dt*rate, LongitudeDot, LongitudeDot_prev);
Latitude += State->Integrate(FGState::TRAPZ, dt*rate, LatitudeDot, LatitudeDot_prev);
Radius += State->Integrate(FGState::TRAPZ, dt*rate, RadiusDot, RadiusDot_prev);
h = Radius - SeaLevelRadius; // Geocentric
vVRPoffset = State->GetTb2l() * MassBalance->StructuralToBody(Aircraft->GetXYZvrp());
// vVRP - the vector to the Visual Reference Point - now contains the
// offset from the CG to the VRP, in units of feet, in the Local coordinate
// frame, where X points north, Y points East, and Z points down. This needs
// to be converted to Lat/Lon/Alt, now.
if (cosLat != 0)
LongitudeVRP = vVRPoffset(eEast) / (Radius * cosLat) + Longitude;
LatitudeVRP = vVRPoffset(eNorth) / Radius + Latitude;
hVRP = h - vVRPoffset(eDown);
/*
cout << "Lat/Lon/Alt : " << Latitude << " / " << Longitude << " / " << h << endl;
cout << "Lat/Lon/Alt VRP: " << LatitudeVRP << " / " << LongitudeVRP << " / " << hVRP << endl << endl;
*/
DistanceAGL = Radius - RunwayRadius; // Geocentric
hoverbcg = DistanceAGL/b;
vMac = State->GetTb2l()*MassBalance->StructuralToBody(Aircraft->GetXYZrp());
hoverbmac = (DistanceAGL + vMac(3)) / b;
if (Vt > 0) {
hdot_Vt = RadiusDot/Vt;
if (fabs(hdot_Vt) <= 1) gamma = asin(hdot_Vt);
} else {
gamma = 0.0;
}
return false;
} else {
return true;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPosition::GetState(void)
{
dt = State->Getdt();
Vt = Translation->GetVt();
vVel = State->GetTb2l() * Translation->GetUVW();
vVelDot = State->GetTb2l() * Translation->GetUVWdot();
b = Aircraft->GetWingSpan();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPosition::Seth(double tt)
{
h = tt;
Radius = h + SeaLevelRadius;
DistanceAGL = Radius - RunwayRadius; // Geocentric
hoverbcg = DistanceAGL/b;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPosition::SetDistanceAGL(double tt)
{
DistanceAGL=tt;
Radius = RunwayRadius + DistanceAGL;
h = Radius - SeaLevelRadius;
hoverbcg = DistanceAGL/b;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPosition::bind(void)
{
PropertyManager->Tie("velocities/v-north-fps", this,
&FGPosition::GetVn);
PropertyManager->Tie("velocities/v-east-fps", this,
&FGPosition::GetVe);
PropertyManager->Tie("velocities/v-down-fps", this,
&FGPosition::GetVd);
PropertyManager->Tie("velocities/vg-fps", this,
&FGPosition::GetVground);
PropertyManager->Tie("flight-path/psi-gt-rad", this,
&FGPosition::GetGroundTrack);
PropertyManager->Tie("position/h-sl-ft", this,
&FGPosition::Geth,
&FGPosition::Seth,
true);
PropertyManager->Tie("velocities/h-dot-fps", this,
&FGPosition::Gethdot);
PropertyManager->Tie("position/lat-gc-rad", this,
&FGPosition::GetLatitude,
&FGPosition::SetLatitude);
PropertyManager->Tie("position/lat-dot-gc-rad", this,
&FGPosition::GetLatitudeDot);
PropertyManager->Tie("position/long-gc-rad", this,
&FGPosition::GetLongitude,
&FGPosition::SetLongitude,
true);
PropertyManager->Tie("position/long-dot-gc-rad", this,
&FGPosition::GetLongitudeDot);
PropertyManager->Tie("metrics/runway-radius", this,
&FGPosition::GetRunwayRadius,
&FGPosition::SetRunwayRadius);
PropertyManager->Tie("position/h-agl-ft", this,
&FGPosition::GetDistanceAGL,
&FGPosition::SetDistanceAGL);
PropertyManager->Tie("position/radius-to-vehicle-ft", this,
&FGPosition::GetRadius);
PropertyManager->Tie("flight-path/gamma-rad", this,
&FGPosition::GetGamma,
&FGPosition::SetGamma);
PropertyManager->Tie("aero/h_b-cg-ft", this,
&FGPosition::GetHOverBCG);
PropertyManager->Tie("aero/h_b-mac-ft", this,
&FGPosition::GetHOverBMAC);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPosition::unbind(void)
{
PropertyManager->Untie("velocities/v-north-fps");
PropertyManager->Untie("velocities/v-east-fps");
PropertyManager->Untie("velocities/v-down-fps");
PropertyManager->Untie("velocities/vg-fps");
PropertyManager->Untie("flight-path/psi-gt-rad");
PropertyManager->Untie("position/h-sl-ft");
PropertyManager->Untie("velocities/h-dot-fps");
PropertyManager->Untie("position/lat-gc-rad");
PropertyManager->Untie("position/lat-dot-gc-rad");
PropertyManager->Untie("position/long-gc-rad");
PropertyManager->Untie("position/long-dot-gc-rad");
PropertyManager->Untie("metrics/runway-radius");
PropertyManager->Untie("position/h-agl-ft");
PropertyManager->Untie("position/radius-to-vehicle-ft");
PropertyManager->Untie("flight-path/gamma-rad");
PropertyManager->Untie("aero/h_b-cg-ft");
PropertyManager->Untie("aero/h_b-mac-ft");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 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 FGPosition::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: FGPosition" << endl;
if (from == 1) cout << "Destroyed: FGPosition" << 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
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -1,154 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGPosition.h
Author: Jon S. Berndt
Date started: 1/5/99
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
01/05/99 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGPOSITION_H
#define FGPOSITION_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGModel.h"
#include "FGColumnVector3.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_POSITION "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models the lateral and longitudinal translational EOM.
@author Jon S. Berndt
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGPosition : public FGModel {
public:
/** Constructor
@param Executive a pointer to the parent executive object */
FGPosition(FGFDMExec* Executive);
/// Destructor
~FGPosition();
bool InitModel(void);
/** Runs the Position model; called by the Executive
@see JSBSim.cpp documentation
@return false if no error */
bool Run(void);
inline FGColumnVector3& GetVel(void) { return vVel; }
inline FGColumnVector3& GetVelDot(void) { return vVelDot; }
inline double GetVn(void) const { return vVel(eX); }
inline double GetVe(void) const { return vVel(eY); }
inline double GetVd(void) const { return vVel(eZ); }
inline double GetVground(void) const { return Vground; }
inline double GetGroundTrack(void) const { return psigt; }
inline double Geth(void) const { return h; }
inline double GethVRP(void) const { return hVRP; }
inline double Gethdot(void) const { return RadiusDot; }
inline double GetLatitude(void) const { return Latitude; }
inline double GetLatitudeVRP(void) const { return LatitudeVRP; }
inline double GetLatitudeDot(void) const { return LatitudeDot; }
inline double GetLongitude(void) const { return Longitude; }
inline double GetLongitudeVRP(void) const { return LongitudeVRP; }
inline double GetLongitudeDot(void) const { return LongitudeDot; }
inline double GetRunwayRadius(void) const { return RunwayRadius; }
inline double GetDistanceAGL(void) const { return DistanceAGL; }
inline double GetRadius(void) const { return Radius; }
inline FGColumnVector3& GetRunwayNormal(void) { return vRunwayNormal; }
inline double GetGamma(void) const { return gamma; }
inline void SetGamma(double tt) { gamma = tt; }
inline double GetHOverBCG(void) const { return hoverbcg; }
inline double GetHOverBMAC(void) const { return hoverbmac; }
void SetvVel(const FGColumnVector3& v) { vVel = v; }
void SetLatitude(double tt) { Latitude = tt; }
void SetLongitude(double tt) { Longitude = tt; }
void Seth(double tt);
void SetRunwayRadius(double tt) { RunwayRadius = tt; }
void SetSeaLevelRadius(double tt) { SeaLevelRadius = tt;}
void SetDistanceAGL(double tt);
inline void SetRunwayNormal(double fgx, double fgy, double fgz ) {
vRunwayNormal << fgx << fgy << fgz;
}
void SetVRP(FGColumnVector3& vrp) {vVRP = vrp;}
void bind(void);
void unbind(void);
private:
FGColumnVector3 vVel;
FGColumnVector3 vVelDot;
FGColumnVector3 vRunwayNormal;
FGColumnVector3 vVRP;
FGColumnVector3 vVRPoffset;
FGColumnVector3 vMac;
double Radius, h, hVRP;
double LatitudeDot, LongitudeDot, RadiusDot;
double LatitudeDot_prev[4], LongitudeDot_prev[4], RadiusDot_prev[4];
double Longitude, Latitude;
double LongitudeVRP, LatitudeVRP;
double dt;
double RunwayRadius;
double DistanceAGL;
double SeaLevelRadius;
double gamma;
double Vt, Vground;
double hoverbcg,hoverbmac,b;
double psigt;
void GetState(void);
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -0,0 +1,380 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGPropagate.cpp
Author: Jon S. Berndt
Date started: 01/05/99
Purpose: Integrate the EOM to determine instantaneous position
Called by: FGFDMExec
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class encapsulates the integration of rates and accelerations to get the
current position of the aircraft.
HISTORY
--------------------------------------------------------------------------------
01/05/99 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] 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
[2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
JSC 12960, July 1977
[3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
[4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
Wiley & Sons, 1979 ISBN 0-471-03032-5
[5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
1982 ISBN 0-471-08936-2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# ifdef SG_HAVE_STD_INCLUDES
# include <cmath>
# include <iomanip>
# else
# include <math.h>
# include <iomanip.h>
# endif
#else
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# if (_COMPILER_VERSION < 740)
# include <iomanip.h>
# else
# include <iomanip>
# endif
# else
# include <cmath>
# include <iomanip>
# endif
#endif
#include "FGPropagate.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGInertial.h"
#include "FGPropertyManager.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_PROPAGATE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGPropagate";
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGPropagate::~FGPropagate(void)
{
unbind();
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGPropagate::InitModel(void)
{
FGModel::InitModel();
SeaLevelRadius = Inertial->RefRadius(); // For initialization ONLY
RunwayRadius = SeaLevelRadius;
VState.vLocation.SetRadius( SeaLevelRadius + 4.0 );
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
{
SeaLevelRadius = FGIC->GetSeaLevelRadiusFtIC();
RunwayRadius = FGIC->GetSeaLevelRadiusFtIC() + FGIC->GetTerrainAltitudeFtIC();
// Set the position lat/lon/radius
VState.vLocation = FGLocation( FGIC->GetLongitudeRadIC(),
FGIC->GetLatitudeRadIC(),
FGIC->GetAltitudeFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
// Set the Orientation from the euler angles
VState.vQtrn = FGQuaternion( FGIC->GetPhiRadIC(),
FGIC->GetThetaRadIC(),
FGIC->GetPsiRadIC() );
// Set the velocities in the instantaneus body frame
VState.vUVW = FGColumnVector3( FGIC->GetUBodyFpsIC(),
FGIC->GetVBodyFpsIC(),
FGIC->GetWBodyFpsIC() );
// Set the angular velocities in the instantaneus body frame.
VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(),
FGIC->GetQRadpsIC(),
FGIC->GetRRadpsIC() );
// Compute some derived values.
vVel = VState.vQtrn.GetTInv()*VState.vUVW;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/*
Purpose: Called on a schedule to perform EOM integration
Notes: [JB] Run in standalone mode, SeaLevelRadius will be reference radius.
In FGFS, SeaLevelRadius is stuffed from FGJSBSim in JSBSim.cxx each pass.
At the top of this Run() function, see several "shortcuts" (or, aliases) being
set up for use later, rather than using the longer class->function() notation.
Here, propagation of state is done using a simple explicit Euler scheme (see the
bottom of the function). This propagation is done using the current state values
and current derivatives. Based on these values we compute an approximation to the
state values for (now + dt).
*/
bool FGPropagate::Run(void)
{
if (FGModel::Run()) return true; // Fast return if we have nothing to do ...
double dt = State->Getdt()*rate; // The 'stepsize'
const FGColumnVector3 omega( 0.0, 0.0, Inertial->omega() ); // earth rotation
const FGColumnVector3& vForces = Aircraft->GetForces(); // current forces
const FGColumnVector3& vMoments = Aircraft->GetMoments(); // current moments
double mass = MassBalance->GetMass(); // mass
const FGMatrix33& J = MassBalance->GetJ(); // inertia matrix
const FGMatrix33& Jinv = MassBalance->GetJinv(); // inertia matrix inverse
double r = GetRadius(); // radius
if (r == 0.0) {cerr << "radius = 0 !" << endl; r = 1e-16;} // radius check
double rInv = 1.0/r;
FGColumnVector3 gAccel( 0.0, 0.0, Inertial->GetGAccel(r) );
// The rotation matrices:
const FGMatrix33& Tl2b = GetTl2b(); // local to body frame
const FGMatrix33& Tb2l = GetTb2l(); // body to local frame
const FGMatrix33& Tec2l = VState.vLocation.GetTec2l(); // earth centered to local frame
const FGMatrix33& Tl2ec = VState.vLocation.GetTl2ec(); // local to earth centered frame
// Inertial angular velocity measured in the body frame.
const FGColumnVector3 pqri = VState.vPQR + Tl2b*(Tec2l*omega);
// Compute vehicle velocity wrt EC frame, expressed in Local horizontal frame.
vVel = Tb2l * VState.vUVW;
// First compute the time derivatives of the vehicle state values:
// Compute body frame rotational accelerations based on the current body moments
vPQRdot = Jinv*(vMoments - pqri*(J*pqri));
// Compute body frame accelerations based on the current body forces
vUVWdot = VState.vUVW*VState.vPQR + vForces/mass;
// Centrifugal acceleration.
FGColumnVector3 ecVel = Tl2ec*vVel;
FGColumnVector3 ace = 2.0*omega*ecVel;
vUVWdot -= Tl2b*(Tec2l*ace);
// Coriolis acceleration.
FGColumnVector3 aeec = omega*(omega*VState.vLocation);
vUVWdot -= Tl2b*(Tec2l*aeec);
// Gravitation accel
vUVWdot += Tl2b*gAccel;
// Compute vehicle velocity wrt EC frame, expressed in EC frame
FGColumnVector3 vLocationDot = Tl2ec * vVel;
FGColumnVector3 omegaLocal( rInv*vVel(eEast),
-rInv*vVel(eNorth),
-rInv*vVel(eEast)*VState.vLocation.GetTanLatitude() );
// Compute quaternion orientation derivative on current body rates
FGQuaternion vQtrndot = VState.vQtrn.GetQDot( VState.vPQR - Tl2b*omegaLocal );
// Propagate velocities
VState.vPQR += dt*vPQRdot;
VState.vUVW += dt*vUVWdot;
// Propagate positions
VState.vQtrn += dt*vQtrndot;
VState.vLocation += dt*vLocationDot;
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::Seth(double tt)
{
VState.vLocation.SetRadius( tt + SeaLevelRadius );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetDistanceAGL(double tt)
{
VState.vLocation.SetRadius( tt + RunwayRadius );
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::bind(void)
{
typedef double (FGPropagate::*PMF)(int) const;
PropertyManager->Tie("velocities/h-dot-fps", this, &FGPropagate::Gethdot);
PropertyManager->Tie("velocities/v-north-fps", this, eNorth, (PMF)&FGPropagate::GetVel);
PropertyManager->Tie("velocities/v-east-fps", this, eEast, (PMF)&FGPropagate::GetVel);
PropertyManager->Tie("velocities/v-down-fps", this, eDown, (PMF)&FGPropagate::GetVel);
PropertyManager->Tie("velocities/u-fps", this, eU, (PMF)&FGPropagate::GetUVW);
PropertyManager->Tie("velocities/v-fps", this, eV, (PMF)&FGPropagate::GetUVW);
PropertyManager->Tie("velocities/w-fps", this, eW, (PMF)&FGPropagate::GetUVW);
PropertyManager->Tie("velocities/p-rad_sec", this, eP, (PMF)&FGPropagate::GetPQR);
PropertyManager->Tie("velocities/q-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQR);
PropertyManager->Tie("velocities/r-rad_sec", this, eR, (PMF)&FGPropagate::GetPQR);
PropertyManager->Tie("accelerations/pdot-rad_sec", this, eP, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/qdot-rad_sec", this, eQ, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/rdot-rad_sec", this, eR, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/udot-fps", this, eU, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("accelerations/vdot-fps", this, eV, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("accelerations/wdot-fps", this, eW, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::Geth, &FGPropagate::Seth, true);
PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
PropertyManager->Tie("position/long-gc-rad", this, &FGPropagate::GetLongitude, &FGPropagate::SetLongitude);
PropertyManager->Tie("position/h-agl-ft", this, &FGPropagate::GetDistanceAGL, &FGPropagate::SetDistanceAGL);
PropertyManager->Tie("position/radius-to-vehicle-ft", this, &FGPropagate::GetRadius);
PropertyManager->Tie("metrics/runway-radius", this, &FGPropagate::GetRunwayRadius, &FGPropagate::SetRunwayRadius);
PropertyManager->Tie("attitude/phi-rad", this, &FGPropagate::Getphi);
PropertyManager->Tie("attitude/theta-rad", this, &FGPropagate::Gettht);
PropertyManager->Tie("attitude/psi-rad", this, &FGPropagate::Getpsi);
PropertyManager->Tie("attitude/roll-rad", this, &FGPropagate::Getphi);
PropertyManager->Tie("attitude/pitch-rad", this, &FGPropagate::Gettht);
PropertyManager->Tie("attitude/heading-true-rad", this, &FGPropagate::Getpsi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::unbind(void)
{
PropertyManager->Untie("velocities/v-north-fps");
PropertyManager->Untie("velocities/v-east-fps");
PropertyManager->Untie("velocities/v-down-fps");
PropertyManager->Untie("velocities/h-dot-fps");
PropertyManager->Untie("velocities/u-fps");
PropertyManager->Untie("velocities/v-fps");
PropertyManager->Untie("velocities/w-fps");
PropertyManager->Untie("velocities/p-rad_sec");
PropertyManager->Untie("velocities/q-rad_sec");
PropertyManager->Untie("velocities/r-rad_sec");
PropertyManager->Untie("accelerations/udot-fps");
PropertyManager->Untie("accelerations/vdot-fps");
PropertyManager->Untie("accelerations/wdot-fps");
PropertyManager->Untie("accelerations/pdot-rad_sec");
PropertyManager->Untie("accelerations/qdot-rad_sec");
PropertyManager->Untie("accelerations/rdot-rad_sec");
PropertyManager->Untie("position/h-sl-ft");
PropertyManager->Untie("position/lat-gc-rad");
PropertyManager->Untie("position/long-gc-rad");
PropertyManager->Untie("position/h-agl-ft");
PropertyManager->Untie("position/radius-to-vehicle-ft");
PropertyManager->Untie("metrics/runway-radius");
PropertyManager->Untie("attitude/phi-rad");
PropertyManager->Untie("attitude/theta-rad");
PropertyManager->Untie("attitude/psi-rad");
PropertyManager->Untie("attitude/roll-rad");
PropertyManager->Untie("attitude/pitch-rad");
PropertyManager->Untie("attitude/heading-true-rad");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 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 FGPropagate::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: FGPropagate" << endl;
if (from == 1) cout << "Destroyed: FGPropagate" << 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
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -0,0 +1,176 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGPropagate.h
Author: Jon S. Berndt
Date started: 1/5/99
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
01/05/99 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGPROPAGATE_H
#define FGPROPAGATE_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGModel.h"
#include "FGColumnVector3.h"
#include "FGInitialCondition.h"
#include "FGLocation.h"
#include "FGQuaternion.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_PROPAGATE "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models the EOM and integration/propagation of state
@author Jon S. Berndt, Mathias Froehlich
@version $Id$
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
// state vector
struct VehicleState {
FGLocation vLocation;
FGColumnVector3 vUVW;
FGColumnVector3 vPQR;
FGQuaternion vQtrn;
};
class FGPropagate : public FGModel {
public:
/** Constructor
@param Executive a pointer to the parent executive object */
FGPropagate(FGFDMExec* Executive);
/// Destructor
~FGPropagate();
bool InitModel(void);
/** Runs the Propagate model; called by the Executive
@return false if no error */
bool Run(void);
const FGColumnVector3& GetVel(void) const { return vVel; }
const FGColumnVector3& GetUVW(void) const { return VState.vUVW; }
const FGColumnVector3& GetUVWdot(void) const { return vUVWdot; }
const FGColumnVector3& GetPQR(void) const {return VState.vPQR;}
const FGColumnVector3& GetPQRdot(void) const {return vPQRdot;}
const FGColumnVector3& GetEuler(void) const { return VState.vQtrn.GetEuler(); }
double GetUVW (int idx) const { return VState.vUVW(idx); }
double GetUVWdot(int idx) const { return vUVWdot(idx); }
double GetVel(int idx) const { return vVel(idx); }
double Geth(void) const { return VState.vLocation.GetRadius() - SeaLevelRadius; }
double GetPQR(int axis) const {return VState.vPQR(axis);}
double GetPQRdot(int idx) const {return vPQRdot(idx);}
double GetEuler(int axis) const { return VState.vQtrn.GetEuler()(axis); }
double Gethdot(void) const { return -vVel(eDown); }
/** Returns the "constant" RunwayRadius.
The RunwayRadius parameter is set by the calling application or set to
zero if JSBSim is running in standalone mode.
@return distance of the runway from the center of the earth.
@units feet */
double GetRunwayRadius(void) const { return RunwayRadius; }
double GetSeaLevelRadius(void) const { return SeaLevelRadius; }
double GetDistanceAGL(void) const { return VState.vLocation.GetRadius()-RunwayRadius; }
double GetRadius(void) const { return VState.vLocation.GetRadius(); }
double GetLongitude(void) const { return VState.vLocation.GetLongitude(); }
double GetLatitude(void) const { return VState.vLocation.GetLatitude(); }
const FGLocation& GetLocation(void) const { return VState.vLocation; }
double Getphi(void) const { return VState.vQtrn.GetEulerPhi(); }
double Gettht(void) const { return VState.vQtrn.GetEulerTheta(); }
double Getpsi(void) const { return VState.vQtrn.GetEulerPsi(); }
double GetCosphi(void) const { return VState.vQtrn.GetCosEulerPhi(); }
double GetCostht(void) const { return VState.vQtrn.GetCosEulerTheta(); }
double GetCospsi(void) const { return VState.vQtrn.GetCosEulerPsi(); }
double GetSinphi(void) const { return VState.vQtrn.GetSinEulerPhi(); }
double GetSintht(void) const { return VState.vQtrn.GetSinEulerTheta(); }
double GetSinpsi(void) const { return VState.vQtrn.GetSinEulerPsi(); }
/** Retrieves the local-to-body transformation matrix.
@return a reference to the local-to-body transformation matrix. */
const FGMatrix33& GetTl2b(void) const { return VState.vQtrn.GetT(); }
/** Retrieves the body-to-local transformation matrix.
@return a reference to the body-to-local matrix. */
const FGMatrix33& GetTb2l(void) const { return VState.vQtrn.GetTInv(); }
// SET functions
void SetLongitude(double lon) { VState.vLocation.SetLongitude(lon); }
void SetLatitude(double lat) { VState.vLocation.SetLatitude(lat); }
void SetRadius(double r) { VState.vLocation.SetRadius(r); }
void SetLocation(const FGLocation& l) { VState.vLocation = l; }
void Seth(double tt);
void SetRunwayRadius(double tt) { RunwayRadius = tt; }
void SetSeaLevelRadius(double tt) { SeaLevelRadius = tt; }
void SetDistanceAGL(double tt);
void SetInitialState(const FGInitialCondition *);
void bind(void);
void unbind(void);
private:
// state vector
struct VehicleState VState;
FGColumnVector3 vVel;
FGColumnVector3 vPQRdot;
FGColumnVector3 vUVWdot;
double RunwayRadius, SeaLevelRadius;
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -35,11 +35,12 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include "FGPropeller.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGFCS.h"
#include "FGPropagate.h"
#include "FGAtmosphere.h"
#include "FGAuxiliary.h"
namespace JSBSim {
@ -60,7 +61,7 @@ FGPropeller::FGPropeller(FGFDMExec* exec, FGConfigFile* Prop_cfg) : FGThruster(e
string token;
int rows, cols;
MaxPitch = MinPitch = P_Factor = Sense = Pitch = 0.0;
MaxPitch = MinPitch = P_Factor = Sense = Pitch = Advance = 0.0;
GearRatio = 1.0;
Name = Prop_cfg->GetValue("NAME");
@ -135,7 +136,7 @@ FGPropeller::~FGPropeller()
double FGPropeller::Calculate(double PowerAvailable)
{
double J, C_Thrust, omega;
double Vel = fdmex->GetTranslation()->GetAeroUVW(eU);
double Vel = fdmex->GetAuxiliary()->GetAeroUVW(eU);
double rho = fdmex->GetAtmosphere()->GetDensity();
double RPS = RPM/60.0;
double alpha, beta;
@ -153,8 +154,8 @@ double FGPropeller::Calculate(double PowerAvailable)
}
if (P_Factor > 0.0001) {
alpha = fdmex->GetTranslation()->Getalpha();
beta = fdmex->GetTranslation()->Getbeta();
alpha = fdmex->GetAuxiliary()->Getalpha();
beta = fdmex->GetAuxiliary()->Getbeta();
SetActingLocationY( GetLocationY() + P_Factor*alpha*Sense);
SetActingLocationZ( GetLocationZ() + P_Factor*beta*Sense);
} else if (P_Factor < 0.000) {
@ -164,12 +165,6 @@ double FGPropeller::Calculate(double PowerAvailable)
Thrust = C_Thrust*RPS*RPS*Diameter*Diameter*Diameter*Diameter*rho;
omega = RPS*2.0*M_PI;
// Check for windmilling.
double radius = Diameter * 0.375; // 75% of radius
double windmill_cutoff = tan(Pitch * 1.745329E-2) * omega * radius;
if (Vel > windmill_cutoff)
Thrust = -Thrust;
vFn(1) = Thrust;
// The Ixx value and rotation speed given below are for rotation about the
@ -185,13 +180,13 @@ double FGPropeller::Calculate(double PowerAvailable)
ExcessTorque = PowerAvailable / omega * GearRatio;
RPM = (RPS + ((ExcessTorque / Ixx) / (2.0 * M_PI)) * deltaT) * 60.0;
// The friction from the engine should
// stop it somewhere; I chose an
// arbitrary point.
// The friction from the engine should
// stop it somewhere; I chose an
// arbitrary point.
if (RPM < 5.0)
RPM = 0;
vMn = fdmex->GetRotation()->GetPQR()*vH + vTorque*Sense;
vMn = fdmex->GetPropagate()->GetPQR()*vH + vTorque*Sense;
return Thrust; // return thrust in pounds
}
@ -204,17 +199,16 @@ double FGPropeller::GetPowerRequired(void)
double cPReq, RPS = RPM / 60.0;
double J = fdmex->GetTranslation()->GetAeroUVW(eU) / (Diameter * RPS);
double J = fdmex->GetAuxiliary()->GetAeroUVW(eU) / (Diameter * RPS);
double rho = fdmex->GetAtmosphere()->GetDensity();
if (MaxPitch == MinPitch) { // Fixed pitch prop
Pitch = MinPitch;
cPReq = cPower->GetValue(J);
} else { // Variable pitch prop
double advance = fdmex->GetFCS()->GetPropAdvance(ThrusterNumber);
if (MaxRPM != MinRPM) { // fixed-speed prop
double rpmReq = MinRPM + (MaxRPM - MinRPM) * advance;
double rpmReq = MinRPM + (MaxRPM - MinRPM) * Advance;
double dRPM = rpmReq - RPM;
Pitch -= dRPM / 10;
@ -223,7 +217,7 @@ double FGPropeller::GetPowerRequired(void)
else if (Pitch > MaxPitch) Pitch = MaxPitch;
} else {
Pitch = MinPitch + (MaxPitch - MinPitch) * advance;
Pitch = MinPitch + (MaxPitch - MinPitch) * Advance;
}
cPReq = cPower->GetValue(J, Pitch);
}
@ -247,6 +241,41 @@ FGColumnVector3 FGPropeller::GetPFactor()
return FGColumnVector3(px, py, pz);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPropeller::GetThrusterLabels(int id)
{
std::ostringstream buf;
buf << Name << "_Torque[" << id << "], "
<< Name << "_PFactor_Pitch[" << id << "], "
<< Name << "_PFactor_Yaw[" << id << "], "
<< Name << "_Thrust[" << id << "], ";
if (IsVPitch())
buf << Name << "_Pitch[" << id << "], ";
buf << Name << "_RPM[" << id << "]";
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPropeller::GetThrusterValues(int id)
{
std::ostringstream buf;
FGColumnVector3 vPFactor = GetPFactor();
buf << vTorque(eX) << ", "
<< vPFactor(ePitch) << ", "
<< vPFactor(eYaw) << ", "
<< Thrust << ", ";
if (IsVPitch())
buf << Pitch << ", ";
buf << RPM;
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -114,6 +114,8 @@ public:
@param pitch the pitch of the blade in degrees. */
void SetPitch(double pitch) {Pitch = pitch;}
void SetAdvance(double advance) {Advance = advance;}
/// Sets the P-Factor constant
void SetPFactor(double pf) {P_Factor = pf;}
@ -151,6 +153,8 @@ public:
@return the thrust in pounds */
double Calculate(double PowerAvailable);
FGColumnVector3 GetPFactor(void);
string GetThrusterLabels(int id);
string GetThrusterValues(int id);
private:
int numBlades;
@ -164,6 +168,7 @@ private:
double P_Factor;
double Sense;
double Pitch;
double Advance;
double ExcessTorque;
FGColumnVector3 vTorque;
FGTable *cThrust;

View file

@ -3,7 +3,7 @@
Module: FGPropulsion.cpp
Author: Jon S. Berndt
Date started: 08/20/00
Purpose: Encapsulates the set of engines, tanks, and thrusters associated
Purpose: Encapsulates the set of engines and tanks associated
with this aircraft
------------- Copyright (C) 2000 Jon S. Berndt (jsb@hal-pc.org) -------------
@ -28,21 +28,13 @@
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
The Propulsion class is the container for the entire propulsion system, which is
comprised of engines, tanks, and "thrusters" (the device that transforms the
engine power into a force that acts on the aircraft, such as a nozzle or
propeller). Once the Propulsion class gets the config file, it reads in
information which is specific to a type of engine. Then:
comprised of engines and tanks. Once the Propulsion class gets the config file,
it reads in information which is specific to a type of engine. Then:
1) The appropriate engine type instance is created
2) A thruster object is instantiated, and is linked to the engine
3) At least one tank object is created, and is linked to an engine.
2) At least one tank object is created, and is linked to an engine.
At Run time each engines Calculate() method is called to return the excess power
generated during that iteration. The drag from the previous iteration is sub-
tracted to give the excess power available for thrust this pass. That quantity
is passed to the thrusters associated with a particular engine - perhaps with a
scaling mechanism (gearing?) to allow the engine to give its associated thrust-
ers specific distributed portions of the excess power.
At Run time each engines Calculate() method is called.
HISTORY
--------------------------------------------------------------------------------
@ -54,22 +46,11 @@ INCLUDES
#include "FGPropulsion.h"
#include "FGRocket.h"
#include "FGSimTurbine.h"
#include "FGTurbine.h"
#include "FGPropeller.h"
#include "FGNozzle.h"
#include "FGPiston.h"
#include "FGElectric.h"
#include "FGPropertyManager.h"
#if defined (__APPLE__)
/* Not all systems have the gcvt function */
inline char* gcvt (double value, int ndigits, char *buf) {
/* note that this is not exactly what gcvt is supposed to do! */
snprintf (buf, ndigits+1, "%f", value);
return buf;
}
#endif
namespace JSBSim {
static const char *IdSrc = "$Id$";
@ -87,11 +68,11 @@ FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
Name = "FGPropulsion";
numSelectedFuelTanks = numSelectedOxiTanks = 0;
numTanks = numEngines = numThrusters = 0;
numTanks = numEngines = 0;
numOxiTanks = numFuelTanks = 0;
dt = 0.0;
ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
tankJ.InitMatrix();
refuel = false;
bind();
@ -112,34 +93,33 @@ FGPropulsion::~FGPropulsion()
bool FGPropulsion::Run(void)
{
double PowerAvailable;
dt = State->Getdt();
if (FGModel::Run()) return true;
double dt = State->Getdt();
vForces.InitMatrix();
vMoments.InitMatrix();
if (!FGModel::Run()) {
for (unsigned int i=0; i<numEngines; i++) {
Thrusters[i]->SetdeltaT(dt*rate);
PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
Thrusters[i]->Calculate(PowerAvailable);
vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
}
return false;
} else {
return true;
for (unsigned int i=0; i<numEngines; i++) {
Engines[i]->Calculate();
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
}
for (unsigned int i=0; i<numTanks; i++) {
Tanks[i]->Calculate( dt * rate );
}
if (refuel) DoRefuel( dt * rate );
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGPropulsion::GetSteadyState(void)
{
double PowerAvailable;
double currentThrust = 0, lastThrust=-1;
dt = State->Getdt();
int steady_count,j=0;
bool steady=false;
@ -149,13 +129,12 @@ bool FGPropulsion::GetSteadyState(void)
if (!FGModel::Run()) {
for (unsigned int i=0; i<numEngines; i++) {
Engines[i]->SetTrimMode(true);
Thrusters[i]->SetdeltaT(dt*rate);
steady=false;
steady_count=0;
while (!steady && j < 6000) {
PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
Engines[i]->Calculate();
lastThrust = currentThrust;
currentThrust = Thrusters[i]->Calculate(PowerAvailable);
currentThrust = Engines[i]->GetThrust();
if (fabs(lastThrust-currentThrust) < 0.0001) {
steady_count++;
if (steady_count > 120) { steady=true; }
@ -164,8 +143,8 @@ bool FGPropulsion::GetSteadyState(void)
}
j++;
}
vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
Engines[i]->SetTrimMode(false);
}
@ -179,24 +158,20 @@ bool FGPropulsion::GetSteadyState(void)
bool FGPropulsion::ICEngineStart(void)
{
double PowerAvailable;
int j;
dt = State->Getdt();
vForces.InitMatrix();
vMoments.InitMatrix();
for (unsigned int i=0; i<numEngines; i++) {
Engines[i]->SetTrimMode(true);
Thrusters[i]->SetdeltaT(dt*rate);
j=0;
while (!Engines[i]->GetRunning() && j < 2000) {
PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
Thrusters[i]->Calculate(PowerAvailable);
Engines[i]->Calculate();
j++;
}
vForces += Thrusters[i]->GetBodyForces(); // sum body frame forces
vMoments += Thrusters[i]->GetMoments(); // sum body frame moments
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
Engines[i]->SetTrimMode(false);
}
return true;
@ -206,20 +181,22 @@ bool FGPropulsion::ICEngineStart(void)
bool FGPropulsion::Load(FGConfigFile* AC_cfg)
{
string token, fullpath;
string token, fullpath, localpath;
string engineFileName, engType;
string thrusterFileName, thrType;
string parameter;
string enginePath = FDMExec->GetEnginePath();
string aircraftPath = FDMExec->GetAircraftPath();
double xLoc, yLoc, zLoc, Pitch, Yaw;
double P_Factor = 0, Sense = 0.0;
int Feed;
bool ThrottleAdded = false;
FGConfigFile* Cfg_ptr = 0;
# ifndef macintosh
fullpath = enginePath + "/";
localpath = aircraftPath + "/Engines/";
# else
fullpath = enginePath + ";";
localpath = aircraftPath + ";Engines;";
# endif
AC_cfg->GetNextConfigLine();
@ -230,25 +207,44 @@ bool FGPropulsion::Load(FGConfigFile* AC_cfg)
engineFileName = AC_cfg->GetValue("FILE");
if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
+ engineFileName + ".xml"<< endl;
// Look in the Aircraft/Engines directory first
Cfg_ptr = 0;
FGConfigFile Local_cfg(localpath + engineFileName + ".xml");
FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
if (Local_cfg.IsOpen()) {
Cfg_ptr = &Local_cfg;
if (debug_lvl > 0) cout << "\n Reading engine from file: " << localpath
+ engineFileName + ".xml"<< endl;
} else {
if (Eng_cfg.IsOpen()) {
Cfg_ptr = &Eng_cfg;
if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
+ engineFileName + ".xml"<< endl;
}
}
if (Eng_cfg.IsOpen()) {
Eng_cfg.GetNextConfigLine();
engType = Eng_cfg.GetValue();
if (Cfg_ptr) {
Cfg_ptr->GetNextConfigLine();
engType = Cfg_ptr->GetValue();
FCS->AddThrottle();
ThrottleAdded = true;
if (engType == "FG_ROCKET") {
Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
Engines.push_back(new FGRocket(FDMExec, Cfg_ptr));
} else if (engType == "FG_PISTON") {
Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
Engines.push_back(new FGPiston(FDMExec, Cfg_ptr));
} else if (engType == "FG_TURBINE") {
Engines.push_back(new FGTurbine(FDMExec, &Eng_cfg));
Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr));
} else if (engType == "FG_SIMTURBINE") {
Engines.push_back(new FGSimTurbine(FDMExec, &Eng_cfg));
cerr << endl;
cerr << "The FG_SIMTURBINE engine type has been renamed to FG_TURBINE." << endl;
cerr << "To fix this problem, simply replace the FG_SIMTURBINE name " << endl;
cerr << "in your engine file to FG_TURBINE." << endl;
cerr << endl;
Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr));
} else if (engType == "FG_ELECTRIC") {
Engines.push_back(new FGElectric(FDMExec, Cfg_ptr));
} else {
cerr << fgred << " Unrecognized engine type: " << underon << engType
<< underoff << " found in config file." << fgdef << endl;
@ -262,7 +258,12 @@ bool FGPropulsion::Load(FGConfigFile* AC_cfg)
else if (token == "YLOC") { *AC_cfg >> yLoc; }
else if (token == "ZLOC") { *AC_cfg >> zLoc; }
else if (token == "PITCH") { *AC_cfg >> Pitch;}
else if (token == "YAW") { *AC_cfg >> Yaw;}
else if (token == "YAW") { *AC_cfg >> Yaw; }
else if (token.find("AC_THRUSTER") != string::npos) {
if (debug_lvl > 0) cout << "\n Reading thruster definition" << endl;
Engines.back()->LoadThruster(AC_cfg);
AC_cfg->GetNextConfigLine();
}
else if (token == "FEED") {
*AC_cfg >> Feed;
Engines[numEngines]->AddFeedTank(Feed);
@ -286,14 +287,14 @@ bool FGPropulsion::Load(FGConfigFile* AC_cfg)
} else {
cerr << fgred << "\n Could not read engine config file: " << underon <<
fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
engineFileName + ".xml" << underoff << fgdef << endl;
return false;
}
} else if (token == "AC_TANK") { // ============== READING TANKS
if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
Tanks.push_back(new FGTank(AC_cfg));
Tanks.push_back(new FGTank(AC_cfg, FDMExec));
switch(Tanks[numTanks]->GetType()) {
case FGTank::ttFUEL:
numSelectedFuelTanks++;
@ -306,59 +307,6 @@ bool FGPropulsion::Load(FGConfigFile* AC_cfg)
}
numTanks++;
} else if (token == "AC_THRUSTER") { // ========== READING THRUSTERS
thrusterFileName = AC_cfg->GetValue("FILE");
if (debug_lvl > 0) cout << "\n Reading thruster from file: " <<
fullpath + thrusterFileName + ".xml" << endl;
FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
if (Thruster_cfg.IsOpen()) {
Thruster_cfg.GetNextConfigLine();
thrType = Thruster_cfg.GetValue();
if (thrType == "FG_PROPELLER") {
Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
} else if (thrType == "FG_NOZZLE") {
Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg ));
} else if (thrType == "FG_DIRECT") {
Thrusters.push_back(new FGThruster( FDMExec, &Thruster_cfg) );
}
AC_cfg->GetNextConfigLine();
while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
*AC_cfg >> token;
if (token == "XLOC") *AC_cfg >> xLoc;
else if (token == "YLOC") *AC_cfg >> yLoc;
else if (token == "ZLOC") *AC_cfg >> zLoc;
else if (token == "PITCH") *AC_cfg >> Pitch;
else if (token == "YAW") *AC_cfg >> Yaw;
else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
else if (token == "SENSE") *AC_cfg >> Sense;
else cerr << "Unknown identifier: " << token << " in engine file: "
<< engineFileName << endl;
}
Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
if (debug_lvl > 0) cout << " P-Factor: " << P_Factor << endl;
((FGPropeller*)Thrusters[numThrusters])->SetSense(fabs(Sense)/Sense);
if (debug_lvl > 0) cout << " Sense: " << Sense << endl;
}
Thrusters[numThrusters]->SetdeltaT(dt*rate);
Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
numThrusters++;
} else {
cerr << "Could not read thruster config file: " << fullpath
+ thrusterFileName + ".xml" << endl;
return false;
}
}
AC_cfg->GetNextConfigLine();
}
@ -375,58 +323,12 @@ string FGPropulsion::GetPropulsionStrings(void)
{
string PropulsionStrings = "";
bool firstime = true;
char buffer[5];
for (unsigned int i=0;i<Engines.size();i++) {
if (firstime) firstime = false;
else PropulsionStrings += ", ";
sprintf(buffer, "%d", i);
switch(Engines[i]->GetType()) {
case FGEngine::etPiston:
PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
break;
case FGEngine::etRocket:
PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
break;
case FGEngine::etTurbine:
break;
case FGEngine::etSimTurbine:
PropulsionStrings += (Engines[i]->GetName() + "_N1[" + buffer + "], ");
PropulsionStrings += (Engines[i]->GetName() + "_N2[" + buffer + "]");
break;
default:
PropulsionStrings += "INVALID ENGINE TYPE";
break;
}
PropulsionStrings += ", ";
FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
switch(Thrusters[i]->GetType()) {
case FGThruster::ttNozzle:
PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
break;
case FGThruster::ttRotor:
break;
case FGThruster::ttPropeller:
PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Roll[" + buffer + "], ");
PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Pitch[" + buffer + "], ");
PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Yaw[" + buffer + "], ");
PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
if (Propeller->IsVPitch())
PropulsionStrings += (Thrusters[i]->GetName() + "_Pitch[" + buffer + "], ");
PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
break;
case FGThruster::ttDirect:
PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
break;
default:
PropulsionStrings += "INVALID THRUSTER TYPE";
break;
}
PropulsionStrings += Engines[i]->GetEngineLabels() + ", ";
}
return PropulsionStrings;
@ -436,7 +338,6 @@ string FGPropulsion::GetPropulsionStrings(void)
string FGPropulsion::GetPropulsionValues(void)
{
char buff[20];
string PropulsionValues = "";
bool firstime = true;
@ -444,45 +345,7 @@ string FGPropulsion::GetPropulsionValues(void)
if (firstime) firstime = false;
else PropulsionValues += ", ";
switch(Engines[i]->GetType()) {
case FGEngine::etPiston:
PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
break;
case FGEngine::etRocket:
PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
break;
case FGEngine::etTurbine:
break;
case FGEngine::etSimTurbine:
PropulsionValues += (string(gcvt(((FGSimTurbine*)Engines[i])->GetN1(), 10, buff))) + ", ";
PropulsionValues += (string(gcvt(((FGSimTurbine*)Engines[i])->GetN2(), 10, buff)));
break;
}
PropulsionValues += ", ";
switch(Thrusters[i]->GetType()) {
case FGThruster::ttNozzle:
PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
break;
case FGThruster::ttRotor:
break;
case FGThruster::ttDirect:
PropulsionValues += (string(gcvt(((FGThruster*)Thrusters[i])->GetThrust(), 10, buff)));
break;
case FGThruster::ttPropeller:
FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
FGColumnVector3 vPFactor = Propeller->GetPFactor();
PropulsionValues += string(gcvt(Propeller->GetTorque(), 10, buff)) + ", ";
PropulsionValues += string(gcvt(vPFactor(eRoll), 10, buff)) + ", ";
PropulsionValues += string(gcvt(vPFactor(ePitch), 10, buff)) + ", ";
PropulsionValues += string(gcvt(vPFactor(eYaw), 10, buff)) + ", ";
PropulsionValues += string(gcvt(Propeller->GetThrust(), 10, buff)) + ", ";
if (Propeller->IsVPitch())
PropulsionValues += string(gcvt(Propeller->GetPitch(), 10, buff)) + ", ";
PropulsionValues += string(gcvt(Propeller->GetRPM(), 10, buff));
break;
}
PropulsionValues += Engines[i]->GetEngineValues() + ", ";
}
return PropulsionValues;
@ -574,15 +437,15 @@ void FGPropulsion::SetCutoff(int setting)
if (ActiveEngine < 0) {
for (unsigned i=0; i<Engines.size(); i++) {
if (setting == 0)
((FGSimTurbine*)Engines[i])->SetCutoff(false);
((FGTurbine*)Engines[i])->SetCutoff(false);
else
((FGSimTurbine*)Engines[i])->SetCutoff(true);
((FGTurbine*)Engines[i])->SetCutoff(true);
}
} else {
if (setting == 0)
((FGSimTurbine*)Engines[ActiveEngine])->SetCutoff(false);
((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
else
((FGSimTurbine*)Engines[ActiveEngine])->SetCutoff(true);
((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
}
}
@ -598,6 +461,44 @@ void FGPropulsion::SetActiveEngine(int engine)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropulsion::Transfer(int source, int target, double amount)
{
double shortage, overage;
if (source == -1) {
shortage = 0.0;
} else {
shortage = Tanks[source]->Drain(amount);
}
if (target == -1) {
overage = 0.0;
} else {
overage = Tanks[target]->Fill(amount - shortage);
}
return overage;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropulsion::DoRefuel(double time_slice)
{
double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
int TanksNotFull = 0;
for (unsigned int i=0; i<numTanks; i++) {
if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
}
if (TanksNotFull) {
for (unsigned int i=0; i<numTanks; i++) {
if (Tanks[i]->GetPctFull() < 99.99)
Transfer(-1, i, fillrate/TanksNotFull);
}
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropulsion::bind(void)
{
typedef double (FGPropulsion::*PMF)(int) const;

View file

@ -55,7 +55,6 @@ INCLUDES
#include "FGModel.h"
#include "FGEngine.h"
#include "FGTank.h"
#include "FGThruster.h"
#include "FGMatrix33.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -76,27 +75,18 @@ CLASS DOCUMENTATION
/** Propulsion management class.
The Propulsion class is the container for the entire propulsion system, which is
comprised of engines, tanks, and "thrusters" (the device that transforms the
engine power into a force that acts on the aircraft, such as a nozzle or
propeller). Once the Propulsion class gets the config file, it reads in
information which is specific to a type of engine. Then:
comprised of engines, and tanks. Once the Propulsion class gets the config file,
it reads in information which is specific to a type of engine. Then:
-# The appropriate engine type instance is created
-# A thruster object is instantiated, and is linked to the engine
-# At least one tank object is created, and is linked to an engine.
At Run time each engines Calculate() method is called to return the excess power
generated during that iteration. The drag from the previous iteration is sub-
tracted to give the excess power available for thrust this pass. That quantity
is passed to the thrusters associated with a particular engine - perhaps with a
scaling mechanism (gearing?) to allow the engine to give its associated thrust-
ers specific distributed portions of the excess power.
At Run time each engines Calculate() method is called.
@author Jon S. Berndt
@version $Id$
@see
FGEngine
FGTank
FGThruster
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -113,22 +103,12 @@ public:
/** Executes the propulsion model.
The initial plan for the FGPropulsion class calls for Run() to be executed,
performing the following tasks:
<ol>
<li>Determine the drag - or power required - for the attached thrust effector
for this engine so that any feedback to the engine can be performed. This
is done by calling FGThruster::CalculatePReq()</li>
<li>Given 1, above, calculate the power available from the engine. This is
done by calling FGEngine::CalculatePAvail()</li>
<li>Next, calculate the thrust output from the thruster model given the power
available and the power required. This may also result in new performance
numbers for the thruster in the case of the propeller, at least. This
result is returned from a call to Calculate().</li></ol>
calculating the power available from the engine.
[Note: Should we be checking the Starved flag here?] */
bool Run(void);
/** Loads the propulsion system (engine[s], tank[s], thruster[s]).
/** Loads the propulsion system (engine[s] and tank[s]).
Characteristics of the propulsion system are read in from the config file.
@param AC_cfg pointer to the config file instance that describes the
aircraft being modeled.
@ -157,21 +137,13 @@ public:
if (index <= Tanks.size()-1) return Tanks[index];
else return 0L; }
/** Retrieves a thruster object pointer from the list of thrusters.
@param index the thruster index within the vector container
@return the address of the specific thruster, or zero if no such thruster is
available */
inline FGThruster* GetThruster(unsigned int index) {
if (index <= Thrusters.size()-1) return Thrusters[index];
else return 0L; }
/** Returns the number of fuel tanks currently actively supplying fuel */
inline int GetnumSelectedFuelTanks(void) const {return numSelectedFuelTanks;}
/** Returns the number of oxidizer tanks currently actively supplying oxidizer */
inline int GetnumSelectedOxiTanks(void) const {return numSelectedOxiTanks;}
/** Loops the engines/thrusters until thrust output steady (used for trimming) */
/** Loops the engines until thrust output steady (used for trimming) */
bool GetSteadyState(void);
/** starts the engines in IC mode (dt=0). All engine-specific setup must
@ -186,6 +158,11 @@ public:
inline FGColumnVector3& GetMoments(void) {return vMoments;}
inline double GetMoments(int n) const {return vMoments(n);}
inline bool GetRefuel(void) {return refuel;}
inline void SetRefuel(bool setting) {refuel = setting;}
double Transfer(int source, int target, double amount);
void DoRefuel(double time_slice);
FGColumnVector3& GetTanksMoment(void);
double GetTanksWeight(void);
@ -209,21 +186,20 @@ private:
vector <FGEngine*> Engines;
vector <FGTank*> Tanks;
vector <FGTank*>::iterator iTank;
vector <FGThruster*> Thrusters;
unsigned int numSelectedFuelTanks;
unsigned int numSelectedOxiTanks;
unsigned int numFuelTanks;
unsigned int numOxiTanks;
unsigned int numEngines;
unsigned int numTanks;
unsigned int numThrusters;
int ActiveEngine;
double dt;
FGColumnVector3 vForces;
FGColumnVector3 vMoments;
FGColumnVector3 vTankXYZ;
FGColumnVector3 vXYZtank_arm;
FGMatrix33 tankJ;
bool refuel;
void Debug(int from);
};
}

View file

@ -0,0 +1,235 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGQuaternion.cpp
Author: Jon Berndt, Mathias Froehlich
Date started: 12/02/98
------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.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 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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU 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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <math.h>
# include <simgear/compiler.h>
# include STL_IOSTREAM
SG_USING_STD(cerr);
SG_USING_STD(cout);
SG_USING_STD(endl);
#else
# include <string>
# if defined(sgi) && !defined(__GNUC__) && (_COMPILER_VERSION < 740)
# include <iostream.h>
# include <math.h>
# else
# include <iostream>
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
using std::cerr;
using std::cout;
using std::endl;
# endif
#endif
#include "FGMatrix33.h"
#include "FGColumnVector3.h"
#include "FGQuaternion.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_QUATERNION;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Initialize from q
FGQuaternion::FGQuaternion(const FGQuaternion& q)
: mCacheValid(q.mCacheValid) {
Entry(1) = q(1);
Entry(2) = q(2);
Entry(3) = q(3);
Entry(4) = 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) {
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;
Entry(1) = Cphid2Cthtd2*Cpsid2 + Sphid2Sthtd2*Spsid2;
Entry(2) = Sphid2Cthtd2*Cpsid2 - Cphid2Sthtd2*Spsid2;
Entry(3) = Cphid2Sthtd2*Cpsid2 + Sphid2Cthtd2*Spsid2;
Entry(4) = Cphid2Cthtd2*Spsid2 - Sphid2Sthtd2*Cpsid2;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/**
Returns the derivative of the quaternion coresponding to the
angular velocities PQR.
*/
FGQuaternion FGQuaternion::GetQDot(const FGColumnVector3& PQR) const {
FGQuaternion QDot;
QDot(1) = -0.5*(Entry(2)*PQR(eP) + Entry(3)*PQR(eQ) + Entry(4)*PQR(eR));
QDot(2) = 0.5*(Entry(1)*PQR(eP) + Entry(3)*PQR(eR) - Entry(4)*PQR(eQ));
QDot(3) = 0.5*(Entry(1)*PQR(eQ) + Entry(4)*PQR(eP) - Entry(2)*PQR(eR));
QDot(4) = 0.5*(Entry(1)*PQR(eR) + Entry(2)*PQR(eQ) - Entry(3)*PQR(eP));
return QDot;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGQuaternion::Normalize()
{
// Note: this does not touch the cache
// since it does not change the orientation ...
double norm = Magnitude();
if (norm == 0.0)
return;
double rnorm = 1.0/norm;
Entry(1) *= rnorm;
Entry(2) *= rnorm;
Entry(3) *= rnorm;
Entry(4) *= rnorm;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Compute the derived values if required ...
void FGQuaternion::ComputeDerivedUnconditional(void) const
{
mCacheValid = true;
// First normalize the 4-vector
double norm = Magnitude();
if (norm == 0.0)
return;
double rnorm = 1.0/norm;
double q1 = rnorm*Entry(1);
double q2 = rnorm*Entry(2);
double q3 = rnorm*Entry(3);
double q4 = rnorm*Entry(4);
// Now compute the transformation matrix.
double q1q1 = q1*q1;
double q2q2 = q2*q2;
double q3q3 = q3*q3;
double q4q4 = q4*q4;
double q1q2 = q1*q2;
double q1q3 = q1*q3;
double q1q4 = q1*q4;
double q2q3 = q2*q3;
double q2q4 = q2*q4;
double q3q4 = q3*q4;
mT(1,1) = q1q1 + q2q2 - q3q3 - q4q4;
mT(1,2) = 2.0*(q2q3 + q1q4);
mT(1,3) = 2.0*(q2q4 - q1q3);
mT(2,1) = 2.0*(q2q3 - q1q4);
mT(2,2) = q1q1 - q2q2 + q3q3 - q4q4;
mT(2,3) = 2.0*(q3q4 + q1q2);
mT(3,1) = 2.0*(q2q4 + q1q3);
mT(3,2) = 2.0*(q3q4 - q1q2);
mT(3,3) = q1q1 - q2q2 - q3q3 + q4q4;
// Since this is an orthogonal matrix, the inverse is simply
// the transpose.
mTInv = mT;
mTInv.T();
// Compute the Euler-angles
if (mT(3,3) == 0.0)
mEulerAngles(ePhi) = 0.5*M_PI;
else
mEulerAngles(ePhi) = atan2(mT(2,3), mT(3,3));
if (mT(1,3) < -1.0)
mEulerAngles(eTht) = 0.5*M_PI;
else if (1.0 < mT(1,3))
mEulerAngles(eTht) = -0.5*M_PI;
else
mEulerAngles(eTht) = asin(-mT(1,3));
if (mT(1,1) == 0.0)
mEulerAngles(ePsi) = 0.5*M_PI;
else {
double psi = atan2(mT(1,2), mT(1,1));
if (psi < 0.0)
psi += 2*M_PI;
mEulerAngles(ePsi) = psi;
}
// 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));
}
} // namespace JSBSim

View file

@ -0,0 +1,499 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGQuaternion.h
Author: Jon Berndt, Mathis Froehlich
Date started: 12/02/98
------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.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 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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU 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 "FGJSBBase.h"
#include "FGMatrix33.h"
#include "FGColumnVector3.h"
#include "FGPropertyManager.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_QUATERNION "$Id$"
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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
: virtual FGJSBBase {
public:
/** Default initializer.
Default initializer, initializes the class with the identity rotation. */
FGQuaternion() : mCacheValid(false) {
Entry(1) = 1.0;
Entry(2) = Entry(3) = Entry(4) = 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);
/// Destructor.
~FGQuaternion() {}
/** Quaternion 'velocity' for given angular rates.
Computes the quaternion derivative which results from the given
angular velocities
@param PQR a constant reference to the body rate vector
@return the quaternion derivative */
FGQuaternion GetQDot(const FGColumnVector3& PQR) const;
/** Transformation matrix.
@return a reference to the transformation/rotation matrix
corresponding to this quaternion rotation. */
const FGMatrix33& GetT() const { ComputeDerived(); return mT; }
/** Backward transformation matrix.
@return a reference to the inverse transformation/rotation matrix
corresponding to this quaternion rotation. */
const FGMatrix33& GetTInv() 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() const {
ComputeDerived();
return mEulerAngles;
}
/** Euler angle theta.
@return the euler angle theta (pitch attitude) corresponding to this
quaternion rotation.
@units radians */
double GetEulerTheta() const {
ComputeDerived();
return mEulerAngles(eTht);
}
/** Euler angle theta.
@return the euler angle theta (pitch attitude) corresponding to
this quaternion rotation.
@units degrees */
double GetEulerThetaDeg() const {
ComputeDerived();
return radtodeg*mEulerAngles(eTht);
}
/** Euler angle psi.
@return the heading euler angle (psi) corresponding to this quaternion
rotation.
@units radians */
double GetEulerPsi() const {
ComputeDerived();
return mEulerAngles(ePsi);
}
/** Retrieves the heading angle.
@return the Euler angle psi (heading) corresponding to this quaternion
rotation.
@units degrees */
double GetEulerPsiDeg() const {
ComputeDerived();
return radtodeg*mEulerAngles(ePsi);
}
/** Retrieves the roll angle.
@return the euler angle phi (roll) corresponding to this quaternion
rotation.
@units radians */
double GetEulerPhi() const {
ComputeDerived();
return mEulerAngles(ePhi);
}
/** Retrieves the roll angle.
Returns the Euler angle phi (roll) corresponding to this quaternion rotation.
@units degrees */
double GetEulerPhiDeg() const {
ComputeDerived();
return radtodeg*mEulerAngles(ePhi);
}
/** Retrieves sine theta.
@return the sine of the Euler angle theta (pitch attitude) corresponding
to this quaternion rotation. */
double GetSinEulerTheta() const {
ComputeDerived();
return mEulerSines(eTht);
}
/** Retrieves sine psi.
@return the sine of the Euler angle psi (heading) corresponding to this
quaternion rotation. */
double GetSinEulerPsi() const {
ComputeDerived();
return mEulerSines(ePsi);
}
/** Sine of euler angle phi.
@return the sine of the Euler angle phi (roll) corresponding to this
quaternion rotation. */
double GetSinEulerPhi() const {
ComputeDerived();
return mEulerSines(ePhi);
}
/** Cosine of euler angle theta.
@return the cosine of the Euler angle theta (pitch) corresponding to this
quaternion rotation. */
double GetCosEulerTheta() const {
ComputeDerived();
return mEulerCosines(eTht);
}
/** Cosine of euler angle psi.
@return the cosine of the Euler angle psi (heading) corresponding to this
quaternion rotation. */
double GetCosEulerPsi() const {
ComputeDerived();
return mEulerCosines(ePsi);
}
/** Cosine of euler angle phi.
@return the cosine of the Euler angle phi (roll) corresponding to this
quaternion rotation. */
double GetCosEulerPhi() const {
ComputeDerived();
return mEulerCosines(ePhi);
}
/** 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 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) { return 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 @ref double
operator()(unsigned int idx) const 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 mData[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 @ref 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 mData[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 ...
Entry(1) = q(1);
Entry(2) = q(2);
Entry(3) = q(3);
Entry(4) = q(4);
// .. 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;
}
/** Comparison operator "==".
@param q a quaternion reference
@return true if both quaternions represent the same rotation. */
bool operator==(const FGQuaternion& q) const {
return Entry(1) == q(1) && Entry(2) == q(2)
&& Entry(3) == q(3) && Entry(4) == q(4);
}
/** 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 ...
Entry(1) += q(1);
Entry(2) += q(2);
Entry(3) += q(3);
Entry(4) += q(4);
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 ...
Entry(1) -= q(1);
Entry(2) -= q(2);
Entry(3) -= q(3);
Entry(4) -= q(4);
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) {
Entry(1) *= scalar;
Entry(2) *= scalar;
Entry(3) *= scalar;
Entry(4) *= 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(Entry(1)+q(1), Entry(2)+q(2),
Entry(3)+q(3), Entry(4)+q(4));
}
/** 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(Entry(1)-q(1), Entry(2)-q(2),
Entry(3)-q(3), Entry(4)-q(4));
}
/** 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(Entry(1)*q(1)-Entry(2)*q(2)-Entry(3)*q(3)-Entry(4)*q(4),
Entry(1)*q(2)+Entry(2)*q(1)+Entry(3)*q(4)-Entry(4)*q(3),
Entry(1)*q(3)-Entry(2)*q(4)+Entry(3)*q(1)+Entry(4)*q(2),
Entry(1)*q(4)+Entry(2)*q(3)-Entry(3)*q(2)+Entry(4)*q(1));
}
/** 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 = Entry(1)*q(1)-Entry(2)*q(2)-Entry(3)*q(3)-Entry(4)*q(4);
double q1 = Entry(1)*q(2)+Entry(2)*q(1)+Entry(3)*q(4)-Entry(4)*q(3);
double q2 = Entry(1)*q(3)-Entry(2)*q(4)+Entry(3)*q(1)+Entry(4)*q(2);
double q3 = Entry(1)*q(4)+Entry(2)*q(3)-Entry(3)*q(2)+Entry(4)*q(1);
Entry(1) = q0;
Entry(2) = q1;
Entry(3) = q2;
Entry(4) = q3;
mCacheValid = false;
return *this;
}
friend FGQuaternion operator*(double, const FGQuaternion&);
/** Length of the vector.
Compute and return the euclidean norm of this vector.
*/
double Magnitude() 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() const {
return Entry(1)*Entry(1)+Entry(2)*Entry(2)
+Entry(3)*Entry(3)+Entry(4)*Entry(4);
}
/** Normialze.
Normalize the vector to have the Magnitude() == 1.0. If the vector
is equal to zero it is left untouched.
*/
void Normalize();
/** 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 ); }
private:
/** Copying by assigning the vector valued components. */
FGQuaternion(double q1, double q2, double q3, double q4) : mCacheValid(false)
{ Entry(1) = q1; Entry(2) = q2; Entry(3) = q3; Entry(4) = 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 this is done here. */
void ComputeDerived(void) const {
if (!mCacheValid)
ComputeDerivedUnconditional();
}
/** The quaternion values itself. This is the master copy. */
double mData[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;
};
/** Scalar multiplication.
@param scalar scalar value to multiply with.
@param p Vector to multiply.
Multiply the Vector with a scalar value.
*/
inline FGQuaternion operator*(double scalar, const FGQuaternion& q) {
return FGQuaternion(scalar*q(1), scalar*q(2), scalar*q(3), scalar*q(4));
}
} // namespace JSBSim
#endif

View file

@ -38,6 +38,8 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include "FGRocket.h"
namespace JSBSim {
@ -88,7 +90,7 @@ FGRocket::~FGRocket(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGRocket::Calculate(double pe)
double FGRocket::Calculate(void)
{
double Cf=0;
@ -103,11 +105,34 @@ double FGRocket::Calculate(double pe)
} else {
PctPower = Throttle / MaxThrottle;
PC = maxPC*PctPower * (1.0 + Variance * ((double)rand()/(double)RAND_MAX - 0.5));
Cf = sqrt(kFactor*(1 - pow(pe/(PC), (SHR-1)/SHR)));
Cf = sqrt(kFactor*(1 - pow(Thruster->GetPowerRequired()/(PC), (SHR-1)/SHR)));
Flameout = false;
}
return Cf*maxPC*PctPower*propEff;
return Thruster->Calculate(Cf*maxPC*PctPower*propEff);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGRocket::GetEngineLabels(void)
{
std::ostringstream buf;
buf << Name << "_ChamberPress[" << EngineNumber << "], "
<< Thruster->GetThrusterLabels(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGRocket::GetEngineValues(void)
{
std::ostringstream buf;
buf << PC << ", " << Thruster->GetThrusterValues(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -110,11 +110,8 @@ public:
~FGRocket(void);
/** Determines the thrust coefficient.
This routine takes the nozzle exit pressure and calculates the thrust
coefficient times the chamber pressure.
@param pe nozzle exit pressure
@return thrust coefficient times chamber pressure */
double Calculate(double pe);
double Calculate(void);
/** Gets the chamber pressure.
@return chamber pressure in psf. */
@ -125,6 +122,8 @@ public:
sustainable setting.
@return true if engine has flamed out. */
bool GetFlameout(void) {return Flameout;}
string GetEngineLabels(void);
string GetEngineValues(void);
private:
double SHR;

View file

@ -1,267 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGRotation.cpp
Author: Jon Berndt
Date started: 12/02/98
Purpose: Integrates the rotational EOM
Called by: FGFDMExec
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class integrates the rotational EOM.
HISTORY
--------------------------------------------------------------------------------
12/02/98 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] 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
[2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
JSC 12960, July 1977
[3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
[4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
Wiley & Sons, 1979 ISBN 0-471-03032-5
[5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
1982 ISBN 0-471-08936-2
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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGRotation.h"
#include "FGAtmosphere.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGPropertyManager.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_ROTATION;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGRotation::FGRotation(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGRotation";
cTht = cPhi = cPsi = 1.0;
sTht = sPhi = sPsi = 0.0;
vPQRdot.InitMatrix();
vPQRdot_prev[0].InitMatrix();
vPQRdot_prev[1].InitMatrix();
vPQRdot_prev[2].InitMatrix();
vPQRdot_prev[3].InitMatrix();
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGRotation::~FGRotation()
{
unbind();
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGRotation::Run(void)
{
double tTheta;
if (!FGModel::Run()) {
GetState();
vPQRdot = MassBalance->GetJinv()*(vMoments - vPQR*(MassBalance->GetJ()*vPQR));
vPQR += State->Integrate(FGState::TRAPZ, dt*rate, vPQRdot, vPQRdot_prev);
vAeroPQR = vPQR + Atmosphere->GetTurbPQR();
State->IntegrateQuat(vPQR, rate);
State->CalcMatrices();
vEuler = State->CalcEuler();
cTht = cos(vEuler(eTht)); sTht = sin(vEuler(eTht));
cPhi = cos(vEuler(ePhi)); sPhi = sin(vEuler(ePhi));
cPsi = cos(vEuler(ePsi)); sPsi = sin(vEuler(ePsi));
vEulerRates(eTht) = vPQR(2)*cPhi - vPQR(3)*sPhi;
if (cTht != 0.0) {
tTheta = sTht/cTht; // what's cheaper: / or tan() ?
vEulerRates(ePhi) = vPQR(1) + (vPQR(2)*sPhi + vPQR(3)*cPhi)*tTheta;
vEulerRates(ePsi) = (vPQR(2)*sPhi + vPQR(3)*cPhi)/cTht;
}
if (debug_lvl > 1) Debug(2);
return false;
} else {
return true;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGRotation::GetState(void)
{
dt = State->Getdt();
vMoments = Aircraft->GetMoments();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGRotation::bind(void)
{
typedef double (FGRotation::*PMF)(int) const;
PropertyManager->Tie("velocities/p-rad_sec", this,1,
(PMF)&FGRotation::GetPQR);
PropertyManager->Tie("velocities/q-rad_sec", this,2,
(PMF)&FGRotation::GetPQR);
PropertyManager->Tie("velocities/r-rad_sec", this,3,
(PMF)&FGRotation::GetPQR);
PropertyManager->Tie("velocities/p-aero-rad_sec", this,1,
(PMF)&FGRotation::GetAeroPQR);
PropertyManager->Tie("velocities/q-aero-rad_sec", this,2,
(PMF)&FGRotation::GetAeroPQR);
PropertyManager->Tie("velocities/r-aero-rad_sec", this,3,
(PMF)&FGRotation::GetAeroPQR);
PropertyManager->Tie("accelerations/pdot-rad_sec", this,1,
(PMF)&FGRotation::GetPQRdot);
PropertyManager->Tie("accelerations/qdot-rad_sec", this,2,
(PMF)&FGRotation::GetPQRdot);
PropertyManager->Tie("accelerations/rdot-rad_sec", this,3,
(PMF)&FGRotation::GetPQRdot);
PropertyManager->Tie("attitude/roll-rad", this,1,
(PMF)&FGRotation::GetEuler);
PropertyManager->Tie("attitude/pitch-rad", this,2,
(PMF)&FGRotation::GetEuler);
PropertyManager->Tie("attitude/heading-true-rad", this,3,
(PMF)&FGRotation::GetEuler);
PropertyManager->Tie("velocities/phidot-rad_sec", this,1,
(PMF)&FGRotation::GetEulerRates);
PropertyManager->Tie("velocities/thetadot-rad_sec", this,2,
(PMF)&FGRotation::GetEulerRates);
PropertyManager->Tie("velocities/psidot-rad_sec", this,3,
(PMF)&FGRotation::GetEulerRates);
PropertyManager->Tie("attitude/phi-rad", this,
&FGRotation::Getphi);
PropertyManager->Tie("attitude/theta-rad", this,
&FGRotation::Gettht);
PropertyManager->Tie("attitude/psi-true-rad", this,
&FGRotation::Getpsi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGRotation::unbind(void)
{
PropertyManager->Untie("velocities/p-rad_sec");
PropertyManager->Untie("velocities/q-rad_sec");
PropertyManager->Untie("velocities/r-rad_sec");
PropertyManager->Untie("velocities/p-aero-rad_sec");
PropertyManager->Untie("velocities/q-aero-rad_sec");
PropertyManager->Untie("velocities/r-aero-rad_sec");
PropertyManager->Untie("accelerations/pdot-rad_sec");
PropertyManager->Untie("accelerations/qdot-rad_sec");
PropertyManager->Untie("accelerations/rdot-rad_sec");
PropertyManager->Untie("attitude/roll-rad");
PropertyManager->Untie("attitude/pitch-rad");
PropertyManager->Untie("attitude/heading-true-rad");
PropertyManager->Untie("velocities/phidot-rad_sec");
PropertyManager->Untie("velocities/thetadot-rad_sec");
PropertyManager->Untie("velocities/psidot-rad_sec");
PropertyManager->Untie("attitude/phi-rad");
PropertyManager->Untie("attitude/theta-rad");
PropertyManager->Untie("attitude/psi-true-rad");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 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 FGRotation::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: FGRotation" << endl;
if (from == 1) cout << "Destroyed: FGRotation" << 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 check variables
if (from == 2) {
if (fabs(vPQR(eP)) > 100)
cout << "FGRotation::P (Roll Rate) out of bounds: " << vPQR(eP) << endl;
if (fabs(vPQR(eQ)) > 100)
cout << "FGRotation::Q (Pitch Rate) out of bounds: " << vPQR(eQ) << endl;
if (fabs(vPQR(eR)) > 100)
cout << "FGRotation::R (Yaw Rate) out of bounds: " << vPQR(eR) << endl;
}
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -1,156 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGRotation.h
Author: Jon Berndt
Date started: 12/02/98
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
12/02/98 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGROTATION_H
#define FGROTATION_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# include <math.h>
#else
# if defined (sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
#endif
#include "FGModel.h"
#include "FGColumnVector3.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ROTATION "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models the rotational portion of the Equations of Motion.
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
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGRotation : public FGModel
{
public:
FGRotation(FGFDMExec*);
~FGRotation();
bool Run(void);
inline FGColumnVector3& GetPQR(void) {return vPQR;}
inline double GetPQR(int axis) const {return vPQR(axis);}
inline FGColumnVector3& GetAeroPQR(void) {return vAeroPQR;}
inline double GetAeroPQR(int axis) const {return vAeroPQR(axis);}
inline FGColumnVector3& GetPQRdot(void) {return vPQRdot;}
inline double GetPQRdot(int idx) const {return vPQRdot(idx);}
inline FGColumnVector3& GetEuler(void) {return vEuler;}
inline double GetEuler(int axis) const {return vEuler(axis);}
inline FGColumnVector3& GetEulerRates(void) { return vEulerRates; }
inline double GetEulerRates(int axis) const { return vEulerRates(axis); }
inline void SetPQR(FGColumnVector3 tt) {vPQR = tt;}
inline void SetPQR(double p, double q, double r) {vPQR(eP)=p;
vPQR(eQ)=q;
vPQR(eR)=r;}
inline void SetAeroPQR(FGColumnVector3 tt) {vAeroPQR = tt;}
inline void SetAeroPQR(double p, double q, double r) {vAeroPQR(eP)=p;
vAeroPQR(eQ)=q;
vAeroPQR(eR)=r;}
inline void SetEuler(FGColumnVector3 tt) {vEuler = tt;}
inline double Getphi(void) const {return vEuler(1);}
inline double Gettht(void) const {return vEuler(2);}
inline double Getpsi(void) const {return vEuler(3);}
inline double GetCosphi(void) const {return cPhi;}
inline double GetCostht(void) const {return cTht;}
inline double GetCospsi(void) const {return cPsi;}
inline double GetSinphi(void) const {return sPhi;}
inline double GetSintht(void) const {return sTht;}
inline double GetSinpsi(void) const {return sPsi;}
void bind(void);
void unbind(void);
private:
FGColumnVector3 vPQR;
FGColumnVector3 vAeroPQR;
FGColumnVector3 vPQRdot;
FGColumnVector3 vPQRdot_prev[4];
FGColumnVector3 vMoments;
FGColumnVector3 vEuler;
FGColumnVector3 vEulerRates;
double cTht,sTht;
double cPhi,sPhi;
double cPsi,sPsi;
double dt;
void GetState(void);
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -66,6 +66,20 @@ double FGRotor::Calculate(double PowerAvailable)
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGRotor::GetThrusterLabels(int id)
{
return "";
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGRotor::GetThrusterValues(int id)
{
return "";
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -70,6 +70,8 @@ public:
~FGRotor();
double Calculate(double);
string GetThrusterLabels(int id);
string GetThrusterValues(int id);
private:
void Debug(int from);

View file

@ -1,447 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGSimTurbine.cpp
Author: David Culp
Date started: 03/11/2003
Purpose: This module models a turbine engine.
------------- Copyright (C) 2003 David Culp (davidculp2@comcast.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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class descends from the FGEngine class and models a Turbine engine based
on parameters given in the engine config file for this class
HISTORY
--------------------------------------------------------------------------------
03/11/2003 DPC Created
09/08/2003 DPC Changed Calculate() and added engine phases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include "FGSimTurbine.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_SIMTURBINE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGSimTurbine::FGSimTurbine(FGFDMExec* exec, FGConfigFile* cfg) : FGEngine(exec)
{
SetDefaults();
Load(cfg);
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGSimTurbine::~FGSimTurbine()
{
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The main purpose of Calculate() is to determine what phase the engine should
// be in, then call the corresponding function.
double FGSimTurbine::Calculate(double dummy)
{
TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
dt = State->Getdt() * Propulsion->GetRate();
ThrottleCmd = FCS->GetThrottleCmd(EngineNumber);
// When trimming is finished check if user wants engine OFF or RUNNING
if ((phase == tpTrim) && (dt > 0)) {
if (Running && !Starved) {
phase = tpRun;
N2 = IdleN2;
N1 = IdleN1;
OilTemp_degK = 366.0;
Cutoff = false;
}
else {
phase = tpOff;
Cutoff = true;
EGT_degC = TAT;
}
}
if (!Running && Cutoff && Starter) {
if (phase == tpOff) phase = tpSpinUp;
}
if (!Running && !Cutoff && (N2 > 15.0)) phase = tpStart;
if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
if (dt == 0) phase = tpTrim;
if (Starved) phase = tpOff;
if (Stalled) phase = tpStall;
if (Seized) phase = tpSeize;
switch (phase) {
case tpOff: Thrust = Off(); break;
case tpRun: Thrust = Run(); break;
case tpSpinUp: Thrust = SpinUp(); break;
case tpStart: Thrust = Start(); break;
case tpStall: Thrust = Stall(); break;
case tpSeize: Thrust = Seize(); break;
case tpTrim: Thrust = Trim(); break;
default: Thrust = Off();
}
return Thrust;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Off(void)
{
double qbar = Translation->Getqbar();
Running = false;
FuelFlow_pph = Seek(&FuelFlow_pph, 0, 1000.0, 10000.0);
N1 = Seek(&N1, qbar/10.0, N1/2.0, N1/2.0);
N2 = Seek(&N2, qbar/15.0, N2/2.0, N2/2.0);
EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
OilPressure_psi = N2 * 0.62;
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
EPR = Seek(&EPR, 1.0, 0.2, 0.2);
Augmentation = false;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Run(void)
{
double idlethrust, milthrust, thrust;
double N2norm; // 0.0 = idle N2, 1.0 = maximum N2
idlethrust = MilThrust * ThrustTables[0]->TotalValue();
milthrust = (MilThrust - idlethrust) * ThrustTables[1]->TotalValue();
Running = true;
Starter = false;
N2 = Seek(&N2, IdleN2 + ThrottleCmd * N2_factor, delay, delay * 3.0);
N1 = Seek(&N1, IdleN1 + ThrottleCmd * N1_factor, delay, delay * 2.4);
N2norm = (N2 - IdleN2) / N2_factor;
thrust = idlethrust + (milthrust * N2norm * N2norm);
thrust = thrust * (1.0 - BleedDemand);
EGT_degC = TAT + 363.1 + ThrottleCmd * 357.1;
OilPressure_psi = N2 * 0.62;
OilTemp_degK = Seek(&OilTemp_degK, 366.0, 1.2, 0.1);
EPR = 1.0 + thrust/MilThrust;
if (!Augmentation) {
FuelFlow_pph = Seek(&FuelFlow_pph, thrust * TSFC, 1000.0, 100000);
if (FuelFlow_pph < IdleFF) FuelFlow_pph = IdleFF;
NozzlePosition = Seek(&NozzlePosition, 1.0 - N2norm, 0.8, 0.8);
}
if (AugMethod == 1) {
if ((ThrottleCmd > 0.99) && (N2 > 97.0)) {Augmentation = true;}
else {Augmentation = false;}
}
if ((Augmented == 1) && Augmentation) {
thrust = MaxThrust * ThrustTables[2]->TotalValue();
FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
}
if ((Injected == 1) && Injection)
thrust = thrust * ThrustTables[3]->TotalValue();
ConsumeFuel();
if (Cutoff) phase = tpOff;
if (Starved) phase = tpOff;
return thrust;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::SpinUp(void)
{
Running = false;
FuelFlow_pph = 0.0;
N2 = Seek(&N2, 25.18, 3.0, N2/2.0);
N1 = Seek(&N1, 5.21, 1.0, N1/2.0);
EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
OilPressure_psi = N2 * 0.62;
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
EPR = 1.0;
NozzlePosition = 1.0;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Start(void)
{
if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
Cranking = true; // provided for sound effects signal
if (N2 < IdleN2) {
N2 = Seek(&N2, IdleN2, 2.0, N2/2.0);
N1 = Seek(&N1, IdleN1, 1.4, N1/2.0);
EGT_degC = Seek(&EGT_degC, TAT + 363.1, 21.3, 7.3);
FuelFlow_pph = Seek(&FuelFlow_pph, IdleFF, 103.7, 103.7);
OilPressure_psi = N2 * 0.62;
}
else {
phase = tpRun;
Running = true;
Starter = false;
Cranking = false;
}
}
else { // no start if N2 < 15%
phase = tpOff;
Starter = false;
}
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Stall(void)
{
double qbar = Translation->Getqbar();
EGT_degC = TAT + 903.14;
FuelFlow_pph = IdleFF;
N1 = Seek(&N1, qbar/10.0, 0, N1/10.0);
N2 = Seek(&N2, qbar/15.0, 0, N2/10.0);
if (ThrottleCmd == 0) phase = tpRun; // clear the stall with throttle
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Seize(void)
{
double qbar = Translation->Getqbar();
N2 = 0.0;
N1 = Seek(&N1, qbar/20.0, 0, N1/15.0);
FuelFlow_pph = IdleFF;
OilPressure_psi = 0.0;
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0, 0.2);
Running = false;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Trim(void)
{
double idlethrust, milthrust, thrust;
idlethrust = MilThrust * ThrustTables[0]->TotalValue();
milthrust = (MilThrust - idlethrust) * ThrustTables[1]->TotalValue();
thrust = idlethrust + (milthrust * ThrottleCmd * ThrottleCmd);
return thrust;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::CalcFuelNeed(void)
{
return FuelFlow_pph /3600 * State->Getdt() * Propulsion->GetRate();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::GetPowerAvailable(void) {
if( ThrottleCmd <= 0.77 )
return 64.94*ThrottleCmd;
else
return 217.38*ThrottleCmd - 117.38;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGSimTurbine::Seek(double *var, double target, double accel, double decel) {
double v = *var;
if (v > target) {
v -= dt * decel;
if (v < target) v = target;
} else if (v < target) {
v += dt * accel;
if (v > target) v = target;
}
return v;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGSimTurbine::SetDefaults(void)
{
Name = "Not defined";
N1 = N2 = 0.0;
Type = etSimTurbine;
MilThrust = 10000.0;
MaxThrust = 10000.0;
BypassRatio = 0.0;
TSFC = 0.8;
ATSFC = 1.7;
IdleN1 = 30.0;
IdleN2 = 60.0;
MaxN1 = 100.0;
MaxN2 = 100.0;
Augmented = 0;
AugMethod = 0;
Injected = 0;
BleedDemand = 0.0;
ThrottleCmd = 0.0;
InletPosition = 1.0;
NozzlePosition = 1.0;
Augmentation = false;
Injection = false;
Reversed = false;
Cutoff = true;
phase = tpOff;
Stalled = false;
Seized = false;
Overtemp = false;
Fire = false;
EGT_degC = 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGSimTurbine::Load(FGConfigFile *Eng_cfg)
{
string token;
Name = Eng_cfg->GetValue("NAME");
Eng_cfg->GetNextConfigLine();
int counter=0;
while (Eng_cfg->GetValue() != string("/FG_SIMTURBINE")) {
*Eng_cfg >> token;
if (token[0] == '<') token.erase(0,1); // Tables are read "<TABLE"
if (token == "MILTHRUST") *Eng_cfg >> MilThrust;
else if (token == "MAXTHRUST") *Eng_cfg >> MaxThrust;
else if (token == "BYPASSRATIO") *Eng_cfg >> BypassRatio;
else if (token == "TSFC") *Eng_cfg >> TSFC;
else if (token == "ATSFC") *Eng_cfg >> ATSFC;
else if (token == "IDLEN1") *Eng_cfg >> IdleN1;
else if (token == "IDLEN2") *Eng_cfg >> IdleN2;
else if (token == "MAXN1") *Eng_cfg >> MaxN1;
else if (token == "MAXN2") *Eng_cfg >> MaxN2;
else if (token == "AUGMENTED") *Eng_cfg >> Augmented;
else if (token == "AUGMETHOD") *Eng_cfg >> AugMethod;
else if (token == "INJECTED") *Eng_cfg >> Injected;
else if (token == "MINTHROTTLE") *Eng_cfg >> MinThrottle;
else if (token == "TABLE") {
if (counter++ == 0) Debug(2); // print engine specs prior to table read
ThrustTables.push_back( new FGCoefficient(FDMExec) );
ThrustTables.back()->Load(Eng_cfg);
}
else cerr << "Unhandled token in Engine config file: " << token << endl;
}
// Pre-calculations and initializations
delay = 60.0 / (BypassRatio + 3.0);
N1_factor = MaxN1 - IdleN1;
N2_factor = MaxN2 - IdleN2;
OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
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 FGSimTurbine::Debug(int from)
{
if (debug_lvl <= 0) return;
if (debug_lvl & 1) { // Standard console startup message output
if (from == 0) { // Constructor
}
if (from == 2) { // called from Load()
cout << "\n Engine Name: " << Name << endl;
cout << " MilThrust: " << MilThrust << endl;
cout << " MaxThrust: " << MaxThrust << endl;
cout << " BypassRatio: " << BypassRatio << endl;
cout << " TSFC: " << TSFC << endl;
cout << " ATSFC: " << ATSFC << endl;
cout << " IdleN1: " << IdleN1 << endl;
cout << " IdleN2: " << IdleN2 << endl;
cout << " MaxN1: " << MaxN1 << endl;
cout << " MaxN2: " << MaxN2 << endl;
cout << " Augmented: " << Augmented << endl;
cout << " AugMethod: " << AugMethod << endl;
cout << " Injected: " << Injected << endl;
cout << " MinThrottle: " << MinThrottle << endl;
cout << endl;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGSimTurbine" << endl;
if (from == 1) cout << "Destroyed: FGSimTurbine" << 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
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -1,243 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGSimTurbine.h
Author: David Culp
Date started: 03/11/2003
------------- Copyright (C) 2003 David Culp (davidculp2@comcast.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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
03/11/2003 DPC Created, based on FGTurbine
09/22/2003 DPC Added starting, stopping, new framework
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGSIMTURBINE_H
#define FGSIMTURBINE_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include "FGEngine.h"
#include "FGConfigFile.h"
#include "FGCoefficient.h"
#define ID_SIMTURBINE "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** This class models a turbine engine. Based on Jon Berndt's FGTurbine module.
Here the term "phase" signifies the engine's mode of operation. At any given
time the engine is in only one phase. At simulator startup the engine will be
placed in the Trim phase in order to provide a simplified thrust value without
throttle lag. When trimming is complete the engine will go to the Off phase,
unless the value FGEngine::Running has been previously set to true, in which
case the engine will go to the Run phase. Once an engine is in the Off phase
the full starting procedure (or airstart) must be used to get it running.
<P>
- STARTING (on ground):
-# Set the control FGEngine::Starter to true. The engine will spin up to
a maximum of about %25 N2 (%5.2 N1). This simulates the action of a
pneumatic starter.
-# After reaching %15 N2 set the control FGEngine::Cutoff to false. If fuel
is available the engine will now accelerate to idle. The starter will
automatically be set to false after the start cycle.
<P>
- STARTING (in air):
-# Increase speed to obtain a minimum of %15 N2. If this is not possible,
the starter may be used to assist.
-# Place the control FGEngine::Cutoff to false.
<P>
Ignition is assumed to be on anytime the Cutoff control is set to false,
therefore a seperate ignition system is not modeled.
Configuration File Format
<pre>
\<FG_SIMTURBINE NAME="<name>">
MILTHRUST \<thrust>
MAXTHRUST \<thrust>
BYPASSRATIO \<bypass ratio>
TSFC \<thrust specific fuel consumption>
ATSFC \<afterburning thrust specific fuel consumption>
IDLEN1 \<idle N1>
IDLEN2 \<idle N2>
MAXN1 \<max N1>
MAXN2 \<max N2>
AUGMENTED \<0|1>
AUGMETHOD \<0|1>
INJECTED \<0|1>
...
\</FG_SIMTURBINE>
</pre>
Definition of the turbine engine configuration file parameters:
<pre>
<b>MILTHRUST</b> - Maximum thrust, static, at sea level, lbf.
<b>MAXTHRUST</b> - Afterburning thrust, static, at sea level, lbf
[this value will be ignored when AUGMENTED is zero (false)].
<b>BYPASSRATIO</b> - Ratio of bypass air flow to core air flow.
<b>TSFC</b> - Thrust-specific fuel consumption, lbm/hr/lbf
[i.e. fuel flow divided by thrust].
<b>ATSFC</b> - Afterburning TSFC, lbm/hr/lbf
[this value will be ignored when AUGMENTED is zero (false)]
<b>IDLEN1</b> - Fan rotor rpm (% of max) at idle
<b>IDLEN2</b> - Core rotor rpm (% of max) at idle
<b>MAXN1</b> - Fan rotor rpm (% of max) at full throttle [not always 100!]
<b>MAXN2</b> - Core rotor rpm (% of max) at full throttle [not always 100!]
<b>AUGMENTED</b>
0 == afterburner not installed
1 == afterburner installed
<b>AUGMETHOD</b>
0 == afterburner activated by property /engines/engine[n]/augmentation
1 == afterburner activated by pushing throttle above 99% position
[this item will be ignored when AUGMENTED == 0]
<b>INJECTED</b>
0 == Water injection not installed
1 == Water injection installed
</pre>
@author David P. Culp
@version "$Id$"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGSimTurbine : public FGEngine
{
public:
/** Constructor
@param Executive pointer to executive structure
@param Eng_cfg pointer to engine config file instance */
FGSimTurbine(FGFDMExec* Executive, FGConfigFile* Eng_cfg);
/// Destructor
~FGSimTurbine();
enum phaseType { tpOff, tpRun, tpSpinUp, tpStart, tpStall, tpSeize, tpTrim };
double Calculate(double PowerRequired);
double CalcFuelNeed(void);
double GetPowerAvailable(void);
double Seek(double* var, double target, double accel, double decel);
phaseType GetPhase(void) { return phase; }
bool GetOvertemp(void) {return Overtemp; }
bool GetInjection(void) {return Injection;}
bool GetFire(void) { return Fire; }
bool GetAugmentation(void) {return Augmentation;}
bool GetReversed(void) { return Reversed; }
bool GetCutoff(void) { return Cutoff; }
int GetIgnition(void) {return Ignition;}
double GetInlet(void) { return InletPosition; }
double GetNozzle(void) { return NozzlePosition; }
double GetBleedDemand(void) {return BleedDemand;}
double GetN1(void) {return N1;}
double GetN2(void) {return N2;}
double GetEPR(void) {return EPR;}
double GetEGT(void) {return EGT_degC;}
double getOilPressure_psi () const {return OilPressure_psi;}
double getOilTemp_degF (void) {return KelvinToFahrenheit(OilTemp_degK);}
void SetInjection(bool injection) {Injection = injection;}
void SetIgnition(int ignition) {Ignition = ignition;}
void SetAugmentation(bool augmentation) {Augmentation = augmentation;}
void SetPhase( phaseType p ) { phase = p; }
void SetEPR(double epr) {EPR = epr;}
void SetBleedDemand(double bleedDemand) {BleedDemand = bleedDemand;}
void SetReverse(bool reversed) { Reversed = reversed; }
void SetCutoff(bool cutoff) { Cutoff = cutoff; }
private:
typedef vector<FGCoefficient*> CoeffArray;
CoeffArray ThrustTables;
phaseType phase; ///< Operating mode, or "phase"
double MilThrust; ///< Maximum Unaugmented Thrust, static @ S.L. (lbf)
double MaxThrust; ///< Maximum Augmented Thrust, static @ S.L. (lbf)
double BypassRatio; ///< Bypass Ratio
double TSFC; ///< Thrust Specific Fuel Consumption (lbm/hr/lbf)
double ATSFC; ///< Augmented TSFC (lbm/hr/lbf)
double IdleN1; ///< Idle N1
double IdleN2; ///< Idle N2
double N1; ///< N1
double N2; ///< N2
double MaxN1; ///< N1 at 100% throttle
double MaxN2; ///< N2 at 100% throttle
double IdleFF; ///< Idle Fuel Flow (lbm/hr)
double delay; ///< Inverse spool-up time from idle to 100% (seconds)
double dt; ///< Simulator time slice
double N1_factor; ///< factor to tie N1 and throttle
double N2_factor; ///< factor to tie N2 and throttle
double ThrottleCmd; ///< FCS-supplied throttle position
double TAT; ///< total air temperature (deg C)
bool Stalled; ///< true if engine is compressor-stalled
bool Seized; ///< true if inner spool is seized
bool Overtemp; ///< true if EGT exceeds limits
bool Fire; ///< true if engine fire detected
bool Injection;
bool Augmentation;
bool Reversed;
bool Cutoff;
int Injected; ///< = 1 if water injection installed
int Ignition;
int Augmented; ///< = 1 if augmentation installed
int AugMethod; ///< = 0 if using property /engine[n]/augmentation
///< = 1 if using last 1% of throttle movement
double EGT_degC;
double EPR;
double OilPressure_psi;
double OilTemp_degK;
double BleedDemand;
double InletPosition;
double NozzlePosition;
double Off(void);
double Run(void);
double SpinUp(void);
double Start(void);
double Stall(void);
double Seize(void);
double Trim(void);
void SetDefaults(void);
bool Load(FGConfigFile *ENG_cfg);
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -47,8 +47,8 @@ INCLUDES
# endif
#endif
#ifdef _MSC_VER
#define snprintf _snprintf
#ifdef _WIN32
//#define snprintf _snprintf
#endif
#include "FGState.h"
@ -74,9 +74,8 @@ FGState::FGState(FGFDMExec* fdex)
dt = 1.0/120.0;
Aircraft = FDMExec->GetAircraft();
Translation = FDMExec->GetTranslation();
Rotation = FDMExec->GetRotation();
Position = FDMExec->GetPosition();
Propagate = FDMExec->GetPropagate();
Auxiliary = FDMExec->GetAuxiliary();
FCS = FDMExec->GetFCS();
Output = FDMExec->GetOutput();
Atmosphere = FDMExec->GetAtmosphere();
@ -85,8 +84,6 @@ FGState::FGState(FGFDMExec* fdex)
Propulsion = FDMExec->GetPropulsion();
PropertyManager = FDMExec->GetPropertyManager();
for(int i=0;i<4;i++) vQdot_prev[i].InitMatrix();
bind();
Debug(0);
@ -100,39 +97,23 @@ FGState::~FGState()
Debug(1);
}
//***************************************************************************
//
// Initialize: Assume all angles GIVEN IN RADIANS !!
//
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGState::Initialize(double U, double V, double W,
double phi, double tht, double psi,
double Latitude, double Longitude, double H,
double wnorth, double weast, double wdown)
void FGState::Initialize(FGInitialCondition *FGIC)
{
double alpha, beta;
double qbar, Vt;
FGColumnVector3 vAeroUVW;
FGColumnVector3 vUVW;
sim_time = 0.0;
Position->SetLatitude(Latitude);
Position->SetLongitude(Longitude);
Position->Seth(H);
Propagate->SetInitialState( FGIC );
Atmosphere->Run();
Atmosphere->SetWindNED( FGIC->GetWindNFpsIC(),
FGIC->GetWindEFpsIC(),
FGIC->GetWindDFpsIC() );
vLocalEuler << phi << tht << psi;
Rotation->SetEuler(vLocalEuler);
InitMatrices(phi, tht, psi);
vUVW << U << V << W;
Translation->SetUVW(vUVW);
Atmosphere->SetWindNED(wnorth, weast, wdown);
vAeroUVW = vUVW + mTl2b*Atmosphere->GetWindNED();
FGColumnVector3 vAeroUVW;
vAeroUVW = Propagate->GetUVW() + Propagate->GetTl2b()*Atmosphere->GetWindNED();
double alpha, beta;
if (vAeroUVW(eW) != 0.0)
alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
else
@ -142,148 +123,15 @@ void FGState::Initialize(double U, double V, double W,
else
beta = 0.0;
Translation->SetAB(alpha, beta);
Auxiliary->SetAB(alpha, beta);
Vt = sqrt(U*U + V*V + W*W);
Translation->SetVt(Vt);
double Vt = vAeroUVW.Magnitude();
Auxiliary->SetVt(Vt);
Translation->SetMach(Vt/Atmosphere->GetSoundSpeed());
Auxiliary->SetMach(Vt/Atmosphere->GetSoundSpeed());
qbar = 0.5*(U*U + V*V + W*W)*Atmosphere->GetDensity();
Translation->Setqbar(qbar);
vLocalVelNED = mTb2l*vUVW;
Position->SetvVel(vLocalVelNED);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGState::Initialize(FGInitialCondition *FGIC)
{
double tht,psi,phi;
double U, V, W, h;
double latitude, longitude;
double wnorth,weast, wdown;
latitude = FGIC->GetLatitudeRadIC();
longitude = FGIC->GetLongitudeRadIC();
h = FGIC->GetAltitudeFtIC();
U = FGIC->GetUBodyFpsIC();
V = FGIC->GetVBodyFpsIC();
W = FGIC->GetWBodyFpsIC();
tht = FGIC->GetThetaRadIC();
phi = FGIC->GetPhiRadIC();
psi = FGIC->GetPsiRadIC();
wnorth = FGIC->GetWindNFpsIC();
weast = FGIC->GetWindEFpsIC();
wdown = FGIC->GetWindDFpsIC();
Position->SetSeaLevelRadius( FGIC->GetSeaLevelRadiusFtIC() );
Position->SetRunwayRadius( FGIC->GetSeaLevelRadiusFtIC() +
FGIC->GetTerrainAltitudeFtIC() );
// need to fix the wind speed args, here.
Initialize(U, V, W, phi, tht, psi, latitude, longitude, h, wnorth, weast, wdown);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGState::InitMatrices(double phi, double tht, double psi)
{
double thtd2, psid2, phid2;
double Sthtd2, Spsid2, Sphid2;
double Cthtd2, Cpsid2, Cphid2;
double Cphid2Cthtd2;
double Cphid2Sthtd2;
double Sphid2Sthtd2;
double Sphid2Cthtd2;
thtd2 = tht/2.0;
psid2 = psi/2.0;
phid2 = phi/2.0;
Sthtd2 = sin(thtd2);
Spsid2 = sin(psid2);
Sphid2 = sin(phid2);
Cthtd2 = cos(thtd2);
Cpsid2 = cos(psid2);
Cphid2 = cos(phid2);
Cphid2Cthtd2 = Cphid2*Cthtd2;
Cphid2Sthtd2 = Cphid2*Sthtd2;
Sphid2Sthtd2 = Sphid2*Sthtd2;
Sphid2Cthtd2 = Sphid2*Cthtd2;
vQtrn(1) = Cphid2Cthtd2*Cpsid2 + Sphid2Sthtd2*Spsid2;
vQtrn(2) = Sphid2Cthtd2*Cpsid2 - Cphid2Sthtd2*Spsid2;
vQtrn(3) = Cphid2Sthtd2*Cpsid2 + Sphid2Cthtd2*Spsid2;
vQtrn(4) = Cphid2Cthtd2*Spsid2 - Sphid2Sthtd2*Cpsid2;
CalcMatrices();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGState::CalcMatrices(void)
{
double Q0Q0, Q1Q1, Q2Q2, Q3Q3;
double Q0Q1, Q0Q2, Q0Q3, Q1Q2;
double Q1Q3, Q2Q3;
Q0Q0 = vQtrn(1)*vQtrn(1);
Q1Q1 = vQtrn(2)*vQtrn(2);
Q2Q2 = vQtrn(3)*vQtrn(3);
Q3Q3 = vQtrn(4)*vQtrn(4);
Q0Q1 = vQtrn(1)*vQtrn(2);
Q0Q2 = vQtrn(1)*vQtrn(3);
Q0Q3 = vQtrn(1)*vQtrn(4);
Q1Q2 = vQtrn(2)*vQtrn(3);
Q1Q3 = vQtrn(2)*vQtrn(4);
Q2Q3 = vQtrn(3)*vQtrn(4);
mTl2b(1,1) = Q0Q0 + Q1Q1 - Q2Q2 - Q3Q3;
mTl2b(1,2) = 2*(Q1Q2 + Q0Q3);
mTl2b(1,3) = 2*(Q1Q3 - Q0Q2);
mTl2b(2,1) = 2*(Q1Q2 - Q0Q3);
mTl2b(2,2) = Q0Q0 - Q1Q1 + Q2Q2 - Q3Q3;
mTl2b(2,3) = 2*(Q2Q3 + Q0Q1);
mTl2b(3,1) = 2*(Q1Q3 + Q0Q2);
mTl2b(3,2) = 2*(Q2Q3 - Q0Q1);
mTl2b(3,3) = Q0Q0 - Q1Q1 - Q2Q2 + Q3Q3;
mTb2l = mTl2b;
mTb2l.T();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGState::IntegrateQuat(FGColumnVector3 vPQR, int rate)
{
vQdot(1) = -0.5*(vQtrn(2)*vPQR(eP) + vQtrn(3)*vPQR(eQ) + vQtrn(4)*vPQR(eR));
vQdot(2) = 0.5*(vQtrn(1)*vPQR(eP) + vQtrn(3)*vPQR(eR) - vQtrn(4)*vPQR(eQ));
vQdot(3) = 0.5*(vQtrn(1)*vPQR(eQ) + vQtrn(4)*vPQR(eP) - vQtrn(2)*vPQR(eR));
vQdot(4) = 0.5*(vQtrn(1)*vPQR(eR) + vQtrn(2)*vPQR(eQ) - vQtrn(3)*vPQR(eP));
vQtrn += Integrate(TRAPZ, dt*rate, vQdot, vQdot_prev);
vQtrn.Normalize();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3& FGState::CalcEuler(void)
{
if (mTl2b(3,3) == 0.0) mTl2b(3,3) = 0.0000001;
if (mTl2b(1,1) == 0.0) mTl2b(1,1) = 0.0000001;
vEuler(ePhi) = atan2(mTl2b(2,3), mTl2b(3,3));
vEuler(eTht) = asin(-mTl2b(1,3));
vEuler(ePsi) = atan2(mTl2b(1,2), mTl2b(1,1));
if (vEuler(ePsi) < 0.0) vEuler(ePsi) += 2*M_PI;
return vEuler;
double qbar = 0.5*Vt*Vt*Atmosphere->GetDensity();
Auxiliary->Setqbar(qbar);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -292,8 +140,8 @@ FGMatrix33& FGState::GetTs2b(void)
{
double ca, cb, sa, sb;
double alpha = Translation->Getalpha();
double beta = Translation->Getbeta();
double alpha = Auxiliary->Getalpha();
double beta = Auxiliary->Getbeta();
ca = cos(alpha);
sa = sin(alpha);
@ -320,8 +168,8 @@ FGMatrix33& FGState::GetTb2s(void)
float alpha,beta;
float ca, cb, sa, sb;
alpha = Translation->Getalpha();
beta = Translation->Getbeta();
alpha = Auxiliary->Getalpha();
beta = Auxiliary->Getbeta();
ca = cos(alpha);
sa = sin(alpha);
@ -345,6 +193,7 @@ FGMatrix33& FGState::GetTb2s(void)
void FGState::ReportState(void)
{
#if 0
#if !defined(__BORLANDCPP__)
char out[80], flap[10], gear[12];
@ -370,33 +219,33 @@ void FGState::ReportState(void)
snprintf(out,80, " Flaps: %3s Gear: %12s\n",flap,gear);
cout << out;
snprintf(out,80, " Speed: %4.0f KCAS Mach: %5.2f\n",
FDMExec->GetAuxiliary()->GetVcalibratedKTS(),
Translation->GetMach() );
Auxiliary->GetVcalibratedKTS(),
Auxiliary->GetMach() );
cout << out;
snprintf(out,80, " Altitude: %7.0f ft. AGL Altitude: %7.0f ft.\n",
Position->Geth(),
Position->GetDistanceAGL() );
Propagate->Geth(),
Propagate->GetDistanceAGL() );
cout << out;
snprintf(out,80, " Angle of Attack: %6.2f deg Pitch Angle: %6.2f deg\n",
Translation->Getalpha()*radtodeg,
Rotation->Gettht()*radtodeg );
Auxiliary->Getalpha()*radtodeg,
Propagate->Gettht()*radtodeg );
cout << out;
snprintf(out,80, " Flight Path Angle: %6.2f deg Climb Rate: %5.0f ft/min\n",
Position->GetGamma()*radtodeg,
Position->Gethdot()*60 );
Auxiliary->GetGamma()*radtodeg,
Propagate->Gethdot()*60 );
cout << out;
snprintf(out,80, " Normal Load Factor: %4.2f g's Pitch Rate: %5.2f deg/s\n",
Aircraft->GetNlf(),
Rotation->GetPQR(2)*radtodeg );
Propagate->GetPQR(2)*radtodeg );
cout << out;
snprintf(out,80, " Heading: %3.0f deg true Sideslip: %5.2f deg Yaw Rate: %5.2f deg/s\n",
Rotation->Getpsi()*radtodeg,
Translation->Getbeta()*radtodeg,
Rotation->GetPQR(3)*radtodeg );
Propagate->Getpsi()*radtodeg,
Auxiliary->Getbeta()*radtodeg,
Propagate->GetPQR(3)*radtodeg );
cout << out;
snprintf(out,80, " Bank Angle: %5.2f deg Roll Rate: %5.2f deg/s\n",
Rotation->Getphi()*radtodeg,
Rotation->GetPQR(1)*radtodeg );
Propagate->Getphi()*radtodeg,
Propagate->GetPQR(1)*radtodeg );
cout << out;
snprintf(out,80, " Elevator: %5.2f deg Left Aileron: %5.2f deg Rudder: %5.2f deg\n",
FCS->GetDePos(ofRad)*radtodeg,
@ -408,15 +257,16 @@ void FGState::ReportState(void)
cout << out;
snprintf(out,80, " Wind Components: %5.2f kts head wind, %5.2f kts cross wind\n",
FDMExec->GetAuxiliary()->GetHeadWind()*fpstokts,
FDMExec->GetAuxiliary()->GetCrossWind()*fpstokts );
Auxiliary->GetHeadWind()*fpstokts,
Auxiliary->GetCrossWind()*fpstokts );
cout << out;
snprintf(out,80, " Ground Speed: %4.0f knots , Ground Track: %3.0f deg true\n",
Position->GetVground()*fpstokts,
Position->GetGroundTrack()*radtodeg );
Auxiliary->GetVground()*fpstokts,
Auxiliary->GetGroundTrack()*radtodeg );
cout << out;
#endif
#endif
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -62,14 +62,12 @@ INCLUDES
#include "FGInitialCondition.h"
#include "FGMatrix33.h"
#include "FGColumnVector3.h"
#include "FGColumnVector4.h"
#include "FGQuaternion.h"
#include "FGFDMExec.h"
#include "FGAtmosphere.h"
#include "FGFCS.h"
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGPosition.h"
#include "FGPropagate.h"
#include "FGAuxiliary.h"
#include "FGAerodynamics.h"
#include "FGOutput.h"
#include "FGAircraft.h"
@ -110,33 +108,6 @@ public:
/// Destructor
~FGState();
/** Initializes the simulation state based on the passed-in parameters.
@param U the body X-Axis velocity in fps.
@param V the body Y-Axis velocity in fps.
@param W the body Z-Axis velocity in fps.
@param lat latitude measured in radians from the equator, negative values are south.
@param lon longitude, measured in radians from the Greenwich meridian, negative values are west.
@param phi the roll angle in radians.
@param tht the pitch angle in radians.
@param psi the heading angle in radians measured clockwise from north.
@param h altitude in feet.
@param wnorth north velocity in feet per second
@param weast eastward velocity in feet per second
@param wdown downward velocity in feet per second
*/
void Initialize(double U,
double V,
double W,
double lat,
double lon,
double phi,
double tht,
double psi,
double h,
double wnorth,
double weast,
double wdown);
/** Initializes the simulation state based on parameters from an Initial Conditions object.
@param FGIC pointer to an initial conditions object.
@see FGInitialConditions.
@ -175,115 +146,6 @@ public:
return sim_time;
}
/** Initializes the transformation matrices.
@param phi the roll angle in radians.
@param tht the pitch angle in radians.
@param psi the heading angle in radians
*/
void InitMatrices(double phi, double tht, double psi);
/** Calculates the local-to-body and body-to-local conversion matrices.
*/
void CalcMatrices(void);
/** Integrates the quaternion.
Given the supplied rotational rate vector and integration rate, the quaternion
is integrated. The quaternion is later used to update the transformation
matrices.
@param vPQR the body rotational rate column vector.
@param rate the integration rate in seconds.
*/
void IntegrateQuat(FGColumnVector3 vPQR, int rate);
// ======================================= General Purpose INTEGRATOR
enum iType {AB4, AB3, AB2, AM3, AM4, EULER, TRAPZ};
/** Multi-method integrator.
@param type Type of intergation scheme to use. Can be one of:
<ul>
<li>AB4 - Adams-Bashforth, fourth order</li>
<li>AB3 - Adams-Bashforth, third order</li>
<li>AB2 - Adams-Bashforth, second order</li>
<li>AM3 - Adams Moulton, third order</li>
<li>AM4 - Adams Moulton, fourth order</li>
<li>EULER - Euler</li>
<li>TRAPZ - Trapezoidal</li>
</ul>
@param delta_t the integration time step in seconds
@param vTDeriv a reference to the current value of the time derivative of
the quantity being integrated (i.e. if vUVW is being integrated
vTDeriv is the current value of vUVWdot)
@param vLastArray an array of previously calculated and saved values of
the quantity being integrated (i.e. if vUVW is being integrated
vLastArray[0] is the past value of vUVWdot, vLastArray[1] is the value of
vUVWdot prior to that, etc.)
@return the current, incremental value of the item integrated to add to the
previous value. */
template <class T> T Integrate(iType type, double delta_t, T& vTDeriv, T *vLastArray)
{
T vResult;
switch (type) {
case AB4:
vResult = (delta_t/24.0)*( 55.0 * vLastArray[0]
- 59.0 * vLastArray[1]
+ 37.0 * vLastArray[2]
- 9.0 * vLastArray[3] );
vLastArray[3] = vLastArray[2];
vLastArray[2] = vLastArray[1];
vLastArray[1] = vLastArray[0];
vLastArray[0] = vTDeriv;
break;
case AB3:
vResult = (delta_t/12.0)*( 23.0 * vLastArray[0]
- 16.0 * vLastArray[1]
+ 5.0 * vLastArray[2] );
vLastArray[2] = vLastArray[1];
vLastArray[1] = vLastArray[0];
vLastArray[0] = vTDeriv;
break;
case AB2:
vResult = (delta_t/2.0)*( 3.0 * vLastArray[0] - vLastArray[1] );
vLastArray[1] = vLastArray[0];
vLastArray[0] = vTDeriv;
break;
case AM4:
vResult = (delta_t/24.0)*( 9.0 * vTDeriv
+ 19.0 * vLastArray[0]
- 5.0 * vLastArray[1]
+ 1.0 * vLastArray[2] );
vLastArray[2] = vLastArray[1];
vLastArray[1] = vLastArray[0];
vLastArray[0] = vTDeriv;
break;
case AM3:
vResult = (delta_t/12.0)*( 5.0 * vTDeriv
+ 8.0 * vLastArray[0]
- 1.0 * vLastArray[1] );
vLastArray[1] = vLastArray[0];
vLastArray[0] = vTDeriv;
break;
case EULER:
vResult = delta_t * vTDeriv;
break;
case TRAPZ:
vResult = (delta_t*0.5) * (vTDeriv + vLastArray[0]);
vLastArray[0] = vTDeriv;
break;
}
return vResult;
}
// =======================================
/** Calculates Euler angles from the local-to-body matrix.
@return a reference to the vEuler column vector.
*/
FGColumnVector3& CalcEuler(void);
/** Calculates and returns the stability-to-body axis transformation matrix.
@return a reference to the stability-to-body transformation matrix.
*/
@ -294,30 +156,6 @@ public:
*/
FGMatrix33& GetTb2s(void);
/** Retrieves the local-to-body transformation matrix.
@return a reference to the local-to-body transformation matrix.
*/
FGMatrix33& GetTl2b(void) { return mTl2b; }
/** Retrieves a specific local-to-body matrix element.
@param r matrix row index.
@param c matrix column index.
@return the matrix element described by the row and column supplied.
*/
double GetTl2b(int r, int c) { return mTl2b(r,c);}
/** Retrieves the body-to-local transformation matrix.
@return a reference to the body-to-local matrix.
*/
FGMatrix33& GetTb2l(void) { return mTb2l; }
/** Retrieves a specific body-to-local matrix element.
@param r matrix row index.
@param c matrix column index.
@return the matrix element described by the row and column supplied.
*/
double GetTb2l(int i, int j) { return mTb2l(i,j);}
/** Prints a summary of simulator state (speed, altitude,
configuration, etc.)
*/
@ -331,29 +169,18 @@ private:
double saved_dt;
FGFDMExec* FDMExec;
FGMatrix33 mTb2l;
FGMatrix33 mTl2b;
FGMatrix33 mTs2b;
FGMatrix33 mTb2s;
FGColumnVector4 vQtrn;
FGColumnVector4 vQdot_prev[4];
FGColumnVector4 vQdot;
FGColumnVector3 vLocalVelNED;
FGColumnVector3 vLocalEuler;
FGColumnVector4 vTmp;
FGColumnVector3 vEuler;
FGAircraft* Aircraft;
FGPosition* Position;
FGTranslation* Translation;
FGRotation* Rotation;
FGPropagate* Propagate;
FGOutput* Output;
FGAtmosphere* Atmosphere;
FGFCS* FCS;
FGAerodynamics* Aerodynamics;
FGGroundReactions* GroundReactions;
FGPropulsion* Propulsion;
FGAuxiliary* Auxiliary;
FGPropertyManager* PropertyManager;
void Debug(int from);

View file

@ -56,6 +56,22 @@ CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGTable::FGTable(int NRows, int NCols, int NTables)
: nRows(NTables), nCols(1), nTables(NTables)
{
Type = tt3D;
colCounter = 1;
rowCounter = 1;
Data = Allocate(); // this data array will contain the keys for the associated tables
Tables.reserve(nTables);
for (int i=0; i<nTables; i++) Tables.push_back(FGTable(NRows, NCols));
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGTable::FGTable(int NRows, int NCols) : nRows(NRows), nCols(NCols)
{
if (NCols > 1) {
@ -90,6 +106,31 @@ FGTable::FGTable(int NRows) : nRows(NRows), nCols(1)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGTable::FGTable(const FGTable& t)
{
Type = t.Type;
colCounter = t.colCounter;
rowCounter = t.rowCounter;
tableCounter = t.tableCounter;
nRows = t.nRows;
nCols = t.nCols;
nTables = t.nTables;
Tables = t.Tables;
Data = Allocate();
for (int r=0; r<=nRows; r++) {
for (int c=0; c<=nCols; c++) {
Data[r][c] = t.Data[r][c];
}
}
lastRowIndex = t.lastRowIndex;
lastColumnIndex = t.lastColumnIndex;
lastTableIndex = t.lastTableIndex;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double** FGTable::Allocate(void)
{
Data = new double*[nRows+1];
@ -106,6 +147,7 @@ double** FGTable::Allocate(void)
FGTable::~FGTable()
{
if (nTables > 0) Tables.clear();
for (int r=0; r<=nRows; r++) if (Data[r]) delete[] Data[r];
if (Data) delete[] Data;
Debug(1);
@ -202,17 +244,68 @@ double FGTable::GetValue(double rowKey, double colKey)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTable::GetValue(double rowKey, double colKey, double tableKey)
{
double Factor, Value, Span;
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
// assume the correct breakpoint has not changed since last frame or
// has only changed very little
if ( r > 2 && Data[r-1][1] > tableKey ) {
while( Data[r-1][1] > tableKey && r > 2) { r--; }
} else if ( Data[r][1] < tableKey ) {
while( Data[r][1] <= tableKey && r <= nRows) { 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-1].GetValue(rowKey, colKey);
return Value;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTable::operator<<(FGConfigFile& infile)
{
int startRow;
int startRow=0;
int startCol=0;
int tableCtr=0;
if (Type == tt1D) startRow = 1;
else startRow = 0;
if (Type == tt1D || Type == tt3D) startRow = 1;
if (Type == tt3D) startCol = 1;
for (int r=startRow; r<=nRows; r++) {
for (int c=0; c<=nCols; c++) {
for (int c=startCol; c<=nCols; c++) {
if (r != 0 || c != 0) {
infile >> Data[r][c];
if (Type == tt3D) {
Tables[tableCtr] << infile;
tableCtr++;
}
}
}
}
@ -244,10 +337,11 @@ FGTable& FGTable::operator<<(const int n)
void FGTable::Print(void)
{
int startRow;
int startRow=0;
int startCol=0;
if (Type == tt1D) startRow = 1;
else startRow = 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);
@ -256,14 +350,17 @@ void FGTable::Print(void)
#endif
cout.precision(4);
for (int r=startRow; r<=nRows; r++) {
cout << " ";
for (int c=0; c<=nCols; c++) {
for (int c=startCol; c<=nCols; c++) {
if (r == 0 && c == 0) {
cout << " ";
cout << " ";
} else {
cout << Data[r][c] << " ";
cout << Data[r][c] << " ";
if (Type == tt3D) {
cout << endl;
Tables[r-1].Print();
}
}
}
cout << endl;

View file

@ -40,6 +40,7 @@ INCLUDES
#include "FGConfigFile.h"
#include "FGJSBBase.h"
#include <vector>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
@ -51,6 +52,8 @@ DEFINITIONS
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
using std::vector;
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -58,7 +61,134 @@ CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Lookup table class.
Models a lookup table for use in FGCoefficient, FGPropeller, etc.
Models a one, two, or three dimensional lookup table for use in FGCoefficient,
FGPropeller, etc. A one-dimensional table is called a "VECTOR" in a coefficient
definition. For example:
<pre>
\<COEFFICIENT NAME="{short name}" TYPE="VECTOR">
{name}
{number of rows}
{row lookup property}
{non-dimensionalizing properties}
{row_1_key} {col_1_data}
{row_2_key} {... }
{ ... } {... }
{row_n_key} {... }
\</COEFFICIENT>
</pre>
A "real life" example is as shown here:
<pre>
\<COEFFICIENT NAME="CLDf" TYPE="VECTOR">
Delta_lift_due_to_flap_deflection
4
fcs/flap-pos-deg
aero/qbar-psf | metrics/Sw-sqft
0 0
10 0.20
20 0.30
30 0.35
\</COEFFICIENT>
</pre>
The first column in the data table represents the lookup index (or "key"). In
this case, the lookup index is fcs/flap-pos-deg (flap extension in degrees).
If the flap position is 10 degrees, the value returned from the lookup table
would be 0.20. This value would be multiplied by qbar (aero/qbar-psf) and wing
area (metrics/Sw-sqft) to get the total lift force that is a result of flap
deflection (measured in pounds force). If the value of the flap-pos-deg property
was 15 (degrees), the value output by the table routine would be 0.25 - an
interpolation. If the flap position in degrees ever went below 0.0, or above
30 (degrees), the output from the table routine would be 0 and 0.35, respectively.
That is, there is no _extrapolation_ to values outside the range of the lookup
index. This is why it is important to chose the data for the table wisely.
The definition for a 2D table - referred to simply as a TABLE, is as follows:
<pre>
\<COEFFICIENT NAME="{short name}" TYPE="TABLE">
{name}
{number of rows}
{number of columns}
{row lookup property}
{column lookup property}
{non-dimensionalizing}
{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} {... ... ... ... }
\</COEFFICIENT>
</pre>
A "real life" example is as shown here:
<pre>
\<COEFFICIENT NAME="CYb" TYPE="TABLE">
Side_force_due_to_beta
3
2
aero/beta-rad
fcs/flap-pos-deg
aero/qbar-psf | metrics/Sw-sqft
0 30
-0.349 0.137 0.106
0 0 0
0.349 -0.137 -0.106
\</COEFFICIENT>
</pre>
The definition for a 3D table in a coefficient would be (for example):
<pre>
\<COEFFICIENT NAME="{short name}" TYPE="TABLE3D">
{name}
{number of rows}
{number of columns}
{number of tables}
{row lookup property}
{column lookup property}
{table lookup property}
{non-dimensionalizing}
{first table 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} {... ... ... ... }
{second table 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} {... ... ... ... }
...
\</COEFFICIENT>
</pre>
[At the present time, all rows and columns for each table must have the
same dimension.]
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:
<pre>
// 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;
</pre>
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:
combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio);
@author Jon S. Berndt
@version $Id$
@see FGCoefficient
@ -72,11 +202,21 @@ CLASS DECLARATION
class FGTable : 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 VECTOR table
@param nRows the number of rows in this VECTOR table. */
FGTable(int nRows);
FGTable(int nRows, int nCols);
FGTable(int nRows, int nCols, int numTables);
double GetValue(double key);
double GetValue(double rowKey, double colKey);
double GetValue(double rowKey, double colKey, double TableKey);
/** 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
@ -87,20 +227,32 @@ public:
-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<<(FGConfigFile&);
FGTable& operator<<(const double n);
FGTable& operator<<(const int n);
inline double GetElement(int r, int c) {return Data[r][c];}
inline double GetElement(int r, int c, int t);
void Print(void);
private:
enum type {tt1D, tt2D} Type;
enum type {tt1D, tt2D, tt3D} Type;
double** Data;
int nRows, nCols;
int colCounter;
int rowCounter;
int lastRowIndex, lastColumnIndex;
vector <FGTable> Tables;
int nRows, nCols, nTables;
int colCounter, rowCounter, tableCounter;
int lastRowIndex, lastColumnIndex, lastTableIndex;
double** Allocate(void);
void Debug(int from);
};

View file

@ -53,10 +53,13 @@ static const char *IdHdr = ID_TANK;
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGTank::FGTank(FGConfigFile* AC_cfg)
FGTank::FGTank(FGConfigFile* AC_cfg, FGFDMExec* exec)
{
string token;
double X, Y, Z;
Area = 1.0;
Temperature = -9999.0;
Auxiliary = exec->GetAuxiliary();
type = AC_cfg->GetValue("TYPE");
@ -72,6 +75,7 @@ FGTank::FGTank(FGConfigFile* AC_cfg)
else if (token == "RADIUS") *AC_cfg >> Radius;
else if (token == "CAPACITY") *AC_cfg >> Capacity;
else if (token == "CONTENTS") *AC_cfg >> Contents;
else if (token == "TEMPERATURE") *AC_cfg >> Temperature;
else cerr << "Unknown identifier: " << token << " in tank definition." << endl;
}
@ -86,6 +90,9 @@ FGTank::FGTank(FGConfigFile* AC_cfg)
PctFull = 0;
}
if (Temperature != -9999.0) Temperature = FahrenheitToCelsius(Temperature);
Area = 40.0 * pow(Capacity/1975, 0.666666667);
Debug(0);
}
@ -98,7 +105,7 @@ FGTank::~FGTank()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTank::Reduce(double used)
double FGTank::Drain(double used)
{
double shortage = Contents - used;
@ -113,6 +120,53 @@ double FGTank::Reduce(double used)
return shortage;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTank::Fill(double amount)
{
double overage = 0.0;
Contents += amount;
if (Contents > Capacity) {
overage = Contents - Capacity;
Contents = Capacity;
PctFull = 100.0;
} else {
PctFull = Contents/Capacity*100.0;
}
return overage;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTank::SetContents(double amount)
{
Contents = amount;
if (Contents > Capacity) {
Contents = Capacity;
PctFull = 100.0;
} else {
PctFull = Contents/Capacity*100.0;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTank::Calculate(double dt)
{
if (Temperature == -9999.0) return 0.0;
double HeatCapacity = 900.0; // Joules/lbm/C
double TempFlowFactor = 1.115; // Watts/sqft/C
double TAT = Auxiliary->GetTAT_C();
double Tdiff = TAT - Temperature;
double dT = 0.0; // Temp change due to one surface
if (fabs(Tdiff) > 0.1) {
dT = (TempFlowFactor * Area * Tdiff * dt) / (Contents * HeatCapacity);
}
return Temperature += (dT + dT); // For now, assume upper/lower the same
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -47,6 +47,7 @@ INCLUDES
#include "FGJSBBase.h"
#include "FGConfigFile.h"
#include "FGColumnVector3.h"
#include "FGAuxiliary.h"
#ifdef FGFS
# include <simgear/compiler.h>
@ -82,6 +83,48 @@ CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models a fuel tank.
@author Jon Berndt
@see Akbar, Raza et al. "A Simple Analysis of Fuel Addition to the CWT of
747", California Institute of Technology, 1998
<P>
Fuel temperature is calculated using the following assumptions:
<P>
Fuel temperature will only be calculated for tanks which have an initial fuel
temperature specified in the configuration file.
<P>
The surface area of the tank is estimated from the capacity in pounds. It
is assumed that the tank is a wing tank with dimensions h by 4h by 10h. The
volume of the tank is then 40(h)(h)(h). The area of the upper or lower
surface is then 40(h)(h). The volume is also equal to the capacity divided
by 49.368 lbs/cu-ft, for jet fuel. The surface area of one side can then be
derived from the tank's capacity.
<P>
The heat capacity of jet fuel is assumed to be 900 Joules/lbm/K, and the
heat transfer factor of the tank is 1.115 Watts/sq-ft/K.
<P>
Configuration File Format
<pre>
\<AC_TANK TYPE="\<FUEL | OXIDIZER>" NUMBER="\<n>">
XLOC \<x location>
YLOC \<y location>
ZLOC \<z location>
RADIUS \<radius>
CAPACITY \<capacity>
CONTENTS \<contents>
TEMPERATURE \<fuel temperature>
\</AC_TANK>
</pre>
Definition of the tank configuration file parameters:
<pre>
<b>TYPE</b> - One of FUEL or OXIDIZER.
<b>XLOC</b> - Location of tank on aircraft's x-axis, inches.
<b>YLOC</b> - Location of tank on aircraft's y-axis, inches.
<b>ZLOC</b> - Location of tank on aircraft's z-axis, inches.
<b>RADIUS</b> - Equivalent radius of tank, inches, for modeling slosh.
<b>CAPACITY</b> - Capacity in pounds.
<b>CONTENTS</b> - Initial contents in pounds.
<b>TEMPERATURE</b> - Initial temperature in degrees Fahrenheit.
</pre>
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -91,18 +134,23 @@ CLASS DECLARATION
class FGTank : public FGJSBBase
{
public:
FGTank(FGConfigFile*);
FGTank(FGConfigFile*, FGFDMExec*);
~FGTank();
double Reduce(double);
double Drain(double);
double Calculate(double dt);
int GetType(void) {return Type;}
bool GetSelected(void) {return Selected;}
double GetPctFull(void) {return PctFull;}
double GetContents(void) {return Contents;}
double GetTemperature_degC(void) {return Temperature;}
double GetTemperature(void) {return CelsiusToFahrenheit(Temperature);}
const FGColumnVector3& GetXYZ(void) {return vXYZ;}
double GetXYZ(int idx) {return vXYZ(idx);}
void SetContents(double contents) { Contents = contents; }
double Fill(double amount);
void SetContents(double amount);
void SetTemperature(double temp) { Temperature = temp; }
enum TankType {ttUNKNOWN, ttFUEL, ttOXIDIZER};
@ -114,7 +162,10 @@ private:
double Radius;
double PctFull;
double Contents;
double Area;
double Temperature;
bool Selected;
FGAuxiliary* Auxiliary;
void Debug(int from);
};
}

View file

@ -35,6 +35,8 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include "FGThruster.h"
namespace JSBSim {
@ -47,8 +49,7 @@ CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGThruster::FGThruster(FGFDMExec *FDMExec) : FGForce(FDMExec),
ThrusterNumber(0)
FGThruster::FGThruster(FGFDMExec *FDMExec) : FGForce(FDMExec)
{
Type = ttDirect;
SetTransformType(FGForce::tCustom);
@ -61,7 +62,6 @@ FGThruster::FGThruster(FGFDMExec *FDMExec) : FGForce(FDMExec),
FGThruster::FGThruster(FGFDMExec *FDMExec,
FGConfigFile *Eng_cfg ): FGForce(FDMExec)
{
ThrusterNumber = 0;
Type = ttDirect;
SetTransformType(FGForce::tCustom);
Name = Eng_cfg->GetValue();
@ -76,6 +76,28 @@ FGThruster::~FGThruster()
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGThruster::GetThrusterLabels(int id)
{
std::ostringstream buf;
buf << Name << "_Thrust[" << id << "]";
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGThruster::GetThrusterValues(int id)
{
std::ostringstream buf;
buf << Thrust;
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -40,6 +40,7 @@ INCLUDES
#include "FGForce.h"
#include "FGConfigFile.h"
#include <string>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
@ -79,21 +80,20 @@ public:
virtual double Calculate(double tt) { Thrust = tt; vFn(1) = Thrust; return 0.0; }
void SetName(string name) {Name = name;}
void SetThrusterNumber(int nn) {ThrusterNumber = nn;}
virtual void SetRPM(double rpm) {};
virtual double GetPowerRequired(void) {return 0.0;}
virtual void SetdeltaT(double dt) {deltaT = dt;}
double GetThrust(void) {return Thrust;}
eType GetType(void) {return Type;}
string GetName(void) {return Name;}
int GetThrusterNumber(void) {return ThrusterNumber;}
virtual double GetRPM(void) { return 0.0; };
double GetGearRatio(void) {return GearRatio; }
virtual string GetThrusterLabels(int id);
virtual string GetThrusterValues(int id);
protected:
eType Type;
string Name;
int ThrusterNumber;
double Thrust;
double PowerRequired;
double deltaT;

View file

@ -1,320 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGTranslation.cpp
Author: Jon Berndt
Date started: 12/02/98
Purpose: Integrates the translational EOM
Called by: FDMExec
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class integrates the translational EOM.
HISTORY
--------------------------------------------------------------------------------
12/02/98 JSB Created
7/23/99 TP Added data member and modified Run and PutState to calcuate
Mach number
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] 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
[2] D. M. Henderson, "Euler Angles, Quaternions, and Transformation Matrices",
JSC 12960, July 1977
[3] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
[4] Barnes W. McCormick, "Aerodynamics, Aeronautics, and Flight Mechanics",
Wiley & Sons, 1979 ISBN 0-471-03032-5
[5] Bernard Etkin, "Dynamics of Flight, Stability and Control", Wiley & Sons,
1982 ISBN 0-471-08936-2
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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGTranslation.h"
#include "FGRotation.h"
#include "FGAtmosphere.h"
#include "FGState.h"
#include "FGFDMExec.h"
#include "FGFCS.h"
#include "FGMassBalance.h"
#include "FGAircraft.h"
#include "FGPosition.h"
#include "FGAuxiliary.h"
#include "FGOutput.h"
#include "FGPropertyManager.h"
namespace JSBSim {
static const char *IdSrc = "$Id$";
static const char *IdHdr = ID_TRANSLATION;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGTranslation::FGTranslation(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGTranslation";
qbar = 0;
qbarUW = 0.0;
qbarUV = 0.0;
Vt = 0.0;
Mach = 0.0;
alpha = beta = 0.0;
adot = bdot = 0.0;
vUVWdot.InitMatrix();
vUVWdot_prev[0].InitMatrix();
vUVWdot_prev[1].InitMatrix();
vUVWdot_prev[2].InitMatrix();
vUVWdot_prev[3].InitMatrix();
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGTranslation::~FGTranslation(void)
{
unbind();
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGTranslation::Run(void)
{
if (!FGModel::Run()) {
vUVWdot = vUVW*Rotation->GetPQR() + Aircraft->GetBodyAccel();
vUVW += State->Integrate(FGState::TRAPZ, State->Getdt()*rate, vUVWdot, vUVWdot_prev);
vAeroUVW = vUVW + State->GetTl2b()*Atmosphere->GetWindNED();
Vt = vAeroUVW.Magnitude();
if ( Vt > 1) {
if (vAeroUVW(eW) != 0.0)
alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
if (vAeroUVW(eV) != 0.0)
beta = vAeroUVW(eU)*vAeroUVW(eU)+vAeroUVW(eW)*vAeroUVW(eW) > 0.0 ? atan2(vAeroUVW(eV),
sqrt(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW))) : 0.0;
double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
double signU=1;
if (vAeroUVW(eU) != 0.0)
signU = vAeroUVW(eU)/fabs(vAeroUVW(eU));
if ( (mUW == 0.0) || (Vt == 0.0) ) {
adot = 0.0;
bdot = 0.0;
} else {
adot = (vAeroUVW(eU)*vAeroUVW(eW) - vAeroUVW(eW)*vUVWdot(eU))/mUW;
bdot = (signU*mUW*vUVWdot(eV) - vAeroUVW(eV)*(vAeroUVW(eU)*vUVWdot(eU)
+ vAeroUVW(eW)*vUVWdot(eW)))/(Vt*Vt*sqrt(mUW));
}
} else {
alpha = beta = adot = bdot = 0;
}
qbar = 0.5*Atmosphere->GetDensity()*Vt*Vt;
qbarUW = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
qbarUV = 0.5*Atmosphere->GetDensity()*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eV)*vAeroUVW(eV));
Mach = Vt / Atmosphere->GetSoundSpeed();
vMachUVW(eU) = vAeroUVW(eU) / Atmosphere->GetSoundSpeed();
vMachUVW(eV) = vAeroUVW(eV) / Atmosphere->GetSoundSpeed();
vMachUVW(eW) = vAeroUVW(eW) / Atmosphere->GetSoundSpeed();
if (debug_lvl > 1) Debug(1);
return false;
} else {
return true;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTranslation::bind(void)
{
typedef double (FGTranslation::*PMF)(int) const;
PropertyManager->Tie("velocities/u-fps", this,1,
(PMF)&FGTranslation::GetUVW /*,
&FGTranslation::SetUVW,
true */);
PropertyManager->Tie("velocities/v-fps", this,2,
(PMF)&FGTranslation::GetUVW /*,
&FGTranslation::SetUVW,
true*/);
PropertyManager->Tie("velocities/w-fps", this,3,
(PMF)&FGTranslation::GetUVW /*,
&FGTranslation::SetUVW,
true*/);
PropertyManager->Tie("accelerations/udot-fps", this,1,
(PMF)&FGTranslation::GetUVWdot);
PropertyManager->Tie("accelerations/vdot-fps", this,2,
(PMF)&FGTranslation::GetUVWdot);
PropertyManager->Tie("accelerations/wdot-fps", this,3,
(PMF)&FGTranslation::GetUVWdot);
PropertyManager->Tie("velocities/u-aero-fps", this,1,
(PMF)&FGTranslation::GetAeroUVW);
PropertyManager->Tie("velocities/v-aero-fps", this,2,
(PMF)&FGTranslation::GetAeroUVW);
PropertyManager->Tie("velocities/w-aero-fps", this,3,
(PMF)&FGTranslation::GetAeroUVW);
PropertyManager->Tie("aero/alpha-rad", this,
&FGTranslation::Getalpha,
&FGTranslation::Setalpha,
true);
PropertyManager->Tie("aero/beta-rad", this,
&FGTranslation::Getbeta,
&FGTranslation::Setbeta,
true);
PropertyManager->Tie("aero/mag-beta-rad", this,
&FGTranslation::GetMagBeta);
PropertyManager->Tie("aero/qbar-psf", this,
&FGTranslation::Getqbar,
&FGTranslation::Setqbar,
true);
PropertyManager->Tie("aero/qbarUW-psf", this,
&FGTranslation::GetqbarUW,
&FGTranslation::SetqbarUW,
true);
PropertyManager->Tie("aero/qbarUV-psf", this,
&FGTranslation::GetqbarUV,
&FGTranslation::SetqbarUV,
true);
PropertyManager->Tie("velocities/vt-fps", this,
&FGTranslation::GetVt,
&FGTranslation::SetVt,
true);
PropertyManager->Tie("velocities/mach-norm", this,
&FGTranslation::GetMach,
&FGTranslation::SetMach,
true);
PropertyManager->Tie("aero/alphadot-rad_sec", this,
&FGTranslation::Getadot,
&FGTranslation::Setadot,
true);
PropertyManager->Tie("aero/betadot-rad_sec", this,
&FGTranslation::Getbdot,
&FGTranslation::Setbdot,
true);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTranslation::unbind(void)
{
PropertyManager->Untie("velocities/u-fps");
PropertyManager->Untie("velocities/v-fps");
PropertyManager->Untie("velocities/w-fps");
PropertyManager->Untie("accelerations/udot-fps");
PropertyManager->Untie("accelerations/vdot-fps");
PropertyManager->Untie("accelerations/wdot-fps");
PropertyManager->Untie("velocities/u-aero-fps");
PropertyManager->Untie("velocities/v-aero-fps");
PropertyManager->Untie("velocities/w-aero-fps");
PropertyManager->Untie("aero/alpha-rad");
PropertyManager->Untie("aero/beta-rad");
PropertyManager->Untie("aero/qbar-psf");
PropertyManager->Untie("aero/qbarUW-psf");
PropertyManager->Untie("aero/qbarUV-psf");
PropertyManager->Untie("velocities/vt-fps");
PropertyManager->Untie("velocities/mach-norm");
PropertyManager->Untie("aero/alphadot-rad_sec");
PropertyManager->Untie("aero/betadot-rad_sec");
PropertyManager->Untie("aero/mag-beta-rad");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 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 FGTranslation::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: FGTranslation" << endl;
if (from == 1) cout << "Destroyed: FGTranslation" << 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 (fabs(vUVW(eU)) > 1e6)
cout << "FGTranslation::U velocity out of bounds: " << vUVW(eU) << endl;
if (fabs(vUVW(eV)) > 1e6)
cout << "FGTranslation::V velocity out of bounds: " << vUVW(eV) << endl;
if (fabs(vUVW(eW)) > 1e6)
cout << "FGTranslation::W velocity out of bounds: " << vUVW(eW) << endl;
if (fabs(vUVWdot(eU)) > 1e4)
cout << "FGTranslation::U acceleration out of bounds: " << vUVWdot(eU) << endl;
if (fabs(vUVWdot(eV)) > 1e4)
cout << "FGTranslation::V acceleration out of bounds: " << vUVWdot(eV) << endl;
if (fabs(vUVWdot(eW)) > 1e4)
cout << "FGTranslation::W acceleration out of bounds: " << vUVWdot(eW) << endl;
if (Mach > 100 || Mach < 0.00)
cout << "FGTranslation::Mach is out of bounds: " << Mach << endl;
if (qbar > 1e6 || qbar < 0.00)
cout << "FGTranslation::qbar is out of bounds: " << qbar << endl;
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -1,155 +0,0 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGTranslation.h
Author: Jon Berndt
Date started: 12/02/98
------------- Copyright (C) 1999 Jon S. Berndt (jsb@hal-pc.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
12/02/98 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGTRANSLATION_H
#define FGTRANSLATION_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifdef FGFS
# include <simgear/compiler.h>
# ifdef SG_HAVE_STD_INCLUDES
# include <cmath>
# else
# include <math.h>
# endif
#else
# if defined(sgi) && !defined(__GNUC__)
# include <math.h>
# else
# include <cmath>
# endif
#endif
#include "FGModel.h"
#include "FGColumnVector3.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_TRANSLATION "$Id$"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models the translation aspects of the EOM.
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
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGTranslation : public FGModel {
public:
FGTranslation(FGFDMExec*);
~FGTranslation();
inline double GetUVW (int idx) const { return vUVW(idx); }
inline FGColumnVector3& GetUVW (void) { return vUVW; }
inline FGColumnVector3& GetUVWdot(void) { return vUVWdot; }
inline double GetUVWdot(int idx) const { return vUVWdot(idx); }
inline FGColumnVector3& GetAeroUVW (void) { return vAeroUVW; }
inline double GetAeroUVW (int idx) const { return vAeroUVW(idx); }
double Getalpha(void) const { return alpha; }
double Getbeta (void) const { return beta; }
inline double GetMagBeta(void) const { return fabs(beta); }
double Getqbar (void) const { return qbar; }
double GetqbarUW (void) const { return qbarUW; }
double GetqbarUV (void) const { return qbarUV; }
inline double GetVt (void) const { return Vt; }
double GetMach (void) const { return Mach; }
double GetMachU(void) const { return vMachUVW(eU); }
double Getadot (void) const { return adot; }
double Getbdot (void) const { return bdot; }
void SetUVW(FGColumnVector3 tt) { vUVW = tt; }
void SetAeroUVW(FGColumnVector3 tt) { vAeroUVW = tt; }
inline void Setalpha(double tt) { alpha = tt; }
inline void Setbeta (double tt) { beta = tt; }
inline void Setqbar (double tt) { qbar = tt; }
inline void SetqbarUW (double tt) { qbarUW = tt; }
inline void SetqbarUV (double tt) { qbarUV = tt; }
inline void SetVt (double tt) { Vt = tt; }
inline void SetMach (double tt) { Mach=tt; }
inline void Setadot (double tt) { adot = tt; }
inline void Setbdot (double tt) { bdot = tt; }
inline void SetAB(double t1, double t2) { alpha=t1; beta=t2; }
bool Run(void);
void bind(void);
void unbind(void);
private:
FGColumnVector3 vUVW;
FGColumnVector3 vUVWdot;
FGColumnVector3 vUVWdot_prev[4];
FGColumnVector3 vAeroUVW;
FGColumnVector3 vMachUVW;
double Vt, Mach;
double qbar, qbarUW, qbarUV;
double alpha, beta;
double adot,bdot;
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -243,6 +243,10 @@ bool FGTrim::DoTrim(void) {
fdmex->GetOutput()->Disable();
fgic->SetPRadpsIC(0.0);
fgic->SetQRadpsIC(0.0);
fgic->SetRRadpsIC(0.0);
//clear the sub iterations counts & zero out the controls
for(current_axis=0;current_axis<TrimAxes.size();current_axis++) {
//cout << current_axis << " " << TrimAxes[current_axis]->GetStateName()
@ -557,7 +561,7 @@ void FGTrim::setupPullup() {
<< fgic->GetVtrueFpsIC() << endl;
q=g*(targetNlf-cgamma)/fgic->GetVtrueFpsIC();
cout << targetNlf << ", " << q << endl;
fdmex->GetRotation()->SetPQR(0,q,0);
fgic->SetQRadpsIC(q);
cout << "setPitchRateInPullup() complete" << endl;
}
@ -593,13 +597,15 @@ void FGTrim::updateRates(void){
} else {
p=q=r=0;
}
fdmex->GetRotation()->SetPQR(p,q,r);
fgic->SetPRadpsIC(p);
fgic->SetQRadpsIC(q);
fgic->SetRRadpsIC(r);
} else if( mode == tPullup && fabs(targetNlf-1) > 0.01) {
float g,q,cgamma;
g=fdmex->GetInertial()->gravity();
cgamma=cos(fgic->GetFlightPathAngleRadIC());
q=g*(targetNlf-cgamma)/fgic->GetVtrueFpsIC();
fdmex->GetRotation()->SetPQR(0,q,0);
fgic->SetQRadpsIC(q);
}
}

View file

@ -120,17 +120,17 @@ FGTrimAxis::FGTrimAxis(FGFDMExec* fdex, FGInitialCondition* ic, State st,
case tAltAGL:
control_min=0;
control_max=30;
control_value=fdmex->GetPosition()->GetDistanceAGL();
control_value=fdmex->GetPropagate()->GetDistanceAGL();
solver_eps=tolerance/100;
break;
case tTheta:
control_min=fdmex->GetRotation()->Gettht() - 5*degtorad;
control_max=fdmex->GetRotation()->Gettht() + 5*degtorad;
control_min=fdmex->GetPropagate()->Gettht() - 5*degtorad;
control_max=fdmex->GetPropagate()->Gettht() + 5*degtorad;
state_convert=radtodeg;
break;
case tPhi:
control_min=fdmex->GetRotation()->Getphi() - 30*degtorad;
control_max=fdmex->GetRotation()->Getphi() + 30*degtorad;
control_min=fdmex->GetPropagate()->Getphi() - 30*degtorad;
control_max=fdmex->GetPropagate()->Getphi() + 30*degtorad;
state_convert=radtodeg;
control_convert=radtodeg;
break;
@ -141,8 +141,8 @@ FGTrimAxis::FGTrimAxis(FGFDMExec* fdex, FGInitialCondition* ic, State st,
control_convert=radtodeg;
break;
case tHeading:
control_min=fdmex->GetRotation()->Getpsi() - 30*degtorad;
control_max=fdmex->GetRotation()->Getpsi() + 30*degtorad;
control_min=fdmex->GetPropagate()->Getpsi() - 30*degtorad;
control_max=fdmex->GetPropagate()->Getpsi() + 30*degtorad;
state_convert=radtodeg;
break;
}
@ -162,12 +162,12 @@ FGTrimAxis::~FGTrimAxis(void)
void FGTrimAxis::getState(void) {
switch(state) {
case tUdot: state_value=fdmex->GetTranslation()->GetUVWdot(1)-state_target; break;
case tVdot: state_value=fdmex->GetTranslation()->GetUVWdot(2)-state_target; break;
case tWdot: state_value=fdmex->GetTranslation()->GetUVWdot(3)-state_target; break;
case tQdot: state_value=fdmex->GetRotation()->GetPQRdot(2)-state_target;break;
case tPdot: state_value=fdmex->GetRotation()->GetPQRdot(1)-state_target; break;
case tRdot: state_value=fdmex->GetRotation()->GetPQRdot(3)-state_target; break;
case tUdot: state_value=fdmex->GetPropagate()->GetUVWdot(1)-state_target; break;
case tVdot: state_value=fdmex->GetPropagate()->GetUVWdot(2)-state_target; break;
case tWdot: state_value=fdmex->GetPropagate()->GetUVWdot(3)-state_target; break;
case tQdot: state_value=fdmex->GetPropagate()->GetPQRdot(2)-state_target;break;
case tPdot: state_value=fdmex->GetPropagate()->GetPQRdot(1)-state_target; break;
case tRdot: state_value=fdmex->GetPropagate()->GetPQRdot(3)-state_target; break;
case tHmgt: state_value=computeHmgt()-state_target; break;
case tNlf: state_value=fdmex->GetAircraft()->GetNlf()-state_target; break;
case tAll: break;
@ -181,19 +181,19 @@ void FGTrimAxis::getState(void) {
void FGTrimAxis::getControl(void) {
switch(control) {
case tThrottle: control_value=fdmex->GetFCS()->GetThrottleCmd(0); break;
case tBeta: control_value=fdmex->GetTranslation()->Getalpha(); break;
case tAlpha: control_value=fdmex->GetTranslation()->Getbeta(); break;
case tBeta: control_value=fdmex->GetAuxiliary()->Getalpha(); break;
case tAlpha: control_value=fdmex->GetAuxiliary()->Getbeta(); 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->GetPosition()->GetDistanceAGL();break;
case tTheta: control_value=fdmex->GetRotation()->Gettht(); break;
case tPhi: control_value=fdmex->GetRotation()->Getphi(); break;
case tGamma: control_value=fdmex->GetPosition()->GetGamma();break;
case tHeading: control_value=fdmex->GetRotation()->Getpsi(); break;
case tAltAGL: control_value=fdmex->GetPropagate()->GetDistanceAGL();break;
case tTheta: control_value=fdmex->GetPropagate()->Gettht(); break;
case tPhi: control_value=fdmex->GetPropagate()->Getphi(); break;
case tGamma: control_value=fdmex->GetAuxiliary()->GetGamma();break;
case tHeading: control_value=fdmex->GetPropagate()->Getpsi(); break;
}
}
@ -202,8 +202,8 @@ void FGTrimAxis::getControl(void) {
double FGTrimAxis::computeHmgt(void) {
double diff;
diff = fdmex->GetRotation()->Getpsi() -
fdmex->GetPosition()->GetGroundTrack();
diff = fdmex->GetPropagate()->Getpsi() -
fdmex->GetAuxiliary()->GetGroundTrack();
if( diff < -M_PI ) {
return (diff + 2*M_PI);
@ -270,11 +270,11 @@ void FGTrimAxis::SetThetaOnGround(double ff) {
}
cout << "SetThetaOnGround ref gear: " << ref << endl;
if(ref >= 0) {
double sp=fdmex->GetRotation()->GetSinphi();
double cp=fdmex->GetRotation()->GetCosphi();
double lx=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1);
double ly=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2);
double lz=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3);
double sp = fdmex->GetPropagate()->GetSinphi();
double cp = fdmex->GetPropagate()->GetCosphi();
double lx = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1);
double ly = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2);
double lz = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3);
double hagl = -1*lx*sin(ff) +
ly*sp*cos(ff) +
lz*cp*cos(ff);
@ -289,7 +289,9 @@ void FGTrimAxis::SetThetaOnGround(double ff) {
/*****************************************************************************/
bool FGTrimAxis::initTheta(void) {
int i,N,iAft, iForward;
int i,N;
int iForward = 0;
int iAft = 1;
double zAft,zForward,zDiff,theta;
double xAft,xForward,xDiff;
bool level;
@ -318,36 +320,31 @@ bool FGTrimAxis::initTheta(void) {
}
// now adjust theta till the wheels are the same distance from the ground
xAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(1);
xForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(1);
xDiff = xForward - xAft;
zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3);
zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3);
zDiff = zForward - zAft;
level=false;
theta=fgic->GetPitchAngleDegIC();
while(!level && (i < 100)) {
theta+=180.0/M_PI*zDiff/fabs(xDiff);
fgic->SetPitchAngleDegIC(theta);
fdmex->RunIC();
xAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(1);
xForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(1);
xDiff = xForward - xAft;
zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3);
zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3);
zDiff = zForward - zAft;
xAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetBodyLocation(1);
xForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetBodyLocation(1);
xDiff = xForward - xAft;
zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3);
zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3);
zDiff = zForward - zAft;
level=false;
theta=fgic->GetPitchAngleDegIC();
while(!level && (i < 100)) {
theta+=radtodeg*atan(zDiff/xDiff);
fgic->SetPitchAngleDegIC(theta);
fdmex->RunIC();
zAft=fdmex->GetGroundReactions()->GetGearUnit(iAft)->GetLocalGear(3);
zForward=fdmex->GetGroundReactions()->GetGearUnit(iForward)->GetLocalGear(3);
zDiff = zForward - zAft;
//cout << endl << theta << " " << zDiff << endl;
//cout << "0: " << fdmex->GetGroundReactions()->GetGearUnit(0)->GetLocalGear() << endl;
//cout << "1: " << fdmex->GetGroundReactions()->GetGearUnit(1)->GetLocalGear() << endl;
if(fabs(zDiff ) < 0.1)
level=true;
i++;
//cout << endl << theta << " " << zDiff << endl;
//cout << "0: " << fdmex->GetGroundReactions()->GetGearUnit(0)->GetLocalGear() << endl;
//cout << "1: " << fdmex->GetGroundReactions()->GetGearUnit(1)->GetLocalGear() << endl;
if(fabs(zDiff ) < 0.1)
level=true;
i++;
}
//cout << i << endl;
if (debug_lvl > 0) {
cout << " Initial Theta: " << fdmex->GetRotation()->Gettht()*radtodeg << endl;
cout << " Initial Theta: " << fdmex->GetPropagate()->Gettht()*radtodeg << endl;
cout << " Used gear unit " << iAft << " as aft and " << iForward << " as forward" << endl;
}
control_min=(theta+5)*degtorad;
@ -366,18 +363,18 @@ void FGTrimAxis::SetPhiOnGround(double ff) {
i=0; ref=-1;
//must have an off-center unit here
while( (ref < 0) && (i < fdmex->GetGroundReactions()->GetNumGearUnits()) ) {
if( (fdmex->GetGroundReactions()->GetGearUnit(i)->GetWOW()) &&
while ( (ref < 0) && (i < fdmex->GetGroundReactions()->GetNumGearUnits()) ) {
if ( (fdmex->GetGroundReactions()->GetGearUnit(i)->GetWOW()) &&
(fabs(fdmex->GetGroundReactions()->GetGearUnit(i)->GetBodyLocation(2)) > 0.01))
ref=i;
i++;
}
if(ref >= 0) {
double st=fdmex->GetRotation()->GetSintht();
double ct=fdmex->GetRotation()->GetCostht();
double lx=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1);
double ly=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2);
double lz=fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3);
if (ref >= 0) {
double st = sin(fdmex->GetPropagate()->Gettht());
double ct = cos(fdmex->GetPropagate()->Gettht());
double lx = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(1);
double ly = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(2);
double lz = fdmex->GetGroundReactions()->GetGearUnit(ref)->GetBodyLocation(3);
double hagl = -1*lx*st +
ly*sin(ff)*ct +
lz*cos(ff)*ct;

View file

@ -1,11 +1,11 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGTurbine.cpp
Author: Jon S. Berndt
Date started: 08/23/2002
Author: David Culp
Date started: 03/11/2003
Purpose: This module models a turbine engine.
------------- Copyright (C) 2002 Jon S. Berndt (jsb@hal-pc.org) --------------
------------- Copyright (C) 2003 David Culp (davidculp2@comcast.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
@ -27,20 +27,22 @@
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class descends from the FGEngine class and models a Turbine engine based
This class descends from the FGEngine class and models a turbine engine based
on parameters given in the engine config file for this class
HISTORY
--------------------------------------------------------------------------------
08/23/2002 JSB Created
03/11/2003 DPC Created
09/08/2003 DPC Changed Calculate() and added engine phases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include "FGTurbine.h"
#include <sstream>
#include "FGTurbine.h"
namespace JSBSim {
@ -54,8 +56,9 @@ CLASS IMPLEMENTATION
FGTurbine::FGTurbine(FGFDMExec* exec, FGConfigFile* cfg) : FGEngine(exec)
{
SetDefaults();
Load(cfg);
PowerCommand=0;
Debug(0);
}
@ -67,147 +70,367 @@ FGTurbine::~FGTurbine()
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The main purpose of Calculate() is to determine what phase the engine should
// be in, then call the corresponding function.
double FGTurbine::Calculate(double dummy)
double FGTurbine::Calculate(void)
{
double idle,mil,aug;
double throttle=FCS->GetThrottlePos(EngineNumber);
double dt=State->Getdt();
if( dt > 0 ) {
PowerCommand+=dt*PowerLag( PowerCommand,
ThrottleToPowerCommand(throttle) );
if(PowerCommand > 100 )
PowerCommand=100;
else if(PowerCommand < 0 )
PowerCommand=0;
TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
dt = State->Getdt() * Propulsion->GetRate();
ThrottlePos = FCS->GetThrottlePos(EngineNumber);
if (ThrottlePos > 1.0) {
AugmentCmd = ThrottlePos - 1.0;
ThrottlePos -= AugmentCmd;
} else {
PowerCommand=ThrottleToPowerCommand(throttle);
AugmentCmd = 0.0;
}
mil=MaxMilThrust*ThrustTables[1]->TotalValue();
// When trimming is finished check if user wants engine OFF or RUNNING
if ((phase == tpTrim) && (dt > 0)) {
if (Running && !Starved) {
phase = tpRun;
N2 = IdleN2 + ThrottlePos * N2_factor;
N1 = IdleN1 + ThrottlePos * N1_factor;
OilTemp_degK = 366.0;
Cutoff = false;
}
else {
phase = tpOff;
Cutoff = true;
EGT_degC = TAT;
}
}
if( PowerCommand <= 50 ) {
idle=MaxMilThrust*ThrustTables[0]->TotalValue();
Thrust = idle + (mil-idle)*PowerCommand*0.02;
} else {
aug=MaxAugThrust*ThrustTables[2]->TotalValue();
Thrust = mil + (aug-mil)*(PowerCommand-50)*0.02;
if (!Running && Cutoff && Starter) {
if (phase == tpOff) phase = tpSpinUp;
}
if (!Running && !Cutoff && (N2 > 15.0)) phase = tpStart;
if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
if (dt == 0) phase = tpTrim;
if (Starved) phase = tpOff;
if (Stalled) phase = tpStall;
if (Seized) phase = tpSeize;
switch (phase) {
case tpOff: Thrust = Off(); break;
case tpRun: Thrust = Run(); break;
case tpSpinUp: Thrust = SpinUp(); break;
case tpStart: Thrust = Start(); break;
case tpStall: Thrust = Stall(); break;
case tpSeize: Thrust = Seize(); break;
case tpTrim: Thrust = Trim(); break;
default: Thrust = Off();
}
return Thruster->Calculate(Thrust);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Off(void)
{
double qbar = Auxiliary->Getqbar();
Running = false;
FuelFlow_pph = Seek(&FuelFlow_pph, 0, 1000.0, 10000.0);
N1 = Seek(&N1, qbar/10.0, N1/2.0, N1/2.0);
N2 = Seek(&N2, qbar/15.0, N2/2.0, N2/2.0);
EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
OilPressure_psi = N2 * 0.62;
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
EPR = Seek(&EPR, 1.0, 0.2, 0.2);
Augmentation = false;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Run(void)
{
double idlethrust, milthrust, thrust;
double N2norm; // 0.0 = idle N2, 1.0 = maximum N2
idlethrust = MilThrust * ThrustTables[0]->TotalValue();
milthrust = (MilThrust - idlethrust) * ThrustTables[1]->TotalValue();
Running = true;
Starter = false;
N2 = Seek(&N2, IdleN2 + ThrottlePos * N2_factor, delay, delay * 3.0);
N1 = Seek(&N1, IdleN1 + ThrottlePos * N1_factor, delay, delay * 2.4);
N2norm = (N2 - IdleN2) / N2_factor;
thrust = idlethrust + (milthrust * N2norm * N2norm);
EGT_degC = TAT + 363.1 + ThrottlePos * 357.1;
OilPressure_psi = N2 * 0.62;
OilTemp_degK = Seek(&OilTemp_degK, 366.0, 1.2, 0.1);
if (!Augmentation) {
double correctedTSFC = TSFC + TSFC - (N2norm * TSFC);
FuelFlow_pph = Seek(&FuelFlow_pph, thrust * correctedTSFC, 1000.0, 100000);
if (FuelFlow_pph < IdleFF) FuelFlow_pph = IdleFF;
NozzlePosition = Seek(&NozzlePosition, 1.0 - N2norm, 0.8, 0.8);
thrust = thrust * (1.0 - BleedDemand);
EPR = 1.0 + thrust/MilThrust;
}
if (AugMethod == 1) {
if ((ThrottlePos > 0.99) && (N2 > 97.0)) {Augmentation = true;}
else {Augmentation = false;}
}
if ((Augmented == 1) && Augmentation && (AugMethod < 2)) {
thrust = MaxThrust * ThrustTables[2]->TotalValue();
FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
}
if ((AugmentCmd > 0.0) && (AugMethod == 2)) {
Augmentation = true;
double tdiff = (MaxThrust * ThrustTables[2]->TotalValue()) - thrust;
thrust += (tdiff * AugmentCmd);
FuelFlow_pph = Seek(&FuelFlow_pph, thrust * ATSFC, 5000.0, 10000.0);
NozzlePosition = Seek(&NozzlePosition, 1.0, 0.8, 0.8);
} else {
Augmentation = false;
}
if ((Injected == 1) && Injection)
thrust = thrust * ThrustTables[3]->TotalValue();
ConsumeFuel();
if (Cutoff) phase = tpOff;
if (Starved) phase = tpOff;
return Thrust;
return thrust;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::ThrottleToPowerCommand(double throttle) {
if( throttle <= 0.77 )
return 64.94*throttle;
double FGTurbine::SpinUp(void)
{
Running = false;
FuelFlow_pph = 0.0;
N2 = Seek(&N2, 25.18, 3.0, N2/2.0);
N1 = Seek(&N1, 5.21, 1.0, N1/2.0);
EGT_degC = Seek(&EGT_degC, TAT, 11.7, 7.3);
OilPressure_psi = N2 * 0.62;
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0.2, 0.2);
EPR = 1.0;
NozzlePosition = 1.0;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Start(void)
{
if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
Cranking = true; // provided for sound effects signal
if (N2 < IdleN2) {
N2 = Seek(&N2, IdleN2, 2.0, N2/2.0);
N1 = Seek(&N1, IdleN1, 1.4, N1/2.0);
EGT_degC = Seek(&EGT_degC, TAT + 363.1, 21.3, 7.3);
FuelFlow_pph = Seek(&FuelFlow_pph, IdleFF, 103.7, 103.7);
OilPressure_psi = N2 * 0.62;
}
else {
phase = tpRun;
Running = true;
Starter = false;
Cranking = false;
}
}
else { // no start if N2 < 15%
phase = tpOff;
Starter = false;
}
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Stall(void)
{
double qbar = Auxiliary->Getqbar();
EGT_degC = TAT + 903.14;
FuelFlow_pph = IdleFF;
N1 = Seek(&N1, qbar/10.0, 0, N1/10.0);
N2 = Seek(&N2, qbar/15.0, 0, N2/10.0);
if (ThrottlePos < 0.01) phase = tpRun; // clear the stall with throttle
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Seize(void)
{
double qbar = Auxiliary->Getqbar();
N2 = 0.0;
N1 = Seek(&N1, qbar/20.0, 0, N1/15.0);
FuelFlow_pph = IdleFF;
OilPressure_psi = 0.0;
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0, 0.2);
Running = false;
return 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::Trim(void)
{
double idlethrust, milthrust, thrust, tdiff;
idlethrust = MilThrust * ThrustTables[0]->TotalValue();
milthrust = (MilThrust - idlethrust) * ThrustTables[1]->TotalValue();
thrust = (idlethrust + (milthrust * ThrottlePos * ThrottlePos)) * (1.0 - BleedDemand);
if (AugmentCmd > 0.0) {
tdiff = (MaxThrust * ThrustTables[2]->TotalValue()) - thrust;
thrust += (tdiff * AugmentCmd);
}
return thrust;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::CalcFuelNeed(void)
{
return FuelFlow_pph /3600 * State->Getdt() * Propulsion->GetRate();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::GetPowerAvailable(void) {
if( ThrottlePos <= 0.77 )
return 64.94*ThrottlePos;
else
return 217.38*throttle - 117.38;
return 217.38*ThrottlePos - 117.38;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::PowerLag(double actual_power, double power_command) {
double t, p2;
if( power_command >= 50 ) {
if( actual_power >= 50 ) {
t=5;
p2=power_command;
} else {
p2=60;
t=rtau(p2-actual_power);
}
} else {
if( actual_power >= 50 ) {
t=5;
p2=40;
} else {
p2=power_command;
t=rtau(p2-actual_power);
}
double FGTurbine::Seek(double *var, double target, double accel, double decel) {
double v = *var;
if (v > target) {
v -= dt * decel;
if (v < target) v = target;
} else if (v < target) {
v += dt * accel;
if (v > target) v = target;
}
return t*(p2-actual_power);
return v;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTurbine::rtau(double delta_power) {
if( delta_power <= 25 )
return 1.0;
else if ( delta_power >= 50)
return 0.1;
else
return 1.9-0.036*delta_power;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doInlet(void)
void FGTurbine::SetDefaults(void)
{
Name = "Not defined";
N1 = N2 = 0.0;
Type = etTurbine;
MilThrust = 10000.0;
MaxThrust = 10000.0;
BypassRatio = 0.0;
TSFC = 0.8;
ATSFC = 1.7;
IdleN1 = 30.0;
IdleN2 = 60.0;
MaxN1 = 100.0;
MaxN2 = 100.0;
Augmented = 0;
AugMethod = 0;
Injected = 0;
BleedDemand = 0.0;
ThrottlePos = 0.0;
AugmentCmd = 0.0;
InletPosition = 1.0;
NozzlePosition = 1.0;
Augmentation = false;
Injection = false;
Reversed = false;
Cutoff = true;
phase = tpOff;
Stalled = false;
Seized = false;
Overtemp = false;
Fire = false;
EGT_degC = 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doCompressor(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doBleedDuct(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doCombustor(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doTurbine(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doConvergingNozzle(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTurbine::doTransition(void)
{
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGTurbine::Load(FGConfigFile *Eng_cfg)
{
int i;
string token;
Name = Eng_cfg->GetValue("NAME");
cout << Name << endl;
Eng_cfg->GetNextConfigLine();
*Eng_cfg >> token >> MaxMilThrust;
*Eng_cfg >> token >> MaxAugThrust;
i=0;
while( Eng_cfg->GetValue() != string("/FG_TURBINE") && i < 10){
ThrustTables.push_back( new FGCoefficient(FDMExec) );
ThrustTables.back()->Load(Eng_cfg);
i++;
int counter=0;
while ( ((token = Eng_cfg->GetValue()) != string("/FG_TURBINE")) &&
(token != string("/FG_SIMTURBINE")) ) {
*Eng_cfg >> token;
if (token[0] == '<') token.erase(0,1); // Tables are read "<TABLE"
if (token == "MILTHRUST") *Eng_cfg >> MilThrust;
else if (token == "MAXTHRUST") *Eng_cfg >> MaxThrust;
else if (token == "BYPASSRATIO") *Eng_cfg >> BypassRatio;
else if (token == "BLEED") *Eng_cfg >> BleedDemand;
else if (token == "TSFC") *Eng_cfg >> TSFC;
else if (token == "ATSFC") *Eng_cfg >> ATSFC;
else if (token == "IDLEN1") *Eng_cfg >> IdleN1;
else if (token == "IDLEN2") *Eng_cfg >> IdleN2;
else if (token == "MAXN1") *Eng_cfg >> MaxN1;
else if (token == "MAXN2") *Eng_cfg >> MaxN2;
else if (token == "AUGMENTED") *Eng_cfg >> Augmented;
else if (token == "AUGMETHOD") *Eng_cfg >> AugMethod;
else if (token == "INJECTED") *Eng_cfg >> Injected;
else if (token == "MINTHROTTLE") *Eng_cfg >> MinThrottle;
else if (token == "TABLE") {
if (counter++ == 0) Debug(2); // print engine specs prior to table read
ThrustTables.push_back( new FGCoefficient(FDMExec) );
ThrustTables.back()->Load(Eng_cfg);
}
else cerr << "Unhandled token in Engine config file: " << token << endl;
}
// Pre-calculations and initializations
delay = 60.0 / (BypassRatio + 3.0);
N1_factor = MaxN1 - IdleN1;
N2_factor = MaxN2 - IdleN2;
OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGTurbine::GetEngineLabels(void)
{
std::ostringstream buf;
buf << Name << "_N1[" << EngineNumber << "], "
<< Name << "_N2[" << EngineNumber << "], "
<< Thruster->GetThrusterLabels(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGTurbine::GetEngineValues(void)
{
std::ostringstream buf;
buf << N1 << ", "
<< N2 << ", "
<< Thruster->GetThrusterValues(EngineNumber);
return buf.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
@ -235,6 +458,24 @@ void FGTurbine::Debug(int from)
if (from == 0) { // Constructor
}
if (from == 2) { // called from Load()
cout << "\n Engine Name: " << Name << endl;
cout << " MilThrust: " << MilThrust << endl;
cout << " MaxThrust: " << MaxThrust << endl;
cout << " BypassRatio: " << BypassRatio << endl;
cout << " TSFC: " << TSFC << endl;
cout << " ATSFC: " << ATSFC << endl;
cout << " IdleN1: " << IdleN1 << endl;
cout << " IdleN2: " << IdleN2 << endl;
cout << " MaxN1: " << MaxN1 << endl;
cout << " MaxN2: " << MaxN2 << endl;
cout << " Augmented: " << Augmented << endl;
cout << " AugMethod: " << AugMethod << endl;
cout << " Injected: " << Injected << endl;
cout << " MinThrottle: " << MinThrottle << endl;
cout << endl;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGTurbine" << endl;

View file

@ -1,10 +1,10 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGTurbine.h
Author: Jon S. Berndt
Date started: 08/23/2002
Author: David Culp
Date started: 03/11/2003
------------- Copyright (C) 2002 Jon S. Berndt (jsb@hal-pc.org) --------------
------------- Copyright (C) 2003 David Culp (davidculp2@comcast.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
@ -25,7 +25,9 @@
HISTORY
--------------------------------------------------------------------------------
08/23/2002 JSB Created
03/11/2003 DPC Created, based on FGTurbine
09/22/2003 DPC Added starting, stopping, new framework
04/29/2004 DPC Renamed from FGSimTurbine to FGTurbine
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
@ -51,6 +53,83 @@ FORWARD DECLARATIONS
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** This class models a turbine engine. Based on Jon Berndt's FGTurbine module.
Here the term "phase" signifies the engine's mode of operation. At any given
time the engine is in only one phase. At simulator startup the engine will be
placed in the Trim phase in order to provide a simplified thrust value without
throttle lag. When trimming is complete the engine will go to the Off phase,
unless the value FGEngine::Running has been previously set to true, in which
case the engine will go to the Run phase. Once an engine is in the Off phase
the full starting procedure (or airstart) must be used to get it running.
<P>
- STARTING (on ground):
-# Set the control FGEngine::Starter to true. The engine will spin up to
a maximum of about %25 N2 (%5.2 N1). This simulates the action of a
pneumatic starter.
-# After reaching %15 N2 set the control FGEngine::Cutoff to false. If fuel
is available the engine will now accelerate to idle. The starter will
automatically be set to false after the start cycle.
<P>
- STARTING (in air):
-# Increase speed to obtain a minimum of %15 N2. If this is not possible,
the starter may be used to assist.
-# Place the control FGEngine::Cutoff to false.
<P>
Ignition is assumed to be on anytime the Cutoff control is set to false,
therefore a seperate ignition system is not modeled.
Configuration File Format
<pre>
\<FG_TURBINE NAME="<name>">
MILTHRUST \<thrust>
MAXTHRUST \<thrust>
BYPASSRATIO \<bypass ratio>
TSFC \<thrust specific fuel consumption>
ATSFC \<afterburning thrust specific fuel consumption>
IDLEN1 \<idle N1>
IDLEN2 \<idle N2>
MAXN1 \<max N1>
MAXN2 \<max N2>
AUGMENTED \<0|1>
AUGMETHOD \<0|1>
INJECTED \<0|1>
...
\</FG_TURBINE>
</pre>
Definition of the turbine engine configuration file parameters:
<pre>
<b>MILTHRUST</b> - Maximum thrust, static, at sea level, lbf.
<b>MAXTHRUST</b> - Afterburning thrust, static, at sea level, lbf
[this value will be ignored when AUGMENTED is zero (false)].
<b>BYPASSRATIO</b> - Ratio of bypass air flow to core air flow.
<b>TSFC</b> - Thrust-specific fuel consumption, lbm/hr/lbf
[i.e. fuel flow divided by thrust].
<b>ATSFC</b> - Afterburning TSFC, lbm/hr/lbf
[this value will be ignored when AUGMENTED is zero (false)]
<b>IDLEN1</b> - Fan rotor rpm (% of max) at idle
<b>IDLEN2</b> - Core rotor rpm (% of max) at idle
<b>MAXN1</b> - Fan rotor rpm (% of max) at full throttle [not always 100!]
<b>MAXN2</b> - Core rotor rpm (% of max) at full throttle [not always 100!]
<b>AUGMENTED</b>
0 == afterburner not installed
1 == afterburner installed
<b>AUGMETHOD</b>
0 == afterburner activated by property /engines/engine[n]/augmentation
1 == afterburner activated by pushing throttle above 99% position
2 == throttle range is expanded in the FCS, and values above 1.0 are afterburner range
[this item will be ignored when AUGMENTED == 0]
<b>INJECTED</b>
0 == Water injection not installed
1 == Water injection installed
</pre>
@author David P. Culp
@version "$Id$"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -58,38 +137,112 @@ CLASS DECLARATION
class FGTurbine : public FGEngine
{
public:
FGTurbine(FGFDMExec* exec, FGConfigFile* Eng_cfg);
/** Constructor
@param Executive pointer to executive structure
@param Eng_cfg pointer to engine config file instance */
FGTurbine(FGFDMExec* Executive, FGConfigFile* Eng_cfg);
/// Destructor
~FGTurbine();
double Calculate(double);
double GetPowerAvailable(void) { return PowerCommand; }
enum phaseType { tpOff, tpRun, tpSpinUp, tpStart, tpStall, tpSeize, tpTrim };
double Calculate(void);
double CalcFuelNeed(void);
double GetPowerAvailable(void);
double Seek(double* var, double target, double accel, double decel);
phaseType GetPhase(void) { return phase; }
bool GetOvertemp(void) {return Overtemp; }
bool GetInjection(void) {return Injection;}
bool GetFire(void) { return Fire; }
bool GetAugmentation(void) {return Augmentation;}
bool GetReversed(void) { return Reversed; }
bool GetCutoff(void) { return Cutoff; }
int GetIgnition(void) {return Ignition;}
double GetInlet(void) { return InletPosition; }
double GetNozzle(void) { return NozzlePosition; }
double GetBleedDemand(void) {return BleedDemand;}
double GetN1(void) {return N1;}
double GetN2(void) {return N2;}
double GetEPR(void) {return EPR;}
double GetEGT(void) {return EGT_degC;}
double getOilPressure_psi () const {return OilPressure_psi;}
double getOilTemp_degF (void) {return KelvinToFahrenheit(OilTemp_degK);}
void SetInjection(bool injection) {Injection = injection;}
void SetIgnition(int ignition) {Ignition = ignition;}
void SetAugmentation(bool augmentation) {Augmentation = augmentation;}
void SetPhase( phaseType p ) { phase = p; }
void SetEPR(double epr) {EPR = epr;}
void SetBleedDemand(double bleedDemand) {BleedDemand = bleedDemand;}
void SetReverse(bool reversed) { Reversed = reversed; }
void SetCutoff(bool cutoff) { Cutoff = cutoff; }
string GetEngineLabels(void);
string GetEngineValues(void);
private:
typedef vector<FGCoefficient*> CoeffArray;
CoeffArray ThrustTables;
string name;
double MaxMilThrust;
double MaxAugThrust;
phaseType phase; ///< Operating mode, or "phase"
double MilThrust; ///< Maximum Unaugmented Thrust, static @ S.L. (lbf)
double MaxThrust; ///< Maximum Augmented Thrust, static @ S.L. (lbf)
double BypassRatio; ///< Bypass Ratio
double TSFC; ///< Thrust Specific Fuel Consumption (lbm/hr/lbf)
double ATSFC; ///< Augmented TSFC (lbm/hr/lbf)
double IdleN1; ///< Idle N1
double IdleN2; ///< Idle N2
double N1; ///< N1
double N2; ///< N2
double MaxN1; ///< N1 at 100% throttle
double MaxN2; ///< N2 at 100% throttle
double IdleFF; ///< Idle Fuel Flow (lbm/hr)
double delay; ///< Inverse spool-up time from idle to 100% (seconds)
double dt; ///< Simulator time slice
double N1_factor; ///< factor to tie N1 and throttle
double N2_factor; ///< factor to tie N2 and throttle
double ThrottlePos; ///< FCS-supplied throttle position
double AugmentCmd; ///< modulated afterburner command (0.0 to 1.0)
double TAT; ///< total air temperature (deg C)
bool Stalled; ///< true if engine is compressor-stalled
bool Seized; ///< true if inner spool is seized
bool Overtemp; ///< true if EGT exceeds limits
bool Fire; ///< true if engine fire detected
bool Injection;
bool Augmentation;
bool Reversed;
bool Cutoff;
int Injected; ///< = 1 if water injection installed
int Ignition;
int Augmented; ///< = 1 if augmentation installed
int AugMethod; ///< = 0 if using property /engine[n]/augmentation
///< = 1 if using last 1% of throttle movement
///< = 2 if using FCS-defined throttle
double EGT_degC;
double EPR;
double OilPressure_psi;
double OilTemp_degK;
double BleedDemand;
double InletPosition;
double NozzlePosition;
double PowerCommand;
double ThrottleToPowerCommand(double throttle);
double PowerLag(double actual_power, double power_command);
double rtau(double delta_power);
void doInlet(void);
void doCompressor(void);
void doBleedDuct(void);
void doCombustor(void);
void doTurbine(void);
void doConvergingNozzle(void);
void doTransition(void);
double Off(void);
double Run(void);
double SpinUp(void);
double Start(void);
double Stall(void);
double Seize(void);
double Trim(void);
void SetDefaults(void);
bool Load(FGConfigFile *ENG_cfg);
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -49,10 +49,8 @@
#include <FDM/JSBSim/FGFDMExec.h>
#include <FDM/JSBSim/FGAircraft.h>
#include <FDM/JSBSim/FGFCS.h>
#include <FDM/JSBSim/FGPosition.h>
#include <FDM/JSBSim/FGRotation.h>
#include <FDM/JSBSim/FGPropagate.h>
#include <FDM/JSBSim/FGState.h>
#include <FDM/JSBSim/FGTranslation.h>
#include <FDM/JSBSim/FGAuxiliary.h>
#include <FDM/JSBSim/FGInitialCondition.h>
#include <FDM/JSBSim/FGTrim.h>
@ -63,11 +61,13 @@
#include <FDM/JSBSim/FGPropertyManager.h>
#include <FDM/JSBSim/FGEngine.h>
#include <FDM/JSBSim/FGPiston.h>
#include <FDM/JSBSim/FGSimTurbine.h>
#include <FDM/JSBSim/FGTurbine.h>
#include <FDM/JSBSim/FGRocket.h>
#include <FDM/JSBSim/FGElectric.h>
#include <FDM/JSBSim/FGNozzle.h>
#include <FDM/JSBSim/FGPropeller.h>
#include <FDM/JSBSim/FGRotor.h>
#include <FDM/JSBSim/FGTank.h>
#include "JSBSim.hxx"
static inline double
@ -117,9 +117,7 @@ FGJSBsim::FGJSBsim( double dt )
MassBalance = fdmex->GetMassBalance();
Propulsion = fdmex->GetPropulsion();
Aircraft = fdmex->GetAircraft();
Translation = fdmex->GetTranslation();
Rotation = fdmex->GetRotation();
Position = fdmex->GetPosition();
Propagate = fdmex->GetPropagate();
Auxiliary = fdmex->GetAuxiliary();
Aerodynamics = fdmex->GetAerodynamics();
GroundReactions = fdmex->GetGroundReactions();
@ -127,10 +125,9 @@ FGJSBsim::FGJSBsim( double dt )
fgic=fdmex->GetIC();
needTrim=true;
SGPath aircraft_path( globals->get_fg_root() );
aircraft_path.append( "Aircraft" );
SGPath aircraft_path( fgGetString("/sim/aircraft-dir") );
SGPath engine_path( globals->get_fg_root() );
SGPath engine_path( fgGetString("/sim/aircraft-dir") );
engine_path.append( "Engine" );
State->Setdt( dt );
@ -157,9 +154,9 @@ FGJSBsim::FGJSBsim( double dt )
SG_LOG( SG_FLIGHT, SG_ALERT, "num gear units = "
<< GroundReactions->GetNumGearUnits() );
SG_LOG( SG_FLIGHT, SG_ALERT, "This is a very bad thing because with 0 gear units, the ground trimming");
SG_LOG( SG_FLIGHT, SG_ALERT, "routine (coming up later in the code) will core dump.");
SG_LOG( SG_FLIGHT, SG_ALERT, "Halting the sim now, and hoping a solution will present itself soon!");
exit(-1);
SG_LOG( SG_FLIGHT, SG_ALERT, "routine (coming up later in the code) will core dump.");
SG_LOG( SG_FLIGHT, SG_ALERT, "Halting the sim now, and hoping a solution will present itself soon!");
exit(-1);
}
init_gear();
@ -225,8 +222,8 @@ FGJSBsim::FGJSBsim( double dt )
for (unsigned int i = 0; i < Propulsion->GetNumEngines(); i++) {
SGPropertyNode * node = fgGetNode("engines/engine", i, true);
Propulsion->GetThruster(i)->SetRPM(node->getDoubleValue("rpm") /
Propulsion->GetThruster(i)->GetGearRatio());
Propulsion->GetEngine(i)->GetThruster()->SetRPM(node->getDoubleValue("rpm") /
Propulsion->GetEngine(i)->GetThruster()->GetGearRatio());
}
}
@ -250,6 +247,9 @@ void FGJSBsim::init()
// Explicitly call the superclass's
// init method first.
#ifdef FG_WEATHERCM
Atmosphere->UseInternal();
#else
if (fgGetBool("/environment/params/control-fdm-atmosphere")) {
Atmosphere->UseExternal();
Atmosphere->SetExTemperature(
@ -266,6 +266,7 @@ void FGJSBsim::init()
} else {
Atmosphere->UseInternal();
}
#endif
fgic->SetVnorthFpsIC( wind_from_north->getDoubleValue() );
fgic->SetVeastFpsIC( wind_from_east->getDoubleValue() );
@ -289,19 +290,19 @@ void FGJSBsim::init()
switch(fgic->GetSpeedSet()) {
case setned:
SG_LOG(SG_FLIGHT,SG_INFO, " Vn,Ve,Vd= "
<< Position->GetVn() << ", "
<< Position->GetVe() << ", "
<< Position->GetVd() << " ft/s");
<< Propagate->GetVel(eNorth) << ", "
<< Propagate->GetVel(eEast) << ", "
<< Propagate->GetVel(eDown) << " ft/s");
break;
case setuvw:
SG_LOG(SG_FLIGHT,SG_INFO, " U,V,W= "
<< Translation->GetUVW(1) << ", "
<< Translation->GetUVW(2) << ", "
<< Translation->GetUVW(3) << " ft/s");
<< Propagate->GetUVW(1) << ", "
<< Propagate->GetUVW(2) << ", "
<< Propagate->GetUVW(3) << " ft/s");
break;
case setmach:
SG_LOG(SG_FLIGHT,SG_INFO, " Mach: "
<< Translation->GetMach() );
<< Auxiliary->GetMach() );
break;
case setvc:
default:
@ -313,17 +314,17 @@ void FGJSBsim::init()
stall_warning->setDoubleValue(0);
SG_LOG( SG_FLIGHT, SG_INFO, " Bank Angle: "
<< Rotation->Getphi()*RADTODEG << " deg" );
<< Propagate->Getphi()*RADTODEG << " deg" );
SG_LOG( SG_FLIGHT, SG_INFO, " Pitch Angle: "
<< Rotation->Gettht()*RADTODEG << " deg" );
<< Propagate->Gettht()*RADTODEG << " deg" );
SG_LOG( SG_FLIGHT, SG_INFO, " True Heading: "
<< Rotation->Getpsi()*RADTODEG << " deg" );
<< Propagate->Getpsi()*RADTODEG << " deg" );
SG_LOG( SG_FLIGHT, SG_INFO, " Latitude: "
<< Position->GetLatitude() << " deg" );
<< Propagate->GetLocation().GetLatitudeDeg() << " deg" );
SG_LOG( SG_FLIGHT, SG_INFO, " Longitude: "
<< Position->GetLongitude() << " deg" );
<< Propagate->GetLocation().GetLongitudeDeg() << " deg" );
SG_LOG( SG_FLIGHT, SG_INFO, " Altitude: "
<< Position->Geth() << " feet" );
<< Propagate->Geth() << " feet" );
SG_LOG( SG_FLIGHT, SG_INFO, " loaded initial conditions" );
SG_LOG( SG_FLIGHT, SG_INFO, " set dt" );
@ -441,16 +442,16 @@ bool FGJSBsim::copy_to_JSBsim()
eng->SetMagnetos( globals->get_controls()->get_magnetos(i) );
break;
} // end FGPiston code block
case FGEngine::etSimTurbine:
{ // FGSimTurbine code block
FGSimTurbine* eng = (FGSimTurbine*)Propulsion->GetEngine(i);
case FGEngine::etTurbine:
{ // FGTurbine code block
FGTurbine* eng = (FGTurbine*)Propulsion->GetEngine(i);
eng->SetAugmentation( globals->get_controls()->get_augmentation(i) );
eng->SetReverse( globals->get_controls()->get_reverser(i) );
eng->SetInjection( globals->get_controls()->get_water_injection(i) );
eng->SetCutoff( globals->get_controls()->get_cutoff(i) );
eng->SetIgnition( globals->get_controls()->get_ignition(i) );
break;
} // end FGSimTurbine code block
} // end FGTurbine code block
case FGEngine::etRocket:
{ // FGRocket code block
FGRocket* eng = (FGRocket*)Propulsion->GetEngine(i);
@ -466,9 +467,10 @@ bool FGJSBsim::copy_to_JSBsim()
} // end FGEngine code block
}
_set_Runway_altitude( cur_fdm_state->get_Runway_altitude() );
Position->SetSeaLevelRadius( get_Sea_level_radius() );
Position->SetRunwayRadius( get_Runway_altitude()
Propagate->SetSeaLevelRadius( get_Sea_level_radius() );
Propagate->SetRunwayRadius( get_Runway_altitude()
+ get_Sea_level_radius() );
Atmosphere->SetExTemperature(
@ -496,6 +498,8 @@ bool FGJSBsim::copy_to_JSBsim()
tank->SetContents(node->getDoubleValue("level-gal_us") * 6.6);
// tank->SetContents(node->getDoubleValue("level-lb"));
}
SGPropertyNode* node = fgGetNode("/systems/refuel", true);
Propulsion->SetRefuel(node->getDoubleValue("contact"));
return true;
}
@ -534,72 +538,62 @@ bool FGJSBsim::copy_from_JSBsim()
// Velocities
_set_Velocities_Local( Position->GetVn(),
Position->GetVe(),
Position->GetVd() );
_set_Velocities_Local( Propagate->GetVel(eNorth),
Propagate->GetVel(eEast),
Propagate->GetVel(eDown) );
_set_Velocities_Wind_Body( Translation->GetUVW(1),
Translation->GetUVW(2),
Translation->GetUVW(3) );
_set_Velocities_Wind_Body( Propagate->GetUVW(1),
Propagate->GetUVW(2),
Propagate->GetUVW(3) );
// Make the HUD work ...
_set_Velocities_Ground( Position->GetVn(),
Position->GetVe(),
-Position->GetVd() );
_set_Velocities_Ground( Propagate->GetVel(eNorth),
Propagate->GetVel(eEast),
-Propagate->GetVel(eDown) );
_set_V_rel_wind( Translation->GetVt() );
_set_V_rel_wind( Auxiliary->GetVt() );
_set_V_equiv_kts( Auxiliary->GetVequivalentKTS() );
_set_V_calibrated_kts( Auxiliary->GetVcalibratedKTS() );
_set_V_ground_speed( Position->GetVground() );
_set_V_ground_speed( Auxiliary->GetVground() );
_set_Omega_Body( Rotation->GetPQR(1),
Rotation->GetPQR(2),
Rotation->GetPQR(3) );
_set_Omega_Body( Propagate->GetPQR(eP),
Propagate->GetPQR(eQ),
Propagate->GetPQR(eR) );
_set_Euler_Rates( Rotation->GetEulerRates(1),
Rotation->GetEulerRates(2),
Rotation->GetEulerRates(3) );
_set_Euler_Rates( Auxiliary->GetEulerRates(ePhi),
Auxiliary->GetEulerRates(eTht),
Auxiliary->GetEulerRates(ePsi) );
_set_Geocentric_Rates(Position->GetLatitudeDot(),
Position->GetLongitudeDot(),
Position->Gethdot() );
_set_Mach_number( Translation->GetMach() );
// Positions
_updateGeocentricPosition( Position->GetLatitude(),
Position->GetLongitude(),
Position->Geth() );
_set_Mach_number( Auxiliary->GetMach() );
// Positions of Visual Reference Point
/*
_updateGeocentricPosition( Position->GetLatitudeVRP(),
Position->GetLongitudeVRP(),
Position->GethVRP() );
*/
_set_Altitude_AGL( Position->GetDistanceAGL() );
_updateGeocentricPosition( Auxiliary->GetLocationVRP().GetLatitude(),
Auxiliary->GetLocationVRP().GetLongitude(),
Auxiliary->GethVRP() );
_set_Euler_Angles( Rotation->Getphi(),
Rotation->Gettht(),
Rotation->Getpsi() );
_set_Altitude_AGL( Propagate->GetDistanceAGL() );
_set_Alpha( Translation->Getalpha() );
_set_Beta( Translation->Getbeta() );
_set_Euler_Angles( Propagate->Getphi(),
Propagate->Gettht(),
Propagate->Getpsi() );
_set_Alpha( Auxiliary->Getalpha() );
_set_Beta( Auxiliary->Getbeta() );
_set_Gamma_vert_rad( Position->GetGamma() );
_set_Gamma_vert_rad( Auxiliary->GetGamma() );
_set_Earth_position_angle( Auxiliary->GetEarthPositionAngle() );
_set_Climb_Rate( Position->Gethdot() );
_set_Climb_Rate( Propagate->Gethdot() );
const FGMatrix33& Tl2b = Propagate->GetTl2b();
for ( i = 1; i <= 3; i++ ) {
for ( j = 1; j <= 3; j++ ) {
_set_T_Local_to_Body( i, j, State->GetTl2b(i,j) );
_set_T_Local_to_Body( i, j, Tl2b(i,j) );
}
}
@ -609,7 +603,7 @@ bool FGJSBsim::copy_from_JSBsim()
char buf[30];
sprintf(buf, "engines/engine[%d]/thruster", i);
SGPropertyNode * tnode = fgGetNode(buf, true);
FGThruster * thruster = Propulsion->GetThruster(i);
FGThruster * thruster = Propulsion->GetEngine(i)->GetThruster();
switch (Propulsion->GetEngine(i)->GetType()) {
case FGEngine::etPiston:
@ -628,9 +622,9 @@ bool FGJSBsim::copy_from_JSBsim()
FGRocket* eng = (FGRocket*)Propulsion->GetEngine(i);
} // end FGRocket code block
break;
case FGEngine::etSimTurbine:
{ // FGSimTurbine code block
FGSimTurbine* eng = (FGSimTurbine*)Propulsion->GetEngine(i);
case FGEngine::etTurbine:
{ // FGTurbine code block
FGTurbine* eng = (FGTurbine*)Propulsion->GetEngine(i);
node->setDoubleValue("n1", eng->GetN1());
node->setDoubleValue("n2", eng->GetN2());
node->setDoubleValue("egt_degf", 32 + eng->GetEGT()*9/5);
@ -642,11 +636,18 @@ bool FGJSBsim::copy_from_JSBsim()
node->setDoubleValue("oil-pressure-psi", eng->getOilPressure_psi());
node->setBoolValue("reversed", eng->GetReversed());
node->setBoolValue("cutoff", eng->GetCutoff());
node->setDoubleValue("epr", eng->GetEPR());
globals->get_controls()->set_reverser(i, eng->GetReversed() );
globals->get_controls()->set_cutoff(i, eng->GetCutoff() );
globals->get_controls()->set_water_injection(i, eng->GetInjection() );
globals->get_controls()->set_augmentation(i, eng->GetAugmentation() );
} // end FGSimTurbine code block
} // end FGTurbine code block
break;
case FGEngine::etElectric:
{ // FGElectric code block
FGElectric* eng = (FGElectric*)Propulsion->GetEngine(i);
node->setDoubleValue("rpm", eng->getRPM());
} // end FGElectric code block
break;
}
@ -695,10 +696,12 @@ bool FGJSBsim::copy_from_JSBsim()
if ( ! fuel_freeze->getBoolValue() ) {
for (i = 0; i < Propulsion->GetNumTanks(); i++) {
SGPropertyNode * node = fgGetNode("/consumables/fuel/tank", i, true);
double contents = Propulsion->GetTank(i)->GetContents();
FGTank* tank = Propulsion->GetTank(i);
double contents = tank->GetContents();
double temp = tank->GetTemperature_degC();
node->setDoubleValue("level-gal_us", contents/6.6);
node->setDoubleValue("level-lb", contents);
// node->setDoubleValue("temperature_degC",
if (temp != -9999.0) node->setDoubleValue("temperature_degC", temp);
}
}

View file

@ -66,9 +66,7 @@ class FGMassBalance;
class FGAerodynamics;
class FGInertial;
class FGAircraft;
class FGTranslation;
class FGRotation;
class FGPosition;
class FGPropagate;
class FGAuxiliary;
class FGOutput;
class FGInitialCondition;
@ -116,17 +114,17 @@ public:
//@{
/** Set geocentric latitude
@param lat latitude in radians measured from the 0 meridian where
the westerly direction is positive and east is negative */
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 */
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 */
@param alt altitude in feet */
void set_Altitude(double alt); // triggers re-calc of AGL altitude
//@}
@ -136,33 +134,33 @@ public:
//@{
/** Sets calibrated airspeed
Setting this will trigger a recalc of the other velocity terms.
@param vc Calibrated airspeed in ft/sec */
@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 */
@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 */
@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 */
@param u X velocity in ft/sec
@param v Y velocity in ft/sec
@param w Z velocity in ft/sec */
void set_Velocities_Wind_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 */
@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
@ -218,9 +216,7 @@ private:
FGPropulsion* Propulsion;
FGMassBalance* MassBalance;
FGAircraft* Aircraft;
FGTranslation* Translation;
FGRotation* Rotation;
FGPosition* Position;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGAerodynamics* Aerodynamics;
FGGroundReactions *GroundReactions;

View file

@ -11,9 +11,7 @@ libJSBSim_a_SOURCES = \
FGAuxiliary.cpp FGAuxiliary.h \
FGCoefficient.cpp FGCoefficient.h \
FGColumnVector3.cpp FGColumnVector3.h \
FGColumnVector4.cpp FGColumnVector4.h \
FGConfigFile.cpp FGConfigFile.h \
FGDefs.h \
FGFCS.cpp FGFCS.h \
FGFDMExec.cpp FGFDMExec.h \
FGFactorGroup.cpp FGFactorGroup.h \
@ -31,25 +29,24 @@ libJSBSim_a_SOURCES = \
FGPiston.cpp FGPiston.h \
FGPropeller.cpp FGPropeller.h \
FGPropulsion.cpp FGPropulsion.h \
FGPosition.cpp FGPosition.h \
FGRotation.cpp FGRotation.h \
FGRotor.cpp FGRotor.h \
FGRocket.cpp FGRocket.h \
FGScript.cpp FGScript.h \
FGState.cpp FGState.h \
FGTable.cpp FGTable.h \
FGThruster.cpp FGThruster.h \
FGTranslation.cpp FGTranslation.h \
FGTrim.cpp FGTrim.h \
FGTrimAxis.cpp FGTrimAxis.h \
FGTurbine.cpp FGTurbine.h \
FGUtility.cpp FGUtility.h \
FGEngine.cpp FGEngine.h \
FGTank.cpp FGTank.h \
FGfdmSocket.cpp FGfdmSocket.h \
FGTurbine.cpp FGTurbine.h \
FGPropertyManager.cpp FGPropertyManager.h \
FGSimTurbine.cpp FGSimTurbine.h \
FGPropagate.cpp FGPropagate.h \
FGLocation.cpp FGLocation.h \
FGQuaternion.cpp FGQuaternion.h \
FGElectric.cpp FGElectric.h \
JSBSim.cxx JSBSim.hxx

View file

@ -115,7 +115,7 @@ FGFilter::FGFilter(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
denom = 2.00*C3 + dt*C4;
ca = (2.00*C1 + dt*C2) / denom;
cb = (dt*C2 - 2.00*C1) / denom;
cc = (2.00*C3 - dt*C2) / denom;
cc = (2.00*C3 - dt*C4) / denom;
break;
case eOrder2:
denom = 4.0*C4 + 2.0*C5*dt + C6*dt*dt;
@ -182,7 +182,7 @@ bool FGFilter::Run(void)
break;
case eOrder2:
Output = Input * ca + PreviousInput1 * cb + PreviousInput2 * cc
- PreviousOutput1 * cd - PreviousOutput2 * ce;
- PreviousOutput1 * cd - PreviousOutput2 * ce;
break;
case eWashout:
Output = Input * ca - PreviousInput1 * ca + PreviousOutput1 * cb;

View file

@ -64,6 +64,8 @@ FGGain::FGGain(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
OutputPct = 0;
invert = false;
ScheduledBy = 0;
clip = false;
clipmin = clipmax = 0.0;
Type = AC_cfg->GetValue("TYPE");
Name = AC_cfg->GetValue("NAME");
@ -91,11 +93,16 @@ FGGain::FGGain(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
*AC_cfg >> Min;
} else if (token == "MAX") {
*AC_cfg >> Max;
} else if (token == "CLIPTO") {
*AC_cfg >> clipmin >> clipmax;
if (clipmax > clipmin) {
clip = true;
}
} else if (token == "INVERT") {
invert = true;
cerr << endl << "The INVERT keyword is being deprecated and will not be "
"supported in the future. Please use a minus sign in front "
"of an input property in the future." << endl << endl;
"of an input property in the future." << endl << endl;
} else if (token == "ROWS") {
*AC_cfg >> Rows;
Table = new FGTable(Rows);
@ -106,8 +113,7 @@ FGGain::FGGain(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
} else if (token == "OUTPUT") {
IsOutput = true;
*AC_cfg >> sOutputIdx;
OutputNode = PropertyManager->GetNode( sOutputIdx );
OutputNode = PropertyManager->GetNode( sOutputIdx, true );
} else {
AC_cfg->ResetLineIndexToZero();
*Table << *AC_cfg;
@ -159,6 +165,11 @@ bool FGGain::Run(void )
}
if (clip) {
if (Output > clipmax) Output = clipmax;
else if (Output < clipmin) Output = clipmin;
}
if (IsOutput) SetOutput();
return true;

View file

@ -94,6 +94,7 @@ CLASS DOCUMENTATION
ROWS \<number_of_rows>
\<lookup_value gain_value>
?
[CLIPTO \<min> \<max> 1]
[OUTPUT \<property>]
\</COMPONENT>
</pre>
@ -181,8 +182,9 @@ private:
FGState* State;
double Gain;
double Min, Max;
double clipmin, clipmax;
double OutputPct;
bool invert;
bool invert, clip;
int Rows;
FGPropertyManager* ScheduledBy;

View file

@ -121,7 +121,7 @@ bool FGKinemat::Run(void )
Input = InputNodes[0]->getDoubleValue();
if (DoScale) Input *= Detents[NumDetents-1];
if (DoScale) Input *= Detents[NumDetents-1];
Output = OutputNode->getDoubleValue();
@ -132,7 +132,7 @@ bool FGKinemat::Run(void )
// Process all detent intervals the movement traverses until either the
// final value is reached or the time interval has finished.
while (0.0 < dt && Input != Output) {
while ( 0.0 < dt && !EqualToRoundoff(Input, Output) ) {
// Find the area where Output is in
int ind;
@ -154,16 +154,20 @@ bool FGKinemat::Run(void )
if (Detents[ind] < ThisInput) ThisInput = Detents[ind];
// Compute the time to reach the value in ThisInput
double ThisDt = fabs((ThisInput-Output)/Rate);
if (ThisDt == 0.0)
break;
// and clip to the timestep size
if (dt < ThisDt) ThisDt = dt;
if (dt < ThisDt) {
ThisDt = dt;
if (Output < Input)
Output += ThisDt*Rate;
else
Output -= ThisDt*Rate;
} else
// Handle this case separate to make shure the termination condition
// is met even in inexact arithmetics ...
Output = ThisInput;
dt -= ThisDt;
// Do the output calculation
if (Output < Input)
Output += ThisDt*Rate;
else
Output -= ThisDt*Rate;
}
}

View file

@ -77,11 +77,29 @@ CLASS DECLARATION
class FGKinemat : public FGFCSComponent {
public:
/** Initializer.
@param fcs A reference to the ccurrent flightcontrolsystem.
@param AC_cfg reference to the current aircraft configuration file.
Initializes the FGKinemat object from the given configuration
file. The Configuration file is expected to be at the stream
position where the KINEMAT object starts. Also it is expected to
be past the end of the current KINEMAT configuration on exit.
*/
FGKinemat(FGFCS* fcs, FGConfigFile* AC_cfg);
/** Destructor.
*/
~FGKinemat();
/** Kinemat output value.
@return the current output of the kinemat object on the range of [0,1].
*/
double GetOutputPct() const { return OutputPct; }
/** Run method, overwrites FGModel::Run().
@return false on success, true on failure.
The routine doing the work.
*/
bool Run (void);
private:

View file

@ -87,7 +87,7 @@ FGSummer::FGSummer(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
} else if (token == "OUTPUT") {
IsOutput = true;
*AC_cfg >> sOutputIdx;
OutputNode = PropertyManager->GetNode(sOutputIdx);
OutputNode = PropertyManager->GetNode(sOutputIdx, true);
}
}

View file

@ -75,7 +75,7 @@ FGSwitch::FGSwitch(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
{
string token, value;
struct test *current_test;
struct FGCondition *current_condition;
string sOutputIdx;
Type = AC_cfg->GetValue("TYPE");
Name = AC_cfg->GetValue("NAME");
@ -124,8 +124,13 @@ FGSwitch::FGSwitch(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
while (AC_cfg->GetValue() != string("/TEST")) {
current_test->conditions.push_back(FGCondition(AC_cfg, PropertyManager));
}
AC_cfg->GetNextConfigLine();
} else if (token == "OUTPUT") {
IsOutput = true;
*AC_cfg >> sOutputIdx;
*AC_cfg >> sOutputIdx;
OutputNode = PropertyManager->GetNode( sOutputIdx, true );
}
AC_cfg->GetNextConfigLine();
}
FGFCSComponent::bind();
@ -178,6 +183,8 @@ bool FGSwitch::Run(void )
*iTests++;
}
if (IsOutput) SetOutput();
return true;
}
@ -256,6 +263,7 @@ void FGSwitch::Debug(int from)
cout << endl;
*iTests++;
}
if (IsOutput) cout << " OUTPUT: " << OutputNode->getName() << endl;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification

View file

@ -86,7 +86,7 @@ additional conditions, as well as possibly additional CONDITION_GROUPs.
<pre>
\<COMPONENT NAME="switch1" TYPE="SWITCH"\>
\<TEST LOGIC="{AND|OR|DEFAULT}" OUTPUT="{property|value}"\>
\<TEST LOGIC="{AND|OR|DEFAULT}" VALUE="{property|value}"\>
{property} {conditional} {property|value}
\<CONDITION_GROUP LOGIC="{AND|OR}"\>
{property} {conditional} {property|value}
@ -94,11 +94,12 @@ additional conditions, as well as possibly additional CONDITION_GROUPs.
\</CONDITION_GROUP\>
...
\</TEST>
\<TEST LOGIC="{AND|OR}" OUTPUT="{property|value}"\>
\<TEST LOGIC="{AND|OR}" VALUE="{property|value}"\>
{property} {conditional} {property|value}
...
\</TEST\>
...
[OUTPUT \<property>]
\</COMPONENT\>
</pre>