1
0
Fork 0

Sync'ed with JSBSim v1.1.11

- Output files are now written in the current directory instead of being written in the aircraft folder (issue GH#337)
- A new exception TrimFailureException is now thrown when trim fails. This eases the detection of the trim failure (previously the exception message needed to be checked).
- An exception is thrown when a latitude higher than 90 degrees is supplied to a <waypoint> control element (PR GH#536)
- Fix the sign of the initial NED climb rate (property ic/gamma-deg) (PR #545)
- JSBSim now checks malformed data in <table> elements. Anything different than numbers and spaces/tabs will be rejected.
- Usage of <location> and <orientation> in engines is now officially dropped (PR #559, #561 and #563). These elements were deprecated long ago in favor of the corresponding elements <location> and <orientation> in thrusters. Therefore the code removed is no-op.
- The computation of the initial rotation rates has been fixed (Issue GH#553). Previously, the rotation rates could be initialized with extremely high values when the vehicle was spawned over the Poles. And for a given set of initial conditions, the initial rotation rates could have different values depending on the initial latitude at which the vehicle was initialized. This now fixed.
- The precision with which values are transmitted thru a socket can now be set via the attribute precision such as <output precision="8"> (PR GH#579)
- Added 2 new methods to FGFDMExec: SetOutputPath and GetOutputPath to specify the path to which the output files will be written.
- All JSBSim exceptions now inherit from JSBSim::BaseException. There still exist std::* exceptions thrown by JSBSim. Cleanup is still in progress.
- JSBSim no longer calls exit() or abort(). Exceptions are thrown instead. This gives the calling application an opportunity to gracefully recover.
This commit is contained in:
Bertrand Coconnier 2022-04-23 19:18:42 +02:00
parent 06063ed82a
commit 60ce826874
34 changed files with 424 additions and 364 deletions

View file

@ -144,9 +144,14 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr)
// this is to catch errors in binding member functions to the property tree.
try {
Allocate();
} catch (const string& msg ) {
cout << "Caught error: " << msg << endl;
exit(1);
}
catch (const string& msg) {
cerr << endl << "Caught error: " << msg << endl;
throw;
}
catch (const BaseException& e) {
cout << endl << "Caught error: " << e.what() << endl;
throw;
}
trim_status = false;
@ -1093,8 +1098,10 @@ bool FGFDMExec::ReadChild(Element* el)
if (location) {
child->Loc = location->FindElementTripletConvertTo("IN");
} else {
cerr << endl << highint << fgred << " No location was found for this child object!" << reset << endl;
exit(-1);
const string s(" No location was found for this child object!");
cerr << el->ReadFrom() << endl << highint << fgred
<< s << reset << endl;
throw BaseException(s);
}
Element* orientation = el->FindElement("orient");
@ -1165,7 +1172,7 @@ void FGFDMExec::DoTrim(int mode)
trim.Report();
if (!success)
throw("Trim Failed");
throw TrimFailureException("Trim Failed");
trim_completed = 1;
}

View file

@ -72,6 +72,11 @@ class FGPropulsion;
class FGMassBalance;
class FGTrim;
class TrimFailureException : public BaseException {
public:
TrimFailureException(const std::string& msg) : BaseException(msg) {}
};
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -194,7 +199,7 @@ class FGFDMExec : public FGJSBBase
mated = true;
internal = false;
}
void Run(void) {exec->Run();}
void AssignState(FGPropagate* source_prop) {
exec->GetPropagate()->SetVState(source_prop->GetVState());
@ -283,44 +288,65 @@ public:
@return true if successful*/
bool LoadModel(const std::string& model, bool addModelToPath = true);
/** Loads a script
/** Load a script
@param Script The full path name and file name for the script to be loaded.
@param deltaT The simulation integration step size, if given. If no value is supplied
then 0.0 is used and the value is expected to be supplied in
the script file itself.
@param initfile The initialization file that will override the initialization file
specified in the script file. If no file name is given on the command line,
the file specified in the script will be used. If an initialization file
is not given in either place, an error will result.
@param deltaT The simulation integration step size, if given. If no value
is supplied then 0.0 is used and the value is expected to be
supplied in the script file itself.
@param initfile The initialization file that will override the
initialization file specified in the script file. If no
file name is given on the command line, the file specified
in the script will be used. If an initialization file is
not given in either place, an error will result.
@return true if successfully loads; false otherwise. */
bool LoadScript(const SGPath& Script, double deltaT=0.0,
const SGPath& initfile=SGPath());
/** Sets the path to the engine config file directories.
@param path path to the directory under which engine config
files are kept, for instance "engine" */
/** Set the path to the engine config file directories.
Relative paths are taken from the root directory.
@param path path to the directory under which engine config files are
kept, for instance "engine".
@see SetRootDir
@see GetEnginePath */
bool SetEnginePath(const SGPath& path) {
EnginePath = GetFullPath(path);
return true;
}
/** Sets the path to the aircraft config file directories.
@param path path to the aircraft directory. For instance:
"aircraft". Under aircraft, then, would be directories for various
modeled aircraft such as C172/, x15/, etc. */
/** Set the path to the aircraft config file directories.
Under this path, then, would be directories for various modeled aircraft
such as C172/, x15/, etc.
Relative paths are taken from the root directory.
@param path path to the aircraft directory, for instance "aircraft".
@see SetRootDir
@see GetAircraftPath */
bool SetAircraftPath(const SGPath& path) {
AircraftPath = GetFullPath(path);
return true;
}
/** Sets the path to the systems config file directories.
@param path path to the directory under which systems config
files are kept, for instance "systems" */
/** Set the path to the systems config file directories.
Relative paths are taken from the root directory.
@param path path to the directory under which systems config files are
kept, for instance "systems"
@see SetRootDir
@see GetSystemsPath */
bool SetSystemsPath(const SGPath& path) {
SystemsPath = GetFullPath(path);
return true;
}
/** Set the directory where the output files will be written.
Relative paths are taken from the root directory.
@param path path to the directory under which the output files will be
written.
@see SetRootDir
@see GetOutputPath */
bool SetOutputPath(const SGPath& path) {
OutputPath = GetFullPath(path);
return true;
}
/// @name Top-level executive State and Model retrieval mechanism
///@{
/// Returns the FGAtmosphere pointer.
@ -364,13 +390,15 @@ public:
///@}
/// Retrieves the engine path.
const SGPath& GetEnginePath(void) {return EnginePath;}
const SGPath& GetEnginePath(void) { return EnginePath; }
/// Retrieves the aircraft path.
const SGPath& GetAircraftPath(void) {return AircraftPath;}
const SGPath& GetAircraftPath(void) { return AircraftPath; }
/// Retrieves the systems path.
const SGPath& GetSystemsPath(void) {return SystemsPath;}
const SGPath& GetSystemsPath(void) { return SystemsPath; }
/// Retrieves the full aircraft path name.
const SGPath& GetFullAircraftPath(void) {return FullAircraftPath;}
const SGPath& GetFullAircraftPath(void) { return FullAircraftPath; }
/// Retrieves the path to the output files.
const SGPath& GetOutputPath(void) { return OutputPath; }
/** Retrieves the value of a property.
@param property the name of the property
@ -535,13 +563,23 @@ public:
@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.
@param rootDir the string containing the root directory. */
/** Set the root directory that is used to obtain absolute paths from
relative paths.
Aircraft, engine, systems and output paths are not updated by this
method. You must call each methods (SetAircraftPath(), SetEnginePath(),
etc.) individually if you need to update these paths as well.
@param rootDir the path to the root directory.
@see GetRootDir
@see SetAircraftPath
@see SetEnginePath
@see SetSystemsPath
@see SetOutputPath
*/
void SetRootDir(const SGPath& rootDir) {RootDir = rootDir;}
/** Retrieves the Root Directory.
@return the string representing the root (base) JSBSim directory. */
/** Retrieve the Root Directory.
@return the path to the root (base) JSBSim directory.
@see SetRootDir */
const SGPath& GetRootDir(void) const {return RootDir;}
/** Increments the simulation time if not in Holding mode. The Frame counter
@ -600,6 +638,7 @@ private:
SGPath FullAircraftPath;
SGPath EnginePath;
SGPath SystemsPath;
SGPath OutputPath;
std::string CFGVersion;
std::string Release;
SGPath RootDir;

View file

@ -42,6 +42,7 @@ INCLUDES
#include <queue>
#include <string>
#include <cmath>
#include <stdexcept>
#include "input_output/string_utilities.h"
@ -55,6 +56,11 @@ FORWARD DECLARATIONS
namespace JSBSim {
class BaseException : public std::runtime_error {
public:
BaseException(const std::string& msg) : std::runtime_error(msg) {}
};
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -263,7 +269,7 @@ public:
static double PitotTotalPressure(double mach, double p);
/** Compute the Mach number from the differential pressure (qc) and the
* static pressure. Based on the formulas in the US Air Force Aircraft
* static pressure. Based on the formulas in the US Air Force Aircraft
* Performance Flight Testing Manual (AFFTC-TIH-99-01).
* @param qc The differential/impact pressure
* @param p Pressure in psf
@ -271,7 +277,7 @@ public:
static double MachFromImpactPressure(double qc, double p);
/** Calculate the calibrated airspeed from the Mach number. Based on the
* formulas in the US Air Force Aircraft Performance Flight Testing
* formulas in the US Air Force Aircraft Performance Flight Testing
* Manual (AFFTC-TIH-99-01).
* @param mach The Mach number
* @param p Pressure in psf
@ -280,7 +286,7 @@ public:
static double VcalibratedFromMach(double mach, double p);
/** Calculate the Mach number from the calibrated airspeed.Based on the
* formulas in the US Air Force Aircraft Performance Flight Testing
* formulas in the US Air Force Aircraft Performance Flight Testing
* Manual (AFFTC-TIH-99-01).
* @param vcas The calibrated airspeed (CAS) in ft/s
* @param p Pressure in psf
@ -321,13 +327,13 @@ public:
static bool EqualToRoundoff(double a, float b) {
return EqualToRoundoff((float)a, b);
}
/** Constrain a value between a minimum and a maximum value.
*/
static constexpr double Constrain(double min, double value, double max) {
return value<min?(min):(value>max?(max):(value));
}
static constexpr double sign(double num) {return num>=0.0?1.0:-1.0;}
static double GaussianRandomNumber(void);

View file

@ -808,7 +808,7 @@ void FGInitialCondition::SetAltitudeASLFtIC(double alt)
}
geodAlt = z/sinGeodLat-N*(1-e2);
}
double longitude = position.GetLongitude();
position.SetPositionGeodetic(longitude, geodLatitude, geodAlt);
}
@ -1013,13 +1013,17 @@ bool FGInitialCondition::Load(const SGPath& rstfile, bool useStoredPath)
// Make sure that the document is valid
if (!document) {
cerr << "File: " << init_file_name << " could not be read." << endl;
exit(-1);
stringstream s;
s << "File: " << init_file_name << " could not be read.";
cerr << s.str() << endl;
throw BaseException(s.str());
}
if (document->GetName() != string("initialize")) {
cerr << "File: " << init_file_name << " is not a reset file." << endl;
exit(-1);
stringstream s;
s << "File: " << init_file_name << " is not a reset file.";
cerr << s.str() << endl;
throw BaseException(s.str());
}
double version = HUGE_VAL;
@ -1031,8 +1035,9 @@ bool FGInitialCondition::Load(const SGPath& rstfile, bool useStoredPath)
if (version == HUGE_VAL) {
result = Load_v1(document); // Default to the old version
} else if (version >= 3.0) {
cerr << "Only initialization file formats 1 and 2 are currently supported" << endl;
exit (-1);
const string s("Only initialization file formats 1 and 2 are currently supported");
cerr << document->ReadFrom() << endl << s << endl;
throw BaseException(s);
} else if (version >= 2.0) {
result = Load_v2(document);
} else if (version >= 1.0) {
@ -1182,15 +1187,7 @@ bool FGInitialCondition::Load_v1(Element* document)
if (document->FindElement("trim"))
SetTrimRequest(document->FindElementValue("trim"));
// Refer to Stevens and Lewis, 1.5-14a, pg. 49.
// 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 = {radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*tan(position.GetLatitude())};
vPQR_body = Tl2b * vOmegaLocal;
vPQR_body.InitMatrix();
return result;
}
@ -1403,19 +1400,12 @@ bool FGInitialCondition::Load_v2(Element* document)
// - Body
Element* attrate_el = document->FindElement("attitude_rate");
const FGMatrix33& Tl2b = orientation.GetT();
// 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 = { radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*tan(position.GetLatitude())};
if (attrate_el) {
string frame = attrate_el->GetAttributeValue("frame");
frame = to_lower(frame);
const FGMatrix33& Tl2b = orientation.GetT();
FGColumnVector3 vAttRate = attrate_el->FindElementTripletConvertTo("RAD/SEC");
if (frame == "eci") {
@ -1424,6 +1414,12 @@ bool FGInitialCondition::Load_v2(Element* document)
} else if (frame == "ecef") {
vPQR_body = Tl2b * position.GetTec2l() * vAttRate;
} else if (frame == "local") {
// 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 = {radInv*vUVW_NED(eEast),
-radInv*vUVW_NED(eNorth),
-radInv*vUVW_NED(eEast)*tan(position.GetLatitude())};
vPQR_body = Tl2b * (vAttRate + vOmegaLocal);
} else if (frame == "body") {
vPQR_body = vAttRate;
@ -1434,11 +1430,11 @@ bool FGInitialCondition::Load_v2(Element* document)
result = false;
} else if (frame.empty()) {
vPQR_body = Tl2b * vOmegaLocal;
vPQR_body.InitMatrix();
}
} else { // Body frame attitude rate assumed 0 relative to local.
vPQR_body = Tl2b * vOmegaLocal;
vPQR_body.InitMatrix();
}
return result;

