1
0
Fork 0

New version of JSBSim, a big rewrite.

This commit is contained in:
Erik Hofman 2011-09-11 11:42:21 +02:00
parent 549c5eccb9
commit 7375166c2b
89 changed files with 4428 additions and 3364 deletions

View file

@ -1,5 +1,86 @@
include(FlightGearComponent)
set(HEADERS
FGFDMExec.h
FGJSBBase.h
JSBSim.hxx
initialization/FGInitialCondition.h
initialization/FGTrim.h
initialization/FGTrimAxis.h
input_output/FGXMLParse.h
input_output/FGXMLFileRead.h
input_output/FGPropertyManager.h
input_output/FGScript.h
input_output/FGfdmSocket.h
input_output/string_utilities.h
input_output/FGXMLElement.h
input_output/net_fdm.hxx
input_output/FGGroundCallback.h
math/FGParameter.h
math/LagrangeMultiplier.h
math/FGColumnVector3.h
math/FGCondition.h
math/FGFunction.h
math/FGLocation.h
math/FGMatrix33.h
math/FGModelFunctions.h
math/FGPropertyValue.h
math/FGQuaternion.h
math/FGRealValue.h
math/FGRungeKutta.h
math/FGTable.h
models/FGAccelerations.h
models/FGAerodynamics.h
models/FGAircraft.h
models/FGAtmosphere.h
models/FGAuxiliary.h
models/FGBuoyantForces.h
models/FGExternalForce.h
models/FGExternalReactions.h
models/FGFCS.h
models/FGGasCell.h
models/FGGroundReactions.h
models/FGInertial.h
models/FGInput.h
models/FGLGear.h
models/FGMassBalance.h
models/FGModel.h
models/FGOutput.h
models/FGPropagate.h
models/FGPropulsion.h
models/atmosphere/FGMSIS.h
models/atmosphere/FGStandardAtmosphere.h
models/atmosphere/FGMars.h
models/atmosphere/FGWinds.h
models/flight_control/FGAccelerometer.h
models/flight_control/FGActuator.h
models/flight_control/FGDeadBand.h
models/flight_control/FGFCSComponent.h
models/flight_control/FGFCSFunction.h
models/flight_control/FGFilter.h
models/flight_control/FGGain.h
models/flight_control/FGGyro.h
models/flight_control/FGKinemat.h
models/flight_control/FGMagnetometer.h
models/flight_control/FGPID.h
models/flight_control/FGSensor.h
models/flight_control/FGSensorOrientation.h
models/flight_control/FGSummer.h
models/flight_control/FGSwitch.h
models/propulsion/FGElectric.h
models/propulsion/FGEngine.h
models/propulsion/FGForce.h
models/propulsion/FGNozzle.h
models/propulsion/FGPiston.h
models/propulsion/FGPropeller.h
models/propulsion/FGRocket.h
models/propulsion/FGRotor.h
models/propulsion/FGTank.h
models/propulsion/FGThruster.h
models/propulsion/FGTurbine.h
models/propulsion/FGTurboProp.h
)
set(SOURCES
FGFDMExec.cpp
FGJSBBase.cpp
@ -24,6 +105,7 @@ set(SOURCES
math/FGRealValue.cpp
math/FGRungeKutta.cpp
math/FGTable.cpp
models/FGAccelerations.cpp
models/FGAerodynamics.cpp
models/FGAircraft.cpp
models/FGAtmosphere.cpp
@ -44,7 +126,9 @@ set(SOURCES
models/FGPropulsion.cpp
models/atmosphere/FGMSIS.cpp
models/atmosphere/FGMSISData.cpp
models/atmosphere/FGStandardAtmosphere.cpp
models/atmosphere/FGMars.cpp
models/atmosphere/FGWinds.cpp
models/flight_control/FGAccelerometer.cpp
models/flight_control/FGActuator.cpp
models/flight_control/FGDeadBand.cpp
@ -52,7 +136,6 @@ set(SOURCES
models/flight_control/FGFCSFunction.cpp
models/flight_control/FGFilter.cpp
models/flight_control/FGGain.cpp
models/flight_control/FGGradient.cpp
models/flight_control/FGGyro.cpp
models/flight_control/FGKinemat.cpp
models/flight_control/FGMagnetometer.cpp
@ -76,4 +159,4 @@ set(SOURCES
include_directories(${PROJECT_SOURCE_DIR}/src/FDM/JSBSim)
flightgear_component(JSBSim "${SOURCES}")
flightgear_component(JSBSim "${SOURCES}" "${HEADERS}")

View file

@ -41,10 +41,13 @@ COMMENTS, REFERENCES, and NOTES
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <iterator>
#include <cstdlib>
#include "FGFDMExec.h"
#include "models/FGAtmosphere.h"
#include "models/atmosphere/FGMSIS.h"
#include "models/atmosphere/FGMars.h"
#include "models/atmosphere/FGStandardAtmosphere.h"
#include "models/atmosphere/FGWinds.h"
#include "models/FGFCS.h"
#include "models/FGPropulsion.h"
#include "models/FGMassBalance.h"
@ -54,24 +57,20 @@ INCLUDES
#include "models/FGAerodynamics.h"
#include "models/FGInertial.h"
#include "models/FGAircraft.h"
#include "models/FGAccelerations.h"
#include "models/FGPropagate.h"
#include "models/FGAuxiliary.h"
#include "models/FGInput.h"
#include "models/FGOutput.h"
#include "initialization/FGInitialCondition.h"
//#include "initialization/FGTrimAnalysis.h" // Remove until later
#include "input_output/FGPropertyManager.h"
#include "input_output/FGScript.h"
#include <iostream>
#include <iterator>
#include <cstdlib>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.95 2011/05/20 10:35:25 jberndt Exp $";
static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.113 2011/09/07 02:37:04 jberndt Exp $";
static const char *IdHdr = ID_FDMEXEC;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -87,19 +86,6 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root)
Frame = 0;
Error = 0;
GroundCallback = 0;
Atmosphere = 0;
FCS = 0;
Propulsion = 0;
MassBalance = 0;
Aerodynamics = 0;
Inertial = 0;
GroundReactions = 0;
ExternalReactions = 0;
BuoyantForces = 0;
Aircraft = 0;
Propagate = 0;
Auxiliary = 0;
Input = 0;
IC = 0;
Trim = 0;
Script = 0;
@ -111,6 +97,7 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root)
holding = false;
Terminate = false;
StandAlone = false;
firstPass = true;
sim_time = 0.0;
dT = 1.0/120.0; // a default timestep size. This is needed for when JSBSim is
@ -197,7 +184,7 @@ FGFDMExec::~FGFDMExec()
PropertyCatalog.clear();
FDMctr--;
if (FDMctr > 0) (*FDMctr)--;
Debug(1);
}
@ -208,44 +195,54 @@ bool FGFDMExec::Allocate(void)
{
bool result=true;
Atmosphere = new FGAtmosphere(this);
FCS = new FGFCS(this);
Propulsion = new FGPropulsion(this);
MassBalance = new FGMassBalance(this);
Aerodynamics = new FGAerodynamics (this);
Inertial = new FGInertial(this);
Models.resize(eNumStandardModels);
GroundCallback = new FGGroundCallback(Inertial->GetRefRadius());
// See the eModels enum specification in the header file. The order of the enums
// specifies the order of execution. The Models[] vector is the primary
// storage array for the list of models.
Models[ePropagate] = new FGPropagate(this);
Models[eInput] = new FGInput(this);
Models[eInertial] = new FGInertial(this);
Models[eAtmosphere] = new FGStandardAtmosphere(this);
Models[eWinds] = new FGWinds(this);
Models[eAuxiliary] = new FGAuxiliary(this);
Models[eSystems] = new FGFCS(this);
Models[ePropulsion] = new FGPropulsion(this);
Models[eAerodynamics] = new FGAerodynamics (this);
GroundReactions = new FGGroundReactions(this);
ExternalReactions = new FGExternalReactions(this);
BuoyantForces = new FGBuoyantForces(this);
Aircraft = new FGAircraft(this);
Propagate = new FGPropagate(this);
Auxiliary = new FGAuxiliary(this);
Input = new FGInput(this);
GroundCallback = new FGGroundCallback(((FGInertial*)Models[eInertial])->GetRefRadius());
// Schedule a model. The second arg (the integer) is the pass number. For
// instance, the atmosphere model could get executed every fifth pass it is called.
Schedule(Input, 1); // Input model is Models[0]
Schedule(Atmosphere, 1); // Input model is Models[1]
Schedule(FCS, 1); // Input model is Models[2]
Schedule(Propulsion, 1); // Input model is Models[3]
Schedule(MassBalance, 1); // Input model is Models[4]
Schedule(Aerodynamics, 1); // Input model is Models[5]
Schedule(Inertial, 1); // Input model is Models[6]
Schedule(GroundReactions, 1); // Input model is Models[7]
Schedule(ExternalReactions, 1); // Input model is Models[8]
Schedule(BuoyantForces, 1); // Input model is Models[9]
Schedule(Aircraft, 1); // Input model is Models[10]
Schedule(Propagate, 1); // Input model is Models[11]
Schedule(Auxiliary, 1); // Input model is Models[12]
Models[eGroundReactions] = new FGGroundReactions(this);
Models[eExternalReactions] = new FGExternalReactions(this);
Models[eBuoyantForces] = new FGBuoyantForces(this);
Models[eMassBalance] = new FGMassBalance(this);
Models[eAircraft] = new FGAircraft(this);
Models[eAccelerations] = new FGAccelerations(this);
// Initialize models so they can communicate with each other
// Assign the Model shortcuts for internal executive use only.
Propagate = (FGPropagate*)Models[ePropagate];
Inertial = (FGInertial*)Models[eInertial];
Atmosphere = (FGAtmosphere*)Models[eAtmosphere];
Winds = (FGWinds*)Models[eWinds];
Auxiliary = (FGAuxiliary*)Models[eAuxiliary];
FCS = (FGFCS*)Models[eSystems];
Propulsion = (FGPropulsion*)Models[ePropulsion];
Aerodynamics = (FGAerodynamics*)Models[eAerodynamics];
GroundReactions = (FGGroundReactions*)Models[eGroundReactions];
ExternalReactions = (FGExternalReactions*)Models[eExternalReactions];
BuoyantForces = (FGBuoyantForces*)Models[eBuoyantForces];
MassBalance = (FGMassBalance*)Models[eMassBalance];
Aircraft = (FGAircraft*)Models[eAircraft];
Accelerations = (FGAccelerations*)Models[eAccelerations];
vector <FGModel*>::iterator it;
for (it = Models.begin(); it != Models.end(); ++it) (*it)->InitModel();
// Initialize planet (environment) constants
LoadPlanetConstants();
// Initialize models
for (unsigned int i = 0; i < Models.size(); i++) {
LoadInputs(i);
Models[i]->InitModel();
}
IC = new FGInitialCondition(this);
@ -258,24 +255,14 @@ bool FGFDMExec::Allocate(void)
bool FGFDMExec::DeAllocate(void)
{
delete Input;
delete Atmosphere;
delete FCS;
delete Propulsion;
delete MassBalance;
delete Aerodynamics;
delete Inertial;
delete GroundReactions;
delete ExternalReactions;
delete BuoyantForces;
delete Aircraft;
delete Propagate;
delete Auxiliary;
delete Script;
for (unsigned int i=0; i<eNumStandardModels; i++) delete Models[i];
Models.clear();
for (unsigned i=0; i<Outputs.size(); i++) delete Outputs[i];
Outputs.clear();
delete Script;
delete IC;
delete Trim;
@ -283,23 +270,6 @@ bool FGFDMExec::DeAllocate(void)
Error = 0;
Input = 0;
Atmosphere = 0;
FCS = 0;
Propulsion = 0;
MassBalance = 0;
Aerodynamics = 0;
Inertial = 0;
GroundReactions = 0;
ExternalReactions = 0;
BuoyantForces = 0;
Aircraft = 0;
Propagate = 0;
Auxiliary = 0;
Script = 0;
Models.clear();
modelLoaded = false;
return modelLoaded;
}
@ -321,22 +291,252 @@ bool FGFDMExec::Run(void)
Debug(2);
for (unsigned int i=1; i<ChildFDMList.size(); i++) {
ChildFDMList[i]->AssignState(Propagate); // Transfer state to the child FDM
ChildFDMList[i]->AssignState( (FGPropagate*)Models[ePropagate] ); // Transfer state to the child FDM
ChildFDMList[i]->Run();
}
if (firstPass && !IntegrationSuspended()) {
// Outputs the initial conditions
for (unsigned int i = 0; i < Outputs.size(); i++)
Outputs[i]->Run(holding);
firstPass = false;
}
// returns true if success, false if complete
if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript();
vector <FGModel*>::iterator it;
for (it = Models.begin(); it != Models.end(); ++it) (*it)->Run(holding);
IncrTime();
for (unsigned int i = 0; i < Models.size(); i++) {
LoadInputs(i);
Models[i]->Run(holding);
}
if (Terminate) success = false;
return (success);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::LoadInputs(unsigned int idx)
{
switch(idx) {
case ePropagate:
Propagate->in.vPQRidot = Accelerations->GetPQRidot();
Propagate->in.vQtrndot = Accelerations->GetQuaterniondot();
Propagate->in.vUVWidot = Accelerations->GetUVWidot();
Propagate->in.DeltaT = dT;
break;
case eInput:
break;
case eInertial:
Inertial->in.Radius = Propagate->GetRadius();
Inertial->in.Latitude = Propagate->GetLatitude();
break;
case eAtmosphere:
Atmosphere->in.altitudeASL = Propagate->GetAltitudeASL();
break;
case eWinds:
Winds->in.AltitudeASL = Propagate->GetAltitudeASL();
Winds->in.DistanceAGL = Propagate->GetDistanceAGL();
Winds->in.Tl2b = Propagate->GetTl2b();
Winds->in.Tw2b = Auxiliary->GetTw2b();
Winds->in.V = Auxiliary->GetVt();
Winds->in.totalDeltaT = dT * Winds->GetRate();
break;
case eAuxiliary:
Auxiliary->in.Pressure = Atmosphere->GetPressure();
Auxiliary->in.Density = Atmosphere->GetDensity();
Auxiliary->in.DensitySL = Atmosphere->GetDensitySL();
Auxiliary->in.PressureSL = Atmosphere->GetPressureSL();
Auxiliary->in.Temperature = Atmosphere->GetTemperature();
Auxiliary->in.SoundSpeed = Atmosphere->GetSoundSpeed();
Auxiliary->in.KinematicViscosity = Atmosphere->GetKinematicViscosity();
Auxiliary->in.DistanceAGL = Propagate->GetDistanceAGL();
Auxiliary->in.Mass = MassBalance->GetMass();
Auxiliary->in.Tl2b = Propagate->GetTl2b();
Auxiliary->in.Tb2l = Propagate->GetTb2l();
Auxiliary->in.vPQR = Propagate->GetPQR();
Auxiliary->in.vPQRdot = Accelerations->GetPQRdot();
Auxiliary->in.vUVW = Propagate->GetUVW();
Auxiliary->in.vUVWdot = Accelerations->GetUVWdot();
Auxiliary->in.vVel = Propagate->GetVel();
Auxiliary->in.vBodyAccel = Accelerations->GetBodyAccel();
Auxiliary->in.ToEyePt = MassBalance->StructuralToBody(Aircraft->GetXYZep());
Auxiliary->in.VRPBody = MassBalance->StructuralToBody(Aircraft->GetXYZvrp());
Auxiliary->in.RPBody = MassBalance->StructuralToBody(Aircraft->GetXYZrp());
Auxiliary->in.vFw = Aerodynamics->GetvFw();
Auxiliary->in.vLocation = Propagate->GetLocation();
Auxiliary->in.Latitude = Propagate->GetLatitude();
Auxiliary->in.Longitude = Propagate->GetLongitude();
Auxiliary->in.CosTht = Propagate->GetCosEuler(eTht);
Auxiliary->in.SinTht = Propagate->GetSinEuler(eTht);
Auxiliary->in.CosPhi = Propagate->GetCosEuler(ePhi);
Auxiliary->in.SinPhi = Propagate->GetSinEuler(ePhi);
Auxiliary->in.Psi = Propagate->GetEuler(ePsi);
Auxiliary->in.TotalWindNED = Winds->GetTotalWindNED();
Auxiliary->in.TurbPQR = Winds->GetTurbPQR();
Auxiliary->in.WindPsi = Winds->GetWindPsi();
Auxiliary->in.Vwind = Winds->GetTotalWindNED().Magnitude();
break;
case eSystems:
// Dynamic inputs come into the components that FCS manages through properties
break;
case ePropulsion:
Propulsion->in.SLPressure = Atmosphere->GetPressureSL();
Propulsion->in.Pressure = Atmosphere->GetPressure();
Propulsion->in.PressureRatio = Atmosphere->GetPressureRatio();
Propulsion->in.Temperature = Atmosphere->GetTemperature();
Propulsion->in.DensityRatio = Atmosphere->GetDensityRatio();
Propulsion->in.Density = Atmosphere->GetDensity();
Propulsion->in.Soundspeed = Atmosphere->GetSoundSpeed();
Propulsion->in.TotalPressure = Auxiliary->GetTotalPressure();
Propulsion->in.TotalTempearture = Auxiliary->GetTotalTemperature();
Propulsion->in.Vc = Auxiliary->GetVcalibratedKTS();
Propulsion->in.Vt = Auxiliary->GetVt();
Propulsion->in.qbar = Auxiliary->Getqbar();
Propulsion->in.TAT_c = Auxiliary->GetTAT_C();
Propulsion->in.AeroUVW = Auxiliary->GetAeroUVW();
Propulsion->in.AeroPQR = Auxiliary->GetAeroPQR();
Propulsion->in.alpha = Auxiliary->Getalpha();
Propulsion->in.beta = Auxiliary->Getbeta();
Propulsion->in.TotalDeltaT = dT * Propulsion->GetRate();
Propulsion->in.ThrottlePos = FCS->GetThrottlePos();
Propulsion->in.MixturePos = FCS->GetMixturePos();
Propulsion->in.ThrottleCmd = FCS->GetThrottleCmd();
Propulsion->in.MixtureCmd = FCS->GetMixtureCmd();
Propulsion->in.PropAdvance = FCS->GetPropAdvance();
Propulsion->in.PropFeather = FCS->GetPropFeather();
Propulsion->in.H_agl = Propagate->GetDistanceAGL();
Propulsion->in.PQR = Propagate->GetPQR();
break;
case eAerodynamics:
Aerodynamics->in.Alpha = Auxiliary->Getalpha();
Aerodynamics->in.Beta = Auxiliary->Getbeta();
Aerodynamics->in.Qbar = Auxiliary->Getqbar();
Aerodynamics->in.Vt = Auxiliary->GetVt();
Aerodynamics->in.Tb2w = Auxiliary->GetTb2w();
Aerodynamics->in.Tw2b = Auxiliary->GetTw2b();
Aerodynamics->in.RPBody = MassBalance->StructuralToBody(Aircraft->GetXYZrp());
break;
case eGroundReactions:
// There are no external inputs to this model.
GroundReactions->in.Vground = Auxiliary->GetVground();
GroundReactions->in.VcalibratedKts = Auxiliary->GetVcalibratedKTS();
GroundReactions->in.Temperature = Atmosphere->GetTemperature();
GroundReactions->in.TakeoffThrottle = (FCS->GetThrottlePos().size() > 0) ? (FCS->GetThrottlePos(0) > 0.90) : false;
GroundReactions->in.SteerPosDeg = FCS->GetSteerPosDeg();
GroundReactions->in.BrakePos = FCS->GetBrakePos();
GroundReactions->in.FCSGearPos = FCS->GetGearPos();
GroundReactions->in.EmptyWeight = MassBalance->GetEmptyWeight();
GroundReactions->in.Tb2l = Propagate->GetTb2l();
GroundReactions->in.Tec2l = Propagate->GetTec2l();
GroundReactions->in.Tec2b = Propagate->GetTec2b();
GroundReactions->in.PQR = Propagate->GetPQR();
GroundReactions->in.UVW = Propagate->GetUVW();
GroundReactions->in.DistanceAGL = Propagate->GetDistanceAGL();
GroundReactions->in.DistanceASL = Propagate->GetAltitudeASL();
GroundReactions->in.TotalDeltaT = dT * GroundReactions->GetRate();
GroundReactions->in.WOW = GroundReactions->GetWOW();
GroundReactions->in.Location = Propagate->GetLocation();
for (unsigned int i=0; i<GroundReactions->GetNumGearUnits(); i++) {
GroundReactions->in.vWhlBodyVec[i] = MassBalance->StructuralToBody(GroundReactions->GetGearUnit(i)->GetLocation());
}
break;
case eExternalReactions:
// There are no external inputs to this model.
break;
case eBuoyantForces:
BuoyantForces->in.Density = Atmosphere->GetDensity();
BuoyantForces->in.Pressure = Atmosphere->GetPressure();
BuoyantForces->in.Temperature = Atmosphere->GetTemperature();
BuoyantForces->in.gravity = Inertial->gravity();
break;
case eMassBalance:
MassBalance->in.GasInertia = BuoyantForces->GetGasMassInertia();
MassBalance->in.GasMass = BuoyantForces->GetGasMass();
MassBalance->in.GasMoment = BuoyantForces->GetGasMassMoment();
MassBalance->in.TanksWeight = Propulsion->GetTanksWeight();
MassBalance->in.TanksMoment = Propulsion->GetTanksMoment();
MassBalance->in.TankInertia = Propulsion->CalculateTankInertias();
break;
case eAircraft:
Aircraft->in.AeroForce = Aerodynamics->GetForces();
Aircraft->in.PropForce = Propulsion->GetForces();
Aircraft->in.GroundForce = GroundReactions->GetForces();
Aircraft->in.ExternalForce = ExternalReactions->GetForces();
Aircraft->in.BuoyantForce = BuoyantForces->GetForces();
Aircraft->in.AeroMoment = Aerodynamics->GetMoments();
Aircraft->in.PropMoment = Propulsion->GetMoments();
Aircraft->in.GroundMoment = GroundReactions->GetMoments();
Aircraft->in.ExternalMoment = ExternalReactions->GetMoments();
Aircraft->in.BuoyantMoment = BuoyantForces->GetMoments();
Aircraft->in.Weight = MassBalance->GetWeight();
Aircraft->in.Tl2b = Propagate->GetTl2b();
break;
case eAccelerations:
Accelerations->in.J = MassBalance->GetJ();
Accelerations->in.Jinv = MassBalance->GetJinv();
Accelerations->in.Ti2b = Propagate->GetTi2b();
Accelerations->in.Tb2i = Propagate->GetTb2i();
Accelerations->in.Tec2b = Propagate->GetTec2b();
Accelerations->in.Tl2b = Propagate->GetTl2b();
Accelerations->in.qAttitudeECI = Propagate->GetQuaternionECI();
Accelerations->in.Moment = Aircraft->GetMoments();
Accelerations->in.GroundMoment = GroundReactions->GetMoments();
Accelerations->in.Force = Aircraft->GetForces();
Accelerations->in.GroundForce = GroundReactions->GetForces();
Accelerations->in.GAccel = Inertial->GetGAccel(Propagate->GetRadius());
Accelerations->in.J2Grav = Inertial->GetGravityJ2(Propagate->GetLocation());
Accelerations->in.vPQRi = Propagate->GetPQRi();
Accelerations->in.vPQR = Propagate->GetPQR();
Accelerations->in.vUVW = Propagate->GetUVW();
Accelerations->in.vInertialPosition = Propagate->GetInertialPosition();
Accelerations->in.DeltaT = dT;
Accelerations->in.Mass = MassBalance->GetMass();
Accelerations->in.MultipliersList = GroundReactions->GetMultipliersList();
Accelerations->in.TerrainVelocity = Propagate->GetTerrainVelocity();
Accelerations->in.TerrainAngularVel = Propagate->GetTerrainAngularVelocity();
break;
default:
break;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::LoadPlanetConstants(void)
{
Propagate->in.vOmegaPlanet = Inertial->GetOmegaPlanet();
Accelerations->in.vOmegaPlanet = Inertial->GetOmegaPlanet();
Propagate->in.RefRadius = Inertial->GetRefRadius();
Propagate->in.SemiMajor = Inertial->GetSemimajor();
Propagate->in.SemiMinor = Inertial->GetSemiminor();
Auxiliary->in.SLGravity = Inertial->SLgravity();
Auxiliary->in.ReferenceRadius = Inertial->GetRefRadius();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::LoadModelConstants(void)
{
Winds->in.wingspan = Aircraft->GetWingSpan();
FCS->in.NumGear = GroundReactions->GetNumGearUnits();
Aerodynamics->in.Wingarea = Aircraft->GetWingArea();
Aerodynamics->in.Wingchord = Aircraft->Getcbar();
Aerodynamics->in.Wingincidence = Aircraft->GetWingIncidence();
Aerodynamics->in.Wingspan = Aircraft->GetWingSpan();
Auxiliary->in.Wingspan = Aircraft->GetWingSpan();
Auxiliary->in.Wingchord = Aircraft->Getcbar();
for (unsigned int i=0; i<GroundReactions->GetNumGearUnits(); i++) {
GroundReactions->in.vWhlBodyVec[i] = MassBalance->StructuralToBody(GroundReactions->GetGearUnit(i)->GetLocation());
}
LoadPlanetConstants();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This call will cause the sim time to reset to 0.0
@ -357,12 +557,15 @@ void FGFDMExec::Initialize(FGInitialCondition *FGIC)
Setsim_time(0.0);
Propagate->SetInitialState( FGIC );
LoadInputs(eAccelerations);
Accelerations->Run(false);
LoadInputs(ePropagate);
Propagate->InitializeDerivatives();
LoadInputs(eAtmosphere);
Atmosphere->Run(false);
Atmosphere->SetWindNED( FGIC->GetWindNFpsIC(),
Winds->SetWindNED( FGIC->GetWindNFpsIC(),
FGIC->GetWindEFpsIC(),
FGIC->GetWindDFpsIC() );
Auxiliary->Run(false);
}
@ -397,6 +600,23 @@ void FGFDMExec::ResetToInitialConditions(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGFDMExec::SetOutputFileName(const string& fname)
{
if (Outputs.size() > 0) Outputs[0]->SetOutputFileName(fname);
else return false;
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGFDMExec::GetOutputFileName(void)
{
if (Outputs.size() > 0) return Outputs[0]->GetOutputFileName();
else return string("");
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::SetGroundCallback(FGGroundCallback* p)
{
delete GroundCallback;
@ -408,6 +628,7 @@ void FGFDMExec::SetGroundCallback(FGGroundCallback* p)
vector <string> FGFDMExec::EnumerateFDMs(void)
{
vector <string> FDMList;
FGAircraft* Aircraft = (FGAircraft*)Models[eAircraft];
FDMList.push_back(Aircraft->GetAircraftName());
@ -493,7 +714,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the metrics element. This element is REQUIRED.
element = document->FindElement("metrics");
if (element) {
result = Aircraft->Load(element);
result = ((FGAircraft*)Models[eAircraft])->Load(element);
if (!result) {
cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -506,7 +727,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the mass_balance element. This element is REQUIRED.
element = document->FindElement("mass_balance");
if (element) {
result = MassBalance->Load(element);
result = ((FGMassBalance*)Models[eMassBalance])->Load(element);
if (!result) {
cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -519,11 +740,12 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the ground_reactions element. This element is REQUIRED.
element = document->FindElement("ground_reactions");
if (element) {
result = GroundReactions->Load(element);
result = ((FGGroundReactions*)Models[eGroundReactions])->Load(element);
if (!result) {
cerr << endl << "Aircraft ground_reactions element has problems in file " << aircraftCfgFileName << endl;
return result;
}
((FGFCS*)Models[eSystems])->AddGear(((FGGroundReactions*)Models[eGroundReactions])->GetNumGearUnits());
} else {
cerr << endl << "No ground_reactions element was found in the aircraft config file." << endl;
return false;
@ -532,7 +754,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the external_reactions element. This element is OPTIONAL.
element = document->FindElement("external_reactions");
if (element) {
result = ExternalReactions->Load(element);
result = ((FGExternalReactions*)Models[eExternalReactions])->Load(element);
if (!result) {
cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -542,7 +764,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the buoyant_forces element. This element is OPTIONAL.
element = document->FindElement("buoyant_forces");
if (element) {
result = BuoyantForces->Load(element);
result = ((FGBuoyantForces*)Models[eBuoyantForces])->Load(element);
if (!result) {
cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -552,17 +774,19 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the propulsion element. This element is OPTIONAL.
element = document->FindElement("propulsion");
if (element) {
result = Propulsion->Load(element);
result = ((FGPropulsion*)Models[ePropulsion])->Load(element);
if (!result) {
cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl;
return result;
}
for (unsigned int i=0; i<((FGPropulsion*)Models[ePropulsion])->GetNumEngines(); i++)
((FGFCS*)Models[eSystems])->AddThrottle();
}
// Process the system element[s]. This element is OPTIONAL, and there may be more than one.
element = document->FindElement("system");
while (element) {
result = FCS->Load(element, FGFCS::stSystem);
result = ((FGFCS*)Models[eSystems])->Load(element, FGFCS::stSystem);
if (!result) {
cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -573,7 +797,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the autopilot element. This element is OPTIONAL.
element = document->FindElement("autopilot");
if (element) {
result = FCS->Load(element, FGFCS::stAutoPilot);
result = ((FGFCS*)Models[eSystems])->Load(element, FGFCS::stAutoPilot);
if (!result) {
cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -583,7 +807,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the flight_control element. This element is OPTIONAL.
element = document->FindElement("flight_control");
if (element) {
result = FCS->Load(element, FGFCS::stFCS);
result = ((FGFCS*)Models[eSystems])->Load(element, FGFCS::stFCS);
if (!result) {
cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -593,7 +817,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the aerodynamics element. This element is OPTIONAL, but almost always expected.
element = document->FindElement("aerodynamics");
if (element) {
result = Aerodynamics->Load(element);
result = ((FGAerodynamics*)Models[eAerodynamics])->Load(element);
if (!result) {
cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -605,7 +829,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the input element. This element is OPTIONAL.
element = document->FindElement("input");
if (element) {
result = Input->Load(element);
result = ((FGInput*)Models[eInput])->Load(element);
if (!result) {
cerr << endl << "Aircraft input element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -616,13 +840,12 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
unsigned int idx=0;
typedef double (FGOutput::*iOPMF)(void) const;
typedef int (FGFDMExec::*iOPV)(void) const;
typedef void (FGFDMExec::*vOPI)(int) const;
element = document->FindElement("output");
while (element) {
if (debug_lvl > 0) cout << endl << " Output data set: " << idx << " ";
FGOutput* Output = new FGOutput(this);
Output->InitModel();
Schedule(Output, 1);
Schedule(Output);
result = Output->Load(element);
if (!result) {
cerr << endl << "Aircraft output element has problems in file " << aircraftCfgFileName << endl;
@ -647,11 +870,16 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
}
}
// Since all vehicle characteristics have been loaded, place the values in the Inputs
// structure for the FGModel-derived classes.
LoadModelConstants();
modelLoaded = true;
if (debug_lvl > 0) {
MassBalance->Run(false); // Update all mass properties for the report.
MassBalance->GetMassPropertiesReport();
LoadInputs(eMassBalance); // Update all input mass properties for the report.
Models[eMassBalance]->Run(false); // Update all mass properties for the report.
((FGMassBalance*)Models[eMassBalance])->GetMassPropertiesReport();
cout << endl << fgblue << highint
<< "End of vehicle configuration loading." << endl
@ -667,6 +895,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
<< fgdef << endl;
}
for (unsigned int i=0; i< Models.size(); i++) LoadInputs(i);
if (result) {
struct PropertyCatalogStructure masterPCS;
masterPCS.base_string = "";
@ -679,6 +909,13 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGFDMExec::GetPropulsionTankReport()
{
return ((FGPropulsion*)Models[ePropulsion])->GetPropulsionTankReport();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs)
{
struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure;
@ -760,7 +997,7 @@ bool FGFDMExec::ReadPrologue(Element* el) // el for ReadPrologue is the document
if (!el) return false;
string AircraftName = el->GetAttributeValue("name");
Aircraft->SetAircraftName(AircraftName);
((FGAircraft*)Models[eAircraft])->SetAircraftName(AircraftName);
if (debug_lvl & 1) cout << underon << "Reading Aircraft Configuration File"
<< underoff << ": " << highint << AircraftName << normint << endl;
@ -899,7 +1136,7 @@ void FGFDMExec::EnableOutput(void)
void FGFDMExec::ForceOutput(int idx)
{
if (idx >= 0 && idx < Outputs.size()) Outputs[idx]->Print();
if (idx >= (int)0 && idx < (int)Outputs.size()) Outputs[idx]->Print();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -911,7 +1148,7 @@ bool FGFDMExec::SetOutputDirectives(const string& fname)
FGOutput* Output = new FGOutput(this);
Output->SetDirectivesFile(RootDir + fname);
Output->InitModel();
Schedule(Output, 1);
Schedule(Output);
result = Output->Load(0);
if (result) {
@ -943,38 +1180,6 @@ void FGFDMExec::DoTrim(int mode)
sim_time = saved_time;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::UseAtmosphereMSIS(void)
{
FGAtmosphere *oldAtmosphere = Atmosphere;
Atmosphere = new MSIS(this);
if (!Atmosphere->InitModel()) {
cerr << fgred << "MSIS Atmosphere model init failed" << fgdef << endl;
Error+=1;
}
Models[1] = Atmosphere; // Reassign the atmosphere model that has already been scheduled
// to the new atmosphere.
delete oldAtmosphere;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::UseAtmosphereMars(void)
{
/*
FGAtmosphere *oldAtmosphere = Atmosphere;
Atmosphere = new FGMars(this);
if (!Atmosphere->InitModel()) {
cerr << fgred << "Mars Atmosphere model init failed" << fgdef << endl;
Error+=1;
}
Models[1] = Atmosphere; // Reassign the atmosphere model that has already been scheduled
// to the new atmosphere.
delete oldAtmosphere;
*/
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
@ -1000,10 +1205,10 @@ void FGFDMExec::Debug(int from)
if (debug_lvl & 1 && IdFDM == 0) { // Standard console startup message output
if (from == 0) { // Constructor
cout << "\n\n " << highint << underon << "JSBSim Flight Dynamics Model v"
<< JSBSim_version << underoff << normint << endl;
cout << halfint << " [JSBSim-ML v" << needed_cfg_version << "]\n\n";
cout << normint << "JSBSim startup beginning ...\n\n";
cout << "\n\n "
<< "JSBSim Flight Dynamics Model v" << JSBSim_version << endl;
cout << " [JSBSim-ML v" << needed_cfg_version << "]\n\n";
cout << "JSBSim startup beginning ...\n\n";
} else if (from == 3) {
cout << "\n\nJSBSim startup complete\n\n";
}

View file

@ -44,8 +44,6 @@ INCLUDES
#include <vector>
#include <string>
#include "models/FGOutput.h"
#include "models/FGInput.h"
#include "initialization/FGTrim.h"
#include "FGJSBBase.h"
#include "input_output/FGPropertyManager.h"
@ -58,7 +56,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_FDMEXEC "$Id: FGFDMExec.h,v 1.64 2011/05/20 03:18:36 jberndt Exp $"
#define ID_FDMEXEC "$Id: FGFDMExec.h,v 1.71 2011/09/07 02:37:04 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -71,6 +69,8 @@ class FGTrim;
class FGAerodynamics;
class FGAircraft;
class FGAtmosphere;
class FGAccelerations;
class FGWinds;
class FGAuxiliary;
class FGBuoyantForces;
class FGExternalReactions;
@ -181,7 +181,7 @@ CLASS DOCUMENTATION
property actually maps toa function call of DoTrim().
@author Jon S. Berndt
@version $Revision: 1.64 $
@version $Revision: 1.71 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -224,6 +224,25 @@ public:
/// Default destructor
~FGFDMExec();
// This list of enums is very important! The order in which models are listed here
// determines the order of execution of the models.
enum eModels { ePropagate=0,
eInput,
eInertial,
eAtmosphere,
eWinds,
eAuxiliary,
eSystems,
ePropulsion,
eAerodynamics,
eGroundReactions,
eExternalReactions,
eBuoyantForces,
eMassBalance,
eAircraft,
eAccelerations,
eNumStandardModels };
/** Unbind all tied JSBSim properties. */
void Unbind(void) {instance->Unbind();}
@ -236,8 +255,9 @@ public:
one is at this time not recommended.
@param model A pointer to the model being scheduled.
@param rate The rate at which to execute the model as described above.
Default is every frame (rate=1).
@return Currently returns 0 always. */
void Schedule(FGModel* model, int rate);
void Schedule(FGModel* model, int rate=1);
/** This function executes each scheduled model in succession.
@return true if successful, false if sim should be ended */
@ -309,31 +329,35 @@ public:
/// @name Top-level executive State and Model retrieval mechanism
//@{
/// Returns the FGAtmosphere pointer.
FGAtmosphere* GetAtmosphere(void) {return Atmosphere;}
FGAtmosphere* GetAtmosphere(void) {return (FGAtmosphere*)Models[eAtmosphere];}
/// Returns the FGAccelerations pointer.
FGAccelerations* GetAccelerations(void) {return (FGAccelerations*)Models[eAccelerations];}
/// Returns the FGWinds pointer.
FGWinds* GetWinds(void) {return (FGWinds*)Models[eWinds];}
/// Returns the FGFCS pointer.
FGFCS* GetFCS(void) {return FCS;}
FGFCS* GetFCS(void) {return (FGFCS*)Models[eSystems];}
/// Returns the FGPropulsion pointer.
FGPropulsion* GetPropulsion(void) {return Propulsion;}
FGPropulsion* GetPropulsion(void) {return (FGPropulsion*)Models[ePropulsion];}
/// Returns the FGAircraft pointer.
FGMassBalance* GetMassBalance(void) {return MassBalance;}
FGMassBalance* GetMassBalance(void) {return (FGMassBalance*)Models[eMassBalance];}
/// Returns the FGAerodynamics pointer
FGAerodynamics* GetAerodynamics(void){return Aerodynamics;}
FGAerodynamics* GetAerodynamics(void){return (FGAerodynamics*)Models[eAerodynamics];}
/// Returns the FGInertial pointer.
FGInertial* GetInertial(void) {return Inertial;}
FGInertial* GetInertial(void) {return (FGInertial*)Models[eInertial];}
/// Returns the FGGroundReactions pointer.
FGGroundReactions* GetGroundReactions(void) {return GroundReactions;}
FGGroundReactions* GetGroundReactions(void) {return (FGGroundReactions*)Models[eGroundReactions];}
/// Returns the FGExternalReactions pointer.
FGExternalReactions* GetExternalReactions(void) {return ExternalReactions;}
FGExternalReactions* GetExternalReactions(void) {return (FGExternalReactions*)Models[eExternalReactions];}
/// Returns the FGBuoyantForces pointer.
FGBuoyantForces* GetBuoyantForces(void) {return BuoyantForces;}
FGBuoyantForces* GetBuoyantForces(void) {return (FGBuoyantForces*)Models[eBuoyantForces];}
/// Returns the FGAircraft pointer.
FGAircraft* GetAircraft(void) {return Aircraft;}
FGAircraft* GetAircraft(void) {return (FGAircraft*)Models[eAircraft];}
/// Returns the FGPropagate pointer.
FGPropagate* GetPropagate(void) {return Propagate;}
FGPropagate* GetPropagate(void) {return (FGPropagate*)Models[ePropagate];}
/// Returns the FGAuxiliary pointer.
FGAuxiliary* GetAuxiliary(void) {return Auxiliary;}
FGAuxiliary* GetAuxiliary(void) {return (FGAuxiliary*)Models[eAuxiliary];}
/// Returns the FGInput pointer.
FGInput* GetInput(void) {return Input;}
FGInput* GetInput(void) {return (FGInput*)Models[eInput];}
/// Returns the FGGroundCallback pointer.
FGGroundCallback* GetGroundCallback(void) {return GroundCallback;}
/// Retrieves the script object
@ -408,19 +432,12 @@ public:
/** Sets (or overrides) the output filename
@param fname the name of the file to output data to
@return true if successful, false if there is no output specified for the flight model */
bool SetOutputFileName(const string& fname) {
if (Outputs.size() > 0) Outputs[0]->SetOutputFileName(fname);
else return false;
return true;
}
bool SetOutputFileName(const string& fname);
/** Retrieves the current output filename.
@return the name of the output file for the first output specified by the flight model.
If none is specified, the empty string is returned. */
string GetOutputFileName(void) {
if (Outputs.size() > 0) return Outputs[0]->GetOutputFileName();
else return string("");
}
string GetOutputFileName(void);
/** Executes trimming in the selected mode.
* @param mode Specifies how to trim:
@ -474,17 +491,13 @@ public:
vector<string>& GetPropertyCatalog(void) {return PropertyCatalog;}
/// Use the MSIS atmosphere model.
void UseAtmosphereMSIS(void);
/// Use the Mars atmosphere model. (Not operative yet.)
void UseAtmosphereMars(void);
void SetTrimStatus(bool status){ trim_status = status; }
bool GetTrimStatus(void) const { return trim_status; }
void SetTrimMode(int mode){ ta_mode = mode; }
int GetTrimMode(void) const { return ta_mode; }
string GetPropulsionTankReport();
/// Returns the cumulative simulation time in seconds.
double GetSimTime(void) const { return sim_time; }
@ -545,6 +558,7 @@ private:
bool Constructing;
bool modelLoaded;
bool IsChild;
bool firstPass;
string modelName;
string AircraftPath;
string FullAircraftPath;
@ -554,23 +568,26 @@ private:
string Release;
string RootDir;
// Standard Model pointers - shortcuts for internal executive use only.
FGPropagate* Propagate;
FGInertial* Inertial;
FGAtmosphere* Atmosphere;
FGWinds* Winds;
FGAuxiliary* Auxiliary;
FGFCS* FCS;
FGPropulsion* Propulsion;
FGAerodynamics* Aerodynamics;
FGGroundReactions* GroundReactions;
FGExternalReactions* ExternalReactions;
FGBuoyantForces* BuoyantForces;
FGMassBalance* MassBalance;
FGAircraft* Aircraft;
FGAccelerations* Accelerations;
bool trim_status;
int ta_mode;
FGGroundCallback* GroundCallback;
FGAtmosphere* Atmosphere;
FGFCS* FCS;
FGPropulsion* Propulsion;
FGMassBalance* MassBalance;
FGAerodynamics* Aerodynamics;
FGInertial* Inertial;
FGGroundReactions* GroundReactions;
FGExternalReactions* ExternalReactions;
FGBuoyantForces* BuoyantForces;
FGAircraft* Aircraft;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGInput* Input;
FGScript* Script;
FGInitialCondition* IC;
FGTrim* Trim;
@ -591,6 +608,9 @@ private:
bool ReadChild(Element*);
bool ReadPrologue(Element*);
void ResetToInitialConditions(int mode);
void LoadInputs(unsigned int idx);
void LoadPlanetConstants(void);
void LoadModelConstants(void);
bool Allocate(void);
bool DeAllocate(void);
void Initialize(FGInitialCondition *FGIC);

View file

@ -44,7 +44,7 @@ INCLUDES
namespace JSBSim {
static const char *IdSrc = "$Id: FGJSBBase.cpp,v 1.30 2011/06/13 11:47:04 jberndt Exp $";
static const char *IdSrc = "$Id: FGJSBBase.cpp,v 1.31 2011/07/27 03:55:48 jberndt Exp $";
static const char *IdHdr = ID_JSBBASE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -228,7 +228,8 @@ FGJSBBase::Message* FGJSBBase::ProcessNextMessage(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGJSBBase::disableHighLighting(void) {
void FGJSBBase::disableHighLighting(void)
{
highint[0]='\0';
halfint[0]='\0';
normint[0]='\0';

View file

@ -43,27 +43,20 @@ INCLUDES
#include <string>
#include <cmath>
using std::min;
using std::max;
#include "input_output/string_utilities.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#if defined(_MSC_VER) && (_MSC_VER < 1300)
namespace std
{
template <class T> inline T max(const T& a, const T& b)
{
return (a > b) ? a : b;
}
}
#endif
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_JSBBASE "$Id: FGJSBBase.h,v 1.32 2011/06/13 11:47:04 jberndt Exp $"
#define ID_JSBBASE "$Id: FGJSBBase.h,v 1.33 2011/06/27 03:14:25 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -79,7 +72,7 @@ CLASS DOCUMENTATION
* This class provides universal constants, utility functions, messaging
* functions, and enumerated constants to JSBSim.
@author Jon S. Berndt
@version $Id: FGJSBBase.h,v 1.32 2011/06/13 11:47:04 jberndt Exp $
@version $Id: FGJSBBase.h,v 1.33 2011/06/27 03:14:25 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -269,7 +262,7 @@ public:
@return if the two values can be considered equal up to roundoff */
static bool EqualToRoundoff(double a, double b) {
double eps = 2.0*DBL_EPSILON;
return std::fabs(a - b) <= eps*std::max(std::fabs(a), std::fabs(b));
return std::fabs(a - b) <= eps * max(std::fabs(a), std::fabs(b));
}
/** Finite precision comparison.
@ -278,7 +271,7 @@ public:
@return if the two values can be considered equal up to roundoff */
static bool EqualToRoundoff(float a, float b) {
float eps = 2.0*FLT_EPSILON;
return std::fabs(a - b) <= eps*std::max(std::fabs(a), std::fabs(b));
return std::fabs(a - b) <= eps * max(std::fabs(a), std::fabs(b));
}
/** Finite precision comparison.

View file

@ -60,6 +60,8 @@
#include <FDM/JSBSim/models/FGLGear.h>
#include <FDM/JSBSim/models/FGGroundReactions.h>
#include <FDM/JSBSim/models/FGPropulsion.h>
#include <FDM/JSBSim/models/FGAccelerations.h>
#include <FDM/JSBSim/models/atmosphere/FGWinds.h>
#include <FDM/JSBSim/models/propulsion/FGEngine.h>
#include <FDM/JSBSim/models/propulsion/FGPiston.h>
#include <FDM/JSBSim/models/propulsion/FGTurbine.h>
@ -138,12 +140,11 @@ FGJSBsim::FGJSBsim( double dt )
{
bool result;
if( TURBULENCE_TYPE_NAMES.empty() ) {
TURBULENCE_TYPE_NAMES["ttNone"] = FGAtmosphere::ttNone;
TURBULENCE_TYPE_NAMES["ttStandard"] = FGAtmosphere::ttStandard;
// TURBULENCE_TYPE_NAMES["ttBerndt"] = FGAtmosphere::ttBerndt;
TURBULENCE_TYPE_NAMES["ttCulp"] = FGAtmosphere::ttCulp;
TURBULENCE_TYPE_NAMES["ttMilspec"] = FGAtmosphere::ttMilspec;
TURBULENCE_TYPE_NAMES["ttTustin"] = FGAtmosphere::ttTustin;
TURBULENCE_TYPE_NAMES["ttNone"] = FGWinds::ttNone;
TURBULENCE_TYPE_NAMES["ttStandard"] = FGWinds::ttStandard;
TURBULENCE_TYPE_NAMES["ttCulp"] = FGWinds::ttCulp;
TURBULENCE_TYPE_NAMES["ttMilspec"] = FGWinds::ttMilspec;
TURBULENCE_TYPE_NAMES["ttTustin"] = FGWinds::ttTustin;
}
// Set up the debugging level
@ -177,6 +178,7 @@ FGJSBsim::FGJSBsim( double dt )
fdmex->SetGroundCallback( new FGFSGroundCallback(this) );
Atmosphere = fdmex->GetAtmosphere();
Winds = fdmex->GetWinds();
FCS = fdmex->GetFCS();
MassBalance = fdmex->GetMassBalance();
Propulsion = fdmex->GetPropulsion();
@ -186,6 +188,7 @@ FGJSBsim::FGJSBsim( double dt )
Inertial = fdmex->GetInertial();
Aerodynamics = fdmex->GetAerodynamics();
GroundReactions = fdmex->GetGroundReactions();
Accelerations = fdmex->GetAccelerations();
fgic=fdmex->GetIC();
needTrim=true;
@ -307,6 +310,7 @@ FGJSBsim::FGJSBsim( double dt )
temperature = fgGetNode("/environment/temperature-degc",true);
pressure = fgGetNode("/environment/pressure-inhg",true);
pressureSL = fgGetNode("/environment/pressure-sea-level-inhg",true);
density = fgGetNode("/environment/density-slugft3",true);
ground_wind = fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt",true);
turbulence_gain = fgGetNode("/environment/turbulence/magnitude-norm",true);
@ -354,21 +358,17 @@ void FGJSBsim::init()
// init method first.
if (fgGetBool("/environment/params/control-fdm-atmosphere")) {
Atmosphere->UseExternal();
Atmosphere->SetExTemperature(
9.0/5.0*(temperature->getDoubleValue()+273.15) );
Atmosphere->SetExPressure(pressure->getDoubleValue()*70.726566);
Atmosphere->SetExDensity(density->getDoubleValue());
Atmosphere->SetTemperature(temperature->getDoubleValue(), get_Altitude(), FGAtmosphere::eCelsius);
Atmosphere->SetPressureSL(pressureSL->getDoubleValue(), FGAtmosphere::eInchesHg);
// initialize to no turbulence, these values get set in the update loop
Atmosphere->SetTurbType(FGAtmosphere::ttNone);
Atmosphere->SetTurbGain(0.0);
Atmosphere->SetTurbRate(0.0);
Atmosphere->SetWindspeed20ft(0.0);
Atmosphere->SetProbabilityOfExceedence(0.0);
} else {
Atmosphere->UseInternal();
Winds->SetTurbType(FGWinds::ttNone);
Winds->SetTurbGain(0.0);
Winds->SetTurbRate(0.0);
Winds->SetWindspeed20ft(0.0);
Winds->SetProbabilityOfExceedence(0.0);
}
fgic->SetSeaLevelRadiusFtIC( get_Sea_level_radius() );
fgic->SetWindNEDFpsIC( -wind_from_north->getDoubleValue(),
-wind_from_east->getDoubleValue(),
-wind_from_down->getDoubleValue() );
@ -409,14 +409,14 @@ void FGJSBsim::init()
if ( startup_trim->getBoolValue() ) {
FGLocation cart(fgic->GetLongitudeRadIC(), fgic->GetLatitudeRadIC(),
fgic->GetSeaLevelRadiusFtIC() + fgic->GetAltitudeASLFtIC());
get_Sea_level_radius() + fgic->GetAltitudeASLFtIC());
double cart_pos[3], contact[3], d[3], vel[3], agl;
update_ground_cache(cart, cart_pos, 0.01);
get_agl_ft(fdmex->GetSimTime(), cart_pos, SG_METER_TO_FEET*2, contact,
d, vel, d, &agl);
double terrain_alt = sqrt(contact[0]*contact[0] + contact[1]*contact[1]
+ contact[2]*contact[2]) - fgic->GetSeaLevelRadiusFtIC();
+ contact[2]*contact[2]) - get_Sea_level_radius();
SG_LOG(SG_FLIGHT, SG_INFO, "Ready to trim, terrain elevation is: "
<< terrain_alt * SG_METER_TO_FEET );
@ -664,30 +664,27 @@ bool FGJSBsim::copy_to_JSBsim()
Propagate->SetSeaLevelRadius( get_Sea_level_radius() );
Atmosphere->SetExTemperature(
9.0/5.0*(temperature->getDoubleValue()+273.15) );
Atmosphere->SetExPressure(pressure->getDoubleValue()*70.726566);
Atmosphere->SetExDensity(density->getDoubleValue());
Atmosphere->SetTemperature(temperature->getDoubleValue(), get_Altitude(), FGAtmosphere::eCelsius);
Atmosphere->SetPressureSL(pressureSL->getDoubleValue(), FGAtmosphere::eInchesHg);
Atmosphere->SetTurbType((FGAtmosphere::tType)TURBULENCE_TYPE_NAMES[turbulence_model->getStringValue()]);
switch( Atmosphere->GetTurbType() ) {
// case FGAtmosphere::ttBerndt:
case FGAtmosphere::ttStandard:
case FGAtmosphere::ttCulp: {
Winds->SetTurbType((FGWinds::tType)TURBULENCE_TYPE_NAMES[turbulence_model->getStringValue()]);
switch( Winds->GetTurbType() ) {
case FGWinds::ttStandard:
case FGWinds::ttCulp: {
double tmp = turbulence_gain->getDoubleValue();
Atmosphere->SetTurbGain(tmp * tmp * 100.0);
Atmosphere->SetTurbRate(turbulence_rate->getDoubleValue());
Winds->SetTurbGain(tmp * tmp * 100.0);
Winds->SetTurbRate(turbulence_rate->getDoubleValue());
break;
}
case FGAtmosphere::ttMilspec:
case FGAtmosphere::ttTustin: {
case FGWinds::ttMilspec:
case FGWinds::ttTustin: {
// milspec turbulence: 3=light, 4=moderate, 6=severe turbulence
// turbulence_gain normalized: 0: none, 1/3: light, 2/3: moderate, 3/3: severe
double tmp = turbulence_gain->getDoubleValue();
Atmosphere->SetProbabilityOfExceedence(
Winds->SetProbabilityOfExceedence(
SGMiscd::roundToInt(TurbulenceSeverityTable.GetValue( tmp ) )
);
Atmosphere->SetWindspeed20ft(ground_wind->getDoubleValue());
Winds->SetWindspeed20ft(ground_wind->getDoubleValue());
break;
}
@ -695,9 +692,9 @@ bool FGJSBsim::copy_to_JSBsim()
break;
}
Atmosphere->SetWindNED( -wind_from_north->getDoubleValue(),
-wind_from_east->getDoubleValue(),
-wind_from_down->getDoubleValue() );
Winds->SetWindNED( -wind_from_north->getDoubleValue(),
-wind_from_east->getDoubleValue(),
-wind_from_down->getDoubleValue() );
// SG_LOG(SG_FLIGHT,SG_INFO, "Wind NED: "
// << get_V_north_airmass() << ", "
// << get_V_east_airmass() << ", "
@ -739,19 +736,19 @@ bool FGJSBsim::copy_from_JSBsim()
MassBalance->GetXYZcg(2),
MassBalance->GetXYZcg(3) );
_set_Accels_Body( Aircraft->GetBodyAccel(1),
Aircraft->GetBodyAccel(2),
Aircraft->GetBodyAccel(3) );
_set_Accels_Body( Accelerations->GetBodyAccel(1),
Accelerations->GetBodyAccel(2),
Accelerations->GetBodyAccel(3) );
_set_Accels_CG_Body_N ( Aircraft->GetNcg(1),
Aircraft->GetNcg(2),
Aircraft->GetNcg(3) );
_set_Accels_CG_Body_N ( Auxiliary->GetNcg(1),
Auxiliary->GetNcg(2),
Auxiliary->GetNcg(3) );
_set_Accels_Pilot_Body( Auxiliary->GetPilotAccel(1),
Auxiliary->GetPilotAccel(2),
Auxiliary->GetPilotAccel(3) );
_set_Nlf( Aircraft->GetNlf() );
_set_Nlf( Auxiliary->GetNlf() );
// Velocities
@ -812,7 +809,7 @@ bool FGJSBsim::copy_from_JSBsim()
_set_Gamma_vert_rad( Auxiliary->GetGamma() );
_set_Earth_position_angle( Inertial->GetEarthPositionAngle() );
_set_Earth_position_angle( Propagate->GetEarthPositionAngle() );
_set_Climb_Rate( Propagate->Gethdot() );
@ -1349,7 +1346,7 @@ bool FGJSBsim::update_ground_cache(FGLocation cart, double* cart_pos, double dt)
<< fgic->GetAltitudeASLFtIC());
SG_LOG(SG_FLIGHT, SG_WARN, "sea level radius = "
<< fgic->GetSeaLevelRadiusFtIC());
<< get_Sea_level_radius());
SG_LOG(SG_FLIGHT, SG_WARN, "latitude = "
<< fgic->GetLatitudeRadIC());

View file

@ -58,6 +58,7 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGAtmosphere;
class FGWinds;
class FGFCS;
class FGPropulsion;
class FGMassBalance;
@ -69,6 +70,7 @@ class FGAuxiliary;
class FGOutput;
class FGInitialCondition;
class FGLocation;
class FGAccelerations;
}
// Adding it here will cause a namespace clash in FlightGear -EMH-
@ -221,16 +223,18 @@ private:
JSBSim::FGInitialCondition *fgic;
bool needTrim;
JSBSim::FGAtmosphere* Atmosphere;
JSBSim::FGFCS* FCS;
JSBSim::FGPropulsion* Propulsion;
JSBSim::FGMassBalance* MassBalance;
JSBSim::FGAircraft* Aircraft;
JSBSim::FGPropagate* Propagate;
JSBSim::FGAuxiliary* Auxiliary;
JSBSim::FGAerodynamics* Aerodynamics;
JSBSim::FGAtmosphere* Atmosphere;
JSBSim::FGWinds* Winds;
JSBSim::FGFCS* FCS;
JSBSim::FGPropulsion* Propulsion;
JSBSim::FGMassBalance* MassBalance;
JSBSim::FGAircraft* Aircraft;
JSBSim::FGPropagate* Propagate;
JSBSim::FGAuxiliary* Auxiliary;
JSBSim::FGAerodynamics* Aerodynamics;
JSBSim::FGGroundReactions* GroundReactions;
JSBSim::FGInertial* Inertial;
JSBSim::FGInertial* Inertial;
JSBSim::FGAccelerations* Accelerations;
int runcount;
double trim_elev;
@ -269,6 +273,7 @@ private:
SGPropertyNode_ptr temperature;
SGPropertyNode_ptr pressure;
SGPropertyNode_ptr pressureSL;
SGPropertyNode_ptr density;
SGPropertyNode_ptr ground_wind;
SGPropertyNode_ptr turbulence_gain;

View file

@ -44,6 +44,10 @@ you have chosen your IC's wisely) even after setting it up with this class.
INCLUDES
*******************************************************************************/
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "FGInitialCondition.h"
#include "FGFDMExec.h"
#include "math/FGQuaternion.h"
@ -51,17 +55,15 @@ INCLUDES
#include "models/FGAtmosphere.h"
#include "models/FGPropagate.h"
#include "models/FGPropulsion.h"
#include "models/FGFCS.h"
#include "input_output/FGPropertyManager.h"
#include "input_output/string_utilities.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.63 2011/06/13 10:30:22 bcoconni Exp $";
static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.69 2011/08/04 12:46:32 jberndt Exp $";
static const char *IdHdr = ID_INITIALCONDITION;
//******************************************************************************
@ -134,7 +136,7 @@ void FGInitialCondition::InitializeIC(void)
terrain_elevation = 0;
sea_level_radius = fdmex->GetInertial()->GetRefRadius();
position.SetPosition(0., 0., sea_level_radius);
position.SetEarthPositionAngle(fdmex->GetInertial()->GetEarthPositionAngle());
position.SetEarthPositionAngle(fdmex->GetPropagate()->GetEarthPositionAngle());
vUVW_NED.InitMatrix();
p=q=r=0;
vt=0;
@ -587,8 +589,9 @@ void FGInitialCondition::SetCrossWindKtsIC(double cross)
// Gram-Schmidt process is used to remove the existing cross wind component
_vWIND_NED -= DotProduct(_vWIND_NED, _vCROSS) * _vCROSS;
// which is now replaced by the new value.
_vWIND_NED += cross * _vCROSS;
// Which is now replaced by the new value. The input cross wind is expected
// in knots, so first convert to fps, which is the internal unit used.
_vWIND_NED += (cross * ktstofps) * _vCROSS;
_vt_NED = vUVW_NED + _vWIND_NED;
vt = _vt_NED.Magnitude();
@ -604,13 +607,19 @@ void FGInitialCondition::SetHeadWindKtsIC(double head)
{
FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
FGColumnVector3 _vWIND_NED = _vt_NED - vUVW_NED;
FGColumnVector3 _vHEAD(cos(psi), sin(psi), 0.);
// This is a head wind, so the direction vector for the wind
// needs to be set opposite to the heading the aircraft
// is taking. So, the cos and sin of the heading (psi)
// are negated in the line below.
FGColumnVector3 _vHEAD(-cos(psi), -sin(psi), 0.);
// Gram-Schmidt process is used to remove the existing head wind component
_vWIND_NED -= DotProduct(_vWIND_NED, _vHEAD) * _vHEAD;
// which is now replaced by the new value.
_vWIND_NED += head * _vHEAD;
// Which is now replaced by the new value. The input head wind is expected
// in knots, so first convert to fps, which is the internal unit used.
_vWIND_NED += (head * ktstofps) * _vHEAD;
_vt_NED = vUVW_NED + _vWIND_NED;
vt = _vt_NED.Magnitude();
calcAeroAngles(_vt_NED);
@ -644,9 +653,9 @@ void FGInitialCondition::SetWindMagKtsIC(double mag)
double windMag = _vHEAD.Magnitude();
if (windMag > 0.001)
_vHEAD *= mag / windMag;
_vHEAD *= (mag*ktstofps) / windMag;
else
_vHEAD = FGColumnVector3(mag, 0., 0.);
_vHEAD = FGColumnVector3((mag*ktstofps), 0., 0.);
_vWIND_NED(eU) = _vHEAD(eU);
_vWIND_NED(eV) = _vHEAD(eV);
@ -1028,7 +1037,7 @@ bool FGInitialCondition::Load_v2(void)
{
FGColumnVector3 vOrient;
bool result = true;
FGColumnVector3 vOmegaEarth = FGColumnVector3(0.0, 0.0, fdmex->GetInertial()->omega());
FGColumnVector3 vOmegaEarth = fdmex->GetInertial()->GetOmegaPlanet();
if (document->FindElement("earth_position_angle"))
position.SetEarthPositionAngle(document->FindElementValueAsNumberConvertTo("earth_position_angle", "RAD"));
@ -1325,10 +1334,6 @@ void FGInitialCondition::bind(void)
&FGInitialCondition::GetAltitudeAGLFtIC,
&FGInitialCondition::SetAltitudeAGLFtIC,
true);
PropertyManager->Tie("ic/sea-level-radius-ft", this,
&FGInitialCondition::GetSeaLevelRadiusFtIC,
&FGInitialCondition::SetSeaLevelRadiusFtIC,
true);
PropertyManager->Tie("ic/terrain-elevation-ft", this,
&FGInitialCondition::GetTerrainElevationFtIC,
&FGInitialCondition::SetTerrainElevationFtIC,

View file

@ -54,7 +54,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_INITIALCONDITION "$Id: FGInitialCondition.h,v 1.27 2011/05/20 00:47:03 bcoconni Exp $"
#define ID_INITIALCONDITION "$Id: FGInitialCondition.h,v 1.28 2011/07/10 19:03:49 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -213,7 +213,7 @@ CLASS DOCUMENTATION
@property ic/r-rad_sec (read/write) Yaw rate initial condition in radians/second
@author Tony Peden
@version "$Id: FGInitialCondition.h,v 1.27 2011/05/20 00:47:03 bcoconni Exp $"
@version "$Id: FGInitialCondition.h,v 1.28 2011/07/10 19:03:49 jberndt Exp $"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -237,7 +237,7 @@ public:
void SetVequivalentKtsIC(double ve);
/** Set true airspeed initial condition in knots.
@param vt True airspeed in knots */
@param vtrue True airspeed in knots */
void SetVtrueKtsIC(double vtrue) { SetVtrueFpsIC(vtrue*ktstofps); }
/** Set ground speed initial condition in knots.
@ -375,10 +375,6 @@ public:
@return Initial altitude AGL in feet */
double GetAltitudeAGLFtIC(void) const { return position.GetRadius() - sea_level_radius - terrain_elevation; }
/** Gets the initial sea level radius.
@return Initial sea level radius */
double GetSeaLevelRadiusFtIC(void) const { return sea_level_radius; }
/** Gets the initial terrain elevation.
@return Initial terrain elevation in feet */
double GetTerrainElevationFtIC(void) const { return terrain_elevation; }

View file

@ -43,19 +43,20 @@ INCLUDES
#include "models/FGAtmosphere.h"
#include "FGInitialCondition.h"
#include "FGTrimAxis.h"
#include "models/FGAircraft.h"
#include "models/FGPropulsion.h"
#include "models/FGAerodynamics.h"
#include "models/FGFCS.h"
#include "models/propulsion/FGEngine.h"
#include "models/FGAuxiliary.h"
#include "models/FGGroundReactions.h"
#include "models/FGPropagate.h"
#include "models/FGAccelerations.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGTrimAxis.cpp,v 1.10 2010/07/08 11:36:28 jberndt Exp $";
static const char *IdSrc = "$Id: FGTrimAxis.cpp,v 1.13 2011/07/28 12:48:19 jberndt Exp $";
static const char *IdHdr = ID_TRIMAXIS;
/*****************************************************************************/
@ -167,14 +168,14 @@ FGTrimAxis::~FGTrimAxis(void)
void FGTrimAxis::getState(void) {
switch(state) {
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 tUdot: state_value=fdmex->GetAccelerations()->GetUVWdot(1)-state_target; break;
case tVdot: state_value=fdmex->GetAccelerations()->GetUVWdot(2)-state_target; break;
case tWdot: state_value=fdmex->GetAccelerations()->GetUVWdot(3)-state_target; break;
case tQdot: state_value=fdmex->GetAccelerations()->GetPQRdot(2)-state_target;break;
case tPdot: state_value=fdmex->GetAccelerations()->GetPQRdot(1)-state_target; break;
case tRdot: state_value=fdmex->GetAccelerations()->GetPQRdot(3)-state_target; break;
case tHmgt: state_value=computeHmgt()-state_target; break;
case tNlf: state_value=fdmex->GetAircraft()->GetNlf()-state_target; break;
case tNlf: state_value=fdmex->GetAuxiliary()->GetNlf()-state_target; break;
case tAll: break;
}
}
@ -423,9 +424,12 @@ void FGTrimAxis::setThrottlesPct(void) {
for(unsigned i=0;i<fdmex->GetPropulsion()->GetNumEngines();i++) {
tMin=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMin();
tMax=fdmex->GetPropulsion()->GetEngine(i)->GetThrottleMax();
//cout << "setThrottlespct: " << i << ", " << control_min << ", " << control_max << ", " << control_value;
// Both the main throttle setting in FGFCS and the copy of the position
// in the Propulsion::Inputs structure need to be set at this time.
fdmex->GetFCS()->SetThrottleCmd(i,tMin+control_value*(tMax-tMin));
//cout << "setThrottlespct: " << fdmex->GetFCS()->GetThrottleCmd(i) << endl;
fdmex->GetPropulsion()->in.ThrottlePos[i] = tMin +control_value*(tMax - tMin);
fdmex->RunIC(); //apply throttle change
fdmex->GetPropulsion()->GetSteadyState();
}

View file

@ -41,20 +41,21 @@ COMMENTS, REFERENCES, and NOTES
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include "FGScript.h"
#include "input_output/FGXMLElement.h"
#include "input_output/FGXMLParse.h"
#include "initialization/FGTrim.h"
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include "models/FGInput.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGScript.cpp,v 1.46 2011/02/18 12:44:16 jberndt Exp $";
static const char *IdSrc = "$Id: FGScript.cpp,v 1.48 2011/09/07 02:36:04 jberndt Exp $";
static const char *IdHdr = ID_FGSCRIPT;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -379,7 +380,12 @@ bool FGScript::RunScript(void)
for (i=0; i<Events[ev_ctr].SetValue.size(); i++) {
Events[ev_ctr].OriginalValue[i] = Events[ev_ctr].SetParam[i]->getDoubleValue();
if (Events[ev_ctr].Functions[i] != 0) { // Parameter should be set to a function value
Events[ev_ctr].SetValue[i] = Events[ev_ctr].Functions[i]->GetValue();
try {
Events[ev_ctr].SetValue[i] = Events[ev_ctr].Functions[i]->GetValue();
} catch (string msg) {
std::cerr << std::endl << "A problem occurred in the execution of the script. " << msg << endl;
throw;
}
}
switch (Events[ev_ctr].Type[i]) {
case FG_VALUE:

View file

@ -37,18 +37,19 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGJSBBase.h"
#include <vector>
#include "FGFDMExec.h"
#include "FGJSBBase.h"
#include "math/FGFunction.h"
#include "math/FGCondition.h"
#include <vector>
#include "input_output/FGXMLFileRead.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_FGSCRIPT "$Id: FGScript.h,v 1.20 2011/02/11 12:43:28 jberndt Exp $"
#define ID_FGSCRIPT "$Id: FGScript.h,v 1.21 2011/08/04 12:46:32 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -157,7 +158,7 @@ CLASS DOCUMENTATION
comes the &quot;run&quot; section, where the conditions are
described in &quot;event&quot; clauses.</p>
@author Jon S. Berndt
@version "$Id: FGScript.h,v 1.20 2011/02/11 12:43:28 jberndt Exp $"
@version "$Id: FGScript.h,v 1.21 2011/08/04 12:46:32 jberndt Exp $"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -42,7 +42,7 @@ FORWARD DECLARATIONS
namespace JSBSim {
static const char *IdSrc = "$Id: FGXMLElement.cpp,v 1.32 2011/02/13 00:42:45 jberndt Exp $";
static const char *IdSrc = "$Id: FGXMLElement.cpp,v 1.33 2011/08/05 12:28:20 jberndt Exp $";
static const char *IdHdr = ID_XMLELEMENT;
bool Element::converterIsInitialized = false;
@ -64,6 +64,8 @@ Element::Element(const string& nm)
// Length
convert["M"]["FT"] = 3.2808399;
convert["FT"]["M"] = 1.0/convert["M"]["FT"];
convert["CM"]["FT"] = 0.032808399;
convert["FT"]["CM"] = 1.0/convert["CM"]["FT"];
convert["KM"]["FT"] = 3280.8399;
convert["FT"]["KM"] = 1.0/convert["KM"]["FT"];
convert["FT"]["IN"] = 12.0;
@ -73,6 +75,8 @@ Element::Element(const string& nm)
// Area
convert["M2"]["FT2"] = convert["M"]["FT"]*convert["M"]["FT"];
convert["FT2"]["M2"] = 1.0/convert["M2"]["FT2"];
convert["CM2"]["FT2"] = convert["CM"]["FT"]*convert["CM"]["FT"];
convert["FT2"]["CM2"] = 1.0/convert["CM2"]["FT2"];
convert["M2"]["IN2"] = convert["M"]["IN"]*convert["M"]["IN"];
convert["IN2"]["M2"] = 1.0/convert["M2"]["IN2"];
convert["FT2"]["IN2"] = 144.0;

View file

@ -43,13 +43,65 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGFunction.cpp,v 1.36 2011/04/05 20:20:21 andgi Exp $";
static const char *IdSrc = "$Id: FGFunction.cpp,v 1.42 2011/09/07 02:36:04 jberndt Exp $";
static const char *IdHdr = ID_FUNCTION;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
const std::string FGFunction::property_string = "property";
const std::string FGFunction::value_string = "value";
const std::string FGFunction::table_string = "table";
const std::string FGFunction::p_string = "p";
const std::string FGFunction::v_string = "v";
const std::string FGFunction::t_string = "t";
const std::string FGFunction::function_string = "function";
const std::string FGFunction::description_string = "description";
const std::string FGFunction::sum_string = "sum";
const std::string FGFunction::difference_string = "difference";
const std::string FGFunction::product_string = "product";
const std::string FGFunction::quotient_string = "quotient";
const std::string FGFunction::pow_string = "pow";
const std::string FGFunction::exp_string = "exp";
const std::string FGFunction::log2_string = "log2";
const std::string FGFunction::ln_string = "ln";
const std::string FGFunction::log10_string = "log10";
const std::string FGFunction::abs_string = "abs";
const std::string FGFunction::sign_string = "sign";
const std::string FGFunction::sin_string = "sin";
const std::string FGFunction::cos_string = "cos";
const std::string FGFunction::tan_string = "tan";
const std::string FGFunction::asin_string = "asin";
const std::string FGFunction::acos_string = "acos";
const std::string FGFunction::atan_string = "atan";
const std::string FGFunction::atan2_string = "atan2";
const std::string FGFunction::min_string = "min";
const std::string FGFunction::max_string = "max";
const std::string FGFunction::avg_string = "avg";
const std::string FGFunction::fraction_string = "fraction";
const std::string FGFunction::mod_string = "mod";
const std::string FGFunction::random_string = "random";
const std::string FGFunction::integer_string = "integer";
const std::string FGFunction::rotation_alpha_local_string = "rotation_alpha_local";
const std::string FGFunction::rotation_beta_local_string = "rotation_beta_local";
const std::string FGFunction::rotation_gamma_local_string = "rotation_gamma_local";
const std::string FGFunction::rotation_bf_to_wf_string = "rotation_bf_to_wf";
const std::string FGFunction::rotation_wf_to_bf_string = "rotation_wf_to_bf";
const std::string FGFunction::lessthan_string = "lt";
const std::string FGFunction::lessequal_string = "le";
const std::string FGFunction::greatthan_string = "gt";
const std::string FGFunction::greatequal_string = "ge";
const std::string FGFunction::equal_string = "eq";
const std::string FGFunction::notequal_string = "nq";
const std::string FGFunction::and_string = "and";
const std::string FGFunction::or_string = "or";
const std::string FGFunction::not_string = "not";
const std::string FGFunction::ifthen_string = "ifthen";
const std::string FGFunction::switch_string = "switch";
FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& prefix)
: PropertyManager(propMan), Prefix(prefix)
{
@ -59,40 +111,6 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
cachedValue = -HUGE_VAL;
invlog2val = 1.0/log10(2.0);
property_string = "property";
value_string = "value";
table_string = "table";
p_string = "p";
v_string = "v";
t_string = "t";
function_string = "function";
description_string = "description";
sum_string = "sum";
difference_string = "difference";
product_string = "product";
quotient_string = "quotient";
pow_string = "pow";
exp_string = "exp";
log2_string = "log2";
ln_string = "ln";
log10_string = "log10";
abs_string = "abs";
sin_string = "sin";
cos_string = "cos";
tan_string = "tan";
asin_string = "asin";
acos_string = "acos";
atan_string = "atan";
atan2_string = "atan2";
min_string = "min";
max_string = "max";
avg_string = "avg";
fraction_string = "fraction";
mod_string = "mod";
random_string = "random";
integer_string = "integer";
Name = el->GetAttributeValue("name");
operation = el->GetName();
@ -116,6 +134,8 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
Type = eLog10;
} else if (operation == abs_string) {
Type = eAbs;
} else if (operation == sign_string) {
Type = eSign;
} else if (operation == sin_string) {
Type = eSin;
} else if (operation == exp_string) {
@ -146,6 +166,38 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
Type = eMod;
} else if (operation == random_string) {
Type = eRandom;
} else if (operation == rotation_alpha_local_string) {
Type = eRotation_alpha_local;
} else if (operation == rotation_beta_local_string) {
Type = eRotation_beta_local;
} else if (operation == rotation_gamma_local_string) {
Type = eRotation_gamma_local;
} else if (operation == rotation_bf_to_wf_string) {
Type = eRotation_bf_to_wf;
} else if (operation == rotation_wf_to_bf_string) {
Type = eRotation_wf_to_bf;
} else if (operation == lessthan_string) {
Type = eLT;
} else if (operation == lessequal_string) {
Type = eLE;
} else if (operation == greatthan_string) {
Type = eGT;
} else if (operation == greatequal_string) {
Type = eGE;
} else if (operation == equal_string) {
Type = eEQ;
} else if (operation == notequal_string) {
Type = eNE;
} else if (operation == and_string) {
Type = eAND;
} else if (operation == or_string) {
Type = eOR;
} else if (operation == not_string) {
Type = eNOT;
} else if (operation == ifthen_string) {
Type = eIfThen;
} else if (operation == switch_string) {
Type = eSwitch;
} else if (operation != description_string) {
cerr << "Bad operation " << operation << " detected in configuration file" << endl;
}
@ -197,6 +249,7 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
operation == ln_string ||
operation == log10_string ||
operation == abs_string ||
operation == sign_string ||
operation == sin_string ||
operation == cos_string ||
operation == tan_string ||
@ -210,7 +263,23 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
operation == integer_string ||
operation == mod_string ||
operation == random_string ||
operation == avg_string )
operation == avg_string ||
operation == rotation_alpha_local_string||
operation == rotation_beta_local_string||
operation == rotation_gamma_local_string||
operation == rotation_bf_to_wf_string||
operation == rotation_wf_to_bf_string ||
operation == lessthan_string ||
operation == lessequal_string ||
operation == greatthan_string ||
operation == greatequal_string ||
operation == equal_string ||
operation == notequal_string ||
operation == and_string ||
operation == or_string ||
operation == not_string ||
operation == ifthen_string ||
operation == switch_string)
{
Parameters.push_back(new FGFunction(PropertyManager, element, Prefix));
} else if (operation != description_string) {
@ -245,6 +314,18 @@ void FGFunction::cacheValue(bool cache)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
unsigned int FGFunction::GetBinary(double val) const
{
val = fabs(val);
if (val < 1E-9) return 0;
else if (val-1 < 1E-9) return 1;
else {
throw("Malformed conditional check in function definition.");
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGFunction::GetValue(void) const
{
unsigned int i;
@ -300,6 +381,9 @@ double FGFunction::GetValue(void) const
case eAbs:
temp = fabs(temp);
break;
case eSign:
temp = temp < 0 ? -1:1; // 0.0 counts as positive.
break;
case eSin:
temp = sin(temp);
break;
@ -350,6 +434,262 @@ double FGFunction::GetValue(void) const
case eRandom:
temp = GaussianRandomNumber();
break;
case eLT:
temp = (temp < Parameters[1]->GetValue())?1:0;
break;
case eLE:
temp = (temp <= Parameters[1]->GetValue())?1:0;
break;
case eGT:
temp = (temp > Parameters[1]->GetValue())?1:0;
break;
case eGE:
temp = (temp >= Parameters[1]->GetValue())?1:0;
break;
case eEQ:
temp = (temp == Parameters[1]->GetValue())?1:0;
break;
case eNE:
temp = (temp != Parameters[1]->GetValue())?1:0;
break;
case eAND:
{
bool flag = (GetBinary(temp) != 0u);
for (i=1; i<Parameters.size() && flag; i++) {
flag = (GetBinary(Parameters[i]->GetValue()) != 0);
}
temp = flag ? 1 : 0;
}
break;
case eOR:
{
bool flag = (GetBinary(temp) != 0);
for (i=1; i<Parameters.size() && !flag; i++) {
flag = (GetBinary(Parameters[i]->GetValue()) != 0);
}
temp = flag ? 1 : 0;
}
break;
case eNOT:
temp = (GetBinary(temp) != 0) ? 0 : 1;
break;
case eIfThen:
{
i = Parameters.size();
if (i != 3) {
if (GetBinary(temp) == 1) {
temp = Parameters[1]->GetValue();
} else {
temp = Parameters[2]->GetValue();
}
} else {
throw("Malformed if/then function statement");
}
}
break;
case eSwitch:
{
unsigned int n = Parameters.size()-1;
i = int(temp+0.5);
if (i >= 0u && i < n) {
temp = Parameters[i+1]->GetValue();
} else {
throw(string("The switch function index selected a value above the range of supplied values"
" - not enough values were supplied."));
}
}
break;
case eRotation_alpha_local:
if (Parameters.size()==6) // calculates local angle of attack for skydiver body component
//Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
{
double alpha = Parameters[0]->GetValue()*degtorad;
double beta = Parameters[1]->GetValue()*degtorad;
double gamma = Parameters[2]->GetValue()*degtorad;
double phi = Parameters[3]->GetValue()*degtorad;
double theta = Parameters[4]->GetValue()*degtorad;
double psi = Parameters[5]->GetValue()*degtorad;
double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
//calculate local body frame to the intermediate body frame rotation quaternion
double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
//calculate the intermediate body frame to global wind frame rotation quaternion
double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
//multiply quaternions
double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
//calculate alpha_local
temp = -atan2(2*(Cy*Ct-Cx*Cz),(Ct*Ct+Cx*Cx-Cy*Cy-Cz*Cz));
temp *= radtodeg;
} else {
temp = 1;
}
break;
case eRotation_beta_local:
if (Parameters.size()==6) // calculates local angle of sideslip for skydiver body component
//Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
{
double alpha = Parameters[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
double beta = Parameters[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
double gamma = Parameters[2]->GetValue()*degtorad; //roll angle of intermediate body frame
double phi = Parameters[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
double theta = Parameters[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
double psi = Parameters[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
//calculate local body frame to the intermediate body frame rotation quaternion
double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
//calculate the intermediate body frame to global wind frame rotation quaternion
double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
//multiply quaternions
double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
//calculate beta_local
temp = asin(2*(Cx*Cy+Cz*Ct));
temp *= radtodeg;
}
else //
{temp = 1;}
break;
case eRotation_gamma_local:
if (Parameters.size()==6) // calculates local angle of attack for skydiver body component
//Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
{
double alpha = Parameters[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
double beta = Parameters[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
double gamma = Parameters[2]->GetValue()*degtorad; //roll angle of intermediate body frame
double phi = Parameters[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
double theta = Parameters[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
double psi = Parameters[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
//calculate local body frame to the intermediate body frame rotation quaternion
double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
//calculate the intermediate body frame to global wind frame rotation quaternion
double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
//multiply quaternions
double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
//calculate local roll anlge
temp = -atan2(2*(Cx*Ct-Cz*Cy),(Ct*Ct-Cx*Cx+Cy*Cy-Cz*Cz));
temp *= radtodeg;
}
else //
{temp = 1;}
break;
case eRotation_bf_to_wf:
if (Parameters.size()==7) // transforms the input vector from a body frame to a wind frame. The origin of the vector remains the same.
{
double rx = Parameters[0]->GetValue(); //x component of input vector
double ry = Parameters[1]->GetValue(); //y component of input vector
double rz = Parameters[2]->GetValue(); //z component of input vector
double alpha = Parameters[3]->GetValue()*degtorad; //angle of attack of the body frame
double beta = Parameters[4]->GetValue()*degtorad; //sideslip angle of the body frame
double gamma = Parameters[5]->GetValue()*degtorad; //roll angle of the body frame
double index = Parameters[6]->GetValue();
double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
//calculate the body frame to wind frame quaternion
double qt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
double qx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
double qy = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
double qz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
//calculate the quaternion conjugate
double qstart = qt;
double qstarx = -qx;
double qstary = -qy;
double qstarz = -qz;
//multiply quaternions v*q
double vqt = -rx*qx - ry*qy - rz*qz;
double vqx = rx*qt + ry*qz - rz*qy;
double vqy = -rx*qz + ry*qt + rz*qx;
double vqz = rx*qy - ry*qx + rz*qt;
//multiply quaternions qstar*vq
double Cx = qstart*vqx + qstarx*vqt + qstary*vqz - qstarz*vqy;
double Cy = qstart*vqy - qstarx*vqz + qstary*vqt + qstarz*vqx;
double Cz = qstart*vqz + qstarx*vqy - qstary*vqx + qstarz*vqt;
if (index == 1) temp = Cx;
else if (index ==2) temp = Cy;
else temp = Cz;
}
else //
{temp = 1;}
break;
case eRotation_wf_to_bf:
if (Parameters.size()==7) // transforms the input vector from q wind frame to a body frame. The origin of the vector remains the same.
{
double rx = Parameters[0]->GetValue(); //x component of input vector
double ry = Parameters[1]->GetValue(); //y component of input vector
double rz = Parameters[2]->GetValue(); //z component of input vector
double alpha = Parameters[3]->GetValue()*degtorad; //angle of attack of the body frame
double beta = Parameters[4]->GetValue()*degtorad; //sideslip angle of the body frame
double gamma = Parameters[5]->GetValue()*degtorad; //roll angle of the body frame
double index = Parameters[6]->GetValue();
double calpha2 = cos(alpha/2), salpha2 = sin(alpha/2);
double cbeta2 = cos(-beta/2), sbeta2 = sin(-beta/2);
double cgamma2 = cos(gamma/2), sgamma2 = sin(gamma/2);
//calculate the wind frame to body frame quaternion
double qt = cgamma2*cbeta2*calpha2 + sgamma2*sbeta2*salpha2;
double qx = -cgamma2*sbeta2*salpha2 + sgamma2*cbeta2*calpha2;
double qy = cgamma2*cbeta2*salpha2 - sgamma2*sbeta2*calpha2;
double qz = cgamma2*sbeta2*calpha2 + sgamma2*cbeta2*salpha2;
//calculate the quaternion conjugate
double qstart = qt;
double qstarx = -qx;
double qstary = -qy;
double qstarz = -qz;
//multiply quaternions v*q
double vqt = -rx*qx - ry*qy - rz*qz;
double vqx = rx*qt + ry*qz - rz*qy;
double vqy = -rx*qz + ry*qt + rz*qx;
double vqz = rx*qy - ry*qx + rz*qt;
//multiply quaternions qstar*vq
double Cx = qstart*vqx + qstarx*vqt + qstary*vqz - qstarz*vqy;
double Cy = qstart*vqy - qstarx*vqz + qstary*vqt + qstarz*vqx;
double Cz = qstart*vqz + qstarx*vqy - qstary*vqx + qstarz*vqt;
if (index == 1) temp = Cx;
else if (index ==2) temp = Cy;
else temp = Cz;
}
else //
{temp = 1;}
break;
default:
cerr << "Unknown function operation type" << endl;
break;

103
src/FDM/JSBSim/math/FGFunction.h Normal file → Executable file
View file

@ -42,7 +42,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_FUNCTION "$Id: FGFunction.h,v 1.21 2009/11/18 04:49:02 jberndt Exp $"
#define ID_FUNCTION "$Id: FGFunction.h,v 1.24 2011/08/06 13:10:00 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -86,6 +86,17 @@ A function definition consists of an operation, a value, a table, or a property
- avg (takes n args)
- fraction
- mod
- lt (less than, takes 2 args)
- le (less equal, takes 2 args)
- gt (greater than, takes 2 args)
- ge (greater than, takes 2 args)
- eq (equal, takes 2 args)
- nq (not equal, takes 2 args)
- and (takes n args)
- or (takes n args)
- not (takes 1 args)
- if-then (takes 2-3 args)
- switch (takes 2 or more args)
- random (Gaussian random number)
- integer
@ -198,43 +209,65 @@ private:
bool cached;
double invlog2val;
std::string Prefix;
std::string description_string;
std::string property_string;
std::string value_string;
std::string table_string;
std::string p_string;
std::string v_string;
std::string t_string;
std::string function_string;
std::string sum_string;
std::string difference_string;
std::string product_string;
std::string quotient_string;
std::string pow_string;
std::string exp_string;
std::string log2_string;
std::string ln_string;
std::string log10_string;
std::string abs_string;
std::string sin_string;
std::string cos_string;
std::string tan_string;
std::string asin_string;
std::string acos_string;
std::string atan_string;
std::string atan2_string;
std::string min_string;
std::string max_string;
std::string avg_string;
std::string fraction_string;
std::string mod_string;
std::string random_string;
std::string integer_string;
static const std::string description_string;
static const std::string property_string;
static const std::string value_string;
static const std::string table_string;
static const std::string p_string;
static const std::string v_string;
static const std::string t_string;
static const std::string function_string;
static const std::string sum_string;
static const std::string difference_string;
static const std::string product_string;
static const std::string quotient_string;
static const std::string pow_string;
static const std::string exp_string;
static const std::string log2_string;
static const std::string ln_string;
static const std::string log10_string;
static const std::string abs_string;
static const std::string sign_string;
static const std::string sin_string;
static const std::string cos_string;
static const std::string tan_string;
static const std::string asin_string;
static const std::string acos_string;
static const std::string atan_string;
static const std::string atan2_string;
static const std::string min_string;
static const std::string max_string;
static const std::string avg_string;
static const std::string fraction_string;
static const std::string mod_string;
static const std::string random_string;
static const std::string integer_string;
static const std::string rotation_alpha_local_string;
static const std::string rotation_beta_local_string;
static const std::string rotation_gamma_local_string;
static const std::string rotation_bf_to_wf_string;
static const std::string rotation_wf_to_bf_string;
static const std::string lessthan_string;
static const std::string lessequal_string;
static const std::string greatthan_string;
static const std::string greatequal_string;
static const std::string equal_string;
static const std::string notequal_string;
static const std::string and_string;
static const std::string or_string;
static const std::string not_string;
static const std::string ifthen_string;
static const std::string switch_string;
double cachedValue;
enum functionType {eTopLevel=0, eProduct, eDifference, eSum, eQuotient, ePow,
eExp, eAbs, eSin, eCos, eTan, eASin, eACos, eATan, eATan2,
eMin, eMax, eAvg, eFrac, eInteger, eMod, eRandom, eLog2, eLn, eLog10} Type;
eExp, eAbs, eSign, eSin, eCos, eTan, eASin, eACos, eATan, eATan2,
eMin, eMax, eAvg, eFrac, eInteger, eMod, eRandom, eLog2, eLn,
eLog10, eLT, eLE, eGE, eGT, eEQ, eNE, eAND, eOR, eNOT,
eIfThen, eSwitch, eRotation_alpha_local, eRotation_beta_local,
eRotation_gamma_local, eRotation_bf_to_wf, eRotation_wf_to_bf} Type;
std::string Name;
unsigned int GetBinary(double) const;
void bind(void);
void Debug(int from);
};

View file

@ -48,7 +48,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_LOCATION "$Id: FGLocation.h,v 1.27 2010/11/29 12:33:58 jberndt Exp $"
#define ID_LOCATION "$Id: FGLocation.h,v 1.28 2011/08/04 12:46:32 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -142,7 +142,7 @@ CLASS DOCUMENTATION
@see W. C. Durham "Aircraft Dynamics & Control", section 2.2
@author Mathias Froehlich
@version $Id: FGLocation.h,v 1.27 2010/11/29 12:33:58 jberndt Exp $
@version $Id: FGLocation.h,v 1.28 2011/08/04 12:46:32 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -229,6 +229,13 @@ public:
respect to the Inertial (ECI) frame in radians. */
void SetEarthPositionAngle(double EPA) {epa = EPA; mCacheValid = false;}
/** Increments the Earth position angle.
This is the relative orientation of the ECEF frame with respect to the
Inertial frame.
@param delta delta to the Earth fixed frame (ECEF) rotation offset about the axis with
respect to the Inertial (ECI) frame in radians. */
void IncrementEarthPositionAngle(double delta) {epa += delta; mCacheValid = false;}
/** Get the longitude.
@return the longitude in rad of the location represented with this
class instance. The returned values are in the range between
@ -290,6 +297,8 @@ public:
return -mTec2l(3,3)/cLat;
}
double GetEPA() const {return epa;}
/** 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

4
src/FDM/JSBSim/math/FGModelFunctions.h Normal file → Executable file
View file

@ -44,7 +44,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_MODELFUNCTIONS "$Id: FGModelFunctions.h,v 1.3 2010/11/17 03:18:37 jberndt Exp $"
#define ID_MODELFUNCTIONS "$Id: FGModelFunctions.h,v 1.4 2011/06/21 04:41:54 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -76,7 +76,7 @@ DECLARATION: FGModelFunctions
class FGModelFunctions : public FGJSBBase
{
public:
~FGModelFunctions();
virtual ~FGModelFunctions();
void RunPreFunctions(void);
void RunPostFunctions(void);
bool Load(Element* el, FGPropertyManager* PropertyManager, std::string prefix="");

View file

@ -0,0 +1,67 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: LaGrangeMultiplier.h
Author: Bertrand Coconnier
Date started: 07/01/11
------------- Copyright (C) 2011 Bertrand Coconnier -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef LAGRANGEMULTIPLIER_H
#define LAGRANGEMULTIPLIER_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_LAGRANGEMULTIPLIER "$Id: LagrangeMultiplier.h,v 1.2 2011/08/21 15:13:22 bcoconni Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
struct LagrangeMultiplier {
FGColumnVector3 ForceJacobian;
FGColumnVector3 MomentJacobian;
double Min;
double Max;
double value;
};
} // namespace
#endif

View file

@ -6,6 +6,6 @@ libMath_a_SOURCES = FGColumnVector3.cpp FGFunction.cpp FGLocation.cpp FGMatrix33
noinst_HEADERS = FGColumnVector3.h FGFunction.h FGLocation.h FGMatrix33.h \
FGParameter.h FGPropertyValue.h FGQuaternion.h FGRealValue.h FGTable.h \
FGCondition.h FGRungeKutta.h FGModelFunctions.h
FGCondition.h FGRungeKutta.h FGModelFunctions.h LagrangeMultiplier.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim

View file

@ -0,0 +1,397 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGAccelerations.cpp
Author: Jon S. Berndt
Date started: 07/12/11
Purpose: Calculates derivatives of rotational and translational rates, and
of the attitude quaternion.
Called by: FGFDMExec
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
This class encapsulates the calculation of the derivatives of the state vectors
UVW and PQR - the translational and rotational rates relative to the planet
fixed frame. The derivatives relative to the inertial frame are also calculated
as a side effect. Also, the derivative of the attitude quaterion is also calculated.
HISTORY
--------------------------------------------------------------------------------
07/12/11 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] Stevens and Lewis, "Aircraft Control and Simulation", Second edition (2004)
Wiley
[2] Richard E. McFarland, "A Standard Kinematic Model for Flight Simulation at
NASA-Ames", NASA CR-2497, January 1975
[3] Erin Catto, "Iterative Dynamics with Temporal Coherence", February 22, 2005
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGAccelerations.h"
#include "FGFDMExec.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGAccelerations.cpp,v 1.8 2011/08/30 20:49:04 bcoconni Exp $";
static const char *IdHdr = ID_ACCELERATIONS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGAccelerations::FGAccelerations(FGFDMExec* fdmex)
: FGModel(fdmex)
{
Debug(0);
Name = "FGAccelerations";
gravType = gtWGS84;
vPQRidot.InitMatrix();
vUVWidot.InitMatrix();
vGravAccel.InitMatrix();
vBodyAccel.InitMatrix();
vQtrndot = FGQuaternion(0,0,0);
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGAccelerations::~FGAccelerations(void)
{
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGAccelerations::InitModel(void)
{
vPQRidot.InitMatrix();
vUVWidot.InitMatrix();
vGravAccel.InitMatrix();
vBodyAccel.InitMatrix();
vQtrndot = FGQuaternion(0,0,0);
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/*
Purpose: Called on a schedule to calculate derivatives.
*/
bool FGAccelerations::Run(bool Holding)
{
if (FGModel::Run(Holding)) return true; // Fast return if we have nothing to do ...
if (Holding) return false;
RunPreFunctions();
CalculatePQRdot(); // Angular rate derivative
CalculateUVWdot(); // Translational rate derivative
CalculateQuatdot(); // Angular orientation derivative
ResolveFrictionForces(in.DeltaT * rate); // Update rate derivatives with friction forces
RunPostFunctions();
Debug(2);
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Compute body frame rotational accelerations based on the current body moments
//
// vPQRdot is the derivative of the absolute angular velocity of the vehicle
// (body rate with respect to the inertial frame), expressed in the body frame,
// where the derivative is taken in the body frame.
// J is the inertia matrix
// Jinv is the inverse inertia matrix
// vMoments is the moment vector in the body frame
// in.vPQRi is the total inertial angular velocity of the vehicle
// expressed in the body frame.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqn 1.5-16e (page 50)
void FGAccelerations::CalculatePQRdot(void)
{
// Compute body frame rotational accelerations based on the current body
// moments and the total inertial angular velocity expressed in the body
// frame.
vPQRidot = in.Jinv * (in.Moment - in.vPQRi * (in.J * in.vPQRi));
vPQRdot = vPQRidot - in.vPQRi * (in.Ti2b * in.vOmegaPlanet);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Compute the quaternion orientation derivative
//
// vQtrndot is the quaternion derivative.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqn 1.5-16b (page 50)
void FGAccelerations::CalculateQuatdot(void)
{
// Compute quaternion orientation derivative on current body rates
vQtrndot = in.qAttitudeECI.GetQDot(in.vPQRi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This set of calculations results in the body and inertial frame accelerations
// being computed.
// Compute body and inertial frames accelerations based on the current body
// forces including centripetal and coriolis accelerations for the former.
// in.vOmegaPlanet is the Earth angular rate - expressed in the inertial frame -
// so it has to be transformed to the body frame. More completely,
// in.vOmegaPlanet is the rate of the ECEF frame relative to the Inertial
// frame (ECI), expressed in the Inertial frame.
// in.Force is the total force on the vehicle in the body frame.
// in.vPQR is the vehicle body rate relative to the ECEF frame, expressed
// in the body frame.
// in.vUVW is the vehicle velocity relative to the ECEF frame, expressed
// in the body frame.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
void FGAccelerations::CalculateUVWdot(void)
{
vBodyAccel = in.Force / in.Mass;
vUVWdot = vBodyAccel - (in.vPQR + 2.0 * (in.Ti2b * in.vOmegaPlanet)) * in.vUVW;
// Include Centripetal acceleration.
vUVWdot -= in.Ti2b * (in.vOmegaPlanet * (in.vOmegaPlanet * in.vInertialPosition));
// Include Gravitation accel
switch (gravType) {
case gtStandard:
vGravAccel = in.Tl2b * FGColumnVector3( 0.0, 0.0, in.GAccel );
break;
case gtWGS84:
vGravAccel = in.Tec2b * in.J2Grav;
break;
}
vUVWdot += vGravAccel;
vUVWidot = in.Tb2i * (vBodyAccel + vGravAccel);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Resolves the contact forces just before integrating the EOM.
// This routine is using Lagrange multipliers and the projected Gauss-Seidel
// (PGS) method.
// Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
// February 22, 2005
// In JSBSim there is only one rigid body (the aircraft) and there can be
// multiple points of contact between the aircraft and the ground. As a
// consequence our matrix J*M^-1*J^T is not sparse and the algorithm described
// in Catto's paper has been adapted accordingly.
// The friction forces are resolved in the body frame relative to the origin
// (Earth center).
void FGAccelerations::ResolveFrictionForces(double dt)
{
const double invMass = 1.0 / in.Mass;
const FGMatrix33& Jinv = in.Jinv;
FGColumnVector3 vdot, wdot;
vector<LagrangeMultiplier*>& multipliers = *in.MultipliersList;
int n = multipliers.size();
vFrictionForces.InitMatrix();
vFrictionMoments.InitMatrix();
// If no gears are in contact with the ground then return
if (!n) return;
vector<double> a(n*n); // Will contain J*M^-1*J^T
vector<double> rhs(n);
// Assemble the linear system of equations
for (int i=0; i < n; i++) {
FGColumnVector3 v1 = invMass * multipliers[i]->ForceJacobian;
FGColumnVector3 v2 = Jinv * multipliers[i]->MomentJacobian; // Should be J^-T but J is symmetric and so is J^-1
for (int j=0; j < i; j++)
a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of J^T*M^-1*J
for (int j=i; j < n; j++)
a[i*n+j] = DotProduct(v1, multipliers[j]->ForceJacobian)
+ DotProduct(v2, multipliers[j]->MomentJacobian);
}
// Assemble the RHS member
// Translation
vdot = vUVWdot;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += (in.vUVW - in.Tec2b * in.TerrainVelocity) / dt;
// Rotation
wdot = vPQRdot;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
wdot += (in.vPQR - in.Tec2b * in.TerrainAngularVel) / dt;
// Prepare the linear system for the Gauss-Seidel algorithm :
// 1. Compute the right hand side member 'rhs'
// 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
// a division computation at each iteration of Gauss-Seidel.
for (int i=0; i < n; i++) {
double d = 1.0 / a[i*n+i];
rhs[i] = -(DotProduct(multipliers[i]->ForceJacobian, vdot)
+DotProduct(multipliers[i]->MomentJacobian, wdot))*d;
for (int j=0; j < n; j++)
a[i*n+j] *= d;
}
// Resolve the Lagrange multipliers with the projected Gauss-Seidel method
for (int iter=0; iter < 50; iter++) {
double norm = 0.;
for (int i=0; i < n; i++) {
double lambda0 = multipliers[i]->value;
double dlambda = rhs[i];
for (int j=0; j < n; j++)
dlambda -= a[i*n+j]*multipliers[j]->value;
multipliers[i]->value = Constrain(multipliers[i]->Min, lambda0+dlambda, multipliers[i]->Max);
dlambda = multipliers[i]->value - lambda0;
norm += fabs(dlambda);
}
if (norm < 1E-5) break;
}
// Calculate the total friction forces and moments
for (int i=0; i< n; i++) {
double lambda = multipliers[i]->value;
vFrictionForces += lambda * multipliers[i]->ForceJacobian;
vFrictionMoments += lambda * multipliers[i]->MomentJacobian;
}
FGColumnVector3 accel = invMass * vFrictionForces;
FGColumnVector3 omegadot = Jinv * vFrictionMoments;
vBodyAccel += accel;
vUVWdot += accel;
vUVWidot += in.Tb2i * accel;
vPQRdot += omegadot;
vPQRidot += omegadot;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAccelerations::InitializeDerivatives(void)
{
// Make an initial run and set past values
CalculatePQRdot(); // Angular rate derivative
CalculateUVWdot(); // Translational rate derivative
CalculateQuatdot(); // Angular orientation derivative
ResolveFrictionForces(0.); // Update rate derivatives with friction forces
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAccelerations::bind(void)
{
typedef double (FGAccelerations::*PMF)(int) const;
PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGAccelerations::GetPQRdot);
PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGAccelerations::GetPQRdot);
PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGAccelerations::GetPQRdot);
PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGAccelerations::GetUVWdot);
PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGAccelerations::GetUVWdot);
PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGAccelerations::GetUVWdot);
PropertyManager->Tie("simulation/gravity-model", &gravType);
PropertyManager->Tie("forces/fbx-total-lbs", this, eX, (PMF)&FGAccelerations::GetForces);
PropertyManager->Tie("forces/fby-total-lbs", this, eY, (PMF)&FGAccelerations::GetForces);
PropertyManager->Tie("forces/fbz-total-lbs", this, eZ, (PMF)&FGAccelerations::GetForces);
PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAccelerations::GetMoments);
PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAccelerations::GetMoments);
PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAccelerations::GetMoments);
PropertyManager->Tie("moments/l-gear-lbsft", this, eL, (PMF)&FGAccelerations::GetGroundMoments);
PropertyManager->Tie("moments/m-gear-lbsft", this, eM, (PMF)&FGAccelerations::GetGroundMoments);
PropertyManager->Tie("moments/n-gear-lbsft", this, eN, (PMF)&FGAccelerations::GetGroundMoments);
PropertyManager->Tie("forces/fbx-gear-lbs", this, eX, (PMF)&FGAccelerations::GetGroundForces);
PropertyManager->Tie("forces/fby-gear-lbs", this, eY, (PMF)&FGAccelerations::GetGroundForces);
PropertyManager->Tie("forces/fbz-gear-lbs", this, eZ, (PMF)&FGAccelerations::GetGroundForces);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print
// out the normally expected messages, essentially echoing
// the config files as they are read. If the environment
// variable is not set, debug_lvl is set to 1 internally
// 0: This requests JSBSim not to output any messages
// whatsoever.
// 1: This value explicity requests the normal JSBSim
// startup messages
// 2: This value asks for a message to be printed out when
// a class is instantiated
// 4: When this value is set, a message is displayed when a
// FGModel object executes its Run() method
// 8: When this value is set, various runtime state variables
// are printed out periodically
// 16: When set various parameters are sanity checked and
// a message is printed out when they go out of bounds
void FGAccelerations::Debug(int from)
{
if (debug_lvl <= 0) return;
if (debug_lvl & 1) { // Standard console startup message output
if (from == 0) { // Constructor
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGAccelerations" << endl;
if (from == 1) cout << "Destroyed: FGAccelerations" << endl;
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
if (debug_lvl & 8 && from == 2) { // Runtime state variables
}
if (debug_lvl & 16) { // Sanity checking
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
}

View file

@ -0,0 +1,230 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGAccelerations.h
Author: Jon S. Berndt
Date started: 07/12/11
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
07/12/11 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGACCELERATIONS_H
#define FGACCELERATIONS_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include "models/FGModel.h"
#include "math/FGColumnVector3.h"
#include "math/LagrangeMultiplier.h"
#include "math/FGMatrix33.h"
#include "math/FGQuaternion.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ACCELERATIONS "$Id: FGAccelerations.h,v 1.7 2011/08/21 15:46:48 bcoconni Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Handles the calculation of accelerations.
-Calculate the angular accelerations
-Calculate the translational accelerations
-Calculate the angular rate
-Calculate the translational velocity
@author Jon S. Berndt, Mathias Froehlich, Bertrand Coconnier
@version $Id: FGAccelerations.h,v 1.7 2011/08/21 15:46:48 bcoconni Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGAccelerations : public FGModel {
public:
/** Constructor.
@param Executive a pointer to the parent executive object */
FGAccelerations(FGFDMExec* Executive);
/// Destructor
~FGAccelerations();
/// These define the indices use to select the gravitation models.
enum eGravType {gtStandard, gtWGS84};
/** Initializes the FGAccelerations class after instantiation and prior to first execution.
The base class FGModel::InitModel is called first, initializing pointers to the
other FGModel objects (and others). */
bool InitModel(void);
/** Runs the state propagation model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@param Holding if true, the executive has been directed to hold the sim from
advancing time. Some models may ignore this flag, such as the Input
model, which may need to be active to listen on a socket for the
"Resume" command to be given.
@return false if no error */
bool Run(bool Holding);
const FGQuaternion& GetQuaterniondot(void) const {return vQtrndot;}
/** Retrieves the body axis acceleration.
Retrieves the computed body axis accelerations based on the
applied forces and accounting for a rotating body frame.
The vector returned is represented by an FGColumnVector reference. The vector
for the acceleration in Body frame is organized (Ax, Ay, Az). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vUVWdot(1) is Ax. Various convenience enumerators are defined
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
eX=1, eY=2, eZ=3.
units ft/sec^2
@return Body axis translational acceleration in ft/sec^2.
*/
const FGColumnVector3& GetUVWdot(void) const { return vUVWdot; }
const FGColumnVector3& GetUVWidot(void) const { return vUVWidot; }
/** Retrieves the body axis angular acceleration vector.
Retrieves the body axis angular acceleration vector in rad/sec^2. The
angular acceleration vector is determined from the applied forces and
accounts for a rotating frame.
The vector returned is represented by an FGColumnVector reference. The vector
for the angular acceleration in Body frame is organized (Pdot, Qdot, Rdot). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vPQRdot(1) is Pdot. Various convenience enumerators are defined
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
eP=1, eQ=2, eR=3.
units rad/sec^2
@return The angular acceleration vector.
*/
const FGColumnVector3& GetPQRdot(void) const {return vPQRdot;}
const FGColumnVector3& GetPQRidot(void) const {return vPQRidot;}
/** Retrieves a body frame acceleration component.
Retrieves a body frame acceleration component. The acceleration returned
is extracted from the vUVWdot vector (an FGColumnVector). The vector for
the acceleration in Body frame is organized (Ax, Ay, Az). The vector is
1-based. In other words, GetUVWdot(1) returns Ax. Various convenience
enumerators are defined in FGJSBBase. The relevant enumerators for the
acceleration returned by this call are, eX=1, eY=2, eZ=3.
units ft/sec^2
@param idx the index of the acceleration component desired (1-based).
@return The body frame acceleration component.
*/
double GetUVWdot(int idx) const { return vUVWdot(idx); }
FGColumnVector3& GetBodyAccel(void) { return vBodyAccel; }
double GetBodyAccel(int idx) const { return vBodyAccel(idx); }
/** Retrieves a body frame angular acceleration component.
Retrieves a body frame angular acceleration component. The angular
acceleration returned is extracted from the vPQRdot vector (an
FGColumnVector). The vector for the angular acceleration in Body frame
is organized (Pdot, Qdot, Rdot). The vector is 1-based. In other words,
GetPQRdot(1) returns Pdot (roll acceleration). Various convenience
enumerators are defined in FGJSBBase. The relevant enumerators for the
angular acceleration returned by this call are, eP=1, eQ=2, eR=3.
units rad/sec^2
@param axis the index of the angular acceleration component desired (1-based).
@return The body frame angular acceleration component.
*/
double GetPQRdot(int axis) const {return vPQRdot(axis);}
double GetMoments(int idx) const { return in.Moment(idx) + vFrictionMoments(idx); }
double GetForces(int idx) const { return in.Force(idx) + vFrictionForces(idx); }
double GetGroundMoments(int idx) const { return in.GroundMoment(idx) + vFrictionMoments(idx); }
double GetGroundForces(int idx) const { return in.GroundForce(idx) + vFrictionForces(idx); }
void InitializeDerivatives(void);
void DumpState(void);
struct Inputs {
FGMatrix33 J;
FGMatrix33 Jinv;
FGMatrix33 Ti2b;
FGMatrix33 Tb2i;
FGMatrix33 Tec2b;
FGMatrix33 Tl2b;
FGQuaternion qAttitudeECI;
FGColumnVector3 Moment;
FGColumnVector3 GroundMoment;
FGColumnVector3 Force;
FGColumnVector3 GroundForce;
FGColumnVector3 J2Grav;
FGColumnVector3 vPQRi;
FGColumnVector3 vPQR;
FGColumnVector3 vUVW;
FGColumnVector3 vInertialPosition;
FGColumnVector3 vOmegaPlanet;
FGColumnVector3 TerrainVelocity;
FGColumnVector3 TerrainAngularVel;
double DeltaT;
double Mass;
double GAccel;
std::vector<LagrangeMultiplier*> *MultipliersList;
} in;
private:
FGColumnVector3 vPQRdot, vPQRidot;
FGColumnVector3 vUVWdot, vUVWidot;
FGQuaternion vQtrndot;
FGColumnVector3 vBodyAccel;
FGColumnVector3 vGravAccel;
FGColumnVector3 vFrictionForces;
FGColumnVector3 vFrictionMoments;
int gravType;
void CalculatePQRdot(void);
void CalculateQuatdot(void);
void CalculateUVWdot(void);
void ResolveFrictionForces(double dt);
void bind(void);
void Debug(int from);
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -40,19 +40,15 @@ INCLUDES
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <FGFDMExec.h>
#include "FGFDMExec.h"
#include "FGAerodynamics.h"
#include "FGPropagate.h"
#include "FGAircraft.h"
#include "FGAuxiliary.h"
#include "FGMassBalance.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGAerodynamics.cpp,v 1.38 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGAerodynamics.cpp,v 1.41 2011/08/04 12:46:32 jberndt Exp $";
static const char *IdHdr = ID_AERODYNAMICS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -139,36 +135,31 @@ bool FGAerodynamics::Run(bool Holding)
if (Holding) return false; // if paused don't execute
unsigned int axis_ctr, ctr;
const double alpha=FDMExec->GetAuxiliary()->Getalpha();
const double twovel=2*FDMExec->GetAuxiliary()->GetVt();
const double qbar = FDMExec->GetAuxiliary()->Getqbar();
const double wingarea = FDMExec->GetAircraft()->GetWingArea(); // TODO: Make these constants constant!
const double wingspan = FDMExec->GetAircraft()->GetWingSpan();
const double wingchord = FDMExec->GetAircraft()->Getcbar();
const double wingincidence = FDMExec->GetAircraft()->GetWingIncidence();
const double twovel=2*in.Vt;
RunPreFunctions();
// calculate some oft-used quantities for speed
if (twovel != 0) {
bi2vel = wingspan / twovel;
ci2vel = wingchord / twovel;
bi2vel = in.Wingspan / twovel;
ci2vel = in.Wingchord / twovel;
}
alphaw = alpha + wingincidence;
qbar_area = wingarea * qbar;
alphaw = in.Alpha + in.Wingincidence;
qbar_area = in.Wingarea * in.Qbar;
if (alphaclmax != 0) {
if (alpha > 0.85*alphaclmax) {
impending_stall = 10*(alpha/alphaclmax - 0.85);
if (in.Alpha > 0.85*alphaclmax) {
impending_stall = 10*(in.Alpha/alphaclmax - 0.85);
} else {
impending_stall = 0;
}
}
if (alphahystmax != 0.0 && alphahystmin != 0.0) {
if (alpha > alphahystmax) {
if (in.Alpha > alphahystmax) {
stall_hyst = 1;
} else if (alpha < alphahystmin) {
} else if (in.Alpha < alphahystmin) {
stall_hyst = 0;
}
}
@ -188,16 +179,16 @@ bool FGAerodynamics::Run(bool Holding)
switch (axisType) {
case atBodyXYZ: // Forces already in body axes; no manipulation needed
vFw = GetTb2w()*vFnative;
vFw = in.Tb2w*vFnative;
vForces = vFnative;
break;
case atLiftDrag: // Copy forces into wind axes
vFw = vFnative;
vFw(eDrag)*=-1; vFw(eLift)*=-1;
vForces = GetTw2b()*vFw;
vForces = in.Tw2b*vFw;
break;
case atAxialNormal: // Convert native forces into Axial|Normal|Side system
vFw = GetTb2w()*vFnative;
vFw = in.Tb2w*vFnative;
vFnative(eX)*=-1; vFnative(eZ)*=-1;
vForces = vFnative;
break;
@ -207,19 +198,23 @@ bool FGAerodynamics::Run(bool Holding)
exit(-1);
}
// Calculate aerodynamic reference point shift, if any
if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->GetValue()*wingchord*12.0;
// Calculate lift coefficient squared
if ( qbar > 0) {
clsq = vFw(eLift) / (wingarea*qbar);
if ( in.Qbar > 0) {
clsq = vFw(eLift) / (in.Wingarea*in.Qbar);
clsq *= clsq;
}
// Calculate lift Lift over Drag
if ( fabs(vFw(eDrag)) > 0.0) lod = fabs( vFw(eLift) / vFw(eDrag) );
vDXYZcg = FDMExec->GetMassBalance()->StructuralToBody(FDMExec->GetAircraft()->GetXYZrp() + vDeltaRP);
// Calculate aerodynamic reference point shift, if any. The shift
// takes place in the body axis. That is, if the shift is negative,
// it is towards the back (tail) of the vehicle. The AeroRPShift
// function should be non-dimensionalized by the wing chord. The
// calculated vDeltaRP will be in feet.
if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->GetValue()*in.Wingchord;
vDXYZcg = in.RPBody + vDeltaRP;
vMoments = vDXYZcg*vForces; // M = r X F
@ -234,75 +229,6 @@ bool FGAerodynamics::Run(bool Holding)
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// From Stevens and Lewis, "Aircraft Control and Simulation", 3rd Ed., the
// transformation from body to wind axes is defined (where "a" is alpha and "B"
// is beta):
//
// cos(a)*cos(B) sin(B) sin(a)*cos(B)
// -cos(a)*sin(B) cos(B) -sin(a)*sin(B)
// -sin(a) 0 cos(a)
//
// The transform from wind to body axes is then,
//
// cos(a)*cos(B) -cos(a)*sin(B) -sin(a)
// sin(B) cos(B) 0
// sin(a)*cos(B) -sin(a)*sin(B) cos(a)
FGMatrix33& FGAerodynamics::GetTw2b(void)
{
double ca, cb, sa, sb;
double alpha = FDMExec->GetAuxiliary()->Getalpha();
double beta = FDMExec->GetAuxiliary()->Getbeta();
ca = cos(alpha);
sa = sin(alpha);
cb = cos(beta);
sb = sin(beta);
mTw2b(1,1) = ca*cb;
mTw2b(1,2) = -ca*sb;
mTw2b(1,3) = -sa;
mTw2b(2,1) = sb;
mTw2b(2,2) = cb;
mTw2b(2,3) = 0.0;
mTw2b(3,1) = sa*cb;
mTw2b(3,2) = -sa*sb;
mTw2b(3,3) = ca;
return mTw2b;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGMatrix33& FGAerodynamics::GetTb2w(void)
{
double alpha,beta;
double ca, cb, sa, sb;
alpha = FDMExec->GetAuxiliary()->Getalpha();
beta = FDMExec->GetAuxiliary()->Getbeta();
ca = cos(alpha);
sa = sin(alpha);
cb = cos(beta);
sb = sin(beta);
mTb2w(1,1) = ca*cb;
mTb2w(1,2) = sb;
mTb2w(1,3) = sa*cb;
mTb2w(2,1) = -ca*sb;
mTb2w(2,2) = cb;
mTb2w(2,3) = -sa*sb;
mTb2w(3,1) = -sa;
mTb2w(3,2) = 0.0;
mTb2w(3,3) = ca;
return mTb2w;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGAerodynamics::Load(Element *element)
@ -318,6 +244,7 @@ bool FGAerodynamics::Load(Element *element)
if (!fname.empty()) {
file = FDMExec->GetFullAircraftPath() + separator + fname;
document = LoadXMLDocument(file);
if (document == 0L) return false;
} else {
document = element;
}

View file

@ -52,7 +52,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_AERODYNAMICS "$Id: FGAerodynamics.h,v 1.23 2011/05/20 03:18:36 jberndt Exp $"
#define ID_AERODYNAMICS "$Id: FGAerodynamics.h,v 1.25 2011/08/04 12:46:32 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -109,7 +109,7 @@ CLASS DOCUMENTATION
Systems may NOT be combined, or a load error will occur.
@author Jon S. Berndt, Tony Peden
@version $Revision: 1.23 $
@version $Revision: 1.25 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -202,18 +202,22 @@ public:
aero functions */
std::string GetAeroFunctionValues(const std::string& delimeter) const;
/** Calculates and returns the wind-to-body axis transformation matrix.
@return a reference to the wind-to-body transformation matrix.
*/
FGMatrix33& GetTw2b(void);
/** Calculates and returns the body-to-wind axis transformation matrix.
@return a reference to the wind-to-body transformation matrix.
*/
FGMatrix33& GetTb2w(void);
std::vector <FGFunction*> * GetAeroFunctions(void) const { return AeroFunctions; }
struct Inputs {
double Alpha;
double Beta;
double Vt;
double Qbar;
double Wingarea;
double Wingspan;
double Wingchord;
double Wingincidence;
FGColumnVector3 RPBody;
FGMatrix33 Tb2w;
FGMatrix33 Tw2b;
} in;
private:
enum eAxisType {atNone, atLiftDrag, atAxialNormal, atBodyXYZ} axisType;
typedef std::map<std::string,int> AxisIndex;
@ -227,8 +231,6 @@ private:
FGColumnVector3 vMoments;
FGColumnVector3 vDXYZcg;
FGColumnVector3 vDeltaRP;
FGMatrix33 mTw2b;
FGMatrix33 mTb2w;
double alphaclmax, alphaclmin;
double alphahystmax, alphahystmin;
double impending_stall, stall_hyst;

View file

@ -44,15 +44,7 @@ INCLUDES
#include <cmath>
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGInertial.h"
#include "FGGroundReactions.h"
#include "FGExternalReactions.h"
#include "FGBuoyantForces.h"
#include "FGAerodynamics.h"
#include "FGFDMExec.h"
#include "FGPropagate.h"
#include "FGPropulsion.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
@ -67,7 +59,7 @@ DEFINITIONS
GLOBAL DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
static const char *IdSrc = "$Id: FGAircraft.cpp,v 1.31 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGAircraft.cpp,v 1.33 2011/08/21 15:06:38 bcoconni Exp $";
static const char *IdHdr = ID_AIRCRAFT;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -78,6 +70,8 @@ FGAircraft::FGAircraft(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGAircraft";
WingSpan = 0.0;
WingArea = 0.0;
cbar = 0.0;
HTailArea = VTailArea = 0.0;
HTailArm = VTailArm = 0.0;
lbarh = lbarv = 0.0;
@ -115,32 +109,24 @@ bool FGAircraft::Run(bool Holding)
vForces.InitMatrix();
if (!HoldDown) {
vForces += FDMExec->GetAerodynamics()->GetForces();
vForces += FDMExec->GetPropulsion()->GetForces();
vForces += FDMExec->GetGroundReactions()->GetForces();
vForces += FDMExec->GetExternalReactions()->GetForces();
vForces += FDMExec->GetBuoyantForces()->GetForces();
vForces += in.AeroForce;
vForces += in.PropForce;
vForces += in.GroundForce;
vForces += in.ExternalForce;
vForces += in.BuoyantForce;
} else {
const FGMatrix33& mTl2b = FDMExec->GetPropagate()->GetTl2b();
vForces = mTl2b * FGColumnVector3(0,0,-FDMExec->GetMassBalance()->GetWeight());
vForces = in.Tl2b * FGColumnVector3(0,0,-in.Weight);
}
vMoments.InitMatrix();
if (!HoldDown) {
vMoments += FDMExec->GetAerodynamics()->GetMoments();
vMoments += FDMExec->GetPropulsion()->GetMoments();
vMoments += FDMExec->GetGroundReactions()->GetMoments();
vMoments += FDMExec->GetExternalReactions()->GetMoments();
vMoments += FDMExec->GetBuoyantForces()->GetMoments();
vMoments += in.AeroMoment;
vMoments += in.PropMoment;
vMoments += in.GroundMoment;
vMoments += in.ExternalMoment;
vMoments += in.BuoyantMoment;
}
vBodyAccel = vForces/FDMExec->GetMassBalance()->GetMass();
vNcg = vBodyAccel/FDMExec->GetInertial()->SLgravity();
vNwcg = FDMExec->GetAerodynamics()->GetTb2w() * vNcg;
vNwcg(3) = 1.0 - vNwcg(3);
RunPostFunctions();
return false;
@ -148,16 +134,6 @@ bool FGAircraft::Run(bool Holding)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAircraft::GetNlf(void) const
{
if (FDMExec->GetMassBalance()->GetWeight() != 0)
return (-FDMExec->GetAerodynamics()->GetvFw(3))/FDMExec->GetMassBalance()->GetWeight();
else
return 0.;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGAircraft::Load(Element* el)
{
string element_name;
@ -240,14 +216,7 @@ void FGAircraft::bind(void)
PropertyManager->Tie("metrics/visualrefpoint-x-in", this, eX, (PMF)&FGAircraft::GetXYZvrp);
PropertyManager->Tie("metrics/visualrefpoint-y-in", this, eY, (PMF)&FGAircraft::GetXYZvrp);
PropertyManager->Tie("metrics/visualrefpoint-z-in", this, eZ, (PMF)&FGAircraft::GetXYZvrp);
PropertyManager->Tie("forces/fbx-total-lbs", this, eX, (PMF)&FGAircraft::GetForces);
PropertyManager->Tie("forces/fby-total-lbs", this, eY, (PMF)&FGAircraft::GetForces);
PropertyManager->Tie("forces/fbz-total-lbs", this, eZ, (PMF)&FGAircraft::GetForces);
PropertyManager->Tie("forces/load-factor", this, &FGAircraft::GetNlf);
PropertyManager->Tie("forces/hold-down", this, &FGAircraft::GetHoldDown, &FGAircraft::SetHoldDown);
PropertyManager->Tie("moments/l-total-lbsft", this, eL, (PMF)&FGAircraft::GetMoments);
PropertyManager->Tie("moments/m-total-lbsft", this, eM, (PMF)&FGAircraft::GetMoments);
PropertyManager->Tie("moments/n-total-lbsft", this, eN, (PMF)&FGAircraft::GetMoments);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -44,12 +44,13 @@ INCLUDES
#include "FGModel.h"
#include "input_output/FGXMLElement.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_AIRCRAFT "$Id: FGAircraft.h,v 1.17 2011/05/20 03:18:36 jberndt Exp $"
#define ID_AIRCRAFT "$Id: FGAircraft.h,v 1.18 2011/07/10 20:18:14 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -90,7 +91,7 @@ CLASS DOCUMENTATION
@endcode
@author Jon S. Berndt
@version $Id: FGAircraft.h,v 1.17 2011/05/20 03:18:36 jberndt Exp $
@version $Id: FGAircraft.h,v 1.18 2011/07/10 20:18:14 jberndt Exp $
@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
@ -159,10 +160,6 @@ public:
double GetMoments(int idx) const { return vMoments(idx); }
const FGColumnVector3& GetForces(void) const { return vForces; }
double GetForces(int idx) const { return vForces(idx); }
FGColumnVector3& GetBodyAccel(void) { return vBodyAccel; }
double GetBodyAccel(int idx) const { return vBodyAccel(idx); }
const FGColumnVector3& GetNcg(void) const { return vNcg; }
double GetNcg(int idx) const { return vNcg(idx); }
const FGColumnVector3& GetXYZrp(void) const { return vXYZrp; }
const FGColumnVector3& GetXYZvrp(void) const { return vXYZvrp; }
const FGColumnVector3& GetXYZep(void) const { return vXYZep; }
@ -177,13 +174,24 @@ public:
void SetWingArea(double S) {WingArea = S;}
double GetNlf(void) const;
FGColumnVector3& GetNwcg(void) { return vNwcg; }
void bind(void);
void unbind(void);
struct Inputs {
FGColumnVector3 AeroForce;
FGColumnVector3 PropForce;
FGColumnVector3 GroundForce;
FGColumnVector3 ExternalForce;
FGColumnVector3 BuoyantForce;
FGColumnVector3 AeroMoment;
FGColumnVector3 PropMoment;
FGColumnVector3 GroundMoment;
FGColumnVector3 ExternalMoment;
FGColumnVector3 BuoyantMoment;
FGMatrix33 Tl2b;
double Weight;
} in;
private:
FGColumnVector3 vMoments;
FGColumnVector3 vForces;
@ -191,9 +199,6 @@ private:
FGColumnVector3 vXYZvrp;
FGColumnVector3 vXYZep;
FGColumnVector3 vDXYZcg;
FGColumnVector3 vBodyAccel;
FGColumnVector3 vNcg;
FGColumnVector3 vNwcg;
double WingArea, WingSpan, cbar, WingIncidence;
double HTailArea, VTailArea, HTailArm, VTailArm;

View file

@ -1,13 +1,12 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGAtmosphere.cpp
Author: Jon Berndt
Implementation of 1959 Standard Atmosphere added by Tony Peden
Date started: 11/24/98
Purpose: Models the atmosphere
Called by: FGSimExec
Author: Jon Berndt, Tony Peden
Date started: 6/2011
Purpose: Models an atmosphere interface class
Called by: FGFDMExec
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
@ -28,92 +27,43 @@
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
Models the atmosphere. The equation used below was determined by a third order
curve fit using Excel. The data is from the ICAO atmosphere model.
This models a base atmosphere class to serve as a common interface to any derived
atmosphere models.
HISTORY
--------------------------------------------------------------------------------
11/24/98 JSB Created
07/23/99 TP Added implementation of 1959 Standard Atmosphere
Moved calculation of Mach number to FGPropagate
Later updated to '76 model
6/18/2011 Started Jon S. Berndt
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] Anderson, John D. "Introduction to Flight, Third Edition", McGraw-Hill,
1989, ISBN 0-07-001641-0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGAtmosphere.h"
#include "FGAircraft.h"
#include "FGPropagate.h"
#include "FGInertial.h"
#include "FGAuxiliary.h"
#include "FGFDMExec.h"
#include "input_output/FGPropertyManager.h"
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;
#include "FGFDMExec.h"
#include "FGAtmosphere.h"
namespace JSBSim {
static const char *IdSrc = "$Id: FGAtmosphere.cpp,v 1.45 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGAtmosphere.cpp,v 1.48 2011/07/10 20:18:14 jberndt Exp $";
static const char *IdHdr = ID_ATMOSPHERE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGAtmosphere::FGAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex)
FGAtmosphere::FGAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex),
PressureAltitude(0.0), // ft
DensityAltitude(0.0), // ft
SutherlandConstant(198.72), // deg Rankine
Beta(2.269690E-08) // slug/(sec ft R^0.5)
{
Name = "FGAtmosphere";
lastIndex = 0;
h = 0.0;
psiw = 0.0;
htab[0]=0;
htab[1]= 36089.0;
htab[2]= 65617.0;
htab[3]=104987.0;
htab[4]=154199.0;
htab[5]=167322.0;
htab[6]=232940.0;
htab[7]=278385.0; //ft.
MagnitudedAccelDt = MagnitudeAccel = Magnitude = 0.0;
SetTurbType( ttMilspec );
TurbGain = 1.0;
TurbRate = 10.0;
Rhythmicity = 0.1;
spike = target_time = strength = 0.0;
wind_from_clockwise = 0.0;
SutherlandConstant = 198.72; // deg Rankine
Beta = 2.269690E-08; // slug/(sec ft R^0.5)
T_dev_sl = T_dev = delta_T = 0.0;
StandardTempOnly = false;
first_pass = true;
vGustNED.InitMatrix();
vTurbulenceNED.InitMatrix();
// Milspec turbulence model
windspeed_at_20ft = 0.;
probability_of_exceedence_index = 0;
POE_Table = new FGTable(7,12);
// this is Figure 7 from p. 49 of MIL-F-8785C
// rows: probability of exceedance curve index, cols: altitude in ft
*POE_Table
<< 500.0 << 1750.0 << 3750.0 << 7500.0 << 15000.0 << 25000.0 << 35000.0 << 45000.0 << 55000.0 << 65000.0 << 75000.0 << 80000.0
<< 1 << 3.2 << 2.2 << 1.5 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 2 << 4.2 << 3.6 << 3.3 << 1.6 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 3 << 6.6 << 6.9 << 7.4 << 6.7 << 4.6 << 2.7 << 0.4 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 4 << 8.6 << 9.6 << 10.6 << 10.1 << 8.0 << 6.6 << 5.0 << 4.2 << 2.7 << 0.0 << 0.0 << 0.0
<< 5 << 11.8 << 13.0 << 16.0 << 15.1 << 11.6 << 9.7 << 8.1 << 8.2 << 7.9 << 4.9 << 3.2 << 2.1
<< 6 << 15.6 << 17.6 << 23.0 << 23.6 << 22.1 << 20.0 << 16.0 << 15.1 << 12.1 << 7.9 << 6.2 << 5.1
<< 7 << 18.7 << 21.5 << 28.4 << 30.2 << 30.7 << 31.0 << 25.2 << 23.1 << 17.5 << 10.7 << 8.4 << 7.2;
bind();
Debug(0);
@ -123,7 +73,6 @@ FGAtmosphere::FGAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex)
FGAtmosphere::~FGAtmosphere()
{
delete(POE_Table);
Debug(1);
}
@ -131,17 +80,16 @@ FGAtmosphere::~FGAtmosphere()
bool FGAtmosphere::InitModel(void)
{
UseInternal(); // this is the default
Calculate(0.0);
SLtemperature = Temperature = 518.67;
SLpressure = Pressure = 2116.22;
SLdensity = Density = Pressure/(Reng*Temperature);
SLsoundspeed = Soundspeed = sqrt(SHRatio*Reng*(Temperature));
Calculate(h);
StdSLtemperature = SLtemperature = 518.67;
StdSLpressure = SLpressure = 2116.22;
StdSLdensity = SLdensity = 0.00237767;
StdSLsoundspeed = SLsoundspeed = sqrt(SHRatio*Reng*StdSLtemperature);
rSLtemperature = 1.0/StdSLtemperature;
rSLpressure = 1.0/StdSLpressure;
rSLdensity = 1.0/StdSLdensity;
rSLsoundspeed = 1.0/StdSLsoundspeed;
rSLtemperature = 1/SLtemperature ;
rSLpressure = 1/SLpressure ;
rSLdensity = 1/SLdensity ;
rSLsoundspeed = 1/SLsoundspeed ;
return true;
}
@ -155,15 +103,7 @@ bool FGAtmosphere::Run(bool Holding)
RunPreFunctions();
T_dev = 0.0;
h = FDMExec->GetPropagate()->GetAltitudeASL();
if (!useExternal) {
Calculate(h);
CalculateDerived();
} else {
CalculateDerived();
}
Calculate(in.altitudeASL);
RunPostFunctions();
@ -172,537 +112,105 @@ bool FGAtmosphere::Run(bool Holding)
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// See reference 1
void FGAtmosphere::Calculate(double altitude)
{
double slope, reftemp, refpress;
int i = lastIndex;
Temperature = GetTemperature(altitude);
Pressure = GetPressure(altitude);
Density = Pressure/(Reng*Temperature);
Soundspeed = sqrt(SHRatio*Reng*(Temperature));
PressureAltitude = altitude;
DensityAltitude = altitude;
if (altitude < htab[lastIndex]) {
if (altitude <= 0) {
i = 0;
altitude=0;
} else {
i = lastIndex-1;
while (htab[i] > altitude) i--;
}
} else if (altitude > htab[lastIndex+1]) {
if (altitude >= htab[7]) {
i = 7;
altitude = htab[7];
} else {
i = lastIndex+1;
while (htab[i+1] < altitude) i++;
}
}
switch(i) {
case 0: // Sea level
slope = -0.00356616; // R/ft.
reftemp = 518.67; // in degrees Rankine, 288.15 Kelvin
refpress = 2116.22; // psf
//refdens = 0.00237767; // slugs/cubic ft.
break;
case 1: // 36089 ft. or 11 km
slope = 0;
reftemp = 389.97; // in degrees Rankine, 216.65 Kelvin
refpress = 472.763;
//refdens = 0.000706032;
break;
case 2: // 65616 ft. or 20 km
slope = 0.00054864;
reftemp = 389.97; // in degrees Rankine, 216.65 Kelvin
refpress = 114.636;
//refdens = 0.000171306;
break;
case 3: // 104986 ft. or 32 km
slope = 0.001536192;
reftemp = 411.57; // in degrees Rankine, 228.65 Kelvin
refpress = 18.128;
//refdens = 1.18422e-05;
break;
case 4: // 154199 ft. 47 km
slope = 0;
reftemp = 487.17; // in degrees Rankine, 270.65 Kelvin
refpress = 2.316;
//refdens = 4.00585e-7;
break;
case 5: // 167322 ft. or 51 km
slope = -0.001536192;
reftemp = 487.17; // in degrees Rankine, 270.65 Kelvin
refpress = 1.398;
//refdens = 8.17102e-7;
break;
case 6: // 232940 ft. or 71 km
slope = -0.00109728;
reftemp = 386.368; // in degrees Rankine, 214.649 Kelvin
refpress = 0.0826;
//refdens = 8.77702e-9;
break;
case 7: // 278385 ft. or 84.8520 km
slope = 0;
reftemp = 336.5; // in degrees Rankine, 186.94 Kelvin
refpress = 0.00831;
//refdens = 2.19541e-10;
break;
default: // sea level
slope = -0.00356616; // R/ft.
reftemp = 518.67; // in degrees Rankine, 288.15 Kelvin
refpress = 2116.22; // psf
//refdens = 0.00237767; // slugs/cubic ft.
break;
}
// If delta_T is set, then that is our temperature deviation at any altitude.
// If not, then we'll estimate a deviation based on the sea level deviation (if set).
if(!StandardTempOnly) {
T_dev = 0.0;
if (delta_T != 0.0) {
T_dev = delta_T;
} else {
if ((altitude < 36089.239) && (T_dev_sl != 0.0)) {
T_dev = T_dev_sl * ( 1.0 - (altitude/36089.239));
}
}
reftemp+=T_dev;
}
if (slope == 0) {
intTemperature = reftemp;
intPressure = refpress*exp(-FDMExec->GetInertial()->SLgravity()/(reftemp*Reng)*(altitude-htab[i]));
intDensity = intPressure/(Reng*intTemperature);
} else {
intTemperature = reftemp+slope*(altitude-htab[i]);
intPressure = refpress*pow(intTemperature/reftemp,-FDMExec->GetInertial()->SLgravity()/(slope*Reng));
intDensity = intPressure/(Reng*intTemperature);
}
lastIndex=i;
Viscosity = Beta * pow(Temperature, 1.5) / (SutherlandConstant + Temperature);
KinematicViscosity = Viscosity / Density;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Calculate parameters derived from T, P and rho
// Sum gust and turbulence values in NED frame into the wind vector.
void FGAtmosphere::CalculateDerived(void)
void FGAtmosphere::SetPressureSL(double pressure, ePressure unit)
{
T_dev = (*temperature) - GetTemperature(h);
double press = ConvertToPSF(pressure, unit);
if (T_dev == 0.0) density_altitude = h;
else density_altitude = 518.67/0.00356616 * (1.0 - pow(GetDensityRatio(),0.235));
if (turbType != ttNone) Turbulence();
vTotalWindNED = vWindNED + vGustNED + vTurbulenceNED;
// psiw (Wind heading) is the direction the wind is blowing towards
if (vWindNED(eX) != 0.0) psiw = atan2( vWindNED(eY), vWindNED(eX) );
if (psiw < 0) psiw += 2*M_PI;
soundspeed = sqrt(SHRatio*Reng*(*temperature));
intViscosity = Beta * pow(intTemperature, 1.5) / (SutherlandConstant + intTemperature);
intKinematicViscosity = intViscosity / intDensity;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the standard atmospheric properties at a specified altitude
void FGAtmosphere::GetStdAtmosphere(double altitude) {
StandardTempOnly = true;
Calculate(altitude);
StandardTempOnly = false;
atmosphere.Temperature = intTemperature;
atmosphere.Pressure = intPressure;
atmosphere.Density = intDensity;
// Reset the internal atmospheric state
Calculate(h);
SLpressure = press;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the standard pressure at a specified altitude
// Get the modeled density at a specified altitude
double FGAtmosphere::GetPressure(double altitude) {
GetStdAtmosphere(altitude);
return atmosphere.Pressure;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the standard temperature at a specified altitude
double FGAtmosphere::GetTemperature(double altitude) {
GetStdAtmosphere(altitude);
return atmosphere.Temperature;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the standard density at a specified altitude
double FGAtmosphere::GetDensity(double altitude) {
GetStdAtmosphere(altitude);
return atmosphere.Density;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// square a value, but preserve the original sign
static inline double square_signed (double value)
double FGAtmosphere::GetDensity(double altitude) const
{
if (value < 0)
return value * value * -1;
else
return value * value;
return GetPressure(altitude)/(Reng * GetTemperature(altitude));
}
/// simply square a value
static inline double sqr(double x) { return x*x; }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// psi is the angle that the wind is blowing *towards*
// This function sets the sea level temperature.
// Internally, the Rankine scale is used for calculations, so any temperature
// supplied must be converted to that unit.
void FGAtmosphere::SetWindspeed(double speed)
void FGAtmosphere::SetTemperatureSL(double t, eTemperature unit)
{
if (vWindNED.Magnitude() == 0.0) {
psiw = 0.0;
vWindNED(eNorth) = speed;
} else {
vWindNED(eNorth) = speed * cos(psiw);
vWindNED(eEast) = speed * sin(psiw);
vWindNED(eDown) = 0.0;
}
SLtemperature = ConvertToRankine(t, unit);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAtmosphere::GetWindspeed(void) const
double FGAtmosphere::ConvertToRankine(double t, eTemperature unit) const
{
return vWindNED.Magnitude();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// psi is the angle that the wind is blowing *towards*
void FGAtmosphere::SetWindPsi(double dir)
{
double mag = GetWindspeed();
psiw = dir;
SetWindspeed(mag);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::Turbulence(void)
{
const double DeltaT = rate*FDMExec->GetDeltaT();
const double wingspan = FDMExec->GetAircraft()->GetWingSpan();
const double HOverBMAC = FDMExec->GetAuxiliary()->GetHOverBMAC();
const FGMatrix33& Tl2b = FDMExec->GetPropagate()->GetTl2b();
const double HTailArm = FDMExec->GetAircraft()->GetHTailArm();
const double VTailArm = FDMExec->GetAircraft()->GetVTailArm();
switch (turbType) {
case ttStandard: {
// TurbGain = TurbGain * TurbGain * 100.0; // what is this!?
vDirectiondAccelDt(eX) = 1 - 2.0*(double(rand())/double(RAND_MAX));
vDirectiondAccelDt(eY) = 1 - 2.0*(double(rand())/double(RAND_MAX));
vDirectiondAccelDt(eZ) = 1 - 2.0*(double(rand())/double(RAND_MAX));
MagnitudedAccelDt = 1 - 2.0*(double(rand())/double(RAND_MAX)) - Magnitude;
// Scale the magnitude so that it moves
// away from the peaks
MagnitudedAccelDt = ((MagnitudedAccelDt - Magnitude) /
(1 + fabs(Magnitude)));
MagnitudeAccel += MagnitudedAccelDt*TurbRate*DeltaT;
Magnitude += MagnitudeAccel*DeltaT;
Magnitude = fabs(Magnitude);
vDirectiondAccelDt.Normalize();
// deemphasise non-vertical forces
vDirectiondAccelDt(eX) = square_signed(vDirectiondAccelDt(eX));
vDirectiondAccelDt(eY) = square_signed(vDirectiondAccelDt(eY));
vDirectionAccel += vDirectiondAccelDt*TurbRate*DeltaT;
vDirectionAccel.Normalize();
vDirection += vDirectionAccel*DeltaT;
vDirection.Normalize();
// Diminish turbulence within three wingspans
// of the ground
vTurbulenceNED = TurbGain * Magnitude * vDirection;
if (HOverBMAC < 3.0)
vTurbulenceNED *= (HOverBMAC / 3.0) * (HOverBMAC / 3.0);
// I don't believe these next two statements calculate the proper gradient over
// the aircraft body. One reason is because this has no relationship with the
// orientation or velocity of the aircraft, which it must have. What is vTurbulenceGrad
// supposed to represent? And the direction and magnitude of the turbulence can change,
// so both accelerations need to be accounted for, no?
// Need to determine the turbulence change in body axes between two time points.
vTurbulenceGrad = TurbGain*MagnitudeAccel * vDirection;
vBodyTurbGrad = Tl2b*vTurbulenceGrad;
if (wingspan > 0) {
vTurbPQR(eP) = vBodyTurbGrad(eY)/wingspan;
} else {
vTurbPQR(eP) = vBodyTurbGrad(eY)/30.0;
}
// if (HTailArm != 0.0)
// vTurbPQR(eQ) = vBodyTurbGrad(eZ)/HTailArm;
// else
// vTurbPQR(eQ) = vBodyTurbGrad(eZ)/10.0;
if (VTailArm > 0)
vTurbPQR(eR) = vBodyTurbGrad(eX)/VTailArm;
else
vTurbPQR(eR) = vBodyTurbGrad(eX)/10.0;
// Clear the horizontal forces
// actually felt by the plane, now
// that we've used them to calculate
// moments.
// Why? (JSB)
// vTurbulenceNED(eX) = 0.0;
// vTurbulenceNED(eY) = 0.0;
double targetTemp=0; // in degrees Rankine
switch(unit) {
case eFahrenheit:
targetTemp = t + 459.67;
break;
}
case ttCulp: {
vTurbPQR(eP) = wind_from_clockwise;
if (TurbGain == 0.0) return;
// keep the inputs within allowable limts for this model
if (TurbGain < 0.0) TurbGain = 0.0;
if (TurbGain > 1.0) TurbGain = 1.0;
if (TurbRate < 0.0) TurbRate = 0.0;
if (TurbRate > 30.0) TurbRate = 30.0;
if (Rhythmicity < 0.0) Rhythmicity = 0.0;
if (Rhythmicity > 1.0) Rhythmicity = 1.0;
// generate a sine wave corresponding to turbulence rate in hertz
double time = FDMExec->GetSimTime();
double sinewave = sin( time * TurbRate * 6.283185307 );
double random = 0.0;
if (target_time == 0.0) {
strength = random = 1 - 2.0*(double(rand())/double(RAND_MAX));
target_time = time + 0.71 + (random * 0.5);
}
if (time > target_time) {
spike = 1.0;
target_time = 0.0;
}
// max vertical wind speed in fps, corresponds to TurbGain = 1.0
double max_vs = 40;
vTurbulenceNED(1) = vTurbulenceNED(2) = vTurbulenceNED(3) = 0.0;
double delta = strength * max_vs * TurbGain * (1-Rhythmicity) * spike;
// Vertical component of turbulence.
vTurbulenceNED(3) = sinewave * max_vs * TurbGain * Rhythmicity;
vTurbulenceNED(3)+= delta;
if (HOverBMAC < 3.0)
vTurbulenceNED(3) *= HOverBMAC * 0.3333;
// Yaw component of turbulence.
vTurbulenceNED(1) = sin( delta * 3.0 );
vTurbulenceNED(2) = cos( delta * 3.0 );
// Roll component of turbulence. Clockwise vortex causes left roll.
vTurbPQR(eP) += delta * 0.04;
spike = spike * 0.9;
case eCelsius:
targetTemp = t*9.0/5.0 + 32.0 + 459.67;
break;
case eRankine:
targetTemp = t;
break;
case eKelvin:
targetTemp = t*9.0/5.0;
}
case ttMilspec:
case ttTustin: {
double V = FDMExec->GetAuxiliary()->GetVt(); // true airspeed in ft/s
// an index of zero means turbulence is disabled
// airspeed occurs as divisor in the code below
if (probability_of_exceedence_index == 0 || V == 0) {
vTurbulenceNED(1) = vTurbulenceNED(2) = vTurbulenceNED(3) = 0.0;
vTurbPQR(1) = vTurbPQR(2) = vTurbPQR(3) = 0.0;
return;
}
return targetTemp;
}
// Turbulence model according to MIL-F-8785C (Flying Qualities of Piloted Aircraft)
double
h = FDMExec->GetPropagate()->GetDistanceAGL(),
b_w = wingspan,
L_u, L_w, sig_u, sig_w;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if (b_w == 0.) b_w = 30.;
double FGAtmosphere::ConvertToPSF(double p, ePressure unit) const
{
double targetPressure=0; // Pressure in PSF
// clip height functions at 10 ft
if (h <= 10.) h = 10;
// Scale lengths L and amplitudes sigma as function of height
if (h <= 1000) {
L_u = h/pow(0.177 + 0.000823*h, 1.2); // MIL-F-8785c, Fig. 10, p. 55
L_w = h;
sig_w = 0.1*windspeed_at_20ft;
sig_u = sig_w/pow(0.177 + 0.000823*h, 0.4); // MIL-F-8785c, Fig. 11, p. 56
} else if (h <= 2000) {
// linear interpolation between low altitude and high altitude models
L_u = L_w = 1000 + (h-1000.)/1000.*750.;
sig_u = sig_w = 0.1*windspeed_at_20ft
+ (h-1000.)/1000.*(POE_Table->GetValue(probability_of_exceedence_index, h) - 0.1*windspeed_at_20ft);
} else {
L_u = L_w = 1750.; // MIL-F-8785c, Sec. 3.7.2.1, p. 48
sig_u = sig_w = POE_Table->GetValue(probability_of_exceedence_index, h);
}
// keep values from last timesteps
// TODO maybe use deque?
static double
xi_u_km1 = 0, nu_u_km1 = 0,
xi_v_km1 = 0, xi_v_km2 = 0, nu_v_km1 = 0, nu_v_km2 = 0,
xi_w_km1 = 0, xi_w_km2 = 0, nu_w_km1 = 0, nu_w_km2 = 0,
xi_p_km1 = 0, nu_p_km1 = 0,
xi_q_km1 = 0, xi_r_km1 = 0;
double
T_V = DeltaT, // for compatibility of nomenclature
sig_p = 1.9/sqrt(L_w*b_w)*sig_w, // Yeager1998, eq. (8)
sig_q = sqrt(M_PI/2/L_w/b_w), // eq. (14)
sig_r = sqrt(2*M_PI/3/L_w/b_w), // eq. (17)
L_p = sqrt(L_w*b_w)/2.6, // eq. (10)
tau_u = L_u/V, // eq. (6)
tau_w = L_w/V, // eq. (3)
tau_p = L_p/V, // eq. (9)
tau_q = 4*b_w/M_PI/V, // eq. (13)
tau_r =3*b_w/M_PI/V, // eq. (17)
nu_u = GaussianRandomNumber(),
nu_v = GaussianRandomNumber(),
nu_w = GaussianRandomNumber(),
nu_p = GaussianRandomNumber(),
xi_u=0, xi_v=0, xi_w=0, xi_p=0, xi_q=0, xi_r=0;
// values of turbulence NED velocities
if (turbType == ttTustin) {
// the following is the Tustin formulation of Yeager's report
double
omega_w = V/L_w, // hidden in nomenclature p. 3
omega_v = V/L_u, // this is defined nowhere
C_BL = 1/tau_u/tan(T_V/2/tau_u), // eq. (19)
C_BLp = 1/tau_p/tan(T_V/2/tau_p), // eq. (22)
C_BLq = 1/tau_q/tan(T_V/2/tau_q), // eq. (24)
C_BLr = 1/tau_r/tan(T_V/2/tau_r); // eq. (26)
// all values calculated so far are strictly positive, except for
// the random numbers nu_*. This means that in the code below, all
// divisors are strictly positive, too, and no floating point
// exception should occur.
xi_u = -(1 - C_BL*tau_u)/(1 + C_BL*tau_u)*xi_u_km1
+ sig_u*sqrt(2*tau_u/T_V)/(1 + C_BL*tau_u)*(nu_u + nu_u_km1); // eq. (18)
xi_v = -2*(sqr(omega_v) - sqr(C_BL))/sqr(omega_v + C_BL)*xi_v_km1
- sqr(omega_v - C_BL)/sqr(omega_v + C_BL) * xi_v_km2
+ sig_u*sqrt(3*omega_v/T_V)/sqr(omega_v + C_BL)*(
(C_BL + omega_v/sqrt(3.))*nu_v
+ 2/sqrt(3.)*omega_v*nu_v_km1
+ (omega_v/sqrt(3.) - C_BL)*nu_v_km2); // eq. (20) for v
xi_w = -2*(sqr(omega_w) - sqr(C_BL))/sqr(omega_w + C_BL)*xi_w_km1
- sqr(omega_w - C_BL)/sqr(omega_w + C_BL) * xi_w_km2
+ sig_w*sqrt(3*omega_w/T_V)/sqr(omega_w + C_BL)*(
(C_BL + omega_w/sqrt(3.))*nu_w
+ 2/sqrt(3.)*omega_w*nu_w_km1
+ (omega_w/sqrt(3.) - C_BL)*nu_w_km2); // eq. (20) for w
xi_p = -(1 - C_BLp*tau_p)/(1 + C_BLp*tau_p)*xi_p_km1
+ sig_p*sqrt(2*tau_p/T_V)/(1 + C_BLp*tau_p) * (nu_p + nu_p_km1); // eq. (21)
xi_q = -(1 - 4*b_w*C_BLq/M_PI/V)/(1 + 4*b_w*C_BLq/M_PI/V) * xi_q_km1
+ C_BLq/V/(1 + 4*b_w*C_BLq/M_PI/V) * (xi_w - xi_w_km1); // eq. (23)
xi_r = - (1 - 3*b_w*C_BLr/M_PI/V)/(1 + 3*b_w*C_BLr/M_PI/V) * xi_r_km1
+ C_BLr/V/(1 + 3*b_w*C_BLr/M_PI/V) * (xi_v - xi_v_km1); // eq. (25)
} else if (turbType == ttMilspec) {
// the following is the MIL-STD-1797A formulation
// as cited in Yeager's report
xi_u = (1 - T_V/tau_u) *xi_u_km1 + sig_u*sqrt(2*T_V/tau_u)*nu_u; // eq. (30)
xi_v = (1 - 2*T_V/tau_u)*xi_v_km1 + sig_u*sqrt(4*T_V/tau_u)*nu_v; // eq. (31)
xi_w = (1 - 2*T_V/tau_w)*xi_w_km1 + sig_w*sqrt(4*T_V/tau_w)*nu_w; // eq. (32)
xi_p = (1 - T_V/tau_p) *xi_p_km1 + sig_p*sqrt(2*T_V/tau_p)*nu_p; // eq. (33)
xi_q = (1 - T_V/tau_q) *xi_q_km1 + M_PI/4/b_w*(xi_w - xi_w_km1); // eq. (34)
xi_r = (1 - T_V/tau_r) *xi_r_km1 + M_PI/3/b_w*(xi_v - xi_v_km1); // eq. (35)
}
// rotate by wind azimuth and assign the velocities
double cospsi = cos(psiw), sinpsi = sin(psiw);
vTurbulenceNED(1) = cospsi*xi_u + sinpsi*xi_v;
vTurbulenceNED(2) = -sinpsi*xi_u + cospsi*xi_v;
vTurbulenceNED(3) = xi_w;
vTurbPQR(1) = cospsi*xi_p + sinpsi*xi_q;
vTurbPQR(2) = -sinpsi*xi_p + cospsi*xi_q;
vTurbPQR(3) = xi_r;
// vTurbPQR is in the body fixed frame, not NED
vTurbPQR = Tl2b*vTurbPQR;
// hand on the values for the next timestep
xi_u_km1 = xi_u; nu_u_km1 = nu_u;
xi_v_km2 = xi_v_km1; xi_v_km1 = xi_v; nu_v_km2 = nu_v_km1; nu_v_km1 = nu_v;
xi_w_km2 = xi_w_km1; xi_w_km1 = xi_w; nu_w_km2 = nu_w_km1; nu_w_km1 = nu_w;
xi_p_km1 = xi_p; nu_p_km1 = nu_p;
xi_q_km1 = xi_q;
xi_r_km1 = xi_r;
}
switch(unit) {
case ePSF:
targetPressure = p;
break;
case eMillibars:
targetPressure = p*2.08854342;
break;
case ePascals:
targetPressure = p*0.0208854342;
break;
case eInchesHg:
targetPressure = p*70.7180803;
break;
default:
break;
throw("Undefined pressure unit given");
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::UseExternal(void)
{
temperature=&exTemperature;
pressure=&exPressure;
density=&exDensity;
useExternal=true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::UseInternal(void)
{
temperature=&intTemperature;
pressure=&intPressure;
density=&intDensity;
useExternal=false;
return targetPressure;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAtmosphere::bind(void)
{
typedef double (FGAtmosphere::*PMF)(int) const;
typedef double (FGAtmosphere::*PMFv)(void) const;
typedef int (FGAtmosphere::*PMFt)(void) const;
typedef void (FGAtmosphere::*PMFd)(int,double);
typedef void (FGAtmosphere::*PMFi)(int);
PropertyManager->Tie("atmosphere/T-R", this, (PMFv)&FGAtmosphere::GetTemperature);
PropertyManager->Tie("atmosphere/rho-slugs_ft3", this, (PMFv)&FGAtmosphere::GetDensity);
PropertyManager->Tie("atmosphere/P-psf", this, (PMFv)&FGAtmosphere::GetPressure);
typedef double (FGAtmosphere::*PMFi)(int) const;
typedef void (FGAtmosphere::*PMF)(int, double);
PropertyManager->Tie("atmosphere/T-R", this, &FGAtmosphere::GetTemperature);
PropertyManager->Tie("atmosphere/rho-slugs_ft3", this, &FGAtmosphere::GetDensity);
PropertyManager->Tie("atmosphere/P-psf", this, &FGAtmosphere::GetPressure);
PropertyManager->Tie("atmosphere/a-fps", this, &FGAtmosphere::GetSoundSpeed);
PropertyManager->Tie("atmosphere/T-sl-R", this, &FGAtmosphere::GetTemperatureSL);
PropertyManager->Tie("atmosphere/rho-sl-slugs_ft3", this, &FGAtmosphere::GetDensitySL);
@ -712,53 +220,8 @@ void FGAtmosphere::bind(void)
PropertyManager->Tie("atmosphere/sigma", this, &FGAtmosphere::GetDensityRatio);
PropertyManager->Tie("atmosphere/delta", this, &FGAtmosphere::GetPressureRatio);
PropertyManager->Tie("atmosphere/a-ratio", this, &FGAtmosphere::GetSoundSpeedRatio);
PropertyManager->Tie("atmosphere/psiw-rad", this, &FGAtmosphere::GetWindPsi, &FGAtmosphere::SetWindPsi);
PropertyManager->Tie("atmosphere/delta-T", this, &FGAtmosphere::GetDeltaT, &FGAtmosphere::SetDeltaT);
PropertyManager->Tie("atmosphere/T-sl-dev-F", this, &FGAtmosphere::GetSLTempDev, &FGAtmosphere::SetSLTempDev);
PropertyManager->Tie("atmosphere/density-altitude", this, &FGAtmosphere::GetDensityAltitude);
PropertyManager->Tie("atmosphere/wind-north-fps", this, eNorth, (PMF)&FGAtmosphere::GetWindNED,
(PMFd)&FGAtmosphere::SetWindNED);
PropertyManager->Tie("atmosphere/wind-east-fps", this, eEast, (PMF)&FGAtmosphere::GetWindNED,
(PMFd)&FGAtmosphere::SetWindNED);
PropertyManager->Tie("atmosphere/wind-down-fps", this, eDown, (PMF)&FGAtmosphere::GetWindNED,
(PMFd)&FGAtmosphere::SetWindNED);
PropertyManager->Tie("atmosphere/wind-mag-fps", this, &FGAtmosphere::GetWindspeed,
&FGAtmosphere::SetWindspeed);
PropertyManager->Tie("atmosphere/total-wind-north-fps", this, eNorth, (PMF)&FGAtmosphere::GetTotalWindNED);
PropertyManager->Tie("atmosphere/total-wind-east-fps", this, eEast, (PMF)&FGAtmosphere::GetTotalWindNED);
PropertyManager->Tie("atmosphere/total-wind-down-fps", this, eDown, (PMF)&FGAtmosphere::GetTotalWindNED);
PropertyManager->Tie("atmosphere/gust-north-fps", this, eNorth, (PMF)&FGAtmosphere::GetGustNED,
(PMFd)&FGAtmosphere::SetGustNED);
PropertyManager->Tie("atmosphere/gust-east-fps", this, eEast, (PMF)&FGAtmosphere::GetGustNED,
(PMFd)&FGAtmosphere::SetGustNED);
PropertyManager->Tie("atmosphere/gust-down-fps", this, eDown, (PMF)&FGAtmosphere::GetGustNED,
(PMFd)&FGAtmosphere::SetGustNED);
PropertyManager->Tie("atmosphere/turb-north-fps", this, eNorth, (PMF)&FGAtmosphere::GetTurbNED,
(PMFd)&FGAtmosphere::SetTurbNED);
PropertyManager->Tie("atmosphere/turb-east-fps", this, eEast, (PMF)&FGAtmosphere::GetTurbNED,
(PMFd)&FGAtmosphere::SetTurbNED);
PropertyManager->Tie("atmosphere/turb-down-fps", this, eDown, (PMF)&FGAtmosphere::GetTurbNED,
(PMFd)&FGAtmosphere::SetTurbNED);
PropertyManager->Tie("atmosphere/p-turb-rad_sec", this,1, (PMF)&FGAtmosphere::GetTurbPQR);
PropertyManager->Tie("atmosphere/q-turb-rad_sec", this,2, (PMF)&FGAtmosphere::GetTurbPQR);
PropertyManager->Tie("atmosphere/r-turb-rad_sec", this,3, (PMF)&FGAtmosphere::GetTurbPQR);
PropertyManager->Tie("atmosphere/turb-type", this, (PMFt)&FGAtmosphere::GetTurbType, (PMFi)&FGAtmosphere::SetTurbType);
PropertyManager->Tie("atmosphere/turb-rate", this, &FGAtmosphere::GetTurbRate, &FGAtmosphere::SetTurbRate);
PropertyManager->Tie("atmosphere/turb-gain", this, &FGAtmosphere::GetTurbGain, &FGAtmosphere::SetTurbGain);
PropertyManager->Tie("atmosphere/turb-rhythmicity", this, &FGAtmosphere::GetRhythmicity,
&FGAtmosphere::SetRhythmicity);
PropertyManager->Tie("atmosphere/turbulence/milspec/windspeed_at_20ft_AGL-fps",
this, &FGAtmosphere::GetWindspeed20ft,
&FGAtmosphere::SetWindspeed20ft);
PropertyManager->Tie("atmosphere/turbulence/milspec/severity",
this, &FGAtmosphere::GetProbabilityOfExceedence,
&FGAtmosphere::SetProbabilityOfExceedence);
PropertyManager->Tie("atmosphere/pressure-altitude", this, &FGAtmosphere::GetPressureAltitude);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -789,8 +252,8 @@ void FGAtmosphere::Debug(int from)
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
if (from == 0) cout << "Instantiated: FGAtmosphere" << endl;
if (from == 1) cout << "Destroyed: FGAtmosphere" << endl;
if (from == 0) std::cout << "Instantiated: FGAtmosphere" << std::endl;
if (from == 1) std::cout << "Destroyed: FGAtmosphere" << std::endl;
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
@ -798,23 +261,12 @@ void FGAtmosphere::Debug(int from)
}
if (debug_lvl & 16) { // Sanity checking
}
if (debug_lvl & 128) { // Turbulence
if (first_pass && from == 2) {
first_pass = false;
cout << "vTurbulenceNED(X), vTurbulenceNED(Y), vTurbulenceNED(Z), "
<< "vTurbulenceGrad(X), vTurbulenceGrad(Y), vTurbulenceGrad(Z), "
<< "vDirection(X), vDirection(Y), vDirection(Z), "
<< "Magnitude, "
<< "vTurbPQR(P), vTurbPQR(Q), vTurbPQR(R), " << endl;
}
if (from == 2) {
cout << vTurbulenceNED << ", " << vTurbulenceGrad << ", " << vDirection << ", " << Magnitude << ", " << vTurbPQR << endl;
}
if (debug_lvl & 128) { //
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
std::cout << IdSrc << std::endl;
std::cout << IdHdr << std::endl;
}
}
}

View file

@ -2,10 +2,9 @@
Header: FGAtmosphere.h
Author: Jon Berndt
Implementation of 1959 Standard Atmosphere added by Tony Peden
Date started: 11/24/98
Date started: 6/2011
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
@ -26,10 +25,7 @@
HISTORY
--------------------------------------------------------------------------------
11/24/98 JSB Created
07/23/99 TP Added implementation of 1959 Standard Atmosphere
Moved calculation of Mach number to FGPropagate
Updated to '76 model
5/2011 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
@ -42,15 +38,14 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGModel.h"
#include "math/FGColumnVector3.h"
#include "math/FGTable.h"
#include <vector>
#include "models/FGModel.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ATMOSPHERE "$Id: FGAtmosphere.h,v 1.26 2011/05/20 03:18:36 jberndt Exp $"
#define ID_ATMOSPHERE "$Id: FGAtmosphere.h,v 1.29 2011/07/10 20:18:14 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -62,62 +57,24 @@ namespace JSBSim {
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models the 1976 Standard Atmosphere.
@author Tony Peden, Jon Berndt
@version $Id: FGAtmosphere.h,v 1.26 2011/05/20 03:18:36 jberndt Exp $
@see Anderson, John D. "Introduction to Flight, Third Edition", McGraw-Hill,
1989, ISBN 0-07-001641-0
/** Models an empty, abstract base atmosphere class.
Additionally, various turbulence models are available. They are specified
via the property <tt>atmosphere/turb-type</tt>. The following models are
available:
- 0: ttNone (turbulence disabled)
- 1: ttStandard
- 2: ttBerndt
- 3: ttCulp
- 4: ttMilspec (Dryden spectrum)
- 5: ttTustin (Dryden spectrum)
The Milspec and Tustin models are described in the Yeager report cited below.
They both use a Dryden spectrum model whose parameters (scale lengths and intensities)
are modelled according to MIL-F-8785C. Parameters are modelled differently
for altitudes below 1000ft and above 2000ft, for altitudes in between they
are interpolated linearly.
The two models differ in the implementation of the transfer functions
described in the milspec.
To use one of these two models, set <tt>atmosphere/turb-type</tt> to 4 resp. 5,
and specify values for <tt>atmosphere/turbulence/milspec/windspeed_at_20ft_AGL-fps<tt>
and <tt>atmosphere/turbulence/milspec/severity<tt> (the latter corresponds to
the probability of exceedence curves from Fig.&nbsp;7 of the milspec, allowable
range is 0 (disabled) to 7). <tt>atmosphere/psiw-rad</tt> is respected as well;
note that you have to specify a positive wind magnitude to prevent psiw from
being reset to zero.
Reference values (cf. figures 7 and 9 from the milspec):
<table>
<tr><td><b>Intensity</b></td>
<td><b><tt>windspeed_at_20ft_AGL-fps</tt></b></td>
<td><b><tt>severity</tt></b></td></tr>
<tr><td>light</td>
<td>25 (15 knots)</td>
<td>3</td></tr>
<tr><td>moderate</td>
<td>50 (30 knots)</td>
<td>4</td></tr>
<tr><td>severe</td>
<td>75 (45 knots)</td>
<td>6</td></tr>
</table>
@see Yeager, Jessie C.: "Implementation and Testing of Turbulence Models for
the F18-HARV" (<a
href="http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19980028448_1998081596.pdf">
pdf</a>), NASA CR-1998-206937, 1998
@see MIL-F-8785C: Military Specification: Flying Qualities of Piloted Aircraft
<h2> Properties </h2>
@property atmosphere/T-R The current modeled temperature in degrees Rankine.
@property atmosphere/rho-slugs_ft3
@property atmosphere/P-psf
@property atmosphere/a-fps
@property atmosphere/T-sl-R
@property atmosphere/rho-sl-slugs_ft3
@property atmosphere/P-sl-psf
@property atmosphere/a-sl-fps
@property atmosphere/theta
@property atmosphere/sigma
@property atmosphere/delta
@property atmosphere/a-ratio
@author Jon Berndt
@version $Id: FGAtmosphere.h,v 1.29 2011/07/10 20:18:14 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -127,11 +84,19 @@ CLASS DECLARATION
class FGAtmosphere : public FGModel {
public:
/// Enums for specifying temperature units.
enum eTemperature {eNoTempUnit=0, eFahrenheit, eCelsius, eRankine, eKelvin};
/// Enums for specifying pressure units.
enum ePressure {eNoPressUnit=0, ePSF, eMillibars, ePascals, eInchesHg};
/// Constructor
FGAtmosphere(FGFDMExec*);
/// Destructor
~FGAtmosphere();
/** Runs the Atmosphere model; called by the Executive
virtual ~FGAtmosphere();
/** Runs the atmosphere forces model; called by the Executive.
Can pass in a value indicating if the executive is directing the simulation to Hold.
@param Holding if true, the executive has been directed to hold the sim from
advancing time. Some models may ignore this flag, such as the Input
@ -139,210 +104,137 @@ public:
"Resume" command to be given.
@return false if no error */
bool Run(bool Holding);
bool InitModel(void);
enum tType {ttNone, ttStandard, ttCulp, ttMilspec, ttTustin} turbType;
/// Returns the temperature in degrees Rankine.
virtual double GetTemperature(void) const {return *temperature;}
/** Returns the density in slugs/ft^3.
<i>This function may <b>only</b> be used if Run() is called first.</i> */
virtual double GetDensity(void) const {return *density;}
// *************************************************************************
/// @name Temperature access functions.
/// There are several ways to get the temperature, and several modeled temperature
/// values that can be retrieved.
// @{
/// Returns the actual, modeled temperature at the current altitude in degrees Rankine.
/// @return Modeled temperature in degrees Rankine.
virtual double GetTemperature() const {return Temperature;}
/// Returns the actual modeled temperature in degrees Rankine at a specified altitude.
/// @param altitude The altitude above sea level (ASL) in feet.
/// @return Modeled temperature in degrees Rankine at the specified altitude.
virtual double GetTemperature(double altitude) const = 0;
/// Returns the actual, modeled sea level temperature in degrees Rankine.
/// @return The modeled temperature in degrees Rankine at sea level.
virtual double GetTemperatureSL() const { return GetTemperature(0.0); }
/// Returns the ratio of the at-current-altitude temperature as modeled
/// over the sea level value.
virtual double GetTemperatureRatio() const { return GetTemperature()*rSLtemperature; }
/// Returns the ratio of the temperature as modeled at the supplied altitude
/// over the sea level value.
virtual double GetTemperatureRatio(double h) const { return GetTemperature(h)*rSLtemperature; }
/// Sets the Sea Level temperature.
/// @param t the temperature value in the unit provided.
/// @param unit the unit of the temperature.
virtual void SetTemperatureSL(double t, eTemperature unit=eFahrenheit);
/// Sets the temperature at the supplied altitude.
/// @param t The temperature value in the unit provided.
/// @param h The altitude in feet above sea level.
/// @param unit The unit of the temperature.
virtual void SetTemperature(double t, double h, eTemperature unit=eFahrenheit) = 0;
//@}
// *************************************************************************
/// @name Pressure access functions.
//@{
/// Returns the pressure in psf.
virtual double GetPressure(void) const {return *pressure;}
/// Returns the standard pressure at a specified altitude
virtual double GetPressure(double altitude);
/// Returns the standard temperature at a specified altitude
virtual double GetTemperature(double altitude);
/// Returns the standard density at a specified altitude
virtual double GetDensity(double altitude);
/// Returns the speed of sound in ft/sec.
virtual double GetSoundSpeed(void) const {return soundspeed;}
/// Returns the absolute viscosity.
virtual double GetAbsoluteViscosity(void) const {return intViscosity;}
/// Returns the kinematic viscosity.
virtual double GetKinematicViscosity(void) const {return intKinematicViscosity;}
virtual double GetPressure(void) const {return Pressure;}
/// Returns the pressure at a specified altitude in psf.
virtual double GetPressure(double altitude) const = 0;
/// Returns the sea level temperature in degrees Rankine.
virtual double GetTemperatureSL(void) const { return SLtemperature; }
/// Returns the sea level density in slugs/ft^3
virtual double GetDensitySL(void) const { return SLdensity; }
/// Returns the sea level pressure in psf.
virtual double GetPressureSL(void) const { return SLpressure; }
/// Returns the ratio of at-altitude pressure over the sea level value.
virtual double GetPressureRatio(void) const { return Pressure*rSLpressure; }
/** Sets the sea level pressure for modeling.
@param pressure The pressure in the units specified (PSF by default).
@param unit the unit of measure that the specified pressure is
supplied in.*/
virtual void SetPressureSL(double pressure, ePressure unit=ePSF);
//@}
// *************************************************************************
/// @name Density access functions.
//@{
/** Returns the density in slugs/ft^3.
This function may only be used if Run() is called first. */
virtual double GetDensity(void) const {return Density;}
/** Returns the density in slugs/ft^3 at a given altitude in ft. */
virtual double GetDensity(double altitude) const;
/// Returns the sea level density in slugs/ft^3
virtual double GetDensitySL(void) const { return SLdensity; }
/// Returns the ratio of at-altitude density over the sea level value.
virtual double GetDensityRatio(void) const { return Density*rSLdensity; }
//@}
// *************************************************************************
/// @name Speed of sound access functions.
//@{
/// Returns the speed of sound in ft/sec.
virtual double GetSoundSpeed(void) const {return Soundspeed;}
/// Returns the sea level speed of sound in ft/sec.
virtual double GetSoundSpeedSL(void) const { return SLsoundspeed; }
/// Returns the ratio of at-altitude temperature over the sea level value.
virtual double GetTemperatureRatio(void) const { return (*temperature)*rSLtemperature; }
/// Returns the ratio of at-altitude density over the sea level value.
virtual double GetDensityRatio(void) const { return (*density)*rSLdensity; }
/// Returns the ratio of at-altitude pressure over the sea level value.
virtual double GetPressureRatio(void) const { return (*pressure)*rSLpressure; }
/// Returns the ratio of at-altitude sound speed over the sea level value.
virtual double GetSoundSpeedRatio(void) const { return soundspeed*rSLsoundspeed; }
virtual double GetSoundSpeedRatio(void) const { return Soundspeed*rSLsoundspeed; }
//@}
/// Tells the simulator to use an externally calculated atmosphere model.
virtual void UseExternal(void);
/// Tells the simulator to use the internal atmosphere model.
virtual void UseInternal(void); //this is the default
/// Gets the boolean that tells if the external atmosphere model is being used.
virtual bool External(void) { return useExternal; }
// *************************************************************************
/// @name Viscosity access functions.
//@{
/// Returns the absolute viscosity.
virtual double GetAbsoluteViscosity(void) const {return Viscosity;}
/// Provides the external atmosphere model with an interface to set the temperature.
virtual void SetExTemperature(double t) { exTemperature=t; }
/// Provides the external atmosphere model with an interface to set the density.
virtual void SetExDensity(double d) { exDensity=d; }
/// Provides the external atmosphere model with an interface to set the pressure.
virtual void SetExPressure(double p) { exPressure=p; }
/// Returns the kinematic viscosity.
virtual double GetKinematicViscosity(void) const {return KinematicViscosity;}
//@}
/// Sets the temperature deviation at sea-level in degrees Fahrenheit
virtual void SetSLTempDev(double d) { T_dev_sl = d; }
/// Gets the temperature deviation at sea-level in degrees Fahrenheit
virtual double GetSLTempDev(void) const { return T_dev_sl; }
/// Sets the current delta-T in degrees Fahrenheit
virtual void SetDeltaT(double d) { delta_T = d; }
/// Gets the current delta-T in degrees Fahrenheit
virtual double GetDeltaT(void) const { return delta_T; }
/// Gets the at-altitude temperature deviation in degrees Fahrenheit
virtual double GetTempDev(void) const { return T_dev; }
/// Gets the density altitude in feet
virtual double GetDensityAltitude(void) const { return density_altitude; }
virtual double GetDensityAltitude() const {return DensityAltitude;}
// TOTAL WIND access functions (wind + gust + turbulence)
virtual double GetPressureAltitude() const {return PressureAltitude;}
/// Retrieves the total wind components in NED frame.
virtual const FGColumnVector3& GetTotalWindNED(void) const { return vTotalWindNED; }
/// Retrieves a total wind component in NED frame.
virtual double GetTotalWindNED(int idx) const {return vTotalWindNED(idx);}
// WIND access functions
/// Sets the wind components in NED frame.
virtual void SetWindNED(double wN, double wE, double wD) { vWindNED(1)=wN; vWindNED(2)=wE; vWindNED(3)=wD;}
/// Sets a wind component in NED frame.
virtual void SetWindNED(int idx, double wind) { vWindNED(idx)=wind;}
/// Retrieves the wind components in NED frame.
virtual FGColumnVector3& GetWindNED(void) { return vWindNED; }
/// Retrieves a wind component in NED frame.
virtual double GetWindNED(int idx) const {return vWindNED(idx);}
/** Retrieves the direction that the wind is coming from.
The direction is defined as north=0 and increases counterclockwise.
The wind heading is returned in radians.*/
virtual double GetWindPsi(void) const { return psiw; }
/** Sets the direction that the wind is coming from.
The direction is defined as north=0 and increases counterclockwise to 2*pi (radians). The
vertical component of wind is assumed to be zero - and is forcibly set to zero. This function
sets the vWindNED vector components based on the supplied direction. The magnitude of
the wind set in the vector is preserved (assuming the vertical component is non-zero).
@param dir wind direction in the horizontal plane, in radians.*/
virtual void SetWindPsi(double dir);
virtual void SetWindspeed(double speed);
virtual double GetWindspeed(void) const;
// GUST access functions
/// Sets a gust component in NED frame.
virtual void SetGustNED(int idx, double gust) { vGustNED(idx)=gust;}
/// Sets a turbulence component in NED frame.
virtual void SetTurbNED(int idx, double turb) { vTurbulenceNED(idx)=turb;}
/// Sets the gust components in NED frame.
virtual void SetGustNED(double gN, double gE, double gD) { vGustNED(eNorth)=gN; vGustNED(eEast)=gE; vGustNED(eDown)=gD;}
/// Retrieves a gust component in NED frame.
virtual double GetGustNED(int idx) const {return vGustNED(idx);}
/// Retrieves a turbulence component in NED frame.
virtual double GetTurbNED(int idx) const {return vTurbulenceNED(idx);}
/// Retrieves the gust components in NED frame.
virtual FGColumnVector3& GetGustNED(void) {return vGustNED;}
/** Turbulence models available: ttNone, ttStandard, ttBerndt, ttCulp, ttMilspec, ttTustin */
virtual void SetTurbType(tType tt) {turbType = tt;}
virtual tType GetTurbType() const {return turbType;}
virtual void SetTurbGain(double tg) {TurbGain = tg;}
virtual double GetTurbGain() const {return TurbGain;}
virtual void SetTurbRate(double tr) {TurbRate = tr;}
virtual double GetTurbRate() const {return TurbRate;}
virtual void SetRhythmicity(double r) {Rhythmicity=r;}
virtual double GetRhythmicity() const {return Rhythmicity;}
virtual double GetTurbPQR(int idx) const {return vTurbPQR(idx);}
virtual double GetTurbMagnitude(void) const {return Magnitude;}
virtual const FGColumnVector3& GetTurbDirection(void) const {return vDirection;}
virtual const FGColumnVector3& GetTurbPQR(void) const {return vTurbPQR;}
virtual void SetWindspeed20ft(double ws) { windspeed_at_20ft = ws;}
virtual double GetWindspeed20ft() const { return windspeed_at_20ft;}
/// allowable range: 0-7, 3=light, 4=moderate, 6=severe turbulence
virtual void SetProbabilityOfExceedence( int idx) {probability_of_exceedence_index = idx;}
virtual int GetProbabilityOfExceedence() const { return probability_of_exceedence_index;}
struct Inputs {
double altitudeASL;
} in;
protected:
double rho;
double SLtemperature, SLdensity, SLpressure, SLsoundspeed; // Sea level conditions
double Temperature, Density, Pressure, Soundspeed; // Current actual conditions at altitude
double rSLtemperature, rSLdensity, rSLpressure, rSLsoundspeed; // Reciprocal of sea level conditions
struct atmType {double Temperature; double Pressure; double Density;};
int lastIndex;
double h;
double htab[8];
double StdSLtemperature,StdSLdensity,StdSLpressure,StdSLsoundspeed;
double rSLtemperature,rSLdensity,rSLpressure,rSLsoundspeed; //reciprocals
double SLtemperature,SLdensity,SLpressure,SLsoundspeed;
double *temperature, *density, *pressure;
double soundspeed;
bool useExternal;
double exTemperature,exDensity,exPressure;
double intTemperature, intDensity, intPressure;
double SutherlandConstant, Beta, intViscosity, intKinematicViscosity;
double T_dev_sl, T_dev, delta_T, density_altitude;
atmType atmosphere;
bool StandardTempOnly;
bool first_pass;
double PressureAltitude;
double DensityAltitude;
double MagnitudedAccelDt, MagnitudeAccel, Magnitude;
double TurbGain;
double TurbRate;
double Rhythmicity;
double wind_from_clockwise;
double spike, target_time, strength;
FGColumnVector3 vDirectiondAccelDt;
FGColumnVector3 vDirectionAccel;
FGColumnVector3 vDirection;
FGColumnVector3 vTurbulenceGrad;
FGColumnVector3 vBodyTurbGrad;
FGColumnVector3 vTurbPQR;
const double SutherlandConstant, Beta;
double Viscosity, KinematicViscosity;
// Dryden turbulence model
double windspeed_at_20ft; ///< in ft/s
int probability_of_exceedence_index; ///< this is bound as the severity property
FGTable *POE_Table; ///< probability of exceedence table
double psiw;
FGColumnVector3 vTotalWindNED;
FGColumnVector3 vWindNED;
FGColumnVector3 vGustNED;
FGColumnVector3 vTurbulenceNED;
/// Calculate the atmosphere for the given altitude, including effects of temperature deviation.
/// Calculate the atmosphere for the given altitude.
void Calculate(double altitude);
/// Calculate atmospheric properties other than the basic T, P and rho.
void CalculateDerived(void);
/// Get T, P and rho for a standard atmosphere at the given altitude.
void GetStdAtmosphere(double altitude);
void Turbulence(void);
// Converts to Rankine from one of several unit systems.
virtual double ConvertToRankine(double t, eTemperature unit) const;
// Converts to PSF (pounds per square foot) from one of several unit systems.
virtual double ConvertToPSF(double t, ePressure unit=ePSF) const;
virtual void bind(void);
void Debug(int from);
};

View file

@ -40,26 +40,17 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGAuxiliary.h"
#include "FGAerodynamics.h"
#include "FGPropagate.h"
#include "FGAtmosphere.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGInertial.h"
#include "FGExternalReactions.h"
#include "FGBuoyantForces.h"
#include "FGGroundReactions.h"
#include "FGPropulsion.h"
#include "FGMassBalance.h"
#include "input_output/FGPropertyManager.h"
#include <iostream>
#include "FGAuxiliary.h"
#include "FGFDMExec.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGAuxiliary.cpp,v 1.49 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGAuxiliary.cpp,v 1.53 2011/08/17 23:56:01 jberndt Exp $";
static const char *IdHdr = ID_AUXILIARY;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -70,9 +61,8 @@ CLASS IMPLEMENTATION
FGAuxiliary::FGAuxiliary(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGAuxiliary";
pt = p = psl = 1.0;
rho = rhosl = 1.0;
tat = sat = 1.0;
pt = 1.0;
tat = 1.0;
tatc = RankineToCelsius(tat);
vcas = veas = 0.0;
@ -91,13 +81,11 @@ FGAuxiliary::FGAuxiliary(FGFDMExec* fdmex) : FGModel(fdmex)
vPilotAccel.InitMatrix();
vPilotAccelN.InitMatrix();
vToEyePt.InitMatrix();
vAeroUVW.InitMatrix();
vAeroPQR.InitMatrix();
vMachUVW.InitMatrix();
vEuler.InitMatrix();
vEulerRates.InitMatrix();
vAircraftAccel.InitMatrix();
bind();
@ -108,11 +96,8 @@ FGAuxiliary::FGAuxiliary(FGFDMExec* fdmex) : FGModel(fdmex)
bool FGAuxiliary::InitModel(void)
{
pt = p = FDMExec->GetAtmosphere()->GetPressure();
rho = FDMExec->GetAtmosphere()->GetDensity();
rhosl = FDMExec->GetAtmosphere()->GetDensitySL();
psl = FDMExec->GetAtmosphere()->GetPressureSL();
tat = sat = FDMExec->GetAtmosphere()->GetTemperature();
pt = in.Pressure;
tat = in.Temperature;
tatc = RankineToCelsius(tat);
vcas = veas = 0.0;
@ -131,13 +116,11 @@ bool FGAuxiliary::InitModel(void)
vPilotAccel.InitMatrix();
vPilotAccelN.InitMatrix();
vToEyePt.InitMatrix();
vAeroUVW.InitMatrix();
vAeroPQR.InitMatrix();
vMachUVW.InitMatrix();
vEuler.InitMatrix();
vEulerRates.InitMatrix();
vAircraftAccel.InitMatrix();
return true;
}
@ -160,139 +143,118 @@ bool FGAuxiliary::Run(bool Holding)
RunPreFunctions();
const double density = FDMExec->GetAtmosphere()->GetDensity();
const double soundspeed = FDMExec->GetAtmosphere()->GetSoundSpeed();
const double DistanceAGL = FDMExec->GetPropagate()->GetDistanceAGL();
const double wingspan = FDMExec->GetAircraft()->GetWingSpan();
const FGMatrix33& Tl2b = FDMExec->GetPropagate()->GetTl2b();
const FGMatrix33& Tb2l = FDMExec->GetPropagate()->GetTb2l();
const FGColumnVector3& vPQR = FDMExec->GetPropagate()->GetPQR();
const FGColumnVector3& vUVW = FDMExec->GetPropagate()->GetUVW();
const FGColumnVector3& vUVWdot = FDMExec->GetPropagate()->GetUVWdot();
const FGColumnVector3& vVel = FDMExec->GetPropagate()->GetVel();
p = FDMExec->GetAtmosphere()->GetPressure();
rhosl = FDMExec->GetAtmosphere()->GetDensitySL();
psl = FDMExec->GetAtmosphere()->GetPressureSL();
sat = FDMExec->GetAtmosphere()->GetTemperature();
// Rotation
double cTht = FDMExec->GetPropagate()->GetCosEuler(eTht);
double sTht = FDMExec->GetPropagate()->GetSinEuler(eTht);
double cPhi = FDMExec->GetPropagate()->GetCosEuler(ePhi);
double sPhi = FDMExec->GetPropagate()->GetSinEuler(ePhi);
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)*sTht;
vEulerRates(eTht) = in.vPQR(eQ)*in.CosPhi - in.vPQR(eR)*in.SinPhi;
if (in.CosTht != 0.0) {
vEulerRates(ePsi) = (in.vPQR(eQ)*in.SinPhi + in.vPQR(eR)*in.CosPhi)/in.CosTht;
vEulerRates(ePhi) = in.vPQR(eP) + vEulerRates(ePsi)*in.SinTht;
}
// Combine the wind speed with aircraft speed to obtain wind relative speed
FGColumnVector3 wind = Tl2b*FDMExec->GetAtmosphere()->GetTotalWindNED();
vAeroPQR = vPQR - FDMExec->GetAtmosphere()->GetTurbPQR();
vAeroUVW = vUVW - wind;
vAeroPQR = in.vPQR - in.TurbPQR;
vAeroUVW = in.vUVW - in.Tl2b * in.TotalWindNED;
Vt = vAeroUVW.Magnitude();
double Vt2 = Vt*Vt;
alpha = beta = adot = bdot = 0;
double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW));
double AeroU2 = vAeroUVW(eU)*vAeroUVW(eU);
double AeroV2 = vAeroUVW(eV)*vAeroUVW(eV);
double AeroW2 = vAeroUVW(eW)*vAeroUVW(eW);
double mUW = AeroU2 + AeroW2;
double Vt2 = Vt*Vt;
if ( Vt > 1.0 ) {
if (vAeroUVW(eW) != 0.0)
alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
alpha = AeroU2 > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0;
if (vAeroUVW(eV) != 0.0)
beta = mUW > 0.0 ? atan2(vAeroUVW(eV), sqrt(mUW)) : 0.0;
beta = mUW > 0.0 ? atan2(vAeroUVW(eV), sqrt(mUW)) : 0.0;
double signU=1;
if (vAeroUVW(eU) < 0.0) signU=-1;
if ( mUW >= 1.0 ) {
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)))/(Vt2*sqrt(mUW));
adot = (vAeroUVW(eU)*in.vUVWdot(eW) - vAeroUVW(eW)*in.vUVWdot(eU))/mUW;
bdot = (signU*mUW*in.vUVWdot(eV)
- vAeroUVW(eV)*(vAeroUVW(eU)*in.vUVWdot(eU) + vAeroUVW(eW)*in.vUVWdot(eW)))/(Vt2*sqrt(mUW));
}
}
Re = Vt * FDMExec->GetAircraft()->Getcbar() / FDMExec->GetAtmosphere()->GetKinematicViscosity();
Re = Vt * in.Wingchord / in.KinematicViscosity;
qbar = 0.5*density*Vt2;
qbarUW = 0.5*density*(mUW);
qbarUV = 0.5*density*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eV)*vAeroUVW(eV));
Mach = Vt / soundspeed;
MachU = vMachUVW(eU) = vAeroUVW(eU) / soundspeed;
vMachUVW(eV) = vAeroUVW(eV) / soundspeed;
vMachUVW(eW) = vAeroUVW(eW) / soundspeed;
double densityD2 = 0.5*in.Density;
qbar = densityD2 * Vt2;
qbarUW = densityD2 * (mUW);
qbarUV = densityD2 * (AeroU2 + AeroV2);
Mach = Vt / in.SoundSpeed;
MachU = vMachUVW(eU) = vAeroUVW(eU) / in.SoundSpeed;
vMachUVW(eV) = vAeroUVW(eV) / in.SoundSpeed;
vMachUVW(eW) = vAeroUVW(eW) / in.SoundSpeed;
double MachU2 = MachU * MachU;
// Position
Vground = sqrt( vVel(eNorth)*vVel(eNorth) + vVel(eEast)*vVel(eEast) );
Vground = sqrt( in.vVel(eNorth)*in.vVel(eNorth) + in.vVel(eEast)*in.vVel(eEast) );
psigt = atan2(vVel(eEast), vVel(eNorth));
psigt = atan2(in.vVel(eEast), in.vVel(eNorth));
if (psigt < 0.0) psigt += 2*M_PI;
gamma = atan2(-vVel(eDown), Vground);
gamma = atan2(-in.vVel(eDown), Vground);
tat = sat*(1 + 0.2*Mach*Mach); // Total Temperature, isentropic flow
tat = in.Temperature*(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);
pt = in.Pressure*pow((1 + 0.2*MachU2),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;
B = 5.76 * MachU2 / (5.6*MachU2 - 0.8);
D = (2.8 * MachU2 - 0.4) * 0.4167;
pt = in.Pressure*pow(B,3.5)*D;
}
A = pow(((pt-p)/psl+1),0.28571);
A = pow(((pt-in.Pressure)/in.PressureSL + 1),0.28571);
if (MachU > 0.0) {
vcas = sqrt(7*psl/rhosl*(A-1));
veas = sqrt(2*qbar/rhosl);
vcas = sqrt(7 * in.PressureSL / in.DensitySL * (A-1));
veas = sqrt(2 * qbar / in.DensitySL);
vtrue = 1116.43559 * MachU * sqrt(in.Temperature / 518.67);
} else {
vcas = veas = 0.0;
vcas = veas = vtrue = 0.0;
}
const double SLgravity = FDMExec->GetInertial()->SLgravity();
vPilotAccel.InitMatrix();
vNcg = in.vBodyAccel/in.SLGravity;
if ( Vt > 1.0 ) {
vAircraftAccel = FDMExec->GetAircraft()->GetBodyAccel();
// Nz is Acceleration in "g's", along normal axis (-Z body axis)
Nz = -vAircraftAccel(eZ)/SLgravity;
vToEyePt = FDMExec->GetMassBalance()->StructuralToBody(FDMExec->GetAircraft()->GetXYZep());
vPilotAccel = vAircraftAccel + FDMExec->GetPropagate()->GetPQRdot() * vToEyePt;
vPilotAccel += vPQR * (vPQR * vToEyePt);
// Nz is Acceleration in "g's", along normal axis (-Z body axis)
Nz = -vNcg(eZ);
vPilotAccel = in.vBodyAccel + in.vPQRdot * in.ToEyePt;
vPilotAccel += in.vPQR * (in.vPQR * in.ToEyePt);
} else {
// The line below handles low velocity (and on-ground) cases, basically
// representing the opposite of the force that the landing gear would
// exert on the ground (which is just the total weight). This eliminates
// any jitter that could be introduced by the landing gear. Theoretically,
// this branch could be eliminated, with a penalty of having a short
// transient at startup (lasting only a fraction of a second).
vPilotAccel = Tl2b * FGColumnVector3( 0.0, 0.0, -SLgravity );
Nz = -vPilotAccel(eZ)/SLgravity;
// The line below handles low velocity (and on-ground) cases, basically
// representing the opposite of the force that the landing gear would
// exert on the ground (which is just the total weight). This eliminates
// any jitter that could be introduced by the landing gear. Theoretically,
// this branch could be eliminated, with a penalty of having a short
// transient at startup (lasting only a fraction of a second).
vPilotAccel = in.Tl2b * FGColumnVector3( 0.0, 0.0, -in.SLGravity );
Nz = -vPilotAccel(eZ) / in.SLGravity;
}
vPilotAccelN = vPilotAccel/SLgravity;
vNwcg = mTb2w * vNcg;
vNwcg(eZ) = 1.0 - vNwcg(eZ);
vPilotAccelN = vPilotAccel / in.SLGravity;
// VRP computation
const FGLocation& vLocation = FDMExec->GetPropagate()->GetLocation();
const FGColumnVector3& vrpStructural = FDMExec->GetAircraft()->GetXYZvrp();
const FGColumnVector3 vrpBody = FDMExec->GetMassBalance()->StructuralToBody( vrpStructural );
const FGColumnVector3 vrpLocal = Tb2l * vrpBody;
vLocationVRP = vLocation.LocalToLocation( vrpLocal );
vLocationVRP = in.vLocation.LocalToLocation( in.Tb2l * in.VRPBody );
// Recompute some derived values now that we know the dependent parameters values ...
hoverbcg = DistanceAGL / wingspan;
hoverbcg = in.DistanceAGL / in.Wingspan;
FGColumnVector3 vMac = Tb2l*FDMExec->GetMassBalance()->StructuralToBody(FDMExec->GetAircraft()->GetXYZrp());
hoverbmac = (DistanceAGL + vMac(3)) / wingspan;
// when all model are executed,
// please calculate the distance from the initial point
FGColumnVector3 vMac = in.Tb2l * in.RPBody;
hoverbmac = (in.DistanceAGL + vMac(3)) / in.Wingspan;
// When all models are executed calculate the distance from the initial point.
CalculateRelativePosition();
RunPostFunctions();
@ -300,6 +262,68 @@ bool FGAuxiliary::Run(bool Holding)
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// From Stevens and Lewis, "Aircraft Control and Simulation", 3rd Ed., the
// transformation from body to wind axes is defined (where "a" is alpha and "B"
// is beta):
//
// cos(a)*cos(B) sin(B) sin(a)*cos(B)
// -cos(a)*sin(B) cos(B) -sin(a)*sin(B)
// -sin(a) 0 cos(a)
//
// The transform from wind to body axes is then,
//
// cos(a)*cos(B) -cos(a)*sin(B) -sin(a)
// sin(B) cos(B) 0
// sin(a)*cos(B) -sin(a)*sin(B) cos(a)
FGMatrix33& FGAuxiliary::GetTw2b(void)
{
double ca, cb, sa, sb;
ca = cos(alpha);
sa = sin(alpha);
cb = cos(beta);
sb = sin(beta);
mTw2b(1,1) = ca*cb;
mTw2b(1,2) = -ca*sb;
mTw2b(1,3) = -sa;
mTw2b(2,1) = sb;
mTw2b(2,2) = cb;
mTw2b(2,3) = 0.0;
mTw2b(3,1) = sa*cb;
mTw2b(3,2) = -sa*sb;
mTw2b(3,3) = ca;
return mTw2b;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGMatrix33& FGAuxiliary::GetTb2w(void)
{
double ca, cb, sa, sb;
ca = cos(alpha);
sa = sin(alpha);
cb = cos(beta);
sb = sin(beta);
mTb2w(1,1) = ca*cb;
mTb2w(1,2) = sb;
mTb2w(1,3) = sa*cb;
mTb2w(2,1) = -ca*sb;
mTb2w(2,2) = cb;
mTb2w(2,3) = -sa*sb;
mTb2w(3,1) = -sa;
mTb2w(3,2) = 0.0;
mTb2w(3,3) = ca;
return mTb2w;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// A positive headwind is blowing with you, a negative headwind is blowing against you.
@ -308,12 +332,7 @@ bool FGAuxiliary::Run(bool Holding)
double FGAuxiliary::GetHeadWind(void) const
{
double psiw,vw;
psiw = FDMExec->GetAtmosphere()->GetWindPsi();
vw = FDMExec->GetAtmosphere()->GetTotalWindNED().Magnitude();
return vw*cos(psiw - FDMExec->GetPropagate()->GetEuler(ePsi));
return in.Vwind * cos(in.WindPsi - in.Psi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -324,23 +343,38 @@ double FGAuxiliary::GetHeadWind(void) const
double FGAuxiliary::GetCrossWind(void) const
{
double psiw,vw;
psiw = FDMExec->GetAtmosphere()->GetWindPsi();
vw = FDMExec->GetAtmosphere()->GetTotalWindNED().Magnitude();
return vw*sin(psiw - FDMExec->GetPropagate()->GetEuler(ePsi));
return in.Vwind * sin(in.WindPsi - in.Psi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::GethVRP(void) const
{
return vLocationVRP.GetRadius() - FDMExec->GetPropagate()->GetSeaLevelRadius();
return vLocationVRP.GetRadius() - in.ReferenceRadius;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::GetNlf(void) const
{
if (in.Mass != 0)
return (-in.vFw(3))/(in.Mass*slugtolb);
else
return 0.;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAuxiliary::CalculateRelativePosition(void) //ToDo: This belongs elsewhere - perhaps in FGPropagate or Exec
{
const double earth_radius_mt = in.ReferenceRadius*fttom;
lat_relative_position=(in.Latitude - FDMExec->GetIC()->GetLatitudeDegIC() *degtorad)*earth_radius_mt;
lon_relative_position=(in.Longitude - FDMExec->GetIC()->GetLongitudeDegIC()*degtorad)*earth_radius_mt;
relative_position = sqrt(lat_relative_position*lat_relative_position + lon_relative_position*lon_relative_position);
};
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAuxiliary::bind(void)
{
typedef double (FGAuxiliary::*PMF)(int) const;
@ -352,6 +386,8 @@ void FGAuxiliary::bind(void)
PropertyManager->Tie("velocities/vc-kts", this, &FGAuxiliary::GetVcalibratedKTS);
PropertyManager->Tie("velocities/ve-fps", this, &FGAuxiliary::GetVequivalentFPS);
PropertyManager->Tie("velocities/ve-kts", this, &FGAuxiliary::GetVequivalentKTS);
PropertyManager->Tie("velocities/vtrue-fps", this, &FGAuxiliary::GetVtrueFPS);
PropertyManager->Tie("velocities/vtrue-kts", this, &FGAuxiliary::GetVtrueKTS);
PropertyManager->Tie("velocities/machU", this, &FGAuxiliary::GetMachU);
PropertyManager->Tie("velocities/p-aero-rad_sec", this, eX, (PMF)&FGAuxiliary::GetAeroPQR);
PropertyManager->Tie("velocities/q-aero-rad_sec", this, eY, (PMF)&FGAuxiliary::GetAeroPQR);
@ -362,8 +398,8 @@ void FGAuxiliary::bind(void)
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", this, &FGAuxiliary::GetMach, &FGAuxiliary::SetMach, true);
PropertyManager->Tie("velocities/vt-fps", this, &FGAuxiliary::GetVt);
PropertyManager->Tie("velocities/mach", this, &FGAuxiliary::GetMach);
PropertyManager->Tie("velocities/vg-fps", this, &FGAuxiliary::GetVground);
PropertyManager->Tie("accelerations/a-pilot-x-ft_sec2", this, eX, (PMF)&FGAuxiliary::GetPilotAccel);
PropertyManager->Tie("accelerations/a-pilot-y-ft_sec2", this, eY, (PMF)&FGAuxiliary::GetPilotAccel);
@ -372,25 +408,26 @@ void FGAuxiliary::bind(void)
PropertyManager->Tie("accelerations/n-pilot-y-norm", this, eY, (PMF)&FGAuxiliary::GetNpilot);
PropertyManager->Tie("accelerations/n-pilot-z-norm", this, eZ, (PMF)&FGAuxiliary::GetNpilot);
PropertyManager->Tie("accelerations/Nz", this, &FGAuxiliary::GetNz);
PropertyManager->Tie("forces/load-factor", this, &FGAuxiliary::GetNlf);
/* 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/alpha-rad", this, (PF)&FGAuxiliary::Getalpha);
PropertyManager->Tie("aero/beta-rad", this, (PF)&FGAuxiliary::Getbeta);
PropertyManager->Tie("aero/mag-beta-rad", this, (PF)&FGAuxiliary::GetMagBeta);
PropertyManager->Tie("aero/alpha-deg", this, inDegrees, (PMF)&FGAuxiliary::Getalpha);
PropertyManager->Tie("aero/beta-deg", this, inDegrees, (PMF)&FGAuxiliary::Getbeta);
PropertyManager->Tie("aero/mag-beta-deg", this, inDegrees, (PMF)&FGAuxiliary::GetMagBeta);
PropertyManager->Tie("aero/Re", this, &FGAuxiliary::GetReynoldsNumber);
PropertyManager->Tie("aero/qbar-psf", this, &FGAuxiliary::Getqbar, &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/qbar-psf", this, &FGAuxiliary::Getqbar);
PropertyManager->Tie("aero/qbarUW-psf", this, &FGAuxiliary::GetqbarUW);
PropertyManager->Tie("aero/qbarUV-psf", this, &FGAuxiliary::GetqbarUV);
PropertyManager->Tie("aero/alphadot-rad_sec", this, (PF)&FGAuxiliary::Getadot);
PropertyManager->Tie("aero/betadot-rad_sec", this, (PF)&FGAuxiliary::Getbdot);
PropertyManager->Tie("aero/alphadot-deg_sec", this, inDegrees, (PMF)&FGAuxiliary::Getadot);
PropertyManager->Tie("aero/betadot-deg_sec", this, inDegrees, (PMF)&FGAuxiliary::Getbdot);
PropertyManager->Tie("aero/h_b-cg-ft", this, &FGAuxiliary::GetHOverBCG);
PropertyManager->Tie("aero/h_b-mac-ft", this, &FGAuxiliary::GetHOverBMAC);
PropertyManager->Tie("flight-path/gamma-rad", this, &FGAuxiliary::GetGamma, &FGAuxiliary::SetGamma);
PropertyManager->Tie("flight-path/gamma-rad", this, &FGAuxiliary::GetGamma);
PropertyManager->Tie("flight-path/psi-gt-rad", this, &FGAuxiliary::GetGroundTrack);
PropertyManager->Tie("position/distance-from-start-lon-mt", this, &FGAuxiliary::GetLongitudeRelativePosition);
@ -403,16 +440,6 @@ void FGAuxiliary::bind(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGAuxiliary::CalculateRelativePosition(void)
{
const double earth_radius_mt = FDMExec->GetInertial()->GetRefRadius()*fttom;
lat_relative_position=(FDMExec->GetPropagate()->GetLatitude() - FDMExec->GetIC()->GetLatitudeDegIC() *degtorad)*earth_radius_mt;
lon_relative_position=(FDMExec->GetPropagate()->GetLongitude() - FDMExec->GetIC()->GetLongitudeDegIC()*degtorad)*earth_radius_mt;
relative_position = sqrt(lat_relative_position*lat_relative_position + lon_relative_position*lon_relative_position);
};
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::BadUnits(void) const
{
cerr << "Bad units" << endl; return 0.0;

View file

@ -41,13 +41,14 @@ INCLUDES
#include "FGModel.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "math/FGLocation.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_AUXILIARY "$Id: FGAuxiliary.h,v 1.20 2011/05/20 03:18:36 jberndt Exp $"
#define ID_AUXILIARY "$Id: FGAuxiliary.h,v 1.23 2011/08/17 23:56:01 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -96,10 +97,9 @@ CLASS DOCUMENTATION
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: FGAuxiliary.h,v 1.20 2011/05/20 03:18:36 jberndt Exp $
@version $Id: FGAuxiliary.h,v 1.23 2011/08/17 23:56:01 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -137,6 +137,10 @@ public:
double GetVequivalentFPS(void) const { return veas; }
/** Returns equivalent airspeed in knots. */
double GetVequivalentKTS(void) const { return veas*fpstokts; }
/** Returns the true airspeed in feet per second. */
double GetVtrueFPS() const { return vtrue; }
/** Returns the true airspeed in knots. */
double GetVtrueKTS() const { return vtrue * fpstokts; }
/** Returns the total pressure.
Total pressure is freestream total pressure for
@ -147,9 +151,9 @@ public:
/** Returns the total temperature.
The total temperature ("tat", isentropic flow) is calculated:
@code
tat = sat*(1 + 0.2*Mach*Mach)
tat = in.Temperature*(1 + 0.2*Mach*Mach)
@endcode
(where "sat" is standard temperature) */
(where "in.Temperature" is standard temperature calculated by the atmosphere model) */
double GetTotalTemperature(void) const { return tat; }
double GetTAT_C(void) const { return tatc; }
@ -161,6 +165,9 @@ public:
const FGColumnVector3& GetPilotAccel (void) const { return vPilotAccel; }
const FGColumnVector3& GetNpilot (void) const { return vPilotAccelN; }
const FGColumnVector3& GetNcg (void) const { return vNcg; }
double GetNcg (int idx) const { return vNcg(idx); }
double GetNlf (void) const;
const FGColumnVector3& GetAeroPQR (void) const { return vAeroPQR; }
const FGColumnVector3& GetEulerRates (void) const { return vEulerRates; }
const FGColumnVector3& GetAeroUVW (void) const { return vAeroUVW; }
@ -185,6 +192,16 @@ public:
double GetMagBeta (int unit) const { if (unit == inDegrees) return fabs(beta)*radtodeg;
else return BadUnits(); }
/** Calculates and returns the wind-to-body axis transformation matrix.
@return a reference to the wind-to-body transformation matrix.
*/
FGMatrix33& GetTw2b(void);
/** Calculates and returns the body-to-wind axis transformation matrix.
@return a reference to the wind-to-body transformation matrix.
*/
FGMatrix33& GetTb2w(void);
double Getqbar (void) const { return qbar; }
double GetqbarUW (void) const { return qbarUW; }
double GetqbarUV (void) const { return qbarUV; }
@ -208,6 +225,8 @@ public:
/** The vertical acceleration in g's of the aircraft center of gravity. */
double GetNz (void) const { return Nz; }
FGColumnVector3& GetNwcg(void) { return vNwcg; }
double GetHOverBCG(void) const { return hoverbcg; }
double GetHOverBMAC(void) const { return hoverbmac; }
@ -217,23 +236,6 @@ public:
double GetHeadWind(void) const;
double GetCrossWind(void) const;
// 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, used by FGMSIS atmosphere
void SetDayOfYear (int doy) { day_of_year = doy; }
@ -248,19 +250,65 @@ public:
void SetAeroPQR(FGColumnVector3 tt) { vAeroPQR = tt; }
struct Inputs {
double Pressure;
double Density;
double DensitySL;
double PressureSL;
double Temperature;
double SoundSpeed;
double KinematicViscosity;
double DistanceAGL;
double Wingspan;
double Wingchord;
double SLGravity;
double Mass;
FGMatrix33 Tl2b;
FGMatrix33 Tb2l;
FGMatrix33 Tb2w;
FGColumnVector3 vPQR;
FGColumnVector3 vPQRdot;
FGColumnVector3 vUVW;
FGColumnVector3 vUVWdot;
FGColumnVector3 vVel;
FGColumnVector3 vBodyAccel;
FGColumnVector3 ToEyePt;
FGColumnVector3 RPBody;
FGColumnVector3 VRPBody;
FGColumnVector3 vFw;
FGLocation vLocation;
double Latitude;
double Longitude;
double InitialLatitude;
double InitialLongitude;
double ReferenceRadius;
double CosTht;
double SinTht;
double CosPhi;
double SinPhi;
double Psi;
FGColumnVector3 TotalWindNED;
FGColumnVector3 TurbPQR;
double WindPsi;
double Vwind;
} in;
private:
double vcas, veas;
double rhosl, rho, p, psl, pt, tat, sat, tatc; // Don't add a getter for pt!
double vcas, veas, vtrue;
double pt, tat, tatc; // Don't add a getter for pt!
FGMatrix33 mTw2b;
FGMatrix33 mTb2w;
FGColumnVector3 vPilotAccel;
FGColumnVector3 vPilotAccelN;
FGColumnVector3 vToEyePt;
FGColumnVector3 vNcg;
FGColumnVector3 vNwcg;
FGColumnVector3 vAeroPQR;
FGColumnVector3 vAeroUVW;
FGColumnVector3 vEuler;
FGColumnVector3 vEulerRates;
FGColumnVector3 vMachUVW;
FGColumnVector3 vAircraftAccel;
FGLocation vLocationVRP;
double Vt, Vground, Mach, MachU;

View file

@ -45,7 +45,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGBuoyantForces.cpp,v 1.19 2011/07/01 21:22:25 andgi Exp $";
static const char *IdSrc = "$Id: FGBuoyantForces.cpp,v 1.20 2011/08/06 13:47:59 jberndt Exp $";
static const char *IdHdr = ID_BUOYANTFORCES;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -131,7 +131,7 @@ bool FGBuoyantForces::Load(Element *element)
gas_cell_element = document->FindElement("gas_cell");
while (gas_cell_element) {
NoneDefined = false;
Cells.push_back(new FGGasCell(FDMExec, gas_cell_element, Cells.size()));
Cells.push_back(new FGGasCell(FDMExec, gas_cell_element, Cells.size(), in));
gas_cell_element = document->FindNextElement("gas_cell");
}

View file

@ -32,8 +32,8 @@ HISTORY
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGBuoyanTFORCES_H
#define FGBuoyanTFORCES_H
#ifndef FGBUOYANTFORCES_H
#define FGBUOYANTFORCES_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
@ -51,7 +51,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_BUOYANTFORCES "$Id: FGBuoyantForces.h,v 1.13 2011/07/01 21:22:25 andgi Exp $"
#define ID_BUOYANTFORCES "$Id: FGBuoyantForces.h,v 1.15 2011/08/14 20:15:56 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -96,7 +96,7 @@ CLASS DOCUMENTATION
See FGGasCell for the full configuration file format for gas cells.
@author Anders Gidenstam, Jon S. Berndt
@version $Id: FGBuoyantForces.h,v 1.13 2011/07/01 21:22:25 andgi Exp $
@version $Id: FGBuoyantForces.h,v 1.15 2011/08/14 20:15:56 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -172,6 +172,8 @@ public:
parameters */
string GetBuoyancyValues(string delimeter);
FGGasCell::Inputs in;
private:
vector <FGGasCell*> Cells;
// Buoyant forces and moments. Excluding the gas weight.

View file

@ -53,7 +53,7 @@ DEFINITIONS
GLOBAL DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
static const char *IdSrc = "$Id: FGExternalReactions.cpp,v 1.10 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGExternalReactions.cpp,v 1.12 2011/07/20 12:16:34 jberndt Exp $";
static const char *IdHdr = ID_EXTERNALREACTIONS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -63,6 +63,7 @@ CLASS IMPLEMENTATION
FGExternalReactions::FGExternalReactions(FGFDMExec* fdmex) : FGModel(fdmex)
{
NoneDefined = true;
Debug(0);
}
@ -70,6 +71,14 @@ FGExternalReactions::FGExternalReactions(FGFDMExec* fdmex) : FGModel(fdmex)
bool FGExternalReactions::Load(Element* el)
{
// check if a file attribute was specified
string fname = el->GetAttributeValue("file");
if (!fname.empty()) {
string file = FDMExec->GetFullAircraftPath() + "/" + fname;
el = LoadXMLDocument(file);
if (el == 0L) return false;
}
FGModel::Load(el); // Call the base class Load() function to load interface properties.
Debug(2);
@ -87,6 +96,8 @@ bool FGExternalReactions::Load(Element* el)
PostLoad(el, PropertyManager);
if (!NoneDefined) bind();
return true;
}
@ -130,6 +141,20 @@ bool FGExternalReactions::Run(bool Holding)
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGExternalReactions::bind(void)
{
typedef double (FGExternalReactions::*PMF)(int) const;
PropertyManager->Tie("moments/l-external-lbsft", this, eL, (PMF)&FGExternalReactions::GetMoments);
PropertyManager->Tie("moments/m-external-lbsft", this, eM, (PMF)&FGExternalReactions::GetMoments);
PropertyManager->Tie("moments/n-external-lbsft", this, eN, (PMF)&FGExternalReactions::GetMoments);
PropertyManager->Tie("forces/fbx-external-lbs", this, eX, (PMF)&FGExternalReactions::GetForces);
PropertyManager->Tie("forces/fby-external-lbs", this, eY, (PMF)&FGExternalReactions::GetForces);
PropertyManager->Tie("forces/fbz-external-lbs", this, eZ, (PMF)&FGExternalReactions::GetForces);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The bitmasked value choices are as follows:
// unset: In this case (the default) JSBSim would only print

View file

@ -41,12 +41,14 @@ INCLUDES
#include <vector>
#include "FGModel.h"
#include "FGExternalForce.h"
#include "input_output/FGXMLFileRead.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_EXTERNALREACTIONS "$Id: FGExternalReactions.h,v 1.11 2011/05/20 03:18:36 jberndt Exp $"
#define ID_EXTERNALREACTIONS "$Id: FGExternalReactions.h,v 1.13 2011/07/20 12:16:34 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -115,7 +117,7 @@ CLASS DOCUMENTATION
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGExternalReactions : public FGModel
class FGExternalReactions : public FGModel, public FGXMLFileRead
{
public:
/** Constructor.
@ -152,11 +154,13 @@ public:
@return the total force in pounds.
*/
FGColumnVector3 GetForces(void) const {return vTotalForces;}
double GetForces(int idx) const {return vTotalForces(idx);}
/** Retrieves the total moment resulting from the forces defined in the external reactions.
@return the total moment in foot-pounds.
*/
FGColumnVector3 GetMoments(void) const {return vTotalMoments;}
double GetMoments(int idx) const {return vTotalMoments(idx);}
private:
@ -167,6 +171,7 @@ private:
bool NoneDefined;
void bind(void);
void Debug(int from);
};
}

View file

@ -37,13 +37,14 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <fstream>
#include <sstream>
#include <iomanip>
#include "FGFCS.h"
#include "FGFDMExec.h"
#include "FGGroundReactions.h"
#include "input_output/FGPropertyManager.h"
#include <fstream>
#include <sstream>
#include <iomanip>
#include "models/flight_control/FGFilter.h"
#include "models/flight_control/FGDeadBand.h"
@ -63,7 +64,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGFCS.cpp,v 1.74 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGFCS.cpp,v 1.76 2011/08/14 20:15:56 jberndt Exp $";
static const char *IdHdr = ID_FCS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -78,7 +79,7 @@ FGFCS::FGFCS(FGFDMExec* fdmex) : FGModel(fdmex)
DaCmd = DeCmd = DrCmd = DsCmd = DfCmd = DsbCmd = DspCmd = 0;
PTrimCmd = YTrimCmd = RTrimCmd = 0.0;
GearCmd = GearPos = 1; // default to gear down
LeftBrake = RightBrake = CenterBrake = 0.0;
BrakePos.resize(FGLGear::bgNumBrakeGroups);
TailhookPos = WingFoldPos = 0.0;
bind();
@ -673,17 +674,7 @@ bool FGFCS::Load(Element* el, SystemType systype)
double FGFCS::GetBrake(FGLGear::BrakeGroup bg)
{
switch (bg) {
case FGLGear::bgLeft:
return LeftBrake;
case FGLGear::bgRight:
return RightBrake;
case FGLGear::bgCenter:
return CenterBrake;
default:
cerr << "GetBrake asked to return a bogus brake value" << endl;
}
return 0.0;
return BrakePos[bg];
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -838,9 +829,10 @@ void FGFCS::AddThrottle(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFCS::AddGear(void)
void FGFCS::AddGear(unsigned int NumGear)
{
SteerPosDeg.push_back(0.0);
SteerPosDeg.clear();
for (unsigned int i=0; i<NumGear; i++) SteerPosDeg.push_back(0.0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -51,7 +51,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_FCS "$Id: FGFCS.h,v 1.36 2011/05/20 03:18:36 jberndt Exp $"
#define ID_FCS "$Id: FGFCS.h,v 1.39 2011/08/14 20:15:56 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -168,7 +168,7 @@ CLASS DOCUMENTATION
@property gear/tailhook-pos-norm
@author Jon S. Berndt
@version $Revision: 1.36 $
@version $Revision: 1.39 $
@see FGActuator
@see FGDeadBand
@see FGFCSFunction
@ -243,11 +243,15 @@ public:
@return throttle command in range from 0 - 1.0 for the given engine */
double GetThrottleCmd(int engine) const;
vector <double> GetThrottleCmd() const {return ThrottleCmd;}
/** Gets the mixture command.
@param engine engine ID number
@return mixture command in range from 0 - 1.0 for the given engine */
double GetMixtureCmd(int engine) const { return MixtureCmd[engine]; }
vector <double> GetMixtureCmd() const {return MixtureCmd;}
/** Gets the prop pitch command.
@param engine engine ID number
@return pitch command in range from 0.0 - 1.0 for the given engine */
@ -318,15 +322,21 @@ public:
@return throttle position for the given engine in range from 0 - 1.0 */
double GetThrottlePos(int engine) const;
vector <double> GetThrottlePos() const {return ThrottlePos;}
/** Gets the mixture position.
@param engine engine ID number
@return mixture position for the given engine in range from 0 - 1.0 */
double GetMixturePos(int engine) const { return MixturePos[engine]; }
vector <double> GetMixturePos() const {return MixturePos;}
/** Gets the steering position.
@return steering position in degrees */
double GetSteerPosDeg(int gear) const { return SteerPosDeg[gear]; }
vector <double> GetSteerPosDeg() const {return SteerPosDeg;}
/** Gets the gear position (0 up, 1 down), defaults to down
@return gear position (0 up, 1 down) */
double GetGearPos(void) const { return GearPos; }
@ -344,10 +354,14 @@ public:
@return prop pitch position for the given engine in range from 0 - 1.0 */
double GetPropAdvance(int engine) const { return PropAdvance[engine]; }
vector <double> GetPropAdvance() const { return PropAdvance; }
/** Gets the prop feather position.
@param engine engine ID number
@return prop fether for the given engine (on / off)*/
bool GetPropFeather(int engine) const { return PropFeather[engine]; }
vector <bool> GetPropFeather() const { return PropFeather; }
//@}
/** Retrieves all component names for inclusion in output stream
@ -499,32 +513,34 @@ public:
//@{
/** Sets the left brake group
@param cmd brake setting in percent (0.0 - 1.0) */
void SetLBrake(double cmd) {LeftBrake = cmd;}
void SetLBrake(double cmd) {BrakePos[FGLGear::bgLeft] = cmd;}
/** Sets the right brake group
@param cmd brake setting in percent (0.0 - 1.0) */
void SetRBrake(double cmd) {RightBrake = cmd;}
void SetRBrake(double cmd) {BrakePos[FGLGear::bgRight] = cmd;}
/** Sets the center brake group
@param cmd brake setting in percent (0.0 - 1.0) */
void SetCBrake(double cmd) {CenterBrake = cmd;}
void SetCBrake(double cmd) {BrakePos[FGLGear::bgCenter] = cmd;}
/** Gets the brake for a specified group.
@param bg which brakegroup to retrieve the command for
@return the brake setting for the supplied brake group argument */
double GetBrake(FGLGear::BrakeGroup bg);
vector <double> GetBrakePos() const {return BrakePos;}
/** Gets the left brake.
@return the left brake setting. */
double GetLBrake(void) const {return LeftBrake;}
double GetLBrake(void) const {return BrakePos[FGLGear::bgLeft];}
/** Gets the right brake.
@return the right brake setting. */
double GetRBrake(void) const {return RightBrake;}
double GetRBrake(void) const {return BrakePos[FGLGear::bgRight];}
/** Gets the center brake.
@return the center brake setting. */
double GetCBrake(void) const {return CenterBrake;}
double GetCBrake(void) const {return BrakePos[FGLGear::bgCenter];}
//@}
enum SystemType { stFCS, stSystem, stAutoPilot };
@ -540,13 +556,17 @@ public:
std::string FindSystemFullPathname(const std::string& system_filename);
void AddThrottle(void);
void AddGear(void);
void AddGear(unsigned int NumGear);
double GetDt(void);
FGPropertyManager* GetPropertyManager(void) { return PropertyManager; }
bool GetTrimStatus(void) const { return FDMExec->GetTrimStatus(); }
struct Inputs {
unsigned int NumGear;
} in;
private:
double DaCmd, DeCmd, DrCmd, DsCmd, DfCmd, DsbCmd, DspCmd;
double DePos[NForms], DaLPos[NForms], DaRPos[NForms], DrPos[NForms];
@ -562,6 +582,7 @@ private:
std::vector <bool> PropFeather;
std::vector <double> SteerPosDeg;
double LeftBrake, RightBrake, CenterBrake; // Brake settings
vector <double> BrakePos; // left, center, right - defined by FGLGear:: enum
double GearCmd,GearPos;
double TailhookPos, WingFoldPos;

View file

@ -36,9 +36,6 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGFDMExec.h"
#include "models/FGAuxiliary.h"
#include "models/FGAtmosphere.h"
#include "models/FGInertial.h"
#include "models/FGMassBalance.h"
#include "FGGasCell.h"
#include "input_output/FGXMLElement.h"
@ -53,7 +50,7 @@ using std::max;
namespace JSBSim {
static const char *IdSrc = "$Id: FGGasCell.cpp,v 1.14 2011/07/01 21:22:25 andgi Exp $";
static const char *IdSrc = "$Id: FGGasCell.cpp,v 1.15 2011/08/06 13:47:59 jberndt Exp $";
static const char *IdHdr = ID_GASCELL;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -65,15 +62,13 @@ const double FGGasCell::M_air = 0.0019186; // [slug/mol]
const double FGGasCell::M_hydrogen = 0.00013841; // [slug/mol]
const double FGGasCell::M_helium = 0.00027409; // [slug/mol]
FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, int num) : FGForce(exec)
FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, int num, const struct Inputs& input)
: FGForce(exec), in(input)
{
string token;
Element* element;
Auxiliary = exec->GetAuxiliary();
Atmosphere = exec->GetAtmosphere();
PropertyManager = exec->GetPropertyManager();
Inertial = exec->GetInertial();
MassBalance = exec->GetMassBalance();
gasCellJ = FGMatrix33();
@ -175,10 +170,10 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, int num) : FGForce(exec)
SetLocation(vXYZ);
if (Temperature == 0.0) {
Temperature = Atmosphere->GetTemperature();
Temperature = in.Temperature;
}
if (Pressure == 0.0) {
Pressure = Atmosphere->GetPressure();
Pressure = in.Pressure;
}
if (Volume != 0.0) {
// Calculate initial gas content.
@ -239,7 +234,7 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, int num) : FGForce(exec)
Ballonet.push_back(new FGBallonet(exec,
ballonet_element,
Ballonet.size(),
this));
this, in));
ballonet_element = el->FindNextElement("ballonet");
}
}
@ -265,10 +260,10 @@ FGGasCell::~FGGasCell()
void FGGasCell::Calculate(double dt)
{
const double AirTemperature = Atmosphere->GetTemperature(); // [Rankine]
const double AirPressure = Atmosphere->GetPressure(); // [lbs/ft²]
const double AirDensity = Atmosphere->GetDensity(); // [slug/ft³]
const double g = Inertial->gravity(); // [lbs/slug]
const double AirTemperature = in.Temperature; // [Rankine]
const double AirPressure = in.Pressure; // [lbs/ft²]
const double AirDensity = in.Density; // [slug/ft³]
const double g = in.gravity; // [lbs/slug]
const double OldTemperature = Temperature;
const double OldPressure = Pressure;
@ -509,15 +504,13 @@ const double FGBallonet::R = 3.4071; // [lbs ft/(mol Rankine)]
const double FGBallonet::M_air = 0.0019186; // [slug/mol]
const double FGBallonet::Cv_air = 5.0/2.0; // [??]
FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, int num, FGGasCell* parent)
FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, int num, FGGasCell* parent, const struct FGGasCell::Inputs& input)
: in(input)
{
string token;
Element* element;
Auxiliary = exec->GetAuxiliary();
Atmosphere = exec->GetAtmosphere();
PropertyManager = exec->GetPropertyManager();
Inertial = exec->GetInertial();
MassBalance = exec->GetMassBalance();
ballonetJ = FGMatrix33();
@ -696,8 +689,8 @@ FGBallonet::~FGBallonet()
void FGBallonet::Calculate(double dt)
{
const double ParentPressure = Parent->GetPressure(); // [lbs/ft²]
const double AirPressure = Atmosphere->GetPressure(); // [lbs/ft²]
const double ParentPressure = Parent->GetPressure(); // [lbs/ft²]
const double AirPressure = Pressure; // [lbs/ft²]
const double OldTemperature = Temperature;
const double OldPressure = Pressure;

View file

@ -32,8 +32,8 @@ This class simulates a generic gas cell for static buoyancy.
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGGasCell_H
#define FGGasCell_H
#ifndef FGGASCELL_H
#define FGGASCELL_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
@ -50,7 +50,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_GASCELL "$Id: FGGasCell.h,v 1.11 2011/07/01 21:22:25 andgi Exp $"
#define ID_GASCELL "$Id: FGGasCell.h,v 1.12 2011/08/06 13:47:59 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -172,11 +172,18 @@ CLASS DECLARATION
class FGGasCell : public FGForce
{
public:
struct Inputs {
double Pressure;
double Temperature;
double Density;
double gravity;
};
/** Constructor
@param exec Executive a pointer to the parent executive object
@param el Pointer to configuration file XML node
@param num Gas cell index number. */
FGGasCell(FGFDMExec* exec, Element* el, int num);
FGGasCell(FGFDMExec* exec, Element* el, int num, const struct Inputs& input);
~FGGasCell();
/** Runs the gas cell model; called by BuoyantForces
@ -221,6 +228,8 @@ public:
@return gas pressure in lbs / ft<sup>2</sup>. */
double GetPressure(void) const {return Pressure;}
const struct Inputs& in;
private:
enum GasType {ttUNKNOWN, ttHYDROGEN, ttHELIUM, ttAIR};
@ -252,10 +261,7 @@ private:
FGMatrix33 gasCellJ; // [slug foot^2]
FGColumnVector3 gasCellM; // [lbs in]
FGAuxiliary* Auxiliary;
FGAtmosphere* Atmosphere;
FGPropertyManager* PropertyManager;
FGInertial* Inertial;
FGMassBalance* MassBalance;
void Debug(int from);
@ -302,7 +308,7 @@ private:
class FGBallonet : public FGJSBBase
{
public:
FGBallonet(FGFDMExec* exec, Element* el, int num, FGGasCell* parent);
FGBallonet(FGFDMExec* exec, Element* el, int num, FGGasCell* parent, const struct FGGasCell::Inputs& input);
~FGBallonet();
/** Runs the ballonet model; called by FGGasCell
@ -333,6 +339,8 @@ public:
@return heat flow in lbs ft / sec. */
double GetHeatFlow(void) const {return dU;} // [lbs ft / sec]
const struct FGGasCell::Inputs& in;
private:
int CellNum;
// Structural constants
@ -356,10 +364,7 @@ private:
double ValveOpen; // 0 <= ValveOpen <= 1 (or higher).
FGMatrix33 ballonetJ; // [slug foot^2]
FGAuxiliary* Auxiliary;
FGAtmosphere* Atmosphere;
FGPropertyManager* PropertyManager;
FGInertial* Inertial;
FGMassBalance* MassBalance;
void Debug(int from);

View file

@ -39,56 +39,16 @@ INCLUDES
#include <iomanip>
#include "FGGroundReactions.h"
#include "FGFCS.h"
#include "FGLGear.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGGroundReactions.cpp,v 1.32 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGGroundReactions.cpp,v 1.36 2011/08/21 15:13:22 bcoconni Exp $";
static const char *IdHdr = ID_GROUNDREACTIONS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION for MultiplierIterator (See below for FGGroundReactions)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
MultiplierIterator::MultiplierIterator(FGGroundReactions* GndReactions)
: GroundReactions(GndReactions),
multiplier(NULL),
gearNum(0),
entry(0)
{
for (int i=0; i < GroundReactions->GetNumGearUnits(); i++) {
FGLGear* gear = GroundReactions->GetGearUnit(i);
if (!gear->GetWOW()) continue;
gearNum = i;
multiplier = gear->GetMultiplierEntry(0);
break;
}
}
MultiplierIterator& MultiplierIterator::operator++()
{
for (int i=gearNum; i < GroundReactions->GetNumGearUnits(); i++) {
FGLGear* gear = GroundReactions->GetGearUnit(i);
if (!gear->GetWOW()) continue;
multiplier = gear->GetMultiplierEntry(++entry);
if (multiplier) {
gearNum = i;
break;
}
else
entry = -1;
}
return *this;
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -131,6 +91,8 @@ bool FGGroundReactions::Run(bool Holding)
vForces.InitMatrix();
vMoments.InitMatrix();
multipliers.clear();
// Sum forces and moments for all gear, here.
// Some optimizations may be made here - or rather in the gear code itself.
// The gear ::Run() method is called several times - once for each gear.
@ -160,20 +122,6 @@ bool FGGroundReactions::GetWOW(void) const
return result;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This function must be called after friction forces are resolved in order to
// include them in the ground reactions total force and moment.
void FGGroundReactions::UpdateForcesAndMoments(void)
{
vForces.InitMatrix();
vMoments.InitMatrix();
for (unsigned int i=0; i<lGear.size(); i++) {
vForces += lGear[i]->UpdateForces();
vMoments += lGear[i]->GetMoments();
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGGroundReactions::Load(Element* el)
@ -182,15 +130,18 @@ bool FGGroundReactions::Load(Element* el)
Debug(2);
unsigned int numContacts = el->GetNumElements("contact");
lGear.resize(numContacts);
Element* contact_element = el->FindElement("contact");
while (contact_element) {
lGear.push_back(new FGLGear(contact_element, FDMExec, num++));
FDMExec->GetFCS()->AddGear(); // make the FCS aware of the landing gear
for (unsigned int idx=0; idx<numContacts; idx++) {
lGear[idx] = new FGLGear(contact_element, FDMExec, num++, in);
contact_element = el->FindNextElement("contact");
}
FGModel::Load(el); // Perform base class Load
in.vWhlBodyVec.resize(lGear.size());
for (unsigned int i=0; i<lGear.size();i++) lGear[i]->bind();
PostLoad(el, PropertyManager);
@ -275,12 +226,6 @@ void FGGroundReactions::bind(void)
typedef double (FGGroundReactions::*PMF)(int) const;
PropertyManager->Tie("gear/num-units", this, &FGGroundReactions::GetNumGearUnits);
PropertyManager->Tie("gear/wow", this, &FGGroundReactions::GetWOW);
PropertyManager->Tie("moments/l-gear-lbsft", this, eL, (PMF)&FGGroundReactions::GetMoments);
PropertyManager->Tie("moments/m-gear-lbsft", this, eM, (PMF)&FGGroundReactions::GetMoments);
PropertyManager->Tie("moments/n-gear-lbsft", this, eN, (PMF)&FGGroundReactions::GetMoments);
PropertyManager->Tie("forces/fbx-gear-lbs", this, eX, (PMF)&FGGroundReactions::GetForces);
PropertyManager->Tie("forces/fby-gear-lbs", this, eY, (PMF)&FGGroundReactions::GetForces);
PropertyManager->Tie("forces/fbz-gear-lbs", this, eZ, (PMF)&FGGroundReactions::GetForces);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -45,7 +45,7 @@ INCLUDES
#include "math/FGColumnVector3.h"
#include "input_output/FGXMLElement.h"
#define ID_GROUNDREACTIONS "$Id: FGGroundReactions.h,v 1.20 2011/05/20 03:18:36 jberndt Exp $"
#define ID_GROUNDREACTIONS "$Id: FGGroundReactions.h,v 1.24 2011/08/21 15:13:22 bcoconni Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -78,19 +78,6 @@ CLASS DOCUMENTATION
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class MultiplierIterator
{
public:
MultiplierIterator(FGGroundReactions* GndReactions);
MultiplierIterator& operator++();
FGPropagate::LagrangeMultiplier* operator*() { return multiplier; }
private:
FGGroundReactions* GroundReactions;
FGPropagate::LagrangeMultiplier* multiplier;
int gearNum;
int entry;
};
class FGGroundReactions : public FGModel
{
public:
@ -114,7 +101,6 @@ public:
string GetGroundReactionStrings(string delimeter) const;
string GetGroundReactionValues(string delimeter) const;
bool GetWOW(void) const;
void UpdateForcesAndMoments(void);
int GetNumGearUnits(void) const { return (int)lGear.size(); }
@ -123,10 +109,16 @@ public:
@return a pointer to the FGLGear instance of the gear unit requested */
FGLGear* GetGearUnit(int gear) const { return lGear[gear]; }
void RegisterLagrangeMultiplier(LagrangeMultiplier* lmult) { multipliers.push_back(lmult); }
vector <LagrangeMultiplier*>* GetMultipliersList(void) { return &multipliers; }
FGLGear::Inputs in;
private:
vector <FGLGear*> lGear;
FGColumnVector3 vForces;
FGColumnVector3 vMoments;
vector <LagrangeMultiplier*> multipliers;
void bind(void);
void Debug(int from);

View file

@ -37,15 +37,13 @@ INCLUDES
#include "FGInertial.h"
#include "FGFDMExec.h"
#include "FGPropagate.h"
#include "FGMassBalance.h"
#include <iostream>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGInertial.cpp,v 1.21 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGInertial.cpp,v 1.24 2011/08/04 12:46:32 jberndt Exp $";
static const char *IdHdr = ID_INERTIAL;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -65,7 +63,6 @@ FGInertial::FGInertial(FGFDMExec* fgex) : FGModel(fgex)
J2 = 1.0826266836E-03; // WGS84 value for J2
a = 20925646.3255; // WGS84 semimajor axis length in feet
b = 20855486.5951; // WGS84 semiminor axis length in feet
earthPosAngle = 0.0;
// Lunar defaults
/*
@ -76,9 +73,9 @@ FGInertial::FGInertial(FGFDMExec* fgex) : FGModel(fgex)
J2 = 2.033542482111609E-4; // value for J2
a = 5702559.05; // semimajor axis length in feet
b = 5695439.63; // semiminor axis length in feet
earthPosAngle = 0.0;
*/
vOmegaPlanet = FGColumnVector3( 0.0, 0.0, RotationRate );
gAccelReference = GM/(RadiusReference*RadiusReference);
gAccel = GM/(RadiusReference*RadiusReference);
@ -98,8 +95,6 @@ FGInertial::~FGInertial(void)
bool FGInertial::InitModel(void)
{
earthPosAngle = 0.0;
return true;
}
@ -114,9 +109,7 @@ bool FGInertial::Run(bool Holding)
RunPreFunctions();
// Gravitation accel
double r = FDMExec->GetPropagate()->GetRadius();
gAccel = GetGAccel(r);
earthPosAngle += FDMExec->GetDeltaT()*RotationRate;
gAccel = GetGAccel(in.Radius);
RunPostFunctions();
@ -143,8 +136,7 @@ FGColumnVector3 FGInertial::GetGravityJ2(FGColumnVector3 position) const
// Gravitation accel
double r = position.Magnitude();
double lat = FDMExec->GetPropagate()->GetLatitude();
double sinLat = sin(lat);
double sinLat = sin(in.Latitude);
double adivr = a/r;
double preCommon = 1.5*J2*adivr*adivr;
@ -163,7 +155,7 @@ FGColumnVector3 FGInertial::GetGravityJ2(FGColumnVector3 position) const
void FGInertial::bind(void)
{
PropertyManager->Tie("position/epa-rad", this, &FGInertial::GetEarthPositionAngle);
PropertyManager->Tie("inertial/sea-level-radius_ft", this, &FGInertial::GetRefRadius);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -47,7 +47,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_INERTIAL "$Id: FGInertial.h,v 1.16 2011/05/20 03:18:36 jberndt Exp $"
#define ID_INERTIAL "$Id: FGInertial.h,v 1.19 2011/08/04 12:46:32 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -86,17 +86,20 @@ public:
double SLgravity(void) const {return gAccelReference;}
double gravity(void) const {return gAccel;}
double omega(void) const {return RotationRate;}
double GetEarthPositionAngle(void) const { return earthPosAngle; }
double GetEarthPositionAngleDeg(void) const { return earthPosAngle*radtodeg;}
FGColumnVector3 GetOmegaPlanet() const {return vOmegaPlanet;}
double GetGAccel(double r) const;
FGColumnVector3 GetGravityJ2(FGColumnVector3 position) const;
double GetRefRadius(void) const {return RadiusReference;}
double GetSemimajor(void) const {return a;}
double GetSemiminor(void) const {return b;}
void SetEarthPositionAngle(double epa) {earthPosAngle = epa;}
struct Inputs {
double Radius;
double Latitude;
} in;
private:
FGColumnVector3 vOmegaPlanet;
double gAccel;
double gAccelReference;
double RadiusReference;

View file

@ -40,16 +40,14 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGLGear.h"
#include "FGGroundReactions.h"
#include "FGFCS.h"
#include "FGAuxiliary.h"
#include "FGAtmosphere.h"
#include "FGMassBalance.h"
#include "math/FGTable.h"
#include <cstdlib>
#include <cstring>
#include "FGLGear.h"
#include "input_output/FGPropertyManager.h"
#include "models/FGGroundReactions.h"
#include "math/FGTable.h"
using namespace std;
namespace JSBSim {
@ -62,7 +60,7 @@ DEFINITIONS
GLOBAL DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
static const char *IdSrc = "$Id: FGLGear.cpp,v 1.80 2011/01/24 13:01:56 jberndt Exp $";
static const char *IdSrc = "$Id: FGLGear.cpp,v 1.88 2011/08/30 21:05:56 bcoconni Exp $";
static const char *IdHdr = ID_LGEAR;
// Body To Structural (body frame is rotated 180 deg about Y and lengths are given in
@ -73,12 +71,13 @@ const FGMatrix33 FGLGear::Tb2s(-1./inchtoft, 0., 0., 0., 1./inchtoft, 0., 0., 0.
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number, const struct Inputs& inputs) :
FGForce(fdmex),
GearNumber(number),
SteerAngle(0.0),
Castered(false),
StaticFriction(false)
StaticFriction(false),
in(inputs)
{
Element *force_table=0;
Element *dampCoeff=0;
@ -102,6 +101,15 @@ FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
eContactType = ctSTRUCTURE;
}
// Default values for structural contact points
if (eContactType == ctSTRUCTURE) {
kSpring = in.EmptyWeight;
bDamp = kSpring;
bDampRebound = kSpring * 10;
staticFCoeff = 1.0;
dynamicFCoeff = 1.0;
}
if (el->FindElement("spring_coeff"))
kSpring = el->FindElementValueAsNumberConvertTo("spring_coeff", "LBS/FT");
if (el->FindElement("damping_coeff")) {
@ -138,12 +146,15 @@ FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
if (el->FindElement("retractable"))
isRetractable = ((unsigned int)el->FindElementValueAsNumber("retractable"))>0.0?true:false;
GroundReactions = fdmex->GetGroundReactions();
PropertyManager = fdmex->GetPropertyManager();
ForceY_Table = 0;
force_table = el->FindElement("table");
while (force_table) {
force_type = force_table->GetAttributeValue("type");
if (force_type == "CORNERING_COEFF") {
ForceY_Table = new FGTable(fdmex->GetPropertyManager(), force_table);
ForceY_Table = new FGTable(PropertyManager, force_table);
} else {
cerr << "Undefined force table for " << name << " contact point" << endl;
}
@ -212,12 +223,6 @@ FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
<< sSteerType << " is undefined." << endl;
}
Auxiliary = fdmex->GetAuxiliary();
Propagate = fdmex->GetPropagate();
FCS = fdmex->GetFCS();
MassBalance = fdmex->GetMassBalance();
GroundReactions = fdmex->GetGroundReactions();
GearUp = false;
GearDown = true;
GearPos = 1.0;
@ -236,8 +241,6 @@ FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number) :
MaximumStrutForce = MaximumStrutTravel = 0.0;
SinkRate = GroundSpeed = 0.0;
vWhlBodyVec = MassBalance->StructuralToBody(vXYZn);
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec;
vWhlVelVec.InitMatrix();
compressLength = 0.0;
@ -274,29 +277,27 @@ FGLGear::~FGLGear()
FGColumnVector3& FGLGear::GetBodyForces(void)
{
double t = fdmex->GetSimTime();
dT = fdmex->GetDeltaT()*GroundReactions->GetRate();
vFn.InitMatrix();
if (isRetractable) ComputeRetractionState();
if (GearDown) {
FGColumnVector3 angularVel;
FGColumnVector3 terrainVel, dummy;
vWhlBodyVec = MassBalance->StructuralToBody(vXYZn); // Get wheel in body frame
vLocalGear = Propagate->GetTb2l() * vWhlBodyVec; // Get local frame wheel location
vLocalGear = in.Tb2l * in.vWhlBodyVec[GearNumber]; // Get local frame wheel location
gearLoc = Propagate->GetLocation().LocalToLocation(vLocalGear);
gearLoc = in.Location.LocalToLocation(vLocalGear);
// Compute the height of the theoretical location of the wheel (if strut is
// not compressed) with respect to the ground level
double height = fdmex->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, cvel, angularVel);
vGroundNormal = Propagate->GetTec2b() * normal;
double height = fdmex->GetGroundCallback()->GetAGLevel(t, gearLoc, contact, normal, terrainVel, dummy);
vGroundNormal = in.Tec2b * normal;
// The height returned above is the AGL and is expressed in the Z direction
// of the ECEF coordinate frame. We now need to transform this height in
// actual compression of the strut (BOGEY) of in the normal direction to the
// ground (STRUCTURE)
double normalZ = (Propagate->GetTec2l()*normal)(eZ);
double normalZ = (in.Tec2l*normal)(eZ);
double LGearProj = -(mTGear.Transposed() * vGroundNormal)(eZ);
switch (eContactType) {
@ -309,7 +310,6 @@ FGColumnVector3& FGLGear::GetBodyForces(void)
}
if (compressLength > 0.00) {
WOW = true;
// The following equations use the vector to the tire contact patch
@ -325,10 +325,10 @@ FGColumnVector3& FGLGear::GetBodyForces(void)
break;
}
FGColumnVector3 vWhlContactVec = vWhlBodyVec + vWhlDisplVec;
FGColumnVector3 vWhlContactVec = in.vWhlBodyVec[GearNumber] + vWhlDisplVec;
vActingXYZn = vXYZn + Tb2s * vWhlDisplVec;
FGColumnVector3 vBodyWhlVel = Propagate->GetPQR() * vWhlContactVec;
vBodyWhlVel += Propagate->GetUVW() - Propagate->GetTec2b() * cvel;
FGColumnVector3 vBodyWhlVel = in.PQR * vWhlContactVec;
vBodyWhlVel += in.UVW - in.Tec2b * terrainVel;
vWhlVelVec = mTGear.Transposed() * vBodyWhlVel;
@ -338,9 +338,13 @@ FGColumnVector3& FGLGear::GetBodyForces(void)
vLocalWhlVel = Transform().Transposed() * vBodyWhlVel;
compressSpeed = -vLocalWhlVel(eX);
if (eContactType == ctBOGEY)
compressSpeed /= LGearProj;
if (fdmex->GetTrimStatus())
compressSpeed = 0.0; // Steady state is sought during trimming
else {
compressSpeed = -vLocalWhlVel(eX);
if (eContactType == ctBOGEY)
compressSpeed /= LGearProj;
}
ComputeVerticalStrutForce();
@ -364,7 +368,7 @@ FGColumnVector3& FGLGear::GetBodyForces(void)
StrutForce = 0.0;
// Let wheel spin down slowly
vWhlVelVec(eX) -= 13.0*dT;
vWhlVelVec(eX) -= 13.0 * in.TotalDeltaT;
if (vWhlVelVec(eX) < 0.0) vWhlVelVec(eX) = 0.0;
// Return to neutral position between 1.0 and 0.8 gear pos.
@ -463,14 +467,14 @@ void FGLGear::ComputeSteeringAngle(void)
{
switch (eSteerType) {
case stSteer:
SteerAngle = degtorad * FCS->GetSteerPosDeg(GearNumber);
SteerAngle = degtorad * in.SteerPosDeg[GearNumber];
break;
case stFixed:
SteerAngle = 0.0;
break;
case stCaster:
if (!Castered)
SteerAngle = degtorad * FCS->GetSteerPosDeg(GearNumber);
SteerAngle = degtorad * in.SteerPosDeg[GearNumber];
else {
// Check that the speed is non-null otherwise use the current angle
if (vWhlVelVec.Magnitude(eX,eY) > 0.1)
@ -488,7 +492,7 @@ void FGLGear::ComputeSteeringAngle(void)
void FGLGear::ResetReporting(void)
{
if (Propagate->GetDistanceAGL() > 200.0) {
if (in.DistanceAGL > 200.0) {
FirstContact = false;
StartedGroundRun = false;
LandingReported = false;
@ -508,16 +512,16 @@ void FGLGear::InitializeReporting(void)
if (!FirstContact) {
FirstContact = true;
SinkRate = compressSpeed;
GroundSpeed = Propagate->GetVel().Magnitude();
GroundSpeed = in.Vground;
TakeoffReported = false;
}
// If the takeoff run is starting, initialize.
if ((Propagate->GetVel().Magnitude() > 0.1) &&
(FCS->GetBrake(bgLeft) == 0) &&
(FCS->GetBrake(bgRight) == 0) &&
(FCS->GetThrottlePos(0) > 0.90) && !StartedGroundRun)
if ((in.Vground > 0.1) &&
(in.BrakePos[bgLeft] == 0) &&
(in.BrakePos[bgRight] == 0) &&
(in.TakeoffThrottle && !StartedGroundRun))
{
TakeoffDistanceTraveled = 0;
TakeoffDistanceTraveled50ft = 0;
@ -531,25 +535,25 @@ void FGLGear::InitializeReporting(void)
void FGLGear::ReportTakeoffOrLanding(void)
{
if (FirstContact)
LandingDistanceTraveled += Auxiliary->GetVground()*dT;
LandingDistanceTraveled += in.Vground * in.TotalDeltaT;
if (StartedGroundRun) {
TakeoffDistanceTraveled50ft += Auxiliary->GetVground()*dT;
if (WOW) TakeoffDistanceTraveled += Auxiliary->GetVground()*dT;
TakeoffDistanceTraveled50ft += in.Vground * in.TotalDeltaT;
if (WOW) TakeoffDistanceTraveled += in.Vground * in.TotalDeltaT;
}
if ( ReportEnable
&& Auxiliary->GetVground() <= 0.05
&& in.Vground <= 0.05
&& !LandingReported
&& GroundReactions->GetWOW())
&& in.WOW)
{
if (debug_lvl > 0) Report(erLand);
}
if ( ReportEnable
&& !TakeoffReported
&& (Propagate->GetDistanceAGL() - vLocalGear(eZ)) > 50.0
&& !GroundReactions->GetWOW())
&& (in.DistanceAGL - vLocalGear(eZ)) > 50.0
&& !in.WOW)
{
if (debug_lvl > 0) Report(erTakeoff);
}
@ -584,24 +588,24 @@ void FGLGear::ComputeBrakeForceCoefficient(void)
{
switch (eBrakeGrp) {
case bgLeft:
BrakeFCoeff = ( rollingFCoeff*(1.0 - FCS->GetBrake(bgLeft)) +
staticFCoeff*FCS->GetBrake(bgLeft) );
BrakeFCoeff = ( rollingFCoeff * (1.0 - in.BrakePos[bgLeft]) +
staticFCoeff * in.BrakePos[bgLeft] );
break;
case bgRight:
BrakeFCoeff = ( rollingFCoeff*(1.0 - FCS->GetBrake(bgRight)) +
staticFCoeff*FCS->GetBrake(bgRight) );
BrakeFCoeff = ( rollingFCoeff * (1.0 - in.BrakePos[bgRight]) +
staticFCoeff * in.BrakePos[bgRight] );
break;
case bgCenter:
BrakeFCoeff = ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
staticFCoeff*FCS->GetBrake(bgCenter) );
BrakeFCoeff = ( rollingFCoeff * (1.0 - in.BrakePos[bgCenter]) +
staticFCoeff * in.BrakePos[bgCenter] );
break;
case bgNose:
BrakeFCoeff = ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
staticFCoeff*FCS->GetBrake(bgCenter) );
BrakeFCoeff = ( rollingFCoeff * (1.0 - in.BrakePos[bgCenter]) +
staticFCoeff * in.BrakePos[bgCenter] );
break;
case bgTail:
BrakeFCoeff = ( rollingFCoeff*(1.0 - FCS->GetBrake(bgCenter)) +
staticFCoeff*FCS->GetBrake(bgCenter) );
BrakeFCoeff = ( rollingFCoeff * (1.0 - in.BrakePos[bgCenter]) +
staticFCoeff * in.BrakePos[bgCenter] );
break;
case bgNone:
BrakeFCoeff = rollingFCoeff;
@ -683,9 +687,9 @@ void FGLGear::ComputeVerticalStrutForce(void)
double FGLGear::GetGearUnitPos(void)
{
// hack to provide backward compatibility to gear/gear-pos-norm property
if( useFCSGearPos || FCS->GetGearPos() != 1.0 ) {
if( useFCSGearPos || in.FCSGearPos != 1.0 ) {
useFCSGearPos = true;
return FCS->GetGearPos();
return in.FCSGearPos;
}
return GearPos;
}
@ -711,7 +715,14 @@ void FGLGear::ComputeJacobian(const FGColumnVector3& vWhlContactVec)
LMultiplier[ftDynamic].MomentJacobian = vWhlContactVec * LMultiplier[ftDynamic].ForceJacobian;
LMultiplier[ftDynamic].Max = 0.;
LMultiplier[ftDynamic].Min = -fabs(dynamicFCoeff * vFn(eX));
// The Lagrange multiplier value obtained from the previous iteration is kept
// This is supposed to accelerate the convergence of the projected Gauss-Seidel
// algorithm. The code just below is to make sure that the initial value
// is consistent with the current friction coefficient and normal reaction.
LMultiplier[ftDynamic].value = Constrain(LMultiplier[ftDynamic].Min, LMultiplier[ftDynamic].value, LMultiplier[ftDynamic].Max);
GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftDynamic]);
}
else {
// Static friction is used for ctSTRUCTURE when the contact point is not moving.
@ -738,39 +749,24 @@ void FGLGear::ComputeJacobian(const FGColumnVector3& vWhlContactVec)
LMultiplier[ftRoll].Min = -LMultiplier[ftRoll].Max;
LMultiplier[ftSide].Min = -LMultiplier[ftSide].Max;
// The Lagrange multiplier value obtained from the previous iteration is kept
// This is supposed to accelerate the convergence of the projected Gauss-Seidel
// algorithm. The code just below is to make sure that the initial value
// is consistent with the current friction coefficient and normal reaction.
LMultiplier[ftRoll].value = Constrain(LMultiplier[ftRoll].Min, LMultiplier[ftRoll].value, LMultiplier[ftRoll].Max);
LMultiplier[ftSide].value = Constrain(LMultiplier[ftSide].Min, LMultiplier[ftSide].value, LMultiplier[ftSide].Max);
GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftRoll]);
GroundReactions->RegisterLagrangeMultiplier(&LMultiplier[ftSide]);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This function is used by the MultiplierIterator class to enumerate the
// Lagrange multipliers of a landing gear. This allows to encapsulate the storage
// of the multipliers in FGLGear without exposing it. From an outside point of
// view, each FGLGear instance has a number of Lagrange multipliers which can be
// accessed through this routine without knowing the exact constraint which they
// model.
FGPropagate::LagrangeMultiplier* FGLGear::GetMultiplierEntry(int entry)
{
switch(entry) {
case 0:
if (StaticFriction)
return &LMultiplier[ftRoll];
else
return &LMultiplier[ftDynamic];
case 1:
if (StaticFriction)
return &LMultiplier[ftSide];
default:
return NULL;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This routine is called after the Lagrange multiplier has been computed. The
// friction forces of the landing gear are then updated accordingly.
FGColumnVector3& FGLGear::UpdateForces(void)
// This routine is called after the Lagrange multiplier has been computed in
// the FGAccelerations class. The friction forces of the landing gear are then
// updated accordingly.
void FGLGear::UpdateForces(void)
{
if (StaticFriction) {
vFn(eY) = LMultiplier[ftRoll].value;
@ -778,9 +774,6 @@ FGColumnVector3& FGLGear::UpdateForces(void)
}
else
vFn += LMultiplier[ftDynamic].value * (Transform ().Transposed() * LMultiplier[ftDynamic].ForceJacobian);
// Return the updated force in the body frame
return FGForce::GetBodyForces();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -789,41 +782,52 @@ void FGLGear::bind(void)
{
string property_name;
string base_property_name;
base_property_name = CreateIndexedPropertyName("gear/unit", GearNumber);
switch(eContactType) {
case ctBOGEY:
base_property_name = CreateIndexedPropertyName("gear/unit", GearNumber);
break;
case ctSTRUCTURE:
base_property_name = CreateIndexedPropertyName("contact/unit", GearNumber);
break;
default:
return;
}
property_name = base_property_name + "/WOW";
PropertyManager->Tie( property_name.c_str(), &WOW );
property_name = base_property_name + "/z-position";
PropertyManager->Tie( property_name.c_str(), (FGForce*)this,
&FGForce::GetLocationZ, &FGForce::SetLocationZ);
property_name = base_property_name + "/compression-ft";
PropertyManager->Tie( property_name.c_str(), &compressLength );
property_name = base_property_name + "/static_friction_coeff";
PropertyManager->Tie( property_name.c_str(), &staticFCoeff );
property_name = base_property_name + "/dynamic_friction_coeff";
PropertyManager->Tie( property_name.c_str(), &dynamicFCoeff );
if (eContactType == ctBOGEY) {
property_name = base_property_name + "/slip-angle-deg";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &WheelSlip );
property_name = base_property_name + "/WOW";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &WOW );
PropertyManager->Tie( property_name.c_str(), &WheelSlip );
property_name = base_property_name + "/wheel-speed-fps";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), (FGLGear*)this,
PropertyManager->Tie( property_name.c_str(), (FGLGear*)this,
&FGLGear::GetWheelRollVel);
property_name = base_property_name + "/z-position";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), (FGForce*)this,
&FGForce::GetLocationZ, &FGForce::SetLocationZ);
property_name = base_property_name + "/compression-ft";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &compressLength );
property_name = base_property_name + "/side_friction_coeff";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &FCoeff );
property_name = base_property_name + "/static_friction_coeff";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &staticFCoeff );
PropertyManager->Tie( property_name.c_str(), &FCoeff );
property_name = base_property_name + "/rolling_friction_coeff";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &rollingFCoeff );
property_name = base_property_name + "/dynamic_friction_coeff";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &dynamicFCoeff );
PropertyManager->Tie( property_name.c_str(), &rollingFCoeff );
if (eSteerType == stCaster) {
property_name = base_property_name + "/steering-angle-deg";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), this, &FGLGear::GetSteerAngleDeg );
PropertyManager->Tie( property_name.c_str(), this, &FGLGear::GetSteerAngleDeg );
property_name = base_property_name + "/castered";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &Castered);
PropertyManager->Tie( property_name.c_str(), &Castered);
}
}
if( isRetractable ) {
property_name = base_property_name + "/pos-norm";
fdmex->GetPropertyManager()->Tie( property_name.c_str(), &GearPos );
PropertyManager->Tie( property_name.c_str(), &GearPos );
}
}
@ -856,11 +860,11 @@ void FGLGear::Report(ReportType repType)
<< " ft, " << TakeoffDistanceTraveled*0.3048 << " meters" << endl;
cout << " Distance traveled (over 50'): " << TakeoffDistanceTraveled50ft
<< " ft, " << TakeoffDistanceTraveled50ft*0.3048 << " meters" << endl;
cout << " [Altitude (ASL): " << Propagate->GetAltitudeASL() << " ft. / "
<< Propagate->GetAltitudeASLmeters() << " m | Temperature: "
<< fdmex->GetAtmosphere()->GetTemperature() - 459.67 << " F / "
<< RankineToCelsius(fdmex->GetAtmosphere()->GetTemperature()) << " C]" << endl;
cout << " [Velocity (KCAS): " << Auxiliary->GetVcalibratedKTS() << "]" << endl;
cout << " [Altitude (ASL): " << in.DistanceASL << " ft. / "
<< in.DistanceASL*FGJSBBase::fttom << " m | Temperature: "
<< in.Temperature - 459.67 << " F / "
<< RankineToCelsius(in.Temperature) << " C]" << endl;
cout << " [Velocity (KCAS): " << in.VcalibratedKts << "]" << endl;
TakeoffReported = true;
break;
case erNone:

View file

@ -38,16 +38,18 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "models/propulsion/FGForce.h"
#include "models/FGPropagate.h"
#include "math/FGColumnVector3.h"
#include <string>
#include "models/propulsion/FGForce.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "math/LagrangeMultiplier.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_LGEAR "$Id: FGLGear.h,v 1.41 2010/09/22 11:33:40 jberndt Exp $"
#define ID_LGEAR "$Id: FGLGear.h,v 1.47 2011/08/30 21:05:56 bcoconni Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -55,13 +57,9 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGAircraft;
class FGPropagate;
class FGFCS;
class FGMassBalance;
class FGAuxiliary;
class FGTable;
class Element;
class FGPropertyManager;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -180,7 +178,7 @@ CLASS DOCUMENTATION
</contact>
@endcode
@author Jon S. Berndt
@version $Id: FGLGear.h,v 1.41 2010/09/22 11:33:40 jberndt Exp $
@version $Id: FGLGear.h,v 1.47 2011/08/30 21:05:56 bcoconni Exp $
@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",
@ -196,8 +194,30 @@ CLASS DECLARATION
class FGLGear : public FGForce
{
public:
struct Inputs {
double Vground;
double VcalibratedKts;
double Temperature;
double DistanceAGL;
double DistanceASL;
double TotalDeltaT;
bool TakeoffThrottle;
bool WOW;
FGMatrix33 Tb2l;
FGMatrix33 Tec2l;
FGMatrix33 Tec2b;
FGColumnVector3 PQR;
FGColumnVector3 UVW;
FGLocation Location;
std::vector <double> SteerPosDeg;
std::vector <double> BrakePos;
std::vector <FGColumnVector3> vWhlBodyVec;
double FCSGearPos;
double EmptyWeight;
};
/// Brake grouping enumerators
enum BrakeGroup {bgNone=0, bgLeft, bgRight, bgCenter, bgNose, bgTail };
enum BrakeGroup {bgNone=0, bgLeft, bgRight, bgCenter, bgNose, bgTail, bgNumBrakeGroups };
/// Steering group membership enumerators
enum SteerType {stSteer, stFixed, stCaster};
/// Contact point type
@ -213,7 +233,7 @@ public:
@param Executive a pointer to the parent executive object
@param number integer identifier for this instance of FGLGear
*/
FGLGear(Element* el, FGFDMExec* Executive, int number);
FGLGear(Element* el, FGFDMExec* Executive, int number, const struct Inputs& input);
/// Destructor
~FGLGear();
@ -221,8 +241,8 @@ public:
FGColumnVector3& GetBodyForces(void);
/// Gets the location of the gear in Body axes
FGColumnVector3& GetBodyLocation(void) { return vWhlBodyVec; }
double GetBodyLocation(int idx) const { return vWhlBodyVec(idx); }
FGColumnVector3 GetBodyLocation(void) const { return in.vWhlBodyVec[GearNumber]; }
double GetBodyLocation(int idx) const { return in.vWhlBodyVec[GearNumber](idx); }
FGColumnVector3& GetLocalGear(void) { return vLocalGear; }
double GetLocalGear(int idx) const { return vLocalGear(idx); }
@ -268,9 +288,11 @@ public:
bool GetGearUnitUp(void) const { return GearUp; }
bool GetGearUnitDown(void) const { return GearDown; }
double GetWheelRollForce(void) {
UpdateForces();
FGColumnVector3 vForce = mTGear.Transposed() * FGForce::GetBodyForces();
return vForce(eX)*cos(SteerAngle) + vForce(eY)*sin(SteerAngle); }
double GetWheelSideForce(void) {
UpdateForces();
FGColumnVector3 vForce = mTGear.Transposed() * FGForce::GetBodyForces();
return vForce(eY)*cos(SteerAngle) - vForce(eX)*sin(SteerAngle); }
double GetWheelRollVel(void) const { return vWhlVelVec(eX)*cos(SteerAngle)
@ -282,9 +304,8 @@ public:
bool IsBogey(void) const { return (eContactType == ctBOGEY);}
double GetGearUnitPos(void);
double GetSteerAngleDeg(void) const { return radtodeg*SteerAngle; }
FGPropagate::LagrangeMultiplier* GetMultiplierEntry(int entry);
void SetLagrangeMultiplier(double lambda, int entry);
FGColumnVector3& UpdateForces(void);
const struct Inputs& in;
void bind(void);
@ -293,13 +314,11 @@ private:
static const FGMatrix33 Tb2s;
FGMatrix33 mTGear;
FGColumnVector3 vGearOrient;
FGColumnVector3 vWhlBodyVec;
FGColumnVector3 vLocalGear;
FGColumnVector3 vWhlVelVec, vLocalWhlVel; // Velocity of this wheel
FGColumnVector3 normal, cvel, vGroundNormal;
FGColumnVector3 normal, vGroundNormal;
FGLocation contact, gearLoc;
FGTable *ForceY_Table;
double dT;
double SteerAngle;
double kSpring;
double bDamp;
@ -348,13 +367,10 @@ private:
DampType eDampTypeRebound;
double maxSteerAngle;
FGPropagate::LagrangeMultiplier LMultiplier[3];
LagrangeMultiplier LMultiplier[3];
FGAuxiliary* Auxiliary;
FGPropagate* Propagate;
FGFCS* FCS;
FGMassBalance* MassBalance;
FGGroundReactions* GroundReactions;
FGPropertyManager* PropertyManager;
void ComputeRetractionState(void);
void ComputeBrakeForceCoefficient(void);
@ -364,6 +380,7 @@ private:
void ComputeVerticalStrutForce(void);
void ComputeGroundCoordSys(void);
void ComputeJacobian(const FGColumnVector3& vWhlContactVec);
void UpdateForces(void);
void CrashDetect(void);
void InitializeReporting(void);
void ResetReporting(void);

View file

@ -38,20 +38,18 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGMassBalance.h"
#include "FGPropulsion.h"
#include "propulsion/FGTank.h"
#include "FGBuoyantForces.h"
#include "input_output/FGPropertyManager.h"
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "FGMassBalance.h"
#include "FGFDMExec.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGMassBalance.cpp,v 1.35 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGMassBalance.cpp,v 1.37 2011/07/12 01:52:49 jberndt Exp $";
static const char *IdHdr = ID_MASSBALANCE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -149,8 +147,8 @@ bool FGMassBalance::Load(Element* el)
if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
}
Weight = EmptyWeight + FDMExec->GetPropulsion()->GetTanksWeight() + GetTotalPointMassWeight()
+ FDMExec->GetBuoyantForces()->GetGasMass()*slugtolb + ChildFDMWeight;
Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
+ in.GasMass*slugtolb + ChildFDMWeight;
Mass = lbtoslug*Weight;
@ -177,16 +175,17 @@ bool FGMassBalance::Run(bool Holding)
if (FDMExec->GetChildFDM(fdm)->mated) ChildFDMWeight += FDMExec->GetChildFDM(fdm)->exec->GetMassBalance()->GetWeight();
}
Weight = EmptyWeight + FDMExec->GetPropulsion()->GetTanksWeight() + GetTotalPointMassWeight()
+ FDMExec->GetBuoyantForces()->GetGasMass()*slugtolb + ChildFDMWeight;
Weight = EmptyWeight + in.TanksWeight + GetTotalPointMassWeight()
+ in.GasMass*slugtolb + ChildFDMWeight;
Mass = lbtoslug*Weight;
// Calculate new CG
vXYZcg = (FDMExec->GetPropulsion()->GetTanksMoment() + EmptyWeight*vbaseXYZcg
vXYZcg = (EmptyWeight*vbaseXYZcg
+ GetPointMassMoment()
+ FDMExec->GetBuoyantForces()->GetGasMassMoment()) / Weight;
+ in.TanksMoment
+ in.GasMoment) / Weight;
// Track frame-by-frame delta CG, and move the EOM-tracked location
// by this amount.
@ -204,8 +203,8 @@ bool FGMassBalance::Run(bool Holding)
mJ += GetPointmassInertia( lbtoslug * EmptyWeight, vbaseXYZcg );
// Then add the contributions from the additional pointmasses.
mJ += CalculatePMInertias();
mJ += FDMExec->GetPropulsion()->CalculateTankInertias();
mJ += FDMExec->GetBuoyantForces()->GetGasMassInertia();
mJ += in.TankInertia;
mJ += in.GasInertia;
Ixx = mJ(1,1);
Iyy = mJ(2,2);
@ -430,24 +429,7 @@ void FGMassBalance::GetMassPropertiesReport(void) const
<< setw(12) << pm->GetPointMassMoI(3,3) << endl;
}
for (unsigned int i=0;i<FDMExec->GetPropulsion()->GetNumTanks() ;i++) {
FGTank* tank = FDMExec->GetPropulsion()->GetTank(i);
string tankname="";
if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
tankname = "Solid Fuel";
} else if (tank->GetType() == FGTank::ttFUEL) {
tankname = "Fuel";
} else if (tank->GetType() == FGTank::ttOXIDIZER) {
tankname = "Oxidizer";
} else {
tankname = "(Unknown tank type)";
}
cout << highint << left << setw(4) << i << setw(30) << tankname << normint
<< right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
<< setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
<< setw(12) << "*" << setw(12) << "*"
<< setw(12) << "*" << endl;
}
cout << FDMExec->GetPropulsionTankReport();
cout << underon << setw(104) << " " << underoff << endl;
cout << highint << left << setw(30) << " Total: " << right << setw(14) << Weight

View file

@ -49,7 +49,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_MASSBALANCE "$Id: FGMassBalance.h,v 1.23 2011/05/20 03:18:36 jberndt Exp $"
#define ID_MASSBALANCE "$Id: FGMassBalance.h,v 1.25 2011/07/28 12:48:19 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONSS
@ -138,13 +138,13 @@ public:
slugs at the given vector r in the structural frame. The units
should be for the mass in slug and the vector in the structural
frame as usual in inches.
@param slugs the mass of this single pointmass given in slugs
@param mass_sl the mass of this single pointmass given in slugs
@param r the location of this single pointmass in the structural frame
*/
FGMatrix33 GetPointmassInertia(double slugs, const FGColumnVector3& r) const
FGMatrix33 GetPointmassInertia(double mass_sl, const FGColumnVector3& r) const
{
FGColumnVector3 v = StructuralToBody( r );
FGColumnVector3 sv = slugs*v;
FGColumnVector3 sv = mass_sl*v;
double xx = sv(1)*v(1);
double yy = sv(2)*v(2);
double zz = sv(3)*v(3);
@ -179,6 +179,15 @@ public:
void SetAircraftBaseInertias(FGMatrix33 BaseJ) {baseJ = BaseJ;}
void GetMassPropertiesReport(void) const;
struct Inputs {
double GasMass;
double TanksWeight;
FGColumnVector3 GasMoment;
FGMatrix33 GasInertia;
FGColumnVector3 TanksMoment;
FGMatrix33 TankInertia;
} in;
private:
double Weight;
double EmptyWeight;

View file

@ -48,7 +48,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_MODEL "$Id: FGModel.h,v 1.19 2011/05/20 03:18:36 jberndt Exp $"
#define ID_MODEL "$Id: FGModel.h,v 1.20 2011/06/21 04:41:54 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -79,7 +79,7 @@ public:
/// Constructor
FGModel(FGFDMExec*);
/// Destructor
~FGModel();
virtual ~FGModel();
std::string Name;

View file

@ -39,9 +39,16 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include "FGOutput.h"
#include "FGFDMExec.h"
#include "FGAtmosphere.h"
#include "FGAccelerations.h"
#include "atmosphere/FGWinds.h"
#include "FGFCS.h"
#include "FGAerodynamics.h"
#include "FGGroundReactions.h"
@ -56,10 +63,6 @@ INCLUDES
#include "models/propulsion/FGEngine.h"
#include "models/propulsion/FGTank.h"
#include "models/propulsion/FGPiston.h"
#include <sstream>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#if defined(WIN32) && !defined(__CYGWIN__)
# include <windows.h>
@ -74,7 +77,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGOutput.cpp,v 1.55 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGOutput.cpp,v 1.59 2011/08/14 20:15:56 jberndt Exp $";
static const char *IdHdr = ID_OUTPUT;
// (stolen from FGFS native_fdm.cxx)
@ -246,9 +249,11 @@ void FGOutput::DelimitedOutput(const string& fname)
const FGAuxiliary* Auxiliary = FDMExec->GetAuxiliary();
const FGAircraft* Aircraft = FDMExec->GetAircraft();
const FGAtmosphere* Atmosphere = FDMExec->GetAtmosphere();
const FGWinds* Winds = FDMExec->GetWinds();
const FGPropulsion* Propulsion = FDMExec->GetPropulsion();
const FGMassBalance* MassBalance = FDMExec->GetMassBalance();
const FGPropagate* Propagate = FDMExec->GetPropagate();
const FGAccelerations* Accelerations = FDMExec->GetAccelerations();
const FGFCS* FCS = FDMExec->GetFCS();
const FGInertial* Inertial = FDMExec->GetInertial();
const FGGroundReactions* GroundReactions = FDMExec->GetGroundReactions();
@ -409,7 +414,7 @@ void FGOutput::DelimitedOutput(const string& fname)
if (SubSystems & ssRates) {
outstream << delimeter;
outstream << (radtodeg*Propagate->GetPQR()).Dump(delimeter) << delimeter;
outstream << (radtodeg*Propagate->GetPQRdot()).Dump(delimeter) << delimeter;
outstream << (radtodeg*Accelerations->GetPQRdot()).Dump(delimeter) << delimeter;
outstream << (radtodeg*Propagate->GetPQRi()).Dump(delimeter);
}
if (SubSystems & ssVelocities) {
@ -453,9 +458,9 @@ void FGOutput::DelimitedOutput(const string& fname)
outstream << Atmosphere->GetTemperature() << delimeter;
outstream << Atmosphere->GetPressureSL() << delimeter;
outstream << Atmosphere->GetPressure() << delimeter;
outstream << Atmosphere->GetTurbMagnitude() << delimeter;
outstream << Atmosphere->GetTurbDirection().Dump(delimeter) << delimeter;
outstream << Atmosphere->GetTotalWindNED().Dump(delimeter);
outstream << Winds->GetTurbMagnitude() << delimeter;
outstream << Winds->GetTurbDirection().Dump(delimeter) << delimeter;
outstream << Winds->GetTotalWindNED().Dump(delimeter);
}
if (SubSystems & ssMassProps) {
outstream << delimeter;
@ -477,7 +482,7 @@ void FGOutput::DelimitedOutput(const string& fname)
outstream << ((FGColumnVector3)Propagate->GetInertialPosition()).Dump(delimeter) << delimeter;
outstream << ((FGColumnVector3)Propagate->GetLocation()).Dump(delimeter) << delimeter;
outstream.precision(14);
outstream << Inertial->GetEarthPositionAngleDeg() << delimeter;
outstream << Propagate->GetEarthPositionAngleDeg() << delimeter;
outstream << Propagate->GetDistanceAGL() << delimeter;
outstream << Propagate->GetTerrainElevation();
outstream.precision(10);
@ -733,8 +738,10 @@ void FGOutput::SocketOutput(void)
const FGPropulsion* Propulsion = FDMExec->GetPropulsion();
const FGMassBalance* MassBalance = FDMExec->GetMassBalance();
const FGPropagate* Propagate = FDMExec->GetPropagate();
const FGAccelerations* Accelerations = FDMExec->GetAccelerations();
const FGFCS* FCS = FDMExec->GetFCS();
const FGAtmosphere* Atmosphere = FDMExec->GetAtmosphere();
const FGWinds* Winds = FDMExec->GetWinds();
const FGAircraft* Aircraft = FDMExec->GetAircraft();
const FGGroundReactions* GroundReactions = FDMExec->GetGroundReactions();
@ -875,9 +882,9 @@ void FGOutput::SocketOutput(void)
socket->Append(radtodeg*Propagate->GetPQR(eP));
socket->Append(radtodeg*Propagate->GetPQR(eQ));
socket->Append(radtodeg*Propagate->GetPQR(eR));
socket->Append(radtodeg*Propagate->GetPQRdot(eP));
socket->Append(radtodeg*Propagate->GetPQRdot(eQ));
socket->Append(radtodeg*Propagate->GetPQRdot(eR));
socket->Append(radtodeg*Accelerations->GetPQRdot(eP));
socket->Append(radtodeg*Accelerations->GetPQRdot(eQ));
socket->Append(radtodeg*Accelerations->GetPQRdot(eR));
}
if (SubSystems & ssVelocities) {
socket->Append(Auxiliary->Getqbar());
@ -910,9 +917,9 @@ void FGOutput::SocketOutput(void)
socket->Append(Atmosphere->GetDensity());
socket->Append(Atmosphere->GetPressureSL());
socket->Append(Atmosphere->GetPressure());
socket->Append(Atmosphere->GetTurbMagnitude());
socket->Append(Atmosphere->GetTurbDirection().Dump(","));
socket->Append(Atmosphere->GetTotalWindNED().Dump(","));
socket->Append(Winds->GetTurbMagnitude());
socket->Append(Winds->GetTurbDirection().Dump(","));
socket->Append(Winds->GetTotalWindNED().Dump(","));
}
if (SubSystems & ssMassProps) {
socket->Append(MassBalance->GetJ()(1,1));

View file

@ -62,16 +62,13 @@ INCLUDES
#include "FGPropagate.h"
#include "FGGroundReactions.h"
#include "FGFDMExec.h"
#include "FGAircraft.h"
#include "FGMassBalance.h"
#include "FGInertial.h"
#include "input_output/FGPropertyManager.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.88 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.95 2011/08/21 16:11:25 bcoconni Exp $";
static const char *IdHdr = ID_PROPAGATE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -86,11 +83,7 @@ FGPropagate::FGPropagate(FGFDMExec* fdmex)
{
Debug(0);
Name = "FGPropagate";
gravType = gtWGS84;
vPQRidot.InitMatrix();
vQtrndot = FGQuaternion(0,0,0);
vUVWidot.InitMatrix();
vInertialVelocity.InitMatrix();
/// These define the indices use to select the various integrators.
@ -122,15 +115,12 @@ FGPropagate::~FGPropagate(void)
bool FGPropagate::InitModel(void)
{
// For initialization ONLY:
SeaLevelRadius = LocalTerrainRadius = FDMExec->GetInertial()->GetRefRadius();
SeaLevelRadius = LocalTerrainRadius = in.RefRadius;
FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(LocalTerrainRadius);
VState.vLocation.SetRadius( LocalTerrainRadius + 4.0 );
VState.vLocation.SetEllipse(FDMExec->GetInertial()->GetSemimajor(), FDMExec->GetInertial()->GetSemiminor());
vOmegaEarth = FGColumnVector3( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // Earth rotation vector
VState.vLocation.SetEllipse(in.SemiMajor, in.SemiMinor);
vPQRidot.InitMatrix();
vQtrndot = FGQuaternion(0,0,0);
vUVWidot.InitMatrix();
vInertialVelocity.InitMatrix();
VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0));
@ -150,7 +140,6 @@ bool FGPropagate::InitModel(void)
void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
{
SetSeaLevelRadius(FGIC->GetSeaLevelRadiusFtIC());
SetTerrainElevation(FGIC->GetTerrainElevationFtIC());
// Initialize the State Vector elements and the transformation matrices
@ -158,9 +147,9 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
// Set the position lat/lon/radius
VState.vLocation.SetPosition( FGIC->GetLongitudeRadIC(),
FGIC->GetLatitudeRadIC(),
FGIC->GetAltitudeASLFtIC() + FGIC->GetSeaLevelRadiusFtIC() );
FGIC->GetAltitudeASLFtIC() + SeaLevelRadius);
VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
VState.vLocation.SetEarthPositionAngle(0.0);
Ti2ec = VState.vLocation.GetTi2ec(); // ECI to ECEF transform
Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
@ -198,10 +187,22 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
FGIC->GetQRadpsIC(),
FGIC->GetRRadpsIC() );
VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
// Make an initial run and set past values
InitializeDerivatives();
CalculateInertialVelocity(); // Translational position derivative
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Initialize the past value deques
void FGPropagate::InitializeDerivatives()
{
for (int i=0; i<4; i++) {
VState.dqPQRidot[i] = in.vPQRidot;
VState.dqUVWidot[i] = in.vUVWidot;
VState.dqInertialVelocity[i] = VState.vInertialVelocity;
VState.dqQtrndot[i] = in.vQtrndot;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -229,28 +230,22 @@ bool FGPropagate::Run(bool Holding)
if (FGModel::Run(Holding)) return true; // Fast return if we have nothing to do ...
if (Holding) return false;
double dt = FDMExec->GetDeltaT()*rate; // The 'stepsize'
double dt = in.DeltaT * rate; // The 'stepsize'
RunPreFunctions();
// Calculate state derivatives
CalculatePQRdot(); // Angular rate derivative
CalculateUVWdot(); // Translational rate derivative
ResolveFrictionForces(dt); // Update rate derivatives with friction forces
CalculateQuatdot(); // Angular orientation derivative
// Propagate rotational / translational velocity, angular /translational position, respectively.
Integrate(VState.vPQRi, vPQRidot, VState.dqPQRidot, dt, integrator_rotational_rate);
Integrate(VState.qAttitudeECI, vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position);
Integrate(VState.vPQRi, in.vPQRidot, VState.dqPQRidot, dt, integrator_rotational_rate);
Integrate(VState.qAttitudeECI, in.vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position);
Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position);
Integrate(VState.vInertialVelocity, vUVWidot, VState.dqUVWidot, dt, integrator_translational_rate);
Integrate(VState.vInertialVelocity, in.vUVWidot, VState.dqUVWidot, dt, integrator_translational_rate);
// CAUTION : the order of the operations below is very important to get transformation
// matrices that are consistent with the new state of the vehicle
// 1. Update the Earth position angle (EPA)
VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
VState.vLocation.IncrementEarthPositionAngle(in.vOmegaPlanet(eZ)*(in.DeltaT*rate));
// 2. Update the Ti2ec and Tec2i transforms from the updated EPA
Ti2ec = VState.vLocation.GetTi2ec(); // ECI to ECEF transform
@ -278,7 +273,7 @@ bool FGPropagate::Run(bool Holding)
VehicleRadius = GetRadius(); // Calculate current aircraft radius from center of planet
VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
VState.qAttitudeLocal = Tl2b.GetQuaternion();
@ -291,88 +286,6 @@ bool FGPropagate::Run(bool Holding)
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Compute body frame rotational accelerations based on the current body moments
//
// vPQRdot is the derivative of the absolute angular velocity of the vehicle
// (body rate with respect to the inertial frame), expressed in the body frame,
// where the derivative is taken in the body frame.
// J is the inertia matrix
// Jinv is the inverse inertia matrix
// vMoments is the moment vector in the body frame
// VState.vPQRi is the total inertial angular velocity of the vehicle
// expressed in the body frame.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqn 1.5-16e (page 50)
void FGPropagate::CalculatePQRdot(void)
{
const FGColumnVector3& vMoments = FDMExec->GetAircraft()->GetMoments(); // current moments
const FGMatrix33& J = FDMExec->GetMassBalance()->GetJ(); // inertia matrix
const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv(); // inertia matrix inverse
// Compute body frame rotational accelerations based on the current body
// moments and the total inertial angular velocity expressed in the body
// frame.
vPQRidot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi));
vPQRdot = vPQRidot - VState.vPQRi * (Ti2b * vOmegaEarth);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Compute the quaternion orientation derivative
//
// vQtrndot is the quaternion derivative.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqn 1.5-16b (page 50)
void FGPropagate::CalculateQuatdot(void)
{
// Compute quaternion orientation derivative on current body rates
vQtrndot = VState.qAttitudeECI.GetQDot( VState.vPQRi);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This set of calculations results in the body and inertial frame accelerations
// being computed.
// Compute body and inertial frames accelerations based on the current body
// forces including centripetal and coriolis accelerations for the former.
// vOmegaEarth is the Earth angular rate - expressed in the inertial frame -
// so it has to be transformed to the body frame. More completely,
// vOmegaEarth is the rate of the ECEF frame relative to the Inertial
// frame (ECI), expressed in the Inertial frame.
// vForces is the total force on the vehicle in the body frame.
// VState.vPQR is the vehicle body rate relative to the ECEF frame, expressed
// in the body frame.
// VState.vUVW is the vehicle velocity relative to the ECEF frame, expressed
// in the body frame.
// Reference: See Stevens and Lewis, "Aircraft Control and Simulation",
// Second edition (2004), eqns 1.5-13 (pg 48) and 1.5-16d (page 50)
void FGPropagate::CalculateUVWdot(void)
{
double mass = FDMExec->GetMassBalance()->GetMass(); // mass
const FGColumnVector3& vForces = FDMExec->GetAircraft()->GetForces(); // current forces
vUVWdot = vForces/mass - (VState.vPQR + 2.0*(Ti2b *vOmegaEarth)) * VState.vUVW;
// Include Centripetal acceleration.
vUVWdot -= Ti2b * (vOmegaEarth*(vOmegaEarth*VState.vInertialPosition));
// Include Gravitation accel
switch (gravType) {
case gtStandard:
vGravAccel = Tl2b * FGColumnVector3( 0.0, 0.0, FDMExec->GetInertial()->GetGAccel(VehicleRadius) );
break;
case gtWGS84:
vGravAccel = Tec2b * FDMExec->GetInertial()->GetGravityJ2(VState.vLocation);
break;
}
vUVWdot += vGravAccel;
vUVWidot = Tb2i * (vForces/mass + vGravAccel);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Transform the velocity vector of the body relative to the origin (Earth
// center) to be expressed in the inertial frame, and add the vehicle velocity
@ -382,7 +295,7 @@ void FGPropagate::CalculateUVWdot(void)
void FGPropagate::CalculateInertialVelocity(void)
{
VState.vInertialVelocity = Tb2i * VState.vUVW + (vOmegaEarth * VState.vInertialPosition);
VState.vInertialVelocity = Tb2i * VState.vUVW + (in.vOmegaPlanet * VState.vInertialPosition);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -392,7 +305,7 @@ void FGPropagate::CalculateInertialVelocity(void)
void FGPropagate::CalculateUVW(void)
{
VState.vUVW = Ti2b * (VState.vInertialVelocity - (vOmegaEarth * VState.vInertialPosition));
VState.vUVW = Ti2b * (VState.vInertialVelocity - (in.vOmegaPlanet * VState.vInertialPosition));
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -449,167 +362,6 @@ void FGPropagate::Integrate( FGQuaternion& Integrand,
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Evaluates the rates (translation or rotation) that the friction forces have
// to resist to. This includes the external forces and moments as well as the
// relative movement between the aircraft and the ground.
// Erin Catto's paper (see ref [6]) only supports Euler integration scheme and
// this algorithm has been adapted to handle the multistep algorithms that
// JSBSim supports (i.e. Trapezoidal, Adams-Bashforth 2, 3 and 4). The capacity
// to handle the multistep integration schemes adds some complexity but it
// significantly helps stabilizing the friction forces.
void FGPropagate::EvaluateRateToResistTo(FGColumnVector3& vdot,
const FGColumnVector3& Val,
const FGColumnVector3& ValDot,
const FGColumnVector3& LocalTerrainVal,
deque <FGColumnVector3>& dqValDot,
const double dt,
const eIntegrateType integration_type)
{
switch(integration_type) {
case eAdamsBashforth4:
vdot = ValDot + Ti2b * (-59.*dqValDot[0]+37.*dqValDot[1]-9.*dqValDot[2])/55.;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += 24.*(Val - Tec2b * LocalTerrainVal) / (55.*dt);
break;
case eAdamsBashforth3:
vdot = ValDot + Ti2b * (-16.*dqValDot[0]+5.*dqValDot[1])/23.;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += 12.*(Val - Tec2b * LocalTerrainVal) / (23.*dt);
break;
case eAdamsBashforth2:
vdot = ValDot - Ti2b * dqValDot[0]/3.;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += 2.*(Val - Tec2b * LocalTerrainVal) / (3.*dt);
break;
case eTrapezoidal:
vdot = ValDot + Ti2b * dqValDot[0];
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += 2.*(Val - Tec2b * LocalTerrainVal) / dt;
break;
case eRectEuler:
vdot = ValDot;
if (dt > 0.) // Zeroes out the relative movement between aircraft and ground
vdot += (Val - Tec2b * LocalTerrainVal) / dt;
break;
case eNone:
break;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Resolves the contact forces just before integrating the EOM.
// This routine is using Lagrange multipliers and the projected Gauss-Seidel
// (PGS) method.
// Reference: See Erin Catto, "Iterative Dynamics with Temporal Coherence",
// February 22, 2005
// In JSBSim there is only one rigid body (the aircraft) and there can be
// multiple points of contact between the aircraft and the ground. As a
// consequence our matrix J*M^-1*J^T is not sparse and the algorithm described
// in Catto's paper has been adapted accordingly.
// The friction forces are resolved in the body frame relative to the origin
// (Earth center).
void FGPropagate::ResolveFrictionForces(double dt)
{
const double invMass = 1.0 / FDMExec->GetMassBalance()->GetMass();
const FGMatrix33& Jinv = FDMExec->GetMassBalance()->GetJinv();
vector <FGColumnVector3> JacF, JacM;
vector<double> lambda, lambdaMin, lambdaMax;
FGColumnVector3 vdot, wdot;
FGColumnVector3 Fc, Mc;
int n = 0;
// Compiles data from the ground reactions to build up the jacobian matrix
for (MultiplierIterator it=MultiplierIterator(FDMExec->GetGroundReactions()); *it; ++it, n++) {
JacF.push_back((*it)->ForceJacobian);
JacM.push_back((*it)->MomentJacobian);
lambda.push_back((*it)->value);
lambdaMax.push_back((*it)->Max);
lambdaMin.push_back((*it)->Min);
}
// If no gears are in contact with the ground then return
if (!n) return;
vector<double> a(n*n); // Will contain J*M^-1*J^T
vector<double> rhs(n);
// Assemble the linear system of equations
for (int i=0; i < n; i++) {
for (int j=0; j < i; j++)
a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of J^T*M^-1*J
for (int j=i; j < n; j++)
a[i*n+j] = DotProduct(JacF[i],invMass*JacF[j])+DotProduct(JacM[i],Jinv*JacM[j]);
}
// Assemble the RHS member
// Translation
EvaluateRateToResistTo(vdot, VState.vUVW, vUVWdot, LocalTerrainVelocity,
VState.dqUVWidot, dt, integrator_translational_rate);
// Rotation
EvaluateRateToResistTo(wdot, VState.vPQR, vPQRdot, LocalTerrainAngularVelocity,
VState.dqPQRidot, dt, integrator_rotational_rate);
// Prepare the linear system for the Gauss-Seidel algorithm :
// 1. Compute the right hand side member 'rhs'
// 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save
// a division computation at each iteration of Gauss-Seidel.
for (int i=0; i < n; i++) {
double d = 1.0 / a[i*n+i];
rhs[i] = -(DotProduct(JacF[i],vdot)+DotProduct(JacM[i],wdot))*d;
for (int j=0; j < n; j++)
a[i*n+j] *= d;
}
// Resolve the Lagrange multipliers with the projected Gauss-Seidel method
for (int iter=0; iter < 50; iter++) {
double norm = 0.;
for (int i=0; i < n; i++) {
double lambda0 = lambda[i];
double dlambda = rhs[i];
for (int j=0; j < n; j++)
dlambda -= a[i*n+j]*lambda[j];
lambda[i] = Constrain(lambdaMin[i], lambda0+dlambda, lambdaMax[i]);
dlambda = lambda[i] - lambda0;
norm += fabs(dlambda);
}
if (norm < 1E-5) break;
}
// Calculate the total friction forces and moments
Fc.InitMatrix();
Mc.InitMatrix();
for (int i=0; i< n; i++) {
Fc += lambda[i]*JacF[i];
Mc += lambda[i]*JacM[i];
}
vUVWdot += invMass * Fc;
vUVWidot += invMass * Tb2i * Fc;
vPQRdot += Jinv * Mc;
vPQRidot += Jinv * Mc;
// Save the value of the Lagrange multipliers to accelerate the convergence
// of the Gauss-Seidel algorithm at next iteration.
int i = 0;
for (MultiplierIterator it=MultiplierIterator(FDMExec->GetGroundReactions()); *it; ++it)
(*it)->value = lambda[i++];
FDMExec->GetGroundReactions()->UpdateForcesAndMoments();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::UpdateLocationMatrices(void)
@ -653,31 +405,7 @@ void FGPropagate::SetInertialVelocity(FGColumnVector3 Vi) {
void FGPropagate::SetInertialRates(FGColumnVector3 vRates) {
VState.vPQRi = Ti2b * vRates;
VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::InitializeDerivatives(void)
{
// Make an initial run and set past values
CalculatePQRdot(); // Angular rate derivative
CalculateUVWdot(); // Translational rate derivative
ResolveFrictionForces(0.); // Update rate derivatives with friction forces
CalculateQuatdot(); // Angular orientation derivative
CalculateInertialVelocity(); // Translational position derivative
// Initialize past values deques
VState.dqPQRidot.clear();
VState.dqUVWidot.clear();
VState.dqInertialVelocity.clear();
VState.dqQtrndot.clear();
for (int i=0; i<4; i++) {
VState.dqPQRidot.push_front(vPQRidot);
VState.dqUVWidot.push_front(vUVWidot);
VState.dqInertialVelocity.push_front(VState.vInertialVelocity);
VState.dqQtrndot.push_front(vQtrndot);
}
VState.vPQR = VState.vPQRi - Ti2b * in.vOmegaPlanet;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -685,13 +413,14 @@ void FGPropagate::InitializeDerivatives(void)
void FGPropagate::RecomputeLocalTerrainRadius(void)
{
FGLocation contactloc;
FGColumnVector3 dv;
FGColumnVector3 dummy;
double t = FDMExec->GetSimTime();
// Get the LocalTerrain radius.
FDMExec->GetGroundCallback()->GetAGLevel(t, VState.vLocation, contactloc, dv,
LocalTerrainVelocity, LocalTerrainAngularVelocity);
LocalTerrainRadius = contactloc.GetRadius();
FDMExec->GetGroundCallback()->GetAGLevel(t, VState.vLocation, contactloc,
dummy, LocalTerrainVelocity, LocalTerrainAngularVelocity);
LocalTerrainRadius = contactloc.GetRadius();
FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(LocalTerrainRadius);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -720,8 +449,9 @@ double FGPropagate::GetDistanceAGL(void) const
void FGPropagate::SetVState(const VehicleState& vstate)
{
//ToDo: Shouldn't all of these be set from the vstate vector passed in?
VState.vLocation = vstate.vLocation;
VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
VState.vLocation.SetEarthPositionAngle(vstate.vLocation.GetEPA());
Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
Tec2i = Ti2ec.Transposed();
UpdateLocationMatrices();
@ -731,10 +461,8 @@ void FGPropagate::SetVState(const VehicleState& vstate)
VState.vUVW = vstate.vUVW;
vVel = Tb2l * VState.vUVW;
VState.vPQR = vstate.vPQR;
VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth;
VState.vPQRi = VState.vPQR + Ti2b * in.vOmegaPlanet;
VState.vInertialPosition = vstate.vInertialPosition;
InitializeDerivatives();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -755,7 +483,7 @@ void FGPropagate::UpdateVehicleState(void)
void FGPropagate::SetLocation(const FGLocation& l)
{
VState.vLocation = l;
VState.vLocation.SetEarthPositionAngle(FDMExec->GetInertial()->GetEarthPositionAngle());
VState.vLocation.SetEarthPositionAngle(l.GetEPA());
Ti2ec = VState.vLocation.GetTi2ec(); // useless ?
Tec2i = Ti2ec.Transposed();
UpdateVehicleState();
@ -822,14 +550,6 @@ void FGPropagate::bind(void)
PropertyManager->Tie("velocities/eci-velocity-mag-fps", this, &FGPropagate::GetInertialVelocityMagnitude);
PropertyManager->Tie("accelerations/pdot-rad_sec2", this, eP, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/qdot-rad_sec2", this, eQ, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/rdot-rad_sec2", this, eR, (PMF)&FGPropagate::GetPQRdot);
PropertyManager->Tie("accelerations/udot-ft_sec2", this, eU, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("accelerations/vdot-ft_sec2", this, eV, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("accelerations/wdot-ft_sec2", this, eW, (PMF)&FGPropagate::GetUVWdot);
PropertyManager->Tie("position/h-sl-ft", this, &FGPropagate::GetAltitudeASL, &FGPropagate::SetAltitudeASL, true);
PropertyManager->Tie("position/h-sl-meters", this, &FGPropagate::GetAltitudeASLmeters, &FGPropagate::SetAltitudeASLmeters, true);
PropertyManager->Tie("position/lat-gc-rad", this, &FGPropagate::GetLatitude, &FGPropagate::SetLatitude);
@ -845,6 +565,7 @@ void FGPropagate::bind(void)
&FGPropagate::GetTerrainElevation,
&FGPropagate::SetTerrainElevation, false);
PropertyManager->Tie("position/epa-rad", this, &FGPropagate::GetEarthPositionAngle);
PropertyManager->Tie("metrics/terrain-radius", this, &FGPropagate::GetLocalTerrainRadius);
PropertyManager->Tie("attitude/phi-rad", this, (int)ePhi, (PMF)&FGPropagate::GetEuler);
@ -859,7 +580,6 @@ void FGPropagate::bind(void)
PropertyManager->Tie("simulation/integrator/rate/translational", (int*)&integrator_translational_rate);
PropertyManager->Tie("simulation/integrator/position/rotational", (int*)&integrator_rotational_position);
PropertyManager->Tie("simulation/integrator/position/translational", (int*)&integrator_translational_position);
PropertyManager->Tie("simulation/gravity-model", &gravType);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -902,7 +622,7 @@ void FGPropagate::Debug(int from)
<< reset << endl;
cout << endl;
cout << highint << " Earth Position Angle (deg): " << setw(8) << setprecision(3) << reset
<< FDMExec->GetInertial()->GetEarthPositionAngleDeg() << endl;
<< GetEarthPositionAngleDeg() << endl;
cout << endl;
cout << highint << " Body velocity (ft/sec): " << setw(8) << setprecision(3) << reset << VState.vUVW << endl;
cout << highint << " Local velocity (ft/sec): " << setw(8) << setprecision(3) << reset << vVel << endl;
@ -911,7 +631,7 @@ void FGPropagate::Debug(int from)
cout << highint << " Latitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLatitudeDeg() << endl;
cout << highint << " Longitude (deg): " << setw(8) << setprecision(3) << reset << VState.vLocation.GetLongitudeDeg() << endl;
cout << highint << " Altitude ASL (ft): " << setw(8) << setprecision(3) << reset << GetAltitudeASL() << endl;
cout << highint << " Acceleration (NED, ft/sec^2): " << setw(8) << setprecision(3) << reset << Tb2l*GetUVWdot() << endl;
// cout << highint << " Acceleration (NED, ft/sec^2): " << setw(8) << setprecision(3) << reset << Tb2l*GetUVWdot() << endl;
cout << endl;
cout << highint << " Matrix ECEF to Body (Orientation of Body with respect to ECEF): "
<< reset << endl << Tec2b.Dump("\t", " ") << endl;

View file

@ -49,7 +49,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_PROPAGATE "$Id: FGPropagate.h,v 1.59 2011/05/20 03:18:36 jberndt Exp $"
#define ID_PROPAGATE "$Id: FGPropagate.h,v 1.63 2011/08/21 15:35:39 bcoconni Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -69,15 +69,6 @@ CLASS DOCUMENTATION
state of the vehicle given the forces and moments that act on it. The
integration accounts for a rotating Earth.
The general execution of this model follows this process:
-Calculate the angular accelerations
-Calculate the translational accelerations
-Calculate the angular rate
-Calculate the translational velocity
-Integrate accelerations and rates
Integration of rotational and translation position and rate can be
customized as needed or frozen by the selection of no integrator. The
selection of which integrator to use is done through the setting of
@ -101,8 +92,8 @@ CLASS DOCUMENTATION
5: Adams Bashforth 4
@endcode
@author Jon S. Berndt, Mathias Froehlich
@version $Id: FGPropagate.h,v 1.59 2011/05/20 03:18:36 jberndt Exp $
@author Jon S. Berndt, Mathias Froehlich, Bertrand Coconnier
@version $Id: FGPropagate.h,v 1.63 2011/08/21 15:35:39 bcoconni Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -169,14 +160,13 @@ public:
/// These define the indices use to select the various integrators.
enum eIntegrateType {eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4};
/// These define the indices use to select the gravitation models.
enum eGravType {gtStandard, gtWGS84};
/** Initializes the FGPropagate class after instantiation and prior to first execution.
The base class FGModel::InitModel is called first, initializing pointers to the
other FGModel objects (and others). */
bool InitModel(void);
void InitializeDerivatives();
/** Runs the state propagation model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@param Holding if true, the executive has been directed to hold the sim from
@ -186,8 +176,6 @@ public:
@return false if no error */
bool Run(bool Holding);
const FGQuaternion& GetQuaterniondot(void) const {return vQtrndot;}
/** Retrieves the velocity vector.
The vector returned is represented by an FGColumnVector reference. The vector
for the velocity in Local frame is organized (Vnorth, Veast, Vdown). The vector
@ -213,20 +201,6 @@ public:
*/
const FGColumnVector3& GetUVW(void) const { return VState.vUVW; }
/** Retrieves the body axis acceleration.
Retrieves the computed body axis accelerations based on the
applied forces and accounting for a rotating body frame.
The vector returned is represented by an FGColumnVector reference. The vector
for the acceleration in Body frame is organized (Ax, Ay, Az). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vUVWdot(1) is Ax. Various convenience enumerators are defined
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
eX=1, eY=2, eZ=3.
units ft/sec^2
@return Body axis translational acceleration in ft/sec^2.
*/
const FGColumnVector3& GetUVWdot(void) const { return vUVWdot; }
/** Retrieves the body angular rates vector, relative to the ECEF frame.
Retrieves the body angular rates (p, q, r), which are calculated by integration
of the angular acceleration.
@ -255,21 +229,6 @@ public:
*/
const FGColumnVector3& GetPQRi(void) const {return VState.vPQRi;}
/** Retrieves the body axis angular acceleration vector.
Retrieves the body axis angular acceleration vector in rad/sec^2. The
angular acceleration vector is determined from the applied forces and
accounts for a rotating frame.
The vector returned is represented by an FGColumnVector reference. The vector
for the angular acceleration in Body frame is organized (Pdot, Qdot, Rdot). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vPQRdot(1) is Pdot. Various convenience enumerators are defined
in FGJSBBase. The relevant enumerators for the vector returned by this call are,
eP=1, eQ=2, eR=3.
units rad/sec^2
@return The angular acceleration vector.
*/
const FGColumnVector3& GetPQRdot(void) const {return vPQRdot;}
/** Retrieves the Euler angles that define the vehicle orientation.
Extracts the Euler angles from the quaternion that stores the orientation
in the Local frame. The order of rotation used is Yaw-Pitch-Roll. The
@ -300,19 +259,6 @@ public:
*/
double GetUVW (int idx) const { return VState.vUVW(idx); }
/** Retrieves a body frame acceleration component.
Retrieves a body frame acceleration component. The acceleration returned
is extracted from the vUVWdot vector (an FGColumnVector). The vector for
the acceleration in Body frame is organized (Ax, Ay, Az). The vector is
1-based. In other words, GetUVWdot(1) returns Ax. Various convenience
enumerators are defined in FGJSBBase. The relevant enumerators for the
acceleration returned by this call are, eX=1, eY=2, eZ=3.
units ft/sec^2
@param idx the index of the acceleration component desired (1-based).
@return The body frame acceleration component.
*/
double GetUVWdot(int idx) const { return vUVWdot(idx); }
/** Retrieves a Local frame velocity component.
Retrieves a Local frame velocity component. The velocity returned is
extracted from the vVel vector (an FGColumnVector). The vector for the
@ -382,20 +328,6 @@ public:
*/
double GetPQRi(int axis) const {return VState.vPQRi(axis);}
/** Retrieves a body frame angular acceleration component.
Retrieves a body frame angular acceleration component. The angular
acceleration returned is extracted from the vPQRdot vector (an
FGColumnVector). The vector for the angular acceleration in Body frame
is organized (Pdot, Qdot, Rdot). The vector is 1-based. In other words,
GetPQRdot(1) returns Pdot (roll acceleration). Various convenience
enumerators are defined in FGJSBBase. The relevant enumerators for the
angular acceleration returned by this call are, eP=1, eQ=2, eR=3.
units rad/sec^2
@param axis the index of the angular acceleration component desired (1-based).
@return The body frame angular acceleration component.
*/
double GetPQRdot(int axis) const {return vPQRdot(axis);}
/** Retrieves a vehicle Euler angle component.
Retrieves an Euler angle (Phi, Theta, or Psi) from the quaternion that
stores the vehicle orientation relative to the Local frame. The order of
@ -447,7 +379,12 @@ public:
*/
double GetLocalTerrainRadius(void) const { return LocalTerrainRadius; }
double GetSeaLevelRadius(void) const { return SeaLevelRadius; }
double GetEarthPositionAngle(void) const { return VState.vLocation.GetEPA(); }
double GetEarthPositionAngleDeg(void) const { return GetEarthPositionAngle()*radtodeg;}
const FGColumnVector3& GetTerrainVelocity(void) const { return LocalTerrainVelocity; }
const FGColumnVector3& GetTerrainAngularVelocity(void) const { return LocalTerrainAngularVelocity; }
double GetTerrainElevation(void) const;
double GetDistanceAGL(void) const;
double GetRadius(void) const {
@ -526,13 +463,14 @@ public:
void SetVState(const VehicleState& vstate);
void InitializeDerivatives(void);
void SetEarthPositionAngle(double epa) {VState.vLocation.SetEarthPositionAngle(epa);}
void SetInertialOrientation(FGQuaternion Qi);
void SetInertialVelocity(FGColumnVector3 Vi);
void SetInertialRates(FGColumnVector3 vRates);
const FGQuaternion GetQuaternion(void) const { return VState.qAttitudeLocal; }
const FGQuaternion GetQuaternionECI(void) const { return VState.qAttitudeECI; }
void SetPQR(unsigned int i, double val) {
if ((i>=1) && (i<=3) )
@ -589,16 +527,19 @@ public:
VState.vLocation -= Tb2ec*deltaLoc;
}
struct LagrangeMultiplier {
FGColumnVector3 ForceJacobian;
FGColumnVector3 MomentJacobian;
double Min;
double Max;
double value;
};
void DumpState(void);
struct Inputs {
FGColumnVector3 vPQRidot;
FGQuaternion vQtrndot;
FGColumnVector3 vUVWidot;
FGColumnVector3 vOmegaPlanet;
double RefRadius;
double SemiMajor;
double SemiMinor;
double DeltaT;
} in;
private:
// state vector
@ -606,14 +547,9 @@ private:
struct VehicleState VState;
FGColumnVector3 vVel;
FGColumnVector3 vPQRdot, vPQRidot;
FGColumnVector3 vUVWdot, vUVWidot;
FGColumnVector3 vInertialVelocity;
FGColumnVector3 vLocation;
FGColumnVector3 vDeltaXYZEC;
FGColumnVector3 vGravAccel;
FGColumnVector3 vOmegaEarth; // The Earth angular velocity vector
FGQuaternion vQtrndot;
FGMatrix33 Tec2b;
FGMatrix33 Tb2ec;
FGMatrix33 Tl2b; // local to body frame matrix copy for immediate local use
@ -633,13 +569,9 @@ private:
eIntegrateType integrator_translational_rate;
eIntegrateType integrator_rotational_position;
eIntegrateType integrator_translational_position;
int gravType;
void CalculatePQRdot(void);
void CalculateQuatdot(void);
void CalculateInertialVelocity(void);
void CalculateUVW(void);
void CalculateUVWdot(void);
void Integrate( FGColumnVector3& Integrand,
FGColumnVector3& Val,
@ -653,16 +585,6 @@ private:
double dt,
eIntegrateType integration_type);
void EvaluateRateToResistTo(FGColumnVector3& vdot,
const FGColumnVector3& Val,
const FGColumnVector3& ValDot,
const FGColumnVector3& LocalTerrainVal,
deque <FGColumnVector3>& dqValDot,
const double dt,
const eIntegrateType integration_type);
void ResolveFrictionForces(double dt);
void UpdateLocationMatrices(void);
void UpdateBodyMatrices(void);
void UpdateVehicleState(void);

View file

@ -44,10 +44,14 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <iomanip>
#include "FGFDMExec.h"
#include "FGPropulsion.h"
#include "models/FGFCS.h"
#include "models/FGMassBalance.h"
#include "models/propulsion/FGThruster.h"
#include "models/propulsion/FGRocket.h"
#include "models/propulsion/FGTurbine.h"
#include "models/propulsion/FGPiston.h"
@ -57,15 +61,12 @@ INCLUDES
#include "input_output/FGPropertyManager.h"
#include "input_output/FGXMLParse.h"
#include "math/FGColumnVector3.h"
#include <iostream>
#include <sstream>
#include <cstdlib>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.46 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.50 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_PROPULSION;
extern short debug_lvl;
@ -87,7 +88,7 @@ FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
tankJ.InitMatrix();
refuel = dump = false;
DumpRate = 0.0;
fuel_freeze = false;
FuelFreeze = false;
TotalFuelQuantity = 0.0;
IsBound =
HavePistonEngine =
@ -158,33 +159,132 @@ bool FGPropulsion::Run(bool Holding)
RunPreFunctions();
double dt = FDMExec->GetDeltaT();
vForces.InitMatrix();
vMoments.InitMatrix();
for (i=0; i<numEngines; i++) {
Engines[i]->Calculate();
ConsumeFuel(Engines[i]);
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
}
TotalFuelQuantity = 0.0;
for (i=0; i<numTanks; i++) {
Tanks[i]->Calculate( dt * rate );
Tanks[i]->Calculate( in.TotalDeltaT, in.TAT_c);
if (Tanks[i]->GetType() == FGTank::ttFUEL) {
TotalFuelQuantity += Tanks[i]->GetContents();
}
}
if (refuel) DoRefuel( dt * rate );
if (dump) DumpFuel( dt * rate );
if (refuel) DoRefuel( in.TotalDeltaT );
if (dump) DumpFuel( in.TotalDeltaT );
RunPostFunctions();
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// The engine can tell us how much fuel it needs, but it is up to the propulsion
// subsystem manager class FGPropulsion to manage fuel flow amongst tanks. Engines
// May burn fuel from more than one tank at a time, and may burn from one tank
// before another - that is, may burn from one tank until the tank is depleted,
// then burn from the next highest priority tank. This can be accompished
// by defining a fuel management system, but this way of specifying priorities
// is more automatic from a user perspective.
void FGPropulsion::ConsumeFuel(FGEngine* engine)
{
if (FuelFreeze) return;
if (FDMExec->GetTrimStatus()) return;
unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
vector <int> FeedListFuel, FeedListOxi;
bool Starved = true; // Initially set Starved to true. Set to false in code below.
// bool hasOxTanks = false;
// For this engine,
// 1) Count how many fuel tanks with the current priority level have fuel
// 2) If there none, then try next lower priority (higher number) - that is,
// increment CurrentPriority.
// 3) Build the feed list.
// 4) Do the same for oxidizer tanks, if needed.
// Process fuel tanks, if any
while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
unsigned int TankId = engine->GetSourceTank(i);
FGTank* Tank = Tanks[TankId];
unsigned int TankPriority = Tank->GetPriority();
if (TankPriority != 0) {
switch(Tank->GetType()) {
case FGTank::ttFUEL:
if ((Tank->GetContents() > 0.0) && Tank->GetSelected() && (TankPriority == CurrentFuelTankPriority)) {
TanksWithFuel++;
Starved = false;
FeedListFuel.push_back(TankId);
}
break;
case FGTank::ttOXIDIZER:
// Skip this here (done below)
break;
}
}
}
if (TanksWithFuel == 0) CurrentFuelTankPriority++; // No tanks at this priority, try next priority
}
// Process Oxidizer tanks, if any
if (engine->GetType() == FGEngine::etRocket) {
while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
unsigned int TankId = engine->GetSourceTank(i);
FGTank* Tank = Tanks[TankId];
unsigned int TankPriority = Tank->GetPriority();
if (TankPriority != 0) {
switch(Tank->GetType()) {
case FGTank::ttFUEL:
// Skip this here (done above)
break;
case FGTank::ttOXIDIZER:
// hasOxTanks = true;
if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
TanksWithOxidizer++;
if (TanksWithFuel > 0) Starved = false;
FeedListOxi.push_back(TankId);
}
break;
}
}
}
if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
}
}
engine->SetStarved(Starved); // Tanks can be refilled, so be sure to reset engine Starved flag here.
// No fuel or fuel/oxidizer found at any priority!
if (Starved) return;
double FuelToBurn = engine->CalcFuelNeed(); // How much fuel does this engine need?
double FuelNeededPerTank = FuelToBurn / TanksWithFuel; // Determine fuel needed per tank.
for (unsigned int i=0; i<FeedListFuel.size(); i++) {
Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank);
}
if (engine->GetType() == FGEngine::etRocket) {
double OxidizerToBurn = engine->CalcOxidizerNeed(); // How much fuel does this engine need?
double OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer; // Determine fuel needed per tank.
for (unsigned int i=0; i<FeedListOxi.size(); i++) {
Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank);
}
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGPropulsion::GetSteadyState(void)
@ -201,28 +301,23 @@ bool FGPropulsion::GetSteadyState(void)
FDMExec->SetTrimStatus(true);
for (unsigned int i=0; i<numEngines; i++) {
// cout << " Finding steady state for engine " << i << endl;
steady=false;
steady_count=0;
j=0;
while (!steady && j < 6000) {
Engines[i]->Calculate();
lastThrust = currentThrust;
currentThrust = Engines[i]->GetThruster()->GetThrust();
currentThrust = Engines[i]->GetThrust();
if (fabs(lastThrust-currentThrust) < 0.0001) {
steady_count++;
if (steady_count > 120) {
steady=true;
// cout << " Steady state found at thrust: " << currentThrust << " lbs." << endl;
}
} else {
steady_count=0;
}
j++;
}
// if (j >= 6000) {
// cout << " Could not find a steady state for this engine." << endl;
// }
vForces += Engines[i]->GetBodyForces(); // sum body frame forces
vMoments += Engines[i]->GetMoments(); // sum body frame moments
}
@ -244,8 +339,10 @@ void FGPropulsion::InitRunning(int n)
if (n >= (int)GetNumEngines() ) {
throw(string("Tried to initialize a non-existent engine!"));
}
FDMExec->GetFCS()->SetThrottleCmd(n,1);
FDMExec->GetFCS()->SetMixtureCmd(n,1);
in.ThrottleCmd[n] = in.ThrottlePos[n] = 1; // Set the throttle command and position
in.MixtureCmd[n] = in.MixturePos[n] = 1; // Set the mixture command and position
GetEngine(n)->InitRunning();
GetSteadyState();
@ -255,14 +352,14 @@ void FGPropulsion::InitRunning(int n)
} else if (n < 0) { // -1 refers to "All Engines"
for (unsigned int i=0; i<GetNumEngines(); i++) {
FDMExec->GetFCS()->SetThrottleCmd(i,1);
FDMExec->GetFCS()->SetMixtureCmd(i,1);
in.ThrottleCmd[i] = in.ThrottlePos[i] = 1; // Set the throttle command and position
in.MixtureCmd[i] = in.MixturePos[i] = 1; // Set the mixture command and position
GetEngine(i)->InitRunning();
}
GetSteadyState();
InitializedEngines = -1;
HasInitializedEngines = true;
}
}
@ -301,6 +398,11 @@ bool FGPropulsion::Load(Element* el)
}
engine_filename = FindEngineFullPathname(engine_filename);
if (engine_filename.empty()) {
// error message already printed by FindEngineFullPathname()
return false;
}
document = LoadXMLDocument(engine_filename);
document->SetParent(engine_element);
@ -309,23 +411,23 @@ bool FGPropulsion::Load(Element* el)
if (type == "piston_engine") {
HavePistonEngine = true;
if (!IsBound) bind();
Engines.push_back(new FGPiston(FDMExec, document, numEngines));
Engines.push_back(new FGPiston(FDMExec, document, numEngines, in));
} else if (type == "turbine_engine") {
HaveTurbineEngine = true;
if (!IsBound) bind();
Engines.push_back(new FGTurbine(FDMExec, document, numEngines));
Engines.push_back(new FGTurbine(FDMExec, document, numEngines, in));
} else if (type == "turboprop_engine") {
HaveTurboPropEngine = true;
if (!IsBound) bind();
Engines.push_back(new FGTurboProp(FDMExec, document, numEngines));
Engines.push_back(new FGTurboProp(FDMExec, document, numEngines, in));
} else if (type == "rocket_engine") {
HaveRocketEngine = true;
if (!IsBound) bind();
Engines.push_back(new FGRocket(FDMExec, document, numEngines));
Engines.push_back(new FGRocket(FDMExec, document, numEngines, in));
} else if (type == "electric_engine") {
HaveElectricEngine = true;
if (!IsBound) bind();
Engines.push_back(new FGElectric(FDMExec, document, numEngines));
Engines.push_back(new FGElectric(FDMExec, document, numEngines, in));
} else {
cerr << "Unknown engine type: " << type << endl;
exit(-5);
@ -335,9 +437,6 @@ bool FGPropulsion::Load(Element* el)
return false;
}
FDMExec->GetFCS()->AddThrottle();
ThrottleAdded = true;
numEngines++;
engine_element = el->FindNextElement("engine");
@ -345,7 +444,6 @@ bool FGPropulsion::Load(Element* el)
}
CalculateTankInertias();
if (!ThrottleAdded) FDMExec->GetFCS()->AddThrottle(); // need to have at least one throttle
// Process fuel dump rate
if (el->FindElement("dump-rate"))
@ -459,6 +557,34 @@ string FGPropulsion::GetPropulsionValues(const string& delimiter) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPropulsion::GetPropulsionTankReport()
{
string out="";
stringstream outstream;
for (unsigned int i=0; i<numTanks; i++)
{
FGTank* tank = Tanks[i];
string tankname="";
if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
tankname = "Solid Fuel";
} else if (tank->GetType() == FGTank::ttFUEL) {
tankname = "Fuel";
} else if (tank->GetType() == FGTank::ttOXIDIZER) {
tankname = "Oxidizer";
} else {
tankname = "(Unknown tank type)";
}
outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
<< right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
<< setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
<< setw(12) << "*" << setw(12) << "*"
<< setw(12) << "*" << endl;
}
return outstream.str();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3& FGPropulsion::GetTanksMoment(void)
{
vXYZtank_arm.InitMatrix();
@ -633,7 +759,7 @@ void FGPropulsion::DumpFuel(double time_slice)
void FGPropulsion::SetFuelFreeze(bool f)
{
fuel_freeze = f;
FuelFreeze = f;
for (unsigned int i=0; i<numEngines; i++) {
Engines[i]->SetFuelFreeze(f);
}

View file

@ -42,6 +42,7 @@ INCLUDES
#include <iosfwd>
#include "FGModel.h"
#include "propulsion/FGEngine.h"
#include "math/FGMatrix33.h"
#include "input_output/FGXMLFileRead.h"
@ -49,7 +50,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_PROPULSION "$Id: FGPropulsion.h,v 1.27 2011/05/20 03:18:36 jberndt Exp $"
#define ID_PROPULSION "$Id: FGPropulsion.h,v 1.30 2011/08/03 03:21:06 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -91,7 +92,7 @@ CLASS DOCUMENTATION
@endcode
@author Jon S. Berndt
@version $Id: FGPropulsion.h,v 1.27 2011/05/20 03:18:36 jberndt Exp $
@version $Id: FGPropulsion.h,v 1.30 2011/08/03 03:21:06 jberndt Exp $
@see
FGEngine
FGTank
@ -164,6 +165,7 @@ public:
std::string GetPropulsionStrings(const std::string& delimiter) const;
std::string GetPropulsionValues(const std::string& delimiter) const;
std::string GetPropulsionTankReport();
const FGColumnVector3& GetForces(void) const {return vForces; }
double GetForces(int n) const { return vForces(n);}
@ -184,7 +186,7 @@ public:
std::ifstream* FindEngineFile(const std::string& filename);
std::string FindEngineFullPathname(const std::string& engine_filename);
inline int GetActiveEngine(void) const {return ActiveEngine;}
inline bool GetFuelFreeze(void) {return fuel_freeze;}
inline bool GetFuelFreeze(void) {return FuelFreeze;}
double GetTotalFuelQuantity(void) const {return TotalFuelQuantity;}
void SetMagnetos(int setting);
@ -194,6 +196,8 @@ public:
void SetFuelFreeze(bool f);
FGMatrix33& CalculateTankInertias(void);
struct FGEngine::Inputs in;
private:
std::vector <FGEngine*> Engines;
std::vector <FGTank*> Tanks;
@ -211,7 +215,7 @@ private:
FGMatrix33 tankJ;
bool refuel;
bool dump;
bool fuel_freeze;
bool FuelFreeze;
double TotalFuelQuantity;
double DumpRate;
bool IsBound;
@ -220,6 +224,7 @@ private:
bool HaveTurboPropEngine;
bool HaveRocketEngine;
bool HaveElectricEngine;
void ConsumeFuel(FGEngine* engine);
int InitializedEngines;
bool HasInitializedEngines;

View file

@ -7,12 +7,12 @@ libModels_a_SOURCES = FGAerodynamics.cpp FGAircraft.cpp FGAtmosphere.cpp \
FGInertial.cpp FGBuoyantForces.cpp FGExternalForce.cpp \
FGLGear.cpp FGMassBalance.cpp FGModel.cpp FGOutput.cpp \
FGPropagate.cpp FGPropulsion.cpp FGInput.cpp \
FGExternalReactions.cpp FGGasCell.cpp
FGExternalReactions.cpp FGGasCell.cpp FGAccelerations.cpp
noinst_HEADERS = FGAerodynamics.h FGAircraft.h FGAtmosphere.h FGAuxiliary.h \
FGFCS.h FGGroundReactions.h FGInertial.h FGLGear.h \
FGMassBalance.h FGBuoyantForces.h FGExternalForce.h \
FGModel.h FGOutput.h FGPropagate.h FGPropulsion.h FGInput.h \
FGExternalReactions.h FGGasCell.h
FGExternalReactions.h FGGasCell.h FGAccelerations.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim

View file

@ -66,7 +66,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGMSIS.cpp,v 1.17 2011/05/20 03:18:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGMSIS.cpp,v 1.18 2011/06/21 13:54:40 jberndt Exp $";
static const char *IdHdr = ID_MSIS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -140,16 +140,16 @@ bool MSIS::InitModel(void)
input.f107 = 150.0;
input.ap = 4.0;
UseInternal();
// UseInternal();
SLtemperature = intTemperature = 518.0;
SLpressure = intPressure = 2116.7;
SLdensity = intDensity = 0.002378;
SLsoundspeed = sqrt(2403.0832 * SLtemperature);
rSLtemperature = 1.0/intTemperature;
rSLpressure = 1.0/intPressure;
rSLdensity = 1.0/intDensity;
rSLsoundspeed = 1.0/SLsoundspeed;
// SLtemperature = intTemperature = 518.0;
// SLpressure = intPressure = 2116.7;
// SLdensity = intDensity = 0.002378;
// SLsoundspeed = sqrt(2403.0832 * SLtemperature);
// rSLtemperature = 1.0/intTemperature;
// rSLpressure = 1.0/intPressure;
// rSLdensity = 1.0/intDensity;
// rSLsoundspeed = 1.0/SLsoundspeed;
return true;
}
@ -163,10 +163,10 @@ bool MSIS::Run(bool Holding)
RunPreFunctions();
h = FDMExec->GetPropagate()->GetAltitudeASL();
double h = FDMExec->GetPropagate()->GetAltitudeASL();
//do temp, pressure, and density first
if (!useExternal) {
// if (!useExternal) {
// get sea-level values
Calculate(FDMExec->GetAuxiliary()->GetDayOfYear(),
FDMExec->GetAuxiliary()->GetSecondsInDay(),
@ -188,14 +188,12 @@ bool MSIS::Run(bool Holding)
h,
FDMExec->GetPropagate()->GetLocation().GetLatitudeDeg(),
FDMExec->GetPropagate()->GetLocation().GetLongitudeDeg());
intTemperature = output.t[1] * 1.8;
intDensity = output.d[5] * 1.940321;
intPressure = 1716.488 * intDensity * intTemperature;
// intTemperature = output.t[1] * 1.8;
// intDensity = output.d[5] * 1.940321;
// intPressure = 1716.488 * intDensity * intTemperature;
//cout << "T=" << intTemperature << " D=" << intDensity << " P=";
//cout << intPressure << " a=" << soundspeed << endl;
}
CalculateDerived();
// }
RunPostFunctions();
@ -1660,16 +1658,6 @@ void MSIS::Debug(int from)
if (debug_lvl & 16) { // Sanity checking
}
if (debug_lvl & 32) { // Turbulence
if (first_pass && from == 2) {
cout << "vTurbulenceNED(X), vTurbulenceNED(Y), vTurbulenceNED(Z), "
<< "vTurbulenceGrad(X), vTurbulenceGrad(Y), vTurbulenceGrad(Z), "
<< "vDirection(X), vDirection(Y), vDirection(Z), "
<< "Magnitude, "
<< "vTurbPQR(P), vTurbPQR(Q), vTurbPQR(R), " << endl;
}
if (from == 2) {
cout << vTurbulenceNED << ", " << vTurbulenceGrad << ", " << vDirection << ", " << Magnitude << ", " << vTurbPQR << endl;
}
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor

19
src/FDM/JSBSim/models/atmosphere/FGMars.cpp Normal file → Executable file
View file

@ -48,7 +48,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGMars.cpp,v 1.10 2010/02/25 05:21:36 jberndt Exp $";
static const char *IdSrc = "$Id: FGMars.cpp,v 1.11 2011/06/21 13:54:40 jberndt Exp $";
static const char *IdHdr = ID_MARS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -74,12 +74,12 @@ void FGMars::Calculate(double altitude)
// LIMIT the temperatures so they do not descend below absolute zero.
if (altitude < 22960.0) {
intTemperature = -25.68 - 0.000548*altitude; // Deg Fahrenheit
Temperature = -25.68 - 0.000548*altitude; // Deg Fahrenheit
} else {
intTemperature = -10.34 - 0.001217*altitude; // Deg Fahrenheit
Temperature = -10.34 - 0.001217*altitude; // Deg Fahrenheit
}
intPressure = 14.62*exp(-0.00003*altitude); // psf - 14.62 psf =~ 7 millibars
intDensity = intPressure/(Reng*intTemperature); // slugs/ft^3 (needs deg R. as input
Pressure = 14.62*exp(-0.00003*altitude); // psf - 14.62 psf =~ 7 millibars
Density = Pressure/(Reng*Temperature); // slugs/ft^3 (needs deg R. as input
//cout << "Atmosphere: h=" << altitude << " rho= " << intDensity << endl;
}
@ -122,15 +122,6 @@ void FGMars::Debug(int from)
if (debug_lvl & 16) { // Sanity checking
}
if (debug_lvl & 32) { // Turbulence
if (first_pass && from == 2) {
cout << "vTurbulenceNED(X), vTurbulenceNED(Y), vTurbulenceNED(Z), "
<< "vTurbulenceGrad(X), vTurbulenceGrad(Y), vTurbulenceGrad(Z), "
<< "vDirection(X), vDirection(Y), vDirection(Z), "
<< "Magnitude, "
<< "vTurbPQR(P), vTurbPQR(Q), vTurbPQR(R), " << endl;
} else if (from == 2) {
cout << vTurbulenceNED << ", " << vTurbulenceGrad << ", " << vDirection << ", " << Magnitude << ", " << vTurbPQR << endl;
}
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor

View file

@ -50,20 +50,16 @@ INCLUDES
namespace JSBSim {
static const char *IdSrc = "$Id: FGStandardAtmosphere.cpp,v 1.9 2011/06/13 12:06:29 jberndt Exp $";
static const char *IdSrc = "$Id: FGStandardAtmosphere.cpp,v 1.18 2011/08/17 23:56:01 jberndt Exp $";
static const char *IdHdr = ID_STANDARDATMOSPHERE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGStandardAtmosphere::FGStandardAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex),
FGStandardAtmosphere::FGStandardAtmosphere(FGFDMExec* fdmex) : FGAtmosphere(fdmex),
TemperatureDeltaGradient(0.0),
TemperatureBias(0.0),
PressureAltitude(0.0), // ft
DensityAltitude(0.0), // ft
SutherlandConstant(198.72), // deg Rankine
Beta(2.269690E-08) // slug/(sec ft R^0.5)
TemperatureBias(0.0)
{
Name = "FGStandardAtmosphere";
@ -102,6 +98,7 @@ FGStandardAtmosphere::FGStandardAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex),
FGStandardAtmosphere::~FGStandardAtmosphere()
{
delete StdAtmosTemperatureTable;
LapseRateVector.clear();
Debug(1);
}
@ -131,38 +128,6 @@ bool FGStandardAtmosphere::InitModel(void)
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGStandardAtmosphere::Run(bool Holding)
{
if (FGModel::Run(Holding)) return true;
if (Holding) return false;
RunPreFunctions();
double altitude = FDMExec->GetPropagate()->GetAltitudeASL();
Calculate(altitude);
RunPostFunctions();
Debug(2);
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGStandardAtmosphere::Calculate(double altitude)
{
Temperature = GetTemperature(altitude);
Pressure = GetPressure(altitude);
Density = Pressure/(Reng*Temperature);
Soundspeed = sqrt(SHRatio*Reng*(Temperature));
Viscosity = Beta * pow(Temperature, 1.5) / (SutherlandConstant + Temperature);
KinematicViscosity = Viscosity / Density;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the actual pressure as modeled at a specified altitude
// These calculations are from equations 33a and 33b in the U.S. Standard Atmosphere
@ -173,6 +138,7 @@ double FGStandardAtmosphere::GetPressure(double altitude) const
unsigned int b=0;
double pressure = 0.0;
double Lmb, Exp, Tmb, deltaH, factor;
double numRows = StdAtmosTemperatureTable->GetNumRows();
// Iterate through the altitudes to find the current Base Altitude
// in the table. That is, if the current altitude (the argument passed in)
@ -180,9 +146,8 @@ double FGStandardAtmosphere::GetPressure(double altitude) const
// passed-in altitude is 40000 ft, the base altitude is 36151.6 ft (and
// the index "b" is 2 - the second entry in the table).
double testAlt = (*StdAtmosTemperatureTable)(b+1,0);
while (altitude >= testAlt) {
while ((altitude >= testAlt) && (b <= numRows-2)) {
b++;
if (b+1 > StdAtmosTemperatureTable->GetNumRows()) break;
testAlt = (*StdAtmosTemperatureTable)(b+1,0);
}
if (b>0) b--;
@ -205,7 +170,7 @@ double FGStandardAtmosphere::GetPressure(double altitude) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGStandardAtmosphere::SetSeaLevelPressure(double pressure, ePressure unit)
void FGStandardAtmosphere::SetPressureSL(double pressure, ePressure unit)
{
double press = ConvertToPSF(pressure, unit);
@ -285,30 +250,21 @@ double FGStandardAtmosphere::GetStdPressure100K(double altitude) const
// Limit this equation to input altitudes of 100000 ft.
if (altitude > 100000.0) altitude = 100000.0;
double alt[6];
double coef[6] = { 2116.22,
-7.583514352598E-02,
1.045494405501E-06,
-5.881341527124E-12,
3.482031690718E-18,
5.683922549284E-23 };
double alt[5];
const double coef[5] = { 2116.217,
-7.648932746E-2,
1.0925498604E-6,
-7.1135726027E-12,
1.7470331356E-17 };
alt[0] = 1;
for (int pwr=1; pwr<=5; pwr++) alt[pwr] = alt[pwr-1]*altitude;
for (int pwr=1; pwr<=4; pwr++) alt[pwr] = alt[pwr-1]*altitude;
double press = 0.0;
for (int ctr=0; ctr<=5; ctr++) press += coef[ctr]*alt[ctr];
for (int ctr=0; ctr<=4; ctr++) press += coef[ctr]*alt[ctr];
return press;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the modeled density at a specified altitude
double FGStandardAtmosphere::GetDensity(double altitude) const
{
return GetPressure(altitude)/(Reng * GetTemperature(altitude));
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Get the standard density at a specified altitude
@ -319,9 +275,23 @@ double FGStandardAtmosphere::GetStdDensity(double altitude) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGStandardAtmosphere::SetTemperatureBias(double t, eTemperature unit)
void FGStandardAtmosphere::SetTemperature(double t, double h, eTemperature unit)
{
TemperatureBias = ConvertToRankine(t, unit);
double targetSLtemp = ConvertToRankine(t, unit);
TemperatureBias = 0.0;
TemperatureBias = targetSLtemp - GetTemperature(h);
CalculatePressureBreakpoints();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGStandardAtmosphere::SetTemperatureBias(eTemperature unit, double t)
{
if (unit == eCelsius || unit == eKelvin)
t *= 1.80; // If temp delta "t" is given in metric, scale up to English
TemperatureBias = t;
CalculatePressureBreakpoints();
}
@ -331,18 +301,15 @@ void FGStandardAtmosphere::SetTemperatureBias(double t, eTemperature unit)
// Internally, the Rankine scale is used for calculations, so any temperature
// supplied must be converted to that unit.
void FGStandardAtmosphere::SetSLTemperature(double t, eTemperature unit)
void FGStandardAtmosphere::SetTemperatureSL(double t, eTemperature unit)
{
double targetSLtemp = ConvertToRankine(t, unit);
TemperatureBias = targetSLtemp - GetStdTemperatureSL();
CalculatePressureBreakpoints();
SetTemperature(t, 0.0, unit);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Sets a Sea Level temperature delta that is ramped out by 86 km (282,152 ft).
void FGStandardAtmosphere::SetSLTemperatureGradedDelta(double deltemp, eTemperature unit)
void FGStandardAtmosphere::SetSLTemperatureGradedDelta(eTemperature unit, double deltemp)
{
SetTemperatureGradedDelta(deltemp, 0.0, unit);
}
@ -356,12 +323,9 @@ void FGStandardAtmosphere::SetSLTemperatureGradedDelta(double deltemp, eTemperat
void FGStandardAtmosphere::SetTemperatureGradedDelta(double deltemp, double h, eTemperature unit)
{
switch(unit) {
case eCelsius:
case eKelvin:
deltemp *= 9.0/5.0; // If temp delta is given in metric, scale up to English
break;
}
if (unit == eCelsius || unit == eKelvin)
deltemp *= 1.80; // If temp delta "t" is given in metric, scale up to English
TemperatureDeltaGradient = deltemp/(GradientFadeoutAltitude - h);
CalculateLapseRates();
CalculatePressureBreakpoints();
@ -376,7 +340,7 @@ void FGStandardAtmosphere::SetTemperatureGradedDelta(double deltemp, double h, e
for (int i=0; i<280000; i+=1000) {
Calculate(i);
std::cout << std::setw(12) << std::setprecision(2) << i
<< " " << std::setw(9) << std::setprecision(2) << Temperature-459.67
<< " " << std::setw(9) << std::setprecision(2) << Temperature - 459.67
<< " " << std::setw(13) << std::setprecision(4) << Pressure
<< " " << std::setw(18) << std::setprecision(8) << Density
<< std::endl;
@ -447,75 +411,16 @@ void FGStandardAtmosphere::ResetSLPressure()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGStandardAtmosphere::ConvertToRankine(double t, eTemperature unit) const
{
double targetTemp=0; // in degrees Rankine
switch(unit) {
case eFahrenheit:
targetTemp = t + 459.67;
break;
case eCelsius:
targetTemp = t*9.0/5.0 + 32.0 + 459.67;
break;
case eRankine:
targetTemp = t;
break;
case eKelvin:
targetTemp = t*9.0/5.0;
}
return targetTemp;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGStandardAtmosphere::ConvertToPSF(double p, ePressure unit) const
{
double targetPressure=0; // Pressure in PSF
switch(unit) {
case ePSF:
targetPressure = p;
break;
case eMillibars:
targetPressure = p*2.08854342;
break;
case ePascals:
targetPressure = p*0.0208854342;
break;
case eInchesHg:
targetPressure = p*70.7180803;
break;
default:
throw("Undefined pressure unit given");
}
return targetPressure;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGStandardAtmosphere::bind(void)
{
typedef double (FGStandardAtmosphere::*PMFi)(int) const;
typedef void (FGStandardAtmosphere::*PMF)(int, double);
PropertyManager->Tie("stdatmosphere/T-R", this, &FGStandardAtmosphere::GetTemperature);
PropertyManager->Tie("stdatmosphere/rho-slugs_ft3", this, &FGStandardAtmosphere::GetDensity);
PropertyManager->Tie("stdatmosphere/P-psf", this, &FGStandardAtmosphere::GetPressure);
PropertyManager->Tie("stdatmosphere/a-fps", this, &FGStandardAtmosphere::GetSoundSpeed);
PropertyManager->Tie("stdatmosphere/T-sl-R", this, &FGStandardAtmosphere::GetTemperatureSL);
PropertyManager->Tie("stdatmosphere/rho-sl-slugs_ft3", this, &FGStandardAtmosphere::GetDensitySL);
PropertyManager->Tie("stdatmosphere/P-sl-psf", this, &FGStandardAtmosphere::GetPressureSL);
PropertyManager->Tie("stdatmosphere/a-sl-fps", this, &FGStandardAtmosphere::GetSoundSpeedSL);
PropertyManager->Tie("stdatmosphere/theta", this, &FGStandardAtmosphere::GetTemperatureRatio);
PropertyManager->Tie("stdatmosphere/sigma", this, &FGStandardAtmosphere::GetDensityRatio);
PropertyManager->Tie("stdatmosphere/delta", this, &FGStandardAtmosphere::GetPressureRatio);
PropertyManager->Tie("stdatmosphere/a-ratio", this, &FGStandardAtmosphere::GetSoundSpeedRatio);
PropertyManager->Tie("stdatmosphere/delta-T", this, eRankine,
PropertyManager->Tie("atmosphere/delta-T", this, eRankine,
(PMFi)&FGStandardAtmosphere::GetTemperatureBias,
(PMF)&FGStandardAtmosphere::SetTemperatureBias);
// PropertyManager->Tie("atmosphere/density-altitude", this, &FGStandardAtmosphere::GetDensityAltitude);
PropertyManager->Tie("atmosphere/SL-graded-delta-T", this, eRankine,
(PMFi)&FGStandardAtmosphere::GetTemperatureDeltaGradient,
(PMF)&FGStandardAtmosphere::SetSLTemperatureGradedDelta);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -39,14 +39,14 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include "models/FGModel.h"
#include "math/FGTable.h"
#include "models/FGAtmosphere.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_STANDARDATMOSPHERE "$Id: FGStandardAtmosphere.h,v 1.9 2011/06/13 12:06:21 jberndt Exp $"
#define ID_STANDARDATMOSPHERE "$Id: FGStandardAtmosphere.h,v 1.15 2011/08/17 23:56:01 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -88,51 +88,26 @@ and/or the sea level standard pressure, so that the entire profile will be
consistently and accurately calculated.
<h2> Properties </h2>
@property atmosphere/T-R The current modeled temperature in degrees Rankine.
@property atmosphere/rho-slugs_ft3
@property atmosphere/P-psf
@property atmosphere/a-fps
@property atmosphere/T-sl-R
@property atmosphere/rho-sl-slugs_ft3
@property atmosphere/P-sl-psf
@property atmosphere/a-sl-fps
@property atmosphere/theta
@property atmosphere/sigma
@property atmosphere/delta
@property atmosphere/a-ratio
@property atmosphere/delta-T
@property atmosphere/T-sl-dev-F
@author Jon Berndt
@see "U.S. Standard Atmosphere, 1976", NASA TM-X-74335
@version $Id: FGStandardAtmosphere.h,v 1.9 2011/06/13 12:06:21 jberndt Exp $
@version $Id: FGStandardAtmosphere.h,v 1.15 2011/08/17 23:56:01 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGStandardAtmosphere : public FGModel {
class FGStandardAtmosphere : public FGAtmosphere {
public:
/// Enums for specifying temperature units.
enum eTemperature {eNoTempUnit=0, eFahrenheit, eCelsius, eRankine, eKelvin};
/// Enums for specifying pressure units.
enum ePressure {eNoPressUnit=0, ePSF, eMillibars, ePascals, eInchesHg};
/// Constructor
FGStandardAtmosphere(FGFDMExec*);
/// Destructor
~FGStandardAtmosphere();
/** Runs the standard atmosphere forces model; called by the Executive.
Can pass in a value indicating if the executive is directing the simulation to Hold.
@param Holding if true, the executive has been directed to hold the sim from
advancing time. Some models may ignore this flag, such as the Input
model, which may need to be active to listen on a socket for the
"Resume" command to be given.
@return false if no error */
bool Run(bool Holding);
virtual ~FGStandardAtmosphere();
bool InitModel(void);
// *************************************************************************
@ -145,19 +120,11 @@ public:
/// at sea level can also be retrieved. These two temperatures DO include the
/// effects of an optionally user-supplied bias or delta gradient.
// @{
/// Returns the actual, modeled temperature at the current altitude in degrees Rankine.
/// @return Modeled temperature in degrees Rankine.
virtual double GetTemperature() const {return Temperature;}
/// Returns the actual modeled temperature in degrees Rankine at a specified altitude.
/// @param altitude The altitude above sea level (ASL) in feet.
/// @return Modeled temperature in degrees Rankine at the specified altitude.
virtual double GetTemperature(double altitude) const;
/// Returns the actual, modeled sea level temperature in degrees Rankine.
/// @return The modeled temperature in degrees Rankine at sea level.
virtual double GetTemperatureSL() const { return GetTemperature(0.0); }
/// Returns the standard temperature in degrees Rankine at a specified altitude.
/// @param altitude The altitude in feet above sea level (ASL) to get the temperature at.
/// @return The STANDARD temperature in degrees Rankine at the specified altitude.
@ -167,14 +134,6 @@ public:
/// @return The STANDARD temperature at sea level in degrees Rankine.
virtual double GetStdTemperatureSL() const { return GetStdTemperature(0.0); }
/// Returns the ratio of the at-current-altitude temperature as modeled
/// over the standard sea level value.
virtual double GetTemperatureRatio() const { return GetTemperature()*rSLtemperature; }
/// Returns the ratio of the temperature as modeled at the supplied altitude
/// over the standard sea level value.
virtual double GetTemperatureRatio(double h) const { return GetTemperature(h)*rSLtemperature; }
/// Returns the ratio of the standard temperature at the supplied altitude
/// over the standard sea level temperature.
virtual double GetStdTemperatureRatio(double h) const { return GetStdTemperature(h)*rSLtemperature; }
@ -194,17 +153,20 @@ public:
/// this function.
/// @param t the temperature value in the unit provided.
/// @param unit the unit of the temperature.
virtual void SetSLTemperature(double t, eTemperature unit=eFahrenheit);
virtual void SetTemperatureSL(double t, eTemperature unit=eFahrenheit);
/// Sets the temperature at the supplied altitude, if it is to be different
/// than the standard temperature.
/// This function will calculate a bias - a difference - from the standard
/// atmosphere temperature at the supplied altitude and will apply that
/// calculated bias to the entire temperature profile.
/// calculated bias to the entire temperature profile. If a graded delta is
/// present, that will be included in the calculation - that is, regardless
/// of any graded delta present, a temperature bias will be determined so that
/// the temperature requested in this function call will be reached.
/// @param t The temperature value in the unit provided.
/// @param h The altitude in feet above sea level.
/// @param unit The unit of the temperature.
virtual void SetTemperature(double t, double h, eTemperature unit=eFahrenheit) {};
virtual void SetTemperature(double t, double h, eTemperature unit=eFahrenheit);
/// Sets the temperature bias to be added to the standard temperature at all altitudes.
/// This function sets the bias - the difference - from the standard
@ -214,7 +176,7 @@ public:
/// this function with a calculated bias.
/// @param t the temperature value in the unit provided.
/// @param unit the unit of the temperature.
virtual void SetTemperatureBias(double t, eTemperature unit=eFahrenheit);
virtual void SetTemperatureBias(eTemperature unit, double t);
/// Sets a Sea Level temperature delta that is ramped out by 86 km.
/// The value of the delta is used to calculate a delta gradient that is
@ -229,7 +191,7 @@ public:
/// temperature profile as desired.
/// @param t the sea level temperature delta value in the unit provided.
/// @param unit the unit of the temperature.
virtual void SetSLTemperatureGradedDelta(double t, eTemperature unit=eFahrenheit);
virtual void SetSLTemperatureGradedDelta(eTemperature unit, double t);
/// Sets the temperature delta value at the supplied altitude/elevation above
/// sea level, to be added to the standard temperature and ramped out by
@ -250,9 +212,6 @@ public:
// *************************************************************************
/// @name Pressure access functions.
//@{
/// Returns the pressure in psf.
virtual double GetPressure(void) const {return Pressure;}
/// Returns the pressure at a specified altitude in psf.
virtual double GetPressure(double altitude) const;
@ -262,19 +221,13 @@ public:
/// Returns the standard pressure at the specified altitude.
virtual double GetStdPressure(double altitude) const;
/// Returns the sea level pressure in psf.
virtual double GetPressureSL(void) const { return SLpressure; }
/// Returns the ratio of at-altitude pressure over the sea level value.
virtual double GetPressureRatio(void) const { return Pressure*rSLpressure; }
/** Sets the sea level pressure for modeling an off-standard pressure
profile. This could be useful in the case where the pressure at an
airfield is known or set for a particular simulation run.
@param pressure The pressure in the units specified (PSF by default).
@param unit the unit of measure that the specified pressure is
supplied in.*/
virtual void SetSeaLevelPressure(double pressure, ePressure unit=ePSF);
virtual void SetPressureSL(double pressure, ePressure unit=ePSF);
/** Resets the sea level to the Standard sea level pressure, and recalculates
dependent parameters so that the pressure calculations are standard. */
@ -284,60 +237,15 @@ public:
// *************************************************************************
/// @name Density access functions.
//@{
/** Returns the density in slugs/ft^3.
This function may only be used if Run() is called first. */
virtual double GetDensity(void) const {return Density;}
/** Returns the density in slugs/ft^3 at a given altitude in ft. */
virtual double GetDensity(double altitude) const;
/// Returns the standard density at a specified altitude
virtual double GetStdDensity(double altitude) const;
/// Returns the sea level density in slugs/ft^3
virtual double GetDensitySL(void) const { return SLdensity; }
/// Returns the ratio of at-altitude density over the sea level value.
virtual double GetDensityRatio(void) const { return Density*rSLdensity; }
//@}
// *************************************************************************
/// @name Speed of sound access functions.
//@{
/// Returns the speed of sound in ft/sec.
virtual double GetSoundSpeed(void) const {return Soundspeed;}
/// Returns the sea level speed of sound in ft/sec.
virtual double GetSoundSpeedSL(void) const { return SLsoundspeed; }
/// Returns the ratio of at-altitude sound speed over the sea level value.
virtual double GetSoundSpeedRatio(void) const { return Soundspeed*rSLsoundspeed; }
//@}
// *************************************************************************
/// @name Viscosity access functions.
//@{
/// Returns the absolute viscosity.
virtual double GetAbsoluteViscosity(void) const {return Viscosity;}
/// Returns the kinematic viscosity.
virtual double GetKinematicViscosity(void) const {return KinematicViscosity;}
//@}
/* /// Gets the density altitude in feet */
// virtual double GetDensityAltitude(void) const { return density_altitude; }
/// Prints the U.S. Standard Atmosphere table.
virtual void PrintStandardAtmosphereTable();
protected:
double StdSLtemperature, StdSLdensity, StdSLpressure, StdSLsoundspeed; // Standard sea level conditions
double SLtemperature, SLdensity, SLpressure, SLsoundspeed; // Sea level conditions
double Temperature, Density, Pressure, Soundspeed; // Current actual conditions at altitude
double rSLtemperature, rSLdensity, rSLpressure, rSLsoundspeed; // Reciprocal of sea level conditions
double PressureAltitude;
double DensityAltitude;
double TemperatureBias;
double TemperatureDeltaGradient;
@ -347,12 +255,6 @@ protected:
std::vector<double> LapseRateVector;
std::vector<double> PressureBreakpointVector;
const double SutherlandConstant, Beta;
double Viscosity, KinematicViscosity;
/// Calculate the atmosphere for the given altitude, including effects of temperature deviation.
void Calculate(double altitude);
/// Recalculate the lapse rate vectors when the temperature profile is altered
/// in a way that would change the lapse rates, such as when a gradient is applied.
/// This function is also called to initialize the lapse rate vector.
@ -362,12 +264,6 @@ protected:
/// altitudes in the standard temperature table.
void CalculatePressureBreakpoints();
// Converts to Rankine from one of several unit systems.
virtual double ConvertToRankine(double t, eTemperature unit) const;
// Converts to PSF (pounds per square foot) from one of several unit systems.
virtual double ConvertToPSF(double t, ePressure unit=ePSF) const;
virtual void bind(void);
void Debug(int from);
};

View file

@ -0,0 +1,553 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Module: FGWinds.cpp
Author: Jon Berndt, Tony Peden, Andreas Gaeb
Date started: Extracted from FGAtmosphere, which originated in 1998
5/2011
Purpose: Models winds, gusts, turbulence, and other atmospheric disturbances
Called by: FGFDMExec
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
HISTORY
--------------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES, and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[1] Anderson, John D. "Introduction to Flight, Third Edition", McGraw-Hill,
1989, ISBN 0-07-001641-0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <cstdlib>
#include "FGWinds.h"
#include "FGFDMExec.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGWinds.cpp,v 1.4 2011/09/07 02:37:04 jberndt Exp $";
static const char *IdHdr = ID_WINDS;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// square a value, but preserve the original sign
static inline double square_signed (double value)
{
if (value < 0)
return value * value * -1;
else
return value * value;
}
/// simply square a value
static inline double sqr(double x) { return x*x; }
FGWinds::FGWinds(FGFDMExec* fdmex) : FGModel(fdmex)
{
Name = "FGWinds";
MagnitudedAccelDt = MagnitudeAccel = Magnitude = 0.0;
SetTurbType( ttMilspec );
TurbGain = 1.0;
TurbRate = 10.0;
Rhythmicity = 0.1;
spike = target_time = strength = 0.0;
wind_from_clockwise = 0.0;
psiw = 0.0;
vGustNED.InitMatrix();
vTurbulenceNED.InitMatrix();
// Milspec turbulence model
windspeed_at_20ft = 0.;
probability_of_exceedence_index = 0;
POE_Table = new FGTable(7,12);
// this is Figure 7 from p. 49 of MIL-F-8785C
// rows: probability of exceedance curve index, cols: altitude in ft
*POE_Table
<< 500.0 << 1750.0 << 3750.0 << 7500.0 << 15000.0 << 25000.0 << 35000.0 << 45000.0 << 55000.0 << 65000.0 << 75000.0 << 80000.0
<< 1 << 3.2 << 2.2 << 1.5 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 2 << 4.2 << 3.6 << 3.3 << 1.6 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 3 << 6.6 << 6.9 << 7.4 << 6.7 << 4.6 << 2.7 << 0.4 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
<< 4 << 8.6 << 9.6 << 10.6 << 10.1 << 8.0 << 6.6 << 5.0 << 4.2 << 2.7 << 0.0 << 0.0 << 0.0
<< 5 << 11.8 << 13.0 << 16.0 << 15.1 << 11.6 << 9.7 << 8.1 << 8.2 << 7.9 << 4.9 << 3.2 << 2.1
<< 6 << 15.6 << 17.6 << 23.0 << 23.6 << 22.1 << 20.0 << 16.0 << 15.1 << 12.1 << 7.9 << 6.2 << 5.1
<< 7 << 18.7 << 21.5 << 28.4 << 30.2 << 30.7 << 31.0 << 25.2 << 23.1 << 17.5 << 10.7 << 8.4 << 7.2;
bind();
Debug(0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGWinds::~FGWinds()
{
delete(POE_Table);
Debug(1);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGWinds::InitModel(void)
{
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGWinds::Run(bool Holding)
{
if (FGModel::Run(Holding)) return true;
if (Holding) return false;
RunPreFunctions();
if (turbType != ttNone) Turbulence(in.AltitudeASL);
if (oneMinusCosineGust.gustProfile.Running) CosineGust();
vTotalWindNED = vWindNED + vGustNED + vCosineGust + vTurbulenceNED;
// psiw (Wind heading) is the direction the wind is blowing towards
if (vWindNED(eX) != 0.0) psiw = atan2( vWindNED(eY), vWindNED(eX) );
if (psiw < 0) psiw += 2*M_PI;
RunPostFunctions();
Debug(2);
return false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// psi is the angle that the wind is blowing *towards*
void FGWinds::SetWindspeed(double speed)
{
if (vWindNED.Magnitude() == 0.0) {
psiw = 0.0;
vWindNED(eNorth) = speed;
} else {
vWindNED(eNorth) = speed * cos(psiw);
vWindNED(eEast) = speed * sin(psiw);
vWindNED(eDown) = 0.0;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGWinds::GetWindspeed(void) const
{
return vWindNED.Magnitude();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// psi is the angle that the wind is blowing *towards*
void FGWinds::SetWindPsi(double dir)
{
double mag = GetWindspeed();
psiw = dir;
SetWindspeed(mag);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGWinds::Turbulence(double h)
{
switch (turbType) {
case ttCulp: {
vTurbPQR(eP) = wind_from_clockwise;
if (TurbGain == 0.0) return;
// keep the inputs within allowable limts for this model
if (TurbGain < 0.0) TurbGain = 0.0;
if (TurbGain > 1.0) TurbGain = 1.0;
if (TurbRate < 0.0) TurbRate = 0.0;
if (TurbRate > 30.0) TurbRate = 30.0;
if (Rhythmicity < 0.0) Rhythmicity = 0.0;
if (Rhythmicity > 1.0) Rhythmicity = 1.0;
// generate a sine wave corresponding to turbulence rate in hertz
double time = FDMExec->GetSimTime();
double sinewave = sin( time * TurbRate * 6.283185307 );
double random = 0.0;
if (target_time == 0.0) {
strength = random = 1 - 2.0*(double(rand())/double(RAND_MAX));
target_time = time + 0.71 + (random * 0.5);
}
if (time > target_time) {
spike = 1.0;
target_time = 0.0;
}
// max vertical wind speed in fps, corresponds to TurbGain = 1.0
double max_vs = 40;
vTurbulenceNED(1) = vTurbulenceNED(2) = vTurbulenceNED(3) = 0.0;
double delta = strength * max_vs * TurbGain * (1-Rhythmicity) * spike;
// Vertical component of turbulence.
vTurbulenceNED(3) = sinewave * max_vs * TurbGain * Rhythmicity;
vTurbulenceNED(3)+= delta;
if (in.DistanceAGL/in.wingspan < 3.0)
vTurbulenceNED(3) *= in.DistanceAGL/in.wingspan * 0.3333;
// Yaw component of turbulence.
vTurbulenceNED(1) = sin( delta * 3.0 );
vTurbulenceNED(2) = cos( delta * 3.0 );
// Roll component of turbulence. Clockwise vortex causes left roll.
vTurbPQR(eP) += delta * 0.04;
spike = spike * 0.9;
break;
}
case ttMilspec:
case ttTustin: {
// an index of zero means turbulence is disabled
// airspeed occurs as divisor in the code below
if (probability_of_exceedence_index == 0 || in.V == 0) {
vTurbulenceNED(1) = vTurbulenceNED(2) = vTurbulenceNED(3) = 0.0;
vTurbPQR(1) = vTurbPQR(2) = vTurbPQR(3) = 0.0;
return;
}
// Turbulence model according to MIL-F-8785C (Flying Qualities of Piloted Aircraft)
double b_w = in.wingspan, L_u, L_w, sig_u, sig_w;
if (b_w == 0.) b_w = 30.;
// clip height functions at 10 ft
if (h <= 10.) h = 10;
// Scale lengths L and amplitudes sigma as function of height
if (h <= 1000) {
L_u = h/pow(0.177 + 0.000823*h, 1.2); // MIL-F-8785c, Fig. 10, p. 55
L_w = h;
sig_w = 0.1*windspeed_at_20ft;
sig_u = sig_w/pow(0.177 + 0.000823*h, 0.4); // MIL-F-8785c, Fig. 11, p. 56
} else if (h <= 2000) {
// linear interpolation between low altitude and high altitude models
L_u = L_w = 1000 + (h-1000.)/1000.*750.;
sig_u = sig_w = 0.1*windspeed_at_20ft
+ (h-1000.)/1000.*(POE_Table->GetValue(probability_of_exceedence_index, h) - 0.1*windspeed_at_20ft);
} else {
L_u = L_w = 1750.; // MIL-F-8785c, Sec. 3.7.2.1, p. 48
sig_u = sig_w = POE_Table->GetValue(probability_of_exceedence_index, h);
}
// keep values from last timesteps
// TODO maybe use deque?
static double
xi_u_km1 = 0, nu_u_km1 = 0,
xi_v_km1 = 0, xi_v_km2 = 0, nu_v_km1 = 0, nu_v_km2 = 0,
xi_w_km1 = 0, xi_w_km2 = 0, nu_w_km1 = 0, nu_w_km2 = 0,
xi_p_km1 = 0, nu_p_km1 = 0,
xi_q_km1 = 0, xi_r_km1 = 0;
double
T_V = in.totalDeltaT, // for compatibility of nomenclature
sig_p = 1.9/sqrt(L_w*b_w)*sig_w, // Yeager1998, eq. (8)
sig_q = sqrt(M_PI/2/L_w/b_w), // eq. (14)
sig_r = sqrt(2*M_PI/3/L_w/b_w), // eq. (17)
L_p = sqrt(L_w*b_w)/2.6, // eq. (10)
tau_u = L_u/in.V, // eq. (6)
tau_w = L_w/in.V, // eq. (3)
tau_p = L_p/in.V, // eq. (9)
tau_q = 4*b_w/M_PI/in.V, // eq. (13)
tau_r =3*b_w/M_PI/in.V, // eq. (17)
nu_u = GaussianRandomNumber(),
nu_v = GaussianRandomNumber(),
nu_w = GaussianRandomNumber(),
nu_p = GaussianRandomNumber(),
xi_u=0, xi_v=0, xi_w=0, xi_p=0, xi_q=0, xi_r=0;
// values of turbulence NED velocities
if (turbType == ttTustin) {
// the following is the Tustin formulation of Yeager's report
double
omega_w = in.V/L_w, // hidden in nomenclature p. 3
omega_v = in.V/L_u, // this is defined nowhere
C_BL = 1/tau_u/tan(T_V/2/tau_u), // eq. (19)
C_BLp = 1/tau_p/tan(T_V/2/tau_p), // eq. (22)
C_BLq = 1/tau_q/tan(T_V/2/tau_q), // eq. (24)
C_BLr = 1/tau_r/tan(T_V/2/tau_r); // eq. (26)
// all values calculated so far are strictly positive, except for
// the random numbers nu_*. This means that in the code below, all
// divisors are strictly positive, too, and no floating point
// exception should occur.
xi_u = -(1 - C_BL*tau_u)/(1 + C_BL*tau_u)*xi_u_km1
+ sig_u*sqrt(2*tau_u/T_V)/(1 + C_BL*tau_u)*(nu_u + nu_u_km1); // eq. (18)
xi_v = -2*(sqr(omega_v) - sqr(C_BL))/sqr(omega_v + C_BL)*xi_v_km1
- sqr(omega_v - C_BL)/sqr(omega_v + C_BL) * xi_v_km2
+ sig_u*sqrt(3*omega_v/T_V)/sqr(omega_v + C_BL)*(
(C_BL + omega_v/sqrt(3.))*nu_v
+ 2/sqrt(3.)*omega_v*nu_v_km1
+ (omega_v/sqrt(3.) - C_BL)*nu_v_km2); // eq. (20) for v
xi_w = -2*(sqr(omega_w) - sqr(C_BL))/sqr(omega_w + C_BL)*xi_w_km1
- sqr(omega_w - C_BL)/sqr(omega_w + C_BL) * xi_w_km2
+ sig_w*sqrt(3*omega_w/T_V)/sqr(omega_w + C_BL)*(
(C_BL + omega_w/sqrt(3.))*nu_w
+ 2/sqrt(3.)*omega_w*nu_w_km1
+ (omega_w/sqrt(3.) - C_BL)*nu_w_km2); // eq. (20) for w
xi_p = -(1 - C_BLp*tau_p)/(1 + C_BLp*tau_p)*xi_p_km1
+ sig_p*sqrt(2*tau_p/T_V)/(1 + C_BLp*tau_p) * (nu_p + nu_p_km1); // eq. (21)
xi_q = -(1 - 4*b_w*C_BLq/M_PI/in.V)/(1 + 4*b_w*C_BLq/M_PI/in.V) * xi_q_km1
+ C_BLq/in.V/(1 + 4*b_w*C_BLq/M_PI/in.V) * (xi_w - xi_w_km1); // eq. (23)
xi_r = - (1 - 3*b_w*C_BLr/M_PI/in.V)/(1 + 3*b_w*C_BLr/M_PI/in.V) * xi_r_km1
+ C_BLr/in.V/(1 + 3*b_w*C_BLr/M_PI/in.V) * (xi_v - xi_v_km1); // eq. (25)
} else if (turbType == ttMilspec) {
// the following is the MIL-STD-1797A formulation
// as cited in Yeager's report
xi_u = (1 - T_V/tau_u) *xi_u_km1 + sig_u*sqrt(2*T_V/tau_u)*nu_u; // eq. (30)
xi_v = (1 - 2*T_V/tau_u)*xi_v_km1 + sig_u*sqrt(4*T_V/tau_u)*nu_v; // eq. (31)
xi_w = (1 - 2*T_V/tau_w)*xi_w_km1 + sig_w*sqrt(4*T_V/tau_w)*nu_w; // eq. (32)
xi_p = (1 - T_V/tau_p) *xi_p_km1 + sig_p*sqrt(2*T_V/tau_p)*nu_p; // eq. (33)
xi_q = (1 - T_V/tau_q) *xi_q_km1 + M_PI/4/b_w*(xi_w - xi_w_km1); // eq. (34)
xi_r = (1 - T_V/tau_r) *xi_r_km1 + M_PI/3/b_w*(xi_v - xi_v_km1); // eq. (35)
}
// rotate by wind azimuth and assign the velocities
double cospsi = cos(psiw), sinpsi = sin(psiw);
vTurbulenceNED(1) = cospsi*xi_u + sinpsi*xi_v;
vTurbulenceNED(2) = -sinpsi*xi_u + cospsi*xi_v;
vTurbulenceNED(3) = xi_w;
vTurbPQR(1) = cospsi*xi_p + sinpsi*xi_q;
vTurbPQR(2) = -sinpsi*xi_p + cospsi*xi_q;
vTurbPQR(3) = xi_r;
// vTurbPQR is in the body fixed frame, not NED
vTurbPQR = in.Tl2b*vTurbPQR;
// hand on the values for the next timestep
xi_u_km1 = xi_u; nu_u_km1 = nu_u;
xi_v_km2 = xi_v_km1; xi_v_km1 = xi_v; nu_v_km2 = nu_v_km1; nu_v_km1 = nu_v;
xi_w_km2 = xi_w_km1; xi_w_km1 = xi_w; nu_w_km2 = nu_w_km1; nu_w_km1 = nu_w;
xi_p_km1 = xi_p; nu_p_km1 = nu_p;
xi_q_km1 = xi_q;
xi_r_km1 = xi_r;
}
default:
break;
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGWinds::CosineGustProfile(double startDuration, double steadyDuration, double endDuration, double elapsedTime)
{
double factor = 0.0;
if (elapsedTime >= 0 && elapsedTime <= startDuration) {
factor = (1.0 - cos(M_PI*elapsedTime/startDuration))/2.0;
} else if (elapsedTime > startDuration && (elapsedTime <= (startDuration + steadyDuration))) {
factor = 1.0;
} else if (elapsedTime > (startDuration + steadyDuration) && elapsedTime <= (startDuration + steadyDuration + endDuration)) {
factor = (1-cos(M_PI*(1-(elapsedTime-(startDuration + steadyDuration))/endDuration)))/2.0;
} else {
factor = 0.0;
}
return factor;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGWinds::CosineGust()
{
struct OneMinusCosineProfile& profile = oneMinusCosineGust.gustProfile;
double factor = CosineGustProfile( profile.startupDuration,
profile.steadyDuration,
profile.endDuration,
profile.elapsedTime);
// Normalize the gust wind vector
oneMinusCosineGust.vWind.Normalize();
if (oneMinusCosineGust.vWindTransformed.Magnitude() == 0.0) {
switch (oneMinusCosineGust.gustFrame) {
case gfBody:
oneMinusCosineGust.vWindTransformed = in.Tl2b.Inverse() * oneMinusCosineGust.vWind;
break;
case gfWind:
oneMinusCosineGust.vWindTransformed = in.Tl2b.Inverse() * in.Tw2b * oneMinusCosineGust.vWind;
break;
case gfLocal:
// this is the native frame - and the default.
oneMinusCosineGust.vWindTransformed = oneMinusCosineGust.vWind;
break;
}
}
vCosineGust = factor * oneMinusCosineGust.vWindTransformed * oneMinusCosineGust.magnitude;
profile.elapsedTime += in.totalDeltaT;
if (profile.elapsedTime > (profile.startupDuration + profile.steadyDuration + profile.endDuration)) {
profile.Running = false;
profile.elapsedTime = 0.0;
oneMinusCosineGust.vWindTransformed.InitMatrix(0.0);
vCosineGust.InitMatrix(0);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGWinds::bind(void)
{
typedef double (FGWinds::*PMF)(int) const;
typedef int (FGWinds::*PMFt)(void) const;
typedef void (FGWinds::*PMFd)(int,double);
typedef void (FGWinds::*PMFi)(int);
typedef double (FGWinds::*Ptr)(void) const;
// User-specified steady, constant, wind properties (local navigational/geographic frame: N-E-D)
PropertyManager->Tie("atmosphere/psiw-rad", this, &FGWinds::GetWindPsi, &FGWinds::SetWindPsi);
PropertyManager->Tie("atmosphere/wind-north-fps", this, eNorth, (PMF)&FGWinds::GetWindNED,
(PMFd)&FGWinds::SetWindNED);
PropertyManager->Tie("atmosphere/wind-east-fps", this, eEast, (PMF)&FGWinds::GetWindNED,
(PMFd)&FGWinds::SetWindNED);
PropertyManager->Tie("atmosphere/wind-down-fps", this, eDown, (PMF)&FGWinds::GetWindNED,
(PMFd)&FGWinds::SetWindNED);
PropertyManager->Tie("atmosphere/wind-mag-fps", this, &FGWinds::GetWindspeed,
&FGWinds::SetWindspeed);
// User-specifieded gust (local navigational/geographic frame: N-E-D)
PropertyManager->Tie("atmosphere/gust-north-fps", this, eNorth, (PMF)&FGWinds::GetGustNED,
(PMFd)&FGWinds::SetGustNED);
PropertyManager->Tie("atmosphere/gust-east-fps", this, eEast, (PMF)&FGWinds::GetGustNED,
(PMFd)&FGWinds::SetGustNED);
PropertyManager->Tie("atmosphere/gust-down-fps", this, eDown, (PMF)&FGWinds::GetGustNED,
(PMFd)&FGWinds::SetGustNED);
// User-specified 1 - cosine gust parameters (in specified frame)
PropertyManager->Tie("atmosphere/cosine-gust/startup-duration-sec", this, (Ptr)0L, &FGWinds::StartupGustDuration);
PropertyManager->Tie("atmosphere/cosine-gust/steady-duration-sec", this, (Ptr)0L, &FGWinds::SteadyGustDuration);
PropertyManager->Tie("atmosphere/cosine-gust/end-duration-sec", this, (Ptr)0L, &FGWinds::EndGustDuration);
PropertyManager->Tie("atmosphere/cosine-gust/magnitude-ft_sec", this, (Ptr)0L, &FGWinds::GustMagnitude);
PropertyManager->Tie("atmosphere/cosine-gust/frame", this, (PMFt)0L, (PMFi)&FGWinds::GustFrame);
PropertyManager->Tie("atmosphere/cosine-gust/X-velocity-ft_sec", this, (Ptr)0L, &FGWinds::GustXComponent);
PropertyManager->Tie("atmosphere/cosine-gust/Y-velocity-ft_sec", this, (Ptr)0L, &FGWinds::GustYComponent);
PropertyManager->Tie("atmosphere/cosine-gust/Z-velocity-ft_sec", this, (Ptr)0L, &FGWinds::GustZComponent);
PropertyManager->Tie("atmosphere/cosine-gust/start", this, (PMFt)0L, (PMFi)&FGWinds::StartGust);
// User-specified turbulence (local navigational/geographic frame: N-E-D)
PropertyManager->Tie("atmosphere/turb-north-fps", this, eNorth, (PMF)&FGWinds::GetTurbNED,
(PMFd)&FGWinds::SetTurbNED);
PropertyManager->Tie("atmosphere/turb-east-fps", this, eEast, (PMF)&FGWinds::GetTurbNED,
(PMFd)&FGWinds::SetTurbNED);
PropertyManager->Tie("atmosphere/turb-down-fps", this, eDown, (PMF)&FGWinds::GetTurbNED,
(PMFd)&FGWinds::SetTurbNED);
// Experimental turbulence parameters
PropertyManager->Tie("atmosphere/p-turb-rad_sec", this,1, (PMF)&FGWinds::GetTurbPQR);
PropertyManager->Tie("atmosphere/q-turb-rad_sec", this,2, (PMF)&FGWinds::GetTurbPQR);
PropertyManager->Tie("atmosphere/r-turb-rad_sec", this,3, (PMF)&FGWinds::GetTurbPQR);
PropertyManager->Tie("atmosphere/turb-type", this, (PMFt)&FGWinds::GetTurbType, (PMFi)&FGWinds::SetTurbType);
PropertyManager->Tie("atmosphere/turb-rate", this, &FGWinds::GetTurbRate, &FGWinds::SetTurbRate);
PropertyManager->Tie("atmosphere/turb-gain", this, &FGWinds::GetTurbGain, &FGWinds::SetTurbGain);
PropertyManager->Tie("atmosphere/turb-rhythmicity", this, &FGWinds::GetRhythmicity,
&FGWinds::SetRhythmicity);
// Parameters for milspec turbulence
PropertyManager->Tie("atmosphere/turbulence/milspec/windspeed_at_20ft_AGL-fps",
this, &FGWinds::GetWindspeed20ft,
&FGWinds::SetWindspeed20ft);
PropertyManager->Tie("atmosphere/turbulence/milspec/severity",
this, &FGWinds::GetProbabilityOfExceedence,
&FGWinds::SetProbabilityOfExceedence);
// Total, calculated winds (local navigational/geographic frame: N-E-D). Read only.
PropertyManager->Tie("atmosphere/total-wind-north-fps", this, eNorth, (PMF)&FGWinds::GetTotalWindNED);
PropertyManager->Tie("atmosphere/total-wind-east-fps", this, eEast, (PMF)&FGWinds::GetTotalWindNED);
PropertyManager->Tie("atmosphere/total-wind-down-fps", this, eDown, (PMF)&FGWinds::GetTotalWindNED);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 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 FGWinds::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: FGWinds" << endl;
if (from == 1) cout << "Destroyed: FGWinds" << endl;
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
if (debug_lvl & 8 ) { // Runtime state variables
}
if (debug_lvl & 16) { // Sanity checking
}
if (debug_lvl & 128) { //
}
if (debug_lvl & 64) {
if (from == 0) { // Constructor
cout << IdSrc << endl;
cout << IdHdr << endl;
}
}
}
} // namespace JSBSim

View file

@ -0,0 +1,349 @@
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Header: FGWinds.h
Author: Jon Berndt, Andreas Gaeb, David Culp
Date started: 5/2011
------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
Further information about the GNU Lesser General Public License can also be found on
the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
5/2011 JSB Created
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SENTRY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#ifndef FGWINDS_H
#define FGWINDS_H
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "models/FGModel.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "math/FGTable.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_WINDS "$Id: FGWinds.h,v 1.5 2011/09/07 12:21:45 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
namespace JSBSim {
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models atmospheric disturbances: winds, gusts, turbulence, downbursts, etc.
Various turbulence models are available. They are specified
via the property <tt>atmosphere/turb-type</tt>. The following models are
available:
- 0: ttNone (turbulence disabled)
- 1: ttStandard
- 2: ttCulp
- 3: ttMilspec (Dryden spectrum)
- 4: ttTustin (Dryden spectrum)
The Milspec and Tustin models are described in the Yeager report cited below.
They both use a Dryden spectrum model whose parameters (scale lengths and intensities)
are modelled according to MIL-F-8785C. Parameters are modelled differently
for altitudes below 1000ft and above 2000ft, for altitudes in between they
are interpolated linearly.
The two models differ in the implementation of the transfer functions
described in the milspec.
To use one of these two models, set <tt>atmosphere/turb-type</tt> to 4 resp. 5,
and specify values for <tt>atmosphere/turbulence/milspec/windspeed_at_20ft_AGL-fps<tt>
and <tt>atmosphere/turbulence/milspec/severity<tt> (the latter corresponds to
the probability of exceedence curves from Fig.&nbsp;7 of the milspec, allowable
range is 0 (disabled) to 7). <tt>atmosphere/psiw-rad</tt> is respected as well;
note that you have to specify a positive wind magnitude to prevent psiw from
being reset to zero.
Reference values (cf. figures 7 and 9 from the milspec):
<table>
<tr><td><b>Intensity</b></td>
<td><b><tt>windspeed_at_20ft_AGL-fps</tt></b></td>
<td><b><tt>severity</tt></b></td></tr>
<tr><td>light</td>
<td>25 (15 knots)</td>
<td>3</td></tr>
<tr><td>moderate</td>
<td>50 (30 knots)</td>
<td>4</td></tr>
<tr><td>severe</td>
<td>75 (45 knots)</td>
<td>6</td></tr>
</table>
@see Yeager, Jessie C.: "Implementation and Testing of Turbulence Models for
the F18-HARV" (<a
href="http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19980028448_1998081596.pdf">
pdf</a>), NASA CR-1998-206937, 1998
@see MIL-F-8785C: Military Specification: Flying Qualities of Piloted Aircraft
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGWinds : public FGModel {
public:
/// Constructor
FGWinds(FGFDMExec*);
/// Destructor
~FGWinds();
/** Runs the winds model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@param Holding if true, the executive has been directed to hold the sim from
advancing time. Some models may ignore this flag, such as the Input
model, which may need to be active to listen on a socket for the
"Resume" command to be given.
@return false if no error */
bool Run(bool Holding);
bool InitModel(void);
enum tType {ttNone, ttStandard, ttCulp, ttMilspec, ttTustin} turbType;
// TOTAL WIND access functions (wind + gust + turbulence)
/// Retrieves the total wind components in NED frame.
virtual const FGColumnVector3& GetTotalWindNED(void) const { return vTotalWindNED; }
/// Retrieves a total wind component in NED frame.
virtual double GetTotalWindNED(int idx) const {return vTotalWindNED(idx);}
// WIND access functions
/// Sets the wind components in NED frame.
virtual void SetWindNED(double wN, double wE, double wD) { vWindNED(1)=wN; vWindNED(2)=wE; vWindNED(3)=wD;}
/// Sets a wind component in NED frame.
virtual void SetWindNED(int idx, double wind) { vWindNED(idx)=wind;}
/// Retrieves the wind components in NED frame.
virtual FGColumnVector3& GetWindNED(void) { return vWindNED; }
/// Retrieves a wind component in NED frame.
virtual double GetWindNED(int idx) const {return vWindNED(idx);}
/** Retrieves the direction that the wind is coming from.
The direction is defined as north=0 and increases counterclockwise.
The wind heading is returned in radians.*/
virtual double GetWindPsi(void) const { return psiw; }
/** Sets the direction that the wind is coming from.
The direction is defined as north=0 and increases counterclockwise to 2*pi (radians). The
vertical component of wind is assumed to be zero - and is forcibly set to zero. This function
sets the vWindNED vector components based on the supplied direction. The magnitude of
the wind set in the vector is preserved (assuming the vertical component is non-zero).
@param dir wind direction in the horizontal plane, in radians.*/
virtual void SetWindPsi(double dir);
virtual void SetWindspeed(double speed);
virtual double GetWindspeed(void) const;
// GUST access functions
/// Sets a gust component in NED frame.
virtual void SetGustNED(int idx, double gust) { vGustNED(idx)=gust;}
/// Sets a turbulence component in NED frame.
virtual void SetTurbNED(int idx, double turb) { vTurbulenceNED(idx)=turb;}
/// Sets the gust components in NED frame.
virtual void SetGustNED(double gN, double gE, double gD) { vGustNED(eNorth)=gN; vGustNED(eEast)=gE; vGustNED(eDown)=gD;}
/// Retrieves a gust component in NED frame.
virtual double GetGustNED(int idx) const {return vGustNED(idx);}
/// Retrieves a turbulence component in NED frame.
virtual double GetTurbNED(int idx) const {return vTurbulenceNED(idx);}
/// Retrieves the gust components in NED frame.
virtual FGColumnVector3& GetGustNED(void) {return vGustNED;}
/** Turbulence models available: ttNone, ttStandard, ttBerndt, ttCulp, ttMilspec, ttTustin */
virtual void SetTurbType(tType tt) {turbType = tt;}
virtual tType GetTurbType() const {return turbType;}
virtual void SetTurbGain(double tg) {TurbGain = tg;}
virtual double GetTurbGain() const {return TurbGain;}
virtual void SetTurbRate(double tr) {TurbRate = tr;}
virtual double GetTurbRate() const {return TurbRate;}
virtual void SetRhythmicity(double r) {Rhythmicity=r;}
virtual double GetRhythmicity() const {return Rhythmicity;}
virtual double GetTurbPQR(int idx) const {return vTurbPQR(idx);}
virtual double GetTurbMagnitude(void) const {return Magnitude;}
virtual const FGColumnVector3& GetTurbDirection(void) const {return vDirection;}
virtual const FGColumnVector3& GetTurbPQR(void) const {return vTurbPQR;}
virtual void SetWindspeed20ft(double ws) { windspeed_at_20ft = ws;}
virtual double GetWindspeed20ft() const { return windspeed_at_20ft;}
/// allowable range: 0-7, 3=light, 4=moderate, 6=severe turbulence
virtual void SetProbabilityOfExceedence( int idx) {probability_of_exceedence_index = idx;}
virtual int GetProbabilityOfExceedence() const { return probability_of_exceedence_index;}
// Stores data defining a 1 - cosine gust profile that builds up, holds steady
// and fades out over specified durations.
struct OneMinusCosineProfile {
bool Running; ///<- This flag is set true through FGWinds::StartGust().
double elapsedTime; ///<- Stores the elapsed time for the ongoing gust.
double startupDuration; ///<- Specifies the time it takes for the gust startup transient.
double steadyDuration; ///<- Specifies the duration of the steady gust.
double endDuration; ///<- Specifies the time it takes for the gust to subsude.
OneMinusCosineProfile() ///<- The constructor.
{
elapsedTime = 0.0;
Running = false;
startupDuration = 2;
steadyDuration = 4;
endDuration = 2;
}
};
enum eGustFrame {gfNone=0, gfBody, gfWind, gfLocal};
/// Stores the information about a single one minus cosine gust instance.
struct OneMinusCosineGust {
FGColumnVector3 vWind; ///<- The input normalized wind vector.
FGColumnVector3 vWindTransformed; ///<- The transformed normal vector at the time the gust is started.
double magnitude; ///<- The magnitude of the wind vector.
eGustFrame gustFrame; ///<- The frame that the wind vector is specified in.
struct OneMinusCosineProfile gustProfile; ///<- The gust shape (profile) data for this gust.
OneMinusCosineGust() ///<- Constructor.
{
vWind.InitMatrix(0.0);
gustFrame = gfLocal;
magnitude = 1.0;
};
};
/// Stores information about a specified Up- or Down-burst.
struct UpDownBurst {
double ringLatitude; ///<- The latitude of the downburst run (radians)
double ringLongitude; ///<- The longitude of the downburst run (radians)
double ringAltitude; ///<- The altitude of the ring (feet).
double ringRadius; ///<- The radius of the ring (feet).
double ringCoreRadius; ///<- The cross-section "core" radius of the ring (feet).
double circulation; ///<- The circulation (gamma) (feet-squared per second).
struct OneMinusCosineProfile oneMCosineProfile;
};
// 1 - Cosine gust setters
/// Initiates the execution of the gust.
virtual void StartGust(bool running) {oneMinusCosineGust.gustProfile.Running = running;}
///Specifies the duration of the startup portion of the gust.
virtual void StartupGustDuration(double dur) {oneMinusCosineGust.gustProfile.startupDuration = dur;}
///Specifies the length of time that the gust is at a steady, full strength.
virtual void SteadyGustDuration(double dur) {oneMinusCosineGust.gustProfile.steadyDuration = dur;}
/// Specifies the length of time it takes for the gust to return to zero velocity.
virtual void EndGustDuration(double dur) {oneMinusCosineGust.gustProfile.endDuration = dur;}
/// Specifies the magnitude of the gust in feet/second.
virtual void GustMagnitude(double mag) {oneMinusCosineGust.magnitude = mag;}
/** Specifies the frame that the gust direction vector components are specified in. The
body frame is defined with the X direction forward, and the Y direction positive out
the right wing. The wind frame is defined with the X axis pointing into the velocity
vector, the Z axis perpendicular to the X axis, in the aircraft XZ plane, and the Y
axis completing the system. The local axis is a navigational frame with X pointing north,
Y pointing east, and Z pointing down. This is a locally vertical, locally horizontal
frame, with the XY plane tangent to the geocentric surface. */
virtual void GustFrame(eGustFrame gFrame) {oneMinusCosineGust.gustFrame = gFrame;}
/// Specifies the X component of velocity in the specified gust frame (ft/sec).
virtual void GustXComponent(double x) {oneMinusCosineGust.vWind(eX) = x;}
/// Specifies the Y component of velocity in the specified gust frame (ft/sec).
virtual void GustYComponent(double y) {oneMinusCosineGust.vWind(eY) = y;}
/// Specifies the Z component of velocity in the specified gust frame (ft/sec).
virtual void GustZComponent(double z) {oneMinusCosineGust.vWind(eZ) = z;}
struct Inputs {
double V;
double wingspan;
double DistanceAGL;
double AltitudeASL;
FGMatrix33 Tl2b;
FGMatrix33 Tw2b;
double totalDeltaT;
} in;
private:
double MagnitudedAccelDt, MagnitudeAccel, Magnitude;
double h;
double TurbGain;
double TurbRate;
double Rhythmicity;
double wind_from_clockwise;
double spike, target_time, strength;
FGColumnVector3 vDirectiondAccelDt;
FGColumnVector3 vDirectionAccel;
FGColumnVector3 vDirection;
FGColumnVector3 vTurbulenceGrad;
FGColumnVector3 vBodyTurbGrad;
FGColumnVector3 vTurbPQR;
struct OneMinusCosineGust oneMinusCosineGust;
// Dryden turbulence model
double windspeed_at_20ft; ///< in ft/s
int probability_of_exceedence_index; ///< this is bound as the severity property
FGTable *POE_Table; ///< probability of exceedence table
double psiw;
FGColumnVector3 vTotalWindNED;
FGColumnVector3 vWindNED;
FGColumnVector3 vGustNED;
FGColumnVector3 vCosineGust;
FGColumnVector3 vBurstGust;
FGColumnVector3 vTurbulenceNED;
/// Get T, P and rho for a standard atmosphere at the given altitude.
void Turbulence(double h);
void CosineGust();
double CosineGustProfile( double startDuration, double steadyDuration,
double endDuration, double elapsedTime);
virtual void bind(void);
void Debug(int from);
};
} // namespace JSBSim
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#endif

View file

@ -1,7 +1,7 @@
noinst_LIBRARIES = libAtmosphere.a
libAtmosphere_a_SOURCES = FGMSIS.cpp FGMSISData.cpp FGMars.cpp
libAtmosphere_a_SOURCES = FGMSIS.cpp FGMSISData.cpp FGMars.cpp FGStandardAtmosphere.cpp FGWinds.cpp
noinst_HEADERS = FGMSIS.h FGMars.h
noinst_HEADERS = FGMSIS.h FGMars.h FGStandardAtmosphere.h FGWinds.h
INCLUDES = -I$(top_srcdir)/src/FDM/JSBSim

View file

@ -41,11 +41,16 @@ INCLUDES
#include <iostream>
#include <cstdlib>
#include "models/FGPropagate.h"
#include "models/FGAccelerations.h"
#include "models/FGMassBalance.h"
#include "models/FGInertial.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGAccelerometer.cpp,v 1.8 2009/10/24 22:59:30 jberndt Exp $";
static const char *IdSrc = "$Id: FGAccelerometer.cpp,v 1.9 2011/07/17 13:51:23 jberndt Exp $";
static const char *IdHdr = ID_ACCELEROMETER;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -57,6 +62,7 @@ FGAccelerometer::FGAccelerometer(FGFCS* fcs, Element* element)
FGSensorOrientation(element)
{
Propagate = fcs->GetExec()->GetPropagate();
Accelerations = fcs->GetExec()->GetAccelerations();
MassBalance = fcs->GetExec()->GetMassBalance();
Inertial = fcs->GetExec()->GetInertial();
@ -88,8 +94,8 @@ bool FGAccelerometer::Run(void )
vAccel = Propagate->GetTl2b() * FGColumnVector3(0, 0, Inertial->gravity());
//aircraft forces
vAccel += (Propagate->GetUVWdot()
+ Propagate->GetPQRdot() * vRadius
vAccel += (Accelerations->GetUVWdot()
+ Accelerations->GetPQRdot() * vRadius
+ Propagate->GetPQR() * (Propagate->GetPQR() * vRadius));
// transform to the specified orientation

12
src/FDM/JSBSim/models/flight_control/FGAccelerometer.h Normal file → Executable file
View file

@ -39,9 +39,6 @@ INCLUDES
#include "FGSensor.h"
#include "input_output/FGXMLElement.h"
#include "models/FGPropagate.h"
#include "models/FGMassBalance.h"
#include "models/FGInertial.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "FGSensorOrientation.h"
@ -50,7 +47,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ACCELEROMETER "$Id: FGAccelerometer.h,v 1.4 2009/10/02 10:30:09 jberndt Exp $"
#define ID_ACCELEROMETER "$Id: FGAccelerometer.h,v 1.5 2011/07/17 13:51:23 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -59,6 +56,10 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGFCS;
class FGPropagate;
class FGAccelerations;
class FGInertial;
class FGMassBalance;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -110,7 +111,7 @@ even varying all the way from 0.95 to 1.05 in adjacent frames - whatever the del
time.
@author Jon S. Berndt
@version $Revision: 1.4 $
@version $Revision: 1.5 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -127,6 +128,7 @@ public:
private:
FGPropagate* Propagate;
FGAccelerations* Accelerations;
FGMassBalance* MassBalance;
FGInertial* Inertial;
FGColumnVector3 vLocation;

View file

@ -43,7 +43,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGActuator.cpp,v 1.21 2011/06/30 03:16:10 jentron Exp $";
static const char *IdSrc = "$Id: FGActuator.cpp,v 1.22 2011/07/12 21:40:32 jentron Exp $";
static const char *IdHdr = ID_ACTUATOR;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -65,6 +65,7 @@ FGActuator::FGActuator(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, eleme
rate_limit = 0.0; // no limit
fail_zero = fail_hardover = fail_stuck = false;
ca = cb = 0.0;
initialized = 0;
if ( element->FindElement("deadband_width") ) {
deadband_width = element->FindElementValueAsNumber("deadband_width");
@ -104,6 +105,8 @@ bool FGActuator::Run(void )
{
Input = InputNodes[0]->getDoubleValue() * InputSigns[0];
if( fcs->GetTrimStatus() ) initialized = 0;
if (fail_zero) Input = 0;
if (fail_hardover) Input = clipmax*sign(Input);
@ -125,6 +128,8 @@ bool FGActuator::Run(void )
}
PreviousOutput = Output; // previous value needed for "stuck" malfunction
initialized = 1;
Clip();
if (IsOutput) SetOutput();
@ -147,7 +152,7 @@ void FGActuator::Lag(void)
// for this Lag filter
double input = Output;
if (!fcs->GetTrimStatus())
if ( initialized )
Output = ca * (input + PreviousLagInput) + PreviousLagOutput * cb;
PreviousLagInput = input;
@ -163,7 +168,7 @@ void FGActuator::Hysteresis(void)
// method.
double input = Output;
if (!fcs->GetTrimStatus()) {
if ( initialized ) {
if (input > PreviousHystOutput)
Output = max(PreviousHystOutput, input-0.5*hysteresis_width);
else if (input < PreviousHystOutput)
@ -181,7 +186,7 @@ void FGActuator::RateLimit(void)
// is - for the purposes of this RateLimit method - really the input to the
// method.
double input = Output;
if (!fcs->GetTrimStatus()) {
if ( initialized ) {
double delta = input - PreviousRateLimOutput;
if (fabs(delta) > dt * rate_limit) {
double signed_rate_limit = delta > 0.0 ? rate_limit : -rate_limit;

View file

@ -44,7 +44,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ACTUATOR "$Id: FGActuator.h,v 1.11 2009/10/02 10:30:09 jberndt Exp $"
#define ID_ACTUATOR "$Id: FGActuator.h,v 1.12 2011/07/12 21:40:32 jentron Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -81,7 +81,7 @@ Syntax:
<actuator name="name">
<input> {[-]property} </input>
<lag> number </lag>
<rate_limit> number <rate_limit>
<rate_limit> number </rate_limit>
<bias> number </bias>
<deadband_width> number </deadband_width>
<hysteresis_width> number </hysteresis_width>
@ -96,14 +96,14 @@ Syntax:
Example:
@code
<actuator name="fcs/gimbal_pitch_position">
<actuator name="fcs/gimbal_pitch_position_radians">
<input> fcs/gimbal_pitch_command </input>
<lag> 60 </lag>
<rate_limit> 0.085 <rate_limit> <!-- 5 degrees/sec -->
<rate_limit> 0.085 </rate_limit> <!-- 0.085 radians/sec -->
<bias> 0.002 </bias>
<deadband_width> 0.002 </deadband_width>
<hysteresis_width> 0.05 </hysteresis_width>
<clipto> <!-- +/- 10 degrees -->
<clipto> <!-- +/- 0.17 radians -->
<min> -0.17 </min>
<max> 0.17 </max>
</clipto>
@ -111,7 +111,7 @@ Example:
@endcode
@author Jon S. Berndt
@version $Revision: 1.11 $
@version $Revision: 1.12 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -160,6 +160,7 @@ private:
bool fail_zero;
bool fail_hardover;
bool fail_stuck;
bool initialized;
void Hysteresis(void);
void Lag(void);

8
src/FDM/JSBSim/models/flight_control/FGGyro.cpp Normal file → Executable file
View file

@ -40,11 +40,13 @@ INCLUDES
#include "FGGyro.h"
#include <iostream>
#include "models/FGAccelerations.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGGyro.cpp,v 1.5 2009/10/24 22:59:30 jberndt Exp $";
static const char *IdSrc = "$Id: FGGyro.cpp,v 1.6 2011/07/17 13:51:23 jberndt Exp $";
static const char *IdHdr = ID_GYRO;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -54,7 +56,7 @@ CLASS IMPLEMENTATION
FGGyro::FGGyro(FGFCS* fcs, Element* element) : FGSensor(fcs, element),
FGSensorOrientation(element)
{
Propagate = fcs->GetExec()->GetPropagate();
Accelerations = fcs->GetExec()->GetAccelerations();
Debug(0);
}
@ -73,7 +75,7 @@ bool FGGyro::Run(void )
// There is no input assumed. This is a dedicated angular acceleration sensor.
//aircraft rates
vAccel = mT * Propagate->GetPQRdot();
vAccel = mT * Accelerations->GetPQRdot();
Input = vAccel(axis);

10
src/FDM/JSBSim/models/flight_control/FGGyro.h Normal file → Executable file
View file

@ -39,9 +39,6 @@ INCLUDES
#include "FGSensor.h"
#include "input_output/FGXMLElement.h"
#include "models/FGPropagate.h"
#include "models/FGMassBalance.h"
#include "models/FGInertial.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "FGSensorOrientation.h"
@ -50,7 +47,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_GYRO "$Id: FGGyro.h,v 1.5 2009/12/11 06:03:06 jberndt Exp $"
#define ID_GYRO "$Id: FGGyro.h,v 1.6 2011/07/17 13:51:23 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -59,6 +56,7 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGFCS;
class FGAccelerations;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -106,7 +104,7 @@ even varying all the way from 0.95 to 1.05 in adjacent frames - whatever the del
time.
@author Jon S. Berndt
@version $Revision: 1.5 $
@version $Revision: 1.6 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -122,7 +120,7 @@ public:
bool Run (void);
private:
FGPropagate* Propagate;
FGAccelerations* Accelerations;
FGColumnVector3 vAccel;
void CalculateTransformMatrix(void);

4
src/FDM/JSBSim/models/flight_control/FGSensor.cpp Normal file → Executable file
View file

@ -46,7 +46,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGSensor.cpp,v 1.20 2009/10/24 22:59:30 jberndt Exp $";
static const char *IdSrc = "$Id: FGSensor.cpp,v 1.23 2011/08/18 12:42:17 jberndt Exp $";
static const char *IdHdr = ID_SENSOR;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -181,7 +181,7 @@ void FGSensor::Noise(void)
double random_value=0.0;
if (DistributionType == eUniform) {
random_value = ((double)rand()/(double)RAND_MAX) - 0.5;
random_value = 2.0*(((double)rand()/(double)RAND_MAX) - 0.5);
} else {
random_value = GaussianRandomNumber();
}

34
src/FDM/JSBSim/models/flight_control/FGSensor.h Normal file → Executable file
View file

@ -44,7 +44,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_SENSOR "$Id: FGSensor.h,v 1.19 2009/10/24 22:59:30 jberndt Exp $"
#define ID_SENSOR "$Id: FGSensor.h,v 1.20 2011/08/18 12:42:17 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -67,7 +67,7 @@ Syntax:
<sensor name="name">
<input> property </input>
<lag> number </lag>
<noise variation="PERCENT|ABSOLUTE"> number </noise>
<noise [variation="PERCENT|ABSOLUTE"] [distribution="UNIFORM|GAUSSIAN"]> number </noise>
<quantization name="name">
<bits> number </bits>
<min> number </min>
@ -98,16 +98,32 @@ Example:
The only required element in the sensor definition is the input element. In that
case, no degradation would be modeled, and the output would simply be the input.
For noise, if the type is PERCENT, then the value supplied is understood to be a
percentage variance. That is, if the number given is 0.05, the the variance is
understood to be +/-0.05 percent maximum variance. So, the actual value for the sensor
will be *anywhere* from 0.95 to 1.05 of the actual "perfect" value at any time -
even varying all the way from 0.95 to 1.05 in adjacent frames - whatever the delta
time. The delay element can specify a frame delay. The integer number provided is
Noise can be Gaussian or uniform, and the noise can be applied as a factor (PERCENT)
or additively (ABSOLUTE). The noise that can be applied at each frame of the
simulation execution is calculated as a random factor times a noise value that
is specified in the config file. When the noise distribution type is Gaussian,
the random number can be between roughly -3 and +3 for a span of six sigma. When
the distribution type is UNIFORM, the random value can be between -1.0 and +1.0.
This random value is multiplied against the specified noise to arrive at a random
noise value for the frame. If the noise type is PERCENT, then random noise value
is added to one, and that sum is then multiplied against the input signal for the
sensor. In this case, the specified noise value in the config file would be
expected to actually be a percent value, such as 0.05 (for a 5% variance). If the
noise type is ABSOLUTE, then the random noise value specified in the config file
is understood to be an absolute value of noise to be added to the input signal
instead of being added to 1.0 and having that sum be multiplied against the input
signal as in the PERCENT type. For the ABSOLUTE noise case, the noise number
specified in the config file could be any number.
If the type is ABSOLUTE, then the noise number times the random number is
added to the input signal instead of being multiplied against it as with the
PERCENT type of noise.
The delay element can specify a frame delay. The integer number provided is
the number of frames to delay the output signal.
@author Jon S. Berndt
@version $Revision: 1.19 $
@version $Revision: 1.20 $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -2,13 +2,13 @@ noinst_LIBRARIES = libFlightControl.a
libFlightControl_a_SOURCES = \
FGPID.cpp FGDeadBand.cpp FGFCSComponent.cpp \
FGFilter.cpp FGGain.cpp FGGradient.cpp FGKinemat.cpp \
FGFilter.cpp FGGain.cpp FGKinemat.cpp \
FGSummer.cpp FGSwitch.cpp FGFCSFunction.cpp FGSensor.cpp \
FGActuator.cpp FGAccelerometer.cpp FGGyro.cpp FGMagnetometer.cpp
noinst_HEADERS = \
FGPID.h FGDeadBand.h FGFCSComponent.h FGFilter.h \
FGGain.h FGGradient.h FGKinemat.h FGSummer.h FGSwitch.h FGFCSFunction.h\
FGGain.h FGKinemat.h FGSummer.h FGSwitch.h FGFCSFunction.h\
FGSensor.h FGActuator.h FGAccelerometer.h FGGyro.h FGMagnetometer.h \
FGSensorOrientation.h

View file

@ -39,27 +39,25 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGElectric.h"
#include "models/FGPropulsion.h"
#include "models/propulsion/FGThruster.h"
#include "FGPropeller.h"
#include <iostream>
#include <sstream>
#include "FGElectric.h"
#include "FGPropeller.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGElectric.cpp,v 1.11 2011/06/06 22:35:08 jentron Exp $";
static const char *IdSrc = "$Id: FGElectric.cpp,v 1.13 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_ELECTRIC;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGElectric::FGElectric(FGFDMExec* exec, Element *el, int engine_number)
: FGEngine(exec, el, engine_number)
FGElectric::FGElectric(FGFDMExec* exec, Element *el, int engine_number, struct FGEngine::Inputs& input)
: FGEngine(exec, el, engine_number, input)
{
string token;
@ -67,8 +65,6 @@ FGElectric::FGElectric(FGFDMExec* exec, Element *el, int engine_number)
PowerWatts = 745.7;
hptowatts = 745.7;
dt = FDMExec->GetDeltaT();
if (el->FindElement("power"))
PowerWatts = el->FindElementValueAsNumberConvertTo("power","WATTS");
@ -93,17 +89,16 @@ void FGElectric::Calculate(void)
{
RunPreFunctions();
Throttle = FCS->GetThrottlePos(EngineNumber);
if (Thruster->GetType() == FGThruster::ttPropeller) {
((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber));
((FGPropeller*)Thruster)->SetFeather(FCS->GetPropFeather(EngineNumber));
((FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
((FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
}
RPM = Thruster->GetRPM() * Thruster->GetGearRatio();
HP = PowerWatts * Throttle / hptowatts;
HP = PowerWatts * in.ThrottlePos[EngineNumber] / hptowatts;
LoadThrusterInputs();
Thruster->Calculate(HP * hptoftlbssec);
RunPostFunctions();

View file

@ -45,7 +45,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ELECTRIC "$Id: FGElectric.h,v 1.10 2011/03/10 01:35:25 dpculp Exp $";
#define ID_ELECTRIC "$Id: FGElectric.h,v 1.11 2011/07/28 12:48:19 jberndt Exp $";
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -65,7 +65,7 @@ CLASS DOCUMENTATION
there is no battery model available, so this motor does not consume any
energy. There is no internal friction.
@author David Culp
@version "$Id: FGElectric.h,v 1.10 2011/03/10 01:35:25 dpculp Exp $"
@version "$Id: FGElectric.h,v 1.11 2011/07/28 12:48:19 jberndt Exp $"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -76,7 +76,7 @@ class FGElectric : public FGEngine
{
public:
/// Constructor
FGElectric(FGFDMExec* exec, Element *el, int engine_number);
FGElectric(FGFDMExec* exec, Element *el, int engine_number, FGEngine::Inputs& input);
/// Destructor
~FGElectric();
@ -92,9 +92,6 @@ private:
double BrakeHorsePower;
// timestep
double dt;
// constants
double hptowatts;

View file

@ -37,32 +37,31 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "FGEngine.h"
#include "FGTank.h"
#include "FGPropeller.h"
#include "FGNozzle.h"
#include "FGRotor.h"
#include "models/FGPropulsion.h"
#include "input_output/FGXMLParse.h"
#include "math/FGColumnVector3.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGEngine.cpp,v 1.42 2011/03/03 12:16:26 jberndt Exp $";
static const char *IdSrc = "$Id: FGEngine.cpp,v 1.46 2011/08/17 23:56:01 jberndt Exp $";
static const char *IdHdr = ID_ENGINE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number)
: EngineNumber(engine_number)
FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number, struct Inputs& input)
: EngineNumber(engine_number), in(input)
{
Element* local_element;
FGColumnVector3 location, orientation;
@ -72,20 +71,13 @@ FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number)
X = Y = Z = 0.0;
EnginePitch = EngineYaw = 0.0;
SLFuelFlowMax = 0.0;
FuelExpended = 0.0;
MaxThrottle = 1.0;
MinThrottle = 0.0;
FuelDensity = 6.0;
unsigned int i;
ResetToIC(); // initialize dynamic terms
FDMExec = exec;
Atmosphere = FDMExec->GetAtmosphere();
FCS = FDMExec->GetFCS();
Propulsion = FDMExec->GetPropulsion();
Aircraft = FDMExec->GetAircraft();
Propagate = FDMExec->GetPropagate();
Auxiliary = FDMExec->GetAuxiliary();
PropertyManager = FDMExec->GetPropertyManager();
@ -118,28 +110,12 @@ FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number)
cerr << "No thruster definition supplied with engine definition." << endl;
}
// Build and initialize the feed tank vector.
for (i=0; i<(Propulsion->GetNumTanks()); i++) {
SourceTanks.push_back(0);
}
// Load feed tank[s] references
local_element = engine_element->GetParent()->FindElement("feed");
if (local_element) {
while (local_element) {
int tankID = (int)local_element->GetDataAsNumber();
FGTank* tank = Propulsion->GetTank(tankID);
if (tank) {
AddFeedTank(tankID, tank->GetPriority());
FuelDensity = tank->GetDensity();
} else {
cerr << "Feed tank " << tankID <<
" specified in engine definition does not exist." << endl;
}
local_element = engine_element->GetParent()->FindNextElement("feed");
}
} else {
cerr << "No feed tank specified in engine definition." << endl;
while (local_element) {
int tankID = (int)local_element->GetDataAsNumber();
SourceTanks.push_back(tankID);
local_element = engine_element->GetParent()->FindNextElement("feed");
}
string property_name, base_property_name;
@ -151,13 +127,13 @@ FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number)
PropertyManager->Tie( property_name.c_str(), Thruster, &FGThruster::GetThrust);
property_name = base_property_name + "/fuel-flow-rate-pps";
PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate);
property_name = base_property_name + "/fuel-flow-rate-gph";
PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRateGPH);
property_name = base_property_name + "/fuel-used-lbs";
PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelUsedLbs);
PostLoad(engine_element, PropertyManager, to_string(EngineNumber));
//cout << "Engine[" << EngineNumber << "] using fuel density: " << FuelDensity << endl;
Debug(0);
}
@ -173,8 +149,6 @@ FGEngine::~FGEngine()
void FGEngine::ResetToIC(void)
{
Throttle = 0.0;
Mixture = 1.0;
Starter = false;
FuelExpended = 0.0;
Starved = Running = Cranking = false;
@ -187,73 +161,24 @@ void FGEngine::ResetToIC(void)
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This base class function should be called from within the
// derived class' Calculate() function before any other calculations are done.
// This base class method removes fuel from the fuel tanks as appropriate,
// and sets the starved flag if necessary.
// This version of the fuel consumption code should never see an oxidizer tank.
void FGEngine::ConsumeFuel(void)
double FGEngine::CalcFuelNeed(void)
{
if (FuelFreeze) return;
if (FDMExec->GetTrimStatus()) return;
unsigned int i;
double Fshortage, FuelNeeded;
FGTank* Tank;
unsigned int TanksWithFuel = 0;
Fshortage = FuelNeeded = 0.0;
double FuelToBurn;
unsigned int CurrentPriority = 1;
vector <int> FeedList;
Starved = false;
FuelToBurn = CalcFuelNeed();
if (FuelToBurn == 0.0) return;
// Count how many fuel tanks with the current priority level have fuel.
// If none, then try next lower priority. Build the feed list.
while ((TanksWithFuel == 0) && (CurrentPriority <= Propulsion->GetNumTanks())) {
for (i=0; i<Propulsion->GetNumTanks(); i++) {
if (SourceTanks[i] != 0) {
Tank = Propulsion->GetTank(i);
if (Tank->GetType() == FGTank::ttFUEL) {
if ((Tank->GetContents() > 0.0) && ((unsigned int)Tank->GetPriority() == CurrentPriority)) {
++TanksWithFuel;
FeedList.push_back(i);
}
} else {
cerr << "No oxidizer tanks should be used for this engine type." << endl;
}
}
}
if (TanksWithFuel == 0) CurrentPriority++;
}
// No fuel found at any priority!
if (TanksWithFuel == 0) {
Starved = true;
return;
}
// Remove equal amount of fuel from each feed tank.
FuelNeeded = FuelToBurn/TanksWithFuel;
for (i=0; i<FeedList.size(); i++) {
Tank = Propulsion->GetTank(FeedList[i]);
Tank->Drain(FuelNeeded);
}
FuelUsedLbs += FuelToBurn;
FuelFlowRate = SLFuelFlowMax*PctPower;
FuelExpended = FuelFlowRate*in.TotalDeltaT;
if (!Starved) FuelUsedLbs += FuelExpended;
return FuelExpended;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGEngine::CalcFuelNeed(void)
unsigned int FGEngine::GetSourceTank(unsigned int i) const
{
double dT = FDMExec->GetDeltaT()*Propulsion->GetRate();
FuelFlowRate = SLFuelFlowMax*PctPower;
FuelExpended = FuelFlowRate*dT;
return FuelExpended;
if (i >= 0 && i < SourceTanks.size()) {
return SourceTanks[i];
} else {
throw("No such source tank is available for this engine");
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -269,14 +194,14 @@ void FGEngine::SetPlacement(FGColumnVector3& location, FGColumnVector3& orientat
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGEngine::AddFeedTank(int tkID, int priority)
double FGEngine::GetThrust(void) const
{
SourceTanks[tkID] = priority;
return Thruster->GetThrust();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3& FGEngine::GetBodyForces(void)
FGColumnVector3& FGEngine::GetBodyForces(void)
{
return Thruster->GetBodyForces();
}
@ -290,6 +215,23 @@ FGColumnVector3& FGEngine::GetMoments(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGEngine::LoadThrusterInputs()
{
Thruster->in.TotalDeltaT = in.TotalDeltaT;
Thruster->in.H_agl = in.H_agl;
Thruster->in.PQR = in.PQR;
Thruster->in.AeroPQR = in.AeroPQR;
Thruster->in.AeroUVW = in.AeroUVW;
Thruster->in.Density = in.Density;
Thruster->in.Pressure = in.Pressure;
Thruster->in.Soundspeed = in.Soundspeed;
Thruster->in.Alpha = in.alpha;
Thruster->in.Beta = in.beta;
Thruster->in.Vt = in.Vt;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGEngine::LoadThruster(Element *thruster_element)
{
string token, fullpath, localpath;
@ -339,7 +281,7 @@ bool FGEngine::LoadThruster(Element *thruster_element)
Thruster = new FGThruster( FDMExec, document, EngineNumber);
}
Thruster->SetdeltaT(FDMExec->GetDeltaT() * Propulsion->GetRate());
Thruster->SetdeltaT(in.TotalDeltaT);
Debug(2);
return true;

View file

@ -43,19 +43,19 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <vector>
#include <string>
#include "math/FGModelFunctions.h"
#include "input_output/FGXMLFileRead.h"
#include "input_output/FGXMLElement.h"
#include "models/FGFCS.h"
#include "math/FGColumnVector3.h"
#include <vector>
#include <string>
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ENGINE "$Id: FGEngine.h,v 1.23 2011/03/03 12:16:26 jberndt Exp $"
#define ID_ENGINE "$Id: FGEngine.h,v 1.27 2011/08/17 23:56:01 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -64,11 +64,6 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGFDMExec;
class FGAtmosphere;
class FGAircraft;
class FGPropagate;
class FGPropulsion;
class FGAuxiliary;
class FGThruster;
class Element;
class FGPropertyManager;
@ -118,7 +113,7 @@ CLASS DOCUMENTATION
documentation for engine and thruster classes.
</pre>
@author Jon S. Berndt
@version $Id: FGEngine.h,v 1.23 2011/03/03 12:16:26 jberndt Exp $
@version $Id: FGEngine.h,v 1.27 2011/08/17 23:56:01 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -128,7 +123,36 @@ CLASS DECLARATION
class FGEngine : public FGModelFunctions, public FGXMLFileRead
{
public:
FGEngine(FGFDMExec* exec, Element* el, int engine_number);
struct Inputs {
double SLPressure;
double Pressure;
double PressureRatio;
double Temperature;
double Density;
double DensityRatio;
double Soundspeed;
double TotalPressure;
double TotalTempearture;
double TAT_c;
double Vt;
double Vc;
double qbar;
double alpha;
double beta;
double H_agl;
FGColumnVector3 AeroUVW;
FGColumnVector3 AeroPQR;
FGColumnVector3 PQR;
vector <double> ThrottleCmd;
vector <double> MixtureCmd;
vector <double> ThrottlePos;
vector <double> MixturePos;
vector <double> PropAdvance;
vector <bool> PropFeather;
double TotalDeltaT;
};
FGEngine(FGFDMExec* exec, Element* el, int engine_number, struct Inputs& input);
virtual ~FGEngine();
enum EngineType {etUnknown, etRocket, etPiston, etTurbine, etTurboprop, etElectric};
@ -139,13 +163,12 @@ public:
// Engine controls
virtual double GetThrottleMin(void) { return MinThrottle; }
virtual double GetThrottleMax(void) { return MaxThrottle; }
virtual double GetThrottle(void) { return Throttle; }
virtual double GetMixture(void) { return Mixture; }
virtual bool GetStarter(void) { return Starter; }
virtual double getFuelFlow_gph () const {return FuelFlow_gph;}
virtual double getFuelFlow_pph () const {return FuelFlow_pph;}
virtual double GetFuelFlowRate(void) const {return FuelFlowRate;}
virtual double GetFuelFlowRateGPH(void) const {return FuelFlowRate*3600/6.02;}
virtual double GetFuelUsedLbs(void) const {return FuelUsedLbs;}
virtual bool GetStarved(void) { return Starved; }
virtual bool GetRunning(void) const { return Running; }
@ -156,7 +179,6 @@ public:
virtual void SetRunning(bool bb) { Running=bb; }
virtual void SetName(string name) { Name = name; }
virtual void AddFeedTank(int tkID, int priority);
virtual void SetFuelFreeze(bool f) { FuelFreeze = f; }
virtual void SetStarter(bool s) { Starter = s; }
@ -169,9 +191,19 @@ public:
/** Calculates the thrust of the engine, and other engine functions. */
virtual void Calculate(void) = 0;
virtual double GetThrust(void) const;
/// Sets engine placement information
virtual void SetPlacement(FGColumnVector3& location, FGColumnVector3& orientation);
/** The fuel need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total fuel requirement for this engine in pounds. */
virtual double CalcFuelNeed(void);
virtual double CalcOxidizerNeed(void) {return 0.0;}
virtual double GetPowerAvailable(void) {return 0.0;};
virtual FGColumnVector3& GetBodyForces(void);
@ -180,22 +212,22 @@ public:
bool LoadThruster(Element *el);
FGThruster* GetThruster(void) {return Thruster;}
unsigned int GetSourceTank(unsigned int i) const;
unsigned int GetNumSourceTanks() const {return SourceTanks.size();}
virtual std::string GetEngineLabels(const std::string& delimiter) = 0;
virtual std::string GetEngineValues(const std::string& delimiter) = 0;
struct Inputs& in;
void LoadThrusterInputs();
protected:
/** Reduces the fuel in the active tanks by the amount required.
This function should be called from within the
derived class' Calculate() function before any other calculations are
done. This base class method removes fuel from the fuel tanks as
appropriate, and sets the starved flag if necessary. */
virtual void ConsumeFuel(void);
/** The fuel need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total fuel requirement for this engine in pounds. */
virtual double CalcFuelNeed(void);
appropriate, and sets the starved flag if necessary. * /
virtual void ConsumeFuel(void); */
FGPropertyManager* PropertyManager;
std::string Name;
@ -208,8 +240,6 @@ protected:
double MaxThrottle;
double MinThrottle;
double Throttle;
double Mixture;
double FuelExpended;
double FuelFlowRate;
double PctPower;
@ -221,16 +251,9 @@ protected:
double FuelFlow_gph;
double FuelFlow_pph;
double FuelDensity;
double FuelUsedLbs;
FGFDMExec* FDMExec;
FGAtmosphere* Atmosphere;
FGFCS* FCS;
FGPropulsion* Propulsion;
FGAircraft* Aircraft;
FGPropagate* Propagate;
FGAuxiliary* Auxiliary;
FGThruster* Thruster;
std::vector <int> SourceTanks;

View file

@ -40,20 +40,20 @@ and the cg.
*/
#include "FGForce.h"
#include "FGFDMExec.h"
#include "models/FGAircraft.h"
#include "models/FGPropagate.h"
#include "models/FGMassBalance.h"
#include "models/FGAerodynamics.h"
#include <iostream>
#include <cstdlib>
#include "FGForce.h"
#include "FGFDMExec.h"
#include "models/FGPropagate.h"
#include "models/FGMassBalance.h"
#include "models/FGAuxiliary.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGForce.cpp,v 1.15 2011/02/17 00:20:52 jberndt Exp $";
static const char *IdSrc = "$Id: FGForce.cpp,v 1.16 2011/08/04 12:46:32 jberndt Exp $";
static const char *IdHdr = ID_FORCE;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -110,7 +110,7 @@ FGMatrix33 FGForce::Transform(void)
{
switch(ttype) {
case tWindBody:
return fdmex->GetAerodynamics()->GetTw2b();
return fdmex->GetAuxiliary()->GetTw2b();
case tLocalBody:
return fdmex->GetPropagate()->GetTl2b();
case tCustom:

View file

@ -40,14 +40,13 @@ INCLUDES
#include <cstdlib>
#include "FGNozzle.h"
#include "models/FGAtmosphere.h"
#include "input_output/FGXMLElement.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGNozzle.cpp,v 1.13 2009/10/26 03:49:58 jberndt Exp $";
static const char *IdSrc = "$Id: FGNozzle.cpp,v 1.14 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_NOZZLE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -64,14 +63,7 @@ FGNozzle::FGNozzle(FGFDMExec* FDMExec, Element* nozzle_element, int num)
cerr << "Fatal Error: Nozzle exit area must be given in nozzle config file." << endl;
exit(-1);
}
/*
if (nozzle_element->FindElement("pe"))
PE = nozzle_element->FindElementValueAsNumberConvertTo("pe", "PSF");
else {
cerr << "Fatal Error: Nozzle exit pressure must be given in nozzle config file." << endl;
exit(-1);
}
*/
Thrust = 0;
Type = ttNozzle;
@ -89,8 +81,7 @@ FGNozzle::~FGNozzle()
double FGNozzle::Calculate(double vacThrust)
{
double pAtm = fdmex->GetAtmosphere()->GetPressure();
Thrust = max((double)0.0, vacThrust - pAtm*Area);
Thrust = max((double)0.0, vacThrust - in.Pressure*Area);
vFn(1) = Thrust * cos(ReverserAngle);

View file

@ -44,7 +44,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_NOZZLE "$Id: FGNozzle.h,v 1.8 2009/10/26 03:49:58 jberndt Exp $";
#define ID_NOZZLE "$Id: FGNozzle.h,v 1.9 2011/08/03 03:21:06 jberndt Exp $";
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -75,7 +75,7 @@ CLASS DOCUMENTATION
All parameters MUST be specified.
@author Jon S. Berndt
@version $Id: FGNozzle.h,v 1.8 2009/10/26 03:49:58 jberndt Exp $
@version $Id: FGNozzle.h,v 1.9 2011/08/03 03:21:06 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -83,7 +83,6 @@ CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGNozzle : public FGThruster {
public:
/// Constructor
FGNozzle(FGFDMExec* exec, Element* el, int num = 0);

View file

@ -40,28 +40,25 @@ HISTORY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <sstream>
#include "FGPiston.h"
#include "models/FGAtmosphere.h"
#include "models/FGAuxiliary.h"
#include "models/FGPropulsion.h"
#include "FGPropeller.h"
#include <iostream>
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGPiston.cpp,v 1.58 2011/06/13 15:23:09 jentron Exp $";
static const char *IdSrc = "$Id: FGPiston.cpp,v 1.64 2011/08/04 13:45:42 jberndt Exp $";
static const char *IdHdr = ID_PISTON;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
: FGEngine(exec, el, engine_number),
FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number, struct Inputs& input)
: FGEngine(exec, el, engine_number, input),
R_air(287.3), // Gas constant for air J/Kg/K
rho_fuel(800), // estimate
calorific_value_fuel(47.3e6), // J/Kg
@ -69,12 +66,13 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
Cp_fuel(1700),
standard_pressure(101320.73)
{
Element *table_element;
string token;
string name="";
// Defaults and initializations
Type = etPiston;
dt = FDMExec->GetDeltaT();
// These items are read from the configuration file
// Defaults are from a Lycoming O-360, more or less
@ -104,6 +102,8 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
// These are internal program variables
Lookup_Combustion_Efficiency = 0;
Mixture_Efficiency_Correlation = 0;
crank_counter = 0;
Magnetos = 0;
minMAP = 21950;
@ -135,39 +135,6 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
BoostSwitchPressure[i] = 0.0;
}
// 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;
Mixture_Efficiency_Correlation = new FGTable(15);
*Mixture_Efficiency_Correlation << 0.05000 << 0.00000;
*Mixture_Efficiency_Correlation << 0.05137 << 0.00862;
*Mixture_Efficiency_Correlation << 0.05179 << 0.21552;
*Mixture_Efficiency_Correlation << 0.05430 << 0.48276;
*Mixture_Efficiency_Correlation << 0.05842 << 0.70690;
*Mixture_Efficiency_Correlation << 0.06312 << 0.83621;
*Mixture_Efficiency_Correlation << 0.06942 << 0.93103;
*Mixture_Efficiency_Correlation << 0.07786 << 1.00000;
*Mixture_Efficiency_Correlation << 0.08845 << 1.00000;
*Mixture_Efficiency_Correlation << 0.09270 << 0.98276;
*Mixture_Efficiency_Correlation << 0.10120 << 0.93103;
*Mixture_Efficiency_Correlation << 0.11455 << 0.72414;
*Mixture_Efficiency_Correlation << 0.12158 << 0.45690;
*Mixture_Efficiency_Correlation << 0.12435 << 0.23276;
*Mixture_Efficiency_Correlation << 0.12500 << 0.00000;
// Read inputs from engine data file where present.
if (el->FindElement("minmp")) // Should have ELSE statement telling default value used?
@ -252,6 +219,21 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
RatedAltitude[2] = el->FindElementValueAsNumberConvertTo("ratedaltitude3", "FT");
}
while(table_element = el->FindNextElement("table")) {
name = table_element->GetAttributeValue("name");
try {
if (name == "COMBUSTION") {
Lookup_Combustion_Efficiency = new FGTable(PropertyManager, table_element);
} else if (name == "MIXTURE") {
Mixture_Efficiency_Correlation = new FGTable(PropertyManager, table_element);
} else {
cerr << "Unknown table type: " << name << " in piston engine definition." << endl;
}
} catch (std::string str) {
throw("Error loading piston engine table:" + name + ". " + str);
}
}
StarterHP = sqrt(MaxHP) * 0.4;
displacement_SI = Displacement * in3tom3;
RatedMeanPistonSpeed_fps = ( MaxRPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS
@ -286,7 +268,6 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
*
*
*/
if(Z_airbox < 0.0){
double Ze=PeakMeanPistonSpeed_fps/RatedMeanPistonSpeed_fps; // engine impedence
Z_airbox = (standard_pressure *Ze / maxMAP) - Ze; // impedence of airbox
@ -295,6 +276,44 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
Z_throttle=(PeakMeanPistonSpeed_fps/((IdleRPM * Stroke) / 360))*(standard_pressure/minMAP - 1) - Z_airbox;
// Z_throttle=(MaxRPM/IdleRPM )*(standard_pressure/minMAP+2); // Constant for Throttle impedence
// Default tables if not provided in the configuration file
if(Lookup_Combustion_Efficiency == 0) {
// 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;
}
// First column is Fuel/Air Ratio, second is neta (mixture efficiency)
if( Mixture_Efficiency_Correlation == 0) {
Mixture_Efficiency_Correlation = new FGTable(15);
*Mixture_Efficiency_Correlation << 0.05000 << 0.00000;
*Mixture_Efficiency_Correlation << 0.05137 << 0.00862;
*Mixture_Efficiency_Correlation << 0.05179 << 0.21552;
*Mixture_Efficiency_Correlation << 0.05430 << 0.48276;
*Mixture_Efficiency_Correlation << 0.05842 << 0.70690;
*Mixture_Efficiency_Correlation << 0.06312 << 0.83621;
*Mixture_Efficiency_Correlation << 0.06942 << 0.93103;
*Mixture_Efficiency_Correlation << 0.07786 << 1.00000;
*Mixture_Efficiency_Correlation << 0.08845 << 1.00000;
*Mixture_Efficiency_Correlation << 0.09270 << 0.98276;
*Mixture_Efficiency_Correlation << 0.10120 << 0.93103;
*Mixture_Efficiency_Correlation << 0.11455 << 0.72414;
*Mixture_Efficiency_Correlation << 0.12158 << 0.45690;
*Mixture_Efficiency_Correlation << 0.12435 << 0.23276;
*Mixture_Efficiency_Correlation << 0.12500 << 0.00000;
}
string property_name, base_property_name;
base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
property_name = base_property_name + "/power-hp";
@ -347,13 +366,13 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
// But we can also make a reasonable estimate, as below.
BoostSwitchAltitude[i] = RatedAltitude[i] + 1000;
}
BoostSwitchPressure[i] = Atmosphere->GetPressure(BoostSwitchAltitude[i]) * psftopa;
BoostSwitchPressure[i] = GetStdPressure100K(BoostSwitchAltitude[i]) * psftopa;
//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() * psftopa + RatedBoost[i] * 6895; // psi*6895 = Pa.
RatedMAP[i] = standard_pressure + RatedBoost[i] * 6895; // 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.
@ -363,7 +382,7 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number)
TakeoffMAP[i] = RatedMAP[i];
bTakeoffBoost = false;
}
BoostMul[i] = RatedMAP[i] / (Atmosphere->GetPressure(RatedAltitude[i]) * psftopa);
BoostMul[i] = RatedMAP[i] / (GetStdPressure100K(RatedAltitude[i]) * psftopa);
}
@ -391,10 +410,10 @@ void FGPiston::ResetToIC(void)
{
FGEngine::ResetToIC();
ManifoldPressure_inHg = Atmosphere->GetPressure() * psftoinhg; // psf to in Hg
MAP = Atmosphere->GetPressure() * psftopa;
ManifoldPressure_inHg = in.Pressure * psftoinhg; // psf to in Hg
MAP = in.Pressure * psftopa;
TMAP = MAP;
double airTemperature_degK = RankineToKelvin(Atmosphere->GetTemperature());
double airTemperature_degK = RankineToKelvin(in.Temperature);
OilTemp_degK = airTemperature_degK;
CylinderHeadTemp_degK = airTemperature_degK;
ExhaustGasTemp_degK = airTemperature_degK;
@ -405,26 +424,22 @@ void FGPiston::ResetToIC(void)
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPiston::Calculate(void)
{
RunPreFunctions();
if (FuelFlow_gph > 0.0) ConsumeFuel();
Throttle = FCS->GetThrottlePos(EngineNumber);
Mixture = FCS->GetMixturePos(EngineNumber);
// Input values.
p_amb = Atmosphere->GetPressure() * psftopa;
double p = Auxiliary->GetTotalPressure() * psftopa;
p_amb = in.Pressure * psftopa;
double p = in.TotalPressure * psftopa;
p_ram = (p - p_amb) * Ram_Air_Factor + p_amb;
T_amb = RankineToKelvin(Atmosphere->GetTemperature());
T_amb = RankineToKelvin(in.Temperature);
RunPreFunctions();
RPM = Thruster->GetRPM() * Thruster->GetGearRatio();
MeanPistonSpeed_fps = ( RPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS
IAS = Auxiliary->GetVcalibratedKTS();
IAS = in.Vc;
doEngineStartup();
if (Boosted) doBoostControl();
@ -449,10 +464,11 @@ void FGPiston::Calculate(void)
doOilPressure();
if (Thruster->GetType() == FGThruster::ttPropeller) {
((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber));
((FGPropeller*)Thruster)->SetFeather(FCS->GetPropFeather(EngineNumber));
((FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
((FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
}
LoadThrusterInputs();
Thruster->Calculate(HP * hptoftlbssec);
RunPostFunctions();
@ -462,22 +478,19 @@ void FGPiston::Calculate(void)
double FGPiston::CalcFuelNeed(void)
{
double dT = FDMExec->GetDeltaT() * Propulsion->GetRate();
FuelExpended = FuelFlowRate * dT;
FuelExpended = FuelFlowRate * in.TotalDeltaT;
return FuelExpended;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int FGPiston::InitRunning(void) {
int FGPiston::InitRunning(void)
{
Magnetos=3;
p_amb = Atmosphere->GetPressure() * psftopa;
double mix= p_amb / (101325.0*1.3);
FCS->SetMixturePos(EngineNumber, mix);
Thruster->SetRPM( 2.*IdleRPM/Thruster->GetGearRatio() );
//Thruster->SetRPM( 1000 );
Running=true;
// cout <<"Set Running in FGPiston. RPM:" << Thruster->GetRPM()*Thruster->GetGearRatio() <<" Pressure:"<<p_amb<<" Mixture:"<< mix <<endl;
in.MixtureCmd[EngineNumber] = in.PressureRatio/1.3;
in.MixturePos[EngineNumber] = in.PressureRatio/1.3;
Thruster->SetRPM( 2.0*IdleRPM/Thruster->GetGearRatio() );
Running = true;
return 1;
}
@ -597,13 +610,18 @@ void FGPiston::doBoostControl(void)
void FGPiston::doMAP(void)
{
double Zt = (1-Throttle)*(1-Throttle)*Z_throttle; // throttle impedence
double Zt = (1 - in.ThrottlePos[EngineNumber])*(1 - in.ThrottlePos[EngineNumber])*Z_throttle; // throttle impedence
double Ze= MeanPistonSpeed_fps > 0 ? PeakMeanPistonSpeed_fps/MeanPistonSpeed_fps : 999999; // engine impedence
double map_coefficient = Ze/(Ze+Z_airbox+Zt);
// Add a one second lag to manifold pressure changes
double dMAP = (TMAP - p_ram * map_coefficient) * dt;
double dMAP=0;
if (in.TotalDeltaT > 0.0)
dMAP = (TMAP - p_ram * map_coefficient) * in.TotalDeltaT;
else
dMAP = (TMAP - p_ram * map_coefficient) / 120;
TMAP -=dMAP;
// Find the mean effective pressure required to achieve this manifold pressure
@ -620,7 +638,7 @@ void FGPiston::doMAP(void)
bool bTakeoffPos = false;
if (bTakeoffBoost) {
if (Throttle > 0.98) {
if (in.ThrottlePos[EngineNumber] > 0.98) {
bTakeoffPos = true;
}
}
@ -661,7 +679,7 @@ void FGPiston::doAirFlow(void)
// loss of volumentric efficiency due to difference between MAP and exhaust pressure
// Eq 6-10 from The Internal Combustion Engine - Charles Taylor Vol 1
double ve =((gamma-1)/gamma) +( CompressionRatio -(p_amb/MAP))/(gamma*( CompressionRatio - 1));
// FGAtmosphere::GetDensity() * FGJSBBase::m3toft3 / FGJSBBase::kgtoslug;
rho_air = p_amb / (R_air * T_amb);
double swept_volume = (displacement_SI * (RPM/60)) / 2;
double v_dot_air = swept_volume * volumetric_efficiency *ve;
@ -682,12 +700,18 @@ void FGPiston::doAirFlow(void)
void FGPiston::doFuelFlow(void)
{
double thi_sea_level = 1.3 * Mixture; // Allows an AFR of infinity:1 to 11.3075:1
double thi_sea_level = 1.3 * in.MixturePos[EngineNumber]; // Allows an AFR of infinity:1 to 11.3075:1
equivalence_ratio = thi_sea_level * 101325.0 / p_amb;
// double AFR = 10+(12*(1-Mixture));// mixture 10:1 to 22:1
// double AFR = 10+(12*(1-in.Mixture[EngineNumber]));// mixture 10:1 to 22:1
// m_dot_fuel = m_dot_air / AFR;
m_dot_fuel = (m_dot_air * equivalence_ratio) / 14.7;
FuelFlowRate = m_dot_fuel * 2.2046; // kg to lb
if(Starved) // There is no fuel, so zero out the flows we've calculated so far
{
equivalence_ratio = 0.0;
FuelFlowRate = 0.0;
m_dot_fuel = 0.0;
}
FuelFlow_pph = FuelFlowRate * 3600; // seconds to hours
FuelFlow_gph = FuelFlow_pph / 6.0; // Assumes 6 lbs / gallon
}
@ -773,8 +797,12 @@ void FGPiston::doEGT(void)
ExhaustGasTemp_degK = T_amb + delta_T_exhaust;
} else { // Drop towards ambient - guess an appropriate time constant for now
combustion_efficiency = 0;
dEGTdt = (RankineToKelvin(Atmosphere->GetTemperature()) - ExhaustGasTemp_degK) / 100.0;
delta_T_exhaust = dEGTdt * dt;
dEGTdt = (RankineToKelvin(in.Temperature) - ExhaustGasTemp_degK) / 100.0;
if (in.TotalDeltaT > 0.0)
delta_T_exhaust = dEGTdt * in.TotalDeltaT;
else
delta_T_exhaust = dEGTdt / 120;
ExhaustGasTemp_degK += delta_T_exhaust;
}
}
@ -812,8 +840,12 @@ void FGPiston::doCHT(void)
double HeatCapacityCylinderHead = CpCylinderHead * MassCylinderHead;
CylinderHeadTemp_degK +=
(dqdt_cylinder_head / HeatCapacityCylinderHead) * dt;
if (in.TotalDeltaT > 0.0)
CylinderHeadTemp_degK +=
(dqdt_cylinder_head / HeatCapacityCylinderHead) * in.TotalDeltaT;
else
CylinderHeadTemp_degK +=
(dqdt_cylinder_head / HeatCapacityCylinderHead) / 120.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -847,7 +879,10 @@ void FGPiston::doOilTemperature(void)
double dOilTempdt = (target_oil_temp - OilTemp_degK) / time_constant;
OilTemp_degK += (dOilTempdt * dt);
if (in.TotalDeltaT > 0.0)
OilTemp_degK += (dOilTempdt * in.TotalDeltaT);
else
OilTemp_degK += (dOilTempdt / 120.0);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -875,6 +910,30 @@ void FGPiston::doOilPressure(void)
OilPressure_psi += (Design_Oil_Temp - OilTemp_degK) * Oil_Viscosity_Index * OilPressure_psi / Oil_Press_Relief_Valve;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// This is a local copy of the same function in FGStandardAtmosphere.
double FGPiston::GetStdPressure100K(double altitude) const
{
// Limit this equation to input altitudes of 100000 ft.
if (altitude > 100000.0) altitude = 100000.0;
double alt[5];
const double coef[5] = { 2116.217,
-7.648932746E-2,
1.0925498604E-6,
-7.1135726027E-12,
1.7470331356E-17 };
alt[0] = 1;
for (int pwr=1; pwr<=4; pwr++) alt[pwr] = alt[pwr-1]*altitude;
double press = 0.0;
for (int ctr=0; ctr<=4; ctr++) press += coef[ctr]*alt[ctr];
return press;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPiston::GetEngineLabels(const string& delimiter)

View file

@ -46,7 +46,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_PISTON "$Id: FGPiston.h,v 1.29 2011/06/16 16:32:10 jentron Exp $";
#define ID_PISTON "$Id: FGPiston.h,v 1.31 2011/08/04 13:45:42 jberndt Exp $";
#define FG_MAX_BOOST_SPEEDS 3
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -198,7 +198,7 @@ boostspeed they refer to:
@author David Megginson (initial porting and additional code)
@author Ron Jensen (additional engine code)
@see Taylor, Charles Fayette, "The Internal Combustion Engine in Theory and Practice"
@version $Id: FGPiston.h,v 1.29 2011/06/16 16:32:10 jentron Exp $
@version $Id: FGPiston.h,v 1.31 2011/08/04 13:45:42 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -209,7 +209,7 @@ class FGPiston : public FGEngine
{
public:
/// Constructor
FGPiston(FGFDMExec* exec, Element* el, int engine_number);
FGPiston(FGFDMExec* exec, Element* el, int engine_number, struct Inputs& input);
/// Destructor
~FGPiston();
@ -244,9 +244,6 @@ private:
double FMEPDynamic;
double FMEPStatic;
// timestep
double dt;
void doEngineStartup(void);
void doBoostControl(void);
void doMAP(void);
@ -257,6 +254,7 @@ private:
void doCHT(void);
void doOilPressure(void);
void doOilTemperature(void);
double GetStdPressure100K(double altitude) const;
int InitRunning(void);

View file

@ -39,16 +39,13 @@ INCLUDES
#include <sstream>
#include "FGPropeller.h"
#include "models/FGPropagate.h"
#include "models/FGAtmosphere.h"
#include "models/FGAuxiliary.h"
#include "input_output/FGXMLElement.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGPropeller.cpp,v 1.34 2011/06/16 14:54:06 jentron Exp $";
static const char *IdSrc = "$Id: FGPropeller.cpp,v 1.36 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_PROPELLER;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -195,19 +192,18 @@ FGPropeller::~FGPropeller()
double FGPropeller::Calculate(double EnginePower)
{
double omega, alpha, beta, PowerAvailable;
double omega, PowerAvailable;
double Vel = fdmex->GetAuxiliary()->GetAeroUVW(eU);
double rho = fdmex->GetAtmosphere()->GetDensity();
double Vel = in.AeroUVW(eU);
double rho = in.Density;
double RPS = RPM/60.0;
PowerAvailable = EnginePower - GetPowerRequired();
// Calculate helical tip Mach
double Area = 0.25*Diameter*Diameter*M_PI;
double Vtip = RPS * Diameter * M_PI;
HelicalTipMach = sqrt(Vtip*Vtip + Vel*Vel) /
fdmex->GetAtmosphere()->GetSoundSpeed();
HelicalTipMach = sqrt(Vtip*Vtip + Vel*Vel) / in.Soundspeed;
PowerAvailable = EnginePower - GetPowerRequired();
if (RPS > 0.0) J = Vel / (Diameter * RPS); // Calculate J normally
else J = Vel / Diameter;
@ -225,10 +221,8 @@ double FGPropeller::Calculate(double EnginePower)
if (CtMach) ThrustCoeff *= CtMach->GetValue(HelicalTipMach);
if (P_Factor > 0.0001) {
alpha = fdmex->GetAuxiliary()->Getalpha();
beta = fdmex->GetAuxiliary()->Getbeta();
SetActingLocationY( GetLocationY() + P_Factor*alpha*Sense);
SetActingLocationZ( GetLocationZ() + P_Factor*beta*Sense);
SetActingLocationY( GetLocationY() + P_Factor*in.Alpha*Sense);
SetActingLocationZ( GetLocationZ() + P_Factor*in.Beta*Sense);
}
Thrust = ThrustCoeff*RPS*RPS*D4*rho;
@ -258,7 +252,7 @@ double FGPropeller::Calculate(double EnginePower)
// Transform Torque and momentum first, as PQR is used in this
// equation and cannot be transformed itself.
vMn = fdmex->GetPropagate()->GetPQR()*(Transform()*vH) + Transform()*vTorque;
vMn = in.PQR*(Transform()*vH) + Transform()*vTorque;
return Thrust; // return thrust in pounds
}
@ -268,8 +262,8 @@ double FGPropeller::Calculate(double EnginePower)
double FGPropeller::GetPowerRequired(void)
{
double cPReq, J;
double rho = fdmex->GetAtmosphere()->GetDensity();
double Vel = fdmex->GetAuxiliary()->GetAeroUVW(eU);
double rho = in.Density;
double Vel = in.AeroUVW(eU);
double RPS = RPM / 60.0;
if (RPS != 0.0) J = Vel / (Diameter * RPS);

View file

@ -41,23 +41,21 @@ INCLUDES
#include <iostream>
#include <sstream>
#include "FGRocket.h"
#include "models/FGPropulsion.h"
#include "FGThruster.h"
#include "FGTank.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGRocket.cpp,v 1.23 2011/01/24 13:01:56 jberndt Exp $";
static const char *IdSrc = "$Id: FGRocket.cpp,v 1.26 2011/08/04 13:45:42 jberndt Exp $";
static const char *IdHdr = ID_ROCKET;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number)
: FGEngine(exec, el, engine_number)
FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number, struct Inputs& input)
: FGEngine(exec, el, engine_number, input)
{
Type = etRocket;
Element* thrust_table_element = 0;
@ -66,6 +64,7 @@ FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number)
previousFuelNeedPerTank = 0.0;
previousOxiNeedPerTank = 0.0;
PropellantFlowRate = 0.0;
TotalPropellantExpended = 0.0;
FuelFlowRate = FuelExpended = 0.0;
OxidizerFlowRate = OxidizerExpended = 0.0;
SLOxiFlowMax = SLFuelFlowMax = 0.0;
@ -126,14 +125,10 @@ void FGRocket::Calculate(void)
{
if (FDMExec->IntegrationSuspended()) return;
double dT = FDMExec->GetDeltaT()*Propulsion->GetRate();
RunPreFunctions();
if (!Flameout && !Starved) ConsumeFuel();
PropellantFlowRate = (FuelExpended + OxidizerExpended)/dT;
Throttle = FCS->GetThrottlePos(EngineNumber);
PropellantFlowRate = (FuelExpended + OxidizerExpended)/in.TotalDeltaT;
TotalPropellantExpended += FuelExpended + OxidizerExpended;
// If there is a thrust table, it is a function of propellant burned. The
// engine is started when the throttle is advanced to 1.0. After that, it
@ -141,30 +136,23 @@ void FGRocket::Calculate(void)
if (ThrustTable != 0L) { // Thrust table given -> Solid fuel used
if ((Throttle == 1 || BurnTime > 0.0 ) && !Starved) {
double TotalEngineFuelBurned=0.0;
for (int i=0; i<(int)SourceTanks.size(); i++) {
FGTank* tank = Propulsion->GetTank(i);
if (SourceTanks[i] == 1) {
TotalEngineFuelBurned += tank->GetCapacity() - tank->GetContents();
}
}
if ((in.ThrottlePos[EngineNumber] == 1 || BurnTime > 0.0 ) && !Starved) {
VacThrust = ThrustTable->GetValue(TotalEngineFuelBurned)
VacThrust = ThrustTable->GetValue(TotalPropellantExpended)
* (ThrustVariation + 1)
* (TotalIspVariation + 1);
if (BurnTime <= BuildupTime && BuildupTime > 0.0) {
VacThrust *= sin((BurnTime/BuildupTime)*M_PI/2.0);
// VacThrust *= (1-cos((BurnTime/BuildupTime)*M_PI))/2.0; // 1 - cos approach
}
BurnTime += FDMExec->GetDeltaT(); // Increment burn time
BurnTime += in.TotalDeltaT; // Increment burn time
} else {
VacThrust = 0.0;
}
} else { // liquid fueled rocket assumed
if (Throttle < MinThrottle || Starved) { // Combustion not supported
if (in.ThrottlePos[EngineNumber] < MinThrottle || Starved) { // Combustion not supported
PctPower = 0.0; // desired thrust
Flameout = true;
@ -172,12 +160,9 @@ void FGRocket::Calculate(void)
} else { // Calculate thrust
// This is nonsensical. Max throttle should be assumed to be 1.0. One might
// conceivably have a throttle setting > 1.0 for some rocket engines. But, 1.0
// should always be the default.
// PctPower = Throttle / MaxThrottle; // Min and MaxThrottle range from 0.0 to 1.0, normally.
PctPower = Throttle;
PctPower = in.ThrottlePos[EngineNumber];
Flameout = false;
VacThrust = Isp * PropellantFlowRate;
@ -185,79 +170,12 @@ void FGRocket::Calculate(void)
} // End thrust calculations
It += Thruster->Calculate(VacThrust) * dT;
LoadThrusterInputs();
It += Thruster->Calculate(VacThrust) * in.TotalDeltaT;
RunPostFunctions();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// This overrides the base class ConsumeFuel() function, for special rocket
// engine processing.
void FGRocket::ConsumeFuel(void)
{
unsigned int i;
FGTank* Tank;
bool haveOxTanks = false;
double Fshortage=0, Oshortage=0, TanksWithFuel=0, TanksWithOxidizer=0;
if (FuelFreeze) return;
if (FDMExec->GetTrimStatus()) return;
// Count how many assigned tanks have fuel for this engine at this time.
// If there is/are fuel tanks but no oxidizer tanks, this indicates
// a solid rocket is being modeled.
for (i=0; i<SourceTanks.size(); i++) {
Tank = Propulsion->GetTank(i);
switch(Tank->GetType()) {
case FGTank::ttFUEL:
if (Tank->GetContents() > 0.0 && Tank->GetSelected() && SourceTanks[i] > 0) ++TanksWithFuel;
break;
case FGTank::ttOXIDIZER:
if (Tank->GetSelected() && SourceTanks[i] > 0) {
haveOxTanks = true;
if (Tank->GetContents() > 0.0) ++TanksWithOxidizer;
}
break;
}
}
// If this engine has burned out, it is starved.
if (TanksWithFuel==0 || (haveOxTanks && TanksWithOxidizer==0)) {
Starved = true;
return;
}
// Expend fuel from the engine's tanks if the tank is selected as a source
// for this engine.
double fuelNeedPerTank = 0;
double oxiNeedPerTank = 0;
if (TanksWithFuel > 0) fuelNeedPerTank = CalcFuelNeed()/TanksWithFuel;
if (TanksWithOxidizer > 0) oxiNeedPerTank = CalcOxidizerNeed()/TanksWithOxidizer;
for (i=0; i<SourceTanks.size(); i++) {
Tank = Propulsion->GetTank(i);
if ( ! Tank->GetSelected() || SourceTanks[i] == 0) continue; // If this tank is not selected as a source, skip it.
switch(Tank->GetType()) {
case FGTank::ttFUEL:
Fshortage += Tank->Drain(2.0*fuelNeedPerTank - previousFuelNeedPerTank);
previousFuelNeedPerTank = fuelNeedPerTank;
break;
case FGTank::ttOXIDIZER:
Oshortage += Tank->Drain(2.0*oxiNeedPerTank - previousOxiNeedPerTank);
previousOxiNeedPerTank = oxiNeedPerTank;
break;
}
}
if (Fshortage < 0.00 || (haveOxTanks && Oshortage < 0.00)) Starved = true;
else Starved = false;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// The FuelFlowRate can be affected by the TotalIspVariation value (settable
@ -267,8 +185,6 @@ void FGRocket::ConsumeFuel(void)
double FGRocket::CalcFuelNeed(void)
{
double dT = FDMExec->GetDeltaT()*Propulsion->GetRate();
if (ThrustTable != 0L) { // Thrust table given - infers solid fuel
FuelFlowRate = VacThrust/Isp; // This calculates wdot (weight flow rate in lbs/sec)
FuelFlowRate /= (1 + TotalIspVariation);
@ -276,7 +192,7 @@ double FGRocket::CalcFuelNeed(void)
FuelFlowRate = SLFuelFlowMax*PctPower;
}
FuelExpended = FuelFlowRate*dT; // For this time step ...
FuelExpended = FuelFlowRate * in.TotalDeltaT; // For this time step ...
return FuelExpended;
}
@ -284,9 +200,8 @@ double FGRocket::CalcFuelNeed(void)
double FGRocket::CalcOxidizerNeed(void)
{
double dT = FDMExec->GetDeltaT()*Propulsion->GetRate();
OxidizerFlowRate = SLOxiFlowMax*PctPower;
OxidizerExpended = OxidizerFlowRate*dT;
OxidizerFlowRate = SLOxiFlowMax * PctPower;
OxidizerExpended = OxidizerFlowRate * in.TotalDeltaT;
return OxidizerExpended;
}

View file

@ -46,7 +46,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_ROCKET "$Id: FGRocket.h,v 1.14 2010/08/21 18:08:25 jberndt Exp $"
#define ID_ROCKET "$Id: FGRocket.h,v 1.17 2011/08/04 13:45:42 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -118,13 +118,11 @@ for the rocket engine to be throttle up to 1. At that time, the solid rocket
fuel begins burning and thrust is provided.
@author Jon S. Berndt
$Id: FGRocket.h,v 1.14 2010/08/21 18:08:25 jberndt Exp $
$Id: FGRocket.h,v 1.17 2011/08/04 13:45:42 jberndt Exp $
@see FGNozzle,
FGThruster,
FGForce,
FGEngine,
FGPropulsion,
FGTank
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -138,7 +136,7 @@ public:
@param exec pointer to JSBSim parent object, the FDM Executive.
@param el a pointer to the XML Element instance representing the engine.
@param engine_number engine number */
FGRocket(FGFDMExec* exec, Element *el, int engine_number);
FGRocket(FGFDMExec* exec, Element *el, int engine_number, struct FGEngine::Inputs& input);
/** Destructor */
~FGRocket(void);
@ -146,6 +144,18 @@ public:
/** Determines the thrust.*/
void Calculate(void);
/** The fuel need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total fuel requirement for this engine in pounds. */
double CalcFuelNeed(void);
/** The oxidizer need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total oxidizer requirement for this engine in pounds. */
double CalcOxidizerNeed(void);
/** Gets the total impulse of the rocket.
@return The cumulative total impulse of the rocket up to this time.*/
double GetTotalImpulse(void) const {return It;}
@ -188,25 +198,6 @@ public:
double GetTotalIspVariation(void) const {return TotalIspVariation;}
private:
/** Reduces the fuel in the active tanks by the amount required.
This function should be called from within the
derived class' Calculate() function before any other calculations are
done. This base class method removes fuel from the fuel tanks as
appropriate, and sets the starved flag if necessary. */
void ConsumeFuel(void);
/** The fuel need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total fuel requirement for this engine in pounds. */
double CalcFuelNeed(void);
/** The oxidizer need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@return Total oxidizer requirement for this engine in pounds. */
double CalcOxidizerNeed(void);
/** Returns the vacuum thrust.
@return The vacuum thrust in lbs. */
double GetVacThrust(void) const {return VacThrust;}
@ -223,6 +214,7 @@ private:
double previousFuelNeedPerTank;
double previousOxiNeedPerTank;
double OxidizerExpended;
double TotalPropellantExpended;
double SLOxiFlowMax;
double OxidizerFlowRate;
double PropellantFlowRate;

View file

@ -45,20 +45,17 @@ INCLUDES
#include <sstream>
#include "FGRotor.h"
#include "models/FGPropagate.h"
#include "models/FGAtmosphere.h"
#include "models/FGAuxiliary.h"
#include "input_output/FGXMLElement.h"
#include "models/FGMassBalance.h"
#include "input_output/FGXMLElement.h"
using namespace std;
using std::cerr;
using std::endl;
using std::ostringstream;
using std::cout;
namespace JSBSim {
static const char *IdSrc = "$Id: FGRotor.cpp,v 1.12 2011/03/10 01:35:25 dpculp Exp $";
static const char *IdSrc = "$Id: FGRotor.cpp,v 1.13 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_ROTOR;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -77,46 +74,28 @@ CLASS IMPLEMENTATION
// Constructor
FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num)
: FGThruster(exec, rotor_element, num),
// environment
dt(0.0), rho(0.002356),
// configuration parameters
Radius(0.0), BladeNum(0),
Sense(1.0), NominalRPM(0.0), ExternalRPM(0), RPMdefinition(0), ExtRPMsource(NULL),
BladeChord(0.0), LiftCurveSlope(0.0), BladeTwist(0.0), HingeOffset(0.0),
BladeFlappingMoment(0.0), BladeMassMoment(0.0), PolarMoment(0.0),
InflowLag(0.0),
TipLossB(0.0),
GroundEffectExp(0.0), GroundEffectShift(0.0),
// derived parameters
LockNumberByRho(0.0), Solidity(0.0),
// dynamic values
RPM(0.0), Omega(0.0),
beta_orient(0.0),
a0(0.0), a_1(0.0), b_1(0.0), a_dw(0.0), a1s(0.0), b1s(0.0),
H_drag(0.0), J_side(0.0), Torque(0.0), C_T(0.0),
lambda(-0.001), mu(0.0), nu(0.001), v_induced(0.0),
theta_downwash(0.0), phi_downwash(0.0),
// control
ControlMap(eMainCtrl),
CollectiveCtrl(0.0), LateralCtrl(0.0), LongitudinalCtrl(0.0),
BrakeCtrlNorm(0.0), MaxBrakePower(0.0),
// free-wheeling-unit (FWU)
FreeWheelPresent(0), FreeWheelThresh(0.0), FreeWheelTransmission(0.0)
: FGThruster(exec, rotor_element, num),
rho(0.002356), // environment
Radius(0.0), BladeNum(0), // configuration parameters
Sense(1.0), NominalRPM(0.0), ExternalRPM(0),
RPMdefinition(0), ExtRPMsource(NULL),
BladeChord(0.0), LiftCurveSlope(0.0), BladeTwist(0.0), HingeOffset(0.0),
BladeFlappingMoment(0.0), BladeMassMoment(0.0), PolarMoment(0.0),
InflowLag(0.0), TipLossB(0.0),
GroundEffectExp(0.0), GroundEffectShift(0.0),
LockNumberByRho(0.0), Solidity(0.0), // derived parameters
RPM(0.0), Omega(0.0), // dynamic values
beta_orient(0.0),
a0(0.0), a_1(0.0), b_1(0.0), a_dw(0.0),
a1s(0.0), b1s(0.0),
H_drag(0.0), J_side(0.0), Torque(0.0), C_T(0.0),
lambda(-0.001), mu(0.0), nu(0.001), v_induced(0.0),
theta_downwash(0.0), phi_downwash(0.0),
ControlMap(eMainCtrl), // control
CollectiveCtrl(0.0), LateralCtrl(0.0), LongitudinalCtrl(0.0),
BrakeCtrlNorm(0.0), MaxBrakePower(0.0),
FreeWheelPresent(0), FreeWheelThresh(0.0), // free-wheeling-unit (FWU)
FreeWheelTransmission(0.0)
{
FGColumnVector3 location(0.0, 0.0, 0.0), orientation(0.0, 0.0, 0.0);
Element *thruster_element;
@ -194,7 +173,7 @@ FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num)
// smooth out jumps in hagl reported, otherwise the ground effect
// calculation would cause jumps too. 1Hz seems sufficient.
damp_hagl = Filter(1.0,dt);
damp_hagl = Filter(1.0, dt);
// avoid too abrupt changes in power transmission
FreeWheelLag = Filter(200.0,dt);
@ -583,21 +562,14 @@ void FGRotor::CalcStatePart1(void)
double B_IC; // longitudinal (pitch) control in radians
double theta_col; // rotor collective pitch in radians
double Vt ;
FGColumnVector3 UVW_h, PQR_h;
FGColumnVector3 vHub_ca, avFus_ca;
double h_agl_ft, filtered_hagl = 0.0;
double filtered_hagl = 0.0;
double ge_factor = 1.0;
// fetch needed values from environment
Vt = fdmex->GetAuxiliary()->GetVt(); // total vehicle velocity including wind
dt = fdmex->GetDeltaT();
rho = fdmex->GetAtmosphere()->GetDensity(); // slugs/ft^3.
UVW_h = fdmex->GetAuxiliary()->GetAeroUVW();
PQR_h = fdmex->GetAuxiliary()->GetAeroPQR();
h_agl_ft = fdmex->GetPropagate()->GetDistanceAGL();
rho = in.Density; // slugs/ft^3.
double h_agl_ft = in.H_agl;
// update InvTransform, the rotor orientation could have been altered
InvTransform = Transform().Transposed();
@ -628,9 +600,9 @@ void FGRotor::CalcStatePart1(void)
// all set, start calculations
vHub_ca = hub_vel_body2ca(UVW_h, PQR_h, A_IC, B_IC);
vHub_ca = hub_vel_body2ca(in.AeroUVW, in.AeroPQR, A_IC, B_IC);
avFus_ca = fus_angvel_body2ca(PQR_h);
avFus_ca = fus_angvel_body2ca(in.AeroPQR);
calc_flow_and_thrust(theta_col, vHub_ca(eU), vHub_ca(eW), ge_factor);
@ -643,8 +615,8 @@ void FGRotor::CalcStatePart1(void)
calc_torque(theta_col);
// Fixme: only valid for a 'decent' rotor
theta_downwash = atan2( - UVW_h(eU), v_induced - UVW_h(eW));
phi_downwash = atan2( UVW_h(eV), v_induced - UVW_h(eW));
theta_downwash = atan2( -in.AeroUVW(eU), v_induced - in.AeroUVW(eW));
phi_downwash = atan2( in.AeroUVW(eV), v_induced - in.AeroUVW(eW));
vFn = body_forces(A_IC, B_IC);
vMn = Transform() * body_moments(A_IC, B_IC);
@ -658,7 +630,7 @@ void FGRotor::CalcStatePart2(double PowerAvailable)
if (! ExternalRPM) {
// calculate new RPM
double ExcessTorque = PowerAvailable / Omega;
double deltaOmega = ExcessTorque / PolarMoment * dt;
double deltaOmega = ExcessTorque / PolarMoment * in.TotalDeltaT;
RPM += deltaOmega/(2.0*M_PI) * 60.0;
if (RPM < 0.0) RPM = 0.0; // Engine won't turn backwards
}

View file

@ -38,7 +38,6 @@ INCLUDES
#include "FGTank.h"
#include "FGFDMExec.h"
#include "models/FGAuxiliary.h"
#include "input_output/FGXMLElement.h"
#include "input_output/FGPropertyManager.h"
#include <iostream>
@ -48,7 +47,7 @@ using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGTank.cpp,v 1.30 2011/06/21 04:41:54 jberndt Exp $";
static const char *IdSrc = "$Id: FGTank.cpp,v 1.31 2011/08/03 03:21:06 jberndt Exp $";
static const char *IdHdr = ID_TANK;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -66,6 +65,7 @@ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number)
InitialTemperature = Temperature = -9999.0;
Ixx = Iyy = Izz = 0.0;
Radius = Contents = Standpipe = Length = InnerRadius = 0.0;
PreviousUsed = 0.0;
ExternalFlow = 0.0;
InitialStandpipe = 0.0;
Capacity = 0.00001;
@ -192,6 +192,7 @@ void FGTank::ResetToIC(void)
SetContents ( InitialContents );
PctFull = 100.0*Contents/Capacity;
SetPriority( InitialPriority );
PreviousUsed = 0.0;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -212,6 +213,7 @@ const double FGTank::GetXYZ(int idx)
double FGTank::Drain(double used)
{
// double AmountToDrain = 2.0*used - PreviousUsed;
double remaining = Contents - used;
if (remaining >= 0) { // Reduce contents by amount used.
@ -224,7 +226,7 @@ double FGTank::Drain(double used)
Contents = 0.0;
PctFull = 0.0;
}
// PreviousUsed = AmountToDrain;
if (grainType != gtUNKNOWN) CalculateInertias();
return remaining;
@ -271,7 +273,7 @@ void FGTank::SetContentsGallons(double gallons)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGTank::Calculate(double dt)
double FGTank::Calculate(double dt, double TAT_C)
{
if(ExternalFlow < 0.) Drain( -ExternalFlow *dt);
else Fill(ExternalFlow * dt);
@ -279,8 +281,7 @@ 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 = Exec->GetAuxiliary()->GetTAT_C();
double Tdiff = TAT - Temperature;
double Tdiff = TAT_C - Temperature;
double dTemp = 0.0; // Temp change due to one surface
if (fabs(Tdiff) > 0.1) {
dTemp = (TempFlowFactor * Area * Tdiff * dt) / (Contents * HeatCapacity);

View file

@ -52,7 +52,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_TANK "$Id: FGTank.h,v 1.23 2011/06/13 15:23:09 jentron Exp $"
#define ID_TANK "$Id: FGTank.h,v 1.24 2011/08/03 03:21:06 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -218,9 +218,10 @@ public:
/** Performs local, tanks-specific calculations, such as fuel temperature.
This function calculates the temperature of the fuel in the tank.
@param dt the time step for this model.
@param TempC the Total Air Temperature in degrees Celsius.
@return the current temperature in degrees Celsius.
*/
double Calculate(double dt);
double Calculate(double dt, double TempC);
/** Retrieves the type of tank: Fuel or Oxidizer.
@return the tank type, 0 for undefined, 1 for fuel, and 2 for oxidizer.
@ -318,6 +319,7 @@ private:
double Izz;
double PctFull;
double Contents, InitialContents;
double PreviousUsed;
double Area;
double Temperature, InitialTemperature;
double Standpipe, InitialStandpipe;
@ -326,6 +328,7 @@ private:
int Priority, InitialPriority;
FGFDMExec* Exec;
FGPropertyManager* PropertyManager;
void CalculateInertias(void);
void Debug(int from);
};

View file

@ -46,7 +46,7 @@ INCLUDES
DEFINITIONS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#define ID_THRUSTER "$Id: FGThruster.h,v 1.16 2011/03/10 01:35:25 dpculp Exp $"
#define ID_THRUSTER "$Id: FGThruster.h,v 1.17 2011/08/03 03:21:06 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -74,7 +74,7 @@ CLASS DOCUMENTATION
1.57 (pi/2) results in no thrust at all.
@author Jon Berndt
@version $Id: FGThruster.h,v 1.16 2011/03/10 01:35:25 dpculp Exp $
@version $Id: FGThruster.h,v 1.17 2011/08/03 03:21:06 jberndt Exp $
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -112,6 +112,20 @@ public:
virtual string GetThrusterLabels(int id, string delimeter);
virtual string GetThrusterValues(int id, string delimeter);
struct Inputs {
double TotalDeltaT;
double H_agl;
FGColumnVector3 PQR;
FGColumnVector3 AeroPQR;
FGColumnVector3 AeroUVW;
double Density;
double Pressure;
double Soundspeed;
double Alpha;
double Beta;
double Vt;
} in;
protected:
eType Type;
string Name;

View file

@ -41,17 +41,15 @@ INCLUDES
#include <iostream>
#include <sstream>
#include "FGTurbine.h"
#include "FGThruster.h"
#include "models/FGPropulsion.h"
#include "models/FGAuxiliary.h"
#include "models/FGAtmosphere.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGTurbine.cpp,v 1.32 2011/06/07 00:28:03 jentron Exp $";
static const char *IdSrc = "$Id: FGTurbine.cpp,v 1.35 2011/08/04 13:45:42 jberndt Exp $";
static const char *IdHdr = ID_TURBINE;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -59,8 +57,8 @@ CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGTurbine::FGTurbine(FGFDMExec* exec, Element *el, int engine_number)
: FGEngine(exec, el, engine_number)
FGTurbine::FGTurbine(FGFDMExec* exec, Element *el, int engine_number, struct Inputs& input)
: FGEngine(exec, el, engine_number, input)
{
Type = etTurbine;
@ -103,12 +101,12 @@ void FGTurbine::ResetToIC(void)
N1 = N2 = 0.0;
N2norm = 0.0;
correctedTSFC = TSFC;
ThrottlePos = AugmentCmd = 0.0;
AugmentCmd = 0.0;
InletPosition = NozzlePosition = 1.0;
Stalled = Seized = Overtemp = Fire = Augmentation = Injection = Reversed = false;
Cutoff = true;
phase = tpOff;
TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
TAT = (in.TotalTempearture - 491.69) * 0.5555556;
EGT_degC = TAT;
OilTemp_degK = TAT + 273.0;
}
@ -123,10 +121,9 @@ void FGTurbine::Calculate(void)
RunPreFunctions();
TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556;
double qbar = Auxiliary->Getqbar();
dt = FDMExec->GetDeltaT() * Propulsion->GetRate();
ThrottlePos = FCS->GetThrottlePos(EngineNumber);
ThrottlePos = in.ThrottlePos[EngineNumber];
TAT = (in.TotalTempearture - 491.69) * 0.5555556;
if (ThrottlePos > 1.0) {
AugmentCmd = ThrottlePos - 1.0;
ThrottlePos -= AugmentCmd;
@ -135,7 +132,7 @@ void FGTurbine::Calculate(void)
}
// When trimming is finished check if user wants engine OFF or RUNNING
if ((phase == tpTrim) && (dt > 0)) {
if ((phase == tpTrim) && (in.TotalDeltaT > 0)) {
if (Running && !Starved) {
phase = tpRun;
N2 = IdleN2 + ThrottlePos * N2_factor;
@ -155,12 +152,12 @@ void FGTurbine::Calculate(void)
}
// start
if ((Starter == true) || (qbar > 30.0)) {
if ((Starter == true) || (in.qbar > 30.0)) {
if (!Running && !Cutoff && (N2 > 15.0)) phase = tpStart;
}
if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
if (dt == 0) phase = tpTrim;
if (in.TotalDeltaT == 0) phase = tpTrim;
if (Starved) phase = tpOff;
if (Stalled) phase = tpStall;
if (Seized) phase = tpSeize;
@ -185,18 +182,16 @@ void FGTurbine::Calculate(void)
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);
N1 = Seek(&N1, in.qbar/10.0, N1/2.0, N1/2.0);
N2 = Seek(&N2, in.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;
ConsumeFuel();
return 0.0;
}
@ -206,8 +201,8 @@ double FGTurbine::Run()
{
double idlethrust, milthrust, thrust;
double spoolup; // acceleration in pct/sec
double sigma = Atmosphere->GetDensityRatio();
double T = Atmosphere->GetTemperature();
double sigma = in.DensityRatio;
double T = in.Temperature;
idlethrust = MilThrust * IdleThrustLookup->GetValue();
milthrust = (MilThrust - idlethrust) * MilThrustLookup->GetValue();
@ -261,7 +256,7 @@ double FGTurbine::Run()
}
if ((Injected == 1) && Injection) {
InjectionTimer += dt;
InjectionTimer += in.TotalDeltaT;
if (InjectionTimer < InjectionTime) {
thrust = thrust * InjectionLookup->GetValue();
} else {
@ -269,7 +264,6 @@ double FGTurbine::Run()
}
}
ConsumeFuel();
if (Cutoff) phase = tpOff;
if (Starved) phase = tpOff;
@ -297,7 +291,6 @@ double FGTurbine::SpinUp(void)
double FGTurbine::Start(void)
{
double qbar = Auxiliary->Getqbar();
if ((N2 > 15.0) && !Starved) { // minimum 15% N2 needed for start
Cranking = true; // provided for sound effects signal
if (N2 < IdleN2) {
@ -306,8 +299,7 @@ double FGTurbine::Start(void)
EGT_degC = Seek(&EGT_degC, TAT + 363.1, 21.3, 7.3);
FuelFlow_pph = IdleFF * N2 / IdleN2;
OilPressure_psi = N2 * 0.62;
ConsumeFuel();
if ((Starter == false) && (qbar < 30.0)) phase = tpOff; // aborted start
if ((Starter == false) && (in.qbar < 30.0)) phase = tpOff; // aborted start
}
else {
phase = tpRun;
@ -328,12 +320,10 @@ double FGTurbine::Start(void)
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);
ConsumeFuel();
N1 = Seek(&N1, in.qbar/10.0, 0, N1/10.0);
N2 = Seek(&N2, in.qbar/15.0, 0, N2/10.0);
if (ThrottlePos < 0.01) {
phase = tpRun; // clear the stall with throttle to idle
Stalled = false;
@ -345,11 +335,9 @@ double FGTurbine::Stall(void)
double FGTurbine::Seize(void)
{
double qbar = Auxiliary->Getqbar();
N2 = 0.0;
N1 = Seek(&N1, qbar/20.0, 0, N1/15.0);
N1 = Seek(&N1, in.qbar/20.0, 0, N1/15.0);
FuelFlow_pph = Cutoff ? 0.0 : IdleFF;
ConsumeFuel();
OilPressure_psi = 0.0;
OilTemp_degK = Seek(&OilTemp_degK, TAT + 273.0, 0, 0.2);
Running = false;
@ -395,9 +383,8 @@ double FGTurbine::Trim()
double FGTurbine::CalcFuelNeed(void)
{
double dT = FDMExec->GetDeltaT() * Propulsion->GetRate();
FuelFlowRate = FuelFlow_pph / 3600.0; // Calculates flow in lbs/sec from lbs/hr
FuelExpended = FuelFlowRate * dT; // Calculates fuel expended in this time step
FuelExpended = FuelFlowRate * in.TotalDeltaT; // Calculates fuel expended in this time step
return FuelExpended;
}
@ -415,10 +402,10 @@ double FGTurbine::GetPowerAvailable(void) {
double FGTurbine::Seek(double *var, double target, double accel, double decel) {
double v = *var;
if (v > target) {
v -= dt * decel;
v -= in.TotalDeltaT * decel;
if (v < target) v = target;
} else if (v < target) {
v += dt * accel;
v += in.TotalDeltaT * accel;
if (v > target) v = target;
}
return v;
@ -488,7 +475,7 @@ bool FGTurbine::Load(FGFDMExec* exec, Element *el)
delay = 90.0 / (BypassRatio + 3.0);
N1_factor = MaxN1 - IdleN1;
N2_factor = MaxN2 - IdleN2;
OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0;
OilTemp_degK = (in.TotalTempearture - 491.69) * 0.5555556 + 273.0;
IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
bindmodel();
@ -544,7 +531,8 @@ void FGTurbine::bindmodel()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int FGTurbine::InitRunning(void) {
int FGTurbine::InitRunning(void)
{
FDMExec->SuspendIntegration();
Cutoff=false;
Running=true;

View file

@ -42,7 +42,7 @@ INCLUDES
#include "FGEngine.h"
#define ID_TURBINE "$Id: FGTurbine.h,v 1.20 2011/06/07 00:28:03 jentron Exp $"
#define ID_TURBINE "$Id: FGTurbine.h,v 1.22 2011/08/04 13:45:42 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -150,7 +150,7 @@ CLASS DOCUMENTATION
/engine/direct.xml
</pre>
@author David P. Culp
@version "$Id: FGTurbine.h,v 1.20 2011/06/07 00:28:03 jentron Exp $"
@version "$Id: FGTurbine.h,v 1.22 2011/08/04 13:45:42 jberndt Exp $"
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -164,7 +164,7 @@ public:
@param Executive pointer to executive structure
@param el pointer to the XML element representing the turbine engine
@param engine_number engine number */
FGTurbine(FGFDMExec* Executive, Element *el, int engine_number);
FGTurbine(FGFDMExec* Executive, Element *el, int engine_number, struct Inputs& input);
/// Destructor
~FGTurbine();
@ -233,10 +233,9 @@ private:
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 ThrottlePos; ///< FCS-supplied throttle position - modified for local use!
double AugmentCmd; ///< modulated afterburner command (0.0 to 1.0)
double TAT; ///< total air temperature (deg C)
double N1_spinup; ///< N1 spin up rate from starter (per second)

74
src/FDM/JSBSim/models/propulsion/FGTurboProp.cpp Normal file → Executable file
View file

@ -44,25 +44,24 @@ INCLUDES
#include <iostream>
#include <sstream>
#include "FGTurboProp.h"
#include "FGPropeller.h"
#include "FGRotor.h"
#include "models/FGPropulsion.h"
#include "models/FGAuxiliary.h"
using namespace std;
namespace JSBSim {
static const char *IdSrc = "$Id: FGTurboProp.cpp,v 1.19 2011/03/10 01:35:25 dpculp Exp $";
static const char *IdSrc = "$Id: FGTurboProp.cpp,v 1.22 2011/08/04 13:45:42 jberndt Exp $";
static const char *IdHdr = ID_TURBOPROP;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGTurboProp::FGTurboProp(FGFDMExec* exec, Element *el, int engine_number)
: FGEngine(exec, el, engine_number),
FGTurboProp::FGTurboProp(FGFDMExec* exec, Element *el, int engine_number, struct Inputs& input)
: FGEngine(exec, el, engine_number, input),
ITT_N1(NULL), EnginePowerRPM_N1(NULL), EnginePowerVC(NULL)
{
SetDefaults();
@ -151,7 +150,7 @@ bool FGTurboProp::Load(FGFDMExec* exec, Element *el)
delay=1;
N1_factor = MaxN1 - IdleN1;
N2_factor = MaxN2 - IdleN2;
OilTemp_degK = Auxiliary->GetTAT_C() + 273.0;
OilTemp_degK = in.TAT_c + 273.0;
if (IdleFF==-1) IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate
// cout << "ENG POWER:" << EnginePowerRPM_N1->GetValue(1200,90) << endl;
@ -167,34 +166,33 @@ void FGTurboProp::Calculate(void)
{
RunPreFunctions();
TAT = Auxiliary->GetTAT_C();
dt = FDMExec->GetDeltaT() * Propulsion->GetRate();
TAT = in.TAT_c;
Throttle = FCS->GetThrottlePos(EngineNumber);
ThrottlePos = in.ThrottlePos[EngineNumber];
RPM = Thruster->GetRPM() * Thruster->GetGearRatio();
if (thrusterType == FGThruster::ttPropeller) {
((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber));
((FGPropeller*)Thruster)->SetFeather(FCS->GetPropFeather(EngineNumber));
((FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
((FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
((FGPropeller*)Thruster)->SetReverse(Reversed);
if (Reversed) {
((FGPropeller*)Thruster)->SetReverseCoef(Throttle);
((FGPropeller*)Thruster)->SetReverseCoef(ThrottlePos);
} else {
((FGPropeller*)Thruster)->SetReverseCoef(0.0);
}
if (Reversed) {
if (Throttle < BetaRangeThrottleEnd) {
Throttle = 0.0; // idle when in Beta-range
if (ThrottlePos < BetaRangeThrottleEnd) {
ThrottlePos = 0.0; // idle when in Beta-range
} else {
// when reversed:
Throttle = (Throttle-BetaRangeThrottleEnd)/(1-BetaRangeThrottleEnd) * ReverseMaxPower;
ThrottlePos = (ThrottlePos-BetaRangeThrottleEnd)/(1-BetaRangeThrottleEnd) * ReverseMaxPower;
}
}
}
// When trimming is finished check if user wants engine OFF or RUNNING
if ((phase == tpTrim) && (dt > 0)) {
if ((phase == tpTrim) && (in.TotalDeltaT > 0)) {
if (Running && !Starved) {
phase = tpRun;
N2 = IdleN2;
@ -221,7 +219,7 @@ void FGTurboProp::Calculate(void)
StartTime = -1;
}
if (Cutoff && (phase != tpSpinUp)) phase = tpOff;
if (dt == 0) phase = tpTrim;
if (in.TotalDeltaT == 0) phase = tpTrim;
if (Starved) phase = tpOff;
if (Condition >= 10) {
phase = tpOff;
@ -239,11 +237,11 @@ void FGTurboProp::Calculate(void)
}
if (Condition < 1) {
if ( abs(torque) > Ielu_max_torque && Throttle >= OldThrottle ) {
Throttle = OldThrottle - 0.1 * dt; //IELU down
if ( abs(torque) > Ielu_max_torque && ThrottlePos >= OldThrottle ) {
ThrottlePos = OldThrottle - 0.1 * in.TotalDeltaT; //IELU down
Ielu_intervent = true;
} else if ( Ielu_intervent && Throttle >= OldThrottle) {
Throttle = OldThrottle + 0.05 * dt; //IELU up
} else if ( Ielu_intervent && ThrottlePos >= OldThrottle) {
ThrottlePos = OldThrottle + 0.05 * in.TotalDeltaT; //IELU up
Ielu_intervent = true;
} else {
Ielu_intervent = false;
@ -251,7 +249,7 @@ void FGTurboProp::Calculate(void)
} else {
Ielu_intervent = false;
}
OldThrottle = Throttle;
OldThrottle = ThrottlePos;
}
switch (phase) {
@ -262,6 +260,7 @@ void FGTurboProp::Calculate(void)
default: HP = 0;
}
LoadThrusterInputs();
Thruster->Calculate(HP * hptoftlbssec);
RunPostFunctions();
@ -271,13 +270,12 @@ void FGTurboProp::Calculate(void)
double FGTurboProp::Off(void)
{
double qbar = Auxiliary->Getqbar();
Running = false; EngStarting = false;
FuelFlow_pph = Seek(&FuelFlow_pph, 0, 800.0, 800.0);
//allow the air turn with generator
N1 = ExpSeek(&N1, qbar/15.0, Idle_Max_Delay*2.5, Idle_Max_Delay * 5);
N1 = ExpSeek(&N1, in.qbar/15.0, Idle_Max_Delay*2.5, Idle_Max_Delay * 5);
OilTemp_degK = ExpSeek(&OilTemp_degK,273.15 + TAT, 400 , 400);
@ -287,9 +285,6 @@ double FGTurboProp::Off(void)
OilPressure_psi = (N1/100.0*0.25+(0.1-(OilTemp_degK-273.15)*0.1/80.0)*N1/100.0) / 7692.0e-6; //from MPa to psi
ConsumeFuel(); // for possible setting Starved = false when fuel tank
// is refilled (fuel crossfeed etc.)
if (RPM>5) return -0.012; // friction in engine when propeller spining (estimate)
return 0.0;
}
@ -303,7 +298,7 @@ double FGTurboProp::Run(void)
//---
double old_N1 = N1;
N1 = ExpSeek(&N1, IdleN1 + Throttle * N1_factor, Idle_Max_Delay, Idle_Max_Delay * 2.4);
N1 = ExpSeek(&N1, IdleN1 + ThrottlePos * N1_factor, Idle_Max_Delay, Idle_Max_Delay * 2.4);
EngPower_HP = EnginePowerRPM_N1->GetValue(RPM,N1);
EngPower_HP *= EnginePowerVC->GetValue();
@ -322,8 +317,6 @@ double FGTurboProp::Run(void)
OilTemp_degK = Seek(&OilTemp_degK, 353.15, 0.4-N1*0.001, 0.04);
ConsumeFuel();
if (Cutoff) phase = tpOff;
if (Starved) phase = tpOff;
@ -360,15 +353,12 @@ double FGTurboProp::SpinUp(void)
EngPower_HP *= EnginePowerVC->GetValue();
if (EngPower_HP > MaxPower) EngPower_HP = MaxPower;
if (StartTime>=0) StartTime+=dt;
if (StartTime>=0) StartTime+=in.TotalDeltaT;
if (StartTime > MaxStartingTime && MaxStartingTime > 0) { //start failed due timeout
phase = tpOff;
StartTime = -1;
}
ConsumeFuel(); // for possible setting Starved = false when fuel tank
// is refilled (fuel crossfeed etc.)
return EngPower_HP;
}
@ -409,8 +399,6 @@ double FGTurboProp::Start(void)
Starter = false;
}
ConsumeFuel();
return EngPower_HP;
}
@ -419,9 +407,8 @@ double FGTurboProp::Start(void)
double FGTurboProp::CalcFuelNeed(void)
{
double dT = FDMExec->GetDeltaT() * Propulsion->GetRate();
FuelFlowRate = FuelFlow_pph / 3600.0;
FuelExpended = FuelFlowRate * dT;
FuelExpended = FuelFlowRate * in.TotalDeltaT;
return FuelExpended;
}
@ -431,10 +418,10 @@ double FGTurboProp::Seek(double *var, double target, double accel, double decel)
{
double v = *var;
if (v > target) {
v -= dt * decel;
v -= in.TotalDeltaT * decel;
if (v < target) v = target;
} else if (v < target) {
v += dt * accel;
v += in.TotalDeltaT * accel;
if (v > target) v = target;
}
return v;
@ -447,9 +434,9 @@ double FGTurboProp::ExpSeek(double *var, double target, double accel_tau, double
// exponential delay instead of the linear delay used in Seek
double v = *var;
if (v > target) {
v = (v - target) * exp ( -dt / decel_tau) + target;
v = (v - target) * exp ( -in.TotalDeltaT / decel_tau) + target;
} else if (v < target) {
v = (target - v) * (1 - exp ( -dt / accel_tau)) + v;
v = (target - v) * (1 - exp ( -in.TotalDeltaT / accel_tau)) + v;
}
return v;
}
@ -467,7 +454,6 @@ void FGTurboProp::SetDefaults(void)
IdleN2 = 60.0;
MaxN1 = 100.0;
MaxN2 = 100.0;
Throttle = 0.0;
InletPosition = 1.0;
NozzlePosition = 1.0;
Reversed = false;
@ -485,7 +471,7 @@ void FGTurboProp::SetDefaults(void)
Idle_Max_Delay = 1.0;
Throttle = OldThrottle = 0.0;
ThrottlePos = OldThrottle = 0.0;
ITT_Delay = 0.05;
ReverseMaxPower = 0.0;
BetaRangeThrottleEnd = 0.0;

9
src/FDM/JSBSim/models/propulsion/FGTurboProp.h Normal file → Executable file
View file

@ -47,7 +47,7 @@ INCLUDES
#include "input_output/FGXMLElement.h"
#include "math/FGTable.h"
#define ID_TURBOPROP "$Id: FGTurboProp.h,v 1.14 2011/03/10 01:35:25 dpculp Exp $"
#define ID_TURBOPROP "$Id: FGTurboProp.h,v 1.16 2011/08/04 13:45:42 jberndt Exp $"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -99,7 +99,7 @@ public:
@param Executive pointer to executive structure
@param el pointer to the XML element representing the turbine engine
@param engine_number engine number*/
FGTurboProp(FGFDMExec* Executive, Element *el, int engine_number);
FGTurboProp(FGFDMExec* Executive, Element *el, int engine_number, struct Inputs& input);
/// Destructor
~FGTurboProp();
@ -110,7 +110,7 @@ public:
double GetPowerAvailable(void) const { return (HP * hptoftlbssec); }
double GetRPM(void) const { return (RPM); }
double GetIeluThrottle(void) const { return (Throttle); }
double GetIeluThrottle(void) const { return (ThrottlePos); }
bool GetIeluIntervent(void) const { return Ielu_intervent; }
double Seek(double* var, double target, double accel, double decel);
@ -162,10 +162,9 @@ private:
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 Throttle; ///< FCS-supplied throttle position
double ThrottlePos; ///< FCS-supplied throttle position, modified locally
double TAT; ///< total air temperature (deg C)
bool Stalled; ///< true if engine is compressor-stalled
bool Seized; ///< true if inner spool is seized

View file

@ -35,9 +35,9 @@ GFX_CODE = fg_os_osgviewer.cxx fg_os_common.cxx fg_os.hxx
JSBSIM_LIBS = \
$(top_builddir)/src/FDM/JSBSim/libJSBSim.a \
$(top_builddir)/src/FDM/JSBSim/initialization/libInit.a \
$(top_builddir)/src/FDM/JSBSim/models/atmosphere/libAtmosphere.a \
$(top_builddir)/src/FDM/JSBSim/models/libModels.a \
$(top_builddir)/src/FDM/JSBSim/models/flight_control/libFlightControl.a\
$(top_builddir)/src/FDM/JSBSim/models/atmosphere/libAtmosphere.a \
$(top_builddir)/src/FDM/JSBSim/models/propulsion/libPropulsion.a \
$(top_builddir)/src/FDM/JSBSim/input_output/libInputOutput.a \
$(top_builddir)/src/FDM/JSBSim/math/libMath.a