1
0
Fork 0

Sync'ed JSBSim

Includes the following new features and bug fixes:
- The <random> function now uses C++ std::default_random_engine
- WGS84 is now used everywhere in JSBSim (previously a mix of spherical/WGS84 earcth was used resulting in inaccuracies).
- A new <planet> XML element can be used to tweak the planet characteristics (such as semimajor/smemiminor axes, gravity, etc.)
- The FGGroundCallback instance is now managed by FGInertial with a dynamic pointer std::unique_ptr<>. JSBSim was previously using a static pointer which was causing memory access failures when several instances of JSBSim were used or when an instance was replaced by a new one (the structure pointed to by the static pointer was then lost when the old instance was destroyed).
- Removed calls to exit() and replaced them by exceptions that can be caught by FlightGear (should avoid FlightGear from being ungracefully stopped by an error found by JSBSim).
- Sockets interface: replaced the usage of obsolete functions gethostbyname()/gethostbyaddr() by getaddrinfo().
- FGColumnVector3 and FGMatrix33 can now be initialized by C++ std::initializer_list
- Disable the delays implemented in some flight control components during trimming (was sometimes preventing the trim algorithm from converging to a solution).
- Fixed the magnetometer initialization (was not updated during the first 1000 time steps)
- More conversions to C++11 features (nullptr, override, std::unique_ptr<>, etc.).
This commit is contained in:
Bertrand Coconnier 2020-10-30 13:52:56 +01:00
parent f4298d676f
commit 388d66b6a6
71 changed files with 1582 additions and 1026 deletions

View file

