diff --git a/src/FDM/JSBSim/FGFDMExec.cpp b/src/FDM/JSBSim/FGFDMExec.cpp index ade4209cd..a3b08a49e 100644 --- a/src/FDM/JSBSim/FGFDMExec.cpp +++ b/src/FDM/JSBSim/FGFDMExec.cpp @@ -71,7 +71,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.84 2011/01/16 16:26:14 bcoconni Exp $"; +static const char *IdSrc = "$Id: FGFDMExec.cpp,v 1.91 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_FDMEXEC; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -116,6 +116,10 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root) dT = 1.0/120.0; // a default timestep size. This is needed for when JSBSim is // run in standalone mode with no initialization file. + AircraftPath = "aircraft"; + EnginePath = "engine"; + SystemsPath = "systems"; + try { char* num = getenv("JSBSIM_DEBUG"); if (num) debug_lvl = atoi(num); // set debug level @@ -154,12 +158,13 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, unsigned int* fdmctr) : Root(root) Constructing = true; typedef int (FGFDMExec::*iPMF)(void) const; -// instance->Tie("simulation/do_trim_analysis", this, (iPMF)0, &FGFDMExec::DoTrimAnalysis); - instance->Tie("simulation/do_simple_trim", this, (iPMF)0, &FGFDMExec::DoTrim); - instance->Tie("simulation/reset", this, (iPMF)0, &FGFDMExec::ResetToInitialConditions); +// instance->Tie("simulation/do_trim_analysis", this, (iPMF)0, &FGFDMExec::DoTrimAnalysis, false); + instance->Tie("simulation/do_simple_trim", this, (iPMF)0, &FGFDMExec::DoTrim, false); + instance->Tie("simulation/reset", this, (iPMF)0, &FGFDMExec::ResetToInitialConditions, false); instance->Tie("simulation/terminate", (int *)&Terminate); instance->Tie("simulation/sim-time-sec", this, &FGFDMExec::GetSimTime); instance->Tie("simulation/jsbsim-debug", this, &FGFDMExec::GetDebugLevel, &FGFDMExec::SetDebugLevel); + instance->Tie("simulation/frame", (int *)&Frame, false); Constructing = false; } @@ -350,6 +355,8 @@ bool FGFDMExec::RunIC(void) void FGFDMExec::Initialize(FGInitialCondition *FGIC) { + Setsim_time(0.0); + Propagate->SetInitialState( FGIC ); Atmosphere->Run(); @@ -358,6 +365,9 @@ void FGFDMExec::Initialize(FGInitialCondition *FGIC) FGIC->GetWindDFpsIC() ); FGColumnVector3 vAeroUVW; + + //ToDo: move this to the Auxiliary class !? + vAeroUVW = Propagate->GetUVW() + Propagate->GetTl2b()*Atmosphere->GetTotalWindNED(); double alpha, beta; @@ -629,7 +639,9 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) // Process the output element[s]. This element is OPTIONAL, and there may be more than one. unsigned int idx=0; - typedef int (FGOutput::*iOPMF)(void) const; + typedef double (FGOutput::*iOPMF)(void) const; + typedef int (FGFDMExec::*iOPV)(void) const; + typedef void (FGFDMExec::*vOPI)(int) const; element = document->FindElement("output"); while (element) { if (debug_lvl > 0) cout << endl << " Output data set: " << idx << " "; @@ -643,7 +655,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) } else { Outputs.push_back(Output); string outputProp = CreateIndexedPropertyName("simulation/output",idx); - instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate); + instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); + instance->Tie("simulation/force-output", this, (iOPV)0, &FGFDMExec::ForceOutput, false); idx++; } element = document->FindNextElement("output"); @@ -679,15 +692,6 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) << fgdef << endl; } - // Late bind previously undefined FCS inputs. - try { - FCS->LateBind(); - } catch (string prop) { - cerr << endl << fgred << " Could not late bind property " << prop - << ". Aborting." << reset << endl; - result = false; - } - if (result) { struct PropertyCatalogStructure masterPCS; masterPCS.base_string = ""; @@ -918,6 +922,13 @@ void FGFDMExec::EnableOutput(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void FGFDMExec::ForceOutput(int idx) +{ + if (idx >= 0 && idx < Outputs.size()) Outputs[idx]->Print(); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + bool FGFDMExec::SetOutputDirectives(const string& fname) { bool result; @@ -930,9 +941,9 @@ bool FGFDMExec::SetOutputDirectives(const string& fname) if (result) { Outputs.push_back(Output); - typedef int (FGOutput::*iOPMF)(void) const; + typedef double (FGOutput::*iOPMF)(void) const; string outputProp = CreateIndexedPropertyName("simulation/output",Outputs.size()-1); - instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate); + instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); } return result; diff --git a/src/FDM/JSBSim/FGFDMExec.h b/src/FDM/JSBSim/FGFDMExec.h index b982654e7..5f103fe15 100644 --- a/src/FDM/JSBSim/FGFDMExec.h +++ b/src/FDM/JSBSim/FGFDMExec.h @@ -44,11 +44,9 @@ INCLUDES #include <vector> #include <string> -//#include "models/FGModel.h" #include "models/FGOutput.h" #include "models/FGInput.h" #include "initialization/FGTrim.h" -#include "initialization/FGInitialCondition.h" #include "FGJSBBase.h" #include "input_output/FGPropertyManager.h" #include "input_output/FGGroundCallback.h" @@ -60,7 +58,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_FDMEXEC "$Id: FGFDMExec.h,v 1.56 2010/11/18 20:37:10 jberndt Exp $" +#define ID_FDMEXEC "$Id: FGFDMExec.h,v 1.63 2011/02/19 16:44:41 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -183,7 +181,7 @@ CLASS DOCUMENTATION property actually maps toa function call of DoTrim(). @author Jon S. Berndt - @version $Revision: 1.56 $ + @version $Revision: 1.63 $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -286,8 +284,11 @@ public: /** Loads 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. @return true if successfully loadsd; false otherwise. */ - bool LoadScript(const string& Script, double deltaT); + bool LoadScript(const string& Script, double deltaT=0.0); /** Sets the path to the engine config file directories. @param path path to the directory under which engine config @@ -401,6 +402,9 @@ public: */ bool SetOutputDirectives(const string& fname); + /** Forces the specified output object to print its items once */ + void ForceOutput(int idx=0); + /** Sets (or overrides) the output filename @param fname the name of the file to output data to @return true if successful, false if there is no output specified for the flight model */ @@ -514,7 +518,7 @@ public: @param rootDir the string containing the root directory. */ void SetRootDir(const string& rootDir) {RootDir = rootDir;} - /** Retrieves teh Root Directory. + /** Retrieves the Root Directory. @return the string representing the root (base) JSBSim directory. */ const string& GetRootDir(void) const {return RootDir;} diff --git a/src/FDM/JSBSim/initialization/FGInitialCondition.cpp b/src/FDM/JSBSim/initialization/FGInitialCondition.cpp index f9bbc5415..0dbcf88ce 100644 --- a/src/FDM/JSBSim/initialization/FGInitialCondition.cpp +++ b/src/FDM/JSBSim/initialization/FGInitialCondition.cpp @@ -61,7 +61,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.56 2011/01/23 12:13:44 bcoconni Exp $"; +static const char *IdSrc = "$Id: FGInitialCondition.cpp,v 1.59 2011/04/03 13:18:51 bcoconni Exp $"; static const char *IdHdr = ID_INITIALCONDITION; //****************************************************************************** @@ -700,6 +700,8 @@ void FGInitialCondition::SetAltitudeASLFtIC(double alt) case setve: SetVtrueFpsIC(ve0 * sqrt(rho/rhoSL)); break; + default: // Make the compiler stop complaining about missing enums + break; } position.SetRadius(alt + sea_level_radius); @@ -998,6 +1000,18 @@ bool FGInitialCondition::Load_v1(void) SetTargetNlfIC(document->FindElementValueAsNumber("targetNlf")); } + // 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() ); + + p = vOmegaLocal(eP); + q = vOmegaLocal(eR); + r = vOmegaLocal(eQ); + return result; } @@ -1032,7 +1046,7 @@ bool FGInitialCondition::Load_v2(void) if (position_el->FindElement("radius")) { position.SetRadius(position_el->FindElementValueAsNumberConvertTo("radius", "FT")); } else if (position_el->FindElement("altitudeAGL")) { - position.SetRadius(sea_level_radius + terrain_elevation + position_el->FindElementValueAsNumberConvertTo("altitude", "FT")); + position.SetRadius(sea_level_radius + terrain_elevation + position_el->FindElementValueAsNumberConvertTo("altitudeAGL", "FT")); } else if (position_el->FindElement("altitudeMSL")) { position.SetRadius(sea_level_radius + position_el->FindElementValueAsNumberConvertTo("altitudeMSL", "FT")); } else { @@ -1096,7 +1110,7 @@ bool FGInitialCondition::Load_v2(void) // // Or, using quaternions (note reverse ordering compared to matrix representation): // - // Q_b/l = Q_e/l * Q_b/i + // Q_b/l = Q_i/l * Q_b/i FGQuaternion QuatI2Body = FGQuaternion(vOrient); QuatI2Body.Normalize(); @@ -1196,6 +1210,9 @@ bool FGInitialCondition::Load_v2(void) FGColumnVector3 vLocalRate; Element* attrate_el = document->FindElement("attitude_rate"); + + // 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), @@ -1209,11 +1226,11 @@ bool FGInitialCondition::Load_v2(void) FGColumnVector3 vAttRate = attrate_el->FindElementTripletConvertTo("RAD/SEC"); if (frame == "eci") { - vLocalRate = Tl2b * (position.GetTi2l() * (vAttRate - vOmegaEarth) - vOmegaLocal); + vLocalRate = Tl2b * position.GetTi2l() * (vAttRate - vOmegaEarth); } else if (frame == "ecef") { - vLocalRate = Tl2b * (position.GetTec2l() * vAttRate - vOmegaLocal); + vLocalRate = Tl2b * position.GetTec2l() * vAttRate; } else if (frame == "local") { - vLocalRate = vAttRate; + vLocalRate = vAttRate + vOmegaLocal; } else if (!frame.empty()) { // misspelling of frame cerr << endl << fgred << " Attitude rate frame type: \"" << frame @@ -1221,11 +1238,11 @@ bool FGInitialCondition::Load_v2(void) result = false; } else if (frame.empty()) { - + vLocalRate = vOmegaLocal; } - + } else { // Body frame attitude rate assumed 0 relative to local. - vLocalRate.InitMatrix(); + vLocalRate = vOmegaLocal; } p = vLocalRate(eP); diff --git a/src/FDM/JSBSim/initialization/FGTrim.cpp b/src/FDM/JSBSim/initialization/FGTrim.cpp index 6b0cd81c0..cc863c85d 100644 --- a/src/FDM/JSBSim/initialization/FGTrim.cpp +++ b/src/FDM/JSBSim/initialization/FGTrim.cpp @@ -41,19 +41,10 @@ scheme. */ INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#include <cstdlib> #include <iomanip> #include "FGTrim.h" -#include "models/FGAtmosphere.h" -#include "FGInitialCondition.h" -#include "models/FGAircraft.h" -#include "models/FGMassBalance.h" #include "models/FGGroundReactions.h" #include "models/FGInertial.h" -#include "models/FGAerodynamics.h" -#include "models/FGPropulsion.h" -#include "models/propulsion/FGEngine.h" -#include "math/FGColumnVector3.h" #if _MSC_VER #pragma warning (disable : 4786 4788) @@ -63,7 +54,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGTrim.cpp,v 1.13 2010/04/23 17:23:40 dpculp Exp $"; +static const char *IdSrc = "$Id: FGTrim.cpp,v 1.15 2011/02/19 16:29:29 bcoconni Exp $"; static const char *IdHdr = ID_TRIM; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -241,7 +232,7 @@ bool FGTrim::DoTrim(void) { fdmex->DisableOutput(); - setEngineTrimMode(true); + fdmex->SetTrimStatus(true); fgic->SetPRadpsIC(0.0); fgic->SetQRadpsIC(0.0); @@ -358,7 +349,7 @@ bool FGTrim::DoTrim(void) { for(i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){ fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true); } - setEngineTrimMode(false); + fdmex->SetTrimStatus(false); fdmex->EnableOutput(); return !trim_failed; } @@ -625,15 +616,6 @@ void FGTrim::setDebug(void) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void FGTrim::setEngineTrimMode(bool mode) { - FGPropulsion* prop = fdmex->GetPropulsion(); - for (unsigned int i=0; i<prop->GetNumEngines(); i++) { - prop->GetEngine(i)->SetTrimMode(mode); - } -} - -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - void FGTrim::SetMode(TrimMode tt) { ClearStates(); mode=tt; diff --git a/src/FDM/JSBSim/initialization/FGTrim.h b/src/FDM/JSBSim/initialization/FGTrim.h index 0d6e3092c..518077f38 100644 --- a/src/FDM/JSBSim/initialization/FGTrim.h +++ b/src/FDM/JSBSim/initialization/FGTrim.h @@ -60,7 +60,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_TRIM "$Id: FGTrim.h,v 1.7 2010/04/23 17:23:40 dpculp Exp $" +#define ID_TRIM "$Id: FGTrim.h,v 1.8 2011/01/24 13:01:55 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -120,7 +120,7 @@ CLASS DOCUMENTATION @endcode @author Tony Peden - @version "$Id: FGTrim.h,v 1.7 2010/04/23 17:23:40 dpculp Exp $" + @version "$Id: FGTrim.h,v 1.8 2011/01/24 13:01:55 jberndt Exp $" */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -176,7 +176,6 @@ private: void setupTurn(void); void updateRates(void); - void setEngineTrimMode(bool mode); void setDebug(void); public: diff --git a/src/FDM/JSBSim/input_output/FGPropertyManager.cpp b/src/FDM/JSBSim/input_output/FGPropertyManager.cpp old mode 100644 new mode 100755 index c0218ec20..6d907924e --- a/src/FDM/JSBSim/input_output/FGPropertyManager.cpp +++ b/src/FDM/JSBSim/input_output/FGPropertyManager.cpp @@ -49,17 +49,17 @@ COMMENTS, REFERENCES, and NOTES [use "class documentation" below for API docs] namespace JSBSim { bool FGPropertyManager::suppress_warning = true; -std::vector<std::string> FGPropertyManager::tied_properties; +std::vector<SGPropertyNode_ptr> FGPropertyManager::tied_properties; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void FGPropertyManager::Unbind(void) { - vector<string>::iterator it; + vector<SGPropertyNode_ptr>::iterator it; + for (it = tied_properties.begin();it < tied_properties.end();it++) - { - Untie(*it); - } + (*it)->untie(); + tied_properties.clear(); } @@ -102,7 +102,6 @@ FGPropertyManager::GetNode (const string &relpath, int index, bool create) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - bool FGPropertyManager::HasNode (const string &path) { // Checking if a node exists shouldn't write a warning if it doesn't exist @@ -314,11 +313,17 @@ void FGPropertyManager::Untie (const string &name) void FGPropertyManager::Tie (const string &name, bool *pointer, bool useDefault) { - if (!tie(name.c_str(), SGRawValuePointer<bool>(pointer), useDefault)) + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + cerr << "Could not get or create property " << name << endl; + return; + } + + if (!property->tie(SGRawValuePointer<bool>(pointer), useDefault)) cerr << "Failed to tie property " << name << " to a pointer" << endl; else { - tied_properties.push_back(name); - if (debug_lvl & 0x20) std::cout << name << std::endl; + tied_properties.push_back(property); + if (debug_lvl & 0x20) cout << name << endl; } } @@ -327,11 +332,17 @@ void FGPropertyManager::Tie (const string &name, bool *pointer, bool useDefault) void FGPropertyManager::Tie (const string &name, int *pointer, bool useDefault ) { - if (!tie(name.c_str(), SGRawValuePointer<int>(pointer), useDefault)) + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + cerr << "Could not get or create property " << name << endl; + return; + } + + if (!property->tie(SGRawValuePointer<int>(pointer), useDefault)) cerr << "Failed to tie property " << name << " to a pointer" << endl; else { - tied_properties.push_back(name); - if (debug_lvl & 0x20) std::cout << name << std::endl; + tied_properties.push_back(property); + if (debug_lvl & 0x20) cout << name << endl; } } @@ -340,11 +351,17 @@ void FGPropertyManager::Tie (const string &name, int *pointer, void FGPropertyManager::Tie (const string &name, long *pointer, bool useDefault ) { - if (!tie(name.c_str(), SGRawValuePointer<long>(pointer), useDefault)) + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + cerr << "Could not get or create property " << name << endl; + return; + } + + if (!property->tie(SGRawValuePointer<long>(pointer), useDefault)) cerr << "Failed to tie property " << name << " to a pointer" << endl; else { - tied_properties.push_back(name); - if (debug_lvl & 0x20) std::cout << name << std::endl; + tied_properties.push_back(property); + if (debug_lvl & 0x20) cout << name << endl; } } @@ -353,11 +370,17 @@ void FGPropertyManager::Tie (const string &name, long *pointer, void FGPropertyManager::Tie (const string &name, float *pointer, bool useDefault ) { - if (!tie(name.c_str(), SGRawValuePointer<float>(pointer), useDefault)) + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + cerr << "Could not get or create property " << name << endl; + return; + } + + if (!property->tie(SGRawValuePointer<float>(pointer), useDefault)) cerr << "Failed to tie property " << name << " to a pointer" << endl; else { - tied_properties.push_back(name); - if (debug_lvl & 0x20) std::cout << name << std::endl; + tied_properties.push_back(property); + if (debug_lvl & 0x20) cout << name << endl; } } @@ -365,11 +388,17 @@ void FGPropertyManager::Tie (const string &name, float *pointer, void FGPropertyManager::Tie (const string &name, double *pointer, bool useDefault) { - if (!tie(name.c_str(), SGRawValuePointer<double>(pointer), useDefault)) + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + cerr << "Could not get or create property " << name << endl; + return; + } + + if (!property->tie(SGRawValuePointer<double>(pointer), useDefault)) cerr << "Failed to tie property " << name << " to a pointer" << endl; else { - tied_properties.push_back(name); - if (debug_lvl & 0x20) std::cout << name << std::endl; + tied_properties.push_back(property); + if (debug_lvl & 0x20) cout << name << endl; } } diff --git a/src/FDM/JSBSim/input_output/FGPropertyManager.h b/src/FDM/JSBSim/input_output/FGPropertyManager.h index a08f89681..f877bfcdd 100644 --- a/src/FDM/JSBSim/input_output/FGPropertyManager.h +++ b/src/FDM/JSBSim/input_output/FGPropertyManager.h @@ -53,7 +53,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PROPERTYMANAGER "$Id: FGPropertyManager.h,v 1.17 2010/07/08 11:36:28 jberndt Exp $" +#define ID_PROPERTYMANAGER "$Id: FGPropertyManager.h,v 1.20 2011/02/13 00:42:45 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -77,7 +77,7 @@ class FGPropertyManager : public SGPropertyNode, public FGJSBBase { private: static bool suppress_warning; - static std::vector<std::string> tied_properties; + static std::vector<SGPropertyNode_ptr> tied_properties; public: /// Constructor FGPropertyManager(void) {suppress_warning = false;} @@ -532,10 +532,16 @@ class FGPropertyManager : public SGPropertyNode, public FGJSBBase template <class V> inline void Tie (const std::string &name, V (*getter)(), void (*setter)(V) = 0, bool useDefault = true) { - if (!tie(name.c_str(), SGRawValueFunctions<V>(getter, setter), useDefault)) - std::cout << "Failed to tie property " << name << " to functions" << std::endl; + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + std::cerr << "Could not get or create property " << name << std::endl; + return; + } + + if (!property->tie(SGRawValueFunctions<V>(getter, setter), useDefault)) + std::cerr << "Failed to tie property " << name << " to functions" << std::endl; else { - tied_properties.push_back(name); + tied_properties.push_back(property); if (debug_lvl & 0x20) std::cout << name << std::endl; } } @@ -562,10 +568,16 @@ class FGPropertyManager : public SGPropertyNode, public FGJSBBase template <class V> inline void Tie (const std::string &name, int index, V (*getter)(int), void (*setter)(int, V) = 0, bool useDefault = true) { - if (!tie(name.c_str(), SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault)) - std::cout << "Failed to tie property " << name << " to indexed functions" << std::endl; + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + std::cerr << "Could not get or create property " << name << std::endl; + return; + } + + if (!property->tie(SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault)) + std::cerr << "Failed to tie property " << name << " to indexed functions" << std::endl; else { - tied_properties.push_back(name); + tied_properties.push_back(property); if (debug_lvl & 0x20) std::cout << name << std::endl; } } @@ -594,10 +606,16 @@ class FGPropertyManager : public SGPropertyNode, public FGJSBBase Tie (const std::string &name, T * obj, V (T::*getter)() const, void (T::*setter)(V) = 0, bool useDefault = true) { - if (!tie(name.c_str(), SGRawValueMethods<T,V>(*obj, getter, setter), useDefault)) - std::cout << "Failed to tie property " << name << " to object methods" << std::endl; + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + std::cerr << "Could not get or create property " << name << std::endl; + return; + } + + if (!property->tie(SGRawValueMethods<T,V>(*obj, getter, setter), useDefault)) + std::cerr << "Failed to tie property " << name << " to object methods" << std::endl; else { - tied_properties.push_back(name); + tied_properties.push_back(property); if (debug_lvl & 0x20) std::cout << name << std::endl; } } @@ -625,10 +643,16 @@ class FGPropertyManager : public SGPropertyNode, public FGJSBBase Tie (const std::string &name, T * obj, int index, V (T::*getter)(int) const, void (T::*setter)(int, V) = 0, bool useDefault = true) { - if (!tie(name.c_str(), SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault)) - std::cout << "Failed to tie property " << name << " to indexed object methods" << std::endl; + SGPropertyNode* property = getNode(name.c_str(), true); + if (!property) { + std::cerr << "Could not get or create property " << name << std::endl; + return; + } + + if (!property->tie(SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault)) + std::cerr << "Failed to tie property " << name << " to indexed object methods" << std::endl; else { - tied_properties.push_back(name); + tied_properties.push_back(property); if (debug_lvl & 0x20) std::cout << name << std::endl; } } diff --git a/src/FDM/JSBSim/input_output/FGScript.cpp b/src/FDM/JSBSim/input_output/FGScript.cpp old mode 100644 new mode 100755 index e20e8c1f2..60d39ed9b --- a/src/FDM/JSBSim/input_output/FGScript.cpp +++ b/src/FDM/JSBSim/input_output/FGScript.cpp @@ -54,7 +54,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGScript.cpp,v 1.43 2011/01/16 15:27:34 jberndt Exp $"; +static const char *IdSrc = "$Id: FGScript.cpp,v 1.46 2011/02/18 12:44:16 jberndt Exp $"; static const char *IdHdr = ID_FGSCRIPT; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -78,12 +78,19 @@ FGScript::FGScript(FGFDMExec* fgex) : FDMExec(fgex) FGScript::~FGScript() { - unsigned int i; + unsigned int i, j; - for (i=0; i<local_properties.size(); i++) delete local_properties[i]; + for (i=0; i<local_properties.size(); i++) { + delete local_properties[i]->value; + delete local_properties[i]; + } local_properties.clear(); - for (i=0; i<Events.size(); i++) delete Events[i].Condition; + for (i=0; i<Events.size(); i++) { + delete Events[i].Condition; + for (j=0; j<Events[i].Functions.size(); j++) + delete Events[i].Functions[j]; + } Events.clear(); Debug(1); @@ -139,6 +146,8 @@ bool FGScript::LoadScript(string script, double deltaT) StartTime = run_element->GetAttributeValueAsNumber("start"); FDMExec->Setsim_time(StartTime); EndTime = run_element->GetAttributeValueAsNumber("end"); + // Make sure that the desired time is reached and executed. + EndTime += 0.99*FDMExec->GetDeltaT(); if (deltaT == 0.0) dt = run_element->GetAttributeValueAsNumber("dt"); @@ -240,11 +249,13 @@ bool FGScript::LoadScript(string script, double deltaT) newCondition = new FGCondition(condition_element, PropertyManager); } catch(string str) { cout << endl << fgred << str << reset << endl << endl; + delete newEvent; return false; } newEvent->Condition = newCondition; } else { cerr << "No condition specified in script event " << newEvent->Name << endl; + delete newEvent; return false; } @@ -258,16 +269,29 @@ bool FGScript::LoadScript(string script, double deltaT) // Notify about when this event is triggered? if ((notify_element = event_element->FindElement("notify")) != 0) { newEvent->Notify = true; + // Check here for new <description> tag that gets echoed + string notify_description = notify_element->FindElementValue("description"); + if (!notify_description.empty()) { + newEvent->Description = notify_description; + } notify_property_element = notify_element->FindElement("property"); while (notify_property_element) { notifyPropertyName = notify_property_element->GetDataLine(); if (PropertyManager->GetNode(notifyPropertyName)) { newEvent->NotifyProperties.push_back( PropertyManager->GetNode(notifyPropertyName) ); + string caption_attribute = notify_property_element->GetAttributeValue("caption"); + if (caption_attribute.empty()) { + newEvent->DisplayString.push_back(notifyPropertyName); + } else { + newEvent->DisplayString.push_back(caption_attribute); + } } else { cout << endl << fgred << " Could not find the property named " << notifyPropertyName << " in script" << endl << " \"" << ScriptName << "\". Execution is aborted. Please recheck " << "your input files and scripts." << reset << endl; + delete newEvent->Condition; + delete newEvent; return false; } notify_property_element = notify_element->FindNextElement("property"); @@ -339,7 +363,7 @@ bool FGScript::RunScript(void) double currentTime = FDMExec->GetSimTime(); double newSetValue = 0; - if (currentTime > EndTime) return false; //Script done! + if (currentTime > EndTime) return false; // Iterate over all events. for (unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) { @@ -426,8 +450,12 @@ bool FGScript::RunScript(void) if (Events[ev_ctr].Notify && !Events[ev_ctr].Notified) { cout << endl << " Event " << event_ctr << " (" << Events[ev_ctr].Name << ")" << " executed at time: " << currentTime << endl; + if (!Events[ev_ctr].Description.empty()) { + cout << " " << Events[ev_ctr].Description << endl; + } for (j=0; j<Events[ev_ctr].NotifyProperties.size();j++) { - cout << " " << Events[ev_ctr].NotifyProperties[j]->GetRelativeName() +// cout << " " << Events[ev_ctr].NotifyProperties[j]->GetRelativeName() + cout << " " << Events[ev_ctr].DisplayString[j] << " = " << Events[ev_ctr].NotifyProperties[j]->getDoubleValue() << endl; } cout << endl; diff --git a/src/FDM/JSBSim/input_output/FGScript.h b/src/FDM/JSBSim/input_output/FGScript.h index 9f8d3ef8e..1ef0cfaa3 100644 --- a/src/FDM/JSBSim/input_output/FGScript.h +++ b/src/FDM/JSBSim/input_output/FGScript.h @@ -48,7 +48,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_FGSCRIPT "$Id: FGScript.h,v 1.18 2010/04/11 13:44:42 jberndt Exp $" +#define ID_FGSCRIPT "$Id: FGScript.h,v 1.20 2011/02/11 12:43:28 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -157,7 +157,7 @@ CLASS DOCUMENTATION comes the "run" section, where the conditions are described in "event" clauses.</p> @author Jon S. Berndt - @version "$Id: FGScript.h,v 1.18 2010/04/11 13:44:42 jberndt Exp $" + @version "$Id: FGScript.h,v 1.20 2011/02/11 12:43:28 jberndt Exp $" */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -178,7 +178,7 @@ public: has been supplied on the command line, it will be override the script- specified simulation step size. @param script the filename (including path name, if any) for the script. - @param deltaT a simulation step size from the command line + @param deltaT a simulation step size. @return true if successful */ bool LoadScript(string script, double deltaT); @@ -215,8 +215,10 @@ private: double StartTime; double TimeSpan; string Name; + string Description; vector <FGPropertyManager*> SetParam; vector <FGPropertyManager*> NotifyProperties; + vector <string> DisplayString; vector <eAction> Action; vector <eType> Type; vector <double> SetValue; diff --git a/src/FDM/JSBSim/input_output/FGXMLElement.cpp b/src/FDM/JSBSim/input_output/FGXMLElement.cpp old mode 100644 new mode 100755 index 832b2c8c6..383603d32 --- a/src/FDM/JSBSim/input_output/FGXMLElement.cpp +++ b/src/FDM/JSBSim/input_output/FGXMLElement.cpp @@ -42,7 +42,7 @@ FORWARD DECLARATIONS namespace JSBSim { -static const char *IdSrc = "$Id: FGXMLElement.cpp,v 1.31 2010/09/29 02:22:03 jberndt Exp $"; +static const char *IdSrc = "$Id: FGXMLElement.cpp,v 1.32 2011/02/13 00:42:45 jberndt Exp $"; static const char *IdHdr = ID_XMLELEMENT; bool Element::converterIsInitialized = false; @@ -64,6 +64,8 @@ Element::Element(const string& nm) // Length convert["M"]["FT"] = 3.2808399; convert["FT"]["M"] = 1.0/convert["M"]["FT"]; + convert["KM"]["FT"] = 3280.8399; + convert["FT"]["KM"] = 1.0/convert["KM"]["FT"]; convert["FT"]["IN"] = 12.0; convert["IN"]["FT"] = 1.0/convert["FT"]["IN"]; convert["IN"]["M"] = convert["IN"]["FT"] * convert["FT"]["M"]; @@ -121,6 +123,8 @@ Element::Element(const string& nm) convert["FT/S"]["M/S"] = 1.0/convert["M/S"]["FT/S"]; convert["M/SEC"]["FT/SEC"] = 3.2808399; convert["FT/SEC"]["M/SEC"] = 1.0/convert["M/SEC"]["FT/SEC"]; + convert["KM/SEC"]["FT/SEC"] = 3280.8399; + convert["FT/SEC"]["KM/SEC"] = 1.0/convert["KM/SEC"]["FT/SEC"]; // Torque convert["FT*LBS"]["N*M"] = 1.35581795; convert["N*M"]["FT*LBS"] = 1/convert["FT*LBS"]["N*M"]; @@ -153,6 +157,7 @@ Element::Element(const string& nm) // Length convert["M"]["M"] = 1.00; + convert["KM"]["KM"] = 1.00; convert["FT"]["FT"] = 1.00; convert["IN"]["IN"] = 1.00; // Area @@ -195,6 +200,7 @@ Element::Element(const string& nm) convert["KTS"]["KTS"] = 1.00; convert["M/S"]["M/S"] = 1.0; convert["M/SEC"]["M/SEC"] = 1.0; + convert["KM/SEC"]["KM/SEC"] = 1.0; // Torque convert["FT*LBS"]["FT*LBS"] = 1.00; convert["N*M"]["N*M"] = 1.00; diff --git a/src/FDM/JSBSim/math/FGCondition.cpp b/src/FDM/JSBSim/math/FGCondition.cpp index 2a1ffd47f..719a3e79d 100644 --- a/src/FDM/JSBSim/math/FGCondition.cpp +++ b/src/FDM/JSBSim/math/FGCondition.cpp @@ -35,6 +35,7 @@ INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include "FGCondition.h" +#include "FGPropertyValue.h" #include "input_output/FGXMLElement.h" #include "input_output/FGPropertyManager.h" #include <iostream> @@ -44,7 +45,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGCondition.cpp,v 1.13 2010/07/14 05:50:40 ehofman Exp $"; +static const char *IdSrc = "$Id: FGCondition.cpp,v 1.14 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_CONDITION; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -122,12 +123,11 @@ FGCondition::FGCondition(const string& test, FGPropertyManager* PropertyManager) exit(-1); } - TestParam1 = PropertyManager->GetNode(property1, false); - if (!TestParam1) { - cerr << fgred << " In condition: " << test << ". Unknown property " - << property1 << " referenced." << endl - << "Creating property. Check usage." << reset << endl; - TestParam1 = PropertyManager->GetNode(property1, true); + FGPropertyManager *node = PropertyManager->GetNode(property1, false); + if (node) { + TestParam1 = new FGPropertyValue(node); + } else { + TestParam1 = new FGPropertyValue(property1, PropertyManager); } Comparison = mComparison[conditional]; if (Comparison == ecUndef) { @@ -136,12 +136,11 @@ FGCondition::FGCondition(const string& test, FGPropertyManager* PropertyManager) if (is_number(property2)) { TestValue = atof(property2.c_str()); } else { - TestParam2 = PropertyManager->GetNode(property2, false); - if (!TestParam2) { - cerr << fgred << " In condition: " << test << ". Unknown property " - << property2 << " referenced." << endl - << "Creating property. Check usage." << reset << endl; - TestParam2 = PropertyManager->GetNode(property2, true); + node = PropertyManager->GetNode(property2, false); + if (node) { + TestParam2 = new FGPropertyValue(node); + } else { + TestParam2 = new FGPropertyValue(property2, PropertyManager); } } } @@ -174,6 +173,8 @@ void FGCondition::InitializeConditionals(void) FGCondition::~FGCondition(void) { + delete TestParam1; + delete TestParam2; for (unsigned int i=0; i<conditions.size(); i++) delete conditions[i]; Debug(1); @@ -267,11 +268,11 @@ void FGCondition::PrintCondition(void ) } else { if (TestParam2 != 0L) - cout << " " << TestParam1->GetRelativeName() << " " + cout << " " << TestParam1->GetName() << " " << conditional << " " - << TestParam2->GetRelativeName(); + << TestParam2->GetName(); else - cout << " " << TestParam1->GetRelativeName() << " " + cout << " " << TestParam1->GetName() << " " << conditional << " " << TestValue; } } diff --git a/src/FDM/JSBSim/math/FGCondition.h b/src/FDM/JSBSim/math/FGCondition.h index 2cd75bdfb..06632a76d 100644 --- a/src/FDM/JSBSim/math/FGCondition.h +++ b/src/FDM/JSBSim/math/FGCondition.h @@ -44,7 +44,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_CONDITION "$Id: FGCondition.h,v 1.5 2009/10/24 22:59:30 jberndt Exp $" +#define ID_CONDITION "$Id: FGCondition.h,v 1.6 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -53,6 +53,7 @@ FORWARD DECLARATIONS namespace JSBSim { class FGPropertyManager; +class FGPropertyValue; class Element; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -82,7 +83,8 @@ private: std::map <std::string, eComparison> mComparison; eLogic Logic; - FGPropertyManager *TestParam1, *TestParam2, *PropertyManager; + FGPropertyManager *PropertyManager; + FGPropertyValue *TestParam1, *TestParam2; double TestValue; eComparison Comparison; bool isGroup; diff --git a/src/FDM/JSBSim/math/FGFunction.cpp b/src/FDM/JSBSim/math/FGFunction.cpp old mode 100644 new mode 100755 index 9ad7c7a99..c0d2e6afd --- a/src/FDM/JSBSim/math/FGFunction.cpp +++ b/src/FDM/JSBSim/math/FGFunction.cpp @@ -43,7 +43,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGFunction.cpp,v 1.35 2010/08/28 12:41:56 jberndt Exp $"; +static const char *IdSrc = "$Id: FGFunction.cpp,v 1.36 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_FUNCTION; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -177,9 +177,10 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr newNode = PropertyManager->GetNode(property_name); Parameters.push_back(new FGPropertyValue( newNode )); } else { - cerr << fgcyan << "The property " + property_name + " is initially undefined." + cerr << fgcyan << "Warning: The property " + property_name + " is initially undefined." << reset << endl; - Parameters.push_back(new FGPropertyValue( property_name )); + Parameters.push_back(new FGPropertyValue( property_name, + PropertyManager )); } } else if (operation == value_string || operation == v_string) { Parameters.push_back(new FGRealValue(element->GetDataAsNumber())); @@ -252,17 +253,8 @@ double FGFunction::GetValue(void) const if (cached) return cachedValue; - try { - temp = Parameters[0]->GetValue(); - } catch (string prop) { - if (PropertyManager->HasNode(prop)) { - ((FGPropertyValue*)Parameters[0])->SetNode(PropertyManager->GetNode(prop)); - temp = Parameters[0]->GetValue(); - } else { - throw("Property " + prop + " was not defined anywhere."); - } - } - + temp = Parameters[0]->GetValue(); + switch (Type) { case eTopLevel: break; diff --git a/src/FDM/JSBSim/math/FGParameter.h b/src/FDM/JSBSim/math/FGParameter.h old mode 100644 new mode 100755 index 85b43b1af..c1d66fc20 --- a/src/FDM/JSBSim/math/FGParameter.h +++ b/src/FDM/JSBSim/math/FGParameter.h @@ -40,7 +40,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PARAMETER "$Id: FGParameter.h,v 1.5 2009/08/30 03:51:28 jberndt Exp $" +#define ID_PARAMETER "$Id: FGParameter.h,v 1.6 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -65,6 +65,10 @@ class FGParameter : public FGJSBBase public: virtual ~FGParameter(void) {}; virtual double GetValue(void) const = 0; + virtual std::string GetName(void) const = 0; + + // SGPropertyNode impersonation. + double getDoubleValue(void) const { return GetValue(); } protected: }; diff --git a/src/FDM/JSBSim/math/FGPropertyValue.cpp b/src/FDM/JSBSim/math/FGPropertyValue.cpp old mode 100644 new mode 100755 index 0824a607b..7f448da3c --- a/src/FDM/JSBSim/math/FGPropertyValue.cpp +++ b/src/FDM/JSBSim/math/FGPropertyValue.cpp @@ -6,6 +6,7 @@ Date started: 12/10/2004 Purpose: Stores property values ------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) ------------- + ------ Copyright (C) 2010 - 2011 Anders Gidenstam (anders(at)gidenstam.org) - This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -32,36 +33,53 @@ INCLUDES namespace JSBSim { -static const char *IdSrc = "$Id: FGPropertyValue.cpp,v 1.6 2010/08/24 10:30:14 jberndt Exp $"; +static const char *IdSrc = "$Id: FGPropertyValue.cpp,v 1.7 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_PROPERTYVALUE; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -FGPropertyValue::FGPropertyValue(FGPropertyManager* propNode) : PropertyManager(propNode) +FGPropertyValue::FGPropertyValue(FGPropertyManager* propNode) + : PropertyManager(0L), PropertyNode(propNode) { } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -FGPropertyValue::FGPropertyValue(std::string propName) : PropertyManager(0L) +FGPropertyValue::FGPropertyValue(std::string propName, FGPropertyManager* propertyManager) + : PropertyManager(propertyManager), PropertyNode(0L), PropertyName(propName) { - PropertyName = propName; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% double FGPropertyValue::GetValue(void) const { - double val; - try { - val = PropertyManager->getDoubleValue(); - } catch (...) { - throw(PropertyName); + FGPropertyManager* node = PropertyNode; + + if (!PropertyNode) { + // The node cannot be cached since this is a const method. + node = PropertyManager->GetNode(PropertyName); + + if (!node) { + throw(std::string("FGPropertyValue::GetValue() The property " + + PropertyName + " does not exist.")); + } } - return val; + return node->getDoubleValue(); +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +std::string FGPropertyValue::GetName(void) const +{ + if (PropertyNode) { + return PropertyNode->GetName(); + } else { + return PropertyName; + } } } diff --git a/src/FDM/JSBSim/math/FGPropertyValue.h b/src/FDM/JSBSim/math/FGPropertyValue.h old mode 100644 new mode 100755 index 753df7c62..c716cf599 --- a/src/FDM/JSBSim/math/FGPropertyValue.h +++ b/src/FDM/JSBSim/math/FGPropertyValue.h @@ -5,6 +5,7 @@ Author: Jon Berndt Date started: December 10 2004 ------------- Copyright (C) 2001 Jon S. Berndt (jon@jsbsim.org) ------------- + ------ Copyright (C) 2010 - 2011 Anders Gidenstam (anders(at)gidenstam.org) - This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -41,7 +42,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PROPERTYVALUE "$Id: FGPropertyValue.h,v 1.8 2010/08/24 10:30:14 jberndt Exp $" +#define ID_PROPERTYVALUE "$Id: FGPropertyValue.h,v 1.9 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -53,8 +54,8 @@ namespace JSBSim { CLASS DOCUMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ - /** Represents a property value - @author Jon Berndt + /** Represents a property value which can use late binding. + @author Jon Berndt, Anders Gidenstam */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -66,14 +67,17 @@ class FGPropertyValue : public FGParameter public: FGPropertyValue(FGPropertyManager* propNode); - FGPropertyValue(std::string propName); + FGPropertyValue(std::string propName, FGPropertyManager* propertyManager); ~FGPropertyValue() {}; double GetValue(void) const; - void SetNode(FGPropertyManager* node) {PropertyManager = node;} + void SetNode(FGPropertyManager* node) {PropertyNode = node;} + + std::string GetName(void) const; private: - FGPropertyManager* PropertyManager; + FGPropertyManager* PropertyManager; // Property root used to do late binding. + FGPropertyManager* PropertyNode; std::string PropertyName; }; diff --git a/src/FDM/JSBSim/math/FGRealValue.h b/src/FDM/JSBSim/math/FGRealValue.h index 277c595c1..9564eabf7 100644 --- a/src/FDM/JSBSim/math/FGRealValue.h +++ b/src/FDM/JSBSim/math/FGRealValue.h @@ -40,7 +40,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_REALVALUE "$Id: FGRealValue.h,v 1.4 2009/08/30 03:51:28 jberndt Exp $" +#define ID_REALVALUE "$Id: FGRealValue.h,v 1.5 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -68,6 +68,7 @@ public: ~FGRealValue() {}; double GetValue(void) const; + std::string GetName(void) const {return "constant";} private: double Value; diff --git a/src/FDM/JSBSim/math/FGTable.h b/src/FDM/JSBSim/math/FGTable.h index 2f2e271cd..3d2224981 100644 --- a/src/FDM/JSBSim/math/FGTable.h +++ b/src/FDM/JSBSim/math/FGTable.h @@ -47,7 +47,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_TABLE "$Id: FGTable.h,v 1.12 2010/09/16 11:01:24 jberndt Exp $" +#define ID_TABLE "$Id: FGTable.h,v 1.13 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -233,7 +233,7 @@ combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio @endcode @author Jon S. Berndt -@version $Id: FGTable.h,v 1.12 2010/09/16 11:01:24 jberndt Exp $ +@version $Id: FGTable.h,v 1.13 2011/04/05 20:20:21 andgi Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -292,6 +292,8 @@ public: void Print(void); + std::string GetName(void) const {return Name;} + private: enum type {tt1D, tt2D, tt3D} Type; enum axis {eRow=0, eColumn, eTable}; diff --git a/src/FDM/JSBSim/models/FGAerodynamics.cpp b/src/FDM/JSBSim/models/FGAerodynamics.cpp index 21583363b..0f08c0160 100644 --- a/src/FDM/JSBSim/models/FGAerodynamics.cpp +++ b/src/FDM/JSBSim/models/FGAerodynamics.cpp @@ -52,7 +52,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGAerodynamics.cpp,v 1.36 2011/01/19 12:41:19 jberndt Exp $"; +static const char *IdSrc = "$Id: FGAerodynamics.cpp,v 1.37 2011/03/11 13:02:26 jberndt Exp $"; static const char *IdHdr = ID_AERODYNAMICS; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -80,7 +80,7 @@ FGAerodynamics::FGAerodynamics(FGFDMExec* FDMExec) : FGModel(FDMExec) axisType = atNone; - Coeff = new CoeffArray[6]; + AeroFunctions = new AeroFunctionArray[6]; impending_stall = stall_hyst = 0.0; alphaclmin = alphaclmax = 0.0; @@ -103,10 +103,10 @@ FGAerodynamics::~FGAerodynamics() unsigned int i,j; for (i=0; i<6; i++) - for (j=0; j<Coeff[i].size(); j++) - delete Coeff[i][j]; + for (j=0; j<AeroFunctions[i].size(); j++) + delete AeroFunctions[i][j]; - delete[] Coeff; + delete[] AeroFunctions; delete AeroRPShift; @@ -142,7 +142,7 @@ bool FGAerodynamics::Run(void) const double alpha=FDMExec->GetAuxiliary()->Getalpha(); const double twovel=2*FDMExec->GetAuxiliary()->GetVt(); const double qbar = FDMExec->GetAuxiliary()->Getqbar(); - const double wingarea = FDMExec->GetAircraft()->GetWingArea(); + const double wingarea = FDMExec->GetAircraft()->GetWingArea(); // TODO: Make these constants constant! const double wingspan = FDMExec->GetAircraft()->GetWingSpan(); const double wingchord = FDMExec->GetAircraft()->Getcbar(); const double wingincidence = FDMExec->GetAircraft()->GetWingIncidence(); @@ -177,8 +177,8 @@ bool FGAerodynamics::Run(void) vFnative.InitMatrix(); for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) { - for (ctr=0; ctr < Coeff[axis_ctr].size(); ctr++) { - vFnative(axis_ctr+1) += Coeff[axis_ctr][ctr]->GetValue(); + for (ctr=0; ctr < AeroFunctions[axis_ctr].size(); ctr++) { + vFnative(axis_ctr+1) += AeroFunctions[axis_ctr][ctr]->GetValue(); } } @@ -224,8 +224,8 @@ bool FGAerodynamics::Run(void) vMoments = vDXYZcg*vForces; // M = r X F for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) { - for (ctr = 0; ctr < Coeff[axis_ctr+3].size(); ctr++) { - vMoments(axis_ctr+1) += Coeff[axis_ctr+3][ctr]->GetValue(); + for (ctr = 0; ctr < AeroFunctions[axis_ctr+3].size(); ctr++) { + vMoments(axis_ctr+1) += AeroFunctions[axis_ctr+3][ctr]->GetValue(); } } @@ -349,7 +349,7 @@ bool FGAerodynamics::Load(Element *element) axis_element = document->FindElement("axis"); while (axis_element) { - CoeffArray ca; + AeroFunctionArray ca; axis = axis_element->GetAttributeValue("name"); function_element = axis_element->FindElement("function"); while (function_element) { @@ -363,7 +363,7 @@ bool FGAerodynamics::Load(Element *element) } function_element = axis_element->FindNextElement("function"); } - Coeff[AxisIdx[axis]] = ca; + AeroFunctions[AxisIdx[axis]] = ca; axis_element = document->FindNextElement("axis"); } @@ -427,35 +427,35 @@ void FGAerodynamics::DetermineAxisSystem() //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -string FGAerodynamics::GetCoefficientStrings(const string& delimeter) const +string FGAerodynamics::GetAeroFunctionStrings(const string& delimeter) const { - string CoeffStrings = ""; + string AeroFunctionStrings = ""; bool firstime = true; unsigned int axis, sd; for (axis = 0; axis < 6; axis++) { - for (sd = 0; sd < Coeff[axis].size(); sd++) { + for (sd = 0; sd < AeroFunctions[axis].size(); sd++) { if (firstime) { firstime = false; } else { - CoeffStrings += delimeter; + AeroFunctionStrings += delimeter; } - CoeffStrings += Coeff[axis][sd]->GetName(); + AeroFunctionStrings += AeroFunctions[axis][sd]->GetName(); } } - return CoeffStrings; + return AeroFunctionStrings; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -string FGAerodynamics::GetCoefficientValues(const string& delimeter) const +string FGAerodynamics::GetAeroFunctionValues(const string& delimeter) const { ostringstream buf; for (unsigned int axis = 0; axis < 6; axis++) { - for (unsigned int sd = 0; sd < Coeff[axis].size(); sd++) { + for (unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) { if (buf.tellp() > 0) buf << delimeter; - buf << setw(9) << Coeff[axis][sd]->GetValue(); + buf << setw(9) << AeroFunctions[axis][sd]->GetValue(); } } diff --git a/src/FDM/JSBSim/models/FGAerodynamics.h b/src/FDM/JSBSim/models/FGAerodynamics.h index a5e278195..c6638b411 100644 --- a/src/FDM/JSBSim/models/FGAerodynamics.h +++ b/src/FDM/JSBSim/models/FGAerodynamics.h @@ -52,7 +52,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_AERODYNAMICS "$Id: FGAerodynamics.h,v 1.21 2010/11/18 12:38:06 jberndt Exp $" +#define ID_AERODYNAMICS "$Id: FGAerodynamics.h,v 1.22 2011/03/11 13:02:26 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -89,7 +89,7 @@ CLASS DOCUMENTATION {function contents} </function> <axis name="{LIFT | DRAG | SIDE | ROLL | PITCH | YAW}"> - {force coefficient definitions} + {force or moment definitions} </axis> {additional axis definitions} </aerodynamics> @@ -103,13 +103,13 @@ CLASS DOCUMENTATION <br> 2) Axial-Normal coordinate system: @code - <axis name="{AXIAL | NORMAL}"> + <axis name="{AXIAL | NORMAL | SIDE}"> @endcode <br> Systems may NOT be combined, or a load error will occur. @author Jon S. Berndt, Tony Peden - @version $Revision: 1.21 $ + @version $Revision: 1.22 $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -186,16 +186,16 @@ public: void SetAlphaCLMax(double tt) { alphaclmax=tt; } void SetAlphaCLMin(double tt) { alphaclmin=tt; } - /** Gets the strings for the current set of coefficients. + /** Gets the strings for the current set of aero functions. @param delimeter either a tab or comma string depending on output type - @return a string containing the descriptive names for all coefficients */ - std::string GetCoefficientStrings(const std::string& delimeter) const; + @return a string containing the descriptive names for all aero functions */ + std::string GetAeroFunctionStrings(const std::string& delimeter) const; - /** Gets the coefficient values. + /** Gets the aero function values. @param delimeter either a tab or comma string depending on output type @return a string containing the numeric values for the current set of - coefficients */ - std::string GetCoefficientValues(const std::string& delimeter) const; + aero functions */ + std::string GetAeroFunctionValues(const std::string& delimeter) const; /** Calculates and returns the wind-to-body axis transformation matrix. @return a reference to the wind-to-body transformation matrix. @@ -207,15 +207,15 @@ public: */ FGMatrix33& GetTb2w(void); - std::vector <FGFunction*> * GetCoeff(void) const { return Coeff; } + std::vector <FGFunction*> * GetAeroFunctions(void) const { return AeroFunctions; } private: enum eAxisType {atNone, atLiftDrag, atAxialNormal, atBodyXYZ} axisType; typedef std::map<std::string,int> AxisIndex; AxisIndex AxisIdx; FGFunction* AeroRPShift; - typedef vector <FGFunction*> CoeffArray; - CoeffArray* Coeff; + typedef vector <FGFunction*> AeroFunctionArray; + AeroFunctionArray* AeroFunctions; FGColumnVector3 vFnative; FGColumnVector3 vFw; FGColumnVector3 vForces; diff --git a/src/FDM/JSBSim/models/FGAtmosphere.cpp b/src/FDM/JSBSim/models/FGAtmosphere.cpp index f40fabcb5..992e7952e 100644 --- a/src/FDM/JSBSim/models/FGAtmosphere.cpp +++ b/src/FDM/JSBSim/models/FGAtmosphere.cpp @@ -61,7 +61,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGAtmosphere.cpp,v 1.41 2010/11/30 12:19:57 jberndt Exp $"; +static const char *IdSrc = "$Id: FGAtmosphere.cpp,v 1.42 2011/02/18 12:44:16 jberndt Exp $"; static const char *IdHdr = ID_ATMOSPHERE; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -124,6 +124,7 @@ FGAtmosphere::FGAtmosphere(FGFDMExec* fdmex) : FGModel(fdmex) FGAtmosphere::~FGAtmosphere() { + delete(POE_Table); Debug(1); } diff --git a/src/FDM/JSBSim/models/FGAuxiliary.cpp b/src/FDM/JSBSim/models/FGAuxiliary.cpp old mode 100644 new mode 100755 index 1dd3e2207..36c1e084a --- a/src/FDM/JSBSim/models/FGAuxiliary.cpp +++ b/src/FDM/JSBSim/models/FGAuxiliary.cpp @@ -59,7 +59,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGAuxiliary.cpp,v 1.45 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGAuxiliary.cpp,v 1.47 2011/03/29 11:49:27 jberndt Exp $"; static const char *IdHdr = ID_AUXILIARY; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -180,33 +180,30 @@ bool FGAuxiliary::Run() vAeroUVW = vUVW - wind; Vt = vAeroUVW.Magnitude(); + double Vt2 = Vt*Vt; + alpha = beta = adot = bdot = 0; + double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW)); + if ( Vt > 1.0 ) { if (vAeroUVW(eW) != 0.0) alpha = vAeroUVW(eU)*vAeroUVW(eU) > 0.0 ? atan2(vAeroUVW(eW), vAeroUVW(eU)) : 0.0; if (vAeroUVW(eV) != 0.0) - beta = vAeroUVW(eU)*vAeroUVW(eU)+vAeroUVW(eW)*vAeroUVW(eW) > 0.0 ? atan2(vAeroUVW(eV), - sqrt(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW))) : 0.0; + beta = mUW > 0.0 ? atan2(vAeroUVW(eV), sqrt(mUW)) : 0.0; - double mUW = (vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW)); double signU=1; if (vAeroUVW(eU) < 0.0) signU=-1; - if ( mUW < 1.0 ) { - adot = 0.0; - bdot = 0.0; - } else { + if ( mUW >= 1.0 ) { adot = (vAeroUVW(eU)*vUVWdot(eW) - vAeroUVW(eW)*vUVWdot(eU))/mUW; - bdot = (signU*mUW*vUVWdot(eV) - vAeroUVW(eV)*(vAeroUVW(eU)*vUVWdot(eU) - + vAeroUVW(eW)*vUVWdot(eW)))/(Vt*Vt*sqrt(mUW)); + bdot = (signU*mUW*vUVWdot(eV) + - vAeroUVW(eV)*(vAeroUVW(eU)*vUVWdot(eU) + vAeroUVW(eW)*vUVWdot(eW)))/(Vt2*sqrt(mUW)); } - } else { - alpha = beta = adot = bdot = 0; } Re = Vt * FDMExec->GetAircraft()->Getcbar() / FDMExec->GetAtmosphere()->GetKinematicViscosity(); - qbar = 0.5*density*Vt*Vt; - qbarUW = 0.5*density*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eW)*vAeroUVW(eW)); + qbar = 0.5*density*Vt2; + qbarUW = 0.5*density*(mUW); qbarUV = 0.5*density*(vAeroUVW(eU)*vAeroUVW(eU) + vAeroUVW(eV)*vAeroUVW(eV)); Mach = Vt / soundspeed; MachU = vMachUVW(eU) = vAeroUVW(eU) / soundspeed; @@ -291,6 +288,7 @@ bool FGAuxiliary::Run() // // A positive headwind is blowing with you, a negative headwind is blowing against you. // psi is the direction the wind is blowing *towards*. +// ToDo: should this simply be in the atmosphere class? Same with Get Crosswind. double FGAuxiliary::GetHeadWind(void) const { diff --git a/src/FDM/JSBSim/models/FGBuoyantForces.cpp b/src/FDM/JSBSim/models/FGBuoyantForces.cpp index b3808d70f..d12777478 100644 --- a/src/FDM/JSBSim/models/FGBuoyantForces.cpp +++ b/src/FDM/JSBSim/models/FGBuoyantForces.cpp @@ -45,7 +45,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGBuoyantForces.cpp,v 1.14 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGBuoyantForces.cpp,v 1.16 2011/03/23 11:58:29 jberndt Exp $"; static const char *IdHdr = ID_BUOYANTFORCES; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -213,13 +213,13 @@ string FGBuoyantForces::GetBuoyancyStrings(string delimeter) } for (axis = 0; axis < 6; axis++) { - for (sd = 0; sd < Coeff[axis].size(); sd++) { + for (sd = 0; sd < AeroFunctions[axis].size(); sd++) { if (firstime) { firstime = false; } else { CoeffStrings += delimeter; } - CoeffStrings += Coeff[axis][sd]->GetName(); + CoeffStrings += AeroFunctions[axis][sd]->GetName(); } } */ @@ -243,13 +243,13 @@ string FGBuoyantForces::GetBuoyancyValues(string delimeter) } for (unsigned int axis = 0; axis < 6; axis++) { - for (unsigned int sd = 0; sd < Coeff[axis].size(); sd++) { + for (unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) { if (firstime) { firstime = false; } else { SDValues += delimeter; } - SDValues += Coeff[axis][sd]->GetValueAsString(); + SDValues += AeroFunctions[axis][sd]->GetValueAsString(); } } */ @@ -260,19 +260,20 @@ string FGBuoyantForces::GetBuoyancyValues(string delimeter) void FGBuoyantForces::bind(void) { - typedef double (FGBuoyantForces::*PMF)(int) const; + typedef double (FGBuoyantForces::*PGF)(int) const; + typedef void (FGBuoyantForces::*PSF)(int, double); PropertyManager->Tie("moments/l-buoyancy-lbsft", this, eL, - (PMF)&FGBuoyantForces::GetMoments); + (PGF)&FGBuoyantForces::GetMoments, (PSF)0, false); PropertyManager->Tie("moments/m-buoyancy-lbsft", this, eM, - (PMF)&FGBuoyantForces::GetMoments); + (PGF)&FGBuoyantForces::GetMoments, (PSF)0, false); PropertyManager->Tie("moments/n-buoyancy-lbsft", this, eN, - (PMF)&FGBuoyantForces::GetMoments); + (PGF)&FGBuoyantForces::GetMoments, (PSF)0, false); PropertyManager->Tie("forces/fbx-buoyancy-lbs", this, eX, - (PMF)&FGBuoyantForces::GetForces); + (PGF)&FGBuoyantForces::GetForces, (PSF)0, false); PropertyManager->Tie("forces/fby-buoyancy-lbs", this, eY, - (PMF)&FGBuoyantForces::GetForces); + (PGF)&FGBuoyantForces::GetForces, (PSF)0, false); PropertyManager->Tie("forces/fbz-buoyancy-lbs", this, eZ, - (PMF)&FGBuoyantForces::GetForces); + (PGF)&FGBuoyantForces::GetForces, (PSF)0, false); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/FDM/JSBSim/models/FGFCS.cpp b/src/FDM/JSBSim/models/FGFCS.cpp index f0b77007d..a3b7e91bc 100644 --- a/src/FDM/JSBSim/models/FGFCS.cpp +++ b/src/FDM/JSBSim/models/FGFCS.cpp @@ -63,7 +63,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGFCS.cpp,v 1.72 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGFCS.cpp,v 1.73 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_FCS; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -182,17 +182,6 @@ bool FGFCS::InitModel(void) return true; } - -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -void FGFCS::LateBind(void) -{ - unsigned int i; - - for (i=0; i<Systems.size(); i++) Systems[i]->LateBind(); - for (i=0; i<APComponents.size(); i++) APComponents[i]->LateBind(); - for (i=0; i<FCSComponents.size(); i++) FCSComponents[i]->LateBind(); -} //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // Notes: In this logic the default engine commands are set. This is simply a diff --git a/src/FDM/JSBSim/models/FGFCS.h b/src/FDM/JSBSim/models/FGFCS.h index a17509212..6b97297d5 100644 --- a/src/FDM/JSBSim/models/FGFCS.h +++ b/src/FDM/JSBSim/models/FGFCS.h @@ -51,7 +51,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_FCS "$Id: FGFCS.h,v 1.31 2010/09/22 11:33:40 jberndt Exp $" +#define ID_FCS "$Id: FGFCS.h,v 1.35 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -168,7 +168,7 @@ CLASS DOCUMENTATION @property gear/tailhook-pos-norm @author Jon S. Berndt - @version $Revision: 1.31 $ + @version $Revision: 1.35 $ @see FGActuator @see FGDeadBand @see FGFCSFunction @@ -540,7 +540,7 @@ public: FGPropertyManager* GetPropertyManager(void) { return PropertyManager; } - void LateBind(void); + bool GetTrimStatus(void) const { return FDMExec->GetTrimStatus(); } private: double DaCmd, DeCmd, DrCmd, DsCmd, DfCmd, DsbCmd, DspCmd; diff --git a/src/FDM/JSBSim/models/FGLGear.cpp b/src/FDM/JSBSim/models/FGLGear.cpp index 596e495f0..4a4c9946f 100644 --- a/src/FDM/JSBSim/models/FGLGear.cpp +++ b/src/FDM/JSBSim/models/FGLGear.cpp @@ -62,7 +62,7 @@ DEFINITIONS GLOBAL DATA %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -static const char *IdSrc = "$Id: FGLGear.cpp,v 1.79 2010/11/28 13:20:47 bcoconni Exp $"; +static const char *IdSrc = "$Id: FGLGear.cpp,v 1.80 2011/01/24 13:01:56 jberndt Exp $"; static const char *IdHdr = ID_LGEAR; // Body To Structural (body frame is rotated 180 deg about Y and lengths are given in @@ -374,13 +374,15 @@ FGColumnVector3& FGLGear::GetBodyForces(void) } } - ReportTakeoffOrLanding(); + if (!fdmex->GetTrimStatus()) { + ReportTakeoffOrLanding(); - // Require both WOW and LastWOW to be true before checking crash conditions - // to allow the WOW flag to be used in terminating a scripted run. - if (WOW && lastWOW) CrashDetect(); + // Require both WOW and LastWOW to be true before checking crash conditions + // to allow the WOW flag to be used in terminating a scripted run. + if (WOW && lastWOW) CrashDetect(); - lastWOW = WOW; + lastWOW = WOW; + } return FGForce::GetBodyForces(); } diff --git a/src/FDM/JSBSim/models/FGModel.cpp b/src/FDM/JSBSim/models/FGModel.cpp index 71f6cb3ed..df7ffbdd0 100644 --- a/src/FDM/JSBSim/models/FGModel.cpp +++ b/src/FDM/JSBSim/models/FGModel.cpp @@ -57,7 +57,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGModel.cpp,v 1.16 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGModel.cpp,v 1.17 2011/02/16 12:30:53 jberndt Exp $"; static const char *IdHdr = ID_MODEL; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -77,7 +77,7 @@ FGModel::FGModel(FGFDMExec* fdmex) //must be brought up now. PropertyManager = FDMExec->GetPropertyManager(); - exe_ctr = 1; + exe_ctr = 0; rate = 1; if (debug_lvl & 2) cout << " FGModel Base Class" << endl; @@ -105,7 +105,7 @@ bool FGModel::Run() if (rate == 1) return false; // Fast exit if nothing to do - if (exe_ctr >= rate) exe_ctr = 1; + if (exe_ctr >= rate) exe_ctr = 0; if (exe_ctr++ == 1) return false; else return true; diff --git a/src/FDM/JSBSim/models/FGOutput.cpp b/src/FDM/JSBSim/models/FGOutput.cpp index 3e5c986c6..ab70cb2d2 100644 --- a/src/FDM/JSBSim/models/FGOutput.cpp +++ b/src/FDM/JSBSim/models/FGOutput.cpp @@ -74,7 +74,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGOutput.cpp,v 1.50 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGOutput.cpp,v 1.54 2011/03/11 13:02:26 jberndt Exp $"; static const char *IdHdr = ID_OUTPUT; // (stolen from FGFS native_fdm.cxx) @@ -182,21 +182,9 @@ bool FGOutput::Run(void) { if (FGModel::Run()) return true; - if (enabled && !FDMExec->IntegrationSuspended()&& !FDMExec->Holding()) { + if (enabled && !FDMExec->IntegrationSuspended() && !FDMExec->Holding()) { RunPreFunctions(); - if (Type == otSocket) { - SocketOutput(); - } else if (Type == otFlightGear) { - FlightGearSocketOutput(); - } else if (Type == otCSV || Type == otTab) { - DelimitedOutput(Filename); - } else if (Type == otTerminal) { - // Not done yet - } else if (Type == otNone) { - // Do nothing - } else { - // Not a valid type of output - } + Print(); RunPostFunctions(); } return false; @@ -204,6 +192,25 @@ bool FGOutput::Run(void) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void FGOutput::Print(void) +{ + if (Type == otSocket) { + SocketOutput(); + } else if (Type == otFlightGear) { + FlightGearSocketOutput(); + } else if (Type == otCSV || Type == otTab) { + DelimitedOutput(Filename); + } else if (Type == otTerminal) { + // Not done yet + } else if (Type == otNone) { + // Do nothing + } else { + // Not a valid type of output + } +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + void FGOutput::SetType(const string& type) { if (type == "CSV") { @@ -296,6 +303,7 @@ void FGOutput::DelimitedOutput(const string& fname) outstream << "UBody" + delimeter + "VBody" + delimeter + "WBody" + delimeter; outstream << "Aero V_{X Body} (ft/s)" + delimeter + "Aero V_{Y Body} (ft/s)" + delimeter + "Aero V_{Z Body} (ft/s)" + delimeter; outstream << "V_{X_{inertial}} (ft/s)" + delimeter + "V_{Y_{inertial}} (ft/s)" + delimeter + "V_{Z_{inertial}} (ft/s)" + delimeter; + outstream << "V_{X_{ecef}} (ft/s)" + delimeter + "V_{Y_{ecef}} (ft/s)" + delimeter + "V_{Z_{ecef}} (ft/s)" + delimeter; outstream << "V_{North} (ft/s)" + delimeter + "V_{East} (ft/s)" + delimeter + "V_{Down} (ft/s)"; } if (SubSystems & ssForces) { @@ -359,8 +367,8 @@ void FGOutput::DelimitedOutput(const string& fname) outstream << "Distance AGL (ft)" + delimeter; outstream << "Terrain Elevation (ft)"; } - if (SubSystems & ssCoefficients) { - scratch = Aerodynamics->GetCoefficientStrings(delimeter); + if (SubSystems & ssAeroFunctions) { + scratch = Aerodynamics->GetAeroFunctionStrings(delimeter); if (scratch.length() != 0) outstream << delimeter << scratch; } if (SubSystems & ssFCS) { @@ -415,6 +423,7 @@ void FGOutput::DelimitedOutput(const string& fname) outstream << setprecision(12) << Propagate->GetUVW().Dump(delimeter) << delimeter; outstream << Auxiliary->GetAeroUVW().Dump(delimeter) << delimeter; outstream << Propagate->GetInertialVelocity().Dump(delimeter) << delimeter; + outstream << Propagate->GetECEFVelocity().Dump(delimeter) << delimeter; outstream << Propagate->GetVel().Dump(delimeter); outstream.precision(10); } @@ -475,8 +484,8 @@ void FGOutput::DelimitedOutput(const string& fname) outstream << Propagate->GetTerrainElevation(); outstream.precision(10); } - if (SubSystems & ssCoefficients) { - scratch = Aerodynamics->GetCoefficientValues(delimeter); + if (SubSystems & ssAeroFunctions) { + scratch = Aerodynamics->GetAeroFunctionValues(delimeter); if (scratch.length() != 0) outstream << delimeter << scratch; } if (SubSystems & ssFCS) { @@ -826,8 +835,8 @@ void FGOutput::SocketOutput(void) socket->Append("Latitude (deg)"); socket->Append("Longitude (deg)"); } - if (SubSystems & ssCoefficients) { - scratch = Aerodynamics->GetCoefficientStrings(","); + if (SubSystems & ssAeroFunctions) { + scratch = Aerodynamics->GetAeroFunctionStrings(","); if (scratch.length() != 0) socket->Append(scratch); } if (SubSystems & ssFCS) { @@ -932,8 +941,8 @@ void FGOutput::SocketOutput(void) socket->Append(Propagate->GetLocation().GetLatitudeDeg()); socket->Append(Propagate->GetLocation().GetLongitudeDeg()); } - if (SubSystems & ssCoefficients) { - scratch = Aerodynamics->GetCoefficientValues(","); + if (SubSystems & ssAeroFunctions) { + scratch = Aerodynamics->GetAeroFunctionValues(","); if (scratch.length() != 0) socket->Append(scratch); } if (SubSystems & ssFCS) { @@ -974,7 +983,7 @@ bool FGOutput::Load(Element* element) { string parameter=""; string name=""; - int OutRate = 0; + double OutRate = 0.0; unsigned int port; Element *property_element; @@ -1003,7 +1012,7 @@ bool FGOutput::Load(Element* element) BaseFilename = Filename = name; } if (!document->GetAttributeValue("rate").empty()) { - OutRate = (int)document->GetAttributeValueAsNumber("rate"); + OutRate = document->GetAttributeValueAsNumber("rate"); } else { OutRate = 1; } @@ -1027,7 +1036,7 @@ bool FGOutput::Load(Element* element) if (document->FindElementValue("position") == string("ON")) SubSystems += ssPropagate; if (document->FindElementValue("coefficients") == string("ON")) - SubSystems += ssCoefficients; + SubSystems += ssAeroFunctions; if (document->FindElementValue("ground_reactions") == string("ON")) SubSystems += ssGroundReactions; if (document->FindElementValue("fcs") == string("ON")) @@ -1058,7 +1067,7 @@ bool FGOutput::Load(Element* element) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void FGOutput::SetRate(int rtHz) +void FGOutput::SetRate(double rtHz) { rtHz = rtHz>1000?1000:(rtHz<0?0:rtHz); if (rtHz > 0) { @@ -1128,7 +1137,7 @@ void FGOutput::Debug(int from) if (SubSystems & ssMoments) cout << " Moments parameters logged" << endl; if (SubSystems & ssAtmosphere) cout << " Atmosphere parameters logged" << endl; if (SubSystems & ssMassProps) cout << " Mass parameters logged" << endl; - if (SubSystems & ssCoefficients) cout << " Coefficient parameters logged" << endl; + if (SubSystems & ssAeroFunctions) cout << " Coefficient parameters logged" << endl; if (SubSystems & ssPropagate) cout << " Propagate parameters logged" << endl; if (SubSystems & ssGroundReactions) cout << " Ground parameters logged" << endl; if (SubSystems & ssFCS) cout << " FCS parameters logged" << endl; diff --git a/src/FDM/JSBSim/models/FGOutput.h b/src/FDM/JSBSim/models/FGOutput.h index d03ac5981..e22dc1319 100644 --- a/src/FDM/JSBSim/models/FGOutput.h +++ b/src/FDM/JSBSim/models/FGOutput.h @@ -51,7 +51,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_OUTPUT "$Id: FGOutput.h,v 1.19 2010/10/31 04:48:46 jberndt Exp $" +#define ID_OUTPUT "$Id: FGOutput.h,v 1.22 2011/03/11 13:02:26 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -124,7 +124,7 @@ CLASS DOCUMENTATION propulsion ON|OFF </pre> NOTE that Time is always output with the data. - @version $Id: FGOutput.h,v 1.19 2010/10/31 04:48:46 jberndt Exp $ + @version $Id: FGOutput.h,v 1.22 2011/03/11 13:02:26 jberndt Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -140,6 +140,7 @@ public: bool InitModel(void); bool Run(void); + void Print(void); void DelimitedOutput(const std::string&); void SocketOutput(void); void FlightGearSocketOutput(void); @@ -153,7 +154,7 @@ public: void SetSubsystems(int tt) {SubSystems = tt;} void SetOutputFileName(const std::string& fname) {Filename = fname;} void SetDirectivesFile(const std::string& fname) {DirectivesFile = fname;} - void SetRate(int rt); + void SetRate(double rt); void Enable(void) { enabled = true; } void Disable(void) { enabled = false; } bool Toggle(void) {enabled = !enabled; return enabled;} @@ -171,7 +172,7 @@ public: /** Subsystem: Moments (= 32) */ ssMoments = 32, /** Subsystem: Atmosphere (= 64) */ ssAtmosphere = 64, /** Subsystem: Mass Properties (= 128) */ ssMassProps = 128, - /** Subsystem: Coefficients (= 256) */ ssCoefficients = 256, + /** Subsystem: Coefficients (= 256) */ ssAeroFunctions = 256, /** Subsystem: Propagate (= 512) */ ssPropagate = 512, /** Subsystem: Ground Reactions (= 1024) */ ssGroundReactions = 1024, /** Subsystem: FCS (= 2048) */ ssFCS = 2048, diff --git a/src/FDM/JSBSim/models/FGPropagate.cpp b/src/FDM/JSBSim/models/FGPropagate.cpp index 4d1ef44a8..ae2174df9 100644 --- a/src/FDM/JSBSim/models/FGPropagate.cpp +++ b/src/FDM/JSBSim/models/FGPropagate.cpp @@ -71,29 +71,35 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.76 2011/01/16 16:10:59 bcoconni Exp $"; +static const char *IdSrc = "$Id: FGPropagate.cpp,v 1.85 2011/04/03 19:24:58 jberndt Exp $"; static const char *IdHdr = ID_PROPAGATE; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -FGPropagate::FGPropagate(FGFDMExec* fdmex) : FGModel(fdmex), -LocalTerrainRadius(0), SeaLevelRadius(0), VehicleRadius(0) +FGPropagate::FGPropagate(FGFDMExec* fdmex) + : FGModel(fdmex), + LocalTerrainRadius(0), + SeaLevelRadius(0), + VehicleRadius(0) { Debug(0); Name = "FGPropagate"; gravType = gtWGS84; - vPQRdot.InitMatrix(); + vPQRidot.InitMatrix(); vQtrndot = FGQuaternion(0,0,0); - vUVWdot.InitMatrix(); + vUVWidot.InitMatrix(); vInertialVelocity.InitMatrix(); - integrator_rotational_rate = eAdamsBashforth2; - integrator_translational_rate = eTrapezoidal; - integrator_rotational_position = eAdamsBashforth2; - integrator_translational_position = eTrapezoidal; + /// These define the indices use to select the various integrators. + // eNone = 0, eRectEuler, eTrapezoidal, eAdamsBashforth2, eAdamsBashforth3, eAdamsBashforth4}; + + integrator_rotational_rate = eRectEuler; + integrator_translational_rate = eAdamsBashforth2; + integrator_rotational_position = eRectEuler; + integrator_translational_position = eAdamsBashforth3; VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0)); VState.dqUVWidot.resize(4, FGColumnVector3(0.0,0.0,0.0)); @@ -124,9 +130,9 @@ bool FGPropagate::InitModel(void) VState.vLocation.SetEllipse(FDMExec->GetInertial()->GetSemimajor(), FDMExec->GetInertial()->GetSemiminor()); vOmegaEarth = FGColumnVector3( 0.0, 0.0, FDMExec->GetInertial()->omega() ); // Earth rotation vector - vPQRdot.InitMatrix(); + vPQRidot.InitMatrix(); vQtrndot = FGQuaternion(0,0,0); - vUVWdot.InitMatrix(); + vUVWidot.InitMatrix(); vInertialVelocity.InitMatrix(); VState.dqPQRidot.resize(4, FGColumnVector3(0.0,0.0,0.0)); @@ -189,23 +195,13 @@ void FGPropagate::SetInitialState(const FGInitialCondition *FGIC) VehicleRadius = GetRadius(); double radInv = 1.0/VehicleRadius; - // Refer to Stevens and Lewis, 1.5-14a, pg. 49. - // This is the rotation rate of the "Local" frame, expressed in the local frame. - - FGColumnVector3 vOmegaLocal = FGColumnVector3( - radInv*vVel(eEast), - -radInv*vVel(eNorth), - -radInv*vVel(eEast)*VState.vLocation.GetTanLatitude() ); - // Set the angular velocities of the body frame relative to the ECEF frame, - // expressed in the body frame. Effectively, this is: - // w_b/e = w_b/l + w_l/e + // expressed in the body frame. VState.vPQR = FGColumnVector3( FGIC->GetPRadpsIC(), FGIC->GetQRadpsIC(), - FGIC->GetRRadpsIC() ) + Tl2b*vOmegaLocal; + FGIC->GetRRadpsIC() ); VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth; - VState.vPQRi_i = Tb2i * VState.vPQRi; // Make an initial run and set past values InitializeDerivatives(); @@ -245,11 +241,10 @@ bool FGPropagate::Run(void) CalculateUVWdot(); // Translational rate derivative ResolveFrictionForces(dt); // Update rate derivatives with friction forces CalculateQuatdot(); // Angular orientation derivative - CalculateUVW(); // Translational position derivative (velocities are integrated in the inertial frame) // Propagate rotational / translational velocity, angular /translational position, respectively. - Integrate(VState.vPQRi_i, vPQRidot, VState.dqPQRidot, dt, integrator_rotational_rate); // ECI integration + Integrate(VState.vPQRi, vPQRidot, VState.dqPQRidot, dt, integrator_rotational_rate); Integrate(VState.qAttitudeECI, vQtrndot, VState.dqQtrndot, dt, integrator_rotational_position); Integrate(VState.vInertialPosition, VState.vInertialVelocity, VState.dqInertialVelocity, dt, integrator_translational_position); Integrate(VState.vInertialVelocity, vUVWidot, VState.dqUVWidot, dt, integrator_translational_rate); @@ -278,12 +273,13 @@ bool FGPropagate::Run(void) // orientation quaternion and vLocation vector. UpdateBodyMatrices(); + CalculateUVW(); // Translational position derivative (velocities are integrated in the inertial frame) + // Set auxililary state variables RecomputeLocalTerrainRadius(); VehicleRadius = GetRadius(); // Calculate current aircraft radius from center of planet - VState.vPQRi = Ti2b * VState.vPQRi_i; VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth; VState.qAttitudeLocal = Tl2b.GetQuaternion(); @@ -321,8 +317,8 @@ void FGPropagate::CalculatePQRdot(void) // moments and the total inertial angular velocity expressed in the body // frame. - vPQRdot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi)); - vPQRidot = Tb2i * vPQRdot; + vPQRidot = Jinv*(vMoments - VState.vPQRi*(J*VState.vPQRi)); + vPQRdot = vPQRidot - VState.vPQRi * (Ti2b * vOmegaEarth); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -605,7 +601,7 @@ void FGPropagate::ResolveFrictionForces(double dt) vUVWdot += invMass * Fc; vUVWidot += invMass * Tb2i * Fc; vPQRdot += Jinv * Mc; - vPQRidot += Tb2i* Jinv * Mc; + vPQRidot += Jinv * Mc; // Save the value of the Lagrange multipliers to accelerate the convergence // of the Gauss-Seidel algorithm at next iteration. @@ -658,8 +654,7 @@ void FGPropagate::SetInertialVelocity(FGColumnVector3 Vi) { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void FGPropagate::SetInertialRates(FGColumnVector3 vRates) { - VState.vPQRi_i = vRates; - VState.vPQRi = Ti2b * VState.vPQRi_i; + VState.vPQRi = Ti2b * vRates; VState.vPQR = VState.vPQRi - Ti2b * vOmegaEarth; } @@ -681,7 +676,7 @@ void FGPropagate::InitializeDerivatives(void) VState.dqQtrndot.clear(); for (int i=0; i<4; i++) { VState.dqPQRidot.push_front(vPQRidot); - VState.dqUVWidot.push_front(vUVWdot); + VState.dqUVWidot.push_front(vUVWidot); VState.dqInertialVelocity.push_front(VState.vInertialVelocity); VState.dqQtrndot.push_front(vQtrndot); } @@ -739,7 +734,6 @@ void FGPropagate::SetVState(const VehicleState& vstate) vVel = Tb2l * VState.vUVW; VState.vPQR = vstate.vPQR; VState.vPQRi = VState.vPQR + Ti2b * vOmegaEarth; - VState.vPQRi_i = Tb2i * VState.vPQRi; VState.vInertialPosition = vstate.vInertialPosition; InitializeDerivatives(); diff --git a/src/FDM/JSBSim/models/FGPropagate.h b/src/FDM/JSBSim/models/FGPropagate.h index 107b3989b..26b2ad73e 100644 --- a/src/FDM/JSBSim/models/FGPropagate.h +++ b/src/FDM/JSBSim/models/FGPropagate.h @@ -49,7 +49,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PROPAGATE "$Id: FGPropagate.h,v 1.55 2011/01/16 16:10:59 bcoconni Exp $" +#define ID_PROPAGATE "$Id: FGPropagate.h,v 1.58 2011/04/03 19:24:58 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -102,7 +102,7 @@ CLASS DOCUMENTATION @endcode @author Jon S. Berndt, Mathias Froehlich - @version $Id: FGPropagate.h,v 1.55 2011/01/16 16:10:59 bcoconni Exp $ + @version $Id: FGPropagate.h,v 1.58 2011/04/03 19:24:58 jberndt Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -135,11 +135,6 @@ public: units rad/sec */ FGColumnVector3 vPQRi; - /** The angular velocity vector for the vehicle body frame relative to the - ECI frame, expressed in the ECI frame. - units rad/sec */ - FGColumnVector3 vPQRi_i; - /** The current orientation of the vehicle, that is, the orientation of the body frame relative to the local, NED frame. */ FGQuaternion qAttitudeLocal; @@ -338,6 +333,10 @@ public: */ const FGColumnVector3& GetInertialPosition(void) const { return VState.vInertialPosition; } + /** Calculates and retrieves the velocity vector relative to the earth centered earth fixed (ECEF) frame. + */ + const FGColumnVector3 GetECEFVelocity(void) const {return Tb2ec * VState.vUVW; } + /** Returns the current altitude above sea level. This function returns the altitude above sea level. units ft @@ -581,8 +580,8 @@ public: void RecomputeLocalTerrainRadius(void); void NudgeBodyLocation(FGColumnVector3 deltaLoc) { - vDeltaXYZEC = Tb2ec*deltaLoc; - VState.vLocation -= vDeltaXYZEC; + VState.vInertialPosition -= Tb2i*deltaLoc; + VState.vLocation -= Tb2ec*deltaLoc; } struct LagrangeMultiplier { @@ -602,8 +601,7 @@ private: struct VehicleState VState; FGColumnVector3 vVel; - FGColumnVector3 vPQRdot; - FGColumnVector3 vPQRidot; + FGColumnVector3 vPQRdot, vPQRidot; FGColumnVector3 vUVWdot, vUVWidot; FGColumnVector3 vInertialVelocity; FGColumnVector3 vLocation; diff --git a/src/FDM/JSBSim/models/FGPropulsion.cpp b/src/FDM/JSBSim/models/FGPropulsion.cpp index 05a2da4c4..da82acf51 100644 --- a/src/FDM/JSBSim/models/FGPropulsion.cpp +++ b/src/FDM/JSBSim/models/FGPropulsion.cpp @@ -65,7 +65,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.43 2010/11/18 12:38:06 jberndt Exp $"; +static const char *IdSrc = "$Id: FGPropulsion.cpp,v 1.45 2011/02/13 00:42:45 jberndt Exp $"; static const char *IdHdr = ID_PROPULSION; extern short debug_lvl; @@ -194,14 +194,16 @@ bool FGPropulsion::GetSteadyState(void) double currentThrust = 0, lastThrust = -1; int steady_count = 0, j = 0; bool steady = false; + bool TrimMode = FDMExec->GetTrimStatus(); vForces.InitMatrix(); vMoments.InitMatrix(); if (!FGModel::Run()) { + FDMExec->SetTrimStatus(true); + for (unsigned int i=0; i<numEngines; i++) { // cout << " Finding steady state for engine " << i << endl; - Engines[i]->SetTrimMode(true); steady=false; steady_count=0; j=0; @@ -225,9 +227,10 @@ bool FGPropulsion::GetSteadyState(void) // } vForces += Engines[i]->GetBodyForces(); // sum body frame forces vMoments += Engines[i]->GetMoments(); // sum body frame moments - Engines[i]->SetTrimMode(false); } + FDMExec->SetTrimStatus(TrimMode); + return false; } else { return true; @@ -648,13 +651,13 @@ void FGPropulsion::bind(void) IsBound = true; PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false); if (HaveTurbineEngine) { - PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true); - PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, true); + PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false); + PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, false); } if (HavePistonEngine) { - PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, true); - PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, true); + PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false); + PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false); } PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine, diff --git a/src/FDM/JSBSim/models/flight_control/FGActuator.cpp b/src/FDM/JSBSim/models/flight_control/FGActuator.cpp index 4c58bebfd..bfbd25920 100644 --- a/src/FDM/JSBSim/models/flight_control/FGActuator.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGActuator.cpp @@ -43,7 +43,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGActuator.cpp,v 1.14 2009/10/24 22:59:30 jberndt Exp $"; +static const char *IdSrc = "$Id: FGActuator.cpp,v 1.17 2011/02/13 00:42:45 jberndt Exp $"; static const char *IdHdr = ID_ACTUATOR; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -114,10 +114,12 @@ bool FGActuator::Run(void ) // the Input will be further processed and the eventual Output // will be overwritten from this perfect value. - if (lag != 0.0) Lag(); // models actuator lag - if (rate_limit != 0) RateLimit(); // limit the actuator rate + if (!fcs->GetTrimStatus()) { + if (lag != 0.0) Lag(); // models actuator lag + if (rate_limit != 0) RateLimit(); // limit the actuator rate + } if (deadband_width != 0.0) Deadband(); - if (hysteresis_width != 0.0) Hysteresis(); + if (!fcs->GetTrimStatus() && hysteresis_width != 0.0) Hysteresis(); if (bias != 0.0) Bias(); // models a finite bias if (fail_stuck) Output = PreviousOutput; @@ -187,6 +189,18 @@ void FGActuator::RateLimit(void) void FGActuator::Deadband(void) { + // Note: this function acts cumulatively on the "Output" parameter. So, "Output" + // is - for the purposes of this Deadband method - really the input to the + // method. + double input = Output; + + if (input < -deadband_width/2.0) { + Output = (input + deadband_width/2.0); + } else if (input > deadband_width/2.0) { + Output = (input - deadband_width/2.0); + } else { + Output = 0.0; + } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -232,9 +246,9 @@ void FGActuator::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor if (InputSigns[0] < 0) - cout << " INPUT: -" << InputNodes[0]->getName() << endl; + cout << " INPUT: -" << InputNames[0] << endl; else - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNames[0] << endl; if (IsOutput) { for (unsigned int i=0; i<OutputNodes.size(); i++) diff --git a/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp b/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp index 0158b6356..361afb66a 100644 --- a/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGDeadBand.cpp @@ -142,7 +142,7 @@ void FGDeadBand::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; if (WidthPropertyNode != 0) { cout << " DEADBAND WIDTH: " << WidthPropertyNode->GetName() << endl; } else { diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp index 6cdf9ab83..64a764927 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp @@ -40,6 +40,7 @@ INCLUDES #include "FGFCSComponent.h" #include "input_output/FGPropertyManager.h" #include "input_output/FGXMLElement.h" +#include "math/FGPropertyValue.h" #include <iostream> #include <cstdlib> @@ -47,7 +48,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGFCSComponent.cpp,v 1.29 2010/09/07 00:40:03 jberndt Exp $"; +static const char *IdSrc = "$Id: FGFCSComponent.cpp,v 1.30 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_FCSCOMPONENT; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -111,8 +112,6 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs) Name = element->GetAttributeValue("name"); - FGPropertyManager *tmp=0; - input_element = element->FindElement("input"); while (input_element) { input = input_element->GetDataLine(); @@ -122,14 +121,14 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs) } else { InputSigns.push_back( 1.0); } + FGPropertyManager* node = 0L; if (PropertyManager->HasNode(input)) { - tmp = PropertyManager->GetNode(input); + node = PropertyManager->GetNode(input); + InputNodes.push_back(new FGPropertyValue( node )); } else { - tmp = 0L; - // cerr << fgcyan << "In component: " + Name + " property " - // + input + " is initially undefined." << reset << endl; + InputNodes.push_back(new FGPropertyValue( input, + PropertyManager )); } - InputNodes.push_back( tmp ); InputNames.push_back( input ); input_element = element->FindNextElement("input"); @@ -200,6 +199,9 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs) FGFCSComponent::~FGFCSComponent() { Debug(1); + for (unsigned int i=0; i<InputNodes.size(); i++) { + delete InputNodes[i]; + } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -238,24 +240,6 @@ void FGFCSComponent::Clip(void) } } -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -void FGFCSComponent::LateBind(void) -{ - FGPropertyManager* node = 0L; - - for (unsigned int i=0; i<InputNodes.size(); i++) { - if (!InputNodes[i]) { - if (PropertyManager->HasNode(InputNames[i])) { - node = PropertyManager->GetNode(InputNames[i]); - InputNodes[i] = node; - } else { - throw(InputNames[i]); - } - } - } -} - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // The old way of naming FCS components allowed upper or lower case, spaces, etc. diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h index 7889cd948..02bca98bd 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.h @@ -38,6 +38,7 @@ INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include "FGJSBBase.h" +#include "math/FGPropertyValue.h" #include <string> #include <vector> @@ -45,7 +46,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_FCSCOMPONENT "$Id: FGFCSComponent.h,v 1.17 2010/08/21 22:56:11 jberndt Exp $" +#define ID_FCSCOMPONENT "$Id: FGFCSComponent.h,v 1.18 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -80,7 +81,7 @@ CLASS DOCUMENTATION - FGActuator @author Jon S. Berndt - @version $Id: FGFCSComponent.h,v 1.17 2010/08/21 22:56:11 jberndt Exp $ + @version $Id: FGFCSComponent.h,v 1.18 2011/04/05 20:20:21 andgi Exp $ @see Documentation for the FGFCS class, and for the configuration file class */ @@ -98,7 +99,6 @@ public: virtual bool Run(void); virtual void SetOutput(void); - void LateBind(void); double GetOutput (void) const {return Output;} std::string GetName(void) const {return Name;} std::string GetType(void) const { return Type; } @@ -111,7 +111,7 @@ protected: std::vector <FGPropertyManager*> OutputNodes; FGPropertyManager* ClipMinPropertyNode; FGPropertyManager* ClipMaxPropertyNode; - std::vector <FGPropertyManager*> InputNodes; + std::vector <FGPropertyValue*> InputNodes; std::vector <std::string> InputNames; std::vector <float> InputSigns; std::vector <double> output_array; diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSFunction.cpp b/src/FDM/JSBSim/models/flight_control/FGFCSFunction.cpp index c6a352f3b..a9143f42a 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSFunction.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFCSFunction.cpp @@ -121,7 +121,7 @@ void FGFCSFunction::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor if (InputNodes.size()>0) - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; // cout << " Function: " << endl; if (IsOutput) { for (unsigned int i=0; i<OutputNodes.size(); i++) diff --git a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp index 2aab6e2b8..54739e837 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFilter.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFilter.cpp @@ -259,7 +259,7 @@ void FGFilter::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; switch (FilterType) { case eLag: if (PropertySign[1] < 0.0) sgn="-"; diff --git a/src/FDM/JSBSim/models/flight_control/FGGain.cpp b/src/FDM/JSBSim/models/flight_control/FGGain.cpp index e52cd37e6..fac82ab61 100644 --- a/src/FDM/JSBSim/models/flight_control/FGGain.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGGain.cpp @@ -209,9 +209,9 @@ void FGGain::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor if (InputSigns[0] < 0) - cout << " INPUT: -" << InputNodes[0]->getName() << endl; + cout << " INPUT: -" << InputNodes[0]->GetName() << endl; else - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; if (GainPropertyNode != 0) { cout << " GAIN: " << GainPropertyNode->GetName() << endl; diff --git a/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp b/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp index 525fcee37..4a597f7b4 100644 --- a/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGKinemat.cpp @@ -188,7 +188,7 @@ void FGKinemat::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; cout << " DETENTS: " << NumDetents << endl; for (int i=0;i<NumDetents;i++) { cout << " " << Detents[i] << " " << TransitionTimes[i] << endl; diff --git a/src/FDM/JSBSim/models/flight_control/FGPID.cpp b/src/FDM/JSBSim/models/flight_control/FGPID.cpp index ac265b8ff..6556ecdb7 100644 --- a/src/FDM/JSBSim/models/flight_control/FGPID.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGPID.cpp @@ -189,9 +189,9 @@ void FGPID::Debug(int from) if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor if (InputSigns[0] < 0) - cout << " INPUT: -" << InputNodes[0]->getName() << endl; + cout << " INPUT: -" << InputNodes[0]->GetName() << endl; else - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; if (IsOutput) { for (unsigned int i=0; i<OutputNodes.size(); i++) diff --git a/src/FDM/JSBSim/models/flight_control/FGSensor.cpp b/src/FDM/JSBSim/models/flight_control/FGSensor.cpp index 1f46dc954..60b5f955a 100644 --- a/src/FDM/JSBSim/models/flight_control/FGSensor.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGSensor.cpp @@ -293,9 +293,9 @@ void FGSensor::Debug(int from) if (from == 0) { // Constructor if (InputSigns.size() > 0) { if (InputSigns[0] < 0) - cout << " INPUT: -" << InputNodes[0]->getName() << endl; + cout << " INPUT: -" << InputNodes[0]->GetName() << endl; else - cout << " INPUT: " << InputNodes[0]->getName() << endl; + cout << " INPUT: " << InputNodes[0]->GetName() << endl; } if (bits != 0) { if (quant_property.empty()) diff --git a/src/FDM/JSBSim/models/flight_control/FGSwitch.cpp b/src/FDM/JSBSim/models/flight_control/FGSwitch.cpp index 4e68ea7ff..cea9f583e 100644 --- a/src/FDM/JSBSim/models/flight_control/FGSwitch.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGSwitch.cpp @@ -69,7 +69,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGSwitch.cpp,v 1.19 2009/10/24 22:59:30 jberndt Exp $"; +static const char *IdSrc = "$Id: FGSwitch.cpp,v 1.20 2011/04/05 20:20:21 andgi Exp $"; static const char *IdHdr = ID_SWITCH; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -135,7 +135,13 @@ FGSwitch::FGSwitch(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element) } else { current_test->sign = 1.0; } - current_test->OutputProp = PropertyManager->GetNode(value); + FGPropertyManager *node = PropertyManager->GetNode(value, false); + if (node) { + current_test->OutputProp = new FGPropertyValue(node); + } else { + current_test->OutputProp = new FGPropertyValue(value, + PropertyManager); + } } } } @@ -151,6 +157,7 @@ FGSwitch::~FGSwitch() { for (unsigned int i=0; i<tests.size(); i++) { for (unsigned int j=0; j<tests[i]->conditions.size(); j++) delete tests[i]->conditions[j]; + delete tests[i]->OutputProp; delete tests[i]; } diff --git a/src/FDM/JSBSim/models/flight_control/FGSwitch.h b/src/FDM/JSBSim/models/flight_control/FGSwitch.h index a230aef7e..cca141b0f 100644 --- a/src/FDM/JSBSim/models/flight_control/FGSwitch.h +++ b/src/FDM/JSBSim/models/flight_control/FGSwitch.h @@ -40,12 +40,13 @@ INCLUDES #include "FGFCSComponent.h" #include "input_output/FGXMLElement.h" #include "math/FGCondition.h" +#include "math/FGPropertyValue.h" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_SWITCH "$Id: FGSwitch.h,v 1.13 2009/10/02 10:30:09 jberndt Exp $" +#define ID_SWITCH "$Id: FGSwitch.h,v 1.14 2011/04/05 20:20:21 andgi Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -124,7 +125,7 @@ ap/attitude_hold takes the value 1), the value of the switch component will be whatever value fcs/roll-ap-error-summer is. @author Jon S. Berndt -@version $Id: FGSwitch.h,v 1.13 2009/10/02 10:30:09 jberndt Exp $ +@version $Id: FGSwitch.h,v 1.14 2011/04/05 20:20:21 andgi Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -156,12 +157,12 @@ private: vector <FGCondition*> conditions; eLogic Logic; double OutputVal; - FGPropertyManager *OutputProp; + FGPropertyValue *OutputProp; float sign; double GetValue(void) { if (OutputProp == 0L) return OutputVal; - else return OutputProp->getDoubleValue()*sign; + else return OutputProp->GetValue()*sign; } test(void) { // constructor for the test structure diff --git a/src/FDM/JSBSim/models/propulsion/FGElectric.cpp b/src/FDM/JSBSim/models/propulsion/FGElectric.cpp index 1138d820c..cbcb9220a 100644 --- a/src/FDM/JSBSim/models/propulsion/FGElectric.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGElectric.cpp @@ -50,7 +50,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGElectric.cpp,v 1.9 2010/08/21 17:13:48 jberndt Exp $"; +static const char *IdSrc = "$Id: FGElectric.cpp,v 1.10 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_ELECTRIC; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -92,16 +92,21 @@ void FGElectric::Calculate(void) RPM = Thruster->GetRPM() * Thruster->GetGearRatio(); HP = PowerWatts * Throttle / hptowatts; - - PowerAvailable = (HP * hptoftlbssec) - Thruster->GetPowerRequired(); - - Thruster->Calculate(PowerAvailable); + + Thruster->Calculate(HP * hptoftlbssec); RunPostFunctions(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +double FGElectric::CalcFuelNeed(void) +{ + return 0; +} + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + string FGElectric::GetEngineLabels(const string& delimiter) { std::ostringstream buf; @@ -174,10 +179,4 @@ void FGElectric::Debug(int from) } } -double -FGElectric::CalcFuelNeed(void) -{ - return 0; -} - } // namespace JSBSim diff --git a/src/FDM/JSBSim/models/propulsion/FGElectric.h b/src/FDM/JSBSim/models/propulsion/FGElectric.h index 268522956..28c703dee 100644 --- a/src/FDM/JSBSim/models/propulsion/FGElectric.h +++ b/src/FDM/JSBSim/models/propulsion/FGElectric.h @@ -45,7 +45,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_ELECTRIC "$Id: FGElectric.h,v 1.9 2010/08/21 18:07:59 jberndt Exp $"; +#define ID_ELECTRIC "$Id: FGElectric.h,v 1.10 2011/03/10 01:35:25 dpculp Exp $"; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -65,7 +65,7 @@ CLASS DOCUMENTATION there is no battery model available, so this motor does not consume any energy. There is no internal friction. @author David Culp - @version "$Id: FGElectric.h,v 1.9 2010/08/21 18:07:59 jberndt Exp $" + @version "$Id: FGElectric.h,v 1.10 2011/03/10 01:35:25 dpculp Exp $" */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -81,7 +81,7 @@ public: ~FGElectric(); void Calculate(void); - double GetPowerAvailable(void) {return PowerAvailable;} + double GetPowerAvailable(void) {return (HP * hptoftlbssec);} double getRPM(void) {return RPM;} std::string GetEngineLabels(const std::string& delimiter); std::string GetEngineValues(const std::string& delimiter); @@ -91,7 +91,6 @@ private: double CalcFuelNeed(void); double BrakeHorsePower; - double PowerAvailable; // timestep double dt; @@ -101,7 +100,7 @@ private: double PowerWatts; // maximum engine power double RPM; // revolutions per minute - double HP; + double HP; // engine output, in horsepower void Debug(int from); }; diff --git a/src/FDM/JSBSim/models/propulsion/FGEngine.cpp b/src/FDM/JSBSim/models/propulsion/FGEngine.cpp index e48e27426..707d425e4 100644 --- a/src/FDM/JSBSim/models/propulsion/FGEngine.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGEngine.cpp @@ -54,7 +54,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGEngine.cpp,v 1.40 2010/10/15 11:32:41 jberndt Exp $"; +static const char *IdSrc = "$Id: FGEngine.cpp,v 1.42 2011/03/03 12:16:26 jberndt Exp $"; static const char *IdHdr = ID_ENGINE; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -151,6 +151,8 @@ FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number) PropertyManager->Tie( property_name.c_str(), Thruster, &FGThruster::GetThrust); property_name = base_property_name + "/fuel-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate); + property_name = base_property_name + "/fuel-used-lbs"; + PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelUsedLbs); PostLoad(engine_element, PropertyManager, to_string(EngineNumber)); @@ -177,11 +179,11 @@ void FGEngine::ResetToIC(void) FuelExpended = 0.0; Starved = Running = Cranking = false; PctPower = 0.0; - TrimMode = false; FuelFlow_gph = 0.0; FuelFlow_pph = 0.0; FuelFlowRate = 0.0; FuelFreeze = false; + FuelUsedLbs = 0.0; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -194,7 +196,7 @@ void FGEngine::ResetToIC(void) void FGEngine::ConsumeFuel(void) { if (FuelFreeze) return; - if (TrimMode) return; + if (FDMExec->GetTrimStatus()) return; unsigned int i; double Fshortage, FuelNeeded; @@ -240,6 +242,7 @@ void FGEngine::ConsumeFuel(void) Tank = Propulsion->GetTank(FeedList[i]); Tank->Drain(FuelNeeded); } + FuelUsedLbs += FuelToBurn; } diff --git a/src/FDM/JSBSim/models/propulsion/FGEngine.h b/src/FDM/JSBSim/models/propulsion/FGEngine.h index c25eebfc0..03b774d94 100644 --- a/src/FDM/JSBSim/models/propulsion/FGEngine.h +++ b/src/FDM/JSBSim/models/propulsion/FGEngine.h @@ -55,7 +55,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_ENGINE "$Id: FGEngine.h,v 1.21 2010/08/21 17:13:48 jberndt Exp $" +#define ID_ENGINE "$Id: FGEngine.h,v 1.23 2011/03/03 12:16:26 jberndt Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -118,7 +118,7 @@ CLASS DOCUMENTATION documentation for engine and thruster classes. </pre> @author Jon S. Berndt - @version $Id: FGEngine.h,v 1.21 2010/08/21 17:13:48 jberndt Exp $ + @version $Id: FGEngine.h,v 1.23 2011/03/03 12:16:26 jberndt Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -146,6 +146,7 @@ public: virtual double getFuelFlow_gph () const {return FuelFlow_gph;} virtual double getFuelFlow_pph () const {return FuelFlow_pph;} virtual double GetFuelFlowRate(void) const {return FuelFlowRate;} + virtual double GetFuelUsedLbs(void) const {return FuelUsedLbs;} virtual bool GetStarved(void) { return Starved; } virtual bool GetRunning(void) const { return Running; } virtual bool GetCranking(void) { return Cranking; } @@ -173,9 +174,6 @@ public: virtual double GetPowerAvailable(void) {return 0.0;}; - virtual bool GetTrimMode(void) {return TrimMode;} - virtual void SetTrimMode(bool state) {TrimMode = state;} - virtual FGColumnVector3& GetBodyForces(void); virtual FGColumnVector3& GetMoments(void); @@ -219,12 +217,12 @@ protected: bool Starved; bool Running; bool Cranking; - bool TrimMode; bool FuelFreeze; double FuelFlow_gph; double FuelFlow_pph; double FuelDensity; + double FuelUsedLbs; FGFDMExec* FDMExec; FGAtmosphere* Atmosphere; diff --git a/src/FDM/JSBSim/models/propulsion/FGForce.cpp b/src/FDM/JSBSim/models/propulsion/FGForce.cpp index 0180d18e4..0bebb1772 100644 --- a/src/FDM/JSBSim/models/propulsion/FGForce.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGForce.cpp @@ -53,7 +53,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGForce.cpp,v 1.14 2009/10/24 22:59:30 jberndt Exp $"; +static const char *IdSrc = "$Id: FGForce.cpp,v 1.15 2011/02/17 00:20:52 jberndt Exp $"; static const char *IdHdr = ID_FORCE; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -62,9 +62,20 @@ FGForce::FGForce(FGFDMExec *FDMExec) : fdmex(FDMExec), ttype(tNone) { - mT(1,1) = 1; //identity matrix - mT(2,2) = 1; - mT(3,3) = 1; + vFn.InitMatrix(); + vMn.InitMatrix(); + vH.InitMatrix(); + vOrient.InitMatrix(); + vXYZn.InitMatrix(); + vActingXYZn.InitMatrix(); + + vFb.InitMatrix(); + vM.InitMatrix(); + vDXYZ.InitMatrix(); + + mT.InitMatrix(1., 0., 0., + 0., 1., 0., + 0., 0., 1.); Debug(0); } diff --git a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp index 522a0b2fe..7726306bf 100644 --- a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp @@ -53,7 +53,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGPiston.cpp,v 1.54 2010/11/30 12:17:10 jberndt Exp $"; +static const char *IdSrc = "$Id: FGPiston.cpp,v 1.55 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_PISTON; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -442,8 +442,7 @@ void FGPiston::Calculate(void) ((FGPropeller*)Thruster)->SetFeather(FCS->GetPropFeather(EngineNumber)); } - PowerAvailable = (HP * hptoftlbssec) - Thruster->GetPowerRequired(); - Thruster->Calculate(PowerAvailable); + Thruster->Calculate(HP * hptoftlbssec); RunPostFunctions(); } @@ -872,7 +871,7 @@ string FGPiston::GetEngineLabels(const string& delimiter) { std::ostringstream buf; - buf << Name << " Power Available (engine " << EngineNumber << " in HP)" << delimiter + buf << Name << " Power Available (engine " << EngineNumber << " in ft-lbs/sec)" << delimiter << Name << " HP (engine " << EngineNumber << ")" << delimiter << Name << " equivalent ratio (engine " << EngineNumber << ")" << delimiter << Name << " MAP (engine " << EngineNumber << " in inHg)" << delimiter @@ -887,7 +886,7 @@ string FGPiston::GetEngineValues(const string& delimiter) { std::ostringstream buf; - buf << PowerAvailable << delimiter << HP << delimiter + buf << (HP * hptoftlbssec) << delimiter << HP << delimiter << equivalence_ratio << delimiter << ManifoldPressure_inHg << delimiter << Thruster->GetThrusterValues(EngineNumber, delimiter); diff --git a/src/FDM/JSBSim/models/propulsion/FGPiston.h b/src/FDM/JSBSim/models/propulsion/FGPiston.h index 510c6e6f2..a8019e5aa 100644 --- a/src/FDM/JSBSim/models/propulsion/FGPiston.h +++ b/src/FDM/JSBSim/models/propulsion/FGPiston.h @@ -46,7 +46,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PISTON "$Id: FGPiston.h,v 1.25 2010/11/30 12:17:10 jberndt Exp $"; +#define ID_PISTON "$Id: FGPiston.h,v 1.26 2011/03/10 01:35:25 dpculp Exp $"; #define FG_MAX_BOOST_SPEEDS 3 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -182,7 +182,7 @@ CLASS DOCUMENTATION @author Dave Luff (engine operational code) @author David Megginson (initial porting and additional code) @author Ron Jensen (additional engine code) - @version $Id: FGPiston.h,v 1.25 2010/11/30 12:17:10 jberndt Exp $ + @version $Id: FGPiston.h,v 1.26 2011/03/10 01:35:25 dpculp Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -201,7 +201,7 @@ public: std::string GetEngineValues(const std::string& delimiter); void Calculate(void); - double GetPowerAvailable(void) const {return PowerAvailable;} + double GetPowerAvailable(void) const {return (HP * hptoftlbssec);} double CalcFuelNeed(void); void ResetToIC(void); @@ -227,7 +227,6 @@ private: double FMEP; double FMEPDynamic; double FMEPStatic; - double PowerAvailable; // timestep double dt; diff --git a/src/FDM/JSBSim/models/propulsion/FGPropeller.cpp b/src/FDM/JSBSim/models/propulsion/FGPropeller.cpp index f83f961b8..d5ed3813d 100644 --- a/src/FDM/JSBSim/models/propulsion/FGPropeller.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGPropeller.cpp @@ -48,7 +48,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGPropeller.cpp,v 1.32 2010/10/21 03:27:40 jberndt Exp $"; +static const char *IdSrc = "$Id: FGPropeller.cpp,v 1.33 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_PROPELLER; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -185,23 +185,22 @@ FGPropeller::~FGPropeller() // We must be getting the aerodynamic velocity here, NOT the inertial velocity. // We need the velocity with respect to the wind. // -// Note that PowerAvailable is the excess power available after the drag of the -// propeller has been subtracted. At equilibrium, PowerAvailable will be zero - -// indicating that the propeller will not accelerate or decelerate. // Remembering that Torque * omega = Power, we can derive the torque on the // propeller and its acceleration to give a new RPM. The current RPM will be // used to calculate thrust. // // Because RPM could be zero, we need to be creative about what RPM is stated as. -double FGPropeller::Calculate(double PowerAvailable) +double FGPropeller::Calculate(double EnginePower) { - double omega, alpha, beta; + double omega, alpha, beta, PowerAvailable; double Vel = fdmex->GetAuxiliary()->GetAeroUVW(eU); double rho = fdmex->GetAtmosphere()->GetDensity(); double RPS = RPM/60.0; + PowerAvailable = EnginePower - GetPowerRequired(); + // Calculate helical tip Mach double Area = 0.25*Diameter*Diameter*M_PI; double Vtip = RPS * Diameter * M_PI; diff --git a/src/FDM/JSBSim/models/propulsion/FGPropeller.h b/src/FDM/JSBSim/models/propulsion/FGPropeller.h index ea7953e1e..3e9c3c259 100644 --- a/src/FDM/JSBSim/models/propulsion/FGPropeller.h +++ b/src/FDM/JSBSim/models/propulsion/FGPropeller.h @@ -45,7 +45,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_PROPELLER "$Id: FGPropeller.h,v 1.16 2010/04/09 12:44:06 jberndt Exp $" +#define ID_PROPELLER "$Id: FGPropeller.h,v 1.17 2011/03/10 01:35:25 dpculp Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -141,7 +141,7 @@ CLASS DOCUMENTATION <li>Various NACA Technical Notes and Reports</li> </ul> @author Jon S. Berndt - @version $Id: FGPropeller.h,v 1.16 2010/04/09 12:44:06 jberndt Exp $ + @version $Id: FGPropeller.h,v 1.17 2011/03/10 01:35:25 dpculp Exp $ @see FGEngine @see FGThruster */ @@ -247,7 +247,7 @@ public: accelerate the prop. It could be negative, dictating that the propeller would be slowed. @return the thrust in pounds */ - double Calculate(double PowerAvailable); + double Calculate(double EnginePower); FGColumnVector3 GetPFactor(void); string GetThrusterLabels(int id, string delimeter); string GetThrusterValues(int id, string delimeter); diff --git a/src/FDM/JSBSim/models/propulsion/FGRocket.cpp b/src/FDM/JSBSim/models/propulsion/FGRocket.cpp index 3fef49616..f8fb091cb 100644 --- a/src/FDM/JSBSim/models/propulsion/FGRocket.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGRocket.cpp @@ -49,7 +49,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGRocket.cpp,v 1.22 2010/12/30 13:35:09 jberndt Exp $"; +static const char *IdSrc = "$Id: FGRocket.cpp,v 1.23 2011/01/24 13:01:56 jberndt Exp $"; static const char *IdHdr = ID_ROCKET; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -202,7 +202,7 @@ void FGRocket::ConsumeFuel(void) double Fshortage=0, Oshortage=0, TanksWithFuel=0, TanksWithOxidizer=0; if (FuelFreeze) return; - if (TrimMode) return; + if (FDMExec->GetTrimStatus()) return; // Count how many assigned tanks have fuel for this engine at this time. // If there is/are fuel tanks but no oxidizer tanks, this indicates diff --git a/src/FDM/JSBSim/models/propulsion/FGRotor.cpp b/src/FDM/JSBSim/models/propulsion/FGRotor.cpp index 5c69728f2..1ac58405a 100644 --- a/src/FDM/JSBSim/models/propulsion/FGRotor.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGRotor.cpp @@ -34,6 +34,8 @@ HISTORY 11/15/10 T.Kreitler treated flow solver bug, flow and torque calculations simplified, tiploss influence removed from flapping angles 01/10/11 T.Kreitler changed to single rotor model +03/06/11 T.Kreitler added brake, clutch, and experimental free-wheeling-unit, + reasonable estimate for inflowlag %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% INCLUDES @@ -56,7 +58,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGRotor.cpp,v 1.11 2011/01/17 22:09:59 jberndt Exp $"; +static const char *IdSrc = "$Id: FGRotor.cpp,v 1.12 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_ROTOR; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -109,7 +111,11 @@ FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num) // control ControlMap(eMainCtrl), - CollectiveCtrl(0.0), LateralCtrl(0.0), LongitudinalCtrl(0.0) + CollectiveCtrl(0.0), LateralCtrl(0.0), LongitudinalCtrl(0.0), + BrakeCtrlNorm(0.0), MaxBrakePower(0.0), + + // free-wheeling-unit (FWU) + FreeWheelPresent(0), FreeWheelThresh(0.0), FreeWheelTransmission(0.0) { FGColumnVector3 location(0.0, 0.0, 0.0), orientation(0.0, 0.0, 0.0); @@ -190,6 +196,9 @@ FGRotor::FGRotor(FGFDMExec *exec, Element* rotor_element, int num) // calculation would cause jumps too. 1Hz seems sufficient. damp_hagl = Filter(1.0,dt); + // avoid too abrupt changes in power transmission + FreeWheelLag = Filter(200.0,dt); + // enable import-export BindModel(); @@ -248,7 +257,7 @@ double FGRotor::ConfigValue(Element* el, const string& ename, double default_val //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // 1. read configuration and try to fill holes, ymmv -// 2. calculate derived parameters and transforms +// 2. calculate derived parameters void FGRotor::Configure(Element* rotor_element) { @@ -279,22 +288,24 @@ void FGRotor::Configure(Element* rotor_element) estimate = sqr(BladeChord) * sqr(Radius - HingeOffset) * 0.57; BladeFlappingMoment = ConfigValueConv(rotor_element, "flappingmoment", estimate, "SLUG*FT2"); - BladeFlappingMoment = Constrain(0.001, BladeFlappingMoment, 1e9); + BladeFlappingMoment = Constrain(1.0e-6, BladeFlappingMoment, 1e9); // guess mass from moment of a thin stick, and multiply by the blades cg distance estimate = ( 3.0 * BladeFlappingMoment / sqr(Radius) ) * (0.45 * Radius) ; BladeMassMoment = ConfigValue(rotor_element, "massmoment", estimate); // unit is slug-ft BladeMassMoment = Constrain(0.001, BladeMassMoment, 1e9); - TipLossB = ConfigValue(rotor_element, "tiplossfactor", 1.0, silent); - estimate = 1.1 * BladeFlappingMoment * BladeNum; PolarMoment = ConfigValueConv(rotor_element, "polarmoment", estimate, "SLUG*FT2"); - PolarMoment = Constrain(0.001, PolarMoment, 1e9); + PolarMoment = Constrain(1e-6, PolarMoment, 1e9); - InflowLag = ConfigValue(rotor_element, "inflowlag", 0.2, yell); // fixme, depends on size - InflowLag = Constrain(1e-6, InflowLag, 2.0); + // "inflowlag" is treated further down. + TipLossB = ConfigValue(rotor_element, "tiplossfactor", 1.0, silent); + + estimate = 0.01 * PolarMoment ; // guesses for huey, bo105 20-30hp + MaxBrakePower = ConfigValueConv(rotor_element, "maxbrakepower", estimate, "HP"); + MaxBrakePower *= hptoftlbssec; // ground effect if (rotor_element->FindElement("cgroundeffect")) { @@ -309,6 +320,17 @@ void FGRotor::Configure(Element* rotor_element) GroundEffectExp = ConfigValue(rotor_element, "groundeffectexp", 0.0); GroundEffectShift = ConfigValueConv(rotor_element, "groundeffectshift", 0.0, "FT"); + // handle optional free-wheeling-unit (FWU) + FreeWheelPresent = 0; + FreeWheelTransmission = 1.0; + if (rotor_element->FindElement("freewheelthresh")) { + FreeWheelThresh = rotor_element->FindElementValueAsNumber("freewheelthresh"); + if (FreeWheelThresh > 1.0) { + FreeWheelPresent = 1; + FreeWheelTransmission = 0.0; + } + } + // precalc often used powers R[0]=1.0; R[1]=Radius; R[2]=R[1]*R[1]; R[3]=R[2]*R[1]; R[4]=R[3]*R[1]; B[0]=1.0; B[1]=TipLossB; B[2]=B[1]*B[1]; B[3]=B[2]*B[1]; B[4]=B[3]*B[1]; @@ -317,6 +339,13 @@ void FGRotor::Configure(Element* rotor_element) LockNumberByRho = LiftCurveSlope * BladeChord * R[4] / BladeFlappingMoment; Solidity = BladeNum * BladeChord / (M_PI * Radius); + // estimate inflow lag, see /GE49/ eqn(1) + double omega_tmp = (NominalRPM/60.0)*2.0*M_PI; + estimate = 16.0/(LockNumberByRho*rho * omega_tmp ); // 16/(gamma*Omega) + // printf("# Est. InflowLag: %f\n", estimate); + InflowLag = ConfigValue(rotor_element, "inflowlag", estimate, yell); + InflowLag = Constrain(1.0e-6, InflowLag, 2.0); + return; } // Configure @@ -362,7 +391,7 @@ FGColumnVector3 FGRotor::fus_angvel_body2ca( const FGColumnVector3 &pqr) av_w_fus(eP)= av_s_fus(eP)*cos(beta_orient) + av_s_fus(eQ)*sin(beta_orient); av_w_fus(eQ)= - av_s_fus(eP)*sin(beta_orient) + av_s_fus(eQ)*cos(beta_orient); av_w_fus(eR)= av_s_fus(eR); - + return av_w_fus; } @@ -382,7 +411,7 @@ void FGRotor::calc_flow_and_thrust( double theta_0, double Uw, double Ww, double ct_over_sigma = 0.0; double c0, ct_l, ct_t0, ct_t1; - double mu2; + double mu2; mu = Uw/(Omega*Radius); // /SH79/ eqn(24) mu2 = sqr(mu); @@ -390,7 +419,7 @@ void FGRotor::calc_flow_and_thrust( double theta_0, double Uw, double Ww, ct_t0 = (1.0/3.0*B[3] + 1.0/2.0 * TipLossB*mu2 - 4.0/(9.0*M_PI) * mu*mu2 ) * theta_0; ct_t1 = (1.0/4.0*B[4] + 1.0/4.0 * B[2]*mu2) * BladeTwist; - ct_l = (1.0/2.0*B[2] + 1.0/4.0 * mu2) * lambda; // first time + ct_l = (1.0/2.0*B[2] + 1.0/4.0 * mu2) * lambda; // first time c0 = (LiftCurveSlope/2.0)*(ct_l + ct_t0 + ct_t1) * Solidity; c0 = c0 / ( 2.0 * sqrt( sqr(mu) + sqr(lambda) ) + 1e-15); @@ -473,7 +502,7 @@ void FGRotor::calc_flapping_angles(double theta_0, const FGColumnVector3 &pqr_fu void FGRotor::calc_drag_and_side_forces(double theta_0) { - double cy_over_sigma ; + double cy_over_sigma; double t075 = theta_0 + 0.75 * BladeTwist; H_drag = Thrust * a_dw; @@ -494,7 +523,7 @@ void FGRotor::calc_drag_and_side_forces(double theta_0) // Simplified version of /SH79/ eqn(36). Uses an estimate for blade drag // (a new config parameter to come...). -// From "Bramwell's Helicopter Dynamics" � second edition, eqn(3.43) and (3.44) +// From "Bramwell's Helicopter Dynamics", second edition, eqn(3.43) and (3.44) void FGRotor::calc_torque(double theta_0) { @@ -560,7 +589,7 @@ void FGRotor::CalcStatePart1(void) FGColumnVector3 vHub_ca, avFus_ca; double h_agl_ft, filtered_hagl = 0.0; - double ge_factor = 1.0; + double ge_factor = 1.0; // fetch needed values from environment Vt = fdmex->GetAuxiliary()->GetVt(); // total vehicle velocity including wind @@ -637,18 +666,52 @@ void FGRotor::CalcStatePart2(double PowerAvailable) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -double FGRotor::GetPowerRequired(void) -{ - CalcStatePart1(); - PowerRequired = Torque * Omega; - return PowerRequired; +// Simulation of a free-wheeling-unit (FWU). Might need improvements. + +void FGRotor::calc_freewheel_state(double p_source, double p_load) { + + // engine is off/detached, release. + if (p_source<1e-3) { + FreeWheelTransmission = 0.0; + return; + } + + // engine is driving the rotor, engage. + if (p_source >= p_load) { + FreeWheelTransmission = 1.0; + return; + } + + // releases if engine is detached, but stays calm if + // the load changes due to rotor dynamics. + if (p_source > 0.0 && p_load/(p_source+0.1) > FreeWheelThresh ) { + FreeWheelTransmission = 0.0; + return; + } + + return; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -double FGRotor::Calculate(double PowerAvailable) +double FGRotor::Calculate(double EnginePower) { - CalcStatePart2(PowerAvailable); + double FWmult = 1.0; + double DeltaPower; + + CalcStatePart1(); + + PowerRequired = Torque * Omega + BrakeCtrlNorm * MaxBrakePower; + + if (FreeWheelPresent) { + calc_freewheel_state(EnginePower * ClutchCtrlNorm, PowerRequired); + FWmult = FreeWheelLag.execute(FreeWheelTransmission); + } + + DeltaPower = EnginePower * ClutchCtrlNorm * FWmult - PowerRequired; + + CalcStatePart2(DeltaPower); + return Thrust; } @@ -702,7 +765,7 @@ bool FGRotor::BindModel(void) property_name = base_property_name + "/phi-downwash-rad"; PropertyManager->Tie( property_name.c_str(), this, &FGRotor::GetPhiDW ); - + switch (ControlMap) { case eTailCtrl: property_name = base_property_name + "/antitorque-ctrl-rad"; @@ -725,6 +788,11 @@ bool FGRotor::BindModel(void) PropertyManager->Tie( property_name.c_str(), this, &FGRotor::GetLongitudinalCtrl, &FGRotor::SetLongitudinalCtrl); } + property_name = base_property_name + "/brake-ctrl-norm"; + PropertyManager->Tie( property_name.c_str(), this, &FGRotor::GetBrakeCtrl, &FGRotor::SetBrakeCtrl); + property_name = base_property_name + "/free-wheel-transmission"; + PropertyManager->Tie( property_name.c_str(), this, &FGRotor::GetFreeWheelTransmission); + if (ExternalRPM) { if (RPMdefinition == -1) { property_name = base_property_name + "/x-rpm-dict"; @@ -826,6 +894,7 @@ void FGRotor::Debug(int from) cout << " Tip Loss = " << TipLossB << endl; cout << " Lock Number = " << LockNumberByRho * 0.002356 << " (SL)" << endl; cout << " Solidity = " << Solidity << endl; + cout << " Max Brake Power = " << MaxBrakePower/hptoftlbssec << " HP" << endl; switch (ControlMap) { case eTailCtrl: ControlMapName = "Tail Rotor"; break; @@ -834,6 +903,12 @@ void FGRotor::Debug(int from) } cout << " Control Mapping = " << ControlMapName << endl; + if (FreeWheelPresent) { + cout << " Free Wheel Threshold = " << FreeWheelThresh << endl; + } else { + cout << " No FWU present" << endl; + } + } } if (debug_lvl & 2 ) { // Instantiation/Destruction notification diff --git a/src/FDM/JSBSim/models/propulsion/FGRotor.h b/src/FDM/JSBSim/models/propulsion/FGRotor.h index 9892baa37..02fbf9295 100644 --- a/src/FDM/JSBSim/models/propulsion/FGRotor.h +++ b/src/FDM/JSBSim/models/propulsion/FGRotor.h @@ -27,6 +27,7 @@ HISTORY -------------------------------------------------------------------------------- 01/01/10 T.Kreitler test implementation 01/10/11 T.Kreitler changed to single rotor model +03/06/11 T.Kreitler added brake, clutch, and experimental free-wheeling-unit %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SENTRY @@ -45,7 +46,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_ROTOR "$Id: FGRotor.h,v 1.8 2011/01/17 22:09:59 jberndt Exp $" +#define ID_ROTOR "$Id: FGRotor.h,v 1.9 2011/03/10 01:35:25 dpculp Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -76,12 +77,15 @@ CLASS DOCUMENTATION <polarmoment unit="{MOMENT}"> {number} </polarmoment> <inflowlag> {number} </inflowlag> <tiplossfactor> {number} </tiplossfactor> + <maxbrakepower unit="{POWER}"> {number} </maxbrakepower> <controlmap> {MAIN|TAIL|TANDEM} </controlmap> <ExternalRPM> {number} </ExternalRPM> <groundeffectexp> {number} </groundeffectexp> <groundeffectshift unit="{LENGTH}"> {number} </groundeffectshift> + + <freewheelthresh> {number} </freewheelthresh> </rotor> // LENGTH means any of the supported units, same for ANGLE and MOMENT. @@ -108,10 +112,11 @@ CLASS DOCUMENTATION \<massmoment> - Blade mass moment. Mass of a single blade times the blade's cg-distance from the hub, optional. \<polarmoment> - Moment of inertia for the whole rotor disk, optional. - \<inflowlag> - Rotor inflow time constant, sec. Smaller values yield to - quicker responses to control input (defaults to 0.2). + \<inflowlag> - Rotor inflow time constant, sec. Smaller values yield to quicker + responses (typical values for main rotor: 0.1 - 0.2 s). \<tiplossfactor> - Tip-loss factor. The Blade fraction that produces lift. Value usually ranges between 0.95 - 1.0, optional (B). + \<maxbrakepower> - Rotor brake, 20-30 hp should work for a mid size helicopter. \<controlmap> - Defines the control inputs used (see notes). \<ExternalRPM> - Links the rotor to another rotor, or an user controllable property. @@ -125,6 +130,10 @@ CLASS DOCUMENTATION Omitting or setting to 0.0 disables the effect calculation. \<groundeffectshift> - Further adjustment of ground effect, approx. hub height or slightly above. + \<freewheelthresh> - Ratio of thruster power to engine power. The FWU will release when above + the threshold. The value shouldn't be too close to 1.0, 1.5 seems ok. + 0 disables this feature, which is also the default. + </pre> <h3>Notes:</h3> @@ -165,8 +174,6 @@ CLASS DOCUMENTATION <h4>- Engine issues -</h4> - Currently the rotor can only be driven with piston and electrical engines. An adaption - for the turboprop engine might become available in the future. In order to keep the rotor speed constant, use of a RPM-Governor system is encouraged (see examples). @@ -188,11 +195,13 @@ CLASS DOCUMENTATION <dt>/AM50/</dt><dd>Amer, Kenneth B.,"Theory of Helicopter Damping in Pitch or Roll and a Comparison With Flight Measurements", NACA TN-2136, 1950.</dd> <dt>/TA77/</dt><dd>Talbot, Peter D., Corliss, Lloyd D., "A Mathematical Force and Moment - Model of a UH-1H Helicopter for Flight Dynamics Simulations", NASA TM-73,254, 1977.</dd> + Model of a UH-1H Helicopter for Flight Dynamics Simulations", NASA TM-73,254, 1977.</dd> + <dt>/GE49/</dt><dd>Gessow, Alfred, Amer, Kenneth B. "An Introduction to the Physical + Aspects of Helicopter Stability", NACA TN-1982, 1949.</dd> </dl> @author Thomas Kreitler - @version $Id: FGRotor.h,v 1.8 2011/01/17 22:09:59 jberndt Exp $ + @version $Id: FGRotor.h,v 1.9 2011/03/10 01:35:25 dpculp Exp $ */ @@ -216,14 +225,11 @@ public: /// Destructor for FGRotor ~FGRotor(); - /** Returns the power required by the rotor. Well, to achieve this the rotor - is cycled through the whole machinery, yielding to a new state. - (hmm, sort of a huge side effect) - */ - double GetPowerRequired(void); + /** Returns the power required by the rotor. */ + double GetPowerRequired(void)const { return PowerRequired; } /** Returns the scalar thrust of the rotor, and adjusts the RPM value. */ - double Calculate(double PowerAvailable); + double Calculate(double EnginePower); /// Retrieves the RPMs of the rotor. @@ -257,6 +263,8 @@ public: double GetCT(void) const { return C_T; } /// Retrieves the torque double GetTorque(void) const { return Torque; } + /// Retrieves the state of the free-wheeling-unit (FWU). + double GetFreeWheelTransmission(void) const { return FreeWheelTransmission; } /// Downwash angle - currently only valid for a rotor that spins horizontally double GetThetaDW(void) const { return theta_downwash; } @@ -269,6 +277,8 @@ public: double GetLateralCtrl(void) const { return LateralCtrl; } /// Retrieves the longitudinal control input in radians. double GetLongitudinalCtrl(void) const { return LongitudinalCtrl; } + /// Retrieves the normalized brake control input. + double GetBrakeCtrl(void) const { return BrakeCtrlNorm; } /// Sets the collective control input in radians. void SetCollectiveCtrl(double c) { CollectiveCtrl = c; } @@ -276,6 +286,8 @@ public: void SetLateralCtrl(double c) { LateralCtrl = c; } /// Sets the longitudinal control input in radians. void SetLongitudinalCtrl(double c) { LongitudinalCtrl = c; } + /// Sets the normalized brake control input. + void SetBrakeCtrl(double c) { BrakeCtrlNorm = c; } // Stubs. Only main rotor RPM is returned string GetThrusterLabels(int id, string delimeter); @@ -303,6 +315,8 @@ private: void calc_drag_and_side_forces(double theta_0); void calc_torque(double theta_0); + void calc_freewheel_state(double pwr_in, double pwr_out); + // transformations FGColumnVector3 hub_vel_body2ca( const FGColumnVector3 &uvw, const FGColumnVector3 &pqr, double a_ic = 0.0 , double b_ic = 0.0 ); @@ -380,6 +394,15 @@ private: double LateralCtrl; double LongitudinalCtrl; + double BrakeCtrlNorm, MaxBrakePower; + + // free-wheeling-unit (FWU) + int FreeWheelPresent; // 'installed' or not + double FreeWheelThresh; // when to release + Filter FreeWheelLag; + double FreeWheelTransmission; // state, 0: free, 1:locked + + }; } diff --git a/src/FDM/JSBSim/models/propulsion/FGThruster.cpp b/src/FDM/JSBSim/models/propulsion/FGThruster.cpp index 5af8c1ddd..147aad5da 100644 --- a/src/FDM/JSBSim/models/propulsion/FGThruster.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGThruster.cpp @@ -45,7 +45,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGThruster.cpp,v 1.13 2010/08/21 22:56:11 jberndt Exp $"; +static const char *IdSrc = "$Id: FGThruster.cpp,v 1.14 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_THRUSTER; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -66,6 +66,7 @@ FGThruster::FGThruster(FGFDMExec *FDMExec, Element *el, int num ): FGForce(FDMEx GearRatio = 1.0; ReverserAngle = 0.0; + ClutchCtrlNorm = 1.0; EngineNum = num; PropertyManager = FDMExec->GetPropertyManager(); @@ -98,6 +99,13 @@ FGThruster::FGThruster(FGFDMExec *FDMExec, Element *el, int num ): FGForce(FDMEx &FGThruster::SetReverserAngle); } + if (el->GetName() == "rotor") // At this time only a rotor can have a clutch. + { + property_name = base_property_name + "/clutch-ctrl-norm"; + PropertyManager->Tie( property_name.c_str(), (FGThruster *)this, &FGThruster::GetClutchCtrl, + &FGThruster::SetClutchCtrl); + } + Debug(0); } diff --git a/src/FDM/JSBSim/models/propulsion/FGThruster.h b/src/FDM/JSBSim/models/propulsion/FGThruster.h index 118005f5a..4ef760e98 100644 --- a/src/FDM/JSBSim/models/propulsion/FGThruster.h +++ b/src/FDM/JSBSim/models/propulsion/FGThruster.h @@ -46,7 +46,7 @@ INCLUDES DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#define ID_THRUSTER "$Id: FGThruster.h,v 1.15 2009/10/24 22:59:30 jberndt Exp $" +#define ID_THRUSTER "$Id: FGThruster.h,v 1.16 2011/03/10 01:35:25 dpculp Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -74,7 +74,7 @@ CLASS DOCUMENTATION 1.57 (pi/2) results in no thrust at all. @author Jon Berndt - @version $Id: FGThruster.h,v 1.15 2009/10/24 22:59:30 jberndt Exp $ + @version $Id: FGThruster.h,v 1.16 2011/03/10 01:35:25 dpculp Exp $ */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -105,6 +105,8 @@ public: string GetName(void) {return Name;} void SetReverserAngle(double angle) {ReverserAngle = angle;} double GetReverserAngle(void) const {return ReverserAngle;} + double GetClutchCtrl(void) const { return ClutchCtrlNorm; } + void SetClutchCtrl(double c) { ClutchCtrlNorm = c; } virtual double GetRPM(void) const { return 0.0; }; double GetGearRatio(void) {return GearRatio; } virtual string GetThrusterLabels(int id, string delimeter); @@ -119,6 +121,7 @@ protected: double GearRatio; double ThrustCoeff; double ReverserAngle; + double ClutchCtrlNorm; int EngineNum; FGPropertyManager* PropertyManager; virtual void Debug(int from); diff --git a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp index 485c01c94..d7277ba87 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGTurbine.cpp @@ -51,7 +51,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGTurbine.cpp,v 1.29 2010/08/31 04:01:32 jberndt Exp $"; +static const char *IdSrc = "$Id: FGTurbine.cpp,v 1.31 2011/03/03 12:16:26 jberndt Exp $"; static const char *IdHdr = ID_TURBINE; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -74,6 +74,7 @@ FGTurbine::FGTurbine(FGFDMExec* exec, Element *el, int engine_number) BypassRatio = BleedDemand = 0.0; IdleThrustLookup = MilThrustLookup = MaxThrustLookup = InjectionLookup = 0; N1_spinup = 1.0; N2_spinup = 3.0; + EPR = 1.0; ResetToIC(); @@ -96,6 +97,9 @@ FGTurbine::~FGTurbine() void FGTurbine::ResetToIC(void) { + + FGEngine::ResetToIC(); + N1 = N2 = 0.0; N2norm = 0.0; correctedTSFC = TSFC; @@ -534,6 +538,8 @@ void FGTurbine::bindmodel() PropertyManager->Tie( property_name.c_str(), &Seized); property_name = base_property_name + "/stalled"; PropertyManager->Tie( property_name.c_str(), &Stalled); + property_name = base_property_name + "/bleed-factor"; + PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetBleedDemand, &FGTurbine::SetBleedDemand); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/FDM/JSBSim/models/propulsion/FGTurboProp.cpp b/src/FDM/JSBSim/models/propulsion/FGTurboProp.cpp old mode 100644 new mode 100755 index 981319961..acc35cca2 --- a/src/FDM/JSBSim/models/propulsion/FGTurboProp.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGTurboProp.cpp @@ -34,6 +34,7 @@ based on parameters given in the engine config file for this class HISTORY -------------------------------------------------------------------------------- 05/14/2004 Created +02/08/2011 T. Kreitler, added rotor support //JVK (mark) @@ -45,6 +46,7 @@ INCLUDES #include <sstream> #include "FGTurboProp.h" #include "FGPropeller.h" +#include "FGRotor.h" #include "models/FGPropulsion.h" #include "models/FGAuxiliary.h" @@ -52,7 +54,7 @@ using namespace std; namespace JSBSim { -static const char *IdSrc = "$Id: FGTurboProp.cpp,v 1.17 2010/08/21 17:13:48 jberndt Exp $"; +static const char *IdSrc = "$Id: FGTurboProp.cpp,v 1.19 2011/03/10 01:35:25 dpculp Exp $"; static const char *IdHdr = ID_TURBOPROP; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -64,8 +66,10 @@ FGTurboProp::FGTurboProp(FGFDMExec* exec, Element *el, int engine_number) ITT_N1(NULL), EnginePowerRPM_N1(NULL), EnginePowerVC(NULL) { SetDefaults(); + thrusterType = Thruster->GetType(); Load(exec, el); + bindmodel(); Debug(0); } @@ -101,6 +105,7 @@ bool FGTurboProp::Load(FGFDMExec* exec, Element *el) MaxN2 = el->FindElementValueAsNumber("maxn2"); if (el->FindElement("betarangeend")) BetaRangeThrottleEnd = el->FindElementValueAsNumber("betarangeend")/100.0; + BetaRangeThrottleEnd = Constrain(0.0, BetaRangeThrottleEnd, 0.99999); if (el->FindElement("reversemaxpower")) ReverseMaxPower = el->FindElementValueAsNumber("reversemaxpower")/100.0; @@ -146,10 +151,10 @@ bool FGTurboProp::Load(FGFDMExec* exec, Element *el) delay=1; N1_factor = MaxN1 - IdleN1; N2_factor = MaxN2 - IdleN2; - OilTemp_degK = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556 + 273.0; + OilTemp_degK = Auxiliary->GetTAT_C() + 273.0; if (IdleFF==-1) IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate - cout << "ENG POWER:" << EnginePowerRPM_N1->GetValue(1200,90) << "\n"; + // cout << "ENG POWER:" << EnginePowerRPM_N1->GetValue(1200,90) << endl; return true; } @@ -162,29 +167,29 @@ void FGTurboProp::Calculate(void) { RunPreFunctions(); - TAT = (Auxiliary->GetTotalTemperature() - 491.69) * 0.5555556; + TAT = Auxiliary->GetTAT_C(); dt = FDMExec->GetDeltaT() * Propulsion->GetRate(); - ThrottleCmd = FCS->GetThrottleCmd(EngineNumber); + Throttle = FCS->GetThrottlePos(EngineNumber); - Prop_RPM = Thruster->GetRPM() * Thruster->GetGearRatio(); - if (Thruster->GetType() == FGThruster::ttPropeller) { + RPM = Thruster->GetRPM() * Thruster->GetGearRatio(); + if (thrusterType == FGThruster::ttPropeller) { ((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber)); ((FGPropeller*)Thruster)->SetFeather(FCS->GetPropFeather(EngineNumber)); ((FGPropeller*)Thruster)->SetReverse(Reversed); if (Reversed) { - ((FGPropeller*)Thruster)->SetReverseCoef(ThrottleCmd); + ((FGPropeller*)Thruster)->SetReverseCoef(Throttle); } else { ((FGPropeller*)Thruster)->SetReverseCoef(0.0); } - } - if (Reversed) { - if (ThrottleCmd < BetaRangeThrottleEnd) { - ThrottleCmd = 0.0; // idle when in Beta-range - } else { - // when reversed: - ThrottleCmd = (ThrottleCmd-BetaRangeThrottleEnd)/(1-BetaRangeThrottleEnd) * ReverseMaxPower; + if (Reversed) { + if (Throttle < BetaRangeThrottleEnd) { + Throttle = 0.0; // idle when in Beta-range + } else { + // when reversed: + Throttle = (Throttle-BetaRangeThrottleEnd)/(1-BetaRangeThrottleEnd) * ReverseMaxPower; + } } } @@ -223,36 +228,41 @@ void FGTurboProp::Calculate(void) StartTime=-1; } - if (Condition < 1) { - if (Ielu_max_torque > 0 - && -Ielu_max_torque > ((FGPropeller*)(Thruster))->GetTorque() - && ThrottleCmd >= OldThrottle ) { - ThrottleCmd = OldThrottle - 0.1 * dt; //IELU down - Ielu_intervent = true; - } else if (Ielu_max_torque > 0 && Ielu_intervent && ThrottleCmd >= OldThrottle) { - ThrottleCmd = OldThrottle; - ThrottleCmd = OldThrottle + 0.05 * dt; //IELU up - Ielu_intervent = true; + // limiter intervention wanted? + if (Ielu_max_torque > 0.0) { + double torque = 0.0; + + if (thrusterType == FGThruster::ttPropeller) { + torque = ((FGPropeller*)(Thruster))->GetTorque(); + } else if (thrusterType == FGThruster::ttRotor) { + torque = ((FGRotor*)(Thruster))->GetTorque(); + } + + if (Condition < 1) { + if ( abs(torque) > Ielu_max_torque && Throttle >= OldThrottle ) { + Throttle = OldThrottle - 0.1 * dt; //IELU down + Ielu_intervent = true; + } else if ( Ielu_intervent && Throttle >= OldThrottle) { + Throttle = OldThrottle + 0.05 * dt; //IELU up + Ielu_intervent = true; + } else { + Ielu_intervent = false; + } } else { Ielu_intervent = false; } - } else { - Ielu_intervent = false; + OldThrottle = Throttle; } - OldThrottle = ThrottleCmd; switch (phase) { - case tpOff: Eng_HP = Off(); break; - case tpRun: Eng_HP = Run(); break; - case tpSpinUp: Eng_HP = SpinUp(); break; - case tpStart: Eng_HP = Start(); break; - default: Eng_HP = 0; + case tpOff: HP = Off(); break; + case tpRun: HP = Run(); break; + case tpSpinUp: HP = SpinUp(); break; + case tpStart: HP = Start(); break; + default: HP = 0; } - - //printf ("EngHP: %lf / Requi: %lf\n",Eng_HP,Prop_Required_Power); - PowerAvailable = (Eng_HP * hptoftlbssec) - Thruster->GetPowerRequired(); - - Thruster->Calculate(PowerAvailable); + + Thruster->Calculate(HP * hptoftlbssec); RunPostFunctions(); } @@ -280,7 +290,7 @@ double FGTurboProp::Off(void) ConsumeFuel(); // for possible setting Starved = false when fuel tank // is refilled (fuel crossfeed etc.) - if (Prop_RPM>5) return -0.012; // friction in engine when propeller spining (estimate) + if (RPM>5) return -0.012; // friction in engine when propeller spining (estimate) return 0.0; } @@ -293,9 +303,9 @@ double FGTurboProp::Run(void) //--- double old_N1 = N1; - N1 = ExpSeek(&N1, IdleN1 + ThrottleCmd * N1_factor, Idle_Max_Delay, Idle_Max_Delay * 2.4); + N1 = ExpSeek(&N1, IdleN1 + Throttle * N1_factor, Idle_Max_Delay, Idle_Max_Delay * 2.4); - EngPower_HP = EnginePowerRPM_N1->GetValue(Prop_RPM,N1); + EngPower_HP = EnginePowerRPM_N1->GetValue(RPM,N1); EngPower_HP *= EnginePowerVC->GetValue(); if (EngPower_HP > MaxPower) EngPower_HP = MaxPower; @@ -346,7 +356,7 @@ double FGTurboProp::SpinUp(void) OilPressure_psi = (N1/100.0*0.25+(0.1-(OilTemp_degK-273.15)*0.1/80.0)*N1/100.0) / 7692.0e-6; //from MPa to psi NozzlePosition = 1.0; - EngPower_HP = EnginePowerRPM_N1->GetValue(Prop_RPM,N1); + EngPower_HP = EnginePowerRPM_N1->GetValue(RPM,N1); EngPower_HP *= EnginePowerVC->GetValue(); if (EngPower_HP > MaxPower) EngPower_HP = MaxPower; @@ -366,13 +376,15 @@ double FGTurboProp::SpinUp(void) double FGTurboProp::Start(void) { - double EngPower_HP,eff_coef; + double EngPower_HP = 0.0; + double eff_coef; + EngStarting = false; if ((N1 > 15.0) && !Starved) { // minimum 15% N2 needed for start double old_N1 = N1; Cranking = true; // provided for sound effects signal if (N1 < IdleN1) { - EngPower_HP = EnginePowerRPM_N1->GetValue(Prop_RPM,N1); + EngPower_HP = EnginePowerRPM_N1->GetValue(RPM,N1); EngPower_HP *= EnginePowerVC->GetValue(); if (EngPower_HP > MaxPower) EngPower_HP = MaxPower; N1 = ExpSeek(&N1, IdleN1*1.1, Idle_Max_Delay*4, Idle_Max_Delay * 2.4); @@ -391,7 +403,6 @@ double FGTurboProp::Start(void) Starter = false; Cranking = false; FuelFlow_pph = 0; - EngPower_HP=0.0; } } else { // no start if N2 < 15% or Starved phase = tpOff; @@ -449,13 +460,14 @@ void FGTurboProp::SetDefaults(void) { // Name = "Not defined"; N1 = N2 = 0.0; + HP = 0.0; Type = etTurboprop; MilThrust = 10000.0; IdleN1 = 30.0; IdleN2 = 60.0; MaxN1 = 100.0; MaxN2 = 100.0; - ThrottleCmd = 0.0; + Throttle = 0.0; InletPosition = 1.0; NozzlePosition = 1.0; Reversed = false; @@ -472,6 +484,11 @@ void FGTurboProp::SetDefaults(void) Ielu_intervent=false; Idle_Max_Delay = 1.0; + + Throttle = OldThrottle = 0.0; + ITT_Delay = 0.05; + ReverseMaxPower = 0.0; + BetaRangeThrottleEnd = 0.0; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -495,9 +512,9 @@ string FGTurboProp::GetEngineValues(const string& delimiter) { std::ostringstream buf; - buf << PowerAvailable << delimiter - << N1 << delimiter + buf << N1 << delimiter << N2 << delimiter + << HP << delimiter << Thruster->GetThrusterValues(EngineNumber,delimiter); return buf.str(); @@ -524,10 +541,18 @@ void FGTurboProp::bindmodel() base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/n1"; PropertyManager->Tie( property_name.c_str(), &N1); - property_name = base_property_name + "/n2"; - PropertyManager->Tie( property_name.c_str(), &N2); + // property_name = base_property_name + "/n2"; + // PropertyManager->Tie( property_name.c_str(), &N2); property_name = base_property_name + "/reverser"; PropertyManager->Tie( property_name.c_str(), &Reversed); + property_name = base_property_name + "/power-hp"; + PropertyManager->Tie( property_name.c_str(), &HP); + property_name = base_property_name + "/itt-c"; + PropertyManager->Tie( property_name.c_str(), &Eng_ITT_degC); + property_name = base_property_name + "/engtemp-c"; + PropertyManager->Tie( property_name.c_str(), &Eng_Temperature); + property_name = base_property_name + "/ielu_intervent"; + PropertyManager->Tie( property_name.c_str(), &Ielu_intervent); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/FDM/JSBSim/models/propulsion/FGTurboProp.h b/src/FDM/JSBSim/models/propulsion/FGTurboProp.h old mode 100644 new mode 100755 index c73c0b765..1c5f48ae9 --- a/src/FDM/JSBSim/models/propulsion/FGTurboProp.h +++ b/src/FDM/JSBSim/models/propulsion/FGTurboProp.h @@ -27,6 +27,7 @@ HISTORY -------------------------------------------------------------------------------- 05/14/2004 Created +02/08/2011 T. Kreitler, added rotor support //JVK (mark) @@ -46,7 +47,7 @@ INCLUDES #include "input_output/FGXMLElement.h" #include "math/FGTable.h" -#define ID_TURBOPROP "$Id: FGTurboProp.h,v 1.12 2010/08/21 18:08:37 jberndt Exp $" +#define ID_TURBOPROP "$Id: FGTurboProp.h,v 1.14 2011/03/10 01:35:25 dpculp Exp $" /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FORWARD DECLARATIONS @@ -107,11 +108,10 @@ public: void Calculate(void); double CalcFuelNeed(void); - inline double GetPowerAvailable(void) const {return (Eng_HP * hptoftlbssec);} - inline double GetPowerAvailable_HP(void) const {return (Eng_HP);} - inline double GetPropRPM(void) const {return (Prop_RPM);} - inline double GetThrottleCmd(void) const {return (ThrottleCmd);} - inline bool GetIeluIntervent(void) const { return Ielu_intervent; } + double GetPowerAvailable(void) const { return (HP * hptoftlbssec); } + double GetRPM(void) const { return (RPM); } + double GetIeluThrottle(void) const { return (Throttle); } + bool GetIeluIntervent(void) const { return Ielu_intervent; } double Seek(double* var, double target, double accel, double decel); double ExpSeek(double* var, double target, double accel, double decel); @@ -165,9 +165,8 @@ private: double dt; ///< Simulator time slice double N1_factor; ///< factor to tie N1 and throttle double N2_factor; ///< factor to tie N2 and throttle - double ThrottleCmd; ///< FCS-supplied throttle position + double Throttle; ///< FCS-supplied throttle position double TAT; ///< total air temperature (deg C) - double PowerAvailable; bool Stalled; ///< true if engine is compressor-stalled bool Seized; ///< true if inner spool is seized bool Overtemp; ///< true if EGT exceeds limits @@ -189,26 +188,27 @@ private: double BetaRangeThrottleEnd; // coef (0-1) where is end of beta-range double ReverseMaxPower; // coef (0-1) multiplies max throttle on reverse - double Idle_Max_Delay; // time delay for exponencial + double Idle_Max_Delay; // time delay for exponential double MaxPower; // max engine power [HP] - double StarterN1; // rotates of generator maked by starter [%] + double StarterN1; // rotates of generator maked by starter [%] double MaxStartingTime; // maximal time for start [s] (-1 means not used) - double Prop_RPM; // propeller RPM + double RPM; // shaft RPM double Velocity; double rho; double PSFC; // Power specific fuel comsumption [lb/(HP*hr)] at best efficiency - double Eng_HP; // current engine power + double HP; // engine power output - double StartTime; // engine strating time [s] (0 when start button pushed) + double StartTime; // engine starting time [s] (0 when start button pushed) - double ITT_Delay; // time delay for exponencial grow of ITT + double ITT_Delay; // time delay for exponential growth of ITT double Eng_ITT_degC; double Eng_Temperature; // temperature inside engine bool EngStarting; // logicaly output - TRUE if engine is starting bool GeneratorPower; int Condition; + int thrusterType; // the attached thruster double Off(void); double Run(void); diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index cf7cc3319..afc7ad8fd 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -55,10 +55,15 @@ FGJoystickInput::joystick::joystick () FGJoystickInput::joystick::~joystick () { -// delete js? why not? -// delete js; + // delete js? no, since js == this - and we're in the destructor already. delete[] axes; delete[] buttons; + jsnum = 0; + js = NULL; + naxes = 0; + nbuttons = 0; + axes = NULL; + buttons = NULL; } @@ -68,13 +73,24 @@ FGJoystickInput::FGJoystickInput() FGJoystickInput::~FGJoystickInput() { + _remove(); } +void FGJoystickInput::_remove() +{ + SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true); + js_nodes->removeChildren("js", false); + for (int i = 0; i < MAX_JOYSTICKS; i++) + { + if (bindings[i].js) + delete bindings[i].js; + bindings[i].js = NULL; + } +} void FGJoystickInput::init() { jsInit(); - // TODO: zero the old bindings first. SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings"); SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true); @@ -121,8 +137,7 @@ void FGJoystickInput::init() void FGJoystickInput::reinit() { SG_LOG(SG_INPUT, SG_DEBUG, "Re-Initializing joystick bindings"); - SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true); - js_nodes->removeChildren("js", false); + _remove(); FGJoystickInput::init(); FGJoystickInput::postinit(); } diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx index 039401e29..392fc54b3 100644 --- a/src/Input/FGJoystickInput.hxx +++ b/src/Input/FGJoystickInput.hxx @@ -52,6 +52,8 @@ public: static const int MAX_JOYSTICK_BUTTONS = 32; private: + void _remove(); + /** * Settings for a single joystick axis. */ diff --git a/utils/fgpanel/.gitignore b/utils/fgpanel/.gitignore new file mode 100644 index 000000000..fd82bd006 --- /dev/null +++ b/utils/fgpanel/.gitignore @@ -0,0 +1,7 @@ +fgpanel +*.o +*.obj +.deps +*.Po +Makefile +Makefile.in diff --git a/utils/fgpanel/ApplicationProperties.hxx b/utils/fgpanel/ApplicationProperties.hxx new file mode 100644 index 000000000..f5578feb2 --- /dev/null +++ b/utils/fgpanel/ApplicationProperties.hxx @@ -0,0 +1,31 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __APPLICATION_PROPERTIES +#define __APPLICATION_PROPERTIES +#include <simgear/misc/sg_path.hxx> +#include <simgear/props/props.hxx> +#include "FGFontCache.hxx" +class ApplicationProperties { +public: + static double getDouble( const char * name, double def = 0.0 ); + static SGPath GetRootPath( const char * subDir = NULL ); + static SGPropertyNode_ptr Properties; + static std::string root; + static FGFontCache fontCache; +}; +#endif diff --git a/utils/fgpanel/FGFontCache.cxx b/utils/fgpanel/FGFontCache.cxx new file mode 100644 index 000000000..129e24743 --- /dev/null +++ b/utils/fgpanel/FGFontCache.cxx @@ -0,0 +1,208 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +using namespace std; +#include <map> +#include <algorithm> +#include "ApplicationProperties.hxx" +#include "FGFontCache.hxx" +//////////////////////////////////////////////////////////////////////// +// FGFontCache class. +//////////////////////////////////////////////////////////////////////// + +//extern puFont FONT_HELVETICA_14; +//extern puFont FONT_SANS_12B; + +namespace +{ +struct GuiFont +{ + const char *name; + puFont *font; + struct Predicate + : public std::unary_function<const GuiFont, bool> + { + Predicate(const char* name_) : name(name_) {} + bool operator() (const GuiFont& f1) const + { + return ::strcmp(f1.name, name) == 0; + } + const char* name; + }; +}; + +const GuiFont guifonts[] = { + { "default", &PUFONT_HELVETICA_12 }, + { "FIXED_8x13", &PUFONT_8_BY_13 }, + { "FIXED_9x15", &PUFONT_9_BY_15 }, + { "TIMES_10", &PUFONT_TIMES_ROMAN_10 }, + { "TIMES_24", &PUFONT_TIMES_ROMAN_24 }, + { "HELVETICA_10", &PUFONT_HELVETICA_10 }, + { "HELVETICA_12", &PUFONT_HELVETICA_12 }, +// { "HELVETICA_14", &FONT_HELVETICA_14 }, + { "HELVETICA_18", &PUFONT_HELVETICA_18 } +// { "SANS_12B", &FONT_SANS_12B } +}; + +const GuiFont* guifontsEnd = &guifonts[sizeof(guifonts)/ sizeof(guifonts[0])]; +} + +FGFontCache::FGFontCache() : + _initialized(false) +{ +} + +FGFontCache::~FGFontCache() +{ + PuFontMap::iterator it, end = _puFonts.end(); + for (it = _puFonts.begin(); it != end; ++it) + delete it->second; +} + +inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1, + const FntParams& f2) const +{ + int comp = f1.name.compare(f2.name); + if (comp < 0) + return true; + else if (comp > 0) + return false; + if (f1.size < f2.size) + return true; + else if (f1.size > f2.size) + return false; + return f1.slant < f2.slant; +} + +struct FGFontCache::fnt * +FGFontCache::getfnt(const char *name, float size, float slant) +{ + string fontName(name); + FntParams fntParams(fontName, size, slant); + PuFontMap::iterator i = _puFonts.find(fntParams); + if (i != _puFonts.end()) + return i->second; + // fntTexFont s are all preloaded into the _texFonts map + TexFontMap::iterator texi = _texFonts.find(fontName); + fntTexFont* texfont = 0; + puFont* pufont = 0; + if (texi != _texFonts.end()) { + texfont = texi->second; + } else { + const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd, + GuiFont::Predicate(name)); + if (guifont != guifontsEnd) { + pufont = guifont->font; + } + } + fnt* f = new fnt; + if (pufont) { + f->pufont = pufont; + } else if (texfont) { + f->texfont = texfont; + f->pufont = new puFont; + f->pufont->initialize(static_cast<fntFont *>(f->texfont), size, slant); + } else { + f->pufont = guifonts[0].font; + } + _puFonts[fntParams] = f; + return f; +} + +puFont * +FGFontCache::get(const char *name, float size, float slant) +{ + return getfnt(name, size, slant)->pufont; +} + +fntTexFont * +FGFontCache::getTexFont(const char *name, float size, float slant) +{ + init(); + return getfnt(name, size, slant)->texfont; +} + +puFont * +FGFontCache::get(SGPropertyNode *node) +{ + if (!node) + return get("Helvetica.txf", 15.0, 0.0); + + const char *name = node->getStringValue("name", "Helvetica.txf"); + float size = node->getFloatValue("size", 15.0); + float slant = node->getFloatValue("slant", 0.0); + + return get(name, size, slant); +} + +void FGFontCache::init() +{ + if (!_initialized) { + char *envp = ::getenv("FG_FONTS"); + if (envp != NULL) { + _path.set(envp); + } else { + _path.set(ApplicationProperties::GetRootPath("Fonts").str()); + } + _initialized = true; + } +} + +SGPath +FGFontCache::getfntpath(const char *name) +{ + init(); + SGPath path(_path); + if (name && std::string(name) != "") { + path.append(name); + if (path.exists()) + return path; + } + + path = SGPath(_path); + path.append("Helvetica.txf"); + + return path; +} + +bool FGFontCache::initializeFonts() +{ + static string fontext("txf"); + init(); + ulDir* fontdir = ulOpenDir(_path.c_str()); + if (!fontdir) + return false; + const ulDirEnt *dirEntry; + while ((dirEntry = ulReadDir(fontdir)) != 0) { + SGPath path(_path); + path.append(dirEntry->d_name); + if (path.extension() == fontext) { + fntTexFont* f = new fntTexFont; + if (f->load((char *)path.c_str())) + _texFonts[string(dirEntry->d_name)] = f; + else + delete f; + } + } + ulCloseDir(fontdir); + return true; +} + +// end of new_gui.cxx + diff --git a/utils/fgpanel/FGFontCache.hxx b/utils/fgpanel/FGFontCache.hxx new file mode 100644 index 000000000..81202eee4 --- /dev/null +++ b/utils/fgpanel/FGFontCache.hxx @@ -0,0 +1,86 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGFONTCACHE_HXX +#define __FGFONTCACHE_HXX +#include <simgear/misc/sg_path.hxx> +#include <simgear/props/props.hxx> +#include <plib/pu.h> +/** + * A class to keep all fonts available for future use. + * This also assures a font isn't resident more than once. + */ +class FGFontCache { +private: + // The parameters of a request to the cache. + struct FntParams + { + const std::string name; + const float size; + const float slant; + FntParams() : size(0.0f), slant(0.0f) {} + FntParams(const FntParams& rhs) + : name(rhs.name), size(rhs.size), slant(rhs.slant) + { + } + FntParams(const std::string& name_, float size_, float slant_) + : name(name_), size(size_), slant(slant_) + { + } + }; + struct FntParamsLess + : public std::binary_function<const FntParams, const FntParams, bool> + { + bool operator() (const FntParams& f1, const FntParams& f2) const; + }; + struct fnt { + fnt(puFont *pu = 0) : pufont(pu), texfont(0) {} + ~fnt() { if (texfont) { delete pufont; delete texfont; } } + // Font used by plib GUI code + puFont *pufont; + // TXF font + fntTexFont *texfont; + }; + // Path to the font directory + SGPath _path; + + typedef map<const string, fntTexFont*> TexFontMap; + typedef map<const FntParams, fnt*, FntParamsLess> PuFontMap; + TexFontMap _texFonts; + PuFontMap _puFonts; + + bool _initialized; + struct fnt *getfnt(const char *name, float size, float slant); + void init(); + +public: + FGFontCache(); + ~FGFontCache(); + + puFont *get(const char *name, float size=15.0, float slant=0.0); + puFont *get(SGPropertyNode *node); + + fntTexFont *getTexFont(const char *name, float size=15.0, float slant=0.0); + + SGPath getfntpath(const char *name); + /** + * Preload all the fonts in the FlightGear font directory. It is + * important to load the font textures early, with the proper + * graphics context current, so that no plib (or our own) code + * tries to load a font from disk when there's no current graphics + * context. + */ + bool initializeFonts(); +}; +#endif diff --git a/utils/fgpanel/FGGLApplication.cxx b/utils/fgpanel/FGGLApplication.cxx new file mode 100644 index 000000000..5c52943b2 --- /dev/null +++ b/utils/fgpanel/FGGLApplication.cxx @@ -0,0 +1,94 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGGLApplication.hxx" +#include "GL/gl.h" +#include "GL/glut.h" + +#include <iostream> +#include <exception> +#include <stdio.h> + +FGGLApplication * FGGLApplication::application = NULL; + +FGGLApplication::FGGLApplication( const char * aName, int argc, char ** argv ) : + gameMode(false), + name( aName ) +{ + if( application != NULL ) { + std::cerr << "Only one instance of FGGLApplication allowed!" << std::endl; + throw std::exception(); + } + application = this; + + glutInit( &argc, argv ); +} + +FGGLApplication::~FGGLApplication() +{ +} + +void FGGLApplication::DisplayCallback() +{ + if( application ) application->Display(); +} + +void FGGLApplication::IdleCallback() +{ + if( application ) application->Idle(); +} + +void FGGLApplication::KeyCallback( unsigned char key, int x, int y ) +{ + if( application ) application->Key( key, x, y ); +} + +void FGGLApplication::ReshapeCallback( int width, int height ) +{ + if( application ) application->Reshape( width, height ); +} + +void FGGLApplication::Run( int glutMode, bool gameMode, int width, int height, int bpp ) +{ + glutInitDisplayMode(glutMode); + if( gameMode ) { + width = glutGet(GLUT_SCREEN_WIDTH); + height = glutGet(GLUT_SCREEN_HEIGHT); + char game_mode_str[20]; + snprintf(game_mode_str, 20, "%dx%d:%d", width, height, bpp ); + glutGameModeString( game_mode_str ); + glutEnterGameMode(); + this->gameMode = gameMode; + } else { + if( width == -1 ) + width = glutGet(GLUT_SCREEN_WIDTH); + + if( height == -1 ) + height = glutGet(GLUT_SCREEN_HEIGHT); + + glutInitDisplayMode(glutMode); +// glutInitWindowSize(width, height); + windowId = glutCreateWindow(name); + } + Init(); + + glutKeyboardFunc(FGGLApplication::KeyCallback); + glutIdleFunc(FGGLApplication::IdleCallback); + glutDisplayFunc(FGGLApplication::DisplayCallback); + glutReshapeFunc(FGGLApplication::ReshapeCallback); + glutMainLoop(); +} diff --git a/utils/fgpanel/FGGLApplication.hxx b/utils/fgpanel/FGGLApplication.hxx new file mode 100644 index 000000000..aba699fa8 --- /dev/null +++ b/utils/fgpanel/FGGLApplication.hxx @@ -0,0 +1,48 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGGLAPPLICATION_HXX +#define __FGGLAPPLICATION_HXX + +class FGGLApplication { +public: + FGGLApplication( const char * name, int argc, char ** argv ); + virtual ~FGGLApplication(); + void Run( int glutMode, bool gameMode, int widht=-1, int height=-1, int bpp = 32 ); +protected: + virtual void Key( unsigned char key, int x, int y ) {} + virtual void Idle() {} + virtual void Display() {} + virtual void Reshape( int width, int height ) {} + + virtual void Init() {} + + int windowId; + bool gameMode; + + const char * name; + + static FGGLApplication * application; +private: + static void KeyCallback( unsigned char key, int x, int y ); + static void IdleCallback(); + static void DisplayCallback(); + static void ReshapeCallback( int width, int height ); + +}; + +#endif diff --git a/utils/fgpanel/FGPNGTextureLoader.cxx b/utils/fgpanel/FGPNGTextureLoader.cxx new file mode 100644 index 000000000..236792b57 --- /dev/null +++ b/utils/fgpanel/FGPNGTextureLoader.cxx @@ -0,0 +1,142 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPNGTextureLoader.hxx" + +#include <GL/glu.h> +#include <png.h> +#include <stdio.h> +#include <stdlib.h> + +#include <iostream> +using namespace std; +GLuint FGPNGTextureLoader::loadTexture( const string & filename ) +{ + //header for testing if it is a png + png_byte header[8]; + + //open file as binary + FILE *fp = fopen(filename.c_str(), "rb"); + if (!fp) { + return NOTEXTURE; + } + + //read the header + fread(header, 1, 8, fp); + + //test if png + int is_png = !png_sig_cmp(header, 0, 8); + if (!is_png) { + fclose(fp); + return NOTEXTURE; + } + + //create png struct + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + fclose(fp); + return (NOTEXTURE); + } + + //create png info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); + fclose(fp); + return (NOTEXTURE); + } + + //create png info struct + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(fp); + return (NOTEXTURE); + } + + //png error stuff, not sure libpng man suggests this. + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return (NOTEXTURE); + } + + //init png reading + png_init_io(png_ptr, fp); + + //let libpng know you already read the first 8 bytes + png_set_sig_bytes(png_ptr, 8); + + // read all the info up to the image data + png_read_info(png_ptr, info_ptr); + + //variables to pass to get info + int bit_depth, color_type; + png_uint_32 twidth, theight; + + // get info about png + png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type, + NULL, NULL, NULL); + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + + // Row size in bytes. + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // Allocate the image_data as a big block, to be given to opengl + png_byte *image_data = new png_byte[rowbytes * theight]; + if (!image_data) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return NOTEXTURE; + } + + //row_pointers is for pointing to image_data for reading the png with libpng + png_bytep *row_pointers = new png_bytep[theight]; + if (!row_pointers) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] image_data; + fclose(fp); + return NOTEXTURE; + } + // set the individual row_pointers to point at the correct offsets of image_data + for (png_uint_32 i = 0; i < theight; ++i) + row_pointers[theight - 1 - i] = image_data + i * rowbytes; + + //read the png into image_data through row_pointers + png_read_image(png_ptr, row_pointers); + + //Now generate the OpenGL texture object + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + gluBuild2DMipmaps( GL_TEXTURE_2D, 4, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)image_data ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); +// glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, twidth, theight, 0, +// GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] image_data; + delete[] row_pointers; + fclose(fp); + return texture; +} diff --git a/utils/fgpanel/FGPNGTextureLoader.hxx b/utils/fgpanel/FGPNGTextureLoader.hxx new file mode 100644 index 000000000..e8bbfa48c --- /dev/null +++ b/utils/fgpanel/FGPNGTextureLoader.hxx @@ -0,0 +1,26 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPNGTEXTURELOADER_HXX +#define __FGPNGTEXTURELOADER_HXX + +#include "FGTextureLoaderInterface.hxx" + +class FGPNGTextureLoader : public FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ); + + const static GLuint NOTEXTURE = 0; +}; +#endif diff --git a/utils/fgpanel/FGPanelApplication.cxx b/utils/fgpanel/FGPanelApplication.cxx new file mode 100644 index 000000000..e01f20695 --- /dev/null +++ b/utils/fgpanel/FGPanelApplication.cxx @@ -0,0 +1,279 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelApplication.hxx" +#include <GL/gl.h> +#include <GL/glut.h> + +#include <simgear/math/SGMisc.hxx> +#include <simgear/misc/sg_path.hxx> +#include <simgear/props/props_io.hxx> +#include <simgear/structure/exception.hxx> + +#include <iostream> + +#include "panel_io.hxx" +#include "ApplicationProperties.hxx" + +using namespace std; + +inline static string ParseArgs( int argc, char ** argv, const string & token ) +{ + for( int i = 0; i < argc; i++ ) { + string arg = argv[i]; + if( arg.find( token ) == 0 ) + return arg.substr( token.length() ); + } + return ""; +} + +inline static string ParseArgs( int argc, char ** argv, const char * token ) +{ + string s = token; + return ParseArgs( argc, argv, s ); +} + +#include "FGPNGTextureLoader.hxx" +#include "FGRGBTextureLoader.hxx" +static FGPNGTextureLoader pngTextureLoader; +static FGRGBTextureLoader rgbTextureLoader; + +FGPanelApplication::FGPanelApplication( int argc, char ** argv ) : + FGGLApplication( "FlightGear Panel", argc, argv ) +{ + sglog().setLogLevels( SG_ALL, SG_WARN ); + FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader ); + FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader ); + + string panelFilename; + string fgRoot; + + for( int i = 1; i < argc; i++ ) { + panelFilename = ParseArgs( argc, argv, "--panel=" ); + fgRoot = ParseArgs( argc, argv, "--fg-root=" ); + } + + if( fgRoot.length() > 0 ) + ApplicationProperties::root = fgRoot; + + if( panelFilename.length() == 0 ) { + cerr << "Need a panel filename. Use --panel=path_to_filename" << endl; + throw exception(); + } + + try { + SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() ); + readProperties( tpath.str(), ApplicationProperties::Properties ); + } + catch( sg_io_exception & e ) { + cerr << e.getFormattedMessage() << endl; + throw; + } + + for( int i = 1; i < argc; i++ ) { + string arg = argv[i]; + if( arg.find( "--prop:" ) == 0 ) { + string s2 = arg.substr( 7 ); + unsigned p = s2.find( "=" ); + if( p != string::npos ) { + string propertyName = s2.substr( 0, p ); + string propertyValue = s2.substr( p+1 ); + ApplicationProperties::Properties->getNode( propertyName.c_str(), true )->setValue( propertyValue.c_str() ); + } + } + } + + SGPropertyNode_ptr n; + if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL ) + panel = FGReadablePanel::read( n ); + + protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) ); + protocol->init(); +} + +FGPanelApplication::~FGPanelApplication() +{ +} + +void FGPanelApplication::Run() +{ + int mode = GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE; + int w = panel == NULL ? 0 : panel->getWidth(); + int h = panel == NULL ? 0 : panel->getHeight(); + if( w == 0 && h == 0 ) { + w = 1024; + h = 768; + } else if( w == 0 ) { + w = h / 0.75; + } else if( h == 0 ) { + h = w * 0.75; + } + + bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue(); + FGGLApplication::Run( mode, gameMode, w, h ); +} + +void FGPanelApplication::Init() +{ + glAlphaFunc(GL_GREATER, 0.1); + glutSetCursor( GLUT_CURSOR_NONE ); + ApplicationProperties::fontCache.initializeFonts(); +} + +void FGPanelApplication::Reshape( int width, int height ) +{ + this->width = width; + this->height = height; + glViewport(0, 0, (GLsizei) width, (GLsizei) height); +} + +void FGPanelApplication::Idle() +{ + double d = glutGet(GLUT_ELAPSED_TIME); + + double dt = Sleep(); + if( dt == 0 ) + return; + + if( panel != NULL ) + panel->update( dt ); + + glutSwapBuffers(); + + if( protocol != NULL ) + protocol->update( dt ); + + static double dsum = 0.0; + static unsigned cnt = 0; + dsum += glutGet(GLUT_ELAPSED_TIME)-d; + cnt++; + if( dsum > 1000.0 ) { + ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum ); + dsum = 0.0; + cnt = 0; + } +} + +void FGPanelApplication::Key( unsigned char key, int x, int y ) +{ + switch( key ) { + case 0x1b: + if( gameMode ) glutLeaveGameMode(); + else glutDestroyWindow( windowId ); + break; + } +} + +double FGPanelApplication::Sleep() +{ + SGTimeStamp current_time_stamp; + static SGTimeStamp last_time_stamp; + + if ( last_time_stamp.get_seconds() == 0 ) + last_time_stamp.stamp(); + + double model_hz = 60; + double throttle_hz = ApplicationProperties::getDouble("/sim/frame-rate-throttle-hz", 0.0); + if ( throttle_hz > 0.0 ) { + // optionally throttle the frame rate (to get consistent frame + // rates or reduce cpu usage. + + double frame_us = 1.0e6 / throttle_hz; + + // sleep based timing loop. + // + // Calling sleep, even usleep() on linux is less accurate than + // we like, but it does free up the cpu for other tasks during + // the sleep so it is desirable. Because of the way sleep() + // is implemented in consumer operating systems like windows + // and linux, you almost always sleep a little longer than the + // requested amount. + // + // To combat the problem of sleeping too long, we calculate the + // desired wait time and shorten it by 2000us (2ms) to avoid + // [hopefully] over-sleep'ing. The 2ms value was arrived at + // via experimentation. We follow this up at the end with a + // simple busy-wait loop to get the final pause timing exactly + // right. + // + // Assuming we don't oversleep by more than 2000us, this + // should be a reasonable compromise between sleep based + // waiting, and busy waiting. + + // sleep() will always overshoot by a bit so undersleep by + // 2000us in the hopes of never oversleeping. + frame_us -= 2000.0; + if ( frame_us < 0.0 ) { + frame_us = 0.0; + } + current_time_stamp.stamp(); + + /* Convert to ms */ + double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs(); + if ( elapsed_us < frame_us ) { + double requested_us = frame_us - elapsed_us; + usleep ( (useconds_t)(requested_us ) ) ; + } + // busy wait timing loop. + // + // This yields the most accurate timing. If the previous + // usleep() call is omitted this will peg the cpu + // (which is just fine if FG is the only app you care about.) + current_time_stamp.stamp(); + SGTimeStamp next_time_stamp = last_time_stamp; + next_time_stamp += SGTimeStamp::fromSec(1e-6*frame_us); + while ( current_time_stamp < next_time_stamp ) { + current_time_stamp.stamp(); + } + + } else { + current_time_stamp.stamp(); + } + + double real_delta_time_sec = double(current_time_stamp.toUSecs() - last_time_stamp.toUSecs()) / 1000000.0; + last_time_stamp = current_time_stamp; +//fprintf(stdout,"\r%4.1lf ", 1/real_delta_time_sec ); +//fflush(stdout); + + // round the real time down to a multiple of 1/model-hz. + // this way all systems are updated the _same_ amount of dt. + static double reminder = 0.0; + static long global_multi_loop = 0; + real_delta_time_sec += reminder; + global_multi_loop = long(floor(real_delta_time_sec*model_hz)); + global_multi_loop = SGMisc<long>::max(0, global_multi_loop); + reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz); + return double(global_multi_loop)/double(model_hz); +} + +double ApplicationProperties::getDouble( const char * name, double def ) +{ + SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false ); + if( n == NULL ) return def; + return n->getDoubleValue(); +} +SGPath ApplicationProperties::GetRootPath( const char * sub ) +{ + SGPath path( ApplicationProperties::root ); + if( sub != NULL ) + path.append( sub ); + return path; +} + +std::string ApplicationProperties::root = "."; +SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode; +FGFontCache ApplicationProperties::fontCache; diff --git a/utils/fgpanel/FGPanelApplication.hxx b/utils/fgpanel/FGPanelApplication.hxx new file mode 100644 index 000000000..f2652ee3a --- /dev/null +++ b/utils/fgpanel/FGPanelApplication.hxx @@ -0,0 +1,55 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPANELAPPLICATION_HXX +#define __FGPANELAPPLICATION_HXX + +#include "FGGLApplication.hxx" +#include "FGPanelProtocol.hxx" + +#include <simgear/structure/subsystem_mgr.hxx> +#include <simgear/props/props.hxx> + +#include <string> + +#include "panel.hxx" + +class FGPanelApplication : public FGGLApplication { +public: + FGPanelApplication( int argc, char ** argv ); + ~FGPanelApplication(); + + void Run(); + +protected: + virtual void Key( unsigned char key, int x, int y ); + virtual void Idle(); +// virtual void Display(); + virtual void Reshape( int width, int height ); + + virtual void Init(); + + double Sleep(); + + SGSharedPtr<FGPanel> panel; + SGSharedPtr<FGPanelProtocol> protocol; + + int width; + int height; +}; + +#endif diff --git a/utils/fgpanel/FGPanelProtocol.cxx b/utils/fgpanel/FGPanelProtocol.cxx new file mode 100644 index 000000000..0743724c8 --- /dev/null +++ b/utils/fgpanel/FGPanelProtocol.cxx @@ -0,0 +1,154 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelProtocol.hxx" +#include "ApplicationProperties.hxx" +#include <simgear/io/sg_socket.hxx> +#include <simgear/misc/strutils.hxx> + +class PropertySetter { +public: + PropertySetter( SGPropertyNode_ptr node ) : _node(node) {} + virtual void setValue( const char * value ) = 0; +protected: + SGPropertyNode_ptr _node; +}; + +class BoolPropertySetter : public PropertySetter { +public: + BoolPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {} + virtual void setValue( const char * value ) { + _node->setBoolValue( atoi( value ) != 0 ); + } +}; + +class IntPropertySetter : public PropertySetter { +public: + IntPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {} + virtual void setValue( const char * value ) { + _node->setIntValue( atol( value ) ); + } +}; + +class FloatPropertySetter : public PropertySetter { +public: + FloatPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {} + virtual void setValue( const char * value ) { + _node->setFloatValue( strtof( value, NULL ) ); + } +}; + +class DoublePropertySetter : public PropertySetter { +public: + DoublePropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {} + virtual void setValue( const char * value ) { + _node->setDoubleValue( strtod( value, NULL ) ); + } +}; + +class StringPropertySetter : public PropertySetter { +public: + StringPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {} + virtual void setValue( const char * value ) { + _node->setStringValue( value ); + } +}; + +FGPanelProtocol::FGPanelProtocol( SGPropertyNode_ptr aRoot ) + : SGSubsystem(), + root(aRoot), + io(NULL) +{ + SGPropertyNode_ptr outputNode = root->getNode( "protocol/generic/output" ); + if( outputNode ) { + vector<SGPropertyNode_ptr> chunks = outputNode->getChildren( "chunk" ); + for( vector<SGPropertyNode_ptr>::size_type i = 0; i < chunks.size(); i++ ) { + SGPropertyNode_ptr chunk = chunks[i]; + + SGPropertyNode_ptr nodeNode = chunk->getNode("node", false ); + if( nodeNode == NULL ) + continue; + + SGPropertyNode_ptr node = ApplicationProperties::Properties->getNode( nodeNode->getStringValue(), true ); + + string type = ""; + SGPropertyNode_ptr typeNode = chunk->getNode( "type", false ); + if( typeNode != NULL ) type = typeNode->getStringValue(); + if( type == "float" ) { + propertySetterVector.push_back( new FloatPropertySetter( node ) ); + } else if( type == "double" || type == "fixed" ) { + propertySetterVector.push_back( new DoublePropertySetter( node ) ); + } else if( type == "bool" || type == "boolean" ) { + propertySetterVector.push_back( new BoolPropertySetter( node ) ); + } else if( type == "string" ) { + propertySetterVector.push_back( new StringPropertySetter( node ) ); + } else { + propertySetterVector.push_back( new IntPropertySetter( node ) ); + } + } + } +} + +FGPanelProtocol::~FGPanelProtocol() +{ + for( PropertySetterVector::size_type i = 0; i < propertySetterVector.size(); i++ ) + delete propertySetterVector[i]; +} + +void FGPanelProtocol::update( double dt ) +{ + char buf[8192]; + + if( io == NULL ) + return; + + int length = io->readline( buf, sizeof(buf)-1 ); + buf[sizeof(buf)-1] = 0; + if ( length > 0 ) { + vector<string> tokens = simgear::strutils::split( buf, "," ); + for( vector<string>::size_type i = 0; i < tokens.size(); i++ ) { + if( i < propertySetterVector.size() ) + propertySetterVector[i]->setValue( tokens[i].c_str() ); + } + } +} + +void FGPanelProtocol::init() +{ + SGPropertyNode_ptr listenNode = root->getNode( "listen" ); + if( listenNode == NULL ) { + return; + } + + string hostname = listenNode->getNode( "host", true )->getStringValue(); + string port = listenNode->getNode( "port", true )->getStringValue(); + string style = listenNode->getNode( "style", true )->getStringValue(); + + if( io != NULL ) + delete io; + + io = new SGSocket( hostname, port, style ); + + if( !io->open( SG_IO_IN ) ) { + cerr << "can't open socket " << style << ":" << hostname << ":" << port << endl; + } +} + +void FGPanelProtocol::reinit() +{ + init(); +} diff --git a/utils/fgpanel/FGPanelProtocol.hxx b/utils/fgpanel/FGPanelProtocol.hxx new file mode 100644 index 000000000..5b08daf7f --- /dev/null +++ b/utils/fgpanel/FGPanelProtocol.hxx @@ -0,0 +1,41 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPANELPROTOCOL_HXX +#define __FGPANELPROTOCOL_HXX +#include <simgear/structure/subsystem_mgr.hxx> +#include <simgear/props/props.hxx> +#include <simgear/io/iochannel.hxx> +class PropertySetter; + +typedef vector<PropertySetter*> PropertySetterVector; +class FGPanelProtocol : public SGSubsystem { +public: + FGPanelProtocol( SGPropertyNode_ptr root ); + virtual ~FGPanelProtocol(); + virtual void init(); + virtual void reinit(); + virtual void update( double dt ); + +protected: + +private: + SGPropertyNode_ptr root; + SGIOChannel * io; + PropertySetterVector propertySetterVector; +}; +#endif diff --git a/utils/fgpanel/FGRGBTextureLoader.cxx b/utils/fgpanel/FGRGBTextureLoader.cxx new file mode 100644 index 000000000..faf136942 --- /dev/null +++ b/utils/fgpanel/FGRGBTextureLoader.cxx @@ -0,0 +1,504 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// From the OpenSceneGraph distribution ReaderWriterRGB.cpp +// Reader for sgi's .rgb format. +// specification can be found at http://local.wasp.uwa.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html + +#include "FGRGBTextureLoader.hxx" +#include <GL/gl.h> +#include <GL/glu.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> +#include <fstream> + +typedef struct _rawImageRec +{ + unsigned short imagic; + unsigned short type; + unsigned short dim; + unsigned short sizeX, sizeY, sizeZ; + unsigned long min, max; + unsigned long wasteBytes; + char name[80]; + unsigned long colorMap; + std::istream *file; + unsigned char *tmp, *tmpR, *tmpG, *tmpB, *tmpA; + unsigned long rleEnd; + GLuint *rowStart; + GLint *rowSize; + GLenum swapFlag; + short bpc; + + typedef unsigned char * BytePtr; + + bool needsBytesSwapped() + { + union { + int testWord; + char testByte[sizeof(int)]; + }endianTest; + endianTest.testWord = 1; + if( endianTest.testByte[0] == 1 ) + return true; + else + return false; + } + + template <class T> + inline void swapBytes( T &s ) + { + if( sizeof( T ) == 1 ) + return; + + T d = s; + BytePtr sptr = (BytePtr)&s; + BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]); + + for( unsigned int i = 0; i < sizeof(T); i++ ) + *(sptr++) = *(dptr--); + } + + void swapBytes() + { + swapBytes( imagic ); + swapBytes( type ); + swapBytes( dim ); + swapBytes( sizeX ); + swapBytes( sizeY ); + swapBytes( sizeZ ); + swapBytes( wasteBytes ); + swapBytes( min ); + swapBytes( max ); + swapBytes( colorMap ); + } +} rawImageRec; + +static void ConvertShort(unsigned short *array, long length) +{ + unsigned long b1, b2; + unsigned char *ptr; + + ptr = (unsigned char *)array; + while (length--) + { + b1 = *ptr++; + b2 = *ptr++; + *array++ = (unsigned short) ((b1 << 8) | (b2)); + } +} + +static void ConvertLong(GLuint *array, long length) +{ + unsigned long b1, b2, b3, b4; + unsigned char *ptr; + + ptr = (unsigned char *)array; + while (length--) + { + b1 = *ptr++; + b2 = *ptr++; + b3 = *ptr++; + b4 = *ptr++; + *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4); + } +} + + +static void RawImageClose(rawImageRec *raw) +{ + if (raw) + { + + if (raw->tmp) delete [] raw->tmp; + if (raw->tmpR) delete [] raw->tmpR; + if (raw->tmpG) delete [] raw->tmpG; + if (raw->tmpB) delete [] raw->tmpB; + if (raw->tmpA) delete [] raw->tmpA; + + if (raw->rowStart) delete [] raw->rowStart; + if (raw->rowSize) delete [] raw->rowSize; + + delete raw; + } +} + + +static rawImageRec *RawImageOpen(std::istream& fin) +{ + union + { + int testWord; + char testByte[4]; + } endianTest; + rawImageRec *raw; + int x; + + raw = new rawImageRec; + if (raw == NULL) + { +// notify(WARN)<< "Out of memory!"<< std::endl; + return NULL; + } + + //Set istream pointer + raw->file = &fin; + + endianTest.testWord = 1; + if (endianTest.testByte[0] == 1) + { + raw->swapFlag = GL_TRUE; + } + else + { + raw->swapFlag = GL_FALSE; + } + + fin.read((char*)raw,12); + if (!fin.good()) + return NULL; + + if (raw->swapFlag) + { + ConvertShort(&raw->imagic, 6); + } + + raw->tmp = raw->tmpR = raw->tmpG = raw->tmpB = raw->tmpA = 0L; + raw->rowStart = 0; + raw->rowSize = 0; + raw->bpc = (raw->type & 0x00FF); + + raw->tmp = new unsigned char [raw->sizeX*256*raw->bpc]; + if (raw->tmp == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + + if( raw->sizeZ >= 1 ) + { + if( (raw->tmpR = new unsigned char [raw->sizeX*raw->bpc]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + } + if( raw->sizeZ >= 2 ) + { + if( (raw->tmpG = new unsigned char [raw->sizeX*raw->bpc]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + } + if( raw->sizeZ >= 3 ) + { + if( (raw->tmpB = new unsigned char [raw->sizeX*raw->bpc]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + } + if (raw->sizeZ >= 4) + { + if( (raw->tmpA = new unsigned char [raw->sizeX*raw->bpc]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + } + + if ((raw->type & 0xFF00) == 0x0100) + { + unsigned int ybyz = raw->sizeY * raw->sizeZ; + if ( (raw->rowStart = new GLuint [ybyz]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + + if ( (raw->rowSize = new GLint [ybyz]) == NULL ) + { +// notify(FATAL)<< "Out of memory!"<< std::endl; + RawImageClose(raw); + return NULL; + } + x = ybyz * sizeof(GLuint); + raw->rleEnd = 512 + (2 * x); + fin.seekg(512,std::ios::beg); + fin.read((char*)raw->rowStart,x); + fin.read((char*)raw->rowSize,x); + if (raw->swapFlag) + { + ConvertLong(raw->rowStart, (long) (x/sizeof(GLuint))); + ConvertLong((GLuint *)raw->rowSize, (long) (x/sizeof(GLint))); + } + } + return raw; +} + + +static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z) +{ + unsigned char *iPtr, *oPtr; + unsigned short pixel; + int count, done = 0; + unsigned short *tempShort; + + if ((raw->type & 0xFF00) == 0x0100) + { + raw->file->seekg((long) raw->rowStart[y+z*raw->sizeY], std::ios::beg); + raw->file->read((char*)raw->tmp, (unsigned int)raw->rowSize[y+z*raw->sizeY]); + + iPtr = raw->tmp; + oPtr = buf; + while (!done) + { + if (raw->bpc == 1) + pixel = *iPtr++; + else + { + tempShort = reinterpret_cast<unsigned short*>(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast<unsigned char *>(tempShort); + } + + if(raw->bpc != 1) + ConvertShort(&pixel, 1); + + count = (int)(pixel & 0x7F); + + // limit the count value to the remiaing row size + if (oPtr + count*raw->bpc > buf + raw->sizeX*raw->bpc) + { + count = ( (buf + raw->sizeX*raw->bpc) - oPtr ) / raw->bpc; + } + + if (count<=0) + { + done = 1; + return; + } + + if (pixel & 0x80) + { + while (count--) + { + if(raw->bpc == 1) + *oPtr++ = *iPtr++; + else{ + tempShort = reinterpret_cast<unsigned short*>(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast<unsigned char *>(tempShort); + + ConvertShort(&pixel, 1); + + tempShort = reinterpret_cast<unsigned short*>(oPtr); + *tempShort = pixel; + tempShort++; + oPtr = reinterpret_cast<unsigned char *>(tempShort); + } + } + } + else + { + if (raw->bpc == 1) + { + pixel = *iPtr++; + } + else + { + tempShort = reinterpret_cast<unsigned short*>(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast<unsigned char *>(tempShort); + } + if(raw->bpc != 1) + ConvertShort(&pixel, 1); + while (count--) + { + if(raw->bpc == 1) + *oPtr++ = pixel; + else + { + tempShort = reinterpret_cast<unsigned short*>(oPtr); + *tempShort = pixel; + tempShort++; + oPtr = reinterpret_cast<unsigned char *>(tempShort); + } + } + } + } + } + else + { + raw->file->seekg(512+(y*raw->sizeX*raw->bpc)+(z*raw->sizeX*raw->sizeY*raw->bpc),std::ios::beg); + raw->file->read((char*)buf, raw->sizeX*raw->bpc); + if(raw->swapFlag && raw->bpc != 1){ + ConvertShort(reinterpret_cast<unsigned short*>(buf), raw->sizeX); + } + } +} + + +static void RawImageGetData(rawImageRec *raw, unsigned char **data ) +{ + unsigned char *ptr; + int i, j; + unsigned short *tempShort; + + // // round the width to a factor 4 + // int width = (int)(floorf((float)raw->sizeX/4.0f)*4.0f); + // if (width!=raw->sizeX) width += 4; + + // byte aligned. + +// osg::notify(osg::INFO)<<"raw->sizeX = "<<raw->sizeX<<std::endl; +// osg::notify(osg::INFO)<<"raw->sizeY = "<<raw->sizeY<<std::endl; +// osg::notify(osg::INFO)<<"raw->sizeZ = "<<raw->sizeZ<<std::endl; +// osg::notify(osg::INFO)<<"raw->bpc = "<<raw->bpc<<std::endl; + + *data = new unsigned char [(raw->sizeX)*(raw->sizeY)*(raw->sizeZ)*(raw->bpc)]; + + ptr = *data; + for (i = 0; i < (int)(raw->sizeY); i++) + { + if( raw->sizeZ >= 1 ) + RawImageGetRow(raw, raw->tmpR, i, 0); + if( raw->sizeZ >= 2 ) + RawImageGetRow(raw, raw->tmpG, i, 1); + if( raw->sizeZ >= 3 ) + RawImageGetRow(raw, raw->tmpB, i, 2); + if( raw->sizeZ >= 4 ) + RawImageGetRow(raw, raw->tmpA, i, 3); + for (j = 0; j < (int)(raw->sizeX); j++) + { + if(raw->bpc == 1){ + if( raw->sizeZ >= 1 ) + *ptr++ = *(raw->tmpR + j); + if( raw->sizeZ >= 2 ) + *ptr++ = *(raw->tmpG + j); + if( raw->sizeZ >= 3 ) + *ptr++ = *(raw->tmpB + j); + if( raw->sizeZ >= 4 ) + *ptr++ = *(raw->tmpA + j); + }else{ + if( raw->sizeZ >= 1 ) + { + tempShort = reinterpret_cast<unsigned short*>(ptr); + *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpR) + j); + tempShort++; + ptr = reinterpret_cast<unsigned char *>(tempShort); + } + if( raw->sizeZ >= 2 ) + { + tempShort = reinterpret_cast<unsigned short*>(ptr); + *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpG) + j); + tempShort++; + ptr = reinterpret_cast<unsigned char *>(tempShort); + } + if( raw->sizeZ >= 3 ) + { + tempShort = reinterpret_cast<unsigned short*>(ptr); + *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpB) + j); + tempShort++; + ptr = reinterpret_cast<unsigned char *>(tempShort); + } + if( raw->sizeZ >= 4 ) + { + tempShort = reinterpret_cast<unsigned short*>(ptr); + *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpA) + j); + tempShort++; + ptr = reinterpret_cast<unsigned char *>(tempShort); + } + } + } + // // pad the image width with blanks to bring it up to the rounded width. + // for(;j<width;++j) *ptr++ = 0; + } +} + + +// supportsExtension("rgb","rgb image format"); +// supportsExtension("rgba","rgba image format"); +// supportsExtension("sgi","sgi image format"); +// supportsExtension("int","int image format"); +// supportsExtension("inta","inta image format"); +// supportsExtension("bw","bw image format"); + + GLuint readRGBStream(std::istream& fin) + { + rawImageRec *raw; + + if( (raw = RawImageOpen(fin)) == NULL ) + { + return 0; + } + + int s = raw->sizeX; + int t = raw->sizeY; +// int r = 1; + + #if 0 + int internalFormat = raw->sizeZ == 3 ? GL_RGB5 : + raw->sizeZ == 4 ? GL_RGB5_A1 : GL_RGB; + #else +// int internalFormat = raw->sizeZ; + #endif + unsigned int pixelFormat = + raw->sizeZ == 1 ? GL_LUMINANCE : + raw->sizeZ == 2 ? GL_LUMINANCE_ALPHA : + raw->sizeZ == 3 ? GL_RGB : + raw->sizeZ == 4 ? GL_RGBA : (GLenum)-1; + GLint component = raw->sizeZ; + + unsigned int dataType = raw->bpc == 1 ? GL_UNSIGNED_BYTE : + GL_UNSIGNED_SHORT; + + unsigned char *data; + RawImageGetData(raw, &data); + RawImageClose(raw); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + gluBuild2DMipmaps( GL_TEXTURE_2D, component, s, t, pixelFormat, dataType, (GLvoid*)data ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + + delete []data; + return texture; + } + +GLuint FGRGBTextureLoader::loadTexture( const std::string & filename ) +{ + GLuint texture = NOTEXTURE; + std::ifstream istream(filename.c_str(), std::ios::in | std::ios::binary ); + texture = readRGBStream(istream); + istream.close(); + return texture; +} + diff --git a/utils/fgpanel/FGRGBTextureLoader.hxx b/utils/fgpanel/FGRGBTextureLoader.hxx new file mode 100644 index 000000000..e3c27664b --- /dev/null +++ b/utils/fgpanel/FGRGBTextureLoader.hxx @@ -0,0 +1,26 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGRGBTEXTURELOADER_HXX +#define __FGRGBTEXTURELOADER_HXX + +#include "FGTextureLoaderInterface.hxx" + +class FGRGBTextureLoader : public FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ); + + const static GLuint NOTEXTURE = 0; +}; +#endif diff --git a/utils/fgpanel/FGTextureLoaderInterface.hxx b/utils/fgpanel/FGTextureLoaderInterface.hxx new file mode 100644 index 000000000..58c15b853 --- /dev/null +++ b/utils/fgpanel/FGTextureLoaderInterface.hxx @@ -0,0 +1,27 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGTEXTURELOADERINTERFACE_HXX +#define __FGTEXTURELOADERINTERFACE_HXX + +#include <GL/gl.h> +#include <string> +class FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ) = 0; +}; +#endif diff --git a/utils/fgpanel/Makefile.am b/utils/fgpanel/Makefile.am new file mode 100644 index 000000000..58804e9c5 --- /dev/null +++ b/utils/fgpanel/Makefile.am @@ -0,0 +1,20 @@ +AM_CXXFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" + +bin_PROGRAMS = fgpanel + +fgpanel_SOURCES = main.cxx \ + FGGLApplication.cxx FGGLApplication.hxx \ + FGPanelApplication.cxx FGPanelApplication.hxx \ + FGPNGTextureLoader.cxx FGPNGTextureLoader.hxx FGTextureLoaderInterface.hxx \ + FGRGBTextureLoader.cxx FGRGBTextureLoader.hxx \ + FGPanelProtocol.cxx \ + FGFontCache.cxx \ + panel.cxx panel.hxx \ + panel_io.cxx panel_io.hxx + +LIBS = + +fgpanel_LDADD = \ + -lGLU -lglut -lsgmath -lsgprops -lsgio -lsgdebug -lsgmisc -lsgstructure -lsgxml -lsgtiming \ + -lplibpu -lplibfnt -lplibul \ + -lrt -lpng diff --git a/utils/fgpanel/README b/utils/fgpanel/README new file mode 100644 index 000000000..a1199d2c9 --- /dev/null +++ b/utils/fgpanel/README @@ -0,0 +1,148 @@ +===================================================================== +This is fgpanel - basically the stripped down 2D-Panel code from +FlightGear. It is designed as a standalone lightweight panel +rendering engine to draw 2d panels on a lowcost computer/graphic card +without 3d acceleration at reasonablel framerates. + +===================================================================== +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +===================================================================== +Usage +start fgpanel with +fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml +with the command args set to +--fg-root shall point to the directory where your FGDATA lives + NOTE: you don't need a full copy of FGDATA, just the panel definition files for + your aircraft, e.g. + - Aircraft/MyAircraft/Panels/* + - Aircraft/Instruments/* (if referenced) + +-panel shall point to a panel-configuration file, relative to FGDATA + +start flightgear with +fgfs --generic=socket,out,10,239.24.10.64,5432,udp,../Aircraft/MyAircraft/Panels/SampleProtocol + +===================================================================== +Sample: + +Create the sample files within your aicraft directory, preferrable under Panels +MyPanel.xml +sample-2d-panel.xml +SampleProtocol.xml + +===================================================================== +Sample panel configuration file (MyPanel.xml) +<PropertyList> + + <!-- true: run full-screen, false; run in window --> + <game-mode type="bool">false</game-mode> + + <!-- include the panel definitions (2d-panel syntax)--> + <panel include="sample-2d-panel.xml"/> + + <!-- compose your property-tree here --> + <sim> + <panel> + <flip-x type="bool">false</flip-x> + </panel> + <instrument-options> + <omit-knobs type="bool">true</omit-knobs> + </instrument-options> + </sim> + + <!-- network communication settings --> + <communication> + <listen> + <!-- interface to bind to, + leave empty for all interfaces --> + <host>239.24.10.64</host> <!-- multicast address! --> + <port>5432</port> <!-- tcp port to listen to --> + <style>udp</style> <!-- udp or tcp (forget about tcp!) --> + </listen> + + <!-- the generic protocol definition + same as used for fgfs --generic=foobar option + --> + <protocol include="SampleProtocol.xml"/> + </communication> +</PropertyList> + +===================================================================== +Sampe 2d-panel configuration file sample-2d-panel.xml +To be included from the panel configuration file + +<?xml version="1.0"?> +<PropertyList> + <name>Sample Instrument Panel</name> + <w>375</w> <!-- screen width: 375mm --> + <h>305</h> <!-- screen height: 305mm --> + <instruments> + <!-- use FlightGear's c172 attitude indicator --> + <instrument include="../../Instruments/ati-c172s.xml"> + <name>Attitude Gyro</name> + <x alias="../../../params/col-2"/> + <y alias="../../../params/row-1"/> + <w>80</w> + <h>80</h> + </instrument> + </instruments> +</PropertyList> + +===================================================================== +Sample protocol configuration file to drive the AI (SampleProtocol.xml) +<?xml version="1.0"?> + +<PropertyList> + <generic> + + <output> + <line_separator>newline</line_separator> + <var_separator>,</var_separator> + + <chunk> + <type>float</type> + <format>%.2f</format> + <node>/position/altitude-agl-ft</node> + </chunk> + + <chunk> + <type>float</type> + <format>%.2f</format> + <node>/instrumentation/attitude-indicator/indicated-roll-deg</node> + </chunk> + + <chunk> + <type>float</type> + <format>%.2f</format> + <node>/instrumentation/attitude-indicator/indicated-pitch-deg</node> + </chunk> + + <chunk> + <type>float</type> + <format>%.2f</format> + <node>/instrumentation/attitude-indicator/horizon-offset-deg</node> + </chunk> + + <chunk> + <type>float</type> + <format>%.4e</format> + <node>/instrumentation/attitude-indicator/spin</node> + </chunk> + </output> + </generic> +</PropertyList> + +===================================================================== diff --git a/utils/fgpanel/main.cxx b/utils/fgpanel/main.cxx new file mode 100644 index 000000000..80215e94a --- /dev/null +++ b/utils/fgpanel/main.cxx @@ -0,0 +1,30 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelApplication.hxx" + +int main( int argc, char ** argv ) +{ + try { + FGPanelApplication app(argc,argv); + app.Run(); + return 0; + } + catch( ... ) { + cerr << "Sorry, your program terminated." << endl; + } +} diff --git a/utils/fgpanel/panel.cxx b/utils/fgpanel/panel.cxx new file mode 100644 index 000000000..89044997f --- /dev/null +++ b/utils/fgpanel/panel.cxx @@ -0,0 +1,962 @@ +// panel.cxx - default, 2D single-engine prop instrument panel +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel.cxx,v 1.44 2006/09/05 20:28:48 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_WINDOWS_H +# include <windows.h> +#endif + +#include <stdio.h> // sprintf +#include <string.h> + +#include <simgear/compiler.h> + +#include <GL/glut.h> + +#include <plib/fnt.h> + +#include <simgear/debug/logstream.hxx> +#include <simgear/misc/sg_path.hxx> + +#include "panel.hxx" +#include "ApplicationProperties.hxx" +//////////////////////////////////////////////////////////////////////// +// Local functions. +//////////////////////////////////////////////////////////////////////// + +class FGDummyTextureLoader : public FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const string & filename ); +}; + +GLuint FGDummyTextureLoader::loadTexture( const string & filename ) +{ + GLuint _texture = 0; + glGenTextures( 1, &_texture ); + glBindTexture( GL_TEXTURE_2D, _texture ); + +// glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; +// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ; +// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ) ; +// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ; +// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ; + + GLubyte image[ 2 * 2 * 3 ] ; + + /* Red and white chequerboard */ + image [ 0 ] = 255 ; image [ 1 ] = 0 ; image [ 2 ] = 0 ; + image [ 3 ] = 255 ; image [ 4 ] = 255 ; image [ 5 ] = 255 ; + image [ 6 ] = 255 ; image [ 7 ] = 255 ; image [ 8 ] = 255 ; + image [ 9 ] = 255 ; image [ 10] = 0 ; image [ 11] = 0 ; + + glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*) image); + + return _texture; +} + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGCropped Texture. +//////////////////////////////////////////////////////////////////////// + +GLuint FGCroppedTexture::current_bound_texture = 0; +map<string,GLuint> FGCroppedTexture::cache; +map<string,FGTextureLoaderInterface*> FGCroppedTexture::textureLoader; +static FGDummyTextureLoader dummyTextureLoader; + +FGCroppedTexture::FGCroppedTexture (const string &path, + float minX, float minY, + float maxX, float maxY) + : _path(path), + _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY), _texture(0) +{ +} + +FGCroppedTexture::~FGCroppedTexture () +{ +} + +void FGCroppedTexture::bind( bool doGLBind ) +{ + if( _texture == 0 ) { + SG_LOG( SG_COCKPIT, SG_DEBUG, "First bind of texture " << _path ); + if( cache.count(_path) > 0 ) { + _texture = cache[_path]; + SG_LOG( SG_COCKPIT, SG_DEBUG, "Using texture " << _path << " from cache (#" << _texture << ")" ); + } else { + SGPath tpath = ApplicationProperties::GetRootPath(_path.c_str()); + string extension = tpath.extension(); + FGTextureLoaderInterface * loader = &dummyTextureLoader; + if( textureLoader.count( extension ) == 0 ) { + SG_LOG( SG_COCKPIT, SG_ALERT, "Can't handle textures of type " << extension ); + } else { + loader = textureLoader[extension]; + } + + _texture = loader->loadTexture( tpath.c_str() ); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Texture " << tpath.c_str() << " loaded from file as #" << _texture ); + + cache[_path] = _texture; + } + } + + if( !doGLBind || current_bound_texture == _texture ) + return; + + glBindTexture( GL_TEXTURE_2D, _texture ); + current_bound_texture = _texture; +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGPanel. +//////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + */ +FGPanel::FGPanel ( SGPropertyNode_ptr root) + : _root(root), + _flipx(root->getNode("/sim/panel/flip-x", true)), + _rotate(root->getNode("/sim/panel/rotate-deg", true)), + _bg_width(1.0), _bg_height(1.0), + initDisplayList(0) +{ +} + + +/** + * Destructor. + */ +FGPanel::~FGPanel () +{ + for (instrument_list_type::iterator it = _instruments.begin(); + it != _instruments.end(); + it++) { + delete *it; + *it = 0; + } +} + + +/** + * Add an instrument to the panel. + */ +void +FGPanel::addInstrument (FGPanelInstrument * instrument) +{ + _instruments.push_back(instrument); +} + + +/** + * Initialize the panel. + */ +void +FGPanel::init () +{ +} + + +/** + * Bind panel properties. + */ +void +FGPanel::bind () +{ +} + + +/** + * Unbind panel properties. + */ +void +FGPanel::unbind () +{ +} + +GLuint FGPanel::getInitDisplayList() +{ + if( initDisplayList != 0 ) return initDisplayList; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if ( _flipx->getBoolValue() ) { + gluOrtho2D( _width, 0, _height, 0 ); /* up side down */ + } else { + gluOrtho2D( 0, _width, 0, _height ); /* right side up */ + } + + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glClear( GL_COLOR_BUFFER_BIT); + + // save some state + glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT + | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE + | GL_DEPTH_BUFFER_BIT ); + + // Draw the background + glEnable(GL_TEXTURE_2D); + + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glEnable(GL_COLOR_MATERIAL); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_DEPTH_TEST); + + if (_bg != NULL) { + _bg->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f(0, 0); + glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0); + glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height); + glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height); + glEnd(); + } else if( _mbg[0] != NULL ) { + for (int i = 0; i < 4; i ++) { + // top row of textures...(1,3,5,7) + _mbg[i*2]->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4, _height/2); + glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2); + glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height); + glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4, _height); + glEnd(); + // bottom row of textures...(2,4,6,8) + _mbg[i*2+1]->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4, 0); + glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0); + glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2); + glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4, _height/2); + glEnd(); + } + } else { + float c[4]; + glGetFloatv( GL_CURRENT_COLOR, c ); + glColor4f( 0.0, 0.0, 0.0, 1.0 ); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(_width, 0); + glVertex2f(_width, _height); + glVertex2f(0, _height); + glEnd(); + glColor4fv( c ); + } + + + return initDisplayList; +} + +void +FGPanel::update (double dt) +{ + glCallList(getInitDisplayList()); + + // Draw the instruments. + // Syd Adams: added instrument clipping + instrument_list_type::const_iterator current = _instruments.begin(); + instrument_list_type::const_iterator end = _instruments.end(); + + GLdouble blx[4]={1.0,0.0,0.0,0.0}; + GLdouble bly[4]={0.0,1.0,0.0,0.0}; + GLdouble urx[4]={-1.0,0.0,0.0,0.0}; + GLdouble ury[4]={0.0,-1.0,0.0,0.0}; + + for ( ; current != end; current++) { + FGPanelInstrument * instr = *current; + glPushMatrix(); + glTranslated(instr->getXPos(), instr->getYPos(), 0); + + int ix= instr->getWidth(); + int iy= instr->getHeight(); + glPushMatrix(); + glTranslated(-ix/2,-iy/2,0); + glClipPlane(GL_CLIP_PLANE0,blx); + glClipPlane(GL_CLIP_PLANE1,bly); + glEnable(GL_CLIP_PLANE0); + glEnable(GL_CLIP_PLANE1); + + glTranslated(ix,iy,0); + glClipPlane(GL_CLIP_PLANE2,urx); + glClipPlane(GL_CLIP_PLANE3,ury); + glEnable(GL_CLIP_PLANE2); + glEnable(GL_CLIP_PLANE3); + glPopMatrix(); + instr->draw(); + + glPopMatrix(); + } + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + // restore some original state + glPopAttrib(); +} + +#if 0 +/** + * Update the panel. + */ +void +FGPanel::update (double dt) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + if ( _flipx->getBoolValue() ) { + gluOrtho2D( _width, 0, _height, 0 ); /* up side down */ + } else { + gluOrtho2D( 0, _width, 0, _height ); /* right side up */ + } + + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + draw(); +} + +void FGPanel::draw() +{ + glClear( GL_COLOR_BUFFER_BIT); + + // save some state + glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT + | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE + | GL_DEPTH_BUFFER_BIT ); + + // Draw the background + glEnable(GL_TEXTURE_2D); + + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_ALPHA_TEST); + glEnable(GL_COLOR_MATERIAL); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_DEPTH_TEST); + + if (_bg != NULL) { + _bg->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f(0, 0); + glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0); + glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height); + glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height); + glEnd(); + } else if( _mbg[0] != NULL ) { + for (int i = 0; i < 4; i ++) { + // top row of textures...(1,3,5,7) + _mbg[i*2]->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4, _height/2); + glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2); + glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height); + glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4, _height); + glEnd(); + // bottom row of textures...(2,4,6,8) + _mbg[i*2+1]->bind(); +// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4, 0); + glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0); + glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2); + glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4, _height/2); + glEnd(); + } + } else { + float c[4]; + glGetFloatv( GL_CURRENT_COLOR, c ); + glColor4f( 0.0, 0.0, 0.0, 1.0 ); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(_width, 0); + glVertex2f(_width, _height); + glVertex2f(0, _height); + glEnd(); + glColor4fv( c ); + } + + // Draw the instruments. + // Syd Adams: added instrument clipping + instrument_list_type::const_iterator current = _instruments.begin(); + instrument_list_type::const_iterator end = _instruments.end(); + + GLdouble blx[4]={1.0,0.0,0.0,0.0}; + GLdouble bly[4]={0.0,1.0,0.0,0.0}; + GLdouble urx[4]={-1.0,0.0,0.0,0.0}; + GLdouble ury[4]={0.0,-1.0,0.0,0.0}; + + for ( ; current != end; current++) { + FGPanelInstrument * instr = *current; + glPushMatrix(); + glTranslated(instr->getXPos(), instr->getYPos(), 0); + + int ix= instr->getWidth(); + int iy= instr->getHeight(); + glPushMatrix(); + glTranslated(-ix/2,-iy/2,0); + glClipPlane(GL_CLIP_PLANE0,blx); + glClipPlane(GL_CLIP_PLANE1,bly); + glEnable(GL_CLIP_PLANE0); + glEnable(GL_CLIP_PLANE1); + + glTranslated(ix,iy,0); + glClipPlane(GL_CLIP_PLANE2,urx); + glClipPlane(GL_CLIP_PLANE3,ury); + glEnable(GL_CLIP_PLANE2); + glEnable(GL_CLIP_PLANE3); + glPopMatrix(); + instr->draw(); + + glPopMatrix(); + } + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + // restore some original state + glPopAttrib(); +} +#endif + +/** + * Set the panel's background texture. + */ +void +FGPanel::setBackground (FGCroppedTexture_ptr texture) +{ + _bg = texture; +} + +/** + * Set the panel's multiple background textures. + */ +void +FGPanel::setMultiBackground (FGCroppedTexture_ptr texture, int idx) +{ + _bg = 0; + _mbg[idx] = texture; +} + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGPanelTransformation. +//////////////////////////////////////////////////////////////////////// + +FGPanelTransformation::FGPanelTransformation () + : table(0) +{ +} + +FGPanelTransformation::~FGPanelTransformation () +{ + delete table; +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGPanelInstrument. +//////////////////////////////////////////////////////////////////////// + + +FGPanelInstrument::FGPanelInstrument () +{ + setPosition(0, 0); + setSize(0, 0); +} + +FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h) +{ + setPosition(x, y); + setSize(w, h); +} + +FGPanelInstrument::~FGPanelInstrument () +{ +} + +void +FGPanelInstrument::setPosition (int x, int y) +{ + _x = x; + _y = y; +} + +void +FGPanelInstrument::setSize (int w, int h) +{ + _w = w; + _h = h; +} + +int +FGPanelInstrument::getXPos () const +{ + return _x; +} + +int +FGPanelInstrument::getYPos () const +{ + return _y; +} + +int +FGPanelInstrument::getWidth () const +{ + return _w; +} + +int +FGPanelInstrument::getHeight () const +{ + return _h; +} + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGLayeredInstrument. +//////////////////////////////////////////////////////////////////////// + +FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h) + : FGPanelInstrument(x, y, w, h) +{ +} + +FGLayeredInstrument::~FGLayeredInstrument () +{ + for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) { + delete *it; + *it = 0; + } +} + +void +FGLayeredInstrument::draw () +{ + if (!test()) + return; + + for (int i = 0; i < (int)_layers.size(); i++) { + glPushMatrix(); + _layers[i]->draw(); + glPopMatrix(); + } +} + +int +FGLayeredInstrument::addLayer (FGInstrumentLayer *layer) +{ + int n = _layers.size(); + if (layer->getWidth() == -1) { + layer->setWidth(getWidth()); + } + if (layer->getHeight() == -1) { + layer->setHeight(getHeight()); + } + _layers.push_back(layer); + return n; +} + +int +FGLayeredInstrument::addLayer (FGCroppedTexture_ptr texture, int w, int h) +{ + return addLayer(new FGTexturedLayer(texture, w, h)); +} + +void +FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation) +{ + int layer = _layers.size() - 1; + _layers[layer]->addTransformation(transformation); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGInstrumentLayer. +//////////////////////////////////////////////////////////////////////// + +FGInstrumentLayer::FGInstrumentLayer (int w, int h) + : _w(w), + _h(h) +{ +} + +FGInstrumentLayer::~FGInstrumentLayer () +{ + for (transformation_list::iterator it = _transformations.begin(); + it != _transformations.end(); + it++) { + delete *it; + *it = 0; + } +} + +void +FGInstrumentLayer::transform () const +{ + transformation_list::const_iterator it = _transformations.begin(); + transformation_list::const_iterator last = _transformations.end(); + while (it != last) { + FGPanelTransformation *t = *it; + if (t->test()) { + float val = (t->node == 0 ? 0.0 : t->node->getFloatValue()); + + if (t->has_mod) + val = fmod(val, t->mod); + if (val < t->min) { + val = t->min; + } else if (val > t->max) { + val = t->max; + } + + if (t->table==0) { + val = val * t->factor + t->offset; + } else { + val = t->table->interpolate(val) * t->factor + t->offset; + } + + switch (t->type) { + case FGPanelTransformation::XSHIFT: + glTranslatef(val, 0.0, 0.0); + break; + case FGPanelTransformation::YSHIFT: + glTranslatef(0.0, val, 0.0); + break; + case FGPanelTransformation::ROTATION: + glRotatef(-val, 0.0, 0.0, 1.0); + break; + } + } + it++; + } +} + +void +FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation) +{ + _transformations.push_back(transformation); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGGroupLayer. +//////////////////////////////////////////////////////////////////////// + +FGGroupLayer::FGGroupLayer () +{ +} + +FGGroupLayer::~FGGroupLayer () +{ + for (unsigned int i = 0; i < _layers.size(); i++) + delete _layers[i]; +} + +void +FGGroupLayer::draw () +{ + if (test()) { + transform(); + int nLayers = _layers.size(); + for (int i = 0; i < nLayers; i++) + _layers[i]->draw( ); + } +} + +void +FGGroupLayer::addLayer (FGInstrumentLayer * layer) +{ + _layers.push_back(layer); +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGTexturedLayer. +//////////////////////////////////////////////////////////////////////// + + +FGTexturedLayer::FGTexturedLayer (FGCroppedTexture_ptr texture, int w, int h) + : FGInstrumentLayer(w, h), + _emissive(false), + displayList(0) +{ + setTexture(texture); +} + + +FGTexturedLayer::~FGTexturedLayer () +{ +} + +GLuint +FGTexturedLayer::getDisplayList() +{ + if( displayList != 0 ) + return displayList; + + int w2 = _w / 2; + int h2 = _h / 2; + + _texture->bind( false ); + displayList = glGenLists(1); + glNewList(displayList,GL_COMPILE_AND_EXECUTE); + glBindTexture( GL_TEXTURE_2D, _texture->getTexture() ); + glBegin(GL_QUADS); + glTexCoord2f(_texture->getMinX(), _texture->getMinY()); glVertex2f(-w2, -h2); + glTexCoord2f(_texture->getMaxX(), _texture->getMinY()); glVertex2f(w2, -h2); + glTexCoord2f(_texture->getMaxX(), _texture->getMaxY()); glVertex2f(w2, h2); + glTexCoord2f(_texture->getMinX(), _texture->getMaxY()); glVertex2f(-w2, h2); + glEnd(); + glEndList(); + + return displayList; +} + +void +FGTexturedLayer::draw ( ) +{ + if (test()) { + transform(); + glCallList(getDisplayList()); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGTextLayer. +//////////////////////////////////////////////////////////////////////// + +fntRenderer FGTextLayer::text_renderer; + +FGTextLayer::FGTextLayer (int w, int h) + : FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("Helvetica.txf") +{ + _then.stamp(); + _color[0] = _color[1] = _color[2] = 0.0; + _color[3] = 1.0; +} + +FGTextLayer::~FGTextLayer () +{ + chunk_list::iterator it = _chunks.begin(); + chunk_list::iterator last = _chunks.end(); + for ( ; it != last; it++) { + delete *it; + } +} + +void +FGTextLayer::draw () +{ + if (test()) { + float c[4]; + glGetFloatv( GL_CURRENT_COLOR, c ); + glColor4fv(_color); + transform(); + + text_renderer.setFont(ApplicationProperties::fontCache.getTexFont(_font_name.c_str())); + + text_renderer.setPointSize(_pointSize); + text_renderer.begin(); + text_renderer.start3f(0, 0, 0); + + _now.stamp(); + long diff = (_now - _then).toUSecs(); + + if (diff > 100000 || diff < 0 ) { + // ( diff < 0 ) is a sanity check and indicates our time stamp + // difference math probably overflowed. We can handle a max + // difference of 35.8 minutes since the returned value is in + // usec. So if the panel is left off longer than that we can + // over flow the math with it is turned back on. This (diff < + // 0) catches that situation, get's us out of trouble, and + // back on track. + recalc_value(); + _then = _now; + } + + // Something is goofy. The code in this file renders only CCW + // polygons, and I have verified that the font code in plib + // renders only CCW trianbles. Yet they come out backwards. + // Something around here or in plib is either changing the winding + // order or (more likely) pushing a left-handed matrix onto the + // stack. But I can't find it; get out the chainsaw... + glFrontFace(GL_CW); + text_renderer.puts((char *)(_value.c_str())); + glFrontFace(GL_CCW); + + text_renderer.end(); + glColor4fv( c ); + } +} + +void +FGTextLayer::addChunk (FGTextLayer::Chunk * chunk) +{ + _chunks.push_back(chunk); +} + +void +FGTextLayer::setColor (float r, float g, float b) +{ + _color[0] = r; + _color[1] = g; + _color[2] = b; + _color[3] = 1.0; +} + +void +FGTextLayer::setPointSize (float size) +{ + _pointSize = size; +} + +void +FGTextLayer::setFontName(const string &name) +{ + _font_name = name + ".txf"; +} + + +void +FGTextLayer::setFont(fntFont * font) +{ + FGTextLayer::text_renderer.setFont(font); +} + + +void +FGTextLayer::recalc_value () const +{ + _value = ""; + chunk_list::const_iterator it = _chunks.begin(); + chunk_list::const_iterator last = _chunks.end(); + for ( ; it != last; it++) { + _value += (*it)->getValue(); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGTextLayer::Chunk. +//////////////////////////////////////////////////////////////////////// + +FGTextLayer::Chunk::Chunk (const string &text, const string &fmt) + : _type(FGTextLayer::TEXT), _fmt(fmt) +{ + _text = text; + if (_fmt.empty()) + _fmt = "%s"; +} + +FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node, + const string &fmt, float mult, float offs, + bool truncation) + : _type(type), _fmt(fmt), _mult(mult), _offs(offs), _trunc(truncation) +{ + if (_fmt.empty()) { + if (type == TEXT_VALUE) + _fmt = "%s"; + else + _fmt = "%.2f"; + } + _node = node; +} + +const char * +FGTextLayer::Chunk::getValue () const +{ + if (test()) { + _buf[0] = '\0'; + switch (_type) { + case TEXT: + sprintf(_buf, _fmt.c_str(), _text.c_str()); + return _buf; + case TEXT_VALUE: + sprintf(_buf, _fmt.c_str(), _node->getStringValue()); + break; + case DOUBLE_VALUE: + double d = _offs + _node->getFloatValue() * _mult; + if (_trunc) d = (d < 0) ? -floor(-d) : floor(d); + sprintf(_buf, _fmt.c_str(), d); + break; + } + return _buf; + } else { + return ""; + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Implementation of FGSwitchLayer. +//////////////////////////////////////////////////////////////////////// + +FGSwitchLayer::FGSwitchLayer () + : FGGroupLayer() +{ +} + +void +FGSwitchLayer::draw () +{ + if (test()) { + transform(); + int nLayers = _layers.size(); + for (int i = 0; i < nLayers; i++) { + if (_layers[i]->test()) { + _layers[i]->draw(); + return; + } + } + } +} + + +// end of panel.cxx diff --git a/utils/fgpanel/panel.hxx b/utils/fgpanel/panel.hxx new file mode 100644 index 000000000..44ce96a05 --- /dev/null +++ b/utils/fgpanel/panel.hxx @@ -0,0 +1,451 @@ +// panel.hxx - generic support classes for a 2D panel. +// +// Written by David Megginson, started January 2000. +// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef __PANEL_HXX +#define __PANEL_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <plib/fnt.h> + +#include <simgear/props/condition.hxx> +#include <simgear/structure/subsystem_mgr.hxx> +#include <simgear/math/interpolater.hxx> +#include <simgear/sg_inlines.h> +#include "FGTextureLoaderInterface.hxx" + +class FGPanelInstrument; + + +//////////////////////////////////////////////////////////////////////// +// Texture management. +//////////////////////////////////////////////////////////////////////// + +class FGCroppedTexture; +typedef SGSharedPtr<FGCroppedTexture> FGCroppedTexture_ptr; +/** + * Cropped texture (should migrate out into FGFS). + * + * This structure wraps an SSG texture with cropping information. + */ +class FGCroppedTexture : public SGReferenced +{ +public: + FGCroppedTexture (const string &path, + float _minX = 0.0, float _minY = 0.0, + float _maxX = 1.0, float _maxY = 1.0); + + virtual ~FGCroppedTexture (); + + virtual void setPath (const string &path) { _path = path; } + + virtual const string &getPath () const { return _path; } + + virtual void setCrop (float minX, float minY, float maxX, float maxY) { + _minX = minX; _minY = minY; _maxX = maxX; _maxY = maxY; + } + + static void registerTextureLoader( const string & extension, FGTextureLoaderInterface * loader ) { + if( textureLoader.count( extension ) == 0 ) + textureLoader[extension] = loader; + } + + virtual float getMinX () const { return _minX; } + virtual float getMinY () const { return _minY; } + virtual float getMaxX () const { return _maxX; } + virtual float getMaxY () const { return _maxY; } + GLuint getTexture() const { return _texture; } + + virtual void bind( bool doGLBind = true ); + +private: + string _path; + float _minX, _minY, _maxX, _maxY; + + GLuint _texture; + static GLuint current_bound_texture; + static map<string,GLuint> cache; + static map<string,FGTextureLoaderInterface*> textureLoader; +}; + + + +//////////////////////////////////////////////////////////////////////// +// Top-level panel. +//////////////////////////////////////////////////////////////////////// + + +/** + * Instrument panel class. + * + * The panel is a container that has a background texture and holds + * zero or more instruments. The panel will order the instruments to + * redraw themselves when necessary, and will pass mouse clicks on to + * the appropriate instruments for processing. + */ +class FGPanel : public SGSubsystem +{ +public: + + FGPanel ( SGPropertyNode_ptr root ); + virtual ~FGPanel (); + + // Update the panel (every frame). + virtual void init (); + virtual void bind (); + virtual void unbind (); +// virtual void draw (); + virtual void update (double dt); + + // transfer pointer ownership!!! + virtual void addInstrument (FGPanelInstrument * instrument); + + // Background texture. + virtual void setBackground (FGCroppedTexture_ptr texture); + inline void setBackgroundWidth( double d ) { + _bg_width = d; + } + + inline void setBackgroundHeight( double d ) { + _bg_height = d; + } + + // Background multiple textures. + virtual void setMultiBackground (FGCroppedTexture_ptr texture , int idx); + + // Full width of panel. + virtual void setWidth (int width) { _width = width; } + virtual int getWidth () const { return _width; } + + // Full height of panel. + virtual void setHeight (int height) { _height = height; } + virtual int getHeight () const { return _height; } + +private: + + typedef vector<FGPanelInstrument *> instrument_list_type; + int _width; + int _height; + + SGPropertyNode_ptr _root; + SGPropertyNode_ptr _flipx; + SGPropertyNode_ptr _rotate; + + FGCroppedTexture_ptr _bg; + double _bg_width; + double _bg_height; + FGCroppedTexture_ptr _mbg[8]; + // List of instruments in panel. + instrument_list_type _instruments; + + GLuint initDisplayList; + + GLuint getInitDisplayList(); +}; + + + +//////////////////////////////////////////////////////////////////////// +// Transformations. +//////////////////////////////////////////////////////////////////////// + + +/** + * A transformation for a layer. + */ +class FGPanelTransformation : public SGConditional +{ +public: + + enum Type { + XSHIFT, + YSHIFT, + ROTATION + }; + + FGPanelTransformation (); + virtual ~FGPanelTransformation (); + + Type type; + SGConstPropertyNode_ptr node; + float min; + float max; + bool has_mod; + float mod; + float factor; + float offset; + SGInterpTable * table; +}; + + + + +//////////////////////////////////////////////////////////////////////// +// Layers +//////////////////////////////////////////////////////////////////////// + + +/** + * A single layer of a multi-layered instrument. + * + * Each layer can be subject to a series of transformations based + * on current FGFS instrument readings: for example, a texture + * representing a needle can rotate to show the airspeed. + */ +class FGInstrumentLayer : public SGConditional +{ +public: + + FGInstrumentLayer (int w = -1, int h = -1); + virtual ~FGInstrumentLayer (); + + virtual void draw () = 0; + virtual void transform () const; + + virtual int getWidth () const { return _w; } + virtual int getHeight () const { return _h; } + virtual void setWidth (int w) { _w = w; } + virtual void setHeight (int h) { _h = h; } + + // Transfer pointer ownership!! + // DEPRECATED + virtual void addTransformation (FGPanelTransformation * transformation); + +protected: + int _w, _h; + + typedef vector<FGPanelTransformation *> transformation_list; + transformation_list _transformations; +}; + + + +//////////////////////////////////////////////////////////////////////// +// Instruments. +//////////////////////////////////////////////////////////////////////// + + +/** + * Abstract base class for a panel instrument. + * + * A panel instrument consists of zero or more actions, associated + * with mouse clicks in rectangular areas. Currently, the only + * concrete class derived from this is FGLayeredInstrument, but others + * may show up in the future (some complex instruments could be + * entirely hand-coded, for example). + */ +class FGPanelInstrument : public SGConditional +{ +public: + FGPanelInstrument (); + FGPanelInstrument (int x, int y, int w, int h); + virtual ~FGPanelInstrument (); + + virtual void draw () = 0; + + virtual void setPosition(int x, int y); + virtual void setSize(int w, int h); + + virtual int getXPos () const; + virtual int getYPos () const; + virtual int getWidth () const; + virtual int getHeight () const; + +protected: + int _x, _y, _w, _h; +}; + + +/** + * An instrument constructed of multiple layers. + * + * Each individual layer can be rotated or shifted to correspond + * to internal FGFS instrument readings. + */ +class FGLayeredInstrument : public FGPanelInstrument +{ +public: + FGLayeredInstrument (int x, int y, int w, int h); + virtual ~FGLayeredInstrument (); + + virtual void draw (); + + // Transfer pointer ownership!! + virtual int addLayer (FGInstrumentLayer *layer); + virtual int addLayer (FGCroppedTexture_ptr texture, int w = -1, int h = -1); + + // Transfer pointer ownership!! + virtual void addTransformation (FGPanelTransformation * transformation); + +protected: + typedef vector<FGInstrumentLayer *> layer_list; + layer_list _layers; +}; + + +/** + * An instrument layer containing a group of sublayers. + * + * This class is useful for gathering together a group of related + * layers, either to hold in an external file or to work under + * the same condition. + */ +class FGGroupLayer : public FGInstrumentLayer +{ +public: + FGGroupLayer (); + virtual ~FGGroupLayer (); + virtual void draw (); + // transfer pointer ownership + virtual void addLayer (FGInstrumentLayer * layer); +protected: + vector<FGInstrumentLayer *> _layers; +}; + + +/** + * A textured layer of an instrument. + * + * This is a layer holding a single texture. Normally, the texture's + * backgound should be transparent so that lower layers and the panel + * background can show through. + */ +class FGTexturedLayer : public FGInstrumentLayer +{ +public: + FGTexturedLayer (int w = -1, int h = -1) : FGInstrumentLayer(w, h) {} + FGTexturedLayer (FGCroppedTexture_ptr texture, int w = -1, int h = -1); + virtual ~FGTexturedLayer (); + + virtual void draw (); + + virtual void setTexture (FGCroppedTexture_ptr texture) { + _texture = texture; + } + FGCroppedTexture_ptr getTexture() { return _texture; } + + void setEmissive(bool e) { _emissive = e; } + +private: + GLuint getDisplayList(); + + FGCroppedTexture_ptr _texture; + bool _emissive; + GLuint displayList; +}; + + +/** + * A text layer of an instrument. + * + * This is a layer holding a string of static and/or generated text. + * It is useful for instruments that have text displays, such as + * a chronometer, GPS, or NavCom radio. + */ +class FGTextLayer : public FGInstrumentLayer +{ +public: + enum ChunkType { + TEXT, + TEXT_VALUE, + DOUBLE_VALUE + }; + + class Chunk : public SGConditional + { + public: + Chunk (const string &text, const string &fmt = "%s"); + Chunk (ChunkType type, const SGPropertyNode * node, + const string &fmt = "", float mult = 1.0, float offs = 0.0, + bool truncation = false); + + const char * getValue () const; + private: + ChunkType _type; + string _text; + SGConstPropertyNode_ptr _node; + string _fmt; + float _mult; + float _offs; + bool _trunc; + mutable char _buf[1024]; + + }; + + FGTextLayer (int w = -1, int h = -1); + virtual ~FGTextLayer (); + + virtual void draw (); + + // Transfer pointer!! + virtual void addChunk (Chunk * chunk); + virtual void setColor (float r, float g, float b); + virtual void setPointSize (float size); + virtual void setFontName ( const string &name ); + virtual void setFont (fntFont * font); + +private: + + void recalc_value () const; + + typedef vector<Chunk *> chunk_list; + chunk_list _chunks; + float _color[4]; + + float _pointSize; + mutable string _font_name; + mutable string _value; + mutable SGTimeStamp _then; + mutable SGTimeStamp _now; + + static fntRenderer text_renderer; +}; + + +/** + * A group layer that switches among its children. + * + * The first layer that passes its condition will be drawn, and + * any following layers will be ignored. + */ +class FGSwitchLayer : public FGGroupLayer +{ +public: + // Transfer pointers!! + FGSwitchLayer (); + virtual void draw (); + +}; + + +#endif // __PANEL_HXX + +// end of panel.hxx + + + diff --git a/utils/fgpanel/panel_io.cxx b/utils/fgpanel/panel_io.cxx new file mode 100644 index 000000000..9f5084cd3 --- /dev/null +++ b/utils/fgpanel/panel_io.cxx @@ -0,0 +1,591 @@ +// panel_io.cxx - I/O for 2D panel. +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel_io.cxx,v 1.26 2006/08/10 11:12:39 mfranz Exp $ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_WINDOWS_H +# include <windows.h> +#endif + +#include <string.h> // for strcmp() + +#include <simgear/compiler.h> +#include <simgear/structure/exception.hxx> +#include <simgear/debug/logstream.hxx> +#include <simgear/misc/sg_path.hxx> +#include <simgear/props/props.hxx> +#include <simgear/props/condition.hxx> + +#include "panel.hxx" +#include "panel_io.hxx" +#include "ApplicationProperties.hxx" + + +//////////////////////////////////////////////////////////////////////// +// Read and construct a panel. +// +// The panel is specified as a regular property list, and each of the +// instruments is its own, separate property list (and thus, a separate +// XML document). The functions in this section read in the files +// as property lists, then extract properties to set up the panel +// itself. +// +// A panel contains zero or more instruments. +// +// An instrument contains one or more layers and zero or more actions. +// +// A layer contains zero or more transformations. +// +// Some special types of layers also contain other objects, such as +// chunks of text or other layers. +// +// There are currently four types of layers: +// +// 1. Textured Layer (type="texture"), the default +// 2. Text Layer (type="text") +// 3. Switch Layer (type="switch") +// 4. Built-in Layer (type="built-in", must also specify class) +// +// The only built-in layer so far is the ribbon for the magnetic compass +// (class="compass-ribbon"). +// +// There are three types of actions: +// +// 1. Adjust (type="adjust"), the default +// 2. Swap (type="swap") +// 3. Toggle (type="toggle") +// +// There are three types of transformations: +// +// 1. X shift (type="x-shift"), the default +// 2. Y shift (type="y-shift") +// 3. Rotation (type="rotation") +// +// Each of these may be associated with a property, so that a needle +// will rotate with the airspeed, for example, or may have a fixed +// floating-point value. +//////////////////////////////////////////////////////////////////////// + + +/** + * Read a cropped texture from the instrument's property list. + * + * The x1 and y1 properties give the starting position of the texture + * (between 0.0 and 1.0), and the the x2 and y2 properties give the + * ending position. For example, to use the bottom-left quarter of a + * texture, x1=0.0, y1=0.0, x2=0.5, y2=0.5. + */ +static FGCroppedTexture_ptr +readTexture (const SGPropertyNode * node) +{ + return new FGCroppedTexture(node->getStringValue("path"), + node->getFloatValue("x1"), + node->getFloatValue("y1"), + node->getFloatValue("x2", 1.0), + node->getFloatValue("y2", 1.0)); + SG_LOG(SG_COCKPIT, SG_DEBUG, "Read texture " << node->getName()); +} + +/** + * Test for a condition in the current node. + */ + +//////////////////////////////////////////////////////////////////////// +// Read a condition and use it if necessary. +//////////////////////////////////////////////////////////////////////// + +static void +readConditions (SGConditional *component, const SGPropertyNode *node) +{ + const SGPropertyNode * conditionNode = node->getChild("condition"); + if (conditionNode != 0) + // The top level is implicitly AND + component->setCondition(sgReadCondition(ApplicationProperties::Properties, + conditionNode) ); + ; +} + + +/** + * Read a transformation from the instrument's property list. + * + * The panel module uses the transformations to slide or spin needles, + * knobs, and other indicators, and to place layers in the correct + * positions. Every layer starts centered exactly on the x,y co-ordinate, + * and many layers need to be moved or rotated simply to display the + * instrument correctly. + * + * There are three types of transformations: + * + * "x-shift" - move the layer horizontally. + * + * "y-shift" - move the layer vertically. + * + * "rotation" - rotate the layer. + * + * Each transformation may have a fixed offset, and may also have + * a floating-point property value to add to the offset. The + * floating-point property may be clamped to a minimum and/or + * maximum range and scaled (after clamping). + * + * Note that because of the way OpenGL works, transformations will + * appear to be applied backwards. + */ +static FGPanelTransformation * +readTransformation (const SGPropertyNode * node, float w_scale, float h_scale) +{ + FGPanelTransformation * t = new FGPanelTransformation; + + string name = node->getName(); + string type = node->getStringValue("type"); + string propName = node->getStringValue("property", ""); + const SGPropertyNode * target = 0; + + if (type.empty()) { + SG_LOG( SG_COCKPIT, SG_INFO, + "No type supplied for transformation " << name + << " assuming \"rotation\"" ); + type = "rotation"; + } + + if (!propName.empty()) + target = ApplicationProperties::Properties->getNode(propName.c_str(), true); + + t->node = target; + t->min = node->getFloatValue("min", -9999999); + t->max = node->getFloatValue("max", 99999999); + t->has_mod = node->hasChild("modulator"); + if (t->has_mod) + t->mod = node->getFloatValue("modulator"); + t->factor = node->getFloatValue("scale", 1.0); + t->offset = node->getFloatValue("offset", 0.0); + + + // Check for an interpolation table + const SGPropertyNode * trans_table = node->getNode("interpolation"); + if (trans_table != 0) { + SG_LOG( SG_COCKPIT, SG_INFO, "Found interpolation table with " + << trans_table->nChildren() << " children" ); + t->table = new SGInterpTable(); + for (int i = 0; i < trans_table->nChildren(); i++) { + const SGPropertyNode * node = trans_table->getChild(i); + if (!strcmp(node->getName(), "entry")) { + double ind = node->getDoubleValue("ind", 0.0); + double dep = node->getDoubleValue("dep", 0.0); + SG_LOG( SG_COCKPIT, SG_INFO, "Adding interpolation entry " + << ind << "==>" << dep ); + t->table->addEntry(ind, dep); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in interpolation" ); + } + } + } else { + t->table = 0; + } + + // Move the layer horizontally. + if (type == "x-shift") { + t->type = FGPanelTransformation::XSHIFT; +// t->min *= w_scale; //removed by Martin Dressler +// t->max *= w_scale; //removed by Martin Dressler + t->offset *= w_scale; + t->factor *= w_scale; //Added by Martin Dressler + } + + // Move the layer vertically. + else if (type == "y-shift") { + t->type = FGPanelTransformation::YSHIFT; + //t->min *= h_scale; //removed + //t->max *= h_scale; //removed + t->offset *= h_scale; + t->factor *= h_scale; //Added + } + + // Rotate the layer. The rotation + // is in degrees, and does not need + // to scale with the instrument size. + else if (type == "rotation") { + t->type = FGPanelTransformation::ROTATION; + } + + else { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized transformation type " << type ); + delete t; + return 0; + } + + readConditions(t, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Read transformation " << name ); + return t; +} + + +/** + * Read a chunk of text from the instrument's property list. + * + * A text layer consists of one or more chunks of text. All chunks + * share the same font size and color (and eventually, font), but + * each can come from a different source. There are three types of + * text chunks: + * + * "literal" - a literal text string (the default) + * + * "text-value" - the current value of a string property + * + * "number-value" - the current value of a floating-point property. + * + * All three may also include a printf-style format string. + */ +FGTextLayer::Chunk * +readTextChunk (const SGPropertyNode * node) +{ + FGTextLayer::Chunk * chunk; + string name = node->getStringValue("name"); + string type = node->getStringValue("type"); + string format = node->getStringValue("format"); + + // Default to literal text. + if (type.empty()) { + SG_LOG( SG_COCKPIT, SG_INFO, "No type provided for text chunk " << name + << " assuming \"literal\""); + type = "literal"; + } + + // A literal text string. + if (type == "literal") { + string text = node->getStringValue("text"); + chunk = new FGTextLayer::Chunk(text, format); + } + + // The value of a string property. + else if (type == "text-value") { + SGPropertyNode * target = + ApplicationProperties::Properties->getNode( node->getStringValue("property"), true); + chunk = new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE, target, format); + } + + // The value of a float property. + else if (type == "number-value") { + string propName = node->getStringValue("property"); + float scale = node->getFloatValue("scale", 1.0); + float offset = node->getFloatValue("offset", 0.0); + bool truncation = node->getBoolValue("truncate", false); + SGPropertyNode * target = ApplicationProperties::Properties->getNode(propName.c_str(), true); + chunk = new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE, target, + format, scale, offset, truncation); + } + + // Unknown type. + else { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized type " << type + << " for text chunk " << name ); + return 0; + } + + readConditions(chunk, node); + return chunk; +} + + +/** + * Read a single layer from an instrument's property list. + * + * Each instrument consists of one or more layers stacked on top + * of each other; the lower layers show through only where the upper + * layers contain an alpha component. Each layer can be moved + * horizontally and vertically and rotated using transformations. + * + * This module currently recognizes four kinds of layers: + * + * "texture" - a layer containing a texture (the default) + * + * "text" - a layer containing text + * + * "switch" - a layer that switches between two other layers + * based on the current value of a boolean property. + * + * "built-in" - a hard-coded layer supported by C++ code in FlightGear. + * + * Currently, the only built-in layer class is "compass-ribbon". + */ +static FGInstrumentLayer * +readLayer (const SGPropertyNode * node, float w_scale, float h_scale) +{ + FGInstrumentLayer * layer = NULL; + string name = node->getStringValue("name"); + string type = node->getStringValue("type"); + int w = node->getIntValue("w", -1); + int h = node->getIntValue("h", -1); + bool emissive = node->getBoolValue("emissive", false); + if (w != -1) + w = int(w * w_scale); + if (h != -1) + h = int(h * h_scale); + + + if (type.empty()) { + SG_LOG( SG_COCKPIT, SG_INFO, + "No type supplied for layer " << name + << " assuming \"texture\"" ); + type = "texture"; + } + + + // A textured instrument layer. + if (type == "texture") { + FGCroppedTexture_ptr texture = readTexture(node->getNode("texture")); + layer = new FGTexturedLayer(texture, w, h); + if (emissive) { + FGTexturedLayer *tl=(FGTexturedLayer*)layer; + tl->setEmissive(true); + } + + } + // A group of sublayers. + else if (type == "group") { + layer = new FGGroupLayer(); + for (int i = 0; i < node->nChildren(); i++) { + const SGPropertyNode * child = node->getChild(i); + if (!strcmp(child->getName(), "layer")) + ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale)); + } + } + + + // A textual instrument layer. + else if (type == "text") { + FGTextLayer * tlayer = new FGTextLayer(w, h); // FIXME + + // Set the text color. + float red = node->getFloatValue("color/red", 0.0); + float green = node->getFloatValue("color/green", 0.0); + float blue = node->getFloatValue("color/blue", 0.0); + tlayer->setColor(red, green, blue); + + // Set the point size. + float pointSize = node->getFloatValue("point-size", 10.0) * w_scale; + tlayer->setPointSize(pointSize); + + // Set the font. + string fontName = node->getStringValue("font", "Helvetica"); + tlayer->setFontName(fontName); + + const SGPropertyNode * chunk_group = node->getNode("chunks"); + if (chunk_group != 0) { + int nChunks = chunk_group->nChildren(); + for (int i = 0; i < nChunks; i++) { + const SGPropertyNode * node = chunk_group->getChild(i); + if (!strcmp(node->getName(), "chunk")) { + FGTextLayer::Chunk * chunk = readTextChunk(node); + if (chunk != 0) + tlayer->addChunk(chunk); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in chunks" ); + } + } + layer = tlayer; + } + } + + // A switch instrument layer. + else if (type == "switch") { + layer = new FGSwitchLayer(); + for (int i = 0; i < node->nChildren(); i++) { + const SGPropertyNode * child = node->getChild(i); + if (!strcmp(child->getName(), "layer")) + ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale)); + } + } + + // An unknown type. + else { + SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized layer type " << type ); + delete layer; + return 0; + } + + // + // Get the transformations for each layer. + // + const SGPropertyNode * trans_group = node->getNode("transformations"); + if (trans_group != 0) { + int nTransformations = trans_group->nChildren(); + for (int i = 0; i < nTransformations; i++) { + const SGPropertyNode * node = trans_group->getChild(i); + if (!strcmp(node->getName(), "transformation")) { + FGPanelTransformation * t = readTransformation(node, w_scale, h_scale); + if (t != 0) + layer->addTransformation(t); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in transformations" ); + } + } + } + + readConditions(layer, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Read layer " << name ); + return layer; +} + + +/** + * Read an instrument from a property list. + * + * The instrument consists of a preferred width and height + * (the panel may override these), together with a list of layers + * and a list of actions to be performed when the user clicks + * the mouse over the instrument. All co-ordinates are relative + * to the instrument's position, so instruments are fully relocatable; + * likewise, co-ordinates for actions and transformations will be + * scaled automatically if the instrument is not at its preferred size. + */ +static FGPanelInstrument * +readInstrument (const SGPropertyNode * node) +{ + const string name = node->getStringValue("name"); + int x = node->getIntValue("x", -1); + int y = node->getIntValue("y", -1); + int real_w = node->getIntValue("w", -1); + int real_h = node->getIntValue("h", -1); + int w = node->getIntValue("w-base", -1); + int h = node->getIntValue("h-base", -1); + + if (x == -1 || y == -1) { + SG_LOG( SG_COCKPIT, SG_ALERT, + "x and y positions must be specified and > 0" ); + return 0; + } + + float w_scale = 1.0; + float h_scale = 1.0; + if (real_w != -1) { + w_scale = float(real_w) / float(w); + w = real_w; + } + if (real_h != -1) { + h_scale = float(real_h) / float(h); + h = real_h; + } + + SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading instrument " << name ); + + FGLayeredInstrument * instrument = + new FGLayeredInstrument(x, y, w, h); + + // + // Get the layers for the instrument. + // + const SGPropertyNode * layer_group = node->getNode("layers"); + if (layer_group != 0) { + int nLayers = layer_group->nChildren(); + for (int i = 0; i < nLayers; i++) { + const SGPropertyNode * node = layer_group->getChild(i); + if (!strcmp(node->getName(), "layer")) { + FGInstrumentLayer * layer = readLayer(node, w_scale, h_scale); + if (layer != 0) + instrument->addLayer(layer); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in layers" ); + } + } + } + + readConditions(instrument, node); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Done reading instrument " << name ); + return instrument; +} + + +/** + * Construct the panel from a property tree. + */ +SGSharedPtr<FGPanel> +FGReadablePanel::read(SGPropertyNode_ptr root) +{ + SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " << + root->getStringValue("name", "[Unnamed Panel]") ); + + FGPanel * panel = new FGPanel(root); + panel->setWidth(root->getIntValue("w", 1024)); + panel->setHeight(root->getIntValue("h", 443)); + + SG_LOG( SG_COCKPIT, SG_INFO, "Size=" << panel->getWidth() << "x" << panel->getHeight() ); + + // Assign the background texture, if any, or a bogus chequerboard. + // + string bgTexture = root->getStringValue("background"); + if( !bgTexture.empty() ) + panel->setBackground( new FGCroppedTexture( bgTexture ) ); + panel->setBackgroundWidth( root->getDoubleValue( "background-width", 1.0 ) ); + panel->setBackgroundHeight( root->getDoubleValue( "background-height", 1.0 ) ); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture ); + + // + // Get multibackground if any... + // + for( int i = 0; i < 8; i++ ) { + SGPropertyNode * mbgNode = root->getChild( "multibackground", i ); + string mbgTexture; + if( mbgNode != NULL ) mbgTexture = mbgNode->getStringValue(); + if( mbgTexture.empty() ) { + if( i == 0 ) break; // if first texture is missing, ignore the rest + else mbgTexture = "FOO"; // if others are missing - set default texture + } + panel->setMultiBackground( new FGCroppedTexture(mbgTexture), i ); + SG_LOG( SG_COCKPIT, SG_INFO, "Set multi-background texture" << i << " to " << mbgTexture ); + } + // + // Create each instrument. + // + SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" ); + const SGPropertyNode * instrument_group = root->getChild("instruments"); + if (instrument_group != 0) { + int nInstruments = instrument_group->nChildren(); + for (int i = 0; i < nInstruments; i++) { + const SGPropertyNode * node = instrument_group->getChild(i); + if (!strcmp(node->getName(), "instrument")) { + FGPanelInstrument * instrument = readInstrument(node); + if (instrument != 0) + panel->addInstrument(instrument); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in instruments section" ); + } + } + } + SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" ); + + + // + // Return the new panel. + // + return panel; +} + +// end of panel_io.cxx + + + diff --git a/utils/fgpanel/panel_io.hxx b/utils/fgpanel/panel_io.hxx new file mode 100644 index 000000000..e353ce04e --- /dev/null +++ b/utils/fgpanel/panel_io.hxx @@ -0,0 +1,40 @@ +// panel_io.cxx - I/O for 2D panel. +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel_io.hxx,v 1.6 2006/04/17 13:03:43 mfranz Exp $ + +#ifndef __PANEL_IO_HXX +#define __PANEL_IO_HXX + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_WINDOWS_H +# include <windows.h> +#endif + +#include "panel.hxx" + +class FGReadablePanel : public FGPanel { +public: + static SGSharedPtr<FGPanel> read(SGPropertyNode_ptr root); +}; + + +#endif // __PANEL_IO_HXX