View file

@ -519,7 +519,7 @@ public:
{
const FGMatrix33& Tb2l = orientation.GetTInv();
FGColumnVector3 _vt_NED = Tb2l * Tw2b * FGColumnVector3(vt, 0., 0.);
return _vt_NED(eW);
return -_vt_NED(eW);
}
/** Gets the initial body velocity

View file

@ -214,7 +214,7 @@ void FGOutputFG::SocketDataFill(FGNetFDM* net)
net->stall_warning = 0.0; // 0.0 - 1.0 indicating the amount of stall
net->slip_deg = (float)(Auxiliary->Getbeta(inDegrees)); // slip ball deflection, deg
net->num_engines = min((unsigned) FGNetFDM::FG_MAX_ENGINES, Propulsion->GetNumEngines()); // Number of valid engines
net->num_engines = min(FGNetFDM::FG_MAX_ENGINES,Propulsion->GetNumEngines()); // Number of valid engines
for (i=0; i<net->num_engines; i++) {
FGEngine* engine = Propulsion->GetEngine(i);
@ -254,7 +254,7 @@ void FGOutputFG::SocketDataFill(FGNetFDM* net)
}
}
net->num_tanks = min((unsigned) FGNetFDM::FG_MAX_TANKS, Propulsion->GetNumTanks()); // Max number of fuel tanks
net->num_tanks = min(FGNetFDM::FG_MAX_TANKS, Propulsion->GetNumTanks()); // Max number of fuel tanks
for (i=0; i<net->num_tanks; i++) {
net->fuel_quantity[i] = (float)(((FGTank *)Propulsion->GetTank(i))->GetContents());

View file

@ -100,7 +100,7 @@ public:
the next call to SetStartNewOutput().
@param name new name */
void SetOutputName(const std::string& fname) override {
Name = (FDMExec->GetRootDir()/fname).utf8Str();
Name = (FDMExec->GetOutputPath()/fname).utf8Str();
runID_postfix = -1;
Filename = SGPath();
}