@ -74,7 +74,7 @@ CLASS IMPLEMENTATION
// Constructor
FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr)
: Root(root), FDMctr(fdmctr)
: Root(root), RandomEngine(new default_random_engine), FDMctr(fdmctr)
{
Frame = 0;
IC = nullptr;
@ -197,11 +197,6 @@ FGFDMExec::~FGFDMExec()
}
for (unsigned int i=1; i<ChildFDMList.size(); i++) delete ChildFDMList[i]->exec;
ChildFDMList.clear();
PropertyCatalog.clear();
SetGroundCallback(0);
if (FDMctr != 0) (*FDMctr)--;
@ -210,6 +205,25 @@ FGFDMExec::~FGFDMExec()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGFDMExec::Setsim_time(double cur_time) {
sim_time = cur_time;
Inertial->SetTime(sim_time);
return sim_time;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGFDMExec::IncrTime(void) {
if (!holding && !IntegrationSuspended()) {
sim_time += dT;
Inertial->SetTime(sim_time);
Frame++;
}
return sim_time;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGFDMExec::Allocate(void)
{
bool result=true;
@ -221,7 +235,6 @@ bool FGFDMExec::Allocate(void)
// Note that this does not affect the order in which the models will be
// executed later.
Models[eInertial] = new FGInertial(this);
SetGroundCallback(new FGDefaultGroundCallback(static_cast<FGInertial*>(Models[eInertial])->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
@ -263,13 +276,7 @@ bool FGFDMExec::Allocate(void)
LoadPlanetConstants();
// Initialize models
for (unsigned int i = 0; i < Models.size(); i++) {
// The Input/Output models must not be initialized prior to IC loading
if (i == eInput || i == eOutput) continue;
LoadInputs(i);
Models[i]->InitModel();
}
InitializeModels();
IC = new FGInitialCondition(this);
IC->bind(instance);
@ -281,6 +288,19 @@ bool FGFDMExec::Allocate(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::InitializeModels(void)
{
for (unsigned int i = 0; i < Models.size(); i++) {
// The Input/Output models must not be initialized prior to IC loading
if (i == eInput || i == eOutput) continue;
LoadInputs(i);
Models[i]->InitModel();
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGFDMExec::DeAllocate(void)
{
@ -511,7 +531,7 @@ void FGFDMExec::LoadPlanetConstants(void)
Accelerations->in.vOmegaPlanet = Inertial->GetOmegaPlanet();
Propagate->in.SemiMajor = Inertial->GetSemimajor();
Propagate->in.SemiMinor = Inertial->GetSemiminor();
Auxiliary->in.SLGravity = Inertial->SLgravity();
Auxiliary->in.StandardGravity = Inertial->GetStandardGravity();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -526,8 +546,6 @@ void FGFDMExec::LoadModelConstants(void)
Auxiliary->in.Wingspan = Aircraft->GetWingSpan();
Auxiliary->in.Wingchord = Aircraft->Getcbar();
GroundReactions->in.vXYZcg = MassBalance->GetXYZcg();
LoadPlanetConstants();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -587,13 +605,7 @@ void FGFDMExec::ResetToInitialConditions(int mode)
if (mode == 1) Output->SetStartNewOutput();
for (unsigned int i = 0; i < Models.size(); i++) {
// The Input/Output models will be initialized during the RunIC() execution
if (i == eInput || i == eOutput) continue;
LoadInputs(i);
Models[i]->InitModel();
}
InitializeModels();
if (Script)
Script->ResetEvents();
@ -705,10 +717,23 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (IsChild) debug_lvl = 0;
// Process the planet element. This element is OPTIONAL.
element = document->FindElement("planet");
if (element) {
result = Models[eInertial]->Load(element);
if (!result) {
cerr << endl << "Planet element has problems in file " << aircraftCfgFileName << endl;
return result;
}
// Reload the planet constants and re-initialize the models.
LoadPlanetConstants();
InitializeModels();
}
// Process the metrics element. This element is REQUIRED.
element = document->FindElement("metrics");
if (element) {
result = ((FGAircraft*)Models[eAircraft])->Load(element);
result = Models[eAircraft]->Load(element);
if (!result) {
cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -721,7 +746,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 = ((FGMassBalance*)Models[eMassBalance])->Load(element);
result = Models[eMassBalance]->Load(element);
if (!result) {
cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -734,7 +759,7 @@ 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 = ((FGGroundReactions*)Models[eGroundReactions])->Load(element);
result = Models[eGroundReactions]->Load(element);
if (!result) {
cerr << endl << element->ReadFrom()
<< "Aircraft ground_reactions element has problems in file "
@ -749,7 +774,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 = ((FGExternalReactions*)Models[eExternalReactions])->Load(element);
result = Models[eExternalReactions]->Load(element);
if (!result) {
cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -759,7 +784,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 = ((FGBuoyantForces*)Models[eBuoyantForces])->Load(element);
result = Models[eBuoyantForces]->Load(element);
if (!result) {
cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -769,19 +794,20 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the propulsion element. This element is OPTIONAL.
element = document->FindElement("propulsion");
if (element) {
result = ((FGPropulsion*)Models[ePropulsion])->Load(element);
auto propulsion = static_cast<FGPropulsion*>(Models[ePropulsion]);
result = propulsion->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++)
for (unsigned int i=0; i < propulsion->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 = ((FGFCS*)Models[eSystems])->Load(element);
result = Models[eSystems]->Load(element);
if (!result) {
cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -792,7 +818,7 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
// Process the autopilot element. This element is OPTIONAL.
element = document->FindElement("autopilot");
if (element) {
result = ((FGFCS*)Models[eSystems])->Load(element);
result = Models[eSystems]->Load(element);
if (!result) {
cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -802,7 +828,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 = ((FGFCS*)Models[eSystems])->Load(element);
result = Models[eSystems]->Load(element);
if (!result) {
cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -812,7 +838,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 = ((FGAerodynamics*)Models[eAerodynamics])->Load(element);
result = Models[eAerodynamics]->Load(element);
if (!result) {
cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl;
return result;
@ -1145,6 +1171,7 @@ void FGFDMExec::SRand(int sr)
{
RandomSeed = sr;
gaussian_random_number_phase = 0;
RandomEngine->seed(sr);
srand(RandomSeed);
}

View file

@ -41,6 +41,9 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <memory>
#include <random>
#include "models/FGPropagate.h"
#include "models/FGOutput.h"
#include "math/FGTemplateFunc.h"
@ -84,7 +87,7 @@ CLASS DOCUMENTATION
executive is subsequently directed to load the chosen aircraft specification
file:
@code
@code{.cpp}
fdmex = new FGFDMExec( ... );
result = fdmex->LoadModel( ... );
@endcode
@ -100,7 +103,7 @@ CLASS DOCUMENTATION
from JSBSim. The state variables are used to drive the instrument displays
and to place the vehicle model in world space for visual rendering:
@code
@code{.cpp}
copy_to_JSBsim(); // copy control inputs to JSBSim
fdmex->RunIC(); // loop JSBSim once w/o integrating
copy_from_JSBsim(); // update the bus
@ -108,7 +111,7 @@ CLASS DOCUMENTATION
Once initialization is complete, cyclic execution proceeds:
@code
@code{.cpp}
copy_to_JSBsim(); // copy control inputs to JSBSim
fdmex->Run(); // execute JSBSim
copy_from_JSBsim(); // update the bus
@ -123,7 +126,7 @@ CLASS DOCUMENTATION
file can be supplied to the stub program. Scripting (see FGScript) provides
a way to supply command inputs to the simulation:
@code
@code{.cpp}
FDMExec = new JSBSim::FGFDMExec();
FDMExec->LoadScript( ScriptName ); // the script loads the aircraft and ICs
result = FDMExec->Run();
@ -249,15 +252,6 @@ public:
@return true if successful */
bool RunIC(void);
/** Sets the ground callback pointer. For optimal memory management, a shared
pointer is used internally that maintains a reference counter. The calling
application must therefore use FGGroundCallback_ptr 'smart pointers' to
manage their copy of the ground callback.
@param gc A pointer to a ground callback object
@see FGGroundCallback
*/
void SetGroundCallback(FGGroundCallback* gc) { FGLocation::SetGroundCallback(gc); }
/** Loads an aircraft model.
@param AircraftPath path to the aircraft/ directory. For instance:
"aircraft". Under aircraft, then, would be directories for various
@ -361,12 +355,6 @@ public:
FGInput* GetInput(void) {return (FGInput*)Models[eInput];}
/// Returns the FGOutput pointer.
FGOutput* GetOutput(void) {return (FGOutput*)Models[eOutput];}
/** Get a pointer to the ground callback currently used. It is recommanded
to store the returned pointer in a 'smart pointer' FGGroundCallback_ptr.
@return A pointer to the current ground callback object.
@see FGGroundCallback
*/
FGGroundCallback* GetGroundCallback(void) {return FGLocation::GetGroundCallback();}
/// Retrieves the script object
FGScript* GetScript(void) {return Script;}
/// Returns a pointer to the FGInitialCondition object
@ -536,17 +524,14 @@ public:
/** Sets the current sim time.
@param cur_time the current time
@return the current simulation time. */
double Setsim_time(double cur_time) {
sim_time = cur_time;
GetGroundCallback()->SetTime(sim_time);
return sim_time;
}
double Setsim_time(double cur_time);
/** Sets the integration time step for the simulation executive.
@param delta_t the time step in seconds. */
void Setdt(double delta_t) { dT = delta_t; }
/** Sets the root directory where JSBSim starts looking for its system directories.
/** Sets the root directory where JSBSim starts looking for its system
directories.
@param rootDir the string containing the root directory. */
void SetRootDir(const SGPath& rootDir) {RootDir = rootDir;}
@ -557,14 +542,7 @@ public:
/** Increments the simulation time if not in Holding mode. The Frame counter
is also incremented.
@return the new simulation time. */
double IncrTime(void) {
if (!holding && !IntegrationSuspended()) {
sim_time += dT;
GetGroundCallback()->SetTime(sim_time);
Frame++;
}
return sim_time;
}
double IncrTime(void);
/** Retrieves the current frame count. */
unsigned int GetFrame(void) const {return Frame;}
@ -595,6 +573,9 @@ public:
TemplateFunctions[name] = new FGTemplateFunc(this, el);
}
const std::shared_ptr<std::default_random_engine>& GetRandomEngine(void) const
{ return RandomEngine; }
private:
unsigned int Frame;
unsigned int IdFDM;
@ -606,7 +587,6 @@ private:
bool holding;
bool IncrementThenHolding;
int TimeStepsUntilHold;
int RandomSeed;
bool Constructing;
bool modelLoaded;
bool IsChild;
@ -651,6 +631,9 @@ private:
bool HoldDown;
int RandomSeed;
std::shared_ptr<std::default_random_engine> RandomEngine;
// The FDM counter is used to give each child FDM an unique ID. The root FDM
// has the ID 0
unsigned int* FDMctr;
@ -670,6 +653,7 @@ private:
void LoadModelConstants(void);
bool Allocate(void);
bool DeAllocate(void);
void InitializeModels(void);
int GetDisperse(void) const {return disperse;}
SGPath GetFullPath(const SGPath& name) {
if (name.isRelative())

View file

@ -113,23 +113,6 @@ public:
return agl;
}
double GetTerrainGeoCentRadius(double t, const FGLocation& l) const override {
double contact[3], normal[3], vel[3], angularVel[3];
mInterface->get_agl_ft(t, l, SG_METER_TO_FEET*2, contact,
normal, vel, angularVel);
return sqrt(contact[0]*contact[0]+contact[1]*contact[1]+contact[2]*contact[2]);
}
double GetSeaLevelRadius(const FGLocation& l) const override {
double seaLevelRadius, latGeoc;
sgGeodToGeoc(l.GetGeodLatitudeRad(), l.GetGeodAltitude(),
&seaLevelRadius, &latGeoc);
return seaLevelRadius * SG_METER_TO_FEET;
}
void SetTerrainGeoCentRadius(double radius) override {}
private:
FGJSBsim* mInterface;
};
@ -200,9 +183,6 @@ FGJSBsim::FGJSBsim( double dt )
fdmex = new FGFDMExec( PropertyManager );
fdmex->Hold();
// Register ground callback.
fdmex->SetGroundCallback( new FGFSGroundCallback(this) );
Atmosphere = fdmex->GetAtmosphere();
Winds = fdmex->GetWinds();
FCS = fdmex->GetFCS();
@ -216,6 +196,9 @@ FGJSBsim::FGJSBsim( double dt )
GroundReactions = fdmex->GetGroundReactions();
Accelerations = fdmex->GetAccelerations();
// Register ground callback.
Inertial->SetGroundCallback( new FGFSGroundCallback(this) );
fgic=fdmex->GetIC();
needTrim=true;

View file

@ -92,12 +92,12 @@ void FGInitialCondition::ResetIC(double u0, double v0, double w0,
InitializeIC();
vPQR_body = FGColumnVector3(p0, q0, r0);
vPQR_body = {p0, q0, r0};
alpha = alpha0; beta = beta0;
position.SetLongitude(lonRad0);
position.SetLatitude(latRad0);
position.SetAltitudeAGL(altAGLFt0);
fdmex->GetInertial()->SetAltitudeAGL(position, altAGLFt0);
lastLatitudeSet = setgeoc;
lastAltitudeSet = setagl;
@ -108,9 +108,9 @@ void FGInitialCondition::ResetIC(double u0, double v0, double w0,
vt = vUVW_NED.Magnitude();
lastSpeedSet = setuvw;
Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha);
Tw2b = { calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha };
Tb2w = Tw2b.Transposed();
SetFlightPathAngleRadIC(gamma0);
@ -122,10 +122,9 @@ void FGInitialCondition::InitializeIC(void)
{
alpha = beta = 0.0;
epa = 0.0;
a = fdmex->GetInertial()->GetSemimajor();
double a = fdmex->GetInertial()->GetSemimajor();
double b = fdmex->GetInertial()->GetSemiminor();
double ec = b/a;
e2 = 1.0 - ec*ec;
position.SetEllipse(a, b);
@ -138,8 +137,8 @@ void FGInitialCondition::InitializeIC(void)
targetNlfIC = 1.0;
Tw2b.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
Tb2w.InitMatrix(1., 0., 0., 0., 1., 0., 0., 0., 1.);
Tw2b = { 1., 0., 0., 0., 1., 0., 0., 0., 1. };
Tb2w = { 1., 0., 0., 0., 1., 0., 0., 0., 1. };
lastSpeedSet = setvt;
lastAltitudeSet = setasl;
@ -152,7 +151,7 @@ void FGInitialCondition::InitializeIC(void)
void FGInitialCondition::SetVequivalentKtsIC(double ve)
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double rho = Atmosphere->GetDensity(altitudeASL);
double rhoSL = Atmosphere->GetDensitySL();
SetVtrueFpsIC(ve*ktstofps*sqrt(rhoSL/rho));
@ -163,7 +162,7 @@ void FGInitialCondition::SetVequivalentKtsIC(double ve)
void FGInitialCondition::SetMachIC(double mach)
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
SetVtrueFpsIC(mach*soundSpeed);
lastSpeedSet = setmach;
@ -173,7 +172,7 @@ void FGInitialCondition::SetMachIC(double mach)
void FGInitialCondition::SetVcalibratedKtsIC(double vcas)
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double pressure = Atmosphere->GetPressure(altitudeASL);
double mach = MachFromVcalibrated(fabs(vcas)*ktstofps, pressure);
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
@ -223,9 +222,9 @@ void FGInitialCondition::calcAeroAngles(const FGColumnVector3& _vt_NED)
sbeta = va / vt;
}
Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha);
Tw2b = { calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha };
Tb2w = Tw2b.Transposed();
}
@ -340,7 +339,7 @@ void FGInitialCondition::calcThetaBeta(double alfa, const FGColumnVector3& _vt_N
FGColumnVector3 v0 = Tpsi * _vt_NED;
FGColumnVector3 n = (Talpha * Tphi).Transposed() * FGColumnVector3(0., 0., 1.);
FGColumnVector3 y = FGColumnVector3(0., 1., 0.);
FGColumnVector3 y = {0., 1., 0.};
FGColumnVector3 u = y - DotProduct(y, n) * n;
FGColumnVector3 p = y * n;
@ -383,9 +382,9 @@ void FGInitialCondition::calcThetaBeta(double alfa, const FGColumnVector3& _vt_N
cbeta = v2(eU) / vt;
sbeta = v2(eV) / vt;
}
Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha);
Tw2b = { calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha };
Tb2w = Tw2b.Transposed();
}
@ -409,9 +408,9 @@ void FGInitialCondition::SetBetaRadIC(double bta)
0., cphi,-sphi,
0., sphi, cphi);
Tw2b = FGMatrix33(calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha);
Tw2b = { calpha*cbeta, -calpha*sbeta, -salpha,
sbeta, cbeta, 0.0,
salpha*cbeta, -salpha*sbeta, calpha };
Tb2w = Tw2b.Transposed();
FGColumnVector3 vf = TphiInv * Tw2b * FGColumnVector3(vt, 0., 0.);
@ -603,7 +602,7 @@ void FGInitialCondition::SetWindMagKtsIC(double mag)
if (windMag > 0.001)
_vHEAD *= (mag*ktstofps) / windMag;
else
_vHEAD = FGColumnVector3((mag*ktstofps), 0., 0.);
_vHEAD = {mag*ktstofps, 0., 0.};
_vWIND_NED(eU) = _vHEAD(eU);
_vWIND_NED(eV) = _vHEAD(eV);
@ -639,8 +638,7 @@ void FGInitialCondition::SetWindDirDegIC(double dir)
void FGInitialCondition::SetTerrainElevationFtIC(double elev)
{
double agl = GetAltitudeAGLFtIC();
fdmex->GetGroundCallback()->SetTerrainGeoCentRadius(elev + position.GetSeaLevelRadius());
fdmex->GetInertial()->SetTerrainElevation(elev);
if (lastAltitudeSet == setagl)
SetAltitudeAGLFtIC(agl);
@ -648,25 +646,94 @@ void FGInitialCondition::SetTerrainElevationFtIC(double elev)
//******************************************************************************
double FGInitialCondition::GetAltitudeAGLFtIC(void) const
double FGInitialCondition::GetAltitudeASLFtIC(void) const
{
return position.GetAltitudeAGL();
return position.GetRadius() - position.GetSeaLevelRadius();
}
//******************************************************************************
double FGInitialCondition::GetAltitudeAGLFtIC(void) const
{
return fdmex->GetInertial()->GetAltitudeAGL(position);
}
//******************************************************************************
double FGInitialCondition::GetTerrainElevationFtIC(void) const
{
return position.GetTerrainRadius() - position.GetSeaLevelRadius();
FGLocation contact;
FGColumnVector3 normal, v, w;
fdmex->GetInertial()->GetContactPoint(position, contact, normal, v, w);
return contact.GetGeodAltitude();
}
//******************************************************************************
void FGInitialCondition::SetAltitudeAGLFtIC(double agl)
{
double terrainElevation = position.GetTerrainRadius()
- position.GetSeaLevelRadius();
SetAltitudeASLFtIC(agl + terrainElevation);
double altitudeASL = GetAltitudeASLFtIC();
double pressure = Atmosphere->GetPressure(altitudeASL);
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
double rho = Atmosphere->GetDensity(altitudeASL);
double rhoSL = Atmosphere->GetDensitySL();
double mach0 = vt / soundSpeed;
double vc0 = VcalibratedFromMach(mach0, pressure);
double ve0 = vt * sqrt(rho/rhoSL);
switch(lastLatitudeSet) {
case setgeod:
fdmex->GetInertial()->SetAltitudeAGL(position, agl);
break;
case setgeoc:
{
double a = fdmex->GetInertial()->GetSemimajor();
double b = fdmex->GetInertial()->GetSemiminor();
double e2 = 1.0-b*b/(a*a);
double tanlat = tan(position.GetLatitude());
double n = e2;
double prev_n = 1.0;
int iter = 0;
double longitude = position.GetLongitude();
double alt = position.GetGeodAltitude();
double h = -2.0*max(a,b);
double geodLat;
while ((fabs(n-prev_n) > 1E-15 || fabs(h-agl) > 1E-10) && iter < 10) {
geodLat = atan(tanlat/(1-n));
position.SetPositionGeodetic(longitude, geodLat, alt);
h = GetAltitudeAGLFtIC();
alt += agl-h;
double sinGeodLat = sin(geodLat);
double N = a/sqrt(1-e2*sinGeodLat*sinGeodLat);
prev_n = n;
n = e2*N/(N+alt);
iter++;
}
}
break;
}
altitudeASL = GetAltitudeASLFtIC();
soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
rho = Atmosphere->GetDensity(altitudeASL);
pressure = Atmosphere->GetPressure(altitudeASL);
switch(lastSpeedSet) {
case setvc:
mach0 = MachFromVcalibrated(vc0, pressure);
SetVtrueFpsIC(mach0 * soundSpeed);
break;
case setmach:
SetVtrueFpsIC(mach0 * soundSpeed);
break;
case setve:
SetVtrueFpsIC(ve0 * sqrt(rhoSL/rho));
break;
default: // Make the compiler stop complaining about missing enums
break;
}
lastAltitudeSet = setagl;
}
@ -677,7 +744,7 @@ void FGInitialCondition::SetAltitudeAGLFtIC(double agl)
void FGInitialCondition::SetAltitudeASLFtIC(double alt)
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double pressure = Atmosphere->GetPressure(altitudeASL);
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
double rho = Atmosphere->GetDensity(altitudeASL);
@ -687,15 +754,71 @@ void FGInitialCondition::SetAltitudeASLFtIC(double alt)
double vc0 = VcalibratedFromMach(mach0, pressure);
double ve0 = vt * sqrt(rho/rhoSL);
double geodLatitude = position.GetGeodLatitudeRad();
altitudeASL=alt;
position.SetAltitudeASL(alt);
// The call to SetAltitudeASL has most likely modified the geodetic latitude
// so we need to restore it to its initial value.
if (lastLatitudeSet == setgeod)
SetGeodLatitudeRadIC(geodLatitude);
switch(lastLatitudeSet) {
case setgeod:
{
// Given an altitude above the mean sea level (or a position radius which
// is the same) and a geodetic latitude, compute the geodetic altitude.
double a = fdmex->GetInertial()->GetSemimajor();
double b = fdmex->GetInertial()->GetSemiminor();
double e2 = 1.0-b*b/(a*a);
double geodLatitude = position.GetGeodLatitudeRad();
double cosGeodLat = cos(geodLatitude);
double sinGeodLat = sin(geodLatitude);
double N = a/sqrt(1-e2*sinGeodLat*sinGeodLat);
double geodAlt = 0.0;
double n = e2;
double prev_n = 1.0;
int iter = 0;
// Use tan or cotan to solve the geodetic altitude to avoid floating point
// exceptions.
if (cosGeodLat > fabs(sinGeodLat)) { // tan() can safely be used.
double tanGeodLat = sinGeodLat/cosGeodLat;
double x0 = N*e2*cosGeodLat;
double x = 0.0;
while (fabs(n-prev_n) > 1E-15 && iter < 10) {
double tanLat = (1-n)*tanGeodLat; // See Stevens & Lewis 1.6-14
double cos2Lat = 1./(1.+tanLat*tanLat);
double slr = b/sqrt(1.-e2*cos2Lat);
double R = slr + alt;
x = R*sqrt(cos2Lat); // OK, cos(latitude) is always positive.
prev_n = n;
n = x0/x;
iter++;
}
geodAlt = x/cosGeodLat-N;
}
else { // better use cotan (i.e. 1./tan())
double cotanGeodLat = cosGeodLat/sinGeodLat;
double z0 = N*e2*sinGeodLat;
double z = 0.0;
while (fabs(n-prev_n) > 1E-15 && iter < 10) {
double cotanLat = cotanGeodLat/(1-n);
double sin2Lat = 1./(1.+cotanLat*cotanLat);
double cos2Lat = 1.-sin2Lat;
double slr = b/sqrt(1.-e2*cos2Lat);
double R = slr + alt;
z = R*sign(cotanLat)*sqrt(sin2Lat);
prev_n = n;
n = z0/(z0+z);
iter++;
}
geodAlt = z/sinGeodLat-N*(1-e2);
}
double longitude = position.GetLongitude();
position.SetPositionGeodetic(longitude, geodLatitude, geodAlt);
}
break;
case setgeoc:
{
double slr = position.GetSeaLevelRadius();
position.SetRadius(slr+alt);
}
break;
}
altitudeASL = position.GetGeodAltitude();
soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
rho = Atmosphere->GetDensity(altitudeASL);
pressure = Atmosphere->GetPressure(altitudeASL);
@ -722,11 +845,26 @@ void FGInitialCondition::SetAltitudeASLFtIC(double alt)
void FGInitialCondition::SetGeodLatitudeRadIC(double geodLatitude)
{
double h = ComputeGeodAltitude(geodLatitude);
double lon = position.GetLongitude();
position.SetPositionGeodetic(lon, geodLatitude, h);
lastLatitudeSet = setgeod;
switch (lastAltitudeSet)
{
case setagl:
{
double agl = GetAltitudeAGLFtIC();
position.SetPositionGeodetic(lon, geodLatitude, 0.);
fdmex->GetInertial()->SetAltitudeAGL(position, agl);
}
break;
case setasl:
{
double asl = GetAltitudeASLFtIC();
position.SetPositionGeodetic(lon, geodLatitude, 0.);
SetAltitudeASLFtIC(asl);
}
break;
}
}
//******************************************************************************
@ -744,7 +882,9 @@ void FGInitialCondition::SetLatitudeRadIC(double lat)
SetAltitudeAGLFtIC(altitude);
break;
default:
altitude = GetAltitudeASLFtIC();
position.SetLatitude(lat);
SetAltitudeASLFtIC(altitude);
break;
}
}
@ -762,9 +902,7 @@ void FGInitialCondition::SetLongitudeRadIC(double lon)
SetAltitudeAGLFtIC(altitude);
break;
default:
altitude = position.GetAltitudeASL();
position.SetLongitude(lon);
position.SetAltitudeASL(altitude);
break;
}
}
@ -819,7 +957,7 @@ double FGInitialCondition::GetBodyWindFpsIC(int idx) const
double FGInitialCondition::GetVcalibratedKtsIC(void) const
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double pressure = Atmosphere->GetPressure(altitudeASL);
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
double mach = vt / soundSpeed;
@ -831,7 +969,7 @@ double FGInitialCondition::GetVcalibratedKtsIC(void) const
double FGInitialCondition::GetVequivalentKtsIC(void) const
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double rho = Atmosphere->GetDensity(altitudeASL);
double rhoSL = Atmosphere->GetDensitySL();
return fpstokts * vt * sqrt(rho/rhoSL);
@ -841,7 +979,7 @@ double FGInitialCondition::GetVequivalentKtsIC(void) const
double FGInitialCondition::GetMachIC(void) const
{
double altitudeASL = position.GetAltitudeASL();
double altitudeASL = GetAltitudeASLFtIC();
double soundSpeed = Atmosphere->GetSoundSpeed(altitudeASL);
return vt / soundSpeed;
}
@ -909,23 +1047,6 @@ bool FGInitialCondition::Load(const SGPath& rstfile, bool useStoredPath)
return result;
}
//******************************************************************************
// Given an altitude above the mean sea level (or a position radius which is the
// same) and a geodetic latitude, compute the geodetic altitude.
//
// TODO: extend the routine to the case where lastAltitudeSet is equal to
// setagl.
double FGInitialCondition::ComputeGeodAltitude(double geodLatitude)
{
double R = position.GetRadius();
double slat = sin(geodLatitude);
double RN = a / sqrt(1.0 - e2*slat*slat);
double p1 = e2*RN*slat*slat;
double p2 = e2*e2*RN*RN*slat*slat-R*R;
return p1 + sqrt(p1*p1-p2) - RN;
}
//******************************************************************************
bool FGInitialCondition::LoadLatitude(Element* position_el)
@ -952,10 +1073,12 @@ bool FGInitialCondition::LoadLatitude(Element* position_el)
string lat_type = latitude_el->GetAttributeValue("type");
if (lat_type == "geod" || lat_type == "geodetic")
if (lat_type == "geod" || lat_type == "geodetic") {
SetGeodLatitudeRadIC(latitude);
lastLatitudeSet = setgeod;
}
else {
position.SetLatitude(latitude);
SetLatitudeRadIC(latitude);
lastLatitudeSet = setgeoc;
}
}
@ -1060,10 +1183,9 @@ bool FGInitialCondition::Load_v1(Element* document)
// This is the rotation rate of the "Local" frame, expressed in the local frame.
const FGMatrix33& Tl2b = orientation.GetT();
double radInv = 1.0 / position.GetRadius();
FGColumnVector3 vOmegaLocal = FGColumnVector3(
radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*position.GetTanLatitude() );
FGColumnVector3 vOmegaLocal = {radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*tan(position.GetLatitude())};
vPQR_body = Tl2b * vOmegaLocal;
@ -1096,8 +1218,9 @@ bool FGInitialCondition::Load_v2(Element* document)
}
FGColumnVector3 vOmegaEarth = fdmex->GetInertial()->GetOmegaPlanet();
if (document->FindElement("elevation"))
fdmex->GetGroundCallback()->SetTerrainGeoCentRadius(document->FindElementValueAsNumberConvertTo("elevation", "FT")+position.GetSeaLevelRadius());
if (document->FindElement("elevation")) {
fdmex->GetInertial()->SetTerrainElevation(document->FindElementValueAsNumberConvertTo("elevation", "FT"));
}
// Initialize vehicle position
//
@ -1113,21 +1236,22 @@ bool FGInitialCondition::Load_v2(Element* document)
position = Ti2ec * position_el->FindElementTripletConvertTo("FT");
} else if (frame == "ecef") {
if (!position_el->FindElement("x") && !position_el->FindElement("y") && !position_el->FindElement("z")) {
if (position_el->FindElement("longitude"))
position.SetLongitude(position_el->FindElementValueAsNumberConvertTo("longitude", "RAD"));
if (position_el->FindElement("longitude")) {
SetLongitudeRadIC(position_el->FindElementValueAsNumberConvertTo("longitude", "RAD"));
}
if (position_el->FindElement("radius")) {
position.SetRadius(position_el->FindElementValueAsNumberConvertTo("radius", "FT"));
} else if (position_el->FindElement("altitudeAGL")) {
position.SetAltitudeAGL(position_el->FindElementValueAsNumberConvertTo("altitudeAGL", "FT"));
SetAltitudeAGLFtIC(position_el->FindElementValueAsNumberConvertTo("altitudeAGL", "FT"));
} else if (position_el->FindElement("altitudeMSL")) {
position.SetAltitudeASL(position_el->FindElementValueAsNumberConvertTo("altitudeMSL", "FT"));
SetAltitudeASLFtIC(position_el->FindElementValueAsNumberConvertTo("altitudeMSL", "FT"));
} else {
cerr << endl << " No altitude or radius initial condition is given." << endl;
result = false;
}
result = LoadLatitude(position_el);
if (result)
result = LoadLatitude(position_el);
} else {
position = position_el->FindElementTripletConvertTo("FT");
@ -1229,7 +1353,6 @@ bool FGInitialCondition::Load_v2(Element* document)
// The vehicle will be defaulted to (0,0,0) in the Body frame if nothing is provided.
Element* velocity_el = document->FindElement("velocity");
FGColumnVector3 vInitVelocity = FGColumnVector3(0.0, 0.0, 0.0);
FGMatrix33 mTec2l = position.GetTec2l();
const FGMatrix33& Tb2l = orientation.GetTInv();
@ -1262,7 +1385,7 @@ bool FGInitialCondition::Load_v2(Element* document)
} else {
vUVW_NED = Tb2l * vInitVelocity;
vUVW_NED.InitMatrix();
}
@ -1282,10 +1405,9 @@ bool FGInitialCondition::Load_v2(Element* document)
// Refer to Stevens and Lewis, 1.5-14a, pg. 49.
// This is the rotation rate of the "Local" frame, expressed in the local frame.
double radInv = 1.0 / position.GetRadius();
FGColumnVector3 vOmegaLocal = FGColumnVector3(
radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*position.GetTanLatitude() );
FGColumnVector3 vOmegaLocal = { radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*tan(position.GetLatitude())};
if (attrate_el) {

View file

@ -377,7 +377,7 @@ public:
/** Gets the initial altitude above sea level.
@return Initial altitude in feet. */
double GetAltitudeASLFtIC(void) const { return position.GetAltitudeASL(); }
double GetAltitudeASLFtIC(void) const;
/** Gets the initial altitude above ground level.
@return Initial altitude AGL in feet */
@ -694,8 +694,7 @@ private:
double targetNlfIC;
FGMatrix33 Tw2b, Tb2w;
double alpha, beta;
double a, e2;
double alpha, beta;
double epa;
speedset lastSpeedSet;

View file

@ -397,7 +397,9 @@ void FGTrim::trimOnGround(void)
FGColumnVector3 normal, vDummy;
FGLocation lDummy;
double height = gearLoc.GetContactPoint(lDummy, normal, vDummy, vDummy);
double height = fdmex->GetInertial()->GetContactPoint(gearLoc, lDummy,
normal, vDummy,
vDummy);
if (gear->IsBogey() && !GroundReactions->GetSolid())
continue;

View file

@ -44,12 +44,16 @@ double FGDefaultGroundCallback::GetAGLevel(double t, const FGLocation& loc,
{
vel.InitMatrix();
angularVel.InitMatrix();
normal = FGColumnVector3(loc).Normalize();
double loc_radius = loc.GetRadius(); // Get the radius of the given location
// (e.g. the CG)
double agl = loc_radius - mTerrainLevelRadius;
contact = (mTerrainLevelRadius/loc_radius)*FGColumnVector3(loc);
return agl;
FGLocation l = loc;
l.SetEllipse(a,b);
double latitude = l.GetGeodLatitudeRad();
double cosLat = cos(latitude);
double longitude = l.GetLongitude();
normal = FGColumnVector3(cosLat*cos(longitude), cosLat*sin(longitude),
sin(latitude));
contact.SetEllipse(a, b);
contact.SetPositionGeodetic(longitude, latitude, mTerrainElevation);
return l.GetGeodAltitude() - mTerrainElevation;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -38,8 +38,6 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "simgear/structure/SGSharedPtr.hxx"
namespace JSBSim {
class FGLocation;
@ -61,7 +59,7 @@ CLASS DOCUMENTATION
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGGroundCallback : public SGReferenced
class FGGroundCallback
{
public:
@ -97,37 +95,29 @@ public:
FGColumnVector3& w) const
{ return GetAGLevel(time, location, contact, normal, v, w); }
/** Compute the local terrain radius
@param t simulation time
@param location location
*/
virtual double GetTerrainGeoCentRadius(double t, const FGLocation& location) const = 0;
/** Compute the local terrain radius
@param location location
*/
virtual double GetTerrainGeoCentRadius(const FGLocation& location) const
{ return GetTerrainGeoCentRadius(time, location); }
/** Return the sea level radius
@param location location
*/
virtual double GetSeaLevelRadius(const FGLocation& location) const = 0;
/** Set the local terrain radius.
/** Set the terrain elevation.
Only needs to be implemented if JSBSim should be allowed
to modify the local terrain radius (see the default implementation)
*/
virtual void SetTerrainGeoCentRadius(double radius) {}
virtual void SetTerrainElevation(double h) {}
/** Set the planet semimajor and semiminor axes.
Only needs to be implemented if JSBSim should be allowed to modify
the planet dimensions.
*/
virtual void SetEllipse(double semimajor, double semiminor) {}
/** Set the simulation time.
The elapsed time can be used by the ground callbck to assess the planet
rotation or the movement of objects.
@param _time elapsed time in seconds since the simulation started.
*/
void SetTime(double _time) { time = _time; }
protected:
double time;
};
typedef SGSharedPtr<FGGroundCallback> FGGroundCallback_ptr;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// The default sphere earth implementation:
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -135,25 +125,23 @@ typedef SGSharedPtr<FGGroundCallback> FGGroundCallback_ptr;
class FGDefaultGroundCallback : public FGGroundCallback
{
public:
explicit FGDefaultGroundCallback(double referenceRadius) :
mSeaLevelRadius(referenceRadius), mTerrainLevelRadius(referenceRadius) {}
explicit FGDefaultGroundCallback(double semiMajor, double semiMinor) :
a(semiMajor), b(semiMinor) {}
double GetAGLevel(double t, const FGLocation& location,
FGLocation& contact,
FGColumnVector3& normal, FGColumnVector3& v,
FGColumnVector3& w) const override;
void SetTerrainGeoCentRadius(double radius) override
{ mTerrainLevelRadius = radius;}
double GetTerrainGeoCentRadius(double t, const FGLocation& location) const override
{ return mTerrainLevelRadius; }
void SetTerrainElevation(double h) override
{ mTerrainElevation = h; }
double GetSeaLevelRadius(const FGLocation& location) const override
{return mSeaLevelRadius; }
void SetEllipse(double semimajor, double semiminor) override
{ a = semimajor; b = semiminor; }
private:
double mSeaLevelRadius;
double mTerrainLevelRadius;
double a, b;
double mTerrainElevation = 0.0;
};
}

View file

@ -66,21 +66,21 @@ public:
FGInputSocket(FGFDMExec* fdmex);
/** Destructor. */
~FGInputSocket();
~FGInputSocket() override;
/** Init the input directives from an XML file.
@param element XML Element that is pointing to the input directives
*/
bool Load(Element* el);
bool Load(Element* el) override;
/** Initializes the instance. This method basically opens the socket to which
inputs will be directed.
@result true if the execution succeeded.
*/
bool InitModel(void);
bool InitModel(void) override;
/// Generates the input.
void Read(bool Holding);
void Read(bool Holding) override;
protected:

View file

@ -72,7 +72,7 @@ void FGInputType::SetIdx(unsigned int idx)
bool FGInputType::Load(Element* element)
{
// Perform base class Load.
if(!FGModel::Load(element, true))
if(!FGModel::Upload(element, true))
return false;
// no common attributes yet (see FGOutputType for example

View file

@ -79,7 +79,7 @@ public:
FGInputType(FGFDMExec* fdmex);
/// Destructor
virtual ~FGInputType();
~FGInputType() override;
/** Set the idx for this input instance
@param idx ID of the input instance that is constructed
@ -89,10 +89,10 @@ public:
/** Init the input directives from an XML file (implement the FGModel interface).
@param element XML Element that is pointing to the input directives
*/
virtual bool Load(Element* el);
bool Load(Element* el) override;
/// Init the input model according to its configitation.
virtual bool InitModel(void);
bool InitModel(void) override;
/** Executes the input directives (implement the FGModel interface).
This method checks that the current time step matches the input
@ -100,7 +100,7 @@ public:
generation and finally the "post" functions.
@result false if no error.
*/
bool Run(bool Holding);
bool Run(bool Holding) override;
/** Generate the input. This is a pure method so it must be implemented by
the classes that inherits from FGInputType. The Read name may not be
@ -132,7 +132,7 @@ protected:
unsigned int InputIdx;
bool enabled;
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -57,7 +57,7 @@ Element_ptr FGModelLoader::Open(Element *el)
if (!fname.empty()) {
FGXMLFileRead XMLFileRead;
SGPath path(SGPath::fromUtf8(fname));
SGPath path(SGPath::fromUtf8(fname.c_str()));
if (path.isRelative())
path = model->FindFullPathName(path);

View file

@ -66,15 +66,15 @@ public:
/// Constructor
FGOutputFG(FGFDMExec* fdmex);
virtual void Print(void);
void Print(void) override;
/** Evaluate the output directives from an XML file.
@param element XML Element that is pointing to the output directives
*/
virtual bool Load(Element*);
bool Load(Element*) override;
protected:
virtual void PrintHeaders(void) {};
void PrintHeaders(void) override {};
private:

View file

@ -74,18 +74,18 @@ public:
FGOutputFile(FGFDMExec* fdmex);
/// Destructor : closes the file.
virtual ~FGOutputFile() { CloseFile(); }
~FGOutputFile() override { CloseFile(); }
/** Init the output directives from an XML file.
@param element XML Element that is pointing to the output directives
*/
bool Load(Element* el);
bool Load(Element* el) override;
/** Initializes the instance. This method basically opens the file to which
outputs will be directed.
@result true if the execution succeeded.
*/
bool InitModel(void);
bool InitModel(void) override;
/** Reset the output prior to a restart of the simulation. This method should
be called when the simulation is restarted with, for example, new initial
conditions. The current file is closed and reopened with a new name. The
@ -93,13 +93,13 @@ public:
constructor or SetOutputName() and is appended with an underscore _ and
an ID that is incremented at each call to this method.
*/
void SetStartNewOutput(void);
void SetStartNewOutput(void) override;
/** Overwrites the name identifier under which the output will be logged.
For this method to take effect, it must be called prior to
FGFDMExec::RunIC(). If it is called after, it will not take effect before
the next call to SetStartNewOutput().
@param name new name */
void SetOutputName(const std::string& fname) {
void SetOutputName(const std::string& fname) override {
Name = (FDMExec->GetRootDir()/fname).utf8Str();
runID_postfix = -1;
Filename = SGPath();
@ -107,7 +107,7 @@ public:
/** Generate the output. This is a pure method so it must be implemented by
the classes that inherits from FGOutputFile.
*/
void Print(void) = 0;
void Print(void) override = 0;
protected:
SGPath Filename;

View file

@ -70,7 +70,7 @@ public:
FGOutputSocket(FGFDMExec* fdmex);
/** Destructor. */
~FGOutputSocket();
~FGOutputSocket() override;
/** Overwrites the name identifier under which the output will be logged.
This method is taken into account if it is called before
@ -80,20 +80,20 @@ public:
hostname could be an ip, port a numerical value and
proto should be UDP or TCP (the default if omitted)
*/
virtual void SetOutputName(const std::string& name);
void SetOutputName(const std::string& name) override;
/** Init the output directives from an XML file.
@param element XML Element that is pointing to the output directives
*/
virtual bool Load(Element* el);
bool Load(Element* el) override;
/** Initializes the instance. This method basically opens the socket to which
outputs will be directed.
@result true if the execution succeeded.
*/
bool InitModel(void);
bool InitModel(void) override;
/// Generates the output.
void Print(void);
void Print(void) override;
/** Outputs a status thru the socket. This method issues a message prepended
by the string "<STATUS>" to the socket.

View file

@ -77,17 +77,17 @@ public:
/** Init the output directives from an XML file.
@param element XML Element that is pointing to the output directives
*/
virtual bool Load(Element* el);
bool Load(Element* el) override;
/// Generates the output to the text file.
virtual void Print(void);
void Print(void) override;
protected:
std::string delimeter;
sg_ofstream datafile;
virtual bool OpenFile(void);
virtual void CloseFile(void) { if (datafile.is_open()) datafile.close(); }
bool OpenFile(void) override;
void CloseFile(void) override { if (datafile.is_open()) datafile.close(); }
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -96,7 +96,7 @@ public:
FGOutputType(FGFDMExec* fdmex);
/// Destructor
virtual ~FGOutputType();
~FGOutputType() override;
/** Set the idx for this output instance
@param idx ID of the output instance that is constructed
@ -135,10 +135,10 @@ public:
/** Init the output directives from an XML file (implement the FGModel interface).
@param element XML Element that is pointing to the output directives
*/
virtual bool Load(Element* el);
bool Load(Element* el) override;
/// Init the output model according to its configitation.
virtual bool InitModel(void);
bool InitModel(void) override;
/** Executes the output directives (implement the FGModel interface).
This method checks that the current time step matches the output
@ -209,7 +209,7 @@ protected:
FGExternalReactions* ExternalReactions;
FGBuoyantForces* BuoyantForces;
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -66,10 +66,10 @@ public:
/** Reads the property names from an XML file.
@param element The root XML Element of the input file.
*/
bool Load(Element* el);
bool Load(Element* el) override;
/// Reads the socket and updates properties accordingly.
void Read(bool Holding);
void Read(bool Holding) override;
protected:

View file

@ -28,6 +28,8 @@
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <sstream> // for assembling the error messages / what of exceptions.
#include <stdexcept> // using domain_error, invalid_argument, and length_error.
#include "FGXMLElement.h"
#include "FGJSBBase.h"
@ -162,6 +164,9 @@ Element::Element(const string& nm)
// Density
convert["KG/L"]["LBS/GAL"] = 8.3454045;
convert["LBS/GAL"]["KG/L"] = 1.0/convert["KG/L"]["LBS/GAL"];
// Gravitational
convert["FT3/SEC2"]["M3/SEC2"] = convert["FT3"]["M3"];
convert["M3/SEC2"]["FT3/SEC2"] = convert["M3"]["FT3"];
// Length
convert["M"]["M"] = 1.00;
@ -235,6 +240,9 @@ Element::Element(const string& nm)
// Density
convert["KG/L"]["KG/L"] = 1.0;
convert["LBS/GAL"]["LBS/GAL"] = 1.0;
// Gravitational
convert["FT3/SEC2"]["FT3/SEC2"] = 1.0;
convert["M3/SEC2"]["M3/SEC2"] = 1.0;
}
}
@ -272,18 +280,20 @@ double Element::GetAttributeValueAsNumber(const string& attr)
string attribute = GetAttributeValue(attr);
if (attribute.empty()) {
cerr << ReadFrom() << "Expecting numeric attribute value, but got no data"
<< endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Expecting numeric attribute value, but got no data";
cerr << s.str() << endl;
throw length_error(s.str());
}
else {
double number=0;
if (is_number(trim(attribute)))
number = atof(attribute.c_str());
else {
cerr << ReadFrom() << "Expecting numeric attribute value, but got: "
<< attribute << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Expecting numeric attribute value, but got: " << attribute;
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
return (number);
@ -334,22 +344,29 @@ double Element::GetDataAsNumber(void)
if (is_number(trim(data_lines[0])))
number = atof(data_lines[0].c_str());
else {
cerr << ReadFrom() << "Expected numeric value, but got: " << data_lines[0]
<< endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Expected numeric value, but got: " << data_lines[0];
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
return number;
} else if (data_lines.size() == 0) {
cerr << ReadFrom() << "Expected numeric value, but got no data" << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Expected numeric value, but got no data";
cerr << s.str() << endl;
throw length_error(s.str());
} else {
cerr << ReadFrom() << "Attempting to get single data value in element "
<< "<" << name << ">" << endl
<< " from multiple lines:" << endl;
for(unsigned int i=0; i<data_lines.size(); ++i)
cerr << data_lines[i] << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Attempting to get single data value in element "
<< "<" << name << ">"
<< " from multiple lines (" << data_lines.size() << ").";
throw length_error(s.str());
}
}
@ -416,9 +433,10 @@ double Element::FindElementValueAsNumber(const string& el)
value = DisperseValue(element, value);
return value;
} else {
cerr << ReadFrom() << "Attempting to get non-existent element " << el
<< endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Attempting to get non-existent element " << el;
cerr << s.str() << endl;
throw length_error(s.str());
}
}
@ -463,24 +481,28 @@ double Element::FindElementValueAsNumberConvertTo(const string& el, const string
Element* element = FindElement(el);
if (!element) {
cerr << ReadFrom() << "Attempting to get non-existent element " << el
<< endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Attempting to get non-existent element " << el;
cerr << s.str() << endl;
throw length_error(s.str());
}
string supplied_units = element->GetAttributeValue("unit");
if (!supplied_units.empty()) {
if (convert.find(supplied_units) == convert.end()) {
cerr << element->ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" does not exist (typo?)." << endl;
exit(-1);
std::stringstream s;
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" does not exist (typo?).";
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
cerr << element->ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" cannot be converted to " << target_units
<< endl;
exit(-1);
std::stringstream s;
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" cannot be converted to " << target_units;
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
}
@ -528,21 +550,26 @@ double Element::FindElementValueAsNumberConvertFromTo( const string& el,
Element* element = FindElement(el);
if (!element) {
cerr << "Attempting to get non-existent element " << el << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Attempting to get non-existent element " << el;
cerr << s.str() << endl;
throw length_error(s.str());
}
if (!supplied_units.empty()) {
if (convert.find(supplied_units) == convert.end()) {
cerr << element->ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" does not exist (typo?)." << endl;
exit(-1);
std::stringstream s;
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" does not exist (typo?).";
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
cerr << element->ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" cannot be converted to " << target_units
<< endl;
exit(-1);
std::stringstream s;
s << element->ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" cannot be converted to " << target_units;
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
}
@ -567,15 +594,18 @@ FGColumnVector3 Element::FindElementTripletConvertTo( const string& target_units
if (!supplied_units.empty()) {
if (convert.find(supplied_units) == convert.end()) {
cerr << ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" does not exist (typo?)." << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" does not exist (typo?).";
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
if (convert[supplied_units].find(target_units) == convert[supplied_units].end()) {
cerr << ReadFrom() << "Supplied unit: \""
<< supplied_units << "\" cannot be converted to " << target_units
<< endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Supplied unit: \"" << supplied_units
<< "\" cannot be converted to " << target_units;
cerr << s.str() << endl;
throw invalid_argument(s.str());
}
}
@ -650,8 +680,10 @@ double Element::DisperseValue(Element *e, double val, const std::string& supplie
value = (val + disp * urn)*(fabs(urn)/urn);
}
} else {
cerr << ReadFrom() << "Unknown dispersion type" << attType << endl;
exit(-1);
std::stringstream s;
s << ReadFrom() << "Unknown dispersion type" << attType;
cerr << s.str() << endl;
throw domain_error(s.str());
}
}

View file

@ -9,21 +9,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
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
--------------------------------------------------------------------------------
@ -42,13 +42,12 @@ INCLUDES
#include <WS2tcpip.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#endif
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdio>
#include "FGfdmSocket.h"
#include "string_utilities.h"
using std::cout;
using std::cerr;
@ -82,46 +81,53 @@ FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol)
sckt = sckt_in = 0;
Protocol = (ProtocolType)protocol;
connected = false;
struct addrinfo *addr = nullptr;
#if defined(_MSC_VER) || defined(__MINGW32__)
if (!LoadWinSockDLL(debug_lvl)) return;
#endif
if (!is_number(address)) {
host = gethostbyname(address.c_str());
if (host == NULL) {
cerr << "Could not get host net address by name..." << endl;
return;
}
} else {
unsigned long ip = inet_addr(address.c_str());
host = gethostbyaddr((char*)&ip, sizeof(ip), PF_INET);
if (host == NULL) {
cerr << "Could not get host net address by number..." << endl;
return;
}
struct addrinfo hints = { 0 };
hints.ai_family = AF_INET;
if (protocol == ptUDP)
hints.ai_socktype = SOCK_DGRAM;
else
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if (!is_number(address))
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
else
hints.ai_flags = AI_NUMERICHOST;
int failure = getaddrinfo(address.c_str(), NULL, &hints, &addr);
if (failure || !addr) {
cerr << "Could not get host net address " << address;
if (hints.ai_flags == AI_NUMERICHOST)
cerr << " by number..." << endl;
else
cerr << " by name..." << endl;
cerr << gai_strerror(failure) << endl;
freeaddrinfo(addr);
return;
}
if (protocol == ptUDP) { //use udp protocol
sckt = socket(AF_INET, SOCK_DGRAM, 0);
sckt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (debug_lvl > 0)
if (debug_lvl > 0) {
if (protocol == ptUDP) //use udp protocol
cout << "Creating UDP socket on port " << port << endl;
}
else { //use tcp protocol
sckt = socket(AF_INET, SOCK_STREAM, 0);
if (debug_lvl > 0)
else //use tcp protocol
cout << "Creating TCP socket on port " << port << endl;
}
if (sckt >= 0) { // successful
memset(&scktName, 0, sizeof(struct sockaddr_in));
scktName.sin_family = AF_INET;
scktName.sin_port = htons(port);
memcpy(&scktName.sin_addr, host->h_addr_list[0], host->h_length);
int len = sizeof(struct sockaddr_in);
memcpy(&scktName, addr->ai_addr, len);
scktName.sin_port = htons(port);
if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
if (debug_lvl > 0)
cout << "Successfully connected to socket for output ..." << endl;
@ -131,6 +137,8 @@ FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol)
} else // unsuccessful
cerr << "Could not create socket for FDM output, error = " << errno << endl;
freeaddrinfo(addr);
Debug(0);
}

View file

@ -7,21 +7,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -39,26 +39,13 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <string>
#include <sstream>
#include <sys/types.h>
#include "FGJSBBase.h"
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <winsock.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#endif
#ifdef _MSC_VER
# pragma comment (lib,"WSock32.lib")
#endif
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -106,8 +93,13 @@ public:
enum ProtocolType {ptUDP, ptTCP};
private:
#if defined(_MSC_VER) || defined(__MINGW32__)
SOCKET sckt;
SOCKET sckt_in;
#else
int sckt;
int sckt_in;
#endif
ProtocolType Protocol;
struct sockaddr_in scktName;
struct hostent *host;

View file

@ -4,24 +4,24 @@ Header: FGColumnVector3.h
Author: Originally by Tony Peden [formatted and adapted here by Jon Berndt]
Date started: Unknown
------------- Copyright (C) 2001 by Tony Peden and Jon S. Berndt (jon@jsbsim.org)
------ Copyright (C) 2001 by Tony Peden and Jon S. Berndt (jon@jsbsim.org)
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -139,6 +139,17 @@ public:
return *this;
}
/** Assignment operator.
@param lv initializer list of at most 3 values (i.e. {x, y, Z})
Copy the content of the list into *this. */
FGColumnVector3& operator=(std::initializer_list<double> lv) {
double *v = data;
for(auto &x : lv)
*(v++) = x;
return *this;
}
/** Comparison operator.
@param b other vector.
Returns true if both vectors are exactly the same. */
@ -178,12 +189,14 @@ public:
/// Addition operator.
FGColumnVector3 operator+(const FGColumnVector3& B) const {
return FGColumnVector3( data[0] + B.data[0], data[1] + B.data[1], data[2] + B.data[2] );
return FGColumnVector3( data[0] + B.data[0], data[1] + B.data[1],
data[2] + B.data[2] );
}
/// Subtraction operator.
FGColumnVector3 operator-(const FGColumnVector3& B) const {
return FGColumnVector3( data[0] - B.data[0], data[1] - B.data[1], data[2] - B.data[2] );
return FGColumnVector3( data[0] - B.data[0], data[1] - B.data[1],
data[2] - B.data[2] );
}
/// Subtract an other vector.

View file

@ -29,6 +29,9 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iomanip>
#include <random>
#include <chrono>
#include <memory>
#include "simgear/misc/strutils.hxx"
#include "FGFDMExec.h"
@ -38,6 +41,7 @@ INCLUDES
#include "input_output/FGXMLElement.h"
#include "math/FGFunctionValue.h"
using namespace std;
namespace JSBSim {
@ -47,7 +51,7 @@ CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
const double invlog2val = 1.0/log10(2.0);
const unsigned int MaxArgs = 9999;
constexpr unsigned int MaxArgs = 9999;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -68,13 +72,13 @@ private:
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
template<typename func_t, unsigned int Nmin, unsigned int Nmax=Nmin,
FGFunction::OddEven odd_even=FGFunction::OddEven::Either>
template<typename func_t, unsigned int Nmin>
class aFunc: public FGFunction
{
public:
aFunc(const func_t& _f, FGFDMExec* fdmex, Element* el,
const string& prefix, FGPropertyValue* v)
const string& prefix, FGPropertyValue* v, unsigned int Nmax=Nmin,
FGFunction::OddEven odd_even=FGFunction::OddEven::Either)
: FGFunction(fdmex->GetPropertyManager()), f(_f)
{
Load(el, v, fdmex, prefix);
@ -83,10 +87,64 @@ public:
CheckOddOrEvenArguments(el, odd_even);
}
double GetValue(void) const {
double GetValue(void) const override {
return cached ? cachedValue : f(Parameters);
}
protected:
void bind(Element* el, const string& Prefix) override {
string nName = CreateOutputNode(el, Prefix);
if (!nName.empty())
PropertyManager->Tie(nName, this, &aFunc<func_t, Nmin>::GetValue);
}
private:
const func_t f;
};
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Template specialization for functions without parameters.
template<typename func_t>
class aFunc<func_t, 0>: public FGFunction
{
public:
aFunc(const func_t& _f, FGPropertyManager* pm, Element* el,
const string& Prefix)
: FGFunction(pm), f(_f)
{
if (el->GetNumElements() != 0) {
ostringstream buffer;
buffer << el->ReadFrom() << fgred << highint
<< "<" << el->GetName() << "> should have no arguments." << reset
<< endl;
throw WrongNumberOfArguments(buffer.str(), Parameters, el);
}
bind(el, Prefix);
}
double GetValue(void) const override {
double result = cached ? cachedValue : f();
if (pNode) pNode->setDoubleValue(result);
return result;
}
// Functions without parameters are assumed to be non-const
bool IsConstant(void) const override {
return false;
}
protected:
// The method GetValue() is not bound for functions without parameters because
// we do not want the property to return a different value each time it is
// read.
void bind(Element* el, const string& Prefix) override {
CreateOutputNode(el, Prefix);
// Initialize the node to a sensible value.
if (pNode) pNode->setDoubleValue(f());
}
private:
const func_t f;
};
@ -129,7 +187,7 @@ FGParameter_ptr VarArgsFn(const func_t& _f, FGFDMExec* fdmex, Element* el,
const string& prefix, FGPropertyValue* v)
{
try {
return new aFunc<func_t, 2, MaxArgs>(_f, fdmex, el, prefix, v);
return new aFunc<func_t, 2>(_f, fdmex, el, prefix, v, MaxArgs);
}
catch(WrongNumberOfArguments& e) {
if ((e.GetElement() == el) && (e.NumberOfArguments() == 1)) {
@ -234,11 +292,27 @@ void FGFunction::CheckOddOrEvenArguments(Element* el, OddEven odd_even)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
shared_ptr<default_random_engine> makeRandomEngine(Element *el, FGFDMExec* fdmex)
{
string seed_attr = el->GetAttributeValue("seed");
unsigned int seed;
if (seed_attr.empty())
return fdmex->GetRandomEngine();
else if (seed_attr == "time_now")
seed = chrono::system_clock::now().time_since_epoch().count();
else
seed = atoi(seed_attr.c_str());
return make_shared<default_random_engine>(seed);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
const string& Prefix)
{
Name = el->GetAttributeValue("name");
Element* element = el->GetElement();
auto sum = [](const decltype(Parameters)& Parameters)->double {
double temp = 0.0;
@ -358,7 +432,8 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
return 1.0;
};
Parameters.push_back(new aFunc<decltype(f), 2, MaxArgs>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
var, MaxArgs));
} else if (operation == "or") {
string ctxMsg = element->ReadFrom();
auto f = [ctxMsg](const decltype(Parameters)& Parameters)->double {
@ -369,7 +444,8 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
return 0.0;
};
Parameters.push_back(new aFunc<decltype(f), 2, MaxArgs>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
var, MaxArgs));
} else if (operation == "quotient") {
auto f = [](const decltype(Parameters)& p)->double {
double y = p[1]->GetValue();
@ -515,15 +591,37 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
};
Parameters.push_back(new aFunc<decltype(f), 3>(f, fdmex, element, Prefix, var));
} else if (operation == "random") {
auto f = [](const decltype(Parameters)& p)->double {
return GaussianRandomNumber();
double mean = 0.0;
double stddev = 1.0;
string mean_attr = element->GetAttributeValue("mean");
string stddev_attr = element->GetAttributeValue("stddev");
if (!mean_attr.empty())
mean = atof(mean_attr.c_str());
if (!stddev_attr.empty())
stddev = atof(stddev_attr.c_str());
auto distribution = make_shared<normal_distribution<double>>(mean, stddev);
auto generator(makeRandomEngine(element, fdmex));
auto f = [generator, distribution]()->double {
return (*distribution.get())(*generator);
};
Parameters.push_back(new aFunc<decltype(f), 0>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
Prefix));
} else if (operation == "urandom") {
auto f = [](const decltype(Parameters)& p)->double {
return -1.0 + (((double)rand()/double(RAND_MAX))*2.0);
double lower = -1.0;
double upper = 1.0;
string lower_attr = element->GetAttributeValue("lower");
string upper_attr = element->GetAttributeValue("upper");
if (!lower_attr.empty())
lower = atof(lower_attr.c_str());
if (!upper_attr.empty())
upper = atof(upper_attr.c_str());
auto distribution = make_shared<uniform_real_distribution<double>>(lower, upper);
auto generator(makeRandomEngine(element, fdmex));
auto f = [generator, distribution]()->double {
return (*distribution.get())(*generator);
};
Parameters.push_back(new aFunc<decltype(f), 0>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
Prefix));
} else if (operation == "switch") {
string ctxMsg = element->ReadFrom();
auto f = [ctxMsg](const decltype(Parameters)& p)->double {
@ -548,7 +646,8 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
throw("Fatal error");
}
};
Parameters.push_back(new aFunc<decltype(f), 2, MaxArgs>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
var, MaxArgs));
} else if (operation == "interpolate1d") {
auto f = [](const decltype(Parameters)& p)->double {
// This is using the bisection algorithm. Special care has been
@ -584,7 +683,8 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
return ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin);
};
Parameters.push_back(new aFunc<decltype(f), 5, MaxArgs, OddEven::Odd>(f, fdmex, element, Prefix, var));
Parameters.push_back(new aFunc<decltype(f), 5>(f, fdmex, element, Prefix,
var, MaxArgs, OddEven::Odd));
} else if (operation == "rotation_alpha_local") {
// Calculates local angle of attack for skydiver body component.
// Euler angles from the intermediate body frame to the local body frame
@ -802,9 +902,6 @@ FGFunction::~FGFunction()
bool FGFunction::IsConstant(void) const
{
// Functions without parameters are assumed to be non-const
if (Parameters.empty()) return false;
for (auto p: Parameters) {
if (!p->IsConstant())
return false;
@ -850,10 +947,11 @@ string FGFunction::GetValueAsString(void) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFunction::bind(Element* el, const string& Prefix)
string FGFunction::CreateOutputNode(Element* el, const string& Prefix)
{
string nName;
if ( !Name.empty() ) {
string nName;
if (Prefix.empty())
nName = PropertyManager->mkPropertyName(Name, false);
else {
@ -878,9 +976,19 @@ void FGFunction::bind(Element* el, const string& Prefix)
<< "Property " << nName << " has already been successfully bound (late)." << endl;
throw("Failed to bind the property to an existing already tied node.");
}
PropertyManager->Tie(nName, this, &FGFunction::GetValue);
}
return nName;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFunction::bind(Element* el, const string& Prefix)
{
string nName = CreateOutputNode(el, Prefix);
if (!nName.empty())
PropertyManager->Tie(nName, this, &FGFunction::GetValue);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -679,7 +679,6 @@ refers to one or more instances of a property, value, or table.
</switch>
Example: if flight-mode is 2, the switch function returns 0.50
<switch>
<p> executive/flight-mode </p>
<v> 0.25 </v>
@ -688,11 +687,32 @@ refers to one or more instances of a property, value, or table.
<v> 1.00 </v>
</switch>
@endcode
- @b random Takes no arguments and returns a Gaussian distributed random number
@code <random/> @endcode
- @b urandom, Takes no arguments and returns a uniformly distributed random
number between -1 and +1
@code<urandom/>@endcode
- @b random Returns a normal distributed random number.
The function, without parameters, returns a normal distributed
random value with a distribution defined by the parameters
mean = 0.0 and standard deviation (stddev) = 1.0
The Mean of the distribution (its expected value, μ).
Which coincides with the location of its peak.
Standard deviation (σ): The square root of variance,
representing the dispersion of values from the distribution mean.
This shall be a positive value (σ>0).
@code
<random/>
<random seed="1234"/>
<random seed="time_now"/>
<random seed="time_now" mean="0.0" stddev="1.0"/>
@endcode
- @b urandom Returns a uniformly distributed random number.
The function, without parameters, returns a random value
between the minimum value -1.0 and the maximum value of 1.0
The two maximum and minimum values can be modified using the
lower and upper parameters.
@code
<urandom/>
<random seed="1234"/>
<random seed="time_now"/>
<random seed="time_now" lower="-1.0" upper="1.0"/>
@endcode
- @b pi Takes no argument and returns the value of Pi
@code<pi/>@endcode
- @b interpolate1d returns the result from a 1-dimensional interpolation of the
@ -734,8 +754,8 @@ class FGFunction : public FGParameter, public FGJSBBase
public:
/// Default constructor.
FGFunction()
: cached(false), cachedValue(-HUGE_VAL), pNode(nullptr), pCopyTo(nullptr),
PropertyManager(nullptr) {}
: cached(false), cachedValue(-HUGE_VAL), PropertyManager(nullptr),
pNode(nullptr), pCopyTo(nullptr) {}
explicit FGFunction(FGPropertyManager* pm)
: FGFunction()
@ -797,6 +817,8 @@ protected:
bool cached;
double cachedValue;
std::vector <FGParameter_ptr> Parameters;
FGPropertyManager* PropertyManager;
FGPropertyNode_ptr pNode;
void Load(Element* element, FGPropertyValue* var, FGFDMExec* fdmex,
const std::string& prefix="");
@ -804,12 +826,11 @@ protected:
void CheckMinArguments(Element* el, unsigned int _min);
void CheckMaxArguments(Element* el, unsigned int _max);
void CheckOddOrEvenArguments(Element* el, OddEven odd_even);
std::string CreateOutputNode(Element* el, const string& Prefix);
private:
std::string Name;
FGPropertyNode_ptr pNode;
FGPropertyNode_ptr pCopyTo; // Property node for CopyTo property string
FGPropertyManager* PropertyManager;
void Debug(int from);
};

View file

@ -10,21 +10,21 @@
------- (C) 2011 Ola Røer Thorsen (ola@silentwings.no) -----------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
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
------------------------------------------------------------------------------
@ -47,9 +47,6 @@ INCLUDES
namespace JSBSim {
// Set up the default ground callback object.
FGGroundCallback_ptr FGLocation::GroundCallback = NULL;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -85,9 +82,9 @@ FGLocation::FGLocation(double lon, double lat, double radius)
double cosLat = cos(lat);
double sinLon = sin(lon);
double cosLon = cos(lon);
mECLoc = FGColumnVector3( radius*cosLat*cosLon,
radius*cosLat*sinLon,
radius*sinLat );
mECLoc = { radius*cosLat*cosLon,
radius*cosLat*sinLon,
radius*sinLat };
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -115,6 +112,7 @@ FGLocation::FGLocation(const FGLocation& l)
c = l.c;
ec = l.ec;
ec2 = l.ec2;
mEllipseSet = l.mEllipseSet;
/*ag
* if the cache is not valid, all of the following values are unset.
@ -137,10 +135,11 @@ FGLocation::FGLocation(const FGLocation& l)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
const FGLocation& FGLocation::operator=(const FGLocation& l)
FGLocation& FGLocation::operator=(const FGLocation& l)
{
mECLoc = l.mECLoc;
mCacheValid = l.mCacheValid;
mEllipseSet = l.mEllipseSet;
a = l.a;
e2 = l.e2;
@ -232,15 +231,16 @@ void FGLocation::SetPosition(double lon, double lat, double radius)
double sinLon = sin(lon);
double cosLon = cos(lon);
mECLoc = FGColumnVector3( radius*cosLat*cosLon,
radius*cosLat*sinLon,
radius*sinLat );
mECLoc = { radius*cosLat*cosLon,
radius*cosLat*sinLon,
radius*sinLat };
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
{
assert(mEllipseSet);
mCacheValid = false;
double slat = sin(lat);
@ -257,6 +257,7 @@ void FGLocation::SetPositionGeodetic(double lon, double lat, double height)
void FGLocation::SetEllipse(double semimajor, double semiminor)
{
mCacheValid = false;
mEllipseSet = true;
a = semimajor;
ec = semiminor/a;
@ -267,6 +268,16 @@ void FGLocation::SetEllipse(double semimajor, double semiminor)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGLocation::GetSeaLevelRadius(void) const
{
assert(mEllipseSet);
ComputeDerived();
double cosLat = cos(mLat);
return a*ec/sqrt(1.0-e2*cosLat*cosLat);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGLocation::ComputeDerivedUnconditional(void) const
{
// The radius is just the Euclidean norm of the vector.
@ -274,49 +285,80 @@ void FGLocation::ComputeDerivedUnconditional(void) const
// The distance of the location to the Z-axis, which is the axis
// through the poles.
double r02 = mECLoc(eX)*mECLoc(eX) + mECLoc(eY)*mECLoc(eY);
double rxy = sqrt(r02);
double rxy = mECLoc.Magnitude(eX, eY);
// Compute the sin/cos values of the longitude
// Compute the longitude and its sin/cos values.
double sinLon, cosLon;
if (rxy == 0.0) {
sinLon = 0.0;
cosLon = 1.0;
mLon = 0.0;
} else {
sinLon = mECLoc(eY)/rxy;
cosLon = mECLoc(eX)/rxy;
mLon = atan2(mECLoc(eY), mECLoc(eX));
}
// Compute the sin/cos values of the latitude
// Compute the geocentric & geodetic latitudes.
double sinLat, cosLat;
if (mRadius == 0.0) {
mLat = 0.0;
sinLat = 0.0;
cosLat = 1.0;
} else {
sinLat = mECLoc(eZ)/mRadius;
cosLat = rxy/mRadius;
if (mEllipseSet) {
mGeodLat = 0.0;
GeodeticAltitude = -a;
}
}
// Compute the longitude and latitude itself
if ( mECLoc( eX ) == 0.0 && mECLoc( eY ) == 0.0 )
mLon = 0.0;
else
mLon = atan2( mECLoc( eY ), mECLoc( eX ) );
if ( rxy == 0.0 && mECLoc( eZ ) == 0.0 )
mLat = 0.0;
else
else {
mLat = atan2( mECLoc(eZ), rxy );
// Calculate the geodetic latitude based on "Transformation from Cartesian to
// geodetic coordinates accelerated by Halley's method", Fukushima T. (2006)
// Journal of Geodesy, Vol. 79, pp. 689-693
// Unlike I. Sofair's method which uses a closed form solution, Fukushima's
// method is an iterative method whose convergence is so fast that only one
// iteration suffices. In addition, Fukushima's method has a much better
// numerical stability over Sofair's method at the North and South poles and
// it also gives the correct result for a spherical Earth.
if (mEllipseSet) {
double s0 = fabs(mECLoc(eZ));
double zc = ec * s0;
double c0 = ec * rxy;
double c02 = c0 * c0;
double s02 = s0 * s0;
double a02 = c02 + s02;
double a0 = sqrt(a02);
double a03 = a02 * a0;
double s1 = zc*a03 + c*s02*s0;
double c1 = rxy*a03 - c*c02*c0;
double cs0c0 = c*c0*s0;
double b0 = 1.5*cs0c0*((rxy*s0-zc*c0)*a0-cs0c0);
s1 = s1*a03-b0*s0;
double cc = ec*(c1*a03-b0*c0);
mGeodLat = sign(mECLoc(eZ))*atan(s1 / cc);
double s12 = s1 * s1;
double cc2 = cc * cc;
double norm = sqrt(s12 + cc2);
cosLat = cc / norm;
sinLat = sign(mECLoc(eZ)) * s1 / norm;
GeodeticAltitude = (rxy*cc + s0*s1 - a*sqrt(ec2*s12 + cc2)) / norm;
}
else {
sinLat = mECLoc(eZ)/mRadius;
cosLat = rxy/mRadius;
}
}
// Compute the transform matrices from and to the earth centered frame.
// See Stevens and Lewis, "Aircraft Control and Simulation", Second Edition,
// Eqn. 1.4-13, page 40. In Stevens and Lewis notation, this is C_n/e - the
// orientation of the navigation (local) frame relative to the ECEF frame,
// and a transformation from ECEF to nav (local) frame.
mTec2l = FGMatrix33( -cosLon*sinLat, -sinLon*sinLat, cosLat,
-sinLon , cosLon , 0.0 ,
-cosLon*cosLat, -sinLon*cosLat, -sinLat );
mTec2l = { -cosLon*sinLat, -sinLon*sinLat, cosLat,
-sinLon , cosLon , 0.0 ,
-cosLon*cosLat, -sinLon*cosLat, -sinLat };
// In Stevens and Lewis notation, this is C_e/n - the
// orientation of the ECEF frame relative to the nav (local) frame,
@ -324,34 +366,6 @@ void FGLocation::ComputeDerivedUnconditional(void) const
mTl2ec = mTec2l.Transposed();
// Calculate the geodetic latitude based on "Transformation from Cartesian
// to geodetic coordinates accelerated by Halley's method", Fukushima T. (2006)
// Journal of Geodesy, Vol. 79, pp. 689-693
// Unlike I. Sofair's method which uses a closed form solution, Fukushima's
// method is an iterative method whose convergence is so fast that only one
// iteration suffices. In addition, Fukushima's method has a much better
// numerical stability over Sofair's method at the North and South poles and
// it also gives the correct result for a spherical Earth.
double s0 = fabs(mECLoc(eZ));
double zc = ec * s0;
double c0 = ec * rxy;
double c02 = c0 * c0;
double s02 = s0 * s0;
double a02 = c02 + s02;
double a0 = sqrt(a02);
double a03 = a02 * a0;
double s1 = zc*a03 + c*s02*s0;
double c1 = rxy*a03 - c*c02*c0;
double cs0c0 = c*c0*s0;
double b0 = 1.5*cs0c0*((rxy*s0-zc*c0)*a0-cs0c0);
s1 = s1*a03-b0*s0;
double cc = ec*(c1*a03-b0*c0);
mGeodLat = sign(mECLoc(eZ))*atan(s1 / cc);
double s12 = s1 * s1;
double cc2 = cc * cc;
GeodeticAltitude = (rxy*cc + s0*s1 - a*sqrt(ec2*s12 + cc2)) / sqrt(s12 + cc2);
// Mark the cached values as valid
mCacheValid = true;
}
@ -377,11 +391,12 @@ void FGLocation::ComputeDerivedUnconditional(void) const
double FGLocation::GetDistanceTo(double target_longitude,
double target_latitude) const
{
double delta_lat_rad = target_latitude - GetLatitude();
double delta_lon_rad = target_longitude - GetLongitude();
ComputeDerived();
double delta_lat_rad = target_latitude - mLat;
double delta_lon_rad = target_longitude - mLon;
double distance_a = pow(sin(0.5*delta_lat_rad), 2.0)
+ (GetCosLatitude() * cos(target_latitude)
+ (cos(mLat) * cos(target_latitude)
* (pow(sin(0.5*delta_lon_rad), 2.0)));
return 2.0 * GetRadius() * atan2(sqrt(distance_a), sqrt(1.0 - distance_a));
@ -406,11 +421,12 @@ double FGLocation::GetDistanceTo(double target_longitude,
double FGLocation::GetHeadingTo(double target_longitude,
double target_latitude) const
{
double delta_lon_rad = target_longitude - GetLongitude();
ComputeDerived();
double delta_lon_rad = target_longitude - mLon;
double Y = sin(delta_lon_rad) * cos(target_latitude);
double X = GetCosLatitude() * sin(target_latitude)
- GetSinLatitude() * cos(target_latitude) * cos(delta_lon_rad);
double X = cos(mLat) * sin(target_latitude)
- sin(mLat) * cos(target_latitude) * cos(delta_lon_rad);
double heading_to_waypoint_rad = atan2(Y, X);
if (heading_to_waypoint_rad < 0) heading_to_waypoint_rad += 2.0*M_PI;

View file

@ -42,10 +42,16 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <cassert>
#include "FGJSBBase.h"
#include "FGColumnVector3.h"
#include "FGMatrix33.h"
#include "input_output/FGGroundCallback.h"
/* Setting the -ffast-math compilation flag is highly discouraged */
#ifdef __FAST_MATH__
#error Usage of -ffast-math is strongly discouraged
#endif
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -58,9 +64,9 @@ CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** FGLocation holds an arbitrary location in the Earth centered Earth fixed
reference frame (ECEF). The coordinate frame ECEF has its center in the middle
of the earth. The X-axis points from the center of the Earth towards a
location with zero latitude and longitude on the Earth surface. The Y-axis
reference frame (ECEF). The coordinate frame ECEF has its center in the
middle of the earth. The X-axis points from the center of the Earth towards
a location with zero latitude and longitude on the Earth surface. The Y-axis
points from the center of the Earth towards a location with zero latitude
and 90 deg East longitude on the Earth surface. The Z-axis points from the
Earth center to the geographic north pole.
@ -72,46 +78,39 @@ CLASS DOCUMENTATION
It is common to associate a parent frame with a location. This frame is
usually called the local horizontal frame or simply the local frame. It is
also called the NED frame (North, East, Down), as well as the Navigation
frame. This frame has its X/Y plane parallel to the surface of the Earth
(with the assumption of a spherical Earth). The X-axis points towards north,
the Y-axis points east and the Z-axis points to the center of the Earth.
frame. This frame has its X/Y plane parallel to the surface of the Earth.
The X-axis points towards north, the Y-axis points east and the Z-axis
is normal to the reference spheroid (WGS84 for Earth).
Since the local frame is determined by the location (and NOT by the
orientation of the vehicle IN any frame), this class also provides the
orientation of the vehicle IN any frame), this class also provides the
rotation matrices required to transform from the Earth centered (ECEF) frame
to the local horizontal frame and back. This class also "owns" the
transformations that go from the inertial frame (Earth-centered Inertial, or
ECI) to and from the ECEF frame, as well as to and from the local frame.
Again, this is because the ECI, ECEF, and local frames do not involve the
actual orientation of the vehicle - only the location on the Earth surface,
and the angular difference between the ECI and ECEF frames. There are
conversion functions for conversion of position vectors given in the one
to the local horizontal frame and back. This class "owns" the
transformations that go from the ECEF frame to and from the local frame.
Again, this is because the ECEF, and local frames do not involve the actual
orientation of the vehicle - only the location on the Earth surface. There
are conversion functions for conversion of position vectors given in the one
frame to positions in the other frame.
To keep the transformation matrices between the ECI and ECEF frames up to
date, the Earth angular position must be updated by calling
SetEarthPositionAngle() or IncrementEarthPositionAngle(). This must be done
prior to any conversion from and to the ECI frame.
The Earth centered reference frame is NOT an inertial frame since it rotates
with the Earth.
The cartesian coordinates (X,Y,Z) in the Earth centered frame are the master values. All other
values are computed from these master values and are cached as long as the
location is changed by access through a non-const member function. Values
are cached to improve performance. It is best practice to work with a
natural set of master values. Other parameters that are derived from these
master values are calculated only when needed, and IF they are needed and
calculated, then they are cached (stored and remembered) so they do not need
to be re-calculated until the master values they are derived from are
themselves changed (and become stale).
The cartesian coordinates (X,Y,Z) in the Earth centered frame are the master
values. All other values are computed from these master values and are
cached as long as the location is changed by access through a non-const
member function. Values are cached to improve performance. It is best
practice to work with a natural set of master values. Other parameters that
are derived from these master values are calculated only when needed, and IF
they are needed and calculated, then they are cached (stored and remembered)
so they do not need to be re-calculated until the master values they are
derived from are themselves changed (and become stale).
Accuracy and round off
Given,
- that we model a vehicle near the Earth
- that the Earth surface radius is about 2*10^7, ft
- that the Earth surface average radius is about 2*10^7, ft
- that we use double values for the representation of the location
we have an accuracy of about
@ -136,9 +135,8 @@ CLASS DOCUMENTATION
change is of the same magnitude for all components in this representation
which is an advantage for numerical stability in implicit time-stepping.
Note: The latitude is a GEOCENTRIC value. FlightGear converts latitude to a
geodetic value and uses that. In order to get best matching relative to a
map, geocentric latitude must be converted to geodetic.
Note: Both GEOCENTRIC and GEODETIC latitudes can be used. In order to get
best matching relative to a map, geodetic latitude must be used.
@see Stevens and Lewis, "Aircraft Control and Simulation", Second edition
@see W. C. Durham "Aircraft Dynamics & Control", section 2.2
@ -174,39 +172,40 @@ public:
/** Set the longitude.
@param longitude Longitude in rad to set.
Sets the longitude of the location represented with this class
instance to the value of the given argument. The value is meant
to be in rad. The latitude and the radius value are preserved
with this call with the exception of radius being equal to
zero. If the radius is previously set to zero it is changed to be
equal to 1.0 past this call. Longitude is positive east and negative west. */
Sets the longitude of the location represented with this class instance to
the value of the given argument. The value is meant to be in rad. The
latitude and the radius value are preserved with this call with the
exception of radius being equal to zero. If the radius is previously set
to zero it is changed to be equal to 1.0 past this call.
Longitude is positive east and negative west.
The arguments should be within the bounds of -pi <= lon <= pi.
The behavior of this function with arguments outside this range is left as
an exercise to the gentle reader ... */
void SetLongitude(double longitude);
/** Set the latitude.
@param latitude Latitude in rad to set.
Sets the latitude of the location represented with this class
instance to the value of the given argument. The value is meant
to be in rad. The longitude and the radius value are preserved
with this call with the exception of radius being equal to
zero. If the radius is previously set to zero it is changed to be
equal to 1.0 past this call.
/** Set the GEOCENTRIC latitude.
@param latitude GEOCENTRIC latitude in rad to set.
Sets the latitude of the location represented with this class instance to
the value of the given argument. The value is meant to be in rad. The
longitude and the radius value are preserved with this call with the
exception of radius being equal to zero. If the radius is previously set
to zero it is changed to be equal to 1.0 past this call.
Latitude is positive north and negative south.
The arguments should be within the bounds of -pi/2 <= lat <= pi/2.
The behavior of this function with arguments outside this range is
left as an exercise to the gentle reader ... */
The behavior of this function with arguments outside this range is left as
an exercise to the gentle reader ... */
void SetLatitude(double latitude);
/** Set the distance from the center of the earth.
@param radius Radius in ft to set.
Sets the radius of the location represented with this class
instance to the value of the given argument. The value is meant
to be in ft. The latitude and longitude values are preserved
with this call with the exception of radius being equal to
zero. If the radius is previously set to zero, latitude and
longitude is set equal to zero past this call.
Sets the radius of the location represented with this class instance to
the value of the given argument. The value is meant to be in ft. The
latitude and longitude values are preserved with this call with the
exception of radius being equal to zero. If the radius is previously set
to zero, latitude and longitude is set equal to zero past this call.
The argument should be positive.
The behavior of this function called with a negative argument is
left as an exercise to the gentle reader ... */
The behavior of this function called with a negative argument is left as
an exercise to the gentle reader ... */
void SetRadius(double radius);
/** Sets the longitude, latitude and the distance from the center of the earth.
@ -215,7 +214,7 @@ public:
@param radius distance from center of earth to vehicle in feet*/
void SetPosition(double lon, double lat, double radius);
/** Sets the longitude, latitude and the distance above the reference ellipsoid.
/** Sets the longitude, latitude and the distance above the reference spheroid.
@param lon longitude in radians
@param lat GEODETIC latitude in radians
@param height distance above the reference ellipsoid to vehicle in feet*/
@ -223,7 +222,9 @@ public:
/** Sets the semimajor and semiminor axis lengths for this planet.
The eccentricity and flattening are calculated from the semimajor
and semiminor axis lengths */
and semiminor axis lengths.
@param semimajor planet semi-major axis in ft.
@param semiminor planet semi-minor axis in ft.*/
void SetEllipse(double semimajor, double semiminor);
/** Get the longitude.
@ -244,137 +245,51 @@ public:
/** Get the cosine of Longitude. */
double GetCosLongitude() const { ComputeDerived(); return mTec2l(2,2); }
/** Get the latitude.
@return the latitude in rad of the location represented with this
class instance. The returned values are in the range between
/** Get the GEOCENTRIC latitude in radians.
@return the geocentric latitude in rad of the location represented with
this class instance. The returned values are in the range between
-pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
double GetLatitude() const { ComputeDerived(); return mLat; }
/** Get the geodetic latitude.
/** Get the GEODETIC latitude in radians.
@return the geodetic latitude in rad of the location represented with this
class instance. The returned values are in the range between
-pi/2 <= lon <= pi/2. Latitude is positive north and negative south. */
double GetGeodLatitudeRad(void) const { ComputeDerived(); return mGeodLat; }
double GetGeodLatitudeRad(void) const {
assert(mEllipseSet);
ComputeDerived(); return mGeodLat;
}
/** Get the latitude.
@return the latitude in deg of the location represented with this
class instance. The returned value is in the range between
/** Get the GEOCENTRIC latitude in degrees.
@return the geocentric latitude in deg of the location represented with
this class instance. The returned value is in the range between
-90 <= lon <= 90. Latitude is positive north and negative south. */
double GetLatitudeDeg() const { ComputeDerived(); return radtodeg*mLat; }
/** Get the geodetic latitude in degrees.
/** Get the GEODETIC latitude in degrees.
@return the geodetic latitude in degrees of the location represented by
this class instance. The returned value is in the range between
-90 <= lon <= 90. Latitude is positive north and negative south. */
double GetGeodLatitudeDeg(void) const { ComputeDerived(); return radtodeg*mGeodLat; }
/** Gets the geodetic altitude in feet. */
double GetGeodAltitude(void) const {ComputeDerived(); return GeodeticAltitude;}
/** Get the sine of Latitude. */
double GetSinLatitude() const { ComputeDerived(); return -mTec2l(3,3); }
/** Get the cosine of Latitude. */
double GetCosLatitude() const { ComputeDerived(); return mTec2l(1,3); }
/** Get the cosine of Latitude. */
double GetTanLatitude() const {
ComputeDerived();
double cLat = mTec2l(1,3);
if (cLat == 0.0)
return 0.0;
else
return -mTec2l(3,3)/cLat;
double GetGeodLatitudeDeg(void) const {
assert(mEllipseSet);
ComputeDerived(); return radtodeg*mGeodLat;
}
/** Get the distance from the center of the earth.
/** Gets the geodetic altitude in feet. */
double GetGeodAltitude(void) const {
assert(mEllipseSet);
ComputeDerived(); return GeodeticAltitude;
}
/** Get the sea level radius in feet below the current location. */
double GetSeaLevelRadius(void) const;
/** Get the distance from the center of the earth in feet.
@return the distance of the location represented with this class
instance to the center of the earth in ft. The radius value is
always positive. */
//double GetRadius() const { return mECLoc.Magnitude(); } // may not work with FlightGear
double GetRadius() const { ComputeDerived(); return mRadius; }
/** @name Functions that rely on the ground callback
The following functions allow to set and get the vehicle position above
the sea or the ground. The sea and the ground levels are obtained by
interrogating an FGGroundCallback instance. A ground callback must
therefore be set with SetGroundCallback() before calling any of these
functions. */
///@{
/** Set the altitude above sea level.
@param altitudeASL altitude above Sea Level in feet.
@see SetGroundCallback */
void SetAltitudeASL(double altitudeASL)
{ SetRadius(GetSeaLevelRadius() + altitudeASL); }
/** Set the altitude above ground level.
@param altitudeAGL altitude above Ground Level in feet.
@see SetGroundCallback */
void SetAltitudeAGL(double altitudeAGL)
{ SetRadius(GetTerrainRadius() + altitudeAGL); }
/** Get the local sea level radius
@return the sea level radius at the location in feet.
@see SetGroundCallback */
double GetSeaLevelRadius(void) const
{ ComputeDerived(); return GroundCallback->GetSeaLevelRadius(*this); }
/** Get the local terrain radius
@return the terrain level radius at the location in feet.
@see SetGroundCallback */
double GetTerrainRadius(void) const
{ ComputeDerived(); return GroundCallback->GetTerrainGeoCentRadius(*this); }
/** Get the altitude above sea level.
@return the altitude ASL in feet.
@see SetGroundCallback */
double GetAltitudeASL(void) const
{ return GetRadius() - GetSeaLevelRadius(); }
/** Get the altitude above ground level.
@return the altitude AGL in feet.
@see SetGroundCallback */
double GetAltitudeAGL(void) const {
FGLocation c;
FGColumnVector3 n,v,w;
return GetContactPoint(c,n,v,w);
}
/** Get terrain contact point information below the current location.
@param contact Contact point location
@param normal Terrain normal vector in contact point (ECEF frame)
@param v Terrain linear velocity in contact point (ECEF frame)
@param w Terrain angular velocity in contact point (ECEF frame)
@return Location altitude above contact point (AGL) in feet.
@see SetGroundCallback */
double GetContactPoint(FGLocation& contact, FGColumnVector3& normal,
FGColumnVector3& v, FGColumnVector3& w) const
{ ComputeDerived(); return GroundCallback->GetAGLevel(*this, contact, normal, v, w); }
///@}
/** Sets the ground callback pointer. The FGGroundCallback instance will be
interrogated by FGLocation each time some terrain informations are needed.
This will mainly occur when altitudes above the sea level or above the
ground level are needed. A 'smart pointer' is used internally to prevent
the FGGroundCallback instance against accidental deletion. This can only
work if the calling application also make use of FGGroundCallback_ptr
'smart pointers' to manage their copy of the ground callback.
@param gc A pointer to a ground callback object
@see FGGroundCallback
*/
static void SetGroundCallback(FGGroundCallback* gc) { GroundCallback = gc; }
/** Get a pointer to the ground callback currently used. Since the
FGGroundcallback instance might have been created outside JSBSim, it is
recommanded to store the returned pointer in a 'smart pointer'
FGGroundCallback_ptr. This pointer maintains a reference counter and
protects the returned pointer against an accidental deletion of the object
it is pointing to.
@return A pointer to the current ground callback object.
@see FGGroundCallback
*/
static FGGroundCallback* GetGroundCallback(void) { return GroundCallback; }
/** Transform matrix from local horizontal to earth centered frame.
@return a const reference to the rotation matrix of the transform from
the local horizontal frame to the earth centered frame. */
@ -388,16 +303,16 @@ public:
/** Get the geodetic distance between the current location and a given
location. This corresponds to the shortest distance between the two
locations. Earth curvature is taken into account.
@param target_longitude the target longitude
@param target_latitude the target latitude
@param target_longitude the target longitude in radians
@param target_latitude the target latitude in radians
@return The geodetic distance between the two locations */
double GetDistanceTo(double target_longitude, double target_latitude) const;
/** Get the heading that should be followed from the current location to
a given location along the shortest path. Earth curvature is
taken into account.
@param target_longitude the target longitude
@param target_latitude the target latitude
a given location along the shortest path. Earth curvature is taken into
account.
@param target_longitude the target longitude in radians
@param target_latitude the target latitude in radians
@return The heading that should be followed to reach the targeted
location along the shortest path */
double GetHeadingTo(double target_longitude, double target_latitude) const;
@ -477,9 +392,9 @@ public:
}
/** Sets this location via the supplied location object.
@param v A location object reference.
@param l A location object reference.
@return a reference to the FGLocation object. */
const FGLocation& operator=(const FGLocation& l);
FGLocation& operator=(const FGLocation& l);
/** This operator returns true if the ECEF location vectors for the two
location objects are equal. */
@ -533,14 +448,18 @@ public:
A new object is returned that defines a position which is the sum of the
cartesian coordinates of the two positions provided. */
FGLocation operator+(const FGLocation& l) const {
return FGLocation(mECLoc + l.mECLoc);
FGLocation result(mECLoc + l.mECLoc);
if (mEllipseSet) result.SetEllipse(a, ec*a);
return result;
}
/** This operator substracts two ECEF position vectors.
A new object is returned that defines a position which is the difference
of the cartesian coordinates of the two positions provided. */
FGLocation operator-(const FGLocation& l) const {
return FGLocation(mECLoc - l.mECLoc);
FGLocation result(mECLoc - l.mECLoc);
if (mEllipseSet) result.SetEllipse(a, ec*a);
return result;
}
/** This operator scales an ECEF position vector.
@ -548,7 +467,9 @@ public:
coordinates of the provided ECEF position scaled by the supplied scalar
value. */
FGLocation operator*(double scalar) const {
return FGLocation(scalar*mECLoc);
FGLocation result(scalar*mECLoc);
if (mEllipseSet) result.SetEllipse(a, ec*a);
return result;
}
/** Cast to a simple 3d vector */
@ -607,9 +528,9 @@ private:
The C++ keyword "mutable" tells the compiler that the data member is
allowed to change during a const member function. */
mutable bool mCacheValid;
/** The ground callback object pointer */
static FGGroundCallback_ptr GroundCallback;
// Flag that checks that geodetic methods are called after SetEllipse() has
// been called.
bool mEllipseSet = false;
};
/** Scalar multiplication.

View file

@ -7,21 +7,21 @@ Date started: Unknown
------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -51,7 +51,6 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGColumnVector3;
class FGQuaternion;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -157,7 +156,8 @@ public:
/** Prints the contents of the matrix.
@param delimeter the item separator (tab or comma, etc.)
@param prefix an additional prefix that is used to indent the 3X3 matrix printout
@param prefix an additional prefix that is used to indent the 3X3 matrix
printout
@return a string with the delimeter-separated contents of the matrix */
std::string Dump(const std::string& delimiter, const std::string& prefix) const;
@ -230,8 +230,8 @@ public:
unsigned int Cols(void) const { return eColumns; }
/** Transposed matrix.
This function only returns the transpose of this matrix. This matrix itself
remains unchanged.
This function only returns the transpose of this matrix. This matrix
itself remains unchanged.
@return the transposed matrix.
*/
FGMatrix33 Transposed(void) const {
@ -318,6 +318,24 @@ public:
return *this;
}
/** Assignment operator.
@param lv initializer list of at most 9 values.
Copy the content of the list into *this. */
FGMatrix33& operator=(std::initializer_list<double> lv)
{
double *v = data;
for(auto& x: lv) {
*v = x;
v += 3;
if (v-data > 8)
v -= 8;
}
return *this;
}
/** Matrix vector multiplication.
@param v vector to multiply with.

View file

@ -329,7 +329,7 @@ bool FGAerodynamics::Load(Element *document)
Name = "Aerodynamics Model: " + document->GetAttributeValue("name");
// Perform base class Pre-Load
if (!FGModel::Load(document, true))
if (!FGModel::Upload(document, true))
return false;
DetermineAxisSystem(document); // Determine if Lift/Side/Drag, etc. is used.

View file

@ -7,21 +7,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -63,7 +63,8 @@ CLASS DOCUMENTATION
as ground effect, aerodynamic reference point shift, and maximum lift curve
tailoff are handled.
@code
<h3>Configuration File Format for \<aerodynamics> Section:</h3>
@code{.xml}
<aerodynamics>
<alphalimits unit="{RAD | DEG}">
<min> {number} </min>
@ -90,12 +91,12 @@ CLASS DOCUMENTATION
Optionally two other coordinate systems may be used.<br><br>
1) Body coordinate system:
@code
@code{.xml}
<axis name="{X | Y | Z}">
@endcode
<br>
2) Axial-Normal coordinate system:
@code
@code{.xml}
<axis name="{AXIAL | NORMAL | SIDE}">
@endcode
<br>
@ -117,9 +118,9 @@ public:
@param Executive a pointer to the parent executive object */
FGAerodynamics(FGFDMExec* Executive);
/// Destructor
~FGAerodynamics();
~FGAerodynamics() override;
bool InitModel(void);
bool InitModel(void) override;
/** Runs the Aerodynamics model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -128,14 +129,14 @@ public:
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 Run(bool Holding) override;
/** Loads the Aerodynamics model.
The Load function for this class expects the XML parser to
have found the aerodynamics keyword in the configuration file.
@param element pointer to the current XML element for aerodynamics parameters.
@return true if successful */
virtual bool Load(Element* element);
bool Load(Element* element) override;
/** Gets the total aerodynamic force vector.
@return a force vector reference. */
@ -282,7 +283,7 @@ private:
void bind(void);
void BuildStabilityTransformMatrices(void);
void Debug(int from);
void Debug(int from) override;
};
} // namespace JSBSim

View file

@ -118,7 +118,7 @@ bool FGAircraft::Load(Element* el)
string element_name;
Element* element;
if (!FGModel::Load(el, true)) return false;
if (!FGModel::Upload(el, true)) return false;
if (el->FindElement("wingarea"))
WingArea = el->FindElementValueAsNumberConvertTo("wingarea", "FT2");

View file

@ -7,21 +7,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -62,7 +62,7 @@ CLASS DOCUMENTATION
<p> The \<metrics> section of the aircraft configuration file is read here, and
the metrical information is held by this class.
<h3>Configuration File Format for \<metrics> Section:</h3>
@code
@code{.xml}
<metrics>
<wingarea unit="{FT2 | M2}"> {number} </wingarea>
<wingspan unit="{FT | M}"> {number} </wingspan>
@ -106,7 +106,7 @@ public:
FGAircraft(FGFDMExec *Executive);
/// Destructor
~FGAircraft();
~FGAircraft() override;
/** Runs the Aircraft model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -116,15 +116,15 @@ public:
"Resume" command to be given.
@see JSBSim.cpp documentation
@return false if no error */
bool Run(bool Holding);
bool Run(bool Holding) override;
bool InitModel(void);
bool InitModel(void) override;
/** Loads the aircraft.
The executive calls this method to load the aircraft into JSBSim.
@param el a pointer to the element tree
@return true if successful */
virtual bool Load(Element* el);
bool Load(Element* el) override;
/** Gets the aircraft name
@return the name of the aircraft as a string type */
@ -150,6 +150,8 @@ public:
double GetMoments(int idx) const { return vMoments(idx); }
const FGColumnVector3& GetForces(void) const { return vForces; }
double GetForces(int idx) const { return vForces(idx); }
/** Gets the the aero reference point (RP) coordinates.
@return a vector containing the RP coordinates in the structural frame. */
const FGColumnVector3& GetXYZrp(void) const { return vXYZrp; }
const FGColumnVector3& GetXYZvrp(void) const { return vXYZvrp; }
const FGColumnVector3& GetXYZep(void) const { return vXYZep; }
@ -162,9 +164,6 @@ public:
void SetWingArea(double S) {WingArea = S;}
void bind(void);
void unbind(void);
struct Inputs {
FGColumnVector3 AeroForce;
FGColumnVector3 PropForce;
@ -191,7 +190,9 @@ private:
double lbarh,lbarv,vbarh,vbarv;
std::string AircraftName;
void Debug(int from);
void bind(void);
void unbind(void);
void Debug(int from) override;
};
} // namespace JSBSim

View file

@ -46,6 +46,7 @@ INCLUDES
#include "initialization/FGInitialCondition.h"
#include "FGFDMExec.h"
#include "input_output/FGPropertyManager.h"
#include "FGInertial.h"
using namespace std;
@ -202,7 +203,7 @@ bool FGAuxiliary::Run(bool Holding)
vcas = veas = 0.0;
vPilotAccel.InitMatrix();
vNcg = in.vBodyAccel/in.SLGravity;
vNcg = in.vBodyAccel/in.StandardGravity;
// Nz is Acceleration in "g's", along normal axis (-Z body axis)
Nz = -vNcg(eZ);
Ny = vNcg(eY);
@ -213,7 +214,7 @@ bool FGAuxiliary::Run(bool Holding)
vNwcg = mTb2w * vNcg;
vNwcg(eZ) = 1.0 - vNwcg(eZ);
vPilotAccelN = vPilotAccel / in.SLGravity;
vPilotAccelN = vPilotAccel / in.StandardGravity;
// VRP computation
vLocationVRP = in.vLocation.LocalToLocation( in.Tb2l * in.VRPBody );
@ -279,33 +280,25 @@ double FGAuxiliary::GetNlf(void) const
double FGAuxiliary::GetLongitudeRelativePosition(void) const
{
FGLocation source(FDMExec->GetIC()->GetLongitudeRadIC(),
FDMExec->GetIC()->GetLatitudeRadIC(),
in.vLocation.GetSeaLevelRadius());
return source.GetDistanceTo(in.vLocation.GetLongitude(),
FDMExec->GetIC()->GetLatitudeRadIC()) * fttom;
return in.vLocation.GetDistanceTo(FDMExec->GetIC()->GetLongitudeRadIC(),
in.vLocation.GetLatitude())* fttom;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::GetLatitudeRelativePosition(void) const
{
FGLocation source(FDMExec->GetIC()->GetLongitudeRadIC(),
FDMExec->GetIC()->GetLatitudeRadIC(),
in.vLocation.GetSeaLevelRadius());
return source.GetDistanceTo(FDMExec->GetIC()->GetLongitudeRadIC(),
in.vLocation.GetLatitude()) * fttom;
return in.vLocation.GetDistanceTo(in.vLocation.GetLongitude(),
FDMExec->GetIC()->GetLatitudeRadIC())* fttom;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGAuxiliary::GetDistanceRelativePosition(void) const
{
FGLocation source(FDMExec->GetIC()->GetLongitudeRadIC(),
FDMExec->GetIC()->GetLatitudeRadIC(),
in.vLocation.GetSeaLevelRadius());
return source.GetDistanceTo(in.vLocation.GetLongitude(),
in.vLocation.GetLatitude()) * fttom;
FGInitialCondition *ic = FDMExec->GetIC();
return in.vLocation.GetDistanceTo(ic->GetLongitudeRadIC(),
ic->GetLatitudeRadIC())* fttom;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -262,7 +262,7 @@ public:
double DistanceAGL;
double Wingspan;
double Wingchord;
double SLGravity;
double StandardGravity;
double Mass;
FGMatrix33 Tl2b;
FGMatrix33 Tb2l;

View file

@ -117,7 +117,7 @@ bool FGBuoyantForces::Load(Element *document)
Debug(2);
// Perform base class Pre-Load
if (!FGModel::Load(document, true))
if (!FGModel::Upload(document, true))
return false;
gas_cell_element = document->FindElement("gas_cell");
@ -168,7 +168,7 @@ const FGMatrix33& FGBuoyantForces::GetGasMassInertia(void)
if (size == 0) return gasCellJ;
gasCellJ = FGMatrix33();
gasCellJ.InitMatrix();
for (unsigned int i=0; i < size; i++) {
gasCellJ += Cells[i]->GetInertia();

View file

@ -103,9 +103,9 @@ public:
@param Executive a pointer to the parent executive object */
FGBuoyantForces(FGFDMExec* Executive);
/// Destructor
~FGBuoyantForces();
~FGBuoyantForces() override;
bool InitModel(void);
bool InitModel(void) override;
/** Runs the Buoyant forces model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -114,14 +114,14 @@ public:
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 Run(bool Holding) override;
/** Loads the Buoyant forces model.
The Load function for this class expects the XML parser to
have found the Buoyant_forces keyword in the configuration file.
@param element pointer to the current XML element for Buoyant forces parameters.
@return true if successful */
virtual bool Load(Element* element);
bool Load(Element* element) override;
/** Gets the total Buoyant force vector.
@return a force vector in lbs. */
@ -181,7 +181,7 @@ private:
void bind(void);
void Debug(int from);
void Debug(int from) override;
};
} // namespace JSBSim

View file

@ -52,6 +52,42 @@ class FGParameter;
class Element;
class FGPropertyManager;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGPropertyVector3
{
public:
FGPropertyVector3(void) {}
FGPropertyVector3(FGPropertyManager* pm, const std::string& baseName,
const std::string& xcmp, const std::string& ycmp,
const std::string& zcmp);
FGPropertyVector3& operator=(const FGColumnVector3& v) {
data[0] = v(1);
data[1] = v(2);
data[2] = v(3);
return *this;
}
operator FGColumnVector3() const {
return FGColumnVector3(data[0], data[1], data[2]);
}
FGColumnVector3 operator*(double a) const {
return FGColumnVector3(a * data[0], a * data[1], a * data[2]);
}
private:
SGPropObjDouble data[3];
};
inline FGColumnVector3 operator*(double a, const FGPropertyVector3& v) {
return v*a;
}
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -169,42 +205,6 @@ CLASS DOCUMENTATION
@endcode
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class FGPropertyVector3
{
public:
FGPropertyVector3(void) {}
FGPropertyVector3(FGPropertyManager* pm, const std::string& baseName,
const std::string& xcmp, const std::string& ycmp,
const std::string& zcmp);
FGPropertyVector3& operator=(const FGColumnVector3& v) {
data[0] = v(1);
data[1] = v(2);
data[2] = v(3);
return *this;
}
operator FGColumnVector3() const {
return FGColumnVector3(data[0], data[1], data[2]);
}
FGColumnVector3 operator*(double a) const {
return FGColumnVector3(a * data[0], a * data[1], a * data[2]);
}
private:
SGPropObjDouble data[3];
};
inline FGColumnVector3 operator*(double a, const FGPropertyVector3& v) {
return v*a;
}
class FGExternalForce : public FGForce
{
public:

View file

@ -58,7 +58,7 @@ FGExternalReactions::FGExternalReactions(FGFDMExec* fdmex) : FGModel(fdmex)
bool FGExternalReactions::Load(Element* el)
{
// Call the base class Load() function to load interface properties.
if (!FGModel::Load(el, true))
if (!FGModel::Upload(el, true))
return false;
Debug(2);

View file

@ -130,9 +130,9 @@ public:
Within the destructor the Forces and interface_properties vectors are
cleared out and the items pointed to are deleted.
*/
~FGExternalReactions(void);
~FGExternalReactions(void) override;
bool InitModel(void);
bool InitModel(void) override;
/** Sum all the constituent forces for this cycle.
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -141,7 +141,7 @@ public:
model, which may need to be active to listen on a socket for the
"Resume" command to be given.
@return true always. */
bool Run(bool Holding);
bool Run(bool Holding) override;
/** Loads the external forces from the XML configuration file.
If the external_reactions section is encountered in the vehicle configuration
@ -149,7 +149,7 @@ public:
a FGExternalForce object will be instantiated for each force definition.
@param el a pointer to the XML element holding the external reactions definition.
*/
virtual bool Load(Element* el);
bool Load(Element* el) override;
/** Retrieves the total forces defined in the external reactions.
@return the total force in pounds.
@ -171,7 +171,7 @@ private:
FGColumnVector3 vTotalMoments;
void bind(void);
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -482,7 +482,7 @@ bool FGFCS::Load(Element* document)
}
// Load interface properties from document
if (!FGModel::Load(document, true))
if (!FGModel::Upload(document, true))
return false;
Name += document->GetAttributeValue("name");

View file

@ -192,9 +192,9 @@ public:
@param Executive a pointer to the parent executive object */
FGFCS(FGFDMExec*);
/// Destructor
~FGFCS();
~FGFCS() override;
bool InitModel(void);
bool InitModel(void) override;
/** Runs the Flight Controls model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -203,7 +203,7 @@ public:
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 Run(bool Holding) override;
/// @name Pilot input command retrieval
//@{
@ -536,9 +536,9 @@ public:
Load() is called from FGFDMExec.
@param el pointer to the Element instance
@return true if succesful */
virtual bool Load(Element* el);
bool Load(Element* el) override;
SGPath FindFullPathName(const SGPath& path) const;
SGPath FindFullPathName(const SGPath& path) const override;
void AddThrottle(void);
double GetDt(void) const;
@ -573,7 +573,7 @@ private:
Channels SystemChannels;
void bind(void);
void bindThrottle(unsigned int);
void Debug(int from);
void Debug(int from) override;
};
}

View file

@ -67,9 +67,6 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
FGPropertyManager* PropertyManager = exec->GetPropertyManager();
MassBalance = exec->GetMassBalance();
gasCellJ = FGMatrix33();
gasCellM = FGColumnVector3();
Buoyancy = MaxVolume = MaxOverpressure = Temperature = Pressure =
Contents = Volume = dVolumeIdeal = 0.0;
Xradius = Yradius = Zradius = Xwidth = Ywidth = Zwidth = 0.0;
@ -361,13 +358,13 @@ void FGGasCell::Calculate(double dt)
// Note: This is gross buoyancy. The weight of the gas itself and
// any ballonets is not deducted here as the effects of the gas mass
// is handled by FGMassBalance.
vFn.InitMatrix(0.0, 0.0, - Buoyancy);
vFn = {0.0, 0.0, - Buoyancy};
// Compute the inertia of the gas cell.
// Consider the gas cell as a shape of uniform density.
// FIXME: If the cell isn't ellipsoid or cylindrical the inertia will
// be wrong.
gasCellJ = FGMatrix33();
gasCellJ.InitMatrix();
const double mass = Contents * M_gas();
double Ixx, Iyy, Izz;
if ((Xradius != 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&
@ -507,8 +504,6 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
FGPropertyManager* PropertyManager = exec->GetPropertyManager();
MassBalance = exec->GetMassBalance();
ballonetJ = FGMatrix33();
MaxVolume = MaxOverpressure = Temperature = Pressure =
Contents = Volume = dVolumeIdeal = dU = 0.0;
Xradius = Yradius = Zradius = Xwidth = Ywidth = Zwidth = 0.0;
@ -743,7 +738,7 @@ void FGBallonet::Calculate(double dt)
// Consider the ballonet as a shape of uniform density.
// FIXME: If the ballonet isn't ellipsoid or cylindrical the inertia will
// be wrong.
ballonetJ = FGMatrix33();
ballonetJ.InitMatrix();
const double mass = Contents * M_air;
double Ixx, Iyy, Izz;
if ((Xradius != 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&

View file

@ -152,7 +152,7 @@ bool FGGroundReactions::Load(Element* document)
Debug(2);
// Perform base class Pre-Load
if (!FGModel::Load(document, true))
if (!FGModel::Upload(document, true))
return false;
unsigned int numContacts = document->GetNumElements("contact");

View file

@ -7,21 +7,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -60,7 +60,7 @@ CLASS DOCUMENTATION
moments so that these may be provided to FGPropagate. Parses the
\<ground_reactions> section of the aircraft configuration file.
<h3>Configuration File Format of \<ground_reactions> Section:</h3>
@code
@code{.xml}
<ground_reactions>
<contact>
... {see FGLGear for specifics of this format}
@ -80,9 +80,9 @@ class FGGroundReactions : public FGModel, public FGSurface
{
public:
FGGroundReactions(FGFDMExec*);
~FGGroundReactions(void);
~FGGroundReactions(void) override;
bool InitModel(void);
bool InitModel(void) override;
/** Runs the Ground Reactions 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
@ -90,8 +90,8 @@ public:
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 bool Load(Element* el);
bool Run(bool Holding) override;
bool Load(Element* el) override;
const FGColumnVector3& GetForces(void) const {return vForces;}
double GetForces(int idx) const {return vForces(idx);}
const FGColumnVector3& GetMoments(void) const {return vMoments;}
@ -100,6 +100,8 @@ public:
std::string GetGroundReactionValues(std::string delimeter) const;
bool GetWOW(void) const;
/** Gets the number of gears.
@return the number of gears of the aircraft.*/
int GetNumGearUnits(void) const { return (int)lGear.size(); }
/** Gets a gear instance
@ -128,7 +130,7 @@ private:
double DsCmd;
void bind(void);
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -36,6 +36,7 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "FGInertial.h"
#include "input_output/FGXMLElement.h"
using namespace std;
@ -46,35 +47,30 @@ CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
FGInertial::FGInertial(FGFDMExec* fgex) : FGModel(fgex)
FGInertial::FGInertial(FGFDMExec* fgex)
: FGModel(fgex)
{
Name = "FGInertial";
Name = "Earth";
// Earth defaults
double RotationRate = 0.00007292115;
// RotationRate = 0.000072921151467;
GM = 14.0764417572E15; // WGS84 value
C2_0 = -4.84165371736E-04; // WGS84 value for the C2,0 coefficient
J2 = 1.08262982E-03; // WGS84 value for J2
a = 20925646.32546; // WGS84 semimajor axis length in feet
// a = 20902254.5305; // Effective Earth radius for a sphere
b = 20855486.5951; // WGS84 semiminor axis length in feet
RadiusReference = a;
gravType = gtWGS84;
// Lunar defaults
/*
double RotationRate = 0.0000026617;
GM = 1.7314079E14; // Lunar GM
RadiusReference = 5702559.05; // Equatorial radius
C2_0 = 0; // value for the C2,0 coefficient
J2 = 2.033542482111609E-4; // value for J2
a = 5702559.05; // semimajor axis length in feet
b = 5695439.63; // semiminor axis length in feet
*/
vOmegaPlanet = FGColumnVector3( 0.0, 0.0, RotationRate );
gAccelReference = GetGAccel(RadiusReference);
vOmegaPlanet = { 0.0, 0.0, RotationRate };
GroundCallback.reset(new FGDefaultGroundCallback(a, b));
bind();
@ -90,6 +86,40 @@ FGInertial::~FGInertial(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGInertial::Load(Element* el)
{
if (!Upload(el, true)) return false;
Name = el->GetAttributeValue("name");
if (el->FindElement("semimajor_axis"))
a = el->FindElementValueAsNumberConvertTo("semimajor_axis", "FT");
if (el->FindElement("semiminor_axis"))
b = el->FindElementValueAsNumberConvertTo("semiminor_axis", "FT");
if (el->FindElement("rotation_rate")) {
double RotationRate = el->FindElementValueAsNumberConvertTo("rotation_rate", "RAD/SEC");
vOmegaPlanet = {0., 0., RotationRate};
}
if (el->FindElement("GM"))
GM = el->FindElementValueAsNumberConvertTo("GM", "FT3/SEC2");
if (el->FindElement("J2"))
J2 = el->FindElementValueAsNumber("J2"); // Dimensionless
GroundCallback->SetEllipse(a, b);
// Messages to warn the user about possible inconsistencies.
if (a != b && J2 == 0.0)
cout << "Gravitational constant J2 is null for a non-spherical planet." << endl;
if (a == b && J2 != 0.0)
cout << "Gravitational constant J2 is non-zero for a spherical planet." << endl;
Debug(2);
return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGInertial::Run(bool Holding)
{
// Fast return if we have nothing to do ...
@ -114,6 +144,36 @@ bool FGInertial::Run(bool Holding)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGMatrix33 FGInertial::GetTl2ec(const FGLocation& location) const
{
FGColumnVector3 North, Down, East{-location(eY), location(eX), 0.};
switch (gravType) {
case gtStandard:
{
Down = location;
Down *= -1.0;
}
break;
case gtWGS84:
{
FGLocation sea_level = location;
sea_level.SetPositionGeodetic(location.GetLongitude(),
location.GetGeodLatitudeRad(), 0.0);
Down = GetGravityJ2(location);
Down -= vOmegaPlanet*(vOmegaPlanet*sea_level);}
}
Down.Normalize();
East.Normalize();
North = East*Down;
return FGMatrix33{North(eX), East(eX), Down(eX),
North(eY), East(eY), Down(eY),
North(eZ), 0.0, Down(eZ)};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGInertial::GetGAccel(double r) const
{
return GM/(r*r);
@ -132,7 +192,7 @@ FGColumnVector3 FGInertial::GetGravityJ2(const FGLocation& position) const
// Gravitation accel
double r = position.GetRadius();
double sinLat = position.GetSinLatitude();
double sinLat = sin(position.GetLatitude());
double adivr = a/r;
double preCommon = 1.5*J2*adivr*adivr;
@ -149,11 +209,45 @@ FGColumnVector3 FGInertial::GetGravityJ2(const FGLocation& position) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGInertial::SetAltitudeAGL(FGLocation& location, double altitudeAGL)
{
FGLocation contact;
FGColumnVector3 vDummy;
GroundCallback->GetAGLevel(location, contact, vDummy, vDummy, vDummy);
double groundHeight = contact.GetGeodAltitude();
double longitude = location.GetLongitude();
double geodLat = location.GetGeodLatitudeRad();
location.SetPositionGeodetic(longitude, geodLat,
groundHeight + altitudeAGL);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGInertial::SetGravityType(int gt)
{
// Messages to warn the user about possible inconsistencies.
switch (gt)
{
case eGravType::gtStandard:
if (a != b)
cout << "Warning: Standard gravity model has been set for a non-spherical planet" << endl;
break;
case eGravType::gtWGS84:
if (J2 == 0.0)
cout << "Warning: WGS84 gravity model has been set without specifying the J2 gravitational constant." << endl;
}
gravType = gt;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGInertial::bind(void)
{
PropertyManager->Tie("inertial/sea-level-radius_ft", this,
&FGInertial::GetRefRadius);
PropertyManager->Tie("simulation/gravity-model", &gravType);
PropertyManager->Tie("inertial/sea-level-radius_ft", &in.Position,
&FGLocation::GetSeaLevelRadius);
PropertyManager->Tie("simulation/gravity-model", this, &FGInertial::GetGravityType,
&FGInertial::SetGravityType);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -180,8 +274,14 @@ void FGInertial::Debug(int from)
if (debug_lvl <= 0) return;
if (debug_lvl & 1) { // Standard console startup message output
if (from == 0) { // Constructor
if (from == 0) {} // Constructor
if (from == 2) { // Loading
cout << endl << " Planet " << Name << endl;
cout << " Semi major axis: " << a << endl;
cout << " Semi minor axis: " << b << endl;
cout << " Rotation rate : " << scientific << vOmegaPlanet(eZ) << endl;
cout << " GM : " << GM << endl;
cout << " J2 : " << J2 << endl << defaultfloat;
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification

View file

@ -38,8 +38,11 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <memory>
#include "FGModel.h"
#include "math/FGLocation.h"
#include "input_output/FGGroundCallback.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -74,21 +77,78 @@ public:
on a socket for the "Resume" command to be given.
@return false if no error */
bool Run(bool Holding) override;
double SLgravity(void) const {return gAccelReference;}
static constexpr double GetStandardGravity(void) { return gAccelReference; }
const FGColumnVector3& GetGravity(void) const {return vGravAccel;}
const FGColumnVector3& GetOmegaPlanet() const {return vOmegaPlanet;}
void SetOmegaPlanet(double rate) {
vOmegaPlanet = FGColumnVector3(0.0, 0.0, rate);
}
double GetRefRadius(void) const {return RadiusReference;}
double GetSemimajor(void) const {return a;}
double GetSemiminor(void) const {return b;}
struct Inputs {
FGLocation Position;
} in;
/** @name Functions that rely on the ground callback
The following functions allow to set and get the vehicle position above
the ground. The ground level is obtained by interrogating an instance of
FGGroundCallback. A ground callback must therefore be set with
SetGroundCallback() before calling any of these functions. */
///@{
/** Get terrain contact point information below the current location.
@param location Location at which the contact point is evaluated.
@param contact Contact point location
@param normal Terrain normal vector in contact point (ECEF frame)
@param velocity Terrain linear velocity in contact point (ECEF frame)
@param ang_velocity Terrain angular velocity in contact point (ECEF frame)
@return Location altitude above contact point (AGL) in feet.
@see SetGroundCallback */
double GetContactPoint(const FGLocation& location, FGLocation& contact,
FGColumnVector3& normal, FGColumnVector3& velocity,
FGColumnVector3& ang_velocity) const
{
return GroundCallback->GetAGLevel(location, contact, normal, velocity,
ang_velocity); }
/** Get the altitude above ground level.
@return the altitude AGL in feet.
@param location Location at which the AGL is evaluated.
@see SetGroundCallback */
double GetAltitudeAGL(const FGLocation& location) const {
FGLocation lDummy;
FGColumnVector3 vDummy;
return GroundCallback->GetAGLevel(location, lDummy, vDummy, vDummy,
vDummy);
}
/** Set the altitude above ground level.
@param location Location at which the AGL is set.
@param altitudeAGL Altitude above Ground Level in feet.
@see SetGroundCallback */
void SetAltitudeAGL(FGLocation& location, double altitudeAGL);
/** Set the terrain elevation above sea level.
@param h Terrain elevation in ft.
@see SetGroundcallback */
void SetTerrainElevation(double h) {
GroundCallback->SetTerrainElevation(h);
}
/** Set the simulation time.
The elapsed time can be used by the ground callbck to assess the planet
rotation or the movement of objects.
@param time elapsed time in seconds since the simulation started.
*/
void SetTime(double time) {
GroundCallback->SetTime(time);
}
///@}
/** Sets the ground callback pointer.
FGInertial will take ownership of the pointer which must therefore be
located in the heap.
@param gc A pointer to a ground callback object
@see FGGroundCallback
*/
void SetGroundCallback(FGGroundCallback* gc) { GroundCallback.reset(gc); }
private:
/// These define the indices use to select the gravitation models.
enum eGravType {
/// Evaluate gravity using Newton's classical formula assuming the Earth is
@ -99,16 +159,61 @@ private:
gtWGS84
};
/// Get the gravity type.
int GetGravityType(void) const { return gravType; }
/// Set the gravity type.
void SetGravityType(int gt);
/** Transform matrix from the local horizontal frame to earth centered.
The local frame is the NED (North-East-Down) frame. Since the Down
direction depends on the gravity so is the local frame.
The East direction is tangent to the spheroid with a null component along
the Z axis.
The North direction is obtained from the cross product between East and
Down.
@param location The location at which the transform matrix must be
evaluated.
@return a rotation matrix of the transform from the earth centered frame
to the local horizontal frame.
*/
FGMatrix33 GetTl2ec(const FGLocation& location) const;
/** Transform matrix from the earth centered to local horizontal frame.
The local frame is the NED (North-East-Down) frame. Since the Down
direction depends on the gravity so is the local frame.
The East direction is tangent to the spheroid with a null component along
the Z axis.
The North direction is obtained from the cross product between East and
Down.
@param location The location at which the transform matrix must be
evaluated.
@return a rotation matrix of the transform from the earth centered frame
to the local horizontal frame.
*/
FGMatrix33 GetTec2l(const FGLocation& location) const
{ return GetTl2ec(location).Transposed(); }
struct Inputs {
FGLocation Position;
} in;
bool Load(Element* el) override;
private:
// Standard gravity (9.80665 m/s^2) in ft/s^2 which is the gravity at 45 deg.
// of latitude (see ISA 1976 and Steven & Lewis)
// It includes the centripetal acceleration.
static constexpr double gAccelReference = 9.80665 / fttom;
FGColumnVector3 vOmegaPlanet;
FGColumnVector3 vGravAccel;
double gAccelReference;
double RadiusReference;
double GM;
double C2_0; // WGS84 value for the C2,0 coefficient
double J2; // WGS84 value for J2
double a; // WGS84 semimajor axis length in feet
double b; // WGS84 semiminor axis length in feet
int gravType;
std::unique_ptr<FGGroundCallback> GroundCallback;
double GetGAccel(double r) const;
FGColumnVector3 GetGravityJ2(const FGLocation& position) const;

View file

@ -85,13 +85,13 @@ class FGInput : public FGModel
{
public:
FGInput(FGFDMExec*);
~FGInput();
~FGInput() override;
/** Load the input directives and adds a new input instance to the Input
Manager list.
@param el XMLElement that is pointing to the input directives
@result true if the execution succeeded. */
bool Load(Element* el);
bool Load(Element* el) override;
/** Initializes the instance. This method is called by FGFDMExec::RunIC().
This is were the initialization of all classes derived from FGInputType
@ -99,7 +99,7 @@ public:
to FGFDMExec::RunIC() so that the initialization process can be executed
properly.
@result true if the execution succeeded. */
bool InitModel(void);
bool InitModel(void) override;
/** Runs the Input model; called by the Executive
Can pass in a value indicating if the executive is directing the simulation to Hold.
@ -108,7 +108,7 @@ public:
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 Run(bool Holding) override;
/** Adds a new input instance to the Input Manager. The definition of the
new input instance is read from a file.
@ -147,7 +147,7 @@ private:
std::vector<FGInputType*> InputTypes;
bool enabled;
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -45,6 +45,7 @@ INCLUDES
#include "models/FGGroundReactions.h"
#include "math/FGTable.h"
#include "input_output/FGXMLElement.h"
#include "models/FGInertial.h"
using namespace std;
@ -288,7 +289,9 @@ const FGColumnVector3& FGLGear::GetBodyForces(FGSurface *surface)
// Compute the height of the theoretical location of the wheel (if strut is
// not compressed) with respect to the ground level
double height = gearLoc.GetContactPoint(contact, normal, terrainVel, dummy);
double height = fdmex->GetInertial()->GetContactPoint(gearLoc, contact,
normal, terrainVel,
dummy);
// Does this surface contact point interact with another surface?
if (surface) {
@ -603,13 +606,11 @@ void FGLGear::ComputeSideForceCoefficient(void)
void FGLGear::ComputeVerticalStrutForce()
{
double springForce = 0;
double dampForce = 0;
if (fStrutForce)
StrutForce = min(fStrutForce->GetValue(), (double)0.0);
else {
springForce = -compressLength * kSpring;
double springForce = -compressLength * kSpring;
double dampForce = 0;
if (compressSpeed >= 0.0) {

View file

@ -7,21 +7,21 @@
------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -145,8 +145,8 @@ CLASS DOCUMENTATION
in body frame.</li>
</ol>
<h3>Configuration File Format:</h3>
@code
<h3>Configuration File Format for \<contact> Section:</h3>
@code{.xml}
<contact type="{BOGEY | STRUCTURE}" name="{string}">
<location unit="{IN | M}">
<x> {number} </x>
@ -162,14 +162,14 @@ CLASS DOCUMENTATION
<dynamic_friction> {number} </dynamic_friction>
<rolling_friction> {number} </rolling_friction>
<spring_coeff unit="{LBS/FT | N/M}"> {number} </spring_coeff>
<damping_coeff [type="SQUARE"] unit="{LBS/FT/SEC | N/M/SEC}"> {number} </damping_coeff>
<damping_coeff_rebound [type="SQUARE"] unit="{LBS/FT/SEC | N/M/SEC}"> {number} </damping_coeff_rebound>
<damping_coeff type="{ | SQUARE}" unit="{LBS/FT/SEC | N/M/SEC}"> {number} </damping_coeff>
<damping_coeff_rebound type="{ | SQUARE}" unit="{LBS/FT/SEC | N/M/SEC}"> {number} </damping_coeff_rebound>
<max_steer unit="DEG"> {number | 0 | 360} </max_steer>
<brake_group> {NONE | LEFT | RIGHT | CENTER | NOSE | TAIL} </brake_group>
<retractable>{0 | 1}</retractable>
<table name="{CORNERING_COEFF}" type="internal">
<tableData>
...
{cornering parameters}
</tableData>
</table>
</contact>

View file

@ -133,7 +133,7 @@ bool FGMassBalance::Load(Element* document)
Name = "Mass Properties Model: " + document->GetAttributeValue("name");
// Perform base class Pre-Load
if (!FGModel::Load(document, true))
if (!FGModel::Upload(document, true))
return false;
SetAircraftBaseInertias(ReadInertiaMatrix(document));
@ -247,9 +247,9 @@ bool FGMassBalance::Run(bool Holding)
k5 = (Ixy*Ixz + Iyz*Ixx)*denom;
k6 = (Ixx*Iyy - Ixy*Ixy)*denom;
mJinv.InitMatrix( k1, k2, k3,
k2, k4, k5,
k3, k5, k6 );
mJinv = { k1, k2, k3,
k2, k4, k5,
k3, k5, k6 };
RunPostFunctions();

View file

@ -111,7 +111,7 @@ public:
explicit FGMassBalance(FGFDMExec*);
~FGMassBalance();
bool Load(Element* el);
bool Load(Element* el) override;
bool InitModel(void) override;
/** Runs the Mass Balance model; called by the Executive
Can pass in a value indicating if the executive is directing the

View file

@ -107,7 +107,7 @@ SGPath FGModel::FindFullPathName(const SGPath& path) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGModel::Load(Element* el, bool preLoad)
bool FGModel::Upload(Element* el, bool preLoad)
{
FGModelLoader ModelLoader(this);
Element* document = ModelLoader.Open(el);

View file

@ -95,18 +95,21 @@ public:
void SetPropertyManager(FGPropertyManager *fgpm) { PropertyManager=fgpm;}
virtual SGPath FindFullPathName(const SGPath& path) const;
const std::string& GetName(void) { return Name; }
virtual bool Load(Element* el) { return true; }
protected:
unsigned int exe_ctr;
unsigned int rate;
std::string Name;
/** Loads this model.
/** Uploads this model in memory.
Uploads the model in memory if its contents are contained in a separate
file.
@param el a pointer to the element
@param preLoad true if model functions and local properties must be
preloaded.
@return true if model is successfully loaded*/
virtual bool Load(Element* el, bool preLoad);
bool Upload(Element* el, bool preLoad);
virtual void Debug(int from);

View file

@ -236,7 +236,7 @@ bool FGOutput::Load(Element* document, const SGPath& dir)
includePath = dir;
// Perform base class Pre-Load
if (!FGModel::Load(document, false))
if (!FGModel::Upload(document, false))
return false;
size_t idx = OutputTypes.size();

View file

@ -195,7 +195,7 @@ public:
@param el XMLElement that is pointing to the output directives
@param dir optional directory path to load included files from
@result true if the execution succeeded. */
virtual bool Load(Element* el, const SGPath& dir = SGPath());
bool Load(Element* el, const SGPath& dir = SGPath());
/** Load the output directives and adds a new output instance to the Output
Manager list. Unlike the Load() method, the new output instance is not
generated from output directives read in a XML file but from a list of

View file

@ -69,6 +69,7 @@ INCLUDES
#include "initialization/FGInitialCondition.h"
#include "FGFDMExec.h"
#include "simgear/io/iostreams/sgstream.hxx"
#include "FGInertial.h"
using namespace std;
@ -84,6 +85,8 @@ FGPropagate::FGPropagate(FGFDMExec* fdmex)
Debug(0);
Name = "FGPropagate";
Inertial = FDMExec->GetInertial();
/// These define the indices use to select the various integrators.
// eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4};
@ -118,7 +121,7 @@ bool FGPropagate::InitModel(void)
// For initialization ONLY:
VState.vLocation.SetEllipse(in.SemiMajor, in.SemiMinor);
VState.vLocation.SetAltitudeAGL(4.0);
Inertial->SetAltitudeAGL(VState.vLocation, 4.0);
VState.dqPQRidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
VState.dqUVWidot.resize(5, FGColumnVector3(0.0,0.0,0.0));
@ -145,9 +148,9 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC)
VState.vLocation = FGIC->GetPosition();
epa = FGIC->GetEarthPositionAngleIC();
Ti2ec = FGMatrix33(cos(epa), sin(epa), 0.0,
-sin(epa), cos(epa), 0.0,
0.0, 0.0, 1.0);
Ti2ec = { cos(epa), sin(epa), 0.0,
-sin(epa), cos(epa), 0.0,
0.0, 0.0, 1.0 };
Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
VState.vInertialPosition = Tec2i * VState.vLocation;
@ -228,8 +231,9 @@ bool FGPropagate::Run(bool Holding)
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
// 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)
epa += in.vOmegaPlanet(eZ)*dt;
@ -237,23 +241,24 @@ bool FGPropagate::Run(bool Holding)
// 2. Update the Ti2ec and Tec2i transforms from the updated EPA
double cos_epa = cos(epa);
double sin_epa = sin(epa);
Ti2ec = FGMatrix33(cos_epa, sin_epa, 0.0,
-sin_epa, cos_epa, 0.0,
0.0, 0.0, 1.0);
Ti2ec = { cos_epa, sin_epa, 0.0,
-sin_epa, cos_epa, 0.0,
0.0, 0.0, 1.0 };
Tec2i = Ti2ec.Transposed(); // ECEF to ECI frame transform
// 3. Update the location from the updated Ti2ec and inertial position
VState.vLocation = Ti2ec*VState.vInertialPosition;
// 4. Update the other "Location-based" transformation matrices from the updated
// vLocation vector.
// 4. Update the other "Location-based" transformation matrices from the
// updated vLocation vector.
UpdateLocationMatrices();
// 5. Update the "Orientation-based" transformation matrices from the updated
// orientation quaternion and vLocation vector.
UpdateBodyMatrices();
// Translational position derivative (velocities are integrated in the inertial frame)
// Translational position derivative (velocities are integrated in the
// inertial frame)
CalculateUVW();
// Set auxilliary state variables
@ -266,7 +271,8 @@ bool FGPropagate::Run(bool Holding)
VState.qAttitudeLocal = Tl2b.GetQuaternion();
// Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal frame.
// Compute vehicle velocity wrt ECEF frame, expressed in Local horizontal
// frame.
vVel = Tb2l * VState.vUVW;
Debug(2);
@ -514,48 +520,76 @@ void FGPropagate::SetInertialRates(const FGColumnVector3& vRates) {
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetAltitudeASL() const
{
return VState.vLocation.GetRadius() - VState.vLocation.GetSeaLevelRadius();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetAltitudeASL(double altASL)
{
double slr = VState.vLocation.GetSeaLevelRadius();
VState.vLocation.SetRadius(slr + altASL);
UpdateVehicleState();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::RecomputeLocalTerrainVelocity()
{
FGLocation contact;
FGColumnVector3 normal;
VState.vLocation.GetContactPoint(contact, normal, LocalTerrainVelocity,
LocalTerrainAngularVelocity);
Inertial->GetContactPoint(VState.vLocation, contact, normal,
LocalTerrainVelocity, LocalTerrainAngularVelocity);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetTerrainElevation(void) const
{
FGLocation contact;
FGColumnVector3 vDummy;
Inertial->GetContactPoint(VState.vLocation, contact, vDummy, vDummy, vDummy);
return contact.GetGeodAltitude();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetTerrainElevation(double terrainElev)
{
double radius = terrainElev + VState.vLocation.GetSeaLevelRadius();
FDMExec->GetGroundCallback()->SetTerrainGeoCentRadius(radius);
Inertial->SetTerrainElevation(terrainElev);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetLocalTerrainRadius(void) const
{
return VState.vLocation.GetTerrainRadius();
FGLocation contact;
FGColumnVector3 vDummy;
Inertial->GetContactPoint(VState.vLocation, contact, vDummy, vDummy, vDummy);
return contact.GetRadius();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetDistanceAGL(void) const
{
return VState.vLocation.GetAltitudeAGL();
return Inertial->GetAltitudeAGL(VState.vLocation);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGPropagate::GetDistanceAGLKm(void) const
{
return VState.vLocation.GetAltitudeAGL()*0.0003048;
return GetDistanceAGL()*0.0003048;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropagate::SetDistanceAGL(double tt)
{
VState.vLocation.SetAltitudeAGL(tt);
Inertial->SetAltitudeAGL(VState.vLocation, tt);
UpdateVehicleState();
}
@ -563,8 +597,7 @@ void FGPropagate::SetDistanceAGL(double tt)
void FGPropagate::SetDistanceAGLKm(double tt)
{
VState.vLocation.SetAltitudeAGL(tt*3280.8399);
UpdateVehicleState();
SetDistanceAGL(tt*3280.8399);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -49,6 +49,7 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGInitialCondition;
class FGInertial;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
@ -143,7 +144,7 @@ public:
- integrator, rotational position = Trapezoidal
- integrator, translational position = Trapezoidal
@param Executive a pointer to the parent executive object */
FGPropagate(FGFDMExec* Executive);
explicit FGPropagate(FGFDMExec* Executive);
/// Destructor
~FGPropagate();
@ -182,7 +183,7 @@ public:
const FGColumnVector3& GetVel(void) const { return vVel; }
/** Retrieves the body frame vehicle velocity vector.
The vector returned is represented by an FGColumnVector reference. The vector
The vector returned is represented by an FGColumnVector3 reference. The vector
for the velocity in Body frame is organized (Vx, Vy, Vz). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vUVW(1) is Vx. Various convenience enumerators are defined
@ -196,7 +197,7 @@ public:
/** 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.
The vector returned is represented by an FGColumnVector reference. The vector
The vector returned is represented by an FGColumnVector3 reference. The vector
for the angular velocity in Body frame is organized (P, Q, R). The vector
is 1-based, so that the first element can be retrieved using the "()" operator.
In other words, vPQR(1) is P. Various convenience enumerators are defined
@ -324,7 +325,7 @@ public:
units ft
@return The current altitude above sea level in feet.
*/
double GetAltitudeASL(void) const { return VState.vLocation.GetAltitudeASL(); }
double GetAltitudeASL(void) const;
/** Returns the current altitude above sea level.
This function returns the altitude above sea level.
@ -436,7 +437,7 @@ public:
const FGColumnVector3& GetTerrainAngularVelocity(void) const { return LocalTerrainAngularVelocity; }
void RecomputeLocalTerrainVelocity();
double GetTerrainElevation(void) const { return GetLocalTerrainRadius() - VState.vLocation.GetSeaLevelRadius(); }
double GetTerrainElevation(void) const;
double GetDistanceAGL(void) const;
double GetDistanceAGLKm(void) const;
double GetRadius(void) const {
@ -571,11 +572,7 @@ public:
VState.vInertialPosition = Tec2i * VState.vLocation;
}
void SetAltitudeASL(double altASL)
{
VState.vLocation.SetAltitudeASL(altASL);
UpdateVehicleState();
}
void SetAltitudeASL(double altASL);
void SetAltitudeASLmeters(double altASL) { SetAltitudeASL(altASL/fttom); }
void SetTerrainElevation(double tt);
@ -623,6 +620,7 @@ private:
struct VehicleState VState;
FGInertial* Inertial = nullptr;
FGColumnVector3 vVel;
FGMatrix33 Tec2b;
FGMatrix33 Tb2ec;

View file

@ -365,7 +365,7 @@ bool FGPropulsion::Load(Element* el)
Name = "Propulsion Model: " + el->GetAttributeValue("name");
// Perform base class Pre-Load
if (!FGModel::Load(el, true))
if (!FGModel::Upload(el, true))
return false;
// Process tank definitions first to establish the number of fuel tanks
@ -581,7 +581,7 @@ const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
if (size == 0) return tankJ;
tankJ = FGMatrix33();
tankJ.InitMatrix();
for (unsigned int i=0; i<size; i++) {
tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),

View file

@ -102,7 +102,7 @@ public:
/// Constructor
FGPropulsion(FGFDMExec*);
/// Destructor
~FGPropulsion();
~FGPropulsion() override;
/** Executes the propulsion model.
The initial plan for the FGPropulsion class calls for Run() to be executed,
@ -113,15 +113,15 @@ public:
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 Run(bool Holding) override;
bool InitModel(void);
bool InitModel(void) override;
/** Loads the propulsion system (engine[s] and tank[s]).
Characteristics of the propulsion system are read in from the config file.
@param el pointer to an XML element that contains the engine information.
@return true if successfully loaded, otherwise false */
virtual bool Load(Element* el);
bool Load(Element* el) override;
/// Retrieves the number of engines defined for the aircraft.
unsigned int GetNumEngines(void) const {return (unsigned int)Engines.size();}
@ -173,7 +173,7 @@ public:
const FGColumnVector3& GetTanksMoment(void);
double GetTanksWeight(void) const;
SGPath FindFullPathName(const SGPath& path) const;
SGPath FindFullPathName(const SGPath& path) const override;
inline int GetActiveEngine(void) const {return ActiveEngine;}
inline bool GetFuelFreeze(void) const {return FuelFreeze;}
@ -221,7 +221,7 @@ private:
bool ReadingEngine;
void bind();
void Debug(int from);
void Debug(int from) override;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -101,7 +101,7 @@ public:
FGStandardAtmosphere(FGFDMExec*);
/// Destructor
virtual ~FGStandardAtmosphere();
~FGStandardAtmosphere() override;
bool InitModel(void) override;

View file

@ -4,27 +4,28 @@
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
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.
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.
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.
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
--------------------------------------------------------------------------------
@ -42,10 +43,9 @@ COMMENTS, REFERENCES, and NOTES
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <iostream>
#include <cstdlib>
#include "FGWinds.h"
#include "FGFDMExec.h"
#include "math/FGTable.h"
using namespace std;
@ -69,7 +69,7 @@ static inline double square_signed (double value)
*/
/// simply square a value
static inline double sqr(double x) { return x*x; }
constexpr double sqr(double x) { return x*x; }
FGWinds::FGWinds(FGFDMExec* fdmex) : FGModel(fdmex)
{
@ -438,7 +438,7 @@ void FGWinds::CosineGust()
if (profile.elapsedTime > (profile.startupDuration + profile.steadyDuration + profile.endDuration)) {
profile.Running = false;
profile.elapsedTime = 0.0;
oneMinusCosineGust.vWindTransformed.InitMatrix(0.0);
oneMinusCosineGust.vWindTransformed.InitMatrix();
vCosineGust.InitMatrix(0);
}
}
@ -521,7 +521,7 @@ void FGWinds::bind(void)
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);
PropertyManager->Tie("atmosphere/cosine-gust/start", this, static_cast<bool (FGWinds::*)(void) const>(nullptr), &FGWinds::StartGust);
// User-specified Up- Down-burst parameters
PropertyManager->Tie("atmosphere/updownburst/number-of-cells", this, (PMFt)0L, &FGWinds::NumberOfUpDownburstCells);

View file

@ -7,21 +7,21 @@
------------- 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.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
--------------------------------------------------------------------------------
@ -39,9 +39,7 @@ INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include "models/FGModel.h"
#include "math/FGColumnVector3.h"
#include "math/FGMatrix33.h"
#include "math/FGTable.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -49,12 +47,15 @@ FORWARD DECLARATIONS
namespace JSBSim {
class FGTable;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Models atmospheric disturbances: winds, gusts, turbulence, downbursts, etc.
<h2>Turbulence</h2>
Various turbulence models are available. They are specified
via the property <tt>atmosphere/turb-type</tt>. The following models are
available:
@ -64,22 +65,23 @@ CLASS DOCUMENTATION
- 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 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.
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>
@ -97,6 +99,57 @@ CLASS DOCUMENTATION
<td>6</td></tr>
</table>
<h2>Cosine Gust</h2>
A one minus cosine gust model is available. This permits a configurable,
predictable gust to be input to JSBSim for testing handling and
dynamics. Here is how a gust can be entered in a script:
~~~{.xml}
<event name="Introduce gust">
<condition> simulation/sim-time-sec ge 10 </condition>
<set name="atmosphere/cosine-gust/startup-duration-sec" value="5"/>
<set name="atmosphere/cosine-gust/steady-duration-sec" value="1"/>
<set name="atmosphere/cosine-gust/end-duration-sec" value="5"/>
<set name="atmosphere/cosine-gust/magnitude-ft_sec" value="30"/>
<set name="atmosphere/cosine-gust/frame" value="2"/>
<set name="atmosphere/cosine-gust/X-velocity-ft_sec" value="-1"/>
<set name="atmosphere/cosine-gust/Y-velocity-ft_sec" value="0"/>
<set name="atmosphere/cosine-gust/Z-velocity-ft_sec" value="0"/>
<set name="atmosphere/cosine-gust/start" value="1"/>
<notify/>
</event>
~~~
The x, y, z velocity components are meant to define the direction vector.
The vector will be normalized by the routine, so it does not need to be a
unit vector.
The startup duration is the time it takes to build up to full strength
(magnitude-ft_sec) from zero. Steady duration is the time the gust stays at
the specified magnitude. End duration is the time it takes to dwindle to
zero from the specified magnitude. The start and end transients are in a
smooth cosine shape.
The frame is specified from the following enum:
enum eGustFrame {gfNone=0, gfBody, gfWind, gfLocal};
That is, if you specify the X, Y, Z gust direction vector in the body frame,
frame will be "1". If the X, Y, and Z gust direction vector is in the Wind
frame, use frame = 2. If you specify the gust direction vector in the local
frame (N-E-D) use frame = 3. Note that an internal local frame direction
vector is created based on the X, Y, Z direction vector you specify and the
frame *at the time the gust is begun*. The direction vector is not updated
after the initial creation. This is to keep the gust at the same direction
independent of aircraft dynamics.
The gust is triggered when the property atmosphere/cosine-gust/start is set
to 1. It can be used repeatedly - the gust resets itself after it has
completed.
The cosine gust is global: it affects the whole world not just the vicinity
of the aircraft.
@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">
@ -114,18 +167,18 @@ class FGWinds : public FGModel {
public:
/// Constructor
FGWinds(FGFDMExec*);
explicit 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.
@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);
bool Run(bool Holding) override;
bool InitModel(void) override;
enum tType {ttNone, ttStandard, ttCulp, ttMilspec, ttTustin} turbType;
// TOTAL WIND access functions (wind + gust + turbulence)
@ -159,10 +212,11 @@ public:
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).
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);
@ -190,7 +244,8 @@ public:
/// Retrieves the gust components in NED frame.
virtual const FGColumnVector3& GetGustNED(void) const {return vGustNED;}
/** Turbulence models available: ttNone, ttStandard, ttBerndt, ttCulp, ttMilspec, ttTustin */
/** Turbulence models available: ttNone, ttStandard, ttBerndt, ttCulp,
ttMilspec, ttTustin */
virtual void SetTurbType(tType tt) {turbType = tt;}
virtual tType GetTurbType() const {return turbType;}
@ -348,7 +403,7 @@ private:
double DistanceFromRingCenter(double lat, double lon);
virtual void bind(void);
void Debug(int from);
void Debug(int from) override;
};
} // namespace JSBSim

View file

@ -51,7 +51,6 @@ CLASS IMPLEMENTATION
FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
{
Element *input_element,*init_element, *clip_el;
Input = Output = delay_time = 0.0;
delay = index = 0;
ClipMin = ClipMax = new FGRealValue(0.0);
@ -111,14 +110,14 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
Name = element->GetAttributeValue("name");
init_element = element->FindElement("init");
Element *init_element = element->FindElement("init");
while (init_element) {
InitNodes.push_back(new FGPropertyValue(init_element->GetDataLine(),
PropertyManager ));
init_element = element->FindNextElement("init");
}
input_element = element->FindElement("input");
Element *input_element = element->FindElement("input");
while (input_element) {
InputNodes.push_back(new FGPropertyValue(input_element->GetDataLine(),
PropertyManager ));
@ -137,9 +136,12 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
throw(string("Invalid output property name in flight control definition"));
}
OutputNodes.push_back(OutputNode);
// Initialize to a sensible value.
// If the node has just been created then it must be initialized to a
// sensible value since FGPropertyNode::GetNode() does not take care of
// that. If the node was already existing, its current value is kept
// unchanged.
if (!node_exists)
OutputNode->setDoubleValue(Output);
OutputNode->setDoubleValue(Output);
out_elem = element->FindNextElement("output");
}
@ -162,7 +164,7 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
for (unsigned int i=0; i<delay; i++) output_array[i] = 0.0;
}
clip_el = element->FindElement("clipto");
Element *clip_el = element->FindElement("clipto");
if (clip_el) {
Element* el = clip_el->FindElement("min");
if (!el) {
@ -220,10 +222,17 @@ void FGFCSComponent::SetOutput(void)
void FGFCSComponent::Delay(void)
{
output_array[index] = Output;
if ((unsigned int)index == delay-1) index = 0;
else index++;
Output = output_array[index];
if (fcs->GetTrimStatus()) {
// Update the whole history while trim routines are executing.
// Don't want to model delays while calculating a trim solution.
std::fill(output_array.begin(), output_array.end(), Output);
}
else {
output_array[index] = Output;
if ((unsigned int)index == delay-1) index = 0;
else index++;
Output = output_array[index];
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -236,10 +245,10 @@ void FGFCSComponent::Clip(void)
double range = vmax - vmin;
if (range < 0.0) {
cerr << "Trying to clip with a max value (" << vmax << ") from "
<< ClipMax->GetName() << " lower than the min value (" << vmin
<< ") from " << ClipMin->GetName() << "." << endl
<< "Clipping is ignored." << endl;
cerr << "Trying to clip with a max value " << ClipMax->GetName()
<< " lower than the min value " << ClipMin->GetName()
<< endl;
throw("JSBSim aborts");
return;
}
@ -276,6 +285,10 @@ void FGFCSComponent::bind(Element* el)
if (node) {
OutputNodes.push_back(node);
// If the node has just been created then it must be initialized to a
// sensible value since FGPropertyNode::GetNode() does not take care of
// that. If the node was already existing, its current value is kept
// unchanged.
if (!node_exists)
node->setDoubleValue(Output);
}

View file

@ -212,7 +212,7 @@ void FGFilter::Debug(int from)
if (from == 0) { // Constructor
cout << " INPUT: " << InputNodes[0]->GetName() << endl;
for (int i=1; i <= 7; i++) {
for (int i=1; i < 7; i++) {
if (!C[i]) break;
cout << " C[" << i << "]";

View file

@ -74,15 +74,20 @@ FGMagnetometer::FGMagnetometer(FGFCS* fcs, Element* element)
//would be better to get the date from the sim if its simulated...
time_t rawtime;
time( &rawtime );
tm * ptm = gmtime ( &rawtime );
struct tm ptm;
#ifdef _MSC_VER
gmtime_s(&ptm, &rawtime);
#else
gmtime_r(&rawtime, &ptm);
#endif
int year = ptm->tm_year;
int year = ptm.tm_year;
if(year>100)
{
year-= 100;
}
//the months here are zero based TODO find out if the function expects 1s based
date = (yymmdd_to_julian_days(ptm->tm_year,ptm->tm_mon,ptm->tm_mday));//Julian 1950-2049 yy,mm,dd
date = (yymmdd_to_julian_days(ptm.tm_year, ptm.tm_mon, ptm.tm_mday)); //Julian 1950-2049 yy,mm,dd
updateInertialMag();
Debug(0);
@ -97,8 +102,7 @@ FGMagnetometer::~FGMagnetometer()
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGMagnetometer::updateInertialMag(void)
{
counter++;
if (counter > INERTIAL_UPDATE_RATE)//dont need to update every iteration
if (counter++ % INERTIAL_UPDATE_RATE == 0)//dont need to update every iteration
{
counter = 0;

View file

@ -52,8 +52,8 @@ FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
I_out_total = 0.0;
Input_prev = Input_prev2 = 0.0;
Trigger = 0;
ProcessVariableDot = 0;
Trigger = nullptr;
ProcessVariableDot = nullptr;
IsStandard = false;
IntType = eNone; // No integrator initially defined.
@ -96,13 +96,20 @@ FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
el = element->FindElement("pvdot");
if (el)
ProcessVariableDot = PropertyManager->GetNode(el->GetDataLine());
ProcessVariableDot = new FGPropertyValue(el->GetDataLine(), PropertyManager);
el = element->FindElement("trigger");
if (el)
Trigger = PropertyManager->GetNode(el->GetDataLine());
Trigger = new FGPropertyValue(el->GetDataLine(), PropertyManager);
bind(element);
bind(el);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPID::bind(Element *el)
{
FGFCSComponent::bind(el);
string tmp;
if (Name.find("/") == string::npos) {
@ -111,7 +118,7 @@ FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
tmp = Name;
}
typedef double (FGPID::*PMF)(void) const;
PropertyManager->Tie(tmp+"/initial-integrator-value", this, (PMF)0,
PropertyManager->Tie(tmp+"/initial-integrator-value", this, (PMF)nullptr,
&FGPID::SetInitialOutput);
Debug(0);
@ -124,6 +131,8 @@ FGPID::~FGPID()
delete Kp;
delete Ki;
delete Kd;
delete Trigger;
delete ProcessVariableDot;
Debug(1);
}
@ -157,7 +166,7 @@ bool FGPID::Run(void )
// is negative.
double test = 0.0;
if (Trigger != 0) test = Trigger->getDoubleValue();
if (Trigger) test = Trigger->getDoubleValue();
if (fabs(test) < 0.000001) {
switch(IntType) {

View file

@ -148,10 +148,9 @@ private:
eIntegrateType IntType;
FGParameter *Kp, *Ki, *Kd;
FGPropertyNode_ptr Trigger;
FGPropertyNode_ptr ProcessVariableDot;
FGParameter *Kp, *Ki, *Kd, *Trigger, *ProcessVariableDot;
void bind(Element* el) override;
void Debug(int from) override;
};
}

View file

@ -40,6 +40,9 @@ INCLUDES
#include "FGWaypoint.h"
#include "input_output/FGXMLElement.h"
#include "math/FGLocation.h"
#include "models/FGFCS.h"
#include "models/FGInertial.h"
#include "initialization/FGInitialCondition.h"
using namespace std;
@ -61,9 +64,11 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element)
target_longitude_unit = 1.0;
source_latitude_unit = 1.0;
source_longitude_unit = 1.0;
source = fcs->GetExec()->GetIC()->GetPosition();
if (element->FindElement("target_latitude") ) {
target_latitude = PropertyManager->CreatePropertyObject<double>(element->FindElementValue("target_latitude"));
target_latitude.reset(new FGPropertyValue(element->FindElementValue("target_latitude"),
PropertyManager));
if (element->FindElement("target_latitude")->HasAttribute("unit")) {
if (element->FindElement("target_latitude")->GetAttributeValue("unit") == "DEG") {
target_latitude_unit = 0.017453293;
@ -77,7 +82,8 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element)
}
if (element->FindElement("target_longitude") ) {
target_longitude = PropertyManager->CreatePropertyObject<double>(element->FindElementValue("target_longitude"));
target_longitude.reset(new FGPropertyValue(element->FindElementValue("target_longitude"),
PropertyManager));
if (element->FindElement("target_longitude")->HasAttribute("unit")) {
if (element->FindElement("target_longitude")->GetAttributeValue("unit") == "DEG") {
target_longitude_unit = 0.017453293;
@ -91,7 +97,8 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element)
}
if (element->FindElement("source_latitude") ) {
source_latitude = PropertyManager->CreatePropertyObject<double>(element->FindElementValue("source_latitude"));
source_latitude.reset(new FGPropertyValue(element->FindElementValue("source_latitude"),
PropertyManager));
if (element->FindElement("source_latitude")->HasAttribute("unit")) {
if (element->FindElement("source_latitude")->GetAttributeValue("unit") == "DEG") {
source_latitude_unit = 0.017453293;
@ -105,7 +112,8 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element)
}
if (element->FindElement("source_longitude") ) {
source_longitude = PropertyManager->CreatePropertyObject<double>(element->FindElementValue("source_longitude"));
source_longitude.reset(new FGPropertyValue(element->FindElementValue("source_longitude"),
PropertyManager));
if (element->FindElement("source_longitude")->HasAttribute("unit")) {
if (element->FindElement("source_longitude")->GetAttributeValue("unit") == "DEG") {
source_longitude_unit = 0.017453293;
@ -121,9 +129,7 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element)
if (element->FindElement("radius"))
radius = element->FindElementValueAsNumberConvertTo("radius", "FT");
else {
FGLocation source(source_longitude * source_latitude_unit,
source_latitude * source_longitude_unit, 1.0);
radius = source.GetSeaLevelRadius(); // Radius of Earth in feet.
radius = 20925646.32546; // Radius of Earth in feet.
}
unit = element->GetAttributeValue("unit");
@ -170,10 +176,11 @@ FGWaypoint::~FGWaypoint()
bool FGWaypoint::Run(void )
{
double target_latitude_rad = target_latitude * target_latitude_unit;
double target_longitude_rad = target_longitude * target_longitude_unit;
FGLocation source(source_longitude * source_latitude_unit,
source_latitude * source_longitude_unit, radius);
double source_latitude_rad = source_latitude->GetValue() * source_latitude_unit;
double source_longitude_rad = source_longitude->GetValue() * source_longitude_unit;
double target_latitude_rad = target_latitude->GetValue() * target_latitude_unit;
double target_longitude_rad = target_longitude->GetValue() * target_longitude_unit;
source.SetPosition(source_longitude_rad, source_latitude_rad, radius);
if (WaypointType == eHeading) { // Calculate Heading
double heading_to_waypoint_rad = source.GetHeadingTo(target_longitude_rad,

View file

@ -37,7 +37,10 @@ SENTRY
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <memory>
#include "FGFCSComponent.h"
#include "math/FGLocation.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FORWARD DECLARATIONS
@ -100,10 +103,11 @@ public:
bool Run(void) override;
private:
simgear::PropertyObject<double> target_latitude;
simgear::PropertyObject<double> target_longitude;
simgear::PropertyObject<double> source_latitude;
simgear::PropertyObject<double> source_longitude;
FGLocation source;
std::unique_ptr<FGPropertyValue> target_latitude;
std::unique_ptr<FGPropertyValue> target_longitude;
std::unique_ptr<FGPropertyValue> source_latitude;
std::unique_ptr<FGPropertyValue> source_longitude;
double target_latitude_unit;
double target_longitude_unit;
double source_latitude_unit;

View file

@ -142,7 +142,7 @@ public:
};
FGEngine(int engine_number, struct Inputs& input);
virtual ~FGEngine();
~FGEngine() override;
enum EngineType {etUnknown, etRocket, etPiston, etTurbine, etTurboprop, etElectric};
@ -241,7 +241,7 @@ protected:
std::vector <int> SourceTanks;
virtual bool Load(FGFDMExec *exec, Element *el);
bool Load(FGFDMExec *exec, Element *el);
void Debug(int from);
};
}

View file

@ -4,24 +4,24 @@
Author: Tony Peden
Date started: 6/10/00
------------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) -------------
--------- Copyright (C) 1999 Anthony K. Peden (apeden@earthlink.net) --------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
Further information about the GNU Lesser General Public License can also be
found on the world wide web at http://www.gnu.org.
HISTORY
@ -33,19 +33,14 @@ FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------
The purpose of this class is to provide storage for computed forces and
encapsulate all the functionality associated with transforming those
forces from their native coord system to the body system. This includes
computing the moments due to the difference between the point of application
and the cg.
encapsulate all the functionality associated with transforming those forces from
their native coord system to the body system. This includes computing the
moments due to the difference between the point of application and the cg.
*/
#include <iostream>
#include <cstdlib>
#include "FGForce.h"
#include "FGFDMExec.h"
#include "models/FGPropagate.h"
#include "models/FGMassBalance.h"
#include "models/FGAuxiliary.h"
@ -68,9 +63,9 @@ FGForce::FGForce(FGFDMExec *FDMExec) :
vFb.InitMatrix();
vM.InitMatrix();
mT.InitMatrix(1., 0., 0.,
0., 1., 0.,
0., 0., 1.);
mT = { 1., 0., 0.,
0., 1., 0.,
0., 0., 1. };
Debug(0);
}

View file

@ -8,21 +8,21 @@
------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
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.
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.
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
--------------------------------------------------------------------------------
@ -199,9 +199,9 @@ FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num)
// shaft representation - a rather simple transform,
// but using a matrix is safer.
TboToHsr.InitMatrix( 0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0 );
TboToHsr = { 0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
-1.0, 0.0, 0.0 };
HsrToTbo = TboToHsr.Transposed();
// smooth out jumps in hagl reported, otherwise the ground effect