View file

@ -121,6 +121,12 @@ bool FGOutputSocket::Load(Element* el)
el->GetAttributeValue("protocol") + "/" +
el->GetAttributeValue("port"));
// Check if output precision for doubles has been specified, default to 7 if not
if(el->HasAttribute("precision"))
precision = (int)el->GetAttributeValueAsNumber("precision");
else
precision = 7;
return true;
}
@ -130,7 +136,7 @@ bool FGOutputSocket::InitModel(void)
{
if (FGOutputType::InitModel()) {
delete socket;
socket = new FGfdmSocket(SockName, SockPort, SockProtocol);
socket = new FGfdmSocket(SockName, SockPort, SockProtocol, precision);
if (socket == 0) return false;
if (!socket->GetConnectStatus()) return false;

View file

@ -108,6 +108,7 @@ protected:
unsigned int SockPort;
FGfdmSocket::ProtocolType SockProtocol;
FGfdmSocket* socket;
int precision;
};
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -28,6 +28,7 @@
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
#include <assert.h>
#include "FGPropertyManager.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -110,13 +111,6 @@ bool FGPropertyNode::HasNode (const string &path)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPropertyNode::GetName( void ) const
{
return getNameString();
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGPropertyNode::GetPrintableName( void ) const
{
string temp_string(getNameString());
@ -301,7 +295,7 @@ void FGPropertyNode::SetWritable (const string &name, bool state )
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropertyManager::Untie (const string &name)
void FGPropertyManager::Untie(const string &name)
{
SGPropertyNode* property = root->getNode(name.c_str());
if (!property) {
@ -309,8 +303,18 @@ void FGPropertyManager::Untie (const string &name)
return;
}
vector <SGPropertyNode_ptr>::iterator it;
for (it = tied_properties.begin(); it != tied_properties.end(); ++it) {
Untie(property);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGPropertyManager::Untie(SGPropertyNode *property)
{
const string& name = property->getNameString();
assert(property->isTied());
for (auto it = tied_properties.begin(); it != tied_properties.end(); ++it) {
if (*it == property) {
property->untie();
tied_properties.erase(it);

View file

@ -97,7 +97,7 @@ class FGPropertyNode : public SGPropertyNode
/**
* Get the name of a node
*/
std::string GetName( void ) const;
const std::string& GetName( void ) const { return getNameString(); }
/**
* Get the name of a node without underscores, etc.
@ -413,9 +413,21 @@ class FGPropertyManager
*
* Classes should use this function to release control of any
* properties they are managing.
*
* @param name The property name to untie (full path).
*/
void Untie (const std::string &name);
/**
* Untie a property from an external data source.
*
* Classes should use this function to release control of any
* properties they are managing.
*
* @param property A pointer to the property to untie.
*/
void Untie (SGPropertyNode* property);
/**
* Unbind all properties bound by this manager to an external data source.
*

View file

@ -150,7 +150,7 @@ bool FGScript::LoadScript(const SGPath& script, double default_dT,
// Make sure that the desired time is reached and executed.
EndTime += 0.99*FDMExec->GetDeltaT();
// read aircraft and initialization files
element = document->FindElement("use");
@ -193,7 +193,7 @@ bool FGScript::LoadScript(const SGPath& script, double default_dT,
while (element) {
if (!FDMExec->GetInput()->Load(element))
return false;
element = document->FindNextElement("input");
}
@ -206,7 +206,7 @@ bool FGScript::LoadScript(const SGPath& script, double default_dT,
while (element) {
if (!FDMExec->GetOutput()->Load(element, scriptDir))
return false;
element = document->FindNextElement("output");
}
@ -295,7 +295,7 @@ bool FGScript::LoadScript(const SGPath& script, double default_dT,
}
else
newEvent->NotifyProperties.push_back(new FGPropertyValue(notifyPropertyName, PropertyManager));
string caption_attribute = notify_property_element->GetAttributeValue("caption");
if (caption_attribute.empty()) {
newEvent->DisplayString.push_back(notifyPropertyName);
@ -496,7 +496,7 @@ bool FGScript::RunScript(void)
} else {
cout << endl << underon
<< highint << thisEvent.Name << normint << underoff
<< " (Event " << event_ctr << ")"
<< " (Event " << event_ctr << ")"
<< " executed at time: " << highint << currentTime << normint
<< endl;
}
@ -573,7 +573,7 @@ void FGScript::Debug(int from)
<< " = " << node->getDoubleValue()
<< endl;
}
if (LocalProperties.empty()) cout << endl;
for (unsigned i=0; i<Events.size(); i++) {
@ -597,38 +597,38 @@ void FGScript::Debug(int from)
for (unsigned j=0; j<Events[i].SetValue.size(); j++) {
if (Events[i].SetValue[j] == 0.0 && Events[i].Functions[j] != 0L) {
if (Events[i].SetParam[j] == 0) {
if (Events[i].SetParamName[j].size() == 0) {
cerr << fgred << highint << endl
<< " An attempt has been made to access a non-existent property" << endl
<< " in this event. Please check the property names used, spelling, etc."
<< reset << endl;
exit(-1);
if (Events[i].SetParamName[j].empty()) {
stringstream s;
s << " An attempt has been made to access a non-existent property" << endl
<< " in this event. Please check the property names used, spelling, etc.";
cerr << fgred << highint << endl << s.str() << reset << endl;
throw BaseException(s.str());
} else {
cout << endl << " set " << Events[i].SetParamName[j]
<< " to function value (Late Bound)";
}
}
} else {
cout << endl << " set "
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
<< " to function value";
cout << endl << " set "
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
<< " to function value";
}
} else {
if (Events[i].SetParam[j] == 0) {
if (Events[i].SetParamName[j].size() == 0) {
cerr << fgred << highint << endl
<< " An attempt has been made to access a non-existent property" << endl
<< " in this event. Please check the property names used, spelling, etc."
<< reset << endl;
exit(-1);
if (Events[i].SetParamName[j].empty()) {
stringstream s;
s << " An attempt has been made to access a non-existent property" << endl
<< " in this event. Please check the property names used, spelling, etc.";
cerr << fgred << highint << endl << s.str() << reset << endl;
throw BaseException(s.str());
} else {
cout << endl << " set " << Events[i].SetParamName[j]
<< " to function value (Late Bound)";
}
}
} else {
cout << endl << " set "
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
<< " to " << Events[i].SetValue[j];
}
cout << endl << " set "
<< Events[i].SetParam[j]->GetRelativeName("/fdm/jsbsim/")
<< " to " << Events[i].SetValue[j];
}
}
switch (Events[i].Type[j]) {

View file

@ -81,12 +81,13 @@ static bool LoadWinSockDLL(int debug_lvl)
}
#endif
FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol)
FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol, int precision)
{
sckt = sckt_in = 0;
Protocol = (ProtocolType)protocol;
connected = false;
struct addrinfo *addr = nullptr;
this->precision = precision;
#if defined(_MSC_VER) || defined(__MINGW32__)
if (!LoadWinSockDLL(debug_lvl)) return;
@ -150,12 +151,13 @@ FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// assumes TCP or UDP socket on localhost, for inbound datagrams
FGfdmSocket::FGfdmSocket(int port, int protocol)
FGfdmSocket::FGfdmSocket(int port, int protocol, int precision)
{
sckt = -1;
connected = false;
Protocol = (ProtocolType)protocol;
string ProtocolName;
this->precision = precision;
#if defined(_MSC_VER) || defined(__MINGW32__)
if (!LoadWinSockDLL(debug_lvl)) return;
@ -338,7 +340,7 @@ void FGfdmSocket::Append(const char* item)
void FGfdmSocket::Append(double item)
{
if (buffer.tellp() > 0) buffer << ',';
buffer << std::setw(12) << std::setprecision(7) << item;
buffer << std::setw(12) << std::setprecision(precision) << item;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -70,10 +70,8 @@ CLASS DECLARATION
class FGfdmSocket : public FGJSBBase
{
public:
FGfdmSocket(const std::string& address, int port)
: FGfdmSocket(address, port, ptTCP) {}
FGfdmSocket(const std::string&, int, int);
FGfdmSocket(int, int);
FGfdmSocket(const std::string& address, int port, int protocol, int precision = 7);
FGfdmSocket(int port, int protocol, int precision = 7);
~FGfdmSocket();
void Send(void);
void Send(const char *data, int length);
@ -104,6 +102,7 @@ private:
struct sockaddr_in scktName;
struct hostent *host;
std::ostringstream buffer;
int precision;
bool connected;
void Debug(int from);
};

View file

@ -124,7 +124,7 @@ FGCondition::FGCondition(const string& test, FGPropertyManager* PropertyManager,
Comparison = mComparison[conditional];
if (Comparison == ecUndef) {
std::invalid_argument("JSBSim FGCondition: Comparison operator: \""+conditional
throw std::invalid_argument("JSBSim FGCondition: Comparison operator: \""+conditional
+"\" does not exist. Please check the conditional.");
}
}

View file

@ -890,10 +890,8 @@ void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
FGFunction::~FGFunction()
{
if (pNode && pNode->isTied()) {
string pName = pNode->GetFullyQualifiedName();
PropertyManager->Untie(pName);
}
if (pNode && pNode->isTied())
PropertyManager->Untie(pNode);
Debug(1);
}

View file

@ -425,19 +425,16 @@ FGMatrix33 FGMatrix33::operator/(const double scalar) const
{
FGMatrix33 Quot;
if ( scalar != 0 ) {
double tmp = 1.0/scalar;
Quot.data[0] = data[0] * tmp;
Quot.data[3] = data[3] * tmp;
Quot.data[6] = data[6] * tmp;
Quot.data[1] = data[1] * tmp;
Quot.data[4] = data[4] * tmp;
Quot.data[7] = data[7] * tmp;
Quot.data[2] = data[2] * tmp;
Quot.data[5] = data[5] * tmp;
Quot.data[8] = data[8] * tmp;
} else
throw MatrixException{"Attempt to divide by zero in method FGMatrix33::operator/(const double scalar)"};
double tmp = 1.0/scalar;
Quot.data[0] = data[0] * tmp;
Quot.data[3] = data[3] * tmp;
Quot.data[6] = data[6] * tmp;
Quot.data[1] = data[1] * tmp;
Quot.data[4] = data[4] * tmp;
Quot.data[7] = data[7] * tmp;
Quot.data[2] = data[2] * tmp;
Quot.data[5] = data[5] * tmp;
Quot.data[8] = data[8] * tmp;
return Quot;
}
@ -446,19 +443,16 @@ FGMatrix33 FGMatrix33::operator/(const double scalar) const
FGMatrix33& FGMatrix33::operator/=(const double scalar)
{
if ( scalar != 0 ) {
double tmp = 1.0/scalar;
data[0] *= tmp;
data[3] *= tmp;
data[6] *= tmp;
data[1] *= tmp;
data[4] *= tmp;
data[7] *= tmp;
data[2] *= tmp;
data[5] *= tmp;
data[8] *= tmp;
} else
throw MatrixException{"Attempt to divide by zero in method FGMatrix33::operator/=(const double scalar)"};
double tmp = 1.0/scalar;
data[0] *= tmp;
data[3] *= tmp;
data[6] *= tmp;
data[1] *= tmp;
data[4] *= tmp;
data[7] *= tmp;
data[2] *= tmp;
data[5] *= tmp;
data[8] *= tmp;
return *this;
}

View file

@ -42,8 +42,8 @@ INCLUDES
#include <string>
#include <iosfwd>
#include <stdexcept>
#include "FGJSBBase.h"
#include "FGColumnVector3.h"
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -54,23 +54,6 @@ namespace JSBSim {
class FGQuaternion;
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/** Exception convenience class.
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DECLARATION: MatrixException
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class MatrixException : public std::runtime_error
{
public:
MatrixException(const std::string& msg) : std::runtime_error{msg} { }
};
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DOCUMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

View file

@ -158,7 +158,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
std::cerr << el->ReadFrom()
<<" An unknown table type attribute is listed: " << call_type
<< endl;
throw TableException("Unknown table type.");
throw BaseException("Unknown table type.");
}
// Determine and store the lookup properties for this table unless this table
@ -199,7 +199,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
} else if (lookup_axis == string("table")) {
lookupProperty[eTable] = node;
} else if (!lookup_axis.empty()) {
throw TableException("Lookup table axis specification not understood: " + lookup_axis);
throw BaseException("Lookup table axis specification not understood: " + lookup_axis);
} else { // assumed single dimension table; row lookup
lookupProperty[eRow] = node;
}
@ -232,7 +232,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
std::cerr << el->ReadFrom()
<< "No independentVars found, and table is not marked as internal,"
<< " nor is it a 3D table." << endl;
throw TableException("No independent variable found for table.");
throw BaseException("No independent variable found for table.");
}
}
// end lookup property code
@ -245,8 +245,16 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
}
for (i=0; i<tableData->GetNumDataLines(); i++) {
buf << tableData->GetDataLine(i) << string(" ");
string line = tableData->GetDataLine(i);
if (line.find_first_not_of("0123456789.-+eE \t\n") != string::npos) {
cerr << " In file " << tableData->GetFileName() << endl
<< " Illegal character found in line "
<< tableData->GetLineNumber() + i + 1 << ": " << endl << line << endl;
throw BaseException("Illegal character");
}
buf << line << " ";
}
switch (dimension) {
case 1:
nRows = tableData->GetNumDataLines();
@ -267,12 +275,12 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
if (nCols < 2) {
std::cerr << tableData->ReadFrom()
<< "Not enough columns in table data" << endl;
throw TableException("Not enough columns in table data.");
throw BaseException("Not enough columns in table data.");
}
} else {
std::cerr << tableData->ReadFrom()
<< "Not enough rows in table data" << endl;
throw TableException("Not enough rows in the table data.");
throw BaseException("Not enough rows in the table data.");
}
Type = tt2D;
@ -330,7 +338,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
std::cerr << ":" << reset << endl
<< " " << Data[b][1] << "<=" << Data[b-1][1] << endl;
throw TableException("Breakpoint lookup is not monotonically increasing");
throw BaseException("Breakpoint lookup is not monotonically increasing");
}
}
}
@ -346,7 +354,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
std::cerr << ":" << reset << endl
<< " " << Data[0][c] << "<=" << Data[0][c-1] << endl;
throw TableException("FGTable: column lookup is not monotonically increasing");
throw BaseException("FGTable: column lookup is not monotonically increasing");
}
}
}
@ -362,7 +370,7 @@ FGTable::FGTable(FGPropertyManager* propMan, Element* el,
if (nameel != 0) std::cerr << " of table in " << nameel->GetAttributeValue("name");
std::cerr << ":" << reset << endl
<< " " << Data[r][0] << "<=" << Data[r-1][0] << endl;
throw TableException("FGTable: row lookup is not monotonically increasing");
throw BaseException("FGTable: row lookup is not monotonically increasing");
}
}
}
@ -393,10 +401,10 @@ FGTable::~FGTable()
// Untie the bound property so that it makes no further reference to this
// instance of FGTable after the destruction is completed.
if (!Name.empty() && !internal) {
string tmp = mkPropertyName(nullptr, "");
string tmp = PropertyManager->mkPropertyName(Name, false);
FGPropertyNode* node = PropertyManager->GetNode(tmp);
if (node->isTied())
PropertyManager->Untie(tmp);
if (node && node->isTied())
PropertyManager->Untie(node);
}
if (nTables > 0) {
@ -653,33 +661,26 @@ void FGTable::Print(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
string FGTable::mkPropertyName(Element* el, const std::string& Prefix)
{
if (!Prefix.empty()) {
if (is_number(Prefix)) {
if (Name.find("#") != string::npos) { // if "#" is found
Name = replace(Name, "#", Prefix);
} else {
cerr << el->ReadFrom()
<< "Malformed table name with number: " << Prefix
<< " and property name: " << Name
<< " but no \"#\" sign for substitution." << endl;
}
} else {
Name = Prefix + "/" + Name;
}
}
return PropertyManager->mkPropertyName(Name, false);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGTable::bind(Element* el, const string& Prefix)
{
typedef double (FGTable::*PMF)(void) const;
if ( !Name.empty() && !internal) {
string tmp = mkPropertyName(el, Prefix);
if (!Prefix.empty()) {
if (is_number(Prefix)) {
if (Name.find("#") != string::npos) { // if "#" is found
Name = replace(Name, "#", Prefix);
} else {
cerr << el->ReadFrom()
<< "Malformed table name with number: " << Prefix
<< " and property name: " << Name
<< " but no \"#\" sign for substitution." << endl;
}
} else {
Name = Prefix + "/" + Name;
}
}
string tmp = PropertyManager->mkPropertyName(Name, false);
if (PropertyManager->HasNode(tmp)) {
FGPropertyNode* _property = PropertyManager->GetNode(tmp);
@ -689,7 +690,8 @@ void FGTable::bind(Element* el, const string& Prefix)
throw("Failed to bind the property to an existing already tied node.");
}
}
PropertyManager->Tie( tmp, this, (PMF)&FGTable::GetValue);
PropertyManager->Tie(tmp, this, (PMF)&FGTable::GetValue);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View file

@ -226,19 +226,6 @@ combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio
@author Jon S. Berndt
*/
/** Exception convenience class.
*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DECLARATION: TableException
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
class TableException : public std::runtime_error
{
public:
TableException(const std::string& msg) : std::runtime_error{msg} { }
};
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS DECLARATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
@ -317,8 +304,6 @@ private:
FGPropertyManager* const PropertyManager;
std::string Name;
void bind(Element* el, const std::string& Prefix);
std::string mkPropertyName(Element* el, const std::string& Prefix);
void Debug(int from);
};
}

View file

@ -229,9 +229,13 @@ bool FGAerodynamics::Run(bool Holding)
vForcesAtCG = Ts2b*vFnativeAtCG;
break;
default:
cerr << endl << " A proper axis type has NOT been selected. Check "
<< "your aerodynamics definition." << endl;
exit(-1);
{
stringstream s;
s << " A proper axis type has NOT been selected. Check "
<< "your aerodynamics definition.";
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
}
// Calculate aerodynamic reference point shift, if any. The shift takes place
// in the structual axis. That is, if the shift is positive, it is towards the
@ -257,7 +261,7 @@ bool FGAerodynamics::Run(bool Holding)
vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
}
}
// Transform moments to bodyXYZ if the moments are specified in stability or
// wind axes
vMomentsMRCBodyXYZ.InitMatrix();
@ -272,13 +276,17 @@ bool FGAerodynamics::Run(bool Holding)
vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
break;
default:
cerr << endl << " A proper axis type has NOT been selected. Check "
<< "your aerodynamics definition." << endl;
exit(-1);
{
stringstream s;
s << " A proper axis type has NOT been selected. Check "
<< "your aerodynamics definition.";
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
}
vMoments = vMomentsMRCBodyXYZ + vDXYZcg*vForces; // M = r X F
// Now add the "at CG" values to base forces - after the moments have been
// transferred.
vForces += vForcesAtCG;
@ -310,7 +318,7 @@ bool FGAerodynamics::Run(bool Holding)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
FGColumnVector3 FGAerodynamics::GetForcesInStabilityAxes(void) const
{
{
FGColumnVector3 vFs = Tb2s*vForces;
// Need sign flips since drag is positive and lift is positive in stability axes
vFs(eDrag) *= -1; vFs(eLift) *= -1;
@ -403,15 +411,15 @@ bool FGAerodynamics::Load(Element *document)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// This private class function checks to verify consistency in the choice of
// aerodynamic axes used in the config file. One set of LIFT|DRAG|SIDE, or
// aerodynamic axes used in the config file. One set of LIFT|DRAG|SIDE, or
// X|Y|Z, or AXIAL|NORMAL|SIDE must be chosen; mixed system axes are not allowed.
// Note that if the "SIDE" axis specifier is entered first in a config file,
// Note that if the "SIDE" axis specifier is entered first in a config file,
// a warning message will be given IF the AXIAL|NORMAL specifiers are also given.
// This is OK, and the warning is due to the SIDE specifier used for both
// the Lift/Drag and Axial/Normal axis systems.
// Alternatively the axis name 'X|Y|Z or ROLL|PITCH|YAW' can be specified in
// Alternatively the axis name 'X|Y|Z or ROLL|PITCH|YAW' can be specified in
// conjunction with a frame 'BODY|STABILITY|WIND', for example:
// <axis name="X" frame="STABILITY"/>
// <axis name="X" frame="STABILITY"/>
void FGAerodynamics::DetermineAxisSystem(Element* document)
{
@ -447,10 +455,12 @@ void FGAerodynamics::DetermineAxisSystem(Element* document)
<< " aircraft config file. (NORMAL AXIAL)" << endl;
}
} else { // error
cerr << endl << axis_element->ReadFrom()
<< endl << " An unknown axis type, " << axis << " has been specified"
<< " in the aircraft configuration file." << endl;
exit(-1);
stringstream s;
s << axis_element->ReadFrom()
<< endl << " An unknown axis type, " << axis << " has been specified"
<< " in the aircraft configuration file.";
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
axis_element = document->FindNextElement("axis");
}
@ -495,8 +505,10 @@ void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType, const string&
<< " aircraft config file." << validNames << " - WIND" << endl;
}
else {
cerr << endl << " Unknown axis frame type of - " << frame << endl;
exit(-1);
stringstream s;
s << " Unknown axis frame type of - " << frame;
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
}
@ -596,7 +608,7 @@ void FGAerodynamics::bind(void)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// Build transformation matrices for transforming from stability axes to
// Build transformation matrices for transforming from stability axes to
// body axes and to wind axes. Where "a" is alpha and "B" is beta:
//
// The transform from body to stability axes is:

View file

@ -86,8 +86,9 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
if (element) {
vXYZ = element->FindElementTripletConvertTo("IN");
} else {
cerr << "Fatal Error: No location found for this gas cell." << endl;
exit(-1);
const string s("Fatal Error: No location found for this gas cell.");
cerr << el->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
if ((el->FindElement("x_radius") || el->FindElement("x_width")) &&
(el->FindElement("y_radius") || el->FindElement("y_width")) &&
@ -126,7 +127,7 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
MaxVolume = M_PI * Yradius * Zradius * Xwidth;
} else {
cerr << "Warning: Unsupported gas cell shape." << endl;
MaxVolume =
MaxVolume =
(4.0 * M_PI * Xradius * Yradius * Zradius / 3.0 +
M_PI * Yradius * Zradius * Xwidth +
M_PI * Xradius * Zradius * Ywidth +
@ -137,8 +138,9 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
Xwidth * Ywidth * Zwidth);
}
} else {
cerr << "Fatal Error: Gas cell shape must be given." << endl;
exit(-1);
const string s("Fatal Error: Gas cell shape must be given.");
cerr << el->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
if (el->FindElement("max_overpressure")) {
MaxOverpressure = el->FindElementValueAsNumberConvertTo("max_overpressure",
@ -146,12 +148,12 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
}
if (el->FindElement("fullness")) {
const double Fullness = el->FindElementValueAsNumber("fullness");
if (0 <= Fullness) {
Volume = Fullness * MaxVolume;
if (0 <= Fullness) {
Volume = Fullness * MaxVolume;
} else {
cerr << "Warning: Invalid initial gas cell fullness value." << endl;
}
}
}
if (el->FindElement("valve_coefficient")) {
ValveCoefficient =
el->FindElementValueAsNumberConvertTo("valve_coefficient",
@ -171,7 +173,7 @@ FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num,
if (Volume != 0.0) {
// Calculate initial gas content.
Contents = Pressure * Volume / (R * Temperature);
// Clip to max allowed value.
const double IdealPressure = Contents * R * Temperature / MaxVolume;
if (IdealPressure > Pressure + MaxOverpressure) {
@ -263,7 +265,7 @@ void FGGasCell::Calculate(double dt)
const size_t no_ballonets = Ballonet.size();
//-- Read ballonet state --
// NOTE: This model might need a more proper integration technique.
// NOTE: This model might need a more proper integration technique.
double BallonetsVolume = 0.0;
double BallonetsHeatFlow = 0.0;
for (i = 0; i < no_ballonets; i++) {
@ -282,7 +284,7 @@ void FGGasCell::Calculate(double dt)
dU += HeatTransferCoeff[i]->GetValue();
}
// Don't include dt when accounting for adiabatic expansion/contraction.
// The rate of adiabatic cooling looks about right: ~5.4 Rankine/1000ft.
// The rate of adiabatic cooling looks about right: ~5.4 Rankine/1000ft.
if (Contents > 0) {
Temperature +=
(dU * dt - Pressure * dVolumeIdeal - BallonetsHeatFlow) /
@ -354,7 +356,7 @@ void FGGasCell::Calculate(double dt)
//-- Current buoyancy --
// The buoyancy is computed using the atmospheres local density.
Buoyancy = Volume * AirDensity * g;
// 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.
@ -372,7 +374,7 @@ void FGGasCell::Calculate(double dt)
// Ellipsoid volume.
Ixx = (1.0 / 5.0) * mass * (Yradius*Yradius + Zradius*Zradius);
Iyy = (1.0 / 5.0) * mass * (Xradius*Xradius + Zradius*Zradius);
Izz = (1.0 / 5.0) * mass * (Xradius*Xradius + Yradius*Yradius);
Izz = (1.0 / 5.0) * mass * (Xradius*Xradius + Yradius*Yradius);
} else if ((Xradius == 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&
(Xwidth != 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) {
// Cylindrical volume (might not be valid with an elliptical cross-section).
@ -407,7 +409,7 @@ void FGGasCell::Calculate(double dt)
// Add the mass, moment and inertia of any ballonets.
for (i = 0; i < no_ballonets; i++) {
Mass += Ballonet[i]->GetMass();
// Add ballonet moments due to mass (in the structural frame).
gasCellM(eX) +=
Ballonet[i]->GetXYZ(eX) * Ballonet[i]->GetMass()*slugtolb;
@ -415,7 +417,7 @@ void FGGasCell::Calculate(double dt)
Ballonet[i]->GetXYZ(eY) * Ballonet[i]->GetMass()*slugtolb;
gasCellM(eZ) +=
Ballonet[i]->GetXYZ(eZ) * Ballonet[i]->GetMass()*slugtolb;
gasCellJ += Ballonet[i]->GetInertia();
}
}
@ -451,9 +453,9 @@ void FGGasCell::Debug(int from)
cout << " Cell location (X, Y, Z) (in.): " << vXYZ(eX) << ", " <<
vXYZ(eY) << ", " << vXYZ(eZ) << endl;
cout << " Maximum volume: " << MaxVolume << " ft3" << endl;
cout << " Relief valve release pressure: " << MaxOverpressure <<
cout << " Relief valve release pressure: " << MaxOverpressure <<
" lbs/ft2" << endl;
cout << " Manual valve coefficient: " << ValveCoefficient <<
cout << " Manual valve coefficient: " << ValveCoefficient <<
" ft4*sec/slug" << endl;
cout << " Initial temperature: " << Temperature << " Rankine" <<
endl;
@ -471,7 +473,7 @@ void FGGasCell::Debug(int from)
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
if (debug_lvl & 8 ) { // Runtime state variables
if (debug_lvl & 8 ) { // Runtime state variables
cout << " " << type << " cell holds " << Contents << " mol " << endl;
cout << " Temperature: " << Temperature << " Rankine" << endl;
cout << " Pressure: " << Pressure << " lbs/ft2" << endl;
@ -517,8 +519,9 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
if (element) {
vXYZ = element->FindElementTripletConvertTo("IN");
} else {
cerr << "Fatal Error: No location found for this ballonet." << endl;
exit(-1);
const string s("Fatal Error: No location found for this ballonet.");
cerr << el->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
if ((el->FindElement("x_radius") || el->FindElement("x_width")) &&
(el->FindElement("y_radius") || el->FindElement("y_width")) &&
@ -557,7 +560,7 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
MaxVolume = M_PI * Yradius * Zradius * Xwidth;
} else {
cerr << "Warning: Unsupported ballonet shape." << endl;
MaxVolume =
MaxVolume =
(4.0 * M_PI * Xradius * Yradius * Zradius / 3.0 +
M_PI * Yradius * Zradius * Xwidth +
M_PI * Xradius * Zradius * Ywidth +
@ -568,8 +571,9 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
Xwidth * Ywidth * Zwidth);
}
} else {
cerr << "Fatal Error: Ballonet shape must be given." << endl;
exit(-1);
const string s("Fatal Error: Ballonet shape must be given.");
cerr << el->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
if (el->FindElement("max_overpressure")) {
MaxOverpressure = el->FindElementValueAsNumberConvertTo("max_overpressure",
@ -577,12 +581,12 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
}
if (el->FindElement("fullness")) {
const double Fullness = el->FindElementValueAsNumber("fullness");
if (0 <= Fullness) {
Volume = Fullness * MaxVolume;
if (0 <= Fullness) {
Volume = Fullness * MaxVolume;
} else {
cerr << "Warning: Invalid initial ballonet fullness value." << endl;
}
}
}
if (el->FindElement("valve_coefficient")) {
ValveCoefficient =
el->FindElementValueAsNumberConvertTo("valve_coefficient",
@ -600,7 +604,7 @@ FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num,
if (Volume != 0.0) {
// Calculate initial air content.
Contents = Pressure * Volume / (R * Temperature);
// Clip to max allowed value.
const double IdealPressure = Contents * R * Temperature / MaxVolume;
if (IdealPressure > Pressure + MaxOverpressure) {
@ -746,7 +750,7 @@ void FGBallonet::Calculate(double dt)
// Ellipsoid volume.
Ixx = (1.0 / 5.0) * mass * (Yradius*Yradius + Zradius*Zradius);
Iyy = (1.0 / 5.0) * mass * (Xradius*Xradius + Zradius*Zradius);
Izz = (1.0 / 5.0) * mass * (Xradius*Xradius + Yradius*Yradius);
Izz = (1.0 / 5.0) * mass * (Xradius*Xradius + Yradius*Yradius);
} else if ((Xradius == 0.0) && (Yradius != 0.0) && (Zradius != 0.0) &&
(Xwidth != 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) {
// Cylindrical volume (might not be valid with an elliptical cross-section).
@ -798,9 +802,9 @@ void FGBallonet::Debug(int from)
cout << " Location (X, Y, Z) (in.): " << vXYZ(eX) << ", " <<
vXYZ(eY) << ", " << vXYZ(eZ) << endl;
cout << " Maximum volume: " << MaxVolume << " ft3" << endl;
cout << " Relief valve release pressure: " << MaxOverpressure <<
cout << " Relief valve release pressure: " << MaxOverpressure <<
" lbs/ft2" << endl;
cout << " Relief valve coefficient: " << ValveCoefficient <<
cout << " Relief valve coefficient: " << ValveCoefficient <<
" ft4*sec/slug" << endl;
cout << " Initial temperature: " << Temperature << " Rankine" <<
endl;
@ -818,7 +822,7 @@ void FGBallonet::Debug(int from)
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
}
if (debug_lvl & 8 ) { // Runtime state variables
if (debug_lvl & 8 ) { // Runtime state variables
cout << " Ballonet holds " << Contents <<
" mol air" << endl;
cout << " Temperature: " << Temperature << " Rankine" << endl;

View file

@ -43,7 +43,7 @@ INCLUDES
#include "input_output/FGUDPInputSocket.h"
#include "input_output/FGXMLFileRead.h"
#include "input_output/FGModelLoader.h"
using namespace std;
namespace JSBSim {
@ -84,7 +84,7 @@ bool FGInput::Load(Element* el)
Element* element = ModelLoader.Open(el);
if (!element) return false;
FGModel::PreLoad(element, FDMExec);
size_t idx = InputTypes.size();
@ -152,6 +152,11 @@ bool FGInput::SetDirectivesFile(const SGPath& fname)
{
FGXMLFileRead XMLFile;
Element* document = XMLFile.LoadXMLDocument(fname);
if (!document) {
stringstream s;
s << "Could not read directive file: " << fname;
throw BaseException(s.str());
}
bool result = Load(document);
if (!result)

View file

@ -180,7 +180,12 @@ FGLGear::FGLGear(Element* el, FGFDMExec* fdmex, int number, const struct Inputs&
Element* element = el->FindElement("location");
if (element) vXYZn = element->FindElementTripletConvertTo("IN");
else {cerr << "No location given for contact " << name << endl; exit(-1);}
else {
stringstream s;
s << "No location given for contact " << name;
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
SetTransformType(FGForce::tCustom);
element = el->FindElement("orientation");

View file

@ -117,7 +117,7 @@ static FGMatrix33 ReadInertiaMatrix(Element* document)
// Transform the inertia products from the structural frame to the body frame
// and create the inertia matrix.
if (document->GetAttributeValue("negated_crossproduct_inertia") == string("false"))
if (document->GetAttributeValue("negated_crossproduct_inertia") == string("false"))
return FGMatrix33( bixx, bixy, -bixz,
bixy, biyy, biyz,
-bixz, biyz, bizz );
@ -268,9 +268,11 @@ void FGMassBalance::AddPointMass(Element* el)
Element* loc_element = el->FindElement("location");
string pointmass_name = el->GetAttributeValue("name");
if (!loc_element) {
cerr << el->ReadFrom() << "Pointmass " << pointmass_name
<< " has no location." << endl;
exit(-1);
stringstream s;
s << el->ReadFrom() << "Pointmass " << pointmass_name
<< " has no location.";
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
double w = el->FindElementValueAsNumberConvertTo("weight", "LBS");
@ -449,11 +451,11 @@ void FGMassBalance::PointMass::bind(FGPropertyManager* PropertyManager,
void FGMassBalance::GetMassPropertiesReport(int i)
{
cout << endl << fgblue << highint
cout << endl << fgblue << highint
<< " Mass Properties Report (English units: lbf, in, slug-ft^2)"
<< reset << endl;
cout << " " << underon << " Weight CG-X CG-Y"
<< " CG-Z Ixx Iyy Izz"
<< " CG-Z Ixx Iyy Izz"
<< " Ixy Ixz Iyz" << underoff << endl;
cout.precision(1);
cout << highint << setw(34) << left << " Base Vehicle " << normint
@ -469,13 +471,13 @@ void FGMassBalance::GetMassPropertiesReport(int i)
<< right << setw(10) << pmweight << setw(8) << pm->GetLocation()(eX)
<< setw(8) << pm->GetLocation()(eY) << setw(8) << pm->GetLocation()(eZ)
<< setw(12) << pm->GetPointMassMoI(1,1) << setw(12) << pm->GetPointMassMoI(2,2) << setw(12) << pm->GetPointMassMoI(3,3)
<< setw(12) << pm->GetPointMassMoI(1,2) << setw(12) << pm->GetPointMassMoI(1,3) << setw(12) << pm->GetPointMassMoI(2,3) << endl;
<< setw(12) << pm->GetPointMassMoI(1,2) << setw(12) << pm->GetPointMassMoI(1,3) << setw(12) << pm->GetPointMassMoI(2,3) << endl;
}
cout << FDMExec->GetPropulsionTankReport();
cout << " " << underon << setw(136) << " " << underoff << endl;
cout << highint << left << setw(30) << " Total: " << right << setw(14) << Weight
cout << highint << left << setw(30) << " Total: " << right << setw(14) << Weight
<< setw(8) << vXYZcg(eX)
<< setw(8) << vXYZcg(eY)
<< setw(8) << vXYZcg(eZ)

View file

@ -173,8 +173,13 @@ bool FGOutput::SetDirectivesFile(const SGPath& fname)
{
FGXMLFileRead XMLFile;
Element* document = XMLFile.LoadXMLDocument(fname);
bool result = Load(document);
if (!document) {
stringstream s;
s << "Could not read directive file: " << fname;
throw BaseException(s.str());
}
bool result = Load(document);
if (!result)
cerr << endl << "Aircraft output element has problems in file " << fname << endl;

View file

@ -688,7 +688,7 @@ void FGPropagate::WriteStateFile(int num)
if (num == 0) return;
SGPath path = FDMExec->GetFullAircraftPath();
SGPath path = FDMExec->GetOutputPath();
if (path.isNull()) path = SGPath("initfile.");
else path.append("initfile.");
@ -968,16 +968,22 @@ void FGPropagate::Debug(int from)
if (debug_lvl & 16) { // Sanity checking
if (from == 2) { // State sanity checking
if (fabs(VState.vPQR.Magnitude()) > 1000.0) {
cerr << endl << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude() << endl;
exit(-1);
stringstream s;
s << "Vehicle rotation rate is excessive (>1000 rad/sec): " << VState.vPQR.Magnitude();
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
if (fabs(VState.vUVW.Magnitude()) > 1.0e10) {
cerr << endl << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude() << endl;
exit(-1);
stringstream s;
s << "Vehicle velocity is excessive (>1e10 ft/sec): " << VState.vUVW.Magnitude();
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
if (fabs(GetDistanceAGL()) > 1e10) {
cerr << endl << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL() << endl;
exit(-1);
stringstream s;
s << "Vehicle altitude is excessive (>1e10 ft): " << GetDistanceAGL();
cerr << endl << s.str() << endl;
throw BaseException(s.str());
}
}
}

View file

@ -52,34 +52,29 @@ CLASS IMPLEMENTATION
FGKinemat::FGKinemat(FGFCS* fcs, Element* element)
: FGFCSComponent(fcs, element)
{
Element *traverse_element, *setting_element;
double tmpDetent;
double tmpTime;
CheckInputNodes(1, 1, element);
Detents.clear();
TransitionTimes.clear();
Output = 0;
DoScale = true;
if (element->FindElement("noscale")) DoScale = false;
traverse_element = element->FindElement("traverse");
setting_element = traverse_element->FindElement("setting");
Element* traverse_element = element->FindElement("traverse");
Element* setting_element = traverse_element->FindElement("setting");
while (setting_element) {
tmpDetent = setting_element->FindElementValueAsNumber("position");
tmpTime = setting_element->FindElementValueAsNumber("time");
double tmpDetent = setting_element->FindElementValueAsNumber("position");
double tmpTime = setting_element->FindElementValueAsNumber("time");
Detents.push_back(tmpDetent);
TransitionTimes.push_back(tmpTime);
setting_element = traverse_element->FindNextElement("setting");
}
if (Detents.size() <= 1) {
cerr << "Kinematic component " << Name
<< " must have more than 1 setting element" << endl;
exit(-1);
stringstream s;
s << "Kinematic component " << Name
<< " must have more than 1 setting element";
cerr << element->ReadFrom() << endl << s.str() << endl;
throw BaseException(s.str());
}
bind(element);

View file

@ -182,7 +182,24 @@ bool FGWaypoint::Run(void )
double target_longitude_rad = target_longitude->GetValue() * target_longitude_unit;
source.SetPosition(source_longitude_rad, source_latitude_rad, radius);
if (fabs(target_latitude_rad) > M_PI/2.0) {
cerr << endl;
cerr << "Target latitude in waypoint \"" << Name << "\" must be less than or equal to 90 degrees." << endl;
cerr << "(is longitude being mistakenly supplied?)" << endl;
cerr << endl;
throw("Waypoint target latitude exceeded 90 degrees.");
}
if (fabs(source_latitude_rad) > M_PI/2.0) {
cerr << endl;
cerr << "Source latitude in waypoint \"" << Name << "\" must be less than or equal to 90 degrees." << endl;
cerr << "(is longitude being mistakenly supplied?)" << endl;
cerr << endl;
throw("Source latitude exceeded 90 degrees.");
}
if (WaypointType == eHeading) { // Calculate Heading
double heading_to_waypoint_rad = source.GetHeadingTo(target_longitude_rad,
target_latitude_rad);
@ -190,11 +207,12 @@ bool FGWaypoint::Run(void )
else Output = heading_to_waypoint_rad;
} else { // Calculate Distance
double wp_distance = source.GetDistanceTo(target_longitude_rad,
target_latitude_rad);
if (eUnit == eMeters) Output = FeetToMeters(wp_distance);
else Output = wp_distance;
}
Clip();

View file

@ -56,8 +56,6 @@ FGEngine::FGEngine(int engine_number, struct Inputs& input)
: in(input), EngineNumber(engine_number)
{
Type = etUnknown;
X = Y = Z = 0.0;
EnginePitch = EngineYaw = 0.0;
SLFuelFlowMax = 0.0;
FuelExpended = 0.0;
MaxThrottle = 1.0;
@ -113,18 +111,6 @@ unsigned int FGEngine::GetSourceTank(unsigned int i) const
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGEngine::SetPlacement(const FGColumnVector3& location,
const FGColumnVector3& orientation)
{
X = location(eX);
Y = location(eY);
Z = location(eZ);
EnginePitch = orientation(ePitch);
EngineYaw = orientation (eYaw);
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double FGEngine::GetThrust(void) const
{
return Thruster->GetThrust();
@ -200,19 +186,17 @@ bool FGEngine::Load(FGFDMExec *exec, Element *engine_element)
// Call ModelFunctions loader
FGModelFunctions::Load(engine_element, exec, to_string((int)EngineNumber));
// Find and set engine location
// If engine location and/or orientation is supplied issue a warning since they
// are ignored. What counts is the location and orientation of the thruster.
local_element = parent_element->FindElement("location");
if (local_element) location = local_element->FindElementTripletConvertTo("IN");
// else cerr << "No engine location found for this engine." << endl;
// Jon: The engine location is not important - the nozzle location is.
if (local_element)
cerr << local_element->ReadFrom()
<< "Engine location ignored, only thruster location is used." << endl;
local_element = parent_element->FindElement("orient");
if (local_element) orientation = local_element->FindElementTripletConvertTo("RAD");
// else cerr << "No engine orientation found for this engine." << endl;
// Jon: The engine orientation has a default and is not normally used.
SetPlacement(location, orientation);
if (local_element)
cerr << local_element->ReadFrom()
<< "Engine orientation ignored, only thruster orientation is used." << endl;
// Load thruster
local_element = parent_element->FindElement("thruster");

View file

@ -71,17 +71,6 @@ CLASS DOCUMENTATION
<h3>Configuration File Format:</h3>
@code
<engine file="{string}">
<location unit="{IN | M}">
<x> {number} </x>
<y> {number} </y>
<z> {number} </z>
</location>
<!-- optional orientation definition -->
<orient unit="{RAD | DEG}">
<roll> {number} </roll>
<pitch> {number} </pitch>
<yaw> {number} </yaw>
</orient>
<feed> {integer} </feed>
... optional more feed tank index numbers ...
<thruster file="{string}">
@ -183,9 +172,6 @@ public:
virtual double GetThrust(void) const;
/// Sets engine placement information
virtual void SetPlacement(const FGColumnVector3& location, const FGColumnVector3& orientation);
/** The fuel need is calculated based on power levels and flow rate for that
power level. It is also turned from a rate into an actual amount (pounds)
by multiplying it by the delta T and the rate.
@ -216,9 +202,6 @@ protected:
std::string Name;
const int EngineNumber;
EngineType Type;
double X, Y, Z;
double EnginePitch;
double EngineYaw;
double SLFuelFlowMax;
double MaxThrottle;
double MinThrottle;

View file

@ -106,8 +106,11 @@ const FGMatrix33& FGForce::Transform(void) const
case tNone:
return mT;
default:
cout << "Unrecognized tranform requested from FGForce::Transform()" << endl;
exit(1);
{
const string s("Unrecognized tranform requested from FGForce::Transform()");
cout << s << endl;
throw BaseException(s);
}
}
}

View file

@ -57,13 +57,14 @@ FGNozzle::FGNozzle(FGFDMExec* FDMExec, Element* nozzle_element, int num)
if (nozzle_element->FindElement("area"))
Area = nozzle_element->FindElementValueAsNumberConvertTo("area", "FT2");
else {
cerr << "Fatal Error: Nozzle exit area must be given in nozzle config file." << endl;
exit(-1);
const string s("Fatal Error: Nozzle exit area must be given in nozzle config file.");
cerr << s << endl;
throw BaseException(s);
}
Thrust = 0;
Type = ttNozzle;
Debug(0);
}

View file

@ -193,10 +193,9 @@ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number)
switch (grainType) {
case gtCYLINDRICAL:
if (Radius <= InnerRadius) {
cerr << element_Grain->ReadFrom()
<< "The bore diameter should be smaller than the total grain diameter!"
<< endl;
exit(-1);
const string s("The bore diameter should be smaller than the total grain diameter!");
cerr << element_Grain->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
Volume = M_PI * Length * (Radius*Radius - InnerRadius*InnerRadius); // cubic inches
break;
@ -207,10 +206,11 @@ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number)
Volume = 1; // Volume is irrelevant for the FUNCTION type, but it can't be zero!
break;
case gtUNKNOWN:
cerr << el->ReadFrom()
<< "Unknown grain type found in this rocket engine definition."
<< endl;
exit(-1);
{
const string s("Unknown grain type found in this rocket engine definition.");
cerr << el->ReadFrom() << endl << s << endl;
throw BaseException(s);
}
}
Density = (Capacity*lbtoslug)/Volume; // slugs/in^3
}
@ -221,7 +221,7 @@ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number)
Area = 40.0 * pow(Capacity/1975, 0.666666667);
// A named fuel type will override a previous density value
if (!strFuelName.empty()) Density = ProcessFuelName(strFuelName);
if (!strFuelName.empty()) Density = ProcessFuelName(strFuelName);
bind(PropertyManager);
@ -375,8 +375,9 @@ void FGTank::CalculateInertias(void)
} else if (Contents <= 0.0) {
Volume = 0;
} else {
cerr << endl << " Solid propellant grain density is zero!" << endl << endl;
exit(-1);
const string s(" Solid propellant grain density is zero!");
cerr << endl << s << endl;
throw BaseException(s);
}
switch (grainType) {
@ -399,9 +400,11 @@ void FGTank::CalculateInertias(void)
Izz = function_izz->GetValue()*izz_unit;
break;
default:
cerr << "Unknown grain type found." << endl;
exit(-1);
break;
{
const string s("Unknown grain type found.");
cerr << s << endl;
throw BaseException(s);
}
}
} else { // assume liquid propellant: shrinking snowball
@ -415,7 +418,7 @@ void FGTank::CalculateInertias(void)
double FGTank::ProcessFuelName(const std::string& name)
{
if (name == "AVGAS") return 6.02;
if (name == "AVGAS") return 6.02;
else if (name == "JET-A") return 6.74;
else if (name == "JET-A1") return 6.74;
else if (name == "JET-B") return 6.48;
@ -442,7 +445,7 @@ double FGTank::ProcessFuelName(const std::string& name)
else if (name == "AVCAT") return 6.81;
else {
cerr << "Unknown fuel type specified: "<< name << endl;
}
}
return 6.6;
}